Working on latch

pull/47/head
Benjamin Runnels 9 years ago
commit 483800fd8d
  1. 20
      Makefile
  2. 21
      README.md
  3. 23
      cmd/cmd.c
  4. 18
      cmd/cmd.h
  5. 95
      cmd/handlers.c
  6. 71
      cmd/mqtt_cmd.c
  7. 89
      cmd/rest.c
  8. 8
      esp-link.vcxproj
  9. 73
      esp-link/cgiwifi.c
  10. 1
      esp-link/cgiwifi.h
  11. 2
      esp-link/config.c
  12. 16
      esp-link/main.c
  13. 46
      httpd/html-old/console.html
  14. 72
      httpd/html-old/console.js
  15. BIN
      httpd/html-old/favicon.ico
  16. 10
      httpd/html-old/head-
  17. 87
      httpd/html-old/home.html
  18. BIN
      httpd/html-old/jl-400x110.png-
  19. 48
      httpd/html-old/log.html
  20. 1388
      httpd/html-old/pure.css
  21. 380
      httpd/html-old/style.css
  22. 397
      httpd/html-old/ui.js
  23. BIN
      httpd/html-old/wifi/icons.png
  24. 84
      httpd/html-old/wifi/wifi.html
  25. 200
      httpd/html-old/wifi/wifi.js
  26. 64
      httpd/httpd.c
  27. 10
      include/esp8266.h
  28. 4
      include/user_config.h
  29. 274
      libraries/Time/Time.c
  30. 130
      libraries/Time/Time.h
  31. 8
      mqtt/mqtt.c
  32. 2
      mqtt/mqtt_msg.h
  33. 5
      serial/slip.c
  34. 14
      serial/uart.c
  35. 37
      user/user_funcs.c
  36. 10
      user/user_funcs.h
  37. 158
      user/user_json.c
  38. 14
      user/user_json.h
  39. 126
      user/user_main.c

