From 886ce62df0bd1d3df8f932ed7409bb026c6a66d6 Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Tue, 28 Jul 2015 11:29:20 -0700 Subject: [PATCH] initial support for TCP client connections from uC --- Makefile | 2 +- html/home.html | 22 ++++ serial/serbridge.c | 261 ++++++++++++++++++++++++++++--------------- serial/serbridge.h | 8 +- serial/tcpclient.c | 272 +++++++++++++++++++++++++++++++++++++++++++++ serial/tcpclient.h | 8 ++ user/cgiflash.c | 1 + user/status.c | 119 +++++++++++++++++++- 8 files changed, 597 insertions(+), 96 deletions(-) create mode 100644 serial/tcpclient.c create mode 100644 serial/tcpclient.h diff --git a/Makefile b/Makefile index 99d5617..32cce5c 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ ESPBAUD ?= 460800 # --------------- chipset configuration --------------- # Pick your flash size: "512KB" or "4MB" -FLASH_SIZE ?= 4MB +FLASH_SIZE ?= 512KB ifeq ("$(FLASH_SIZE)","512KB") # Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 diff --git a/html/home.html b/html/home.html index 59e9ab2..35c8436 100644 --- a/html/home.html +++ b/html/home.html @@ -44,6 +44,28 @@ +
+
+

HTTP service

