From ff5a74b20aeb72ac5c882f65ed617e213969aab5 Mon Sep 17 00:00:00 2001 From: KatAst Date: Thu, 24 Dec 2015 14:03:15 +0100 Subject: [PATCH 01/37] Added SOFTAP hard-coded settings Set the values of SOFTAP config straight in the Makefile --- Makefile | 75 +++- esp-link/cgiwifi.c | 1012 ++++++++++++++++++++++++-------------------- esp-link/main.c | 22 - 3 files changed, 604 insertions(+), 505 deletions(-) diff --git a/Makefile b/Makefile index 3a6dfbb..95e2b82 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ # Makefile heavily adapted to esp-link and wireless flashing by Thorsten von Eicken # Lots of work, in particular to support windows, by brunnels # Original from esphttpd and others... +# Added support for SOFTAP hard-coded configuration by KatAst # VERBOSE=1 # # Start by setting the directories for the toolchain a few lines down @@ -19,9 +20,37 @@ # The Wifi station configuration can be hard-coded here, which makes esp-link come up in STA+AP # mode trying to connect to the specified AP *only* if the flash wireless settings are empty! # This happens on a full serial flash and avoids having to hunt for the AP... -# STA_SSID ?= +# STA_SSID ?= # STA_PASS ?= +# The SOFTAP configuration can be hard-coded here, the minimum parameters to set are AP_SSID && AP_PASS +# The AP SSID has to be at least 8 characters long, same for AP PASSWORD +# The AP AUTH MODE can be set to ( default = AUTH_WPA_WPA2_PSK ) +# AUTH_OPEN = 0, +# AUTH_WEP, +# AUTH_WPA_PSK, +# AUTH_WPA2_PSK, +# AUTH_WPA_WPA2_PSK +# SSID hidden default 0, ( 0 | 1 ) +# Max connections default 4, ( 1 ~ 4 ) +# Beacon interval default 100, ( 100 ~ 60000ms ) + +AP_SSID ?=esp-link-test +AP_PASS ?=esp-link-test +# AP_AUTH_MODE ?=AUTH_WPA_WPA2_PSK +# AP_SSID_HIDDEN ?=0 +# AP_MAX_CONN ?=3 +# AP_BEACON_INTERVAL ?=150 + + +# If CHANGE_TO_STA is set to "yes" the esp-link module will switch to station mode +# once successfully connected to an access point. Else it will stay in STA+AP mode. + +CHANGE_TO_STA ?= no + +# hostname or IP address for wifi flashing +ESP_HOSTNAME ?= esp-link + # --------------- toolchain configuration --------------- # Base directory for the compiler. Needs a / at the end. @@ -30,7 +59,7 @@ XTENSA_TOOLS_ROOT ?= $(abspath ../esp-open-sdk/xtensa-lx106-elf/bin)/ # Base directory of the ESP8266 SDK package, absolute # Typically you'll download from Espressif's BBS, http://bbs.espressif.com/viewforum.php?f=5 -SDK_BASE ?= $(abspath ../esp_iot_sdk_v1.5.0) +SDK_BASE ?= $(abspath ../esp-open-sdk/esp_iot_sdk_v1.5.0) # Esptool.py path and port, only used for 1-time serial flashing # Typically you'll use https://github.com/themadinventor/esptool @@ -39,15 +68,6 @@ ESPTOOL ?= $(abspath ../esp-open-sdk/esptool/esptool.py) ESPPORT ?= /dev/ttyUSB0 ESPBAUD ?= 460800 -# The Wifi station configuration can be hard-coded here, which makes esp-link come up in STA+AP -# mode trying to connect to the specified AP *only* if the flash wireless settings are empty! -# This happens on a full serial flash and avoids having to hunt for the AP... -# STA_SSID ?= -# STA_PASS ?= - -# hostname or IP address for wifi flashing -ESP_HOSTNAME ?= esp-link - # --------------- chipset configuration --------------- # Pick your flash size: "512KB", "1MB", or "4MB" @@ -64,14 +84,9 @@ LED_CONN_PIN ?= 0 # GPIO pin used for "serial activity" LED, active low LED_SERIAL_PIN ?= 14 -# --------------- esp-link config options --------------- +# --------------- esp-link modules config options --------------- -# If CHANGE_TO_STA is set to "yes" the esp-link module will switch to station mode -# once successfully connected to an access point. Else it will stay in AP+STA mode. - -CHANGE_TO_STA ?= yes - -# Optional Modules +# Optional Modules mqtt MODULES ?= mqtt rest syslog # --------------- esphttpd config options --------------- @@ -268,6 +283,30 @@ ifneq ($(strip $(STA_PASS)),) CFLAGS += -DSTA_PASS="$(STA_PASS)" endif +ifneq ($(strip $(AP_SSID)),) +CFLAGS += -DAP_SSID="$(AP_SSID)" +endif + +ifneq ($(strip $(AP_PASS)),) +CFLAGS += -DAP_PASS="$(AP_PASS)" +endif + +ifneq ($(strip $(AP_AUTH_MODE)),) +CFLAGS += -DAP_AUTH_MODE="$(AP_AUTH_MODE)" +endif + +ifneq ($(strip $(AP_SSID_HIDDEN)),) +CFLAGS += -DAP_SSID_HIDDEN="$(AP_SSID_HIDDEN)" +endif + +ifneq ($(strip $(AP_MAX_CONN)),) +CFLAGS += -DAP_MAX_CONN="$(AP_MAX_CONN)" +endif + +ifneq ($(strip $(AP_BEACON_INTERVAL)),) +CFLAGS += -DAP_BEACON_INTERVAL="$(AP_BEACON_INTERVAL)" +endif + ifeq ("$(GZIP_COMPRESSION)","yes") CFLAGS += -DGZIP_COMPRESSION endif diff --git a/esp-link/cgiwifi.c b/esp-link/cgiwifi.c index b83a330..cf36135 100644 --- a/esp-link/cgiwifi.c +++ b/esp-link/cgiwifi.c @@ -1,6 +1,6 @@ /* -Cgi/template routines for the /wifi url. -*/ + Cgi/template routines for the /wifi url. + */ /* * ---------------------------------------------------------------------------- @@ -26,21 +26,30 @@ Cgi/template routines for the /wifi url. #define DBG(format, ...) do { } while(0) #endif +# define VERS_STR_STR(V) #V +# define VERS_STR(V) VERS_STR_STR(V) + bool mdns_started = false; // ===== wifi status change callbacks static WifiStateChangeCb wifi_state_change_cb[4]; +// Temp store for new ap info. +static struct station_config stconf; + +// Temp store for new ap config +static struct softap_config apconf; + uint8_t wifiState = wifiIsDisconnected; // reasons for which a connection failed uint8_t wifiReason = 0; static char *wifiReasons[] = { - "", "unspecified", "auth_expire", "auth_leave", "assoc_expire", "assoc_toomany", "not_authed", - "not_assoced", "assoc_leave", "assoc_not_authed", "disassoc_pwrcap_bad", "disassoc_supchan_bad", - "ie_invalid", "mic_failure", "4way_handshake_timeout", "group_key_update_timeout", - "ie_in_4way_differs", "group_cipher_invalid", "pairwise_cipher_invalid", "akmp_invalid", - "unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected", - "beacon_timeout", "no_ap_found" }; + "", "unspecified", "auth_expire", "auth_leave", "assoc_expire", "assoc_toomany", "not_authed", + "not_assoced", "assoc_leave", "assoc_not_authed", "disassoc_pwrcap_bad", "disassoc_supchan_bad", + "ie_invalid", "mic_failure", "4way_handshake_timeout", "group_key_update_timeout", + "ie_in_4way_differs", "group_cipher_invalid", "pairwise_cipher_invalid", "akmp_invalid", + "unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected", + "beacon_timeout", "no_ap_found" }; static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; @@ -48,100 +57,100 @@ static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; void (*wifiStatusCb)(uint8_t); // callback when wifi status changes static char* ICACHE_FLASH_ATTR wifiGetReason(void) { - if (wifiReason <= 24) return wifiReasons[wifiReason]; - if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24]; - return wifiReasons[1]; + if (wifiReason <= 24) return wifiReasons[wifiReason]; + if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24]; + return wifiReasons[1]; } // handler for wifi status change callback coming in from espressif library static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) { - switch (evt->event) { - case EVENT_STAMODE_CONNECTED: - wifiState = wifiIsConnected; - wifiReason = 0; - DBG("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid, - evt->event_info.connected.channel); - statusWifiUpdate(wifiState); - break; - case EVENT_STAMODE_DISCONNECTED: - wifiState = wifiIsDisconnected; - wifiReason = evt->event_info.disconnected.reason; - DBG("Wifi disconnected from ssid %s, reason %s (%d)\n", - evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); - statusWifiUpdate(wifiState); - break; - case EVENT_STAMODE_AUTHMODE_CHANGE: - DBG("Wifi auth mode: %d -> %d\n", - evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); - break; - case EVENT_STAMODE_GOT_IP: - wifiState = wifiGotIP; - wifiReason = 0; - DBG("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", - IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), - IP2STR(&evt->event_info.got_ip.gw)); - statusWifiUpdate(wifiState); - if (!mdns_started) - wifiStartMDNS(evt->event_info.got_ip.ip); - break; - case EVENT_SOFTAPMODE_STACONNECTED: - DBG("Wifi AP: station " MACSTR " joined, AID = %d\n", - MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); - break; - case EVENT_SOFTAPMODE_STADISCONNECTED: - DBG("Wifi AP: station " MACSTR " left, AID = %d\n", - MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); - break; - default: - break; - } - - for (int i = 0; i < 4; i++) { - if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState); - } + switch (evt->event) { + case EVENT_STAMODE_CONNECTED: + wifiState = wifiIsConnected; + wifiReason = 0; + DBG("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid, + evt->event_info.connected.channel); + statusWifiUpdate(wifiState); + break; + case EVENT_STAMODE_DISCONNECTED: + wifiState = wifiIsDisconnected; + wifiReason = evt->event_info.disconnected.reason; + DBG("Wifi disconnected from ssid %s, reason %s (%d)\n", + evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); + statusWifiUpdate(wifiState); + break; + case EVENT_STAMODE_AUTHMODE_CHANGE: + DBG("Wifi auth mode: %d -> %d\n", + evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); + break; + case EVENT_STAMODE_GOT_IP: + wifiState = wifiGotIP; + wifiReason = 0; + DBG("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", + IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), + IP2STR(&evt->event_info.got_ip.gw)); + statusWifiUpdate(wifiState); + if (!mdns_started) + wifiStartMDNS(evt->event_info.got_ip.ip); + break; + case EVENT_SOFTAPMODE_STACONNECTED: + DBG("Wifi AP: station " MACSTR " joined, AID = %d\n", + MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); + break; + case EVENT_SOFTAPMODE_STADISCONNECTED: + DBG("Wifi AP: station " MACSTR " left, AID = %d\n", + MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); + break; + default: + break; + } + + for (int i = 0; i < 4; i++) { + if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState); + } } void ICACHE_FLASH_ATTR wifiAddStateChangeCb(WifiStateChangeCb cb) { - for (int i = 0; i < 4; i++) { - if (wifi_state_change_cb[i] == cb) return; - if (wifi_state_change_cb[i] == NULL) { - wifi_state_change_cb[i] = cb; - return; + for (int i = 0; i < 4; i++) { + if (wifi_state_change_cb[i] == cb) return; + if (wifi_state_change_cb[i] == NULL) { + wifi_state_change_cb[i] = cb; + return; + } } - } - DBG("WIFI: max state change cb count exceeded\n"); + DBG("WIFI: max state change cb count exceeded\n"); } void ICACHE_FLASH_ATTR wifiStartMDNS(struct ip_addr ip) { - if (flashConfig.mdns_enable) { - struct mdns_info *mdns_info = (struct mdns_info *)os_zalloc(sizeof(struct mdns_info)); - mdns_info->host_name = flashConfig.hostname; - mdns_info->server_name = flashConfig.mdns_servername; - mdns_info->server_port = 80; - mdns_info->ipAddr = ip.addr; - espconn_mdns_init(mdns_info); - } - else { - espconn_mdns_server_unregister(); - espconn_mdns_close(); - } - mdns_started = true; + if (flashConfig.mdns_enable) { + struct mdns_info *mdns_info = (struct mdns_info *)os_zalloc(sizeof(struct mdns_info)); + mdns_info->host_name = flashConfig.hostname; + mdns_info->server_name = flashConfig.mdns_servername; + mdns_info->server_port = 80; + mdns_info->ipAddr = ip.addr; + espconn_mdns_init(mdns_info); + } + else { + espconn_mdns_server_unregister(); + espconn_mdns_close(); + } + mdns_started = true; } // ===== wifi scanning //WiFi access point data typedef struct { - char ssid[32]; - sint8 rssi; - char enc; + char ssid[32]; + sint8 rssi; + char enc; } ApData; //Scan result typedef struct { - char scanInProgress; //if 1, don't access the underlying stuff from the webpage. - ApData **apData; - int noAps; + char scanInProgress; //if 1, don't access the underlying stuff from the webpage. + ApData **apData; + int noAps; } ScanResultData; //Static scan status storage. @@ -150,129 +159,129 @@ static ScanResultData cgiWifiAps; //Callback the code calls when a wlan ap scan is done. Basically stores the result in //the cgiWifiAps struct. void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { - int n; - struct bss_info *bss_link = (struct bss_info *)arg; - - if (status!=OK) { - DBG("wifiScanDoneCb status=%d\n", status); - cgiWifiAps.scanInProgress=0; - return; - } - - //Clear prev ap data if needed. - if (cgiWifiAps.apData!=NULL) { - for (n=0; nnext.stqe_next; - n++; - } - //Allocate memory for access point data - cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); - cgiWifiAps.noAps=n; - DBG("Scan done: found %d APs\n", 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. - DBG("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); - break; + int n; + struct bss_info *bss_link = (struct bss_info *)arg; + + if (status!=OK) { + DBG("wifiScanDoneCb status=%d\n", status); + cgiWifiAps.scanInProgress=0; + return; } - //Save the ap data. - cgiWifiAps.apData[n]=(ApData *)os_malloc(sizeof(ApData)); - cgiWifiAps.apData[n]->rssi=bss_link->rssi; - cgiWifiAps.apData[n]->enc=bss_link->authmode; - strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); - DBG("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi); - - bss_link = bss_link->next.stqe_next; - n++; - } - //We're done. - cgiWifiAps.scanInProgress=0; + + //Clear prev ap data if needed. + if (cgiWifiAps.apData!=NULL) { + for (n=0; nnext.stqe_next; + n++; + } + //Allocate memory for access point data + cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); + cgiWifiAps.noAps=n; + DBG("Scan done: found %d APs\n", 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. + DBG("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); + break; + } + //Save the ap data. + cgiWifiAps.apData[n]=(ApData *)os_malloc(sizeof(ApData)); + cgiWifiAps.apData[n]->rssi=bss_link->rssi; + cgiWifiAps.apData[n]->enc=bss_link->authmode; + strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); + DBG("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi); + + bss_link = bss_link->next.stqe_next; + n++; + } + //We're done. + cgiWifiAps.scanInProgress=0; } static ETSTimer scanTimer; static void ICACHE_FLASH_ATTR scanStartCb(void *arg) { - DBG("Starting a scan\n"); - wifi_station_scan(NULL, wifiScanDoneCb); + DBG("Starting a scan\n"); + wifi_station_scan(NULL, wifiScanDoneCb); } static int ICACHE_FLASH_ATTR cgiWiFiStartScan(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); - if (!cgiWifiAps.scanInProgress) { - cgiWifiAps.scanInProgress = 1; - os_timer_disarm(&scanTimer); - os_timer_setfn(&scanTimer, scanStartCb, NULL); - os_timer_arm(&scanTimer, 200, 0); - } - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); + if (!cgiWifiAps.scanInProgress) { + cgiWifiAps.scanInProgress = 1; + os_timer_disarm(&scanTimer); + os_timer_setfn(&scanTimer, scanStartCb, NULL); + os_timer_arm(&scanTimer, 200, 0); + } + return HTTPD_CGI_DONE; } static int ICACHE_FLASH_ATTR cgiWiFiGetScan(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - char buff[1460]; - const int chunk = 1460/64; // ssid is up to 32 chars - int len = 0; - - os_printf("GET scan: cgiData=%d noAps=%d\n", (int)connData->cgiData, cgiWifiAps.noAps); - - // handle continuation call, connData->cgiData-1 is the position in the scan results where we - // we need to continue sending from (using -1 'cause 0 means it's the first call) - if (connData->cgiData) { - int next = (int)connData->cgiData-1; - int pos = next; - while (pos < cgiWifiAps.noAps && pos < next+chunk) { - len += os_sprintf(buff+len, "{\"essid\": \"%s\", \"rssi\": %d, \"enc\": \"%d\"}%c\n", - cgiWifiAps.apData[pos]->ssid, cgiWifiAps.apData[pos]->rssi, cgiWifiAps.apData[pos]->enc, - (pos+1 == cgiWifiAps.noAps) ? ' ' : ','); - pos++; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + char buff[1460]; + const int chunk = 1460/64; // ssid is up to 32 chars + int len = 0; + + os_printf("GET scan: cgiData=%d noAps=%d\n", (int)connData->cgiData, cgiWifiAps.noAps); + + // handle continuation call, connData->cgiData-1 is the position in the scan results where we + // we need to continue sending from (using -1 'cause 0 means it's the first call) + if (connData->cgiData) { + int next = (int)connData->cgiData-1; + int pos = next; + while (pos < cgiWifiAps.noAps && pos < next+chunk) { + len += os_sprintf(buff+len, "{\"essid\": \"%s\", \"rssi\": %d, \"enc\": \"%d\"}%c\n", + cgiWifiAps.apData[pos]->ssid, cgiWifiAps.apData[pos]->rssi, cgiWifiAps.apData[pos]->enc, + (pos+1 == cgiWifiAps.noAps) ? ' ' : ','); + pos++; + } + // done or more? + if (pos == cgiWifiAps.noAps) { + len += os_sprintf(buff+len, "]}}\n"); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; + } else { + connData->cgiData = (void*)(pos+1); + httpdSend(connData, buff, len); + return HTTPD_CGI_MORE; + } } - // done or more? - if (pos == cgiWifiAps.noAps) { - len += os_sprintf(buff+len, "]}}\n"); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; - } else { - connData->cgiData = (void*)(pos+1); - httpdSend(connData, buff, len); - return HTTPD_CGI_MORE; + + jsonHeader(connData, 200); + + if (cgiWifiAps.scanInProgress==1) { + //We're still scanning. Tell Javascript code that. + len = os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n"); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } - } - - jsonHeader(connData, 200); - - if (cgiWifiAps.scanInProgress==1) { - //We're still scanning. Tell Javascript code that. - len = os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n"); + + len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); + connData->cgiData = (void *)1; // start with first result next time we're called httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; - } - - len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); - connData->cgiData = (void *)1; // start with first result next time we're called - httpdSend(connData, buff, len); - return HTTPD_CGI_MORE; + return HTTPD_CGI_MORE; } int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) { - if (connData->requestType == HTTPD_METHOD_GET) { - return cgiWiFiGetScan(connData); - } else if (connData->requestType == HTTPD_METHOD_POST) { - return cgiWiFiStartScan(connData); - } else { - jsonHeader(connData, 404); - return HTTPD_CGI_DONE; - } + if (connData->requestType == HTTPD_METHOD_GET) { + return cgiWiFiGetScan(connData); + } else if (connData->requestType == HTTPD_METHOD_POST) { + return cgiWiFiStartScan(connData); + } else { + jsonHeader(connData, 404); + return HTTPD_CGI_DONE; + } } // ===== timers to change state and rescue from failed associations @@ -285,235 +294,242 @@ static ETSTimer resetTimer; // the connect succeeds, this gets the module in STA-only mode. If it fails, it ensures // that the module is in STA+AP mode so the user has a chance to recover. static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { - int x = wifi_station_get_connect_status(); - int m = wifi_get_opmode() & 0x3; - DBG("Wifi check: mode=%s status=%d\n", wifiMode[m], x); - - if (x == STATION_GOT_IP) { - if (m != 1) { + int x = wifi_station_get_connect_status(); + int m = wifi_get_opmode() & 0x3; + DBG("Wifi check: mode=%s status=%d\n", wifiMode[m], x); + + if (x == STATION_GOT_IP) { + if (m != 1) { #ifdef CHANGE_TO_STA - // We're happily connected, go to STA mode - DBG("Wifi got IP. Going into STA mode..\n"); - wifi_set_opmode(1); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); // check one more time after switching to STA-only + // We're happily connected, go to STA mode + DBG("Wifi got IP. Going into STA mode..\n"); + wifi_set_opmode(1); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); // check one more time after switching to STA-only #endif + } + log_uart(false); + // no more resetTimer at this point, gotta use physical reset to recover if in trouble + } else { + + if (m != 3) { + DBG("Wifi connect failed. Going into STA+AP mode..\n"); + // Set STA+AP mode + wifi_set_opmode(3); +#ifdef AP_SSID + // Call ap set after mode change to AP or STA+AP + wifi_softap_set_config(&apconf); +#endif + } + log_uart(true); + DBG("Enabling/continuing uart log\n"); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } - log_uart(false); - // no more resetTimer at this point, gotta use physical reset to recover if in trouble - } else { - if (m != 3) { - DBG("Wifi connect failed. Going into STA+AP mode..\n"); - wifi_set_opmode(3); - } - log_uart(true); - DBG("Enabling/continuing uart log\n"); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); - } } -// Temp store for new ap info. -static struct station_config stconf; // Reassociate timer to delay change of association so the original request can finish static ETSTimer reassTimer; // Callback actually doing reassociation static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) { - DBG("Wifi changing association\n"); - wifi_station_disconnect(); - stconf.bssid_set = 0; - wifi_station_set_config(&stconf); - wifi_station_connect(); - // Schedule check, we give some extra time (4x) 'cause the reassociation can cause the AP - // to have to change channel, and then the client needs to follow before it can see the - // IP address - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, 4*RESET_TIMEOUT, 0); + DBG("Wifi changing association\n"); + wifi_station_disconnect(); + stconf.bssid_set = 0; + wifi_station_set_config(&stconf); + wifi_station_connect(); + // Schedule check, we give some extra time (4x) 'cause the reassociation can cause the AP + // to have to change channel, and then the client needs to follow before it can see the + // IP address + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, 4*RESET_TIMEOUT, 0); } // This cgi uses the routines above to connect to a specific access point with the // given ESSID using the given password. int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { - char essid[128]; - char passwd[128]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; - - int el = httpdFindArg(connData->getArgs, "essid", essid, sizeof(essid)); - int pl = httpdFindArg(connData->getArgs, "passwd", passwd, sizeof(passwd)); - - if (el > 0 && pl >= 0) { - //Set to 0 if you want to disable the actual reconnecting bit - os_strncpy((char*)stconf.ssid, essid, 32); - os_strncpy((char*)stconf.password, passwd, 64); - DBG("Wifi try to connect to AP %s pw %s\n", essid, passwd); - - //Schedule disconnect/connect - os_timer_disarm(&reassTimer); - os_timer_setfn(&reassTimer, reassTimerCb, NULL); - os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it - jsonHeader(connData, 200); - } else { - jsonHeader(connData, 400); - httpdSend(connData, "Cannot parse ssid or password", -1); - } - return HTTPD_CGI_DONE; + char essid[128]; + char passwd[128]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + int el = httpdFindArg(connData->getArgs, "essid", essid, sizeof(essid)); + int pl = httpdFindArg(connData->getArgs, "passwd", passwd, sizeof(passwd)); + + if (el > 0 && pl >= 0) { + //Set to 0 if you want to disable the actual reconnecting bit + os_strncpy((char*)stconf.ssid, essid, 32); + os_strncpy((char*)stconf.password, passwd, 64); + DBG("Wifi try to connect to AP %s pw %s\n", essid, passwd); + + //Schedule disconnect/connect + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, reassTimerCb, NULL); + os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it + jsonHeader(connData, 200); + } else { + jsonHeader(connData, 400); + httpdSend(connData, "Cannot parse ssid or password", -1); + } + return HTTPD_CGI_DONE; } static bool ICACHE_FLASH_ATTR parse_ip(char *buff, ip_addr_t *ip_ptr) { - char *next = buff; // where to start parsing next integer - int found = 0; // number of integers parsed - uint32_t ip = 0; // the ip addres parsed - for (int i=0; i<32; i++) { // 32 is just a safety limit - char c = buff[i]; - if (c == '.' || c == 0) { - // parse the preceding integer and accumulate into IP address - bool last = c == 0; - buff[i] = 0; - uint32_t v = atoi(next); - ip = ip | ((v&0xff)<<(found*8)); - next = buff+i+1; // next integer starts after the '.' - found++; - if (last) { // if at end of string we better got 4 integers - ip_ptr->addr = ip; - return found == 4; - } - continue; + char *next = buff; // where to start parsing next integer + int found = 0; // number of integers parsed + uint32_t ip = 0; // the ip addres parsed + for (int i=0; i<32; i++) { // 32 is just a safety limit + char c = buff[i]; + if (c == '.' || c == 0) { + // parse the preceding integer and accumulate into IP address + bool last = c == 0; + buff[i] = 0; + uint32_t v = atoi(next); + ip = ip | ((v&0xff)<<(found*8)); + next = buff+i+1; // next integer starts after the '.' + found++; + if (last) { // if at end of string we better got 4 integers + ip_ptr->addr = ip; + return found == 4; + } + continue; + } + if (c < '0' || c > '9') return false; } - if (c < '0' || c > '9') return false; - } - return false; + return false; } #ifdef DEBUGIP static void ICACHE_FLASH_ATTR debugIP() { - struct ip_info info; - if (wifi_get_ip_info(0, &info)) { - os_printf("\"ip\": \"%d.%d.%d.%d\"\n", IP2STR(&info.ip.addr)); - os_printf("\"netmask\": \"%d.%d.%d.%d\"\n", IP2STR(&info.netmask.addr)); - os_printf("\"gateway\": \"%d.%d.%d.%d\"\n", IP2STR(&info.gw.addr)); - os_printf("\"hostname\": \"%s\"\n", wifi_station_get_hostname()); - } else { - os_printf("\"ip\": \"-none-\"\n"); - } + struct ip_info info; + if (wifi_get_ip_info(0, &info)) { + os_printf("\"ip\": \"%d.%d.%d.%d\"\n", IP2STR(&info.ip.addr)); + os_printf("\"netmask\": \"%d.%d.%d.%d\"\n", IP2STR(&info.netmask.addr)); + os_printf("\"gateway\": \"%d.%d.%d.%d\"\n", IP2STR(&info.gw.addr)); + os_printf("\"hostname\": \"%s\"\n", wifi_station_get_hostname()); + } else { + os_printf("\"ip\": \"-none-\"\n"); + } } #endif // configure Wifi, specifically DHCP vs static IP address based on flash config void ICACHE_FLASH_ATTR configWifiIP() { - if (flashConfig.staticip == 0) { - // let's DHCP! - wifi_station_set_hostname(flashConfig.hostname); - if (wifi_station_dhcpc_status() == DHCP_STARTED) - wifi_station_dhcpc_stop(); - wifi_station_dhcpc_start(); - DBG("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname); - } else { - // no DHCP, we got static network config! - wifi_station_dhcpc_stop(); - struct ip_info ipi; - ipi.ip.addr = flashConfig.staticip; - ipi.netmask.addr = flashConfig.netmask; - ipi.gw.addr = flashConfig.gateway; - wifi_set_ip_info(0, &ipi); - DBG("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr)); - } + if (flashConfig.staticip == 0) { + // let's DHCP! + wifi_station_set_hostname(flashConfig.hostname); + if (wifi_station_dhcpc_status() == DHCP_STARTED) + wifi_station_dhcpc_stop(); + wifi_station_dhcpc_start(); + DBG("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname); + } else { + // no DHCP, we got static network config! + wifi_station_dhcpc_stop(); + struct ip_info ipi; + ipi.ip.addr = flashConfig.staticip; + ipi.netmask.addr = flashConfig.netmask; + ipi.gw.addr = flashConfig.gateway; + wifi_set_ip_info(0, &ipi); + DBG("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr)); + } #ifdef DEBUGIP - debugIP(); + debugIP(); #endif } // Change special settings int ICACHE_FLASH_ATTR cgiWiFiSpecial(HttpdConnData *connData) { - char dhcp[8]; - char staticip[20]; - char netmask[20]; - char gateway[20]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; - - // get args and their string lengths - int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); - int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); - int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); - int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); - - if (!(dl > 0 && sl >= 0 && nl >= 0 && gl >= 0)) { - jsonHeader(connData, 400); - httpdSend(connData, "Request is missing fields", -1); - return HTTPD_CGI_DONE; - } - - char url[64]; // redirect URL - if (os_strcmp(dhcp, "off") == 0) { - // parse static IP params - struct ip_info ipi; - bool ok = parse_ip(staticip, &ipi.ip); - if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); - else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); - if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); - else ipi.gw.addr = 0; - if (!ok) { - jsonHeader(connData, 400); - httpdSend(connData, "Cannot parse static IP config", -1); - return HTTPD_CGI_DONE; + char dhcp[8]; + char staticip[20]; + char netmask[20]; + char gateway[20]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + // get args and their string lengths + int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); + int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); + int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); + int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); + + if (!(dl > 0 && sl >= 0 && nl >= 0 && gl >= 0)) { + jsonHeader(connData, 400); + httpdSend(connData, "Request is missing fields", -1); + return HTTPD_CGI_DONE; } - // save the params in flash - flashConfig.staticip = ipi.ip.addr; - flashConfig.netmask = ipi.netmask.addr; - flashConfig.gateway = ipi.gw.addr; - // construct redirect URL - os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); - - } else { - // dynamic IP - flashConfig.staticip = 0; - os_sprintf(url, "{\"url\": \"http://%s\"}", flashConfig.hostname); - } - - configSave(); // ignore error... - // schedule change-over - os_timer_disarm(&reassTimer); - os_timer_setfn(&reassTimer, configWifiIP, NULL); - os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it - // return redirect info - jsonHeader(connData, 200); - httpdSend(connData, url, -1); - return HTTPD_CGI_DONE; + + char url[64]; // redirect URL + if (os_strcmp(dhcp, "off") == 0) { + // parse static IP params + struct ip_info ipi; + bool ok = parse_ip(staticip, &ipi.ip); + if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); + else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); + if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); + else ipi.gw.addr = 0; + if (!ok) { + jsonHeader(connData, 400); + httpdSend(connData, "Cannot parse static IP config", -1); + return HTTPD_CGI_DONE; + } + // save the params in flash + flashConfig.staticip = ipi.ip.addr; + flashConfig.netmask = ipi.netmask.addr; + flashConfig.gateway = ipi.gw.addr; + // construct redirect URL + os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); + + } else { + // dynamic IP + flashConfig.staticip = 0; + os_sprintf(url, "{\"url\": \"http://%s\"}", flashConfig.hostname); + } + + configSave(); // ignore error... + // schedule change-over + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, configWifiIP, NULL); + os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it + // return redirect info + jsonHeader(connData, 200); + httpdSend(connData, url, -1); + return HTTPD_CGI_DONE; } //This cgi changes the operating mode: STA / AP / STA+AP int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) { - int len; - char buff[1024]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - - len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); - if (len!=0) { - int m = atoi(buff); - DBG("Wifi switching to mode %d\n", m); - wifi_set_opmode(m&3); - if (m == 1) { - // STA-only mode, reset into STA+AP after a timeout if we don't get an IP address - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + int len; + char buff[1024]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + + len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); + if (len!=0) { + int m = atoi(buff); + DBG("Wifi switching to mode %d\n", m); + wifi_set_opmode(m&3); + // Call softap set after changing to AP or STA+AP + wifi_softap_set_config(&apconf); + + if (m == 1) { + // STA-only mode, reset into STA+AP after a timeout if we don't get an IP address + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + } + jsonHeader(connData, 200); + } else { + jsonHeader(connData, 400); } - jsonHeader(connData, 200); - } else { - jsonHeader(connData, 400); - } - return HTTPD_CGI_DONE; + return HTTPD_CGI_DONE; } static char *connStatuses[] = { "idle", "connecting", "wrong password", "AP not found", - "failed", "got IP address" }; + "failed", "got IP address" }; static char *wifiWarn[] = { 0, - "Switch to STA+AP mode", - "Can't scan in this mode! Switch to STA+AP mode", - "Switch to STA mode", + "Switch to STA+AP mode", + "Can't scan in this mode! Switch to STA+AP mode", + "Switch to STA mode", }; #ifdef CHANGE_TO_STA @@ -524,114 +540,180 @@ static char *wifiWarn[] = { 0, // print various Wifi information into json buffer int ICACHE_FLASH_ATTR printWifiInfo(char *buff) { - int len; - - struct station_config stconf; - wifi_station_get_config(&stconf); - - uint8_t op = wifi_get_opmode() & 0x3; - char *mode = wifiMode[op]; - char *status = "unknown"; - int st = wifi_station_get_connect_status(); - if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st]; - int p = wifi_get_phy_mode(); - char *phy = wifiPhy[p&3]; - char *warn = wifiWarn[op]; - sint8 rssi = wifi_station_get_rssi(); - if (rssi > 0) rssi = 0; - uint8 mac_addr[6]; - wifi_get_macaddr(0, mac_addr); - uint8_t chan = wifi_get_channel(); - - len = os_sprintf(buff, - "\"mode\": \"%s\", \"modechange\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", " - "\"rssi\": \"%ddB\", \"warn\": \"%s\", \"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\", \"chan\":%d", - mode, MODECHANGE, (char*)stconf.ssid, status, phy, rssi, warn, - mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], chan); - - struct ip_info info; - if (wifi_get_ip_info(0, &info)) { - len += os_sprintf(buff+len, ", \"ip\": \"%d.%d.%d.%d\"", IP2STR(&info.ip.addr)); - len += os_sprintf(buff+len, ", \"netmask\": \"%d.%d.%d.%d\"", IP2STR(&info.netmask.addr)); - len += os_sprintf(buff+len, ", \"gateway\": \"%d.%d.%d.%d\"", IP2STR(&info.gw.addr)); - len += os_sprintf(buff+len, ", \"hostname\": \"%s\"", flashConfig.hostname); - } else { - len += os_sprintf(buff+len, ", \"ip\": \"-none-\""); - } - len += os_sprintf(buff+len, ", \"staticip\": \"%d.%d.%d.%d\"", IP2STR(&flashConfig.staticip)); - len += os_sprintf(buff+len, ", \"dhcp\": \"%s\"", flashConfig.staticip > 0 ? "off" : "on"); - - return len; + int len; + + struct station_config stconf; + wifi_station_get_config(&stconf); + + uint8_t op = wifi_get_opmode() & 0x3; + char *mode = wifiMode[op]; + char *status = "unknown"; + int st = wifi_station_get_connect_status(); + if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st]; + int p = wifi_get_phy_mode(); + char *phy = wifiPhy[p&3]; + char *warn = wifiWarn[op]; + sint8 rssi = wifi_station_get_rssi(); + if (rssi > 0) rssi = 0; + uint8 mac_addr[6]; + wifi_get_macaddr(0, mac_addr); + uint8_t chan = wifi_get_channel(); + + len = os_sprintf(buff, + "\"mode\": \"%s\", \"modechange\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", " + "\"rssi\": \"%ddB\", \"warn\": \"%s\", \"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\", \"chan\":%d", + mode, MODECHANGE, (char*)stconf.ssid, status, phy, rssi, warn, + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], chan); + + struct ip_info info; + if (wifi_get_ip_info(0, &info)) { + len += os_sprintf(buff+len, ", \"ip\": \"%d.%d.%d.%d\"", IP2STR(&info.ip.addr)); + len += os_sprintf(buff+len, ", \"netmask\": \"%d.%d.%d.%d\"", IP2STR(&info.netmask.addr)); + len += os_sprintf(buff+len, ", \"gateway\": \"%d.%d.%d.%d\"", IP2STR(&info.gw.addr)); + len += os_sprintf(buff+len, ", \"hostname\": \"%s\"", flashConfig.hostname); + } else { + len += os_sprintf(buff+len, ", \"ip\": \"-none-\""); + } + len += os_sprintf(buff+len, ", \"staticip\": \"%d.%d.%d.%d\"", IP2STR(&flashConfig.staticip)); + len += os_sprintf(buff+len, ", \"dhcp\": \"%s\"", flashConfig.staticip > 0 ? "off" : "on"); + + return len; } int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) { - char buff[1024]; - int len; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); - - len = os_sprintf(buff, "{"); - len += printWifiInfo(buff+len); - len += os_sprintf(buff+len, ", "); - - if (wifiReason != 0) { - len += os_sprintf(buff+len, "\"reason\": \"%s\", ", wifiGetReason()); - } - + char buff[1024]; + int len; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); + + len = os_sprintf(buff, "{"); + len += printWifiInfo(buff+len); + len += os_sprintf(buff+len, ", "); + + if (wifiReason != 0) { + len += os_sprintf(buff+len, "\"reason\": \"%s\", ", wifiGetReason()); + } + #if 0 - // commented out 'cause often the client that requested the change can't get a request in to - // find out that it succeeded. Better to just wait the std 15 seconds... - int st=wifi_station_get_connect_status(); - if (st == STATION_GOT_IP) { - if (wifi_get_opmode() != 1) { - // Reset into AP-only mode sooner. - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, 1000, 0); + // commented out 'cause often the client that requested the change can't get a request in to + // find out that it succeeded. Better to just wait the std 15 seconds... + int st=wifi_station_get_connect_status(); + if (st == STATION_GOT_IP) { + if (wifi_get_opmode() != 1) { + // Reset into AP-only mode sooner. + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, 1000, 0); + } } - } #endif - - len += os_sprintf(buff+len, "\"x\":0}\n"); - //DBG(" -> %s\n", buff); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + + len += os_sprintf(buff+len, "\"x\":0}\n"); + //DBG(" -> %s\n", buff); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } // Cgi to return various Wifi information int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) { - char buff[1024]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - - os_strcpy(buff, "{"); - printWifiInfo(buff+1); - os_strcat(buff, "}"); - - jsonHeader(connData, 200); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; + char buff[1024]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + + os_strcpy(buff, "{"); + printWifiInfo(buff+1); + os_strcat(buff, "}"); + + jsonHeader(connData, 200); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } // Init the wireless, which consists of setting a timer if we expect to connect to an AP // so we can revert to STA+AP mode if we can't connect. void ICACHE_FLASH_ATTR wifiInit() { - // wifi_set_phy_mode(2); // limit to 802.11b/g 'cause n is flaky - int x = wifi_get_opmode() & 0x3; - x = x; - DBG("Wifi init, mode=%s\n", wifiMode[x]); - configWifiIP(); - - // The default sleep mode should be modem_sleep, but we set it here explicitly for good - // measure. We can't use light_sleep because that powers off everthing and we would loose - // all connections. - wifi_set_sleep_type(MODEM_SLEEP_T); - - wifi_set_event_handler_cb(wifiHandleEventCb); - // check on the wifi in a few seconds to see whether we need to switch mode - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + + // Check te wifi opmode + int x = wifi_get_opmode() & 0x3; + +#ifdef CGIWIFI_DBG + os_printf("Wifi init, mode=%s\n",wifiMode[x]); +#endif + + // STATION parameters only on a full flash +#if defined(STA_SSID) && defined(STA_PASS) + if( x == 2 ){ + // Create struct for station config + struct station_config stconf; + // Call it + wifi_station_get_config(&stconf); + // Set parameters + if (os_strlen((char*)stconf.ssid) == 0 && os_strlen((char*)stconf.password) == 0) { + os_strncpy((char*)stconf.ssid, VERS_STR(STA_SSID), 32); + os_strncpy((char*)stconf.password, VERS_STR(STA_PASS), 64); +#ifdef CGIWIFI_DBG + os_printf("Wifi pre-config trying to connect to AP %s pw %s\n",(char*)stconf.ssid, (char*)stconf.password); +#endif + // wifi_set_phy_mode(2); // limit to 802.11b/g 'cause n is flaky + // Set wifi mode + wifi_set_opmode(3); // sta+ap, will switch to sta-only 15 secs after connecting + stconf.bssid_set = 0; + wifi_station_set_config(&stconf); + } + } +#endif + + // SOFT_AP parameters +#if defined(AP_SSID) && defined(AP_PASS) + // Call it + wifi_softap_get_config(&apconf); + // Clean memory and set the value of SSID + memset(apconf.ssid, 0, sizeof(apconf.ssid)); + os_memcpy(apconf.ssid, VERS_STR(AP_SSID), strlen(VERS_STR(AP_SSID))); + // Clean memory and set the value of PASS + memset(apconf.password, 0, sizeof(apconf.password)); + os_memcpy(apconf.password, VERS_STR(AP_PASS), strlen(VERS_STR(AP_PASS))); + // Specify the length of pass + apconf.ssid_len= os_strlen((char*)VERS_STR(AP_PASS)); +#ifdef AP_AUTH_MODE + // If set, use specified auth mode + apconf.authmode = AP_AUTH_MODE; +#else + // If not, use wpa wpa2 psk + apconf.authmode = AUTH_WPA_WPA2_PSK; +#endif +#ifdef AP_SSID_HIDDEN + // If set, use specified ssid hidden parameter + apconf.ssid_hidden = AP_SSID_HIDDEN; +#endif +#ifdef AP_MAX_CONN + // If set, use specified max conn number + apconf.max_connection = AP_MAX_CONN; +#endif +#ifdef AP_BEACON_INTERVAL + // If set use specified beacon interval + apconf.beacon_interval = AP_BEACON_INTERVAL; +#endif + // Set to use the new conf +#ifdef CGIWIFI_DBG + os_printf("Wifi AP parameters: %s pw %s\n",(char*)apconf.ssid, (char*)apconf.password); +#endif + // MUST BE called after enabling AP or STA+AP mode + wifi_softap_set_config(&apconf); +#endif // AP_SSID && AP_PASS + + configWifiIP(); + + // The default sleep mode should be modem_sleep, but we set it here explicitly for good + // measure. We can't use light_sleep because that powers off everthing and we would loose + // all connections. + wifi_set_sleep_type(MODEM_SLEEP_T); + + wifi_set_event_handler_cb(wifiHandleEventCb); + // check on the wifi in a few seconds to see whether we need to switch mode + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } diff --git a/esp-link/main.c b/esp-link/main.c index 09904e1..bfbea0f 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -123,28 +123,6 @@ void user_init(void) { os_printf("\n\n** %s\n", esp_link_version); os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); -#if defined(STA_SSID) && defined(STA_PASS) - int x = wifi_get_opmode() & 0x3; - if (x == 2) { - // we only force the STA settings when a full flash of the module has been made, which - // resets the wifi settings not to have anything configured - struct station_config stconf; - wifi_station_get_config(&stconf); - - if (os_strlen((char*)stconf.ssid) == 0 && os_strlen((char*)stconf.password) == 0) { - os_strncpy((char*)stconf.ssid, VERS_STR(STA_SSID), 32); - os_strncpy((char*)stconf.password, VERS_STR(STA_PASS), 64); -#ifdef CGIWIFI_DBG - os_printf("Wifi pre-config trying to connect to AP %s pw %s\n", - (char*)stconf.ssid, (char*)stconf.password); -#endif - wifi_set_opmode(3); // sta+ap, will switch to sta-only 15 secs after connecting - stconf.bssid_set = 0; - wifi_station_set_config(&stconf); - } - } -#endif - // Status LEDs statusInit(); serledInit(); From 896693b5b2173379e5e43f9189d9d5d19e5afada Mon Sep 17 00:00:00 2001 From: KatAst Date: Sun, 27 Dec 2015 13:11:03 +0100 Subject: [PATCH 02/37] Soft-AP Settings Added Soft-AP settings support both in Makefile and web interface --- Makefile | 12 +- esp-link/cgi.c | 5 +- esp-link/cgiwifi.c | 1155 ++++++++++++++----------- esp-link/cgiwifi.h | 2 + esp-link/main.c | 20 +- html/wifi/wifiAp.html | 124 +++ html/wifi/wifiAp.js | 98 +++ html/wifi/{wifi.html => wifiSta.html} | 8 +- html/wifi/{wifi.js => wifiSta.js} | 0 9 files changed, 918 insertions(+), 506 deletions(-) create mode 100644 html/wifi/wifiAp.html create mode 100644 html/wifi/wifiAp.js rename html/wifi/{wifi.html => wifiSta.html} (96%) rename html/wifi/{wifi.js => wifiSta.js} (100%) diff --git a/Makefile b/Makefile index 95e2b82..98660ae 100644 --- a/Makefile +++ b/Makefile @@ -35,12 +35,12 @@ # Max connections default 4, ( 1 ~ 4 ) # Beacon interval default 100, ( 100 ~ 60000ms ) -AP_SSID ?=esp-link-test -AP_PASS ?=esp-link-test -# AP_AUTH_MODE ?=AUTH_WPA_WPA2_PSK -# AP_SSID_HIDDEN ?=0 -# AP_MAX_CONN ?=3 -# AP_BEACON_INTERVAL ?=150 +# AP_SSID ?= +# AP_PASS ?= +# AP_AUTH_MODE ?= +# AP_SSID_HIDDEN ?= +# AP_MAX_CONN ?= +# AP_BEACON_INTERVAL ?= # If CHANGE_TO_STA is set to "yes" the esp-link module will switch to station mode diff --git a/esp-link/cgi.c b/esp-link/cgi.c index 081ef49..c658fb6 100644 --- a/esp-link/cgi.c +++ b/esp-link/cgi.c @@ -106,7 +106,7 @@ int8_t ICACHE_FLASH_ATTR getUInt16Arg(HttpdConnData *connData, char *name, uint1 return 1; } -int8_t ICACHE_FLASH_ATTR getBoolArg(HttpdConnData *connData, char *name, bool *config) { +int8_t ICACHE_FLASH_ATTR getBoolArg(HttpdConnData *connData, char *name, uint8_t *config) { char buff[16]; int len = httpdFindArg(connData->getArgs, name, buff, sizeof(buff)); if (len < 0) return 0; // not found, skip @@ -206,7 +206,8 @@ int ICACHE_FLASH_ATTR cgiMenu(HttpdConnData *connData) { "{ " "\"menu\": [ " "\"Home\", \"/home.html\", " - "\"WiFI\", \"/wifi/wifi.html\", " + "\"WiFi Station\", \"/wifi/wifiSta.html\", " + "\"WiFi Soft-AP\", \"/wifi/wifiAp.html\", " "\"µC Console\", \"/console.html\", " "\"Services\", \"/services.html\", " #ifdef MQTT diff --git a/esp-link/cgiwifi.c b/esp-link/cgiwifi.c index cf36135..be63d85 100644 --- a/esp-link/cgiwifi.c +++ b/esp-link/cgiwifi.c @@ -1,7 +1,3 @@ -/* - Cgi/template routines for the /wifi url. - */ - /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): @@ -11,6 +7,8 @@ * ---------------------------------------------------------------------------- * Heavily modified and enhanced by Thorsten von Eicken in 2015 * ---------------------------------------------------------------------------- + * Once again heavily modified to add soft-ap settings by KatAst in 2015 + * ---------------------------------------------------------------------------- */ #include @@ -34,22 +32,23 @@ bool mdns_started = false; // ===== wifi status change callbacks static WifiStateChangeCb wifi_state_change_cb[4]; -// Temp store for new ap info. -static struct station_config stconf; +// Temp store for new staion config +struct station_config stconf; // Temp store for new ap config -static struct softap_config apconf; +struct softap_config apconf; uint8_t wifiState = wifiIsDisconnected; + // reasons for which a connection failed uint8_t wifiReason = 0; static char *wifiReasons[] = { - "", "unspecified", "auth_expire", "auth_leave", "assoc_expire", "assoc_toomany", "not_authed", - "not_assoced", "assoc_leave", "assoc_not_authed", "disassoc_pwrcap_bad", "disassoc_supchan_bad", - "ie_invalid", "mic_failure", "4way_handshake_timeout", "group_key_update_timeout", - "ie_in_4way_differs", "group_cipher_invalid", "pairwise_cipher_invalid", "akmp_invalid", - "unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected", - "beacon_timeout", "no_ap_found" }; + "", "unspecified", "auth_expire", "auth_leave", "assoc_expire", "assoc_toomany", "not_authed", + "not_assoced", "assoc_leave", "assoc_not_authed", "disassoc_pwrcap_bad", "disassoc_supchan_bad", + "ie_invalid", "mic_failure", "4way_handshake_timeout", "group_key_update_timeout", + "ie_in_4way_differs", "group_cipher_invalid", "pairwise_cipher_invalid", "akmp_invalid", + "unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected", + "beacon_timeout", "no_ap_found" }; static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; @@ -57,100 +56,100 @@ static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; void (*wifiStatusCb)(uint8_t); // callback when wifi status changes static char* ICACHE_FLASH_ATTR wifiGetReason(void) { - if (wifiReason <= 24) return wifiReasons[wifiReason]; - if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24]; - return wifiReasons[1]; + if (wifiReason <= 24) return wifiReasons[wifiReason]; + if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24]; + return wifiReasons[1]; } // handler for wifi status change callback coming in from espressif library static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) { - switch (evt->event) { - case EVENT_STAMODE_CONNECTED: - wifiState = wifiIsConnected; - wifiReason = 0; - DBG("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid, - evt->event_info.connected.channel); - statusWifiUpdate(wifiState); - break; - case EVENT_STAMODE_DISCONNECTED: - wifiState = wifiIsDisconnected; - wifiReason = evt->event_info.disconnected.reason; - DBG("Wifi disconnected from ssid %s, reason %s (%d)\n", - evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); - statusWifiUpdate(wifiState); - break; - case EVENT_STAMODE_AUTHMODE_CHANGE: - DBG("Wifi auth mode: %d -> %d\n", - evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); - break; - case EVENT_STAMODE_GOT_IP: - wifiState = wifiGotIP; - wifiReason = 0; - DBG("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", - IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), - IP2STR(&evt->event_info.got_ip.gw)); - statusWifiUpdate(wifiState); - if (!mdns_started) - wifiStartMDNS(evt->event_info.got_ip.ip); - break; - case EVENT_SOFTAPMODE_STACONNECTED: - DBG("Wifi AP: station " MACSTR " joined, AID = %d\n", - MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); - break; - case EVENT_SOFTAPMODE_STADISCONNECTED: - DBG("Wifi AP: station " MACSTR " left, AID = %d\n", - MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); - break; - default: - break; - } - - for (int i = 0; i < 4; i++) { - if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState); - } + switch (evt->event) { + case EVENT_STAMODE_CONNECTED: + wifiState = wifiIsConnected; + wifiReason = 0; + DBG("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid, + evt->event_info.connected.channel); + statusWifiUpdate(wifiState); + break; + case EVENT_STAMODE_DISCONNECTED: + wifiState = wifiIsDisconnected; + wifiReason = evt->event_info.disconnected.reason; + DBG("Wifi disconnected from ssid %s, reason %s (%d)\n", + evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); + statusWifiUpdate(wifiState); + break; + case EVENT_STAMODE_AUTHMODE_CHANGE: + DBG("Wifi auth mode: %d -> %d\n", + evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); + break; + case EVENT_STAMODE_GOT_IP: + wifiState = wifiGotIP; + wifiReason = 0; + DBG("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", + IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), + IP2STR(&evt->event_info.got_ip.gw)); + statusWifiUpdate(wifiState); + if (!mdns_started) + wifiStartMDNS(evt->event_info.got_ip.ip); + break; + case EVENT_SOFTAPMODE_STACONNECTED: + DBG("Wifi AP: station " MACSTR " joined, AID = %d\n", + MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); + break; + case EVENT_SOFTAPMODE_STADISCONNECTED: + DBG("Wifi AP: station " MACSTR " left, AID = %d\n", + MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); + break; + default: + break; + } + + for (int i = 0; i < 4; i++) { + if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState); + } } void ICACHE_FLASH_ATTR wifiAddStateChangeCb(WifiStateChangeCb cb) { - for (int i = 0; i < 4; i++) { - if (wifi_state_change_cb[i] == cb) return; - if (wifi_state_change_cb[i] == NULL) { - wifi_state_change_cb[i] = cb; - return; - } + for (int i = 0; i < 4; i++) { + if (wifi_state_change_cb[i] == cb) return; + if (wifi_state_change_cb[i] == NULL) { + wifi_state_change_cb[i] = cb; + return; } - DBG("WIFI: max state change cb count exceeded\n"); + } + DBG("WIFI: max state change cb count exceeded\n"); } void ICACHE_FLASH_ATTR wifiStartMDNS(struct ip_addr ip) { - if (flashConfig.mdns_enable) { - struct mdns_info *mdns_info = (struct mdns_info *)os_zalloc(sizeof(struct mdns_info)); - mdns_info->host_name = flashConfig.hostname; - mdns_info->server_name = flashConfig.mdns_servername; - mdns_info->server_port = 80; - mdns_info->ipAddr = ip.addr; - espconn_mdns_init(mdns_info); - } - else { - espconn_mdns_server_unregister(); - espconn_mdns_close(); - } - mdns_started = true; + if (flashConfig.mdns_enable) { + struct mdns_info *mdns_info = (struct mdns_info *)os_zalloc(sizeof(struct mdns_info)); + mdns_info->host_name = flashConfig.hostname; + mdns_info->server_name = flashConfig.mdns_servername; + mdns_info->server_port = 80; + mdns_info->ipAddr = ip.addr; + espconn_mdns_init(mdns_info); + } + else { + espconn_mdns_server_unregister(); + espconn_mdns_close(); + } + mdns_started = true; } // ===== wifi scanning //WiFi access point data typedef struct { - char ssid[32]; - sint8 rssi; - char enc; + char ssid[32]; + sint8 rssi; + char enc; } ApData; //Scan result typedef struct { - char scanInProgress; //if 1, don't access the underlying stuff from the webpage. - ApData **apData; - int noAps; + char scanInProgress; //if 1, don't access the underlying stuff from the webpage. + ApData **apData; + int noAps; } ScanResultData; //Static scan status storage. @@ -159,126 +158,135 @@ static ScanResultData cgiWifiAps; //Callback the code calls when a wlan ap scan is done. Basically stores the result in //the cgiWifiAps struct. void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { - int n; - struct bss_info *bss_link = (struct bss_info *)arg; - - if (status!=OK) { - DBG("wifiScanDoneCb status=%d\n", status); - cgiWifiAps.scanInProgress=0; - return; - } - - //Clear prev ap data if needed. - if (cgiWifiAps.apData!=NULL) { - for (n=0; nnext.stqe_next; - n++; - } - //Allocate memory for access point data - cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); - cgiWifiAps.noAps=n; - DBG("Scan done: found %d APs\n", 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. - DBG("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); - break; - } - //Save the ap data. - cgiWifiAps.apData[n]=(ApData *)os_malloc(sizeof(ApData)); - cgiWifiAps.apData[n]->rssi=bss_link->rssi; - cgiWifiAps.apData[n]->enc=bss_link->authmode; - strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); - DBG("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi); - - bss_link = bss_link->next.stqe_next; - n++; - } - //We're done. + int n; + struct bss_info *bss_link = (struct bss_info *)arg; + + if (status!=OK) { + DBG("wifiScanDoneCb status=%d\n", status); cgiWifiAps.scanInProgress=0; + return; + } + + //Clear prev ap data if needed. + if (cgiWifiAps.apData!=NULL) { + for (n=0; nnext.stqe_next; + n++; + } + //Allocate memory for access point data + cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); + cgiWifiAps.noAps=n; + DBG("Scan done: found %d APs\n", 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. + DBG("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps); + break; + } + //Save the ap data. + cgiWifiAps.apData[n]=(ApData *)os_malloc(sizeof(ApData)); + cgiWifiAps.apData[n]->rssi=bss_link->rssi; + cgiWifiAps.apData[n]->enc=bss_link->authmode; + strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); + DBG("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi); + + bss_link = bss_link->next.stqe_next; + n++; + } + //We're done. + cgiWifiAps.scanInProgress=0; } static ETSTimer scanTimer; static void ICACHE_FLASH_ATTR scanStartCb(void *arg) { - DBG("Starting a scan\n"); - wifi_station_scan(NULL, wifiScanDoneCb); + DBG("Starting a scan\n"); + wifi_station_scan(NULL, wifiScanDoneCb); } static int ICACHE_FLASH_ATTR cgiWiFiStartScan(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); - if (!cgiWifiAps.scanInProgress) { - cgiWifiAps.scanInProgress = 1; - os_timer_disarm(&scanTimer); - os_timer_setfn(&scanTimer, scanStartCb, NULL); - os_timer_arm(&scanTimer, 200, 0); - } - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); + if (!cgiWifiAps.scanInProgress) { + cgiWifiAps.scanInProgress = 1; + os_timer_disarm(&scanTimer); + os_timer_setfn(&scanTimer, scanStartCb, NULL); + os_timer_arm(&scanTimer, 200, 0); + } + return HTTPD_CGI_DONE; } static int ICACHE_FLASH_ATTR cgiWiFiGetScan(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - char buff[1460]; - const int chunk = 1460/64; // ssid is up to 32 chars - int len = 0; - - os_printf("GET scan: cgiData=%d noAps=%d\n", (int)connData->cgiData, cgiWifiAps.noAps); - - // handle continuation call, connData->cgiData-1 is the position in the scan results where we - // we need to continue sending from (using -1 'cause 0 means it's the first call) - if (connData->cgiData) { - int next = (int)connData->cgiData-1; - int pos = next; - while (pos < cgiWifiAps.noAps && pos < next+chunk) { - len += os_sprintf(buff+len, "{\"essid\": \"%s\", \"rssi\": %d, \"enc\": \"%d\"}%c\n", - cgiWifiAps.apData[pos]->ssid, cgiWifiAps.apData[pos]->rssi, cgiWifiAps.apData[pos]->enc, - (pos+1 == cgiWifiAps.noAps) ? ' ' : ','); - pos++; - } - // done or more? - if (pos == cgiWifiAps.noAps) { - len += os_sprintf(buff+len, "]}}\n"); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; - } else { - connData->cgiData = (void*)(pos+1); - httpdSend(connData, buff, len); - return HTTPD_CGI_MORE; - } + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + char buff[1460]; + const int chunk = 1460/64; // ssid is up to 32 chars + int len = 0; + + os_printf("GET scan: cgiData=%d noAps=%d\n", (int)connData->cgiData, cgiWifiAps.noAps); + + // handle continuation call, connData->cgiData-1 is the position in the scan results where we + // we need to continue sending from (using -1 'cause 0 means it's the first call) + if (connData->cgiData) { + int next = (int)connData->cgiData-1; + int pos = next; + while (pos < cgiWifiAps.noAps && pos < next+chunk) { + len += os_sprintf(buff+len, "{\"essid\": \"%s\", \"rssi\": %d, \"enc\": \"%d\"}%c\n", + cgiWifiAps.apData[pos]->ssid, cgiWifiAps.apData[pos]->rssi, cgiWifiAps.apData[pos]->enc, + (pos+1 == cgiWifiAps.noAps) ? ' ' : ','); + pos++; } - - jsonHeader(connData, 200); - - if (cgiWifiAps.scanInProgress==1) { - //We're still scanning. Tell Javascript code that. - len = os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n"); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + // done or more? + if (pos == cgiWifiAps.noAps) { + len += os_sprintf(buff+len, "]}}\n"); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; + } else { + connData->cgiData = (void*)(pos+1); + httpdSend(connData, buff, len); + return HTTPD_CGI_MORE; } - - len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); - connData->cgiData = (void *)1; // start with first result next time we're called + } + + jsonHeader(connData, 200); + + if (cgiWifiAps.scanInProgress==1) { + //We're still scanning. Tell Javascript code that. + len = os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n"); httpdSend(connData, buff, len); - return HTTPD_CGI_MORE; + return HTTPD_CGI_DONE; + } + + len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); + connData->cgiData = (void *)1; // start with first result next time we're called + httpdSend(connData, buff, len); + return HTTPD_CGI_MORE; } int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) { + if (connData->requestType == HTTPD_METHOD_GET) { return cgiWiFiGetScan(connData); - } else if (connData->requestType == HTTPD_METHOD_POST) { - return cgiWiFiStartScan(connData); - } else { + }else if(connData->requestType == HTTPD_METHOD_POST) + { + // DO NOT start APs scan in AP mode + int mode = wifi_get_opmode(); + if(mode==2){ + jsonHeader(connData, 400); + return HTTPD_CGI_DONE; + }else{ + return cgiWiFiStartScan(connData); + } + }else{ jsonHeader(connData, 404); return HTTPD_CGI_DONE; } @@ -290,39 +298,32 @@ int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) { #define RESET_TIMEOUT (15000) // 15 seconds static ETSTimer resetTimer; -// 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. If it fails, it ensures -// that the module is in STA+AP mode so the user has a chance to recover. +/* + * This routine is ran some time after a connection attempt to an access point. + * If the connection succeeds and Makefile hard-coded CHANGE_TO_STA is set to yes, this gets the module in STA-only mode. + * If it fails and only STA mode is set, it ensures that the module is in STA+AP mode so the user has a chance to recover. + */ static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { + int x = wifi_station_get_connect_status(); int m = wifi_get_opmode() & 0x3; DBG("Wifi check: mode=%s status=%d\n", wifiMode[m], x); - if (x == STATION_GOT_IP) { - if (m != 1) { -#ifdef CHANGE_TO_STA - // We're happily connected, go to STA mode - DBG("Wifi got IP. Going into STA mode..\n"); - wifi_set_opmode(1); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); // check one more time after switching to STA-only -#endif - } - log_uart(false); - // no more resetTimer at this point, gotta use physical reset to recover if in trouble - } else { - - if (m != 3) { - DBG("Wifi connect failed. Going into STA+AP mode..\n"); - // Set STA+AP mode - wifi_set_opmode(3); -#ifdef AP_SSID - // Call ap set after mode change to AP or STA+AP - wifi_softap_set_config(&apconf); -#endif + if(m!=2){ + if( x == STATION_GOT_IP ){ + #ifdef CHANGE_TO_STA + wifi_set_opmode(1); + #endif + log_uart(false); + }else{ + log_uart(true); + DBG("Enabling/continuing uart log\n"); + if (m==1){ + wifi_set_opmode(3); + wifi_softap_set_config(&apconf); + } + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } - log_uart(true); - DBG("Enabling/continuing uart log\n"); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } } @@ -331,191 +332,347 @@ static ETSTimer reassTimer; // Callback actually doing reassociation static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) { - DBG("Wifi changing association\n"); - wifi_station_disconnect(); - stconf.bssid_set = 0; - wifi_station_set_config(&stconf); - wifi_station_connect(); - // Schedule check, we give some extra time (4x) 'cause the reassociation can cause the AP - // to have to change channel, and then the client needs to follow before it can see the - // IP address - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, 4*RESET_TIMEOUT, 0); + DBG("Wifi changing association\n"); + wifi_station_disconnect(); + stconf.bssid_set = 0; + wifi_station_set_config(&stconf); + wifi_station_connect(); + // Schedule check, we give some extra time (4x) 'cause the reassociation can cause the AP + // to have to change channel, and then the client needs to follow before it can see the + // IP address + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, 4*RESET_TIMEOUT, 0); } // This cgi uses the routines above to connect to a specific access point with the // given ESSID using the given password. int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { - char essid[128]; - char passwd[128]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; - - int el = httpdFindArg(connData->getArgs, "essid", essid, sizeof(essid)); - int pl = httpdFindArg(connData->getArgs, "passwd", passwd, sizeof(passwd)); - - if (el > 0 && pl >= 0) { - //Set to 0 if you want to disable the actual reconnecting bit - os_strncpy((char*)stconf.ssid, essid, 32); - os_strncpy((char*)stconf.password, passwd, 64); - DBG("Wifi try to connect to AP %s pw %s\n", essid, passwd); - - //Schedule disconnect/connect - os_timer_disarm(&reassTimer); - os_timer_setfn(&reassTimer, reassTimerCb, NULL); - os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it - jsonHeader(connData, 200); - } else { + + int mode = wifi_get_opmode(); + if(mode == 2){ jsonHeader(connData, 400); - httpdSend(connData, "Cannot parse ssid or password", -1); + httpdSend(connData, "Can't associate to an AP en SoftAP mode", -1); + return HTTPD_CGI_DONE; } - return HTTPD_CGI_DONE; + + char essid[128]; + char passwd[128]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + int el = httpdFindArg(connData->getArgs, "essid", essid, sizeof(essid)); + int pl = httpdFindArg(connData->getArgs, "passwd", passwd, sizeof(passwd)); + + if (el > 0 && pl >= 0) { + //Set to 0 if you want to disable the actual reconnecting bit + os_strncpy((char*)stconf.ssid, essid, 32); + os_strncpy((char*)stconf.password, passwd, 64); + DBG("Wifi try to connect to AP %s pw %s\n", essid, passwd); + + //Schedule disconnect/connect + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, reassTimerCb, NULL); + os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it + jsonHeader(connData, 200); + } else { + jsonHeader(connData, 400); + httpdSend(connData, "Cannot parse ssid or password", -1); + } + jsonHeader(connData, 400); + httpdSend(connData, "Invalid ssid or password", -1); + return HTTPD_CGI_DONE; } static bool ICACHE_FLASH_ATTR parse_ip(char *buff, ip_addr_t *ip_ptr) { - char *next = buff; // where to start parsing next integer - int found = 0; // number of integers parsed - uint32_t ip = 0; // the ip addres parsed - for (int i=0; i<32; i++) { // 32 is just a safety limit - char c = buff[i]; - if (c == '.' || c == 0) { - // parse the preceding integer and accumulate into IP address - bool last = c == 0; - buff[i] = 0; - uint32_t v = atoi(next); - ip = ip | ((v&0xff)<<(found*8)); - next = buff+i+1; // next integer starts after the '.' - found++; - if (last) { // if at end of string we better got 4 integers - ip_ptr->addr = ip; - return found == 4; - } - continue; - } - if (c < '0' || c > '9') return false; + char *next = buff; // where to start parsing next integer + int found = 0; // number of integers parsed + uint32_t ip = 0; // the ip addres parsed + for (int i=0; i<32; i++) { // 32 is just a safety limit + char c = buff[i]; + if (c == '.' || c == 0) { + // parse the preceding integer and accumulate into IP address + bool last = c == 0; + buff[i] = 0; + uint32_t v = atoi(next); + ip = ip | ((v&0xff)<<(found*8)); + next = buff+i+1; // next integer starts after the '.' + found++; + if (last) { // if at end of string we better got 4 integers + ip_ptr->addr = ip; + return found == 4; + } + continue; } - return false; + if (c < '0' || c > '9') return false; + } + return false; } #ifdef DEBUGIP static void ICACHE_FLASH_ATTR debugIP() { - struct ip_info info; - if (wifi_get_ip_info(0, &info)) { - os_printf("\"ip\": \"%d.%d.%d.%d\"\n", IP2STR(&info.ip.addr)); - os_printf("\"netmask\": \"%d.%d.%d.%d\"\n", IP2STR(&info.netmask.addr)); - os_printf("\"gateway\": \"%d.%d.%d.%d\"\n", IP2STR(&info.gw.addr)); - os_printf("\"hostname\": \"%s\"\n", wifi_station_get_hostname()); - } else { - os_printf("\"ip\": \"-none-\"\n"); - } + struct ip_info info; + if (wifi_get_ip_info(0, &info)) { + os_printf("\"ip\": \"%d.%d.%d.%d\"\n", IP2STR(&info.ip.addr)); + os_printf("\"netmask\": \"%d.%d.%d.%d\"\n", IP2STR(&info.netmask.addr)); + os_printf("\"gateway\": \"%d.%d.%d.%d\"\n", IP2STR(&info.gw.addr)); + os_printf("\"hostname\": \"%s\"\n", wifi_station_get_hostname()); + } else { + os_printf("\"ip\": \"-none-\"\n"); + } } #endif // configure Wifi, specifically DHCP vs static IP address based on flash config void ICACHE_FLASH_ATTR configWifiIP() { - if (flashConfig.staticip == 0) { - // let's DHCP! - wifi_station_set_hostname(flashConfig.hostname); - if (wifi_station_dhcpc_status() == DHCP_STARTED) - wifi_station_dhcpc_stop(); - wifi_station_dhcpc_start(); - DBG("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname); - } else { - // no DHCP, we got static network config! - wifi_station_dhcpc_stop(); - struct ip_info ipi; - ipi.ip.addr = flashConfig.staticip; - ipi.netmask.addr = flashConfig.netmask; - ipi.gw.addr = flashConfig.gateway; - wifi_set_ip_info(0, &ipi); - DBG("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr)); - } + if (flashConfig.staticip == 0) { + // let's DHCP! + wifi_station_set_hostname(flashConfig.hostname); + if (wifi_station_dhcpc_status() == DHCP_STARTED) + wifi_station_dhcpc_stop(); + wifi_station_dhcpc_start(); + DBG("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname); + } else { + // no DHCP, we got static network config! + wifi_station_dhcpc_stop(); + struct ip_info ipi; + ipi.ip.addr = flashConfig.staticip; + ipi.netmask.addr = flashConfig.netmask; + ipi.gw.addr = flashConfig.gateway; + wifi_set_ip_info(0, &ipi); + DBG("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr)); + } #ifdef DEBUGIP - debugIP(); + debugIP(); #endif } // Change special settings int ICACHE_FLASH_ATTR cgiWiFiSpecial(HttpdConnData *connData) { - char dhcp[8]; - char staticip[20]; - char netmask[20]; - char gateway[20]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; + char dhcp[8]; + char staticip[20]; + char netmask[20]; + char gateway[20]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + // get args and their string lengths + int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); + int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); + int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); + int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); + + if (!(dl > 0 && sl >= 0 && nl >= 0 && gl >= 0)) { + jsonHeader(connData, 400); + httpdSend(connData, "Request is missing fields", -1); + return HTTPD_CGI_DONE; + } + + char url[64]; // redirect URL + if (os_strcmp(dhcp, "off") == 0) { + // parse static IP params + struct ip_info ipi; + bool ok = parse_ip(staticip, &ipi.ip); + if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); + else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); + if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); + else ipi.gw.addr = 0; + if (!ok) { + jsonHeader(connData, 400); + httpdSend(connData, "Cannot parse static IP config", -1); + return HTTPD_CGI_DONE; + } + // save the params in flash + flashConfig.staticip = ipi.ip.addr; + flashConfig.netmask = ipi.netmask.addr; + flashConfig.gateway = ipi.gw.addr; + // construct redirect URL + os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); + + } else { + // dynamic IP + flashConfig.staticip = 0; + os_sprintf(url, "{\"url\": \"http://%s\"}", flashConfig.hostname); + } + + configSave(); // ignore error... + // schedule change-over + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, configWifiIP, NULL); + os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it + // return redirect info + jsonHeader(connData, 200); + httpdSend(connData, url, -1); + return HTTPD_CGI_DONE; +} + +// ==== Soft-AP related functions + +// Change Soft-AP main settings +int ICACHE_FLASH_ATTR cgiApSettingsChange(HttpdConnData *connData) { - // get args and their string lengths - int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); - int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); - int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); - int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - if (!(dl > 0 && sl >= 0 && nl >= 0 && gl >= 0)) { + // No changes for Soft-AP in STA mode + int mode = wifi_get_opmode(); + if ( mode == 1 ){ jsonHeader(connData, 400); - httpdSend(connData, "Request is missing fields", -1); + httpdSend(connData, "No changes allowed in STA mode", -1); return HTTPD_CGI_DONE; } + // Get Soft-Ap config, just in case + wifi_softap_get_config(&apconf); + + char buff[96]; + int len; - char url[64]; // redirect URL - if (os_strcmp(dhcp, "off") == 0) { - // parse static IP params - struct ip_info ipi; - bool ok = parse_ip(staticip, &ipi.ip); - if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); - else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); - if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); - else ipi.gw.addr = 0; - if (!ok) { + // Check extra security measure + len=httpdFindArg(connData->getArgs, "100", buff, sizeof(buff)); + if(len>0){ + if(atoi(buff)!=1){ jsonHeader(connData, 400); - httpdSend(connData, "Cannot parse static IP config", -1); return HTTPD_CGI_DONE; } - // save the params in flash - flashConfig.staticip = ipi.ip.addr; - flashConfig.netmask = ipi.netmask.addr; - flashConfig.gateway = ipi.gw.addr; - // construct redirect URL - os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); + } + // Get the new SSID and set + len=httpdFindArg(connData->getArgs, "ap_ssid", buff, sizeof(buff)); + if(len>7 && len<30){ + // STRING PREPROCESSING DONE IN CLIENT SIDE + memset(apconf.ssid, 0, 32); + os_memcpy(apconf.ssid, buff, len); + }else{ + jsonHeader(connData, 400); + httpdSend(connData, "SSID name out of range", -1); + return HTTPD_CGI_DONE; + } + // Set new PASSWORD + len=httpdFindArg(connData->getArgs, "ap_password", buff, sizeof(buff)); + if(len>7 && len<62){ + // STRING PREPROCESSING DONE IN CLIENT SIDE + memset(apconf.password, 0, 64); + os_memcpy(apconf.password, buff, len); + }else{ + jsonHeader(connData, 400); + httpdSend(connData, "PASSWORD out of range", -1); + return HTTPD_CGI_DONE; + } + // Set max connection number + len=httpdFindArg(connData->getArgs, "ap_maxconn", buff, sizeof(buff)); + if(len>0){ - } else { - // dynamic IP - flashConfig.staticip = 0; - os_sprintf(url, "{\"url\": \"http://%s\"}", flashConfig.hostname); + int value = atoi(buff); + if(value > 0 && value <= 4){ + apconf.max_connection = value; + }else{ + // If out of range set by default + apconf.max_connection = 4; + } } + // Set beacon interval value + len=httpdFindArg(connData->getArgs, "ap_beacon", buff, sizeof(buff)); + if(len>0){ + int value = atoi(buff); + if(value >= 100 && value <= 60000){ + apconf.beacon_interval = value; + }else{ + // If out of range set by default + apconf.beacon_interval = 100; + } + } + // Set authentication mode + len=httpdFindArg(connData->getArgs, "ap_authmode", buff, sizeof(buff)); + if(len>0){ + int value = atoi(buff); + if(value >= 0 && value <= 4){ + apconf.authmode = value; + }else{ + // If out of range set by default + apconf.authmode = 4; + } + } + // Set ssid to be hidden or not + len=httpdFindArg(connData->getArgs, "ap_hidden", buff, sizeof(buff)); + if(len>0){ + int value = atoi(buff); + if(value == 0 || value == 1){ + apconf.ssid_hidden = value; + }else{ + // If out of range set by default + apconf.ssid_hidden = 0; + } + } + // Store new configuration + // This should apply new config values + wifi_softap_set_config(&apconf); + + jsonHeader(connData, 200); + return HTTPD_CGI_DONE; +} + +// Get current Soft-AP settings +int ICACHE_FLASH_ATTR cgiApSettingsInfo(HttpdConnData *connData) { + + char buff[1024]; + if (connData->conn == NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + os_sprintf(buff, + "{ " + "\"ap_ssid\": \"%s\", " + "\"ap_password\": \"%s\", " + "\"ap_authmode\": %d, " + "\"ap_maxconn\": %d, " + "\"ap_beacon\": %d, " + "\"ap_hidden\": \"%s\" " + " }", + apconf.ssid, + apconf.password, + apconf.authmode, + apconf.max_connection, + apconf.beacon_interval, + apconf.ssid_hidden ? "enabled" : "disabled" + ); - configSave(); // ignore error... - // schedule change-over - os_timer_disarm(&reassTimer); - os_timer_setfn(&reassTimer, configWifiIP, NULL); - os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it - // return redirect info jsonHeader(connData, 200); - httpdSend(connData, url, -1); + httpdSend(connData, buff, -1); return HTTPD_CGI_DONE; } +// ===== Wifi set mode end info functions + //This cgi changes the operating mode: STA / AP / STA+AP int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) { + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. int len; char buff[1024]; - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + int previous_mode = wifi_get_opmode(); len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); + + int next_mode = atoi(buff); + if (len!=0) { - int m = atoi(buff); - DBG("Wifi switching to mode %d\n", m); - wifi_set_opmode(m&3); - // Call softap set after changing to AP or STA+AP - wifi_softap_set_config(&apconf); - - if (m == 1) { - // STA-only mode, reset into STA+AP after a timeout if we don't get an IP address + if (next_mode == 2){ + // moving to AP mode, so disconnect before leave STA mode + wifi_station_disconnect(); + } + + DBG("Wifi switching to mode %d\n", next_mode); + wifi_set_opmode(next_mode&3); + + if (previous_mode == 2) { + // movint to STA-only mode from AP, so reset into STA+AP after a timeout if we don't get an IP address + stconf.bssid_set = 0; + wifi_station_set_config(&stconf); + wifi_station_connect(); os_timer_disarm(&resetTimer); os_timer_setfn(&resetTimer, resetTimerCb, NULL); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } + if(previous_mode == 1){ + // moving to STA or STA+AP, so softap config call needed + wifi_softap_set_config(&apconf); + } jsonHeader(connData, 200); } else { jsonHeader(connData, 400); @@ -523,15 +680,30 @@ int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) { return HTTPD_CGI_DONE; } +// Collection of wifi info variables + static char *connStatuses[] = { "idle", "connecting", "wrong password", "AP not found", - "failed", "got IP address" }; + "failed", "got IP address" }; static char *wifiWarn[] = { 0, "Switch to STA+AP mode", - "Can't scan in this mode! Switch to STA+AP mode", + "Switch to STA+AP mode", "Switch to STA mode", }; +static char *apWifiWarn[] = { 0, + "Switch to STA+AP mode", + "Switch to STA+AP mode", + "Switch to AP mode", +}; + +static char *apAuthMode[] = { "OPEN", + "WEP", + "WPA_PSK", + "WPA2_PSK", + "WPA_WPA2_PSK", +}; + #ifdef CHANGE_TO_STA #define MODECHANGE "yes" #else @@ -540,176 +712,191 @@ static char *wifiWarn[] = { 0, // print various Wifi information into json buffer int ICACHE_FLASH_ATTR printWifiInfo(char *buff) { - int len; - - struct station_config stconf; - wifi_station_get_config(&stconf); - - uint8_t op = wifi_get_opmode() & 0x3; - char *mode = wifiMode[op]; - char *status = "unknown"; - int st = wifi_station_get_connect_status(); - if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st]; - int p = wifi_get_phy_mode(); - char *phy = wifiPhy[p&3]; - char *warn = wifiWarn[op]; - sint8 rssi = wifi_station_get_rssi(); - if (rssi > 0) rssi = 0; - uint8 mac_addr[6]; - wifi_get_macaddr(0, mac_addr); - uint8_t chan = wifi_get_channel(); - - len = os_sprintf(buff, - "\"mode\": \"%s\", \"modechange\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", " - "\"rssi\": \"%ddB\", \"warn\": \"%s\", \"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\", \"chan\":%d", - mode, MODECHANGE, (char*)stconf.ssid, status, phy, rssi, warn, - mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], chan); - - struct ip_info info; - if (wifi_get_ip_info(0, &info)) { - len += os_sprintf(buff+len, ", \"ip\": \"%d.%d.%d.%d\"", IP2STR(&info.ip.addr)); - len += os_sprintf(buff+len, ", \"netmask\": \"%d.%d.%d.%d\"", IP2STR(&info.netmask.addr)); - len += os_sprintf(buff+len, ", \"gateway\": \"%d.%d.%d.%d\"", IP2STR(&info.gw.addr)); - len += os_sprintf(buff+len, ", \"hostname\": \"%s\"", flashConfig.hostname); - } else { - len += os_sprintf(buff+len, ", \"ip\": \"-none-\""); - } - len += os_sprintf(buff+len, ", \"staticip\": \"%d.%d.%d.%d\"", IP2STR(&flashConfig.staticip)); - len += os_sprintf(buff+len, ", \"dhcp\": \"%s\"", flashConfig.staticip > 0 ? "off" : "on"); - - return len; + + int len; + //struct station_config stconf; + wifi_station_get_config(&stconf); + //struct softap_config apconf; + wifi_softap_get_config(&apconf); + + uint8_t op = wifi_get_opmode() & 0x3; + char *mode = wifiMode[op]; + char *status = "unknown"; + int st = wifi_station_get_connect_status(); + if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st]; + int p = wifi_get_phy_mode(); + char *phy = wifiPhy[p&3]; + char *warn = wifiWarn[op]; + char *apwarn = apWifiWarn[op]; + char *apauth = apAuthMode[apconf.authmode]; + sint8 rssi = wifi_station_get_rssi(); + if (rssi > 0) rssi = 0; + uint8 mac_addr[6]; + uint8 apmac_addr[6]; + wifi_get_macaddr(0, mac_addr); + wifi_get_macaddr(1, apmac_addr); + uint8_t chan = wifi_get_channel(); + + len = os_sprintf(buff, + "\"mode\": \"%s\", \"modechange\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", " + "\"rssi\": \"%ddB\", \"warn\": \"%s\", \"apwarn\": \"%s\",\"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\", \"chan\":\"%d\", \"apssid\": \"%s\", " + "\"appass\": \"%s\", \"apchan\": \"%d\", \"apmaxc\": \"%d\", \"aphidd\": \"%s\", \"apbeac\": \"%d\", \"apauth\": \"%s\",\"apmac\":\"%02x:%02x:%02x:%02x:%02x:%02x\"", + mode, MODECHANGE, (char*)stconf.ssid, status, phy, rssi, warn, apwarn, + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], chan, (char*)apconf.ssid,(char*)apconf.password,apconf.channel,apconf.max_connection,apconf.ssid_hidden?"enabled":"disabled",apconf.beacon_interval, apauth,apmac_addr[0], apmac_addr[1], apmac_addr[2], apmac_addr[3], apmac_addr[4], apmac_addr[5]); + + struct ip_info info; + if (wifi_get_ip_info(0, &info)) { + len += os_sprintf(buff+len, ", \"ip\": \"%d.%d.%d.%d\"", IP2STR(&info.ip.addr)); + len += os_sprintf(buff+len, ", \"netmask\": \"%d.%d.%d.%d\"", IP2STR(&info.netmask.addr)); + len += os_sprintf(buff+len, ", \"gateway\": \"%d.%d.%d.%d\"", IP2STR(&info.gw.addr)); + len += os_sprintf(buff+len, ", \"hostname\": \"%s\"", flashConfig.hostname); + } else { + len += os_sprintf(buff+len, ", \"ip\": \"-none-\""); + } + len += os_sprintf(buff+len, ", \"staticip\": \"%d.%d.%d.%d\"", IP2STR(&flashConfig.staticip)); + len += os_sprintf(buff+len, ", \"dhcp\": \"%s\"", flashConfig.staticip > 0 ? "off" : "on"); + + return len; } int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) { - char buff[1024]; - int len; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); - - len = os_sprintf(buff, "{"); - len += printWifiInfo(buff+len); - len += os_sprintf(buff+len, ", "); - - if (wifiReason != 0) { - len += os_sprintf(buff+len, "\"reason\": \"%s\", ", wifiGetReason()); - } - + char buff[1024]; + int len; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + jsonHeader(connData, 200); + + len = os_sprintf(buff, "{"); + len += printWifiInfo(buff+len); + len += os_sprintf(buff+len, ", "); + + if (wifiReason != 0) { + len += os_sprintf(buff+len, "\"reason\": \"%s\", ", wifiGetReason()); + } + #if 0 - // commented out 'cause often the client that requested the change can't get a request in to - // find out that it succeeded. Better to just wait the std 15 seconds... - int st=wifi_station_get_connect_status(); - if (st == STATION_GOT_IP) { - if (wifi_get_opmode() != 1) { - // Reset into AP-only mode sooner. - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, 1000, 0); - } + // commented out 'cause often the client that requested the change can't get a request in to + // find out that it succeeded. Better to just wait the std 15 seconds... + int st=wifi_station_get_connect_status(); + if (st == STATION_GOT_IP) { + if (wifi_get_opmode() != 1) { + // Reset into AP-only mode sooner. + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, 1000, 0); } + } #endif - - len += os_sprintf(buff+len, "\"x\":0}\n"); - //DBG(" -> %s\n", buff); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + + len += os_sprintf(buff+len, "\"x\":0}\n"); + DBG(" -> %s\n", buff); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } // Cgi to return various Wifi information int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) { - char buff[1024]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - - os_strcpy(buff, "{"); - printWifiInfo(buff+1); - os_strcat(buff, "}"); - - jsonHeader(connData, 200); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; + char buff[1024]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + + os_strcpy(buff, "{"); + printWifiInfo(buff+1); + os_strcat(buff, "}"); + + jsonHeader(connData, 200); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } -// Init the wireless, which consists of setting a timer if we expect to connect to an AP -// so we can revert to STA+AP mode if we can't connect. +/* Init the wireless + * + * Call both Soft-AP and Station default config + * Change values according to Makefile hard-coded variables + * Anyway set wifi opmode to STA+AP, it will change to STA if CHANGE_TO_STA is set to yes in Makefile + * Call a timer to check the STA connection + */ void ICACHE_FLASH_ATTR wifiInit() { - + // Check te wifi opmode int x = wifi_get_opmode() & 0x3; -#ifdef CGIWIFI_DBG - os_printf("Wifi init, mode=%s\n",wifiMode[x]); -#endif + // Set opmode to 3 to let system scan aps, otherwise it won't scan + wifi_set_opmode(3); - // STATION parameters only on a full flash -#if defined(STA_SSID) && defined(STA_PASS) + // Call both STATION and SOFTAP default config + wifi_station_get_config_default(&stconf); + wifi_softap_get_config_default(&apconf); + + #ifdef CGIWIFI_DBG + os_printf("Wifi init, mode=%s\n",wifiMode[x]); + #endif + + // STATION parameters only on a full flash, because default opmode is 2 + #if defined(STA_SSID) && defined(STA_PASS) if( x == 2 ){ - // Create struct for station config - struct station_config stconf; - // Call it - wifi_station_get_config(&stconf); // Set parameters if (os_strlen((char*)stconf.ssid) == 0 && os_strlen((char*)stconf.password) == 0) { os_strncpy((char*)stconf.ssid, VERS_STR(STA_SSID), 32); os_strncpy((char*)stconf.password, VERS_STR(STA_PASS), 64); -#ifdef CGIWIFI_DBG + #ifdef CGIWIFI_DBG os_printf("Wifi pre-config trying to connect to AP %s pw %s\n",(char*)stconf.ssid, (char*)stconf.password); -#endif + #endif // wifi_set_phy_mode(2); // limit to 802.11b/g 'cause n is flaky - // Set wifi mode - wifi_set_opmode(3); // sta+ap, will switch to sta-only 15 secs after connecting stconf.bssid_set = 0; wifi_station_set_config(&stconf); } } -#endif - - // SOFT_AP parameters -#if defined(AP_SSID) && defined(AP_PASS) - // Call it - wifi_softap_get_config(&apconf); - // Clean memory and set the value of SSID - memset(apconf.ssid, 0, sizeof(apconf.ssid)); - os_memcpy(apconf.ssid, VERS_STR(AP_SSID), strlen(VERS_STR(AP_SSID))); - // Clean memory and set the value of PASS - memset(apconf.password, 0, sizeof(apconf.password)); - os_memcpy(apconf.password, VERS_STR(AP_PASS), strlen(VERS_STR(AP_PASS))); - // Specify the length of pass - apconf.ssid_len= os_strlen((char*)VERS_STR(AP_PASS)); -#ifdef AP_AUTH_MODE - // If set, use specified auth mode - apconf.authmode = AP_AUTH_MODE; -#else - // If not, use wpa wpa2 psk - apconf.authmode = AUTH_WPA_WPA2_PSK; -#endif -#ifdef AP_SSID_HIDDEN - // If set, use specified ssid hidden parameter - apconf.ssid_hidden = AP_SSID_HIDDEN; -#endif -#ifdef AP_MAX_CONN - // If set, use specified max conn number - apconf.max_connection = AP_MAX_CONN; -#endif -#ifdef AP_BEACON_INTERVAL - // If set use specified beacon interval - apconf.beacon_interval = AP_BEACON_INTERVAL; -#endif - // Set to use the new conf -#ifdef CGIWIFI_DBG - os_printf("Wifi AP parameters: %s pw %s\n",(char*)apconf.ssid, (char*)apconf.password); -#endif - // MUST BE called after enabling AP or STA+AP mode - wifi_softap_set_config(&apconf); -#endif // AP_SSID && AP_PASS - + #endif + + // Change SOFT_AP parameters if defined + #if defined(AP_SSID) && defined(AP_PASS) + // Clean memory and set the value of SSID + memset(apconf.ssid, 0, 32); + os_memcpy(apconf.ssid, VERS_STR(AP_SSID), strlen(VERS_STR(AP_SSID))); + + // Clean memory and set the value of PASS + memset(apconf.password, 0, 64); + os_memcpy(apconf.password, VERS_STR(AP_PASS), strlen(VERS_STR(AP_PASS))); + + // Specify the length of pass + apconf.ssid_len= os_strlen((char*)VERS_STR(AP_PASS)); + #ifdef AP_AUTH_MODE + // If set, use specified auth mode + apconf.authmode = AP_AUTH_MODE; + #else + // If not, use wpa wpa2 psk + apconf.authmode = AUTH_WPA_WPA2_PSK; + #endif + #ifdef AP_SSID_HIDDEN + // If set, use specified ssid hidden parameter + apconf.ssid_hidden = AP_SSID_HIDDEN; + #endif + #ifdef AP_MAX_CONN + // If set, use specified max conn number + apconf.max_connection = AP_MAX_CONN; + #endif + #ifdef AP_BEACON_INTERVAL + // If set use specified beacon interval + apconf.beacon_interval = AP_BEACON_INTERVAL; + #endif + // Check save softap config + bool softap_set_conf = wifi_softap_set_config(&apconf); + #ifdef CGIWIFI_DBG + // Debug info + os_printf("Wifi AP parameters: %s pw %s\n",(char*)apconf.ssid, (char*)apconf.password); + os_printf("Wifi Soft-AP parameters set: %s\n",softap_set_conf? "success":"fail"); + #endif + + #endif // AP_SSID && AP_PASS + configWifiIP(); - + // The default sleep mode should be modem_sleep, but we set it here explicitly for good // measure. We can't use light_sleep because that powers off everthing and we would loose // all connections. wifi_set_sleep_type(MODEM_SLEEP_T); - + wifi_set_event_handler_cb(wifiHandleEventCb); // check on the wifi in a few seconds to see whether we need to switch mode os_timer_disarm(&resetTimer); diff --git a/esp-link/cgiwifi.h b/esp-link/cgiwifi.h index e2e98bc..5fa177c 100644 --- a/esp-link/cgiwifi.h +++ b/esp-link/cgiwifi.h @@ -13,6 +13,8 @@ int cgiWiFiConnect(HttpdConnData *connData); int cgiWiFiSetMode(HttpdConnData *connData); int cgiWiFiConnStatus(HttpdConnData *connData); int cgiWiFiSpecial(HttpdConnData *connData); +int cgiApSettingsChange(HttpdConnData *connData); +int cgiApSettingsInfo(HttpdConnData *connData); void configWifiIP(); void wifiInit(void); void wifiAddStateChangeCb(WifiStateChangeCb cb); diff --git a/esp-link/main.c b/esp-link/main.c index bfbea0f..1bbf3b6 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -72,6 +72,8 @@ HttpdBuiltInUrl builtInUrls[] = { { "/wifi/connstatus", cgiWiFiConnStatus, NULL }, { "/wifi/setmode", cgiWiFiSetMode, NULL }, { "/wifi/special", cgiWiFiSpecial, NULL }, + { "/wifi/apinfo", cgiApSettingsInfo, NULL }, + { "/wifi/apchange", cgiApSettingsChange, NULL }, { "/system/info", cgiSystemInfo, NULL }, { "/system/update", cgiSystemSet, NULL }, { "/services/info", cgiServicesInfo, NULL }, @@ -86,7 +88,6 @@ HttpdBuiltInUrl builtInUrls[] = { #ifdef SHOW_HEAP_USE static ETSTimer prHeapTimer; - static void ICACHE_FLASH_ATTR prHeapTimerCb(void *arg) { os_printf("Heap: %ld\n", (unsigned long)system_get_free_heap_size()); } @@ -109,26 +110,25 @@ void user_rf_pre_init(void) { // Main routine to initialize esp-link. void user_init(void) { + // get the flash config so we know how to init things -// configWipe(); // uncomment to reset the config for testing purposes + // configWipe(); // uncomment to reset the config for testing purposes bool restoreOk = configRestore(); - // init gpio pin registers + // Init gpio pin registers gpio_init(); gpio_output_set(0, 0, 0, (1<<15)); // some people tie it to GND, gotta ensure it's disabled // init UART uart_init(flashConfig.baud_rate, 115200); logInit(); // must come after init of uart - // say hello (leave some time to cause break in TX after boot loader's msg + // Say hello (leave some time to cause break in TX after boot loader's msg os_delay_us(10000L); os_printf("\n\n** %s\n", esp_link_version); os_printf("Flash config restore %s\n", restoreOk ? "ok" : "*FAILED*"); - // Status LEDs statusInit(); serledInit(); // Wifi wifiInit(); - // init the flash filesystem with the html stuff espFsInit(&_binary_espfs_img_start); //EspFsInitResult res = espFsInit(&_binary_espfs_img_start); @@ -153,16 +153,14 @@ void user_init(void) { NOTICE("Flash map %s, manuf 0x%02lX chip 0x%04lX", flash_maps[system_get_flash_size_map()], fid & 0xff, (fid&0xff00)|((fid>>16)&0xff)); NOTICE("** esp-link ready"); - + + // Init SNTP service cgiServicesSNTPInit(); - #ifdef MQTT NOTICE("initializing MQTT"); mqtt_client_init(); #endif - NOTICE("initializing user application"); app_init(); - - NOTICE("waiting for work to do..."); + NOTICE("Waiting for work to do..."); } diff --git a/html/wifi/wifiAp.html b/html/wifi/wifiAp.html new file mode 100644 index 0000000..e93e54d --- /dev/null +++ b/html/wifi/wifiAp.html @@ -0,0 +1,124 @@ +
+
+

