incorporated tuanpmt's slip protocol and rest library

pull/37/head
Thorsten von Eicken 9 years ago
parent 7490e45aab
commit ddd2af1a65
  1. 2
      Makefile
  2. 179
      cmd/cmd.c
  3. 90
      cmd/cmd.h
  4. 79
      cmd/handlers.c
  5. 394
      cmd/rest.c
  6. 39
      cmd/rest.h
  7. 0
      cmd/tcpclient.c
  8. 2
      cmd/tcpclient.h
  9. 78
      serial/crc16.c
  10. 100
      serial/crc16.h
  11. 670
      serial/serbridge.c
  12. 2
      serial/serbridge.h
  13. 136
      serial/slip.c
  14. 2
      serial/uart.h
  15. 5
      user/cgiwifi.c
  16. 2
      user/cgiwifi.h

@ -146,7 +146,7 @@ TARGET = httpd
APPGEN_TOOL ?= gen_appbin.py
# which modules (subdirectories) of the project to include in compiling
MODULES = espfs httpd user serial
MODULES = espfs httpd user serial cmd
EXTRA_INCDIR = include . # lib/heatshrink/
# libraries used in this project, mainly provided by the SDK

@ -0,0 +1,179 @@
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
//
// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh
#include "esp8266.h"
#include "cmd.h"
#include "crc16.h"
#include "serbridge.h"
#include "uart.h"
extern const CmdList commands[];
//===== ESP -> Serial responses
static void ICACHE_FLASH_ATTR
CMD_ProtoWrite(uint8_t data) {
switch(data){
case SLIP_START:
case SLIP_END:
case SLIP_REPL:
uart0_write_char(SLIP_REPL);
uart0_write_char(SLIP_ESC(data));
break;
default:
uart0_write_char(data);
}
}
static void ICACHE_FLASH_ATTR
CMD_ProtoWriteBuf(uint8_t *data, short len) {
while (len--) CMD_ProtoWrite(*data++);
}
// 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 crc = 0;
uart0_write_char(SLIP_START);
CMD_ProtoWriteBuf((uint8_t*)&cmd, 2);
crc = crc16_data((uint8_t*)&cmd, 2, crc);
CMD_ProtoWriteBuf((uint8_t*)&callback, 4);
crc = crc16_data((uint8_t*)&callback, 4, crc);
CMD_ProtoWriteBuf((uint8_t*)&_return, 4);
crc = crc16_data((uint8_t*)&_return, 4, crc);
CMD_ProtoWriteBuf((uint8_t*)&argc, 2);
crc = crc16_data((uint8_t*)&argc, 2, crc);
return crc;
}
// 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) {
short pad_len = len+3 - (len+3)%4; // round up to multiple of 4
CMD_ProtoWriteBuf((uint8_t*)&pad_len, 2);
crc_in = crc16_data((uint8_t*)&pad_len, 2, crc_in);
CMD_ProtoWriteBuf(data, len);
crc_in = crc16_data(data, len, crc_in);
if (pad_len > len) {
uint32_t temp = 0;
CMD_ProtoWriteBuf((uint8_t*)&temp, pad_len-len);
crc_in = crc16_data((uint8_t*)&temp, pad_len-len, crc_in);
}
return crc_in;
}
// Ends a response
void ICACHE_FLASH_ATTR
CMD_ResponseEnd(uint16_t crc) {
CMD_ProtoWriteBuf((uint8_t*)&crc, 2);
uart0_write_char(SLIP_END);
}
//===== serial -> ESP commands
// Execute a parsed command
static uint32_t ICACHE_FLASH_ATTR
CMD_Exec(const CmdList *scp, CmdPacket *packet) {
uint16_t crc = 0;
// Iterate through the command table and call the appropriate function
while (scp->sc_function != NULL) {
if(scp->sc_name == packet->cmd) {
//os_printf("CMD: Dispatching cmd=%d\n", packet->cmd);
// call command function
uint32_t ret = scp->sc_function(packet);
// if requestor asked for a response, send it
if (packet->_return){
os_printf("CMD: Response: %lu, cmd: %d\r\n", ret, packet->cmd);
crc = CMD_ResponseStart(packet->cmd, 0, ret, 0);
CMD_ResponseEnd(crc);
} else {
//os_printf("CMD: no response (%lu)\n", packet->_return);
}
return ret;
}
scp++;
}
os_printf("CMD: cmd=%d not found\n", packet->cmd);
return 0;
}
// Parse a packet and print info about it
void ICACHE_FLASH_ATTR
CMD_parse_packet(uint8_t *buf, short len) {
// minimum command length
if (len < 12) return;
// init pointers into buffer
CmdPacket *packet = (CmdPacket*)buf;
uint8_t *data_ptr = (uint8_t*)&packet->args;
uint8_t *data_limit = data_ptr+len;
uint16_t argc = packet->argc;
uint16_t argn = 0;
os_printf("CMD: cmd=%d argc=%d cb=%p ret=%lu\n",
packet->cmd, packet->argc, (void *)packet->callback, packet->_return);
// print out arguments
while (data_ptr+2 < data_limit && argc--) {
short l = *(uint16_t*)data_ptr;
os_printf("CMD: arg[%d] len=%d:", argn++, l);
data_ptr += 2;
while (data_ptr < data_limit && l--) {
os_printf(" %02X", *data_ptr++);
}
os_printf("\n");
}
if (data_ptr <= data_limit) {
CMD_Exec(commands, packet);
} else {
os_printf("CMD: packet length overrun, parsing arg %d\n", argn-1);
}
}
//===== Helpers to parse a command packet
// Fill out a CmdRequest struct given a CmdPacket
void ICACHE_FLASH_ATTR
CMD_Request(CmdRequest *req, CmdPacket* cmd) {
req->cmd = cmd;
req->arg_num = 0;
req->arg_ptr = (uint8_t*)&cmd->args;
}
// Return the number of arguments given a command struct
uint32_t ICACHE_FLASH_ATTR
CMD_GetArgc(CmdRequest *req) {
return req->cmd->argc;
}
// Copy the next argument from a command structure into the data pointer, returns 0 on success
// -1 on error
int32_t ICACHE_FLASH_ATTR
CMD_PopArg(CmdRequest *req, void *data, uint16_t len) {
uint16_t length;
if (req->arg_num >= req->cmd->argc)
return -1;
length = *(uint16_t*)req->arg_ptr;
if (length != len) return -1; // safety check
req->arg_ptr += 2;
os_memcpy(data, req->arg_ptr, length);
req->arg_ptr += length;
req->arg_num ++;
return 0;
}
// Return the length of the next argument
uint16_t ICACHE_FLASH_ATTR
CMD_ArgLen(CmdRequest *req) {
return *(uint16_t*)req->arg_ptr;
}

