Skip to content

Commit 9d74cd5

Browse files
authored
Improve the enduser setup experience by triggering captive portal detection. (#3282)
* Make captive portal detection work on macOS * Change the default SSID prefix to be NodeMCU
1 parent dc334f8 commit 9d74cd5

File tree

5 files changed

+346
-291
lines changed

5 files changed

+346
-291
lines changed

app/include/user_config.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@
174174
// If you use the enduser_setup module, then you can also set the default
175175
// SSID when this module is running in AP mode.
176176

177-
#define ENDUSER_SETUP_AP_SSID "SetupGadget"
177+
#define ENDUSER_SETUP_AP_SSID "NodeMCU"
178178

179179

180180
// I2C software driver partially supports use of GPIO16 (D0) pin for SCL line.

app/modules/enduser_setup.c

+50-27
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ static const char http_html_gz_filename[] = "enduser_setup.html.gz";
9292
static const char http_html_filename[] = "enduser_setup.html";
9393
static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html; charset=utf-8\r\n"; /* Note single \r\n here! */
9494
static const char http_header_204[] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
95-
static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
95+
static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: http://nodemcu.portal/\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
9696
static const char http_header_302_trying[] = "HTTP/1.1 302 Moved\r\nLocation: /?trying=true\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
9797
static const char http_header_400[] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n";
9898
static const char http_header_404[] = "HTTP/1.1 404 Not found\r\nContent-Length:10\r\nConnection:close\r\n\r\nNot found\n";
@@ -135,6 +135,7 @@ typedef struct
135135
struct tcp_pcb *http_pcb;
136136
char *http_payload_data;
137137
uint32_t http_payload_len;
138+
char *ap_ssid;
138139
os_timer_t check_station_timer;
139140
os_timer_t shutdown_timer;
140141
int lua_connected_cb_ref;
@@ -931,6 +932,8 @@ static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len)
931932
{
932933
tcp_sent (pcb, 0);
933934
deferred_close (pcb);
935+
free(state->http_payload_data);
936+
state->http_payload_data = NULL;
934937
}
935938
else
936939
tcp_arg (pcb, (void *)offs);
@@ -1123,9 +1126,9 @@ static void enduser_setup_handle_OPTIONS (struct tcp_pcb *http_client, char *dat
11231126

11241127
int type = 0;
11251128

1126-
if (strncmp(data, "GET ", 4) == 0)
1129+
if (strncmp(data, "OPTIONS ", 8) == 0)
11271130
{
1128-
if (strncmp(data + 4, "/aplist", 7) == 0 || strncmp(data + 4, "/setwifi?", 9) == 0 || strncmp(data + 4, "/status.json", 12) == 0)
1131+
if (strncmp(data + 8, "/aplist", 7) == 0 || strncmp(data + 8, "/setwifi?", 9) == 0 || strncmp(data + 8, "/status.json", 12) == 0)
11291132
{
11301133
enduser_setup_http_serve_header (http_client, json, strlen(json));
11311134
return;
@@ -1153,7 +1156,7 @@ static void enduser_setup_handle_POST(struct tcp_pcb *http_client, char* data, s
11531156
{
11541157
case 0: {
11551158
// all went fine, extract all the form data into a file
1156-
enduser_setup_write_file_with_extra_configuration_data(body, bodylength);
1159+
enduser_setup_write_file_with_extra_configuration_data(body, bodylength);
11571160
// redirect user to the base page with the trying flag
11581161
enduser_setup_http_serve_header(http_client, http_header_302_trying, LITLEN(http_header_302_trying));
11591162
break;
@@ -1283,7 +1286,7 @@ static void on_scan_done (void *arg, STATUS status)
12831286
const size_t hdr_sz = sizeof (header_fmt) +1 -1; /* +expand %4d, -\0 */
12841287

12851288
/* To be able to safely escape a pathological SSID, we need 2*32 bytes */
1286-
const size_t max_entry_sz = 27 + 2*32 + 6; /* {"ssid":"","rssi":,"chan":} */
1289+
const size_t max_entry_sz = 35 + 2*32 + 9; /* {"ssid":"","rssi":,"chan":,"auth":} */
12871290
const size_t alloc_sz = hdr_sz + num_nets * max_entry_sz + 3;
12881291
char *http = calloc (1, alloc_sz);
12891292
if (!http)
@@ -1319,6 +1322,12 @@ static void on_scan_done (void *arg, STATUS status)
13191322

13201323
p += sprintf (p, "%d", wn->channel);
13211324

1325+
const char entry_auth[] = ",\"auth\":";
1326+
strcpy (p, entry_auth);
1327+
p += sizeof (entry_auth) -1;
1328+
1329+
p += sprintf (p, "%d", wn->authmode);
1330+
13221331
*p++ = '}';
13231332
}
13241333
*p++ = ']';
@@ -1579,15 +1588,10 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s
15791588
break;
15801589
}
15811590
}
1582-
else if (strncmp(data + 4, "/generate_204", 13) == 0)
1583-
{
1584-
/* Convince Android devices that they have internet access to avoid pesky dialogues. */
1585-
enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204));
1586-
}
15871591
else
15881592
{
1589-
ENDUSER_SETUP_DEBUG("serving 404");
1590-
enduser_setup_http_serve_header(http_client, http_header_404, LITLEN(http_header_404));
1593+
// All other URLs redirect to http://nodemcu.portal/ -- this triggers captive portal.
1594+
enduser_setup_http_serve_header(http_client, http_header_302, LITLEN(http_header_302));
15911595
}
15921596
}
15931597
else if (strncmp(data, "OPTIONS ", 8) == 0)
@@ -1706,18 +1710,23 @@ static void enduser_setup_ap_start(void)
17061710
memset(&(cnf), 0, sizeof(struct softap_config));
17071711