@ -90,7 +90,8 @@ LED_SERIAL_PIN ?= 14
# on the release tag, make release, upload esp-link.tgz into the release files # on the release tag, make release, upload esp-link.tgz into the release files
#VERSION ?= "esp-link custom version" #VERSION ?= "esp-link custom version"
DATE := $(shell date '+%F %T') DATE := $(shell date '+%F %T')
BRANCH := $(shell git describe --tags) BRANCH := $(shell if git diff --quiet HEAD; then git describe --tags; \
else git symbolic-ref --short HEAD; fi)
SHA := $(shell if git diff --quiet HEAD; then git rev-parse --short HEAD | cut -d"/" -f 3; \ SHA := $(shell if git diff --quiet HEAD; then git rev-parse --short HEAD | cut -d"/" -f 3; \
else echo "development"; fi) else echo "development"; fi)
VERSION ?=esp-link $(BRANCH) - $(DATE) - $(SHA) VERSION ?=esp-link $(BRANCH) - $(DATE) - $(SHA)
@ -142,11 +143,13 @@ TARGET = httpd
APPGEN_TOOL ?= gen_appbin.py APPGEN_TOOL ?= gen_appbin.py
# which modules (subdirectories) of the project to include in compiling # which modules (subdirectories) of the project to include in compiling
LIBRARIES_DIR = libraries
MODULES = espfs httpd user serial cmd mqtt esp-link MODULES = espfs httpd user serial cmd mqtt esp-link
EXTRA_INCDIR = include . MODULES += $(foreach sdir,$(LIBRARIES_DIR),$(wildcard $(sdir)/*))
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
LIBS = c gcc hal phy pp net80211 wpa main lwip LIBS = c gcc hal phy pp net80211 wpa main lwip json
# compiler flags using during compilation of source files # compiler flags using during compilation of source files
CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ CFLAGS = -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
@ -285,7 +288,7 @@ baseflash: all
$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x01000 $(FW_BASE)/user1.bin $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x01000 $(FW_BASE)/user1.bin
flash: all flash: all
$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) -fs $(ET_FS) -ff $(ET_FF) write_flash \ $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash -fs $(ET_FS) -ff $(ET_FF) \
0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \ 0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \
$(ET_BLANK) $(SDK_BASE)/bin/blank.bin $(ET_BLANK) $(SDK_BASE)/bin/blank.bin
@ -350,10 +353,13 @@ espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/
$(Q) $(MAKE) -C espfs/mkespfsimage GZIP_COMPRESSION="$(GZIP_COMPRESSION)" $(Q) $(MAKE) -C espfs/mkespfsimage GZIP_COMPRESSION="$(GZIP_COMPRESSION)"
release: all release: all
$(Q) rm -rf release; mkdir -p release/esp-link $(Q) rm -rf release; mkdir -p release/esp-link-$(BRANCH)
$(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user1.bin | cut -b 1-80
$(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80
$(Q) cp $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin $(SDK_BASE)/bin/blank.bin \ $(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 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link-$(BRANCH)
$(Q) tar zcf esp-link.tgz -C release esp-link $(Q) tar zcf esp-link-$(BRANCH)-$(FLASH_SIZE).tgz -C release esp-link-$(BRANCH)
$(Q) echo "Release file: esp-link-$(BRANCH)-$(FLASH_SIZE).tgz"
$(Q) rm -rf release $(Q) rm -rf release
clean: clean:

@ -7,16 +7,24 @@ It implements a number of features:
- flash-programming attached Arduino/AVR microcontrollers as well as LPC800-series and other - flash-programming attached Arduino/AVR microcontrollers as well as LPC800-series and other
ARM microcontrollers via Wifi ARM microcontrollers via Wifi
- outbound TCP (and thus HTTP) connections from the attached micro-controller to the internet - outbound TCP (and thus HTTP) connections from the attached micro-controller to the internet
- outbound REST HTTP requests from the attached micro-controller to the internet, protocol
based on espduino and compatible with [tuanpmt/espduino](https://github.com/tuanpmt/espduino)
The firmware includes a tiny HTTP server based on The firmware includes a tiny HTTP server based on
[esphttpd](http://www.esp8266.com/viewforum.php?f=34) [esphttpd](http://www.esp8266.com/viewforum.php?f=34)
with a simple web interface, many thanks to Jeroen Domburg for making it available! with a simple web interface, many thanks to Jeroen Domburg for making it available!
Many thanks to https://github.com/brunnels for contributions around the espduino functionality.
[![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) ###[Releases](https://github.com/jeelabs/esp-link/releases)
- [V2.0.beta2](https://github.com/jeelabs/esp-link/releases/tag/v2.0.beta2) has REST support but
requires a 1MByte or 4MByte ESP8266 flash, e.g. esp-12 or wroom-02
- [V1.0.1](https://github.com/jeelabs/esp-link/releases/tag/v1.0.1) is _stable_
and has the web server, transparent bridge, flash-programming support, but lacks
the REST and upcoming MQTT support. V1 works with 512KB flash, e.g. esp-1, esp-3, ...
###[Latest release](https://github.com/jeelabs/esp-link/releases) For quick support and questions:
Note that the [stable V1.0 release](https://github.com/jeelabs/esp-link/releases/tag/v1.0.0) is [![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)
recommended if you do not need the outbound TCP connections and have a 512KB flash chip.
Eye Candy Eye Candy
--------- ---------
@ -31,7 +39,7 @@ attached microcontroller, and the pin assignments card:
Hardware info Hardware info
------------- -------------
This firmware is designed for esp8266 modules which have most ESP I/O pins available and This firmware is designed for esp8266 modules which have most ESP I/O pins available and
512KB flash. at least 1MB flash. (The V1 firmware supports modules with 512KB flash).
The default connections are: The default connections are:
- URXD: connect to TX of microcontroller - URXD: connect to TX of microcontroller
- UTXD: connect to RX of microcontroller - UTXD: connect to RX of microcontroller
@ -42,6 +50,9 @@ The default connections are:
If you are using an FTDI connector, GPIO12 goes to DTR and GPIO13 goes to CTS. If you are using an FTDI connector, GPIO12 goes to DTR and GPIO13 goes to CTS.
If you are using an esp-12 module, you can avoid the initial boot message from the esp8266
bootloader by using the swap-pins option. This swaps the esp8266 TX/RX to gpio15/gpio13 respectively.
The GPIO pin assignments can be changed dynamically in the web UI and are saved in flash. The GPIO pin assignments can be changed dynamically in the web UI and are saved in flash.
Initial flashing Initial flashing

@ -83,22 +83,30 @@ CMD_Exec(const CmdList *scp, CmdPacket *packet) {
// Iterate through the command table and call the appropriate function // Iterate through the command table and call the appropriate function
while (scp->sc_function != NULL) { while (scp->sc_function != NULL) {
if(scp->sc_name == packet->cmd) { if(scp->sc_name == packet->cmd) {
//os_printf("CMD: Dispatching cmd=%d\n", packet->cmd); #ifdef CMD_DBG
os_printf("CMD: Dispatching cmd=%d\n", packet->cmd);
#endif
// call command function // call command function
uint32_t ret = scp->sc_function(packet); uint32_t ret = scp->sc_function(packet);
// if requestor asked for a response, send it // if requestor asked for a response, send it
if (packet->_return){ if (packet->_return){
#ifdef CMD_DBG
os_printf("CMD: Response: 0x%lx, cmd: %d\r\n", ret, packet->cmd); os_printf("CMD: Response: 0x%lx, cmd: %d\r\n", ret, packet->cmd);
#endif
crc = CMD_ResponseStart(packet->cmd, 0, ret, 0); crc = CMD_ResponseStart(packet->cmd, 0, ret, 0);
CMD_ResponseEnd(crc); CMD_ResponseEnd(crc);
} else { } else {
//os_printf("CMD: no response (%lu)\n", packet->_return); #ifdef CMD_DBG
os_printf("CMD: no response (%lu)\n", packet->_return);
#endif
} }
return ret; return ret;
} }
scp++; scp++;
} }
#ifdef CMD_DBG
os_printf("CMD: cmd=%d not found\n", packet->cmd); os_printf("CMD: cmd=%d not found\n", packet->cmd);
#endif
return 0; return 0;
} }
@ -113,26 +121,35 @@ CMD_parse_packet(uint8_t *buf, short len) {
uint8_t *data_ptr = (uint8_t*)&packet->args; uint8_t *data_ptr = (uint8_t*)&packet->args;
uint8_t *data_limit = data_ptr+len; uint8_t *data_limit = data_ptr+len;
uint16_t argc = packet->argc; uint16_t argc = packet->argc;
#ifdef CMD_DBG
uint16_t argn = 0; uint16_t argn = 0;
os_printf("CMD: cmd=%d argc=%d cb=%p ret=%lu\n", os_printf("CMD: cmd=%d argc=%d cb=%p ret=%lu\n",
packet->cmd, packet->argc, (void *)packet->callback, packet->_return); packet->cmd, packet->argc, (void *)packet->callback, packet->_return);
#endif
// print out arguments // print out arguments
while (data_ptr+2 < data_limit && argc--) { while (data_ptr+2 < data_limit && argc--) {
short l = *(uint16_t*)data_ptr; short l = *(uint16_t*)data_ptr;
#ifdef CMD_DBG
os_printf("CMD: arg[%d] len=%d:", argn++, l); os_printf("CMD: arg[%d] len=%d:", argn++, l);
#endif
data_ptr += 2; data_ptr += 2;
while (data_ptr < data_limit && l--) { while (data_ptr < data_limit && l--) {
#ifdef CMD_DBG
os_printf(" %02X", *data_ptr++); os_printf(" %02X", *data_ptr++);
#endif
} }
#ifdef CMD_DBG
os_printf("\n"); os_printf("\n");
#endif
} }
if (data_ptr <= data_limit) { if (data_ptr <= data_limit) {
CMD_Exec(commands, packet); CMD_Exec(commands, packet);
} else { } else {
#ifdef CMD_DBG
os_printf("CMD: packet length overrun, parsing arg %d\n", argn-1); os_printf("CMD: packet length overrun, parsing arg %d\n", argn-1);
#endif
} }
} }

@ -55,7 +55,7 @@ typedef enum {
CMD_REST_REQUEST, CMD_REST_REQUEST,
CMD_REST_SETHEADER, CMD_REST_SETHEADER,
CMD_REST_EVENTS, CMD_REST_EVENTS,
CMD_ADD_SENSOR, // 15 CMD_ADD_CALLBACK, // 15
CMD_SENSOR_EVENTS CMD_SENSOR_EVENTS
} CmdName; } CmdName;
@ -71,19 +71,23 @@ typedef struct {
uint32_t callback; uint32_t callback;
} cmdCallback; } cmdCallback;
void ICACHE_FLASH_ATTR CMD_parse_packet(uint8_t *buf, short len); // Used by slip protocol to cause parsing of a received packet
cmdCallback* ICACHE_FLASH_ATTR CMD_GetCbByName(char* name); void CMD_parse_packet(uint8_t *buf, short len);
// Return the info about a callback to the attached uC by name, these are callbacks that the
// attached uC registers using the ADD_SENSOR command
cmdCallback* CMD_GetCbByName(char* name);
// Responses // Responses
// Start a response, returns the partial CRC // Start a response, returns the partial CRC
uint16_t ICACHE_FLASH_ATTR CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc); uint16_t CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc);
// Adds data to a response, returns the partial CRC // Adds data to a response, returns the partial CRC
uint16_t ICACHE_FLASH_ATTR CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len); uint16_t CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len);
// Ends a response // Ends a response
void ICACHE_FLASH_ATTR CMD_ResponseEnd(uint16_t crc); void CMD_ResponseEnd(uint16_t crc);
//void ICACHE_FLASH_ATTR CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]); //void CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]);
// Requests // Requests

@ -11,18 +11,20 @@
#include "cgiwifi.h" #include "cgiwifi.h"
#include "mqtt_cmd.h" #include "mqtt_cmd.h"
static uint32_t ICACHE_FLASH_ATTR CMD_Null(CmdPacket *cmd); static uint32_t CMD_Null(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_IsReady(CmdPacket *cmd); static uint32_t CMD_IsReady(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd); static uint32_t CMD_Reset(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_AddSensor(CmdPacket *cmd); static uint32_t CMD_WifiConnect(CmdPacket *cmd);
static uint32_t CMD_AddCallback(CmdPacket *cmd);
// keep track of last status sent to uC so we can notify it when it changes
static uint8_t lastWifiStatus = wifiIsDisconnected; static uint8_t lastWifiStatus = wifiIsDisconnected;
static bool wifiCbAdded = false; static bool wifiCbAdded = false;
// Command dispatch table for serial -> ESP commands // Command dispatch table for serial -> ESP commands
const CmdList commands[] = { const CmdList commands[] = {
{CMD_NULL, CMD_Null}, {CMD_NULL, CMD_Null},
{CMD_RESET, CMD_Null}, {CMD_RESET, CMD_Reset},
{CMD_IS_READY, CMD_IsReady}, {CMD_IS_READY, CMD_IsReady},
{CMD_WIFI_CONNECT, CMD_WifiConnect}, {CMD_WIFI_CONNECT, CMD_WifiConnect},
@ -37,54 +39,65 @@ const CmdList commands[] = {
{CMD_REST_REQUEST, REST_Request}, {CMD_REST_REQUEST, REST_Request},
{CMD_REST_SETHEADER, REST_SetHeader}, {CMD_REST_SETHEADER, REST_SetHeader},
{CMD_ADD_SENSOR, CMD_AddSensor }, {CMD_ADD_CALLBACK, CMD_AddCallback },
{CMD_NULL, NULL} {CMD_NULL, NULL}
}; };
// WifiCb plus 10 for sensors // WifiCb plus 10 for sensors
cmdCallback callbacks[12] = { #define MAX_CALLBACKS 12
{ { '\0' }, -1 }, cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 }
};
// 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;
} }
static void ICACHE_FLASH_ATTR // Command handler for Reset command, this was originally to reset the ESP but we don't want to
// do that is esp-link. It is still good to clear any information the ESP has about the attached
// uC.
static uint32_t ICACHE_FLASH_ATTR
CMD_Reset(CmdPacket *cmd) {
#ifdef CMD_DBG
os_printf("CMD_Reset\n");
#endif
// clear callbacks table
os_memset(callbacks, 0, sizeof(callbacks));
return 1;
}
static uint32_t ICACHE_FLASH_ATTR
CMD_AddCb(char* name, uint32_t cb) { CMD_AddCb(char* name, uint32_t cb) {
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_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback); os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, (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, checkname) == 0 || callbacks[i].name[0] == '\0') {
os_strncpy(callbacks[i].name, checkname, sizeof(checkname)); 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);
break; #endif
return 1;
} }
} }
return 0;
} }
cmdCallback* ICACHE_FLASH_ATTR cmdCallback* ICACHE_FLASH_ATTR
@ -92,14 +105,20 @@ 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 < sizeof(commands); i++) {
#ifdef CMD_DBG
os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback); os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, (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;
} }
@ -107,7 +126,9 @@ 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) {
@ -124,7 +145,9 @@ 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;
@ -133,33 +156,37 @@ CMD_WifiConnect(CmdPacket *cmd) {
wifiCbAdded = true; wifiCbAdded = true;
} }
CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback
lastWifiStatus = wifiIsDisconnected; lastWifiStatus = 0xff; // set to invalid value so we immediately send status cb in all cases
CMD_WifiCb(wifiState); CMD_WifiCb(wifiState);
return 1; return 1;
} }
// Command handler for Wifi connect command // Command handler to add a callback to the named-callbacks list, this is for a callback to the uC
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
CMD_AddSensor(CmdPacket *cmd) { CMD_AddCallback(CmdPacket *cmd) {
CmdRequest req; CmdRequest req;
CMD_Request(&req, cmd); CMD_Request(&req, cmd);
os_printf("CMD_AddSensor: setup argc=%ld\n", CMD_GetArgc(&req)); #ifdef CMD_DBG
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;
uint8_t* name; char name[16];
uint16_t len; uint16_t len;
// get the sensor name // get the sensor name
len = CMD_ArgLen(&req); len = CMD_ArgLen(&req);
os_printf("CMD_AddSensor: name len=%d\n", len); #ifdef CMD_DBG
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
name = (uint8_t*)os_zalloc(len + 1); if (CMD_PopArg(&req, (uint8_t *)name, len)) return 0;
if (CMD_PopArg(&req, name, len)) return 0;
name[len] = 0; name[len] = 0;
os_printf("CMD_AddSensor: name=%s\n", name); #ifdef CMD_DBG
os_printf("CMD_AddCallback: name=%s\n", name);
#endif
CMD_AddCb((char*)name, (uint32_t)cmd->callback); // save the sensor callback return CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback
return 1;
} }

@ -6,11 +6,13 @@ 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;
os_printf("MQTT: Connected connectedCb=%p, disconnectedCb=%p, publishedCb=%p, dataCb=%p\n", #ifdef MQTT_CMD_DBG
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);
} }
@ -19,7 +21,9 @@ 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;
os_printf("MQTT: TCP Disconnected\n"); #ifdef MQTT_CMD_DBG
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);
} }
@ -28,7 +32,9 @@ 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;
os_printf("MQTT: Disconnected\n"); #ifdef MQTT_CMD_DBG
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);
} }
@ -37,7 +43,9 @@ 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;
os_printf("MQTT: Published\n"); #ifdef MQTT_CMD_DBG
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);
} }
@ -47,7 +55,9 @@ 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);
@ -99,9 +109,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("MQTT: 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("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);
@ -150,8 +160,9 @@ 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;
os_printf("MQTT: MQTTCMD_Lwt client ptr=%p\n", (void*)client_ptr); #ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Lwt: client ptr=%p\n", (void*)client_ptr);
#endif
uint16_t len; uint16_t len;
// get topic // get topic
@ -177,12 +188,13 @@ 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("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%d, retain=%d\n", os_printf("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;
} }
@ -198,8 +210,9 @@ 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;
os_printf("MQTT: MQTTCMD_Connect client ptr=%p\n", (void*)client_ptr); #ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Connect: client ptr=%p\n", (void*)client_ptr);
#endif
uint16_t len; uint16_t len;
// get host // get host
@ -216,12 +229,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("MQTT: MQTTCMD_Connect host=%s, port=%ld, security=%d\n", os_printf("MQTTCMD_Connect: host=%s, port=%ld, 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;
} }
@ -238,8 +251,9 @@ 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;
os_printf("MQTT: MQTTCMD_Disconnect client ptr=%p\n", (void*)client_ptr); #ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Disconnect: client ptr=%p\n", (void*)client_ptr);
#endif
// disconnect // disconnect
MQTT_Disconnect(client); MQTT_Disconnect(client);
return 1; return 1;
@ -257,8 +271,9 @@ 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;
os_printf("MQTT: MQTTCMD_Publish client ptr=%p\n", (void*)client_ptr); #ifdef MQTT_CMD_DBG
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;
@ -288,13 +303,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("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%ld, retain=%ld\n", os_printf("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);
@ -313,8 +328,9 @@ 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;
os_printf("MQTT: MQTTCMD_Subscribe client ptr=%p\n", (void*)client_ptr); #ifdef MQTT_CMD_DBG
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;
@ -328,8 +344,9 @@ 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;

@ -25,59 +25,66 @@ tcpclient_discon_cb(void *arg) {
client->data = 0; client->data = 0;
} }
// Receive HTTP response - this hacky function assumes that the full response is received in
// one go. Sigh...
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
tcpclient_recv(void *arg, char *pdata, unsigned short len) { tcpclient_recv(void *arg, char *pdata, unsigned short len) {
uint8_t currentLineIsBlank = 0;
uint8_t httpBody = 0;
uint8_t inStatus = 0;
char statusCode[4];
int i = 0, j;
uint32_t code = 0;
uint16_t crc;
struct espconn *pCon = (struct espconn*)arg; struct espconn *pCon = (struct espconn*)arg;
RestClient *client = (RestClient *)pCon->reverse; RestClient *client = (RestClient *)pCon->reverse;
for(j=0 ;j<len; j++){ // parse status line
char c = pdata[j]; int pi = 0;
int32_t code = -1;
if(c == ' ' && !inStatus){ char statusCode[4] = "\0\0\0\0";
inStatus = 1; int statusLen = 0;
} bool inStatus = false;
if(inStatus && i < 3 && c != ' '){ while (pi < len) {
statusCode[i] = c; if (pdata[pi] == '\n') {
i++; // end of status line
if (code == -1) code = 502; // BAD GATEWAY
break;
} else if (pdata[pi] == ' ') {
if (inStatus) code = atoi(statusCode);
inStatus = !inStatus;
} else if (inStatus) {
if (statusLen < 3) statusCode[statusLen] = pdata[pi];
statusLen++;
} }
if(i == 3){ pi++;
statusCode[i] = '\0';
code = atoi(statusCode);
} }
if(httpBody){ // parse header, all this does is look for the end of the header
//only write response if its not null bool currentLineIsBlank = false;
uint32_t body_len = len - j; while (pi < len) {
os_printf("REST: status=%ld, body=%ld\n", code, body_len); if (pdata[pi] == '\n') {
if(body_len == 0){ if (currentLineIsBlank) {
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0); // body is starting
} else { pi++;
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1);
crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+j), body_len);
}
CMD_ResponseEnd(crc);
break; break;
} else {
if (c == '\n' && currentLineIsBlank) {
httpBody = true;
} }
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true; currentLineIsBlank = true;
} else if (c != '\r') { } else if (pdata[pi] != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false; currentLineIsBlank = false;
} }
pi++;
} }
//if (pi < len && pdata[pi] == '\r') pi++; // hacky!
// collect body and send it
uint16_t crc;
int body_len = len-pi;
os_printf("REST: status=%ld, body=%d\n", code, body_len);
if (pi == len) {
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0);
} else {
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1);
crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+pi), body_len);
CMD_ResponseEnd(crc);
os_printf("REST: body=");
for (int j=pi; j<len; j++) os_printf(" %02x", pdata[j]);
os_printf("\n");
} }
//if(client->security) //if(client->security)
// espconn_secure_disconnect(client->pCon); // espconn_secure_disconnect(client->pCon);
//else //else
@ -336,7 +343,8 @@ REST_Request(CmdPacket *cmd) {
// we need to allocate memory for the header plus the body. First we count the length of the // we need to allocate memory for the header plus the body. First we count the length of the
// header (including some extra counted "%s" and then we add the body length. We allocate the // header (including some extra counted "%s" and then we add the body length. We allocate the
// whole shebang and copy everything into it. // whole shebang and copy everything into it.
char *headerFmt = "%s %s HTTP/1.1\r\n" // BTW, use http/1.0 to avoid responses with transfer-encoding: chunked
char *headerFmt = "%s %s HTTP/1.0\r\n"
"Host: %s\r\n" "Host: %s\r\n"
"%s" "%s"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
@ -358,11 +366,12 @@ REST_Request(CmdPacket *cmd) {
CMD_PopArg(&req, client->data + client->data_len, realLen); CMD_PopArg(&req, client->data + client->data_len, realLen);
client->data_len += realLen; client->data_len += realLen;
} }
os_printf("\n");
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);
espconn_regist_reconcb(client->pCon, tcpclient_recon_cb); espconn_regist_reconcb(client->pCon, tcpclient_recon_cb);
os_printf("\n");
if(UTILS_StrToIP((char *)client->host, &client->pCon->proto.tcp->remote_ip)) { if(UTILS_StrToIP((char *)client->host, &client->pCon->proto.tcp->remote_ip)) {
os_printf("REST: Connect to ip %s:%ld\n",client->host, client->port); os_printf("REST: Connect to ip %s:%ld\n",client->host, client->port);

@ -28,7 +28,7 @@
<PropertyGroup> <PropertyGroup>
<NMakeOutput /> <NMakeOutput />
<NMakePreprocessorDefinitions>__ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__</NMakePreprocessorDefinitions> <NMakePreprocessorDefinitions>__ets__;_STDINT_H;ICACHE_FLASH;__MINGW32__;__WIN32__</NMakePreprocessorDefinitions>
<NMakeIncludeSearchPath>.\esp-link;.\mqtt;.\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include</NMakeIncludeSearchPath> <NMakeIncludeSearchPath>.\libraries\Time;.\esp-link;.\mqtt;.\cmd;.\serial;.\user;.\espfs;.\httpd;.\include;..\esp_iot_sdk_v1.3.0\include;..\xtensa-lx106-elf\xtensa-lx106-elf\include;c:\tools\mingw64\x86_64-w64-mingw32\include;c:\tools\mingw64\lib\gcc\x86_64-w64-mingw32\4.8.3\include</NMakeIncludeSearchPath>
<ExecutablePath /> <ExecutablePath />
<ReferencePath /> <ReferencePath />
<LibraryPath /> <LibraryPath />
@ -76,6 +76,7 @@
<ClCompile Include="httpd\base64.c" /> <ClCompile Include="httpd\base64.c" />
<ClCompile Include="httpd\httpd.c" /> <ClCompile Include="httpd\httpd.c" />
<ClCompile Include="httpd\httpdespfs.c" /> <ClCompile Include="httpd\httpdespfs.c" />
<ClCompile Include="libraries\Time\Time.c" />
<ClCompile Include="mqtt\mqtt.c" /> <ClCompile Include="mqtt\mqtt.c" />
<ClCompile Include="mqtt\mqtt_msg.c" /> <ClCompile Include="mqtt\mqtt_msg.c" />
<ClCompile Include="mqtt\proto.c" /> <ClCompile Include="mqtt\proto.c" />
@ -104,6 +105,8 @@
<ClCompile Include="user\config.c" /> <ClCompile Include="user\config.c" />
<ClCompile Include="user\log.c" /> <ClCompile Include="user\log.c" />
<ClCompile Include="user\status.c" /> <ClCompile Include="user\status.c" />
<ClCompile Include="user\user_funcs.c" />
<ClCompile Include="user\user_json.c" />
<ClCompile Include="user\user_main.c" /> <ClCompile Include="user\user_main.c" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -122,6 +125,7 @@
<ClInclude Include="include\espmissingincludes.h" /> <ClInclude Include="include\espmissingincludes.h" />
<ClInclude Include="include\uart_hw.h" /> <ClInclude Include="include\uart_hw.h" />
<ClInclude Include="include\user_config.h" /> <ClInclude Include="include\user_config.h" />
<ClInclude Include="libraries\Time\Time.h" />
<ClInclude Include="mqtt\mqtt.h" /> <ClInclude Include="mqtt\mqtt.h" />
<ClInclude Include="mqtt\mqtt_msg.h" /> <ClInclude Include="mqtt\mqtt_msg.h" />
<ClInclude Include="mqtt\proto.h" /> <ClInclude Include="mqtt\proto.h" />
@ -148,6 +152,8 @@
<ClInclude Include="user\config.h" /> <ClInclude Include="user\config.h" />
<ClInclude Include="user\log.h" /> <ClInclude Include="user\log.h" />
<ClInclude Include="user\status.h" /> <ClInclude Include="user\status.h" />
<ClInclude Include="user\user_funcs.h" />
<ClInclude Include="user\user_json.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

@ -24,7 +24,8 @@ Cgi/template routines for the /wifi url.
//#define SLEEP_MODE LIGHT_SLEEP_T //#define SLEEP_MODE LIGHT_SLEEP_T
#define SLEEP_MODE MODEM_SLEEP_T #define SLEEP_MODE MODEM_SLEEP_T
// ===== wifi status change callback // ===== wifi status change callbacks
static WifiStateChangeCb wifi_state_change_cb[4];
uint8_t wifiState = wifiIsDisconnected; uint8_t wifiState = wifiIsDisconnected;
// reasons for which a connection failed // reasons for which a connection failed
@ -54,41 +55,69 @@ static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) {
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);
#endif
statusWifiUpdate(wifiState); statusWifiUpdate(wifiState);
break; break;
case EVENT_STAMODE_DISCONNECTED: case EVENT_STAMODE_DISCONNECTED:
wifiState = wifiIsDisconnected; wifiState = wifiIsDisconnected;
wifiReason = evt->event_info.disconnected.reason; wifiReason = evt->event_info.disconnected.reason;
#ifdef WIFI_DBG
os_printf("Wifi disconnected from ssid %s, reason %s (%d)\n", os_printf("Wifi disconnected from ssid %s, reason %s (%d)\n",
evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason); evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason);
#endif
statusWifiUpdate(wifiState); statusWifiUpdate(wifiState);
break; break;
case EVENT_STAMODE_AUTHMODE_CHANGE: case EVENT_STAMODE_AUTHMODE_CHANGE:
#ifdef WIFI_DBG
os_printf("Wifi auth mode: %d -> %d\n", os_printf("Wifi auth mode: %d -> %d\n",
evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode); evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode);
#endif
break; break;
case EVENT_STAMODE_GOT_IP: case EVENT_STAMODE_GOT_IP:
wifiState = wifiGotIP; wifiState = wifiGotIP;
wifiReason = 0; wifiReason = 0;
#ifdef WIFI_DBG
os_printf("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n", os_printf("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n",
IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask), IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask),
IP2STR(&evt->event_info.got_ip.gw)); IP2STR(&evt->event_info.got_ip.gw));
#endif
statusWifiUpdate(wifiState); statusWifiUpdate(wifiState);
break; break;
case EVENT_SOFTAPMODE_STACONNECTED: case EVENT_SOFTAPMODE_STACONNECTED:
#ifdef WIFI_DBG
os_printf("Wifi AP: station " MACSTR " joined, AID = %d\n", os_printf("Wifi AP: station " MACSTR " joined, AID = %d\n",
MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid); MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid);
#endif
break; break;
case EVENT_SOFTAPMODE_STADISCONNECTED: case EVENT_SOFTAPMODE_STADISCONNECTED:
#ifdef WIFI_DBG
os_printf("Wifi AP: station " MACSTR " left, AID = %d\n", os_printf("Wifi AP: station " MACSTR " left, AID = %d\n",
MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid); MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid);
#endif
break; break;
default: default:
break; break;
} }
if (wifiStatusCb) (*wifiStatusCb)(wifiState);
for (int i = 0; i < 4; i++) {
if (wifi_state_change_cb[i] != NULL) (wifi_state_change_cb[i])(wifiState);
}
}
void ICACHE_FLASH_ATTR
wifiAddStateChangeCb(WifiStateChangeCb cb) {
for (int i = 0; i < 4; i++) {
if (wifi_state_change_cb[i] == NULL) {
wifi_state_change_cb[i] = cb;
return;
}
}
#ifdef WIFI_DBG
os_printf("WIFI: max state change cb count exceeded\n");
#endif
} }
// ===== wifi scanning // ===== wifi scanning
@ -117,7 +146,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
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);
#endif
cgiWifiAps.scanInProgress=0; cgiWifiAps.scanInProgress=0;
return; return;
} }
@ -137,7 +168,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
//Allocate memory for access point data //Allocate memory for access point data
cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n); cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n);
cgiWifiAps.noAps=n; cgiWifiAps.noAps=n;
#ifdef WIFI_DBG
os_printf("Scan done: found %d APs\n", n); os_printf("Scan done: found %d APs\n", n);
#endif
//Copy access point data to the static struct //Copy access point data to the static struct
n=0; n=0;
@ -146,7 +179,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
if (n>=cgiWifiAps.noAps) { if (n>=cgiWifiAps.noAps) {
//This means the bss_link changed under our nose. Shouldn't happen! //This means the bss_link changed under our nose. Shouldn't happen!
//Break because otherwise we will write in unallocated memory. //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); os_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps);
#endif
break; break;
} }
//Save the ap data. //Save the ap data.
@ -154,7 +189,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
cgiWifiAps.apData[n]->rssi=bss_link->rssi; cgiWifiAps.apData[n]->rssi=bss_link->rssi;
cgiWifiAps.apData[n]->enc=bss_link->authmode; cgiWifiAps.apData[n]->enc=bss_link->authmode;
strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32); 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); os_printf("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi);
#endif
bss_link = bss_link->next.stqe_next; bss_link = bss_link->next.stqe_next;
n++; n++;
@ -165,7 +202,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
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");
#endif
wifi_station_scan(NULL, wifiScanDoneCb); wifi_station_scan(NULL, wifiScanDoneCb);
} }
@ -202,7 +241,9 @@ static int ICACHE_FLASH_ATTR cgiWiFiGetScan(HttpdConnData *connData) {
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");
//os_printf("Sending %d bytes: %s\n", len, buff); #ifdef WIFI_DBG
os_printf("Sending %d bytes: %s\n", len, buff);
#endif
httpdSend(connData, buff, len); httpdSend(connData, buff, len);
return HTTPD_CGI_DONE; return HTTPD_CGI_DONE;
} }
@ -230,13 +271,17 @@ static ETSTimer resetTimer;
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");
#endif
wifi_set_opmode(1); wifi_set_opmode(1);
wifi_set_sleep_type(SLEEP_MODE); wifi_set_sleep_type(SLEEP_MODE);
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
@ -246,11 +291,15 @@ static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) {
// no more resetTimer at this point, gotta use physical reset to recover if in trouble // no more resetTimer at this point, gotta use physical reset to recover if in trouble
} else { } else {
if (m != 3) { if (m != 3) {
#ifdef WIFI_DBG
os_printf("Wifi connect failed. Going into STA+AP mode..\n"); os_printf("Wifi connect failed. Going into STA+AP mode..\n");
#endif
wifi_set_opmode(3); wifi_set_opmode(3);
} }
log_uart(true); log_uart(true);
#ifdef WIFI_DBG
os_printf("Enabling/continuing uart log\n"); os_printf("Enabling/continuing uart log\n");
#endif
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
} }
} }
@ -262,7 +311,9 @@ 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");
#endif
wifi_station_disconnect(); wifi_station_disconnect();
stconf.bssid_set = 0; stconf.bssid_set = 0;
wifi_station_set_config(&stconf); wifi_station_set_config(&stconf);
@ -288,7 +339,9 @@ int ICACHE_FLASH_ATTR cgiWiFiConnect(HttpdConnData *connData) {
//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 //Schedule disconnect/connect
os_timer_disarm(&reassTimer); os_timer_disarm(&reassTimer);
@ -327,8 +380,7 @@ static bool parse_ip(char *buff, ip_addr_t *ip_ptr) {
return false; return false;
} }
#define DEBUGIP #ifdef WIFI_DBG
#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)) {
@ -350,7 +402,9 @@ static void ICACHE_FLASH_ATTR configWifiIP() {
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);
#endif
} else { } else {
// no DHCP, we got static network config! // no DHCP, we got static network config!
wifi_station_dhcpc_stop(); wifi_station_dhcpc_stop();
@ -359,7 +413,9 @@ static void ICACHE_FLASH_ATTR configWifiIP() {
ipi.netmask.addr = flashConfig.netmask; ipi.netmask.addr = flashConfig.netmask;
ipi.gw.addr = flashConfig.gateway; ipi.gw.addr = flashConfig.gateway;
wifi_set_ip_info(0, &ipi); wifi_set_ip_info(0, &ipi);
#ifdef WIFI_DBG
os_printf("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr)); os_printf("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr));
#endif
} }
#ifdef DEBUGIP #ifdef DEBUGIP
debugIP(); debugIP();
@ -439,7 +495,9 @@ int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) {
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);
#endif
wifi_set_opmode(m&3); wifi_set_opmode(m&3);
if (m == 1) { if (m == 1) {
wifi_set_sleep_type(SLEEP_MODE); wifi_set_sleep_type(SLEEP_MODE);
@ -542,8 +600,9 @@ int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
#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;
} }
@ -567,8 +626,10 @@ int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) {
// 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]);
#endif
configWifiIP(); configWifiIP();
wifi_set_event_handler_cb(wifiHandleEventCb); wifi_set_event_handler_cb(wifiHandleEventCb);

@ -17,6 +17,5 @@ void wifiInit(void);
void wifiAddStateChangeCb(WifiStateChangeCb cb); void wifiAddStateChangeCb(WifiStateChangeCb cb);
extern uint8_t wifiState; extern uint8_t wifiState;
//extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes
#endif #endif

@ -86,7 +86,9 @@ bool ICACHE_FLASH_ATTR configSave(void) {
spi_flash_write(addr, (void *)&ff, sizeof(uint32_t)); spi_flash_write(addr, (void *)&ff, sizeof(uint32_t));
return true; return true;
fail: fail:
#ifdef CONFIG_DBG
os_printf("*** Failed to save config ***\n"); os_printf("*** Failed to save config ***\n");
#endif
return false; return false;
} }

@ -34,19 +34,19 @@
//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
//the builtInUrls below. //the builtInUrls below.
int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) { //int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) {
if (no == 0) { // if (no == 0) {
os_strcpy(user, "admin"); // os_strcpy(user, "admin");
os_strcpy(pass, "s3cr3t"); // os_strcpy(pass, "s3cr3t");
return 1; // return 1;
//Add more users this way. Check against incrementing no for each user added. //Add more users this way. Check against incrementing no for each user added.
// } else if (no==1) { // } else if (no==1) {
// os_strcpy(user, "user1"); // os_strcpy(user, "user1");
// os_strcpy(pass, "something"); // os_strcpy(pass, "something");
// return 1; // return 1;
} // }
return 0; // return 0;
} //}
/* /*

@ -1,46 +0,0 @@
<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 = [57600, 115200, 230400, 460800];
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>

@ -1,72 +0,0 @@
function fetchText(delay, repeat) {
var el = $("#console");
if (el.textEnd == undefined) {
el.textEnd = 0;
el.innerHTML = "";
}
window.setTimeout(function() {
ajaxJson('GET', console_url + "?start=" + el.textEnd,
function(resp) {
var dly = updateText(resp);
if (repeat) fetchText(dly, repeat);
},
function() { retryLoad(repeat); });
}, delay);
}
function updateText(resp) {
var el = $("#console");
var delay = 3000;
if (resp != null && resp.len > 0) {
console.log("updateText got", resp.len, "chars at", resp.start);
if (resp.start > el.textEnd) {
el.innerHTML = el.innerHTML.concat("\r\n<missing lines\r\n");
}
el.innerHTML = el.innerHTML.concat(resp.text);
el.textEnd = resp.start + resp.len;
delay = 500;
}
return delay;
}
function retryLoad(repeat) {
fetchText(1000, repeat);
}
//===== Console page
function showRate(rate) {
rates.forEach(function(r) {
var el = $("#"+r+"-button");
el.className = el.className.replace(" button-selected", "");
});
var el = $("#"+rate+"-button");
if (el != null) el.className += " button-selected";
}
function baudButton(baud) {
$("#baud-btns").appendChild(m(
' <a id="'+baud+'-button" href="#" class="pure-button">'+baud+'</a>'));
$("#"+baud+"-button").addEventListener("click", function(e) {
e.preventDefault();
ajaxSpin('POST', "/console/baud?rate="+baud,
function(resp) { showNotification("" + baud + " baud set"); showRate(baud); },
function(s, st) { showWarning("Error setting baud rate: " + st); }
);
});
}
//===== Log page
function showDbgMode(mode) {
var btns = $('.dbg-btn');
for (var i=0; i < btns.length; i++) {
if (btns[i].id === "dbg-"+mode)
addClass(btns[i], "button-selected");
else
removeClass(btns[i], "button-selected");
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

@ -1,10 +0,0 @@
<!doctype html>
<html><head>
<title>esp-link</title>
<link rel="stylesheet" href="/pure.css">
<link rel="stylesheet" href="/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/ui.js"></script>
</head>
<body>
<div id="layout">

@ -1,87 +0,0 @@
<div id="main">
<div class="header">
<div><img src="favicon.ico" height="64"><span class="jl">JEELABS</span></div>
<h1 style="margin-top:0"><span class="esp">esp</span>-link</h1>
<h2 id="version"></h2>
</div>
<div class="content">
<div class="pure-g">
<div class="pure-u-1"><div class="card">
<p>The JeeLabs esp-link firmware bridges the ESP8266 serial port to Wifi and can
program microcontrollers over the serial port, in particular Arduinos, AVRs, and
NXP's LPC800 and other ARM processors.</p>
<p style="margin-bottom:0;">Program an Arduino/AVR using avrdude using a command
line similar to:</p>
<div class="tt">/home/arduino-1.0.5/hardware/tools/avrdude \<br>
&nbsp;&nbsp;-DV -patmega328p -Pnet:esp-link.local:23 -carduino -b115200 -U \<br>
&nbsp;&nbsp;-C /home/arduino-1.0.5/hardware/tools/avrdude.conf flash:w:my_sketch.hex:i
</div>
<p>where <tt>-Pnet:esp-link.local:23</tt> tells avrdude to connect to port 23 of esp-link.
You can substitute the IP address of your esp-link for esp-link.local if necessary.</p>
<p>Please refer to
<a href="https://github.com/jeelabs/esp-link/blob/master/README.md">the online README</a>
for up-to-date help and to the forthcoming
<a href="http://jeelabs.org">JeeLabs blog</a> for an intro to the codebase.</p>
</div></div>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-2">
<div class="card">
<h1>Wifi summary</h1>
<div id="wifi-spinner" class="spinner spinner-small"></div>
<table id="wifi-table" class="pure-table pure-table-horizontal" hidden><tbody>
<tr><td>WiFi mode</td><td id="wifi-mode"></td></tr>
<tr><td>Configured network</td><td id="wifi-ssid"></td></tr>
<tr><td>Wifi channel</td><td id="wifi-chan"></td></tr>
<tr><td>Wifi status</td><td id="wifi-status"></td></tr>
<tr><td>Wifi address</td><td id="wifi-ip"></td></tr>
<tr><td>Configured hostname</td><td id="wifi-hostname"></td></tr>
</tbody> </table>
</div>
<div class="card">
<h1>TCP client</h1>
<form action="#" id="tcpform" class="pure-form">
<legend>TCP client support in esp-link</legend>
<div class="form-horizontal">
<input type="checkbox" name="tcp_enable"/>
<label>Enable serial port TCP client</label>
</div>
<br>
<legend>Grovestreams data push</legend>
<div class="form-horizontal">
<input type="checkbox" name="rssi_enable"/>
<label>Send RSSI</label>
</div>
<div class="pure-form-stacked">
<label>API key/passwd</label>
<input type="password" name="api_key"/>
</div>
<button id="tcp-button" type="submit"
class="pure-button button-primary">Change!</button>
</form>
</div>
</div>
<div class="pure-u-1 pure-u-md-1-2"><div class="card">
<h1>Pin assignment</h1>
<legend>Select one of the following signal/pin assignments to match your hardware</legend>
<fieldset class='radios' id='pin-mux'>
<div class="spinner spinner-small"></div>
</fieldset>
</div></div>
</div>
<div class="pure-g">
</div>
</div>
</div>
</div>
<script type="text/javascript">
onLoad(function() {
fetchPins();
getWifiInfo();
fetchTcpClient();
bnd($("#tcpform"), "submit", changeTcpClient);
});
</script>
</body></html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

@ -1,48 +0,0 @@
<div id="main">
<div class="header">
<h1>Debug Log</h1>
</div>
<div class="content">
<p>The debug log shows the most recent characters printed by the esp-link software itself to
its own debug log.</p>
<div class="pure-g">
<p class="pure-u-1-4">
<a id="refresh-button" class="pure-button button-primary" href="#">Refresh</a>
</p>
<p class="pure-u-3-4" style="vertical-align: baseline">
UART debug log:
<a id="dbg-auto" class="dbg-btn pure-button" href="#">auto</a>
<a id="dbg-off" class="dbg-btn pure-button" href="#">off</a>
<a id="dbg-on" class="dbg-btn pure-button" href="#">on</a>
</p>
</div>
<pre id="console" class="console" style="margin-top: 0px;"></pre>
</div>
</div>
</div>
<script type="text/javascript">console_url = "/log/text"</script>
<script src="console.js"></script>
<script type="text/javascript">
onLoad(function() {
fetchText(100, false);
$("#refresh-button").addEventListener("click", function(e) {
e.preventDefault();
fetchText(100, false);
});
["auto", "off", "on"].forEach(function(mode) {
bnd($('#dbg-'+mode), "click", function(el) {
ajaxJsonSpin('POST', "/log/dbg?mode="+mode,
function(data) { showNotification("UART mode " + data.mode); showDbgMode(data.mode); },
function(s, st) { showWarning("Error setting UART mode: " + st); }
);
});
});
ajaxJson('GET', "/log/dbg", function(data) { showDbgMode(data.mode); }, function() {});
});
</script>
</body></html>

File diff suppressed because it is too large Load Diff

@ -1,380 +0,0 @@
/* All fonts */
html, button, input, select, textarea, .pure-g [class *= "pure-u"] {
font-family: sans-serif;
}
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);
}
}