@ -0,0 +1,90 @@
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
//
// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh
#ifndef CMD_H
#define CMD_H
// Escape chars used by tuanpmt, dunno why he didn't use std ones...
#define SLIP_START 0x7E
#define SLIP_END 0x7F
#define SLIP_REPL 0x7D
#define SLIP_ESC(x) (x ^ 0x20)
#if 0
// Proper SLIP escape chars from RFC
#define SLIP_END 0300 // indicates end of packet
#define SLIP_ESC 0333 // indicates byte stuffing
#define SLIP_ESC_END 0334 // ESC ESC_END means END data byte
#define SLIP_ESC_ESC 0335 // ESC ESC_ESC means ESC data byte
#endif
typedef struct __attribute((__packed__)) {
uint16_t len; // length of data
uint8_t data[0]; // really data[len]
} CmdArg;
typedef struct __attribute((__packed__)) {
uint16_t cmd; // command to perform, from CmdName enum
uint32_t callback; // callback pointer to embed in response
uint32_t _return; // return value to embed in response (?)
uint16_t argc; // number of arguments to command
CmdArg args[0]; // really args[argc]
} CmdPacket;
typedef struct {
CmdPacket *cmd; // command packet header
uint32_t arg_num; // number of args parsed
uint8_t *arg_ptr; // pointer to ??
} CmdRequest;
typedef enum {
CMD_NULL = 0,
CMD_RESET, // reset esp (not honored in this implementation)
CMD_IS_READY, // health-check
CMD_WIFI_CONNECT, // (3) connect to AP (not honored in this implementation)
CMD_MQTT_SETUP,
CMD_MQTT_CONNECT,
CMD_MQTT_DISCONNECT,
CMD_MQTT_PUBLISH,
CMD_MQTT_SUBSCRIBE,
CMD_MQTT_LWT,
CMD_MQTT_EVENTS,
CMD_REST_SETUP, // (11)
CMD_REST_REQUEST,
CMD_REST_SETHEADER,
CMD_REST_EVENTS
} CmdName;
typedef uint32_t (*cmdfunc_t)(CmdPacket *cmd);
typedef struct {
CmdName sc_name;
cmdfunc_t sc_function;
} CmdList;
void CMD_parse_packet(uint8_t *buf, short len);
// Responses
// Start a response, returns the partial CRC
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 CMD_ResponseBody(uint16_t crc_in, uint8_t* data, short len);
// Ends a response
void CMD_ResponseEnd(uint16_t crc);
void CMD_Response(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc, CmdArg* args[]);
// Requests
// Fill out a CmdRequest struct given a CmdPacket
void CMD_Request(CmdRequest *req, CmdPacket* cmd);
// Return the number of arguments given a request
uint32_t CMD_GetArgc(CmdRequest *req);
// Return the length of the next argument
uint16_t CMD_ArgLen(CmdRequest *req);
// Copy next arg from request into the data pointer, returns 0 on success, -1 on error
int32_t CMD_PopArg(CmdRequest *req, void *data, uint16_t len);
#endif

@ -0,0 +1,79 @@
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
//
// Adapted from: github.com/tuanpmt/esp_bridge, Created on: Jan 9, 2015, Author: Minh
#include "esp8266.h"
#include "cmd.h"
#include "rest.h"
#include "crc16.h"
#include "serbridge.h"
#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);
// Command dispatch table for serial -> ESP commands
const CmdList commands[] = {
{CMD_NULL, CMD_Null},
{CMD_RESET, CMD_Null},
{CMD_IS_READY, CMD_IsReady},
{CMD_WIFI_CONNECT, CMD_WifiConnect},
/*
{CMD_MQTT_SETUP, MQTTAPP_Setup},
{CMD_MQTT_CONNECT, MQTTAPP_Connect},
{CMD_MQTT_DISCONNECT, MQTTAPP_Disconnect},
{CMD_MQTT_PUBLISH, MQTTAPP_Publish},
{CMD_MQTT_SUBSCRIBE , MQTTAPP_Subscribe},
{CMD_MQTT_LWT, MQTTAPP_Lwt},
*/
{CMD_REST_SETUP, REST_Setup},
{CMD_REST_REQUEST, REST_Request},
{CMD_REST_SETHEADER, REST_SetHeader},
{CMD_NULL, NULL}
};
// Command handler for IsReady (healthcheck) command
static uint32_t ICACHE_FLASH_ATTR
CMD_IsReady(CmdPacket *cmd) {
os_printf("CMD: Check ready\n");
return 1;
}
// Command handler for Null command
static uint32_t ICACHE_FLASH_ATTR
CMD_Null(CmdPacket *cmd) {
os_printf("CMD: NULL/unsupported command\n");
return 1;
}
static uint8_t lastWifiStatus;
static uint32_t wifiCallback;
// Callback from wifi subsystem to notify us of status changes
static void ICACHE_FLASH_ATTR
CMD_WifiCb(uint8_t wifiStatus) {
if (wifiStatus != lastWifiStatus){
lastWifiStatus = wifiStatus;
if (wifiCallback) {
uint8_t status = wifiStatus == wifiGotIP ? 5 : 1;
uint16_t crc = CMD_ResponseStart(CMD_WIFI_CONNECT, wifiCallback, 0, 1);
crc = CMD_ResponseBody(crc, (uint8_t*)&status, 1);
CMD_ResponseEnd(crc);
}
}
}
// Command handler for Wifi connect command
static uint32_t ICACHE_FLASH_ATTR
CMD_WifiConnect(CmdPacket *cmd) {
if(cmd->argc != 2 || cmd->callback == 0)
return 0xFFFFFFFF;
wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem
wifiCallback = cmd->callback; // save the MCU's callback
return 1;
}

