Merge branch 'master' into windows

Conflicts:
	Makefile
	cmd/handlers.c
	esp-link/cgi.c
	esp-link/cgiwifi.c
	html/console.html
	html/style.css
	html/ui.js
	httpd/httpd.c
	mqtt/mqtt.c
	mqtt/mqtt.h
	mqtt/mqtt_cmd.c
	user/user_main.c
pull/47/head
Benjamin Runnels 9 years ago
commit 965b70a408
  1. 16
      Makefile
  2. 39
      README.md
  3. 61
      cmd/handlers.c
  4. 61
      esp-link/cgi.c
  5. 3
      esp-link/cgi.h
  6. 111
      esp-link/cgimqtt.c
  7. 8
      esp-link/cgimqtt.h
  8. 46
      esp-link/cgipins.c
  9. 870
      esp-link/cgiwifi.c
  10. 21
      esp-link/config.c
  11. 5
      esp-link/config.h
  12. 19
      esp-link/main.c
  13. 176
      esp-link/status.c
  14. 47
      html/console.html
  15. 99
      html/mqtt.html
  16. 384
      html/style.css
  17. 473
      html/ui.js
  18. 68
      httpd/httpd.c
  19. 929
      mqtt/mqtt.c
  20. 171
      mqtt/mqtt.h
  21. 80
      mqtt/mqtt_cmd.c
  22. 1
      mqtt/mqtt_cmd.h
  23. 9
      mqtt/mqtt_msg.c
  24. 48
      mqtt/mqtt_msg.h
  25. 64
      mqtt/pktbuf.c
  26. 29
      mqtt/pktbuf.h
  27. 86
      mqtt/proto.c
  28. 21
      mqtt/proto.h
  29. 53
      mqtt/queue.c
  30. 46
      mqtt/queue.h
  31. 63
      mqtt/ringbuf.c
  32. 17
      mqtt/ringbuf.h
  33. 12
      rest/rest.c
  34. 15
      serial/serbridge.c
  35. 1
      serial/serled.h
  36. 19
      serial/slip.c
  37. 6
      serial/slip.h
  38. 2
      serial/uart.c
  39. 163
      user/user_main.c

@ -157,9 +157,9 @@ ifneq (,$(findstring rest,$(MODULES)))
endif endif
# which modules (subdirectories) of the project to include in compiling # which modules (subdirectories) of the project to include in compiling
LIBRARIES_DIR = libraries LIBRARIES_DIR = libraries
MODULES = espfs httpd user serial cmd mqtt esp-link MODULES = espfs httpd user serial cmd mqtt esp-link
MODULES += $(foreach sdir,$(LIBRARIES_DIR),$(wildcard $(sdir)/*)) MODULES += $(foreach sdir,$(LIBRARIES_DIR),$(wildcard $(sdir)/*))
EXTRA_INCDIR = include . include/json EXTRA_INCDIR = include . include/json
# libraries used in this project, mainly provided by the SDK # libraries used in this project, mainly provided by the SDK
@ -184,7 +184,7 @@ LD_SCRIPT2 := build/eagle.esphttpd2.v6.ld
# various paths from the SDK used in this project # various paths from the SDK used in this project
SDK_LIBDIR = lib SDK_LIBDIR = lib
SDK_LDDIR = ld SDK_LDDIR = ld
SDK_INCDIR = include include/json SDK_INCDIR = include include/json
SDK_TOOLSDIR = tools SDK_TOOLSDIR = tools
@ -192,8 +192,8 @@ SDK_TOOLSDIR = tools
CC := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc CC := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc
AR := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-ar AR := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-ar
LD := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc LD := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-gcc
OBJCP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objcopy OBJCP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objcopy
OBJDP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objdump OBJDP := $(XTENSA_TOOLS_ROOT)xtensa-lx106-elf-objdump
#### ####
@ -231,7 +231,7 @@ CFLAGS += -DGZIP_COMPRESSION
endif endif
ifeq ("$(CHANGE_TO_STA)","yes") ifeq ("$(CHANGE_TO_STA)","yes")
CFLAGS += -DCHANGE_TO_STA CFLAGS += -DCHANGE_TO_STA
endif endif
vpath %.c $(SRC_DIR) vpath %.c $(SRC_DIR)
@ -373,8 +373,8 @@ release: all
$(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80 $(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80
$(Q) cp $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin $(SDK_BASE)/bin/blank.bin \ $(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) "$(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) tar zcf esp-link-$(BRANCH).tgz -C release esp-link-$(BRANCH)
$(Q) echo "Release file: esp-link-$(BRANCH)-$(FLASH_SIZE).tgz" $(Q) echo "Release file: esp-link-$(BRANCH).tgz"
$(Q) rm -rf release $(Q) rm -rf release
clean: clean:

@ -26,6 +26,28 @@ Many thanks to https://github.com/brunnels for contributions around the espduino
For quick support and questions: 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) [![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 Eye Candy
--------- ---------
These screen shots show the Home page, the Wifi configuration page, the console for the 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). [serial protocol](https://gist.github.com/tve/a46c44bf1f6b42bc572e).
More info and sample code forthcoming... 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 Contact
------- -------
If you find problems with esp-link, please create a github issue. If you have a question, please 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.

@ -4,12 +4,13 @@
#include "esp8266.h" #include "esp8266.h"
#include "cmd.h" #include "cmd.h"
#include "rest.h" #include <cgiwifi.h>
#include "crc16.h" #ifdef MQTT
#include "serbridge.h" #include <mqtt_cmd.h>
#include "uart.h" #endif
#include "cgiwifi.h" #ifdef REST
#include "mqtt_cmd.h" #include <rest.h>
#endif
static uint32_t CMD_Null(CmdPacket *cmd); static uint32_t CMD_Null(CmdPacket *cmd);
static uint32_t CMD_IsReady(CmdPacket *cmd); static uint32_t CMD_IsReady(CmdPacket *cmd);
@ -27,20 +28,20 @@ const CmdList commands[] = {
{CMD_RESET, CMD_Reset}, {CMD_RESET, CMD_Reset},
{CMD_IS_READY, CMD_IsReady}, {CMD_IS_READY, CMD_IsReady},
{CMD_WIFI_CONNECT, CMD_WifiConnect}, {CMD_WIFI_CONNECT, CMD_WifiConnect},
#ifdef MQTT
{CMD_MQTT_SETUP, MQTTCMD_Setup}, {CMD_MQTT_SETUP, MQTTCMD_Setup},
{CMD_MQTT_CONNECT, MQTTCMD_Connect}, {CMD_MQTT_CONNECT, MQTTCMD_Connect},
{CMD_MQTT_DISCONNECT, MQTTCMD_Disconnect}, {CMD_MQTT_DISCONNECT, MQTTCMD_Disconnect},
{CMD_MQTT_PUBLISH, MQTTCMD_Publish}, {CMD_MQTT_PUBLISH, MQTTCMD_Publish},
{CMD_MQTT_SUBSCRIBE , MQTTCMD_Subscribe}, {CMD_MQTT_SUBSCRIBE , MQTTCMD_Subscribe},
{CMD_MQTT_LWT, MQTTCMD_Lwt}, {CMD_MQTT_LWT, MQTTCMD_Lwt},
#endif
#ifdef REST
{CMD_REST_SETUP, REST_Setup}, {CMD_REST_SETUP, REST_Setup},
{CMD_REST_REQUEST, REST_Request}, {CMD_REST_REQUEST, REST_Request},
{CMD_REST_SETHEADER, REST_SetHeader}, {CMD_REST_SETHEADER, REST_SetHeader},
#endif
{CMD_ADD_CALLBACK, CMD_AddCallback }, { CMD_CB_ADD, CMD_AddCallback },
{CMD_NULL, NULL} {CMD_NULL, NULL}
}; };
@ -51,18 +52,14 @@ cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset
// Command handler for IsReady (healthcheck) command // Command handler for IsReady (healthcheck) command
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
CMD_IsReady(CmdPacket *cmd) { CMD_IsReady(CmdPacket *cmd) {
#ifdef CMD_DBG
os_printf("CMD_IsReady: Check ready\n"); os_printf("CMD_IsReady: Check ready\n");
#endif
return 1; return 1;
} }
// Command handler for Null command // Command handler for Null command
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
CMD_Null(CmdPacket *cmd) { CMD_Null(CmdPacket *cmd) {
#ifdef CMD_DBG
os_printf("CMD_Null: NULL/unsupported command\n"); os_printf("CMD_Null: NULL/unsupported command\n");
#endif
return 1; return 1;
} }
@ -71,9 +68,7 @@ CMD_Null(CmdPacket *cmd) {
// uC. // uC.
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
CMD_Reset(CmdPacket *cmd) { CMD_Reset(CmdPacket *cmd) {
#ifdef CMD_DBG
os_printf("CMD_Reset\n"); os_printf("CMD_Reset\n");
#endif
// clear callbacks table // clear callbacks table
os_memset(callbacks, 0, sizeof(callbacks)); os_memset(callbacks, 0, sizeof(callbacks));
return 1; return 1;
@ -81,19 +76,14 @@ CMD_Reset(CmdPacket *cmd) {
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
CMD_AddCb(char* name, uint32_t cb) { CMD_AddCb(char* name, uint32_t cb) {
char checkname[16];
os_strncpy(checkname, name, sizeof(checkname));
for (uint8_t i = 0; i < MAX_CALLBACKS; i++) { for (uint8_t i = 0; i < MAX_CALLBACKS; i++) {
#ifdef CMD_DBG //os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name,
os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback); // (void *)callbacks[i].callback);
#endif
// find existing callback or add to the end // find existing callback or add to the end
if (os_strcmp(callbacks[i].name, checkname) == 0 || callbacks[i].name[0] == '\0') { if (os_strcmp(callbacks[i].name, name) == 0 || callbacks[i].name[0] == '\0') {
os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name)); os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name));
callbacks[i].callback = cb; callbacks[i].callback = cb;
#ifdef CMD_DBG
os_printf("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i); os_printf("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i);
#endif
return 1; return 1;
} }
} }
@ -104,21 +94,16 @@ cmdCallback* ICACHE_FLASH_ATTR
CMD_GetCbByName(char* name) { CMD_GetCbByName(char* name) {
char checkname[16]; char checkname[16];
os_strncpy(checkname, name, sizeof(checkname)); os_strncpy(checkname, name, sizeof(checkname));
for (uint8_t i = 0; i < sizeof(commands); i++) { for (uint8_t i = 0; i < MAX_CALLBACKS; i++) {
#ifdef CMD_DBG //os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name,
os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback); // (void *)callbacks[i].callback);
#endif
// if callback doesn't exist or it's null // if callback doesn't exist or it's null
if (os_strcmp(callbacks[i].name, checkname) == 0) { if (os_strcmp(callbacks[i].name, checkname) == 0) {
#ifdef CMD_DBG
os_printf("CMD_GetCbByName: cb %s found at index %d\n", callbacks[i].name, i); os_printf("CMD_GetCbByName: cb %s found at index %d\n", callbacks[i].name, i);
#endif
return &callbacks[i]; return &callbacks[i];
} }
} }
#ifdef CMD_DBG
os_printf("CMD_GetCbByName: cb %s not found\n", name); os_printf("CMD_GetCbByName: cb %s not found\n", name);
#endif
return 0; return 0;
} }
@ -126,9 +111,7 @@ CMD_GetCbByName(char* name) {
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
CMD_WifiCb(uint8_t wifiStatus) { CMD_WifiCb(uint8_t wifiStatus) {
if (wifiStatus != lastWifiStatus){ if (wifiStatus != lastWifiStatus){
#ifdef CMD_DBG
os_printf("CMD_WifiCb: wifiStatus=%d\n", wifiStatus); os_printf("CMD_WifiCb: wifiStatus=%d\n", wifiStatus);
#endif
lastWifiStatus = wifiStatus; lastWifiStatus = wifiStatus;
cmdCallback *wifiCb = CMD_GetCbByName("wifiCb"); cmdCallback *wifiCb = CMD_GetCbByName("wifiCb");
if ((uint32_t)wifiCb->callback != -1) { if ((uint32_t)wifiCb->callback != -1) {
@ -145,9 +128,7 @@ static uint32_t ICACHE_FLASH_ATTR
CMD_WifiConnect(CmdPacket *cmd) { CMD_WifiConnect(CmdPacket *cmd) {
CmdRequest req; CmdRequest req;
CMD_Request(&req, cmd); CMD_Request(&req, cmd);
#ifdef CMD_DBG
os_printf("CMD_WifiConnect: setup argc=%ld\n", CMD_GetArgc(&req)); os_printf("CMD_WifiConnect: setup argc=%ld\n", CMD_GetArgc(&req));
#endif
if(cmd->argc != 2 || cmd->callback == 0) if(cmd->argc != 2 || cmd->callback == 0)
return 0; return 0;
@ -167,9 +148,7 @@ static uint32_t ICACHE_FLASH_ATTR
CMD_AddCallback(CmdPacket *cmd) { CMD_AddCallback(CmdPacket *cmd) {
CmdRequest req; CmdRequest req;
CMD_Request(&req, cmd); CMD_Request(&req, cmd);
#ifdef CMD_DBG
os_printf("CMD_AddCallback: setup argc=%ld\n", CMD_GetArgc(&req)); os_printf("CMD_AddCallback: setup argc=%ld\n", CMD_GetArgc(&req));
#endif
if (cmd->argc != 1 || cmd->callback == 0) if (cmd->argc != 1 || cmd->callback == 0)
return 0; return 0;
@ -178,15 +157,11 @@ CMD_AddCallback(CmdPacket *cmd) {
// get the sensor name // get the sensor name
len = CMD_ArgLen(&req); len = CMD_ArgLen(&req);
#ifdef CMD_DBG
os_printf("CMD_AddCallback: name len=%d\n", len); os_printf("CMD_AddCallback: name len=%d\n", len);
#endif
if (len > 15) return 0; // max size of name is 15 characters if (len > 15) return 0; // max size of name is 15 characters
if (CMD_PopArg(&req, (uint8_t *)name, len)) return 0; if (CMD_PopArg(&req, (uint8_t *)name, len)) return 0;
name[len] = 0; name[len] = 0;
#ifdef CMD_DBG
os_printf("CMD_AddCallback: name=%s\n", name); os_printf("CMD_AddCallback: name=%s\n", name);
#endif
return CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback return CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback
} }

@ -12,6 +12,9 @@ Some random cgi routines.
* Heavily modified and enhanced by Thorsten von Eicken in 2015 * Heavily modified and enhanced by Thorsten von Eicken in 2015
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
*/ */
#include <esp8266.h>
#include "cgi.h" #include "cgi.h"
static char* chipIdStr = ""; static char* chipIdStr = "";
char* ICACHE_FLASH_ATTR system_get_chip_id_str(){ char* ICACHE_FLASH_ATTR system_get_chip_id_str(){
@ -22,16 +25,63 @@ char* ICACHE_FLASH_ATTR system_get_chip_id_str(){
return chipIdStr; return chipIdStr;
} }
void ICACHE_FLASH_ATTR void noCacheHeaders(HttpdConnData *connData, int code) {
jsonHeader(HttpdConnData *connData, int code) {
httpdStartResponse(connData, code); httpdStartResponse(connData, code);
httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate");
httpdHeader(connData, "Pragma", "no-cache"); httpdHeader(connData, "Pragma", "no-cache");
httpdHeader(connData, "Expires", "0"); httpdHeader(connData, "Expires", "0");
}
void ICACHE_FLASH_ATTR
jsonHeader(HttpdConnData *connData, int code) {
noCacheHeaders(connData, code);
httpdHeader(connData, "Content-Type", "application/json"); httpdHeader(connData, "Content-Type", "application/json");
httpdEndHeaders(connData); httpdEndHeaders(connData);
} }
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);
}
// 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;
}
uint8_t ICACHE_FLASH_ATTR uint8_t ICACHE_FLASH_ATTR
UTILS_StrToIP(const char* str, void *ip){ UTILS_StrToIP(const char* str, void *ip){
/* The count of the number of bytes processed. */ /* The count of the number of bytes processed. */
@ -106,8 +156,11 @@ int ICACHE_FLASH_ATTR cgiMenu(HttpdConnData *connData) {
httpdEndHeaders(connData); httpdEndHeaders(connData);
// construct json response // construct json response
os_sprintf(buff, os_sprintf(buff,
"{\"menu\": [\"Home\", \"/home.html\", \"Wifi\", \"/wifi/wifi.html\"," "{\"menu\": [\"Home\", \"/home.html\", "
"\"\xC2\xB5" "C Console\", \"/console.html\", \"Debug log\", \"/log.html\" ],\n" "\"Wifi\", \"/wifi/wifi.html\","
"\"\xC2\xB5" "C Console\", \"/console.html\", "
"\"REST/MQTT\", \"/mqtt.html\","
"\"Debug log\", \"/log.html\" ],\n"
" \"version\": \"%s\" }", esp_link_version); " \"version\": \"%s\" }", esp_link_version);
httpdSend(connData, buff, -1); httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;

@ -5,6 +5,9 @@
#include "httpd.h" #include "httpd.h"
void jsonHeader(HttpdConnData *connData, int code); 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); int cgiMenu(HttpdConnData *connData);
uint8_t UTILS_StrToIP(const char* str, void *ip); uint8_t UTILS_StrToIP(const char* str, void *ip);
char* system_get_chip_id_str(); char* system_get_chip_id_str();

@ -0,0 +1,111 @@
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
// // TCP Client settings
#include <esp8266.h>
#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;
}
}

