+
+
+
+
+
+
+
+
+
+
+
WiFi Settings - ESP8266 Remote Terminal
+
+
+
+
+
+
+
+
+
WiFi settings
+
+
+
+
+ WiFi mode
+ %WiFiMode%
+
+
+ IP
+ %StaIP%
+
+
+ Switch to
+
+
+
+ AP channel
+
+
+
+
+
+ AP name
+
+
+
+
+
+ Some changes require a reboot, dropping connection. It can take a while to re-connect.
+
+ If you lose access , hold the BOOT button for 2 seconds (the Tx LED starts blinking) to re-enable AP mode.
+ If that fails, hold the BOOT button for over 5 seconds (rapid Tx LED flashing) to perform a factory reset.
+
+
+
+
+
+
+
Select AP to join
+
Scanning.
+
Can't scan in AP-only mode.
+
+
+
+
+ Terminal Help About
+
+
+
+
+
+
+
diff --git a/html_orig/jssrc/wifi.js b/html_orig/jssrc/wifi.js
index dd7f370..9fcf4c0 100644
--- a/html_orig/jssrc/wifi.js
+++ b/html_orig/jssrc/wifi.js
@@ -118,9 +118,9 @@
}
$('#modeswitch').html([
- '
Client+AP AP only ',
- '
Client+AP ',
- '
Client only AP only '
+ '
Client+AP AP only ',
+ '
Client+AP ',
+ '
Client only AP only '
][obj.mode-1]);
};
diff --git a/html_orig/wifi.html b/html_orig/wifi.html
index 32ee1a8..e59f965 100644
--- a/html_orig/wifi.html
+++ b/html_orig/wifi.html
@@ -32,8 +32,8 @@
AP channel
-
@@ -41,8 +41,8 @@
AP name
-
diff --git a/libesphttpd b/libesphttpd
index 03003ea..38c6c91 160000
--- a/libesphttpd
+++ b/libesphttpd
@@ -1 +1 @@
-Subproject commit 03003ea591a272df50159ba52f84ca84c5cad78e
+Subproject commit 38c6c91f50e5a5cfba8df8309a95e814695accba
diff --git a/user/cgi_wifi.c b/user/cgi_wifi.c
new file mode 100644
index 0000000..11a9291
--- /dev/null
+++ b/user/cgi_wifi.c
@@ -0,0 +1,654 @@
+/*
+Cgi/template routines for the /wifi url.
+*/
+
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Jeroen Domburg wrote this file. As long as you retain
+ * this notice you can do whatever you want with this stuff. If we meet some day,
+ * and you think this stuff is worth it, you can buy me a beer in return.
+ * ----------------------------------------------------------------------------
+ *
+ * File adapted and improved by Ondřej Hruška
+ */
+
+// TODO convert to work with WiFi Manager
+// TODO make changes write to wificonf and apply when a different CGI is run (/wifi/apply or something)
+// TODO (connection will trigger this immediately, with some delayto show the connecting page. Then polling cna proceed as usual)
+
+#include
+#include "cgi_wifi.h"
+
+/** WiFi access point data */
+typedef struct {
+ char ssid[32];
+ char bssid[8];
+ int channel;
+ char rssi;
+ char enc;
+} ApData;
+
+/** Scan result type */
+typedef struct {
+ char scanInProgress; //if 1, don't access the underlying stuff from the webpage.
+ ApData **apData;
+ int noAps;
+} ScanResultData;
+
+/** Static scan status storage. */
+static ScanResultData cgiWifiAps;
+
+/** Progress of connection to AP enum */
+typedef enum {
+ CONNTRY_IDLE = 0,
+ CONNTRY_WORKING = 1,
+ CONNTRY_SUCCESS = 2,
+ CONNTRY_FAIL = 3,
+} ConnTry;
+
+/** Connection result var */
+static ConnTry connTryStatus = CONNTRY_IDLE;
+
+/** Connection to AP periodic check timer */
+static os_timer_t staCheckTimer;
+
+/** reset_later() timer */
+static ETSTimer resetTmr;
+
+/**
+ * Callback for reset_later()
+ */
+static void ICACHE_FLASH_ATTR resetTmrCb(void *arg)
+{
+ system_restart();
+}
+
+/**
+ * Schedule a reset
+ * @param ms reset delay (milliseconds)
+ */
+static void ICACHE_FLASH_ATTR reset_later(int ms)
+{
+ os_timer_disarm(&resetTmr);
+ os_timer_setfn(&resetTmr, resetTmrCb, NULL);
+ os_timer_arm(&resetTmr, ms, false);
+}
+
+/**
+ * Calculate approximate signal strength % from RSSI
+ */
+int ICACHE_FLASH_ATTR rssi2perc(int rssi)
+{
+ int r;
+
+ if (rssi > 200)
+ r = 100;
+ else if (rssi < 100)
+ r = 0;
+ else
+ r = 100 - 2 * (200 - rssi); // approx.
+
+ if (r > 100) r = 100;
+ if (r < 0) r = 0;
+
+ return r;
+}
+
+/**
+ * Convert Auth type to string
+ */
+const ICACHE_FLASH_ATTR char *auth2str(AUTH_MODE auth)
+{
+ switch (auth) {
+ case AUTH_OPEN:
+ return "Open";
+ case AUTH_WEP:
+ return "WEP";
+ case AUTH_WPA_PSK:
+ return "WPA";
+ case AUTH_WPA2_PSK:
+ return "WPA2";
+ case AUTH_WPA_WPA2_PSK:
+ return "WPA/WPA2";
+ default:
+ return "Unknown";
+ }
+}
+
+/**
+ * Convert WiFi opmode to string
+ */
+const ICACHE_FLASH_ATTR char *opmode2str(WIFI_MODE opmode)
+{
+ switch (opmode) {
+ case NULL_MODE:
+ return "Disabled";
+ case STATION_MODE:
+ return "Client";
+ case SOFTAP_MODE:
+ return "AP only";
+ case STATIONAP_MODE:
+ return "Client+AP";
+ default:
+ return "Unknown";
+ }
+}
+
+/**
+ * Callback the code calls when a wlan ap scan is done. Basically stores the result in
+ * the static cgiWifiAps struct.
+ *
+ * @param arg - a pointer to {struct bss_info}, which is a linked list of the found APs
+ * @param status - OK if the scan succeeded
+ */
+void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status)
+{
+ int n;
+ struct bss_info *bss_link = (struct bss_info *) arg;
+ dbg("wifiScanDoneCb %d", status);
+ if (status != OK) {
+ cgiWifiAps.scanInProgress = 0;
+ return;
+ }
+
+ // Clear prev ap data if needed.
+ if (cgiWifiAps.apData != NULL) {
+ for (n = 0; n < cgiWifiAps.noAps; n++) free(cgiWifiAps.apData[n]);
+ free(cgiWifiAps.apData);
+ }
+
+ // Count amount of access points found.
+ n = 0;
+ while (bss_link != NULL) {
+ bss_link = bss_link->next.stqe_next;
+ n++;
+ }
+ // Allocate memory for access point data
+ cgiWifiAps.apData = (ApData **) malloc(sizeof(ApData *) * n);
+ if (cgiWifiAps.apData == NULL) {
+ error("Out of memory allocating apData");
+ return;
+ }
+ cgiWifiAps.noAps = n;
+ info("Scan done: found %d APs", n);
+
+ // Copy access point data to the static struct
+ n = 0;
+ bss_link = (struct bss_info *) arg;
+ while (bss_link != NULL) {
+ if (n >= cgiWifiAps.noAps) {
+ // This means the bss_link changed under our nose. Shouldn't happen!
+ // Break because otherwise we will write in unallocated memory.
+ error("Huh? I have more than the allocated %d aps!", cgiWifiAps.noAps);
+ break;
+ }
+ // Save the ap data.
+ cgiWifiAps.apData[n] = (ApData *) malloc(sizeof(ApData));
+ if (cgiWifiAps.apData[n] == NULL) {
+ error("Can't allocate mem for ap buff.");
+ cgiWifiAps.scanInProgress = 0;
+ return;
+ }
+ cgiWifiAps.apData[n]->rssi = bss_link->rssi;
+ cgiWifiAps.apData[n]->channel = bss_link->channel;
+ cgiWifiAps.apData[n]->enc = bss_link->authmode;
+ strncpy(cgiWifiAps.apData[n]->ssid, (char *) bss_link->ssid, 32);
+ strncpy(cgiWifiAps.apData[n]->bssid, (char *) bss_link->bssid, 6);
+
+ bss_link = bss_link->next.stqe_next;
+ n++;
+ }
+ // We're done.
+ cgiWifiAps.scanInProgress = 0;
+}
+
+/**
+ * Routine to start a WiFi access point scan.
+ */
+static void ICACHE_FLASH_ATTR wifiStartScan(void)
+{
+ if (cgiWifiAps.scanInProgress) return;
+ cgiWifiAps.scanInProgress = 1;
+ wifi_station_scan(NULL, wifiScanDoneCb);
+}
+
+/**
+ * This CGI is called from the bit of AJAX-code in wifi.tpl. It will initiate a
+ * scan for access points and if available will return the result of an earlier scan.
+ * The result is embedded in a bit of JSON parsed by the javascript in wifi.tpl.
+ */
+httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData)
+{
+ int pos = (int) connData->cgiData;
+ int len;
+ char buff[256];
+
+ // 2nd and following runs of the function via MORE:
+ if (!cgiWifiAps.scanInProgress && pos != 0) {
+ // Fill in json code for an access point
+ if (pos - 1 < cgiWifiAps.noAps) {
+ int rssi = cgiWifiAps.apData[pos - 1]->rssi;
+
+ len = sprintf(buff, "{\"essid\": \"%s\", \"bssid\": \""
+ MACSTR
+ "\", \"rssi\": %d, \"rssi_perc\": %d, \"enc\": %d, \"channel\": %d}%s",
+ cgiWifiAps.apData[pos - 1]->ssid,
+ MAC2STR(cgiWifiAps.apData[pos - 1]->bssid),
+ rssi,
+ rssi2perc(rssi),
+ cgiWifiAps.apData[pos - 1]->enc,
+ cgiWifiAps.apData[pos - 1]->channel,
+ (pos - 1 == cgiWifiAps.noAps - 1) ? "\n " : ",\n "); //<-terminator
+
+ httpdSend(connData, buff, len);
+ }
+ pos++;
+ if ((pos - 1) >= cgiWifiAps.noAps) {
+ len = sprintf(buff, " ]\n }\n}"); // terminate the whole object
+ httpdSend(connData, buff, len);
+ // Also start a new scan.
+ wifiStartScan();
+ return HTTPD_CGI_DONE;
+ }
+ else {
+ connData->cgiData = (void *) pos;
+ return HTTPD_CGI_MORE;
+ }
+ }
+
+ // First run of the function
+ httpdStartResponse(connData, 200);
+ httpdHeader(connData, "Content-Type", "application/json");
+ httpdEndHeaders(connData);
+
+ if (cgiWifiAps.scanInProgress == 1) {
+ // We're still scanning. Tell Javascript code that.
+ len = sprintf(buff, "{\n \"result\": {\n \"inProgress\": 1\n }\n}");
+ httpdSend(connData, buff, len);
+ return HTTPD_CGI_DONE;
+ }
+ else {
+ // We have a scan result. Pass it on.
+ len = sprintf(buff, "{\n \"result\": {\n \"inProgress\": 0,\n \"APs\": [\n ");
+ httpdSend(connData, buff, len);
+ if (cgiWifiAps.apData == NULL) cgiWifiAps.noAps = 0;
+ connData->cgiData = (void *) 1;
+ return HTTPD_CGI_MORE;
+ }
+}
+
+/** Temp store for new ap info. */
+static struct station_config stconf;
+
+/**
+ * This routine is ran some time after a connection attempt to an access point. If
+ * the connect succeeds, this gets the module in STA-only mode.
+ */
+static void ICACHE_FLASH_ATTR staCheckConnStatus(void *arg)
+{
+ int x = wifi_station_get_connect_status();
+ if (x == STATION_GOT_IP) {
+ info("Connected to AP.");
+ connTryStatus = CONNTRY_SUCCESS;
+
+ // This would enter STA only mode, but that kills the browser page if using STA+AP.
+ // Instead we stay in the current mode and let the user switch manually.
+
+ //wifi_set_opmode(STATION_MODE);
+ //system_restart();
+ }
+ else {
+ connTryStatus = CONNTRY_FAIL;
+ error("Connection failed.");
+ }
+}
+
+/**
+ * Actually connect to a station. This routine is timed because I had problems
+ * with immediate connections earlier. It probably was something else that caused it,
+ * but I can't be arsed to put the code back :P
+ */
+static void ICACHE_FLASH_ATTR cgiWiFiConnect_do(void *arg)
+{
+ int x;
+ dbg("Try to connect to AP...");
+
+ wifi_station_disconnect();
+ wifi_station_set_config(&stconf);
+ wifi_station_connect();
+
+ x = wifi_get_opmode();
+ connTryStatus = CONNTRY_WORKING;
+ if (x != STATION_MODE) {
+ //Schedule check
+ os_timer_disarm(&staCheckTimer);
+ os_timer_setfn(&staCheckTimer, staCheckConnStatus, NULL);
+ os_timer_arm(&staCheckTimer, 15000, 0); //time out after 15 secs of trying to connect
+ }
+}
+
+/**
+ * This cgi uses the routines above to connect to a specific access point with the
+ * given ESSID using the given password.
+ *
+ * Args:
+ * - essid = SSID to connect to
+ * - passwd = password to connect with
+ */
+httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData)
+{
+ char essid[128];
+ char passwd[128];
+ static os_timer_t reassTimer;
+
+ if (connData->conn == NULL) {
+ //Connection aborted. Clean up.
+ return HTTPD_CGI_DONE;
+ }
+
+ int ssilen = httpdFindArg(connData->post->buff, "essid", essid, sizeof(essid));
+ int passlen = httpdFindArg(connData->post->buff, "passwd", passwd, sizeof(passwd));
+
+ if (ssilen == -1 || passlen == -1) {
+ error("Not rx needed args!");
+ httpdRedirect(connData, "/wifi");
+ }
+ else {
+ strncpy((char *) stconf.ssid, essid, 32);
+ strncpy((char *) stconf.password, passwd, 64);
+ info("Try to connect to AP %s pw %s", essid, passwd);
+
+ //Schedule disconnect/connect
+ os_timer_disarm(&reassTimer);
+ os_timer_setfn(&reassTimer, cgiWiFiConnect_do, NULL);
+ // redirect & start connecting a little bit later
+ os_timer_arm(&reassTimer, 2000, 0); // was 500, increased so the connecting page has time to load
+
+ connTryStatus = CONNTRY_IDLE;
+ httpdRedirect(connData, "/wifi/connecting");
+ }
+ return HTTPD_CGI_DONE;
+}
+
+/**
+ * Cgi to get connection status.
+ *
+ * This endpoint returns JSON with keys:
+ * - status = 'idle', 'working' or 'fail',
+ * - ip = IP address, after connection succeeds
+ */
+httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData)
+{
+ char buff[100];
+ int len;
+ struct ip_info info;
+ int st = wifi_station_get_connect_status();
+
+ httpdStartResponse(connData, 200);
+ httpdHeader(connData, "Content-Type", "application/json");
+ httpdEndHeaders(connData);
+
+ if (connTryStatus == CONNTRY_IDLE) {
+ len = sprintf(buff, "{\"status\": \"idle\"}");
+ }
+ else if (connTryStatus == CONNTRY_WORKING || connTryStatus == CONNTRY_SUCCESS) {
+ if (st == STATION_GOT_IP) {
+ wifi_get_ip_info(STATION_IF, &info);
+ len = sprintf(buff, "{\"status\": \"success\", \"ip\": \""
+ IPSTR
+ "\"}", GOOD_IP2STR(info.ip.addr));
+ os_timer_disarm(&staCheckTimer);
+ os_timer_setfn(&staCheckTimer, staCheckConnStatus, NULL);
+ os_timer_arm(&staCheckTimer, 1000, 0);
+ } else {
+ len = sprintf(buff, "{\"status\": \"working\"}");
+ }
+ }
+ else {
+ len = sprintf(buff, "{\"status\": \"fail\"}");
+ }
+
+ httpdSend(connData, buff, len);
+ return HTTPD_CGI_DONE;
+}
+
+/**
+ * Universal CGI endpoint to set WiFi params.
+ * Note that some may cause a (delayed) restart.
+ *
+ * Args:
+ * - ap_ch = channel 1-14
+ * - ap_ssid = SSID name for AP mode
+ * - opmode = WiFi mode (resets device)
+ * - hostname = set client hostname
+ * - tpw = set transmit power
+ * - sta_dhcp_lt = DHCP server lease time
+ * - sta_ip = station mode static IP
+ * - sta_mask = station mode static IP mask (apply only if 'ip' is also sent)
+ * - sta_gw = station mode default gateway (apply only if 'ip' is also sent)
+ * (can be left out, then 0.0.0.0 is used and outbound connections won't work -
+ * but we're normally not making any)
+ * - dhcp = enable or disable DHCP on the station interface
+ */
+httpd_cgi_state ICACHE_FLASH_ATTR cgiWiFiSetParams(HttpdConnData *connData)
+{
+ int len, len2, len3;
+ char buff[50];
+ char buff2[50];
+ char buff3[50];
+
+ // TODO change so that settings are not applied immediately, but persisted first
+ // TODO apply temporary changes like static IP in wifi event CBs
+
+ if (connData->conn == NULL) {
+ //Connection aborted. Clean up.
+ return HTTPD_CGI_DONE;
+ }
+
+ // AP channel (applies in AP-only mode)
+ len = httpdFindArg(connData->getArgs, "ap_ch", buff, sizeof(buff));
+ if (len > 0) {
+ info("Setting WiFi channel for AP-only mode to: %s", buff);
+ int channel = atoi(buff);
+ if (channel > 0 && channel < 15) {
+ dbg("Setting channel=%d", channel);
+
+ struct softap_config wificfg;
+ wifi_softap_get_config(&wificfg);
+ wificfg.channel = (uint8) channel;
+ wifi_softap_set_config(&wificfg);
+ } else {
+ warn("Bad channel value %s, allowed 1-14", buff);
+ }
+ }
+
+ // SSID name in AP mode
+ len = httpdFindArg(connData->getArgs, "ap_ssid", buff, sizeof(buff));
+ if (len > 0) {
+ int i;
+ for (i = 0; i < 32; i++) {
+ char c = buff[i];
+ if (c == 0) break;
+ if (c < 32 || c >= 127) buff[i] = '_';
+ }
+ buff[i] = 0;
+
+ info("Setting SSID to %s", buff);
+
+ struct softap_config wificfg;
+ wifi_softap_get_config(&wificfg);
+ sprintf((char *) wificfg.ssid, buff);
+ wificfg.ssid_len = strlen((char *) wificfg.ssid);
+ wifi_softap_set_config(&wificfg);
+ }
+
+ // WiFi mode
+ len = httpdFindArg(connData->getArgs, "opmode", buff, sizeof(buff));
+ if (len > 0) {
+ dbg("Setting WiFi opmode to: %s", buff);
+ int mode = atoi(buff);
+ if (mode > NULL_MODE && mode < MAX_MODE) {
+ wifi_set_opmode(mode);
+ reset_later(200);
+ } else {
+ warn("Bad opmode value %s", buff);
+ }
+ }
+
+ // Hostname in station mode (for DHCP)
+ len = httpdFindArg(connData->getArgs, "hostname", buff, sizeof(buff));
+ if (len > 0) {
+ dbg("Setting station sta_hostname to: %s", buff);
+ wifi_station_set_hostname(buff);
+ // TODO persistency, re-apply on boot
+ }
+
+ // Hostname in station mode (for DHCP)
+ len = httpdFindArg(connData->getArgs, "tpw", buff, sizeof(buff));
+ if (len > 0) {
+ dbg("Setting AP power to: %s", buff);
+ int tpw = atoi(buff);
+ // min tpw to avoid user locking themselves out TODO verify
+ if (tpw >= 0 && tpw <= 82) {
+ // TODO persistency, re-apply on boot
+ system_phy_set_max_tpw(tpw);
+ } else {
+ warn("tpw %s out of allowed range 0-82.", buff);
+ }
+ }
+
+ // DHCP server lease time
+ len = httpdFindArg(connData->getArgs, "ap_dhcp_lt", buff, sizeof(buff));
+ if (len > 0) {
+ dbg("Setting DHCP lease time to: %s min.", buff);
+ int min = atoi(buff);
+ if (min >= 1 && min <= 2880) {
+ // TODO persistency, re-apply on boot
+ // TODO set only if we're in the right opmode
+ wifi_softap_set_dhcps_lease_time(min);
+ } else {
+ warn("Lease time %s out of allowed range 1-2880.", buff);
+ }
+ }
+
+ // DHCP enable / disable (disable means static IP is enabled)
+ len = httpdFindArg(connData->getArgs, "sta_dhcp", buff, sizeof(buff));
+ if (len > 0) {
+ dbg("DHCP enable = %s", buff);
+ int enable = atoi(buff);
+ if (enable != 0) {
+ wifi_station_dhcpc_stop();
+ } else {
+ wifi_station_dhcpc_start();
+ }
+ // TODO persistency
+ }
+
+ // Static IP
+ len = httpdFindArg(connData->getArgs, "sta_ip", buff, sizeof(buff));
+ len2 = httpdFindArg(connData->getArgs, "sta_mask", buff2, sizeof(buff2));
+ len3 = httpdFindArg(connData->getArgs, "sta_gw", buff3, sizeof(buff3));
+ if (len > 0) {
+ // TODO set only if we're in the right opmode
+ // TODO persistency
+ dbg("Setting static IP = %s", buff);
+ struct ip_info ipinfo;
+ ipinfo.ip.addr = ipaddr_addr(buff);
+ ipinfo.netmask.addr = IPADDR_NONE;
+ ipinfo.gw.addr = IPADDR_NONE;
+ if (len2 > 0) {
+ dbg("Netmask = %s", buff2);
+ ipinfo.netmask.addr = ipaddr_addr(buff2);
+ }
+ if (len3 > 0) {
+ dbg("Gateway = %s", buff3);
+ ipinfo.gw.addr = ipaddr_addr(buff3);
+ }
+ // TODO ...
+ wifi_station_dhcpc_stop();
+ wifi_set_ip_info(STATION_IF, &ipinfo);
+ }
+
+ httpdRedirect(connData, "/wifi");
+ return HTTPD_CGI_DONE;
+}
+
+
+//Template code for the WLAN page.
+httpd_cgi_state ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg)
+{
+ char buff[500];
+ int x;
+ int connectStatus;
+ static struct station_config stconf;
+ static struct softap_config apconf;
+
+ if (token == NULL) {
+ // We're done
+ return HTTPD_CGI_DONE;
+ }
+
+ wifi_station_get_config(&stconf);
+ wifi_softap_get_config(&apconf);
+
+
+ strcpy(buff, "Unknown");
+ if (streq(token, "WiFiMode")) {
+ x = wifi_get_opmode();
+ strcpy(buff, opmode2str(x));
+ }
+ else if (streq(token, "WiFiModeNum")) {
+ x = wifi_get_opmode();
+ sprintf(buff, "%d", x);
+ }
+ else if (streq(token, "WiFiChannel")) {
+ sprintf(buff, "%d", apconf.channel);
+ }
+ else if (streq(token, "APName")) {
+ sprintf(buff, "%s", apconf.ssid);
+ }
+ else if (streq(token, "StaIP")) {
+ x = wifi_get_opmode();
+ connectStatus = wifi_station_get_connect_status();
+
+ if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP) {
+ strcpy(buff, "");
+ }
+ else {
+ struct ip_info info;
+ wifi_get_ip_info(STATION_IF, &info);
+ sprintf(buff, IPSTR, GOOD_IP2STR(info.ip.addr));
+ }
+ }
+ else if (streq(token, "StaSSID")) {
+ connectStatus = wifi_station_get_connect_status();
+ x = wifi_get_opmode();
+ if (x == SOFTAP_MODE || connectStatus != STATION_GOT_IP) {
+ strcpy(buff, "");
+ }
+ else {
+ strcpy(buff, (char *) stconf.ssid);
+ }
+ }
+ else if (streq(token, "WiFiPasswd")) {
+ strcpy(buff, (char *) stconf.password);
+ }
+ else if (streq(token, "WiFiapwarn")) {
+ // TODO get rid of this
+ x = wifi_get_opmode();
+ if (x == SOFTAP_MODE) { // 2
+ strcpy(buff, "Enable client for scanning.");
+ }
+ else if (x == STATIONAP_MODE) { // 3
+ strcpy(buff,
+ "Switch: Client only , AP only ");
+ }
+ else { // 1
+ strcpy(buff,
+ "Switch: Client+AP , AP only ");
+ }
+ }
+ httpdSend(connData, buff, -1);
+ return HTTPD_CGI_DONE;
+}
diff --git a/user/cgi_wifi.h b/user/cgi_wifi.h
new file mode 100644
index 0000000..a300d2a
--- /dev/null
+++ b/user/cgi_wifi.h
@@ -0,0 +1,33 @@
+#ifndef CGIWIFI_H
+#define CGIWIFI_H
+
+#include "httpd.h"
+
+/**
+ * Convert IP hex to arguments for printf.
+ * Library IP2STR(ip) does not work correctly due to unaligned memory access.
+ */
+#define GOOD_IP2STR(ip) ((ip)>>0)&0xff, ((ip)>>8)&0xff, ((ip)>>16)&0xff, ((ip)>>24)&0xff
+
+httpd_cgi_state cgiWiFiScan(HttpdConnData *connData);
+httpd_cgi_state cgiWiFiConnect(HttpdConnData *connData);
+httpd_cgi_state cgiWiFiConnStatus(HttpdConnData *connData);
+httpd_cgi_state cgiWiFiSetParams(HttpdConnData *connData);
+httpd_cgi_state tplWlan(HttpdConnData *connData, char *token, void **arg);
+
+// WiFi config options:
+// - Persistent
+// - channel
+// - AP ssid
+// - opmode
+// - AP to connect to
+// - Temporary
+// - sta_hostname (sta)
+// - tpw (ap, sta+ap?)
+// - dhcp_lt (ap, sta+ap)
+// - static IP
+// - static mask
+// - static gw
+// - dhcp enable or disable
+
+#endif
diff --git a/user/routes.c b/user/routes.c
index 7c77313..2f3dc68 100644
--- a/user/routes.c
+++ b/user/routes.c
@@ -2,11 +2,10 @@
#include
#include
#include
-#include
#include
#include "routes.h"
-
+#include "cgi_wifi.h"
#include "cgi_reset.h"
#include "cgi_ping.h"
#include "cgi_main.h"
@@ -48,9 +47,7 @@ HttpdBuiltInUrl routes[] = {
ROUTE_CGI("/wifi/connect", cgiWiFiConnect),
ROUTE_CGI("/wifi/connstatus", cgiWiFiConnStatus),
ROUTE_FILE("/wifi/connecting", "/wifi_conn.tpl"),
- ROUTE_CGI("/wifi/setmode", cgiWiFiSetMode),
- ROUTE_CGI("/wifi/setchannel", cgiWiFiSetChannel),
- ROUTE_CGI("/wifi/setname", cgiWiFiSetSSID),
+ ROUTE_CGI("/wifi/set", cgiWiFiSetParams),
ROUTE_FILESYSTEM(),
ROUTE_END(),
diff --git a/user/user_main.c b/user/user_main.c
index 95364d4..0990b41 100644
--- a/user/user_main.c
+++ b/user/user_main.c
@@ -26,6 +26,7 @@
#include "user_main.h"
#include "uart_driver.h"
#include "ansi_parser_callbacks.h"
+#include "wifi_manager.h"
#ifdef ESPFS_POS
CgiUploadFlashDef uploadParams={
@@ -48,6 +49,7 @@ CgiUploadFlashDef uploadParams={
#endif
static ETSTimer prHeapTimer;
+static ETSTimer userStartTimer;
/** Periodically show heap usage */
static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg)
@@ -77,11 +79,49 @@ static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg)
cnt++;
}
+static void user_start(void *unused)
+{
+ // TODO load persistent data, init wificonf
+
+ // Change AP name if AI-THINKER found (means un-initialized device)
+ struct softap_config apconf;
+ wifi_softap_get_config(&apconf);
+ if (strstarts((char*)apconf.ssid, "AI-THINKER")) {
+ warn("Un-initialized device, performing factory reset.");
+ apars_handle_OSC_FactoryReset();
+ return;
+ }
+
+ // Set up WiFi & connect
+ wifimgr_restore_defaults();
+ wifimgr_apply_settings();
+
+ // Captive portal
+ captdnsInit();
+
+ // Server
+ httpdInit(routes, 80);
+
+ // The terminal screen
+ screen_init();
+
+ // Print the CANCEL character to indicate the module has restarted
+ // Critically important for client application if any kind of screen persistence / content re-use is needed
+ UART_WriteChar(UART0, 24, UART_TIMEOUT_US); // 0x18 - 24 - CAN
+
+ info("Listening on UART0, 115200-8-N-1!");
+}
+
//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done.
void ICACHE_FLASH_ATTR user_init(void)
{
serialInit();
+ // Prevent WiFi starting and connecting by default
+ // let wifi manager handle it
+ wifi_station_set_auto_connect(false);
+ wifi_set_opmode(NULL_MODE);
+
printf("\r\n");
banner("====== ESP8266 Remote Terminal ======");
banner_info("Firmware (c) Ondrej Hruska, 2017");
@@ -94,15 +134,6 @@ void ICACHE_FLASH_ATTR user_init(void)
ioInit();
- // Change AP name if AI-THINKER found (means un-initialized device)
- struct softap_config apconf;
- wifi_softap_get_config(&apconf);
- if (strstarts((char*)apconf.ssid, "AI-THINKER")) {
- warn("Un-initialized device, performing factory reset.");
- apars_handle_OSC_FactoryReset();
- return;
- }
-
// 0x40200000 is the base address for spi flash memory mapping, ESPFS_POS is the position
// where image is written in flash that is defined in Makefile.
#ifdef ESPFS_POS
@@ -111,25 +142,15 @@ void ICACHE_FLASH_ATTR user_init(void)
espFsInit((void *) (webpages_espfs_start));
#endif
- // Captive portal
- captdnsInit();
-
- // Server
- httpdInit(routes, 80);
-
// Heap use timer & blink
os_timer_disarm(&prHeapTimer);
os_timer_setfn(&prHeapTimer, prHeapTimerCb, NULL);
os_timer_arm(&prHeapTimer, 1000, 1);
- // The terminal screen
- screen_init();
-
- // Print the CANCEL character to indicate the module has restarted
- // Critically important for client application if any kind of screen persistence / content re-use is needed
- UART_WriteChar(UART0, 24, UART_TIMEOUT_US); // 0x18 - 24 - CAN
-
- info("Listening on UART0, 115200-8-N-1!");
+ // do later (some functions do not yet work if called from user_init)
+ os_timer_disarm(&userStartTimer);
+ os_timer_setfn(&userStartTimer, user_start, NULL);
+ os_timer_arm(&userStartTimer, 10, 0);
}
// ---- unused funcs removed from sdk to save space ---
diff --git a/user/wifi_manager.c b/user/wifi_manager.c
new file mode 100644
index 0000000..d19a6dc
--- /dev/null
+++ b/user/wifi_manager.c
@@ -0,0 +1,194 @@
+//
+// Created by MightyPork on 2017/07/08.
+//
+
+#include "wifi_manager.h"
+
+WiFiSettingsBlock wificonf;
+
+/**
+ * Restore defaults in the WiFi config block.
+ * This is to be called if the WiFi config is corrupted on startup,
+ * before applying the config.
+ */
+void wifimgr_restore_defaults(void)
+{
+ u8 mac[6];
+ wifi_get_macaddr(SOFTAP_IF, mac);
+
+ wificonf.opmode = STATIONAP_MODE;
+ wificonf.tpw = 20;
+ wificonf.ap_channel = 1;
+ sprintf((char *) wificonf.ap_ssid, "TERM-%02X%02X%02X", mac[3], mac[4], mac[5]);
+ wificonf.ap_password[0] = 0; // PSK2 always if password is not null.
+ wificonf.ap_dhcp_lease_time = 120;
+ wificonf.ap_hidden = false;
+
+ IP4_ADDR(&wificonf.ap_ip.ip, 192, 168, mac[5], 1);
+ IP4_ADDR(&wificonf.ap_ip.netmask, 255, 255, 255, 0);
+ IP4_ADDR(&wificonf.ap_ip.gw, 192, 168, mac[5], 1);
+
+ // --- Client config ---
+ wificonf.sta_ssid[0] = 0;
+ wificonf.sta_password[0] = 0;
+ //sprintf((char *) wificonf.sta_ssid, "Chlivek");
+ //sprintf((char *) wificonf.sta_password, "prase chrochta");
+ strcpy((char *) wificonf.sta_hostname, (char *) wificonf.ap_ssid); // use the same value for sta_hostname as AP name
+ wificonf.sta_dhcp_enable = true;
+
+ IP4_ADDR(&wificonf.sta_ip.ip, 192, 168, 0, (mac[5]==1?2:mac[5]));// avoid being the same as "default gw"
+ IP4_ADDR(&wificonf.sta_ip.netmask, 255, 255, 255, 0);
+ IP4_ADDR(&wificonf.sta_ip.gw, 192, 168, 0, 1);
+}
+
+/**
+ * Event handler
+ */
+void wifimgr_event_cb(System_Event_t *event)
+{
+ switch (event->event) {
+// case EVENT_STAMODE_CONNECTED:
+// EVENT_STAMODE_DISCONNECTED,
+// EVENT_STAMODE_AUTHMODE_CHANGE,
+// EVENT_STAMODE_GOT_IP,
+// EVENT_STAMODE_DHCP_TIMEOUT,
+// EVENT_SOFTAPMODE_STACONNECTED,
+// EVENT_SOFTAPMODE_STADISCONNECTED,
+// EVENT_SOFTAPMODE_PROBEREQRECVED,
+ }
+}
+
+static void configure_station(void)
+{
+ info("[WiFi] Configuring Station mode...");
+ struct station_config conf;
+ strcpy((char *) conf.ssid, (char *) wificonf.sta_ssid);
+ strcpy((char *) conf.password, (char *) wificonf.sta_password);
+ dbg("[WiFi] Connecting to \"%s\", password \"%s\"", conf.ssid, conf.password);
+ conf.bssid_set = 0;
+ conf.bssid[0] = 0;
+ wifi_station_disconnect();
+ wifi_station_set_config_current(&conf);
+ dbg("[WiFi] Hostname = %s", wificonf.sta_hostname);
+ wifi_station_set_hostname((char*)wificonf.sta_hostname);
+
+ if (wificonf.sta_dhcp_enable) {
+ dbg("[WiFi] Starting DHCP...");
+ if (!wifi_station_dhcpc_start()) {
+ error("[WiFi] DHCp failed to start!");
+ return;
+ }
+ }
+ else {
+ info("[WiFi] Setting up static IP...");
+ dbg("[WiFi] Client.ip = "IPSTR, GOOD_IP2STR(wificonf.sta_ip.ip.addr));
+ dbg("[WiFi] Client.mask = "IPSTR, GOOD_IP2STR(wificonf.sta_ip.netmask.addr));
+ dbg("[WiFi] Client.gw = "IPSTR, GOOD_IP2STR(wificonf.sta_ip.gw.addr));
+
+ wifi_station_dhcpc_stop();
+ // Load static IP config
+ if (!wifi_set_ip_info(STATION_IF, &wificonf.sta_ip)) {
+ error("[WiFi] Error setting static IP!");
+ return;
+ }
+ }
+
+ info("[WiFi] Trying to connect to AP...");
+ wifi_station_connect();
+}
+
+static void configure_ap(void)
+{
+ bool suc;
+
+ info("[WiFi] Configuring SoftAP mode...");
+ // AP is enabled
+ struct softap_config conf;
+ conf.channel = wificonf.ap_channel;
+ strcpy((char *) conf.ssid, (char *) wificonf.ap_ssid);
+ strcpy((char *) conf.password, (char *) wificonf.ap_password);
+ conf.authmode = (wificonf.ap_password[0] == 0 ? AUTH_OPEN : AUTH_WPA2_PSK);
+ conf.ssid_len = strlen((char *) conf.ssid);
+ conf.ssid_hidden = wificonf.ap_hidden;
+ conf.max_connection = 4; // default 4 (max possible)
+ conf.beacon_interval = 100; // default 100 ms
+
+ // Set config
+ //ETS_UART_INTR_DISABLE();
+ suc = wifi_softap_set_config_current(&conf);
+ //ETS_UART_INTR_ENABLE();
+ if (!suc) {
+ error("[WiFi] AP config set fail!");
+ return;
+ }
+
+ // Set IP
+ info("[WiFi] Configuring SoftAP local IP...");
+ dbg("[WiFi] SoftAP.ip = "IPSTR, GOOD_IP2STR(wificonf.ap_ip.ip.addr));
+ dbg("[WiFi] SoftAP.mask = "IPSTR, GOOD_IP2STR(wificonf.ap_ip.netmask.addr));
+ dbg("[WiFi] SoftAP.gw = "IPSTR, GOOD_IP2STR(wificonf.ap_ip.gw.addr));
+
+ wifi_softap_dhcps_stop();
+
+ // Configure DHCP
+ if (!wifi_set_ip_info(SOFTAP_IF, &wificonf.ap_ip)) {
+ error("[WiFi] IP set fail!");
+ return;
+ }
+
+ info("[WiFi] Configuring SoftAP DHCP server...");
+ struct dhcps_lease dhcp_lease;
+ struct ip_addr ip;
+ ip.addr = wificonf.ap_ip.ip.addr;
+ ip.addr = (ip.addr & 0x00FFFFFFUL) | ((((ip.addr >> 24) & 0xFF) + 99UL) << 24);
+ dhcp_lease.start_ip.addr = ip.addr;
+ ip.addr = (ip.addr & 0x00FFFFFFUL) | ((((ip.addr >> 24) & 0xFF) + 100UL) << 24);
+ dhcp_lease.end_ip.addr = ip.addr;
+
+ dbg("[WiFi] DHCP.start = "IPSTR, GOOD_IP2STR(dhcp_lease.start_ip.addr));
+ dbg("[WiFi] DHCP.end = "IPSTR, GOOD_IP2STR(dhcp_lease.end_ip.addr));
+ dbg("[WiFi] DHCP.lease = %d minutes", wificonf.ap_dhcp_lease_time);
+
+ if (!wifi_softap_set_dhcps_lease(&dhcp_lease)) {
+ error("[WiFi] DHCP address range set fail!");
+ return;
+ }
+
+ if (!wifi_softap_set_dhcps_lease_time(wificonf.ap_dhcp_lease_time)) {
+ error("[WiFi] DHCP lease time set fail!");
+ return;
+ }
+
+ // some weird magic shit about router
+ uint8 mode = 1;
+ wifi_softap_set_dhcps_offer_option(OFFER_ROUTER, &mode);
+
+ if (!wifi_softap_dhcps_start()) {
+ error("[WiFi] Failed to start DHCP server!");
+ return;
+ }
+}
+
+/**
+ * Register the WiFi event listener, cycle WiFi, apply settings
+ */
+void wifimgr_apply_settings(void)
+{
+ info("[WiFi] Initializing WiFi manager...");
+// wifi_set_event_handler_cb(wifimgr_event_cb);
+
+ // Force wifi cycle
+ dbg("[WiFi] WiFi reset to apply new settings");
+ wifi_set_opmode(NULL_MODE);
+ wifi_set_opmode(wificonf.opmode);
+
+ // Configure the client
+ if (wificonf.opmode == STATIONAP_MODE || wificonf.opmode == STATION_MODE) {
+ configure_station();
+ }
+
+ // Configure the AP
+ if (wificonf.opmode == STATIONAP_MODE || wificonf.opmode == SOFTAP_MODE) {
+ configure_ap();
+ }
+}
diff --git a/user/wifi_manager.h b/user/wifi_manager.h
new file mode 100644
index 0000000..daf09e3
--- /dev/null
+++ b/user/wifi_manager.h
@@ -0,0 +1,47 @@
+//
+// Created by MightyPork on 2017/07/08.
+// This module handles all WiFi configuration and is interfaced
+// by the cgi_wifi functions.
+//
+
+#ifndef ESP_VT100_FIRMWARE_WIFI_MANAGER_H
+#define ESP_VT100_FIRMWARE_WIFI_MANAGER_H
+
+#include
+#include "cgi_wifi.h"
+
+/**
+ * A structure holding all configured WiFi parameters
+ * and the active state.
+ *
+ * This block can be used eg. for WiFi config backup.
+ */
+typedef struct {
+ WIFI_MODE opmode : 32;
+ u8 sta_hostname[32];
+ u32 tpw;
+
+ // --- AP config ---
+ u32 ap_channel; // 32 for alignment, needs 8
+ u8 ap_ssid[32];
+ u8 ap_password[32];
+ u32 ap_hidden;
+ u32 ap_dhcp_lease_time; // in minutes
+
+ struct ip_info ap_ip;
+
+ // --- Client config ---
+ u8 sta_ssid[32];
+ u8 sta_password[64];
+ u32 sta_dhcp_enable;
+
+ struct ip_info sta_ip;
+} WiFiSettingsBlock;
+
+extern WiFiSettingsBlock wificonf;
+
+void wifimgr_restore_defaults(void);
+
+void wifimgr_apply_settings(void);
+
+#endif //ESP_VT100_FIRMWARE_WIFI_MANAGER_H