@ -0,0 +1,394 @@
/*
* api.c
*
* Created on: Mar 4, 2015
* Author: Minh
*/
#include "esp8266.h"
#include "rest.h"
#include "cmd.h"
extern uint8_t ICACHE_FLASH_ATTR UTILS_StrToIP(const char* str, void *ip);
static void ICACHE_FLASH_ATTR
tcpclient_discon_cb(void *arg) {
//struct espconn *pespconn = (struct espconn *)arg;
//RestClient* client = (RestClient *)pespconn->reverse;
}
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++;
}
if(i == 3){
statusCode[i] = '\0';
code = atoi(statusCode);
}
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);
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
currentLineIsBlank = false;
}
}
}
//if(client->security)
// espconn_secure_disconnect(client->pCon);
//else
espconn_disconnect(client->pCon);
}
static void ICACHE_FLASH_ATTR
tcpclient_sent_cb(void *arg) {
//struct espconn *pCon = (struct espconn *)arg;
//RestClient* client = (RestClient *)pCon->reverse;
os_printf("REST: Sent\n");
}
static void ICACHE_FLASH_ATTR
tcpclient_connect_cb(void *arg) {
struct espconn *pCon = (struct espconn *)arg;
RestClient* client = (RestClient *)pCon->reverse;
espconn_regist_disconcb(client->pCon, tcpclient_discon_cb);
espconn_regist_recvcb(client->pCon, tcpclient_recv);////////
espconn_regist_sentcb(client->pCon, tcpclient_sent_cb);///////
//if(client->security){
// espconn_secure_sent(client->pCon, client->data, client->data_len);
//}
//else{
espconn_sent(client->pCon, client->data, client->data_len);
//}
}
static void ICACHE_FLASH_ATTR
tcpclient_recon_cb(void *arg, sint8 errType) {
//struct espconn *pCon = (struct espconn *)arg;
//RestClient* client = (RestClient *)pCon->reverse;
}
static void ICACHE_FLASH_ATTR
rest_dns_found(const char *name, ip_addr_t *ipaddr, void *arg) {
struct espconn *pConn = (struct espconn *)arg;
RestClient* client = (RestClient *)pConn->reverse;
if(ipaddr == NULL) {
os_printf("REST DNS: Got no ip, try to reconnect\n");
return;
}
os_printf("REST DNS: found ip %d.%d.%d.%d\n",
*((uint8 *) &ipaddr->addr),
*((uint8 *) &ipaddr->addr + 1),
*((uint8 *) &ipaddr->addr + 2),
*((uint8 *) &ipaddr->addr + 3));
if(client->ip.addr == 0 && ipaddr->addr != 0) {
os_memcpy(client->pCon->proto.tcp->remote_ip, &ipaddr->addr, 4);
//if(client->security){
// espconn_secure_connect(client->pCon);
//}
//else {
espconn_connect(client->pCon);
//}
os_printf("REST: connecting...\n");
}
}
uint32_t ICACHE_FLASH_ATTR
REST_Setup(CmdPacket *cmd) {
CmdRequest req;
RestClient *client;
uint8_t *rest_host;
uint16_t len;
uint32_t port, security;
// start parsing the command
CMD_Request(&req, cmd);
os_printf("REST: setup argc=%ld\n", CMD_GetArgc(&req));
if(CMD_GetArgc(&req) != 3)
return 0;
// get the hostname
len = CMD_ArgLen(&req);
os_printf("REST: len=%d\n", len);
if (len > 128) return 0; // safety check
rest_host = (uint8_t*)os_zalloc(len + 1);
if (CMD_PopArg(&req, rest_host, len)) return 0;
rest_host[len] = 0;
os_printf("REST: setup host=%s", rest_host);
// get the port
if (CMD_PopArg(&req, (uint8_t*)&port, 4)) {
os_free(rest_host);
return 0;
}
os_printf(" port=%ld", port);
// get the security mode
if (CMD_PopArg(&req, (uint8_t*)&security, 4)) {
os_free(rest_host);
return 0;
}
os_printf(" security=%ld\n", security);
// allocate a connection structure
client = (RestClient*)os_zalloc(sizeof(RestClient));
os_memset(client, 0, sizeof(RestClient));
if(client == NULL)
return 0;
os_printf("REST: setup host=%s port=%ld security=%ld\n", rest_host, port, security);
client->resp_cb = cmd->callback;
client->host = rest_host;
client->port = port;
client->security = security;
client->ip.addr = 0;
client->data = (uint8_t*)os_zalloc(1024);
client->header = (uint8_t*)os_zalloc(4);
client->header[0] = 0;
client->content_type = (uint8_t*)os_zalloc(22);
os_sprintf((char *)client->content_type, "x-www-form-urlencoded");
client->content_type[21] = 0;
client->user_agent = (uint8_t*)os_zalloc(9);
os_sprintf((char *)client->user_agent, "esp-link");
client->pCon = (struct espconn *)os_zalloc(sizeof(struct espconn));
client->pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
client->pCon->type = ESPCONN_TCP;
client->pCon->state = ESPCONN_NONE;
client->pCon->proto.tcp->local_port = espconn_port();
client->pCon->proto.tcp->remote_port = client->port;
client->pCon->reverse = client;
return (uint32_t)client;
}
uint32_t ICACHE_FLASH_ATTR
REST_SetHeader(CmdPacket *cmd) {
CmdRequest req;
RestClient *client;
uint16_t len;
uint32_t header_index, client_ptr = 0;
CMD_Request(&req, cmd);
if(CMD_GetArgc(&req) != 3)
return 0;
// Get client -- TODO: Whoa, this is totally unsafe!
if (CMD_PopArg(&req, (uint8_t*)&client_ptr, 4)) return 0;
client = (RestClient*)client_ptr;
// Get header selector
if (CMD_PopArg(&req, (uint8_t*)&header_index, 4)) return 0;
// Get header value
len = CMD_ArgLen(&req);
if (len > 256) return 0; //safety check
switch(header_index) {
case HEADER_GENERIC:
if(client->header)
os_free(client->header);
client->header = (uint8_t*)os_zalloc(len + 1);
CMD_PopArg(&req, (uint8_t*)client->header, len);
client->header[len] = 0;
os_printf("REST: Set header: %s\r\n", client->header);
break;
case HEADER_CONTENT_TYPE:
if(client->content_type)
os_free(client->content_type);
client->content_type = (uint8_t*)os_zalloc(len + 1);
CMD_PopArg(&req, (uint8_t*)client->content_type, len);
client->content_type[len] = 0;
os_printf("REST: Set content_type: %s\r\n", client->content_type);
break;
case HEADER_USER_AGENT:
if(client->user_agent)
os_free(client->user_agent);
client->user_agent = (uint8_t*)os_zalloc(len + 1);
CMD_PopArg(&req, (uint8_t*)client->user_agent, len);
client->user_agent[len] = 0;
os_printf("REST: Set user_agent: %s\r\n", client->user_agent);
break;
}
return 1;
}
uint32_t ICACHE_FLASH_ATTR
REST_Request(CmdPacket *cmd) {
CmdRequest req;
RestClient *client;
uint16_t len, realLen = 0;
uint32_t client_ptr;
uint8_t *body = NULL;
uint8_t method[16];
uint8_t path[1024];
CMD_Request(&req, cmd);
if(CMD_GetArgc(&req) <3)
return 0;
// Get client -- TODO: Whoa, this is totally unsafe!
if(CMD_PopArg(&req, (uint8_t*)&client_ptr, 4)) return 0;
client = (RestClient*)client_ptr;
// Get HTTP method
len = CMD_ArgLen(&req);
if (len > 15) return 0;
CMD_PopArg(&req, method, len);
method[len] = 0;
// Get HTTP path
len = CMD_ArgLen(&req);
if (len > 1023) return 0;
CMD_PopArg(&req, path, len);
path[len] = 0;
// Get HTTP body
if (CMD_GetArgc(&req) == 3){
realLen = 0;
len = 0;
} else {
CMD_PopArg(&req, (uint8_t*)&realLen, 4);
len = CMD_ArgLen(&req);
if (len > 2048 || realLen > len) return 0;
body = (uint8_t*)os_zalloc(len + 1);
CMD_PopArg(&req, body, len);
body[len] = 0;
}
client->pCon->state = ESPCONN_NONE;
os_printf("REST: method: %s, path: %s\n", method, path);
client->data_len = os_sprintf((char*)client->data, "%s %s HTTP/1.1\r\n"
"Host: %s\r\n"
"%s"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"Content-Type: %s\r\n"
"User-Agent: %s\r\n\r\n",
method, path,
client->host,
client->header,
realLen,
client->content_type,
client->user_agent);
if (realLen > 0){
os_memcpy(client->data + client->data_len, body, realLen);
client->data_len += realLen;
//os_sprintf(client->data + client->data_len, "\r\n\r\n");
//client->data_len += 4;
}
client->pCon->state = ESPCONN_NONE;
espconn_regist_connectcb(client->pCon, tcpclient_connect_cb);
espconn_regist_reconcb(client->pCon, tcpclient_recon_cb);
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);
//if(client->security){
// espconn_secure_connect(client->pCon);
//}
//else {
espconn_connect(client->pCon);
//}
} else {
os_printf("REST: Connect to host %s:%ld\n", client->host, client->port);
espconn_gethostbyname(client->pCon, (char *)client->host, &client->ip, rest_dns_found);
}
if(body) os_free(body);
return 1;
}
uint8_t ICACHE_FLASH_ATTR
UTILS_StrToIP(const char* str, void *ip)
{
/* The count of the number of bytes processed. */
int i;
/* A pointer to the next digit to process. */
const char * start;
start = str;
for (i = 0; i < 4; i++) {
/* The digit being processed. */
char c;
/* The value of this byte. */
int n = 0;
while (1) {
c = * start;
start++;
if (c >= '0' && c <= '9') {
n *= 10;
n += c - '0';
}
/* We insist on stopping at "." if we are still parsing
the first, second, or third numbers. If we have reached
the end of the numbers, we will allow any character. */
else if ((i < 3 && c == '.') || i == 3) {
break;
}
else {
return 0;
}
}
if (n >= 256) {
return 0;
}
((uint8_t*)ip)[i] = n;
}
return 1;
}