@ -0,0 +1,8 @@
#ifndef CGIMQTT_H
#define CGIMQTT_H
#include "httpd.h"
int cgiMqtt(HttpdConnData *connData);
#endif

@ -24,9 +24,9 @@ static const int num_map_func = sizeof(map_func)/sizeof(char*);
// Cgi to return choice of pin assignments // Cgi to return choice of pin assignments
int ICACHE_FLASH_ATTR cgiPinsGet(HttpdConnData *connData) { 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; int len;
// figure out current mapping // figure out current mapping
@ -62,27 +62,27 @@ int ICACHE_FLASH_ATTR cgiPinsGet(HttpdConnData *connData) {
} }
len += os_sprintf(buff+len, "\n] }"); len += os_sprintf(buff+len, "\n] }");
jsonHeader(connData, 200); jsonHeader(connData, 200);
httpdSend(connData, buff, len); httpdSend(connData, buff, len);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
// Cgi to change choice of pin assignments // Cgi to change choice of pin assignments
int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) {
if (connData->conn==NULL) { if (connData->conn==NULL) {
return HTTPD_CGI_DONE; // Connection aborted return HTTPD_CGI_DONE; // Connection aborted
} }
char buff[128]; char buff[128];
int len = httpdFindArg(connData->getArgs, "map", buff, sizeof(buff)); int len = httpdFindArg(connData->getArgs, "map", buff, sizeof(buff));
if (len <= 0) { if (len <= 0) {
jsonHeader(connData, 400); jsonHeader(connData, 400);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
int m = atoi(buff); int m = atoi(buff);
if (m < 0 || m >= num_map_names) { if (m < 0 || m >= num_map_names) {
jsonHeader(connData, 400); jsonHeader(connData, 400);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
@ -106,17 +106,17 @@ int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) {
httpdEndHeaders(connData); httpdEndHeaders(connData);
httpdSend(connData, "Failed to save config", -1); httpdSend(connData, "Failed to save config", -1);
} }
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
int ICACHE_FLASH_ATTR cgiPins(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiPins(HttpdConnData *connData) {
if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
if (connData->requestType == HTTPD_METHOD_GET) { if (connData->requestType == HTTPD_METHOD_GET) {
return cgiPinsGet(connData); return cgiPinsGet(connData);
} else if (connData->requestType == HTTPD_METHOD_POST) { } else if (connData->requestType == HTTPD_METHOD_POST) {
return cgiPinsSet(connData); return cgiPinsSet(connData);
} else { } else {
jsonHeader(connData, 404); jsonHeader(connData, 404);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
} }

@ -31,12 +31,12 @@ uint8_t wifiState = wifiIsDisconnected;
// reasons for which a connection failed // reasons for which a connection failed
uint8_t wifiReason = 0; uint8_t wifiReason = 0;
static char *wifiReasons[] = { static char *wifiReasons[] = {
"", "unspecified", "auth_expire", "auth_leave", "assoc_expire", "assoc_toomany", "not_authed", "", "unspecified", "auth_expire", "auth_leave", "assoc_expire", "assoc_toomany", "not_authed",
"not_assoced", "assoc_leave", "assoc_not_authed", "disassoc_pwrcap_bad", "disassoc_supchan_bad", "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_invalid", "mic_failure", "4way_handshake_timeout", "group_key_update_timeout",
"ie_in_4way_differs", "group_cipher_invalid", "pairwise_cipher_invalid", "akmp_invalid", "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", "unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected",
"beacon_timeout", "no_ap_found" }; "beacon_timeout", "no_ap_found" };
static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" };
static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; static char *wifiPhy[] = { 0, "11b", "11g", "11n" };
@ -44,63 +44,51 @@ static char *wifiPhy[] = { 0, "11b", "11g", "11n" };
void (*wifiStatusCb)(uint8_t); // callback when wifi status changes void (*wifiStatusCb)(uint8_t); // callback when wifi status changes
static char* ICACHE_FLASH_ATTR wifiGetReason(void) { static char* ICACHE_FLASH_ATTR wifiGetReason(void) {
if (wifiReason <= 24) return wifiReasons[wifiReason]; if (wifiReason <= 24) return wifiReasons[wifiReason];
if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24]; if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24];
return wifiReasons[1]; return wifiReasons[1];
} }
// handler for wifi status change callback coming in from espressif library // handler for wifi status change callback coming in from espressif library
static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) { static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) {
switch (evt->event) { switch (evt->event) {
case EVENT_STAMODE_CONNECTED: case EVENT_STAMODE_CONNECTED:
wifiState = wifiIsConnected; wifiState = wifiIsConnected;
wifiReason = 0; wifiReason = 0;
#ifdef WIFI_DBG os_printf("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid,
os_printf("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid, evt->event_info.connected.channel);
evt->event_info.connected.channel); statusWifiUpdate(wifiState);
#endif break;
statusWifiUpdate(wifiState); case EVENT_STAMODE_DISCONNECTED:
break; wifiState = wifiIsDisconnected;
case EVENT_STAMODE_DISCONNECTED: wifiReason = evt->event_info.disconnected.reason;
wifiState = wifiIsDisconnected; os_printf("Wifi disconnected from ssid %s, reason %s (%d)\n",
wifiReason = evt->event_info.disconnected.reason; evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason);
#ifdef WIFI_DBG statusWifiUpdate(wifiState);
os_printf("Wifi disconnected from ssid %s, reason %s (%d)\n", break;
evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); case EVENT_STAMODE_AUTHMODE_CHANGE:
#endif os_printf("Wifi auth mode: %d -> %d\n",
statusWifiUpdate(wifiState); evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode);
break; break;
case EVENT_STAMODE_AUTHMODE_CHANGE: case EVENT_STAMODE_GOT_IP:
#ifdef WIFI_DBG wifiState = wifiGotIP;
os_printf("Wifi auth mode: %d -> %d\n", wifiReason = 0;
evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); os_printf("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n",
#endif IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask),
break; IP2STR(&evt->event_info.got_ip.gw));
case EVENT_STAMODE_GOT_IP: statusWifiUpdate(wifiState);
wifiState = wifiGotIP; break;
wifiReason = 0; case EVENT_SOFTAPMODE_STACONNECTED:
#ifdef WIFI_DBG os_printf("Wifi AP: station " MACSTR " joined, AID = %d\n",
os_printf("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid);
IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), break;
IP2STR(&evt->event_info.got_ip.gw)); case EVENT_SOFTAPMODE_STADISCONNECTED:
#endif os_printf("Wifi AP: station " MACSTR " left, AID = %d\n",
statusWifiUpdate(wifiState); MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid);
break; break;
case EVENT_SOFTAPMODE_STACONNECTED: default:
#ifdef WIFI_DBG break;
os_printf("Wifi AP: station " MACSTR " joined, AID = %d\n", }
MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid);
#endif
break;
case EVENT_SOFTAPMODE_STADISCONNECTED:
#ifdef WIFI_DBG
os_printf("Wifi AP: station " MACSTR " left, AID = %d\n",
MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid);
#endif
break;
default:
break;
}
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState); if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState);
@ -115,25 +103,23 @@ wifiAddStateChangeCb(WifiStateChangeCb cb) {
return; return;
} }
} }
#ifdef WIFI_DBG
os_printf("WIFI: max state change cb count exceeded\n"); os_printf("WIFI: max state change cb count exceeded\n");
#endif
} }
// ===== wifi scanning // ===== wifi scanning
//WiFi access point data //WiFi access point data
typedef struct { typedef struct {
char ssid[32]; char ssid[32];
sint8 rssi; sint8 rssi;
char enc; char enc;
} ApData; } ApData;
//Scan result //Scan result
typedef struct { typedef struct {
char scanInProgress; //if 1, don't access the underlying stuff from the webpage. char scanInProgress; //if 1, don't access the underlying stuff from the webpage.
ApData **apData; ApData **apData;
int noAps; int noAps;
} ScanResultData; } ScanResultData;
//Static scan status storage. //Static scan status storage.
@ -142,121 +128,109 @@ static ScanResultData cgiWifiAps;
//Callback the code calls when a wlan ap scan is done. Basically stores the result in //Callback the code calls when a wlan ap scan is done. Basically stores the result in
//the cgiWifiAps struct. //the cgiWifiAps struct.
void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) { void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
int n; int n;
struct bss_info *bss_link = (struct bss_info *)arg; struct bss_info *bss_link = (struct bss_info *)arg;
if (status!=OK) { if (status!=OK) {
#ifdef WIFI_DBG os_printf("wifiScanDoneCb status=%d\n", status);
os_printf("wifiScanDoneCb status=%d\n", status); cgiWifiAps.scanInProgress=0;
#endif return;
cgiWifiAps.scanInProgress=0; }
return;
}
//Clear prev ap data if needed.
if (cgiWifiAps.apData!=NULL) {
for (n=0; n<cgiWifiAps.noAps; n++) os_free(cgiWifiAps.apData[n]);
os_free(cgiWifiAps.apData);
}
//Count amount of access points found.
n=0;
while (bss_link != NULL) {
bss_link = bss_link->next.stqe_next;
n++;
}
//Allocate memory for access point data
cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n);
cgiWifiAps.noAps=n;
#ifdef WIFI_DBG
os_printf("Scan done: found %d APs\n", n);
#endif
//Copy access point data to the static struct //Clear prev ap data if needed.
n=0; if (cgiWifiAps.apData!=NULL) {
bss_link = (struct bss_info *)arg; for (n=0; n<cgiWifiAps.noAps; n++) os_free(cgiWifiAps.apData[n]);
while (bss_link != NULL) { os_free(cgiWifiAps.apData);
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.
#ifdef WIFI_DBG
os_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps);
#endif
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);
#ifdef WIFI_DBG
os_printf("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi);
#endif
bss_link = bss_link->next.stqe_next; //Count amount of access points found.
n++; n=0;
} while (bss_link != NULL) {
//We're done. bss_link = bss_link->next.stqe_next;
cgiWifiAps.scanInProgress=0; 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 ETSTimer scanTimer;
static void ICACHE_FLASH_ATTR scanStartCb(void *arg) { static void ICACHE_FLASH_ATTR scanStartCb(void *arg) {
#ifdef WIFI_DBG os_printf("Starting a scan\n");
os_printf("Starting a scan\n"); wifi_station_scan(NULL, wifiScanDoneCb);
#endif
wifi_station_scan(NULL, wifiScanDoneCb);
} }
static int ICACHE_FLASH_ATTR cgiWiFiStartScan(HttpdConnData *connData) { static int ICACHE_FLASH_ATTR cgiWiFiStartScan(HttpdConnData *connData) {
if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
jsonHeader(connData, 200); jsonHeader(connData, 200);
if (!cgiWifiAps.scanInProgress) { if (!cgiWifiAps.scanInProgress) {
cgiWifiAps.scanInProgress = 1; cgiWifiAps.scanInProgress = 1;
os_timer_disarm(&scanTimer); os_timer_disarm(&scanTimer);
os_timer_setfn(&scanTimer, scanStartCb, NULL); os_timer_setfn(&scanTimer, scanStartCb, NULL);
os_timer_arm(&scanTimer, 1000, 0); os_timer_arm(&scanTimer, 1000, 0);
} }
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
static int ICACHE_FLASH_ATTR cgiWiFiGetScan(HttpdConnData *connData) { static int ICACHE_FLASH_ATTR cgiWiFiGetScan(HttpdConnData *connData) {
if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
char buff[2048]; char buff[2048];
int len; int len;
jsonHeader(connData, 200); jsonHeader(connData, 200);
if (cgiWifiAps.scanInProgress==1) { if (cgiWifiAps.scanInProgress==1) {
//We're still scanning. Tell Javascript code that. //We're still scanning. Tell Javascript code that.
len = os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n"); len = os_sprintf(buff, "{\n \"result\": { \n\"inProgress\": \"1\"\n }\n}\n");
httpdSend(connData, buff, len); httpdSend(connData, buff, len);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n"); len = os_sprintf(buff, "{\"result\": {\"inProgress\": \"0\", \"APs\": [\n");
for (int pos=0; pos<cgiWifiAps.noAps; pos++) { for (int pos=0; pos<cgiWifiAps.noAps; pos++) {
len += os_sprintf(buff+len, "{\"essid\": \"%s\", \"rssi\": %d, \"enc\": \"%d\"}%s\n", len += os_sprintf(buff+len, "{\"essid\": \"%s\", \"rssi\": %d, \"enc\": \"%d\"}%s\n",
cgiWifiAps.apData[pos]->ssid, cgiWifiAps.apData[pos]->rssi, cgiWifiAps.apData[pos]->ssid, cgiWifiAps.apData[pos]->rssi,
cgiWifiAps.apData[pos]->enc, (pos==cgiWifiAps.noAps-1)?"":","); cgiWifiAps.apData[pos]->enc, (pos==cgiWifiAps.noAps-1)?"":",");
} }
len += os_sprintf(buff+len, "]}}\n"); len += os_sprintf(buff+len, "]}}\n");
#ifdef WIFI_DBG //os_printf("Sending %d bytes: %s\n", len, buff);
os_printf("Sending %d bytes: %s\n", len, buff); httpdSend(connData, buff, len);
#endif return HTTPD_CGI_DONE;
httpdSend(connData, buff, len);
return HTTPD_CGI_DONE;
} }
int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiWiFiScan(HttpdConnData *connData) {
if (connData->requestType == HTTPD_METHOD_GET) { if (connData->requestType == HTTPD_METHOD_GET) {
return cgiWiFiGetScan(connData); return cgiWiFiGetScan(connData);
} else if (connData->requestType == HTTPD_METHOD_POST) { } else if (connData->requestType == HTTPD_METHOD_POST) {
return cgiWiFiStartScan(connData); return cgiWiFiStartScan(connData);
} else { } else {
jsonHeader(connData, 404); jsonHeader(connData, 404);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
} }
// ===== timers to change state and rescue from failed associations // ===== timers to change state and rescue from failed associations
@ -269,39 +243,31 @@ static ETSTimer resetTimer;
// the connect succeeds, this gets the module in STA-only mode. If it fails, it ensures // 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. // that the module is in STA+AP mode so the user has a chance to recover.
static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) {
int x = wifi_station_get_connect_status(); int x = wifi_station_get_connect_status();
int m = wifi_get_opmode() & 0x3; int m = wifi_get_opmode() & 0x3;
#ifdef WIFI_DBG os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x);
os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x);
#endif
if (x == STATION_GOT_IP) { if (x == STATION_GOT_IP) {
if (m != 1) { if (m != 1) {
#ifdef CHANGE_TO_STA #ifdef CHANGE_TO_STA
// We're happily connected, go to STA mode // We're happily connected, go to STA mode
#ifdef WIFI_DBG os_printf("Wifi got IP. Going into STA mode..\n");
os_printf("Wifi got IP. Going into STA mode..\n"); wifi_set_opmode(1);
#endif wifi_set_sleep_type(SLEEP_MODE);
wifi_set_opmode(1); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
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) {
#ifdef WIFI_DBG
os_printf("Wifi connect failed. Going into STA+AP mode..\n");
#endif #endif
wifi_set_opmode(3); }
} log_uart(false);
log_uart(true); // no more resetTimer at this point, gotta use physical reset to recover if in trouble
#ifdef WIFI_DBG } else {
os_printf("Enabling/continuing uart log\n"); if (m != 3) {
#endif os_printf("Wifi connect failed. Going into STA+AP mode..\n");
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); 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. // Temp store for new ap info.
@ -311,215 +277,206 @@ static ETSTimer reassTimer;
// Callback actually doing reassociation // Callback actually doing reassociation
static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) { static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) {
#ifdef WIFI_DBG os_printf("Wifi changing association\n");
os_printf("Wifi changing association\n"); wifi_station_disconnect();
#endif stconf.bssid_set = 0;
wifi_station_disconnect(); wifi_station_set_config(&stconf);
stconf.bssid_set = 0; wifi_station_connect();
wifi_station_set_config(&stconf); // Schedule check
wifi_station_connect(); os_timer_disarm(&resetTimer);
// Schedule check os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_disarm(&resetTimer); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
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 // This cgi uses the routines above to connect to a specific access point with the
// given ESSID using the given password. // given ESSID using the given password.
int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) {
char essid[128]; char essid[128];
char passwd[128]; char passwd[128];
if (connData->conn==NULL) return HTTPD_CGI_DONE; if (connData->conn==NULL) return HTTPD_CGI_DONE;
int el = httpdFindArg(connData->getArgs, "essid", essid, sizeof(essid)); int el = httpdFindArg(connData->getArgs, "essid", essid, sizeof(essid));
int pl = httpdFindArg(connData->getArgs, "passwd", passwd, sizeof(passwd)); int pl = httpdFindArg(connData->getArgs, "passwd", passwd, sizeof(passwd));
if (el > 0 && pl >= 0) { if (el > 0 && pl >= 0) {
//Set to 0 if you want to disable the actual reconnecting bit //Set to 0 if you want to disable the actual reconnecting bit
os_strncpy((char*)stconf.ssid, essid, 32); os_strncpy((char*)stconf.ssid, essid, 32);
os_strncpy((char*)stconf.password, passwd, 64); os_strncpy((char*)stconf.password, passwd, 64);
#ifdef WIFI_DBG os_printf("Wifi try to connect to AP %s pw %s\n", essid, passwd);
os_printf("Wifi try to connect to AP %s pw %s\n", essid, passwd);
#endif //Schedule disconnect/connect
os_timer_disarm(&reassTimer);
//Schedule disconnect/connect os_timer_setfn(&reassTimer, reassTimerCb, NULL);
os_timer_disarm(&reassTimer); os_timer_arm(&reassTimer, 1000, 0);
os_timer_setfn(&reassTimer, reassTimerCb, NULL); jsonHeader(connData, 200);
os_timer_arm(&reassTimer, 1000, 0); } else {
jsonHeader(connData, 200); jsonHeader(connData, 400);
} else { httpdSend(connData, "Cannot parse ssid or password", -1);
jsonHeader(connData, 400); }
httpdSend(connData, "Cannot parse ssid or password", -1); return HTTPD_CGI_DONE;
}
return HTTPD_CGI_DONE;
} }
static bool parse_ip(char *buff, ip_addr_t *ip_ptr) { static bool parse_ip(char *buff, ip_addr_t *ip_ptr) {
char *next = buff; // where to start parsing next integer char *next = buff; // where to start parsing next integer
int found = 0; // number of integers parsed int found = 0; // number of integers parsed
uint32_t ip = 0; // the ip addres parsed uint32_t ip = 0; // the ip addres parsed
for (int i=0; i<32; i++) { // 32 is just a safety limit for (int i=0; i<32; i++) { // 32 is just a safety limit
char c = buff[i]; char c = buff[i];
if (c == '.' || c == 0) { if (c == '.' || c == 0) {
// parse the preceding integer and accumulate into IP address // parse the preceding integer and accumulate into IP address
bool last = c == 0; bool last = c == 0;
buff[i] = 0; buff[i] = 0;
uint32_t v = atoi(next); uint32_t v = atoi(next);
ip = ip | ((v&0xff)<<(found*8)); ip = ip | ((v&0xff)<<(found*8));
next = buff+i+1; // next integer starts after the '.' next = buff+i+1; // next integer starts after the '.'
found++; found++;
if (last) { // if at end of string we better got 4 integers if (last) { // if at end of string we better got 4 integers
ip_ptr->addr = ip; ip_ptr->addr = ip;
return found == 4; return found == 4;
} }
continue; continue;
} }
if (c < '0' || c > '9') return false; if (c < '0' || c > '9') return false;
} }
return false; return false;
} }
#ifdef WIFI_DBG #define DEBUGIP
#ifdef DEBUGIP
static void ICACHE_FLASH_ATTR debugIP() { static void ICACHE_FLASH_ATTR debugIP() {
struct ip_info info; struct ip_info info;
if (wifi_get_ip_info(0, &info)) { if (wifi_get_ip_info(0, &info)) {
os_printf("\"ip\": \"%d.%d.%d.%d\"\n", IP2STR(&info.ip.addr)); 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("\"netmask\": \"%d.%d.%d.%d\"\n", IP2STR(&info.netmask.addr));
os_printf("\"gateway\": \"%d.%d.%d.%d\"\n", IP2STR(&info.gw.addr)); os_printf("\"gateway\": \"%d.%d.%d.%d\"\n", IP2STR(&info.gw.addr));
os_printf("\"hostname\": \"%s\"\n", wifi_station_get_hostname()); os_printf("\"hostname\": \"%s\"\n", wifi_station_get_hostname());
} else { } else {
os_printf("\"ip\": \"-none-\"\n"); os_printf("\"ip\": \"-none-\"\n");
} }
} }
#endif #endif
// configure Wifi, specifically DHCP vs static IP address based on flash config // configure Wifi, specifically DHCP vs static IP address based on flash config
static void ICACHE_FLASH_ATTR configWifiIP() { static void ICACHE_FLASH_ATTR configWifiIP() {
if (flashConfig.staticip == 0) { if (flashConfig.staticip == 0) {
// let's DHCP! // let's DHCP!
wifi_station_set_hostname(flashConfig.hostname); wifi_station_set_hostname(flashConfig.hostname);
if (wifi_station_dhcpc_status() == DHCP_STARTED) if (wifi_station_dhcpc_status() == DHCP_STARTED)
wifi_station_dhcpc_stop(); wifi_station_dhcpc_stop();
wifi_station_dhcpc_start(); wifi_station_dhcpc_start();
#ifdef WIFI_DBG os_printf("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname);
os_printf("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname); } else {
#endif // no DHCP, we got static network config!
} else { wifi_station_dhcpc_stop();
// no DHCP, we got static network config! struct ip_info ipi;
wifi_station_dhcpc_stop(); ipi.ip.addr = flashConfig.staticip;
struct ip_info ipi; ipi.netmask.addr = flashConfig.netmask;
ipi.ip.addr = flashConfig.staticip; ipi.gw.addr = flashConfig.gateway;
ipi.netmask.addr = flashConfig.netmask; wifi_set_ip_info(0, &ipi);
ipi.gw.addr = flashConfig.gateway; os_printf("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr));
wifi_set_ip_info(0, &ipi); }
#ifdef WIFI_DBG
os_printf("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr));
#endif
}
#ifdef DEBUGIP #ifdef DEBUGIP
debugIP(); debugIP();
#endif #endif
} }
// Change special settings // Change special settings
int ICACHE_FLASH_ATTR cgiWiFiSpecial(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiWiFiSpecial(HttpdConnData *connData) {
char dhcp[8]; char dhcp[8];
char hostname[32]; char hostname[32];
char staticip[20]; char staticip[20];
char netmask[20]; char netmask[20];
char gateway[20]; char gateway[20];
if (connData->conn==NULL) return HTTPD_CGI_DONE; if (connData->conn==NULL) return HTTPD_CGI_DONE;
// get args and their string lengths // get args and their string lengths
int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp));
int hl = httpdFindArg(connData->getArgs, "hostname", hostname, sizeof(hostname)); int hl = httpdFindArg(connData->getArgs, "hostname", hostname, sizeof(hostname));
int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip));
int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask));
int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway));
if (!(dl > 0 && hl >= 0 && sl >= 0 && nl >= 0 && gl >= 0)) { if (!(dl > 0 && hl >= 0 && sl >= 0 && nl >= 0 && gl >= 0)) {
jsonHeader(connData, 400); jsonHeader(connData, 400);
httpdSend(connData, "Request is missing fields", -1); httpdSend(connData, "Request is missing fields", -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
char url[64]; // redirect URL char url[64]; // redirect URL
if (os_strcmp(dhcp, "off") == 0) { if (os_strcmp(dhcp, "off") == 0) {
// parse static IP params // parse static IP params
struct ip_info ipi; struct ip_info ipi;
bool ok = parse_ip(staticip, &ipi.ip); bool ok = parse_ip(staticip, &ipi.ip);
if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask);
else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0);
if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw);
else ipi.gw.addr = 0; else ipi.gw.addr = 0;
if (!ok) { if (!ok) {
jsonHeader(connData, 400); jsonHeader(connData, 400);
httpdSend(connData, "Cannot parse static IP config", -1); httpdSend(connData, "Cannot parse static IP config", -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
// save the params in flash // save the params in flash
flashConfig.staticip = ipi.ip.addr; flashConfig.staticip = ipi.ip.addr;
flashConfig.netmask = ipi.netmask.addr; flashConfig.netmask = ipi.netmask.addr;
flashConfig.gateway = ipi.gw.addr; flashConfig.gateway = ipi.gw.addr;
// construct redirect URL // construct redirect URL
os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip));
} else { } else {
// no static IP, set hostname // no static IP, set hostname
if (hl == 0) os_strcpy(hostname, "esp-link"); if (hl == 0) os_strcpy(hostname, "esp-link");
flashConfig.staticip = 0; flashConfig.staticip = 0;
os_strcpy(flashConfig.hostname, hostname); os_strcpy(flashConfig.hostname, hostname);
os_sprintf(url, "{\"url\": \"http://%s\"}", hostname); os_sprintf(url, "{\"url\": \"http://%s\"}", hostname);
} }
configSave(); // ignore error... configSave(); // ignore error...
// schedule change-over // schedule change-over
os_timer_disarm(&reassTimer); os_timer_disarm(&reassTimer);
os_timer_setfn(&reassTimer, configWifiIP, NULL); os_timer_setfn(&reassTimer, configWifiIP, NULL);
os_timer_arm(&reassTimer, 1000, 0); os_timer_arm(&reassTimer, 1000, 0);
// return redirect info // return redirect info
jsonHeader(connData, 200); jsonHeader(connData, 200);
httpdSend(connData, url, -1); httpdSend(connData, url, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
//This cgi changes the operating mode: STA / AP / STA+AP //This cgi changes the operating mode: STA / AP / STA+AP
int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) {
int len; int len;
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.
len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff)); len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff));
if (len!=0) { if (len!=0) {
int m = atoi(buff); int m = atoi(buff);
#ifdef WIFI_DBG os_printf("Wifi switching to mode %d\n", m);
os_printf("Wifi switching to mode %d\n", m); wifi_set_opmode(m&3);
#endif if (m == 1) {
wifi_set_opmode(m&3); wifi_set_sleep_type(SLEEP_MODE);
if (m == 1) { // STA-only mode, reset into STA+AP after a timeout
wifi_set_sleep_type(SLEEP_MODE); os_timer_disarm(&resetTimer);
// STA-only mode, reset into STA+AP after a timeout os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_disarm(&resetTimer); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
os_timer_setfn(&resetTimer, resetTimerCb, NULL); }
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); jsonHeader(connData, 200);
} } else {
jsonHeader(connData, 200); jsonHeader(connData, 400);
} else { }
jsonHeader(connData, 400); return HTTPD_CGI_DONE;
}
return HTTPD_CGI_DONE;
} }
static char *connStatuses[] = { "idle", "connecting", "wrong password", "AP not found", static char *connStatuses[] = { "idle", "connecting", "wrong password", "AP not found",
"failed", "got IP address" }; "failed", "got IP address" };
static char *wifiWarn[] = { 0, static char *wifiWarn[] = { 0,
"Switch to <a href=\\\"#\\\" onclick=\\\"changeWifiMode(3)\\\">STA+AP mode</a>", "Switch to <a href=\\\"#\\\" onclick=\\\"changeWifiMode(3)\\\">STA+AP mode</a>",
"<b>Can't scan in this mode!</b> Switch to <a href=\\\"#\\\" onclick=\\\"changeWifiMode(3)\\\">STA+AP mode</a>", "<b>Can't scan in this mode!</b> Switch to <a href=\\\"#\\\" onclick=\\\"changeWifiMode(3)\\\">STA+AP mode</a>",
"Switch to <a href=\\\"#\\\" onclick=\\\"changeWifiMode(1)\\\">STA mode</a>", "Switch to <a href=\\\"#\\\" onclick=\\\"changeWifiMode(1)\\\">STA mode</a>",
}; };
#ifdef CHANGE_TO_STA #ifdef CHANGE_TO_STA
@ -530,112 +487,109 @@ static char *wifiWarn[] = { 0,
// print various Wifi information into json buffer // print various Wifi information into json buffer
int ICACHE_FLASH_ATTR printWifiInfo(char *buff) { int ICACHE_FLASH_ATTR printWifiInfo(char *buff) {
int len; int len;
struct station_config stconf; struct station_config stconf;
wifi_station_get_config(&stconf); wifi_station_get_config(&stconf);
uint8_t op = wifi_get_opmode() & 0x3; uint8_t op = wifi_get_opmode() & 0x3;
char *mode = wifiMode[op]; char *mode = wifiMode[op];
char *status = "unknown"; char *status = "unknown";
int st = wifi_station_get_connect_status(); int st = wifi_station_get_connect_status();
if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st]; if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st];
int p = wifi_get_phy_mode(); int p = wifi_get_phy_mode();
char *phy = wifiPhy[p&3]; char *phy = wifiPhy[p&3];
char *warn = wifiWarn[op]; char *warn = wifiWarn[op];
sint8 rssi = wifi_station_get_rssi(); sint8 rssi = wifi_station_get_rssi();
if (rssi > 0) rssi = 0; if (rssi > 0) rssi = 0;
uint8 mac_addr[6]; uint8 mac_addr[6];
wifi_get_macaddr(0, mac_addr); wifi_get_macaddr(0, mac_addr);
uint8_t chan = wifi_get_channel(); uint8_t chan = wifi_get_channel();
len = os_sprintf(buff, len = os_sprintf(buff,
"\"mode\": \"%s\", \"modechange\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", " "\"mode\": \"%s\", \"modechange\": \"%s\", \"ssid\": \"%s\", \"status\": \"%s\", \"phy\": \"%s\", "
"\"rssi\": \"%ddB\", \"warn\": \"%s\", \"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\", \"chan\":%d", "\"rssi\": \"%ddB\", \"warn\": \"%s\", \"mac\":\"%02x:%02x:%02x:%02x:%02x:%02x\", \"chan\":%d",
mode, MODECHANGE, (char*)stconf.ssid, status, phy, rssi, warn, 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); mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5], chan);
struct ip_info info; struct ip_info info;
if (wifi_get_ip_info(0, &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, ", \"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, ", \"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, ", \"gateway\": \"%d.%d.%d.%d\"", IP2STR(&info.gw.addr));
len += os_sprintf(buff+len, ", \"hostname\": \"%s\"", flashConfig.hostname); len += os_sprintf(buff+len, ", \"hostname\": \"%s\"", flashConfig.hostname);
} else { } else {
len += os_sprintf(buff+len, ", \"ip\": \"-none-\""); 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, ", \"staticip\": \"%d.%d.%d.%d\"", IP2STR(&flashConfig.staticip));
len += os_sprintf(buff+len, ", \"dhcp\": \"%s\"", flashConfig.staticip > 0 ? "off" : "on"); len += os_sprintf(buff+len, ", \"dhcp\": \"%s\"", flashConfig.staticip > 0 ? "off" : "on");
return len; return len;
} }
int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) { int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
char buff[1024]; char buff[1024];
int len; int len;
if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up.
jsonHeader(connData, 200); jsonHeader(connData, 200);
len = os_sprintf(buff, "{"); len = os_sprintf(buff, "{");
len += printWifiInfo(buff+len); len += printWifiInfo(buff+len);
len += os_sprintf(buff+len, ", "); len += os_sprintf(buff+len, ", ");
if (wifiReason != 0) { if (wifiReason != 0) {
len += os_sprintf(buff+len, "\"reason\": \"%s\", ", wifiGetReason()); len += os_sprintf(buff+len, "\"reason\": \"%s\", ", wifiGetReason());
} }
#if 0 #if 0
// commented out 'cause often the client that requested the change can't get a request in to // 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... // find out that it succeeded. Better to just wait the std 15 seconds...
int st=wifi_station_get_connect_status(); int st=wifi_station_get_connect_status();
if (st == STATION_GOT_IP) { if (st == STATION_GOT_IP) {
if (wifi_get_opmode() != 1) { if (wifi_get_opmode() != 1) {
// Reset into AP-only mode sooner. // Reset into AP-only mode sooner.
os_timer_disarm(&resetTimer); os_timer_disarm(&resetTimer);
os_timer_setfn(&resetTimer, resetTimerCb, NULL); os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_arm(&resetTimer, 1000, 0); os_timer_arm(&resetTimer, 1000, 0);
} }
} }
#endif #endif
len += os_sprintf(buff+len, "\"x\":0}\n"); len += os_sprintf(buff+len, "\"x\":0}\n");
#ifdef WIFI_DBG
os_printf(" -> %s\n", buff); os_printf(" -> %s\n", buff);
#endif httpdSend(connData, buff, len);
httpdSend(connData, buff, len); return HTTPD_CGI_DONE;
return HTTPD_CGI_DONE;
} }
// Cgi to return various Wifi information // Cgi to return various Wifi information
int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) { 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, "{"); os_strcpy(buff, "{");
printWifiInfo(buff+1); printWifiInfo(buff+1);
os_strcat(buff, "}"); os_strcat(buff, "}");
jsonHeader(connData, 200); jsonHeader(connData, 200);
httpdSend(connData, buff, -1); httpdSend(connData, buff, -1);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
// Init the wireless, which consists of setting a timer if we expect to connect to an AP // 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. // so we can revert to STA+AP mode if we can't connect.
void ICACHE_FLASH_ATTR wifiInit() { void ICACHE_FLASH_ATTR wifiInit() {
wifi_set_phy_mode(2); wifi_set_phy_mode(2);
#ifdef WIFI_DBG int x = wifi_get_opmode() & 0x3;
int x = wifi_get_opmode() & 0x3; os_printf("Wifi init, mode=%s\n", wifiMode[x]);
os_printf("Wifi init, mode=%s\n", wifiMode[x]); configWifiIP();
#endif
configWifiIP(); wifi_set_event_handler_cb(wifiHandleEventCb);
// check on the wifi in a few seconds to see whether we need to switch mode
wifi_set_event_handler_cb(wifiHandleEventCb); os_timer_disarm(&resetTimer);
// check on the wifi in a few seconds to see whether we need to switch mode os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_disarm(&resetTimer); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
} }

