From 2fbd9953d592be54fc8e54652f2612a9ae1787f6 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Sat, 1 Aug 2015 13:56:13 -0700 Subject: [PATCH] TCP client support; Grovestreams RSSI submission support --- html/home.html | 66 ++++++----- html/ui.js | 39 +++++++ httpd/httpd.c | 6 +- serial/serbridge.c | 66 +++++++++-- serial/tcpclient.c | 272 ++++++++++++++++++++++++++------------------- serial/tcpclient.h | 10 +- user/cgipins.c | 3 +- user/cgitcp.c | 74 ++++++++++++ user/cgitcp.h | 8 ++ user/config.c | 13 ++- user/config.h | 3 + user/status.c | 102 ++++------------- user/user_main.c | 7 +- 13 files changed, 422 insertions(+), 247 deletions(-) create mode 100644 user/cgitcp.c create mode 100644 user/cgitcp.h diff --git a/html/home.html b/html/home.html index 35c8436..ddc8e4a 100644 --- a/html/home.html +++ b/html/home.html @@ -26,16 +26,40 @@
-
-

Wifi summary

-
- - - - - - -
+
+
+

Wifi summary

+
+ + + + + + +
+
+

TCP client

+
+ TCP client support in esp-link +
+ + +
+
+ Grovestreams data push +
+ + +
+
+ + +
+ +
+
+

Pin assignment

Select one of the following signal/pin assignments to match your hardware @@ -45,26 +69,6 @@
-
-

HTTP service