@ -0,0 +1,39 @@
/*
* api.h
*
* Created on: Mar 4, 2015
* Author: Minh
*/
#ifndef MODULES_API_H_
#define MODULES_API_H_
#include "c_types.h"
#include "ip_addr.h"
#include "cmd.h"
typedef enum {
HEADER_GENERIC = 0,
HEADER_CONTENT_TYPE,
HEADER_USER_AGENT
} HEADER_TYPE;
typedef struct {
uint8_t *host;
uint32_t port;
uint32_t security;
ip_addr_t ip;
struct espconn *pCon;
uint8_t *header;
uint8_t *data;
uint32_t data_len;
uint8_t *content_type;
uint8_t *user_agent;
uint32_t resp_cb;
} RestClient;
uint32_t REST_Setup(CmdPacket *cmd);
uint32_t REST_Request(CmdPacket *cmd);
uint32_t REST_SetHeader(CmdPacket *cmd);
#endif /* MODULES_INCLUDE_API_H_ */

@ -4,7 +4,7 @@
// max number of channels the client can open
#define MAX_TCP_CHAN 8
// Parse and perform the commandm cmdBuf must be null-terminated
// Parse and perform the command, cmdBuf must be null-terminated
bool tcpClientCommand(uint8_t chan, char cmd, char *cmdBuf);
// Append a character to the specified channel

@ -0,0 +1,78 @@
/*
* Copyright (c) 2005, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/** \addtogroup crc16
* @{ */
/**
* \file
* Implementation of the CRC16 calculcation
* \author
* Adam Dunkels <adam@sics.se>
*
*/
/* CITT CRC16 polynomial ^16 + ^12 + ^5 + 1 */
/*---------------------------------------------------------------------------*/
unsigned short
crc16_add(unsigned char b, unsigned short acc)
{
/*
acc = (unsigned char)(acc >> 8) | (acc << 8);
acc ^= b;
acc ^= (unsigned char)(acc & 0xff) >> 4;
acc ^= (acc << 8) << 4;
acc ^= ((acc & 0xff) << 4) << 1;
*/
acc ^= b;
acc = (acc >> 8) | (acc << 8);
acc ^= (acc & 0xff00) << 4;
acc ^= (acc >> 8) >> 4;
acc ^= (acc & 0xff00) >> 5;
return acc;
}
/*---------------------------------------------------------------------------*/
unsigned short
crc16_data(const unsigned char *data, int len, unsigned short acc)
{
int i;
for(i = 0; i < len; ++i) {
acc = crc16_add(*data, acc);
++data;
}
return acc;
}
/*---------------------------------------------------------------------------*/
/** @} */

@ -0,0 +1,100 @@
/*
* Copyright (c) 2005, Swedish Institute of Computer Science
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/**
* \file
* Header file for the CRC16 calculcation
* \author
* Adam Dunkels <adam@sics.se>
*
*/
/** \addtogroup lib
* @{ */
/**
* \defgroup crc16 Cyclic Redundancy Check 16 (CRC16) calculation
*
* The Cyclic Redundancy Check 16 is a hash function that produces a
* checksum that is used to detect errors in transmissions. The CRC16
* calculation module is an iterative CRC calculator that can be used
* to cumulatively update a CRC checksum for every incoming byte.
*
* @{
*/
#ifndef CRC16_H_
#define CRC16_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Update an accumulated CRC16 checksum with one byte.
* \param b The byte to be added to the checksum
* \param crc The accumulated CRC that is to be updated.
* \return The updated CRC checksum.
*
* This function updates an accumulated CRC16 checksum
* with one byte. It can be used as a running checksum, or
* to checksum an entire data block.
*
* \note The algorithm used in this implementation is
* tailored for a running checksum and does not perform as
* well as a table-driven algorithm when checksumming an
* entire data block.
*
*/
unsigned short crc16_add(unsigned char b, unsigned short crc);
/**
* \brief Calculate the CRC16 over a data area
* \param data Pointer to the data
* \param datalen The length of the data
* \param acc The accumulated CRC that is to be updated (or zero).
* \return The CRC16 checksum.
*
* This function calculates the CRC16 checksum of a data area.
*
* \note The algorithm used in this implementation is
* tailored for a running checksum and does not perform as
* well as a table-driven algorithm when checksumming an
* entire data block.
*/
unsigned short crc16_data(const unsigned char *data, int datalen,
unsigned short acc);
#ifdef __cplusplus
}
#endif
#endif /* CRC16_H_ */
/** @} */
/** @} */