@ -5,26 +5,27 @@
#include <osapi.h> #include <osapi.h>
#include "config.h" #include "config.h"
#include "espfs.h" #include "espfs.h"
#include "crc16.h"
// hack: this from LwIP
extern uint16_t inet_chksum(void *dataptr, uint16_t len);
FlashConfig flashConfig; FlashConfig flashConfig;
FlashConfig flashDefault = { FlashConfig flashDefault = {
33, 0, 0, 33, 0, 0,
MCU_RESET_PIN, MCU_ISP_PIN, LED_CONN_PIN, LED_SERIAL_PIN, MCU_RESET_PIN, MCU_ISP_PIN, LED_CONN_PIN, LED_SERIAL_PIN,
115200, 115200,
"esp-link\0 ", // hostname "esp-link\0", // hostname
0, 0x00ffffff, 0, // static ip, netmask, gateway 0, 0x00ffffff, 0, // static ip, netmask, gateway
0, // log mode 0, // log mode
0, // swap uart (don't by default) 0, // swap uart (don't by default)
1, 0, // tcp_enable, rssi_enable 1, 0, // tcp_enable, rssi_enable
"\0", // api_key "\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 { typedef union {
FlashConfig fc; FlashConfig fc;
uint8_t block[128]; uint8_t block[1024];
} FlashFull; } FlashFull;
// magic number to recognize thet these are our flash settings as opposed to some random stuff // 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; ff.fc.crc = 0;
//os_printf("cksum of: "); //os_printf("cksum of: ");
//memDump(&ff, sizeof(ff)); //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); //os_printf("cksum is %04x\n", ff.fc.crc);
// write primary with incorrect seq // write primary with incorrect seq
ff.fc.seq = 0xffffffff; ff.fc.seq = 0xffffffff;
@ -123,12 +124,16 @@ static int ICACHE_FLASH_ATTR selectPrimary(FlashFull *ff0, FlashFull *ff1) {
// check CRC of ff0 // check CRC of ff0
uint16_t crc = ff0->fc.crc; uint16_t crc = ff0->fc.crc;
ff0->fc.crc = 0; 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 // check CRC of ff1
crc = ff1->fc.crc; crc = ff1->fc.crc;
ff1->fc.crc = 0; 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 // decided which we like better
if (ff0_crc_ok) if (ff0_crc_ok)

@ -16,6 +16,11 @@ typedef struct {
uint8_t swap_uart; // swap uart0 to gpio 13&15 uint8_t swap_uart; // swap uart0 to gpio 13&15
uint8_t tcp_enable, rssi_enable; // TCP client settings uint8_t tcp_enable, rssi_enable; // TCP client settings
char api_key[48]; // RSSI submission API key (Grovestreams for now) 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; } FlashConfig;
extern FlashConfig flashConfig; extern FlashConfig flashConfig;

@ -17,6 +17,7 @@
#include "cgiwifi.h" #include "cgiwifi.h"
#include "cgipins.h" #include "cgipins.h"
#include "cgitcp.h" #include "cgitcp.h"
#include "cgimqtt.h"
#include "cgiflash.h" #include "cgiflash.h"
#include "auth.h" #include "auth.h"
#include "espfs.h" #include "espfs.h"
@ -29,7 +30,7 @@
#include "log.h" #include "log.h"
#include <gpio.h> #include <gpio.h>
//#define SHOW_HEAP_USE #define SHOW_HEAP_USE
//Function that tells the authentication system what users/passwords live on the system. //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 //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 }, { "/wifi/special", cgiWiFiSpecial, NULL },
{ "/pins", cgiPins, NULL }, { "/pins", cgiPins, NULL },
{ "/tcpclient", cgiTcp, NULL }, { "/tcpclient", cgiTcp, NULL },
{ "/mqtt", cgiMqtt, NULL },
{ "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem { "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem
{ NULL, NULL, NULL } { NULL, NULL, NULL }
@ -120,16 +122,22 @@ static char *rst_codes[] = {
# define VERS_STR(V) VERS_STR_STR(V) # define VERS_STR(V) VERS_STR_STR(V)
char* esp_link_version = VERS_STR(VERSION); 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) { 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 // get the flash config so we know how to init things
//configWipe(); // uncomment to reset the config for testing purposes //configWipe(); // uncomment to reset the config for testing purposes
bool restoreOk = configRestore(); bool restoreOk = configRestore();
// init gpio pin registers // init gpio pin registers
gpio_init(); gpio_init();
gpio_output_set(0, 0, 0, (1<<15)); // some people tie it GND, gotta ensure it's disabled
// init UART // init UART
uart_init(flashConfig.baud_rate, 115200); // uart_init(flashConfig.baud_rate, 115200);
logInit(); // must come after init of uart // logInit(); // must come after init of uart
// say hello (leave some time to cause break in TX after boot loader's msg // say hello (leave some time to cause break in TX after boot loader's msg
os_delay_us(10000L); os_delay_us(10000L);
os_printf("\n\n** %s\n", esp_link_version); os_printf("\n\n** %s\n", esp_link_version);
@ -163,6 +171,5 @@ void user_init(void) {
os_printf("** esp-link ready\n"); os_printf("** esp-link ready\n");
// call user_main init app_init();
init();
} }

@ -14,56 +14,56 @@ static ETSTimer ledTimer;
static void ICACHE_FLASH_ATTR setLed(int on) { static void ICACHE_FLASH_ATTR setLed(int on) {
int8_t pin = flashConfig.conn_led_pin; int8_t pin = flashConfig.conn_led_pin;
if (pin < 0) return; // disabled if (pin < 0) return; // disabled
// LED is active-low // LED is active-low
if (on) { if (on) {
gpio_output_set(0, (1<<pin), (1<<pin), 0); gpio_output_set(0, (1<<pin), (1<<pin), 0);
} else { } else {
gpio_output_set((1<<pin), 0, (1<<pin), 0); gpio_output_set((1<<pin), 0, (1<<pin), 0);
} }
} }
static uint8_t ledState = 0; static uint8_t ledState = 0;
// Timer callback to update the LED // Timer callback to update the LED
static void ICACHE_FLASH_ATTR ledTimerCb(void *v) { static void ICACHE_FLASH_ATTR ledTimerCb(void *v) {
int time = 1000; int time = 1000;
if (wifiState == wifiGotIP) { if (wifiState == wifiGotIP) {
// connected, all is good, solid light with a short dark blip every 3 seconds // connected, all is good, solid light with a short dark blip every 3 seconds
ledState = 1-ledState; ledState = 1-ledState;
time = ledState ? 2900 : 100; time = ledState ? 2900 : 100;
} else if (wifiState == wifiIsConnected) { } else if (wifiState == wifiIsConnected) {
// waiting for DHCP, go on/off every second // waiting for DHCP, go on/off every second
ledState = 1 - ledState; ledState = 1 - ledState;
time = 1000; time = 1000;
} else { } else {
// not connected // not connected
switch (wifi_get_opmode()) { switch (wifi_get_opmode()) {
case 1: // STA case 1: // STA
ledState = 0; ledState = 0;
break; break;
case 2: // AP case 2: // AP
ledState = 1-ledState; ledState = 1-ledState;
time = ledState ? 50 : 1950; time = ledState ? 50 : 1950;
break; break;
case 3: // STA+AP case 3: // STA+AP
ledState = 1-ledState; ledState = 1-ledState;
time = ledState ? 50 : 950; time = ledState ? 50 : 950;
break; break;
} }
} }
setLed(ledState); setLed(ledState);
os_timer_arm(&ledTimer, time, 0); os_timer_arm(&ledTimer, time, 0);
} }
// change the wifi state indication // change the wifi state indication
void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) {
wifiState = state; wifiState = state;
// schedule an update (don't want to run into concurrency issues) // schedule an update (don't want to run into concurrency issues)
os_timer_disarm(&ledTimer); os_timer_disarm(&ledTimer);
os_timer_setfn(&ledTimer, ledTimerCb, NULL); os_timer_setfn(&ledTimer, ledTimerCb, NULL);
os_timer_arm(&ledTimer, 500, 0); os_timer_arm(&ledTimer, 500, 0);
} }
//===== RSSI Status update sent to GroveStreams //===== RSSI Status update sent to GroveStreams
@ -76,59 +76,59 @@ static ETSTimer rssiTimer;
// Timer callback to send an RSSI update to a monitoring system // Timer callback to send an RSSI update to a monitoring system
static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) {
if (!flashConfig.rssi_enable || !flashConfig.tcp_enable || flashConfig.api_key[0]==0) if (!flashConfig.rssi_enable || !flashConfig.tcp_enable || flashConfig.api_key[0]==0)
return; return;
sint8 rssi = wifi_station_get_rssi(); sint8 rssi = wifi_station_get_rssi();
os_printf("timer rssi=%d\n", rssi); os_printf("timer rssi=%d\n", rssi);
if (rssi >= 0) return; // not connected or other error if (rssi >= 0) return; // not connected or other error
// compose TCP command // compose TCP command
uint8_t chan = MAX_TCP_CHAN-1; uint8_t chan = MAX_TCP_CHAN-1;
tcpClientCommand(chan, 'T', "grovestreams.com:80"); tcpClientCommand(chan, 'T', "grovestreams.com:80");
// compose http header // compose http header
char buf[1024]; char buf[1024];
int hdrLen = os_sprintf(buf, int hdrLen = os_sprintf(buf,
"PUT /api/feed?api_key=%s HTTP/1.0\r\n" "PUT /api/feed?api_key=%s HTTP/1.0\r\n"
"Content-Type: application/json\r\n" "Content-Type: application/json\r\n"
"Content-Length: XXXXX\r\n\r\n", "Content-Length: XXXXX\r\n\r\n",
flashConfig.api_key); flashConfig.api_key);
// http body // http body
int dataLen = os_sprintf(buf+hdrLen, int dataLen = os_sprintf(buf+hdrLen,
"[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r",
flashConfig.hostname, GS_STREAM, rssi); flashConfig.hostname, GS_STREAM, rssi);
buf[hdrLen+dataLen++] = 0; buf[hdrLen+dataLen++] = 0;
buf[hdrLen+dataLen++] = '\n'; buf[hdrLen+dataLen++] = '\n';
// hackish way to fill in the content-length // hackish way to fill in the content-length
os_sprintf(buf+hdrLen-9, "%5d", dataLen); os_sprintf(buf+hdrLen-9, "%5d", dataLen);
buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!) buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!)
// send the request off and forget about it... // send the request off and forget about it...
for (short i=0; i<hdrLen+dataLen; i++) { for (short i=0; i<hdrLen+dataLen; i++) {
tcpClientSendChar(chan, buf[i]); tcpClientSendChar(chan, buf[i]);
} }
tcpClientSendPush(chan); tcpClientSendPush(chan);
} }
//===== Init status stuff //===== Init status stuff
void ICACHE_FLASH_ATTR statusInit(void) { void ICACHE_FLASH_ATTR statusInit(void) {
if (flashConfig.conn_led_pin >= 0) { if (flashConfig.conn_led_pin >= 0) {
makeGpio(flashConfig.conn_led_pin); makeGpio(flashConfig.conn_led_pin);
setLed(1); setLed(1);
} }
os_printf("CONN led=%d\n", flashConfig.conn_led_pin); os_printf("CONN led=%d\n", flashConfig.conn_led_pin);
os_timer_disarm(&ledTimer); os_timer_disarm(&ledTimer);
os_timer_setfn(&ledTimer, ledTimerCb, NULL); os_timer_setfn(&ledTimer, ledTimerCb, NULL);
os_timer_arm(&ledTimer, 2000, 0); os_timer_arm(&ledTimer, 2000, 0);
os_timer_disarm(&rssiTimer); os_timer_disarm(&rssiTimer);
os_timer_setfn(&rssiTimer, rssiTimerCb, NULL); os_timer_setfn(&rssiTimer, rssiTimerCb, NULL);
os_timer_arm(&rssiTimer, RSSI_INTERVAL, 1); // recurring timer os_timer_arm(&rssiTimer, RSSI_INTERVAL, 1); // recurring timer
} }