@ -1,397 +0,0 @@
//===== 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');
getWifiInfo();
}, function(s, st) {
showWarning("Error: "+st);
removeClass(cb, 'pure-button-disabled');
getWifiInfo();
});
}
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);
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

@ -1,84 +0,0 @@
<div id="main">
<div class="header">
<h1>Wifi Configuration</h1>
</div>
<div class="content">
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-2"><div class="card">
<h1>Wifi State</h1>
<div id="wifi-spinner" class="spinner spinner-small"></div>
<table id="wifi-table" class="pure-table pure-table-horizontal" hidden><tbody>
<tr><td>WiFi mode</td><td id="wifi-mode"></td></tr>
<tr><td>Wifi channel</td><td id="wifi-chan"></td></tr>
<tr><td>Configured network</td><td id="wifi-ssid"></td></tr>
<tr><td>Wifi status</td><td id="wifi-status"></td></tr>
<tr><td>Wifi address</td><td id="wifi-ip"></td></tr>
<tr><td>Wifi rssi</td><td id="wifi-rssi"></td></tr>
<tr><td>Wifi phy</td><td id="wifi-phy"></td></tr>
<tr><td>Wifi MAC</td><td id="wifi-mac"></td></tr>
<tr><td colspan="2" id="wifi-warn"></td></tr>
</tbody> </table>
</div></div>
<div class="pure-u-1 pure-u-md-1-2"><div class="card">
<h1>Wifi Association</h1>
<p id="reconnect" style="color: #600" hidden></p>
<form action="#" id="wifiform" class="pure-form pure-form-stacked">
<legend>To connect to a WiFi network, please select one of the detected networks,
enter the password, and hit the connect button...</legend>
<label>Network SSID</label>
<div id="aps">Scanning... <div class="spinner spinner-small"></div></div>
<label>WiFi password, if applicable:</label>
<input id="wifi-passwd" type="password" name="passwd" placeholder="password">
<button id="connect-button" type="submit" class="pure-button button-primary">Connect!</button>
</form>
</div></div>
</div>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-2"><div class="card">
<h1>Special Settings</h1>
<form action="#" id="specform" class="pure-form">
<legend>Special settings, use with care!</legend>
<div class="form-horizontal">
<label for="dhcp-ron" style="margin-right:1em">
<input type="radio" name="dhcp" value="on" id="dhcp-ron"/>
DHCP</label>
<label for="dhcp-roff">
<input type="radio" name="dhcp" value="off" id="dhcp-roff"/>
Static IP</label>
</div>
<div id="dhcp-on" class="pure-form-stacked">
<label>Hostname when requesting DHCP lease</label>
<input id="wifi-hostname" type="text" name="hostname"/>
</div>
<div id="dhcp-off" class="pure-form-stacked">
<label>Static IP address</label>
<input id="wifi-staticip" type="text" name="staticip"/>
<label>Netmask (for static IP)</label>
<input id="wifi-netmask" type="text" name="netmask"/>
<label>Gateway (for static IP)</label>
<input id="wifi-gateway" type="text" name="gateway"/>
</div>
<button id="special-button" type="submit"
class="pure-button button-primary">Change!</button>
</form>
</div></div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
</script>
<script src="wifi.js"></script>
<script type="text/javascript">
onLoad(function() {
getWifiInfo();
bnd($("#wifiform"), "submit", changeWifiAp);
bnd($("#specform"), "submit", changeSpecial);
bnd($("#dhcp-ron"), "click", doDhcp);
bnd($("#dhcp-roff"), "click", doStatic);
scanTimeout = window.setTimeout(scanAPs, 500);
});
</script>
</body></html>