@ -9,24 +9,27 @@
#include "gpio.h"
#include "uart.h"
#include "crc16.h"
#include "serbridge.h"
#include "serled.h"
#include "config.h"
#include "console.h"
#include "tcpclient.h"
#include "cmd.h"
static struct espconn serbridgeConn;
static esp_tcp serbridgeTcp;
static int8_t mcu_reset_pin, mcu_isp_pin;
extern uint8_t slip_disabled; // disable slip to allow flashing of attached MCU
static sint8 ICACHE_FLASH_ATTR espbuffsend(serbridgeConnData *conn, const char *data, uint16 len);
// Connection pool
serbridgeConnData connData[MAX_CONN];
// 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 arg == NULL ? NULL : (serbridgeConnData *)conn->reverse;
struct espconn *conn = arg;
return arg == NULL ? NULL : (serbridgeConnData *)conn->reverse;
}
//===== TCP -> UART
@ -50,149 +53,152 @@ enum { TN_normal, TN_iac, TN_will, TN_start, TN_end, TN_comPort, TN_setControl }
static uint8_t ICACHE_FLASH_ATTR
telnetUnwrap(uint8_t *inBuf, int len, uint8_t state)
{
for (int i=0; i<len; i++) {
uint8_t c = inBuf[i];
switch (state) {
default:
case TN_normal:
if (c == IAC) state = TN_iac; // escape char: see what's next
else uart0_write_char(c); // regular char
break;
case TN_iac:
switch (c) {
case IAC: // second escape -> write one to outbuf and go normal again
state = TN_normal;
uart0_write_char(c);
break;
case WILL: // negotiation
state = TN_will;
break;
case SB: // command sequence begin
state = TN_start;
break;
case SE: // command sequence end
state = TN_normal;
break;
default: // not sure... let's ignore
uart0_write_char(IAC);
uart0_write_char(c);
}
break;
case TN_will:
state = TN_normal; // yes, we do COM port options, let's go back to normal
break;
case TN_start: // in command seq, now comes the type of cmd
if (c == ComPortOpt) state = TN_comPort;
else state = TN_end; // an option we don't know, skip 'til the end seq
break;
case TN_end: // wait for end seq
if (c == IAC) state = TN_iac; // simple wait to accept end or next escape seq
break;
case TN_comPort:
if (c == SetControl) state = TN_setControl;
else state = TN_end;
break;
case TN_setControl: // switch control line and delay a tad
switch (c) {
case DTR_ON:
if (mcu_reset_pin >= 0) {
os_printf("MCU reset gpio%d\n", mcu_reset_pin);
GPIO_OUTPUT_SET(mcu_reset_pin, 0);
os_delay_us(100L);
} else os_printf("MCU reset: no pin\n");
break;
case DTR_OFF:
if (mcu_reset_pin >= 0) {
GPIO_OUTPUT_SET(mcu_reset_pin, 1);
os_delay_us(100L);
}
break;
case RTS_ON:
if (mcu_isp_pin >= 0) {
os_printf("MCU ISP gpio%d\n", mcu_isp_pin);
GPIO_OUTPUT_SET(mcu_isp_pin, 0);
os_delay_us(100L);
} else os_printf("MCU isp: no pin\n");
break;
case RTS_OFF:
if (mcu_isp_pin >= 0) {
GPIO_OUTPUT_SET(mcu_isp_pin, 1);
os_delay_us(100L);
}
break;
}
state = TN_end;
break;
}
}
return state;
for (int i=0; i<len; i++) {
uint8_t c = inBuf[i];
switch (state) {
default:
case TN_normal:
if (c == IAC) state = TN_iac; // escape char: see what's next
else uart0_write_char(c); // regular char
break;
case TN_iac:
switch (c) {
case IAC: // second escape -> write one to outbuf and go normal again
state = TN_normal;
uart0_write_char(c);
break;
case WILL: // negotiation
state = TN_will;
break;
case SB: // command sequence begin
state = TN_start;
break;
case SE: // command sequence end
state = TN_normal;
break;
default: // not sure... let's ignore
uart0_write_char(IAC);
uart0_write_char(c);
}
break;
case TN_will:
state = TN_normal; // yes, we do COM port options, let's go back to normal
break;
case TN_start: // in command seq, now comes the type of cmd
if (c == ComPortOpt) state = TN_comPort;
else state = TN_end; // an option we don't know, skip 'til the end seq
break;
case TN_end: // wait for end seq
if (c == IAC) state = TN_iac; // simple wait to accept end or next escape seq
break;
case TN_comPort:
if (c == SetControl) state = TN_setControl;
else state = TN_end;
break;
case TN_setControl: // switch control line and delay a tad
switch (c) {
case DTR_ON:
if (mcu_reset_pin >= 0) {
os_printf("MCU reset gpio%d\n", mcu_reset_pin);
GPIO_OUTPUT_SET(mcu_reset_pin, 0);
os_delay_us(100L);
} else os_printf("MCU reset: no pin\n");
break;
case DTR_OFF:
if (mcu_reset_pin >= 0) {
GPIO_OUTPUT_SET(mcu_reset_pin, 1);
os_delay_us(100L);
}
break;
case RTS_ON:
if (mcu_isp_pin >= 0) {
os_printf("MCU ISP gpio%d\n", mcu_isp_pin);
GPIO_OUTPUT_SET(mcu_isp_pin, 0);
os_delay_us(100L);
} else os_printf("MCU isp: no pin\n");
slip_disabled++;
break;
case RTS_OFF:
if (mcu_isp_pin >= 0) {
GPIO_OUTPUT_SET(mcu_isp_pin, 1);
os_delay_us(100L);
}
if (slip_disabled > 0) slip_disabled--;
break;
}
state = TN_end;
break;
}
}
return state;
}
// Generate a reset pulse for the attached microcontroller
void ICACHE_FLASH_ATTR serbridgeReset() {
if (mcu_reset_pin >= 0) {
os_printf("MCU reset gpio%d\n", mcu_reset_pin);
GPIO_OUTPUT_SET(mcu_reset_pin, 0);
os_delay_us(100L);
GPIO_OUTPUT_SET(mcu_reset_pin, 1);
} else os_printf("MCU reset: no pin\n");
if (mcu_reset_pin >= 0) {
os_printf("MCU reset gpio%d\n", mcu_reset_pin);
GPIO_OUTPUT_SET(mcu_reset_pin, 0);
os_delay_us(100L);
GPIO_OUTPUT_SET(mcu_reset_pin, 1);
} 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);
//os_printf("Receive callback on conn %p\n", conn);
if (conn == NULL) return;
// at the start of a connection we're in cmInit mode and we wait for the first few characters
// to arrive in order to decide what type of connection this is.. The following if statements
// do this dispatch. An issue here is that we assume that the first few characters all arrive
// in the same TCP packet, which is true if the sender is a program, but not necessarily
// if the sender is a person typing (although in that case the line-oriented TTY input seems
// to make it work too). If this becomes a problem we need to buffer the first few chars...
if (conn->conn_mode == cmInit) {
// If the connection starts with the Arduino or ARM reset sequence we perform a RESET
if ((len == 2 && strncmp(data, "0 ", 2) == 0) ||
(len == 2 && strncmp(data, "?\n", 2) == 0) ||
(len == 3 && strncmp(data, "?\r\n", 3) == 0)) {
os_printf("MCU Reset=%d ISP=%d\n", mcu_reset_pin, mcu_isp_pin);
os_delay_us(2*1000L); // time for os_printf to happen
// send reset to arduino/ARM
if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 0);
os_delay_us(100L);
if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 0);
os_delay_us(100L);
if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1);
os_delay_us(100L);
if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1);
os_delay_us(1000L);
conn->conn_mode = cmAVR;
// If the connection starts with a telnet negotiation we will do telnet
} else if (len >= 3 && strncmp(data, (char[]){IAC, WILL, ComPortOpt}, 3) == 0) {
conn->conn_mode = cmTelnet;
conn->telnet_state = TN_normal;
// note that the three negotiation chars will be gobbled-up by telnetUnwrap
os_printf("telnet mode\n");
// looks like a plain-vanilla connection!
} else {
conn->conn_mode = cmTransparent;
}
// Process return data on TCP client connections
} else if (conn->conn_mode == cmTcpClient) {
}
// write the buffer to the uart
if (conn->conn_mode == cmTelnet) {
conn->telnet_state = telnetUnwrap((uint8_t *)data, len, conn->telnet_state);
} else {
uart0_tx_buffer(data, len);
}
serledFlash(50); // short blink on serial LED
serbridgeConnData *conn = serbridgeFindConnData(arg);
//os_printf("Receive callback on conn %p\n", conn);
if (conn == NULL) return;
// at the start of a connection we're in cmInit mode and we wait for the first few characters
// to arrive in order to decide what type of connection this is.. The following if statements
// do this dispatch. An issue here is that we assume that the first few characters all arrive
// in the same TCP packet, which is true if the sender is a program, but not necessarily
// if the sender is a person typing (although in that case the line-oriented TTY input seems
// to make it work too). If this becomes a problem we need to buffer the first few chars...
if (conn->conn_mode == cmInit) {
// If the connection starts with the Arduino or ARM reset sequence we perform a RESET
if ((len == 2 && strncmp(data, "0 ", 2) == 0) ||
(len == 2 && strncmp(data, "?\n", 2) == 0) ||
(len == 3 && strncmp(data, "?\r\n", 3) == 0)) {
os_printf("MCU Reset=gpio%d ISP=gpio%d\n", mcu_reset_pin, mcu_isp_pin);
os_delay_us(2*1000L); // time for os_printf to happen
// send reset to arduino/ARM
if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 0);
os_delay_us(100L);
if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 0);
os_delay_us(100L);
if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1);
os_delay_us(100L);
if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1);
os_delay_us(1000L);
conn->conn_mode = cmAVR;
slip_disabled++; // disable SLIP so it doesn't interfere with flashing
// If the connection starts with a telnet negotiation we will do telnet
} else if (len >= 3 && strncmp(data, (char[]){IAC, WILL, ComPortOpt}, 3) == 0) {
conn->conn_mode = cmTelnet;
conn->telnet_state = TN_normal;
// note that the three negotiation chars will be gobbled-up by telnetUnwrap
os_printf("telnet mode\n");
// looks like a plain-vanilla connection!
} else {
conn->conn_mode = cmTransparent;
}
// Process return data on TCP client connections
} else if (conn->conn_mode == cmTcpClient) {
}
// write the buffer to the uart
if (conn->conn_mode == cmTelnet) {
conn->telnet_state = telnetUnwrap((uint8_t *)data, len, conn->telnet_state);
} else {
uart0_tx_buffer(data, len);
}
serledFlash(50); // short blink on serial LED
}
//===== UART -> TCP
@ -203,18 +209,19 @@ 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;
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
@ -222,173 +229,44 @@ static sint8 ICACHE_FLASH_ATTR sendtxbuffer(serbridgeConnData *conn) {
// 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;
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;
}
void ICACHE_FLASH_ATTR
console_process(char *buf, short len) {
// push buffer into web-console
for (short i=0; i<len; i++)
console_write_char(buf[i]);
// push the buffer into each open connection
for (short i=0; i<MAX_CONN; i++) {
if (connData[i].conn && connData[i].conn_mode != cmTcpClient) {
espbuffsend(&connData[i], buf, len);
}
}
}
//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 start (@) seen
TC_cmdChar, // command character seen
TC_cmdLine, // accumulating command
TC_tdchan, // saw data channel character
TC_tdlen1, // saw first data length character
TC_tdata0, // accumulate data, zero-terminated
TC_tdataN, // accumulate data, length-terminated
};
static uint8_t tcState = TC_newline;
static uint8_t tcChan; // channel for current command (index into tcConn)
#define CMD_MAX 256
static char tcCmdBuf[CMD_MAX];
static short tcCmdBufLen = 0;
static char tcCmdChar;
static short tcLen;
// scan a buffer for tcp client commands
static int ICACHE_FLASH_ATTR
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 ~
} else {
break;
}
case TC_start: // saw ~, expect channel number
if (c == '@') {
tcState = TC_cmd;
continue;
} else if (c >= '0' && c <= '9') {
tcChan = c-'0';
tcState = TC_tdchan;
continue;
}
*out++ = '~'; // make up for '~' we skipped
break;
case TC_cmd: // saw control char (@), expect channel char
if (c >= '0' && c <= '9') {
tcChan = c-'0';
tcState = TC_cmdChar;
continue;
} else {
*out++ = '~'; // make up for '~' we skipped
*out++ = '@'; // make up for '@' we skipped
break;
}
case TC_cmdChar: // saw channel number, expect command char
tcCmdChar = c; // save command character
tcCmdBufLen = 0; // empty the command buffer
tcState = TC_cmdLine;
continue;
case TC_cmdLine: // accumulating command in buffer
if (c != '\n') {
if (tcCmdBufLen < CMD_MAX) tcCmdBuf[tcCmdBufLen++] = c;
} else {
tcpClientCommand(tcChan, tcCmdChar, tcCmdBuf);
tcState = TC_newline;
}
continue;
case TC_tdchan: // saw channel number, getting first length char
if (c >= '0' && c <= '9') {
tcLen = c-'0';
} else if (c >= 'A' && c <= 'F') {
tcLen = c-'A'+10;
} else {
*out++ = '~'; // make up for '~' we skipped
*out++ = '0'+tcChan;
break;
}
tcState = TC_tdlen1;
continue;
case TC_tdlen1: // saw first length char, get second
tcLen *= 16;
if (c >= '0' && c <= '9') {
tcLen += c-'0';
} else if (c >= 'A' && c <= 'F') {
tcLen += c-'A'+10;
} else {
*out++ = '~'; // make up for '~' we skipped
*out++ = '0'+tcChan;
break;
}
tcState = tcLen == 0 ? TC_tdata0 : TC_tdataN;
continue;
case TC_tdata0: // saw data length, getting data characters zero-terminated
if (c != 0) {
tcpClientSendChar(tcChan, c);
} else {
tcpClientSendPush(tcChan);
tcState = TC_idle;
}
continue;
case TC_tdataN: // saw data length, getting data characters length-terminated
tcpClientSendChar(tcChan, c);
tcLen--;
if (tcLen == 0) {
tcpClientSendPush(tcChan);
tcState = TC_idle;
}
continue;
}
*out++ = c;
}
//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
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
}
//===== Connect / disconnect
@ -396,101 +274,107 @@ serbridgeUartCb(char *buf, int length) {
// 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 *sbConn = serbridgeFindConnData(arg);
if (sbConn == NULL) return;
// Close the connection
espconn_disconnect(sbConn->conn);
// free connection slot
sbConn->conn = NULL;
serbridgeConnData *sbConn = serbridgeFindConnData(arg);
if (sbConn == NULL) return;
if (sbConn->conn_mode == cmAVR) {
if (slip_disabled > 0) slip_disabled--;
}
// Close the connection
espconn_disconnect(sbConn->conn);
// free connection slot
sbConn->conn = NULL;
}
// Disconnection callback
static void ICACHE_FLASH_ATTR serbridgeDisconCb(void *arg) {
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;
serbridgeConnData *sbConn = serbridgeFindConnData(arg);
if (sbConn == NULL) return;
// send reset to arduino/ARM
if (sbConn->conn_mode == cmAVR) {
if (slip_disabled > 0) slip_disabled--;
if (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
int i;
for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break;
os_printf("Accept port 23, conn=%p, pool slot %d\n", conn, i);
if (i==MAX_CONN) {
os_printf("Aiee, conn pool overflow!\n");
espconn_disconnect(conn);
return;
}
conn->reverse = connData+i;
connData[i].conn = conn;
connData[i].txbufferlen = 0;
connData[i].readytosend = true;
connData[i].telnet_state = 0;
connData[i].conn_mode = cmInit;
espconn_regist_recvcb(conn, serbridgeRecvCb);
espconn_regist_reconcb(conn, serbridgeReconCb);
espconn_regist_disconcb(conn, serbridgeDisconCb);
espconn_regist_sentcb(conn, serbridgeSentCb);
espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY);
struct espconn *conn = arg;
// Find empty conndata in pool
int i;
for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break;
os_printf("Accept port 23, conn=%p, pool slot %d\n", conn, i);
if (i==MAX_CONN) {
os_printf("Aiee, conn pool overflow!\n");
espconn_disconnect(conn);
return;
}
conn->reverse = connData+i;
connData[i].conn = conn;
connData[i].txbufferlen = 0;
connData[i].readytosend = true;
connData[i].telnet_state = 0;
connData[i].conn_mode = cmInit;
espconn_regist_recvcb(conn, serbridgeRecvCb);
espconn_regist_reconcb(conn, serbridgeReconCb);
espconn_regist_disconcb(conn, serbridgeDisconCb);
espconn_regist_sentcb(conn, serbridgeSentCb);
espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY);
}
//===== Initialization
void ICACHE_FLASH_ATTR serbridgeInitPins() {
mcu_reset_pin = flashConfig.reset_pin;
mcu_isp_pin = flashConfig.isp_pin;
os_printf("Serbridge pins: reset=%d isp=%d swap=%d\n",
mcu_reset_pin, mcu_isp_pin, flashConfig.swap_uart);
if (flashConfig.swap_uart) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 4);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 4);
PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTCK_U);
PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U);
system_uart_swap();
} else {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, 0);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, 0);
system_uart_de_swap();
}
// set both pins to 1 before turning them on so we don't cause a reset
if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1);
if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1);
// switch pin mux to make these pins GPIO pins
if (mcu_reset_pin >= 0) makeGpio(mcu_reset_pin);
if (mcu_isp_pin >= 0) makeGpio(mcu_isp_pin);
mcu_reset_pin = flashConfig.reset_pin;
mcu_isp_pin = flashConfig.isp_pin;
os_printf("Serbridge pins: reset=%d isp=%d swap=%d\n",
mcu_reset_pin, mcu_isp_pin, flashConfig.swap_uart);
if (flashConfig.swap_uart) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 4);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 4);
PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTCK_U);
PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U);
system_uart_swap();
} else {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, 0);
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, 0);
system_uart_de_swap();
}
// set both pins to 1 before turning them on so we don't cause a reset
if (mcu_isp_pin >= 0) GPIO_OUTPUT_SET(mcu_isp_pin, 1);
if (mcu_reset_pin >= 0) GPIO_OUTPUT_SET(mcu_reset_pin, 1);
// switch pin mux to make these pins GPIO pins
if (mcu_reset_pin >= 0) makeGpio(mcu_reset_pin);
if (mcu_isp_pin >= 0) makeGpio(mcu_isp_pin);
}
// Start transparent serial bridge TCP server on specified port (typ. 23)
void ICACHE_FLASH_ATTR serbridgeInit(int port) {
serbridgeInitPins();
int i;
for (i = 0; i < MAX_CONN; i++) {
connData[i].conn = NULL;
connData[i].txbuffer = txbuffer[i];
}
serbridgeConn.type = ESPCONN_TCP;
serbridgeConn.state = ESPCONN_NONE;
serbridgeTcp.local_port = port;
serbridgeConn.proto.tcp = &serbridgeTcp;
espconn_regist_connectcb(&serbridgeConn, serbridgeConnectCb);
espconn_accept(&serbridgeConn);
espconn_tcp_set_max_con_allow(&serbridgeConn, MAX_CONN);
espconn_regist_time(&serbridgeConn, SER_BRIDGE_TIMEOUT, 0);
serbridgeInitPins();
int i;
for (i = 0; i < MAX_CONN; i++) {
connData[i].conn = NULL;
connData[i].txbuffer = txbuffer[i];
}
serbridgeConn.type = ESPCONN_TCP;
serbridgeConn.state = ESPCONN_NONE;
serbridgeTcp.local_port = port;
serbridgeConn.proto.tcp = &serbridgeTcp;
espconn_regist_connectcb(&serbridgeConn, serbridgeConnectCb);
espconn_accept(&serbridgeConn);
espconn_tcp_set_max_con_allow(&serbridgeConn, MAX_CONN);
espconn_regist_time(&serbridgeConn, SER_BRIDGE_TIMEOUT, 0);
}