@ -1 +1,46 @@
<!DOCTYPE html><script src='http://linux-ws/esplink/console.js'></script> <div id="main">
<div class="header">
<h1>Microcontroller Console</h1>
</div>
<div class="content">
<p>The Microcontroller console shows the last 1024 characters
received from UART0, to which a microcontroller is typically attached.
The UART is configured for 8 bits, no parity, 1 stop bit (8N1).</p>
<p>
<a id="reset-button" class="pure-button button-primary" href="#">Reset µC</a>
&nbsp;Baud:
<span id="baud-btns"></span>
</p>
<pre class="console" id="console"></pre>
</div>
</div>
</div>
<script type="text/javascript">console_url = "/console/text"</script>
<script src="console.js"></script>
<script type="text/javascript">
var rates = [9600, 57600, 115200, 250000];
onLoad(function() {
fetchText(100, true);
$("#reset-button").addEventListener("click", function(e) {
e.preventDefault();
var co = $("#console");
co.innerHTML = "";
ajaxSpin('POST', "/console/reset",
function(resp) { showNotification("uC reset"); co.textEnd = 0; },
function(s, st) { showWarning("Error resetting uC"); }
);
});
rates.forEach(function(r) { baudButton(r); });
ajaxJson('GET', "/console/baud",
function(data) { showRate(data.rate); },
function(s, st) { showNotification(st); }
);
});
</script>
</body></html>