@ -1,200 +0,0 @@
var currAp = "";
var blockScan = 0;
function createInputForAp(ap) {
if (ap.essid=="" && ap.rssi==0) return;
var input = e("input");
input.type = "radio";
input.name = "essid";
input.value=ap.essid;
input.id = "opt-" + ap.essid;
if (currAp == ap.essid) input.checked = "1";
var bars = e("div");
var rssiVal = -Math.floor(ap.rssi/51)*32;
bars.className = "lock-icon";
bars.style.backgroundPosition = "0px "+rssiVal+"px";
var rssi = e("div");
rssi.innerHTML = "" + ap.rssi +"dB";
var encrypt = e("div");
var encVal = "-64"; //assume wpa/wpa2
if (ap.enc == "0") encVal = "0"; //open
if (ap.enc == "1") encVal = "-32"; //wep
encrypt.className = "lock-icon";
encrypt.style.backgroundPosition = "-32px "+encVal+"px";
var label = e("div");
label.innerHTML = ap.essid;
var div = m('<label for=\"opt-' + ap.essid + '"></label>').childNodes[0];
div.appendChild(input);
div.appendChild(encrypt);
div.appendChild(bars);
div.appendChild(rssi);
div.appendChild(label);
return div;
}
function getSelectedEssid() {
var e = document.forms.wifiform.elements;
for (var i=0; i<e.length; i++) {
if (e[i].type == "radio" && e[i].checked) return e[i].value;
}
return currAp;
}
var scanTimeout = null;
var scanReqCnt = 0;
function scanResult() {
if (scanReqCnt > 60) {
return scanAPs();
}
scanReqCnt += 1;
ajaxJson('GET', "scan", function(data) {
currAp = getSelectedEssid();
if (data.result.inProgress == "0" && data.result.APs.length > 1) {
$("#aps").innerHTML = "";
var n = 0;
for (var i=0; i<data.result.APs.length; i++) {
if (data.result.APs[i].essid == "" && data.result.APs[i].rssi == 0) continue;
$("#aps").appendChild(createInputForAp(data.result.APs[i]));
n = n+1;
}
showNotification("Scan found " + n + " networks");
var cb = $("#connect-button");
cb.className = cb.className.replace(" pure-button-disabled", "");
if (scanTimeout != null) clearTimeout(scanTimeout);
scanTimeout = window.setTimeout(scanAPs, 20000);
} else {
window.setTimeout(scanResult, 1000);
}
}, function(s, st) {
window.setTimeout(scanResult, 5000);
});
}
function scanAPs() {
console.log("scanning now");
if (blockScan) {
scanTimeout = window.setTimeout(scanAPs, 1000);
return;
}
scanTimeout = null;
scanReqCnt = 0;
ajaxReq('POST', "scan", function(data) {
//showNotification("Wifi scan started");
window.setTimeout(scanResult, 1000);
}, function(s, st) {
//showNotification("Wifi scan may have started?");
window.setTimeout(scanResult, 1000);
});
}
function getStatus() {
ajaxJsonSpin("GET", "connstatus", function(data) {
if (data.status == "idle" || data.status == "connecting") {
$("#aps").innerHTML = "Connecting...";
showNotification("Connecting...");
window.setTimeout(getStatus, 1000);
} else if (data.status == "got IP address") {
var txt = "Connected! Got IP "+data.ip;
showNotification(txt);
showWifiInfo(data);
blockScan = 0;
if (data.modechange == "yes") {
var txt2 = "esp-link will switch to STA-only mode in a few seconds";
window.setTimeout(function() { showNotification(txt2); }, 4000);
}
$("#reconnect").removeAttribute("hidden");
$("#reconnect").innerHTML =
"If you are in the same network, go to <a href=\"http://"+data.ip+
"/\">"+data.ip+"</a>, else connect to network "+data.ssid+" first.";
} else {
blockScan = 0;
showWarning("Connection failed: " + data.status + ", " + data.reason);
$("#aps").innerHTML =
"Check password and selected AP. <a href=\"wifi.tpl\">Go Back</a>";
}
}, function(s, st) {
//showWarning("Can't get status: " + st);
window.setTimeout(getStatus, 2000);
});
}
function changeWifiMode(m) {
blockScan = 1;
hideWarning();
ajaxSpin("POST", "setmode?mode=" + m, function(resp) {
showNotification("Mode changed");
window.setTimeout(getWifiInfo, 100);
blockScan = 0;
}, function(s, st) {
showWarning("Error changing mode: " + st);
window.setTimeout(getWifiInfo, 100);
blockScan = 0;
});
}
function changeWifiAp(e) {
e.preventDefault();
var passwd = $("#wifi-passwd").value;
var essid = getSelectedEssid();
showNotification("Connecting to " + essid);
var url = "connect?essid="+encodeURIComponent(essid)+"&passwd="+encodeURIComponent(passwd);
hideWarning();
$("#reconnect").setAttribute("hidden", "");
$("#wifi-passwd").value = "";
var cb = $("#connect-button");
var cn = cb.className;
cb.className += ' pure-button-disabled';
blockScan = 1;
ajaxSpin("POST", url, function(resp) {
$("#spinner").removeAttribute('hidden'); // hack
showNotification("Waiting for network change...");
window.scrollTo(0, 0);
window.setTimeout(getStatus, 2000);
}, function(s, st) {
showWarning("Error switching network: "+st);
cb.className = cn;
window.setTimeout(scanAPs, 1000);
});
}
function changeSpecial(e) {
e.preventDefault();
var url = "special";
url += "?dhcp=" + document.querySelector('input[name="dhcp"]:checked').value;
url += "&hostname=" + encodeURIComponent($("#wifi-hostname").value);
url += "&staticip=" + encodeURIComponent($("#wifi-staticip").value);
url += "&netmask=" + encodeURIComponent($("#wifi-netmask").value);
url += "&gateway=" + encodeURIComponent($("#wifi-gateway").value);
hideWarning();
var cb = $("#special-button");
addClass(cb, 'pure-button-disabled');
ajaxSpin("POST", url, function(resp) {
removeClass(cb, 'pure-button-disabled');
getWifiInfo();
}, function(s, st) {
showWarning("Error: "+st);
removeClass(cb, 'pure-button-disabled');
getWifiInfo();
});
}
function doDhcp() {
$('#dhcp-on').removeAttribute('hidden');
$('#dhcp-off').setAttribute('hidden', '');
}
function doStatic() {
$('#dhcp-off').removeAttribute('hidden');
$('#dhcp-on').setAttribute('hidden', '');
}