-
- External web service configuration -
- - - - -
-
- - - - -
- -
-
@@ -74,6 +78,8 @@ onLoad(function() { fetchPins(); getWifiInfo(); + fetchTcpClient(); + bnd($("#tcpform"), "submit", changeTcpClient); }); diff --git a/html/ui.js b/html/ui.js index 5de2f4c..56cc256 100644 --- a/html/ui.js +++ b/html/ui.js @@ -356,3 +356,42 @@ function setPins(v, name) { }); } +//===== 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); + }); +} + + diff --git a/httpd/httpd.c b/httpd/httpd.c index 3249889..fe22047 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -314,13 +314,13 @@ static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { conn->cgi=NULL; //mark for destruction. } if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { - os_printf("%s ERROR! CGI fn returns code %d after sending data! Bad CGI!\n", connStr, r); + os_printf("%s ERROR! Bad CGI code %d\n", connStr, r); conn->cgi=NULL; //mark for destruction. } xmitSendBuff(conn); } -static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-httpd/"HTTPDVER"\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n"; +static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n"; //This is called when the headers have been received and the connection is ready to send //the result headers and data. @@ -419,7 +419,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { if (conn->getArgs!=0) { *conn->getArgs=0; conn->getArgs++; - os_printf("%s GET args = %s\n", connStr, conn->getArgs); + os_printf("%s args = %s\n", connStr, conn->getArgs); } else { conn->getArgs=NULL; } diff --git a/serial/serbridge.c b/serial/serbridge.c index 528c486..ca6e0af 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -263,9 +263,13 @@ enum { TC_idle, // in-between commands TC_newline, // newline seen TC_start, // start character (~) seen - TC_cmd, // command character (0) seen + TC_cmd, // command start (@) seen + TC_cmdChar, // command character seen TC_cmdLine, // accumulating command - TC_tdata, // data character (1-9) seen, and in text data mode + TC_tdchan, // saw data channel character + TC_tdlen1, // saw first data length character + TC_tdata0, // accumulate data, zero-terminated + TC_tdataN, // accumulate data, length-terminated }; static uint8_t tcState = TC_newline; static uint8_t tcChan; // channel for current command (index into tcConn) @@ -274,6 +278,7 @@ static uint8_t tcChan; // channel for current command (index into tcConn) static char tcCmdBuf[CMD_MAX]; static short tcCmdBufLen = 0; static char tcCmdChar; +static short tcLen; // scan a buffer for tcp client commands static int ICACHE_FLASH_ATTR @@ -291,17 +296,27 @@ tcpClientProcess(char *buf, int len) if (c == '~') tcState = TC_start; continue; // gobble up the ~ case TC_start: // saw ~, expect channel number - if (c == '0') { + if (c == '@') { tcState = TC_cmd; continue; - } else if (c >= '1' && c <= '9') { - tcChan = c-'1'; - tcState = TC_tdata; + } else if (c >= '0' && c <= '9') { + tcChan = c-'0'; + tcState = TC_tdchan; continue; } *out++ = '~'; // make up for '~' we skipped break; - case TC_cmd: // saw channel number 0 (command), expect command char + case TC_cmd: // saw control char (@), expect channel char + if (c >= '0' && c <= '9') { + tcChan = c-'0'; + tcState = TC_cmdChar; + continue; + } else { + *out++ = '~'; // make up for '~' we skipped + *out++ = '@'; // make up for '@' we skipped + break; + } + case TC_cmdChar: // saw channel number, expect command char tcCmdChar = c; // save command character tcCmdBufLen = 0; // empty the command buffer tcState = TC_cmdLine; @@ -310,11 +325,36 @@ tcpClientProcess(char *buf, int len) if (c != '\n') { if (tcCmdBufLen < CMD_MAX) tcCmdBuf[tcCmdBufLen++] = c; } else { - tcpClientCommand(tcCmdChar, tcCmdBuf); + tcpClientCommand(tcChan, tcCmdChar, tcCmdBuf); tcState = TC_newline; } continue; - case TC_tdata: // saw channel number, getting data characters + case TC_tdchan: // saw channel number, getting first length char + if (c >= '0' && c <= '9') { + tcLen = c-'0'; + } else if (c >= 'A' && c <= 'F') { + tcLen = c-'A'+10; + } else { + *out++ = '~'; // make up for '~' we skipped + *out++ = '0'+tcChan; + break; + } + tcState = TC_tdlen1; + continue; + case TC_tdlen1: // saw first length char, get second + tcLen *= 16; + if (c >= '0' && c <= '9') { + tcLen += c-'0'; + } else if (c >= 'A' && c <= 'F') { + tcLen += c-'A'+10; + } else { + *out++ = '~'; // make up for '~' we skipped + *out++ = '0'+tcChan; + break; + } + tcState = tcLen == 0 ? TC_tdata0 : TC_tdataN; + continue; + case TC_tdata0: // saw data length, getting data characters zero-terminated if (c != 0) { tcpClientSendChar(tcChan, c); } else { @@ -322,6 +362,14 @@ tcpClientProcess(char *buf, int len) tcState = TC_idle; } continue; + case TC_tdataN: // saw data length, getting data characters length-terminated + tcpClientSendChar(tcChan, c); + tcLen--; + if (tcLen == 0) { + tcpClientSendPush(tcChan); + tcState = TC_idle; + } + continue; } *out++ = c; } diff --git a/serial/tcpclient.c b/serial/tcpclient.c index 9580b18..3515459 100644 --- a/serial/tcpclient.c +++ b/serial/tcpclient.c @@ -1,4 +1,8 @@ // Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// +// TCP client library allowing uControllers attached to the serial port to send commands +// to open/close TCP connections and send/recv data. +// The serial protocol is described in https://gist.github.com/tve/a46c44bf1f6b42bc572e #include #include "config.h" @@ -7,7 +11,7 @@ #include "tcpclient.h" // max number of channels the client can open -#define MAX_CHAN 8 +#define MAX_CHAN MAX_TCP_CHAN // size of tx buffer #define MAX_TXBUF 1024 @@ -28,8 +32,54 @@ typedef struct { enum TcpState state; } TcpConn; -#define MAX_CONN (8) -static TcpConn tcpConn[MAX_CONN]; +static TcpConn tcpConn[MAX_CHAN]; + +// forward declarations +static void tcpConnFree(TcpConn* tci); +static TcpConn* tcpConnAlloc(uint8_t chan); +static void tcpDoSend(TcpConn *tci); +static void tcpConnectCb(void *arg); +static void tcpDisconCb(void *arg); +static void tcpResetCb(void *arg, sint8 err); +static void tcpSentCb(void *arg); +static void tcpRecvCb(void *arg, char *data, uint16_t len); + +//===== allocate / free connections + +// Allocate a new connection dynamically and return it. Returns NULL if buf alloc failed +static TcpConn* ICACHE_FLASH_ATTR +tcpConnAlloc(uint8_t chan) { + TcpConn *tci = tcpConn+chan; + if (tci->state != TCP_idle && tci->conn != NULL) return tci; + + // malloc and return espconn struct + tci->conn = os_malloc(sizeof(struct espconn)); + if (tci->conn == NULL) goto fail; + memset(tci->conn, 0, sizeof(struct espconn)); + // malloc esp_tcp struct + tci->tcp = os_malloc(sizeof(esp_tcp)); + if (tci->tcp == NULL) goto fail; + memset(tci->tcp, 0, sizeof(esp_tcp)); + + // common init + tci->state = TCP_dns; + tci->conn->type = ESPCONN_TCP; + tci->conn->state = ESPCONN_NONE; + tci->conn->proto.tcp = tci->tcp; + tci->tcp->remote_port = 80; + espconn_regist_connectcb(tci->conn, tcpConnectCb); + espconn_regist_reconcb(tci->conn, tcpResetCb); + espconn_regist_sentcb(tci->conn, tcpSentCb); + espconn_regist_recvcb(tci->conn, tcpRecvCb); + espconn_regist_disconcb(tci->conn, tcpDisconCb); + tci->conn->reverse = tci; + + return tci; + +fail: + tcpConnFree(tci); + return NULL; +} // Free a connection dynamically. static void ICACHE_FLASH_ATTR @@ -41,6 +91,8 @@ tcpConnFree(TcpConn* tci) { memset(tci, 0, sizeof(TcpConn)); } +//===== DNS + // DNS name resolution callback static void ICACHE_FLASH_ATTR tcpClientHostnameCb(const char *name, ip_addr_t *ipaddr, void *arg) { @@ -66,12 +118,59 @@ tcpClientHostnameCb(const char *name, ip_addr_t *ipaddr, void *arg) { tcpConnFree(tci); } +//===== Connect / disconnect + +// Connected callback +static void ICACHE_FLASH_ATTR +tcpConnectCb(void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP connect CB (%p %p)\n", arg, tci); + tci->state = TCP_data; + // send any buffered data + if (tci->txBuf != NULL && tci->txBufLen > 0) tcpDoSend(tci); + // reply to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dC\n", tci-tcpConn); + uart0_tx_buffer(buf, l); +} + +// Disconnect callback +static void ICACHE_FLASH_ATTR tcpDisconCb(void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP disconnect CB (%p %p)\n", arg, tci); + // notify to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dZ\n", tci-tcpConn); + uart0_tx_buffer(buf, l); + // free + tcpConnFree(tci); +} + +// Connection reset callback +static void ICACHE_FLASH_ATTR tcpResetCb(void *arg, sint8 err) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP reset CB (%p %p) err=%d\n", arg, tci, err); + // notify to serial + char buf[6]; + short l = os_sprintf(buf, "\n~@%dZ\n", tci-tcpConn); + uart0_tx_buffer(buf, l); + // free + tcpConnFree(tci); +} + +//===== Sending and receiving + // Send the next buffer (assumes that the connection is in a state that allows it) -static void tcpDoSend(TcpConn *tci) { +static void ICACHE_FLASH_ATTR +tcpDoSend(TcpConn *tci) { sint8 err = espconn_sent(tci->conn, (uint8*)tci->txBuf, tci->txBufLen); if (err == ESPCONN_OK) { // send successful os_printf("TCP sent (%p %p)\n", tci->conn, tci); + tci->txBuf[tci->txBufLen] = 0; os_printf("TCP data: %s\n", tci->txBuf); tci->txBufSent = tci->txBuf; tci->txBuf = NULL; tci->txBufLen = 0; @@ -81,18 +180,9 @@ static void tcpDoSend(TcpConn *tci) { } } -// Connected callback -static void ICACHE_FLASH_ATTR tcpConnectCb(void *arg) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP connect CB (%p %p)\n", arg, tci); - tci->state = TCP_data; - // send any buffered data - if (tci->txBuf != NULL && tci->txBufLen > 0) tcpDoSend(tci); -} - // Sent callback -static void ICACHE_FLASH_ATTR tcpSentCb(void *arg) { +static void ICACHE_FLASH_ATTR +tcpSentCb(void *arg) { struct espconn *conn = arg; TcpConn *tci = conn->reverse; os_printf("TCP sent CB (%p %p)\n", arg, tci); @@ -111,84 +201,71 @@ static void ICACHE_FLASH_ATTR tcpRecvCb(void *arg, char *data, uint16_t len) { TcpConn *tci = conn->reverse; os_printf("TCP recv CB (%p %p)\n", arg, tci); if (tci->state == TCP_data) { + uint8_t chan; + for (chan=0; chan= MAX_CHAN) return; // oops!? + char buf[6]; + short l = os_sprintf(buf, "\n~%d", chan); + uart0_tx_buffer(buf, l); uart0_tx_buffer(data, len); + uart0_tx_buffer("\0\n", 2); } serledFlash(50); // short blink on serial LED } -// Disconnect callback -static void ICACHE_FLASH_ATTR tcpDisconCb(void *arg) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP disconnect CB (%p %p)\n", arg, tci); - tcpConnFree(tci); -} - -// Connection reset callback -static void ICACHE_FLASH_ATTR tcpResetCb(void *arg, sint8 err) { - struct espconn *conn = arg; - TcpConn *tci = conn->reverse; - os_printf("TCP reset CB (%p %p) err=%d\n", arg, tci, err); - tcpConnFree(tci); -} +void ICACHE_FLASH_ATTR +tcpClientSendChar(uint8_t chan, char c) { + TcpConn *tci = tcpConn+chan; + if (tci->state == TCP_idle) return; -// Allocate a new connection dynamically and return it. Returns NULL if no -// connection could be allocated. -static TcpConn* ICACHE_FLASH_ATTR -tcpConnAlloc(void) { - int i; - for (i=0; itxBuf != NULL) { + // we have a buffer + if (tci->txBufLen < MAX_TXBUF) { + // buffer has space, add char and return + tci->txBuf[tci->txBufLen++] = c; + return; + } else if (tci->txBufSent == NULL) { + // we don't have a send pending, send full buffer off + if (tci->state == TCP_data) tcpDoSend(tci); + if (tci->txBuf != NULL) return; // something went wrong + } else { + // buffers all backed-up, drop char + return; + } } - if (i == MAX_CONN) return NULL; - - // found an empty slot, malloc and return espconn struct - TcpConn *tci = tcpConn+i; - tci->conn = os_malloc(sizeof(struct espconn)); - if (tci->conn == NULL) goto fail; - memset(tci->conn, 0, sizeof(struct espconn)); - // malloc esp_tcp struct - tci->tcp = os_malloc(sizeof(esp_tcp)); - if (tci->tcp == NULL) goto fail; - memset(tci->tcp, 0, sizeof(esp_tcp)); - - // common init - tci->state = TCP_dns; - tci->conn->type = ESPCONN_TCP; - tci->conn->state = ESPCONN_NONE; - tci->conn->proto.tcp = tci->tcp; - tci->tcp->remote_port = 80; - tci->tcp->remote_ip[0] = 173; - espconn_regist_connectcb(tci->conn, tcpConnectCb); - espconn_regist_reconcb(tci->conn, tcpResetCb); - espconn_regist_sentcb(tci->conn, tcpSentCb); - espconn_regist_recvcb(tci->conn, tcpRecvCb); - espconn_regist_disconcb(tci->conn, tcpDisconCb); - tci->conn->reverse = tci; - - return tci; + // we do not have a buffer (either didn't have one or sent it off) + // allocate one + tci->txBuf = os_malloc(MAX_TXBUF); + tci->txBufLen = 0; + if (tci->txBuf != NULL) { + tci->txBuf[tci->txBufLen++] = c; + } +} -fail: - tcpConnFree(tci); - return NULL; +void ICACHE_FLASH_ATTR +tcpClientSendPush(uint8_t chan) { + TcpConn *tci = tcpConn+chan; + if (tci->state != TCP_data) return; // no active connection on this channel + if (tci->txBuf == NULL || tci->txBufLen == 0) return; // no chars accumulated to send + if (tci->txBufSent != NULL) return; // already got a send in progress + tcpDoSend(tci); } -static TcpConn *tcConn[MAX_CHAN]; +//===== Command parsing // Perform a TCP command: parse the command and do the right thing. // Returns true on success. bool ICACHE_FLASH_ATTR -tcpClientCommand(char cmd, char *cmdBuf) { - uint8_t tcChan; +tcpClientCommand(uint8_t chan, char cmd, char *cmdBuf) { TcpConn *tci; + char *hostname; + char *port; switch (cmd) { //== TCP Connect command case 'T': - if (*cmdBuf < '1' || *cmdBuf > ('0'+MAX_CHAN)) break; - tcChan = *cmdBuf++ - '1'; - char *hostname = cmdBuf; - char *port = hostname; + hostname = cmdBuf; + port = hostname; while (*port != 0 && *port != ':') port++; if (*port != ':') break; *port = 0; @@ -197,13 +274,13 @@ tcpClientCommand(char cmd, char *cmdBuf) { if (portInt < 1 || portInt > 65535) break; // allocate a connection - tci = tcpConnAlloc(); + tci = tcpConnAlloc(chan); if (tci == NULL) break; tci->state = TCP_dns; tci->tcp->remote_port = portInt; // start the DNS resolution - os_printf("TCP %p resolving %s (conn=%p)\n", tci, hostname, tci->conn); + os_printf("TCP %p resolving %s for chan %d (conn=%p)\n", tci, hostname, chan ,tci->conn); ip_addr_t ip; err_t err = espconn_gethostbyname(tci->conn, hostname, &ip, tcpClientHostnameCb); if (err == ESPCONN_OK) { @@ -215,14 +292,12 @@ tcpClientCommand(char cmd, char *cmdBuf) { break; } - tcConn[tcChan] = tci; return true; //== TCP Close/disconnect command case 'C': - if (*cmdBuf < '1' || *cmdBuf > ('0'+MAX_CHAN)) break; - tcChan = *cmdBuf++ - '1'; - tci = tcConn[tcChan]; + os_printf("TCP closing chan %d\n", chan); + tci = tcpConn+chan; if (tci->state > TCP_idle) { tci->state = TCP_idle; // hackish... espconn_disconnect(tci->conn); @@ -233,40 +308,3 @@ tcpClientCommand(char cmd, char *cmdBuf) { return false; } -void ICACHE_FLASH_ATTR -tcpClientSendChar(uint8_t chan, char c) { - TcpConn *tci = tcConn[chan]; - if (tci->state == TCP_idle) return; - - if (tci->txBuf != NULL) { - // we have a buffer - if (tci->txBufLen < MAX_TXBUF) { - // buffer has space, add char and return - tci->txBuf[tci->txBufLen++] = c; - return; - } else if (tci->txBufSent == NULL) { - // we don't have a send pending, send full buffer off - tcpDoSend(tci); - if (tci->txBuf != NULL) return; // something went wrong - } else { - // buffers all backed-up, drop char - return; - } - } - // we do not have a buffer (either didn't have one or sent it off) - // allocate one - tci->txBuf = os_malloc(MAX_TXBUF); - tci->txBufLen = 0; - if (tci->txBuf != NULL) { - tci->txBuf[tci->txBufLen++] = c; - } -} - -void ICACHE_FLASH_ATTR -tcpClientSendPush(uint8_t chan) { - TcpConn *tci = tcConn[chan]; - if (tci->state == TCP_idle) return; // no active connection on this channel - if (tci->txBuf == NULL || tci->txBufLen == 0) return; // no chars accumulated to send - if (tci->txBufSent != NULL) return; // already got a send in progress - tcpDoSend(tci); -} diff --git a/serial/tcpclient.h b/serial/tcpclient.h index c80d2d3..9bfeb01 100644 --- a/serial/tcpclient.h +++ b/serial/tcpclient.h @@ -1,8 +1,16 @@ #ifndef __TCP_CLIENT_H__ #define __TCP_CLIENT_H__ -bool tcpClientCommand(char cmd, char *cmdBuf); +// max number of channels the client can open +#define MAX_TCP_CHAN 8 + +// Parse and perform the commandm cmdBuf must be null-terminated +bool tcpClientCommand(uint8_t chan, char cmd, char *cmdBuf); + +// Append a character to the specified channel void tcpClientSendChar(uint8_t chan, char c); + +// Enqueue the buffered characters for transmission on the specified channel void tcpClientSendPush(uint8_t chan); #endif /* __TCP_CLIENT_H__ */ diff --git a/user/cgipins.c b/user/cgipins.c index cfee33f..459f03e 100644 --- a/user/cgipins.c +++ b/user/cgipins.c @@ -1,3 +1,4 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt #include #include "cgi.h" @@ -93,11 +94,9 @@ int ICACHE_FLASH_ATTR cgiPinsSet(HttpdConnData *connData) { statusInit(); if (configSave()) { - os_printf("New config saved\n"); httpdStartResponse(connData, 200); httpdEndHeaders(connData); } else { - os_printf("*** Failed to save config ***\n"); httpdStartResponse(connData, 500); httpdEndHeaders(connData); httpdSend(connData, "Failed to save config", -1); diff --git a/user/cgitcp.c b/user/cgitcp.c new file mode 100644 index 0000000..f8d9036 --- /dev/null +++ b/user/cgitcp.c @@ -0,0 +1,74 @@ +// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt +// // TCP Client settings + +#include +#include "cgi.h" +#include "config.h" +#include "cgitcp.h" + +// Cgi to return TCP client settings +int ICACHE_FLASH_ATTR cgiTcpGet(HttpdConnData *connData) { + char buff[1024]; + int len; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + len = os_sprintf(buff, "{ \"tcp_enable\":%d, \"rssi_enable\": %d, \"api_key\":\"%s\" }", + flashConfig.tcp_enable, flashConfig.rssi_enable, flashConfig.api_key); + + jsonHeader(connData, 200); + httpdSend(connData, buff, len); + return HTTPD_CGI_DONE; +} + +// Cgi to change choice of pin assignments +int ICACHE_FLASH_ATTR cgiTcpSet(HttpdConnData *connData) { + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + // 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); + + 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 cgiTcp(HttpdConnData *connData) { + if (connData->requestType == HTTPD_METHOD_GET) { + return cgiTcpGet(connData); + } else if (connData->requestType == HTTPD_METHOD_POST) { + return cgiTcpSet(connData); + } else { + jsonHeader(connData, 404); + return HTTPD_CGI_DONE; + } +} diff --git a/user/cgitcp.h b/user/cgitcp.h new file mode 100644 index 0000000..2b7b2a9 --- /dev/null +++ b/user/cgitcp.h @@ -0,0 +1,8 @@ +#ifndef CGITCP_H +#define CGITCP_H + +#include "httpd.h" + +int cgiTcp(HttpdConnData *connData); + +#endif diff --git a/user/config.c b/user/config.c index b840c8f..6023f5d 100644 --- a/user/config.c +++ b/user/config.c @@ -17,6 +17,9 @@ FlashConfig flashDefault = { "esp-link\0 ", // hostname 0, 0x00ffffff, 0, // static ip, netmask, gateway 0, // log mode + 0, // swap_uart + 1, 0, // tcp_enable, rssi_enable + "\0", // api_key }; typedef union { @@ -40,7 +43,6 @@ static void memDump(void *addr, int len) { #endif bool ICACHE_FLASH_ATTR configSave(void) { - FlashFull ff; memset(&ff, 0, sizeof(ff)); memcpy(&ff, &flashConfig, sizeof(FlashConfig)); @@ -48,7 +50,7 @@ bool ICACHE_FLASH_ATTR configSave(void) { // erase secondary uint32_t addr = FLASH_ADDR + (1-flash_pri)*FLASH_SECT; if (spi_flash_erase_sector(addr>>12) != SPI_FLASH_RESULT_OK) - return false; // no harm done, give up + goto fail; // no harm done, give up // calculate CRC ff.fc.seq = seq; ff.fc.magic = FLASH_MAGIC; @@ -60,11 +62,11 @@ bool ICACHE_FLASH_ATTR configSave(void) { // write primary with incorrect seq ff.fc.seq = 0xffffffff; if (spi_flash_write(addr, (void *)&ff, sizeof(ff)) != SPI_FLASH_RESULT_OK) - return false; // no harm done, give up + goto fail; // no harm done, give up // fill in correct seq ff.fc.seq = seq; if (spi_flash_write(addr, (void *)&ff, sizeof(uint32_t)) != SPI_FLASH_RESULT_OK) - return false; // most likely failed, but no harm if successful + goto fail; // most likely failed, but no harm if successful // now that we have safely written the new version, erase old primary addr = FLASH_ADDR + flash_pri*FLASH_SECT; flash_pri = 1-flash_pri; @@ -77,6 +79,9 @@ bool ICACHE_FLASH_ATTR configSave(void) { ff.fc.seq = seq; spi_flash_write(addr, (void *)&ff, sizeof(uint32_t)); return true; +fail: + os_printf("*** Failed to save config ***\n"); + return false; } void ICACHE_FLASH_ATTR configWipe(void) { diff --git a/user/config.h b/user/config.h index c326a37..51b0995 100644 --- a/user/config.h +++ b/user/config.h @@ -9,6 +9,9 @@ typedef struct { char hostname[32]; // if using DHCP uint32_t staticip, netmask, gateway; // using DHCP if staticip==0 uint8_t log_mode; // UART log debug mode + 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) } FlashConfig; extern FlashConfig flashConfig; diff --git a/user/status.c b/user/status.c index 7b46d72..6d7747e 100644 --- a/user/status.c +++ b/user/status.c @@ -4,6 +4,7 @@ #include "config.h" #include "serled.h" #include "cgiwifi.h" +#include "tcpclient.h" //===== "CONN" LED status indication @@ -72,101 +73,44 @@ void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { static ETSTimer rssiTimer; -static uint8_t rssiSendState = 0; -static sint8 rssiLast = 0; //last RSSI value - -static esp_tcp rssiTcp; -static struct espconn rssiConn; - -#define GS_API_KEY "2eb868a8-224f-3faa-939d-c79bd605912a" -#define GS_COMP_ID "esp-link" #define GS_STREAM "rssi" -// Connected callback -static void ICACHE_FLASH_ATTR rssiConnectCb(void *arg) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI connect CB (%p %p)\n", arg, conn->reverse); +// Timer callback to send an RSSI update to a monitoring system +static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { + if (!flashConfig.rssi_enable || !flashConfig.tcp_enable || flashConfig.api_key[0]==0) + return; + + sint8 rssi = wifi_station_get_rssi(); + if (rssi >= 0) return; // not connected or other error - char buf[2000]; + // compose TCP command + uint8_t chan = MAX_TCP_CHAN-1; + tcpClientCommand(chan, 'T', "grovestreams.com:80"); - // http header + // compose http header + char buf[1024]; int hdrLen = os_sprintf(buf, "PUT /api/feed?api_key=%s HTTP/1.0\r\n" "Content-Type: application/json\r\n" "Content-Length: XXXXX\r\n\r\n", - GS_API_KEY); + flashConfig.api_key); // http body int dataLen = os_sprintf(buf+hdrLen, - "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]", - GS_COMP_ID, GS_STREAM, rssiLast); + "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]\r", + flashConfig.hostname, GS_STREAM, rssi); + buf[hdrLen+dataLen++] = 0; + buf[hdrLen+dataLen++] = '\r'; // hackish way to fill in the content-length os_sprintf(buf+hdrLen-9, "%5d", dataLen); - buf[hdrLen-4] = '\r'; - - // send it off - if (espconn_sent(conn, (uint8*)buf, hdrLen+dataLen) == ESPCONN_OK) { - os_printf("RSSI sent rssi=%d\n", rssiLast); - os_printf("RSSI sent <<%s>>\n", buf); - } -} - -// Sent callback -static void ICACHE_FLASH_ATTR rssiSentCb(void *arg) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI sent CB (%p %p)\n", arg, conn->reverse); -} + buf[hdrLen-4] = '\r'; // fix-up the \0 inserted by sprintf (hack!) -// Recv callback -static void ICACHE_FLASH_ATTR rssiRecvCb(void *arg, char *data, uint16_t len) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI recv CB (%p %p)\n", arg, conn->reverse); - data[len] = 0; // hack!!! - os_printf("GOT %d: <<%s>>\n", len, data); - - espconn_disconnect(conn); -} - -// Disconnect callback -static void ICACHE_FLASH_ATTR rssiDisconCb(void *arg) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI disconnect CB (%p %p)\n", arg, conn->reverse); - rssiSendState = 0; -} - -// Connection reset callback -static void ICACHE_FLASH_ATTR rssiResetCb(void *arg, sint8 err) { - struct espconn *conn = (struct espconn *)arg; - os_printf("RSSI reset CB (%p %p) err=%d\n", arg, conn->reverse, err); - rssiSendState = 0; -} - -// Timer callback to send an RSSI update to a monitoring system -static void ICACHE_FLASH_ATTR rssiTimerCb(void *v) { - sint8 rssi = wifi_station_get_rssi(); - if (rssi >= 0) return; // not connected or other error - rssiLast = rssi; - if (rssiSendState > 0) return; // not done with previous rssi report - - rssiConn.type = ESPCONN_TCP; - rssiConn.state = ESPCONN_NONE; - rssiConn.proto.tcp = &rssiTcp; - rssiTcp.remote_port = 80; - rssiTcp.remote_ip[0] = 173; - rssiTcp.remote_ip[1] = 236; - rssiTcp.remote_ip[2] = 12; - rssiTcp.remote_ip[3] = 163; - espconn_regist_connectcb(&rssiConn, rssiConnectCb); - espconn_regist_reconcb(&rssiConn, rssiResetCb); - espconn_regist_sentcb(&rssiConn, rssiSentCb); - espconn_regist_recvcb(&rssiConn, rssiRecvCb); - espconn_regist_disconcb(&rssiConn, rssiDisconCb); - rssiConn.reverse = (void *)0xdeadf00d; - os_printf("RSSI connect (%p)\n", &rssiConn); - if (espconn_connect(&rssiConn) == ESPCONN_OK) { - rssiSendState++; + // send the request off and forget about it... + for (short i=0; i