@ -0,0 +1,99 @@
<div id="main">
<div class="header">
<h1>REST &amp; MQTT</h1>
</div>
<div class="content">
<div class="pure-g">
<div class="pure-u-1"><div class="card">
<p>The REST &amp; MQTT support uses the SLIP protocol over the serial port to enable
the attached microcontroller to initiate outbound connections.
The REST support lets the uC initiate simple HTTP requests while the MQTT support
lets it communicate with an MQTT server bidirectionally at QoS 0 thru 2.</p>
<p>The MQTT support is in the form of a built-in client that connects to a server
using parameters set below and stored in esp-link's flash settings. This allows
esp-link to take care of connection parameters and disconnect/reconnect operations.</p>
<p>The MQTT client also supports sending periodic status messages about esp-link itself,
including Wifi RSSI, and free heap memory.</p>
<div class="form-horizontal">
<input type="checkbox" name="slip-enable"/>
<label>Enable SLIP on serial port</label>
</div>
</div></div>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-2">
<div class="card">
<h1>MQTT
<div id="mqtt-spinner" class="spinner spinner-small"></div>
</h1>
<form action="#" id="mqtt-form" class="pure-form" hidden>
<div class="form-horizontal">
<input type="checkbox" name="mqtt-enable"/>
<label>Enable MQTT client</label>
</div>
<div class="form-horizontal">
<label>MQTT client state: </label>
<b id="mqtt-state"></b>
</div>
<br>
<legend>MQTT server settings</legend>
<div class="pure-form-stacked">
<label>Server hostname/ip</label>
<input type="text" name="mqtt-host"/>
<label>Server port/ip</label>
<input type="text" name="mqtt-port"/>
<label>Client ID</label>
<input type="text" name="mqtt-client-id"/>
<label>Username</label>
<input type="text" name="mqtt-username"/>
<label>Password</label>
<input type="password" name="mqtt-password"/>
</div>
<button id="mqtt-button" type="submit" class="pure-button button-primary">
Update server settings!
</button>
</form>
</div>
</div>
<div class="pure-u-1 pure-u-md-1-2">
<div class="card">
<h1>Status reporting
<div id="mqtt-status-spinner" class="spinner spinner-small"></div>
</h1>
<form action="#" id="mqtt-status-form" class="pure-form" hidden>
<div class="form-horizontal">
<input type="checkbox" name="mqtt-status-enable"/>
<label>Enable status reporting via MQTT</label>
</div>
<br>
<legend>Status reporting settings</legend>
<div class="pure-form-stacked">
<label>Topic prefix</label>
<input type="text" name="mqtt-status-topic"/>
Suffixes: rssi, heap-free, ...
</div>
<button id="mqtt-status-button" type="submit" class="pure-button button-primary">
Update status settings!
</button>
</form>
</div>
<div class="card">
<h1>REST</h1>
<p>REST requests are enabled as soon as SLIP is enabled.
There are no REST-specific settings.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
onLoad(function() {
fetchMqtt();
bnd($("#mqtt-form"), "submit", changeMqtt);
bnd($("#mqtt-status-form"), "submit", changeMqttStatus);
});
</script>
</body></html>

@ -0,0 +1,384 @@
/* All fonts */
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;
}
a:visited, a:link {
color: #009;
}
a:hover {
color: #00c;
}
.card {
background-color: #eee;
padding: 1em;
margin: 0.5em;
-moz-border-radius: 0.5em;
-webkit-border-radius: 0.5em;
border-radius: 0.5em;
border: 0px solid #000000;
}
/* wifi AP selection form */
#aps label div {
display: inline-block;
margin: 0em 0.2em;
}
fieldset.radios {
border: none;
padding-left: 0px;
}
fieldset fields {
clear: both;
}
#pin-mux input {
display: block;
margin-top: 0.4em;
float: left;
}
#pin-mux label {
display: block;
margin: 0em 0.2em 0em 1em;
width: 90%;
}
.pure-table td, .pure-table th {
padding: 0.5em 0.5em;
}
/* make images size-up */
.xx-pure-img-responsive {
max-width: 100%;
height: auto;
}
/* Add transition to containers so they can push in and out */
#layout, #menu, .menu-link {
-webkit-transition: all 0.2s ease-out;
-moz-transition: all 0.2s ease-out;
-ms-transition: all 0.2s ease-out;
-o-transition: all 0.2s ease-out;
transition: all 0.2s ease-out;
}
/* This is the parent `<div>` that contains the menu and the content area */
#layout {
position: relative;
padding-left: 0;
}
#layout.active #menu {
left: 150px;
width: 150px;
}
#layout.active .menu-link {
left: 150px;
}
div.tt {
font-family: monospace;
font-size: 120%;
color: #390;
background-color: #ddd;
padding: 2px;
margin: 2px 0;
line-height: 100%;
}
/* The content `<div>` */
.content {
margin: 0 auto;
padding: 0 2em;
max-width: 800px;
margin-bottom: 50px;
line-height: 1.6em;
}
.header {
margin: 0;
color: #333;
text-align: center;
padding: 2.5em 2em 0;
border-bottom: 1px solid #eee;
background-color: #fc0;
}
.header h1 {
margin: 0.2em 0;
font-size: 3em;
font-weight: 300;
}
.header h1 .esp {
font-size: 1.25em;
}
.jl {
font: normal 800 1.5em sans-serif;
position: relative;
bottom: 19px;
color: #9d1414;
margin-left: 3px;
}
.content-subhead {
margin: 50px 0 20px 0;
font-weight: 300;
color: #888;
}
form button {
margin-top: 0.5em;
}
.button-primary {
background-color: #99f;
}
.button-selected {
background-color: #fc6;
}
/* Text console */
pre.console {
background-color: #663300;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
border: 0px solid #000000;
color: #66ff66;
padding: 5px;
}
pre.console a {
color: #66ff66;
}
/* log page */
.dbg-btn, #refresh-button {
vertical-align: baseline;
}
.lock-icon {
background-image: url("/wifi/icons.png");
background-color: transparent;
width: 32px;
height: 32px;
display: inline-block;
}
#menu {
margin-left: -150px;
width: 150px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: 1000;
background: #191818;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
#menu a {
color: #999;
border: none;
padding: 0.6em 0 0.6em 0.6em;
}
#menu .pure-menu, #menu .pure-menu ul {
border: none;
background: transparent;
}
#menu .pure-menu ul, #menu .pure-menu .menu-item-divided {
border-top: 1px solid #333;
}
#menu .pure-menu li a:hover, #menu .pure-menu li a:focus {
background: #333;
}
#menu .pure-menu-selected, #menu .pure-menu-heading {
background: #9d1414;
}
#menu .pure-menu-selected a {
color: #fff;
}
#menu .pure-menu-heading {
font-size: 110%;
color: #fff;
margin: 0;
text-transform: none;
}
#menu .pure-menu-heading img {
vertical-align: middle;
top: -1px;
position: relative;
}
#menu .pure-menu-item {
height:2em;
}
/* -- Dynamic Button For Responsive Menu -------------------------------------*/
.menu-link {
position: fixed;
display: block;
top: 0;
left: 0;
background: #000;
background: rgba(0,0,0,0.7);
font-size: 10px;
z-index: 10;
width: 2em;
height: auto;
padding: 2.1em 1.6em;
}
.menu-link:hover, .menu-link:focus {
background: #000;
}
.menu-link span {
position: relative;
display: block;
}
.menu-link span, .menu-link span:before, .menu-link span:after {
background-color: #fff;
width: 100%;
height: 0.2em;
}
.menu-link span:before, .menu-link span:after {
position: absolute;
margin-top: -0.6em;
content: " ";
}
.menu-link span:after {
margin-top: 0.6em;
}
/* -- Responsive Styles (Media Queries) ------------------------------------- */
@media (min-width: 56em) {
.header, .content {
padding-left: 2em;
padding-right: 2em;
}
#layout {
padding-left: 150px;
left: 0;
}
#menu {
left: 150px;
}
.menu-link {
position: fixed;
left: 150px;
display: none;
}
#layout.active .menu-link {
left: 150px;
}
}
@media (max-width: 56em) {
#layout.active {
position: relative;
left: 150px;
}
}
/*===== spinners and notification messages */
#messages {
position: absolute;
left: 25%;
width: 50%;
top: 10;
z-index: 200;
font-size: 110%;
text-align: center;
}
#warning {
background-color: #933;
color: #fcc;
padding: 0.1em 0.4em;
}
#notification {
background-color: #693;
color: #cfc;
padding: 0.1em 0.4em;
}
#spinner {
position: absolute;
right: 10%;
top: 20;
z-index: 1000;
}
.spinner {
height: 50px;
width: 50px;
-webkit-animation: rotation 1s infinite linear;
-moz-animation: rotation 1s infinite linear;
-o-animation: rotation 1s infinite linear;
animation: rotation 1s infinite linear;
border-left: 10px solid rgba(204, 51, 0, 0.15);
border-right: 10px solid rgba(204, 51, 0, 0.15);
border-bottom: 10px solid rgba(204, 51, 0, 0.15);
border-top: 10px solid rgba(204, 51, 0, 0.8);
border-radius: 100%;
}
.spinner-small {
display: inline-block;
height: 1em;
width: 1em;
border-width: 4px;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
}
}
@-moz-keyframes rotation {
from {
-moz-transform: rotate(0deg);
}
to {
-moz-transform: rotate(359deg);
}
}
@-o-keyframes rotation {
from {
-o-transform: rotate(0deg);
}
to {
-o-transform: rotate(359deg);
}
}
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}