@ -92,7 +92,9 @@ 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);
//os_printf("%s %s\n", connStr, what); #ifdef HTTPD_DBG
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
@ -102,7 +104,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)
{ {
#if 0 #ifdef HTTPD_DBG
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);
@ -112,7 +114,9 @@ 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;
} }
@ -130,8 +134,10 @@ 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.
@ -180,18 +186,24 @@ 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) {
//os_printf("findArg: %s\n", p); #ifdef HTTPD_DBG
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);
//os_printf("findArg: val %s len %d\n", p, (e-p)); #ifdef HTTPD_DBG
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
} }
@ -272,8 +284,10 @@ 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);
@ -286,7 +300,9 @@ 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;
} }
@ -304,7 +320,9 @@ 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?
//os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn); #ifdef HTTPD_DBG
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.
} }
@ -314,7 +332,9 @@ 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);
@ -330,7 +350,9 @@ 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.
@ -344,7 +366,9 @@ 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) {
//os_printf("Is url index %d\n", i); #ifdef HTTPD_DBG
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;
@ -355,7 +379,9 @@ 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
@ -411,15 +437,18 @@ 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;
} }
@ -438,7 +467,9 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
} else { } else {
conn->post->buffSize = conn->post->len; conn->post->buffSize = conn->post->len;
} }
//os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize); #ifdef HTTPD_DBG
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) {
@ -449,7 +480,9 @@ 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] = '-';
//os_printf("boundary = %s\n", conn->post->multipartBoundary); #ifdef HTTPD_DBG
os_printf("boundary = %s\n", conn->post->multipartBoundary);
#endif
} }
} }
} }
@ -525,7 +558,9 @@ static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) {
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);
} }
@ -537,14 +572,18 @@ 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;
//os_printf("Con req, conn=%p, pool slot %d\n", conn, i); #ifdef HTTPD_DBG
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;
} }
#if 0 #ifdef HTTPD_DBG
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);
@ -582,8 +621,9 @@ 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);