WiFi Soft-AP Configuration

+
+ +
+
+
+
+

Soft-AP State

+
+
+ + + + + + + + + + + + +
+
+
+ +
+

Soft-AP Settings

+
+ + +
+
+
+
+
+ + + + + + diff --git a/html/wifi/wifiAp.js b/html/wifi/wifiAp.js new file mode 100644 index 0000000..e2fef60 --- /dev/null +++ b/html/wifi/wifiAp.js @@ -0,0 +1,98 @@ +var specials = []; +specials["ap_ssid"] = "SSID name"; +specials["ap_password"] = "PASSWORD"; +specials["ap_maxconn"] = "Max Connections number"; +specials["ap_beacon"] = "Beacon Interval"; + +function changeWifiMode(m) { + blockScan = 1; + hideWarning(); + ajaxSpin("POST", "setmode?mode=" + m, function(resp) { + showNotification("Mode changed"); + window.setTimeout(getWifiInfo, 100); + blockScan = 0; + }, function(s, st) { + showWarning("Error changing mode: " + st); + window.setTimeout(getWifiInfo, 100); + blockScan = 0; + }); +} + +function changeApSettings(e) { + e.preventDefault(); + var url = "/wifi/apchange?100=1"; + var i, inputs = document.querySelectorAll("#" + e.target.id + " input,select"); + for (i = 0; i < inputs.length; i++) { + if (inputs[i].type == "checkbox") { + var val = (inputs[i].checked) ? 1 : 0; + url += "&" + inputs[i].name + "=" + val; + } + else{ + var clean = inputs[i].value.replace(/[^\w]/gi, ""); + var comp = clean.localeCompare(inputs[i].value); + if ( comp != 0 ){ + showWarning("Invalid characters in " + specials[inputs[i].name]); + return; + } + url += "&" + inputs[i].name + "=" + clean; + } + }; + + hideWarning(); + var n = e.target.id.replace("-form", ""); + var cb = $("#" + n + "-button"); + addClass(cb, "pure-button-disabled"); + ajaxSpin("POST", url, function (resp) { + showNotification(n + " updated"); + removeClass(cb, "pure-button-disabled"); + window.setTimeout(getWifiInfo, 100); + }, function (s, st) { + showWarning(st); + removeClass(cb, "pure-button-disabled"); + window.setTimeout(fetchApSettings, 2000); + }); +} + +function displayApSettings(data) { + Object.keys(data).forEach(function (v) { + el = $("#" + v); + if (el != null) { + if (el.nodeName === "INPUT") el.value = data[v]; + else el.innerHTML = data[v]; + return; + } + + el = document.querySelector('input[name="' + v + '"]'); + if (el == null) + el = document.querySelector('select[name="' + v + '"]'); + + if (el != null) { + if (el.type == "checkbox") { + el.checked = data[v] == "enabled"; + } else el.value = data[v]; + } + }); + + $("#AP_Settings-spinner").setAttribute("hidden", ""); + $("#AP_Settings-form").removeAttribute("hidden"); + showWarning("Don't modify SOFTAP parameters with active connections"); + window.setTimeout(hideWarning(), 2000); +} + +function fetchApSettings() { + ajaxJson("GET", "/wifi/apinfo", displayApSettings, function () { + window.setTimeout(fetchApSettings, 1000); + }); +} + +function doApAdvanced() { + $('#AP_Settings-on').removeAttribute('hidden'); + $("#AP_Settings-off").setAttribute("hidden", ""); + $("#AP_Settings-roff").removeAttribute("checked"); +} + +function undoApAdvanced(){ + $("#AP_Settings-on").setAttribute("hidden", ""); + $("#AP_Settings-off").removeAttribute("hidden"); + $("#AP_Settings-roff").setAttribute("checked", ""); +} \ No newline at end of file diff --git a/html/wifi/wifi.html b/html/wifi/wifiSta.html similarity index 96% rename from html/wifi/wifi.html rename to html/wifi/wifiSta.html index f6d6646..bdc6dca 100644 --- a/html/wifi/wifi.html +++ b/html/wifi/wifiSta.html @@ -1,6 +1,6 @@
-