17081712
#ifndef ENDUSER_SETUP_AP_SSID
1709-
#define ENDUSER_SETUP_AP_SSID "SetupGadget"
1713+
#define ENDUSER_SETUP_AP_SSID "NodeMCU"
17101714
#endif
17111715

1712-
char ssid[] = ENDUSER_SETUP_AP_SSID;
1713-
int ssid_name_len = strlen(ssid);
1714-
memcpy(&(cnf.ssid), ssid, ssid_name_len);
1716+
if (state->ap_ssid) {
1717+
strncpy(cnf.ssid, state->ap_ssid, sizeof(cnf.ssid));
1718+
cnf.ssid_len = strlen(cnf.ssid);
1719+
} else {
1720+
char ssid[] = ENDUSER_SETUP_AP_SSID;
1721+
int ssid_name_len = strlen(ssid);
1722+
memcpy(&(cnf.ssid), ssid, ssid_name_len);
17151723

1716-
uint8_t mac[6];
1717-
wifi_get_macaddr(SOFTAP_IF, mac);
1718-
cnf.ssid[ssid_name_len] = '_';
1719-
sprintf(cnf.ssid + ssid_name_len + 1, "%02X%02X%02X", mac[3], mac[4], mac[5]);
1720-
cnf.ssid_len = ssid_name_len + 7;
1724+
uint8_t mac[6];
1725+
wifi_get_macaddr(SOFTAP_IF, mac);
1726+
cnf.ssid[ssid_name_len] = '_';
1727+
sprintf(cnf.ssid + ssid_name_len + 1, "%02X%02X%02X", mac[3], mac[4], mac[5]);
1728+
cnf.ssid_len = ssid_name_len + 7;
1729+
}
17211730
cnf.channel = state == NULL? 1 : state->softAPchannel;
17221731
cnf.authmode = AUTH_OPEN;
17231732
cnf.ssid_hidden = 0;
@@ -1895,6 +1904,8 @@ static void enduser_setup_free(void)
18951904

18961905
free_scan_listeners ();
18971906

1907+
free(state->ap_ssid);
1908+
18981909
free(state);
18991910
state = NULL;
19001911
}
@@ -1999,21 +2010,33 @@ static int enduser_setup_init(lua_State *L)
19992010
}
20002011
}
20012012

2002-
if (!lua_isnoneornil(L, 1))
2013+
int argno = 1;
2014+
2015+
if (lua_isstring(L, argno)) {
2016+
/* Get the SSID */
2017+
state->ap_ssid = strdup(lua_tostring(L, argno));
2018+
argno++;
2019+
}
2020+
2021+
if (!lua_isnoneornil(L, argno))
20032022
{
2004-
lua_pushvalue(L, 1);
2023+
lua_pushvalue(L, argno);
20052024
state->lua_connected_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
20062025
}
20072026

2008-
if (!lua_isnoneornil(L, 2))
2027+
argno++;
2028+
2029+
if (!lua_isnoneornil(L, argno))
20092030
{
2010-
lua_pushvalue (L, 2);
2031+
lua_pushvalue (L, argno);
20112032
state->lua_err_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
20122033
}
20132034

2014-
if (!lua_isnoneornil(L, 3))
2035+
argno++;
2036+
2037+
if (!lua_isnoneornil(L, argno))
20152038
{
2016-
lua_pushvalue (L, 3);
2039+
lua_pushvalue (L, argno);
20172040
state->lua_dbg_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
20182041
ENDUSER_SETUP_DEBUG("enduser_setup_init: Debug callback has been set");
20192042
}

app/modules/enduser_setup/enduser_setup.html