@ -1,4 +1,7 @@
// Combined include file for esp8266 // Combined include file for esp8266
#ifndef _ESP8266_H_
#define _ESP8266_H_
#include <user_config.h> #include <user_config.h>
#include <ctype.h> #include <ctype.h>
#include <stdio.h> #include <stdio.h>
@ -19,7 +22,14 @@
extern char* esp_link_version; extern char* esp_link_version;
void ICACHE_FLASH_ATTR init(void); void ICACHE_FLASH_ATTR init(void);
inline char* ICACHE_FLASH_ATTR system_get_chip_id_str(){
char *chipId = (char*)os_zalloc(9);
os_sprintf(chipId, "%06x", system_get_chip_id());
return chipId;
}
#ifdef __WIN32__ #ifdef __WIN32__
#include <_mingw.h> #include <_mingw.h>
#endif #endif
#endif // _ESP8266_H_

@ -8,13 +8,13 @@
#define MQTT_PORT 1883 #define MQTT_PORT 1883
#define MQTT_SECURITY 0 #define MQTT_SECURITY 0
#define MQTT_CLIENT_ID "esp-link" // "" #define MQTT_CLIENT_ID system_get_chip_id_str() // "esp-link"
#define MQTT_USER "" #define MQTT_USER ""
#define MQTT_PASS "" #define MQTT_PASS ""
#define MQTT_KEEPALIVE 120 // seconds #define MQTT_KEEPALIVE 120 // seconds
#define MQTT_CLSESSION true #define MQTT_CLSESSION true
#define PROTOCOL_NAMEv31 // MQTT version 3.1 compatible with Mosquitto v0.15 #define PROTOCOL_NAMEv31 // MQTT version 3.1 compatible with Mosquitto v0.15/
//PROTOCOL_NAMEv311 // MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/ //PROTOCOL_NAMEv311 // MQTT version 3.11 compatible with https://eclipse.org/paho/clients/testing/
#endif #endif