WiFi Configuration

+

WiFi Station Configuration

@@ -19,7 +19,9 @@ WiFi MAC -
+ + +

WiFi Association

@@ -70,7 +72,7 @@ - + - \ No newline at end of file + diff --git a/html/style.css b/html/style.css index d3f9348..1bb11cd 100644 --- a/html/style.css +++ b/html/style.css @@ -85,10 +85,10 @@ a:hover { .popup, div.popup { position: absolute; /*top: 100%;*/ - bottom: 100%; + bottom: 125%; background-color: #fff0b3; border-radius: 5px; - border: 0px solid #000; + border: 1px solid #e6b800; color: #333; font-size: 80%; line-height: 110%; From 481818429ee8644cafc8db0c8563c7591d87de76 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 23 Jan 2016 10:31:10 -0800 Subject: [PATCH 32/37] fix RX pull-up; revamp avr flashing --- esp-link/cgioptiboot.c | 132 +++++++++++++++++------------------------ serial/serbridge.c | 10 ++-- 2 files changed, 60 insertions(+), 82 deletions(-) diff --git a/esp-link/cgioptiboot.c b/esp-link/cgioptiboot.c index d9e20f1..6594973 100644 --- a/esp-link/cgioptiboot.c +++ b/esp-link/cgioptiboot.c @@ -10,11 +10,11 @@ #include "serbridge.h" #include "serled.h" -#define SYNC_TIMEOUT 4800 // to achieve sync, in milliseconds -#define SYNC_INTERVAL 77 // interval at which we try to sync +#define INIT_DELAY 150 // wait this many millisecs before sending anything #define BAUD_INTERVAL 600 // interval after which we change baud rate -#define PGM_TIMEOUT 20000 // timeout when sync is achieved, in milliseconds +#define PGM_TIMEOUT 20000 // timeout after sync is achieved, in milliseconds #define PGM_INTERVAL 200 // send sync at this interval in ms when in programming mode +#define ATTEMPTS 8 // number of attempts total to make #ifdef OPTIBOOT_DBG #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) @@ -29,15 +29,14 @@ static ETSTimer optibootTimer; static enum { // overall programming states - stateSync = 0, // trying to get initial response - stateSync2, // trying to get in sync - stateSync3, // trying to get second sync + stateInit = 0, // initial delay + stateSync, // waiting to hear back stateGetSig, // reading device signature stateGetVersLo, // reading optiboot version, low bits stateGetVersHi, // reading optiboot version, high bits stateProg, // programming... } progState; -static short syncCnt; // counter & timeout for sync attempts +static char* progStates[] = { "init", "sync", "sig", "ver0", "ver1", "prog" }; static short baudCnt; // counter for sync attempts at different baud rates static short ackWait; // counter of expected ACKs static uint16_t optibootVers; @@ -70,12 +69,11 @@ static void optibootTimerCB(void *); static void optibootUartRecv(char *buffer, short length); static bool processRecord(char *buf, short len); static bool programPage(void); -static void armTimer(void); +static void armTimer(uint32_t ms); static void initBaud(void); static void ICACHE_FLASH_ATTR optibootInit() { - progState = stateSync; - syncCnt = 0; + progState = stateInit; baudCnt = 0; uart0_baud(flashConfig.baud_rate); ackWait = 0; @@ -142,7 +140,7 @@ int ICACHE_FLASH_ATTR cgiOptibootSync(HttpdConnData *connData) { // start sync timer os_timer_disarm(&optibootTimer); os_timer_setfn(&optibootTimer, optibootTimerCB, NULL); - os_timer_arm(&optibootTimer, 50, 0); // fire in 50ms and don't recur + os_timer_arm(&optibootTimer, INIT_DELAY, 0); // respond with optimistic OK noCacheHeaders(connData, 204); @@ -155,7 +153,7 @@ int ICACHE_FLASH_ATTR cgiOptibootSync(HttpdConnData *connData) { if (!errMessage[0] && progState >= stateProg) { char buf[64]; DBG("OB got sync\n"); - os_sprintf(buf, "SYNC at %ld baud: Optiboot %d.%d", + os_sprintf(buf, "SYNC at %ld baud: bootloader v%d.%d", baudRate, optibootVers>>8, optibootVers&0xff); httpdSend(connData, buf, -1); } else if (errMessage[0] && progState == stateSync) { @@ -202,7 +200,8 @@ static uint32_t ICACHE_FLASH_ATTR getHexValue(char *buf, short len) { //===== Cgi to write firmware to Optiboot, requires prior sync call int ICACHE_FLASH_ATTR cgiOptibootData(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - DBG("OB pgm: state=%d PrivData=%p postLen=%d\n", progState, connData->cgiPrivData, connData->post->len); + if (!optibootData) + DBG("OB pgm: state=%d postLen=%d\n", progState, connData->post->len); // check that we have sync if (errMessage[0] || progState < stateProg) { @@ -427,7 +426,7 @@ static bool pollAck() { // Program a flash page static bool ICACHE_FLASH_ATTR programPage(void) { if (optibootData->pageLen == 0) return true; - armTimer(); // keep the timerCB out of the picture + armTimer(PGM_TIMEOUT); // keep the timerCB out of the picture if (ackWait > 7) { os_sprintf(errMessage, "Lost sync while programming\n"); @@ -436,7 +435,7 @@ static bool ICACHE_FLASH_ATTR programPage(void) { uint16_t pgmLen = optibootData->pageLen; if (pgmLen > optibootData->pgmSz) pgmLen = optibootData->pgmSz; - DBG("OB pgm %d@0x%lx ackWait=%d\n", pgmLen, optibootData->address, ackWait); + DBG("OB pgm %d@0x%lx\n", pgmLen, optibootData->address); // send address to optiboot (little endian format) #ifdef DBG_GPIO5 @@ -448,12 +447,12 @@ static bool ICACHE_FLASH_ATTR programPage(void) { uart0_write_char(addr & 0xff); uart0_write_char(addr >> 8); uart0_write_char(CRC_EOP); - armTimer(); + armTimer(PGM_TIMEOUT); if (!pollAck()) { DBG("OB pgm failed in load address\n"); return false; } - armTimer(); + armTimer(PGM_TIMEOUT); // send page length (big-endian format, go figure...) #ifdef DBG_GPIO5 @@ -470,9 +469,9 @@ static bool ICACHE_FLASH_ATTR programPage(void) { uart0_write_char(optibootData->pageBuf[i]); uart0_write_char(CRC_EOP); - armTimer(); + armTimer(PGM_TIMEOUT); bool ok = pollAck(); - armTimer(); + armTimer(PGM_TIMEOUT); if (!ok) { DBG("OB pgm failed in prog page\n"); return false; @@ -490,18 +489,17 @@ static bool ICACHE_FLASH_ATTR programPage(void) { //===== Rebooting and getting sync -static void ICACHE_FLASH_ATTR armTimer() { +static void ICACHE_FLASH_ATTR armTimer(uint32_t ms) { os_timer_disarm(&optibootTimer); - // time-out every 50ms, except when programming to allow for 9600baud (133ms for 128 bytes) - os_timer_arm(&optibootTimer, progState==stateProg ? PGM_INTERVAL : SYNC_INTERVAL, 0); + os_timer_arm(&optibootTimer, ms, 0); } static int baudRates[] = { 0, 9600, 57600, 115200 }; static void ICACHE_FLASH_ATTR setBaud() { - baudRate = baudRates[(syncCnt / (BAUD_INTERVAL/SYNC_INTERVAL)) % 4]; + baudRate = baudRates[(baudCnt++) % 4]; uart0_baud(baudRate); - //DBG("OB changing to %d baud\n", b); + //DBG("OB changing to %ld baud\n", baudRate); } static void ICACHE_FLASH_ATTR initBaud() { @@ -511,45 +509,41 @@ static void ICACHE_FLASH_ATTR initBaud() { static void ICACHE_FLASH_ATTR optibootTimerCB(void *arg) { // see whether we've issued so many sync in a row that it's time to give up - syncCnt++; switch (progState) { - case stateSync: // we're trying to get sync, all we do here is send a sync request - if (syncCnt >= SYNC_TIMEOUT/SYNC_INTERVAL) { + case stateInit: // initial delay expired, send sync chars + uart0_write_char(STK_GET_SYNC); + uart0_write_char(CRC_EOP); + progState++; + armTimer(BAUD_INTERVAL-INIT_DELAY); + return; + case stateSync: // oops, must have not heard back!? + if (baudCnt > ATTEMPTS) { // we're doomed, give up - DBG("OB sync abandoned after timeout, state=%d syncCnt=%d\n", progState, syncCnt); + DBG("OB abandoned after %d attempts\n", baudCnt); optibootInit(); - strcpy(errMessage, "sync abandoned after timeout"); + strcpy(errMessage, "sync abandoned after 8 attempts"); return; } - if (syncCnt % (BAUD_INTERVAL/SYNC_INTERVAL) == 0) { - // time to switch baud rate and issue a reset - setBaud(); - serbridgeReset(); - // no point sending chars if we just switched - } else { - //uart0_write_char(STK_GET_SYNC); - uart0_write_char(CRC_EOP); - uart0_write_char(CRC_EOP); - } - break; - case stateSync2: // need one more CRC_EOP? - uart0_write_char(CRC_EOP); - progState++; - break; + // time to switch baud rate and issue a reset + DBG("OB no sync response @%ld baud\n", baudRate); + setBaud(); + serbridgeReset(); + progState = stateInit; + armTimer(INIT_DELAY); + return; case stateProg: // we're programming and we timed-out of inaction uart0_write_char(STK_GET_SYNC); uart0_write_char(CRC_EOP); ackWait++; // we now expect an ACK - break; + armTimer(PGM_INTERVAL); + return; default: // we're trying to get some info from optiboot and it should have responded! optibootInit(); // abort - os_sprintf(errMessage, "No response in state %d\n", progState); + os_sprintf(errMessage, "No response in state %s(%d) @%ld baud\n", + progStates[progState], progState, baudRate); DBG("OB %s\n", errMessage); return; // do not re-arm timer } - - // we need to come back... - armTimer(); } // skip in-sync responses @@ -575,8 +569,9 @@ static void ICACHE_FLASH_ATTR optibootUartRecv(char *buf, short length) { // dispatch based the current state switch (progState) { - case stateSync: // we're trying to get a sync response - case stateSync3: // we're trying to get a second sync response + case stateInit: // we haven't sent anything, this must be garbage + break; + case stateSync: // we're trying to get a sync response // look for STK_INSYNC+STK_OK at end of buffer if (responseLen > 0 && responseBuf[responseLen-1] == STK_INSYNC) { // missing STK_OK after STK_INSYNC, shift stuff out and try again @@ -584,19 +579,14 @@ static void ICACHE_FLASH_ATTR optibootUartRecv(char *buf, short length) { responseLen = 1; } else if (responseLen > 1 && responseBuf[responseLen-2] == STK_INSYNC && responseBuf[responseLen-1] == STK_OK) { - // got sync response, send more... + // got sync response os_memcpy(responseBuf, responseBuf+2, responseLen-2); responseLen -= 2; - if (progState==stateSync) { - // need to deal with odd-even sync issue, send one more to see whether we get a response - uart0_write_char(CRC_EOP); - } else { - // got clean sync, send request to get signature - uart0_write_char(STK_READ_SIGN); - uart0_write_char(CRC_EOP); - } + // send request to get signature + uart0_write_char(STK_READ_SIGN); + uart0_write_char(CRC_EOP); progState++; - armTimer(); // reset timer + armTimer(PGM_INTERVAL); // reset timer } else { // nothing useful, keep at most half the buffer for error message purposes if (responseLen > RESP_SZ/2) { @@ -606,18 +596,6 @@ static void ICACHE_FLASH_ATTR optibootUartRecv(char *buf, short length) { } } break; - case stateSync2: // we're trying to actually get in sync - if (responseLen > 1 && responseBuf[responseLen-2] == STK_INSYNC && - responseBuf[responseLen-1] == STK_OK) { - // got sync response, send signature request - os_memcpy(responseBuf, responseBuf+2, responseLen-2); - responseLen -= 2; - uart0_write_char(STK_READ_SIGN); - uart0_write_char(CRC_EOP); - progState = stateGetSig; - } - armTimer(); // reset timer - break; case stateGetSig: // expecting signature responseLen = skipInSync(responseBuf, responseLen); if (responseLen >= 5 && responseBuf[0] == STK_INSYNC && responseBuf[4] == STK_OK) { @@ -627,7 +605,7 @@ static void ICACHE_FLASH_ATTR optibootUartRecv(char *buf, short length) { uart0_write_char(STK_GET_PARAMETER); uart0_write_char(0x82); uart0_write_char(CRC_EOP); - armTimer(); // reset timer + armTimer(PGM_INTERVAL); // reset timer } else { optibootInit(); // abort os_sprintf(errMessage, "Bad programmer signature: 0x%02x 0x%02x 0x%02x\n", @@ -646,7 +624,7 @@ static void ICACHE_FLASH_ATTR optibootUartRecv(char *buf, short length) { uart0_write_char(STK_GET_PARAMETER); uart0_write_char(0x81); uart0_write_char(CRC_EOP); - armTimer(); // reset timer + armTimer(PGM_INTERVAL); // reset timer } break; case stateGetVersHi: // expecting version @@ -655,7 +633,7 @@ static void ICACHE_FLASH_ATTR optibootUartRecv(char *buf, short length) { progState++; os_memcpy(responseBuf, responseBuf+3, responseLen-3); responseLen -= 3; - armTimer(); // reset timer + armTimer(PGM_INTERVAL); // reset timer ackWait = 0; } break; @@ -666,7 +644,7 @@ static void ICACHE_FLASH_ATTR optibootUartRecv(char *buf, short length) { os_memmove(responseBuf, responseBuf+2, responseLen-2); responseLen -= 2; } - armTimer(); // reset timer + armTimer(PGM_INTERVAL); // reset timer default: break; } diff --git a/serial/serbridge.c b/serial/serbridge.c index 696d635..1e82e3d 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -445,11 +445,11 @@ serbridgeInitPins() #endif if (flashConfig.swap_uart) { - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 4); - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 4); - PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTCK_U); - if (flashConfig.rx_pullup) PIN_PULLUP_EN(PERIPHS_IO_MUX_MTDO_U); - else PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 4); // RX + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 4); // TX + PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U); + if (flashConfig.rx_pullup) PIN_PULLUP_EN(PERIPHS_IO_MUX_MTCK_U); + else PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTCK_U); system_uart_swap(); } else { PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, 0); From 109c10c3f25b19cddd197bb35d58c8e2cb508e47 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sun, 24 Jan 2016 09:48:33 -0800 Subject: [PATCH 33/37] make new slip proto work --- cmd/cmd.h | 4 ++-- cmd/handlers.c | 17 ++++++----------- include/user_config.h | 2 +- serial/slip.c | 17 ++++++++--------- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/cmd/cmd.h b/cmd/cmd.h index 06486b5..d18d90d 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -20,7 +20,7 @@ typedef struct __attribute__((__packed__)) { typedef struct __attribute__((__packed__)) { uint16_t cmd; // command to perform, from CmdName enum uint32_t callback; // callback pointer to embed in response - uint32_t _return; // return value to embed in response (?) + uint32_t _return; // token to embed in response uint16_t argc; // number of arguments to command CmdArg args[0]; // really args[argc] } CmdPacket; @@ -33,7 +33,7 @@ typedef struct { typedef enum { CMD_NULL = 0, - CMD_RESET, // reset esp (not honored in this implementation) + CMD_SYNC, // synchronize and clear CMD_IS_READY, // health-check CMD_WIFI_CONNECT, // (3) connect to AP (not honored in this implementation) CMD_MQTT_SETUP, diff --git a/cmd/handlers.c b/cmd/handlers.c index 50af21d..62d2f94 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -19,8 +19,7 @@ #endif static uint32_t CMD_Null(CmdPacket *cmd); -static uint32_t CMD_IsReady(CmdPacket *cmd); -static uint32_t CMD_Reset(CmdPacket *cmd); +//static uint32_t CMD_Reset(CmdPacket *cmd); static uint32_t CMD_WifiConnect(CmdPacket *cmd); static uint32_t CMD_AddCallback(CmdPacket *cmd); @@ -30,9 +29,9 @@ static bool wifiCbAdded = false; // Command dispatch table for serial -> ESP commands const CmdList commands[] = { - {CMD_NULL, CMD_Null}, - {CMD_RESET, CMD_Reset}, - {CMD_IS_READY, CMD_IsReady}, + {CMD_NULL, CMD_Null}, // no-op + //{CMD_SYNC, CMD_Sync}, // synchronize + //{CMD_IS_READY, CMD_IsReady}, {CMD_WIFI_CONNECT, CMD_WifiConnect}, #ifdef MQTT {CMD_MQTT_SETUP, MQTTCMD_Setup}, @@ -55,18 +54,13 @@ const CmdList commands[] = { #define MAX_CALLBACKS 12 cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset -// Command handler for IsReady (healthcheck) command -static uint32_t ICACHE_FLASH_ATTR -CMD_IsReady(CmdPacket *cmd) { - return 1; -} - // Command handler for Null command static uint32_t ICACHE_FLASH_ATTR CMD_Null(CmdPacket *cmd) { return 1; } +#if 0 // Command handler for Reset command, this was originally to reset the ESP but we don't want to // do that in esp-link. It is still good to clear any information the ESP has about the attached // uC. @@ -76,6 +70,7 @@ CMD_Reset(CmdPacket *cmd) { os_memset(callbacks, 0, sizeof(callbacks)); return 1; } +#endif static uint32_t ICACHE_FLASH_ATTR CMD_AddCb(char* name, uint32_t cb) { diff --git a/include/user_config.h b/include/user_config.h index 72fe1ac..a939533 100644 --- a/include/user_config.h +++ b/include/user_config.h @@ -27,7 +27,7 @@ #define RESTCMD_DBG #define SERBR_DBG #define SERLED_DBG -#undef SLIP_DBG +#define SLIP_DBG #define UART_DBG #define MDNS_DBG #define OPTIBOOT_DBG diff --git a/serial/slip.c b/serial/slip.c index 727f60c..cd88c1e 100644 --- a/serial/slip.c +++ b/serial/slip.c @@ -45,13 +45,12 @@ slip_process() { if (crc == rcv) { CMD_parse_packet((uint8_t*)slip_buf, slip_len-2); } else { - os_printf("SLIP: bad CRC, crc=%x rcv=%x\n", crc, rcv); + os_printf("SLIP: bad CRC, crc=%04x rcv=%04x len=%d\n", crc, rcv, slip_len); for (short i=0; i= ' ' && slip_buf[i] <= '~') { DBG("%c", slip_buf[i]); - } - else { + } else { DBG("\\%02X", slip_buf[i]); } } @@ -79,12 +78,12 @@ static void ICACHE_FLASH_ATTR slip_parse_char(char c) { if (c == SLIP_END) { // either start or end of packet, process whatever we may have accumulated + DBG("SLIP: start or end len=%d inpkt=%d\n", slip_len, slip_inpkt); if (slip_len > 0) { - if (slip_inpkt) slip_process(); else console_process(slip_buf, slip_len); + if (slip_len > 2 && slip_inpkt) slip_process(); + else console_process(slip_buf, slip_len); } slip_reset(); - //slip_inpkt = true; - DBG("SLIP: start or end\n"); } else if (slip_escaped) { // prev char was SLIP_ESC if (c == SLIP_ESC_END) c = SLIP_END; @@ -94,7 +93,7 @@ slip_parse_char(char c) { } else if (slip_inpkt && c == SLIP_ESC) { slip_escaped = true; } else { - if (slip_len == 0 && slip_printable(c)) { + if (slip_len == 1 && slip_printable(slip_buf[0]) && slip_printable(c)) { // start of packet and it's a printable character, we're gonna assume that this is console text slip_inpkt = false; } @@ -111,8 +110,8 @@ slip_parse_buf(char *buf, short length) { // if we're in-between packets (debug console) then print it now if (!slip_inpkt && length > 0) { - slip_process(); - slip_reset(); + console_process(slip_buf, slip_len); + slip_len = 0; } } From 5a891c3c1ef5fbf6e97093cb464d866a831e801a Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sun, 31 Jan 2016 00:09:21 -0800 Subject: [PATCH 34/37] new SLIP protocol; basic REST working --- cmd/cmd.c | 114 ++++++++++++++-------------------- cmd/cmd.h | 57 +++++++++-------- cmd/handlers.c | 160 ++++++++++++++++++++++++++---------------------- mqtt/mqtt_cmd.c | 128 +++++++++++++++++++------------------- mqtt/mqtt_cmd.h | 12 ++-- rest/rest.c | 143 ++++++++++++++++++++++++++----------------- rest/rest.h | 29 +-------- serial/slip.c | 2 +- 8 files changed, 325 insertions(+), 320 deletions(-) diff --git a/cmd/cmd.c b/cmd/cmd.c index 0d625cd..52ee3b3 100644 --- a/cmd/cmd.c +++ b/cmd/cmd.c @@ -9,13 +9,6 @@ #ifdef CMD_DBG #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) -static const char *cmd_names[] = { - "NULL", "RESET", "IS_READY", "WIFI_CONNECT", - "MQTT_SETUP", "MQTT_CONNECT", "MQTT_DISCONNECT", - "MQTT_PUBLISH", "MQTT_SUBSCRIBE", "MQTT_LWT", "MQTT_EVENTS", - "REST_SETUP", "REST_REQUEST", "REST_SETHEADER", "REST_EVENTS", - "CB_ADD", "CB_EVENTS", -}; #else #define DBG(format, ...) do { } while(0) #endif @@ -25,7 +18,7 @@ extern const CmdList commands[]; //===== ESP -> Serial responses static void ICACHE_FLASH_ATTR -CMD_ProtoWrite(uint8_t data) { +cmdProtoWrite(uint8_t data) { switch(data){ case SLIP_END: uart0_write_char(SLIP_ESC); @@ -41,98 +34,83 @@ CMD_ProtoWrite(uint8_t data) { } static void ICACHE_FLASH_ATTR -CMD_ProtoWriteBuf(uint8_t *data, short len) { - while (len--) CMD_ProtoWrite(*data++); +cmdProtoWriteBuf(uint8_t *data, short len) { + while (len--) cmdProtoWrite(*data++); } +static uint16_t resp_crc; + // Start a response, returns the partial CRC -uint16_t ICACHE_FLASH_ATTR -CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc) { - uint16_t crc = 0; +void ICACHE_FLASH_ATTR +cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc) { + DBG("cmdResponse: cmd=%d val=%ld argc=%d\n", cmd, value, argc); uart0_write_char(SLIP_END); - CMD_ProtoWriteBuf((uint8_t*)&cmd, 2); - crc = crc16_data((uint8_t*)&cmd, 2, crc); - CMD_ProtoWriteBuf((uint8_t*)&callback, 4); - crc = crc16_data((uint8_t*)&callback, 4, crc); - CMD_ProtoWriteBuf((uint8_t*)&_return, 4); - crc = crc16_data((uint8_t*)&_return, 4, crc); - CMD_ProtoWriteBuf((uint8_t*)&argc, 2); - crc = crc16_data((uint8_t*)&argc, 2, crc); - return crc; + cmdProtoWriteBuf((uint8_t*)&cmd, 2); + resp_crc = crc16_data((uint8_t*)&cmd, 2, 0); + cmdProtoWriteBuf((uint8_t*)&argc, 2); + resp_crc = crc16_data((uint8_t*)&argc, 2, resp_crc); + cmdProtoWriteBuf((uint8_t*)&value, 4); + resp_crc = crc16_data((uint8_t*)&value, 4, resp_crc); } // Adds data to a response, returns the partial CRC -uint16_t ICACHE_FLASH_ATTR -CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len) { - short pad_len = len+3 - (len+3)%4; // round up to multiple of 4 - CMD_ProtoWriteBuf((uint8_t*)&pad_len, 2); - crc_in = crc16_data((uint8_t*)&pad_len, 2, crc_in); +void ICACHE_FLASH_ATTR +cmdResponseBody(void *data, uint16_t len) { + cmdProtoWriteBuf((uint8_t*)&len, 2); + resp_crc = crc16_data((uint8_t*)&len, 2, resp_crc); - CMD_ProtoWriteBuf(data, len); - crc_in = crc16_data(data, len, crc_in); + cmdProtoWriteBuf(data, len); + resp_crc = crc16_data(data, len, resp_crc); - if (pad_len > len) { + uint16_t pad = (4-(len&3))&3; // get to multiple of 4 + if (pad > 0) { uint32_t temp = 0; - CMD_ProtoWriteBuf((uint8_t*)&temp, pad_len-len); - crc_in = crc16_data((uint8_t*)&temp, pad_len-len, crc_in); + cmdProtoWriteBuf((uint8_t*)&temp, pad); + resp_crc = crc16_data((uint8_t*)&temp, pad, resp_crc); } - - return crc_in; } // Ends a response void ICACHE_FLASH_ATTR -CMD_ResponseEnd(uint16_t crc) { - CMD_ProtoWriteBuf((uint8_t*)&crc, 2); +cmdResponseEnd() { + cmdProtoWriteBuf((uint8_t*)&resp_crc, 2); uart0_write_char(SLIP_END); } //===== serial -> ESP commands // Execute a parsed command -static uint32_t ICACHE_FLASH_ATTR -CMD_Exec(const CmdList *scp, CmdPacket *packet) { - uint16_t crc = 0; +static void ICACHE_FLASH_ATTR +cmdExec(const CmdList *scp, CmdPacket *packet) { // Iterate through the command table and call the appropriate function while (scp->sc_function != NULL) { if(scp->sc_name == packet->cmd) { - DBG("CMD_Exec: Dispatching cmd=%s\n", cmd_names[packet->cmd]); + DBG("cmdExec: Dispatching cmd=%s\n", scp->sc_text); // call command function - uint32_t ret = scp->sc_function(packet); - // if requestor asked for a response, send it - if (packet->_return){ - DBG("CMD_Exec: Response: 0x%lx, cmd: %d\r\n", ret, packet->cmd); - crc = CMD_ResponseStart(packet->cmd, 0, ret, 0); - CMD_ResponseEnd(crc); - } else { - DBG("CMD_Exec: no response (%lu)\n", packet->_return); - } - return ret; + scp->sc_function(packet); + return; } scp++; } - DBG("CMD_Exec: cmd=%d not found\n", packet->cmd); - return 0; + DBG("cmdExec: cmd=%d not found\n", packet->cmd); } // Parse a packet and print info about it void ICACHE_FLASH_ATTR -CMD_parse_packet(uint8_t *buf, short len) { +cmdParsePacket(uint8_t *buf, short len) { // minimum command length - if (len < 12) return; + if (len < sizeof(CmdPacket)) return; // init pointers into buffer CmdPacket *packet = (CmdPacket*)buf; uint8_t *data_ptr = (uint8_t*)&packet->args; uint8_t *data_limit = data_ptr+len; - DBG("CMD_parse_packet: cmd=%d(%s) argc=%d cb=%p ret=%lu\n", + DBG("cmdParsePacket: cmd=%d argc=%d value=%lu\n", packet->cmd, - cmd_names[packet->cmd], packet->argc, - (void *)packet->callback, - packet->_return + packet->value ); #if 0 @@ -141,7 +119,7 @@ CMD_parse_packet(uint8_t *buf, short len) { uint16_t argc = packet->argc; while (data_ptr+2 < data_limit && argc--) { short l = *(uint16_t*)data_ptr; - os_printf("CMD_parse_packet: arg[%d] len=%d:", argn++, l); + os_printf("cmdParsePacket: arg[%d] len=%d:", argn++, l); data_ptr += 2; while (data_ptr < data_limit && l--) { os_printf(" %02X", *data_ptr++); @@ -151,9 +129,9 @@ CMD_parse_packet(uint8_t *buf, short len) { #endif if (data_ptr <= data_limit) { - CMD_Exec(commands, packet); + cmdExec(commands, packet); } else { - DBG("CMD_parse_packet: packet length overrun, parsing arg %d\n", packet->argc); + DBG("cmdParsePacket: packet length overrun, parsing arg %d\n", packet->argc); } } @@ -161,7 +139,7 @@ CMD_parse_packet(uint8_t *buf, short len) { // Fill out a CmdRequest struct given a CmdPacket void ICACHE_FLASH_ATTR -CMD_Request(CmdRequest *req, CmdPacket* cmd) { +cmdRequest(CmdRequest *req, CmdPacket* cmd) { req->cmd = cmd; req->arg_num = 0; req->arg_ptr = (uint8_t*)&cmd->args; @@ -169,14 +147,14 @@ CMD_Request(CmdRequest *req, CmdPacket* cmd) { // Return the number of arguments given a command struct uint32_t ICACHE_FLASH_ATTR -CMD_GetArgc(CmdRequest *req) { +cmdGetArgc(CmdRequest *req) { return req->cmd->argc; } // Copy the next argument from a command structure into the data pointer, returns 0 on success // -1 on error int32_t ICACHE_FLASH_ATTR -CMD_PopArg(CmdRequest *req, void *data, uint16_t len) { +cmdPopArg(CmdRequest *req, void *data, uint16_t len) { uint16_t length; if (req->arg_num >= req->cmd->argc) @@ -187,7 +165,7 @@ CMD_PopArg(CmdRequest *req, void *data, uint16_t len) { req->arg_ptr += 2; os_memcpy(data, req->arg_ptr, length); - req->arg_ptr += length; + req->arg_ptr += (length+3)&~3; // round up to multiple of 4 req->arg_num ++; return 0; @@ -195,7 +173,7 @@ CMD_PopArg(CmdRequest *req, void *data, uint16_t len) { // Skip the next argument void ICACHE_FLASH_ATTR -CMD_SkipArg(CmdRequest *req) { +cmdSkipArg(CmdRequest *req) { uint16_t length; if (req->arg_num >= req->cmd->argc) return; @@ -203,12 +181,12 @@ CMD_SkipArg(CmdRequest *req) { length = *(uint16_t*)req->arg_ptr; req->arg_ptr += 2; - req->arg_ptr += length; + req->arg_ptr += (length+3)&~3; req->arg_num ++; } // Return the length of the next argument uint16_t ICACHE_FLASH_ATTR -CMD_ArgLen(CmdRequest *req) { +cmdArgLen(CmdRequest *req) { return *(uint16_t*)req->arg_ptr; } diff --git a/cmd/cmd.h b/cmd/cmd.h index d18d90d..42e6fe0 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -19,9 +19,8 @@ typedef struct __attribute__((__packed__)) { typedef struct __attribute__((__packed__)) { uint16_t cmd; // command to perform, from CmdName enum - uint32_t callback; // callback pointer to embed in response - uint32_t _return; // token to embed in response uint16_t argc; // number of arguments to command + uint32_t value; // callback pointer for response or first argument CmdArg args[0]; // really args[argc] } CmdPacket; @@ -34,65 +33,73 @@ typedef struct { typedef enum { CMD_NULL = 0, CMD_SYNC, // synchronize and clear - CMD_IS_READY, // health-check - CMD_WIFI_CONNECT, // (3) connect to AP (not honored in this implementation) - CMD_MQTT_SETUP, + CMD_RESP_V, // response with a value + CMD_RESP_CB, // response with a callback + CMD_WIFI_STATUS, // get the current wifi status + CMD_CB_ADD, + CMD_CB_EVENTS, + CMD_GET_TIME, // get current time in seconds since the unix epoch + + CMD_MQTT_SETUP = 10, CMD_MQTT_CONNECT, CMD_MQTT_DISCONNECT, CMD_MQTT_PUBLISH, CMD_MQTT_SUBSCRIBE, CMD_MQTT_LWT, CMD_MQTT_EVENTS, - CMD_REST_SETUP, // (11) + + CMD_REST_SETUP = 20, CMD_REST_REQUEST, CMD_REST_SETHEADER, CMD_REST_EVENTS, - CMD_CB_ADD, // 15 - CMD_CB_EVENTS } CmdName; -typedef uint32_t (*cmdfunc_t)(CmdPacket *cmd); +typedef void (*cmdfunc_t)(CmdPacket *cmd); typedef struct { - CmdName sc_name; - cmdfunc_t sc_function; + CmdName sc_name; // name as CmdName enum + char *sc_text; // name as string + cmdfunc_t sc_function; // pointer to function } CmdList; #define CMD_CBNLEN 16 typedef struct { char name[CMD_CBNLEN]; uint32_t callback; -} cmdCallback; +} CmdCallback; // Used by slip protocol to cause parsing of a received packet -void CMD_parse_packet(uint8_t *buf, short len); +void cmdParsePacket(uint8_t *buf, short len); // Return the info about a callback to the attached uC by name, these are callbacks that the // attached uC registers using the ADD_SENSOR command -cmdCallback* CMD_GetCbByName(char* name); +CmdCallback* cmdGetCbByName(char* name); + +// Add a callback +uint32_t cmdAddCb(char *name, uint32_t callback); // Responses -// Start a response, returns the partial CRC -uint16_t CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc); -// Adds data to a response, returns the partial CRC -uint16_t CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len); +// Start a response +void cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc); +// Adds data to a response +void cmdResponseBody(void* data, uint16_t len); // Ends a response -void CMD_ResponseEnd(uint16_t crc); +void cmdResponseEnd(); -//void CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]); +//void cmdResponse(uint16_t cmd, uint32_t callback, uint32_t value, uint16_t argc, CmdArg* args[]); // Requests // Fill out a CmdRequest struct given a CmdPacket -void CMD_Request(CmdRequest *req, CmdPacket* cmd); +void cmdRequest(CmdRequest *req, CmdPacket* cmd); // Return the number of arguments given a request -uint32_t CMD_GetArgc(CmdRequest *req); +uint32_t cmdGetArgc(CmdRequest *req); // Return the length of the next argument -uint16_t CMD_ArgLen(CmdRequest *req); +uint16_t cmdArgLen(CmdRequest *req); // Copy next arg from request into the data pointer, returns 0 on success, -1 on error -int32_t CMD_PopArg(CmdRequest *req, void *data, uint16_t len); +int32_t cmdPopArg(CmdRequest *req, void *data, uint16_t len); // Skip next arg -void CMD_SkipArg(CmdRequest *req); +void cmdSkipArg(CmdRequest *req); #endif diff --git a/cmd/handlers.c b/cmd/handlers.c index 62d2f94..cb4aa21 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -18,10 +18,10 @@ #define DBG(format, ...) do { } while(0) #endif -static uint32_t CMD_Null(CmdPacket *cmd); -//static uint32_t CMD_Reset(CmdPacket *cmd); -static uint32_t CMD_WifiConnect(CmdPacket *cmd); -static uint32_t CMD_AddCallback(CmdPacket *cmd); +static void cmdNull(CmdPacket *cmd); +static void cmdSync(CmdPacket *cmd); +static void cmdWifiStatus(CmdPacket *cmd); +static void cmdAddCallback(CmdPacket *cmd); // keep track of last status sent to uC so we can notify it when it changes static uint8_t lastWifiStatus = wifiIsDisconnected; @@ -29,133 +29,145 @@ static bool wifiCbAdded = false; // Command dispatch table for serial -> ESP commands const CmdList commands[] = { - {CMD_NULL, CMD_Null}, // no-op - //{CMD_SYNC, CMD_Sync}, // synchronize - //{CMD_IS_READY, CMD_IsReady}, - {CMD_WIFI_CONNECT, CMD_WifiConnect}, + {CMD_NULL, "NULL", cmdNull}, // no-op + {CMD_SYNC, "SYNC", cmdSync}, // synchronize + {CMD_WIFI_STATUS, "WIFI_STATUS", cmdWifiStatus}, + {CMD_CB_ADD, "ADD_CB", cmdAddCallback}, #ifdef MQTT - {CMD_MQTT_SETUP, MQTTCMD_Setup}, - {CMD_MQTT_CONNECT, MQTTCMD_Connect}, - {CMD_MQTT_DISCONNECT, MQTTCMD_Disconnect}, - {CMD_MQTT_PUBLISH, MQTTCMD_Publish}, - {CMD_MQTT_SUBSCRIBE , MQTTCMD_Subscribe}, - {CMD_MQTT_LWT, MQTTCMD_Lwt}, + {CMD_MQTT_SETUP, "MQTT_SETUP", MQTTCMD_Setup}, + {CMD_MQTT_CONNECT, "MQTT_CONN", MQTTCMD_Connect}, + {CMD_MQTT_DISCONNECT, "MQTT_DISCON", MQTTCMD_Disconnect}, + {CMD_MQTT_PUBLISH, "MQTT_PUB", MQTTCMD_Publish}, + {CMD_MQTT_SUBSCRIBE , "MQTT_SUB", MQTTCMD_Subscribe}, + {CMD_MQTT_LWT, "MQTT_LWT", MQTTCMD_Lwt}, #endif #ifdef REST - {CMD_REST_SETUP, REST_Setup}, - {CMD_REST_REQUEST, REST_Request}, - {CMD_REST_SETHEADER, REST_SetHeader}, + {CMD_REST_SETUP, "REST_SETUP", REST_Setup}, + {CMD_REST_REQUEST, "REST_REQ", REST_Request}, + {CMD_REST_SETHEADER, "REST_SETHDR", REST_SetHeader}, #endif - {CMD_CB_ADD, CMD_AddCallback}, - {CMD_NULL, NULL} }; +//===== List of registered callbacks (to uC) + // WifiCb plus 10 for sensors #define MAX_CALLBACKS 12 -cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset - -// Command handler for Null command -static uint32_t ICACHE_FLASH_ATTR -CMD_Null(CmdPacket *cmd) { - return 1; -} - -#if 0 -// Command handler for Reset command, this was originally to reset the ESP but we don't want to -// do that in esp-link. It is still good to clear any information the ESP has about the attached -// uC. -static uint32_t ICACHE_FLASH_ATTR -CMD_Reset(CmdPacket *cmd) { - // clear callbacks table - os_memset(callbacks, 0, sizeof(callbacks)); - return 1; -} -#endif +CmdCallback callbacks[MAX_CALLBACKS]; // cleared in cmdSync -static uint32_t ICACHE_FLASH_ATTR -CMD_AddCb(char* name, uint32_t cb) { +uint32_t ICACHE_FLASH_ATTR +cmdAddCb(char* name, uint32_t cb) { for (uint8_t i = 0; i < MAX_CALLBACKS; i++) { - //os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, + //os_printf("cmdAddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, // (void *)callbacks[i].callback); // find existing callback or add to the end if (os_strncmp(callbacks[i].name, name, CMD_CBNLEN) == 0 || callbacks[i].name[0] == '\0') { os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name)); callbacks[i].name[CMD_CBNLEN-1] = 0; // strncpy doesn't null terminate callbacks[i].callback = cb; - DBG("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i); + DBG("cmdAddCb: '%s'->0x%lx added at %d\n", callbacks[i].name, cb, i); return 1; } } return 0; } -cmdCallback* ICACHE_FLASH_ATTR -CMD_GetCbByName(char* name) { +CmdCallback* ICACHE_FLASH_ATTR +cmdGetCbByName(char* name) { for (uint8_t i = 0; i < MAX_CALLBACKS; i++) { - //os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, + //os_printf("cmdGetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, // (void *)callbacks[i].callback); // if callback doesn't exist or it's null if (os_strncmp(callbacks[i].name, name, CMD_CBNLEN) == 0) { - DBG("CMD_GetCbByName: cb %s found at index %d\n", name, i); + DBG("cmdGetCbByName: cb %s found at index %d\n", name, i); return &callbacks[i]; } } - os_printf("CMD_GetCbByName: cb %s not found\n", name); + os_printf("cmdGetCbByName: cb %s not found\n", name); return 0; } +//===== Wifi callback + // Callback from wifi subsystem to notify us of status changes static void ICACHE_FLASH_ATTR -CMD_WifiCb(uint8_t wifiStatus) { +cmdWifiCb(uint8_t wifiStatus) { if (wifiStatus != lastWifiStatus){ - DBG("CMD_WifiCb: wifiStatus=%d\n", wifiStatus); + DBG("cmdWifiCb: wifiStatus=%d\n", wifiStatus); lastWifiStatus = wifiStatus; - cmdCallback *wifiCb = CMD_GetCbByName("wifiCb"); + CmdCallback *wifiCb = cmdGetCbByName("wifiCb"); if ((uint32_t)wifiCb->callback != -1) { uint8_t status = wifiStatus == wifiGotIP ? 5 : 1; - uint16_t crc = CMD_ResponseStart(CMD_WIFI_CONNECT, (uint32_t)wifiCb->callback, 0, 1); - crc = CMD_ResponseBody(crc, (uint8_t*)&status, 1); - CMD_ResponseEnd(crc); + cmdResponseStart(CMD_RESP_CB, (uint32_t)wifiCb->callback, 1); + cmdResponseBody((uint8_t*)&status, 1); + cmdResponseEnd(); } } } -// Command handler for Wifi connect command -static uint32_t ICACHE_FLASH_ATTR -CMD_WifiConnect(CmdPacket *cmd) { +//===== Command handlers + +// Command handler for Null command +static void ICACHE_FLASH_ATTR +cmdNull(CmdPacket *cmd) { +} + +// Command handler for sync command +static void ICACHE_FLASH_ATTR +cmdSync(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); - if(cmd->argc != 2 || cmd->callback == 0) - return 0; + cmdRequest(&req, cmd); + if(cmd->argc != 0 || cmd->value == 0) { + cmdResponseStart(CMD_RESP_V, 0, 0); + cmdResponseEnd(); + return; + } + // clear callbacks table + os_memset(callbacks, 0, sizeof(callbacks)); + + // register our callback with wifi subsystem if (!wifiCbAdded) { - wifiAddStateChangeCb(CMD_WifiCb); // register our callback with wifi subsystem + wifiAddStateChangeCb(cmdWifiCb); wifiCbAdded = true; } - CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback + + // send OK response + cmdResponseStart(CMD_RESP_V, cmd->value, 0); + cmdResponseEnd(); + + // save the MCU's callback and trigger an initial callback + cmdAddCb("wifiCb", cmd->value); lastWifiStatus = 0xff; // set to invalid value so we immediately send status cb in all cases - CMD_WifiCb(wifiState); + cmdWifiCb(wifiState); + + return; +} - return 1; +// Command handler for wifi status command +static void ICACHE_FLASH_ATTR +cmdWifiStatus(CmdPacket *cmd) { + cmdResponseStart(CMD_RESP_V, wifiState, 0); + cmdResponseEnd(); + return; } + // Command handler to add a callback to the named-callbacks list, this is for a callback to the uC -static uint32_t ICACHE_FLASH_ATTR -CMD_AddCallback(CmdPacket *cmd) { +static void ICACHE_FLASH_ATTR +cmdAddCallback(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); - if (cmd->argc != 1 || cmd->callback == 0) - return 0; + cmdRequest(&req, cmd); + if (cmd->argc != 1 || cmd->value == 0) return; char name[16]; uint16_t len; - // get the sensor name - len = CMD_ArgLen(&req); - if (len > 15) return 0; // max size of name is 15 characters - if (CMD_PopArg(&req, (uint8_t *)name, len)) return 0; + // get the callback name + len = cmdArgLen(&req); + if (len > 15) return; // max size of name is 15 characters + if (cmdPopArg(&req, (uint8_t *)name, len)) return; name[len] = 0; - DBG("CMD_AddCallback: name=%s\n", name); + DBG("cmdAddCallback: name=%s\n", name); - return CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback + cmdAddCb(name, cmd->value); // save the sensor callback } diff --git a/mqtt/mqtt_cmd.c b/mqtt/mqtt_cmd.c index 85cdfa1..4571f0a 100644 --- a/mqtt/mqtt_cmd.c +++ b/mqtt/mqtt_cmd.c @@ -30,8 +30,8 @@ cmdMqttConnectedCb(uint32_t* args) { (void*)cb->disconnectedCb, (void*)cb->publishedCb, (void*)cb->dataCb); - uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0); - CMD_ResponseEnd(crc); + uint16_t crc = cmdResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0); + cmdResponseEnd(crc); } void ICACHE_FLASH_ATTR @@ -39,8 +39,8 @@ cmdMqttDisconnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; DBG("MQTT: Disconnected\n"); - uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->disconnectedCb, 0, 0); - CMD_ResponseEnd(crc); + uint16_t crc = cmdResponseStart(CMD_MQTT_EVENTS, cb->disconnectedCb, 0, 0); + cmdResponseEnd(crc); } void ICACHE_FLASH_ATTR @@ -48,8 +48,8 @@ cmdMqttPublishedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; DBG("MQTT: Published\n"); - uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->publishedCb, 0, 0); - CMD_ResponseEnd(crc); + uint16_t crc = cmdResponseStart(CMD_MQTT_EVENTS, cb->publishedCb, 0, 0); + cmdResponseEnd(crc); } void ICACHE_FLASH_ATTR @@ -58,23 +58,23 @@ cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; - crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->dataCb, 0, 2); - crc = CMD_ResponseBody(crc, (uint8_t*)topic, topic_len); - crc = CMD_ResponseBody(crc, (uint8_t*)data, data_len); - CMD_ResponseEnd(crc); + crc = cmdResponseStart(CMD_MQTT_EVENTS, cb->dataCb, 0, 2); + crc = cmdResponseBody(crc, (uint8_t*)topic, topic_len); + crc = cmdResponseBody(crc, (uint8_t*)data, data_len); + cmdResponseEnd(crc); } uint32_t ICACHE_FLASH_ATTR MQTTCMD_Lwt(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); + cmdRequest(&req, cmd); - if (CMD_GetArgc(&req) != 5) + if (cmdGetArgc(&req) != 5) return 0; // get mqtt client uint32_t client_ptr; - CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + cmdPopArg(&req, (uint8_t*)&client_ptr, 4); #ifdef MQTT_1_CLIENT MQTT_Client* client = &mqttClient; #else @@ -91,24 +91,24 @@ MQTTCMD_Lwt(CmdPacket *cmd) { uint16_t len; // get topic - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 128) return 0; // safety check client->connect_info.will_topic = (char*)os_zalloc(len + 1); - CMD_PopArg(&req, client->connect_info.will_topic, len); + cmdPopArg(&req, client->connect_info.will_topic, len); client->connect_info.will_topic[len] = 0; // get message - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 128) return 0; // safety check client->connect_info.will_message = (char*)os_zalloc(len + 1); - CMD_PopArg(&req, client->connect_info.will_message, len); + cmdPopArg(&req, client->connect_info.will_message, len); client->connect_info.will_message[len] = 0; // get qos - CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_qos, 4); + cmdPopArg(&req, (uint8_t*)&client->connect_info.will_qos, 4); // get retain - CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); + cmdPopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); DBG("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%d, retain=%d\n", client->connect_info.will_topic, @@ -124,14 +124,14 @@ MQTTCMD_Lwt(CmdPacket *cmd) { uint32_t ICACHE_FLASH_ATTR MQTTCMD_Publish(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); + cmdRequest(&req, cmd); - if (CMD_GetArgc(&req) != 6) + if (cmdGetArgc(&req) != 6) return 0; // get mqtt client uint32_t client_ptr; - CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + cmdPopArg(&req, (uint8_t*)&client_ptr, 4); #ifdef MQTT_1_CLIENT MQTT_Client* client = &mqttClient; #else @@ -142,33 +142,33 @@ MQTTCMD_Publish(CmdPacket *cmd) { uint16_t len; // get topic - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 128) return 0; // safety check uint8_t *topic = (uint8_t*)os_zalloc(len + 1); - CMD_PopArg(&req, topic, len); + cmdPopArg(&req, topic, len); topic[len] = 0; // get data - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); uint8_t *data = (uint8_t*)os_zalloc(len+1); if (!data) { // safety check os_free(topic); return 0; } - CMD_PopArg(&req, data, len); + cmdPopArg(&req, data, len); data[len] = 0; uint32_t qos, retain, data_len; // get data length // this isn't used but we have to pull it off the stack - CMD_PopArg(&req, (uint8_t*)&data_len, 4); + cmdPopArg(&req, (uint8_t*)&data_len, 4); // get qos - CMD_PopArg(&req, (uint8_t*)&qos, 4); + cmdPopArg(&req, (uint8_t*)&qos, 4); // get retain - CMD_PopArg(&req, (uint8_t*)&retain, 4); + cmdPopArg(&req, (uint8_t*)&retain, 4); DBG("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%ld, retain=%ld\n", topic, @@ -185,14 +185,14 @@ MQTTCMD_Publish(CmdPacket *cmd) { uint32_t ICACHE_FLASH_ATTR MQTTCMD_Subscribe(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); + cmdRequest(&req, cmd); - if (CMD_GetArgc(&req) != 3) + if (cmdGetArgc(&req) != 3) return 0; // get mqtt client uint32_t client_ptr; - CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + cmdPopArg(&req, (uint8_t*)&client_ptr, 4); #ifdef MQTT_1_CLIENT MQTT_Client* client = &mqttClient; #else @@ -203,15 +203,15 @@ MQTTCMD_Subscribe(CmdPacket *cmd) { uint16_t len; // get topic - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 128) return 0; // safety check uint8_t* topic = (uint8_t*)os_zalloc(len + 1); - CMD_PopArg(&req, topic, len); + cmdPopArg(&req, topic, len); topic[len] = 0; // get qos uint32_t qos = 0; - CMD_PopArg(&req, (uint8_t*)&qos, 4); + cmdPopArg(&req, (uint8_t*)&qos, 4); DBG("MQTT: MQTTCMD_Subscribe topic=%s, qos=%ld\n", topic, qos); @@ -223,17 +223,17 @@ MQTTCMD_Subscribe(CmdPacket *cmd) { uint32_t ICACHE_FLASH_ATTR MQTTCMD_Setup(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); + cmdRequest(&req, cmd); #ifdef MQTT_1_CLIENT MQTT_Client* client = &mqttClient; - CMD_SkipArg(&req); - CMD_SkipArg(&req); - CMD_SkipArg(&req); - CMD_SkipArg(&req); - CMD_SkipArg(&req); + cmdSkipArg(&req); + cmdSkipArg(&req); + cmdSkipArg(&req); + cmdSkipArg(&req); + cmdSkipArg(&req); #else - if (CMD_GetArgc(&req) != 9) + if (cmdGetArgc(&req) != 9) return 0; // create mqtt client @@ -247,31 +247,31 @@ MQTTCMD_Setup(CmdPacket *cmd) { uint32_t keepalive, clean_session; // get client id - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 32) return 0; // safety check client_id = (uint8_t*)os_zalloc(len + 1); - CMD_PopArg(&req, client_id, len); + cmdPopArg(&req, client_id, len); client_id[len] = 0; // get username - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 32) return 0; // safety check user_data = (uint8_t*)os_zalloc(len + 1); - CMD_PopArg(&req, user_data, len); + cmdPopArg(&req, user_data, len); user_data[len] = 0; // get password - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 32) return 0; // safety check pass_data = (uint8_t*)os_zalloc(len + 1); - CMD_PopArg(&req, pass_data, len); + cmdPopArg(&req, pass_data, len); pass_data[len] = 0; // get keepalive - CMD_PopArg(&req, (uint8_t*)&keepalive, 4); + cmdPopArg(&req, (uint8_t*)&keepalive, 4); // get clean session - CMD_PopArg(&req, (uint8_t*)&clean_session, 4); + cmdPopArg(&req, (uint8_t*)&clean_session, 4); #ifdef MQTTCMD_DBG DBG("MQTT: MQTTCMD_Setup clientid=%s, user=%s, pw=%s, keepalive=%ld, clean_session=%ld\n", client_id, user_data, pass_data, keepalive, clean_session); #endif @@ -289,13 +289,13 @@ MQTTCMD_Setup(CmdPacket *cmd) { MqttCmdCb* callback = (MqttCmdCb*)os_zalloc(sizeof(MqttCmdCb)); uint32_t cb_data; - CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + cmdPopArg(&req, (uint8_t*)&cb_data, 4); callback->connectedCb = cb_data; - CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + cmdPopArg(&req, (uint8_t*)&cb_data, 4); callback->disconnectedCb = cb_data; - CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + cmdPopArg(&req, (uint8_t*)&cb_data, 4); callback->publishedCb = cb_data; - CMD_PopArg(&req, (uint8_t*)&cb_data, 4); + cmdPopArg(&req, (uint8_t*)&cb_data, 4); callback->dataCb = cb_data; client->user_data = callback; @@ -311,7 +311,7 @@ MQTTCMD_Setup(CmdPacket *cmd) { uint32_t ICACHE_FLASH_ATTR MQTTCMD_Connect(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); + cmdRequest(&req, cmd); #ifdef MQTT_1_CLIENT @@ -325,12 +325,12 @@ MQTTCMD_Connect(CmdPacket *cmd) { return 1; #else - if (CMD_GetArgc(&req) != 4) + if (cmdGetArgc(&req) != 4) return 0; // get mqtt client uint32_t client_ptr; - CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + cmdPopArg(&req, (uint8_t*)&client_ptr, 4); MQTT_Client* client = (MQTT_Client*)client_ptr; DBG("MQTT: MQTTCMD_Connect client ptr=%p\n", (void*)client_ptr); @@ -339,17 +339,17 @@ MQTTCMD_Connect(CmdPacket *cmd) { // get host if (client->host) os_free(client->host); - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 128) return 0; // safety check client->host = (char*)os_zalloc(len + 1); - CMD_PopArg(&req, client->host, len); + cmdPopArg(&req, client->host, len); client->host[len] = 0; // get port - CMD_PopArg(&req, (uint8_t*)&client->port, 4); + cmdPopArg(&req, (uint8_t*)&client->port, 4); // get security - CMD_PopArg(&req, (uint8_t*)&client->security, 4); + cmdPopArg(&req, (uint8_t*)&client->security, 4); DBG("MQTT: MQTTCMD_Connect host=%s, port=%d, security=%d\n", client->host, client->port, @@ -363,18 +363,18 @@ MQTTCMD_Connect(CmdPacket *cmd) { uint32_t ICACHE_FLASH_ATTR MQTTCMD_Disconnect(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); + cmdRequest(&req, cmd); #ifdef MQTT_1_CLIENT return 1; #else - if (CMD_GetArgc(&req) != 1) + if (cmdGetArgc(&req) != 1) return 0; // get mqtt client uint32_t client_ptr; - CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); + cmdPopArg(&req, (uint8_t*)&client_ptr, 4); MQTT_Client* client = (MQTT_Client*)client_ptr; DBG("MQTT: MQTTCMD_Disconnect client ptr=%p\n", (void*)client_ptr); diff --git a/mqtt/mqtt_cmd.h b/mqtt/mqtt_cmd.h index 69997d1..6a14a63 100644 --- a/mqtt/mqtt_cmd.h +++ b/mqtt/mqtt_cmd.h @@ -10,11 +10,11 @@ typedef struct { uint32_t dataCb; } MqttCmdCb; -uint32_t MQTTCMD_Connect(CmdPacket *cmd); -uint32_t MQTTCMD_Disconnect(CmdPacket *cmd); -uint32_t MQTTCMD_Setup(CmdPacket *cmd); -uint32_t MQTTCMD_Publish(CmdPacket *cmd); -uint32_t MQTTCMD_Subscribe(CmdPacket *cmd); -uint32_t MQTTCMD_Lwt(CmdPacket *cmd); +void MQTTCMD_Connect(CmdPacket *cmd); +void MQTTCMD_Disconnect(CmdPacket *cmd); +void MQTTCMD_Setup(CmdPacket *cmd); +void MQTTCMD_Publish(CmdPacket *cmd); +void MQTTCMD_Subscribe(CmdPacket *cmd); +void MQTTCMD_Lwt(CmdPacket *cmd); #endif /* MODULES_MQTT_CMD_H_ */ diff --git a/rest/rest.c b/rest/rest.c index 4aec0a8..81e296a 100644 --- a/rest/rest.c +++ b/rest/rest.c @@ -3,6 +3,8 @@ // Adapted from: github.com/tuanpmt/esp_bridge, Created on: Mar 4, 2015, Author: Minh #include "esp8266.h" +#include "c_types.h" +#include "ip_addr.h" #include "rest.h" #include "cmd.h" @@ -12,6 +14,27 @@ #define DBG_REST(format, ...) do { } while(0) #endif +typedef enum { + HEADER_GENERIC = 0, + HEADER_CONTENT_TYPE, + HEADER_USER_AGENT +} HEADER_TYPE; + +typedef struct { + char *host; + uint32_t port; + uint32_t security; + ip_addr_t ip; + struct espconn *pCon; + char *header; + char *data; + uint16_t data_len; + uint16_t data_sent; + char *content_type; + char *user_agent; + uint32_t resp_cb; +} RestClient; + // Connection pool for REST clients. Attached MCU's just call REST_setup and this allocates // a connection, They never call any 'free' and given that the attached MCU could restart at @@ -32,7 +55,7 @@ tcpclient_recv(void *arg, char *pdata, unsigned short len) { // parse status line int pi = 0; - int32_t code = -1; + int16_t code = -1; char statusCode[4] = "\0\0\0\0"; int statusLen = 0; bool inStatus = false; @@ -69,15 +92,17 @@ tcpclient_recv(void *arg, char *pdata, unsigned short len) { //if (pi < len && pdata[pi] == '\r') pi++; // hacky! // collect body and send it - uint16_t crc; int body_len = len-pi; - DBG_REST("REST: status=%ld, body=%d\n", code, body_len); + DBG_REST("REST: status=%d, body=%d\n", code, body_len); if (pi == len) { - crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0); + cmdResponseStart(CMD_RESP_CB, client->resp_cb, 1); + cmdResponseBody(&code, sizeof(code)); + cmdResponseEnd(); } else { - crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1); - crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+pi), body_len); - CMD_ResponseEnd(crc); + cmdResponseStart(CMD_RESP_CB, client->resp_cb, 2); + cmdResponseBody(&code, sizeof(code)); + cmdResponseBody(pdata+pi, body_len>100?100:body_len); + cmdResponseEnd(); #if 0 os_printf("REST: body="); for (int j=pi; jpCon); //else espconn_disconnect(client->pCon); - } static void ICACHE_FLASH_ATTR @@ -173,33 +197,39 @@ rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { } } -uint32_t ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR REST_Setup(CmdPacket *cmd) { CmdRequest req; uint32_t port, security; + int32_t err = -1; // error code in case of failure // start parsing the command - CMD_Request(&req, cmd); - if(CMD_GetArgc(&req) != 3) return 0; + cmdRequest(&req, cmd); + if(cmdGetArgc(&req) != 3) goto fail; + err--; // get the hostname - uint16_t len = CMD_ArgLen(&req); - if (len > 128) return 0; // safety check + uint16_t len = cmdArgLen(&req); + if (len > 128) goto fail; // safety check + err--; uint8_t *rest_host = (uint8_t*)os_zalloc(len + 1); - if (CMD_PopArg(&req, rest_host, len)) return 0; + if (cmdPopArg(&req, rest_host, len)) goto fail; + err--; rest_host[len] = 0; // get the port - if (CMD_PopArg(&req, (uint8_t*)&port, 4)) { + if (cmdPopArg(&req, (uint8_t*)&port, 2)) { os_free(rest_host); - return 0; + goto fail; } + err--; // get the security mode - if (CMD_PopArg(&req, (uint8_t*)&security, 4)) { + if (cmdPopArg(&req, (uint8_t*)&security, 1)) { os_free(rest_host); - return 0; + goto fail; } + err--; // clear connection structures the first time if (restNum == 0xff) { @@ -224,7 +254,7 @@ REST_Setup(CmdPacket *cmd) { os_memset(client, 0, sizeof(RestClient)); DBG_REST("REST: setup #%d host=%s port=%ld security=%ld\n", clientNum, rest_host, port, security); - client->resp_cb = cmd->callback; + client->resp_cb = cmd->value; client->host = (char *)rest_host; client->port = port; @@ -249,35 +279,39 @@ REST_Setup(CmdPacket *cmd) { client->pCon->reverse = client; - return REST_CB | (uint32_t)clientNum; + cmdResponseStart(CMD_RESP_V, clientNum, 0); + cmdResponseEnd(); + return; + +fail: + cmdResponseStart(CMD_RESP_V, err, 0); + cmdResponseEnd(); + return; } -uint32_t ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR REST_SetHeader(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); + cmdRequest(&req, cmd); - if(CMD_GetArgc(&req) != 3) - return 0; + if(cmdGetArgc(&req) != 2) return; // Get client - uint32_t clientNum; - if (CMD_PopArg(&req, (uint8_t*)&clientNum, 4)) return 0; - if ((clientNum & 0xffff0000) != REST_CB) return 0; - RestClient *client = restClient + ((clientNum & 0xffff) % MAX_REST); + uint32_t clientNum = cmd->value; + RestClient *client = restClient + (clientNum % MAX_REST); // Get header selector uint32_t header_index; - if (CMD_PopArg(&req, (uint8_t*)&header_index, 4)) return 0; + if (cmdPopArg(&req, (uint8_t*)&header_index, 4)) return; // Get header value - uint16_t len = CMD_ArgLen(&req); - if (len > 256) return 0; //safety check + uint16_t len = cmdArgLen(&req); + if (len > 256) return; //safety check switch(header_index) { case HEADER_GENERIC: if(client->header) os_free(client->header); client->header = (char*)os_zalloc(len + 3); - CMD_PopArg(&req, (uint8_t*)client->header, len); + cmdPopArg(&req, (uint8_t*)client->header, len); client->header[len] = '\r'; client->header[len+1] = '\n'; client->header[len+2] = 0; @@ -286,7 +320,7 @@ REST_SetHeader(CmdPacket *cmd) { case HEADER_CONTENT_TYPE: if(client->content_type) os_free(client->content_type); client->content_type = (char*)os_zalloc(len + 3); - CMD_PopArg(&req, (uint8_t*)client->content_type, len); + cmdPopArg(&req, (uint8_t*)client->content_type, len); client->content_type[len] = '\r'; client->content_type[len+1] = '\n'; client->content_type[len+2] = 0; @@ -295,52 +329,50 @@ REST_SetHeader(CmdPacket *cmd) { case HEADER_USER_AGENT: if(client->user_agent) os_free(client->user_agent); client->user_agent = (char*)os_zalloc(len + 3); - CMD_PopArg(&req, (uint8_t*)client->user_agent, len); + cmdPopArg(&req, (uint8_t*)client->user_agent, len); client->user_agent[len] = '\r'; client->user_agent[len+1] = '\n'; client->user_agent[len+2] = 0; DBG_REST("REST: Set user_agent: %s\r\n", client->user_agent); break; } - return 1; } -uint32_t ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR REST_Request(CmdPacket *cmd) { CmdRequest req; - CMD_Request(&req, cmd); + cmdRequest(&req, cmd); DBG_REST("REST: request"); + if (cmd->argc != 2 && cmd->argc != 3) return; + // Get client - uint32_t clientNum; - if (CMD_PopArg(&req, (uint8_t*)&clientNum, 4)) goto fail; - if ((clientNum & 0xffff0000) != REST_CB) goto fail; - clientNum &= 0xffff; - RestClient *client = restClient + clientNum % MAX_REST; + uint32_t clientNum = cmd->value; + RestClient *client = restClient + (clientNum % MAX_REST); DBG_REST(" #%ld", clientNum); + // Get HTTP method - uint16_t len = CMD_ArgLen(&req); + uint16_t len = cmdArgLen(&req); if (len > 15) goto fail; char method[16]; - CMD_PopArg(&req, method, len); + cmdPopArg(&req, method, len); method[len] = 0; DBG_REST(" method=%s", method); + // Get HTTP path - len = CMD_ArgLen(&req); + len = cmdArgLen(&req); if (len > 1023) goto fail; char path[1024]; - CMD_PopArg(&req, path, len); + cmdPopArg(&req, path, len); path[len] = 0; DBG_REST(" path=%s", path); + // Get HTTP body uint32_t realLen = 0; - if (CMD_GetArgc(&req) == 3) { + if (cmdGetArgc(&req) == 2) { realLen = 0; - len = 0; } else { - CMD_PopArg(&req, (uint8_t*)&realLen, 4); - - len = CMD_ArgLen(&req); - if (len > 2048 || realLen > len) goto fail; + realLen = cmdArgLen(&req); + if (realLen > 2048) goto fail; } DBG_REST(" bodyLen=%ld", realLen); @@ -367,14 +399,14 @@ REST_Request(CmdPacket *cmd) { DBG_REST(" hdrLen=%d", client->data_len); if (realLen > 0) { - CMD_PopArg(&req, client->data + client->data_len, realLen); + cmdPopArg(&req, client->data + client->data_len, realLen); client->data_len += realLen; } DBG_REST("\n"); //DBG_REST("REST request: %s", (char*)client->data); - DBG_REST("REST: pCon state=%d\n", client->pCon->state); + //DBG_REST("REST: pCon state=%d\n", client->pCon->state); client->pCon->state = ESPCONN_NONE; espconn_regist_connectcb(client->pCon, tcpclient_connect_cb); espconn_regist_reconcb(client->pCon, tcpclient_recon_cb); @@ -392,9 +424,8 @@ REST_Request(CmdPacket *cmd) { espconn_gethostbyname(client->pCon, (char *)client->host, &client->ip, rest_dns_found); } - return 1; + return; fail: DBG_REST("\n"); - return 0; } diff --git a/rest/rest.h b/rest/rest.h index 7161e1f..281a9f4 100644 --- a/rest/rest.h +++ b/rest/rest.h @@ -8,33 +8,10 @@ #ifndef MODULES_API_H_ #define MODULES_API_H_ -#include "c_types.h" -#include "ip_addr.h" #include "cmd.h" -typedef enum { - HEADER_GENERIC = 0, - HEADER_CONTENT_TYPE, - HEADER_USER_AGENT -} HEADER_TYPE; - -typedef struct { - char *host; - uint32_t port; - uint32_t security; - ip_addr_t ip; - struct espconn *pCon; - char *header; - char *data; - uint16_t data_len; - uint16_t data_sent; - char *content_type; - char *user_agent; - uint32_t resp_cb; -} RestClient; - -uint32_t REST_Setup(CmdPacket *cmd); -uint32_t REST_Request(CmdPacket *cmd); -uint32_t REST_SetHeader(CmdPacket *cmd); +void REST_Setup(CmdPacket *cmd); +void REST_Request(CmdPacket *cmd); +void REST_SetHeader(CmdPacket *cmd); #endif /* MODULES_INCLUDE_API_H_ */ diff --git a/serial/slip.c b/serial/slip.c index cd88c1e..76d8f97 100644 --- a/serial/slip.c +++ b/serial/slip.c @@ -43,7 +43,7 @@ slip_process() { uint16_t crc = crc16_data((uint8_t*)slip_buf, slip_len-2, 0); uint16_t rcv = ((uint16_t)slip_buf[slip_len-2]) | ((uint16_t)slip_buf[slip_len-1] << 8); if (crc == rcv) { - CMD_parse_packet((uint8_t*)slip_buf, slip_len-2); + cmdParsePacket((uint8_t*)slip_buf, slip_len-2); } else { os_printf("SLIP: bad CRC, crc=%04x rcv=%04x len=%d\n", crc, rcv, slip_len); From 219b6c80064368cd0435c0160c816bdc86a1b1a2 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sun, 31 Jan 2016 17:39:15 -0800 Subject: [PATCH 35/37] fix mqtt for new slip protocol --- README.md | 17 ++++- cmd/cmd.c | 4 +- cmd/cmd.h | 14 ++-- cmd/handlers.c | 2 - esp-link/mqtt_client.c | 23 +++--- esp-link/status.c | 2 +- mqtt/mqtt.c | 25 ++++--- mqtt/mqtt.h | 12 +-- mqtt/mqtt_cmd.c | 166 +++++++++++++++-------------------------- 9 files changed, 112 insertions(+), 153 deletions(-) diff --git a/README.md b/README.md index 44e97f3..f817ae9 100644 --- a/README.md +++ b/README.md @@ -235,18 +235,29 @@ Troubleshooting Building the firmware --------------------- The firmware has been built using the [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk) -on a Linux system. Create an esp8266 directory, install the esp-open-sdk into a sub-directory. +on a Linux system. Create an esp8266 directory, install the esp-open-sdk into a sub-directory +using the *non-standalone* install (i.e., there should not be an sdk directory in the esp-open-sdk +dir when done installing, if you use the standalone install you will get compilation errors +with std types, such as `uint32_t`). + Download the Espressif SDK (use the version mentioned in the release notes) from their [download forum](http://bbs.espressif.com/viewforum.php?f=5) and also expand it into a -sub-directory. Then clone the esp-link repository into a third sub-directory. +sub-directory. + +Clone the esp-link repository into a third sub-directory and check out the tag you would like, +such as `git checkout v2.1.7`. This way the relative paths in the Makefile will work. If you choose a different directory structure look at the Makefile for the appropriate environment variables to define. +Do not use the source tarballs from the release page on github, +these will give you trouble compiling because the Makefile uses git to determine the esp-link +version being built. In order to OTA-update the esp8266 you should `export ESP_HOSTNAME=...` with the hostname or IP address of your module. -Now, build the code: `make` in the top-level of esp-link. +Now, build the code: `make` in the top-level of esp-link. If you want to se the commands being +issued, use `VERBOSE=1 make`. A few notes from others (I can't fully verify these): - You may need to install `zlib1g-dev` and `python-serial` diff --git a/cmd/cmd.c b/cmd/cmd.c index 52ee3b3..714d185 100644 --- a/cmd/cmd.c +++ b/cmd/cmd.c @@ -34,7 +34,7 @@ cmdProtoWrite(uint8_t data) { } static void ICACHE_FLASH_ATTR -cmdProtoWriteBuf(uint8_t *data, short len) { +cmdProtoWriteBuf(const uint8_t *data, short len) { while (len--) cmdProtoWrite(*data++); } @@ -56,7 +56,7 @@ cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc) { // Adds data to a response, returns the partial CRC void ICACHE_FLASH_ATTR -cmdResponseBody(void *data, uint16_t len) { +cmdResponseBody(const void *data, uint16_t len) { cmdProtoWriteBuf((uint8_t*)&len, 2); resp_crc = crc16_data((uint8_t*)&len, 2, resp_crc); diff --git a/cmd/cmd.h b/cmd/cmd.h index 42e6fe0..6b4b018 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -40,18 +40,14 @@ typedef enum { CMD_CB_EVENTS, CMD_GET_TIME, // get current time in seconds since the unix epoch - CMD_MQTT_SETUP = 10, - CMD_MQTT_CONNECT, - CMD_MQTT_DISCONNECT, - CMD_MQTT_PUBLISH, - CMD_MQTT_SUBSCRIBE, - CMD_MQTT_LWT, - CMD_MQTT_EVENTS, + CMD_MQTT_SETUP = 10, // set-up callbacks + CMD_MQTT_PUBLISH, // publish a message + CMD_MQTT_SUBSCRIBE, // subscribe to a topic + CMD_MQTT_LWT, // set the last-will-topic and messge CMD_REST_SETUP = 20, CMD_REST_REQUEST, CMD_REST_SETHEADER, - CMD_REST_EVENTS, } CmdName; typedef void (*cmdfunc_t)(CmdPacket *cmd); @@ -83,7 +79,7 @@ uint32_t cmdAddCb(char *name, uint32_t callback); // Start a response void cmdResponseStart(uint16_t cmd, uint32_t value, uint16_t argc); // Adds data to a response -void cmdResponseBody(void* data, uint16_t len); +void cmdResponseBody(const void* data, uint16_t len); // Ends a response void cmdResponseEnd(); diff --git a/cmd/handlers.c b/cmd/handlers.c index cb4aa21..2b9c1ad 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -35,8 +35,6 @@ const CmdList commands[] = { {CMD_CB_ADD, "ADD_CB", cmdAddCallback}, #ifdef MQTT {CMD_MQTT_SETUP, "MQTT_SETUP", MQTTCMD_Setup}, - {CMD_MQTT_CONNECT, "MQTT_CONN", MQTTCMD_Connect}, - {CMD_MQTT_DISCONNECT, "MQTT_DISCON", MQTTCMD_Disconnect}, {CMD_MQTT_PUBLISH, "MQTT_PUB", MQTTCMD_Publish}, {CMD_MQTT_SUBSCRIBE , "MQTT_SUB", MQTTCMD_Subscribe}, {CMD_MQTT_LWT, "MQTT_LWT", MQTTCMD_Lwt}, diff --git a/esp-link/mqtt_client.c b/esp-link/mqtt_client.c index 12b9a8d..f0d9108 100644 --- a/esp-link/mqtt_client.c +++ b/esp-link/mqtt_client.c @@ -18,34 +18,31 @@ static MqttCallback published_cb; static MqttDataCallback data_cb; void ICACHE_FLASH_ATTR -mqttConnectedCb(uint32_t *args) { +mqttConnectedCb(MQTT_Client* client) { DBG("MQTT Client: Connected\n"); - //MQTT_Client* client = (MQTT_Client*)args; //MQTT_Subscribe(client, "system/time", 0); // handy for testing if (connected_cb) - connected_cb(args); + connected_cb(client); } void ICACHE_FLASH_ATTR -mqttDisconnectedCb(uint32_t *args) { -// MQTT_Client* client = (MQTT_Client*)args; +mqttDisconnectedCb(MQTT_Client* client) { DBG("MQTT Client: Disconnected\n"); if (disconnected_cb) - disconnected_cb(args); + disconnected_cb(client); } void ICACHE_FLASH_ATTR -mqttPublishedCb(uint32_t *args) { -// MQTT_Client* client = (MQTT_Client*)args; +mqttPublishedCb(MQTT_Client* client) { DBG("MQTT Client: Published\n"); if (published_cb) - published_cb(args); + published_cb(client); } void ICACHE_FLASH_ATTR -mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { - // MQTT_Client* client = (MQTT_Client*)args; - +mqttDataCb(MQTT_Client* client, const char* topic, uint32_t topic_len, + const char *data, uint32_t data_len) +{ #ifdef MQTTCLIENT_DBG char *topicBuf = (char*)os_zalloc(topic_len + 1); char *dataBuf = (char*)os_zalloc(data_len + 1); @@ -62,7 +59,7 @@ mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *da #endif if (data_cb) - data_cb(args, topic, topic_len, data, data_len); + data_cb(client, topic, topic_len, data, data_len); } void ICACHE_FLASH_ATTR diff --git a/esp-link/status.c b/esp-link/status.c index a49726f..6905850 100644 --- a/esp-link/status.c +++ b/esp-link/status.c @@ -37,7 +37,7 @@ static void ICACHE_FLASH_ATTR mqttStatusCb(void *v) { char buf[128]; mqttStatusMsg(buf); - MQTT_Publish(&mqttClient, flashConfig.mqtt_status_topic, buf, 1, 0); + MQTT_Publish(&mqttClient, flashConfig.mqtt_status_topic, buf, os_strlen(buf), 1, 0); } diff --git a/mqtt/mqtt.c b/mqtt/mqtt.c index d3d174e..30e2dd0 100644 --- a/mqtt/mqtt.c +++ b/mqtt/mqtt.c @@ -89,9 +89,9 @@ deliver_publish(MQTT_Client* client, uint8_t* message, uint16_t length) { // callback to client if (client->dataCb) - client->dataCb((uint32_t*)client, topic, topic_length, data, data_length); + client->dataCb(client, topic, topic_length, data, data_length); if (client->cmdDataCb) - client->cmdDataCb((uint32_t*)client, topic, topic_length, data, data_length); + client->cmdDataCb(client, topic, topic_length, data, data_length); } /** @@ -164,8 +164,8 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_CONNACK: //DBG_MQTT("MQTT: Connect successful\n"); // callbacks for internal and external clients - if (client->connectedCb) client->connectedCb((uint32_t*)client); - if (client->cmdConnectedCb) client->cmdConnectedCb((uint32_t*)client); + if (client->connectedCb) client->connectedCb(client); + if (client->cmdConnectedCb) client->cmdConnectedCb(client); client->reconTimeout = 1; // reset the reconnect backoff break; @@ -357,8 +357,8 @@ mqtt_tcpclient_discon_cb(void* arg) { // if this is an aborted connection we're done if (client == NULL) return; DBG_MQTT("MQTT: Disconnected from %s:%d\n", client->host, client->port); - if (client->disconnectedCb) client->disconnectedCb((uint32_t*)client); - if (client->cmdDisconnectedCb) client->cmdDisconnectedCb((uint32_t*)client); + if (client->disconnectedCb) client->disconnectedCb(client); + if (client->cmdDisconnectedCb) client->cmdDisconnectedCb(client); // reconnect unless we're in a permanently disconnected state if (client->connState == MQTT_DISCONNECTED) return; @@ -380,8 +380,8 @@ mqtt_tcpclient_recon_cb(void* arg, int8_t err) { if (pespconn->proto.tcp) os_free(pespconn->proto.tcp); os_free(pespconn); os_printf("MQTT: Connection reset from %s:%d\n", client->host, client->port); - if (client->disconnectedCb) client->disconnectedCb((uint32_t*)client); - if (client->cmdDisconnectedCb) client->cmdDisconnectedCb((uint32_t*)client); + if (client->disconnectedCb) client->disconnectedCb(client); + if (client->cmdDisconnectedCb) client->cmdDisconnectedCb(client); // reconnect unless we're in a permanently disconnected state if (client->connState == MQTT_DISCONNECTED) return; @@ -547,10 +547,11 @@ msg_conn_init(mqtt_connection_t *new_msg, mqtt_connection_t *old_msg, * @retval TRUE if success queue */ bool ICACHE_FLASH_ATTR -MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain) { +MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint16_t data_length, + uint8_t qos, uint8_t retain) +{ // estimate the packet size to allocate a buffer uint16_t topic_length = os_strlen(topic); - uint16_t data_length = os_strlen(data); // estimate: fixed hdr, pkt-id, topic length, topic, data, fudge uint16_t buf_len = 3 + 2 + 2 + topic_length + data_length + 16; PktBuf *buf = PktBuf_New(buf_len); @@ -738,8 +739,8 @@ mqtt_doAbort(MQTT_Client* client) { else espconn_disconnect(client->pCon); - if (client->disconnectedCb) client->disconnectedCb((uint32_t*)client); - if (client->cmdDisconnectedCb) client->cmdDisconnectedCb((uint32_t*)client); + if (client->disconnectedCb) client->disconnectedCb(client); + if (client->cmdDisconnectedCb) client->cmdDisconnectedCb(client); if (client->sending_buffer != NULL) { os_free(client->sending_buffer); diff --git a/mqtt/mqtt.h b/mqtt/mqtt.h index 0e98474..b5a99a5 100644 --- a/mqtt/mqtt.h +++ b/mqtt/mqtt.h @@ -44,14 +44,16 @@ typedef enum { MQTT_CONNECTED, // conneted (or connecting) } tConnState; +typedef struct MQTT_Client MQTT_Client; // forward definition + // Simple notification callback -typedef void (*MqttCallback)(uint32_t* args); +typedef void (*MqttCallback)(MQTT_Client *client); // Callback with data messge -typedef void (*MqttDataCallback)(uint32_t* args, const char* topic, uint32_t topic_len, +typedef void (*MqttDataCallback)(MQTT_Client *client, const char* topic, uint32_t topic_len, const char* data, uint32_t data_len); // MQTTY client data structure -typedef struct { +struct MQTT_Client { struct espconn* pCon; // socket // connection information char* host; // MQTT server @@ -89,7 +91,7 @@ typedef struct { MqttDataCallback cmdDataCb; // misc void* user_data; -} MQTT_Client; +}; // Initialize client data structure void MQTT_Init(MQTT_Client* mqttClient, char* host, uint32 port, @@ -119,7 +121,7 @@ void MQTT_Disconnect(MQTT_Client* mqttClient); bool MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos); // Publish a message -bool MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, +bool MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint16_t data_len, uint8_t qos, uint8_t retain); // Callback when connected diff --git a/mqtt/mqtt_cmd.c b/mqtt/mqtt_cmd.c index 4571f0a..05f277f 100644 --- a/mqtt/mqtt_cmd.c +++ b/mqtt/mqtt_cmd.c @@ -13,74 +13,51 @@ #define DBG(format, ...) do { } while(0) #endif -// if MQTT_1_CLIENT is defined we only support the one client that is built into esp-link. -// this keeps everything simpler. Undefining it brings back old code that supports creating -// a new client and setting all its params. Most likely that old code no longer works... -#define MQTT_1_CLIENT - -// callbacks to the attached uC -uint32_t connectedCb = 0, disconnectCb = 0, publishedCb = 0, dataCb = 0; - void ICACHE_FLASH_ATTR -cmdMqttConnectedCb(uint32_t* args) { - MQTT_Client* client = (MQTT_Client*)args; +cmdMqttConnectedCb(MQTT_Client* client) { MqttCmdCb* cb = (MqttCmdCb*)client->user_data; - DBG("MQTT: Connected connectedCb=%p, disconnectedCb=%p, publishedCb=%p, dataCb=%p\n", - (void*)cb->connectedCb, - (void*)cb->disconnectedCb, - (void*)cb->publishedCb, - (void*)cb->dataCb); - uint16_t crc = cmdResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0); - cmdResponseEnd(crc); + DBG("MQTT: Connected Cb=%p\n", (void*)cb->connectedCb); + cmdResponseStart(CMD_RESP_CB, cb->connectedCb, 0); + cmdResponseEnd(); } void ICACHE_FLASH_ATTR -cmdMqttDisconnectedCb(uint32_t* args) { - MQTT_Client* client = (MQTT_Client*)args; +cmdMqttDisconnectedCb(MQTT_Client* client) { MqttCmdCb* cb = (MqttCmdCb*)client->user_data; - DBG("MQTT: Disconnected\n"); - uint16_t crc = cmdResponseStart(CMD_MQTT_EVENTS, cb->disconnectedCb, 0, 0); - cmdResponseEnd(crc); + DBG("MQTT: Disconnected cb=%p\n", (void*)cb->disconnectedCb); + cmdResponseStart(CMD_RESP_CB, cb->disconnectedCb, 0); + cmdResponseEnd(); } void ICACHE_FLASH_ATTR -cmdMqttPublishedCb(uint32_t* args) { - MQTT_Client* client = (MQTT_Client*)args; +cmdMqttPublishedCb(MQTT_Client* client) { MqttCmdCb* cb = (MqttCmdCb*)client->user_data; - DBG("MQTT: Published\n"); - uint16_t crc = cmdResponseStart(CMD_MQTT_EVENTS, cb->publishedCb, 0, 0); - cmdResponseEnd(crc); + DBG("MQTT: Published cb=%p\n", (void*)cb->publishedCb); + cmdResponseStart(CMD_RESP_CB, cb->publishedCb, 0); + cmdResponseEnd(); } void ICACHE_FLASH_ATTR -cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t data_len) { - uint16_t crc = 0; - MQTT_Client* client = (MQTT_Client*)args; +cmdMqttDataCb(MQTT_Client* client, const char* topic, uint32_t topic_len, + const char* data, uint32_t data_len) +{ MqttCmdCb* cb = (MqttCmdCb*)client->user_data; + DBG("MQTT: Data cb=%p topic=%s len=%ld\n", (void*)cb->dataCb, topic, data_len); - crc = cmdResponseStart(CMD_MQTT_EVENTS, cb->dataCb, 0, 2); - crc = cmdResponseBody(crc, (uint8_t*)topic, topic_len); - crc = cmdResponseBody(crc, (uint8_t*)data, data_len); - cmdResponseEnd(crc); + cmdResponseStart(CMD_RESP_CB, cb->dataCb, 2); + cmdResponseBody(topic, topic_len); + cmdResponseBody(data, data_len); + cmdResponseEnd(); } -uint32_t ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR MQTTCMD_Lwt(CmdPacket *cmd) { CmdRequest req; cmdRequest(&req, cmd); - if (cmdGetArgc(&req) != 5) - return 0; + if (cmdGetArgc(&req) != 4) return; - // get mqtt client - uint32_t client_ptr; - cmdPopArg(&req, (uint8_t*)&client_ptr, 4); -#ifdef MQTT_1_CLIENT MQTT_Client* client = &mqttClient; -#else - MQTT_Client* client = (MQTT_Client*)client_ptr; - DBG("MQTT: MQTTCMD_Lwt client ptr=%p\n", (void*)client_ptr); -#endif // free old topic & message if (client->connect_info.will_topic) @@ -92,14 +69,14 @@ MQTTCMD_Lwt(CmdPacket *cmd) { // get topic len = cmdArgLen(&req); - if (len > 128) return 0; // safety check + if (len > 128) return; // safety check client->connect_info.will_topic = (char*)os_zalloc(len + 1); cmdPopArg(&req, client->connect_info.will_topic, len); client->connect_info.will_topic[len] = 0; // get message len = cmdArgLen(&req); - if (len > 128) return 0; // safety check + if (len > 128) return; // safety check client->connect_info.will_message = (char*)os_zalloc(len + 1); cmdPopArg(&req, client->connect_info.will_message, len); client->connect_info.will_message[len] = 0; @@ -118,32 +95,22 @@ MQTTCMD_Lwt(CmdPacket *cmd) { // trigger a reconnect to set the LWT MQTT_Reconnect(client); - return 1; } -uint32_t ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR MQTTCMD_Publish(CmdPacket *cmd) { CmdRequest req; cmdRequest(&req, cmd); - if (cmdGetArgc(&req) != 6) - return 0; + if (cmdGetArgc(&req) != 5) return; - // get mqtt client - uint32_t client_ptr; - cmdPopArg(&req, (uint8_t*)&client_ptr, 4); -#ifdef MQTT_1_CLIENT MQTT_Client* client = &mqttClient; -#else - MQTT_Client* client = (MQTT_Client*)client_ptr; - DBG("MQTT: MQTTCMD_Publish client ptr=%p\n", (void*)client_ptr); -#endif uint16_t len; // get topic len = cmdArgLen(&req); - if (len > 128) return 0; // safety check + if (len > 128) return; // safety check uint8_t *topic = (uint8_t*)os_zalloc(len + 1); cmdPopArg(&req, topic, len); topic[len] = 0; @@ -153,58 +120,46 @@ MQTTCMD_Publish(CmdPacket *cmd) { uint8_t *data = (uint8_t*)os_zalloc(len+1); if (!data) { // safety check os_free(topic); - return 0; + return; } cmdPopArg(&req, data, len); data[len] = 0; - uint32_t qos, retain, data_len; + uint16_t data_len; + uint8_t qos, retain; // get data length - // this isn't used but we have to pull it off the stack - cmdPopArg(&req, (uint8_t*)&data_len, 4); + cmdPopArg(&req, &data_len, sizeof(data_len)); // get qos - cmdPopArg(&req, (uint8_t*)&qos, 4); + cmdPopArg(&req, &qos, sizeof(qos)); // get retain - cmdPopArg(&req, (uint8_t*)&retain, 4); + cmdPopArg(&req, &retain, sizeof(retain)); - DBG("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%ld, retain=%ld\n", - topic, - os_strlen((char*)data), - qos, - retain); + DBG("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%d, retain=%d\n", + topic, data_len, qos, retain); - MQTT_Publish(client, (char*)topic, (char*)data, (uint8_t)qos, (uint8_t)retain); + MQTT_Publish(client, (char*)topic, (char*)data, data_len, qos%3, retain&1); os_free(topic); os_free(data); - return 1; + return; } -uint32_t ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR MQTTCMD_Subscribe(CmdPacket *cmd) { CmdRequest req; cmdRequest(&req, cmd); - if (cmdGetArgc(&req) != 3) - return 0; + if (cmdGetArgc(&req) != 2) return; - // get mqtt client - uint32_t client_ptr; - cmdPopArg(&req, (uint8_t*)&client_ptr, 4); -#ifdef MQTT_1_CLIENT MQTT_Client* client = &mqttClient; -#else - MQTT_Client* client = (MQTT_Client*)client_ptr; - DBG("MQTT: MQTTCMD_Subscribe client ptr=%p\n", (void*)client_ptr); -#endif uint16_t len; // get topic len = cmdArgLen(&req); - if (len > 128) return 0; // safety check + if (len > 128) return; // safety check uint8_t* topic = (uint8_t*)os_zalloc(len + 1); cmdPopArg(&req, topic, len); topic[len] = 0; @@ -217,22 +172,19 @@ MQTTCMD_Subscribe(CmdPacket *cmd) { MQTT_Subscribe(client, (char*)topic, (uint8_t)qos); os_free(topic); - return 1; + return; } -uint32_t ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR MQTTCMD_Setup(CmdPacket *cmd) { CmdRequest req; cmdRequest(&req, cmd); -#ifdef MQTT_1_CLIENT MQTT_Client* client = &mqttClient; - cmdSkipArg(&req); - cmdSkipArg(&req); - cmdSkipArg(&req); - cmdSkipArg(&req); - cmdSkipArg(&req); -#else + + if (cmdGetArgc(&req) != 4) return; + +#if 0 if (cmdGetArgc(&req) != 9) return 0; @@ -287,27 +239,28 @@ MQTTCMD_Setup(CmdPacket *cmd) { // create callback MqttCmdCb* callback = (MqttCmdCb*)os_zalloc(sizeof(MqttCmdCb)); - uint32_t cb_data; - - cmdPopArg(&req, (uint8_t*)&cb_data, 4); - callback->connectedCb = cb_data; - cmdPopArg(&req, (uint8_t*)&cb_data, 4); - callback->disconnectedCb = cb_data; - cmdPopArg(&req, (uint8_t*)&cb_data, 4); - callback->publishedCb = cb_data; - cmdPopArg(&req, (uint8_t*)&cb_data, 4); - callback->dataCb = cb_data; - + cmdPopArg(&req, &callback->connectedCb, 4); + cmdPopArg(&req, &callback->disconnectedCb, 4); + cmdPopArg(&req, &callback->publishedCb, 4); + cmdPopArg(&req, &callback->dataCb, 4); client->user_data = callback; + DBG("MQTT connectedCb=%lx\n", callback->connectedCb); + client->cmdConnectedCb = cmdMqttConnectedCb; client->cmdDisconnectedCb = cmdMqttDisconnectedCb; client->cmdPublishedCb = cmdMqttPublishedCb; client->cmdDataCb = cmdMqttDataCb; - return 0xf00df00d; //(uint32_t)client; + if (client->connState == MQTT_CONNECTED) { + if (callback->connectedCb) + cmdMqttConnectedCb(client); + } else if (callback->disconnectedCb) { + cmdMqttDisconnectedCb(client); + } } +#if 0 uint32_t ICACHE_FLASH_ATTR MQTTCMD_Connect(CmdPacket *cmd) { CmdRequest req; @@ -383,3 +336,4 @@ MQTTCMD_Disconnect(CmdPacket *cmd) { return 1; #endif } +#endif From af0e0907f9e436c906d733ba6c125be24a989668 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sun, 31 Jan 2016 21:27:43 -0800 Subject: [PATCH 36/37] convert readme to asciidoc --- README.md => README.adoc | 192 ++++++++++++++++++++++++--------------- 1 file changed, 118 insertions(+), 74 deletions(-) rename README.md => README.adoc (91%) diff --git a/README.md b/README.adoc similarity index 91% rename from README.md rename to README.adoc index f817ae9..d6f226f 100644 --- a/README.md +++ b/README.adoc @@ -1,36 +1,54 @@ -ESP-LINK -======== +ESP-LINK: Wifi-Serial Bridge w/REST&MQTT +======================================== +Thorsten von Eicken +:toc: +:toc-title!: +:toc-placement!: This firmware connects an attached micro-controller to the internet using a ESP8266 Wifi module. It implements a number of features: + +[options="compact"] - transparent bridge between Wifi and serial, useful for debugging or inputting into a uC - flash-programming attached Arduino/AVR microcontrollers, esp8266 modules, as well as LPC800-series and other ARM microcontrollers via Wifi -- built-in stk500v1 programmer for AVR uC's with optiboot: program using HTTP upload of hex file -- outbound TCP (and thus HTTP) connections from the attached micro-controller to the internet -- outbound REST HTTP requests from the attached micro-controller to the internet, protocol - based on espduino and compatible with [tuanpmt/espduino](https://github.com/tuanpmt/espduino) +- built-in stk500v1 programmer for AVR uC's: program using HTTP upload of hex file +- outbound REST HTTP requests from the attached micro-controller to the internet +- MQTT client pub/sub from the attached micro-controller to the internet The firmware includes a tiny HTTP server based on -[esphttpd](http://www.esp8266.com/viewforum.php?f=34) +http://www.esp8266.com/viewforum.php?f=34[esphttpd] with a simple web interface, many thanks to Jeroen Domburg for making it available! +The REST and MQTT functionality are loosely based on https://github.com/tuanpmt/espduino +but significantly reqritten and no longer protocol compatible, thanks to tuanpmt for the +inspiration! Many thanks to https://github.com/brunnels for contributions in particular around the espduino functionality. Thank you also to https://github.com/susisstrolch and https://github.com/bc547 for additional contributions! -###[Releases & Downloads](https://github.com/jeelabs/esp-link/releases) +[float] +Table of Contents +----------------- + +toc::[] + +Releases & Downloads +-------------------- -- [V2.1.7](https://github.com/jeelabs/esp-link/releases/tag/v2.1.7) is the most recent release. +- https://github.com/jeelabs/esp-link/releases/tag/v2.1.7[V2.1.7] is the most recent release. It has the new built-in stk500v1 programmer and works on all modules (esp-01 through esp-12). -- [V2.2.beta1](https://github.com/jeelabs/esp-link/releases/tag/v2.2.beta1) will be coming +- https://github.com/jeelabs/esp-link/releases/tag/v2.2.beta2[V2.2.beta2] will be coming up shortly with mDNS, sNTP, and syslog support, stay tuned... +- See https://github.com/jeelabs/esp-link/releases[all releases]. -For quick support and questions: -[![Chat at https://gitter.im/jeelabs/esp-link](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jeelabs/esp-link?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +For quick support and questions chat at +image:https://badges.gitter.im/Join%20Chat.svg[link="https://gitter.im/jeelabs/esp-link"] -Esp-link goals --------------- +Intro +----- + +### Esp-link goals The goal of the esp-link project is to create an advanced Wifi co-processor. Esp-link assumes that there is a "main processor" (also referred to as "attached uController") and that esp-link's role @@ -38,6 +56,7 @@ is to facilitate communication over Wifi. Where esp-link is a bit unusual is tha just a Wifi interface or a slave co-processor. In some sense it's the master, because the main processor can be reset, controlled and reprogrammed through esp-link. The three main areas of functionality in esp-link are: + - reprogramming and debugging the attached uC - letting the attached uC make outbound communication and offloading the protocol processing - forwarding inbound communication and offloading the protocol processing (this part is the @@ -49,8 +68,8 @@ actuators directly to it. A few users have taken esp-link as a starting point fo things and that's great, but there's also value in keeping the mainline esp-link project focused on a clear mission. -Esp-link uses -------------- +### Esp-link uses + The simplest use of esp-link is as a transparent serial to wifi bridge. You can flash an attached uC over wifi and you can watch the uC's serial debug output by connecting to port 23 or looking at the uC Console web page. @@ -71,26 +90,31 @@ the attached uC registers callbacks at start-up such that the code in the esp do know which exact sensors/actuators the attached uC has, it learns that through the initial callback registration. -Eye Candy ---------- +### Eye Candy + These screen shots show the Home page, the Wifi configuration page, the console for the attached microcontroller, and the pin assignments card: - - - - +image:https://cloud.githubusercontent.com/assets/39480/8261425/6ca395a6-167f-11e5-8e92-77150371135a.png[width="45%"] +image:https://cloud.githubusercontent.com/assets/39480/8261427/6caf7326-167f-11e5-8085-bc8b20159b2b.png[width="45%"] +image:https://cloud.githubusercontent.com/assets/39480/8261426/6ca7f75e-167f-11e5-827d-9a1c582ad05d.png[width="45%"] +image:https://cloud.githubusercontent.com/assets/39480/8261658/11e6c64a-1681-11e5-82d0-ea5ec90a6ddb.png[width="45%"] + +Getting Started +--------------- + +### Hardware configuration -Hardware info -------------- This firmware is designed for any esp8266 module. The recommended connections for an esp-01 module are: + - URXD: connect to TX of microcontroller - UTXD: connect to RX of microcontroller - GPIO0: connect to RESET of microcontroller - GPIO2: optionally connect green LED to 3.3V (indicates wifi status) The recommended connections for an esp-12 module are: + - URXD: connect to TX of microcontroller - UTXD: connect to RX of microcontroller - GPIO12: connect to RESET of microcontroller @@ -102,6 +126,7 @@ The recommended connections for an esp-12 module are: If your application has problems with the boot message that is output at ~74600 baud by the ROM at boot time you can connect an esp-12 module as follows and choose the "swap_uart" pin assignment in the esp-link web interface: + - GPIO13: connect to TX of microcontroller - GPIO15: connect to RX of microcontroller - GPIO1/UTXD: connect to RESET of microcontroller @@ -115,10 +140,10 @@ seen both used, sigh). The GPIO pin assignments can be changed dynamically in the web UI and are saved in flash. -Initial flashing ----------------- +### Initial flashing + If you want to simply flash a pre-built firmware binary, you can download the latest -[release](https://github.com/jeelabs/esp-link/releases) and use your favorite +https://github.com/jeelabs/esp-link/releases[release] and use your favorite ESP8266 flashing tool to flash the bootloader, the firmware, and blank settings. Detailed instructions are provided in the release notes. @@ -127,16 +152,17 @@ stored in the boot sector (address 0). This is the standard way that the esp8266 the flash size. What this means is that you need to set this properly when you flash the bootloader. If you use esptool.py you can do it using the -ff and -fs options. -Wifi configuration overview ------------------- +### Wifi configuration overview + For proper operation the end state that esp-link needs to arrive at is to have it join your pre-existing wifi network as a pure station. However, in order to get there esp-link will start out as an access point and you'll have to join its network to configure it. The short version is: + 1. esp-link creates a wifi access point with an SSID of the form `ESP_012ABC` (some modules use a different SSID form, such as `ai-thinker-012ABC`) 2. you join your laptop or phone to esp-link's network as a station and you configure - esp-link wifi with your network info by pointing your browser at http://192.168.4.1/ + esp-link wifi with your network info by pointing your browser at `http://192.168.4.1/` 3. you set a hostname for esp-link on the "home" page, or leave the default ("esp-link") 4. esp-link starts to connect to your network while continuing to also be an access point ("AP+STA"), the esp-link may show up with a `${hostname}.local` hostname @@ -145,10 +171,11 @@ to join its network to configure it. The short version is: you reconnect your laptop/phone to your normal network and access esp-link via its hostname or IP address -LED indicators --------------- +### LED indicators + Assuming appropriate hardware attached to GPIO pins, the green "conn" LED will show the wifi status as follows: + - Very short flash once a second: not connected to a network and running as AP+STA, i.e. trying to connect to the configured network - Very short flash once every two seconds: not connected to a network and running as AP-only @@ -158,8 +185,31 @@ status as follows: The yellow "ser" LED will blink briefly every time serial data is sent or received by the esp-link. -Wifi configuration details --------------------------- +### Troubleshooting + +- verify that you have sufficient power, borderline power can cause the esp module to seemingly + function until it tries to transmit and the power rail collapses +- if you just cannot flash your esp8266 module (some people call it the zombie mode) make sure you + have gpio0 and gpio15 pulled to gnd with a 1K resistor, gpio2 tied to 3.3V with 1K resistor, and + RX/TX connected without anything in series. If you need to level shift the signal going into the + esp8266's RX use a 1K resistor. Use 115200 baud in the flasher. + (For a permanent set-up I would use higher resistor values but + when nothing seems to work these are the ones I try.) +- if the flashing succeeded, check the "conn" LED to see which mode esp-link is in (see LED info above) +- reset or power-cycle the esp-link to force it to become an access-point if it can't + connect to your network within 15-20 seconds +- if the LED says that esp-link is on your network but you can't get to it, make sure your + laptop is on the same network (and no longer on the esp's network) +- if you do not know the esp-link's IP address on your network, try `esp-link.local`, try to find + the lease in your DHCP server; if all fails, you may have to turn off your access point (or walk + far enough away) and reset/power-cycle esp-link, it will then fail to connect and start its + own AP after 15-20 seconds + +Configuration details +--------------------- + +### Wifi + After you have serially flashed the module it will create a wifi access point (AP) with an SSID of the form `ESP_012ABC` where 012ABC is a piece of the module's MAC address. Using a laptop, phone, or tablet connect to this SSID and then open a browser pointed at @@ -193,11 +243,12 @@ used to configure the ssid/password info. The problem is that the initial STA+AP channel 1 and you configure it to connect to an AP on channel 6. This requires the ESP8266's AP to also switch to channel 6 disconnecting you in the meantime. -Hostname, description, DHCP, mDNS ---------------------------------- +### Hostname, description, DHCP, mDNS + You can set a hostname on the "home" page, this should be just the hostname and not a domain name, i.e., something like "test-module-1" and not "test-module-1.mydomain.com". This has a number of effects: + - you will see the first 12 chars of the hostname in the menu bar (top left of the page) so if you have multiple modules you can distinguish them visually - esp-link will use the hostname in its DHCP request, which allows you to identify the module's @@ -212,36 +263,19 @@ You can also enter a description of up to 128 characters on the home page (botto allows you to leave a memo for yourself, such as "installed in basement to control the heating system". This descritpion is not used anywhere else. -Troubleshooting ---------------- -- verify that you have sufficient power, borderline power can cause the esp module to seemingly - function until it tries to transmit and the power rail collapses -- if you just cannot flash your esp8266 module (some people call it the zombie mode) make sure you - have gpio0 and gpio15 pulled to gnd with a 1K resistor, gpio2 tied to 3.3V with 1K resistor, and - RX/TX connected without anything in series. If you need to level shift the signal going into the - esp8266's RX use a 1K resistor. Use 115200 baud in the flasher. - (For a permanent set-up I would use higher resistor values but - when nothing seems to work these are the ones I try.) -- if the flashing succeeded, check the "conn" LED to see which mode esp-link is in (see LED info above) -- reset or power-cycle the esp-link to force it to become an access-point if it can't - connect to your network within 15-20 seconds -- if the LED says that esp-link is on your network but you can't get to it, make sure your - laptop is on the same network (and no longer on the esp's network) -- if you do not know the esp-link's IP address on your network, try `esp-link.local`, try to find - the lease in your DHCP server; if all fails, you may have to turn off your access point (or walk - far enough away) and reset/power-cycle esp-link, it will then fail to connect and start its - own AP after 15-20 seconds - Building the firmware --------------------- -The firmware has been built using the [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk) + +### Linux + +The firmware has been built using the https://github.com/pfalcon/esp-open-sdk[esp-open-sdk] on a Linux system. Create an esp8266 directory, install the esp-open-sdk into a sub-directory using the *non-standalone* install (i.e., there should not be an sdk directory in the esp-open-sdk dir when done installing, if you use the standalone install you will get compilation errors with std types, such as `uint32_t`). Download the Espressif SDK (use the version mentioned in the release notes) from their -[download forum](http://bbs.espressif.com/viewforum.php?f=5) and also expand it into a +http://bbs.espressif.com/viewforum.php?f=5[download forum] and also expand it into a sub-directory. Clone the esp-link repository into a third sub-directory and check out the tag you would like, @@ -260,12 +294,16 @@ Now, build the code: `make` in the top-level of esp-link. If you want to se the issued, use `VERBOSE=1 make`. A few notes from others (I can't fully verify these): + - You may need to install `zlib1g-dev` and `python-serial` - Make sure you have the correct version of the esp_iot_sdk - Make sure the paths at the beginning of the makefile are correct - Make sure `esp-open-sdk/xtensa-lx106-elf/bin` is in the PATH set in the Makefile +### Windows + It is possible to build esp-link on Windows, but it requires a gaggle of software to be installed: + - Install the unofficial sdk, mingw, SourceTree (gui git client), python 2.7, git cli, Java - Use SourceTree to checkout under C:\espressif or wherever you installed the unofficial sdk, (see this thread for the unofficial sdk http://www.esp8266.com/viewtopic.php?t=820) @@ -273,8 +311,8 @@ It is possible to build esp-link on Windows, but it requires a gaggle of softwar the java bin directory under program files. - ... -Updating the firmware over-the-air ---------------------- +### Updating the firmware over-the-air + This firmware supports over-the-air (OTA) flashing, so you do not have to deal with serial flashing again after the initial one! The recommended way to flash is to use `make wiflash` if you are also building the firmware. @@ -303,6 +341,7 @@ The flash configuration and the OTA upgrade process is described in more detail Serial bridge and connections to Arduino, AVR, ARM, LPC microcontrollers ------------------------------------------------------------------------ + In order to connect through the esp-link to a microcontroller use port 23. For example, on linux you can use `nc esp-hostname 23` or `telnet esp-hostname 23`. @@ -313,6 +352,7 @@ broadcast incoming characters from the serial RX to all connections. Use with ca ### Flashing an attached AVR/Arduino There are three options for reprogramming an attached AVR/Arduino microcontroller: + - Use avrdude and point it at port 23 of esp-link. Esp-link automatically detects the programming sequence and issues a reset to the AVR. - Use avrdude and point it at port 2323 of esp-link. This is the same as port 23 except that the @@ -350,11 +390,13 @@ If your AVR doesn't use optiboot then use port 2323 since esp-link may not recog sequence and not issue a reset if you use port 23. If you are having trouble with the built-in programmer and see something like this: -``` + +-------------------- # ./avrflash 192.168.3.104 blink.hex Error checking sync: FAILED to SYNC: abandoned after timeout, got: :\xF/\x00\xCj\xCz\xCJ\xCZ\xC\xAÜ\xC\xAä\xC\xAÜ\xC\xAä\xC\xBì\xC\xBô\xC\xBì\xC\xBô\xC\xAÜ\xC\xAä\xC -``` +-------------------- + the most likely cause is a baud rate mismatch and/or a bad connection from the esp8266 to the AVRs reset line. The baud rate used by esp-link is set on the uC Console web page and, as mentioned above, it will @@ -366,9 +408,11 @@ correct baud rate configured but reset isn't functioning, or reset may be functi baud rate may be incorrect. The output of a successful flash using the built-in programmer looks like this: -``` + +-------------------- Success. 3098 bytes at 57600 baud in 0.8s, 3674B/s 63% efficient -``` +-------------------- + This says that the sketch comprises 3098 bytes of flash, was written in 0.8 seconds (excludes the initial sync time) at 57600 baud, and the 3098 bytes were flashed at a rate of 3674 bytes per second. @@ -378,7 +422,7 @@ The efficiency is not 100% because there is protocol overhead (such as sync, rec length characters) and there is dead time waiting for an ack or preparing the next record to be sent. -#### Details of built-in AVR flash algorithm +### Details of built-in AVR flash algorithm The built-in flashing algorithm differs a bit from what avrdude does. The programming protocol states that STK_GET_SYNC+CRC_EOP (0x30 0x20) should be sent to synchronize, but that works poorly @@ -436,7 +480,7 @@ baud rate to 115200 is recommended.) Another option is to use a serial-to-tcp port forwarding driver and point that to port 2323 of esp-link. On windows users have reported success with -[HW Virtual Serial Port](http://www.hw-group.com/products/hw_vsp/hw_vsp2_en.html) +http://www.hw-group.com/products/hw_vsp/hw_vsp2_en.html[HW Virtual Serial Port] Now to the interference problem: once the attached esp8266 is reset it starts outputting its 26Mhz clock on gpio0, which needs to be attached to @@ -453,12 +497,13 @@ add a series 100ohm resistor and 100pf capacitor to ground as close to the gpio0 pin as possible (basically a low pass filter); and/or pass the cable connecting the two esp8266's through a ferrite bead. -Debug log ---------- +### Debug log + The esp-link web UI can display the esp-link debug log (os_printf statements in the code). This is handy but sometimes not sufficient. Esp-link also prints the debug info to the UART where it is sometimes more convenient and sometimes less... For this reason three UART debug log modes are supported that can be set in the web UI (and the mode is saved in flash): + - auto: the UART log starts enabled at boot using uart0 and disables itself when esp-link associates with an AP. It re-enables itself if the association is lost. - off: the UART log is always off @@ -470,7 +515,8 @@ esp8266 comes out of reset. This cannot be disabled. Outbound HTTP REST requests and MQTT client ------------------------------------------- -The V2 versions of esp-link support the espduino SLIP protocol that supports simple outbound + +The V2 versions of esp-link use the SLIP protocol over the serial link to support simple outbound HTTP REST requests as well as an MQTT client. The SLIP protocol consists of commands with binary arguments sent from the attached microcontroller to the esp8266, which then performs the command and responds back. @@ -479,13 +525,11 @@ command sent by the uC contains a callback address and the response from the esp with that callback address. This enables asynchronous communication where esp-link can notify the uC when requests complete or when other actions happen, such as wifi connectivity status changes. -You can find a demo sketch in a fork of the espduino library at -https://github.com/tve/espduino in the -[examples/demo folder](https://github.com/tve/espduino/tree/master/espduino/examples/demo). - -More docs forthcoming... +You can find REST and MQTT libraries as well as demo sketches in the +https://github.com/jeelabs/el-client[el-client] repository. Contact ------- + If you find problems with esp-link, please create a github issue. If you have a question, please use the gitter chat link at the top of this page. From ad5182ae590ad3c255b61c237cd570f2150fc341 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sun, 31 Jan 2016 22:00:59 -0800 Subject: [PATCH 37/37] fix reference to old boot file in Makefile --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d316dbb..4e5fa72 100644 --- a/Makefile +++ b/Makefile @@ -382,7 +382,7 @@ baseflash: all flash: all $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash -fs $(ET_FS) -ff $(ET_FF) \ - 0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \ + 0x00000 "$(SDK_BASE)/bin/boot_v1.5.bin" 0x01000 $(FW_BASE)/user1.bin \ $(ET_BLANK) $(SDK_BASE)/bin/blank.bin ifeq ($(OS),Windows_NT) @@ -464,7 +464,7 @@ release: all $(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user1.bin | cut -b 1-80 $(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80 $(Q) cp $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin $(SDK_BASE)/bin/blank.bin \ - "$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash avrflash release/esp-link-$(BRANCH) + "$(SDK_BASE)/bin/boot_v1.5.bin" wiflash avrflash release/esp-link-$(BRANCH) $(Q) tar zcf esp-link-$(BRANCH).tgz -C release esp-link-$(BRANCH) $(Q) echo "Release file: esp-link-$(BRANCH).tgz" $(Q) rm -rf release