@ -0,0 +1,473 @@
//===== Collection of small utilities
/*
* Bind/Unbind events
*
* Usage:
* var el = document.getElementyById('#container');
* bnd(el, 'click', function() {
* console.log('clicked');
* });
*/
var bnd = function(
d, // a DOM element
e, // an event name such as "click"
f // a handler function
){
d.addEventListener(e, f, false);
}
/*
* Create DOM element
*
* Usage:
* var el = m('<h1>Hello</h1>');
* document.body.appendChild(el);
*
* Copyright (C) 2011 Jed Schmidt <http://jed.is> - WTFPL
* More: https://gist.github.com/966233
*/
var m = function(
a, // an HTML string
b, // placeholder
c // placeholder
){
b = document; // get the document,
c = b.createElement("p"); // create a container element,
c.innerHTML = a; // write the HTML to it, and
a = b.createDocumentFragment(); // create a fragment.
while ( // while
b = c.firstChild // the container element has a first child
) a.appendChild(b); // append the child to the fragment,
return a // and then return the fragment.
}
/*
* DOM selector
*
* Usage:
* $('div');
* $('#name');
* $('.name');
*
* Copyright (C) 2011 Jed Schmidt <http://jed.is> - WTFPL
* More: https://gist.github.com/991057
*/
var $ = function(
a, // take a simple selector like "name", "#name", or ".name", and
b // an optional context, and
){
a = a.match(/^(\W)?(.*)/); // split the selector into name and symbol.
return( // return an element or list, from within the scope of
b // the passed context
|| document // or document,
)[
"getElement" + ( // obtained by the appropriate method calculated by
a[1]
? a[1] == "#"
? "ById" // the node by ID,
: "sByClassName" // the nodes by class name, or
: "sByTagName" // the nodes by tag name,
)
](
a[2] // called with the name.
)
}
/*
* Get cross browser xhr object
*
* Copyright (C) 2011 Jed Schmidt <http://jed.is>
* More: https://gist.github.com/993585
*/
var j = function(
a // cursor placeholder
){
for( // for all a
a=0; // from 0
a<4; // to 4,
a++ // incrementing
) try { // try
return a // returning
? new ActiveXObject( // a new ActiveXObject
[ // reflecting
, // (elided)
"Msxml2", // the various
"Msxml3", // working
"Microsoft" // options
][a] + // for Microsoft implementations, and
".XMLHTTP" // the appropriate suffix,
) // but make sure to
: new XMLHttpRequest // try the w3c standard first, and
}
catch(e){} // ignore when it fails.
}
// createElement short-hand
e = function(a) { return document.createElement(a); }
// chain onload handlers
function onLoad(f) {
var old = window.onload;
if (typeof old != 'function') {
window.onload = f;
} else {
window.onload = function() {
old();
f();
}
}
}
//===== helpers to add/remove/toggle HTML element classes
function addClass(el, cl) {
el.className += ' ' + cl;
}
function removeClass(el, cl) {
var cls = el.className.split(/\s+/),
l = cls.length;
for (var i=0; i<l; i++) {
if (cls[i] === cl) cls.splice(i, 1);
}
el.className = cls.join(' ');
return cls.length != l
}
function toggleClass(el, cl) {
if (!removeClass(el, cl)) addClass(el, cl);
}
//===== AJAX
function ajaxReq(method, url, ok_cb, err_cb) {
var xhr = j();
xhr.open(method, url, true);
var timeout = setTimeout(function() {
xhr.abort();
console.log("XHR abort:", method, url);
xhr.status = 599;
xhr.responseText = "request time-out";
}, 9000);
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) { return; }
clearTimeout(timeout);
if (xhr.status >= 200 && xhr.status < 300) {
console.log("XHR done:", method, url, "->", xhr.status);
ok_cb(xhr.responseText);
} else {
console.log("XHR ERR :", method, url, "->", xhr.status, xhr.responseText, xhr);
err_cb(xhr.status, xhr.responseText);
}
}
console.log("XHR send:", method, url);
try {
xhr.send();
} catch(err) {
console.log("XHR EXC :", method, url, "->", err);
err_cb(599, err);
}
}
function dispatchJson(resp, ok_cb, err_cb) {
var j;
try { j = JSON.parse(resp); }
catch(err) {
console.log("JSON parse error: " + err + ". In: " + resp);
err_cb(500, "JSON parse error: " + err);
return;
}
ok_cb(j);
}
function ajaxJson(method, url, ok_cb, err_cb) {
ajaxReq(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb);
}
function ajaxSpin(method, url, ok_cb, err_cb) {
$("#spinner").removeAttribute('hidden');
ajaxReq(method, url, function(resp) {
$("#spinner").setAttribute('hidden', '');
ok_cb(resp);
}, function(status, statusText) {
$("#spinner").setAttribute('hidden', '');
//showWarning("Error: " + statusText);
err_cb(status, statusText);
});
}
function ajaxJsonSpin(method, url, ok_cb, err_cb) {
ajaxSpin(method, url, function(resp) { dispatchJson(resp, ok_cb, err_cb); }, err_cb);
}
//===== main menu, header spinner and notification boxes
onLoad(function() {
var l = $("#layout");
var o = l.childNodes[0];
// spinner
l.insertBefore(m('<div id="spinner" class="spinner" hidden></div>'), o);
// notification boxes
l.insertBefore(m(
'<div id="messages"><div id="warning" hidden></div><div id="notification" hidden></div></div>'), o);
// menu hamburger button
l.insertBefore(m('<a href="#menu" id="menuLink" class="menu-link"><span></span></a>'), o);
// menu left-pane
var mm = m(
'<div id="menu">\
<div class="pure-menu">\
<a class="pure-menu-heading" href="https://github.com/jeelabs/esp-link">\
<img src="/favicon.ico" height="32">&nbsp;esp-link</a>\
<ul id="menu-list" class="pure-menu-list"></ul>\
</div>\
</div>\
');
l.insertBefore(mm, o);
// make hamburger button pull out menu
var ml = $('#menuLink'), mm = $('#menu');
bnd(ml, 'click', function (e) {
console.log("hamburger time");
var active = 'active';
e.preventDefault();
toggleClass(l, active);
toggleClass(mm, active);
toggleClass(ml, active);
});
// populate menu via ajax call
var getMenu = function() {
ajaxJson("GET", "/menu", function(data) {
var html = "", path = window.location.pathname;
for (var i=0; i<data.menu.length; i+=2) {
var href = data.menu[i+1];
html = html.concat(" <li class=\"pure-menu-item" +
(path === href ? " pure-menu-selected" : "") + "\">" +
"<a href=\"" + href + "\" class=\"pure-menu-link\">" +
data.menu[i] + "</a></li>");
}
$("#menu-list").innerHTML = html;
v = $("#version");
if (v != null) { v.innerHTML = data.version; }
}, function() { setTimeout(getMenu, 1000); });
};
getMenu();
});
//===== Wifi info
function showWifiInfo(data) {
Object.keys(data).forEach(function(v) {
el = $("#wifi-" + v);
if (el != null) {
if (el.nodeName === "INPUT") el.value = data[v];
else el.innerHTML = data[v];
}
});
var dhcp = $('#dhcp-r'+data.dhcp);
if (dhcp) dhcp.click();
$("#wifi-spinner").setAttribute("hidden", "");
$("#wifi-table").removeAttribute("hidden");
currAp = data.ssid;
}
function getWifiInfo() {
ajaxJson('GET', "/wifi/info", showWifiInfo,
function(s, st) { window.setTimeout(getWifiInfo, 1000); });
}
//===== Notifications
function showWarning(text) {
var el = $("#warning");
el.innerHTML = text;
el.removeAttribute('hidden');
}
function hideWarning() {
el = $("#warning").setAttribute('hidden', '');
}
var notifTimeout = null;
function showNotification(text) {
var el = $("#notification");
el.innerHTML = text;
el.removeAttribute('hidden');
if (notifTimeout != null) clearTimeout(notifTimeout);
notifTimout = setTimeout(function() {
el.setAttribute('hidden', '');
notifTimout = null;
}, 4000);
}
//===== GPIO Pin mux card
var currPin;
// pin={reset:12, isp:13, LED_conn:0, LED_ser:2}
function createInputForPin(pin) {
var input = document.createElement("input");
input.type = "radio";
input.name = "pins";
input.data = pin.name;
input.className = "pin-input";
input.value= pin.value;
input.id = "opt-" + pin.value;
if (currPin == pin.name) input.checked = "1";
var descr = m('<label for="opt-'+pin.value+'"><b>'+pin.name+":</b>"+pin.descr+"</label>");
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<inputs.length; i++) {
inputs[i].onclick = function() { setPins(this.value, this.data) };
};
}
function fetchPins() {
ajaxJson("GET", "/pins", displayPins, function() {
window.setTimeout(fetchPins, 1000);
});
}
function setPins(v, name) {
ajaxSpin("POST", "/pins?map="+v, function() {
showNotification("Pin assignment changed to " + name);
}, function() {
showNotification("Pin assignment change failed");
window.setTimeout(fetchPins, 100);
});
}
//===== TCP client card
function tcpEn(){return document.querySelector('input[name="tcp_enable"]')}
function rssiEn(){return document.querySelector('input[name="rssi_enable"]')}
function apiKey(){return document.querySelector('input[name="api_key"]')}
function changeTcpClient(e) {
e.preventDefault();
var url = "tcpclient";
url += "?tcp_enable=" + tcpEn().checked;
url += "&rssi_enable=" + rssiEn().checked;
url += "&api_key=" + encodeURIComponent(apiKey().value);
hideWarning();
var cb = $("#tcp-button");
addClass(cb, 'pure-button-disabled');
ajaxSpin("POST", url, function(resp) {
removeClass(cb, 'pure-button-disabled');
fetchTcpClient();
}, function(s, st) {
showWarning("Error: "+st);
removeClass(cb, 'pure-button-disabled');
fetchTcpClient();
});
}
function displayTcpClient(resp) {
tcpEn().checked = resp.tcp_enable > 0;
rssiEn().checked = resp.rssi_enable > 0;
apiKey().value = resp.api_key;
}
function fetchTcpClient() {
ajaxJson("GET", "/tcpclient", displayTcpClient, function() {
window.setTimeout(fetchTcpClient, 1000);
});
}
//===== MQTT cards
function changeMqtt(e) {
e.preventDefault();
var url = "mqtt?1=1";
var i, inputs = document.querySelectorAll('#mqtt-form input');
for (i=0; i<inputs.length; i++) {
if (inputs[i].type != "checkbox")
url += "&" + inputs[i].name + "=" + inputs[i].value;
};
hideWarning();
var cb = $("#mqtt-button");
addClass(cb, 'pure-button-disabled');
ajaxSpin("POST", url, function(resp) {
showNotification("MQTT updated");
removeClass(cb, 'pure-button-disabled');
}, function(s, st) {
showWarning("Error: "+st);
removeClass(cb, 'pure-button-disabled');
window.setTimeout(fetchMqtt, 100);
});
}
function displayMqtt(data) {
Object.keys(data).forEach(function(v) {
el = $("#" + v);
if (el != null) {
if (el.nodeName === "INPUT") el.value = data[v];
else el.innerHTML = data[v];
return;
}
el = document.querySelector('input[name="' + v + '"]');
if (el != null) {
if (el.type == "checkbox") el.checked = data[v] > 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<inputs.length; i++) {
if (inputs[i].type == "checkbox")
inputs[i].onclick = function() { console.log(this); setMqtt(this.name, this.checked) };
}
}
function fetchMqtt() {
ajaxJson("GET", "/mqtt", displayMqtt, function() {
window.setTimeout(fetchMqtt, 1000);
});
}
function changeMqttStatus(e) {
e.preventDefault();
var v = document.querySelector('input[name="mqtt-status-topic"]').value;
ajaxSpin("POST", "/mqtt?mqtt-status-topic=" + v, function() {
showNotification("MQTT status settings updated");
}, function(s, st) {
showWarning("Error: "+st);
window.setTimeout(fetchMqtt, 100);
});
}
function setMqtt(name, v) {
ajaxSpin("POST", "/mqtt?" + name + "=" + (v ? 1 : 0), function() {
var n = name.replace("-enable", "");
showNotification(n + " is now " + (v ? "enabled" : "disabled"));
}, function() {
showWarning("Enable/disable failed");
window.setTimeout(fetchMqtt, 100);
});
}

@ -92,9 +92,7 @@ static void debugConn(void *arg, char *what) {
os_sprintf(connStr, "%d.%d.%d.%d:%d", os_sprintf(connStr, "%d.%d.%d.%d:%d",
tcp->remote_ip[0], tcp->remote_ip[1], tcp->remote_ip[2], tcp->remote_ip[3], tcp->remote_ip[0], tcp->remote_ip[1], tcp->remote_ip[2], tcp->remote_ip[3],
tcp->remote_port); tcp->remote_port);
#ifdef HTTPD_DBG //os_printf("%s %s\n", connStr, what);
os_printf("%s %s\n", connStr, what);
#endif
} }
//Looks up the connData info for a specific esp connection //Looks up the connData info for a specific esp connection
@ -104,7 +102,7 @@ static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) {
if (connData[i].remote_port == espconn->proto.tcp->remote_port && if (connData[i].remote_port == espconn->proto.tcp->remote_port &&
os_memcmp(connData[i].remote_ip, espconn->proto.tcp->remote_ip, 4) == 0) os_memcmp(connData[i].remote_ip, espconn->proto.tcp->remote_ip, 4) == 0)
{ {
#ifdef HTTPD_DBG #if 0
os_printf("FindConn: 0x%p->0x%p", arg, &connData[i]); os_printf("FindConn: 0x%p->0x%p", arg, &connData[i]);
if (arg == connData[i].conn) os_printf("\n"); if (arg == connData[i].conn) os_printf("\n");
else os_printf(" *** was 0x%p\n", connData[i].conn); else os_printf(" *** was 0x%p\n", connData[i].conn);
@ -114,9 +112,7 @@ static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) {
} }
} }
//Shouldn't happen. //Shouldn't happen.
#ifdef HTTPD_DBG
os_printf("%s *** Unknown connection 0x%p\n", connStr, arg); os_printf("%s *** Unknown connection 0x%p\n", connStr, arg);
#endif
return NULL; return NULL;
} }
@ -134,10 +130,8 @@ static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) {
uint32 dt = conn->startTime; uint32 dt = conn->startTime;
if (dt > 0) dt = (system_get_time() - dt)/1000; if (dt > 0) dt = (system_get_time() - dt)/1000;
#ifdef HTTPD_DBG
os_printf("%s Closed, %ums, heap=%ld\n", connStr, dt, os_printf("%s Closed, %ums, heap=%ld\n", connStr, dt,
(unsigned long)system_get_free_heap_size()); (unsigned long)system_get_free_heap_size());
#endif
} }
//Stupid li'l helper function that returns the value of a hex char. //Stupid li'l helper function that returns the value of a hex char.
@ -186,24 +180,18 @@ int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLe
if (line==NULL) return 0; if (line==NULL) return 0;
p=line; p=line;
while(p!=NULL && *p!='\n' && *p!='\r' && *p!=0) { while(p!=NULL && *p!='\n' && *p!='\r' && *p!=0) {
#ifdef HTTPD_DBG //os_printf("findArg: %s\n", p);
os_printf("findArg: %s\n", p);
#endif
if (os_strncmp(p, arg, os_strlen(arg))==0 && p[strlen(arg)]=='=') { if (os_strncmp(p, arg, os_strlen(arg))==0 && p[strlen(arg)]=='=') {
p+=os_strlen(arg)+1; //move p to start of value p+=os_strlen(arg)+1; //move p to start of value
e=(char*)os_strstr(p, "&"); e=(char*)os_strstr(p, "&");
if (e==NULL) e=p+os_strlen(p); if (e==NULL) e=p+os_strlen(p);
#ifdef HTTPD_DBG //os_printf("findArg: val %s len %d\n", p, (e-p));
os_printf("findArg: val %s len %d\n", p, (e-p));
#endif
return httpdUrlDecode(p, (e-p), buff, buffLen); return httpdUrlDecode(p, (e-p), buff, buffLen);
} }
p=(char*)os_strstr(p, "&"); p=(char*)os_strstr(p, "&");
if (p!=NULL) p+=1; if (p!=NULL) p+=1;
} }
#ifdef HTTPD_DBG //os_printf("Finding %s in %s: Not found :/\n", arg, line);
os_printf("Finding %s in %s: Not found :/\n", arg, line);
#endif
return -1; //not found return -1; //not found
} }
@ -284,10 +272,8 @@ int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) {
int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len) { int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len) {
if (len<0) len=strlen(data); if (len<0) len=strlen(data);
if (conn->priv->sendBuffLen+len>MAX_SENDBUFF_LEN) { if (conn->priv->sendBuffLen+len>MAX_SENDBUFF_LEN) {
#ifdef HTTPD_DBG
os_printf("%s ERROR! httpdSend full (%d of %d)\n", os_printf("%s ERROR! httpdSend full (%d of %d)\n",
connStr, conn->priv->sendBuffLen, MAX_SENDBUFF_LEN); connStr, conn->priv->sendBuffLen, MAX_SENDBUFF_LEN);
#endif
return 0; return 0;
} }
os_memcpy(conn->priv->sendBuff+conn->priv->sendBuffLen, data, len); os_memcpy(conn->priv->sendBuff+conn->priv->sendBuffLen, data, len);
@ -300,9 +286,7 @@ static void ICACHE_FLASH_ATTR xmitSendBuff(HttpdConnData *conn) {
if (conn->priv->sendBuffLen!=0) { if (conn->priv->sendBuffLen!=0) {
sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen); sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen);
if (status != 0) { if (status != 0) {
#ifdef HTTPD_DBG
os_printf("%s ERROR! espconn_sent returned %d\n", connStr, status); os_printf("%s ERROR! espconn_sent returned %d\n", connStr, status);
#endif
} }
conn->priv->sendBuffLen=0; conn->priv->sendBuffLen=0;
} }
@ -320,9 +304,7 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
conn->priv->sendBuffLen=0; conn->priv->sendBuffLen=0;
if (conn->cgi==NULL) { //Marked for destruction? if (conn->cgi==NULL) { //Marked for destruction?
#ifdef HTTPD_DBG //os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn);
os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn);
#endif
espconn_disconnect(conn->conn); // we will get a disconnect callback espconn_disconnect(conn->conn); // we will get a disconnect callback
return; //No need to call xmitSendBuff. return; //No need to call xmitSendBuff.
} }
@ -332,9 +314,7 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
conn->cgi=NULL; //mark for destruction. conn->cgi=NULL; //mark for destruction.
} }
if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) {
#ifdef HTTPD_DBG
os_printf("%s ERROR! Bad CGI code %d\n", connStr, r); os_printf("%s ERROR! Bad CGI code %d\n", connStr, r);
#endif
conn->cgi=NULL; //mark for destruction. conn->cgi=NULL; //mark for destruction.
} }
xmitSendBuff(conn); xmitSendBuff(conn);
@ -350,9 +330,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
int r; int r;
int i=0; int i=0;
if (conn->url==NULL) { if (conn->url==NULL) {
#ifdef HTTPD_DBG
os_printf("%s WtF? url = NULL\n", connStr); os_printf("%s WtF? url = NULL\n", connStr);
#endif
return; //Shouldn't happen return; //Shouldn't happen
} }
//See if we can find a CGI that's happy to handle the request. //See if we can find a CGI that's happy to handle the request.
@ -366,9 +344,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
if (builtInUrls[i].url[os_strlen(builtInUrls[i].url)-1]=='*' && if (builtInUrls[i].url[os_strlen(builtInUrls[i].url)-1]=='*' &&
os_strncmp(builtInUrls[i].url, conn->url, os_strlen(builtInUrls[i].url)-1)==0) match=1; os_strncmp(builtInUrls[i].url, conn->url, os_strlen(builtInUrls[i].url)-1)==0) match=1;
if (match) { if (match) {
#ifdef HTTPD_DBG //os_printf("Is url index %d\n", i);
os_printf("Is url index %d\n", i);
#endif
conn->cgiData=NULL; conn->cgiData=NULL;
conn->cgi=builtInUrls[i].cgiCb; conn->cgi=builtInUrls[i].cgiCb;
conn->cgiArg=builtInUrls[i].cgiArg; conn->cgiArg=builtInUrls[i].cgiArg;
@ -379,9 +355,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
if (builtInUrls[i].url==NULL) { if (builtInUrls[i].url==NULL) {
//Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just //Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just
//generate a built-in 404 to handle this. //generate a built-in 404 to handle this.
#ifdef HTTPD_DBG
os_printf("%s %s not found. 404!\n", connStr, conn->url); os_printf("%s %s not found. 404!\n", connStr, conn->url);
#endif
httpdSend(conn, httpNotFoundHeader, -1); httpdSend(conn, httpNotFoundHeader, -1);
xmitSendBuff(conn); xmitSendBuff(conn);
conn->cgi=NULL; //mark for destruction conn->cgi=NULL; //mark for destruction
@ -437,18 +411,15 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
// Count number of open connections // Count number of open connections
int open = 0; int open = 0;
for (int j=0; j<MAX_CONN; j++) if (connData[j].conn != NULL) open++; for (int j=0; j<MAX_CONN; j++) if (connData[j].conn != NULL) open++;
#ifdef HTTPD_DBG
os_printf("%s %s %s (%d conn open)\n", connStr, os_printf("%s %s %s (%d conn open)\n", connStr,
conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, open); conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, open);
#endif
//Parse out the URL part before the GET parameters. //Parse out the URL part before the GET parameters.
conn->getArgs=(char*)os_strstr(conn->url, "?"); conn->getArgs=(char*)os_strstr(conn->url, "?");
if (conn->getArgs!=0) { if (conn->getArgs!=0) {
*conn->getArgs=0; *conn->getArgs=0;
conn->getArgs++; conn->getArgs++;
#ifdef HTTPD_DBG
os_printf("%s args = %s\n", connStr, conn->getArgs); os_printf("%s args = %s\n", connStr, conn->getArgs);
#endif
} else { } else {
conn->getArgs=NULL; conn->getArgs=NULL;
} }
@ -467,9 +438,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
} else { } else {
conn->post->buffSize = conn->post->len; conn->post->buffSize = conn->post->len;
} }
#ifdef HTTPD_DBG //os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize);
os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize);
#endif
conn->post->buff=(char*)os_malloc(conn->post->buffSize + 1); conn->post->buff=(char*)os_malloc(conn->post->buffSize + 1);
conn->post->buffLen=0; conn->post->buffLen=0;
} else if (os_strncmp(h, "Content-Type: ", 14)==0) { } else if (os_strncmp(h, "Content-Type: ", 14)==0) {
@ -480,9 +449,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
conn->post->multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes conn->post->multipartBoundary = b + 7; // move the pointer 2 chars before boundary then fill them with dashes
conn->post->multipartBoundary[0] = '-'; conn->post->multipartBoundary[0] = '-';
conn->post->multipartBoundary[1] = '-'; conn->post->multipartBoundary[1] = '-';
#ifdef HTTPD_DBG //os_printf("boundary = %s\n", conn->post->multipartBoundary);
os_printf("boundary = %s\n", conn->post->multipartBoundary);
#endif
} }
} }
} }
@ -554,13 +521,11 @@ static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) {
} }
// Callback indicating a failure in the connection. "Recon" is probably intended in the sense // Callback indicating a failure in the connection. "Recon" is probably intended in the sense
// of "you need to reconnect". Sigh... Note that there is no DiconCb after ReconCb // of "you need to reconnect". Sigh... Note that there is no DisconCb after ReconCb
static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) { static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) {
debugConn(arg, "httpdReconCb"); debugConn(arg, "httpdReconCb");
HttpdConnData *conn = httpdFindConnData(arg); HttpdConnData *conn = httpdFindConnData(arg);
#ifdef HTTPD_DBG
os_printf("%s ***** reset, err=%d\n", connStr, err); os_printf("%s ***** reset, err=%d\n", connStr, err);
#endif
if (conn == NULL) return; if (conn == NULL) return;
httpdRetireConn(conn); httpdRetireConn(conn);
} }
@ -572,18 +537,14 @@ static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) {
int i; int i;
//Find empty conndata in pool //Find empty conndata in pool
for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break; for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break;
#ifdef HTTPD_DBG //os_printf("Con req, conn=%p, pool slot %d\n", conn, i);
os_printf("Con req, conn=%p, pool slot %d\n", conn, i);
#endif
if (i==MAX_CONN) { if (i==MAX_CONN) {
#ifdef HTTPD_DBG
os_printf("%s Aiee, conn pool overflow!\n", connStr); os_printf("%s Aiee, conn pool overflow!\n", connStr);
#endif
espconn_disconnect(conn); espconn_disconnect(conn);
return; return;
} }
#ifdef HTTPD_DBG #if 0
int num = 0; int num = 0;
for (int j=0; j<MAX_CONN; j++) if (connData[j].conn != NULL) num++; for (int j=0; j<MAX_CONN; j++) if (connData[j].conn != NULL) num++;
os_printf("%s Connect (%d open)\n", connStr, num+1); os_printf("%s Connect (%d open)\n", connStr, num+1);
@ -621,9 +582,8 @@ void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) {
httpdTcp.local_port=port; httpdTcp.local_port=port;
httpdConn.proto.tcp=&httpdTcp; httpdConn.proto.tcp=&httpdTcp;
builtInUrls=fixedUrls; builtInUrls=fixedUrls;
#ifdef HTTPD_DBG
os_printf("Httpd init, conn=%p\n", &httpdConn); os_printf("Httpd init, conn=%p\n", &httpdConn);
#endif
espconn_regist_connectcb(&httpdConn, httpdConnectCb); espconn_regist_connectcb(&httpdConn, httpdConnectCb);
espconn_accept(&httpdConn); espconn_accept(&httpdConn);
espconn_tcp_set_max_con_allow(&httpdConn, MAX_CONN); espconn_tcp_set_max_con_allow(&httpdConn, MAX_CONN);

File diff suppressed because it is too large Load Diff

@ -27,118 +27,101 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef USER_AT_MQTT_H_ #ifndef MQTT_H_
#define USER_AT_MQTT_H_ #define MQTT_H_
#include <esp8266.h>
#include "mqtt_msg.h" #include "mqtt_msg.h"
#include "queue.h" #include "pktbuf.h"
#include <rest.h>
typedef struct mqtt_event_data_t { // in rest.c
uint8_t type; uint8_t UTILS_StrToIP(const char* str, void *ip);
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;
// State of MQTT connection
typedef enum { typedef enum {
WIFI_INIT, MQTT_DISCONNECTED, // we're in disconnected state
WIFI_CONNECTING, TCP_RECONNECT_REQ, // connect failed, needs reconnecting
WIFI_CONNECTING_ERROR, TCP_CONNECTING, // in TCP connection process
WIFI_CONNECTED, MQTT_CONNECTED, // conneted (or connecting)
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
} tConnState; } tConnState;
// Simple notification callback
typedef void (*MqttCallback)(uint32_t* args); 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 { typedef struct {
struct espconn* pCon; struct espconn* pCon; // socket
uint8_t security; // connection information
char* host; char* host; // MQTT server
uint32_t port; uint16_t port;
ip_addr_t ip; uint8_t security; // 0=tcp, 1=ssl
mqtt_state_t mqtt_state; ip_addr_t ip; // MQTT server IP address
mqtt_connect_info_t connect_info; mqtt_connect_info_t connect_info; // info to connect/reconnect
MqttCallback connectedCb; // protocol state and message assembly
MqttCallback cmdConnectedCb; tConnState connState; // connection state
MqttCallback disconnectedCb; bool sending; // espconn_send is pending
MqttCallback cmdDisconnectedCb; mqtt_connection_t mqtt_connection; // message assembly descriptor
MqttCallback tcpDisconnectedCb; PktBuf* msgQueue; // queued outbound messages
MqttCallback cmdTcpDisconnectedCb; // TCP input buffer
MqttCallback publishedCb; uint8_t* in_buffer;
MqttCallback cmdPublishedCb; int in_buffer_size; // length allocated
MqttDataCallback dataCb; int in_buffer_filled; // number of bytes held
MqttDataCallback cmdDataCb; // outstanding message when we expect an ACK
ETSTimer mqttTimer; PktBuf* pending_buffer; // buffer sent and awaiting ACK
uint32_t keepAliveTick; PktBuf* sending_buffer; // buffer sent not awaiting ACK
uint32_t reconnectTick; // timer and associated timeout counters
uint32_t sendTimeout; ETSTimer mqttTimer; // timer for this connection
tConnState connState; uint8_t keepAliveTick; // seconds 'til keep-alive is required (0=no k-a)
QUEUE msgQueue; uint8_t keepAliveAckTick; // seconds 'til keep-alive ack is overdue (0=no k-a)
void* user_data; 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; } MQTT_Client;
#define SEC_NONSSL 0 // Initialize client data structure
#define SEC_SSL 1 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 // Subscribe to a topic
#define MQTT_FLAG_READY 2 bool MQTT_Subscribe(MQTT_Client* client, char* topic, uint8_t qos);
#define MQTT_FLAG_EXIT 4
#define MQTT_EVENT_TYPE_NONE 0 // Publish a message
#define MQTT_EVENT_TYPE_CONNECTED 1 bool MQTT_Publish(MQTT_Client* client, const char* topic, const char* data,
#define MQTT_EVENT_TYPE_DISCONNECTED 2 uint8_t qos, uint8_t retain);
#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
void MQTT_InitConnection(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t security); // Callback when connected
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);
void MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb); void MQTT_OnConnected(MQTT_Client* mqttClient, MqttCallback connectedCb);
// Callback when disconnected
void MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb); void MQTT_OnDisconnected(MQTT_Client* mqttClient, MqttCallback disconnectedCb);
// Callback when publish succeeded
void MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb); void MQTT_OnPublished(MQTT_Client* mqttClient, MqttCallback publishedCb);
// Callback when data arrives for subscription
void MQTT_OnData(MQTT_Client* mqttClient, MqttDataCallback dataCb); 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_ */ #endif /* USER_AT_MQTT_H_ */