+52-36
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ <h3>Connect device to your Wi-Fi</h3>
160160
<select id=aplist name=aplist></select>
161161
</div>
162162
<input id=ssid name=wifi_ssid type=text autocorrect=off autocapitalize=none placeholder='Wi-Fi Name' />
163-
<input name=wifi_password type=password autocorrect=off autocapitalize=none autocomplete=off placeholder=Password />
163+
<input id=wifi_password name=wifi_password type=password autocorrect=off autocapitalize=none autocomplete=off placeholder=Password />
164164
<!-- You can add inputs here and have them pop up in your lua code through the file eus_params.lua -->
165165
<input type="submit" value="Save"/>
166166
</form>
@@ -262,46 +262,51 @@ <h4 id='st'>Updating Status...</h4>
262262
xhr.send();
263263
}
264264
function gotAp(s, json) {
265-
var list;
266-
if (s === 200 && json != null) {
267-
if (typeof json === 'string' && json.length > 0) {
268-
list = JSON.parse(json);
269-
} else if (typeof json === 'object') {
270-
list = json;
271-
}
265+
var list;
266+
if (s === 200 && json != null) {
267+
if (typeof json === 'string' && json.length > 0) {
268+
list = JSON.parse(json);
269+
} else if (typeof json === 'object') {
270+
list = json;
271+
}
272272

273-
list.sort(function (a, b) {
274-
return b.rssi - a.rssi;
275-
});
276-
var ops = '<option>Select a Network...</option>';
277-
var seen = {};
278-
for (var i = 0; i < list.length; ++i) {
279-
var ssid = list[i].ssid;
280-
if (!seen[ssid]) {
281-
seen[ssid] = 1;
282-
ops += '<option>' + ssid.replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;") + '</option>';
283-
}
284-
}
285-
ap.innerHTML = ops;
286-
ab.disabled = false;
287-
togAp(null, true);
288-
ab.onclick = togAp;
289-
} else {
290-
ab.innerText = 'No networks found (' + s + ')';
291-
ra = to(refrAp, 5);
292-
}
273+
list.sort(function (a, b) {
274+
return b.rssi - a.rssi;
275+
});
276+
var ops = '<option>Select a Network...</option>';
277+
var seen = {};
278+
for (var i = 0; i < list.length; ++i) {
279+
var ssid = list[i].ssid;
280+
if (!seen[ssid]) {
281+
seen[ssid] = 1;
282+
ops += '<option data-auth=' + list[i].auth + '>' +
283+
ssid.replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;") + '</option>';
284+
}
285+
}
286+
ap.innerHTML = ops;
287+
ab.disabled = false;
288+
togAp(null, true);
289+
ab.onclick = togAp;
290+
} else {
291+
ab.innerText = 'No networks found (' + s + ')';
292+
ra = to(refrAp, 5);
293+
}
293294
}
294295
function togAp(ev, force) {
295296
if (!force || ap.style.display == 'block') {
296-
hide('#dropdown');
297-
show('#ssid');
298-
ab.innerText = 'Scan for Networks';
299-
ab.onclick = refrAp;
297+
hide('#dropdown');
298+
show('#ssid');
299+
ab.innerText = 'Scan for Networks';
300+
ab.onclick = refrAp;
300301
} else {
301-
show('#dropdown');
302-
hide('#ssid');
303-
ab.innerText = 'Manual Entry';
302+
show('#dropdown');
303+
hide('#ssid');
304+
ab.innerText = 'Manual Entry';
304305
}
306+
let pw = $('#wifi_password');
307+
pw.placeholder = "Password";
308+
pw.disabled = false;
309+
pw.required = true;
305310
}
306311
function refrAp() {
307312
ab.innerText = 'Searching for networks...';
@@ -315,13 +320,24 @@ <h4 id='st'>Updating Status...</h4>
315320
ab.innerText = 'Scan for Networks';
316321
ab.onclick = refrAp;
317322
$('#aplist').onchange = function () {
318-
$('#ssid').value = $('#aplist').value;
323+
$('#ssid').value = $('#aplist').value;
324+
let pw = $('#wifi_password');
325+
if ($('#aplist').selectedOptions[0].dataset.auth > 0) {
326+
pw.placeholder = "Password";
327+
pw.disabled = false;
328+
pw.required = true;
329+
} else {
330+
pw.placeholder = "Open -- no password";
331+
pw.disabled = true;
332+
pw.required = false;
333+
}
319334
};
320335
$('#bk2').onclick = function () {
321336
cur('#f1')
322337
}
323338
rs = to(refr, 0.5);
324339
if( trying ) cur("#f3");
340+
refrAp();
325341
}
326342
</script>
327343
</body>

0 commit comments

Comments
 (0)