From d698b94ac6ce6420879cef36f64078e8d9e71e81 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 7 Sep 2015 23:38:07 -0700 Subject: [PATCH] fix flash config CRC; add MQTT web page --- esp-link/cgi.c | 75 ++++++++++++++++++----------------- esp-link/cgimqtt.c | 89 +++++++++++++++++++++++++++++++++++++++++ esp-link/cgimqtt.h | 8 ++++ esp-link/config.c | 21 ++++++---- esp-link/config.h | 5 +++ esp-link/main.c | 9 ++++- html/mqtt.html | 99 ++++++++++++++++++++++++++++++++++++++++++++++ html/style.css | 4 ++ html/ui.js | 69 +++++++++++++++++++++++++++++++- mqtt/mqtt.c | 19 +++++---- 10 files changed, 342 insertions(+), 56 deletions(-) create mode 100644 esp-link/cgimqtt.c create mode 100644 esp-link/cgimqtt.h create mode 100644 html/mqtt.html diff --git a/esp-link/cgi.c b/esp-link/cgi.c index 8f87d38..3a37347 100644 --- a/esp-link/cgi.c +++ b/esp-link/cgi.c @@ -20,12 +20,12 @@ Some random cgi routines. void ICACHE_FLASH_ATTR jsonHeader(HttpdConnData *connData, int code) { - httpdStartResponse(connData, code); - httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); - httpdHeader(connData, "Pragma", "no-cache"); - httpdHeader(connData, "Expires", "0"); - httpdHeader(connData, "Content-Type", "application/json"); - httpdEndHeaders(connData); + httpdStartResponse(connData, code); + httpdHeader(connData, "Cache-Control", "no-cache, no-store, must-revalidate"); + httpdHeader(connData, "Pragma", "no-cache"); + httpdHeader(connData, "Expires", "0"); + httpdHeader(connData, "Content-Type", "application/json"); + httpdEndHeaders(connData); } #define TOKEN(x) (os_strcmp(token, x) == 0) @@ -33,40 +33,43 @@ jsonHeader(HttpdConnData *connData, int code) { // Handle system information variables and print their value, returns the number of // characters appended to buff int ICACHE_FLASH_ATTR printGlobalInfo(char *buff, int buflen, char *token) { - if (TOKEN("si_chip_id")) { - return os_sprintf(buff, "0x%x", system_get_chip_id()); - } else if (TOKEN("si_freeheap")) { - return os_sprintf(buff, "%dKB", system_get_free_heap_size()/1024); - } else if (TOKEN("si_uptime")) { - uint32 t = system_get_time() / 1000000; // in seconds - return os_sprintf(buff, "%dd%dh%dm%ds", t/(24*3600), (t/(3600))%24, (t/60)%60, t%60); - } else if (TOKEN("si_boot_version")) { - return os_sprintf(buff, "%d", system_get_boot_version()); - } else if (TOKEN("si_boot_address")) { - return os_sprintf(buff, "0x%x", system_get_userbin_addr()); - } else if (TOKEN("si_cpu_freq")) { - return os_sprintf(buff, "%dMhz", system_get_cpu_freq()); - } else { - return 0; - } + if (TOKEN("si_chip_id")) { + return os_sprintf(buff, "0x%x", system_get_chip_id()); + } else if (TOKEN("si_freeheap")) { + return os_sprintf(buff, "%dKB", system_get_free_heap_size()/1024); + } else if (TOKEN("si_uptime")) { + uint32 t = system_get_time() / 1000000; // in seconds + return os_sprintf(buff, "%dd%dh%dm%ds", t/(24*3600), (t/(3600))%24, (t/60)%60, t%60); + } else if (TOKEN("si_boot_version")) { + return os_sprintf(buff, "%d", system_get_boot_version()); + } else if (TOKEN("si_boot_address")) { + return os_sprintf(buff, "0x%x", system_get_userbin_addr()); + } else if (TOKEN("si_cpu_freq")) { + return os_sprintf(buff, "%dMhz", system_get_cpu_freq()); + } else { + return 0; + } } #endif extern char *esp_link_version; // in user_main.c int ICACHE_FLASH_ATTR cgiMenu(HttpdConnData *connData) { - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - char buff[1024]; - // don't use jsonHeader so the response does get cached - httpdStartResponse(connData, 200); - httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate"); - httpdHeader(connData, "Content-Type", "application/json"); - httpdEndHeaders(connData); - // construct json response - os_sprintf(buff, - "{\"menu\": [\"Home\", \"/home.html\", \"Wifi\", \"/wifi/wifi.html\"," - "\"\xC2\xB5" "C Console\", \"/console.html\", \"Debug log\", \"/log.html\" ],\n" - " \"version\": \"%s\" }", esp_link_version); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + char buff[1024]; + // don't use jsonHeader so the response does get cached + httpdStartResponse(connData, 200); + httpdHeader(connData, "Cache-Control", "max-age=3600, must-revalidate"); + httpdHeader(connData, "Content-Type", "application/json"); + httpdEndHeaders(connData); + // construct json response + os_sprintf(buff, + "{\"menu\": [\"Home\", \"/home.html\", " + "\"Wifi\", \"/wifi/wifi.html\"," + "\"\xC2\xB5" "C Console\", \"/console.html\", " + "\"REST/MQTT\", \"/mqtt.html\"," + "\"Debug log\", \"/log.html\" ],\n" + " \"version\": \"%s\" }", esp_link_version); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; } diff --git a/esp-link/cgimqtt.c b/esp-link/cgimqtt.c new file mode 100644 index 0000000..dbfbc14 --- /dev/null +++ b/esp-link/cgimqtt.c @@ -0,0 +1,89 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// // TCP Client settings + +#include +#include "cgi.h" +#include "config.h" +#include "cgimqtt.h" + +// Cgi to return MQTT settings +int ICACHE_FLASH_ATTR cgiMqttGet(HttpdConnData *connData) { + char buff[2048]; + int len; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + len = os_sprintf(buff, "{ " + "\"slip-enable\":%d, " + "\"mqtt-enable\":%d, " + "\"mqtt-status-enable\":%d, " + "\"mqtt-port\":%d, " + "\"mqtt-host\":\"%s\", " + "\"mqtt-client-id\":\"%s\", " + "\"mqtt-username\":\"%s\", " + "\"mqtt-password\":\"%s\", " + "\"mqtt-status-topic\":\"%s\", " + "\"mqtt-state\":\"%s\" }", + flashConfig.slip_enable, flashConfig.mqtt_enable, flashConfig.mqtt_status_enable, + flashConfig.mqtt_port, flashConfig.mqtt_hostname, flashConfig.mqtt_client, + flashConfig.mqtt_username, flashConfig.mqtt_password, + flashConfig.mqtt_status_topic, "connected"); + + jsonHeader(connData, 200); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; +} + +// Cgi to change choice of pin assignments +int ICACHE_FLASH_ATTR cgiMqttSet(HttpdConnData *connData) { + if (connData->conn==NULL) return HTTPD_CGI_DONE; + +#if 0 + // Handle tcp_enable flag + char buff[128]; + int len = httpdFindArg(connData->getArgs, "tcp_enable", buff, sizeof(buff)); + if (len <= 0) { + jsonHeader(connData, 400); + return HTTPD_CGI_DONE; + } + flashConfig.tcp_enable = os_strcmp(buff, "true") == 0; + + // Handle rssi_enable flag + len = httpdFindArg(connData->getArgs, "rssi_enable", buff, sizeof(buff)); + if (len <= 0) { + jsonHeader(connData, 400); + return HTTPD_CGI_DONE; + } + flashConfig.rssi_enable = os_strcmp(buff, "true") == 0; + + // Handle api_key flag + len = httpdFindArg(connData->getArgs, "api_key", buff, sizeof(buff)); + if (len < 0) { + jsonHeader(connData, 400); + return HTTPD_CGI_DONE; + } + buff[sizeof(flashConfig.api_key)-1] = 0; // ensure we don't get an overrun + os_strcpy(flashConfig.api_key, buff); +#endif + + if (configSave()) { + httpdStartResponse(connData, 200); + httpdEndHeaders(connData); + } else { + httpdStartResponse(connData, 500); + httpdEndHeaders(connData); + httpdSend(connData, "Failed to save config", -1); + } + return HTTPD_CGI_DONE; +} + +int ICACHE_FLASH_ATTR cgiMqtt(HttpdConnData *connData) { + if (connData->requestType == HTTPD_METHOD_GET) { + return cgiMqttGet(connData); + } else if (connData->requestType == HTTPD_METHOD_POST) { + return cgiMqttSet(connData); + } else { + jsonHeader(connData, 404); + return HTTPD_CGI_DONE; + } +} diff --git a/esp-link/cgimqtt.h b/esp-link/cgimqtt.h new file mode 100644 index 0000000..54a6011 --- /dev/null +++ b/esp-link/cgimqtt.h @@ -0,0 +1,8 @@ +#ifndef CGIMQTT_H +#define CGIMQTT_H + +#include "httpd.h" + +int cgiMqtt(HttpdConnData *connData); + +#endif diff --git a/esp-link/config.c b/esp-link/config.c index b44b329..80ab55e 100644 --- a/esp-link/config.c +++ b/esp-link/config.c @@ -5,26 +5,27 @@ #include #include "config.h" #include "espfs.h" - -// hack: this from LwIP -extern uint16_t inet_chksum(void *dataptr, uint16_t len); +#include "crc16.h" FlashConfig flashConfig; FlashConfig flashDefault = { 33, 0, 0, MCU_RESET_PIN, MCU_ISP_PIN, LED_CONN_PIN, LED_SERIAL_PIN, 115200, - "esp-link\0 ", // hostname + "esp-link\0", // hostname 0, 0x00ffffff, 0, // static ip, netmask, gateway 0, // log mode 0, // swap uart (don't by default) 1, 0, // tcp_enable, rssi_enable "\0", // api_key + 0, 0, 0, // slip_enable, mqtt_enable, mqtt_status_enable + 1833, // mqtt port + "\0", "\0", "\0", "\0", "\0", // mqtt host, client, user, password, status-topic }; typedef union { FlashConfig fc; - uint8_t block[128]; + uint8_t block[1024]; } FlashFull; // magic number to recognize thet these are our flash settings as opposed to some random stuff @@ -63,7 +64,7 @@ bool ICACHE_FLASH_ATTR configSave(void) { ff.fc.crc = 0; //os_printf("cksum of: "); //memDump(&ff, sizeof(ff)); - ff.fc.crc = inet_chksum(&ff, sizeof(ff)); + ff.fc.crc = crc16_data((unsigned char*)&ff, sizeof(ff), 0); //os_printf("cksum is %04x\n", ff.fc.crc); // write primary with incorrect seq ff.fc.seq = 0xffffffff; @@ -121,12 +122,16 @@ static int ICACHE_FLASH_ATTR selectPrimary(FlashFull *ff0, FlashFull *ff1) { // check CRC of ff0 uint16_t crc = ff0->fc.crc; ff0->fc.crc = 0; - bool ff0_crc_ok = inet_chksum(ff0, sizeof(FlashFull)) == crc; + bool ff0_crc_ok = crc16_data((unsigned char*)ff0, sizeof(FlashFull), 0) == crc; + + os_printf("FLASH chk=0x%04x crc=0x%04x full_sz=%d sz=%d\n", + crc16_data((unsigned char*)ff0, sizeof(FlashFull), 0), + crc, sizeof(FlashFull), sizeof(FlashConfig)); // check CRC of ff1 crc = ff1->fc.crc; ff1->fc.crc = 0; - bool ff1_crc_ok = inet_chksum(ff1, sizeof(FlashFull)) == crc; + bool ff1_crc_ok = crc16_data((unsigned char*)ff1, sizeof(FlashFull), 0) == crc; // decided which we like better if (ff0_crc_ok) diff --git a/esp-link/config.h b/esp-link/config.h index 18284a5..89287e6 100644 --- a/esp-link/config.h +++ b/esp-link/config.h @@ -16,6 +16,11 @@ typedef struct { uint8_t swap_uart; // swap uart0 to gpio 13&15 uint8_t tcp_enable, rssi_enable; // TCP client settings char api_key[48]; // RSSI submission API key (Grovestreams for now) + uint8_t slip_enable, mqtt_enable, // SLIP protocol, MQTT client + mqtt_status_enable; // MQTT status reporting + uint16_t mqtt_port; + char mqtt_hostname[32], mqtt_client[48], mqtt_username[32], mqtt_password[32]; + char mqtt_status_topic[32]; } FlashConfig; extern FlashConfig flashConfig; diff --git a/esp-link/main.c b/esp-link/main.c index 12d715e..aea1846 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -17,6 +17,7 @@ #include "cgiwifi.h" #include "cgipins.h" #include "cgitcp.h" +#include "cgimqtt.h" #include "cgiflash.h" #include "auth.h" #include "espfs.h" @@ -90,6 +91,7 @@ HttpdBuiltInUrl builtInUrls[] = { { "/wifi/special", cgiWiFiSpecial, NULL }, { "/pins", cgiPins, NULL }, { "/tcpclient", cgiTcp, NULL }, + { "/mqtt", cgiMqtt, NULL }, { "*", cgiEspFsHook, NULL }, //Catch-all cgi function for the filesystem { NULL, NULL, NULL } @@ -124,6 +126,9 @@ extern void app_init(void); // Main routine to initialize esp-link. void user_init(void) { + uart_init(115200, 115200); + logInit(); // must come after init of uart + os_delay_us(10000L); // get the flash config so we know how to init things //configWipe(); // uncomment to reset the config for testing purposes bool restoreOk = configRestore(); @@ -131,8 +136,8 @@ void user_init(void) { gpio_init(); gpio_output_set(0, 0, 0, (1<<15)); // some people tie it GND, gotta ensure it's disabled // init UART - uart_init(flashConfig.baud_rate, 115200); - logInit(); // must come after init of uart +// uart_init(flashConfig.baud_rate, 115200); +// logInit(); // must come after init of uart // say hello (leave some time to cause break in TX after boot loader's msg os_delay_us(10000L); os_printf("\n\n** %s\n", esp_link_version); diff --git a/html/mqtt.html b/html/mqtt.html new file mode 100644 index 0000000..dd9653c --- /dev/null +++ b/html/mqtt.html @@ -0,0 +1,99 @@ +
+
+