@ -1,3 +1,5 @@
#include <esp8266.h>
#include "mqtt.h"
#include "mqtt_cmd.h" #include "mqtt_cmd.h"
uint32_t connectedCb = 0, disconnectCb = 0, tcpDisconnectedCb = 0, publishedCb = 0, dataCb = 0; uint32_t connectedCb = 0, disconnectCb = 0, tcpDisconnectedCb = 0, publishedCb = 0, dataCb = 0;
@ -6,13 +8,11 @@ void ICACHE_FLASH_ATTR
cmdMqttConnectedCb(uint32_t* args) { cmdMqttConnectedCb(uint32_t* args) {
MQTT_Client* client = (MQTT_Client*)args; MQTT_Client* client = (MQTT_Client*)args;
MqttCmdCb* cb = (MqttCmdCb*)client->user_data; MqttCmdCb* cb = (MqttCmdCb*)client->user_data;
#ifdef MQTT_CMD_DBG os_printf("MQTT: Connected connectedCb=%p, disconnectedCb=%p, publishedCb=%p, dataCb=%p\n",
os_printf("cmdMqttConnectedCb: connectedCb=%p, disconnectedCb=%p, publishedCb=%p, dataCb=%p\n",
(void*)cb->connectedCb, (void*)cb->connectedCb,
(void*)cb->disconnectedCb, (void*)cb->disconnectedCb,
(void*)cb->publishedCb, (void*)cb->publishedCb,
(void*)cb->dataCb); (void*)cb->dataCb);
#endif
uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0); uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0);
CMD_ResponseEnd(crc); CMD_ResponseEnd(crc);
} }
@ -21,9 +21,7 @@ void ICACHE_FLASH_ATTR
cmdMqttTcpDisconnectedCb(uint32_t *args) { cmdMqttTcpDisconnectedCb(uint32_t *args) {
MQTT_Client* client = (MQTT_Client*)args; MQTT_Client* client = (MQTT_Client*)args;
MqttCmdCb *cb = (MqttCmdCb*)client->user_data; MqttCmdCb *cb = (MqttCmdCb*)client->user_data;
#ifdef MQTT_CMD_DBG os_printf("MQTT: TCP Disconnected\n");
os_printf("cmdMqttTcpDisconnectedCb\n");
#endif
uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->tcpDisconnectedCb, 0, 0); uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->tcpDisconnectedCb, 0, 0);
CMD_ResponseEnd(crc); CMD_ResponseEnd(crc);
} }
@ -32,9 +30,7 @@ void ICACHE_FLASH_ATTR
cmdMqttDisconnectedCb(uint32_t* args) { cmdMqttDisconnectedCb(uint32_t* args) {
MQTT_Client* client = (MQTT_Client*)args; MQTT_Client* client = (MQTT_Client*)args;
MqttCmdCb* cb = (MqttCmdCb*)client->user_data; MqttCmdCb* cb = (MqttCmdCb*)client->user_data;
#ifdef MQTT_CMD_DBG os_printf("MQTT: Disconnected\n");
os_printf("cmdMqttDisconnectedCb\n");
#endif
uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->disconnectedCb, 0, 0); uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->disconnectedCb, 0, 0);
CMD_ResponseEnd(crc); CMD_ResponseEnd(crc);
} }
@ -43,9 +39,7 @@ void ICACHE_FLASH_ATTR
cmdMqttPublishedCb(uint32_t* args) { cmdMqttPublishedCb(uint32_t* args) {
MQTT_Client* client = (MQTT_Client*)args; MQTT_Client* client = (MQTT_Client*)args;
MqttCmdCb* cb = (MqttCmdCb*)client->user_data; MqttCmdCb* cb = (MqttCmdCb*)client->user_data;
#ifdef MQTT_CMD_DBG os_printf("MQTT: Published\n");
os_printf("cmdMqttPublishedCb\n");
#endif
uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->publishedCb, 0, 0); uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->publishedCb, 0, 0);
CMD_ResponseEnd(crc); CMD_ResponseEnd(crc);
} }
@ -55,9 +49,7 @@ cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char*
uint16_t crc = 0; uint16_t crc = 0;
MQTT_Client* client = (MQTT_Client*)args; MQTT_Client* client = (MQTT_Client*)args;
MqttCmdCb* cb = (MqttCmdCb*)client->user_data; MqttCmdCb* cb = (MqttCmdCb*)client->user_data;
#ifdef MQTT_CMD_DBG
os_printf("cmdMqttDataCb\n");
#endif
crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->dataCb, 0, 2); crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->dataCb, 0, 2);
crc = CMD_ResponseBody(crc, (uint8_t*)topic, topic_len); crc = CMD_ResponseBody(crc, (uint8_t*)topic, topic_len);
crc = CMD_ResponseBody(crc, (uint8_t*)data, data_len); crc = CMD_ResponseBody(crc, (uint8_t*)data, data_len);
@ -75,10 +67,12 @@ MQTTCMD_Setup(CmdPacket *cmd) {
// create mqtt client // create mqtt client
uint8_t clientLen = sizeof(MQTT_Client); uint8_t clientLen = sizeof(MQTT_Client);
MQTT_Client* client = (MQTT_Client*)os_zalloc(clientLen); MQTT_Client* client = (MQTT_Client*)os_zalloc(clientLen);
if (client == NULL) if (client == NULL) return 0;
return 0;
os_memset(client, 0, clientLen); os_memset(client, 0, clientLen);
return 0;
#if 0
uint16_t len; uint16_t len;
uint8_t *client_id, *user_data, *pass_data; uint8_t *client_id, *user_data, *pass_data;
uint32_t keepalive, clean_session, cb_data; uint32_t keepalive, clean_session, cb_data;
@ -109,9 +103,9 @@ MQTTCMD_Setup(CmdPacket *cmd) {
// get clean session // get clean session
CMD_PopArg(&req, (uint8_t*)&clean_session, 4); CMD_PopArg(&req, (uint8_t*)&clean_session, 4);
#ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Setup: clientid=%s, user=%s, pw=%s, keepalive=%ld, clean_session=%ld\n", client_id, user_data, pass_data, keepalive, clean_session); 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);
#endif
// init client // init client
// TODO: why malloc these all here, pass to MQTT_InitClient to be malloc'd again? // 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); MQTT_InitClient(client, (char*)client_id, (char*)user_data, (char*)pass_data, keepalive, clean_session);
@ -146,6 +140,7 @@ MQTTCMD_Setup(CmdPacket *cmd) {
os_free(pass_data); os_free(pass_data);
return (uint32_t)client; return (uint32_t)client;
#endif
} }
uint32_t ICACHE_FLASH_ATTR uint32_t ICACHE_FLASH_ATTR
@ -160,9 +155,8 @@ MQTTCMD_Lwt(CmdPacket *cmd) {
uint32_t client_ptr; uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
MQTT_Client* client = (MQTT_Client*)client_ptr; MQTT_Client* client = (MQTT_Client*)client_ptr;
#ifdef MQTT_CMD_DBG os_printf("MQTT: MQTTCMD_Lwt client ptr=%p\n", (void*)client_ptr);
os_printf("MQTTCMD_Lwt: client ptr=%p\n", (void*)client_ptr);
#endif
uint16_t len; uint16_t len;
// get topic // get topic
@ -188,13 +182,12 @@ MQTTCMD_Lwt(CmdPacket *cmd) {
// get retain // get retain
CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4); CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4);
#ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Lwt: topic=%s, message=%s, qos=%d, retain=%d\n", os_printf("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%d, retain=%d\n",
client->connect_info.will_topic, client->connect_info.will_topic,
client->connect_info.will_message, client->connect_info.will_message,
client->connect_info.will_qos, client->connect_info.will_qos,
client->connect_info.will_retain); client->connect_info.will_retain);
#endif
return 1; return 1;
} }
@ -210,9 +203,8 @@ MQTTCMD_Connect(CmdPacket *cmd) {
uint32_t client_ptr; uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
MQTT_Client* client = (MQTT_Client*)client_ptr; MQTT_Client* client = (MQTT_Client*)client_ptr;
#ifdef MQTT_CMD_DBG os_printf("MQTT: MQTTCMD_Connect client ptr=%p\n", (void*)client_ptr);
os_printf("MQTTCMD_Connect: client ptr=%p\n", (void*)client_ptr);
#endif
uint16_t len; uint16_t len;
// get host // get host
@ -229,12 +221,12 @@ MQTTCMD_Connect(CmdPacket *cmd) {
// get security // get security
CMD_PopArg(&req, (uint8_t*)&client->security, 4); CMD_PopArg(&req, (uint8_t*)&client->security, 4);
#ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Connect: host=%s, port=%ld, security=%d\n", os_printf("MQTT: MQTTCMD_Connect host=%s, port=%d, security=%d\n",
client->host, client->host,
client->port, client->port,
client->security); client->security);
#endif
MQTT_Connect(client); MQTT_Connect(client);
return 1; return 1;
} }
@ -251,9 +243,8 @@ MQTTCMD_Disconnect(CmdPacket *cmd) {
uint32_t client_ptr; uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
MQTT_Client* client = (MQTT_Client*)client_ptr; MQTT_Client* client = (MQTT_Client*)client_ptr;
#ifdef MQTT_CMD_DBG os_printf("MQTT: MQTTCMD_Disconnect client ptr=%p\n", (void*)client_ptr);
os_printf("MQTTCMD_Disconnect: client ptr=%p\n", (void*)client_ptr);
#endif
// disconnect // disconnect
MQTT_Disconnect(client); MQTT_Disconnect(client);
return 1; return 1;
@ -271,9 +262,8 @@ MQTTCMD_Publish(CmdPacket *cmd) {
uint32_t client_ptr; uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
MQTT_Client* client = (MQTT_Client*)client_ptr; MQTT_Client* client = (MQTT_Client*)client_ptr;
#ifdef MQTT_CMD_DBG os_printf("MQTT: MQTTCMD_Publish client ptr=%p\n", (void*)client_ptr);
os_printf("MQTTCMD_Publish: client ptr=%p\n", (void*)client_ptr);
#endif
uint16_t len; uint16_t len;
uint8_t *topic, *data; uint8_t *topic, *data;
uint32_t qos = 0, retain = 0, data_len; uint32_t qos = 0, retain = 0, data_len;
@ -303,13 +293,13 @@ MQTTCMD_Publish(CmdPacket *cmd) {
// get retain // get retain
CMD_PopArg(&req, (uint8_t*)&retain, 4); CMD_PopArg(&req, (uint8_t*)&retain, 4);
#ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Publish: topic=%s, data_len=%d, qos=%ld, retain=%ld\n", os_printf("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%ld, retain=%ld\n",
topic, topic,
os_strlen((char*)data), os_strlen((char*)data),
qos, qos,
retain); retain);
#endif
MQTT_Publish(client, (char*)topic, (char*)data, (uint8_t)qos, (uint8_t)retain); MQTT_Publish(client, (char*)topic, (char*)data, (uint8_t)qos, (uint8_t)retain);
os_free(topic); os_free(topic);
os_free(data); os_free(data);
@ -328,9 +318,8 @@ MQTTCMD_Subscribe(CmdPacket *cmd) {
uint32_t client_ptr; uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4); CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
MQTT_Client* client = (MQTT_Client*)client_ptr; MQTT_Client* client = (MQTT_Client*)client_ptr;
#ifdef MQTT_CMD_DBG os_printf("MQTT: MQTTCMD_Subscribe client ptr=%p\n", (void*)client_ptr);
os_printf("MQTTCMD_Subscribe: client ptr=%p\n", (void*)client_ptr);
#endif
uint16_t len; uint16_t len;
uint8_t* topic; uint8_t* topic;
uint32_t qos = 0; uint32_t qos = 0;
@ -344,9 +333,8 @@ MQTTCMD_Subscribe(CmdPacket *cmd) {
// get qos // get qos
CMD_PopArg(&req, (uint8_t*)&qos, 4); CMD_PopArg(&req, (uint8_t*)&qos, 4);
#ifdef MQTT_CMD_DBG
os_printf("MQTT: MQTTCMD_Subscribe topic=%s, qos=%ld\n", topic, qos); os_printf("MQTT: MQTTCMD_Subscribe topic=%s, qos=%ld\n", topic, qos);
#endif
MQTT_Subscribe(client, (char*)topic, (uint8_t)qos); MQTT_Subscribe(client, (char*)topic, (uint8_t)qos);
os_free(topic); os_free(topic);
return 1; return 1;

@ -2,7 +2,6 @@
#define MODULES_MQTT_CMD_H_ #define MODULES_MQTT_CMD_H_
#include "cmd.h" #include "cmd.h"
#include "mqtt.h"
typedef struct { typedef struct {
uint32_t connectedCb; uint32_t connectedCb;

@ -29,6 +29,7 @@
* *
*/ */
#include <esp8266.h>
#include "mqtt_msg.h" #include "mqtt_msg.h"
#define MQTT_MAX_FIXED_HEADER_SIZE 3 #define MQTT_MAX_FIXED_HEADER_SIZE 3
@ -129,7 +130,7 @@ mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_le
} }
int ICACHE_FLASH_ATTR int ICACHE_FLASH_ATTR
mqtt_get_total_length(uint8_t* buffer, uint16_t length) { mqtt_get_total_length(const uint8_t* buffer, uint16_t length) {
int i; int i;
int totlen = 0; int totlen = 0;
@ -146,7 +147,7 @@ mqtt_get_total_length(uint8_t* buffer, uint16_t length) {
} }
const char* ICACHE_FLASH_ATTR const char* ICACHE_FLASH_ATTR
mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) { mqtt_get_publish_topic(const uint8_t* buffer, uint16_t* length) {
int i; int i;
int totlen = 0; int totlen = 0;
int topiclen; int topiclen;
@ -173,7 +174,7 @@ mqtt_get_publish_topic(uint8_t* buffer, uint16_t* length) {
} }
const char* ICACHE_FLASH_ATTR const char* ICACHE_FLASH_ATTR
mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) { mqtt_get_publish_data(const uint8_t* buffer, uint16_t* length) {
int i; int i;
int totlen = 0; int totlen = 0;
int topiclen; int topiclen;
@ -215,7 +216,7 @@ mqtt_get_publish_data(uint8_t* buffer, uint16_t* length) {
} }
uint16_t ICACHE_FLASH_ATTR uint16_t ICACHE_FLASH_ATTR
mqtt_get_id(uint8_t* buffer, uint16_t length) { mqtt_get_id(const uint8_t* buffer, uint16_t length) {
if (length < 1) if (length < 1)
return 0; return 0;

@ -30,8 +30,9 @@
*/ */
#ifndef MQTT_MSG_H #ifndef MQTT_MSG_H
#define MQTT_MSG_H #define MQTT_MSG_H
#include <esp8266.h>
#define PROTOCOL_NAMEv311
enum mqtt_message_type { enum mqtt_message_type {
MQTT_MSG_TYPE_CONNECT = 1, MQTT_MSG_TYPE_CONNECT = 1,
@ -50,21 +51,22 @@ enum mqtt_message_type {
MQTT_MSG_TYPE_DISCONNECT = 14 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 { typedef struct mqtt_message {
uint8_t* data; uint8_t* data;
uint16_t length; uint16_t length;
} mqtt_message_t; } mqtt_message_t;
// Descriptor for a connection with message assembly storage
typedef struct mqtt_connection { typedef struct mqtt_connection {
mqtt_message_t message; mqtt_message_t message; // resulting message
uint16_t message_id; // id of assembled message and memo to calculate next message id
uint16_t message_id; uint8_t* buffer; // buffer for assembling messages
uint8_t* buffer; uint16_t buffer_length; // buffer length
uint16_t buffer_length;
} mqtt_connection_t; } mqtt_connection_t;
// Descriptor for a connect request
typedef struct mqtt_connect_info { typedef struct mqtt_connect_info {
char* client_id; char* client_id;
char* username; char* username;
@ -75,32 +77,40 @@ typedef struct mqtt_connect_info {
uint8_t will_qos; uint8_t will_qos;
uint8_t will_retain; uint8_t will_retain;
uint8_t clean_session; uint8_t clean_session;
} mqtt_connect_info_t; } mqtt_connect_info_t;
static inline int ICACHE_FLASH_ATTR mqtt_get_type(const uint8_t* buffer) {
static inline int ICACHE_FLASH_ATTR mqtt_get_type(uint8_t* buffer) {
return (buffer[0] & 0xf0) >> 4; 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; 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; 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); return (buffer[0] & 0x01);
} }
// Init a connection descriptor
void mqtt_msg_init(mqtt_connection_t* connection, uint8_t* buffer, uint16_t buffer_length); 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_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_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); mqtt_message_t* mqtt_msg_puback(mqtt_connection_t* connection, uint16_t message_id);

@ -0,0 +1,64 @@
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
#include <esp8266.h>
#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;
}

@ -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

@ -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;
}

@ -1,21 +0,0 @@
#ifndef _PROTO_H_
#define _PROTO_H_
#include <esp8266.h>
#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

@ -1,53 +0,0 @@
/* str_queue.c
*
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
* 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;
}

@ -1,46 +0,0 @@
/* str_queue.h --
*
* Copyright (c) 2014-2015, Tuan PM <tuanpm at live dot com>
* 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 <esp8266.h>
#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_ */

@ -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;
}

@ -1,17 +0,0 @@
#ifndef _RING_BUF_H_
#define _RING_BUF_H_
#include <esp8266.h>
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

@ -154,12 +154,12 @@ rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) {
if(client->ip.addr == 0 && ipaddr->addr != 0) { if(client->ip.addr == 0 && ipaddr->addr != 0) {
os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4); os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4);
//if(client->security){ #ifdef CLIENT_SSL_ENABLE
// espconn_secure_connect(client->pCon); if(client->security) {
//} espconn_secure_connect(client->pCon);
//else { } else
#endif
espconn_connect(client->pCon); espconn_connect(client->pCon);
//}
os_printf("REST: connecting...\n"); os_printf("REST: connecting...\n");
} }
} }
@ -368,6 +368,8 @@ REST_Request(CmdPacket *cmd) {
} }
os_printf("\n"); os_printf("\n");
//os_printf("REST request: %s", (char*)client->data);
os_printf("REST: pCon state=%d\n", client->pCon->state); os_printf("REST: pCon state=%d\n", client->pCon->state);
client->pCon->state = ESPCONN_NONE; client->pCon->state = ESPCONN_NONE;
espconn_regist_connectcb(client->pCon, tcpclient_connect_cb); espconn_regist_connectcb(client->pCon, tcpclient_connect_cb);