@ -0,0 +1,274 @@
#include "Time.h"
static tmElements_t tm; // a cache of time elements
static time_t cacheTime; // the time the cache was updated
static time_t syncInterval = 300; // time sync will be attempted after this many seconds
static os_timer_t micros_overflow_timer;
static uint32_t micros_at_last_overflow_tick = 0;
static uint32_t micros_overflow_count = 0;
static time_t sysTime = 0;
static time_t prevMillis = 0;
static time_t nextSyncTime = 0;
static timeStatus_t Status = timeNotSet;
getExternalTime getTimePtr;
int16_t ICACHE_FLASH_ATTR
totalMinutes(int16_t hours, int8_t minutes)
{
return (hours * 60) + minutes;
}
void ICACHE_FLASH_ATTR
micros_overflow_tick(void* arg) {
uint32_t m = system_get_time();
if (m < micros_at_last_overflow_tick)
++micros_overflow_count;
micros_at_last_overflow_tick = m;
}
unsigned long ICACHE_FLASH_ATTR
millis() {
uint32_t m = system_get_time();
uint32_t c = micros_overflow_count + ((m < micros_at_last_overflow_tick) ? 1 : 0);
return c * 4294967 + m / 1000;
}
unsigned long ICACHE_FLASH_ATTR
micros() {
return system_get_time();
}
void ICACHE_FLASH_ATTR
time_init() {
os_timer_setfn(&micros_overflow_timer, (os_timer_func_t*)&micros_overflow_tick, 0);
os_timer_arm(&micros_overflow_timer, 60000, 1);
}
void ICACHE_FLASH_ATTR
breakTime(time_t time, tmElements_t *tm){
uint8_t year;
uint8_t month, monthLength;
unsigned long days;
tm->Second = time % 60;
time /= 60; // now it is minutes
tm->Minute = time % 60;
time /= 60; // now it is hours
tm->Hour = time % 24;
time /= 24; // now it is days
tm->Wday = ((time + 4) % 7) + 1; // Sunday is day 1
year = 0;
days = 0;
while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
year++;
}
tm->Year = year; // year is offset from 1970
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // now it is days in this year, starting at 0
days = 0;
month = 0;
monthLength = 0;
for (month = 0; month<12; month++) {
if (month == 1) { // february
if (LEAP_YEAR(year)) {
monthLength = 29;
}
else {
monthLength = 28;
}
}
else {
monthLength = monthDays[month];
}
if (time >= monthLength) {
time -= monthLength;
}
else {
break;
}
}
tm->Month = month + 1; // jan is month 1
tm->Day = time + 1; // day of month
}
/*
* Convert the "timeInput" time_t count into "struct tm" time components.
* This is a more compact version of the C library localtime function.
* Note that year is offset from 1970 !!!
*/
void ICACHE_FLASH_ATTR
timet_to_tm(time_t timeInput, struct tmElements *tmel){
uint8_t year;
uint8_t month, monthLength;
uint32_t time;
unsigned long days;
time = (uint32_t)timeInput;
tmel->Second = time % 60;
time /= 60; // Now it is minutes.
tmel->Minute = time % 60;
time /= 60; // Now it is hours.
tmel->Hour = time % 24;
time /= 24; // Now it is days.
tmel->Wday = ((time + 4) % 7) + 1; // Sunday is day 1.
year = 0;
days = 0;
while ((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
year++;
}
tmel->Year = year; // The year is offset from 1970.
days -= LEAP_YEAR(year) ? 366 : 365;
time -= days; // Now it is days in this year, starting at 0.
days = 0;
month = 0;
monthLength = 0;
for (month = 0; month<12; month++) {
if (month == 1) { // February.
if (LEAP_YEAR(year)) {
monthLength = 29;
}
else {
monthLength = 28;
}
}
else {
monthLength = monthDays[month];
}
if (time >= monthLength) {
time -= monthLength;
}
else {
break;
}
}
tmel->Month = month + 1; // Jan is month 1.
tmel->Day = time + 1; // Day of month.
}
/*
* Reconstitute "struct tm" elements into a time_t count value.
* Note that the year argument is offset from 1970.
*/
time_t ICACHE_FLASH_ATTR
tm_to_timet(struct tmElements *tmel){
int i;
uint32_t seconds;
// Seconds from 1970 till 1st Jan 00:00:00 of the given year.
seconds = tmel->Year*(SECS_PER_DAY * 365);
for (i = 0; i < tmel->Year; i++) {
if (LEAP_YEAR(i)) {
seconds += SECS_PER_DAY; // Add extra days for leap years.
}
}
// Add the number of elapsed days for the given year. Months start from 1.
for (i = 1; i < tmel->Month; i++) {
if ((i == 2) && LEAP_YEAR(tmel->Year)) {
seconds += SECS_PER_DAY * 29;
}
else {
seconds += SECS_PER_DAY * monthDays[i - 1]; // "monthDay" array starts from 0.
}
}
seconds += (tmel->Day - 1) * SECS_PER_DAY; // Days...
seconds += tmel->Hour * SECS_PER_HOUR; // Hours...
seconds += tmel->Minute * SECS_PER_MIN; // Minutes...
seconds += tmel->Second; // ...and finally, Seconds.
return (time_t)seconds;
}
void ICACHE_FLASH_ATTR
refreshCache(time_t t){
if (t != cacheTime)
{
breakTime(t, &tm);
cacheTime = t;
}
}
int ICACHE_FLASH_ATTR
hour() { // the hour now
time_t t = now();
refreshCache(t);
return tm.Hour;
}
int ICACHE_FLASH_ATTR
minute() { // the minute now
time_t t = now();
refreshCache(t);
return tm.Minute;
}
int ICACHE_FLASH_ATTR
second() { // the second now
time_t t = now();
refreshCache(t);
return tm.Second;
}
void ICACHE_FLASH_ATTR
setTime(time_t t){
#ifdef TIME_DRIFT_INFO
if (sysUnsyncedTime == 0)
sysUnsyncedTime = t; // store the time of the first call to set a valid Time
#endif
sysTime = t;
nextSyncTime = t + syncInterval;
Status = timeSet;
prevMillis = millis(); // restart counting from now (thanks to Korman for this fix)
}
time_t ICACHE_FLASH_ATTR
now(){
while (millis() - prevMillis >= 1000){
sysTime++;
prevMillis += 1000;
#ifdef TIME_DRIFT_INFO
sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift
#endif
}
if (nextSyncTime <= sysTime){
if (getTimePtr != 0){
time_t t = getTimePtr();
if (t != 0)
setTime(t);
else
Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync;
}
}
return sysTime;
}
timeStatus_t ICACHE_FLASH_ATTR
timeStatus(){ // indicates if time has been set and recently synchronized
return Status;
}
void ICACHE_FLASH_ATTR
setSyncProvider(getExternalTime getTimeFunction){
getTimePtr = getTimeFunction;
nextSyncTime = sysTime;
now(); // this will sync the clock
}
void ICACHE_FLASH_ATTR
setSyncInterval(time_t interval){ // set the number of seconds between re-sync
syncInterval = interval;
}

@ -0,0 +1,130 @@
#ifndef _TIME_H_
#define _TIME_H_
#include <esp8266.h>
/*
* Constants.
*/
#define SECS_PER_MIN (60UL)
#define SECS_PER_HOUR (3600UL)
#define SECS_PER_DAY (SECS_PER_HOUR * 24UL)
#define DAYS_PER_WEEK (7UL)
#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
#define SECS_YR_2000 (946684800UL) // The time_t value at the very start of Y2K.
/*
* Days per month.
* Note that the standard time.h starts months from "1", but this
* version starts from "0".
*/
static const uint8_t monthDays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/*
* Standard "struct tm" equivalent.
* =NOTE= Differences:-
* - Year offset is from 1970 (standard tm is from 1900).
* - Wday Sunday is day "1" (standard tm has it as day "0").
* - The "Day" field is what standard tm refers to as "yday".
* - There is no "date" or Day-of-Month field.
* - There is no "isdst" field.
*/
struct tmElements {
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday; // Day of week, with Sunday as day 1.
uint8_t Day; // This is "yday" - Day of Year.
uint8_t Month;
uint8_t Year; // Offset from 1970.
};
typedef enum {
timeNotSet, timeNeedsSync, timeSet
} timeStatus_t;
typedef enum {
dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
} timeDayOfWeek_t;
typedef enum {
tmSecond, tmMinute, tmHour, tmWday, tmDay, tmMonth, tmYear, tmNbrFields
} tmByteFields;
typedef struct {
uint8_t Second;
uint8_t Minute;
uint8_t Hour;
uint8_t Wday; // day of week, sunday is day 1
uint8_t Day;
uint8_t Month;
uint8_t Year; // offset from 1970;
} tmElements_t, TimeElements, *tmElementsPtr_t;
// This leap year calulator expects year argument as years offset from 1970.
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
/*
* Convenience macros to convert to and from tm years.
*/
#define tmYearToCalendar(Y) ((Y) + 1970) // Full, four-digit year.
#define CalendarYrToTm(Y) ((Y) - 1970)
#define tmYearToY2k(Y) ((Y) - 30) // Offset from 2000.
#define y2kYearToTm(Y) ((Y) + 30)
typedef time_t(*getExternalTime)();
/*
* Useful Macros for getting elapsed time values.
*/
#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)
#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN)
#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
#define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday.
#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // The number of days since Jan 1 1970.
#define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // The number of seconds since last midnight.
/*
* The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971.
* Always set the correct time before setting alarms.
*/
#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // Time at the start of the given day.
#define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // Time at the end of the given day.
#define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // Week starts on day 1.
#define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // Time at the start of the week for the given time.
#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // Time at the end of the week for the given time.
/*
* Useful Macros for converting elapsed time to a time_t value.
*/
#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN)
#define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR)
#define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // Fixed on Jul 22 2011.
#define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK)
/*
* Time and date function defines.
* Low-level functions to convert to and from system time.
*/
int16_t totalMinutes(int16_t hours, int8_t minutes);
void breakTime(time_t time, tmElements_t* tm);
void timet_to_tm(time_t time, struct tmElements *tmel); // Convert a time_t value into "struct tm" elements.
time_t tm_to_timet(struct tmElements *tmel); // Reconstitute "struct tm" elements into a time_t value.
time_t now();
void setTime(time_t t);
int hour();
int minute();
int second();
void time_init();
unsigned long micros();
unsigned long millis();
/* time sync functions */
timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
void setSyncProvider(getExternalTime getTimeFunction); // identify the external time provider
void setSyncInterval(time_t interval); // set the number of seconds between re-sync
#endif // _TIME_H_

@ -497,7 +497,7 @@ MQTT_InitConnection(MQTT_Client* mqttClient, char* host, uint32 port, uint8_t se
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) { MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) {
uint32_t temp; uint32_t temp;
os_printf("MQTT_InitClient\n"); os_printf("MQTT_InitClient: ");
os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t)); os_memset(&mqttClient->connect_info, 0, sizeof(mqtt_connect_info_t));
@ -516,7 +516,6 @@ MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, cha
os_strcpy(mqttClient->connect_info.password, client_pass); os_strcpy(mqttClient->connect_info.password, client_pass);
mqttClient->connect_info.password[temp] = 0; mqttClient->connect_info.password[temp] = 0;
mqttClient->connect_info.keepalive = keepAliveTime; mqttClient->connect_info.keepalive = keepAliveTime;
mqttClient->connect_info.clean_session = cleanSession; mqttClient->connect_info.clean_session = cleanSession;
@ -532,6 +531,11 @@ MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, cha
system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE); system_os_task(MQTT_Task, MQTT_TASK_PRIO, mqtt_procTaskQueue, MQTT_TASK_QUEUE_SIZE);
system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient); system_os_post(MQTT_TASK_PRIO, 0, (os_param_t)mqttClient);
os_printf("client_id:%s, keepAlive:%d, cleanSession:%d\n",
mqttClient->connect_info.client_id,
mqttClient->connect_info.keepalive,
mqttClient->connect_info.clean_session
);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR

@ -71,7 +71,7 @@ typedef struct mqtt_connect_info {
char* password; char* password;
char* will_topic; char* will_topic;
char* will_message; char* will_message;
uint32_t keepalive; uint8_t keepalive;
uint8_t will_qos; uint8_t will_qos;
uint8_t will_retain; uint8_t will_retain;
uint8_t clean_session; uint8_t clean_session;

@ -70,6 +70,7 @@ slip_printable(char c) {
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
slip_reset() { slip_reset() {
//os_printf("SLIP: reset\n");
slip_inpkt = false; slip_inpkt = false;
slip_escaped = false; slip_escaped = false;
slip_len = 0; slip_len = 0;
@ -83,6 +84,7 @@ slip_parse_char(char c) {
if (slip_len > 0) console_process(slip_buf, slip_len); if (slip_len > 0) console_process(slip_buf, slip_len);
slip_reset(); slip_reset();
slip_inpkt = true; slip_inpkt = true;
os_printf("SLIP: start\n");
return; return;
} }
} else if (slip_escaped) { } else if (slip_escaped) {
@ -101,6 +103,9 @@ slip_parse_char(char c) {
return; return;
case SLIP_START: case SLIP_START:
os_printf("SLIP: got SLIP_START while in packet?\n"); os_printf("SLIP: got SLIP_START while in packet?\n");
//os_printf("SLIP: rcv %d:", slip_len);
//for (int i=0; i<slip_len; i++) os_printf(" %02x", slip_buf[i]);
//os_printf("\n");
slip_reset(); slip_reset();
return; return;
} }

@ -190,7 +190,9 @@ uart0_rx_intr_handler(void *para)
if (READ_PERI_REG(UART_INT_RAW(uart_no)) & UART_FRM_ERR_INT_RAW) { if (READ_PERI_REG(UART_INT_RAW(uart_no)) & UART_FRM_ERR_INT_RAW) {
uint32 now = system_get_time(); uint32 now = system_get_time();
if (last_frm_err == 0 || (now - last_frm_err) > one_sec) { if (last_frm_err == 0 || (now - last_frm_err) > one_sec) {
#ifdef UART_DBG
os_printf("UART framing error (bad baud rate?)\n"); os_printf("UART framing error (bad baud rate?)\n");
#endif
last_frm_err = now; last_frm_err = now;
} }
// clear rx fifo (apparently this is not optional at this point) // clear rx fifo (apparently this is not optional at this point)
@ -206,7 +208,9 @@ uart0_rx_intr_handler(void *para)
if (UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST) if (UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)
|| UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST)) || UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST))
{ {
//os_printf("stat:%02X",*(uint8 *)UART_INT_ENA(uart_no)); #ifdef UART_DBG
os_printf("stat:%02X",*(uint8 *)UART_INT_ENA(uart_no));
#endif
ETS_UART_INTR_DISABLE(); ETS_UART_INTR_DISABLE();
system_os_post(recvTaskPrio, 0, 0); system_os_post(recvTaskPrio, 0, 0);
} }
@ -229,7 +233,9 @@ uart_recvTask(os_event_t *events)
(length < 128)) { (length < 128)) {
buf[length++] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF; buf[length++] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
} }
//os_printf("%d ix %d\n", system_get_time(), length); #ifdef UART_DBG
os_printf("%d ix %d\n", system_get_time(), length);
#endif
for (int i=0; i<MAX_CB; i++) { for (int i=0; i<MAX_CB; i++) {
if (uart_recv_cb[i] != NULL) (uart_recv_cb[i])(buf, length); if (uart_recv_cb[i] != NULL) (uart_recv_cb[i])(buf, length);
@ -241,7 +247,9 @@ uart_recvTask(os_event_t *events)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
uart0_baud(int rate) { uart0_baud(int rate) {
#ifdef UART_DBG
os_printf("UART %d baud\n", rate); os_printf("UART %d baud\n", rate);
#endif
uart_div_modify(UART0, UART_CLK_FREQ / rate); uart_div_modify(UART0, UART_CLK_FREQ / rate);
} }
@ -278,7 +286,9 @@ uart_add_recv_cb(UartRecv_cb cb) {
return; return;
} }
} }
#ifdef UART_DBG
os_printf("UART: max cb count exceeded\n"); os_printf("UART: max cb count exceeded\n");
#endif
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR

@ -0,0 +1,37 @@
#include "user_funcs.h"
bool ICACHE_FLASH_ATTR pwmPinStateForSchedule(uint8_t onHour, uint8_t onMinute, uint8_t offHour, uint8_t offMinute) {
uint16_t NumMinsToday = totalMinutes(hour(), minute());
bool state = false;
if (totalMinutes(offHour, offMinute) > totalMinutes(onHour, onMinute)) {
state = (NumMinsToday >= totalMinutes(onHour, onMinute)) ? true : false;
if (NumMinsToday >= totalMinutes(offHour, offMinute))
state = false;
}
else {
state = (NumMinsToday >= totalMinutes(offHour, offMinute)) ? false : true;
if (NumMinsToday >= totalMinutes(onHour, onMinute))
state = true;
}
return state;
}
const char* ICACHE_FLASH_ATTR byteToBin(uint8_t num) {
static char b[9];
b[0] = '\0';
int z;
for (z = 128; z > 0; z >>= 1) {
strcat(b, ((num & z) == z) ? "1" : "0");
}
return b;
}
const uint8_t ICACHE_FLASH_ATTR binToByte(char* bin_str) {
char * tmp;
long x = strtol(bin_str, &tmp, 2);
return (x <= 255) ? (uint8_t)x : -1;
}

@ -0,0 +1,10 @@
#ifndef _USER_FUNCS_H_
#define _USER_FUNCS_H_
#include <esp8266.h>
#include <Time.h>
bool pwmPinStateForSchedule(uint8_t onHour, uint8_t onMinute, uint8_t offHour, uint8_t offMinute);
const char* byteToBin(uint8_t num);
const uint8_t binToByte(char* bin_str);
#endif // _USER_FUNCS_H_

@ -0,0 +1,158 @@
/******************************************************************************
* Copyright 2013-2014 Espressif Systems (Wuxi)
*
* FileName: user_json.c
*
* Description: JSON format set up and parse.
* Check your hardware transmation while use this data format.
*
* Modification history:
* 2014/5/09, v1.0 create this file.
*******************************************************************************/
#include "user_json.h"
LOCAL char* json_buf;
LOCAL int pos;
LOCAL int size;
/******************************************************************************
* FunctionName : find_json_path
* Description : find the JSON format tree's path
* Parameters : json -- A pointer to a JSON set up
* path -- A pointer to the JSON format tree's path
* Returns : A pointer to the JSON format tree
*******************************************************************************/
struct jsontree_value*ICACHE_FLASH_ATTR
find_json_path(struct jsontree_context* json, const char* path) {
struct jsontree_value* v;
const char* start;
const char* end;
int len;
v = json->values[0];
start = path;
do {
end = (const char *)os_strstr(start, "/");
if (end == start) {
break;
}
if (end != NULL) {
len = end - start;
end++;
}
else {
len = os_strlen(start);
}
if (v->type != JSON_TYPE_OBJECT) {
v = NULL;
}
else {
struct jsontree_object* o;
int i;
o = (struct jsontree_object *)v;
v = NULL;
for (i = 0; i < o->count; i++) {
if (os_strncmp(start, o->pairs[i].name, len) == 0) {
v = o->pairs[i].value;
json->index[json->depth] = i;
json->depth++;
json->values[json->depth] = v;
json->index[json->depth] = 0;
break;
}
}
}
start = end;
}
while (end != NULL && *end != '\0' && v != NULL);
json->callback_state = 0;
return v;
}
/******************************************************************************
* FunctionName : json_putchar
* Description : write the value to the JSON format tree
* Parameters : c -- the value which write the JSON format tree
* Returns : result
*******************************************************************************/
int ICACHE_FLASH_ATTR
json_putchar(int c) {
if (json_buf != NULL && pos <= size) {
json_buf[pos++] = c;
return c;
}
return 0;
}
/******************************************************************************
* FunctionName : json_ws_send
* Description : set up the JSON format tree for string
* Parameters : tree -- A pointer to the JSON format tree
* path -- A pointer to the JSON format tree's path
* pbuf -- A pointer for the data sent
* Returns : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
json_ws_send(struct jsontree_value* tree, const char* path, char* pbuf) {
struct jsontree_context json;
/* maxsize = 128 bytes */
json_buf = (char *)os_malloc(JSON_SIZE);
/* reset state and set max-size */
/* NOTE: packet will be truncated at 512 bytes */
pos = 0;
size = JSON_SIZE;
json.values[0] = (struct jsontree_value *)tree;
jsontree_reset(&json);
find_json_path(&json, path);
json.path = json.depth;
json.putchar = json_putchar;
while (jsontree_print_next(&json) && json.path <= json.depth);
json_buf[pos] = 0;
os_memcpy(pbuf, json_buf, pos);
os_free(json_buf);
}
/******************************************************************************
* FunctionName : json_parse
* Description : parse the data as a JSON format
* Parameters : js_ctx -- A pointer to a JSON set up
* ptrJSONMessage -- A pointer to the data
* Returns : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
json_parse(struct jsontree_context* json, char* ptrJSONMessage) {
/* Set value */
struct jsontree_value* v;
struct jsontree_callback* c;
struct jsontree_callback* c_bak = NULL;
while ((v = jsontree_find_next(json, JSON_TYPE_CALLBACK)) != NULL) {
c = (struct jsontree_callback *)v;
if (c == c_bak) {
continue;
}
c_bak = c;
if (c->set != NULL) {
struct jsonparse_state js;
jsonparse_setup(&js, ptrJSONMessage, os_strlen(ptrJSONMessage));
c->set(json, &js);
}
}
}

@ -0,0 +1,14 @@
#ifndef __USER_JSON_H__
#define __USER_JSON_H__
#include <esp8266.h>
#include "json/jsonparse.h"
#include "json/jsontree.h"
#define JSON_SIZE 1024
void json_parse(struct jsontree_context* json, char* ptrJSONMessage);
void json_ws_send(struct jsontree_value* tree, const char* path, char* pbuf);
int json_putchar(int c);
struct jsontree_value* find_json_path(struct jsontree_context* json, const char* path);
#endif

@ -1,31 +1,100 @@
#include <esp8266.h> #include <esp8266.h>
#include <mqtt.h> #include <mqtt.h>
#include <cgiwifi.h> #include <cgiwifi.h>
#include <json/jsontree.h>
#include <json/jsonparse.h>
#include "user_json.h"
#include "user_funcs.h"
MQTT_Client mqttClient; MQTT_Client mqttClient;
void ICACHE_FLASH_ATTR typedef struct {
mqttConnectedCb(uint32_t *args) { uint8_t fallbackStateBits;
MQTT_Client* client = (MQTT_Client*)args; uint8_t stateBits;
MQTT_Publish(client, "announce/all", "Hello World!", 0, 0); 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);
}
} }
void ICACHE_FLASH_ATTR LOCAL int ICACHE_FLASH_ATTR
mqttDisconnectedCb(uint32_t *args) { latchGet(struct jsontree_context *js_ctx) {
// MQTT_Client* client = (MQTT_Client*)args; return 0;
os_printf("MQTT Disconnected\n");
} }
void ICACHE_FLASH_ATTR LOCAL int ICACHE_FLASH_ATTR
mqttTcpDisconnectedCb(uint32_t *args) { latchSet(struct jsontree_context *js_ctx, struct jsonparse_state *parser) {
// MQTT_Client* client = (MQTT_Client*)args; int type;
os_printf("MQTT TCP Disconnected\n"); int ix = -1;
bool sendLatchUpdate = false;
while ((type = jsonparse_next(parser)) != 0) {
if (type == JSON_TYPE_ARRAY) {
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,
JSONTREE_PAIR("states", &latchCallback),
JSONTREE_PAIR("fallbackstates", &latchCallback));
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
mqttPublishedCb(uint32_t *args) { mqttConnectedCb(uint32_t *args) {
// MQTT_Client* client = (MQTT_Client*)args; MQTT_Client* client = (MQTT_Client*)args;
os_printf("MQTT Published\n"); MQTT_Publish(client, "announce/all", "Hello World!", 0, 0);
char* latchQueue = "/latch";
char *buff = (char*)os_zalloc(strlen(system_get_chip_id_str()) + strlen(latchQueue) + 1);
os_strcpy(buff, system_get_chip_id_str());
os_strcat(buff, latchQueue);
latchQueueName = buff;
MQTT_Subscribe(client, latchQueueName, 0);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
@ -41,7 +110,14 @@ 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, data: %s\n", topicBuf, dataBuf); os_printf("Receive topic: %s\n 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);
} }
@ -57,6 +133,24 @@ wifiStateChangeCb(uint8_t status)
} }
} }
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() { void init() {
wifiAddStateChangeCb(wifiStateChangeCb); wifiAddStateChangeCb(wifiStateChangeCb);
MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY); MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY);

Loading…
Cancel
Save