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. 91
      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 # on the release tag, make release, upload esp-link.tgz into the release files
#VERSION ?= "esp-link custom version" #VERSION ?= "esp-link custom version"
DATE := $(shell date '+%F %T') 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; \ SHA := $(shell if git diff --quiet HEAD; then git rev-parse --short HEAD | cut -d"/" -f 3; \
else echo "development"; fi) else echo "development"; fi)
VERSION ?=esp-link $(BRANCH) - $(DATE) - $(SHA) 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 $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash 0x01000 $(FW_BASE)/user1.bin
flash: all 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 \ 0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \
$(ET_BLANK) $(SDK_BASE)/bin/blank.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)" $(Q) $(MAKE) -C espfs/mkespfsimage GZIP_COMPRESSION="$(GZIP_COMPRESSION)"
release: all 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 \ $(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 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" wiflash release/esp-link-$(BRANCH)
$(Q) tar zcf esp-link.tgz -C release esp-link $(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 $(Q) rm -rf release
clean: clean:

@ -7,16 +7,24 @@ It implements a number of features:
- flash-programming attached Arduino/AVR microcontrollers as well as LPC800-series and other - flash-programming attached Arduino/AVR microcontrollers as well as LPC800-series and other
ARM microcontrollers via Wifi ARM microcontrollers via Wifi
- outbound TCP (and thus HTTP) connections from the attached micro-controller to the internet - 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 The firmware includes a tiny HTTP server based on
[esphttpd](http://www.esp8266.com/viewforum.php?f=34) [esphttpd](http://www.esp8266.com/viewforum.php?f=34)
with a simple web interface, many thanks to Jeroen Domburg for making it available! 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) For quick support and questions:
Note that the [stable V1.0 release](https://github.com/jeelabs/esp-link/releases/tag/v1.0.0) is [![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)
recommended if you do not need the outbound TCP connections and have a 512KB flash chip.
Eye Candy Eye Candy
--------- ---------
@ -31,7 +39,7 @@ attached microcontroller, and the pin assignments card:
Hardware info Hardware info
------------- -------------
This firmware is designed for esp8266 modules which have most ESP I/O pins available and 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: The default connections are:
- URXD: connect to TX of microcontroller - URXD: connect to TX of microcontroller
- UTXD: connect to RX 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 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. The GPIO pin assignments can be changed dynamically in the web UI and are saved in flash.
Initial flashing Initial flashing

@ -55,7 +55,7 @@ typedef enum {
CMD_REST_REQUEST, CMD_REST_REQUEST,
CMD_REST_SETHEADER, CMD_REST_SETHEADER,
CMD_REST_EVENTS, CMD_REST_EVENTS,
CMD_ADD_SENSOR, // 15 CMD_ADD_CALLBACK, // 15
CMD_SENSOR_EVENTS CMD_SENSOR_EVENTS
} CmdName; } CmdName;
@ -71,19 +71,23 @@ typedef struct {
uint32_t callback; uint32_t callback;
} cmdCallback; } cmdCallback;
void ICACHE_FLASH_ATTR CMD_parse_packet(uint8_t *buf, short len); // Used by slip protocol to cause parsing of a received packet
cmdCallback* ICACHE_FLASH_ATTR CMD_GetCbByName(char* name); 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 // Responses
// Start a response, returns the partial CRC // 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 // 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 // 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 // Requests

@ -10,17 +10,19 @@
#include "uart.h" #include "uart.h"
#include "cgiwifi.h" #include "cgiwifi.h"
static uint32_t ICACHE_FLASH_ATTR CMD_Null(CmdPacket *cmd); static uint32_t CMD_Null(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_IsReady(CmdPacket *cmd); static uint32_t CMD_IsReady(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_WifiConnect(CmdPacket *cmd); static uint32_t CMD_Reset(CmdPacket *cmd);
static uint32_t ICACHE_FLASH_ATTR CMD_AddSensor(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; static uint8_t lastWifiStatus = wifiIsDisconnected;
// Command dispatch table for serial -> ESP commands // Command dispatch table for serial -> ESP commands
const CmdList commands[] = { const CmdList commands[] = {
{CMD_NULL, CMD_Null}, {CMD_NULL, CMD_Null},
{CMD_RESET, CMD_Null}, {CMD_RESET, CMD_Reset},
{CMD_IS_READY, CMD_IsReady}, {CMD_IS_READY, CMD_IsReady},
{CMD_WIFI_CONNECT, CMD_WifiConnect}, {CMD_WIFI_CONNECT, CMD_WifiConnect},
@ -37,25 +39,14 @@ const CmdList commands[] = {
{CMD_REST_REQUEST, REST_Request}, {CMD_REST_REQUEST, REST_Request},
{CMD_REST_SETHEADER, REST_SetHeader}, {CMD_REST_SETHEADER, REST_SetHeader},
{CMD_ADD_SENSOR, CMD_AddSensor }, {CMD_ADD_CALLBACK, CMD_AddCallback },
{CMD_NULL, NULL} {CMD_NULL, NULL}
}; };
// WifiCb plus 10 for sensors // WifiCb plus 10 for sensors
cmdCallback callbacks[12] = { #define MAX_CALLBACKS 12
{ { '\0' }, -1 }, cmdCallback callbacks[MAX_CALLBACKS]; // cleared in CMD_Reset
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 },
{ { '\0' }, -1 }
};
// Command handler for IsReady (healthcheck) command // Command handler for IsReady (healthcheck) command
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
@ -71,20 +62,31 @@ CMD_Null(CmdPacket *cmd) {
return 1; 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) { CMD_AddCb(char* name, uint32_t cb) {
char checkname[16]; for (uint8_t i = 0; i < MAX_CALLBACKS; i++) {
os_strncpy(checkname, name, sizeof(checkname)); //os_printf("CMD_AddCb: index %d name=%s cb=%p\n", i, callbacks[i].name,
for (uint8_t i = 0; i < sizeof(commands); i++) { // (void *)callbacks[i].callback);
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 // find existing callback or add to the end
if (os_strcmp(callbacks[i].name, checkname) == 0 || callbacks[i].name[0] == '\0') { if (os_strcmp(callbacks[i].name, name) == 0 || callbacks[i].name[0] == '\0') {
os_strncpy(callbacks[i].name, checkname, sizeof(checkname)); os_strncpy(callbacks[i].name, name, sizeof(callbacks[i].name));
callbacks[i].callback = cb; callbacks[i].callback = cb;
os_printf("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i); os_printf("CMD_AddCb: cb %s added at index %d\n", callbacks[i].name, i);
break; return 1;
} }
} }
return 0;
} }
cmdCallback* ICACHE_FLASH_ATTR cmdCallback* ICACHE_FLASH_ATTR
@ -92,7 +94,8 @@ CMD_GetCbByName(char* name) {
char checkname[16]; char checkname[16];
os_strncpy(checkname, name, sizeof(checkname)); os_strncpy(checkname, name, sizeof(checkname));
for (uint8_t i = 0; i < sizeof(commands); i++) { 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 callback doesn't exist or it's null
if (os_strcmp(callbacks[i].name, checkname) == 0) { if (os_strcmp(callbacks[i].name, checkname) == 0) {
os_printf("CMD_GetCbByName: cb %s found at index %d\n", callbacks[i].name, i); 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 wifiStatusCb = CMD_WifiCb; // register our callback with wifi subsystem
CMD_AddCb("wifiCb", (uint32_t)cmd->callback); // save the MCU's callback 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); CMD_WifiCb(wifiState);
return 1; 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 static uint32_t ICACHE_FLASH_ATTR
CMD_AddSensor(CmdPacket *cmd) { CMD_AddCallback(CmdPacket *cmd) {
CmdRequest req; CmdRequest req;
CMD_Request(&req, cmd); 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) if (cmd->argc != 1 || cmd->callback == 0)
return 0; return 0;
uint8_t* name; char name[16];
uint16_t len; uint16_t len;
// get the sensor name // get the sensor name
len = CMD_ArgLen(&req); 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 if (len > 15) return 0; // max size of name is 15 characters
name = (uint8_t*)os_zalloc(len + 1); if (CMD_PopArg(&req, (uint8_t *)name, len)) return 0;
if (CMD_PopArg(&req, name, len)) return 0;
name[len] = 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 CMD_AddCb(name, (uint32_t)cmd->callback); // save the sensor callback
return 1;
} }

@ -27,59 +27,66 @@ tcpclient_discon_cb(void *arg) {
client->data = 0; 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 static void ICACHE_FLASH_ATTR
tcpclient_recv(void *arg, char *pdata, unsigned short len) { 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; struct espconn *pCon = (struct espconn*)arg;
RestClient *client = (RestClient *)pCon->reverse; RestClient *client = (RestClient *)pCon->reverse;
for(j=0 ;j<len; j++){ // parse status line
char c = pdata[j]; int pi = 0;
int32_t code = -1;
if(c == ' ' && !inStatus){ char statusCode[4] = "\0\0\0\0";
inStatus = 1; 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++;
}
pi++;
}
// 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;
} }
if(inStatus && i < 3 && c != ' '){ currentLineIsBlank = true;
statusCode[i] = c; } else if (pdata[pi] != '\r') {
i++; currentLineIsBlank = false;
} }
if(i == 3){ pi++;
statusCode[i] = '\0';
code = atoi(statusCode);
} }
//if (pi < len && pdata[pi] == '\r') pi++; // hacky!
if(httpBody){ // collect body and send it
//only write response if its not null uint16_t crc;
uint32_t body_len = len - j; int body_len = len-pi;
os_printf("REST: status=%ld, body=%ld\n", code, body_len); os_printf("REST: status=%ld, body=%d\n", code, body_len);
if(body_len == 0){ if (pi == len) {
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0); crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 0);
} else { } else {
crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1); crc = CMD_ResponseStart(CMD_REST_EVENTS, client->resp_cb, code, 1);
crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+j), body_len); crc = CMD_ResponseBody(crc, (uint8_t*)(pdata+pi), body_len);
}
CMD_ResponseEnd(crc); CMD_ResponseEnd(crc);
break; os_printf("REST: body=");
} else { for (int j=pi; j<len; j++) os_printf(" %02x", pdata[j]);
if (c == '\n' && currentLineIsBlank) { os_printf("\n");
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) //if(client->security)
// espconn_secure_disconnect(client->pCon); // espconn_secure_disconnect(client->pCon);
//else //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 // 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 // header (including some extra counted "%s" and then we add the body length. We allocate the
// whole shebang and copy everything into it. // 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" "Host: %s\r\n"
"%s" "%s"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
@ -360,11 +368,12 @@ REST_Request(CmdPacket *cmd) {
CMD_PopArg(&req, client->data + client->data_len, realLen); CMD_PopArg(&req, client->data + client->data_len, realLen);
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; client->pCon->state = ESPCONN_NONE;
espconn_regist_connectcb(client->pCon, tcpclient_connect_cb); espconn_regist_connectcb(client->pCon, tcpclient_connect_cb);
espconn_regist_reconcb(client->pCon, tcpclient_recon_cb); espconn_regist_reconcb(client->pCon, tcpclient_recon_cb);
os_printf("\n");
if(UTILS_StrToIP((char *)client->host, &client->pCon->proto.tcp->remote_ip)) { 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); 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 static void ICACHE_FLASH_ATTR
slip_reset() { slip_reset() {
//os_printf("SLIP: reset\n");
slip_inpkt = false; slip_inpkt = false;
slip_escaped = false; slip_escaped = false;
slip_len = 0; slip_len = 0;
@ -83,6 +84,7 @@ slip_parse_char(char c) {
if (slip_len > 0) console_process(slip_buf, slip_len); if (slip_len > 0) console_process(slip_buf, slip_len);
slip_reset(); slip_reset();
slip_inpkt = true; slip_inpkt = true;
os_printf("SLIP: start\n");
return; return;
} }
} else if (slip_escaped) { } else if (slip_escaped) {
@ -101,6 +103,9 @@ slip_parse_char(char c) {
return; return;
case SLIP_START: case SLIP_START:
os_printf("SLIP: got SLIP_START while in packet?\n"); 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(); slip_reset();
return; return;
} }

Loading…
Cancel
Save