@ -14,6 +14,7 @@
#include "serled.h" #include "serled.h"
#include "config.h" #include "config.h"
#include "console.h" #include "console.h"
#include "slip.h"
#include "cmd.h" #include "cmd.h"
static struct espconn serbridgeConn; 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 //callback after the data are sent
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
serbridgeSentCb(void *arg) { serbridgeSentCb(void *arg) {
@ -273,6 +287,7 @@ serbridgeSentCb(void *arg) {
// Error callback (it's really poorly named, it's not a "connection reconnected" callback, // Error callback (it's really poorly named, it's not a "connection reconnected" callback,
// it's really a "connection broken, please reconnect" 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) { static void ICACHE_FLASH_ATTR serbridgeReconCb(void *arg, sint8 err) {
serbridgeConnData *sbConn = serbridgeFindConnData(arg); serbridgeConnData *sbConn = serbridgeFindConnData(arg);
if (sbConn == NULL) return; if (sbConn == NULL) return;

@ -6,4 +6,3 @@ void serledInit(void);
void makeGpio(uint8_t pin); void makeGpio(uint8_t pin);
#endif #endif

@ -4,11 +4,10 @@
#include "uart.h" #include "uart.h"
#include "crc16.h" #include "crc16.h"
#include "serbridge.h" #include "serbridge.h"
#include "serled.h"
#include "console.h" #include "console.h"
#include "cmd.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); 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 // callback with a buffer of characters that have arrived on the uart
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
serbridgeUartCb(char *buf, short length) { slip_parse_buf(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<length; i++)
if (buf[i] == SLIP_START) {
os_printf("SLIP: START while disabled=%d\n", slip_disabled);
break;
}
return;
}
// do SLIP parsing // do SLIP parsing
for (short i=0; i<length; i++) for (short i=0; i<length; i++)
slip_parse_char(buf[i]); slip_parse_char(buf[i]);
@ -136,6 +124,5 @@ serbridgeUartCb(char *buf, short length) {
slip_process(); slip_process();
slip_reset(); slip_reset();
} }
serledFlash(50); // short blink on serial LED
} }

@ -0,0 +1,6 @@
#ifndef SLIP_H
#define SLIP_H
void slip_parse_buf(char *buf, short length);
#endif

@ -23,7 +23,7 @@
#include "user_interface.h" #include "user_interface.h"
#include "uart.h" #include "uart.h"
#define recvTaskPrio 0 #define recvTaskPrio 1
#define recvTaskQueueLen 64 #define recvTaskQueueLen 64
// UartDev is defined and initialized in rom code. // UartDev is defined and initialized in rom code.

@ -1,100 +1,61 @@
#include <esp8266.h> #include <esp8266.h>
#include <mqtt.h> #include "cgiwifi.h"
#include <cgiwifi.h> #include "mqtt.h"
#include <json/jsontree.h>
#include <json/jsonparse.h>
#include "user_json.h"
#include "user_funcs.h"
MQTT_Client mqttClient; MQTT_Client mqttClient;
typedef struct { static ETSTimer mqttTimer;
uint8_t fallbackStateBits;
uint8_t stateBits;
uint8_t init;
uint8_t fallbackSecondsForBits[8];
} LatchState;
static LatchState latch;
static void ICACHE_FLASH_ATTR
updateLatch() {
os_printf("ESP: Latch Callback\n");
cmdCallback* latchCb = CMD_GetCbByName("Latch");
if (latchCb->callback != -1) {
// uint16_t crc = CMD_ResponseStart(CMD_SENSOR_EVENTS, (uint32_t)&latchCb->callback, 0, 1);
// crc = CMD_ResponseBody(crc, (uint8_t*)&latch, sizeof(LatchState));
// CMD_ResponseEnd(crc);
}
}
LOCAL int ICACHE_FLASH_ATTR static int once = 0;
latchGet(struct jsontree_context *js_ctx) { static void ICACHE_FLASH_ATTR mqttTimerCb(void *arg) {
return 0; 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);
} }
LOCAL int ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
latchSet(struct jsontree_context *js_ctx, struct jsonparse_state *parser) { wifiStateChangeCb(uint8_t status)
int type; {
int ix = -1; if (status == wifiGotIP) {
bool sendLatchUpdate = false; os_timer_disarm(&mqttTimer);
while ((type = jsonparse_next(parser)) != 0) { os_timer_setfn(&mqttTimer, mqttTimerCb, NULL);
if (type == JSON_TYPE_ARRAY) { os_timer_arm(&mqttTimer, 15000, 0);
ix = -1;
}
else if (type == JSON_TYPE_OBJECT) {
ix++;
}
else if (type == JSON_TYPE_PAIR_NAME) {
if (jsonparse_strcmp_value(parser, "states") == 0) {
char latchStates[9];
jsonparse_next(parser); jsonparse_next(parser);
jsonparse_copy_value(parser, latchStates, sizeof(latchStates));
os_printf("latch states %s\n", latchStates);
uint8_t states = binToByte(latchStates);
// if (latch.stateBits != states) {
latch.stateBits = states;
sendLatchUpdate = true;
// }
}
else if (jsonparse_strcmp_value(parser, "fallbackstates") == 0) {
char fallbackStates[9];
jsonparse_next(parser); jsonparse_next(parser);
jsonparse_copy_value(parser, fallbackStates, sizeof(fallbackStates));
os_printf("latch states %s\n", fallbackStates);
uint8_t fbstates = binToByte(fallbackStates);
// if (latch.fallbackStateBits != fbstates) {
latch.fallbackStateBits = fbstates;
sendLatchUpdate = true;
// }
}
}
if (sendLatchUpdate) {
updateLatch();
}
} }
return 0;
} }
static struct jsontree_callback latchCallback = JSONTREE_CALLBACK(latchGet, latchSet);
static char* latchQueueName;
JSONTREE_OBJECT(latchJsonObj, // initialize the custom stuff that goes beyond esp-link
JSONTREE_PAIR("states", &latchCallback), void app_init() {
JSONTREE_PAIR("fallbackstates", &latchCallback)); wifiAddStateChangeCb(wifiStateChangeCb);
}
#if 0
MQTT_Client mqttClient;
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
mqttConnectedCb(uint32_t *args) { mqttConnectedCb(uint32_t *args) {
MQTT_Client* client = (MQTT_Client*)args; MQTT_Client* client = (MQTT_Client*)args;
MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); MQTT_Publish(client, "announce/all", "Hello World!", 0, 0);
}
char* latchQueue = "/latch"; void ICACHE_FLASH_ATTR
char *buff = (char*)os_zalloc(strlen(system_get_chip_id_str()) + strlen(latchQueue) + 1); mqttDisconnectedCb(uint32_t *args) {
os_strcpy(buff, system_get_chip_id_str()); // MQTT_Client* client = (MQTT_Client*)args;
os_strcat(buff, latchQueue); os_printf("MQTT Disconnected\n");
latchQueueName = buff; }
MQTT_Subscribe(client, latchQueueName, 0);
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 void ICACHE_FLASH_ATTR
@ -110,49 +71,11 @@ mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *da
os_memcpy(dataBuf, data, data_len); os_memcpy(dataBuf, data, data_len);
dataBuf[data_len] = 0; dataBuf[data_len] = 0;
os_printf("Receive topic: %s\n Data: %s\n", topicBuf, dataBuf); os_printf("Receive topic: %s, data: %s\n", topicBuf, dataBuf);
if (!strcoll(topicBuf, latchQueueName)) {
struct jsontree_context js;
jsontree_setup(&js, (struct jsontree_value *)&latchJsonObj, json_putchar);
json_parse(&js, dataBuf);
}
os_free(topicBuf); os_free(topicBuf);
os_free(dataBuf); 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 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 init() {
wifiAddStateChangeCb(wifiStateChangeCb);
MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY);
MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION); MQTT_InitClient(&mqttClient, MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS, MQTT_KEEPALIVE, MQTT_CLSESSION);
MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0); MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0);
@ -161,4 +84,4 @@ void init() {
MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb); MQTT_OnDisconnected(&mqttClient, mqttTcpDisconnectedCb);
MQTT_OnPublished(&mqttClient, mqttPublishedCb); MQTT_OnPublished(&mqttClient, mqttPublishedCb);
MQTT_OnData(&mqttClient, mqttDataCb); MQTT_OnData(&mqttClient, mqttDataCb);
} #endif

Loading…
Cancel
Save