Merge pull request #7 from jeelabs/master

merge with upstream
pull/41/head
Benjamin Runnels 9 years ago
commit c11cf7def5
  1. 14
      Makefile
  2. 21
      README.md
  3. 18
      cmd/cmd.h
  4. 79
      cmd/handlers.c
  5. 89
      cmd/rest.c
  6. 5
      serial/slip.c

@ -90,7 +90,8 @@ LED_SERIAL_PIN ?= 14
# on the release tag, make release, upload esp-link.tgz into the release files
#VERSION ?= "esp-link custom version"
DATE := $(shell date '+%F %T')
BRANCH := $(shell git describe --tags)
BRANCH := $(shell if git diff --quiet HEAD; then git describe --tags; \
else git symbolic-ref --short HEAD; fi)
SHA := $(shell if git diff --quiet HEAD; then git rev-parse --short HEAD | cut -d"/" -f 3; \
else echo "development"; fi)
VERSION ?=esp-link $(BRANCH) - $(DATE) - $(SHA)
@ -285,7 +286,7 @@ baseflash: all
$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x01000 $(FW_BASE)/user1.bin
flash: all
$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) -fs $(ET_FS) -ff $(ET_FF) write_flash \
$(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash -fs $(ET_FS) -ff $(ET_FF) \
0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \
$(ET_BLANK) $(SDK_BASE)/bin/blank.bin
@ -350,10 +351,13 @@ espfs/mkespfsimage/mkespfsimage: espfs/mkespfsimage/
$(Q) $(MAKE) -C espfs/mkespfsimage GZIP_COMPRESSION="$(GZIP_COMPRESSION)"
release: all
$(Q) rm -rf release; mkdir -p release/esp-link
$(Q) rm -rf release; mkdir -p release/esp-link-$(BRANCH)
$(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user1.bin | cut -b 1-80
$(Q) egrep -a 'esp-link [a-z0-9.]+ - 201' $(FW_BASE)/user2.bin | cut -b 1-80
$(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
"$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link-$(BRANCH)
$(Q) tar zcf esp-link-$(BRANCH)-$(FLASH_SIZE).tgz -C release esp-link-$(BRANCH)
$(Q) echo "Release file: esp-link-$(BRANCH)-$(FLASH_SIZE).tgz"
$(Q) rm -rf release
clean:

@ -7,16 +7,24 @@ It implements a number of features:
- flash-programming attached Arduino/AVR microcontrollers as well as LPC800-series and other
ARM microcontrollers via Wifi
- outbound TCP (and thus HTTP) connections from the attached micro-controller to the internet
- outbound REST HTTP requests from the attached micro-controller to the internet, protocol
based on espduino and compatible with [tuanpmt/espduino](https://github.com/tuanpmt/espduino)
The firmware includes a tiny HTTP server based on
[esphttpd](http://www.esp8266.com/viewforum.php?f=34)
with a simple web interface, many thanks to Jeroen Domburg for making it available!
Many thanks to https://github.com/brunnels for contributions around the espduino functionality.
[![Chat at https://gitter.im/jeelabs/esp-link](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jeelabs/esp-link?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
###[Releases](https://github.com/jeelabs/esp-link/releases)
- [V2.0.beta2](https://github.com/jeelabs/esp-link/releases/tag/v2.0.beta2) has REST support but
requires a 1MByte or 4MByte ESP8266 flash, e.g. esp-12 or wroom-02
- [V1.0.1](https://github.com/jeelabs/esp-link/releases/tag/v1.0.1) is _stable_
and has the web server, transparent bridge, flash-programming support, but lacks
the REST and upcoming MQTT support. V1 works with 512KB flash, e.g. esp-1, esp-3, ...
###[Latest release](https://github.com/jeelabs/esp-link/releases)
Note that the [stable V1.0 release](https://github.com/jeelabs/esp-link/releases/tag/v1.0.0) is
recommended if you do not need the outbound TCP connections and have a 512KB flash chip.
For quick support and questions:
[![Chat at https://gitter.im/jeelabs/esp-link](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jeelabs/esp-link?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Eye Candy
---------
@ -31,7 +39,7 @@ attached microcontroller, and the pin assignments card:
Hardware info
-------------
This firmware is designed for esp8266 modules which have most ESP I/O pins available and
512KB flash.
at least 1MB flash. (The V1 firmware supports modules with 512KB flash).
The default connections are:
- URXD: connect to TX of microcontroller
- UTXD: connect to RX of microcontroller
@ -42,6 +50,9 @@ The default connections are:
If you are using an FTDI connector, GPIO12 goes to DTR and GPIO13 goes to CTS.
If you are using an esp-12 module, you can avoid the initial boot message from the esp8266
bootloader by using the swap-pins option. This swaps the esp8266 TX/RX to gpio15/gpio13 respectively.
The GPIO pin assignments can be changed dynamically in the web UI and are saved in flash.
Initial flashing

@ -55,7 +55,7 @@ typedef enum {
CMD_REST_REQUEST,
CMD_REST_SETHEADER,
CMD_REST_EVENTS,
CMD_ADD_SENSOR, // 15
CMD_ADD_CALLBACK, // 15
CMD_SENSOR_EVENTS
} CmdName;
@ -71,19 +71,23 @@ typedef struct {
uint32_t callback;
} cmdCallback;
void ICACHE_FLASH_ATTR CMD_parse_packet(uint8_t *buf, short len);
cmdCallback* ICACHE_FLASH_ATTR CMD_GetCbByName(char* name);
// Used by slip protocol to cause parsing of a received packet
void CMD_parse_packet(uint8_t *buf, short len);
// Return the info about a callback to the attached uC by name, these are callbacks that the
// attached uC registers using the ADD_SENSOR command
cmdCallback* CMD_GetCbByName(char* name);
// Responses
// Start a response, returns the partial CRC
uint16_t ICACHE_FLASH_ATTR CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc);
uint16_t CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc);
// Adds data to a response, returns the partial CRC
uint16_t ICACHE_FLASH_ATTR CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len);
uint16_t CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len);
// Ends a response
void ICACHE_FLASH_ATTR CMD_ResponseEnd(uint16_t crc);
void CMD_ResponseEnd(uint16_t crc);
//void ICACHE_FLASH_ATTR CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]);
//void CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]);
// Requests

@ -10,17 +10,19 @@
#include "uart.h"
#include "cgiwifi.h"
static uint32_t ICACHE_FLASH_ATTR CMD_Null(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_IsReady(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_AddSensor(CmdPacket *cmd);
static uint32_t CMD_Null(CmdPacket *cmd);
static uint32_t CMD_IsReady(CmdPacket *cmd);
static uint32_t CMD_Reset(CmdPacket *cmd);
static uint32_t CMD_WifiConnect(CmdPacket *cmd);
static uint32_t CMD_AddCallback(CmdPacket *cmd);
// keep track of last status sent to uC so we can notify it when it changes
static uint8_t lastWifiStatus = wifiIsDisconnected;
// Command dispatch table for serial -> ESP commands
const CmdList commands[] = {
{CMD_NULL, CMD_Null},
{CMD_RESET, CMD_Null},
{CMD_RESET, CMD_Reset},
{CMD_IS_READY, CMD_IsReady},
{CMD_WIFI_CONNECT, CMD_WifiConnect},
@ -37,25 +39,14 @@ const CmdList commands[] = {
{CMD_REST_REQUEST, REST_Request},
{CMD_REST_SETHEADER, REST_SetHeader},
{CMD_ADD_SENSOR, CMD_AddSensor },
{CMD_ADD_CALLBACK, CMD_AddCallback },
{CMD_NULL, NULL}
};
// WifiCb plus 10 for sensors
cmdCallback callbacks[12] = {
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 }
};
#define MAX_CALLBACKS 12
cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset
// Command handler for IsReady (healthcheck) command
static uint32_t ICACHE_FLASH_ATTR
@ -71,20 +62,31 @@ CMD_Null(CmdPacket *cmd) {
return 1;
}
static void ICACHE_FLASH_ATTR
// Command handler for Reset command, this was originally to reset the ESP but we don't want to
// do that is esp-link. It is still good to clear any information the ESP has about the attached
// uC.
static uint32_t ICACHE_FLASH_ATTR
CMD_Reset(CmdPacket *cmd) {
os_printf("CMD_Reset\n");
// clear callbacks table
os_memset(callbacks, 0, sizeof(callbacks));
return 1;
}
static uint32_t ICACHE_FLASH_ATTR
CMD_AddCb(char* name, uint32_t cb) {
char checkname[16];
os_strncpy(checkname, name, sizeof(checkname));
for (uint8_t i = 0; i < sizeof(commands); i++) {
os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback);
for (uint8_t i = 0; i < MAX_CALLBACKS; i++) {
//os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name,
// (void *)callbacks[i].callback);
// find existing callback or add to the end
if (os_strcmp(callbacks[i].name, checkname) == 0 || callbacks[i].name[0] == '\0') {
os_strncpy(callbacks[i].name, checkname, sizeof(checkname));
if (os_strcmp(callbacks[i].name, name) == 0 || callbacks[i].name[0] == '\0') {
os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name));
callbacks[i].callback = cb;
os_printf("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i);
break;
return 1;
}
}
return 0;
}
cmdCallback* ICACHE_FLASH_ATTR
@ -92,7 +94,8 @@ CMD_GetCbByName(char* name) {
char checkname[16];
os_strncpy(checkname, name, sizeof(checkname));
for (uint8_t i = 0; i < sizeof(commands); i++) {
os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name, (void *)callbacks[i].callback);
//os_printf("CMD_GetCbByName: index %d name=%s cb=%p\n", i, callbacks[i].name,
// (void *)callbacks[i].callback);
// if callback doesn't exist or it's null
if (os_strcmp(callbacks[i].name, checkname) == 0) {
os_printf("CMD_GetCbByName: cb %s found at index %d\n", callbacks[i].name, i);
@ -130,33 +133,31 @@ CMD_WifiConnect(CmdPacket *cmd) {
wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem
CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback
lastWifiStatus = wifiIsDisconnected;
lastWifiStatus = 0xff; // set to invalid value so we immediately send status cb in all cases
CMD_WifiCb(wifiState);
return 1;
}
// Command handler for Wifi connect command
// Command handler to add a callback to the named-callbacks list, this is for a callback to the uC
static uint32_t ICACHE_FLASH_ATTR
CMD_AddSensor(CmdPacket *cmd) {
CMD_AddCallback(CmdPacket *cmd) {
CmdRequest req;
CMD_Request(&req, cmd);
os_printf("CMD_AddSensor: setup argc=%ld\n", CMD_GetArgc(&req));
os_printf("CMD_AddCallback: setup argc=%ld\n", CMD_GetArgc(&req));
if (cmd->argc != 1 || cmd->callback == 0)
return 0;
uint8_t* name;
char name[16];
uint16_t len;
// get the sensor name
len = CMD_ArgLen(&req);
os_printf("CMD_AddSensor: name len=%d\n", len);
os_printf("CMD_AddCallback: name len=%d\n", len);
if (len > 15) return 0; // max size of name is 15 characters
name = (uint8_t*)os_zalloc(len + 1);
if (CMD_PopArg(&req, name, len)) return 0;
if (CMD_PopArg(&req, (uint8_t *)name, len)) return 0;
name[len] = 0;
os_printf("CMD_AddSensor: name=%s\n", name);
os_printf("CMD_AddCallback: name=%s\n", name);
CMD_AddCb((char*)name, (uint32_t)cmd->callback); // save the sensor callback
return 1;
return CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback
}

@ -27,59 +27,66 @@ tcpclient_discon_cb(void *arg) {
client->data = 0;
}
// Receive HTTP response - this hacky function assumes that the full response is received in
// one go. Sigh...
static void ICACHE_FLASH_ATTR
tcpclient_recv(void *arg, char *pdata, unsigned short len) {
uint8_t currentLineIsBlank = 0;
uint8_t httpBody = 0;
uint8_t inStatus = 0;
char statusCode[4];
int i = 0, j;
uint32_t code = 0;
uint16_t crc;
struct espconn *pCon = (struct espconn*)arg;
RestClient *client = (RestClient *)pCon->reverse;
for(j=0 ;j<len; j++){
char c = pdata[j];
if(c == ' ' && !inStatus){
inStatus = 1;
}
if(inStatus && i < 3 && c != ' '){
statusCode[i] = c;
i++;
// parse status line
int pi = 0;
int32_t code = -1;
char statusCode[4] = "\0\0\0\0";
int statusLen = 0;
bool inStatus = false;
while (pi < len) {
if (pdata[pi] == '\n') {
// end of status line
if (code == -1) code = 502; // BAD GATEWAY
break;
} else if (pdata[pi] == ' ') {
if (inStatus) code = atoi(statusCode);
inStatus = !inStatus;
} else if (inStatus) {
if (statusLen < 3) statusCode[statusLen] = pdata[pi];
statusLen++;
}
if(i == 3){
statusCode[i] = '\0';
code = atoi(statusCode);
pi++;
}
if(httpBody){
//only write response if its not null
uint32_t body_len = len - j;
os_printf("REST: status=%ld, body=%ld\n", code, body_len);
if(body_len == 0){
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0);
} else {
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1);
crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+j), body_len);
}
CMD_ResponseEnd(crc);
// parse header, all this does is look for the end of the header
bool currentLineIsBlank = false;
while (pi < len) {
if (pdata[pi] == '\n') {
if (currentLineIsBlank) {
// body is starting
pi++;
break;
} else {
if (c == '\n' && currentLineIsBlank) {
httpBody = true;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
} else if (pdata[pi] != '\r') {
currentLineIsBlank = false;
}
pi++;
}
//if (pi < len && pdata[pi] == '\r') pi++; // hacky!
// collect body and send it
uint16_t crc;
int body_len = len-pi;
os_printf("REST: status=%ld, body=%d\n", code, body_len);
if (pi == len) {
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0);
} else {
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1);
crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+pi), body_len);
CMD_ResponseEnd(crc);
os_printf("REST: body=");
for (int j=pi; j<len; j++) os_printf(" %02x", pdata[j]);
os_printf("\n");
}
//if(client->security)
// espconn_secure_disconnect(client->pCon);
//else
@ -338,7 +345,8 @@ REST_Request(CmdPacket *cmd) {
// we need to allocate memory for the header plus the body. First we count the length of the
// header (including some extra counted "%s" and then we add the body length. We allocate the
// whole shebang and copy everything into it.
char *headerFmt = "%s %s HTTP/1.1\r\n"
// BTW, use http/1.0 to avoid responses with transfer-encoding: chunked
char *headerFmt = "%s %s HTTP/1.0\r\n"
"Host: %s\r\n"
"%s"
"Content-Length: %d\r\n"
@ -360,11 +368,12 @@ REST_Request(CmdPacket *cmd) {
CMD_PopArg(&req, client->data + client->data_len, realLen);
client->data_len += realLen;
}
os_printf("\n");
os_printf("REST: pCon state=%d\n", client->pCon->state);
client->pCon->state = ESPCONN_NONE;
espconn_regist_connectcb(client->pCon, tcpclient_connect_cb);
espconn_regist_reconcb(client->pCon, tcpclient_recon_cb);
os_printf("\n");
if(UTILS_StrToIP((char *)client->host, &client->pCon->proto.tcp->remote_ip)) {
os_printf("REST: Connect to ip %s:%ld\n",client->host, client->port);

@ -70,6 +70,7 @@ slip_printable(char c) {
static void ICACHE_FLASH_ATTR
slip_reset() {
//os_printf("SLIP: reset\n");
slip_inpkt = false;
slip_escaped = false;
slip_len = 0;
@ -83,6 +84,7 @@ slip_parse_char(char c) {
if (slip_len > 0) console_process(slip_buf, slip_len);
slip_reset();
slip_inpkt = true;
os_printf("SLIP: start\n");
return;
}
} else if (slip_escaped) {
@ -101,6 +103,9 @@ slip_parse_char(char c) {
return;
case SLIP_START:
os_printf("SLIP: got SLIP_START while in packet?\n");
//os_printf("SLIP: rcv %d:", slip_len);
//for (int i=0; i<slip_len; i++) os_printf(" %02x", slip_buf[i]);
//os_printf("\n");
slip_reset();
return;
}

Loading…
Cancel
Save