initial support for TCP client connections from uC

pull/32/head
Thorsten von Eicken 9 years ago
parent 26db66c9e4
commit 886ce62df0
  1. 2
      Makefile
  2. 22
      html/home.html
  3. 249
      serial/serbridge.c
  4. 8
      serial/serbridge.h
  5. 272
      serial/tcpclient.c
  6. 8
      serial/tcpclient.h
  7. 1
      user/cgiflash.c
  8. 119
      user/status.c

@ -30,7 +30,7 @@ ESPBAUD ?= 460800
# --------------- chipset configuration --------------- # --------------- chipset configuration ---------------
# Pick your flash size: "512KB" or "4MB" # Pick your flash size: "512KB" or "4MB"
FLASH_SIZE ?= 4MB FLASH_SIZE ?= 512KB
ifeq ("$(FLASH_SIZE)","512KB") ifeq ("$(FLASH_SIZE)","512KB")
# Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 # Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11

@ -44,6 +44,28 @@
</fieldset> </fieldset>
</div></div> </div></div>
</div> </div>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-2"><div class="card">
<h1>HTTP service</h1>
<form action="#" id="httpform" class="pure-form">
<legend>External web service configuration</legend>
<div class="form-horizontal">
<label>Enable serial port HTTP</label>
<input type="checkbox" name="http_enable"/>
<label>Send RSSI</label>
<input type="checkbox" name="send_rssi"/>
</div>
<div class="pure-form-stacked">
<label>Server hostname</label>
<input type="text" name="hostname"/>
<label>API key/passwd</label>
<input type="password" name="api_key"/>
</div>
<button id="special-button" type="submit"
class="pure-button button-primary">Change!</button>
</form>
</div></div>
</div>
</div> </div>
</div> </div>
</div> </div>