REST & MQTT

+
+ +
+
+
+

The REST & MQTT support uses the SLIP protocol over the serial port to enable + the attached microcontroller to initiate outbound connections. + The REST support lets the uC initiate simple HTTP requests while the MQTT support + lets it communicate with an MQTT server bidirectionally at QoS 0 thru 2.

+

The MQTT support is in the form of a built-in client that connects to a server + using parameters set below and stored in esp-link's flash settings. This allows + esp-link to take care of connection parameters and disconnect/reconnect operations.

+

The MQTT client also supports sending periodic status messages about esp-link itself, + including Wifi RSSI, and free heap memory.

+
+ + +
+
+
+
+
+
+

MQTT +
+

+ +
+
+
+
+

Status reporting +
+

+ +
+
+

REST

+

REST requests are enabled as soon as SLIP is enabled. + There are no REST-specific settings.

+ +
+
+
+
+
+ + + + diff --git a/html/style.css b/html/style.css index 29d15a3..ca072d8 100644 --- a/html/style.css +++ b/html/style.css @@ -3,6 +3,10 @@ html, button, input, select, textarea, .pure-g [class *= "pure-u"] { font-family: sans-serif; } +input[type="text"], input[type="password"] { + width: 100%; +} + body { color: #777; } diff --git a/html/ui.js b/html/ui.js index 56cc256..2a4e20e 100644 --- a/html/ui.js +++ b/html/ui.js @@ -374,11 +374,11 @@ function changeTcpClient(e) { addClass(cb, 'pure-button-disabled'); ajaxSpin("POST", url, function(resp) { removeClass(cb, 'pure-button-disabled'); - getWifiInfo(); + fetchTcpClient(); }, function(s, st) { showWarning("Error: "+st); removeClass(cb, 'pure-button-disabled'); - getWifiInfo(); + fetchTcpClient(); }); } @@ -394,4 +394,69 @@ function fetchTcpClient() { }); } +//===== MQTT cards + +function changeMqtt(e) { + e.preventDefault(); + var url = "mqtt?1=1"; + var i, inputs = $("input"); + for (i=0; i 0; + else el.value = data[v]; + } + }); + $("#mqtt-spinner").setAttribute("hidden", ""); + $("#mqtt-status-spinner").setAttribute("hidden", ""); + $("#mqtt-form").removeAttribute("hidden"); + $("#mqtt-status-form").removeAttribute("hidden"); + + var i, inputs = $("input"); + for (i=0; ipending_buffer->data, client->pending_buffer->filled); } - os_printf("MQTT: Recv type=%d id=%04X len=%d; Pend type=%d id=%02X\n", - msg_type, msg_id, msg_len, pending_msg_type, pending_msg_id); + os_printf("MQTT: Recv type=%s id=%04X len=%d; Pend type=%s id=%02X\n", + mqtt_msg_type[msg_type], msg_id, msg_len, mqtt_msg_type[pending_msg_type], pending_msg_id); switch (msg_type) { case MQTT_MSG_TYPE_CONNACK: @@ -177,7 +182,7 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_PUBREC: // rec for a publish we sent if (pending_msg_type == MQTT_MSG_TYPE_PUBLISH && pending_msg_id == msg_id) { - os_printf("MQTT: Recv PUBREC, cont QoS2 publish\n"); + os_printf("MQTT: QoS2 publish cont\n"); client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); // we need to send PUBREL mqtt_msg_pubrel(&client->mqtt_connection, msg_id); @@ -202,7 +207,6 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { if (msg_qos == 1) mqtt_msg_puback(&client->mqtt_connection, msg_id); if (msg_qos == 2) mqtt_msg_pubrec(&client->mqtt_connection, msg_id); if (msg_qos == 1 || msg_qos == 2) { - os_printf("MQTT: Queue response QoS: %d\n", msg_qos); mqtt_enq_message(client, client->mqtt_connection.message.data, client->mqtt_connection.message.length); } @@ -213,7 +217,7 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { case MQTT_MSG_TYPE_PUBREL: // rel for a rec we sent (originally publish received) if (pending_msg_type == MQTT_MSG_TYPE_PUBREC && pending_msg_id == msg_id) { - os_printf("MQTT: Recv PUBREL, cont QoS2 recv\n"); + os_printf("MQTT: Cont QoS2 recv\n"); client->pending_buffer = PktBuf_ShiftFree(client->pending_buffer); // we need to send PUBCOMP mqtt_msg_pubcomp(&client->mqtt_connection, msg_id); @@ -223,7 +227,6 @@ mqtt_tcpclient_recv(void* arg, char* pdata, unsigned short len) { break; case MQTT_MSG_TYPE_PINGRESP: - os_printf("MQTT: Recv PINGRESP\n"); client->keepAliveAckTick = 0; break; } @@ -299,7 +302,7 @@ mqtt_timer(void* arg) { // check whether we need to send a keep-alive message if (client->keepAliveTick > 0 && --client->keepAliveTick == 0) { // timeout: we need to send a ping message - os_printf("MQTT: Send keepalive to %s:%d\n", client->host, client->port); + //os_printf("MQTT: Send keepalive\n"); mqtt_msg_pingreq(&client->mqtt_connection); PktBuf *buf = PktBuf_New(client->mqtt_connection.message.length); os_memcpy(buf->data, client->mqtt_connection.message.data, @@ -432,7 +435,7 @@ mqtt_send_message(MQTT_Client* client) { // get some details about the message uint16_t msg_type = mqtt_get_type(buf->data); uint8_t msg_id = mqtt_get_id(buf->data, buf->filled); - os_printf("MQTT: Send type=%d, id=%04X len=%d\n", msg_type, msg_id, buf->filled); + os_printf("MQTT: Send type=%s id=%04X len=%d\n", mqtt_msg_type[msg_type], msg_id, buf->filled); #if 0 for (int i=0; ifilled; i++) { if (buf->data[i] >= ' ' && buf->data[i] <= '~') os_printf("%c", buf->data[i]);