Working on latch

pull/47/head
Benjamin Runnels 9 years ago
commit 483800fd8d
  1. 24
      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. 99
      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,9 +90,10 @@ LED_SERIAL_PIN ?= 14
# on the release tag, make release, upload esp-link.tgz into the release files
#VERSION ?= "esp-link custom version"
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; \
else echo "development"; fi)
else echo "development"; fi)
VERSION ?=esp-link $(BRANCH) - $(DATE) - $(SHA)
# --------------- esp-link config options ---------------
@ -142,11 +143,13 @@ TARGET = httpd
APPGEN_TOOL ?= gen_appbin.py
# which modules (subdirectories) of the project to include in compiling
MODULES = espfs httpd user serial cmd mqtt esp-link
EXTRA_INCDIR = include .
LIBRARIES_DIR = libraries
MODULES = espfs httpd user serial cmd mqtt esp-link
MODULES += $(foreach sdir,$(LIBRARIES_DIR),$(wildcard $(sdir)/*))
EXTRA_INCDIR = include . include/json
# 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
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
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 \
$(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)"
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 \
"$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link
$(Q) tar zcf esp-link.tgz -C release esp-link
"$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link-$(BRANCH)
$(Q) tar zcf esp-link-$(BRANCH)-$(FLASH_SIZE).tgz -C release esp-link-$(BRANCH)
$(Q) echo "Release file: esp-link-$(BRANCH)-$(FLASH_SIZE).tgz"
$(Q) rm -rf release
clean:

@ -7,16 +7,24 @@ It implements a number of features:
- flash-programming attached Arduino/AVR microcontrollers as well as LPC800-series and other
ARM microcontrollers via Wifi
- 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
[esphttpd](http://www.esp8266.com/viewforum.php?f=34)
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)
Note that the [stable V1.0 release](https://github.com/jeelabs/esp-link/releases/tag/v1.0.0) is
recommended if you do not need the outbound TCP connections and have a 512KB flash chip.
For quick support and questions:
[![Chat at https://gitter.im/jeelabs/esp-link](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jeelabs/esp-link?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Eye Candy
---------
@ -31,7 +39,7 @@ attached microcontroller, and the pin assignments card:
Hardware info
-------------
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:
- URXD: connect to TX 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 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.
Initial flashing

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

@ -55,7 +55,7 @@ typedef enum {
CMD_REST_REQUEST,
CMD_REST_SETHEADER,
CMD_REST_EVENTS,
CMD_ADD_SENSOR, // 15
CMD_ADD_CALLBACK, // 15
CMD_SENSOR_EVENTS
} CmdName;
@ -71,19 +71,23 @@ typedef struct {
uint32_t callback;
} cmdCallback;
void ICACHE_FLASH_ATTR CMD_parse_packet(uint8_t *buf, short len);
cmdCallback* ICACHE_FLASH_ATTR CMD_GetCbByName(char* name);
// Used by slip protocol to cause parsing of a received packet
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
// 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
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
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

@ -11,18 +11,20 @@
#include "cgiwifi.h"
#include "mqtt_cmd.h"
static uint32_t ICACHE_FLASH_ATTR CMD_Null(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_IsReady(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_AddSensor(CmdPacket *cmd);
static uint32_t CMD_Null(CmdPacket *cmd);
static uint32_t CMD_IsReady(CmdPacket *cmd);
static uint32_t CMD_Reset(CmdPacket *cmd);
static uint32_t CMD_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 bool wifiCbAdded = false;
// Command dispatch table for serial -> ESP commands
const CmdList commands[] = {
{CMD_NULL, CMD_Null},
{CMD_RESET, CMD_Null},
{CMD_RESET, CMD_Reset},
{CMD_IS_READY, CMD_IsReady},
{CMD_WIFI_CONNECT, CMD_WifiConnect},
@ -37,54 +39,65 @@ const CmdList commands[] = {
{CMD_REST_REQUEST, REST_Request},
{CMD_REST_SETHEADER, REST_SetHeader},
{CMD_ADD_SENSOR, CMD_AddSensor },
{CMD_ADD_CALLBACK, CMD_AddCallback },
{CMD_NULL, NULL}
};
// WifiCb plus 10 for sensors
cmdCallback callbacks[12] = {
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 }
};
#define MAX_CALLBACKS 12
cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset
// Command handler for IsReady (healthcheck) command
static uint32_t ICACHE_FLASH_ATTR
CMD_IsReady(CmdPacket *cmd) {
#ifdef CMD_DBG
os_printf("CMD_IsReady: Check ready\n");
#endif
return 1;
}
// Command handler for Null command
static uint32_t ICACHE_FLASH_ATTR
CMD_Null(CmdPacket *cmd) {
#ifdef CMD_DBG
os_printf("CMD_Null: NULL/unsupported command\n");
#endif
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) {
char checkname[16];
os_strncpy(checkname, name, sizeof(checkname));
for (uint8_t i = 0; i < sizeof(commands); i++) {
for (uint8_t i = 0; i < MAX_CALLBACKS; i++) {
#ifdef CMD_DBG
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
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;
#ifdef CMD_DBG
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
@ -92,14 +105,20 @@ CMD_GetCbByName(char* name) {
char checkname[16];
os_strncpy(checkname, name, sizeof(checkname));
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);
#endif
// if callback doesn't exist or it's null
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);
#endif
return &callbacks[i];
}
}
#ifdef CMD_DBG
os_printf("CMD_GetCbByName: cb %s not found\n", name);
#endif
return 0;
}
@ -107,7 +126,9 @@ CMD_GetCbByName(char* name) {
static void ICACHE_FLASH_ATTR
CMD_WifiCb(uint8_t wifiStatus) {
if (wifiStatus != lastWifiStatus){
#ifdef CMD_DBG
os_printf("CMD_WifiCb: wifiStatus=%d\n", wifiStatus);
#endif
lastWifiStatus = wifiStatus;
cmdCallback *wifiCb = CMD_GetCbByName("wifiCb");
if ((uint32_t)wifiCb->callback != -1) {
@ -124,7 +145,9 @@ static uint32_t ICACHE_FLASH_ATTR
CMD_WifiConnect(CmdPacket *cmd) {
CmdRequest req;
CMD_Request(&req, cmd);
#ifdef CMD_DBG
os_printf("CMD_WifiConnect: setup argc=%ld\n", CMD_GetArgc(&req));
#endif
if(cmd->argc != 2 || cmd->callback == 0)
return 0;
@ -133,33 +156,37 @@ CMD_WifiConnect(CmdPacket *cmd) {
wifiCbAdded = true;
}
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);
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
CMD_AddSensor(CmdPacket *cmd) {
CMD_AddCallback(CmdPacket *cmd) {
CmdRequest req;
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)
return 0;
uint8_t* name;
char name[16];
uint16_t len;
// get the sensor name
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
name = (uint8_t*)os_zalloc(len + 1);
if (CMD_PopArg(&req, name, len)) return 0;
if (CMD_PopArg(&req, (uint8_t *)name, len)) return 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 1;
return CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback
}

@ -6,11 +6,13 @@ void ICACHE_FLASH_ATTR
cmdMqttConnectedCb(uint32_t* args) {
MQTT_Client* client = (MQTT_Client*)args;
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->disconnectedCb,
(void*)cb->publishedCb,
(void*)cb->dataCb);
#endif
uint16_t crc = CMD_ResponseStart(CMD_MQTT_EVENTS, cb->connectedCb, 0, 0);
CMD_ResponseEnd(crc);
}
@ -19,7 +21,9 @@ void ICACHE_FLASH_ATTR
cmdMqttTcpDisconnectedCb(uint32_t *args) {
MQTT_Client* client = (MQTT_Client*)args;
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);
CMD_ResponseEnd(crc);
}
@ -28,7 +32,9 @@ void ICACHE_FLASH_ATTR
cmdMqttDisconnectedCb(uint32_t* args) {
MQTT_Client* client = (MQTT_Client*)args;
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);
CMD_ResponseEnd(crc);
}
@ -37,7 +43,9 @@ void ICACHE_FLASH_ATTR
cmdMqttPublishedCb(uint32_t* args) {
MQTT_Client* client = (MQTT_Client*)args;
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);
CMD_ResponseEnd(crc);
}
@ -47,7 +55,9 @@ cmdMqttDataCb(uint32_t* args, const char* topic, uint32_t topic_len, const char*
uint16_t crc = 0;
MQTT_Client* client = (MQTT_Client*)args;
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_ResponseBody(crc, (uint8_t*)topic, topic_len);
crc = CMD_ResponseBody(crc, (uint8_t*)data, data_len);
@ -99,9 +109,9 @@ MQTTCMD_Setup(CmdPacket *cmd) {
// get clean session
CMD_PopArg(&req, (uint8_t*)&clean_session, 4);
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);
#ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Setup: clientid=%s, user=%s, pw=%s, keepalive=%ld, clean_session=%ld\n", client_id, user_data, pass_data, keepalive, clean_session);
#endif
// init client
// TODO: why malloc these all here, pass to MQTT_InitClient to be malloc'd again?
MQTT_InitClient(client, (char*)client_id, (char*)user_data, (char*)pass_data, keepalive, clean_session);
@ -150,8 +160,9 @@ MQTTCMD_Lwt(CmdPacket *cmd) {
uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
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;
// get topic
@ -177,12 +188,13 @@ MQTTCMD_Lwt(CmdPacket *cmd) {
// get retain
CMD_PopArg(&req, (uint8_t*)&client->connect_info.will_retain, 4);
os_printf("MQTT: MQTTCMD_Lwt topic=%s, message=%s, qos=%d, retain=%d\n",
#ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Lwt: topic=%s, message=%s, qos=%d, retain=%d\n",
client->connect_info.will_topic,
client->connect_info.will_message,
client->connect_info.will_qos,
client->connect_info.will_retain);
#endif
return 1;
}
@ -198,8 +210,9 @@ MQTTCMD_Connect(CmdPacket *cmd) {
uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
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;
// get host
@ -216,12 +229,12 @@ MQTTCMD_Connect(CmdPacket *cmd) {
// get security
CMD_PopArg(&req, (uint8_t*)&client->security, 4);
os_printf("MQTT: MQTTCMD_Connect host=%s, port=%ld, security=%d\n",
#ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Connect: host=%s, port=%ld, security=%d\n",
client->host,
client->port,
client->security);
#endif
MQTT_Connect(client);
return 1;
}
@ -238,8 +251,9 @@ MQTTCMD_Disconnect(CmdPacket *cmd) {
uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
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
MQTT_Disconnect(client);
return 1;
@ -257,8 +271,9 @@ MQTTCMD_Publish(CmdPacket *cmd) {
uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
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;
uint8_t *topic, *data;
uint32_t qos = 0, retain = 0, data_len;
@ -288,13 +303,13 @@ MQTTCMD_Publish(CmdPacket *cmd) {
// get retain
CMD_PopArg(&req, (uint8_t*)&retain, 4);
os_printf("MQTT: MQTTCMD_Publish topic=%s, data_len=%d, qos=%ld, retain=%ld\n",
#ifdef MQTT_CMD_DBG
os_printf("MQTTCMD_Publish: topic=%s, data_len=%d, qos=%ld, retain=%ld\n",
topic,
os_strlen((char*)data),
qos,
retain);
#endif
MQTT_Publish(client, (char*)topic, (char*)data, (uint8_t)qos, (uint8_t)retain);
os_free(topic);
os_free(data);
@ -313,8 +328,9 @@ MQTTCMD_Subscribe(CmdPacket *cmd) {
uint32_t client_ptr;
CMD_PopArg(&req, (uint8_t*)&client_ptr, 4);
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;
uint8_t* topic;
uint32_t qos = 0;
@ -328,8 +344,9 @@ MQTTCMD_Subscribe(CmdPacket *cmd) {
// get qos
CMD_PopArg(&req, (uint8_t*)&qos, 4);
#ifdef MQTT_CMD_DBG
os_printf("MQTT: MQTTCMD_Subscribe topic=%s, qos=%ld\n", topic, qos);
#endif
MQTT_Subscribe(client, (char*)topic, (uint8_t)qos);
os_free(topic);
return 1;

@ -25,59 +25,66 @@ tcpclient_discon_cb(void *arg) {
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
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;
RestClient *client = (RestClient *)pCon->reverse;
for(j=0 ;j<len; j++){
char c = pdata[j];
if(c == ' ' && !inStatus){
inStatus = 1;
}
if(inStatus && i < 3 && c != ' '){
statusCode[i] = c;
i++;
}
if(i == 3){
statusCode[i] = '\0';
code = atoi(statusCode);
// parse status line
int pi = 0;
int32_t code = -1;
char statusCode[4] = "\0\0\0\0";
int statusLen = 0;
bool inStatus = false;
while (pi < len) {
if (pdata[pi] == '\n') {
// 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++;
}
pi++;
}
if(httpBody){
//only write response if its not null
uint32_t body_len = len - j;
os_printf("REST: status=%ld, body=%ld\n", code, body_len);
if(body_len == 0){
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+j), body_len);
}
CMD_ResponseEnd(crc);
break;
} else {
if (c == '\n' && currentLineIsBlank) {
httpBody = true;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
// parse header, all this does is look for the end of the header
bool currentLineIsBlank = false;
while (pi < len) {
if (pdata[pi] == '\n') {
if (currentLineIsBlank) {
// body is starting
pi++;
break;
}
currentLineIsBlank = true;
} else if (pdata[pi] != '\r') {
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)
// espconn_secure_disconnect(client->pCon);
//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
// header (including some extra counted "%s" and then we add the body length. We allocate the
// 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"
"%s"
"Content-Length: %d\r\n"
@ -358,11 +366,12 @@ REST_Request(CmdPacket *cmd) {
CMD_PopArg(&req, client->data + 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;
espconn_regist_connectcb(client->pCon, tcpclient_connect_cb);
espconn_regist_reconcb(client->pCon, tcpclient_recon_cb);
os_printf("\n");
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);

@ -28,7 +28,7 @@
<PropertyGroup>
<NMakeOutput />
<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 />
<ReferencePath />
<LibraryPath />
@ -76,6 +76,7 @@
<ClCompile Include="httpd\base64.c" />
<ClCompile Include="httpd\httpd.c" />
<ClCompile Include="httpd\httpdespfs.c" />
<ClCompile Include="libraries\Time\Time.c" />
<ClCompile Include="mqtt\mqtt.c" />
<ClCompile Include="mqtt\mqtt_msg.c" />
<ClCompile Include="mqtt\proto.c" />
@ -104,6 +105,8 @@
<ClCompile Include="user\config.c" />
<ClCompile Include="user\log.c" />
<ClCompile Include="user\status.c" />
<ClCompile Include="user\user_funcs.c" />
<ClCompile Include="user\user_json.c" />
<ClCompile Include="user\user_main.c" />
</ItemGroup>
<ItemGroup>
@ -122,6 +125,7 @@
<ClInclude Include="include\espmissingincludes.h" />
<ClInclude Include="include\uart_hw.h" />
<ClInclude Include="include\user_config.h" />
<ClInclude Include="libraries\Time\Time.h" />
<ClInclude Include="mqtt\mqtt.h" />
<ClInclude Include="mqtt\mqtt_msg.h" />
<ClInclude Include="mqtt\proto.h" />
@ -148,6 +152,8 @@
<ClInclude Include="user\config.h" />
<ClInclude Include="user\log.h" />
<ClInclude Include="user\status.h" />
<ClInclude Include="user\user_funcs.h" />
<ClInclude Include="user\user_json.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

@ -24,7 +24,8 @@ Cgi/template routines for the /wifi url.
//#define SLEEP_MODE LIGHT_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;
// reasons for which a connection failed
@ -54,41 +55,69 @@ static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) {
case EVENT_STAMODE_CONNECTED:
wifiState = wifiIsConnected;
wifiReason = 0;
#ifdef WIFI_DBG
os_printf("Wifi connected to ssid %s, ch %d\n", evt->event_info.connected.ssid,
evt->event_info.connected.channel);
#endif
statusWifiUpdate(wifiState);
break;
case EVENT_STAMODE_DISCONNECTED:
wifiState = wifiIsDisconnected;
wifiReason = evt->event_info.disconnected.reason;
#ifdef WIFI_DBG
os_printf("Wifi disconnected from ssid %s, reason %s (%d)\n",
evt->event_info.disconnected.ssid, wifiGetReason(), evt->event_info.disconnected.reason);
#endif
statusWifiUpdate(wifiState);
break;
case EVENT_STAMODE_AUTHMODE_CHANGE:
#ifdef WIFI_DBG
os_printf("Wifi auth mode: %d -> %d\n",
evt->event_info.auth_change.old_mode, evt->event_info.auth_change.new_mode);
#endif
break;
case EVENT_STAMODE_GOT_IP:
wifiState = wifiGotIP;
wifiReason = 0;
#ifdef WIFI_DBG
os_printf("Wifi got ip:" IPSTR ",mask:" IPSTR ",gw:" IPSTR "\n",
IP2STR(&evt->event_info.got_ip.ip), IP2STR(&evt->event_info.got_ip.mask),
IP2STR(&evt->event_info.got_ip.gw));
#endif
statusWifiUpdate(wifiState);
break;
case EVENT_SOFTAPMODE_STACONNECTED:
#ifdef WIFI_DBG
os_printf("Wifi AP: station " MACSTR " joined, AID = %d\n",
MAC2STR(evt->event_info.sta_connected.mac), evt->event_info.sta_connected.aid);
#endif
break;
case EVENT_SOFTAPMODE_STADISCONNECTED:
#ifdef WIFI_DBG
os_printf("Wifi AP: station " MACSTR " left, AID = %d\n",
MAC2STR(evt->event_info.sta_disconnected.mac), evt->event_info.sta_disconnected.aid);
#endif
break;
default:
break;
}
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
@ -117,7 +146,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
struct bss_info *bss_link = (struct bss_info *)arg;
if (status!=OK) {
#ifdef WIFI_DBG
os_printf("wifiScanDoneCb status=%d\n", status);
#endif
cgiWifiAps.scanInProgress=0;
return;
}
@ -137,7 +168,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
//Allocate memory for access point data
cgiWifiAps.apData=(ApData **)os_malloc(sizeof(ApData *)*n);
cgiWifiAps.noAps=n;
#ifdef WIFI_DBG
os_printf("Scan done: found %d APs\n", n);
#endif
//Copy access point data to the static struct
n=0;
@ -146,7 +179,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
if (n>=cgiWifiAps.noAps) {
//This means the bss_link changed under our nose. Shouldn't happen!
//Break because otherwise we will write in unallocated memory.
#ifdef WIFI_DBG
os_printf("Huh? I have more than the allocated %d aps!\n", cgiWifiAps.noAps);
#endif
break;
}
//Save the ap data.
@ -154,7 +189,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
cgiWifiAps.apData[n]->rssi=bss_link->rssi;
cgiWifiAps.apData[n]->enc=bss_link->authmode;
strncpy(cgiWifiAps.apData[n]->ssid, (char*)bss_link->ssid, 32);
#ifdef WIFI_DBG
os_printf("bss%d: %s (%d)\n", n+1, (char*)bss_link->ssid, bss_link->rssi);
#endif
bss_link = bss_link->next.stqe_next;
n++;
@ -165,7 +202,9 @@ void ICACHE_FLASH_ATTR wifiScanDoneCb(void *arg, STATUS status) {
static ETSTimer scanTimer;
static void ICACHE_FLASH_ATTR scanStartCb(void *arg) {
#ifdef WIFI_DBG
os_printf("Starting a scan\n");
#endif
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)?"":",");
}
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);
return HTTPD_CGI_DONE;
}
@ -230,13 +271,17 @@ static ETSTimer resetTimer;
static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) {
int x = wifi_station_get_connect_status();
int m = wifi_get_opmode() & 0x3;
#ifdef WIFI_DBG
os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x);
#endif
if (x == STATION_GOT_IP) {
if (m != 1) {
#ifdef CHANGE_TO_STA
// We're happily connected, go to STA mode
#ifdef WIFI_DBG
os_printf("Wifi got IP. Going into STA mode..\n");
#endif
wifi_set_opmode(1);
wifi_set_sleep_type(SLEEP_MODE);
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
} else {
if (m != 3) {
#ifdef WIFI_DBG
os_printf("Wifi connect failed. Going into STA+AP mode..\n");
#endif
wifi_set_opmode(3);
}
log_uart(true);
#ifdef WIFI_DBG
os_printf("Enabling/continuing uart log\n");
#endif
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
}
}
@ -262,7 +311,9 @@ static ETSTimer reassTimer;
// Callback actually doing reassociation
static void ICACHE_FLASH_ATTR reassTimerCb(void *arg) {
#ifdef WIFI_DBG
os_printf("Wifi changing association\n");
#endif
wifi_station_disconnect();
stconf.bssid_set = 0;
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
os_strncpy((char*)stconf.ssid, essid, 32);
os_strncpy((char*)stconf.password, passwd, 64);
#ifdef WIFI_DBG
os_printf("Wifi try to connect to AP %s pw %s\n", essid, passwd);
#endif
//Schedule disconnect/connect
os_timer_disarm(&reassTimer);
@ -327,8 +380,7 @@ static bool parse_ip(char *buff, ip_addr_t *ip_ptr) {
return false;
}
#define DEBUGIP
#ifdef DEBUGIP
#ifdef WIFI_DBG
static void ICACHE_FLASH_ATTR debugIP() {
struct ip_info 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)
wifi_station_dhcpc_stop();
wifi_station_dhcpc_start();
#ifdef WIFI_DBG
os_printf("Wifi uses DHCP, hostname=%s\n", flashConfig.hostname);
#endif
} else {
// no DHCP, we got static network config!
wifi_station_dhcpc_stop();
@ -359,7 +413,9 @@ static void ICACHE_FLASH_ATTR configWifiIP() {
ipi.netmask.addr = flashConfig.netmask;
ipi.gw.addr = flashConfig.gateway;
wifi_set_ip_info(0, &ipi);
#ifdef WIFI_DBG
os_printf("Wifi uses static IP %d.%d.%d.%d\n", IP2STR(&ipi.ip.addr));
#endif
}
#ifdef DEBUGIP
debugIP();
@ -439,7 +495,9 @@ int ICACHE_FLASH_ATTR cgiWiFiSetMode(HttpdConnData *connData) {
len=httpdFindArg(connData->getArgs, "mode", buff, sizeof(buff));
if (len!=0) {
int m = atoi(buff);
#ifdef WIFI_DBG
os_printf("Wifi switching to mode %d\n", m);
#endif
wifi_set_opmode(m&3);
if (m == 1) {
wifi_set_sleep_type(SLEEP_MODE);
@ -542,8 +600,9 @@ int ICACHE_FLASH_ATTR cgiWiFiConnStatus(HttpdConnData *connData) {
#endif
len += os_sprintf(buff+len, "\"x\":0}\n");
#ifdef WIFI_DBG
os_printf(" -> %s\n", buff);
#endif
httpdSend(connData, buff, len);
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.
void ICACHE_FLASH_ATTR wifiInit() {
wifi_set_phy_mode(2);
#ifdef WIFI_DBG
int x = wifi_get_opmode() & 0x3;
os_printf("Wifi init, mode=%s\n", wifiMode[x]);
#endif
configWifiIP();
wifi_set_event_handler_cb(wifiHandleEventCb);

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

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

@ -34,19 +34,19 @@
//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
//the builtInUrls below.
int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) {
if (no == 0) {
os_strcpy(user, "admin");
os_strcpy(pass, "s3cr3t");
return 1;
//int myPassFn(HttpdConnData *connData, int no, char *user, int userLen, char *pass, int passLen) {
// if (no == 0) {
// os_strcpy(user, "admin");
// os_strcpy(pass, "s3cr3t");
// return 1;
//Add more users this way. Check against incrementing no for each user added.
// } else if (no==1) {
// os_strcpy(user, "user1");
// os_strcpy(pass, "something");
// 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",
tcp->remote_ip[0], tcp->remote_ip[1], tcp->remote_ip[2], tcp->remote_ip[3],
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
@ -102,7 +104,7 @@ static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) {
if (connData[i].remote_port == espconn->proto.tcp->remote_port &&
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]);
if (arg == connData[i].conn) os_printf("\n");
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.
#ifdef HTTPD_DBG
os_printf("%s *** Unknown connection 0x%p\n", connStr, arg);
#endif
return NULL;
}
@ -130,8 +134,10 @@ static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) {
uint32 dt = conn->startTime;
if (dt > 0) dt = (system_get_time() - dt)/1000;
#ifdef HTTPD_DBG
os_printf("%s Closed, %ums, heap=%ld\n", connStr, dt,
(unsigned long)system_get_free_heap_size());
#endif
}
//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;
p=line;
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)]=='=') {
p+=os_strlen(arg)+1; //move p to start of value
e=(char*)os_strstr(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);
}
p=(char*)os_strstr(p, "&");
if (p!=NULL) p+=1;
}
#ifdef HTTPD_DBG
os_printf("Finding %s in %s: Not found :/\n", arg, line);
#endif
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) {
if (len<0) len=strlen(data);
if (conn->priv->sendBuffLen+len>MAX_SENDBUFF_LEN) {
#ifdef HTTPD_DBG
os_printf("%s ERROR! httpdSend full (%d of %d)\n",
connStr, conn->priv->sendBuffLen, MAX_SENDBUFF_LEN);
#endif
return 0;
}
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) {
sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen);
if (status != 0) {
#ifdef HTTPD_DBG
os_printf("%s ERROR! espconn_sent returned %d\n", connStr, status);
#endif
}
conn->priv->sendBuffLen=0;
}
@ -304,7 +320,9 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
conn->priv->sendBuffLen=0;
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
return; //No need to call xmitSendBuff.
}
@ -314,7 +332,9 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
conn->cgi=NULL; //mark for destruction.
}
if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) {
#ifdef HTTPD_DBG
os_printf("%s ERROR! Bad CGI code %d\n", connStr, r);
#endif
conn->cgi=NULL; //mark for destruction.
}
xmitSendBuff(conn);
@ -330,7 +350,9 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
int r;
int i=0;
if (conn->url==NULL) {
#ifdef HTTPD_DBG
os_printf("%s WtF? url = NULL\n", connStr);
#endif
return; //Shouldn't happen
}
//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]=='*' &&
os_strncmp(builtInUrls[i].url, conn->url, os_strlen(builtInUrls[i].url)-1)==0) match=1;
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->cgi=builtInUrls[i].cgiCb;
conn->cgiArg=builtInUrls[i].cgiArg;
@ -355,7 +379,9 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
if (builtInUrls[i].url==NULL) {
//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.
#ifdef HTTPD_DBG
os_printf("%s %s not found. 404!\n", connStr, conn->url);
#endif
httpdSend(conn, httpNotFoundHeader, -1);
xmitSendBuff(conn);
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
int open = 0;
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,
conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, open);
#endif
//Parse out the URL part before the GET parameters.
conn->getArgs=(char*)os_strstr(conn->url, "?");
if (conn->getArgs!=0) {
*conn->getArgs=0;
conn->getArgs++;
#ifdef HTTPD_DBG
os_printf("%s args = %s\n", connStr, conn->getArgs);
#endif
} else {
conn->getArgs=NULL;
}
@ -438,7 +467,9 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
} else {
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->buffLen=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[0] = '-';
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) {
debugConn(arg, "httpdReconCb");
HttpdConnData *conn = httpdFindConnData(arg);
#ifdef HTTPD_DBG
os_printf("%s ***** reset, err=%d\n", connStr, err);
#endif
if (conn == NULL) return;
httpdRetireConn(conn);
}
@ -537,14 +572,18 @@ static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) {
int i;
//Find empty conndata in pool
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) {
#ifdef HTTPD_DBG
os_printf("%s Aiee, conn pool overflow!\n", connStr);
#endif
espconn_disconnect(conn);
return;
}
#if 0
#ifdef HTTPD_DBG
int num = 0;
for (int j=0; j<MAX_CONN; j++) if (connData[j].conn != NULL) num++;
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;
httpdConn.proto.tcp=&httpdTcp;
builtInUrls=fixedUrls;
#ifdef HTTPD_DBG
os_printf("Httpd init, conn=%p\n", &httpdConn);
#endif
espconn_regist_connectcb(&httpdConn, httpdConnectCb);
espconn_accept(&httpdConn);
espconn_tcp_set_max_con_allow(&httpdConn, MAX_CONN);

@ -1,4 +1,7 @@
// Combined include file for esp8266
#ifndef _ESP8266_H_
#define _ESP8266_H_
#include <user_config.h>
#include <ctype.h>
#include <stdio.h>
@ -19,7 +22,14 @@
extern char* esp_link_version;
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__
#include <_mingw.h>
#endif
#endif // _ESP8266_H_

@ -8,13 +8,13 @@
#define MQTT_PORT 1883
#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_PASS ""
#define MQTT_KEEPALIVE 120 // seconds
#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/
#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
MQTT_InitClient(MQTT_Client* mqttClient, char* client_id, char* client_user, char* client_pass, uint8_t keepAliveTime, uint8_t cleanSession) {
uint32_t temp;
os_printf("MQTT_InitClient\n");
os_printf("MQTT_InitClient: ");
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);
mqttClient->connect_info.password[temp] = 0;
mqttClient->connect_info.keepalive = keepAliveTime;
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_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

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

@ -70,6 +70,7 @@ slip_printable(char c) {
static void ICACHE_FLASH_ATTR
slip_reset() {
//os_printf("SLIP: reset\n");
slip_inpkt = false;
slip_escaped = false;
slip_len = 0;
@ -83,6 +84,7 @@ slip_parse_char(char c) {
if (slip_len > 0) console_process(slip_buf, slip_len);
slip_reset();
slip_inpkt = true;
os_printf("SLIP: start\n");
return;
}
} else if (slip_escaped) {
@ -101,6 +103,9 @@ slip_parse_char(char c) {
return;
case SLIP_START:
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();
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) {
uint32 now = system_get_time();
if (last_frm_err == 0 || (now - last_frm_err) > one_sec) {
#ifdef UART_DBG
os_printf("UART framing error (bad baud rate?)\n");
#endif
last_frm_err = now;
}
// 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)
|| 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();
system_os_post(recvTaskPrio, 0, 0);
}
@ -229,7 +233,9 @@ uart_recvTask(os_event_t *events)
(length < 128)) {
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++) {
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
uart0_baud(int rate) {
#ifdef UART_DBG
os_printf("UART %d baud\n", rate);
#endif
uart_div_modify(UART0, UART_CLK_FREQ / rate);
}
@ -278,7 +286,9 @@ uart_add_recv_cb(UartRecv_cb cb) {
return;
}
}
#ifdef UART_DBG
os_printf("UART: max cb count exceeded\n");
#endif
}
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 <mqtt.h>
#include <cgiwifi.h>
#include <json/jsontree.h>
#include <json/jsonparse.h>
#include "user_json.h"
#include "user_funcs.h"
MQTT_Client mqttClient;
void ICACHE_FLASH_ATTR
mqttConnectedCb(uint32_t *args) {
MQTT_Client* client = (MQTT_Client*)args;
MQTT_Publish(client, "announce/all", "Hello World!", 0, 0);
typedef struct {
uint8_t fallbackStateBits;
uint8_t stateBits;
uint8_t init;
uint8_t fallbackSecondsForBits[8];
} LatchState;
static LatchState latch;
static void ICACHE_FLASH_ATTR
updateLatch() {
os_printf("ESP: Latch Callback\n");
cmdCallback* latchCb = CMD_GetCbByName("Latch");
if (latchCb->callback != -1) {
// uint16_t crc = CMD_ResponseStart(CMD_SENSOR_EVENTS, (uint32_t)&latchCb->callback, 0, 1);
// crc = CMD_ResponseBody(crc, (uint8_t*)&latch, sizeof(LatchState));
// CMD_ResponseEnd(crc);
}
}
void ICACHE_FLASH_ATTR
mqttDisconnectedCb(uint32_t *args) {
// MQTT_Client* client = (MQTT_Client*)args;
os_printf("MQTT Disconnected\n");
LOCAL int ICACHE_FLASH_ATTR
latchGet(struct jsontree_context *js_ctx) {
return 0;
}
void ICACHE_FLASH_ATTR
mqttTcpDisconnectedCb(uint32_t *args) {
// MQTT_Client* client = (MQTT_Client*)args;
os_printf("MQTT TCP Disconnected\n");
LOCAL int ICACHE_FLASH_ATTR
latchSet(struct jsontree_context *js_ctx, struct jsonparse_state *parser) {
int type;
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
mqttPublishedCb(uint32_t *args) {
// MQTT_Client* client = (MQTT_Client*)args;
os_printf("MQTT Published\n");
mqttConnectedCb(uint32_t *args) {
MQTT_Client* client = (MQTT_Client*)args;
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
@ -41,7 +110,14 @@ mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *da
os_memcpy(dataBuf, data, data_len);
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(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() {
wifiAddStateChangeCb(wifiStateChangeCb);
MQTT_InitConnection(&mqttClient, MQTT_HOST, MQTT_PORT, MQTT_SECURITY);

Loading…
Cancel
Save