@ -13,20 +13,21 @@
#include "serled.h" #include "serled.h"
#include "config.h" #include "config.h"
#include "console.h" #include "console.h"
#include "tcpclient.h"
static struct espconn serbridgeConn; static struct espconn serbridgeConn;
static esp_tcp serbridgeTcp; static esp_tcp serbridgeTcp;
static int8_t mcu_reset_pin, mcu_isp_pin; 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 // Connection pool
serbridgeConnData connData[MAX_CONN]; 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 // Given a pointer to an espconn struct find the connection that correcponds to it
static serbridgeConnData ICACHE_FLASH_ATTR *serbridgeFindConnData(void *arg) { static serbridgeConnData ICACHE_FLASH_ATTR *serbridgeFindConnData(void *arg) {
struct espconn *conn = arg;
return (serbridgeConnData *)conn->reverse;
#if 0
for (int i=0; i<MAX_CONN; i++) { for (int i=0; i<MAX_CONN; i++) {
if (connData[i].conn == (struct espconn *)arg) { if (connData[i].conn == (struct espconn *)arg) {
return &connData[i]; return &connData[i];
@ -34,54 +35,10 @@ static serbridgeConnData ICACHE_FLASH_ATTR *serbridgeFindConnData(void *arg) {
} }
//os_printf("FindConnData: Huh? Couldn't find connection for %p\n", arg); //os_printf("FindConnData: Huh? Couldn't find connection for %p\n", arg);
return NULL; // not found, may be closed already... return NULL; // not found, may be closed already...
#endif
} }
// Send all data in conn->txbuffer //===== TCP -> UART
// 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
}
// Telnet protocol characters // Telnet protocol characters
#define IAC 255 // escape #define IAC 255 // escape
@ -189,7 +146,6 @@ void ICACHE_FLASH_ATTR serbridgeReset() {
} else os_printf("MCU reset: no pin\n"); } else os_printf("MCU reset: no pin\n");
} }
// Receive callback // Receive callback
static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) { static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) {
serbridgeConnData *conn = serbridgeFindConnData(arg); serbridgeConnData *conn = serbridgeFindConnData(arg);
@ -233,6 +189,9 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh
} else { } else {
conn->conn_mode = cmTransparent; conn->conn_mode = cmTransparent;
} }
// Process return data on TCP client connections
} else if (conn->conn_mode == cmTcpClient) {
} }
// write the buffer to the uart // write the buffer to the uart
@ -245,35 +204,175 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh
serledFlash(50); // short blink on serial LED 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<len; i++) {
char c = *in++;
//os_printf("tcState=%d c=%c\n", tcState, c);
switch (tcState) {
case TC_idle:
if (c == '\n') tcState = TC_newline;
break;
case TC_newline: // saw newline, expect ~
if (c == '~') tcState = TC_start;
continue; // gobble up the ~
case TC_start: // saw ~, expect channel number
if (c == '0') {
tcState = TC_cmd;
continue;
} else if (c >= '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<length; i++)
console_write_char(buf[i]);
// parse the buffer for TCP commands, this may remove characters from the buffer
length = tcpClientProcess(buf, length);
// push the buffer into each open connection
if (length > 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, // Error callback (it's really poorly named, it's not a "connection reconnected" callback,
// it's really a "connection broken, please reconnect" callback) // it's really a "connection broken, please reconnect" callback)
static void ICACHE_FLASH_ATTR serbridgeReconCb(void *arg, sint8 err) { static void ICACHE_FLASH_ATTR serbridgeReconCb(void *arg, sint8 err) {
serbridgeConnData *conn=serbridgeFindConnData(arg); serbridgeConnData *sbConn = serbridgeFindConnData(arg);
if (conn == NULL) return; if (sbConn == NULL) return;
// Close the connection // Close the connection
espconn_disconnect(conn->conn); espconn_disconnect(sbConn->conn);
conn->conn = NULL; // free connection slot
sbConn->conn = NULL;
} }
// Disconnection callback // Disconnection callback
static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) { static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) {
// Iterate through all the connections and deallocate the ones that are in a state that serbridgeConnData *sbConn = serbridgeFindConnData(arg);
// indicates that they're closed if (sbConn == NULL) return;
for (int i=0; i<MAX_CONN; i++) {
if (connData[i].conn != NULL &&
(connData[i].conn->state == ESPCONN_NONE || connData[i].conn->state == ESPCONN_CLOSE))
{
if (connData[i].conn_mode == cmAVR) {
// send reset to arduino/ARM // send reset to arduino/ARM
if (mcu_reset_pin >= 0) { if (sbConn->conn_mode == cmAVR && mcu_reset_pin >= 0) {
GPIO_OUTPUT_SET(mcu_reset_pin, 0); GPIO_OUTPUT_SET(mcu_reset_pin, 0);
os_delay_us(100L); os_delay_us(100L);
GPIO_OUTPUT_SET(mcu_reset_pin, 1); GPIO_OUTPUT_SET(mcu_reset_pin, 1);
} }
} // free connection slot
connData[i].conn = NULL; sbConn->conn = NULL;
}
}
} }
// New connection callback, use one of the connection descriptors, if we have one left. // New connection callback, use one of the connection descriptors, if we have one left.
@ -290,6 +389,7 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) {
return; return;
} }
conn->reverse = connData+i;
connData[i].conn = conn; connData[i].conn = conn;
connData[i].txbufferlen = 0; connData[i].txbufferlen = 0;
connData[i].readytosend = true; connData[i].readytosend = true;
@ -304,20 +404,7 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) {
espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY); espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY);
} }
// callback with a buffer of characters that have arrived on the uart //===== Initialization
void ICACHE_FLASH_ATTR
serbridgeUartCb(char *buf, int length) {
// push the buffer into the microcontroller console
for (int i=0; i<length; i++)
console_write_char(buf[i]);
// push the buffer into each open connection
for (int i = 0; i < MAX_CONN; ++i) {
if (connData[i].conn) {
espbuffsend(&connData[i], buf, length);
}
}
serledFlash(50); // short blink on serial LED
}
void ICACHE_FLASH_ATTR serbridgeInitPins() { void ICACHE_FLASH_ATTR serbridgeInitPins() {
mcu_reset_pin = flashConfig.reset_pin; mcu_reset_pin = flashConfig.reset_pin;

@ -11,26 +11,24 @@
//Max send buffer len //Max send buffer len
#define MAX_TXBUFFER 1024 #define MAX_TXBUFFER 1024
typedef struct serbridgeConnData serbridgeConnData;
enum connModes { enum connModes {
cmInit = 0, // initialization mode: nothing received yet cmInit = 0, // initialization mode: nothing received yet
cmTransparent, // transparent mode cmTransparent, // transparent mode
cmAVR, // Arduino/AVR programming mode cmAVR, // Arduino/AVR programming mode
cmARM, // ARM (LPC8xx) programming cmARM, // ARM (LPC8xx) programming
cmEcho, // simply echo characters (used for debugging latency) cmEcho, // simply echo characters (used for debugging latency)
cmCommand, // AT command mode
cmTelnet, // use telnet escape sequences for programming mode cmTelnet, // use telnet escape sequences for programming mode
cmTcpClient, // client connection (initiated via serial)
}; };
struct serbridgeConnData { typedef struct serbridgeConnData {
struct espconn *conn; struct espconn *conn;
enum connModes conn_mode; // connection mode enum connModes conn_mode; // connection mode
char *txbuffer; // buffer for the data to send char *txbuffer; // buffer for the data to send
uint16 txbufferlen; // length of data in txbuffer uint16 txbufferlen; // length of data in txbuffer
bool readytosend; // true, if txbuffer can send by espconn_sent bool readytosend; // true, if txbuffer can send by espconn_sent
uint8_t telnet_state; uint8_t telnet_state;
}; } serbridgeConnData;
void ICACHE_FLASH_ATTR serbridgeInit(int port); void ICACHE_FLASH_ATTR serbridgeInit(int port);
void ICACHE_FLASH_ATTR serbridgeInitPins(void); void ICACHE_FLASH_ATTR serbridgeInitPins(void);

@ -0,0 +1,272 @@
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
#include <esp8266.h>
#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; i<MAX_CONN; i++) {
if (tcpConn[i].state == TCP_idle && tcpConn[i].conn == NULL) break;
}
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;
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);
}