@ -32,7 +32,7 @@ typedef struct serbridgeConnData {
void ICACHE_FLASH_ATTR serbridgeInit(int port);
void ICACHE_FLASH_ATTR serbridgeInitPins(void);
void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, int len);
void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, short len);
void ICACHE_FLASH_ATTR serbridgeReset();
#endif /* __SER_BRIDGE_H__ */

@ -0,0 +1,136 @@
// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt
#include "esp8266.h"
#include "uart.h"
#include "crc16.h"
#include "serbridge.h"
#include "serled.h"
#include "console.h"
#include "cmd.h"
uint8_t slip_disabled; // disable slip to allow flashing of attached MCU
extern void ICACHE_FLASH_ATTR console_process(char *buf, short len);
// This SLIP parser does not conform to RFC 1055 https://tools.ietf.org/html/rfc1055,
// instead, it implements the framing implemented in https://github.com/tuanpmt/esp_bridge
// It accumulates each packet into a static buffer and calls cmd_parse() when the end
// of a packet is reached. It expects cmd_parse() to copy anything it needs from the
// buffer elsewhere as the buffer is immediately reused.
// One special feature is that if the first two characters of a packet are both printable or
// \n or \r then the parser assumes it's dealing with console debug output and calls
// slip_console(c) for each character and does not accumulate chars in the buffer until the
// next SLIP_END marker is seen. This allows random console debug output to come in between
// packets as long as each packet starts *and* ends with SLIP_END (which is an official
// variation on the SLIP protocol).
static bool slip_escaped; // true when prev char received is escape
static bool slip_inpkt; // true when we're after SLIP_START and before SLIP_END
#define SLIP_MAX 1024 // max length of SLIP packet
static char slip_buf[SLIP_MAX]; // buffer for current SLIP packet
static short slip_len; // accumulated length in slip_buf
// SLIP process a packet or a bunch of debug console chars
static void ICACHE_FLASH_ATTR
slip_process() {
if (slip_len < 1) return;
if (!slip_inpkt) {
// debug console stuff
console_process(slip_buf, slip_len);
} else {
// proper SLIP packet, invoke command processor after checking CRC
//os_printf("SLIP: rcv %d\n", slip_len);
if (slip_len > 2) {
uint16_t crc = crc16_data((uint8_t*)slip_buf, slip_len-2, 0);
uint16_t rcv = ((uint16_t)slip_buf[slip_len-2]) | ((uint16_t)slip_buf[slip_len-1] << 8);
if (crc == rcv) {
CMD_parse_packet((uint8_t*)slip_buf, slip_len-2);
} else {
os_printf("SLIP: bad CRC, crc=%x rcv=%x\n", crc, rcv);
for (short i=0; i<slip_len; i++) {
if (slip_buf[i] >= ' ' && slip_buf[i] <= '~')
os_printf("%c", slip_buf[i]);
else
os_printf("\\%02X", slip_buf[i]);
}
os_printf("\n");
}
}
}
}
#if 0
// determine whether a character is printable or not (or \r \n)
static bool ICACHE_FLASH_ATTR
slip_printable(char c) {
return (c >= ' ' && c <= '~') || c == '\n' || c == '\r';
}
#endif
static void ICACHE_FLASH_ATTR
slip_reset() {
slip_inpkt = false;
slip_escaped = false;
slip_len = 0;
}
// SLIP parse a single character
static void ICACHE_FLASH_ATTR
slip_parse_char(char c) {
if (!slip_inpkt) {
if (c == SLIP_START) {
if (slip_len > 0) console_process(slip_buf, slip_len);
slip_reset();
slip_inpkt = true;
return;
}
} else if (slip_escaped) {
// prev char was SLIP_REPL
c = SLIP_ESC(c);
slip_escaped = false;
} else {
switch (c) {
case SLIP_REPL:
slip_escaped = true;
return;
case SLIP_END:
// end of packet, process it and get ready for next one
if (slip_len > 0) slip_process();
slip_reset();
return;
case SLIP_START:
os_printf("SLIP: got SLIP_START while in packet?\n");
slip_reset();
return;
}
}
if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c;
}
// callback with a buffer of characters that have arrived on the uart
void ICACHE_FLASH_ATTR
serbridgeUartCb(char *buf, short length) {
if (slip_disabled > 0) {
//os_printf("SLIP: disabled got %d\n", length);
console_process(buf, length);
for (short i=0; i<length; i++)
if (buf[i] == SLIP_START) {
os_printf("SLIP: START while disabled=%d\n", slip_disabled);
break;
}
return;
}
// do SLIP parsing
for (short i=0; i<length; i++)
slip_parse_char(buf[i]);
// if we're in-between packets (debug console) then print it now
if (!slip_inpkt && length > 0) {
slip_process();
slip_reset();
}
serledFlash(50); // short blink on serial LED
}

