diff --git a/app/modules/enduser_setup.c b/app/modules/enduser_setup.c index 39a9975b..261d4183 100644 --- a/app/modules/enduser_setup.c +++ b/app/modules/enduser_setup.c @@ -31,6 +31,7 @@ * @author Robert Foss * * Additions & fixes: Johny Mattsson + * Jason Follas */ #include "module.h" @@ -50,6 +51,8 @@ #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #define LITLEN(strliteral) (sizeof (strliteral) -1) +#define STRINGIFY(x) #x +#define NUMLEN(x) (sizeof(STRINGIFY(x)) - 1) #define ENDUSER_SETUP_ERR_FATAL (1 << 0) #define ENDUSER_SETUP_ERR_NONFATAL (1 << 1) @@ -59,7 +62,7 @@ #define ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND 2 #define ENDUSER_SETUP_ERR_UNKOWN_ERROR 3 #define ENDUSER_SETUP_ERR_SOCKET_ALREADY_OPEN 4 - +#define ENDUSER_SETUP_ERR_MAX_NUMBER 5 /** * DNS Response Packet: @@ -78,159 +81,19 @@ static const char dns_body[] = { 0x00, 0x01, 0x00, 0x01, 0xC0, 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x04 }; static const char http_html_filename[] = "enduser_setup.html"; -static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nContent-Type: text/html\r\n"; /* Note single \r\n here! */ -static const char http_header_204[] = "HTTP/1.1 204 No Content\r\n\r\n"; -static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\n\r\n"; -static const char http_header_401[] = "HTTP/1.1 401 Bad request\r\n\r\n"; -static const char http_header_404[] = "HTTP/1.1 404 Not found\r\n\r\n"; -static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\n\r\n"; +static const char http_header_200[] = "HTTP/1.1 200 OK\r\nCache-control:no-cache\r\nConnection:close\r\nContent-Type:text/html\r\n"; /* Note single \r\n here! */ +static const char http_header_204[] = "HTTP/1.1 204 No Content\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_302[] = "HTTP/1.1 302 Moved\r\nLocation: /\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_400[] = "HTTP/1.1 400 Bad request\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_404[] = "HTTP/1.1 404 Not found\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_405[] = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; +static const char http_header_500[] = "HTTP/1.1 500 Internal Error\r\nContent-Length:0\r\nConnection:close\r\n\r\n"; -/* The below is the un-minified version of the http_html_backup[] string. - * Minified using https://kangax.github.io/html-minifier/ - * Note: using method="get" due to iOS not always sending body in same - * packet as the HTTP header, and us thus missing it in that case - */ -#if 0 - - - - - - - WiFi Login - - - -
-
-
- -

WiFi Login

-

Connect gadget to your WiFi network

- - - - -

Status: Updating...

-
-

-
-
- - - -#endif -static const char http_html_backup[] = -"WiFi Login

WiFi Login

Connect gadget to your WiFi network

Status: Updating...

