diff --git a/Makefile b/Makefile index 113944c..15d4bab 100644 --- a/Makefile +++ b/Makefile @@ -164,7 +164,7 @@ EXTRA_INCDIR =include . EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK -LIBS = c gcc hal phy pp net80211 wpa main lwip +LIBS = c gcc hal phy pp net80211 wpa main lwip # crypto ssl # compiler flags using during compilation of source files CFLAGS += -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ @@ -373,8 +373,8 @@ release: all $(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 release/esp-link-$(BRANCH) - $(Q) tar zcf esp-link-$(BRANCH)-$(FLASH_SIZE).tgz -C release esp-link-$(BRANCH) - $(Q) echo "Release file: esp-link-$(BRANCH)-$(FLASH_SIZE).tgz" + $(Q) tar zcf esp-link-$(BRANCH).tgz -C release esp-link-$(BRANCH) + $(Q) echo "Release file: esp-link-$(BRANCH).tgz" $(Q) rm -rf release clean: diff --git a/README.md b/README.md index 34d3090..d90fa9b 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,28 @@ Many thanks to https://github.com/brunnels for contributions around the espduino 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) +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. + +The next level is to use the outbound connectivity of esp-link in the uC code. For example, the +uC can use REST requests to services like thingspeak.com to send sensor values that then get +stored and plotted by the external service. +The uC can also use REST requests to retrieve simple configuration +information or push other forms of notifications. (MQTT functionality is forthcoming.) + +An additional option is to add code to esp-link to customize it and put all the communication +code into esp-link and only keep simple sensor/actuator control in the attached uC. In this +mode the attached uC sends custom commands to esp-link with sensor/acturator info and +registers a set of callbacks with esp-link that control sensors/actuators. This way, custom +commands in esp-link can receive MQTT messages, make simple callbacks into the uC to get sensor +values or change actuators, and then respond back with MQTT. The way this is architected is that +the attached uC registers callbacks at start-up such that the code in the esp doesn't need to +know which exact sensors/actuators the attached uC has, it learns thta through the initial +callback registration. + Eye Candy --------- These screen shots show the Home page, the Wifi configuration page, the console for the @@ -237,7 +259,22 @@ The attached micro-controller can open outbound TCP connections using a simple [serial protocol](https://gist.github.com/tve/a46c44bf1f6b42bc572e). More info and sample code forthcoming... +Outbound HTTP REST requests +--------------------------- +The V2 versions of esp-link support the espduino SLIP protocol that supports simple outbound +HTTP REST requests. 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. +The responses back use a callback address in the attached microcontroller code, i.e., the +command sent by the uC contains a callback address and the response from the esp8266 starts +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. +Support for MQTT is forthcoming. + +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). + Contact ------- If you find problems with esp-link, please create a github issue. If you have a question, please -use the gitter link at the top of this page. +use the gitter chat link at the top of this page. diff --git a/cmd/handlers.c b/cmd/handlers.c index af6eab7..adf90b9 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -94,7 +94,7 @@ cmdCallback* ICACHE_FLASH_ATTR CMD_GetCbByName(char* name) { char checkname[16]; os_strncpy(checkname, name, sizeof(checkname)); - for (uint8_t i = 0; i < sizeof(commands); i++) { + for (uint8_t i = 0; i < MAX_CALLBACKS; i++) { //os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, // (void *)callbacks[i].callback); // if callback doesn't exist or it's null diff --git a/esp-link/cgi.c b/esp-link/cgi.c index 65cab94..bfb7028 100644 --- a/esp-link/cgi.c +++ b/esp-link/cgi.c @@ -12,95 +12,112 @@ Some random cgi routines. * Heavily modified and enhanced by Thorsten von Eicken in 2015 * ---------------------------------------------------------------------------- */ + + +#include #include "cgi.h" +#include "espfs.h" + +void noCacheHeaders(HttpdConnData *connData, int code) { + httpdStartResponse(connData, code); + httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); + httpdHeader(connData, "Pragma", "no-cache"); + httpdHeader(connData, "Expires", "0"); +} void ICACHE_FLASH_ATTR jsonHeader(HttpdConnData *connData, int code) { - httpdStartResponse(connData, code); - httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); - httpdHeader(connData, "Pragma", "no-cache"); - httpdHeader(connData, "Expires", "0"); - httpdHeader(connData, "Content-Type", "application/json"); - httpdEndHeaders(connData); + noCacheHeaders(connData, code); + httpdHeader(connData, "Content-Type", "application/json"); + httpdEndHeaders(connData); } -uint8_t ICACHE_FLASH_ATTR -UTILS_StrToIP(const char* str, void *ip){ - /* The count of the number of bytes processed. */ - int i; - /* A pointer to the next digit to process. */ - const char * start; +void ICACHE_FLASH_ATTR +errorResponse(HttpdConnData *connData, int code, char *message) { + noCacheHeaders(connData, code); + httpdEndHeaders(connData); + httpdSend(connData, message, -1); + os_printf("HTTP %d error response: \"%s\"\n", code, message); +} - start = str; - for (i = 0; i < 4; i++) { - /* The digit being processed. */ - char c; - /* The value of this byte. */ - int n = 0; - while (1) { - c = *start; - start++; - if (c >= '0' && c <= '9') { - n *= 10; - n += c - '0'; - } - /* We insist on stopping at "." if we are still parsing - the first, second, or third numbers. If we have reached - the end of the numbers, we will allow any character. */ - else if ((i < 3 && c == '.') || i == 3) { - break; - } - else { - return 0; - } - } - if (n >= 256) { - return 0; - } - ((uint8_t*)ip)[i] = n; +// look for the HTTP arg 'name' and store it at 'config' with max length 'max_len' (incl +// terminating zero), returns -1 on error, 0 if not found, 1 if found and OK +int ICACHE_FLASH_ATTR +getStringArg(HttpdConnData *connData, char *name, char *config, int max_len) { + char buff[128]; + int len = httpdFindArg(connData->getArgs, name, buff, sizeof(buff)); + if (len < 0) return 0; // not found, skip + if (len >= max_len) { + os_sprintf(buff, "Value for %s too long (%d > %d allowed)", name, len, max_len-1); + errorResponse(connData, 400, buff); + return -1; } + strcpy(config, buff); return 1; } +int ICACHE_FLASH_ATTR +getBoolArg(HttpdConnData *connData, char *name, bool*config) { + char buff[64]; + int len = httpdFindArg(connData->getArgs, name, buff, sizeof(buff)); + if (len < 0) return 0; // not found, skip + + if (strcmp(buff, "1") == 0 || strcmp(buff, "true") == 0) { + *config = true; + return 1; + } + if (strcmp(buff, "0") == 0 || strcmp(buff, "false") == 0) { + *config = false; + return 1; + } + os_sprintf(buff, "Invalid value for %s", name); + errorResponse(connData, 400, buff); + return -1; +} + + #define TOKEN(x) (os_strcmp(token, x) == 0) #if 0 // Handle system information variables and print their value, returns the number of // characters appended to buff int ICACHE_FLASH_ATTR printGlobalInfo(char *buff, int buflen, char *token) { - if (TOKEN("si_chip_id")) { - return os_sprintf(buff, "0x%x", system_get_chip_id()); - } else if (TOKEN("si_freeheap")) { - return os_sprintf(buff, "%dKB", system_get_free_heap_size()/1024); - } else if (TOKEN("si_uptime")) { - uint32 t = system_get_time() / 1000000; // in seconds - return os_sprintf(buff, "%dd%dh%dm%ds", t/(24*3600), (t/(3600))%24, (t/60)%60, t%60); - } else if (TOKEN("si_boot_version")) { - return os_sprintf(buff, "%d", system_get_boot_version()); - } else if (TOKEN("si_boot_address")) { - return os_sprintf(buff, "0x%x", system_get_userbin_addr()); - } else if (TOKEN("si_cpu_freq")) { - return os_sprintf(buff, "%dMhz", system_get_cpu_freq()); - } else { - return 0; - } + if (TOKEN("si_chip_id")) { + return os_sprintf(buff, "0x%x", system_get_chip_id()); + } else if (TOKEN("si_freeheap")) { + return os_sprintf(buff, "%dKB", system_get_free_heap_size()/1024); + } else if (TOKEN("si_uptime")) { + uint32 t = system_get_time() / 1000000; // in seconds + return os_sprintf(buff, "%dd%dh%dm%ds", t/(24*3600), (t/(3600))%24, (t/60)%60, t%60); + } else if (TOKEN("si_boot_version")) { + return os_sprintf(buff, "%d", system_get_boot_version()); + } else if (TOKEN("si_boot_address")) { + return os_sprintf(buff, "0x%x", system_get_userbin_addr()); + } else if (TOKEN("si_cpu_freq")) { + return os_sprintf(buff, "%dMhz", system_get_cpu_freq()); + } else { + return 0; + } } #endif extern char *esp_link_version; // in user_main.c int ICACHE_FLASH_ATTR cgiMenu(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - char buff[1024]; - // don't use jsonHeader so the response does get cached - httpdStartResponse(connData, 200); - httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate"); - httpdHeader(connData, "Content-Type", "application/json"); - httpdEndHeaders(connData); - // construct json response - os_sprintf(buff, - "{\"menu\": [\"Home\", \"/home.html\", \"Wifi\", \"/wifi/wifi.html\"," - "\"\xC2\xB5" "C Console\", \"/console.html\", \"Debug log\", \"/log.html\" ],\n" - " \"version\": \"%s\" }", esp_link_version); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + char buff[1024]; + // don't use jsonHeader so the response does get cached + httpdStartResponse(connData, 200); + httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate"); + httpdHeader(connData, "Content-Type", "application/json"); + httpdEndHeaders(connData); + // construct json response + os_sprintf(buff, + "{\"menu\": [\"Home\", \"/home.html\", " + "\"Wifi\", \"/wifi/wifi.html\"," + "\"\xC2\xB5" "C Console\", \"/console.html\", " + "\"REST/MQTT\", \"/mqtt.html\"," + "\"Debug log\", \"/log.html\" ],\n" + " \"version\": \"%s\" }", esp_link_version); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } diff --git a/esp-link/cgi.h b/esp-link/cgi.h index 200a176..aa124f1 100644 --- a/esp-link/cgi.h +++ b/esp-link/cgi.h @@ -5,6 +5,9 @@ #include "httpd.h" void jsonHeader(HttpdConnData *connData, int code); +void errorResponse(HttpdConnData *connData, int code, char *message); +int getStringArg(HttpdConnData *connData, char *name, char *config, int max_len); +int getBoolArg(HttpdConnData *connData, char *name, bool*config); int cgiMenu(HttpdConnData *connData); uint8_t UTILS_StrToIP(const char* str, void *ip); diff --git a/esp-link/cgimqtt.c b/esp-link/cgimqtt.c new file mode 100644 index 0000000..cbf4c16 --- /dev/null +++ b/esp-link/cgimqtt.c @@ -0,0 +1,111 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// // TCP Client settings + +#include +#include "cgi.h" +#include "config.h" +#include "cgimqtt.h" + +// Cgi to return MQTT settings +int ICACHE_FLASH_ATTR cgiMqttGet(HttpdConnData *connData) { + char buff[2048]; + int len; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + len = os_sprintf(buff, "{ " + "\"slip-enable\":%d, " + "\"mqtt-enable\":%d, " + "\"mqtt-status-enable\":%d, " + "\"mqtt-port\":%d, " + "\"mqtt-host\":\"%s\", " + "\"mqtt-client-id\":\"%s\", " + "\"mqtt-username\":\"%s\", " + "\"mqtt-password\":\"%s\", " + "\"mqtt-status-topic\":\"%s\", " + "\"mqtt-state\":\"%s\" }", + flashConfig.slip_enable, flashConfig.mqtt_enable, flashConfig.mqtt_status_enable, + flashConfig.mqtt_port, flashConfig.mqtt_hostname, flashConfig.mqtt_client, + flashConfig.mqtt_username, flashConfig.mqtt_password, + flashConfig.mqtt_status_topic, "connected"); + + jsonHeader(connData, 200); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; +} + +// Cgi to change choice of pin assignments +int ICACHE_FLASH_ATTR cgiMqttSet(HttpdConnData *connData) { + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + // handle MQTT server settings + int mqtt_server = 0; // accumulator for changes/errors + mqtt_server |= getStringArg(connData, "mqtt-host", + flashConfig.mqtt_hostname, sizeof(flashConfig.mqtt_hostname)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getStringArg(connData, "mqtt-client-id", + flashConfig.mqtt_client, sizeof(flashConfig.mqtt_client)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getStringArg(connData, "mqtt-username", + flashConfig.mqtt_username, sizeof(flashConfig.mqtt_username)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getStringArg(connData, "mqtt-password", + flashConfig.mqtt_password, sizeof(flashConfig.mqtt_password)); + if (mqtt_server < 0) return HTTPD_CGI_DONE; + mqtt_server |= getBoolArg(connData, "mqtt-enable", + &flashConfig.mqtt_enable); + + // handle mqtt port + char buff[16]; + if (httpdFindArg(connData->getArgs, "mqtt-port", buff, sizeof(buff)) > 0) { + int32_t port = atoi(buff); + if (port > 0 && port < 65536) { + flashConfig.mqtt_port = port; + mqtt_server |= 1; + } else { + errorResponse(connData, 400, "Invalid MQTT port"); + return HTTPD_CGI_DONE; + } + } + + // if server setting changed, we need to "make it so" + if (mqtt_server) { + os_printf("MQTT server settings changed, enable=%d\n", flashConfig.mqtt_enable); + // TODO + } + + // no action required if mqtt status settings change, they just get picked up at the + // next status tick + if (getBoolArg(connData, "mqtt-status-enable", &flashConfig.mqtt_status_enable) < 0) + return HTTPD_CGI_DONE; + if (getStringArg(connData, "mqtt-status-topic", + flashConfig.mqtt_status_topic, sizeof(flashConfig.mqtt_status_topic)) < 0) + return HTTPD_CGI_DONE; + + // if SLIP-enable is toggled it gets picked-up immediately by the parser + int slip_update = getBoolArg(connData, "slip-enable", &flashConfig.slip_enable); + if (slip_update < 0) return HTTPD_CGI_DONE; + if (slip_update > 0) os_printf("SLIP-enable changed: %d\n", flashConfig.slip_enable); + + os_printf("Saving config\n"); + if (configSave()) { + httpdStartResponse(connData, 200); + httpdEndHeaders(connData); + } else { + httpdStartResponse(connData, 500); + httpdEndHeaders(connData); + httpdSend(connData, "Failed to save config", -1); + } + return HTTPD_CGI_DONE; +} + +int ICACHE_FLASH_ATTR cgiMqtt(HttpdConnData *connData) { + if (connData->requestType == HTTPD_METHOD_GET) { + return cgiMqttGet(connData); + } else if (connData->requestType == HTTPD_METHOD_POST) { + return cgiMqttSet(connData); + } else { + jsonHeader(connData, 404); + return HTTPD_CGI_DONE; + } +} diff --git a/esp-link/cgimqtt.h b/esp-link/cgimqtt.h new file mode 100644 index 0000000..54a6011 --- /dev/null +++ b/esp-link/cgimqtt.h @@ -0,0 +1,8 @@ +#ifndef CGIMQTT_H +#define CGIMQTT_H + +#include "httpd.h" + +int cgiMqtt(HttpdConnData *connData); + +#endif diff --git a/esp-link/cgipins.c b/esp-link/cgipins.c index 2d71849..dabc1fd 100644 --- a/esp-link/cgipins.c +++ b/esp-link/cgipins.c @@ -24,9 +24,9 @@ static const int num_map_func = sizeof(map_func)/sizeof(char*); // Cgi to return choice of pin assignments int ICACHE_FLASH_ATTR cgiPinsGet(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted - char buff[2048]; + char buff[2048]; int len; // figure out current mapping @@ -62,27 +62,27 @@ int ICACHE_FLASH_ATTR cgiPinsGet(HttpdConnData *connData) { } len += os_sprintf(buff+len, "\n] }"); - jsonHeader(connData, 200); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + jsonHeader(connData, 200); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } // Cgi to change choice of pin assignments int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { - if (connData->conn==NULL) { - return HTTPD_CGI_DONE; // Connection aborted - } + if (connData->conn==NULL) { + return HTTPD_CGI_DONE; // Connection aborted + } char buff[128]; - int len = httpdFindArg(connData->getArgs, "map", buff, sizeof(buff)); - if (len <= 0) { - jsonHeader(connData, 400); + int len = httpdFindArg(connData->getArgs, "map", buff, sizeof(buff)); + if (len <= 0) { + jsonHeader(connData, 400); return HTTPD_CGI_DONE; } int m = atoi(buff); - if (m < 0 || m >= num_map_names) { - jsonHeader(connData, 400); + if (m < 0 || m >= num_map_names) { + jsonHeader(connData, 400); return HTTPD_CGI_DONE; } @@ -106,17 +106,17 @@ int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { httpdEndHeaders(connData); httpdSend(connData, "Failed to save config", -1); } - return HTTPD_CGI_DONE; + return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR cgiPins(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - if (connData->requestType == HTTPD_METHOD_GET) { - return cgiPinsGet(connData); - } else if (connData->requestType == HTTPD_METHOD_POST) { - return cgiPinsSet(connData); - } else { - jsonHeader(connData, 404); - return HTTPD_CGI_DONE; - } + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + if (connData->requestType == HTTPD_METHOD_GET) { + return cgiPinsGet(connData); + } else if (connData->requestType == HTTPD_METHOD_POST) { + return cgiPinsSet(connData); + } else { + jsonHeader(connData, 404); + return HTTPD_CGI_DONE; + } } diff --git a/esp-link/cgiwifi.c b/esp-link/cgiwifi.c index 2354b13..69d59b4 100644 --- a/esp-link/cgiwifi.c +++ b/esp-link/cgiwifi.c @@ -31,12 +31,12 @@ 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" }; @@ -44,51 +44,51 @@ 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; - os_printf("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; - os_printf("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: - os_printf("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; - os_printf("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); - break; - case EVENT_SOFTAPMODE_STACONNECTED: - os_printf("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: - os_printf("Wifi AP: station " MACSTR " left, AID = %d\n", - MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); - break; - default: - break; - } + switch (evt->event) { + case EVENT_STAMODE_CONNECTED: + wifiState = wifiIsConnected; + wifiReason = 0; + os_printf("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; + os_printf("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: + os_printf("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; + os_printf("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); + break; + case EVENT_SOFTAPMODE_STACONNECTED: + os_printf("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: + os_printf("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); @@ -110,16 +110,16 @@ wifiAddStateChangeCb(WifiStateChangeCb cb) { //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. @@ -128,109 +128,109 @@ 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) { - os_printf("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; - os_printf("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. - os_printf("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); - os_printf("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; + int n; + struct bss_info *bss_link = (struct bss_info *)arg; + + if (status!=OK) { + os_printf("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; + os_printf("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. + os_printf("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); + os_printf("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) { - os_printf("Starting a scan\n"); - wifi_station_scan(NULL, wifiScanDoneCb); + os_printf("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, 1000, 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, 1000, 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[2048]; - int len; - - 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; - } - - len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); - for (int pos=0; posssid, cgiWifiAps.apData[pos]->rssi, - cgiWifiAps.apData[pos]->enc, (pos==cgiWifiAps.noAps-1)?"":","); - } - len += os_sprintf(buff+len, "]}}\n"); - //os_printf("Sending %d bytes: %s\n", len, buff); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + char buff[2048]; + int len; + + 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; + } + + len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); + for (int pos=0; posssid, cgiWifiAps.apData[pos]->rssi, + cgiWifiAps.apData[pos]->enc, (pos==cgiWifiAps.noAps-1)?"":","); + } + len += os_sprintf(buff+len, "]}}\n"); + //os_printf("Sending %d bytes: %s\n", len, buff); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; } 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 @@ -243,31 +243,31 @@ 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; - os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x); + int x = wifi_station_get_connect_status(); + int m = wifi_get_opmode() & 0x3; + os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x); - if (x == STATION_GOT_IP) { - if (m != 1) { + if (x == STATION_GOT_IP) { + if (m != 1) { #ifdef CHANGE_TO_STA - // We're happily connected, go to STA mode - os_printf("Wifi got IP. Going into STA mode..\n"); - wifi_set_opmode(1); - wifi_set_sleep_type(SLEEP_MODE); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + // We're happily connected, go to STA mode + os_printf("Wifi got IP. Going into STA mode..\n"); + wifi_set_opmode(1); + wifi_set_sleep_type(SLEEP_MODE); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); #endif - } - log_uart(false); - // no more resetTimer at this point, gotta use physical reset to recover if in trouble - } else { - if (m != 3) { - os_printf("Wifi connect failed. Going into STA+AP mode..\n"); - wifi_set_opmode(3); - } - log_uart(true); - os_printf("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) { + os_printf("Wifi connect failed. Going into STA+AP mode..\n"); + wifi_set_opmode(3); + } + log_uart(true); + os_printf("Enabling/continuing uart log\n"); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + } } // Temp store for new ap info. @@ -277,206 +277,206 @@ static ETSTimer reassTimer; // Callback actually doing reassociation static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) { - os_printf("Wifi changing association\n"); - wifi_station_disconnect(); - stconf.bssid_set = 0; - wifi_station_set_config(&stconf); - wifi_station_connect(); - // Schedule check - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + os_printf("Wifi changing association\n"); + wifi_station_disconnect(); + stconf.bssid_set = 0; + wifi_station_set_config(&stconf); + wifi_station_connect(); + // Schedule check + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, 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); - os_printf("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); - 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); + os_printf("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); + jsonHeader(connData, 200); + } else { + jsonHeader(connData, 400); + httpdSend(connData, "Cannot parse ssid or password", -1); + } + return HTTPD_CGI_DONE; } static bool 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; - } - 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; + } + if (c < '0' || c > '9') return false; + } + return false; } #define DEBUGIP #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 static 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(); - os_printf("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); - os_printf("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(); + os_printf("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); + os_printf("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 hostname[32]; - 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 hl = httpdFindArg(connData->getArgs, "hostname", hostname, sizeof(hostname)); - 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 && hl >= 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 { - // no static IP, set hostname - if (hl == 0) os_strcpy(hostname, "esp-link"); - flashConfig.staticip = 0; - os_strcpy(flashConfig.hostname, hostname); - os_sprintf(url, "{\"url\": \"http://%s\"}", hostname); - } - - configSave(); // ignore error... - // schedule change-over - os_timer_disarm(&reassTimer); - os_timer_setfn(&reassTimer, configWifiIP, NULL); - os_timer_arm(&reassTimer, 1000, 0); - // return redirect info - jsonHeader(connData, 200); - httpdSend(connData, url, -1); - return HTTPD_CGI_DONE; + char dhcp[8]; + char hostname[32]; + 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 hl = httpdFindArg(connData->getArgs, "hostname", hostname, sizeof(hostname)); + 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 && hl >= 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 { + // no static IP, set hostname + if (hl == 0) os_strcpy(hostname, "esp-link"); + flashConfig.staticip = 0; + os_strcpy(flashConfig.hostname, hostname); + os_sprintf(url, "{\"url\": \"http://%s\"}", hostname); + } + + configSave(); // ignore error... + // schedule change-over + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, configWifiIP, NULL); + os_timer_arm(&reassTimer, 1000, 0); + // 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); - os_printf("Wifi switching to mode %d\n", m); - wifi_set_opmode(m&3); - if (m == 1) { - wifi_set_sleep_type(SLEEP_MODE); - // STA-only mode, reset into STA+AP after a timeout - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); - } - jsonHeader(connData, 200); - } else { - jsonHeader(connData, 400); - } - return HTTPD_CGI_DONE; + 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); + os_printf("Wifi switching to mode %d\n", m); + wifi_set_opmode(m&3); + if (m == 1) { + wifi_set_sleep_type(SLEEP_MODE); + // STA-only mode, reset into STA+AP after a timeout + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); + } + jsonHeader(connData, 200); + } else { + jsonHeader(connData, 400); + } + return HTTPD_CGI_DONE; } static char *connStatuses[] = { "idle", "connecting", "wrong password", "AP not found", "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 @@ -487,109 +487,109 @@ 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; + char buff[1024]; + int len; - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - jsonHeader(connData, 200); + 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, ", "); + 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 (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"); + len += os_sprintf(buff+len, "\"x\":0}\n"); - os_printf(" -> %s\n", buff); - httpdSend(connData, buff, len); - return HTTPD_CGI_DONE; + os_printf(" -> %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]; + char buff[1024]; - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - os_strcpy(buff, "{"); - printWifiInfo(buff+1); - os_strcat(buff, "}"); + os_strcpy(buff, "{"); + printWifiInfo(buff+1); + os_strcat(buff, "}"); - jsonHeader(connData, 200); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; + 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); - int x = wifi_get_opmode() & 0x3; - os_printf("Wifi init, mode=%s\n", wifiMode[x]); - configWifiIP(); - - 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); + wifi_set_phy_mode(2); + int x = wifi_get_opmode() & 0x3; + os_printf("Wifi init, mode=%s\n", wifiMode[x]); + configWifiIP(); + + 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/config.c b/esp-link/config.c index b44b329..80ab55e 100644 --- a/esp-link/config.c +++ b/esp-link/config.c @@ -5,26 +5,27 @@ #include #include "config.h" #include "espfs.h" - -// hack: this from LwIP -extern uint16_t inet_chksum(void *dataptr, uint16_t len); +#include "crc16.h" FlashConfig flashConfig; FlashConfig flashDefault = { 33, 0, 0, MCU_RESET_PIN, MCU_ISP_PIN, LED_CONN_PIN, LED_SERIAL_PIN, 115200, - "esp-link\0 ", // hostname + "esp-link\0", // hostname 0, 0x00ffffff, 0, // static ip, netmask, gateway 0, // log mode 0, // swap uart (don't by default) 1, 0, // tcp_enable, rssi_enable "\0", // api_key + 0, 0, 0, // slip_enable, mqtt_enable, mqtt_status_enable + 1833, // mqtt port + "\0", "\0", "\0", "\0", "\0", // mqtt host, client, user, password, status-topic }; typedef union { FlashConfig fc; - uint8_t block[128]; + uint8_t block[1024]; } FlashFull; // magic number to recognize thet these are our flash settings as opposed to some random stuff @@ -63,7 +64,7 @@ bool ICACHE_FLASH_ATTR configSave(void) { ff.fc.crc = 0; //os_printf("cksum of: "); //memDump(&ff, sizeof(ff)); - ff.fc.crc = inet_chksum(&ff, sizeof(ff)); + ff.fc.crc = crc16_data((unsigned char*)&ff, sizeof(ff), 0); //os_printf("cksum is %04x\n", ff.fc.crc); // write primary with incorrect seq ff.fc.seq = 0xffffffff; @@ -121,12 +122,16 @@ static int ICACHE_FLASH_ATTR selectPrimary(FlashFull *ff0, FlashFull *ff1) { // check CRC of ff0 uint16_t crc = ff0->fc.crc; ff0->fc.crc = 0; - bool ff0_crc_ok = inet_chksum(ff0, sizeof(FlashFull)) == crc; + bool ff0_crc_ok = crc16_data((unsigned char*)ff0, sizeof(FlashFull), 0) == crc; + + os_printf("FLASH chk=0x%04x crc=0x%04x full_sz=%d sz=%d\n", + crc16_data((unsigned char*)ff0, sizeof(FlashFull), 0), + crc, sizeof(FlashFull), sizeof(FlashConfig)); // check CRC of ff1 crc = ff1->fc.crc; ff1->fc.crc = 0; - bool ff1_crc_ok = inet_chksum(ff1, sizeof(FlashFull)) == crc; + bool ff1_crc_ok = crc16_data((unsigned char*)ff1, sizeof(FlashFull), 0) == crc; // decided which we like better if (ff0_crc_ok) diff --git a/esp-link/config.h b/esp-link/config.h index 18284a5..89287e6 100644 --- a/esp-link/config.h +++ b/esp-link/config.h @@ -16,6 +16,11 @@ typedef struct { uint8_t swap_uart; // swap uart0 to gpio 13&15 uint8_t tcp_enable, rssi_enable; // TCP client settings char api_key[48]; // RSSI submission API key (Grovestreams for now) + uint8_t slip_enable, mqtt_enable, // SLIP protocol, MQTT client + mqtt_status_enable; // MQTT status reporting + uint16_t mqtt_port; + char mqtt_hostname[32], mqtt_client[48], mqtt_username[32], mqtt_password[32]; + char mqtt_status_topic[32]; } FlashConfig; extern FlashConfig flashConfig; diff --git a/esp-link/main.c b/esp-link/main.c index 5febf87..aea1846 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -17,6 +17,7 @@ #include "cgiwifi.h" #include "cgipins.h" #include "cgitcp.h" +#include "cgimqtt.h" #include "cgiflash.h" #include "auth.h" #include "espfs.h" @@ -29,7 +30,7 @@ #include "log.h" #include -//#define SHOW_HEAP_USE +#define SHOW_HEAP_USE //Function that tells the authentication system what users/passwords live on the system. //This is disabled in the default build; if you want to try it, enable the authBasic line in @@ -90,6 +91,7 @@ HttpdBuiltInUrl builtInUrls[] = { { "/wifi/special", cgiWiFiSpecial, NULL }, { "/pins", cgiPins, NULL }, { "/tcpclient", cgiTcp, NULL }, + { "/mqtt", cgiMqtt, NULL }, { "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem { NULL, NULL, NULL } @@ -120,16 +122,22 @@ static char *rst_codes[] = { # define VERS_STR(V) VERS_STR_STR(V) char* esp_link_version = VERS_STR(VERSION); -//Main routine. Initialize stdout, the I/O, filesystem and the webserver and we're done. +extern void app_init(void); + +// Main routine to initialize esp-link. void user_init(void) { + uart_init(115200, 115200); + logInit(); // must come after init of uart + os_delay_us(10000L); // get the flash config so we know how to init things //configWipe(); // uncomment to reset the config for testing purposes bool restoreOk = configRestore(); // init gpio pin registers gpio_init(); + gpio_output_set(0, 0, 0, (1<<15)); // some people tie it GND, gotta ensure it's disabled // init UART - uart_init(flashConfig.baud_rate, 115200); - logInit(); // must come after init of 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 os_delay_us(10000L); os_printf("\n\n** %s\n", esp_link_version); @@ -163,6 +171,5 @@ void user_init(void) { os_printf("** esp-link ready\n"); - // call user_main init - init(); -} \ No newline at end of file + app_init(); +} diff --git a/esp-link/status.c b/esp-link/status.c index a6080fe..4e0da55 100644 --- a/esp-link/status.c +++ b/esp-link/status.c @@ -14,56 +14,56 @@ static ETSTimer ledTimer; static void ICACHE_FLASH_ATTR setLed(int on) { int8_t pin = flashConfig.conn_led_pin; if (pin < 0) return; // disabled - // LED is active-low - if (on) { - gpio_output_set(0, (1<= 0) return; // not connected or other error - - // compose TCP command - uint8_t chan = MAX_TCP_CHAN-1; - tcpClientCommand(chan, 'T', "grovestreams.com:80"); - - // compose http header - char buf[1024]; - int hdrLen = os_sprintf(buf, - "PUT /api/feed?api_key=%s HTTP/1.0\r\n" - "Content-Type: application/json\r\n" - "Content-Length: XXXXX\r\n\r\n", - flashConfig.api_key); - - // http body - int dataLen = os_sprintf(buf+hdrLen, - "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", - flashConfig.hostname, GS_STREAM, rssi); - buf[hdrLen+dataLen++] = 0; - buf[hdrLen+dataLen++] = '\n'; - - // hackish way to fill in the content-length - os_sprintf(buf+hdrLen-9, "%5d", dataLen); - buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!) - - // send the request off and forget about it... - for (short i=0; i= 0) return; // not connected or other error + + // compose TCP command + uint8_t chan = MAX_TCP_CHAN-1; + tcpClientCommand(chan, 'T', "grovestreams.com:80"); + + // compose http header + char buf[1024]; + int hdrLen = os_sprintf(buf, + "PUT /api/feed?api_key=%s HTTP/1.0\r\n" + "Content-Type: application/json\r\n" + "Content-Length: XXXXX\r\n\r\n", + flashConfig.api_key); + + // http body + int dataLen = os_sprintf(buf+hdrLen, + "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", + flashConfig.hostname, GS_STREAM, rssi); + buf[hdrLen+dataLen++] = 0; + buf[hdrLen+dataLen++] = '\n'; + + // hackish way to fill in the content-length + os_sprintf(buf+hdrLen-9, "%5d", dataLen); + buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!) + + // send the request off and forget about it... + for (short i=0; i= 0) { - makeGpio(flashConfig.conn_led_pin); - setLed(1); - } - os_printf("CONN led=%d\n", flashConfig.conn_led_pin); - - os_timer_disarm(&ledTimer); - os_timer_setfn(&ledTimer, ledTimerCb, NULL); - os_timer_arm(&ledTimer, 2000, 0); - - os_timer_disarm(&rssiTimer); - os_timer_setfn(&rssiTimer, rssiTimerCb, NULL); - os_timer_arm(&rssiTimer, RSSI_INTERVAL, 1); // recurring timer + if (flashConfig.conn_led_pin >= 0) { + makeGpio(flashConfig.conn_led_pin); + setLed(1); + } + os_printf("CONN led=%d\n", flashConfig.conn_led_pin); + + os_timer_disarm(&ledTimer); + os_timer_setfn(&ledTimer, ledTimerCb, NULL); + os_timer_arm(&ledTimer, 2000, 0); + + os_timer_disarm(&rssiTimer); + os_timer_setfn(&rssiTimer, rssiTimerCb, NULL); + os_timer_arm(&rssiTimer, RSSI_INTERVAL, 1); // recurring timer } diff --git a/html/console.html b/html/console.html index db4c0a6..9dc359c 100644 --- a/html/console.html +++ b/html/console.html @@ -20,7 +20,7 @@ + diff --git a/html/style.css b/html/style.css index 29d15a3..ca072d8 100644 --- a/html/style.css +++ b/html/style.css @@ -3,6 +3,10 @@ html, button, input, select, textarea, .pure-g [class *= "pure-u"] { font-family: sans-serif; } +input[type="text"], input[type="password"] { + width: 100%; +} + body { color: #777; } diff --git a/html/ui.js b/html/ui.js index 56cc256..1d4c365 100644 --- a/html/ui.js +++ b/html/ui.js @@ -316,44 +316,44 @@ function createInputForPin(pin) { input.type = "radio"; input.name = "pins"; input.data = pin.name; - input.className = "pin-input"; + input.className = "pin-input"; input.value= pin.value; input.id = "opt-" + pin.value; if (currPin == pin.name) input.checked = "1"; - var descr = m('"); - var div = document.createElement("div"); - div.appendChild(input); - div.appendChild(descr); - return div; + var descr = m('"); + var div = document.createElement("div"); + div.appendChild(input); + div.appendChild(descr); + return div; } function displayPins(resp) { - var po = $("#pin-mux"); - po.innerHTML = ""; - currPin = resp.curr; - resp.map.forEach(function(v) { - po.appendChild(createInputForPin(v)); - }); - var i, inputs = $(".pin-input"); - for (i=0; i 0; + else el.value = data[v]; + } + }); + $("#mqtt-spinner").setAttribute("hidden", ""); + $("#mqtt-status-spinner").setAttribute("hidden", ""); + $("#mqtt-form").removeAttribute("hidden"); + $("#mqtt-status-form").removeAttribute("hidden"); + + var i, inputs = $("input"); + for (i=0; i * All rights reserved. * +* Modified by Thorsten von Eicken to make it fully callback based +* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * @@ -29,527 +31,628 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include "mqtt.h" - -#define MQTT_TASK_PRIO 0 -#define MQTT_TASK_QUEUE_SIZE 1 -#define MQTT_SEND_TIMOUT 5 - -#ifndef QUEUE_BUFFER_SIZE -#define QUEUE_BUFFER_SIZE 2048 -#endif - -unsigned char* default_certificate; -unsigned int default_certificate_len = 0; -unsigned char* default_private_key; -unsigned int default_private_key_len = 0; - -os_event_t mqtt_procTaskQueue[MQTT_TASK_QUEUE_SIZE]; - -LOCAL void ICACHE_FLASH_ATTR -mqtt_dns_found(const char* name, ip_addr_t* ipaddr, void* arg) { - struct espconn* pConn = (struct espconn *)arg; - MQTT_Client* client = (MQTT_Client *)pConn->reverse; - +// TODO: +// Handle SessionPresent=0 in CONNACK and rexmit subscriptions +// Improve timeout for CONNACK, currently only has keep-alive timeout (maybe send artificial ping?) +// Allow messages that don't require ACK to be sent even when pending_buffer is != NULL +// Set dup flag in retransmissions - if (ipaddr == NULL) { - os_printf("DNS: Found, but got no ip, try to reconnect\n"); - client->connState = TCP_RECONNECT_REQ; - return; - } - - os_printf("DNS: found ip %d.%d.%d.%d\n", - *((uint8 *)&ipaddr->addr), - *((uint8 *)&ipaddr->addr + 1), - *((uint8 *)&ipaddr->addr + 2), - *((uint8 *)&ipaddr->addr + 3)); +#include +#include "pktbuf.h" +#include "mqtt.h" - if (client->ip.addr == 0 && ipaddr->addr != 0) { - os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); -#ifdef CLIENT_SSL_ENABLE - if (client->security){ - espconn_secure_connect(client->pCon); - } - else -#endif - espconn_connect(client->pCon); +// HACK +sint8 espconn_secure_connect(struct espconn *espconn) { + return espconn_connect(espconn); +} +sint8 espconn_secure_disconnect(struct espconn *espconn) { + return espconn_disconnect(espconn); +} +sint8 espconn_secure_sent(struct espconn *espconn, uint8 *psent, uint16 length) { + return espconn_sent(espconn, psent, length); +} - client->connState = TCP_CONNECTING; - os_printf("MQTT-TCP: connecting...\n"); - } +// max message size supported for receive +#define MQTT_MAX_RCV_MESSAGE 2048 +// max message size for sending (except publish) +#define MQTT_MAX_SHORT_MESSAGE 128 - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); -} +static char* mqtt_msg_type[] = { + "NULL", "TYPE_CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", "PUBCOMP", + "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", "PINGREQ", "PINGRESP", "DISCONNECT", "RESV", +}; +// forward declarations +static void mqtt_enq_message(MQTT_Client *client, const uint8_t *data, uint16_t len); +static void mqtt_send_message(MQTT_Client* client); +static void mqtt_doAbort(MQTT_Client* client); -LOCAL void ICACHE_FLASH_ATTR +// Deliver a publish message to the client +static void ICACHE_FLASH_ATTR deliver_publish(MQTT_Client* client, uint8_t* message, uint16_t length) { - mqtt_event_data_t event_data; - event_data.topic_length = length; - event_data.topic = mqtt_get_publish_topic(message, &event_data.topic_length); - event_data.data_length = length; - event_data.data = mqtt_get_publish_data(message, &event_data.data_length); + // parse the message into topic and data + uint16_t topic_length = length; + const char *topic = mqtt_get_publish_topic(message, &topic_length); + uint16_t data_length = length; + const char *data = mqtt_get_publish_data(message, &data_length); + // callback to client if (client->dataCb) - client->dataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); - + client->dataCb((uint32_t*)client, topic, topic_length, data, data_length); if (client->cmdDataCb) - client->cmdDataCb((uint32_t*)client, event_data.topic, event_data.topic_length, event_data.data, event_data.data_length); - + client->cmdDataCb((uint32_t*)client, topic, topic_length, data, data_length); } /** * @brief Client received callback function. * @param arg: contain the ip link information * @param pdata: received data -* @param len: the lenght of received data +* @param len: the length of received data * @retval None */ -void ICACHE_FLASH_ATTR +static void ICACHE_FLASH_ATTR mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { + //os_printf("MQTT: recv CB\n"); uint8_t msg_type; - uint8_t msg_qos; uint16_t msg_id; + uint16_t msg_len; struct espconn* pCon = (struct espconn*)arg; MQTT_Client* client = (MQTT_Client *)pCon->reverse; + if (client == NULL) return; // aborted connection + + //os_printf("MQTT: Data received %d bytes\n", len); + + do { + // append data to our buffer + int avail = client->in_buffer_size - client->in_buffer_filled; + if (len <= avail) { + os_memcpy(client->in_buffer + client->in_buffer_filled, pdata, len); + client->in_buffer_filled += len; + len = 0; + } else { + os_memcpy(client->in_buffer + client->in_buffer_filled, pdata, avail); + client->in_buffer_filled += avail; + len -= avail; + pdata += avail; + } -READPACKET: - os_printf("MQTT-TCP: Data received %d bytes\n", len); - if (len < MQTT_BUF_SIZE && len > 0) { - os_memcpy(client->mqtt_state.in_buffer, pdata, len); - - msg_type = mqtt_get_type(client->mqtt_state.in_buffer); - msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer); - msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length); - if (client->connState == MQTT_CONNECT_SENDING) { - if (msg_type == MQTT_MSG_TYPE_CONNACK) { - if (client->mqtt_state.pending_msg_type != MQTT_MSG_TYPE_CONNECT) { - os_printf("MQTT: Invalid packet\n"); -#ifdef CLIENT_SSL_ENABLE - if (client->security){ - espconn_secure_disconnect(client->pCon); - } - else -#endif - espconn_disconnect(client->pCon); - } - else { - os_printf("MQTT: Connected to %s:%ld\n", client->host, client->port); - client->connState = MQTT_DATA; - if (client->connectedCb) - client->connectedCb((uint32_t*)client); - if (client->cmdConnectedCb) - client->cmdConnectedCb((uint32_t*)client); - } - } + // check out what's at the head of the buffer + msg_type = mqtt_get_type(client->in_buffer); + msg_id = mqtt_get_id(client->in_buffer, client->in_buffer_size); + msg_len = mqtt_get_total_length(client->in_buffer, client->in_buffer_size); + + if (msg_len > client->in_buffer_size) { + // oops, too long a message for us to digest, disconnect and hope for a miracle + os_printf("MQTT: Too long a message (%d bytes)\n", msg_len); + mqtt_doAbort(client); + return; + } + + // check whether what's left in the buffer is a complete message + if (msg_len > client->in_buffer_filled) break; + + if (client->connState != MQTT_CONNECTED) { + // why are we receiving something?? + os_printf("MQTT ERROR: recv in invalid state %d\n", client->connState); + mqtt_doAbort(client); + return; + } + + // we are connected and are sending/receiving data messages + uint8_t pending_msg_type = 0; + uint16_t pending_msg_id = 0; + if (client->pending_buffer != NULL) { + pending_msg_type = mqtt_get_type(client->pending_buffer->data); + pending_msg_id = mqtt_get_id(client->pending_buffer->data, client->pending_buffer->filled); } - else if (client->connState == MQTT_DATA) { - client->mqtt_state.message_length_read = len; - client->mqtt_state.message_length = mqtt_get_total_length(client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + os_printf("MQTT: Recv type=%s id=%04X len=%d; Pend type=%s id=%02X\n", + mqtt_msg_type[msg_type], msg_id, msg_len, mqtt_msg_type[pending_msg_type], pending_msg_id); - if (msg_type == MQTT_MSG_TYPE_SUBACK) { - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) + switch (msg_type) { + case MQTT_MSG_TYPE_CONNACK: + os_printf("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); + break; + + case MQTT_MSG_TYPE_SUBACK: + if (pending_msg_type == MQTT_MSG_TYPE_SUBSCRIBE && pending_msg_id == msg_id) { os_printf("MQTT: Subscribe successful\n"); + client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); } - else if (msg_type == MQTT_MSG_TYPE_UNSUBACK) { - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && client->mqtt_state.pending_msg_id == msg_id) - os_printf("MQTT: UnSubscribe successful\n"); - } - else if (msg_type == MQTT_MSG_TYPE_PUBLISH) { - if (msg_qos == 1) - client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id); - else if (msg_qos == 2) - client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id); - if (msg_qos == 1 || msg_qos == 2) { - os_printf("MQTT: Queue response QoS: %d\n", msg_qos); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - os_printf("MQTT: Queue full\n"); - } - } - deliver_publish(client, client->mqtt_state.in_buffer, client->mqtt_state.message_length_read); + break; + + case MQTT_MSG_TYPE_UNSUBACK: + if (pending_msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE && pending_msg_id == msg_id) { + os_printf("MQTT: Unsubscribe successful\n"); + client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); } - else if (msg_type == MQTT_MSG_TYPE_PUBACK) { - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { - os_printf("MQTT: received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish\n"); - } + break; + + case MQTT_MSG_TYPE_PUBACK: // ack for a publish we sent + if (pending_msg_type == MQTT_MSG_TYPE_PUBLISH && pending_msg_id == msg_id) { + os_printf("MQTT: QoS1 Publish successful\n"); + client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); } - else if (msg_type == MQTT_MSG_TYPE_PUBREC) { - client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - os_printf("MQTT: Queue full\n"); - } + break; + + case MQTT_MSG_TYPE_PUBREC: // rec for a publish we sent + if (pending_msg_type == MQTT_MSG_TYPE_PUBLISH && pending_msg_id == msg_id) { + os_printf("MQTT: QoS2 publish cont\n"); + client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); + // we need to send PUBREL + mqtt_msg_pubrel(&client->mqtt_connection, msg_id); + mqtt_enq_message(client, client->mqtt_connection.message.data, + client->mqtt_connection.message.length); } - else if (msg_type == MQTT_MSG_TYPE_PUBREL) { - client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - os_printf("MQTT: Queue full\n"); - } + break; + + case MQTT_MSG_TYPE_PUBCOMP: // comp for a pubrel we sent (originally publish we sent) + if (pending_msg_type == MQTT_MSG_TYPE_PUBREL && pending_msg_id == msg_id) { + os_printf("MQTT: QoS2 Publish successful\n"); + client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); } - else if (msg_type == MQTT_MSG_TYPE_PUBCOMP) { - if (client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH && client->mqtt_state.pending_msg_id == msg_id) { - os_printf("MQTT: receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish\n"); + break; + + case MQTT_MSG_TYPE_PUBLISH: { // incoming publish + // we may need to ACK the publish + uint8_t msg_qos = mqtt_get_qos(client->in_buffer); + uint16_t topic_length = msg_len; + os_printf("MQTT: Recv PUBLISH qos=%d %s\n", msg_qos, + mqtt_get_publish_topic(client->in_buffer, &topic_length)); + if (msg_qos == 1) mqtt_msg_puback(&client->mqtt_connection, msg_id); + if (msg_qos == 2) mqtt_msg_pubrec(&client->mqtt_connection, msg_id); + if (msg_qos == 1 || msg_qos == 2) { + mqtt_enq_message(client, client->mqtt_connection.message.data, + client->mqtt_connection.message.length); } + // send the publish message to clients + deliver_publish(client, client->in_buffer, msg_len); } - else if (msg_type == MQTT_MSG_TYPE_PINGREQ) { - client->mqtt_state.outbound_message = mqtt_msg_pingresp(&client->mqtt_state.mqtt_connection); - if (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - os_printf("MQTT: Queue full\n"); - } + break; + + case MQTT_MSG_TYPE_PUBREL: // rel for a rec we sent (originally publish received) + if (pending_msg_type == MQTT_MSG_TYPE_PUBREC && pending_msg_id == msg_id) { + os_printf("MQTT: Cont QoS2 recv\n"); + client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); + // we need to send PUBCOMP + mqtt_msg_pubcomp(&client->mqtt_connection, msg_id); + mqtt_enq_message(client, client->mqtt_connection.message.data, + client->mqtt_connection.message.length); } + break; - // NOTE: this is done down here and not in the switch case above - // because the PSOCK_READBUF_LEN() won't work inside a switch - // statement due to the way protothreads resume. - if (msg_type == MQTT_MSG_TYPE_PUBLISH) { - len = client->mqtt_state.message_length_read; + case MQTT_MSG_TYPE_PINGRESP: + client->keepAliveAckTick = 0; + break; + } - if (client->mqtt_state.message_length < client->mqtt_state.message_length_read) { - //client->connState = MQTT_PUBLISH_RECV; - //Not Implement yet - len -= client->mqtt_state.message_length; - pdata += client->mqtt_state.message_length; + // Shift out the message and see whether we have another one + if (msg_len < client->in_buffer_filled) + os_memcpy(client->in_buffer, client->in_buffer+msg_len, client->in_buffer_filled-msg_len); + client->in_buffer_filled -= msg_len; + } while(client->in_buffer_filled > 0 || len > 0); - os_printf("Get another published message\n"); - goto READPACKET; - } - } - } - } - else { - os_printf("ERROR: Message too long\n"); + // Send next packet out, if possible + if (!client->sending && client->pending_buffer == NULL && client->msgQueue != NULL) { + mqtt_send_message(client); } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); } /** -* @brief Client send over callback function. +* @brief Callback from TCP that previous send completed * @param arg: contain the ip link information * @retval None */ -void ICACHE_FLASH_ATTR +static void ICACHE_FLASH_ATTR mqtt_tcpclient_sent_cb(void* arg) { + //os_printf("MQTT: sent CB\n"); struct espconn* pCon = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pCon->reverse; - os_printf("MQTT-TCP: Sent\n"); - client->sendTimeout = 0; - if (client->connState == MQTT_DATA && client->mqtt_state.pending_msg_type == MQTT_MSG_TYPE_PUBLISH) { - if (client->publishedCb) - client->publishedCb((uint32_t*)client); - if (client->cmdPublishedCb) - client->cmdPublishedCb((uint32_t*)client); + if (client == NULL) return; // aborted connection ? + //os_printf("MQTT: Sent\n"); + + // if the message we sent is not a "pending" one, we need to free the buffer + if (client->sending_buffer != NULL) { + PktBuf *buf = client->sending_buffer; + //os_printf("PktBuf free %p l=%d\n", buf, buf->filled); + os_free(buf); + client->sending_buffer = NULL; + } + client->sending = false; + + // send next message if one is queued and we're not expecting an ACK + if (client->connState == MQTT_CONNECTED && client->pending_buffer == NULL && + client->msgQueue != NULL) { + mqtt_send_message(client); } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); } -void ICACHE_FLASH_ATTR +/* + * @brief: Timer function to handle timeouts + */ +static void ICACHE_FLASH_ATTR mqtt_timer(void* arg) { MQTT_Client* client = (MQTT_Client*)arg; + //os_printf("MQTT: timer CB\n"); - if (client->connState == MQTT_DATA) { - client->keepAliveTick++; - if (client->keepAliveTick > client->mqtt_state.connect_info->keepalive) { - - os_printf("\nMQTT: Send keepalive packet to %s:%ld!\n", client->host, client->port); - client->mqtt_state.outbound_message = mqtt_msg_pingreq(&client->mqtt_state.mqtt_connection); - client->mqtt_state.pending_msg_type = MQTT_MSG_TYPE_PINGREQ; - client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); - client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + switch (client->connState) { + default: break; + case MQTT_CONNECTED: + // first check whether we're timing out for an ACK + if (client->pending_buffer != NULL && --client->timeoutTick == 0) { + // looks like we're not getting a response in time, abort the connection + mqtt_doAbort(client); + client->timeoutTick = 0; // trick to make reconnect happen in 1 second + return; + } - client->sendTimeout = MQTT_SEND_TIMOUT; - os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); -#ifdef CLIENT_SSL_ENABLE - if (client->security){ - espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); - } - else -#endif - espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); - - client->mqtt_state.outbound_message = NULL; + // check whether our last keep-alive timed out + if (client->keepAliveAckTick > 0 && --client->keepAliveAckTick == 0) { + os_printf("\nMQTT ERROR: Keep-alive timed out\n"); + mqtt_doAbort(client); + return; + } - client->keepAliveTick = 0; - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + // check whether we need to send a keep-alive message + if (client->keepAliveTick > 0 && --client->keepAliveTick == 0) { + // timeout: we need to send a ping message + //os_printf("MQTT: Send keepalive\n"); + mqtt_msg_pingreq(&client->mqtt_connection); + PktBuf *buf = PktBuf_New(client->mqtt_connection.message.length); + os_memcpy(buf->data, client->mqtt_connection.message.data, + client->mqtt_connection.message.length); + buf->filled = client->mqtt_connection.message.length; + client->msgQueue = PktBuf_Unshift(client->msgQueue, buf); + mqtt_send_message(client); + client->keepAliveTick = client->connect_info.keepalive; + client->keepAliveAckTick = client->sendTimeout; } - } - else if (client->connState == TCP_RECONNECT_REQ) { - client->reconnectTick++; - if (client->reconnectTick > MQTT_RECONNECT_TIMEOUT) { - client->reconnectTick = 0; - client->connState = TCP_RECONNECT; - if (client->tcpDisconnectedCb) - client->tcpDisconnectedCb((uint32_t*)client); - if (client->cmdTcpDisconnectedCb) - client->cmdTcpDisconnectedCb((uint32_t*)client); - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + break; + + case TCP_RECONNECT_REQ: + if (client->timeoutTick == 0 || --client->timeoutTick == 0) { + // it's time to reconnect! start by re-enqueueing anything pending + if (client->pending_buffer != NULL) { + client->msgQueue = PktBuf_Unshift(client->msgQueue, client->pending_buffer); + client->pending_buffer = NULL; + } + MQTT_Connect(client); } } - if (client->sendTimeout > 0) - client->sendTimeout--; } +/** + * @brief Callback from SDK that socket is disconnected + * @param arg: contain the ip link information + * @retval None + */ void ICACHE_FLASH_ATTR mqtt_tcpclient_discon_cb(void* arg) { - struct espconn* pespconn = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pespconn->reverse; - os_printf("MQTT-TCP: Disconnected callback\n"); + + os_printf("MQTT: Disconnect CB, freeing espconn %p\n", arg); + if (pespconn->proto.tcp) os_free(pespconn->proto.tcp); + os_free(pespconn); + + // if this is an aborted connection we're done + if (client == NULL) return; + + os_printf("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); + + // reconnect unless we're in a permanently disconnected state + if (client->connState == MQTT_DISCONNECTED) return; + client->timeoutTick = 2; client->connState = TCP_RECONNECT_REQ; - if (client->disconnectedCb) - client->disconnectedCb((uint32_t*)client); - if (client->cmdDisconnectedCb) - client->cmdDisconnectedCb((uint32_t*)client); +} + +/** +* @brief Callback from SDK that socket got reset, note that no discon_cb will follow +* @param arg: contain the ip link information +* @retval None +*/ +static void ICACHE_FLASH_ATTR +mqtt_tcpclient_recon_cb(void* arg, int8_t err) { + struct espconn* pespconn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pespconn->reverse; + + os_printf("MQTT: Reset CB, freeing espconn %p (err=%d)\n", arg, 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); - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + // reconnect unless we're in a permanently disconnected state + if (client->connState == MQTT_DISCONNECTED) return; + client->timeoutTick = 2; + client->connState = TCP_RECONNECT_REQ; } + /** -* @brief Tcp client connect success callback function. +* @brief Callback from SDK that socket is connected * @param arg: contain the ip link information * @retval None */ -void ICACHE_FLASH_ATTR +static void ICACHE_FLASH_ATTR mqtt_tcpclient_connect_cb(void* arg) { struct espconn* pCon = (struct espconn *)arg; MQTT_Client* client = (MQTT_Client *)pCon->reverse; + if (client == NULL) return; // aborted connection espconn_regist_disconcb(client->pCon, mqtt_tcpclient_discon_cb); - espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv);//////// - espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb);/////// - os_printf("MQTT: Connected to broker %s:%ld\n", client->host, client->port); - - mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer, client->mqtt_state.out_buffer_length); - client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection, client->mqtt_state.connect_info); - client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data); - client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); + espconn_regist_recvcb(client->pCon, mqtt_tcpclient_recv); + espconn_regist_sentcb(client->pCon, mqtt_tcpclient_sent_cb); + os_printf("MQTT: TCP connected to %s:%d\n", client->host, client->port); + + // send MQTT connect message to broker + mqtt_msg_connect(&client->mqtt_connection, &client->connect_info); + PktBuf *buf = PktBuf_New(client->mqtt_connection.message.length); + os_memcpy(buf->data, client->mqtt_connection.message.data, + client->mqtt_connection.message.length); + buf->filled = client->mqtt_connection.message.length; + client->msgQueue = PktBuf_Unshift(client->msgQueue, buf); // prepend to send (rexmit) queue + mqtt_send_message(client); + client->connState = MQTT_CONNECTED; // v3.1.1 allows publishing while still connecting +} +/** + * @brief Allocate and enqueue mqtt message, kick sending, if appropriate + */ +static void ICACHE_FLASH_ATTR +mqtt_enq_message(MQTT_Client *client, const uint8_t *data, uint16_t len) { + PktBuf *buf = PktBuf_New(len); + os_memcpy(buf->data, data, len); + buf->filled = len; + client->msgQueue = PktBuf_Push(client->msgQueue, buf); + + if (client->connState == MQTT_CONNECTED && !client->sending && client->pending_buffer == NULL) { + mqtt_send_message(client); + } +} - client->sendTimeout = MQTT_SEND_TIMOUT; - os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); -#ifdef CLIENT_SSL_ENABLE - if (client->security){ - espconn_secure_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); +/** + * @brief Send out top message in queue onto socket + */ +static void ICACHE_FLASH_ATTR +mqtt_send_message(MQTT_Client* client) { + //os_printf("MQTT: Send_message\n"); + PktBuf *buf = client->msgQueue; + if (buf == NULL || client->sending) return; // ahem... + client->msgQueue = PktBuf_Shift(client->msgQueue); + + // get some details about the message + uint16_t msg_type = mqtt_get_type(buf->data); + uint8_t msg_id = mqtt_get_id(buf->data, buf->filled); + os_printf("MQTT: Send type=%s id=%04X len=%d\n", mqtt_msg_type[msg_type], msg_id, buf->filled); +#if 0 + for (int i=0; ifilled; i++) { + if (buf->data[i] >= ' ' && buf->data[i] <= '~') os_printf("%c", buf->data[i]); + else os_printf("\\x%02X", buf->data[i]); } - else + os_printf("\n"); #endif - espconn_sent(client->pCon, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length); - client->mqtt_state.outbound_message = NULL; - client->connState = MQTT_CONNECT_SENDING; - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + // send the message out + if (client->security) + espconn_secure_sent(client->pCon, buf->data, buf->filled); + else + espconn_sent(client->pCon, buf->data, buf->filled); + client->sending = true; + + // depending on whether it needs an ack we need to hold on to the message + bool needsAck = + (msg_type == MQTT_MSG_TYPE_PUBLISH && mqtt_get_qos(buf->data) > 0) || + msg_type == MQTT_MSG_TYPE_PUBREL || msg_type == MQTT_MSG_TYPE_PUBREC || + msg_type == MQTT_MSG_TYPE_SUBSCRIBE || msg_type == MQTT_MSG_TYPE_UNSUBSCRIBE || + msg_type == MQTT_MSG_TYPE_PINGREQ; + if (msg_type == MQTT_MSG_TYPE_PINGREQ) { + client->pending_buffer = NULL; // we don't need to rexmit this one + client->sending_buffer = buf; + } else if (needsAck) { + client->pending_buffer = buf; // remeber for rexmit on disconnect/reconnect + client->sending_buffer = NULL; + client->timeoutTick = client->sendTimeout+1; // +1 to ensure full sendTireout seconds + } else { + client->pending_buffer = NULL; + client->sending_buffer = buf; + client->timeoutTick = 0; + } + client->keepAliveTick = client->connect_info.keepalive > 0 ? client->connect_info.keepalive+1 : 0; } /** -* @brief Tcp client connect repeat callback function. -* @param arg: contain the ip link information -* @retval None +* @brief DNS lookup for broker hostname completed, move to next phase */ -void ICACHE_FLASH_ATTR -mqtt_tcpclient_recon_cb(void* arg, int8_t errType) { - struct espconn* pCon = (struct espconn *)arg; - MQTT_Client* client = (MQTT_Client *)pCon->reverse; +static void ICACHE_FLASH_ATTR +mqtt_dns_found(const char* name, ip_addr_t* ipaddr, void* arg) { + struct espconn* pConn = (struct espconn *)arg; + MQTT_Client* client = (MQTT_Client *)pConn->reverse; - os_printf("MQTT-TCP: Reconnect to %s:%ld\n", client->host, client->port); + if (ipaddr == NULL) { + os_printf("MQTT DNS: Got no ip, try to reconnect\n"); + client->timeoutTick = 10; + client->connState = TCP_RECONNECT_REQ; // the timer will kick-off a reconnection + return; + } - client->connState = TCP_RECONNECT_REQ; + os_printf("MQTT DNS: found ip %d.%d.%d.%d\n", + *((uint8 *)&ipaddr->addr), + *((uint8 *)&ipaddr->addr + 1), + *((uint8 *)&ipaddr->addr + 2), + *((uint8 *)&ipaddr->addr + 3)); + + if (client->ip.addr == 0 && ipaddr->addr != 0) { + os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); + uint8_t err; + if (client->security) + err = espconn_secure_connect(client->pCon); + else + err = espconn_connect(client->pCon); + if (err != 0) { + os_printf("MQTT ERROR: Failed to connect\n"); + client->connState = TCP_RECONNECT_REQ; + client->timeoutTick = 10; + } else { + os_printf("MQTT: connecting...\n"); + } + } +} + +//===== publish / subscribe - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); +static void ICACHE_FLASH_ATTR +msg_conn_init(mqtt_connection_t *new_msg, mqtt_connection_t *old_msg, + uint8_t *buf, uint16_t buflen) { + new_msg->message_id = old_msg->message_id; + new_msg->buffer = buf; + new_msg->buffer_length = buflen; } /** * @brief MQTT publish function. -* @param client: MQTT_Client reference -* @param topic: string topic will publish to -* @param data: buffer data send point to +* @param client: MQTT_Client reference +* @param topic: string topic will publish to +* @param data: buffer data send point to * @param data_length: length of data -* @param qos: qos -* @param retain: retain +* @param qos: qos +* @param retain: retain * @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) { - int data_length = os_strlen(data); - uint8_t dataBuffer[MQTT_BUF_SIZE]; - uint16_t dataLen; - client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection, - topic, data, data_length, - qos, retain, - &client->mqtt_state.pending_msg_id); - if (client->mqtt_state.outbound_message->length == 0) { - os_printf("MQTT: Queuing Publish failed\n"); + // 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); + if (buf == NULL) { + os_printf("MQTT ERROR: Cannot allocate buffer for %d byte publish\n", buf_len); return FALSE; } - os_printf("MQTT: Queuing Publish, length: %d, queue size(%ld/%ld)\n", client->mqtt_state.outbound_message->length, client->msgQueue.rb.fill_cnt, client->msgQueue.rb.size); - while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - os_printf("MQTT: Queue full\n"); - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { - os_printf("MQTT: Serious buffer error\n"); - return FALSE; - } + // use a temporary mqtt_message_t pointing to our buffer, this is a bit of a mess because we + // need to keep track of the message_id that is embedded in it + mqtt_connection_t msg; + msg_conn_init(&msg, &client->mqtt_connection, buf->data, buf_len); + uint16_t msg_id; + if (!mqtt_msg_publish(&msg, topic, data, data_length, qos, retain, &msg_id)){ + os_printf("MQTT ERROR: Queuing Publish failed\n"); + os_free(buf); + return FALSE; + } + client->mqtt_connection.message_id = msg.message_id; + + os_printf("MQTT: Publish, topic: \"%s\", length: %d\n", topic, msg.message.length); + client->msgQueue = PktBuf_Push(client->msgQueue, buf); + + if (!client->sending && client->pending_buffer == NULL) { + mqtt_send_message(client); } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); return TRUE; } /** -* @brief MQTT subscibe function. -* @param client: MQTT_Client reference -* @param topic: string topic will subscribe -* @param qos: qos +* @brief MQTT subscribe function. +* @param client: MQTT_Client reference +* @param topic: string topic will subscribe +* @param qos: qos * @retval TRUE if success queue */ bool ICACHE_FLASH_ATTR MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos) { - uint8_t dataBuffer[MQTT_BUF_SIZE]; - uint16_t dataLen; - - client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection, - topic, 0, - &client->mqtt_state.pending_msg_id); - os_printf("MQTT: Queue Subscribe, topic: \"%s\", id: %d\n", topic, client->mqtt_state.pending_msg_id); - while (QUEUE_Puts(&client->msgQueue, client->mqtt_state.outbound_message->data, client->mqtt_state.outbound_message->length) == -1) { - os_printf("MQTT: Queue full\n"); - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == -1) { - os_printf("MQTT: Serious buffer error\n"); - return FALSE; - } + uint16_t msg_id; + if (!mqtt_msg_subscribe(&client->mqtt_connection, topic, 0, &msg_id)) { + os_printf("MQTT ERROR: Queuing Subscribe failed (too long)\n"); + return FALSE; } - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)client); + os_printf("MQTT: Subscribe, topic: \"%s\"\n", topic); + mqtt_enq_message(client, client->mqtt_connection.message.data, + client->mqtt_connection.message.length); return TRUE; } -void ICACHE_FLASH_ATTR -MQTT_Task(os_event_t* e) { - MQTT_Client* client = (MQTT_Client*)e->par; - uint8_t dataBuffer[MQTT_BUF_SIZE]; - uint16_t dataLen; - if (e->par == 0) - return; - - if (client->connState == TCP_RECONNECT_REQ) { - return; - } - else if (client->connState == TCP_RECONNECT) { - MQTT_Connect(client); - os_printf("MQTT-TCP: Reconnect to: %s:%ld\n", client->host, client->port); - client->connState = TCP_CONNECTING; - } - else if (client->connState == MQTT_DATA) { - if (QUEUE_IsEmpty(&client->msgQueue) || client->sendTimeout != 0) - return; - - if (QUEUE_Gets(&client->msgQueue, dataBuffer, &dataLen, MQTT_BUF_SIZE) == 0) { - client->mqtt_state.pending_msg_type = mqtt_get_type(dataBuffer); - client->mqtt_state.pending_msg_id = mqtt_get_id(dataBuffer, dataLen); - client->sendTimeout = MQTT_SEND_TIMOUT; - os_printf("MQTT: Sending, type: %d, id: %04X\n", client->mqtt_state.pending_msg_type, client->mqtt_state.pending_msg_id); -#ifdef CLIENT_SSL_ENABLE - if (client->security){ - espconn_secure_sent(client->pCon, dataBuffer, dataLen); - } - else -#endif - espconn_sent(client->pCon, dataBuffer, dataLen); - - client->mqtt_state.outbound_message = NULL; - return; - } - return; - } -} +//===== Initialization and connect/disconnect /** -* @brief MQTT initialization connection function -* @param client: MQTT_Client reference -* @param host: Domain or IP string -* @param port: Port to connect -* @param security: 1 for ssl, 0 for none +* @brief MQTT initialization mqtt client function +* @param client: MQTT_Client reference +* @param host: Domain or IP string +* @param port: Port to connect +* @param security: 1 for ssl, 0 for none +* @param clientid: MQTT client id +* @param client_user: MQTT client user +* @param client_pass: MQTT client password +* @param keepAliveTime: MQTT keep alive timer, in second +* @param cleanSession: MQTT ... * @retval None */ void ICACHE_FLASH_ATTR -MQTT_InitConnection(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security) { - os_printf("MQTT_InitConnection\n"); - uint8_t len = sizeof(MQTT_Client); - os_memset(mqttClient, 0, len); +MQTT_Init(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security, uint8_t sendTimeout, + char* client_id, char* client_user, char* client_pass, + uint8_t keepAliveTime, uint8_t cleanSession) { + os_printf("MQTT_Init\n"); + + os_memset(mqttClient, 0, sizeof(MQTT_Client)); - uint32_t temp = os_strlen(host); - mqttClient->host = (char*)os_zalloc(temp + 1); + mqttClient->host = (char*)os_zalloc(os_strlen(host) + 1); os_strcpy(mqttClient->host, host); - mqttClient->host[temp] = 0; mqttClient->port = port; - mqttClient->security = security; -} + mqttClient->security = !!security; -/** -* @brief MQTT initialization mqtt client function -* @param client: MQTT_Client reference -* @param clientid: MQTT client id -* @param client_user:MQTT client user -* @param client_pass:MQTT client password -* @param client_pass:MQTT keep alive timer, in second -* @retval None -*/ -void ICACHE_FLASH_ATTR -MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) { - uint32_t temp; - os_printf("MQTT_InitClient\n"); + // timeouts with sanity checks + mqttClient->sendTimeout = sendTimeout == 0 ? 1 : sendTimeout; os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); - temp = os_strlen(client_id); - mqttClient->connect_info.client_id = (char*)os_zalloc(temp + 1); + mqttClient->connect_info.client_id = (char*)os_zalloc(os_strlen(client_id) + 1); os_strcpy(mqttClient->connect_info.client_id, client_id); - mqttClient->connect_info.client_id[temp] = 0; - temp = os_strlen(client_user); - mqttClient->connect_info.username = (char*)os_zalloc(temp + 1); + mqttClient->connect_info.username = (char*)os_zalloc(os_strlen(client_user) + 1); os_strcpy(mqttClient->connect_info.username, client_user); - mqttClient->connect_info.username[temp] = 0; - temp = os_strlen(client_pass); - mqttClient->connect_info.password = (char*)os_zalloc(temp + 1); + mqttClient->connect_info.password = (char*)os_zalloc(os_strlen(client_pass) + 1); os_strcpy(mqttClient->connect_info.password, client_pass); - mqttClient->connect_info.password[temp] = 0; - mqttClient->connect_info.keepalive = keepAliveTime; mqttClient->connect_info.clean_session = cleanSession; - mqttClient->mqtt_state.in_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); - mqttClient->mqtt_state.in_buffer_length = MQTT_BUF_SIZE; - mqttClient->mqtt_state.out_buffer = (uint8_t *)os_zalloc(MQTT_BUF_SIZE); - mqttClient->mqtt_state.out_buffer_length = MQTT_BUF_SIZE; - mqttClient->mqtt_state.connect_info = &mqttClient->connect_info; + mqttClient->in_buffer = (uint8_t *)os_zalloc(MQTT_MAX_RCV_MESSAGE); + mqttClient->in_buffer_size = MQTT_MAX_RCV_MESSAGE; - mqtt_msg_init(&mqttClient->mqtt_state.mqtt_connection, mqttClient->mqtt_state.out_buffer, mqttClient->mqtt_state.out_buffer_length); - - QUEUE_Init(&mqttClient->msgQueue, QUEUE_BUFFER_SIZE); - - system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); - system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); + uint8_t *out_buffer = (uint8_t *)os_zalloc(MQTT_MAX_SHORT_MESSAGE); + mqtt_msg_init(&mqttClient->mqtt_connection, out_buffer, MQTT_MAX_SHORT_MESSAGE); } +/** + * @brief MQTT Set Last Will Topic, must be called before MQTT_Connect + */ void ICACHE_FLASH_ATTR -MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, uint8_t will_qos, uint8_t will_retain) { - uint32_t temp; - temp = os_strlen((char*)will_topic); - mqttClient->connect_info.will_topic = (char*)os_zalloc(temp + 1); - os_strcpy((char*)mqttClient->connect_info.will_topic, (char*)will_topic); - mqttClient->connect_info.will_topic[temp] = 0; +MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, + uint8_t will_qos, uint8_t will_retain) { - temp = os_strlen((char*)will_msg); - mqttClient->connect_info.will_message = (char*)os_zalloc(temp + 1); - os_strcpy((char*)mqttClient->connect_info.will_message, (char*)will_msg); - mqttClient->connect_info.will_message[temp] = 0; + mqttClient->connect_info.will_topic = (char*)os_zalloc(os_strlen(will_topic) + 1); + os_strcpy((char*)mqttClient->connect_info.will_topic, will_topic); + mqttClient->connect_info.will_message = (char*)os_zalloc(os_strlen(will_msg) + 1); + os_strcpy((char*)mqttClient->connect_info.will_message, will_msg); mqttClient->connect_info.will_qos = will_qos; mqttClient->connect_info.will_retain = will_retain; + + // TODO: if we're connected we should disconnect and reconnect to establish the new LWT } /** @@ -559,7 +662,7 @@ MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, uint8_t */ void ICACHE_FLASH_ATTR MQTT_Connect(MQTT_Client* mqttClient) { - MQTT_Disconnect(mqttClient); + //MQTT_Disconnect(mqttClient); mqttClient->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); mqttClient->pCon->type = ESPCONN_TCP; mqttClient->pCon->state = ESPCONN_NONE; @@ -570,42 +673,69 @@ MQTT_Connect(MQTT_Client* mqttClient) { espconn_regist_connectcb(mqttClient->pCon, mqtt_tcpclient_connect_cb); espconn_regist_reconcb(mqttClient->pCon, mqtt_tcpclient_recon_cb); - mqttClient->keepAliveTick = 0; - mqttClient->reconnectTick = 0; - - + // start timer function to tick every second os_timer_disarm(&mqttClient->mqttTimer); os_timer_setfn(&mqttClient->mqttTimer, (os_timer_func_t *)mqtt_timer, mqttClient); os_timer_arm(&mqttClient->mqttTimer, 1000, 1); - if (UTILS_StrToIP((const char *)mqttClient->host, (void*)&mqttClient->pCon->proto.tcp->remote_ip)) { - os_printf("MQTT-TCP: Connect to ip %s:%ld\n", mqttClient->host, mqttClient->port); -#ifdef CLIENT_SSL_ENABLE - if (mqttClient->security){ - espconn_secure_connect(mqttClient->pCon); - } + // initiate the TCP connection or DNS lookup + os_printf("MQTT: Connect to %s:%d %p\n", mqttClient->host, mqttClient->port, mqttClient->pCon); + if (UTILS_StrToIP((const char *)mqttClient->host, + (void*)&mqttClient->pCon->proto.tcp->remote_ip)) { + uint8_t err; + if (mqttClient->security) + err = espconn_secure_connect(mqttClient->pCon); else -#endif - espconn_connect(mqttClient->pCon); - } - else { - os_printf("MQTT-TCP: Connect to domain %s:%ld\n", mqttClient->host, mqttClient->port); - espconn_gethostbyname(mqttClient->pCon, (const char *)mqttClient->host, &mqttClient->ip, mqtt_dns_found); + err = espconn_connect(mqttClient->pCon); + if (err != 0) { + os_printf("MQTT ERROR: Failed to connect\n"); + os_free(mqttClient->pCon->proto.tcp); + os_free(mqttClient->pCon); + mqttClient->pCon = NULL; + return; + } + } else { + espconn_gethostbyname(mqttClient->pCon, (const char *)mqttClient->host, &mqttClient->ip, + mqtt_dns_found); } + mqttClient->connState = TCP_CONNECTING; + mqttClient->timeoutTick = 20; // generous timeout to allow for DNS, etc + mqttClient->sending = FALSE; + mqttClient->msgQueue = NULL; } -void ICACHE_FLASH_ATTR -MQTT_Disconnect(MQTT_Client* mqttClient) { - if (mqttClient->pCon) { - os_printf("Free memory\n"); - if (mqttClient->pCon->proto.tcp) - os_free(mqttClient->pCon->proto.tcp); - os_free(mqttClient->pCon); - mqttClient->pCon = NULL; +static void ICACHE_FLASH_ATTR +mqtt_doAbort(MQTT_Client* client) { + os_printf("MQTT: Disconnecting from %s:%d (%p)\n", client->host, client->port, client->pCon); + client->pCon->reverse = NULL; // ensure we jettison this pCon... + if (client->security) + espconn_secure_disconnect(client->pCon); + else + espconn_disconnect(client->pCon); + + if (client->disconnectedCb) client->disconnectedCb((uint32_t*)client); + if (client->cmdDisconnectedCb) client->cmdDisconnectedCb((uint32_t*)client); + + if (client->sending_buffer != NULL) { + os_free(client->sending_buffer); + client->sending_buffer = NULL; } + client->pCon = NULL; // it will be freed in disconnect callback + client->connState = TCP_RECONNECT_REQ; + client->timeoutTick = 2; // reconnect in a few seconds +} +void ICACHE_FLASH_ATTR +MQTT_Disconnect(MQTT_Client* mqttClient) { os_timer_disarm(&mqttClient->mqttTimer); + if (mqttClient->connState == MQTT_DISCONNECTED) return; + if (mqttClient->connState == TCP_RECONNECT_REQ) { + mqttClient->connState = MQTT_DISCONNECTED; + return; + } + mqtt_doAbort(mqttClient); + mqttClient->connState = MQTT_DISCONNECTED; // ensure we don't automatically reconnect } void ICACHE_FLASH_ATTR @@ -619,8 +749,7 @@ MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb) { } void ICACHE_FLASH_ATTR -MQTT_OnTcpDisconnected(MQTT_Client *mqttClient, MqttCallback tcpDisconnectedCb) -{ +MQTT_OnTcpDisconnected(MQTT_Client *mqttClient, MqttCallback tcpDisconnectedCb) { mqttClient->tcpDisconnectedCb = tcpDisconnectedCb; } diff --git a/mqtt/mqtt.h b/mqtt/mqtt.h index 00c432a..6ca7091 100644 --- a/mqtt/mqtt.h +++ b/mqtt/mqtt.h @@ -27,117 +27,101 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USER_AT_MQTT_H_ -#define USER_AT_MQTT_H_ +#ifndef MQTT_H_ +#define MQTT_H_ -#include #include "mqtt_msg.h" -#include "queue.h" +#include "pktbuf.h" -typedef struct mqtt_event_data_t { - uint8_t type; - const char* topic; - const char* data; - uint16_t topic_length; - uint16_t data_length; - uint16_t data_offset; -} mqtt_event_data_t; - -typedef struct mqtt_state_t { - uint16_t port; - int auto_reconnect; - mqtt_connect_info_t* connect_info; - uint8_t* in_buffer; - uint8_t* out_buffer; - int in_buffer_length; - int out_buffer_length; - uint16_t message_length; - uint16_t message_length_read; - mqtt_message_t* outbound_message; - mqtt_connection_t mqtt_connection; - uint16_t pending_msg_id; - int pending_msg_type; - int pending_publish_qos; -} mqtt_state_t; +// in rest.c +uint8_t UTILS_StrToIP(const char* str, void *ip); +// State of MQTT connection typedef enum { - WIFI_INIT, - WIFI_CONNECTING, - WIFI_CONNECTING_ERROR, - WIFI_CONNECTED, - DNS_RESOLVE, - TCP_DISCONNECTED, - TCP_RECONNECT_REQ, - TCP_RECONNECT, - TCP_CONNECTING, - TCP_CONNECTING_ERROR, - TCP_CONNECTED, - MQTT_CONNECT_SEND, - MQTT_CONNECT_SENDING, - MQTT_SUBSCIBE_SEND, - MQTT_SUBSCIBE_SENDING, - MQTT_DATA, - MQTT_PUBLISH_RECV, - MQTT_PUBLISHING + MQTT_DISCONNECTED, // we're in disconnected state + TCP_RECONNECT_REQ, // connect failed, needs reconnecting + TCP_CONNECTING, // in TCP connection process + MQTT_CONNECTED, // conneted (or connecting) } tConnState; +// Simple notification callback typedef void (*MqttCallback)(uint32_t* args); -typedef void (*MqttDataCallback)(uint32_t* args, const char* topic, uint32_t topic_len, const char* data, uint32_t lengh); +// Callback with data messge +typedef void (*MqttDataCallback)(uint32_t* args, const char* topic, uint32_t topic_len, + const char* data, uint32_t data_len); +// MQTTY client data structure typedef struct { - struct espconn* pCon; - uint8_t security; - char* host; - uint32_t port; - ip_addr_t ip; - mqtt_state_t mqtt_state; - mqtt_connect_info_t connect_info; - MqttCallback connectedCb; - MqttCallback cmdConnectedCb; - MqttCallback disconnectedCb; - MqttCallback cmdDisconnectedCb; - MqttCallback tcpDisconnectedCb; - MqttCallback cmdTcpDisconnectedCb; - MqttCallback publishedCb; - MqttCallback cmdPublishedCb; - MqttDataCallback dataCb; - MqttDataCallback cmdDataCb; - ETSTimer mqttTimer; - uint32_t keepAliveTick; - uint32_t reconnectTick; - uint32_t sendTimeout; - tConnState connState; - QUEUE msgQueue; - void* user_data; + struct espconn* pCon; // socket + // connection information + char* host; // MQTT server + uint16_t port; + uint8_t security; // 0=tcp, 1=ssl + ip_addr_t ip; // MQTT server IP address + mqtt_connect_info_t connect_info; // info to connect/reconnect + // protocol state and message assembly + tConnState connState; // connection state + bool sending; // espconn_send is pending + mqtt_connection_t mqtt_connection; // message assembly descriptor + PktBuf* msgQueue; // queued outbound messages + // TCP input buffer + uint8_t* in_buffer; + int in_buffer_size; // length allocated + int in_buffer_filled; // number of bytes held + // outstanding message when we expect an ACK + PktBuf* pending_buffer; // buffer sent and awaiting ACK + PktBuf* sending_buffer; // buffer sent not awaiting ACK + // timer and associated timeout counters + ETSTimer mqttTimer; // timer for this connection + uint8_t keepAliveTick; // seconds 'til keep-alive is required (0=no k-a) + uint8_t keepAliveAckTick; // seconds 'til keep-alive ack is overdue (0=no k-a) + uint8_t timeoutTick; // seconds 'til other timeout + uint8_t sendTimeout; // value of send timeout setting + // callbacks + MqttCallback connectedCb; + MqttCallback cmdConnectedCb; + MqttCallback disconnectedCb; + MqttCallback cmdDisconnectedCb; + MqttCallback tcpDisconnectedCb; + MqttCallback cmdTcpDisconnectedCb; + MqttCallback publishedCb; + MqttCallback cmdPublishedCb; + MqttDataCallback dataCb; + MqttDataCallback cmdDataCb; + // misc + void* user_data; } MQTT_Client; -#define SEC_NONSSL 0 -#define SEC_SSL 1 +// Initialize client data structure +void MQTT_Init(MQTT_Client* mqttClient, char* host, uint32 port, + uint8_t security, uint8_t sendTimeout, + char* client_id, char* client_user, char* client_pass, + uint8_t keepAliveTime, uint8_t cleanSession); + +// Set Last Will Topic on client, must be called before MQTT_InitConnection +void MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, + uint8_t will_qos, uint8_t will_retain); + +// Kick of a persistent connection to the broker, will reconnect anytime conn breaks +void MQTT_Connect(MQTT_Client* mqttClient); + +// Kill persistent connection +void MQTT_Disconnect(MQTT_Client* mqttClient); -#define MQTT_FLAG_CONNECTED 1 -#define MQTT_FLAG_READY 2 -#define MQTT_FLAG_EXIT 4 +// Subscribe to a topic +bool MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos); -#define MQTT_EVENT_TYPE_NONE 0 -#define MQTT_EVENT_TYPE_CONNECTED 1 -#define MQTT_EVENT_TYPE_DISCONNECTED 2 -#define MQTT_EVENT_TYPE_SUBSCRIBED 3 -#define MQTT_EVENT_TYPE_UNSUBSCRIBED 4 -#define MQTT_EVENT_TYPE_PUBLISH 5 -#define MQTT_EVENT_TYPE_PUBLISHED 6 -#define MQTT_EVENT_TYPE_EXITED 7 -#define MQTT_EVENT_TYPE_PUBLISH_CONTINUATION 8 +// Publish a message +bool MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, + uint8_t qos, uint8_t retain); -void MQTT_InitConnection(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security); -void MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession); -void MQTT_InitLWT(MQTT_Client* mqttClient, char* will_topic, char* will_msg, uint8_t will_qos, uint8_t will_retain); +// Callback when connected void MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb); +// Callback when disconnected void MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb); +// Callback when publish succeeded void MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb); +// Callback when data arrives for subscription void MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb); -bool MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos); -void MQTT_Connect(MQTT_Client* mqttClient); -void MQTT_Disconnect(MQTT_Client* mqttClient); -bool MQTT_Publish(MQTT_Client* client, const char* topic, const char* data, uint8_t qos, uint8_t retain); #endif /* USER_AT_MQTT_H_ */ diff --git a/mqtt/mqtt_cmd.c b/mqtt/mqtt_cmd.c index 358ad48..d2a01e2 100644 --- a/mqtt/mqtt_cmd.c +++ b/mqtt/mqtt_cmd.c @@ -1,8 +1,10 @@ +#include +#include "mqtt.h" #include "mqtt_cmd.h" uint32_t connectedCb = 0, disconnectCb = 0, tcpDisconnectedCb = 0, publishedCb = 0, dataCb = 0; -void ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR cmdMqttConnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; @@ -24,7 +26,7 @@ cmdMqttTcpDisconnectedCb(uint32_t *args) { CMD_ResponseEnd(crc); } -void ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR cmdMqttDisconnectedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; @@ -33,7 +35,7 @@ cmdMqttDisconnectedCb(uint32_t* args) { CMD_ResponseEnd(crc); } -void ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR cmdMqttPublishedCb(uint32_t* args) { MQTT_Client* client = (MQTT_Client*)args; MqttCmdCb* cb = (MqttCmdCb*)client->user_data; @@ -42,7 +44,7 @@ cmdMqttPublishedCb(uint32_t* args) { CMD_ResponseEnd(crc); } -void ICACHE_FLASH_ATTR +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; @@ -54,7 +56,7 @@ cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char* CMD_ResponseEnd(crc); } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Setup(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); @@ -65,10 +67,12 @@ MQTTCMD_Setup(CmdPacket *cmd) { // create mqtt client uint8_t clientLen = sizeof(MQTT_Client); MQTT_Client* client = (MQTT_Client*)os_zalloc(clientLen); - if (client == NULL) - return 0; + if (client == NULL) return 0; os_memset(client, 0, clientLen); + return 0; +#if 0 + uint16_t len; uint8_t *client_id, *user_data, *pass_data; uint32_t keepalive, clean_session, cb_data; @@ -102,7 +106,7 @@ MQTTCMD_Setup(CmdPacket *cmd) { os_printf("MQTT: MQTTCMD_Setup clientid=%s, user=%s, pw=%s, keepalive=%ld, clean_session=%ld\n", client_id, user_data, pass_data, keepalive, clean_session); - // init client + // init client // TODO: why malloc these all here, pass to MQTT_InitClient to be malloc'd again? MQTT_InitClient(client, (char*)client_id, (char*)user_data, (char*)pass_data, keepalive, clean_session); @@ -112,11 +116,11 @@ MQTTCMD_Setup(CmdPacket *cmd) { CMD_PopArg(&req, (uint8_t*)&cb_data, 4); callback->connectedCb = cb_data; CMD_PopArg(&req, (uint8_t*)&cb_data, 4); - callback->disconnectedCb = cb_data; + callback->disconnectedCb = cb_data; CMD_PopArg(&req, (uint8_t*)&cb_data, 4); callback->publishedCb = cb_data; CMD_PopArg(&req, (uint8_t*)&cb_data, 4); - callback->dataCb = cb_data; + callback->dataCb = cb_data; client->user_data = callback; @@ -136,9 +140,10 @@ MQTTCMD_Setup(CmdPacket *cmd) { os_free(pass_data); return (uint32_t)client; +#endif } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Lwt(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); @@ -153,7 +158,7 @@ MQTTCMD_Lwt(CmdPacket *cmd) { os_printf("MQTT: MQTTCMD_Lwt client ptr=%p\n", (void*)client_ptr); uint16_t len; - + // get topic if (client->connect_info.will_topic) os_free(client->connect_info.will_topic); @@ -174,7 +179,7 @@ MQTTCMD_Lwt(CmdPacket *cmd) { // get qos CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_qos, 4); - + // get retain CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); @@ -186,13 +191,13 @@ MQTTCMD_Lwt(CmdPacket *cmd) { return 1; } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Connect(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); if (CMD_GetArgc(&req) != 4) - return 0; + return 0; // get mqtt client uint32_t client_ptr; @@ -202,7 +207,7 @@ MQTTCMD_Connect(CmdPacket *cmd) { uint16_t len; - // get host + // get host if (client->host) os_free(client->host); len = CMD_ArgLen(&req); @@ -217,7 +222,7 @@ MQTTCMD_Connect(CmdPacket *cmd) { // get security CMD_PopArg(&req, (uint8_t*)&client->security, 4); - os_printf("MQTT: MQTTCMD_Connect host=%s, port=%ld, security=%d\n", + os_printf("MQTT: MQTTCMD_Connect host=%s, port=%d, security=%d\n", client->host, client->port, client->security); @@ -226,7 +231,7 @@ MQTTCMD_Connect(CmdPacket *cmd) { return 1; } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Disconnect(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); @@ -245,7 +250,7 @@ MQTTCMD_Disconnect(CmdPacket *cmd) { return 1; } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Publish(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); @@ -279,7 +284,7 @@ MQTTCMD_Publish(CmdPacket *cmd) { // TODO: next line not originally present data[len] = 0; - // get data length + // get data length // TODO: this isn't used but we have to pull it off the stack CMD_PopArg(&req, (uint8_t*)&data_len, 4); @@ -301,7 +306,7 @@ MQTTCMD_Publish(CmdPacket *cmd) { return 1; } -uint32_t ICACHE_FLASH_ATTR +uint32_t ICACHE_FLASH_ATTR MQTTCMD_Subscribe(CmdPacket *cmd) { CmdRequest req; CMD_Request(&req, cmd); diff --git a/mqtt/mqtt_cmd.h b/mqtt/mqtt_cmd.h index 9ccbd08..72ffe98 100644 --- a/mqtt/mqtt_cmd.h +++ b/mqtt/mqtt_cmd.h @@ -2,7 +2,6 @@ #define MODULES_MQTT_CMD_H_ #include "cmd.h" -#include "mqtt.h" typedef struct { uint32_t connectedCb; diff --git a/mqtt/mqtt_msg.c b/mqtt/mqtt_msg.c index 03da28e..867dadd 100644 --- a/mqtt/mqtt_msg.c +++ b/mqtt/mqtt_msg.c @@ -29,6 +29,7 @@ * */ +#include #include "mqtt_msg.h" #define MQTT_MAX_FIXED_HEADER_SIZE 3 @@ -57,7 +58,7 @@ struct uint8_t keepaliveLsb; }; -static int ICACHE_FLASH_ATTR +static int ICACHE_FLASH_ATTR append_string(mqtt_connection_t* connection, const char* string, int len) { if (connection->message.length + len + 2 > connection->buffer_length) return -1; @@ -70,7 +71,7 @@ append_string(mqtt_connection_t* connection, const char* string, int len) { return len + 2; } -static uint16_t ICACHE_FLASH_ATTR +static uint16_t ICACHE_FLASH_ATTR append_message_id(mqtt_connection_t* connection, uint16_t message_id) { // If message_id is zero then we should assign one, otherwise // we'll use the one supplied by the caller @@ -86,20 +87,20 @@ append_message_id(mqtt_connection_t* connection, uint16_t message_id) { return message_id; } -static int ICACHE_FLASH_ATTR +static int ICACHE_FLASH_ATTR init_message(mqtt_connection_t* connection) { connection->message.length = MQTT_MAX_FIXED_HEADER_SIZE; return MQTT_MAX_FIXED_HEADER_SIZE; } -static mqtt_message_t* ICACHE_FLASH_ATTR +static mqtt_message_t* ICACHE_FLASH_ATTR fail_message(mqtt_connection_t* connection) { connection->message.data = connection->buffer; connection->message.length = 0; return &connection->message; } -static mqtt_message_t* ICACHE_FLASH_ATTR +static mqtt_message_t* ICACHE_FLASH_ATTR fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int retain) { int remaining_length = connection->message.length - MQTT_MAX_FIXED_HEADER_SIZE; @@ -120,7 +121,7 @@ fini_message(mqtt_connection_t* connection, int type, int dup, int qos, int reta return &connection->message; } -void ICACHE_FLASH_ATTR +void ICACHE_FLASH_ATTR mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length) { uint8_t len = sizeof(connection); memset(connection, '\0', len); @@ -128,8 +129,8 @@ mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_le connection->buffer_length = buffer_length; } -int ICACHE_FLASH_ATTR -mqtt_get_total_length(uint8_t* buffer, uint16_t length) { +int ICACHE_FLASH_ATTR +mqtt_get_total_length(const uint8_t* buffer, uint16_t length) { int i; int totlen = 0; @@ -145,8 +146,8 @@ mqtt_get_total_length(uint8_t* buffer, uint16_t length) { return totlen; } -const char* ICACHE_FLASH_ATTR -mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) { +const char* ICACHE_FLASH_ATTR +mqtt_get_publish_topic(const uint8_t* buffer, uint16_t* length) { int i; int totlen = 0; int topiclen; @@ -172,8 +173,8 @@ mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) { return (const char*)(buffer + i); } -const char* ICACHE_FLASH_ATTR -mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) { +const char* ICACHE_FLASH_ATTR +mqtt_get_publish_data(const uint8_t* buffer, uint16_t* length) { int i; int totlen = 0; int topiclen; @@ -214,8 +215,8 @@ mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) { return (const char*)(buffer + i); } -uint16_t ICACHE_FLASH_ATTR -mqtt_get_id(uint8_t* buffer, uint16_t length) { +uint16_t ICACHE_FLASH_ATTR +mqtt_get_id(const uint8_t* buffer, uint16_t length) { if (length < 1) return 0; @@ -271,7 +272,7 @@ mqtt_get_id(uint8_t* buffer, uint16_t length) { } } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) { struct mqtt_connect_variable_header* variable_header; @@ -339,7 +340,7 @@ mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info) { return fini_message(connection, MQTT_MSG_TYPE_CONNECT, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id) { init_message(connection); @@ -364,7 +365,7 @@ mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* d return fini_message(connection, MQTT_MSG_TYPE_PUBLISH, 0, qos, retain); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) { init_message(connection); if (append_message_id(connection, message_id) == 0) @@ -372,7 +373,7 @@ mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id) { return fini_message(connection, MQTT_MSG_TYPE_PUBACK, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) { init_message(connection); if (append_message_id(connection, message_id) == 0) @@ -380,7 +381,7 @@ mqtt_msg_pubrec(mqtt_connection_t* connection, uint16_t message_id) { return fini_message(connection, MQTT_MSG_TYPE_PUBREC, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) { init_message(connection); if (append_message_id(connection, message_id) == 0) @@ -388,7 +389,7 @@ mqtt_msg_pubrel(mqtt_connection_t* connection, uint16_t message_id) { return fini_message(connection, MQTT_MSG_TYPE_PUBREL, 0, 1, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) { init_message(connection); if (append_message_id(connection, message_id) == 0) @@ -396,7 +397,7 @@ mqtt_msg_pubcomp(mqtt_connection_t* connection, uint16_t message_id) { return fini_message(connection, MQTT_MSG_TYPE_PUBCOMP, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, uint16_t* message_id) { init_message(connection); @@ -416,7 +417,7 @@ mqtt_msg_subscribe(mqtt_connection_t* connection, const char* topic, int qos, ui return fini_message(connection, MQTT_MSG_TYPE_SUBSCRIBE, 0, 1, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* message_id) { init_message(connection); @@ -432,19 +433,19 @@ mqtt_msg_unsubscribe(mqtt_connection_t* connection, const char* topic, uint16_t* return fini_message(connection, MQTT_MSG_TYPE_UNSUBSCRIBE, 0, 1, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingreq(mqtt_connection_t* connection) { init_message(connection); return fini_message(connection, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_pingresp(mqtt_connection_t* connection) { init_message(connection); return fini_message(connection, MQTT_MSG_TYPE_PINGRESP, 0, 0, 0); } -mqtt_message_t* ICACHE_FLASH_ATTR +mqtt_message_t* ICACHE_FLASH_ATTR mqtt_msg_disconnect(mqtt_connection_t* connection) { init_message(connection); return fini_message(connection, MQTT_MSG_TYPE_DISCONNECT, 0, 0, 0); diff --git a/mqtt/mqtt_msg.h b/mqtt/mqtt_msg.h index bf305df..0a01bba 100644 --- a/mqtt/mqtt_msg.h +++ b/mqtt/mqtt_msg.h @@ -30,8 +30,9 @@ */ #ifndef MQTT_MSG_H -#define MQTT_MSG_H -#include +#define MQTT_MSG_H + +#define PROTOCOL_NAMEv311 enum mqtt_message_type { MQTT_MSG_TYPE_CONNECT = 1, @@ -50,21 +51,22 @@ enum mqtt_message_type { MQTT_MSG_TYPE_DISCONNECT = 14 }; +// Descriptor for a serialized MQTT message, this is returned by functions that compose a message +// (It's really an MQTT packet in v3.1.1 terminology) typedef struct mqtt_message { uint8_t* data; uint16_t length; - } mqtt_message_t; +// Descriptor for a connection with message assembly storage typedef struct mqtt_connection { - mqtt_message_t message; - - uint16_t message_id; - uint8_t* buffer; - uint16_t buffer_length; - + mqtt_message_t message; // resulting message + uint16_t message_id; // id of assembled message and memo to calculate next message id + uint8_t* buffer; // buffer for assembling messages + uint16_t buffer_length; // buffer length } mqtt_connection_t; +// Descriptor for a connect request typedef struct mqtt_connect_info { char* client_id; char* username; @@ -75,32 +77,40 @@ typedef struct mqtt_connect_info { uint8_t will_qos; uint8_t will_retain; uint8_t clean_session; - } mqtt_connect_info_t; - -static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) { +static inline int ICACHE_FLASH_ATTR mqtt_get_type(const uint8_t* buffer) { return (buffer[0] & 0xf0) >> 4; } -static inline int ICACHE_FLASH_ATTR mqtt_get_dup(uint8_t* buffer) { +static inline int ICACHE_FLASH_ATTR mqtt_get_dup(const uint8_t* buffer) { return (buffer[0] & 0x08) >> 3; } -static inline int ICACHE_FLASH_ATTR mqtt_get_qos(uint8_t* buffer) { +static inline int ICACHE_FLASH_ATTR mqtt_get_qos(const uint8_t* buffer) { return (buffer[0] & 0x06) >> 1; } -static inline int ICACHE_FLASH_ATTR mqtt_get_retain(uint8_t* buffer) { +static inline int ICACHE_FLASH_ATTR mqtt_get_retain(const uint8_t* buffer) { return (buffer[0] & 0x01); } +// Init a connection descriptor void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); -int mqtt_get_total_length(uint8_t* buffer, uint16_t length); -const char* mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length); -const char* mqtt_get_publish_data(uint8_t* buffer, uint16_t* length); -uint16_t mqtt_get_id(uint8_t* buffer, uint16_t length); +// Returns the total length of a message including MQTT fixed header +int mqtt_get_total_length(const uint8_t* buffer, uint16_t length); + +// Return pointer to topic, length in in/out param: in=length of buffer, out=length of topic +const char* mqtt_get_publish_topic(const uint8_t* buffer, uint16_t* length); + +// Return pointer to data, length in in/out param: in=length of buffer, out=length of data +const char* mqtt_get_publish_data(const uint8_t* buffer, uint16_t* length); + +// Return message id +uint16_t mqtt_get_id(const uint8_t* buffer, uint16_t length); + +// The following functions construct an outgoing message mqtt_message_t* mqtt_msg_connect(mqtt_connection_t* connection, mqtt_connect_info_t* info); mqtt_message_t* mqtt_msg_publish(mqtt_connection_t* connection, const char* topic, const char* data, int data_length, int qos, int retain, uint16_t* message_id); mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id); diff --git a/mqtt/pktbuf.c b/mqtt/pktbuf.c new file mode 100644 index 0000000..2984fb4 --- /dev/null +++ b/mqtt/pktbuf.c @@ -0,0 +1,64 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt + +#include +#include "pktbuf.h" + +void ICACHE_FLASH_ATTR +PktBuf_Print(PktBuf *buf) { + os_printf("PktBuf:"); + for (int i=-16; i<0; i++) + os_printf(" %02X", ((uint8_t*)buf)[i]); + os_printf(" %p", buf); + for (int i=0; i<16; i++) + os_printf(" %02X", ((uint8_t*)buf)[i]); + os_printf("\n"); + os_printf("PktBuf: next=%p len=0x%04x\n", + ((void**)buf)[-4], ((uint16_t*)buf)[-6]); +} + + +PktBuf * ICACHE_FLASH_ATTR +PktBuf_New(uint16_t length) { + PktBuf *buf = os_zalloc(length+sizeof(PktBuf)); + buf->next = NULL; + buf->filled = 0; + //os_printf("PktBuf_New: %p l=%d->%d d=%p\n", + // buf, length, length+sizeof(PktBuf), buf->data); + return buf; +} + +PktBuf * ICACHE_FLASH_ATTR +PktBuf_Push(PktBuf *headBuf, PktBuf *buf) { + if (headBuf == NULL) { + //os_printf("PktBuf_Push: %p\n", buf); + return buf; + } + PktBuf *h = headBuf; + while (h->next != NULL) h = h->next; + h->next = buf; + //os_printf("PktBuf_Push: %p->..->%p\n", headBuf, buf); + return headBuf; +} + +PktBuf * ICACHE_FLASH_ATTR +PktBuf_Unshift(PktBuf *headBuf, PktBuf *buf) { + buf->next = headBuf; + //os_printf("PktBuf_Unshift: %p->%p\n", buf, buf->next); + return buf; +} + +PktBuf * ICACHE_FLASH_ATTR +PktBuf_Shift(PktBuf *headBuf) { + PktBuf *buf = headBuf->next; + headBuf->next = NULL; + //os_printf("PktBuf_Shift: (%p)->%p\n", headBuf, buf); + return buf; +} + +PktBuf * ICACHE_FLASH_ATTR +PktBuf_ShiftFree(PktBuf *headBuf) { + PktBuf *buf = headBuf->next; + //os_printf("PktBuf_ShiftFree: (%p)->%p\n", headBuf, buf); + os_free(headBuf); + return buf; +} diff --git a/mqtt/pktbuf.h b/mqtt/pktbuf.h new file mode 100644 index 0000000..0c8ff2c --- /dev/null +++ b/mqtt/pktbuf.h @@ -0,0 +1,29 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt + +#ifndef PKTBUF_H +#define PKTBUF_H + +typedef struct PktBuf { + struct PktBuf *next; // next buffer in chain + uint16_t filled; // number of bytes filled in buffer + uint8_t data[0]; // data in buffer +} PktBuf; + +// Allocate a new packet buffer of given length +PktBuf *PktBuf_New(uint16_t length); + +// Append a buffer to the end of a packet buffer queue, returns new head +PktBuf *PktBuf_Push(PktBuf *headBuf, PktBuf *buf); + +// Prepend a buffer to the beginning of a packet buffer queue, return new head +PktBuf * PktBuf_Unshift(PktBuf *headBuf, PktBuf *buf); + +// Shift first buffer off queue, returns new head (not shifted buffer!) +PktBuf *PktBuf_Shift(PktBuf *headBuf); + +// Shift first buffer off queue, free it, return new head +PktBuf *PktBuf_ShiftFree(PktBuf *headBuf); + +void PktBuf_Print(PktBuf *buf); + +#endif diff --git a/mqtt/proto.c b/mqtt/proto.c deleted file mode 100644 index 69b5367..0000000 --- a/mqtt/proto.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "proto.h" - -int8_t ICACHE_FLASH_ATTR -PROTO_Init(PROTO_PARSER* parser, PROTO_PARSE_CALLBACK* completeCallback, uint8_t* buf, uint16_t bufSize) { - parser->buf = buf; - parser->bufSize = bufSize; - parser->dataLen = 0; - parser->callback = completeCallback; - parser->isEsc = 0; - return 0; -} - -int8_t ICACHE_FLASH_ATTR -PROTO_ParseByte(PROTO_PARSER* parser, uint8_t value) { - switch (value) { - case 0x7D: - parser->isEsc = 1; - break; - - case 0x7E: - parser->dataLen = 0; - parser->isEsc = 0; - parser->isBegin = 1; - break; - - case 0x7F: - if (parser->callback != NULL) - parser->callback(); - parser->isBegin = 0; - return 0; - break; - - default: - if (parser->isBegin == 0) break; - - if (parser->isEsc) { - value ^= 0x20; - parser->isEsc = 0; - } - - if (parser->dataLen < parser->bufSize) - parser->buf[parser->dataLen++] = value; - - break; - } - return -1; -} - -int16_t ICACHE_FLASH_ATTR -PROTO_ParseRb(RINGBUF* rb, uint8_t* bufOut, uint16_t* len, uint16_t maxBufLen) { - uint8_t c; - - PROTO_PARSER proto; - PROTO_Init(&proto, NULL, bufOut, maxBufLen); - while (RINGBUF_Get(rb, &c) == 0) { - if (PROTO_ParseByte(&proto, c) == 0) { - *len = proto.dataLen; - return 0; - } - } - return -1; -} - -int16_t ICACHE_FLASH_ATTR -PROTO_AddRb(RINGBUF* rb, const uint8_t* packet, int16_t len) { - uint16_t i = 2; - if (RINGBUF_Put(rb, 0x7E) == -1) return -1; - while (len--) { - switch (*packet) { - case 0x7D: - case 0x7E: - case 0x7F: - if (RINGBUF_Put(rb, 0x7D) == -1) return -1; - if (RINGBUF_Put(rb, *packet++ ^ 0x20) == -1) return -1; - i += 2; - break; - default: - if (RINGBUF_Put(rb, *packet++) == -1) return -1; - i++; - break; - } - } - if (RINGBUF_Put(rb, 0x7F) == -1) return -1; - - return i; -} diff --git a/mqtt/proto.h b/mqtt/proto.h deleted file mode 100644 index 67b3e26..0000000 --- a/mqtt/proto.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _PROTO_H_ -#define _PROTO_H_ -#include -#include "ringbuf.h" - -typedef void (PROTO_PARSE_CALLBACK)(); - -typedef struct { - uint8_t* buf; - uint16_t bufSize; - uint16_t dataLen; - uint8_t isEsc; - uint8_t isBegin; - PROTO_PARSE_CALLBACK* callback; -} PROTO_PARSER; - -int8_t PROTO_Init(PROTO_PARSER* parser, PROTO_PARSE_CALLBACK* completeCallback, uint8_t* buf, uint16_t bufSize); -int16_t PROTO_AddRb(RINGBUF* rb, const uint8_t* packet, int16_t len); -int8_t PROTO_ParseByte(PROTO_PARSER* parser, uint8_t value); -int16_t PROTO_ParseRb(RINGBUF* rb, uint8_t* bufOut, uint16_t* len, uint16_t maxBufLen); -#endif diff --git a/mqtt/queue.c b/mqtt/queue.c deleted file mode 100644 index 39c4790..0000000 --- a/mqtt/queue.c +++ /dev/null @@ -1,53 +0,0 @@ -/* str_queue.c -* -* Copyright (c) 2014-2015, Tuan PM -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* -* * Redistributions of source code must retain the above copyright notice, -* this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Redis nor the names of its contributors may be used -* to endorse or promote products derived from this software without -* specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -*/ -#include "queue.h" - -void ICACHE_FLASH_ATTR -QUEUE_Init(QUEUE* queue, int bufferSize) { - queue->buf = (uint8_t*)os_zalloc(bufferSize); - RINGBUF_Init(&queue->rb, queue->buf, bufferSize); -} - -int32_t ICACHE_FLASH_ATTR -QUEUE_Puts(QUEUE* queue, uint8_t* buffer, uint16_t len) { - return PROTO_AddRb(&queue->rb, buffer, len); -} - -int32_t ICACHE_FLASH_ATTR -QUEUE_Gets(QUEUE* queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen) { - return PROTO_ParseRb(&queue->rb, buffer, len, maxLen); -} - -bool ICACHE_FLASH_ATTR -QUEUE_IsEmpty(QUEUE* queue) { - if (queue->rb.fill_cnt <= 0) - return TRUE; - return FALSE; -} diff --git a/mqtt/queue.h b/mqtt/queue.h deleted file mode 100644 index bdbc503..0000000 --- a/mqtt/queue.h +++ /dev/null @@ -1,46 +0,0 @@ -/* str_queue.h -- -* -* Copyright (c) 2014-2015, Tuan PM -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* -* * Redistributions of source code must retain the above copyright notice, -* this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of Redis nor the names of its contributors may be used -* to endorse or promote products derived from this software without -* specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef USER_QUEUE_H_ -#define USER_QUEUE_H_ -#include -#include "proto.h" -#include "ringbuf.h" - -typedef struct { - uint8_t* buf; - RINGBUF rb; -} QUEUE; - -void QUEUE_Init(QUEUE* queue, int bufferSize); -int32_t QUEUE_Puts(QUEUE* queue, uint8_t* buffer, uint16_t len); -int32_t QUEUE_Gets(QUEUE* queue, uint8_t* buffer, uint16_t* len, uint16_t maxLen); -bool QUEUE_IsEmpty(QUEUE* queue); -#endif /* USER_QUEUE_H_ */ diff --git a/mqtt/ringbuf.c b/mqtt/ringbuf.c deleted file mode 100644 index a0cc782..0000000 --- a/mqtt/ringbuf.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "ringbuf.h" - -/** -* \brief init a RINGBUF object -* \param r pointer to a RINGBUF object -* \param buf pointer to a byte array -* \param size size of buf -* \return 0 if successfull, otherwise failed -*/ -int16_t ICACHE_FLASH_ATTR -RINGBUF_Init(RINGBUF* r, uint8_t* buf, int32_t size) { - if (r == NULL || buf == NULL || size < 2) return -1; - - r->p_o = r->p_r = r->p_w = buf; - r->fill_cnt = 0; - r->size = size; - - return 0; -} - -/** -* \brief put a character into ring buffer -* \param r pointer to a ringbuf object -* \param c character to be put -* \return 0 if successfull, otherwise failed -*/ -int16_t ICACHE_FLASH_ATTR -RINGBUF_Put(RINGBUF* r, uint8_t c) { - if (r->fill_cnt >= r->size)return -1; // ring buffer is full, this should be atomic operation - - - r->fill_cnt++; // increase filled slots count, this should be atomic operation - - - *r->p_w++ = c; // put character into buffer - - if (r->p_w >= r->p_o + r->size) // rollback if write pointer go pass - r->p_w = r->p_o; // the physical boundary - - return 0; -} - -/** -* \brief get a character from ring buffer -* \param r pointer to a ringbuf object -* \param c read character -* \return 0 if successfull, otherwise failed -*/ -int16_t ICACHE_FLASH_ATTR -RINGBUF_Get(RINGBUF* r, uint8_t* c) { - if (r->fill_cnt <= 0)return -1; // ring buffer is empty, this should be atomic operation - - - r->fill_cnt--; // decrease filled slots count - - - *c = *r->p_r++; // get the character out - - if (r->p_r >= r->p_o + r->size) // rollback if write pointer go pass - r->p_r = r->p_o; // the physical boundary - - return 0; -} diff --git a/mqtt/ringbuf.h b/mqtt/ringbuf.h deleted file mode 100644 index d504d53..0000000 --- a/mqtt/ringbuf.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _RING_BUF_H_ -#define _RING_BUF_H_ - -#include - -typedef struct { - uint8_t* p_o; /**< Original pointer */ - uint8_t* volatile p_r; /**< Read pointer */ - uint8_t* volatile p_w; /**< Write pointer */ - volatile int32_t fill_cnt; /**< Number of filled slots */ - int32_t size; /**< Buffer size */ -} RINGBUF; - -int16_t RINGBUF_Init(RINGBUF* r, uint8_t* buf, int32_t size); -int16_t RINGBUF_Put(RINGBUF* r, uint8_t c); -int16_t RINGBUF_Get(RINGBUF* r, uint8_t* c); -#endif diff --git a/rest/rest.c b/rest/rest.c index a9cd298..40b62de 100644 --- a/rest/rest.c +++ b/rest/rest.c @@ -154,12 +154,12 @@ rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) { if(client->ip.addr == 0 && ipaddr->addr != 0) { os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); - //if(client->security){ - // espconn_secure_connect(client->pCon); - //} - //else { +#ifdef CLIENT_SSL_ENABLE + if(client->security) { + espconn_secure_connect(client->pCon); + } else +#endif espconn_connect(client->pCon); - //} os_printf("REST: connecting...\n"); } } @@ -368,6 +368,8 @@ REST_Request(CmdPacket *cmd) { } os_printf("\n"); + //os_printf("REST request: %s", (char*)client->data); + os_printf("REST: pCon state=%d\n", client->pCon->state); client->pCon->state = ESPCONN_NONE; espconn_regist_connectcb(client->pCon, tcpclient_connect_cb); diff --git a/serial/serbridge.c b/serial/serbridge.c index 415772b..b91dbbc 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -14,6 +14,7 @@ #include "serled.h" #include "config.h" #include "console.h" +#include "slip.h" #include "cmd.h" static struct espconn serbridgeConn; @@ -258,6 +259,19 @@ console_process(char *buf, short len) { } } +// callback with a buffer of characters that have arrived on the uart +void ICACHE_FLASH_ATTR +serbridgeUartCb(char *buf, short length) { + if (!flashConfig.slip_enable || slip_disabled > 0) { + //os_printf("SLIP: disabled got %d\n", length); + console_process(buf, length); + } else { + slip_parse_buf(buf, length); + } + + serledFlash(50); // short blink on serial LED +} + //callback after the data are sent static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) { @@ -273,6 +287,7 @@ serbridgeSentCb(void *arg) { // Error callback (it's really poorly named, it's not a "connection reconnected" callback, // it's really a "connection broken, please reconnect" callback) +// Note that there is no DisconCb after a ReconCb static void ICACHE_FLASH_ATTR serbridgeReconCb(void *arg, sint8 err) { serbridgeConnData *sbConn = serbridgeFindConnData(arg); if (sbConn == NULL) return; diff --git a/serial/serled.h b/serial/serled.h index 6b6637c..4eb728b 100644 --- a/serial/serled.h +++ b/serial/serled.h @@ -6,4 +6,3 @@ void serledInit(void); void makeGpio(uint8_t pin); #endif - diff --git a/serial/slip.c b/serial/slip.c index 0e067a1..76948d5 100644 --- a/serial/slip.c +++ b/serial/slip.c @@ -4,11 +4,10 @@ #include "uart.h" #include "crc16.h" #include "serbridge.h" -#include "serled.h" #include "console.h" #include "cmd.h" -uint8_t slip_disabled; // disable slip to allow flashing of attached MCU +uint8_t slip_disabled; // temporarily disable slip to allow flashing of attached MCU extern void ICACHE_FLASH_ATTR console_process(char *buf, short len); @@ -115,18 +114,7 @@ slip_parse_char(char c) { // callback with a buffer of characters that have arrived on the uart void ICACHE_FLASH_ATTR -serbridgeUartCb(char *buf, short length) { - if (slip_disabled > 0) { - //os_printf("SLIP: disabled got %d\n", length); - console_process(buf, length); - for (short i=0; i -//#include -//#include +#include "cgiwifi.h" +#include "mqtt.h" + +MQTT_Client mqttClient; + +static ETSTimer mqttTimer; + +static int once = 0; +static void ICACHE_FLASH_ATTR mqttTimerCb(void *arg) { + if (once++ > 0) return; + MQTT_Init(&mqttClient, "h.voneicken.com", 1883, 0, 2, "test1", "", "", 10, 1); + MQTT_Connect(&mqttClient); + MQTT_Subscribe(&mqttClient, "system/time", 0); +} + +void ICACHE_FLASH_ATTR +wifiStateChangeCb(uint8_t status) +{ + if (status == wifiGotIP) { + os_timer_disarm(&mqttTimer); + os_timer_setfn(&mqttTimer, mqttTimerCb, NULL); + os_timer_arm(&mqttTimer, 15000, 0); + } +} + + +// initialize the custom stuff that goes beyond esp-link +void app_init() { + wifiAddStateChangeCb(wifiStateChangeCb); +} + + +#if 0 +MQTT_Client mqttClient; + +void ICACHE_FLASH_ATTR +mqttConnectedCb(uint32_t *args) { + MQTT_Client* client = (MQTT_Client*)args; + MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); +} + +void ICACHE_FLASH_ATTR +mqttDisconnectedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT Disconnected\n"); +} + +void ICACHE_FLASH_ATTR +mqttTcpDisconnectedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT TCP Disconnected\n"); +} + +void ICACHE_FLASH_ATTR +mqttPublishedCb(uint32_t *args) { +// MQTT_Client* client = (MQTT_Client*)args; + os_printf("MQTT Published\n"); +} + +void ICACHE_FLASH_ATTR +mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { + char *topicBuf = (char*)os_zalloc(topic_len + 1); + char *dataBuf = (char*)os_zalloc(data_len + 1); -//MQTT_Client mqttClient; -// -//void ICACHE_FLASH_ATTR -//mqttConnectedCb(uint32_t *args) { // MQTT_Client* client = (MQTT_Client*)args; -// MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); -//} -// -//void ICACHE_FLASH_ATTR -//mqttDisconnectedCb(uint32_t *args) { -//// MQTT_Client* client = (MQTT_Client*)args; -// os_printf("MQTT Disconnected\n"); -//} -// -//void ICACHE_FLASH_ATTR -//mqttTcpDisconnectedCb(uint32_t *args) { -//// MQTT_Client* client = (MQTT_Client*)args; -// os_printf("MQTT TCP Disconnected\n"); -//} -// -//void ICACHE_FLASH_ATTR -//mqttPublishedCb(uint32_t *args) { -//// MQTT_Client* client = (MQTT_Client*)args; -// os_printf("MQTT Published\n"); -//} -// -//void ICACHE_FLASH_ATTR -//mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len) { -// char *topicBuf = (char*)os_zalloc(topic_len + 1); -// char *dataBuf = (char*)os_zalloc(data_len + 1); -// -//// MQTT_Client* client = (MQTT_Client*)args; -// -// os_memcpy(topicBuf, topic, topic_len); -// topicBuf[topic_len] = 0; -// -// os_memcpy(dataBuf, data, data_len); -// dataBuf[data_len] = 0; -// -// os_printf("Receive topic: %s, data: %s\n", topicBuf, dataBuf); -// os_free(topicBuf); -// os_free(dataBuf); -//} -// -//void ICACHE_FLASH_ATTR -//wifiStateChangeCb(uint8_t status) -//{ -// if (status == wifiGotIP && mqttClient.connState != TCP_CONNECTING){ -// MQTT_Connect(&mqttClient); -// } -// else if (status == wifiIsDisconnected && mqttClient.connState == TCP_CONNECTING){ -// MQTT_Disconnect(&mqttClient); -// } -//} - -void init() { -// wifiAddStateChangeCb(wifiStateChangeCb); -// MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); -// MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); -// MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); -// MQTT_OnConnected(&mqttClient, mqttConnectedCb); -// MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); -// MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); -// MQTT_OnPublished(&mqttClient, mqttPublishedCb); -// MQTT_OnData(&mqttClient, mqttDataCb); -} \ No newline at end of file + + os_memcpy(topicBuf, topic, topic_len); + topicBuf[topic_len] = 0; + + os_memcpy(dataBuf, data, data_len); + dataBuf[data_len] = 0; + + os_printf("Receive topic: %s, data: %s\n", topicBuf, dataBuf); + os_free(topicBuf); + os_free(dataBuf); +} + + MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); + MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); + MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); + MQTT_OnConnected(&mqttClient, mqttConnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb); + MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); + MQTT_OnPublished(&mqttClient, mqttPublishedCb); + MQTT_OnData(&mqttClient, mqttDataCb); +#endif