@ -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__ */

@ -160,6 +160,7 @@ int ICACHE_FLASH_ATTR cgiRebootFirmware(HttpdConnData *connData) {
int address = id == 1 ? 4*1024 // either start after 4KB boot partition 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 : 4*1024 + FIRMWARE_SIZE + 16*1024 + 4*1024; // 4KB boot, fw1, 16KB user param, 4KB reserved
uint32 buf[8]; uint32 buf[8];
os_printf("Checking %p\n", (void *)address);
spi_flash_read(address, buf, sizeof(buf)); spi_flash_read(address, buf, sizeof(buf));
char *err = check_header(buf); char *err = check_header(buf);
if (err != NULL) { if (err != NULL) {

@ -5,8 +5,11 @@
#include "serled.h" #include "serled.h"
#include "cgiwifi.h" #include "cgiwifi.h"
//===== "CONN" LED status indication
static ETSTimer ledTimer; static ETSTimer ledTimer;
// Set the LED on or off, respecting the defined polarity
static void ICACHE_FLASH_ATTR setLed(int on) { static void ICACHE_FLASH_ATTR setLed(int on) {
int8_t pin = flashConfig.conn_led_pin; int8_t pin = flashConfig.conn_led_pin;
if (pin < 0) return; // disabled 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 ledState = 0;
static uint8_t wifiState = 0; static uint8_t wifiState = 0;
// Timer callback to update the LED
static void ICACHE_FLASH_ATTR ledTimerCb(void *v) { static void ICACHE_FLASH_ATTR ledTimerCb(void *v) {
int time = 1000; int time = 1000;
if (wifiState == wifiGotIP) { 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; ledState = 1-ledState;
time = ledState ? 2900 : 100; time = ledState ? 2900 : 100;
} else if (wifiState == wifiIsConnected) { } else if (wifiState == wifiIsConnected) {
@ -33,7 +37,7 @@ static void ICACHE_FLASH_ATTR ledTimerCb(void *v) {
ledState = 1 - ledState; ledState = 1 - ledState;
time = 1000; time = 1000;
} else { } else {
// idle // not connected
switch (wifi_get_opmode()) { switch (wifi_get_opmode()) {
case 1: // STA case 1: // STA
ledState = 0; ledState = 0;
@ -53,7 +57,7 @@ static void ICACHE_FLASH_ATTR ledTimerCb(void *v) {
os_timer_arm(&ledTimer, time, 0); os_timer_arm(&ledTimer, time, 0);
} }
// change the wifi state // change the wifi state indication
void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) { void ICACHE_FLASH_ATTR statusWifiUpdate(uint8_t state) {
wifiState = state; wifiState = state;
// schedule an update (don't want to run into concurrency issues) // 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); 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) { void ICACHE_FLASH_ATTR statusInit(void) {
if (flashConfig.conn_led_pin >= 0) { if (flashConfig.conn_led_pin >= 0) {
makeGpio(flashConfig.conn_led_pin); makeGpio(flashConfig.conn_led_pin);
@ -72,6 +181,10 @@ void ICACHE_FLASH_ATTR statusInit(void) {
os_timer_disarm(&ledTimer); os_timer_disarm(&ledTimer);
os_timer_setfn(&ledTimer, ledTimerCb, NULL); os_timer_setfn(&ledTimer, ledTimerCb, NULL);
os_timer_arm(&ledTimer, 2000, 0); 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
} }

Loading…
Cancel
Save