"; +static const char http_header_content_len_fmt[] = "Content-length:%5d\r\n\r\n"; +static const char http_html_gzip_contentencoding[] = "Content-Encoding: gzip\r\n"; +// Externally defined: static const char http_html_backup[] = ... +#include "eus/http_html_backup.def" typedef struct scan_listener { @@ -250,9 +113,15 @@ typedef struct int lua_err_cb_ref; int lua_dbg_cb_ref; scan_listener_t *scan_listeners; + uint8_t softAPchannel; + uint8_t success; + uint8_t callbackDone; + uint8_t lastStationStatus; + uint8_t connecting; } enduser_setup_state_t; static enduser_setup_state_t *state; + static bool manual = false; static task_handle_t do_station_cfg_handle; @@ -340,7 +209,7 @@ static void enduser_setup_check_station_start(void) ENDUSER_SETUP_DEBUG("enduser_setup_check_station_start"); os_timer_setfn(&(state->check_station_timer), enduser_setup_check_station, NULL); - os_timer_arm(&(state->check_station_timer), 1*1000, TRUE); + os_timer_arm(&(state->check_station_timer), 3*1000, TRUE); } @@ -377,13 +246,60 @@ static void enduser_setup_check_station(void *p) has_ip |= ((char *) &ip)[i]; } + uint8_t currChan = wifi_get_channel(); + if (has_ip == 0) { - return; + // No IP Address yet, so check the reported status + uint8_t curr_status = wifi_station_get_connect_status(); + char buf[20]; + c_sprintf(buf, "status=%d,chan=%d", curr_status, currChan); + ENDUSER_SETUP_DEBUG(buf); + + if (curr_status == 2 || curr_status == 3 || curr_status == 4) + { + state->connecting = 0; + + // If the status is an error status and the channel changed, then cache the + // status to state since the Station won't be able to report the same status + // after switching the channel back to the SoftAP's + if (currChan != state->softAPchannel) { + state->lastStationStatus = curr_status; + + ENDUSER_SETUP_DEBUG("Turning off Station due to different channel than AP"); + + wifi_station_disconnect(); + wifi_set_opmode(SOFTAP_MODE); + enduser_setup_ap_start(); + } + } + return; } + state->success = 1; + state->lastStationStatus = 5; // We have an IP Address, so the status is 5 (as of SDK 1.5.1) + state->connecting = 0; + +#if ENDUSER_SETUP_DEBUG_ENABLE + char debuginfo[100]; + c_sprintf(debuginfo, "AP_CHAN: %d, STA_CHAN: %d", state->softAPchannel, currChan); + ENDUSER_SETUP_DEBUG(debuginfo); +#endif + + if (currChan == state->softAPchannel) + { + enduser_setup_connected_callback(); + state->callbackDone = 1; + } + else + { + ENDUSER_SETUP_DEBUG("Turning off Station due to different channel than AP"); + wifi_station_disconnect(); + wifi_set_opmode(SOFTAP_MODE); + enduser_setup_ap_start(); + } + enduser_setup_check_station_stop(); - enduser_setup_connected_callback(); /* Trigger shutdown, but allow time for HTTP client to fetch last status. */ if (!manual) @@ -411,6 +327,8 @@ static void enduser_setup_check_station(void *p) /* Callback on timeout to hard-close a connection */ static err_t force_abort (void *arg, struct tcp_pcb *pcb) { + ENDUSER_SETUP_DEBUG("force_abort"); + (void)arg; tcp_poll (pcb, 0, 0); tcp_abort (pcb); @@ -420,6 +338,8 @@ static err_t force_abort (void *arg, struct tcp_pcb *pcb) /* Callback to detect a remote-close of a connection */ static err_t handle_remote_close (void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { + ENDUSER_SETUP_DEBUG("handle_remote_close"); + (void)arg; (void)err; if (p) /* server sent us data, just ACK and move on */ { @@ -438,6 +358,8 @@ static err_t handle_remote_close (void *arg, struct tcp_pcb *pcb, struct pbuf *p /* Set up a deferred close of a connection, as discussed above. */ static inline void deferred_close (struct tcp_pcb *pcb) { + ENDUSER_SETUP_DEBUG("deferred_close"); + tcp_poll (pcb, force_abort, 15); /* ~3sec from now */ tcp_recv (pcb, handle_remote_close); tcp_sent (pcb, 0); @@ -446,6 +368,8 @@ static inline void deferred_close (struct tcp_pcb *pcb) /* Convenience function to queue up a close-after-send. */ static err_t close_once_sent (void *arg, struct tcp_pcb *pcb, u16_t len) { + ENDUSER_SETUP_DEBUG("close_once_sent"); + (void)arg; (void)len; deferred_close (pcb); return ERR_OK; @@ -473,7 +397,6 @@ static int enduser_setup_srch_str(const char *str, const char *srch_str) } } - /** * Load HTTP Payload * @@ -495,14 +418,26 @@ static int enduser_setup_http_load_payload(void) err2 = vfs_lseek(f, 0, VFS_SEEK_SET); } - const char cl_hdr[] = "Content-length:%5d\r\n\r\n"; - const size_t cl_len = LITLEN(cl_hdr) + 3; /* room to expand %4d */ + char cl_hdr[30]; + size_t ce_len = 0; + + c_sprintf(cl_hdr, http_header_content_len_fmt, file_len); + size_t cl_len = c_strlen(cl_hdr); if (!f || err != VFS_RES_OK || err2 != VFS_RES_OK) { ENDUSER_SETUP_DEBUG("enduser_setup_http_load_payload unable to load file enduser_setup.html, loading backup HTML."); - int payload_len = LITLEN(http_header_200) + cl_len + LITLEN(http_html_backup); + c_sprintf(cl_hdr, http_header_content_len_fmt, sizeof(http_html_backup)); + cl_len = c_strlen(cl_hdr); + + if (http_html_backup[0] == 0x1f && http_html_backup[1] == 0x8b) + { + ce_len = c_strlen(http_html_gzip_contentencoding); + ENDUSER_SETUP_DEBUG("Content is gzipped"); + } + + int payload_len = LITLEN(http_header_200) + cl_len + ce_len + LITLEN(http_html_backup); state->http_payload_len = payload_len; state->http_payload_data = (char *) c_malloc(payload_len); if (state->http_payload_data == NULL) @@ -513,13 +448,25 @@ static int enduser_setup_http_load_payload(void) int offset = 0; c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200)); offset += LITLEN(http_header_200); - offset += c_sprintf(state->http_payload_data + offset, cl_hdr, LITLEN(http_html_backup)); - c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), LITLEN(http_html_backup)); - - return 1; + if (ce_len > 0) + offset += c_sprintf(state->http_payload_data + offset, http_html_gzip_contentencoding, ce_len); + c_memcpy(&(state->http_payload_data[offset]), &(cl_hdr), cl_len); + offset += cl_len; + c_memcpy(&(state->http_payload_data[offset]), &(http_html_backup), sizeof(http_html_backup)); + + return 1; } - int payload_len = LITLEN(http_header_200) + cl_len + file_len; + char magic[2]; + vfs_read(f, magic, 2); + + if (magic[0] == 0x1f && magic[1] == 0x8b) + { + ce_len = c_strlen(http_html_gzip_contentencoding); + ENDUSER_SETUP_DEBUG("Content is gzipped"); + } + + int payload_len = LITLEN(http_header_200) + cl_len + ce_len + file_len; state->http_payload_len = payload_len; state->http_payload_data = (char *) c_malloc(payload_len); if (state->http_payload_data == NULL) @@ -527,13 +474,19 @@ static int enduser_setup_http_load_payload(void) return 2; } + vfs_lseek(f, 0, VFS_SEEK_SET); + int offset = 0; + c_memcpy(&(state->http_payload_data[offset]), &(http_header_200), LITLEN(http_header_200)); offset += LITLEN(http_header_200); - offset += c_sprintf(state->http_payload_data + offset, cl_hdr, file_len); + if (ce_len > 0) + offset += c_sprintf(state->http_payload_data + offset, http_html_gzip_contentencoding, ce_len); + c_memcpy(&(state->http_payload_data[offset]), &(cl_hdr), cl_len); + offset += cl_len; vfs_read(f, &(state->http_payload_data[offset]), file_len); - vfs_close(f); - + vfs_close(f); + return 0; } @@ -605,6 +558,9 @@ static int enduser_setup_http_urldecode(char *dst, const char *src, int src_len, */ static void do_station_cfg (task_param_t param, uint8_t prio) { + ENDUSER_SETUP_DEBUG("do_station_cfg"); + + state->connecting = 1; struct station_config *cnf = (struct station_config *)param; (void)prio; @@ -635,6 +591,9 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data { ENDUSER_SETUP_DEBUG("enduser_setup_http_handle_credentials"); + state->success = 0; + state->lastStationStatus = 0; + char *name_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_ssid=")); char *pwd_str = (char *) ((uint32_t)strstr(&(data[6]), "wifi_password=")); if (name_str == NULL || pwd_str == NULL) @@ -663,7 +622,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data int err; err = enduser_setup_http_urldecode(cnf->ssid, name_str_start, name_str_len, sizeof(cnf->ssid)); err |= enduser_setup_http_urldecode(cnf->password, pwd_str_start, pwd_str_len, sizeof(cnf->password)); - if (err != 0) + if (err != 0 || c_strlen(cnf->ssid) == 0) { ENDUSER_SETUP_DEBUG("Unable to decode HTTP parameter to valid password or SSID"); return 1; @@ -694,7 +653,7 @@ static int enduser_setup_http_handle_credentials(char *data, unsigned short data static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const char *header, uint32_t header_len) { ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_header"); - + err_t err = tcp_write (http_client, header, header_len, TCP_WRITE_FLAG_COPY); if (err != ERR_OK) { @@ -708,8 +667,9 @@ static int enduser_setup_http_serve_header(struct tcp_pcb *http_client, const ch static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len) { - (void)len; + ENDUSER_SETUP_DEBUG("streamout_sent"); + (void)len; unsigned offs = (unsigned)arg; if (!state || !state->http_payload_data) @@ -754,6 +714,7 @@ static err_t streamout_sent (void *arg, struct tcp_pcb *pcb, u16_t len) static int enduser_setup_http_serve_html(struct tcp_pcb *http_client) { ENDUSER_SETUP_DEBUG("enduser_setup_http_serve_html"); + if (state->http_payload_data == NULL) { enduser_setup_http_load_payload(); @@ -774,7 +735,7 @@ static int enduser_setup_http_serve_html(struct tcp_pcb *http_client) } -static void serve_status(struct tcp_pcb *conn) +static void enduser_setup_serve_status(struct tcp_pcb *conn) { ENDUSER_SETUP_DEBUG("enduser_setup_serve_status"); @@ -782,11 +743,12 @@ static void serve_status(struct tcp_pcb *conn) "HTTP/1.1 200 OK\r\n" "Cache-control:no-cache\r\n" "Connection:close\r\n" + "Access-Control-Allow-Origin: *\r\n" "Content-type:text/plain\r\n" "Content-length: %d\r\n" "\r\n" - "%s%s"; - const char *state[] = + "%s"; + const char *states[] = { "Idle.", "Connecting to \"%s\".", @@ -796,8 +758,8 @@ static void serve_status(struct tcp_pcb *conn) "Connected to \"%s\" (%s)." }; - const size_t num_states = sizeof(state)/sizeof(state[0]); - uint8_t curr_state = wifi_station_get_connect_status (); + const size_t num_states = sizeof(states)/sizeof(states[0]); + uint8_t curr_state = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status (); if (curr_state < num_states) { switch (curr_state) @@ -807,22 +769,21 @@ static void serve_status(struct tcp_pcb *conn) case STATION_NO_AP_FOUND: case STATION_GOT_IP: { - const char *s = state[curr_state]; + const char *s = states[curr_state]; struct station_config config; wifi_station_get_config(&config); config.ssid[31] = '\0'; - struct ip_info ip_info; + struct ip_info ip_info; - wifi_get_ip_info(STATION_IF , &ip_info); - - char ip_addr[16]; - ip_addr[0] = '\0'; - if (curr_state == STATION_GOT_IP) - { - c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr)); - } + wifi_get_ip_info(STATION_IF , &ip_info); + char ip_addr[16]; + ip_addr[0] = '\0'; + if (curr_state == STATION_GOT_IP) + { + c_sprintf (ip_addr, "%d.%d.%d.%d", IP2STR(&ip_info.ip.addr)); + } int state_len = c_strlen(s); int ip_len = c_strlen(ip_addr); @@ -844,7 +805,7 @@ static void serve_status(struct tcp_pcb *conn) /* Handle non-formatted strings */ default: { - const char *s = state[curr_state]; + const char *s = states[curr_state]; int status_len = c_strlen(s); int buf_len = sizeof(fmt) + status_len + 10; //10 = (9+1), 1 byte is '\0' and 9 are reserved for length field char buf[buf_len]; @@ -862,12 +823,76 @@ static void serve_status(struct tcp_pcb *conn) } } +static void enduser_setup_serve_status_as_json (struct tcp_pcb *http_client) +{ + ENDUSER_SETUP_DEBUG("enduser_setup_serve_status_as_json"); + + // If the station is currently shut down because of wi-fi channel issue, use the cached status + uint8_t curr_status = state->lastStationStatus > 0 ? state->lastStationStatus : wifi_station_get_connect_status (); + + char json_payload[64]; + c_sprintf(json_payload, "{\"deviceid\":\"%06X\", \"status\":%d}", system_get_chip_id(), curr_status); + + const char fmt[] = + "HTTP/1.1 200 OK\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Content-Type: application/json\r\n" + "Content-Length: %d\r\n" + "\r\n" + "%s"; + + int len = c_strlen(json_payload); + char buf[c_strlen(fmt) + NUMLEN(len) + len - 4]; + len = c_sprintf (buf, fmt, len, json_payload); + enduser_setup_http_serve_header (http_client, buf, len); +} + + +static void enduser_setup_handle_OPTIONS (struct tcp_pcb *http_client, char *data, unsigned short data_len) +{ + ENDUSER_SETUP_DEBUG("enduser_setup_handle_OPTIONS"); + + const char json[] = + "HTTP/1.1 200 OK\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: application/json\r\n" + "Content-Length: 0\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Access-Control-Allow-Methods: GET\r\n" + "Access-Control-Allow-Age: 300\r\n" + "\r\n"; + + const char others[] = + "HTTP/1.1 200 OK\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Length: 0\r\n" + "\r\n"; + + int type = 0; + + if (c_strncmp(data, "GET ", 4) == 0) + { + if (c_strncmp(data + 4, "/aplist", 7) == 0 || c_strncmp(data + 4, "/setwifi?", 9) == 0 || c_strncmp(data + 4, "/status.json", 12) == 0) + { + enduser_setup_http_serve_header (http_client, json, c_strlen(json)); + return; + } + } + enduser_setup_http_serve_header (http_client, others, c_strlen(others)); + return; +} /* --- WiFi AP scanning support -------------------------------------------- */ static void free_scan_listeners (void) { + ENDUSER_SETUP_DEBUG("free_scan_listeners"); + if (!state || !state->scan_listeners) { return; @@ -886,6 +911,8 @@ static void free_scan_listeners (void) static void remove_scan_listener (scan_listener_t *l) { + ENDUSER_SETUP_DEBUG("remove_scan_listener"); + if (state) { scan_listener_t **sl = &state->scan_listeners; @@ -921,6 +948,8 @@ static char *escape_ssid (char *dst, const char *src) static void notify_scan_listeners (const char *payload, size_t sz) { + ENDUSER_SETUP_DEBUG("notify_scan_listeners"); + if (!state) { return; @@ -944,6 +973,8 @@ static void notify_scan_listeners (const char *payload, size_t sz) static void on_scan_done (void *arg, STATUS status) { + ENDUSER_SETUP_DEBUG("on_scan_done"); + if (!state || !state->scan_listeners) { return; @@ -961,13 +992,14 @@ static void on_scan_done (void *arg, STATUS status) "HTTP/1.1 200 OK\r\n" "Connection:close\r\n" "Cache-control:no-cache\r\n" + "Access-Control-Allow-Origin: *\r\n" "Content-type:application/json\r\n" "Content-length:%4d\r\n" "\r\n"; const size_t hdr_sz = sizeof (header_fmt) +1 -1; /* +expand %4d, -\0 */ /* To be able to safely escape a pathological SSID, we need 2*32 bytes */ - const size_t max_entry_sz = sizeof("{\"ssid\":\"\",\"rssi\":},") + 2*32 + 6; + const size_t max_entry_sz = 27 + 2*32 + 6; // {"ssid":"","rssi":,"chan":} const size_t alloc_sz = hdr_sz + num_nets * max_entry_sz + 3; char *http = os_zalloc (alloc_sz); if (!http) @@ -996,6 +1028,12 @@ static void on_scan_done (void *arg, STATUS status) p += sizeof (entry_mid) -1; p += c_sprintf (p, "%d", wn->rssi); + + const char entry_chan[] = ",\"chan\":"; + strcpy (p, entry_chan); + p += sizeof (entry_chan) -1; + + p += c_sprintf (p, "%d", wn->channel); *p++ = '}'; } @@ -1006,6 +1044,8 @@ static void on_scan_done (void *arg, STATUS status) http[hdr_sz] = '['; /* Rewrite the \0 with the correct start of body */ notify_scan_listeners (http, hdr_sz + body_sz); + ENDUSER_SETUP_DEBUG(http + hdr_sz); + c_free (http); return; } @@ -1048,6 +1088,9 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s pbuf_free (p); err_t ret = ERR_OK; + + ENDUSER_SETUP_DEBUG(data); + if (c_strncmp(data, "GET ", 4) == 0) { if (c_strncmp(data + 4, "/ ", 2) == 0) @@ -1061,38 +1104,52 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s goto free_out; /* streaming now in progress */ } } - else if (c_strncmp(data + 4, "/aplist ", 8) == 0) + else if (c_strncmp(data + 4, "/aplist", 7) == 0) { - scan_listener_t *l = os_malloc (sizeof (scan_listener_t)); - if (!l) + // Don't do an AP Scan while station is trying to connect to Wi-Fi + if (state->connecting == 0) { - ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); - } - - bool already = (state->scan_listeners != NULL); - - tcp_arg (http_client, l); - /* TODO: check if also need a tcp_err() cb, or if recv() is enough */ - l->conn = http_client; - l->next = state->scan_listeners; - state->scan_listeners = l; - - if (!already) - { - if (!wifi_station_scan(NULL, on_scan_done)) + scan_listener_t *l = os_malloc (sizeof (scan_listener_t)); + if (!l) { - enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500)); - deferred_close (l->conn); - l->conn = 0; - free_scan_listeners(); + ENDUSER_SETUP_ERROR("out of memory", ENDUSER_SETUP_ERR_OUT_OF_MEMORY, ENDUSER_SETUP_ERR_NONFATAL); } + + bool already = (state->scan_listeners != NULL); + + tcp_arg (http_client, l); + /* TODO: check if also need a tcp_err() cb, or if recv() is enough */ + l->conn = http_client; + l->next = state->scan_listeners; + state->scan_listeners = l; + + if (!already) + { + if (!wifi_station_scan(NULL, on_scan_done)) + { + enduser_setup_http_serve_header(http_client, http_header_500, LITLEN(http_header_500)); + deferred_close (l->conn); + l->conn = 0; + free_scan_listeners(); + } + } + goto free_out; /* request queued */ + } + else + { + // Return No Content status to the caller + enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204)); } - goto free_out; /* request queued */ } - else if (c_strncmp(data + 4, "/status ", 8) == 0) + else if (c_strncmp(data + 4, "/status.json", 12) == 0) { - serve_status(http_client); + enduser_setup_serve_status_as_json(http_client); + } + else if (c_strncmp(data + 4, "/status", 7) == 0) + { + enduser_setup_serve_status(http_client); } + else if (c_strncmp(data + 4, "/update?", 8) == 0) { switch (enduser_setup_http_handle_credentials(data, data_len)) @@ -1101,14 +1158,29 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s enduser_setup_http_serve_header(http_client, http_header_302, LITLEN(http_header_302)); break; case 1: - enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401)); + enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400)); break; default: ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); break; } } - else if (c_strncmp(data + 4, "/generate_204 ", 14) == 0) + else if (c_strncmp(data + 4, "/setwifi?", 9) == 0) + { + switch (enduser_setup_http_handle_credentials(data, data_len)) + { + case 0: + enduser_setup_serve_status_as_json(http_client); + break; + case 1: + enduser_setup_http_serve_header(http_client, http_header_400, LITLEN(http_header_400)); + break; + default: + ENDUSER_SETUP_ERROR("http_recvcb failed. Failed to handle wifi credentials.", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + break; + } + } + else if (c_strncmp(data + 4, "/generate_204", 13) == 0) { /* Convince Android devices that they have internet access to avoid pesky dialogues. */ enduser_setup_http_serve_header(http_client, http_header_204, LITLEN(http_header_204)); @@ -1116,13 +1188,16 @@ static err_t enduser_setup_http_recvcb(void *arg, struct tcp_pcb *http_client, s else { ENDUSER_SETUP_DEBUG("serving 404"); - ENDUSER_SETUP_DEBUG(data + 4); enduser_setup_http_serve_header(http_client, http_header_404, LITLEN(http_header_404)); } } - else /* not GET */ + else if (c_strncmp(data, "OPTIONS ", 8) == 0) { - enduser_setup_http_serve_header(http_client, http_header_401, LITLEN(http_header_401)); + enduser_setup_handle_OPTIONS(http_client, data, data_len); + } + else /* not GET or OPTIONS */ + { + enduser_setup_http_serve_header(http_client, http_header_405, LITLEN(http_header_405)); } deferred_close (http_client); @@ -1135,6 +1210,7 @@ free_out: static err_t enduser_setup_http_connectcb(void *arg, struct tcp_pcb *pcb, err_t err) { ENDUSER_SETUP_DEBUG("enduser_setup_http_connectcb"); + if (!state) { ENDUSER_SETUP_DEBUG("connect callback but no state?!"); @@ -1208,7 +1284,7 @@ static void enduser_setup_http_stop(void) static void enduser_setup_ap_stop(void) { - ENDUSER_SETUP_DEBUG("enduser_setup_station_stop"); + ENDUSER_SETUP_DEBUG("enduser_setup_ap_stop"); wifi_set_opmode(~SOFTAP_MODE & wifi_get_opmode()); } @@ -1234,15 +1310,47 @@ static void enduser_setup_ap_start(void) cnf.ssid[ssid_name_len] = '_'; c_sprintf(cnf.ssid + ssid_name_len + 1, "%02X%02X%02X", mac[3], mac[4], mac[5]); cnf.ssid_len = ssid_name_len + 7; - cnf.channel = 1; + cnf.channel = state == NULL? 1 : state->softAPchannel; cnf.authmode = AUTH_OPEN; cnf.ssid_hidden = 0; cnf.max_connection = 5; cnf.beacon_interval = 100; wifi_set_opmode(STATIONAP_MODE); wifi_softap_set_config(&cnf); + +#if ENDUSER_SETUP_DEBUG_ENABLE + char debuginfo[100]; + c_sprintf(debuginfo, "SSID: %s, CHAN: %d", cnf.ssid, cnf.channel); + ENDUSER_SETUP_DEBUG(debuginfo); +#endif } +static void on_initial_scan_done (void *arg, STATUS status) +{ + ENDUSER_SETUP_DEBUG("on_initial_scan_done"); + + if (!state) + { + return; + } + + int8_t rssi = -100; + + if (status == OK) + { + for (struct bss_info *wn = arg; wn; wn = wn->next.stqe_next) + { + if (wn->rssi > rssi) + { + state->softAPchannel = wn->channel; + rssi = wn->rssi; + } + } + } + + enduser_setup_ap_start(); + enduser_setup_check_station_start(); +} static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned short recv_len) { @@ -1255,6 +1363,31 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned uint32_t dns_reply_static_len = (uint32_t) sizeof(dns_header) + (uint32_t) sizeof(dns_body) + 2 + 4; // dns_id=2bytes, ip=4bytes uint32_t dns_reply_len = dns_reply_static_len + qname_len; +#if ENDUSER_SETUP_DEBUG_ENABLE + char *qname = c_malloc(qname_len + 12); + if (qname != NULL) + { + c_sprintf(qname, "DNS QUERY = %s", &(recv_data[12])); + + uint32_t p; + int i, j; + + for(i=12;i<(int)strlen(qname);i++) + { + p=qname[i]; + for(j=0;j<(int)p;j++) + { + qname[i]=qname[i+1]; + i=i+1; + } + qname[i]='.'; + } + qname[i-1]='\0'; + ENDUSER_SETUP_DEBUG(qname); + c_free(qname); + } +#endif + uint8_t if_mode = wifi_get_opmode(); if ((if_mode & SOFTAP_MODE) == 0) { @@ -1305,6 +1438,14 @@ static void enduser_setup_dns_recv_callback(void *arg, char *recv_data, unsigned { ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Can't execute transmission.", ENDUSER_SETUP_ERR_CONNECTION_NOT_FOUND, ENDUSER_SETUP_ERR_FATAL); } + else if (err == ESPCONN_MAXNUM) + { + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Buffer full. Discarding...", ENDUSER_SETUP_ERR_MAX_NUMBER, ENDUSER_SETUP_ERR_NONFATAL); + } + else if (err == ESPCONN_IF) + { + ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. Send UDP data failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_NONFATAL); + } else if (err != 0) { ENDUSER_SETUP_ERROR_VOID("dns_recv_callback failed. espconn_send failed", ENDUSER_SETUP_ERR_UNKOWN_ERROR, ENDUSER_SETUP_ERR_FATAL); @@ -1408,6 +1549,7 @@ static void enduser_setup_dns_stop(void) static int enduser_setup_init(lua_State *L) { + // Note: Normal to not see this debug message on first invocation because debug callback is set below ENDUSER_SETUP_DEBUG("enduser_setup_init"); if (state != NULL) @@ -1452,12 +1594,19 @@ static int enduser_setup_init(lua_State *L) { lua_pushvalue (L, 3); state->lua_dbg_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); + ENDUSER_SETUP_DEBUG("enduser_setup_init: Debug callback has been defined"); } else { state->lua_dbg_cb_ref = LUA_NOREF; } + state->softAPchannel = 1; + state->success = 0; + state->callbackDone = 0; + state->lastStationStatus = 0; + state->connecting = 0; + return 0; } @@ -1475,6 +1624,7 @@ static int enduser_setup_manual(lua_State *L) static int enduser_setup_start(lua_State *L) { + // Note: The debug callback is set in enduser_setup_init. It's normal to not see this debug message on first invocation. ENDUSER_SETUP_DEBUG("enduser_setup_start"); if (!do_station_cfg_handle) @@ -1487,10 +1637,14 @@ static int enduser_setup_start(lua_State *L) goto failed; } - enduser_setup_check_station_start(); if (!manual) { - enduser_setup_ap_start(); + ENDUSER_SETUP_DEBUG("Performing AP Scan to identify likely AP's channel"); + wifi_station_scan(NULL, on_initial_scan_done); + } + else + { + enduser_setup_check_station_start(); } if(enduser_setup_dns_start()) @@ -1529,6 +1683,12 @@ static int enduser_setup_stop(lua_State* L) { enduser_setup_ap_stop(); } + if (state->success && !state->callbackDone) + { + wifi_set_opmode(STATION_MODE | wifi_get_opmode()); + wifi_station_connect(); + enduser_setup_connected_callback(); + } enduser_setup_dns_stop(); enduser_setup_http_stop(); enduser_setup_free(); diff --git a/app/modules/eus/enduser_setup.html b/app/modules/eus/enduser_setup.html new file mode 100644 index 00000000..b1e0de09 --- /dev/null +++ b/app/modules/eus/enduser_setup.html @@ -0,0 +1,315 @@ + + + + + + WiFi Login + + + + +
+
+
+
+

Connect device to your Wi-Fi

+ + + + + +
+
+

Success!

+
+

Your device has successfully connected to the Wi-Fi network.

+
+
+
+

Trying...

+ +
+
+

Updating Status...

+
+ + + + \ No newline at end of file diff --git a/app/modules/eus/enduser_setup.html.gz b/app/modules/eus/enduser_setup.html.gz new file mode 100644 index 00000000..3abbaa13 Binary files /dev/null and b/app/modules/eus/enduser_setup.html.gz differ diff --git a/app/modules/eus/http_html_backup.def b/app/modules/eus/http_html_backup.def new file mode 100644 index 00000000..58ddccad --- /dev/null +++ b/app/modules/eus/http_html_backup.def @@ -0,0 +1,203 @@ +static const char http_html_backup[] = { + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x95, 0x59, + 0xfd, 0x72, 0xdb, 0x36, 0x12, 0xff, 0xbb, 0x37, 0x73, 0xef, 0xb0, 0x1e, + 0xf7, 0x42, 0xb9, 0xa1, 0x64, 0x7d, 0xd8, 0x69, 0xc6, 0x12, 0xd5, 0xc9, + 0xe5, 0x92, 0x26, 0x37, 0x49, 0xda, 0xa9, 0xdd, 0xe9, 0x75, 0x32, 0x99, + 0x0e, 0x44, 0x82, 0x26, 0x62, 0x0a, 0xe0, 0x01, 0xa0, 0x65, 0x5d, 0xea, + 0x77, 0x3f, 0x2c, 0x01, 0x5a, 0x86, 0x40, 0x87, 0xb5, 0x5a, 0x57, 0x04, + 0xb0, 0xdf, 0xfb, 0xc3, 0xee, 0x52, 0x5d, 0x1c, 0xfc, 0xeb, 0xa7, 0x97, + 0x17, 0xbf, 0xff, 0xfc, 0x0a, 0x0a, 0xbd, 0x2e, 0x97, 0x7f, 0xff, 0xdb, + 0xc2, 0x7d, 0xe3, 0x13, 0x25, 0x99, 0x79, 0xfa, 0x66, 0xb1, 0xa6, 0x9a, + 0x00, 0x27, 0x6b, 0x9a, 0x44, 0xd7, 0x8c, 0x6e, 0x2a, 0x21, 0x75, 0x04, + 0xa9, 0xe0, 0x9a, 0x72, 0x9d, 0x44, 0x1b, 0x96, 0xe9, 0x22, 0xc9, 0xe8, + 0x35, 0x4b, 0xe9, 0xb0, 0x59, 0xc4, 0xc0, 0x38, 0xd3, 0x8c, 0x94, 0x43, + 0x95, 0x92, 0x92, 0x26, 0x93, 0xd1, 0x38, 0x6a, 0x04, 0x69, 0xa6, 0x4b, + 0xba, 0xfc, 0x8d, 0xbd, 0x66, 0xf0, 0x4e, 0x5c, 0x32, 0xbe, 0x38, 0xb6, + 0x3b, 0x78, 0xa6, 0xf4, 0xb6, 0xa4, 0xa0, 0xb7, 0x15, 0x4d, 0x34, 0xbd, + 0xd1, 0xc7, 0xa9, 0x52, 0xb8, 0xff, 0xcd, 0x77, 0xf0, 0x05, 0xbf, 0xbe, + 0x59, 0x13, 0x69, 0x38, 0xce, 0x60, 0x3c, 0x6f, 0x96, 0x15, 0xc9, 0x32, + 0xc6, 0x2f, 0xdb, 0xf5, 0x2d, 0xfe, 0x07, 0xff, 0xd0, 0xfc, 0x18, 0x1f, + 0x56, 0x22, 0xdb, 0x3a, 0xd6, 0x82, 0xb2, 0xcb, 0x42, 0x9f, 0xc1, 0x64, + 0x3c, 0xfe, 0x87, 0xe5, 0xce, 0x8d, 0xf1, 0xc3, 0x9c, 0xac, 0x59, 0xb9, + 0x3d, 0x03, 0x45, 0xb8, 0x1a, 0x2a, 0x2a, 0x59, 0x6e, 0x0f, 0x51, 0xfd, + 0x90, 0x94, 0xec, 0xd2, 0x68, 0x4b, 0x29, 0xd7, 0x54, 0xda, 0xfd, 0x15, + 0x49, 0xaf, 0x2e, 0xa5, 0xa8, 0x79, 0x76, 0x06, 0x87, 0x27, 0x27, 0x27, + 0xd9, 0xc9, 0x89, 0xaf, 0xfb, 0xd0, 0xc5, 0xc4, 0xa9, 0xad, 0x84, 0x62, + 0x9a, 0x09, 0x23, 0x85, 0xac, 0x94, 0x28, 0x6b, 0x4d, 0x9d, 0x7c, 0x51, + 0xdd, 0xb9, 0x21, 0xad, 0x65, 0x6e, 0xb5, 0x12, 0x5a, 0x8b, 0xf5, 0xdd, + 0xb2, 0xa4, 0xf9, 0xee, 0xac, 0x89, 0xec, 0x19, 0xcc, 0xa6, 0xe3, 0xea, + 0x66, 0xee, 0xb9, 0x75, 0xf2, 0x1c, 0xb7, 0xbc, 0x20, 0x91, 0x5a, 0x0b, + 0xdf, 0x36, 0xc6, 0xab, 0x5a, 0xdb, 0xc0, 0xd4, 0x46, 0x09, 0x6f, 0x1e, + 0x15, 0x2d, 0x69, 0xda, 0x9a, 0x3b, 0xdc, 0xd0, 0xd5, 0x15, 0x33, 0x9e, + 0x57, 0x15, 0x25, 0x92, 0xf0, 0x94, 0x9e, 0x01, 0x17, 0x9c, 0xb6, 0xa6, + 0xc9, 0x8c, 0xca, 0xa1, 0x24, 0x19, 0xab, 0x55, 0x10, 0xf5, 0x9c, 0xd1, + 0x32, 0x53, 0xd4, 0x89, 0x72, 0xc4, 0xf7, 0xdc, 0xba, 0x19, 0xaa, 0x82, + 0x64, 0x62, 0x63, 0xb6, 0xcc, 0x3f, 0x93, 0xd3, 0xea, 0x06, 0x26, 0xe6, + 0x4f, 0x5e, 0xae, 0xc8, 0x60, 0x1c, 0x83, 0xfd, 0x77, 0x74, 0x72, 0x74, + 0x8f, 0x9e, 0xfd, 0xaf, 0x49, 0xaf, 0xd3, 0x6b, 0xb6, 0xf6, 0xf2, 0x8e, + 0x71, 0x80, 0x19, 0x7a, 0x1e, 0x26, 0x27, 0xcf, 0x73, 0x17, 0x0f, 0xc6, + 0x87, 0x36, 0x4c, 0x18, 0xb9, 0x20, 0x4c, 0x43, 0x63, 0x44, 0x18, 0xa6, + 0x7d, 0x2f, 0xd0, 0x52, 0x93, 0x3f, 0x96, 0xc1, 0x61, 0x9a, 0xa6, 0xf7, + 0x25, 0x0c, 0xdb, 0x84, 0x4d, 0x9c, 0x68, 0x97, 0x26, 0x07, 0xb5, 0x5e, + 0x67, 0x52, 0x51, 0x0a, 0x69, 0xec, 0x9d, 0x4e, 0xa7, 0x3b, 0x5c, 0x1a, + 0xe6, 0x67, 0x46, 0xe3, 0x5a, 0x70, 0xa1, 0x2a, 0x92, 0x52, 0xdf, 0x6f, + 0x0c, 0x9e, 0x6f, 0xb3, 0x9f, 0xc5, 0x6e, 0x09, 0x7e, 0x84, 0x86, 0x4e, + 0xaf, 0x36, 0x59, 0x36, 0x04, 0x92, 0x72, 0xdd, 0xa7, 0xc4, 0xa1, 0xc6, + 0x29, 0xb1, 0xfc, 0x2e, 0xce, 0x41, 0xc2, 0x03, 0xb0, 0xcc, 0xaa, 0xd6, + 0xdf, 0x5a, 0x2a, 0x64, 0xac, 0x04, 0xdb, 0x5d, 0xab, 0x8c, 0xa9, 0xaa, + 0x24, 0x5b, 0x13, 0x9d, 0x52, 0xa4, 0x57, 0x41, 0x1c, 0x3a, 0x6f, 0x67, + 0x46, 0x53, 0x21, 0x89, 0xbd, 0x5c, 0x0e, 0xa3, 0x9e, 0xf9, 0x88, 0x8d, + 0xd3, 0x4e, 0x68, 0xcc, 0x26, 0xab, 0x93, 0xd3, 0xef, 0xc3, 0x5c, 0x75, + 0x38, 0x7b, 0x96, 0x8b, 0xb4, 0x56, 0xf1, 0xbd, 0x8d, 0x42, 0x5c, 0x53, + 0x09, 0x5f, 0x3a, 0x41, 0x3d, 0x86, 0xa9, 0x51, 0x8a, 0x21, 0x89, 0xed, + 0x1a, 0xdd, 0xde, 0xe9, 0xf3, 0x6a, 0xd4, 0xec, 0x5e, 0xb2, 0x10, 0x1c, + 0xd4, 0xfa, 0xea, 0x83, 0xe2, 0xd9, 0xb3, 0x67, 0x9d, 0x58, 0xb3, 0x30, + 0xf6, 0xe4, 0x9d, 0x78, 0x79, 0x71, 0x30, 0xf5, 0x23, 0x12, 0x30, 0x8d, + 0x6a, 0xcd, 0x4a, 0xa6, 0xb7, 0xad, 0x29, 0xa5, 0x20, 0x26, 0xe4, 0x4d, + 0x3d, 0x72, 0x76, 0x94, 0x94, 0x48, 0xc4, 0xac, 0x2e, 0x5a, 0x3b, 0x6e, + 0x86, 0x2e, 0x64, 0xdf, 0x9f, 0xda, 0x88, 0x79, 0x1e, 0xcc, 0xaa, 0x6e, + 0x58, 0xb7, 0x1e, 0x58, 0x2b, 0x82, 0x02, 0xde, 0x14, 0x83, 0xee, 0x7b, + 0xec, 0xfc, 0x08, 0x8d, 0x76, 0xa9, 0xf1, 0xb6, 0x1e, 0x9f, 0x9c, 0x50, + 0xfe, 0x61, 0x26, 0x45, 0x65, 0xb8, 0x78, 0xdc, 0xac, 0xf2, 0xa9, 0xfb, + 0x9e, 0xd9, 0xef, 0xd5, 0xd5, 0xd4, 0x29, 0x70, 0xa0, 0x75, 0xe8, 0xeb, + 0x96, 0x11, 0xb4, 0x01, 0x49, 0x4b, 0xa2, 0xd9, 0x35, 0x7d, 0xa0, 0x52, + 0xa0, 0xfd, 0x26, 0x0d, 0x1b, 0x57, 0xbe, 0xbd, 0x1a, 0x7f, 0xea, 0x2a, + 0x55, 0x77, 0xe5, 0xf1, 0xf5, 0x93, 0xaa, 0x64, 0xaa, 0xa7, 0x09, 0x85, + 0xda, 0xc3, 0x26, 0xa9, 0x45, 0x15, 0xb4, 0xa2, 0xa0, 0x4d, 0xf5, 0xd4, + 0x49, 0xaf, 0xd1, 0x7a, 0x05, 0x29, 0xb8, 0xae, 0xa1, 0x1f, 0x52, 0x8a, + 0xcd, 0x1e, 0xb2, 0x9f, 0x3f, 0x7f, 0x3e, 0xff, 0xaa, 0x5f, 0xae, 0xa1, + 0x3e, 0xb7, 0xd2, 0x9c, 0x13, 0x93, 0x50, 0x38, 0x83, 0x2f, 0x41, 0xb3, + 0xff, 0x5c, 0x2b, 0xcd, 0xf2, 0xed, 0x1d, 0xe5, 0xe2, 0xb8, 0x19, 0x4a, + 0x70, 0x2a, 0x3a, 0x76, 0xc3, 0x90, 0x79, 0xc4, 0xc1, 0x62, 0x89, 0xa7, + 0x19, 0xbb, 0x06, 0x96, 0x25, 0xae, 0xe9, 0x2f, 0x91, 0x6b, 0xe1, 0x1a, + 0xa1, 0x5d, 0xb5, 0x24, 0x6e, 0x38, 0x7a, 0x9b, 0x2d, 0x17, 0xc7, 0x66, + 0xc7, 0x3f, 0xcb, 0x27, 0xb8, 0xc6, 0x8d, 0x62, 0xb6, 0x7c, 0x29, 0x38, + 0xc7, 0x5a, 0x6e, 0x19, 0x40, 0x0b, 0xd8, 0x8a, 0x5a, 0xc2, 0x6f, 0x6c, + 0xf8, 0x9a, 0x19, 0x1b, 0x66, 0x2d, 0xa9, 0xad, 0x47, 0xc8, 0xce, 0xa9, + 0xde, 0x08, 0x79, 0xa5, 0xec, 0xe4, 0xe4, 0xf6, 0xd3, 0x92, 0x28, 0x95, + 0xb8, 0x8b, 0x61, 0x94, 0xda, 0x6d, 0xc7, 0xdc, 0x2a, 0x76, 0x30, 0xb5, + 0xdb, 0xb8, 0x6f, 0x52, 0xd3, 0x88, 0x6c, 0xe2, 0xbe, 0x7c, 0x72, 0x78, + 0x33, 0x3d, 0x5d, 0xa5, 0x73, 0x13, 0x84, 0x8a, 0xec, 0x88, 0x6c, 0xb3, + 0x41, 0x32, 0x07, 0x33, 0x9c, 0x0b, 0xdd, 0xb3, 0xd1, 0x64, 0x8f, 0x1d, + 0xb5, 0xf3, 0xd6, 0x3e, 0xdb, 0xce, 0x6a, 0xf8, 0x94, 0x62, 0xd9, 0x6e, + 0xce, 0x6b, 0xb0, 0x9e, 0x0a, 0x29, 0x0d, 0x5b, 0x22, 0xf2, 0xdc, 0xae, + 0x49, 0xc5, 0x34, 0x29, 0x4d, 0x59, 0x49, 0xf0, 0x7e, 0x41, 0x55, 0x92, + 0x94, 0x16, 0xa2, 0xcc, 0xa8, 0x4c, 0xa2, 0x26, 0x18, 0xf0, 0xc1, 0xa8, + 0x8d, 0xe0, 0x38, 0x90, 0xbe, 0x61, 0x39, 0xfb, 0xa3, 0x32, 0xee, 0x9b, + 0xa8, 0x3c, 0x52, 0x8d, 0xa3, 0x59, 0x57, 0x25, 0xd5, 0x14, 0x89, 0x3c, + 0xbd, 0x3f, 0xb7, 0x32, 0x8f, 0xc3, 0x24, 0xa8, 0x7a, 0xb5, 0x66, 0xfa, + 0x7e, 0x0a, 0x96, 0xe7, 0xe4, 0x9a, 0xfa, 0x81, 0xef, 0xca, 0xfd, 0xf4, + 0x2e, 0xf7, 0x93, 0xe5, 0x79, 0x9d, 0xa6, 0x54, 0xa9, 0x03, 0x93, 0xe7, + 0xc9, 0x5e, 0xaa, 0x98, 0x5d, 0x5b, 0x8c, 0xfc, 0x8e, 0x88, 0x70, 0x00, + 0x29, 0x88, 0x02, 0x65, 0xf9, 0xf2, 0xba, 0x2c, 0xb7, 0x90, 0x5a, 0x00, + 0xd1, 0x0c, 0xb1, 0xa3, 0x0b, 0x0a, 0x36, 0x5a, 0x0e, 0x25, 0x23, 0xc4, + 0x50, 0x98, 0x9c, 0x2e, 0xcb, 0xee, 0xc8, 0x8a, 0xe9, 0xf2, 0x42, 0x6e, + 0xcd, 0x3d, 0x1d, 0x8d, 0x90, 0x7d, 0x1a, 0x7a, 0x8f, 0x55, 0x31, 0x44, + 0x5f, 0xe4, 0xe0, 0x17, 0x2d, 0x7f, 0x14, 0xf0, 0x4f, 0x53, 0xd5, 0xd1, + 0x22, 0x6b, 0xcd, 0x39, 0xd5, 0x75, 0xd5, 0x1d, 0x1c, 0x7c, 0xf2, 0xee, + 0xd0, 0xa2, 0x38, 0x41, 0x1d, 0x91, 0xd2, 0xd1, 0xf2, 0xd7, 0x2a, 0x23, + 0xda, 0x58, 0x02, 0xe7, 0x9a, 0xe8, 0x5a, 0x59, 0x83, 0x4e, 0x90, 0xec, + 0x8e, 0x7d, 0xa1, 0x52, 0xc9, 0x2a, 0x64, 0x05, 0xf7, 0xb9, 0x26, 0x12, + 0xbe, 0x85, 0x04, 0xf2, 0x9a, 0xa7, 0x58, 0x30, 0x60, 0x60, 0x31, 0x2a, + 0xe4, 0x11, 0x7c, 0x01, 0x69, 0x4c, 0x91, 0x1c, 0x32, 0x91, 0xd6, 0x6b, + 0xca, 0xf5, 0xe8, 0xbf, 0x35, 0x95, 0xdb, 0x73, 0x47, 0xb0, 0xa3, 0x9c, + 0xc3, 0xed, 0xdc, 0x17, 0x49, 0x56, 0x46, 0xe6, 0xb7, 0x83, 0xe8, 0xb0, + 0xbd, 0x80, 0xd1, 0x51, 0x0c, 0xa4, 0x72, 0x9b, 0xf6, 0x3e, 0x44, 0x47, + 0x7b, 0x4c, 0x4a, 0x8b, 0xea, 0x45, 0x59, 0xa2, 0x35, 0xa4, 0x54, 0x34, + 0x06, 0x49, 0xcc, 0x9f, 0x32, 0x54, 0x77, 0x74, 0x3b, 0x3b, 0x55, 0x21, + 0x36, 0x83, 0x3c, 0x86, 0xad, 0xb1, 0xd3, 0x1d, 0xbb, 0x0f, 0xcb, 0x61, + 0xb0, 0x85, 0x24, 0x01, 0x6e, 0x72, 0x7e, 0x04, 0x5b, 0x94, 0x37, 0xf7, + 0x48, 0x8c, 0x11, 0xf9, 0xd1, 0xa8, 0xa9, 0x5e, 0x23, 0xdb, 0xaa, 0x90, + 0xa8, 0xe1, 0xc9, 0xe1, 0x07, 0x88, 0x9a, 0x51, 0x2b, 0x82, 0x33, 0x88, + 0x10, 0xf8, 0xd1, 0x3d, 0xe6, 0xdb, 0x0e, 0x4b, 0x0a, 0x96, 0xd1, 0x41, + 0x8e, 0x66, 0xf4, 0xea, 0xf8, 0x6b, 0x02, 0xb5, 0x18, 0xa4, 0xab, 0x18, + 0x6e, 0x02, 0x91, 0x2e, 0x1d, 0x8a, 0xea, 0x0b, 0xb6, 0xa6, 0xa2, 0xd6, + 0x48, 0x87, 0x2d, 0x69, 0x0c, 0xdf, 0x19, 0xf2, 0x1e, 0xb1, 0x92, 0xe6, + 0x72, 0xd0, 0x19, 0xad, 0x03, 0x17, 0xf9, 0x23, 0xef, 0xc8, 0x72, 0x53, + 0x9d, 0x16, 0x83, 0xe8, 0x58, 0x59, 0x4c, 0x7d, 0x56, 0x82, 0xff, 0xc0, + 0x93, 0x08, 0x9e, 0xc2, 0x7b, 0xa2, 0x8b, 0x91, 0x24, 0x3c, 0x13, 0xeb, + 0x81, 0x49, 0x6e, 0xf4, 0xe3, 0xab, 0x8b, 0x28, 0x06, 0x4e, 0x37, 0xe7, + 0x3a, 0x86, 0x69, 0x9f, 0x31, 0x69, 0x2d, 0xc3, 0x98, 0xd9, 0xa4, 0x46, + 0x87, 0xf9, 0xc4, 0x48, 0xca, 0x9d, 0x88, 0xe0, 0x70, 0xfa, 0xb5, 0xc3, + 0x19, 0x1e, 0xf6, 0x28, 0xb7, 0x46, 0x0e, 0x54, 0x0c, 0x59, 0x60, 0x41, + 0x33, 0xd6, 0xb5, 0xd1, 0x95, 0x6a, 0x5f, 0x8d, 0x54, 0x90, 0x60, 0x7e, + 0x30, 0x96, 0x31, 0xcc, 0xf0, 0x38, 0x0c, 0xa7, 0x82, 0x83, 0xc4, 0xcc, + 0xa1, 0xe3, 0x40, 0x38, 0x7e, 0x10, 0xff, 0x88, 0xfd, 0x11, 0xe3, 0x9c, + 0xca, 0x0b, 0x7a, 0xa3, 0x11, 0x16, 0x2f, 0x36, 0x84, 0xdd, 0xbb, 0xbb, + 0x30, 0xc0, 0x08, 0x2b, 0xf3, 0x17, 0x1d, 0x45, 0x7b, 0x26, 0xdc, 0x02, + 0x2d, 0x15, 0x0d, 0x44, 0x3b, 0xdd, 0x58, 0x69, 0x44, 0x0e, 0x99, 0xc1, + 0xb2, 0x11, 0xab, 0xb4, 0x34, 0x42, 0xa3, 0xc0, 0x10, 0xf7, 0x41, 0x32, + 0xf8, 0xf7, 0xf9, 0x4f, 0x1f, 0x46, 0x15, 0x91, 0x8a, 0x0e, 0xb2, 0x7d, + 0x77, 0x5d, 0x00, 0xbb, 0xbd, 0x68, 0xdb, 0xf6, 0x9e, 0x2f, 0xd9, 0xc8, + 0x1e, 0xb0, 0x2c, 0x08, 0x4e, 0x7b, 0xd1, 0xd3, 0x86, 0xac, 0x22, 0x0c, + 0xad, 0x7b, 0x90, 0x0a, 0x43, 0xfd, 0xb1, 0xdb, 0xee, 0xe8, 0x6d, 0x56, + 0xd2, 0x28, 0x7e, 0xe0, 0xd0, 0x8d, 0x09, 0xb6, 0x28, 0x3f, 0x48, 0xf5, + 0x9a, 0xb0, 0x92, 0x66, 0x30, 0x84, 0x8d, 0x14, 0x26, 0xf0, 0x6d, 0x53, + 0xec, 0xa7, 0x77, 0x25, 0x0d, 0xb8, 0xd0, 0x90, 0x8b, 0x9a, 0xf7, 0xb1, + 0x3c, 0x78, 0x6c, 0xcb, 0x7d, 0x77, 0x83, 0x3a, 0x88, 0x42, 0xa6, 0x4f, + 0x1f, 0xb3, 0x91, 0xbd, 0x87, 0x9f, 0xda, 0xa0, 0xf5, 0x43, 0x4b, 0x39, + 0xd2, 0x10, 0x2a, 0xad, 0xb0, 0x06, 0x2a, 0xa7, 0x0f, 0x61, 0x04, 0x6f, + 0xaa, 0xbd, 0x75, 0x01, 0x38, 0xdc, 0x67, 0x57, 0xb6, 0xb5, 0xac, 0x69, + 0x40, 0xd4, 0x75, 0xaf, 0x48, 0x87, 0x2c, 0x07, 0x6c, 0xdf, 0xb4, 0x25, + 0x4c, 0xfa, 0x0c, 0x9b, 0x44, 0x0f, 0xa0, 0xb6, 0x7b, 0xd9, 0x59, 0x10, + 0xec, 0x84, 0x12, 0x16, 0x47, 0x84, 0x61, 0x2d, 0xd1, 0x35, 0x53, 0x01, + 0xa9, 0xc6, 0xe9, 0xe9, 0x87, 0x66, 0x84, 0xc2, 0x29, 0xad, 0xa9, 0x82, + 0x94, 0xa7, 0x22, 0xa3, 0xbf, 0xfe, 0xf2, 0xf6, 0xa5, 0x58, 0x57, 0x82, + 0x53, 0xae, 0x07, 0x98, 0x06, 0x3c, 0x37, 0x89, 0xb8, 0x26, 0x65, 0x4d, + 0x8f, 0xf0, 0x1a, 0x3f, 0xf1, 0x26, 0xaf, 0xaf, 0xb1, 0x7a, 0x84, 0x77, + 0x32, 0xe6, 0xbd, 0x65, 0x2a, 0xac, 0xda, 0xc6, 0xf2, 0xee, 0xba, 0x1c, + 0x06, 0x71, 0x16, 0xf5, 0xd5, 0xcc, 0x7b, 0x22, 0xd7, 0x54, 0x17, 0x22, + 0x8b, 0x21, 0x25, 0x65, 0x89, 0x2f, 0xa4, 0x31, 0x68, 0x63, 0xc9, 0x1f, + 0xc6, 0x94, 0xce, 0x00, 0xde, 0x14, 0x12, 0x12, 0x34, 0x00, 0xfe, 0xf3, + 0xfe, 0xdd, 0x1b, 0xad, 0xab, 0x5f, 0xa8, 0x99, 0x23, 0x94, 0x1e, 0xec, + 0xd9, 0x82, 0x84, 0x23, 0xc1, 0x4b, 0x41, 0x32, 0xca, 0x33, 0x6f, 0x26, + 0xe9, 0xc4, 0x40, 0xab, 0x7e, 0x80, 0x7c, 0x16, 0x2f, 0x71, 0x23, 0x43, + 0x52, 0x65, 0xe2, 0xa9, 0x28, 0xde, 0x80, 0x7d, 0x1d, 0xb7, 0x5d, 0x2a, + 0xb5, 0x0d, 0xe4, 0x23, 0x54, 0x0e, 0x27, 0xb1, 0x1d, 0x2f, 0xfe, 0x82, + 0xf8, 0x8a, 0xf2, 0x41, 0x1b, 0x32, 0x8c, 0x5f, 0x73, 0x4d, 0xba, 0x7c, + 0x57, 0x54, 0xbb, 0xd0, 0xbc, 0xa1, 0x24, 0xa3, 0x26, 0x31, 0x2f, 0xd2, + 0x94, 0x56, 0xda, 0xa4, 0x2f, 0x22, 0x55, 0x55, 0xb2, 0x94, 0xa0, 0x6d, + 0xc7, 0xd8, 0x84, 0xa3, 0x2e, 0x01, 0x3b, 0x3f, 0x06, 0x6d, 0x46, 0xe0, + 0xcf, 0x3f, 0x61, 0x62, 0x3a, 0xd1, 0x77, 0xcd, 0x98, 0xd0, 0xa9, 0x94, + 0x67, 0x83, 0xbe, 0xe4, 0x5f, 0x0a, 0xfd, 0xa2, 0xc2, 0x86, 0x89, 0xaa, + 0x3b, 0x93, 0x8c, 0x03, 0xdd, 0xbc, 0xab, 0x15, 0x26, 0x49, 0xd3, 0x0b, + 0xe1, 0xc9, 0x93, 0x86, 0x19, 0x0e, 0xdc, 0x5c, 0xd6, 0xd3, 0xc0, 0x90, + 0xd6, 0xeb, 0x61, 0xad, 0x80, 0x51, 0x49, 0xf9, 0xa5, 0x2e, 0x60, 0x09, + 0xe3, 0x07, 0x2b, 0x03, 0x1a, 0xe3, 0x77, 0x36, 0xe4, 0xec, 0xa9, 0x39, + 0x81, 0x62, 0xb1, 0xfa, 0x4c, 0x53, 0x1d, 0xf5, 0x69, 0x41, 0x86, 0x9e, + 0xb6, 0xe9, 0xb1, 0x8c, 0x94, 0x90, 0x7a, 0xb0, 0x03, 0x1a, 0x89, 0x61, + 0xe5, 0xeb, 0x08, 0x27, 0xbe, 0xd5, 0x48, 0x2a, 0xc5, 0x60, 0x08, 0xa4, + 0x79, 0xe8, 0x52, 0x17, 0x78, 0xe7, 0x12, 0x23, 0x2a, 0x85, 0xe5, 0x6b, + 0x21, 0x2a, 0x54, 0xb7, 0xb4, 0xc3, 0x3b, 0x10, 0xf8, 0x60, 0xbb, 0x58, + 0xf3, 0x96, 0xe0, 0xce, 0xa2, 0x0e, 0x11, 0xb9, 0x90, 0x30, 0x40, 0x39, + 0xcc, 0x48, 0x19, 0xcf, 0xcd, 0xd7, 0xc2, 0x7a, 0x61, 0xd3, 0x30, 0x87, + 0xa7, 0x4f, 0xd9, 0x83, 0xd6, 0xa3, 0xee, 0xa7, 0xf7, 0x94, 0x63, 0xd5, + 0x43, 0xe6, 0x8f, 0xec, 0xd3, 0x08, 0x2b, 0x24, 0x16, 0xc6, 0xaf, 0x6a, + 0xbf, 0x0d, 0xb7, 0x48, 0x65, 0xdb, 0xdb, 0x9b, 0x8b, 0xf7, 0xef, 0x20, + 0x41, 0x15, 0x1d, 0x7c, 0x64, 0x85, 0x33, 0x37, 0x59, 0x95, 0x34, 0x6b, + 0xdf, 0x26, 0x3a, 0xa8, 0xb4, 0xb8, 0x34, 0xa8, 0x46, 0x34, 0x76, 0xdf, + 0x49, 0x27, 0x49, 0xf0, 0xb4, 0x64, 0xe9, 0x15, 0x24, 0x96, 0xa1, 0x67, + 0x1c, 0xf3, 0x58, 0xbd, 0x19, 0xef, 0x83, 0x68, 0x47, 0x07, 0x65, 0xe7, + 0x86, 0xae, 0x29, 0xcf, 0x4b, 0x3e, 0xd9, 0x0d, 0x9b, 0x2f, 0xaa, 0xd8, + 0xf4, 0xe8, 0xf9, 0x63, 0xba, 0x9a, 0xf3, 0x8f, 0x5e, 0xc7, 0x98, 0xc5, + 0x94, 0x76, 0x8f, 0xfe, 0xcd, 0x11, 0x56, 0x09, 0x52, 0xed, 0xbf, 0xad, + 0x24, 0xed, 0x9b, 0x50, 0x57, 0x82, 0xed, 0x6b, 0x4f, 0xd4, 0xfe, 0x9c, + 0xe7, 0x17, 0x24, 0x7f, 0x2a, 0x77, 0xdd, 0x70, 0xde, 0x1f, 0xa2, 0xf3, + 0x94, 0x70, 0x34, 0xb6, 0x45, 0xa7, 0x8a, 0xfa, 0x52, 0x82, 0xb1, 0xe9, + 0xc9, 0x49, 0x60, 0x50, 0x60, 0x74, 0xe8, 0xd8, 0x63, 0x8c, 0x7e, 0x4f, + 0x78, 0x4d, 0x4a, 0x78, 0xc5, 0xb5, 0xdc, 0x46, 0x8f, 0xca, 0x90, 0xb3, + 0x3e, 0xec, 0x35, 0x41, 0x5c, 0x28, 0x91, 0x69, 0x61, 0xca, 0x20, 0x06, + 0xe7, 0x0e, 0x45, 0xe6, 0xee, 0x3a, 0x7d, 0xdd, 0xc0, 0x0f, 0xe7, 0xb1, + 0xe0, 0xfa, 0xb4, 0x57, 0x13, 0x5a, 0xae, 0x25, 0xa6, 0x80, 0x1b, 0x4d, + 0x61, 0x61, 0x78, 0xf4, 0x3b, 0xa3, 0x7d, 0xcd, 0xff, 0xda, 0xeb, 0xa2, + 0x6d, 0x2c, 0xf8, 0x0a, 0xfb, 0x40, 0x07, 0xda, 0x30, 0xc3, 0xb0, 0x71, + 0x53, 0xc1, 0xbd, 0xfe, 0xec, 0x85, 0xec, 0xf1, 0x40, 0xea, 0x81, 0x90, + 0xf7, 0x23, 0x05, 0x52, 0x16, 0x84, 0x5f, 0xd2, 0xbe, 0xf1, 0x60, 0x7f, + 0xee, 0xf3, 0x7f, 0xec, 0x70, 0x9b, 0x9e, 0x9a, 0xdb, 0x40, 0xa9, 0x1d, + 0x44, 0xad, 0x52, 0x67, 0x9e, 0xdd, 0x0a, 0x28, 0x57, 0x57, 0x53, 0x8f, + 0xac, 0xd3, 0xb4, 0x70, 0x54, 0xf6, 0xd4, 0xe3, 0xa2, 0xfb, 0xcd, 0x76, + 0x3c, 0x3a, 0x0d, 0x32, 0xb2, 0x38, 0xbe, 0xfb, 0xf5, 0x68, 0x71, 0xec, + 0x7e, 0xe6, 0xc5, 0xc7, 0xe6, 0x7f, 0x89, 0xff, 0x1f, 0x01, 0x02, 0x3f, + 0xe6, 0x2a, 0x1f, 0x00, 0x00 +}; +unsigned int http_html_backup_len = 2393; diff --git a/app/modules/eus/prepare.sh b/app/modules/eus/prepare.sh new file mode 100644 index 00000000..133ea9bb --- /dev/null +++ b/app/modules/eus/prepare.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Uses zopfli for better gzip compression +# sudo apt-get install zopfli +zopfli --gzip ./enduser_setup.html +xxd -i ./enduser_setup.html.gz | sed 's/unsigned char/static const char/; s/__enduser_setup_html_gz/http_html_backup/' > http_html_backup.def + diff --git a/docs/en/modules/enduser-setup.md b/docs/en/modules/enduser-setup.md index 704d1dfc..ade57594 100644 --- a/docs/en/modules/enduser-setup.md +++ b/docs/en/modules/enduser-setup.md @@ -7,10 +7,29 @@ This module provides a simple way of configuring ESP8266 chips without using a s ![enduser setup config dialog](../../img/enduser-setup.jpg "enduser setup config dialog") -After running [`enduser_setup.start()`](#enduser_setupstart) a portal like the above can be accessed through a wireless network called SetupGadget_XXXXXX. The portal is used to submit the credentials for the WiFi of the enduser. -After an IP address has been successfully obtained this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. +After running [`enduser_setup.start()`](#enduser_setupstart), a wireless network named "SetupGadget_XXXXXX" will start. Connect to that SSID and then navigate to the root +of any website (e.g., `http://example.com/` will work, but do not use `.local` domains because it will fail on iOS). A web page similar to the picture above will load, allowing the +end user to provide their Wi-Fi information. + +After an IP address has been successfully obtained, then this module will stop as if [`enduser_setup.stop()`](#enduser_setupstop) had been called. There is a 10-second delay before +teardown to allow connected clients to obtain a last status message while the SoftAP is still active. + +Alternative HTML can be served by placing a file called `enduser_setup.html` on the filesystem. Everything needed by the web page must be included in this one file. This file will be kept +in RAM, so keep it as small as possible. The file can be gzip'd ahead of time to reduce the size (i.e., using `gzip -n` or `zopfli`), and when served, the End User Setup module will add +the appropriate `Content-Encoding` header to the response. *Note: Even if gzipped, the file still needs to be named `enduser_setup.html`.* + +The following HTTP endpoints exist: + +|Endpoint|Description| +|--------|-----------| +|/|Returns HTML for the web page. Will return the contents of `enduser_setup.html` if it exists on the filesystem, otherwise will return a page embedded into the firmware image.| +|/aplist|Forces the ESP8266 to perform a site survey across all channels, reporting access points that it can find. Return payload is a JSON array: `[{"ssid":"foobar","rssi":-36,"chan":3}]`| +|/generate_204|Returns a HTTP 204 status (expected by certain Android clients during Wi-Fi connectivity checks)| +|/status|Returns plaintext status description, used by the web page| +|/status.json|Returns a JSON payload containing the ESP8266's chip id in hexadecimal format and the status code: 0=Idle, 1=Connecting, 2=Wrong Password, 3=Network not Found, 4=Failed, 5=Success| +|/setwifi|Endpoint intended for services to use for setting the wifi credentials. Identical to `/update` except returns the same payload as `/status.json` instead of redirecting to `/`.| +|/update|Form submission target. Example: `http://example.com/update?wifi_ssid=foobar&wifi_password=CorrectHorseBatteryStaple`. Must be a GET request. Will redirect to `/` when complete. | -Alternative HTML can be served by placing a file called `enduser_setup.html` in the filesystem. This file will be kept in RAM, so keep it as small as possible. ## enduser_setup.manual() @@ -53,7 +72,7 @@ Starts the captive portal. #### Parameters - `onConnected()` callback will be fired when an IP-address has been obtained, just before the enduser_setup module will terminate itself - `onError()` callback will be fired if an error is encountered. `err_num` is a number describing the error, and `string` contains a description of the error. - - `onDebug()` callback is disabled by default. It is intended to be used to find internal issues in the module. `string` contains a description of what is going on. + - `onDebug()` callback is disabled by default (controlled by `#define ENDUSER_SETUP_DEBUG_ENABLE` in `enduser_setup.c`). It is intended to be used to find internal issues in the module. `string` contains a description of what is going on. #### Returns `nil`