diff --git a/Makefile b/Makefile
index cad8624..32cce5c 100644
--- a/Makefile
+++ b/Makefile
@@ -29,11 +29,28 @@ ESPBAUD ?= 460800
# --------------- chipset configuration ---------------
+# Pick your flash size: "512KB" or "4MB"
+FLASH_SIZE ?= 512KB
+
+ifeq ("$(FLASH_SIZE)","512KB")
+# Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11
ESP_SPI_SIZE ?= 0 # 0->512KB
ESP_FLASH_MODE ?= 0 # 0->QIO
ESP_FLASH_FREQ_DIV ?= 0 # 0->40Mhz
ESP_FLASH_MAX ?= 241664 # max bin file for 512KB flash: 236KB
+else
+# Winbond 25Q32 4MB flash, typ for esp-12
+# Here we're using two partitions of approx 0.5MB because that's what's easily available in terms
+# of linker scripts in the SDK. Ideally we'd use two partitions of approx 1MB, the remaining 2MB
+# cannot be used for code.
+ESP_SPI_SIZE ?= 4 # 6->4MB (1MB+1MB) or 4->4MB (512KB+512KB)
+ESP_FLASH_MODE ?= 0 # 0->QIO, 2->DIO
+ESP_FLASH_FREQ_DIV ?= 15 # 15->80Mhz
+ESP_FLASH_MAX ?= 503808 # max bin file for 512KB flash partition: 492KB
+#ESP_FLASH_MAX ?= 1028096 # max bin file for 1MB flash partition: 1004KB
+endif
+
# hostname or IP address for wifi flashing
ESP_HOSTNAME ?= esp-link
@@ -163,7 +180,6 @@ SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c))
OBJ := $(patsubst %.c,$(BUILD_BASE)/%.o,$(SRC)) $(BUILD_BASE)/espfs_img.o
LIBS := $(addprefix -l,$(LIBS))
APP_AR := $(addprefix $(BUILD_BASE)/,$(TARGET)_app.a)
-TARGET_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).out)
USER1_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).user1.out)
USER2_OUT := $(addprefix $(BUILD_BASE)/,$(TARGET).user2.out)
@@ -202,16 +218,11 @@ endef
.PHONY: all checkdirs clean webpages.espfs wiflash
-all: echo_version checkdirs $(FW_BASE) firmware/user1.bin firmware/user2.bin
+all: echo_version checkdirs $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin
echo_version:
@echo VERSION: $(VERSION)
-$(TARGET_OUT): $(APP_AR) $(LD_SCRIPT)
- $(vecho) "LD $@"
- $(Q) $(LD) -L$(SDK_LIBDIR) -T$(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@
-# $(OBJDP) -x $(TARGET_OUT) | egrep '(espfs_img)'
-
$(USER1_OUT): $(APP_AR) $(LD_SCRIPT1)
$(vecho) "LD $@"
$(Q) $(LD) -L$(SDK_LIBDIR) -T$(LD_SCRIPT1) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@
@@ -224,12 +235,11 @@ $(USER2_OUT): $(APP_AR) $(LD_SCRIPT2)
$(Q) $(LD) -L$(SDK_LIBDIR) -T$(LD_SCRIPT2) $(LDFLAGS) -Wl,--start-group $(LIBS) $(APP_AR) -Wl,--end-group -o $@
# $(Q) $(OBJDP) -x $(TARGET_OUT) | egrep espfs_img
-$(FW_BASE): $(TARGET_OUT)
+$(FW_BASE):
$(vecho) "FW $@"
$(Q) mkdir -p $@
- $(Q) $(ESPTOOL) elf2image $(TARGET_OUT) --output $@/
-firmware/user1.bin: $(USER1_OUT)
+$(FW_BASE)/user1.bin: $(USER1_OUT) $(FW_BASE)
$(Q) $(OBJCP) --only-section .text -O binary $(USER1_OUT) eagle.app.v6.text.bin
$(Q) $(OBJCP) --only-section .data -O binary $(USER1_OUT) eagle.app.v6.data.bin
$(Q) $(OBJCP) --only-section .rodata -O binary $(USER1_OUT) eagle.app.v6.rodata.bin
@@ -241,7 +251,7 @@ firmware/user1.bin: $(USER1_OUT)
@echo "** user1.bin uses $$(stat -c '%s' $@) bytes of" $(ESP_FLASH_MAX) "available"
$(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; false; fi
-firmware/user2.bin: $(USER2_OUT)
+$(FW_BASE)/user2.bin: $(USER2_OUT) $(FW_BASE)
$(Q) $(OBJCP) --only-section .text -O binary $(USER2_OUT) eagle.app.v6.text.bin
$(Q) $(OBJCP) --only-section .data -O binary $(USER2_OUT) eagle.app.v6.data.bin
$(Q) $(OBJCP) --only-section .rodata -O binary $(USER2_OUT) eagle.app.v6.rodata.bin
@@ -251,7 +261,6 @@ firmware/user2.bin: $(USER2_OUT)
$(Q) mv eagle.app.flash.bin $@
$(Q) if [ $$(stat -c '%s' $@) -gt $$(( $(ESP_FLASH_MAX) )) ]; then echo "$@ too big!"; false; fi
-
$(APP_AR): $(OBJ)
$(vecho) "AR $@"
$(Q) $(AR) cru $@ $^
@@ -262,7 +271,7 @@ $(BUILD_DIR):
$(Q) mkdir -p $@
wiflash: all
- ./wiflash $(ESP_HOSTNAME) firmware/user1.bin firmware/user2.bin
+ ./wiflash $(ESP_HOSTNAME) $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin
flash: all
$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash \
@@ -302,24 +311,32 @@ endif
# we also adjust the sizes of the segments 'cause we need more irom0
# in the end the only thing that matters wrt size is that the whole shebang fits into the
# 236KB available (in a 512KB flash)
-build/eagle.esphttpd.v6.ld: $(SDK_LDDIR)/eagle.app.v6.ld
- $(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \
- $(SDK_LDDIR)/eagle.app.v6.ld >$@
+ifeq ("$(FLASH_SIZE)","512KB")
build/eagle.esphttpd1.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.512.app1.ld
$(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \
-e '/^ irom0_0_seg/ s/2B000/38000/' \
- $(SDK_LDDIR)/eagle.app.v6.new.512.app1.ld >$@
+ $(SDK_LDDIR)/eagle.app.v6.new.512.app1.ld >$@
build/eagle.esphttpd2.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.512.app2.ld
$(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \
-e '/^ irom0_0_seg/ s/2B000/38000/' \
- $(SDK_LDDIR)/eagle.app.v6.new.512.app2.ld >$@
+ $(SDK_LDDIR)/eagle.app.v6.new.512.app2.ld >$@
+else
+build/eagle.esphttpd1.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.1024.app1.ld
+ $(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \
+ -e '/^ irom0_0_seg/ s/6B000/7C000/' \
+ $(SDK_LDDIR)/eagle.app.v6.new.1024.app1.ld >$@
+build/eagle.esphttpd2.v6.ld: $(SDK_LDDIR)/eagle.app.v6.new.1024.app2.ld
+ $(Q) sed -e '/\.irom\.text/{' -e 'a . = ALIGN (4);' -e 'a *(.espfs)' -e '}' \
+ -e '/^ irom0_0_seg/ s/6B000/7C000/' \
+ $(SDK_LDDIR)/eagle.app.v6.new.1024.app2.ld >$@
+endif
espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/
$(Q) $(MAKE) -C espfs/mkespfsimage USE_HEATSHRINK="$(USE_HEATSHRINK)" GZIP_COMPRESSION="$(GZIP_COMPRESSION)"
release: all
$(Q) rm -rf release; mkdir -p release/esp-link
- $(Q) cp firmware/user1.bin firmware/user2.bin $(SDK_BASE)/bin/blank.bin \
+ $(Q) cp $(FW_BASE)/user1.bin $(FW_BASE)/user2.bin $(SDK_BASE)/bin/blank.bin \
"$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link
$(Q) tar zcf esp-link.tgz -C release esp-link
$(Q) rm -rf release
diff --git a/html/favicon.ico b/html/favicon.ico
index ea4eca1..fe24a7b 100755
Binary files a/html/favicon.ico and b/html/favicon.ico differ
diff --git a/html/home.html b/html/home.html
index f16a0e0..926ec2c 100644
--- a/html/home.html
+++ b/html/home.html
@@ -1,6 +1,6 @@
@@ -46,6 +46,28 @@
+
diff --git a/html/jl-200x55.png b/html/jl-200x55.png
deleted file mode 100755
index 3bc4a42..0000000
Binary files a/html/jl-200x55.png and /dev/null differ
diff --git a/html/style.css b/html/style.css
index 4fa393a..29d15a3 100644
--- a/html/style.css
+++ b/html/style.css
@@ -114,6 +114,13 @@ div.tt {
.header h1 .esp {
font-size: 1.25em;
}
+.jl {
+ font: normal 800 1.5em sans-serif;
+ position: relative;
+ bottom: 19px;
+ color: #9d1414;
+ margin-left: 3px;
+}
.content-subhead {
margin: 50px 0 20px 0;
diff --git a/html/ui.js b/html/ui.js
index b67f077..5de2f4c 100644
--- a/html/ui.js
+++ b/html/ui.js
@@ -225,7 +225,7 @@ onLoad(function() {
'\
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 2c5dbd4..597b73a 100644
--- a/user/cgiflash.c
+++ b/user/cgiflash.c
@@ -22,8 +22,10 @@ Some flash handling cgi routines. Used for reading the existing flash and updati
// Check that the header of the firmware blob looks like actual firmware...
static char* ICACHE_FLASH_ATTR check_header(void *buf) {
uint8_t *cd = (uint8_t *)buf;
+ uint32_t *buf32 = buf;
+ os_printf("%p: %08lX %08lX %08lX %08lX\n", buf, buf32[0], buf32[1], buf32[2], buf32[3]);
if (cd[0] != 0xEA) return "IROM magic missing";
- if (cd[1] != 4 || cd[2] > 3 || cd[3] > 0x40) return "bad flash header";
+ if (cd[1] != 4 || cd[2] > 3 || (cd[3]>>4) > 6) return "bad flash header";
if (((uint16_t *)buf)[3] != 0x4010) return "Invalid entry addr";
if (((uint32_t *)buf)[2] != 0) return "Invalid start offset";
return NULL;
@@ -151,6 +153,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
}
diff --git a/user/user_main.c b/user/user_main.c
index 57443ae..645d215 100644
--- a/user/user_main.c
+++ b/user/user_main.c
@@ -156,6 +156,7 @@ void user_init(void) {
os_printf("exccause=%d epc1=0x%x epc2=0x%x epc3=0x%x excvaddr=0x%x depc=0x%x\n",
rst_info->exccause, rst_info->epc1, rst_info->epc2, rst_info->epc3,
rst_info->excvaddr, rst_info->depc);
+ os_printf("Flash map %d, chip %08X\n", system_get_flash_size_map(), spi_flash_get_id());
os_printf("** esp-link ready\n");
}