@ -4,7 +4,7 @@
#include "uart_hw.h"
// Receive callback function signature
typedef void (*UartRecv_cb)(char *buf, int len);
typedef void (*UartRecv_cb)(char *buf, short len);
// Initialize UARTs to the provided baud rates (115200 recommended). This also makes the os_printf
// calls use uart1 for output (for debugging purposes)

@ -40,6 +40,8 @@ static char *wifiReasons[] = {
static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" };
static char *wifiPhy[] = { 0, "11b", "11g", "11n" };
void (*wifiStatusCb)(uint8_t); // callback when wifi status changes
static char* ICACHE_FLASH_ATTR wifiGetReason(void) {
if (wifiReason <= 24) return wifiReasons[wifiReason];
if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+24];
@ -86,6 +88,7 @@ static void ICACHE_FLASH_ATTR wifiHandleEventCb(System_Event_t *evt) {
default:
break;
}
if (wifiStatusCb) (*wifiStatusCb)(wifiState);
}
// ===== wifi scanning
@ -478,7 +481,7 @@ int ICACHE_FLASH_ATTR printWifiInfo(char *buff) {
char *mode = wifiMode[op];
char *status = "unknown";
int st = wifi_station_get_connect_status();
if (st > 0 && st < sizeof(connStatuses)) status = connStatuses[st];
if (st >= 0 && st < sizeof(connStatuses)) status = connStatuses[st];
int p = wifi_get_phy_mode();
char *phy = wifiPhy[p&3];
char *warn = wifiWarn[op];

@ -14,4 +14,6 @@ int cgiWiFiConnStatus(HttpdConnData *connData);
int cgiWiFiSpecial(HttpdConnData *connData);
void wifiInit(void);
extern void (*wifiStatusCb)(uint8_t); // callback when wifi status changes
#endif

Loading…
Cancel
Save