+
+ External web service configuration +
+ + + + +
+
+ + + + +
+ +
+
+
diff --git a/serial/serbridge.c b/serial/serbridge.c index 6d45610..528c486 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -13,20 +13,21 @@ #include "serled.h" #include "config.h" #include "console.h" +#include "tcpclient.h" static struct espconn serbridgeConn; static esp_tcp serbridgeTcp; static int8_t mcu_reset_pin, mcu_isp_pin; -sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len); +static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len); // Connection pool serbridgeConnData connData[MAX_CONN]; -// Transmit buffers for the connection pool -static char txbuffer[MAX_CONN][MAX_TXBUFFER]; - // Given a pointer to an espconn struct find the connection that correcponds to it static serbridgeConnData ICACHE_FLASH_ATTR *serbridgeFindConnData(void *arg) { + struct espconn *conn = arg; + return (serbridgeConnData *)conn->reverse; +#if 0 for (int i=0; itxbuffer -// returns result from espconn_sent if data in buffer or ESPCONN_OK (0) -// Use only internally from espbuffsend and serbridgeSentCb -static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) { - sint8 result = ESPCONN_OK; - if (conn->txbufferlen != 0) { - //os_printf("%d TX %d\n", system_get_time(), conn->txbufferlen); - conn->readytosend = false; - result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen); - conn->txbufferlen = 0; - if (result != ESPCONN_OK) { - os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); - } - } - return result; -} - -// espbuffsend adds data to the send buffer. If the previous send was completed it calls -// sendtxbuffer and espconn_sent. -// Returns ESPCONN_OK (0) for success, -128 if buffer is full or error from espconn_sent -// Use espbuffsend instead of espconn_sent as it solves the problem that espconn_sent must -// only be called *after* receiving an espconn_sent_callback for the previous packet. -sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) { - if (conn->txbufferlen + len > MAX_TXBUFFER) { - os_printf("espbuffsend: txbuffer full on conn %p\n", conn); - return -128; - } - os_memcpy(conn->txbuffer + conn->txbufferlen, data, len); - conn->txbufferlen += len; - if (conn->readytosend) { - return sendtxbuffer(conn); - } else { - //os_printf("%d QU %d\n", system_get_time(), conn->txbufferlen); - } - return ESPCONN_OK; -} - -//callback after the data are sent -static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) { - serbridgeConnData *conn = serbridgeFindConnData(arg); - //os_printf("Sent callback on conn %p\n", conn); - if (conn == NULL) return; - //os_printf("%d ST\n", system_get_time()); - conn->readytosend = true; - sendtxbuffer(conn); // send possible new data in txbuffer -} +//===== TCP -> UART // Telnet protocol characters #define IAC 255 // escape @@ -189,7 +146,6 @@ void ICACHE_FLASH_ATTR serbridgeReset() { } else os_printf("MCU reset: no pin\n"); } - // Receive callback static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) { serbridgeConnData *conn = serbridgeFindConnData(arg); @@ -233,6 +189,9 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh } else { conn->conn_mode = cmTransparent; } + + // Process return data on TCP client connections + } else if (conn->conn_mode == cmTcpClient) { } // write the buffer to the uart @@ -245,41 +204,181 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh serledFlash(50); // short blink on serial LED } +//===== UART -> TCP + +// Transmit buffers for the connection pool +static char txbuffer[MAX_CONN][MAX_TXBUFFER]; + +// Send all data in conn->txbuffer +// returns result from espconn_sent if data in buffer or ESPCONN_OK (0) +// Use only internally from espbuffsend and serbridgeSentCb +static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) { + sint8 result = ESPCONN_OK; + if (conn->txbufferlen != 0) { + //os_printf("%d TX %d\n", system_get_time(), conn->txbufferlen); + conn->readytosend = false; + result = espconn_sent(conn->conn, (uint8_t*)conn->txbuffer, conn->txbufferlen); + conn->txbufferlen = 0; + if (result != ESPCONN_OK) { + os_printf("sendtxbuffer: espconn_sent error %d on conn %p\n", result, conn); + } + } + return result; +} + +// espbuffsend adds data to the send buffer. If the previous send was completed it calls +// sendtxbuffer and espconn_sent. +// Returns ESPCONN_OK (0) for success, -128 if buffer is full or error from espconn_sent +// Use espbuffsend instead of espconn_sent as it solves the problem that espconn_sent must +// only be called *after* receiving an espconn_sent_callback for the previous packet. +static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len) { + if (conn->txbufferlen + len > MAX_TXBUFFER) { + os_printf("espbuffsend: txbuffer full on conn %p\n", conn); + return -128; + } + os_memcpy(conn->txbuffer + conn->txbufferlen, data, len); + conn->txbufferlen += len; + if (conn->readytosend) { + return sendtxbuffer(conn); + } else { + //os_printf("%d QU %d\n", system_get_time(), conn->txbufferlen); + } + return ESPCONN_OK; +} + +//callback after the data are sent +static void ICACHE_FLASH_ATTR +serbridgeSentCb(void *arg) { + serbridgeConnData *conn = serbridgeFindConnData(arg); + //os_printf("Sent callback on conn %p\n", conn); + if (conn == NULL) return; + //os_printf("%d ST\n", system_get_time()); + conn->readytosend = true; + sendtxbuffer(conn); // send possible new data in txbuffer +} + +// TCP client connection state machine +// This processes commands from the attached uC to open outboud TCP connections +enum { + TC_idle, // in-between commands + TC_newline, // newline seen + TC_start, // start character (~) seen + TC_cmd, // command character (0) seen + TC_cmdLine, // accumulating command + TC_tdata, // data character (1-9) seen, and in text data mode +}; +static uint8_t tcState = TC_newline; +static uint8_t tcChan; // channel for current command (index into tcConn) + +#define CMD_MAX 256 +static char tcCmdBuf[CMD_MAX]; +static short tcCmdBufLen = 0; +static char tcCmdChar; + +// scan a buffer for tcp client commands +static int ICACHE_FLASH_ATTR +tcpClientProcess(char *buf, int len) +{ + char *in=buf, *out=buf; + for (short i=0; i= '1' && c <= '9') { + tcChan = c-'1'; + tcState = TC_tdata; + continue; + } + *out++ = '~'; // make up for '~' we skipped + break; + case TC_cmd: // saw channel number 0 (command), expect command char + tcCmdChar = c; // save command character + tcCmdBufLen = 0; // empty the command buffer + tcState = TC_cmdLine; + continue; + case TC_cmdLine: // accumulating command in buffer + if (c != '\n') { + if (tcCmdBufLen < CMD_MAX) tcCmdBuf[tcCmdBufLen++] = c; + } else { + tcpClientCommand(tcCmdChar, tcCmdBuf); + tcState = TC_newline; + } + continue; + case TC_tdata: // saw channel number, getting data characters + if (c != 0) { + tcpClientSendChar(tcChan, c); + } else { + tcpClientSendPush(tcChan); + tcState = TC_idle; + } + continue; + } + *out++ = c; + } + if (tcState != TC_idle) os_printf("tcState=%d\n", tcState); + return out-buf; +} + +// callback with a buffer of characters that have arrived on the uart +void ICACHE_FLASH_ATTR +serbridgeUartCb(char *buf, int length) { + // push the buffer into the microcontroller console + for (int i=0; i 0) { + for (int i = 0; i < MAX_CONN; ++i) { + if (connData[i].conn && connData[i].conn_mode != cmTcpClient) { + espbuffsend(&connData[i], buf, length); + } + } + } + serledFlash(50); // short blink on serial LED +} + +//===== Connect / disconnect + // Error callback (it's really poorly named, it's not a "connection reconnected" callback, // it's really a "connection broken, please reconnect" callback) static void ICACHE_FLASH_ATTR serbridgeReconCb(void *arg, sint8 err) { - serbridgeConnData *conn=serbridgeFindConnData(arg); - if (conn == NULL) return; + serbridgeConnData *sbConn = serbridgeFindConnData(arg); + if (sbConn == NULL) return; // Close the connection - espconn_disconnect(conn->conn); - conn->conn = NULL; + espconn_disconnect(sbConn->conn); + // free connection slot + sbConn->conn = NULL; } // Disconnection callback static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) { - // Iterate through all the connections and deallocate the ones that are in a state that - // indicates that they're closed - for (int i=0; istate == ESPCONN_NONE || connData[i].conn->state == ESPCONN_CLOSE)) - { - if (connData[i].conn_mode == cmAVR) { - // send reset to arduino/ARM - if (mcu_reset_pin >= 0) { - GPIO_OUTPUT_SET(mcu_reset_pin, 0); - os_delay_us(100L); - GPIO_OUTPUT_SET(mcu_reset_pin, 1); - } - } - connData[i].conn = NULL; - } + serbridgeConnData *sbConn = serbridgeFindConnData(arg); + if (sbConn == NULL) return; + // send reset to arduino/ARM + if (sbConn->conn_mode == cmAVR && mcu_reset_pin >= 0) { + GPIO_OUTPUT_SET(mcu_reset_pin, 0); + os_delay_us(100L); + GPIO_OUTPUT_SET(mcu_reset_pin, 1); } + // free connection slot + sbConn->conn = NULL; } // New connection callback, use one of the connection descriptors, if we have one left. static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { struct espconn *conn = arg; - //Find empty conndata in pool + // Find empty conndata in pool int i; for (i=0; ireverse = connData+i; + connData[i].conn = conn; connData[i].txbufferlen = 0; connData[i].readytosend = true; connData[i].telnet_state = 0; @@ -304,20 +404,7 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY); } -// callback with a buffer of characters that have arrived on the uart -void ICACHE_FLASH_ATTR -serbridgeUartCb(char *buf, int length) { - // push the buffer into the microcontroller console - for (int i=0; i +#include "config.h" +#include "uart.h" +#include "serled.h" +#include "tcpclient.h" + +// max number of channels the client can open +#define MAX_CHAN 8 +// size of tx buffer +#define MAX_TXBUF 1024 + +enum TcpState { + TCP_idle, // unused connection + TCP_dns, // doing gethostbyname + TCP_conn, // connecting to remote server + TCP_data, // connected +}; + +// Connections +typedef struct { + struct espconn *conn; // esp connection structure + esp_tcp *tcp; // esp TCP parameters + char *txBuf; // buffer to accumulate into + char *txBufSent; // buffer held by espconn + uint8_t txBufLen; // number of chars in txbuf + enum TcpState state; +} TcpConn; + +#define MAX_CONN (8) +static TcpConn tcpConn[MAX_CONN]; + +// Free a connection dynamically. +static void ICACHE_FLASH_ATTR +tcpConnFree(TcpConn* tci) { + if (tci->conn != NULL) os_free(tci->conn); + if (tci->tcp != NULL) os_free(tci->tcp); + if (tci->txBuf != NULL) os_free(tci->txBuf); + if (tci->txBufSent != NULL) os_free(tci->txBufSent); + memset(tci, 0, sizeof(TcpConn)); +} + +// DNS name resolution callback +static void ICACHE_FLASH_ATTR +tcpClientHostnameCb(const char *name, ip_addr_t *ipaddr, void *arg) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP dns CB (%p %p)\n", arg, tci); + if (ipaddr == NULL) { + os_printf("TCP %s not found\n", name); + } else { + os_printf("TCP %s -> %d.%d.%d.%d\n", name, IP2STR(ipaddr)); + tci->tcp->remote_ip[0] = ip4_addr1(ipaddr); + tci->tcp->remote_ip[1] = ip4_addr2(ipaddr); + tci->tcp->remote_ip[2] = ip4_addr3(ipaddr); + tci->tcp->remote_ip[3] = ip4_addr4(ipaddr); + os_printf("TCP connect %d.%d.%d.%d (%p)\n", IP2STR(tci->tcp->remote_ip), tci); + if (espconn_connect(tci->conn) == ESPCONN_OK) { + tci->state = TCP_conn; + return; + } + os_printf("TCP connect failure\n"); + } + // oops + tcpConnFree(tci); +} + +// Send the next buffer (assumes that the connection is in a state that allows it) +static void 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->txBufSent = tci->txBuf; + tci->txBuf = NULL; + tci->txBufLen = 0; + } else { + // send error, leave as-is and try again later... + os_printf("TCP send err (%p %p) %d\n", tci->conn, tci, err); + } +} + +// 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) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP sent CB (%p %p)\n", arg, tci); + if (tci->txBufSent != NULL) os_free(tci->txBufSent); + tci->txBufSent = NULL; + + if (tci->txBuf != NULL && tci->txBufLen == MAX_TXBUF) { + // next buffer is full, send it now + tcpDoSend(tci); + } +} + +// Recv callback +static void ICACHE_FLASH_ATTR tcpRecvCb(void *arg, char *data, uint16_t len) { + struct espconn *conn = arg; + TcpConn *tci = conn->reverse; + os_printf("TCP recv CB (%p %p)\n", arg, tci); + if (tci->state == TCP_data) { + uart0_tx_buffer(data, len); + } + 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); +} + +// 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; iconn = 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; + +fail: + tcpConnFree(tci); + return NULL; +} + +static TcpConn *tcConn[MAX_CHAN]; + +// 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; + TcpConn *tci; + + switch (cmd) { + //== TCP Connect command + case 'T': + if (*cmdBuf < '1' || *cmdBuf > ('0'+MAX_CHAN)) break; + tcChan = *cmdBuf++ - '1'; + char *hostname = cmdBuf; + char *port = hostname; + while (*port != 0 && *port != ':') port++; + if (*port != ':') break; + *port = 0; + port++; + int portInt = atoi(port); + if (portInt < 1 || portInt > 65535) break; + + // allocate a connection + tci = tcpConnAlloc(); + 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); + ip_addr_t ip; + err_t err = espconn_gethostbyname(tci->conn, hostname, &ip, tcpClientHostnameCb); + if (err == ESPCONN_OK) { + // dns cache hit, got the IP address, fake the callback (sigh) + os_printf("TCP DNS hit\n"); + tcpClientHostnameCb(hostname, &ip, tci->conn); + } else if (err != ESPCONN_INPROGRESS) { + tcpConnFree(tci); + 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]; + if (tci->state > TCP_idle) { + tci->state = TCP_idle; // hackish... + espconn_disconnect(tci->conn); + } + break; + + } + 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 new file mode 100644 index 0000000..c80d2d3 --- /dev/null +++ b/serial/tcpclient.h @@ -0,0 +1,8 @@ +#ifndef __TCP_CLIENT_H__ +#define __TCP_CLIENT_H__ + +bool tcpClientCommand(char cmd, char *cmdBuf); +void tcpClientSendChar(uint8_t chan, char c); +void tcpClientSendPush(uint8_t chan); + +#endif /* __TCP_CLIENT_H__ */ diff --git a/user/cgiflash.c b/user/cgiflash.c index 70d4b1c..847b5ab 100644 --- a/user/cgiflash.c +++ b/user/cgiflash.c @@ -160,6 +160,7 @@ int ICACHE_FLASH_ATTR cgiRebootFirmware(HttpdConnData *connData) { int address = id == 1 ? 4*1024 // either start after 4KB boot partition : 4*1024 + FIRMWARE_SIZE + 16*1024 + 4*1024; // 4KB boot, fw1, 16KB user param, 4KB reserved uint32 buf[8]; + os_printf("Checking %p\n", (void *)address); spi_flash_read(address, buf, sizeof(buf)); char *err = check_header(buf); if (err != NULL) { diff --git a/user/status.c b/user/status.c index 23f693f..7b46d72 100644 --- a/user/status.c +++ b/user/status.c @@ -5,8 +5,11 @@ #include "serled.h" #include "cgiwifi.h" +//===== "CONN" LED status indication + static ETSTimer ledTimer; +// Set the LED on or off, respecting the defined polarity static void ICACHE_FLASH_ATTR setLed(int on) { int8_t pin = flashConfig.conn_led_pin; if (pin < 0) return; // disabled @@ -21,11 +24,12 @@ static void ICACHE_FLASH_ATTR setLed(int on) { static uint8_t ledState = 0; static uint8_t wifiState = 0; +// Timer callback to update the LED static void ICACHE_FLASH_ATTR ledTimerCb(void *v) { int time = 1000; if (wifiState == wifiGotIP) { - // connected, all is good, solid light + // connected, all is good, solid light with a short dark blip every 3 seconds ledState = 1-ledState; time = ledState ? 2900 : 100; } else if (wifiState == wifiIsConnected) { @@ -33,7 +37,7 @@ static void ICACHE_FLASH_ATTR ledTimerCb(void *v) { ledState = 1 - ledState; time = 1000; } else { - // idle + // not connected switch (wifi_get_opmode()) { case 1: // STA ledState = 0; @@ -53,7 +57,7 @@ static void ICACHE_FLASH_ATTR ledTimerCb(void *v) { os_timer_arm(&ledTimer, time, 0); } -// change the wifi state +// change the wifi state indication void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { wifiState = state; // schedule an update (don't want to run into concurrency issues) @@ -62,6 +66,111 @@ void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { os_timer_arm(&ledTimer, 500, 0); } +//===== RSSI Status update sent to GroveStreams + +#define RSSI_INTERVAL (60*1000) + +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); + + char buf[2000]; + + // http header + 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); + + // http body + int dataLen = os_sprintf(buf+hdrLen, + "[{\"compId\":\"%s\", \"streamId\":\"%s\", \"data\":%d}]", + GS_COMP_ID, GS_STREAM, rssiLast); + + // 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); +} + +// 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++; + } +} + +//===== Init status stuff + void ICACHE_FLASH_ATTR statusInit(void) { if (flashConfig.conn_led_pin >= 0) { makeGpio(flashConfig.conn_led_pin); @@ -72,6 +181,10 @@ void ICACHE_FLASH_ATTR statusInit(void) { os_timer_disarm(&ledTimer); os_timer_setfn(&ledTimer, ledTimerCb, NULL); os_timer_arm(&ledTimer, 2000, 0); + + os_timer_disarm(&rssiTimer); + os_timer_setfn(&rssiTimer, rssiTimerCb, NULL); + os_timer_arm(&rssiTimer, RSSI_INTERVAL, 1); // recurring timer }