change SLIP to std escape chars; add to readme

pull/95/head
Thorsten von Eicken 9 years ago
parent 762d7eaf46
commit 5dbebcd841
  1. 49
      README.md
  2. 12
      cmd/cmd.c
  3. 10
      cmd/cmd.h
  4. 2
      cmd/handlers.c
  5. 12
      html/home.html
  6. 2
      html/mqtt.html
  7. 85
      serial/slip.c

@ -29,6 +29,26 @@ additional contributions!
For quick support and questions: 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) [![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)
Esp-link goals
--------------
The goal of the esp-link project is to create an advanced Wifi co-processor. Esp-link assumes that
there is a "main processor" (also referred to as "attached uController") and that esp-link's role
is to facilitate communication over Wifi. Where esp-link is a bit unusual is that it's not really
just a Wifi interface or a slave co-processor. In some sense it's the master, because the main
processor can be reset, controlled and reprogrammed through esp-link. The three main areas of
functionality in esp-link are:
- reprogramming and debugging the attached uC
- letting the attached uC make outbound communication and offloading the protocol processing
- forwarding inbound communication and offloading the protocol processing (this part is the
least developed)
The goal of the project is also to remain focused on the above mission. In particular, esp-link
is not a platform for stand-alone applications and it does not support connecting sensors or
actuators directly to it. A few users have taken esp-link as a starting point for doing these
things and that's great, but there's also value in keeping the mainline esp-link project
focused on a clear mission.
Esp-link uses Esp-link uses
------------- -------------
The simplest use of esp-link is as a transparent serial to wifi bridge. You can flash an attached The simplest use of esp-link is as a transparent serial to wifi bridge. You can flash an attached
@ -347,6 +367,35 @@ The efficiency is not 100% because there is protocol overhead (such as sync, rec
length characters) length characters)
and there is dead time waiting for an ack or preparing the next record to be sent. and there is dead time waiting for an ack or preparing the next record to be sent.
#### Details of built-in AVR flash algorithm
The built-in flashing algorithm differs a bit from what avrdude does. The programming protocol
states that STK_GET_SYNC+CRC_EOP (0x30 0x20) should be sent to synchronize, but that works poorly
because the AVR's UART only buffers one character. This means that if STK_GET_SYNC+CRC_EOP is
sent twice there is a high chance that only the last character (CRC_EOP) is actually
received. If that is followed by another STK_GET_SYNC+CRC_EOP sequence then optiboot receives
CRC_EOP+STK_GET_SYNC+CRC_EOP which causes it to abort and run the old sketch. Ending up in that
situation is quite likely because optiboot initializes the UART as one of the first things, but
then goes off an flashes an LED for ~300ms during which it doesn't empty the UART.
Looking at the optiboot code, the good news is that CRC_EOP+CRC_EOP can be used to get an initial
response without the overrun danger of the normal sync sequence and this is what esp-link does.
The programming sequence runs as follows:
- esp-link sends a brief reset pulse (1ms)
- esp-link sends CRC_EOP+CRC_EOP ~50ms later
- esp-link sends CRC_EOP+CRC_EOP every ~70-80ms
- eventually optiboot responds with STK_INSYNC+STK_OK (0x14;0x10)
- esp-link sends one CRC_EOP to sort out the even/odd issue
- either optiboot responds with STK_INSYNC+STK_OK or nothing happens for 70-80ms, in which case
esp-link sends another CRC_EOP
- esp-link sends STK_GET_SYNC+CRC_EOP and optiboot responds with STK_INSYNC+STK_OK and we're in
sync now
- esp-link sends the next command (starts with 'u') and programming starts...
If no sync is achieved, esp-link changes baud rate and the whole thing starts over with a reset
pulse about 600ms, esp-link gives up after about 5 seconds and reports an error.
### Flashing an attached ARM processor ### Flashing an attached ARM processor
You can reprogram NXP's LPC800-series and many other ARM processors as well by pointing your You can reprogram NXP's LPC800-series and many other ARM processors as well by pointing your

@ -27,11 +27,13 @@ extern const CmdList commands[];
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
CMD_ProtoWrite(uint8_t data) { CMD_ProtoWrite(uint8_t data) {
switch(data){ switch(data){
case SLIP_START:
case SLIP_END: case SLIP_END:
case SLIP_REPL: uart0_write_char(SLIP_ESC);
uart0_write_char(SLIP_REPL); uart0_write_char(SLIP_ESC_END);
uart0_write_char(SLIP_ESC(data)); break;
case SLIP_ESC:
uart0_write_char(SLIP_ESC);
uart0_write_char(SLIP_ESC_ESC);
break; break;
default: default:
uart0_write_char(data); uart0_write_char(data);
@ -48,7 +50,7 @@ uint16_t ICACHE_FLASH_ATTR
CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc) { CMD_ResponseStart(uint16_t cmd, uint32_t callback, uint32_t _return, uint16_t argc) {
uint16_t crc = 0; uint16_t crc = 0;
uart0_write_char(SLIP_START); uart0_write_char(SLIP_END);
CMD_ProtoWriteBuf((uint8_t*)&cmd, 2); CMD_ProtoWriteBuf((uint8_t*)&cmd, 2);
crc = crc16_data((uint8_t*)&cmd, 2, crc); crc = crc16_data((uint8_t*)&cmd, 2, crc);
CMD_ProtoWriteBuf((uint8_t*)&callback, 4); CMD_ProtoWriteBuf((uint8_t*)&callback, 4);

@ -6,19 +6,11 @@
#define CMD_H #define CMD_H
#include <esp8266.h> #include <esp8266.h>
// Escape chars used by tuanpmt, dunno why he didn't use std ones... // Standard SLIP escape chars from RFC
#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_END 0300 // indicates end of packet
#define SLIP_ESC 0333 // indicates byte stuffing #define SLIP_ESC 0333 // indicates byte stuffing
#define SLIP_ESC_END 0334 // ESC ESC_END means END data byte #define SLIP_ESC_END 0334 // ESC ESC_END means END data byte
#define SLIP_ESC_ESC 0335 // ESC ESC_ESC means ESC data byte #define SLIP_ESC_ESC 0335 // ESC ESC_ESC means ESC data byte
#endif
typedef struct __attribute__((__packed__)) { typedef struct __attribute__((__packed__)) {
uint16_t len; // length of data uint16_t len; // length of data

@ -68,7 +68,7 @@ CMD_Null(CmdPacket *cmd) {
} }
// Command handler for Reset command, this was originally to reset the ESP but we don't want to // 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 // do that in esp-link. It is still good to clear any information the ESP has about the attached
// uC. // uC.
static uint32_t ICACHE_FLASH_ATTR static uint32_t ICACHE_FLASH_ATTR
CMD_Reset(CmdPacket *cmd) { CMD_Reset(CmdPacket *cmd) {

@ -22,8 +22,8 @@
</div> </div>
</td></tr> </td></tr>
<tr><td>Network SSID</td><td id="wifi-ssid"></td></tr> <tr><td>Network SSID</td><td id="wifi-ssid"></td></tr>
<tr><td>WiFI status</td><td id="wifi-status"></td></tr> <tr><td>WiFi status</td><td id="wifi-status"></td></tr>
<tr><td>WiFI address</td><td id="wifi-ip"></td></tr> <tr><td>WiFi address</td><td id="wifi-ip"></td></tr>
<tr><td>SLIP status</td><td class="system-slip"></td></tr> <tr><td>SLIP status</td><td class="system-slip"></td></tr>
<tr><td>MQTT status</td><td class="system-mqtt"></td></tr> <tr><td>MQTT status</td><td class="system-mqtt"></td></tr>
<tr><td>Serial baud</td><td class="system-baud"></td></tr> <tr><td>Serial baud</td><td class="system-baud"></td></tr>
@ -32,7 +32,7 @@
<div class="card"> <div class="card">
<h1>Info</h1> <h1>Info</h1>
<p style="margin-bottom:0;">The JeeLabs esp-link firmware bridges the ESP8266 <p style="margin-bottom:0;">The JeeLabs esp-link firmware bridges the ESP8266
serial port to WiFI and can serial port to WiFi and can
program microcontrollers over the serial port, in particular Arduinos, AVRs, and program microcontrollers over the serial port, in particular Arduinos, AVRs, and
NXP's LPC800 and other ARM processors. Typical avrdude command line to NXP's LPC800 and other ARM processors. Typical avrdude command line to
program an Arduino:</p> program an Arduino:</p>
@ -79,7 +79,7 @@
<div class="pure-control-group"> <div class="pure-control-group">
<label for="pin-conn">Conn LED</label> <label for="pin-conn">Conn LED</label>
<select id="pin-conn"></select> <select id="pin-conn"></select>
<div class="popup">LED to show wifi connectivity</div> <div class="popup">LED to show WiFi connectivity</div>
</div> </div>
<div class="pure-control-group"> <div class="pure-control-group">
<label for="pin-ser">Serial LED</label> <label for="pin-ser">Serial LED</label>
@ -109,8 +109,8 @@
<h1>System details</h1> <h1>System details</h1>
<div id="system-spinner" class="spinner spinner-small"></div> <div id="system-spinner" class="spinner spinner-small"></div>
<table id="system-table" class="pure-table pure-table-horizontal" hidden><tbody> <table id="system-table" class="pure-table pure-table-horizontal" hidden><tbody>
<tr><td>WiFI mode</td><td id="wifi-mode"></td></tr> <tr><td>WiFi mode</td><td id="wifi-mode"></td></tr>
<tr><td>WiFI channel</td><td id="wifi-chan"></td></tr> <tr><td>WiFi channel</td><td id="wifi-chan"></td></tr>
<tr><td>Flash chip ID</td><td> <tr><td>Flash chip ID</td><td>
<div> <div>
<span class="system-id"></span> <span class="system-id"></span>

@ -14,7 +14,7 @@
using parameters set below and stored in esp-link's flash settings. This allows using parameters set below and stored in esp-link's flash settings. This allows
esp-link to take care of connection parameters and disconnect/reconnect operations.</p> esp-link to take care of connection parameters and disconnect/reconnect operations.</p>
<p>The MQTT client also supports sending periodic status messages about esp-link itself, <p>The MQTT client also supports sending periodic status messages about esp-link itself,
including WiFI RSSI, and free heap memory.</p> including WiFi RSSI, and free heap memory.</p>
<div class="form-horizontal"> <div class="form-horizontal">
<input type="checkbox" name="slip-enable"/> <input type="checkbox" name="slip-enable"/>
<label>Enable SLIP on serial port</label> <label>Enable SLIP on serial port</label>

@ -17,8 +17,7 @@ uint8_t slip_disabled; // temporarily disable slip to allow flashing of attach
extern void ICACHE_FLASH_ATTR console_process(char *buf, short len); 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, // This SLIP parser tries to 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 // 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 // of a packet is reached. It expects cmd_parse() to copy anything it needs from the
// buffer elsewhere as the buffer is immediately reused. // buffer elsewhere as the buffer is immediately reused.
@ -38,48 +37,39 @@ static short slip_len; // accumulated length in slip_buf
// SLIP process a packet or a bunch of debug console chars // SLIP process a packet or a bunch of debug console chars
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
slip_process() { slip_process() {
if (slip_len < 1) return; if (slip_len > 2) {
if (!slip_inpkt) {
// debug console stuff
console_process(slip_buf, slip_len);
} else {
// proper SLIP packet, invoke command processor after checking CRC // proper SLIP packet, invoke command processor after checking CRC
//os_printf("SLIP: rcv %d\n", slip_len); //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 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);
uint16_t rcv = ((uint16_t)slip_buf[slip_len-2]) | ((uint16_t)slip_buf[slip_len-1] << 8); if (crc == rcv) {
if (crc == rcv) { CMD_parse_packet((uint8_t*)slip_buf, slip_len-2);
CMD_parse_packet((uint8_t*)slip_buf, slip_len-2); } else {
} else { os_printf("SLIP: bad CRC, crc=%x rcv=%x\n", crc, rcv);
os_printf("SLIP: bad CRC, crc=%x rcv=%x\n", crc, rcv);
for (short i=0; i<slip_len; i++) { for (short i=0; i<slip_len; i++) {
if (slip_buf[i] >= ' ' && slip_buf[i] <= '~') { if (slip_buf[i] >= ' ' && slip_buf[i] <= '~') {
DBG("%c", slip_buf[i]); DBG("%c", slip_buf[i]);
} }
else { else {
DBG("\\%02X", slip_buf[i]); DBG("\\%02X", slip_buf[i]);
}
} }
DBG("\n");
} }
DBG("\n");
} }
} }
} }
#if 0
// determine whether a character is printable or not (or \r \n) // determine whether a character is printable or not (or \r \n)
static bool ICACHE_FLASH_ATTR static bool ICACHE_FLASH_ATTR
slip_printable(char c) { slip_printable(char c) {
return (c >= ' ' && c <= '~') || c == '\n' || c == '\r'; return (c >= ' ' && c <= '~') || c == '\n' || c == '\r';
} }
#endif
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
slip_reset() { slip_reset() {
//os_printf("SLIP: reset\n"); //os_printf("SLIP: reset\n");
slip_inpkt = false; slip_inpkt = true;
slip_escaped = false; slip_escaped = false;
slip_len = 0; slip_len = 0;
} }
@ -87,38 +77,29 @@ slip_reset() {
// SLIP parse a single character // SLIP parse a single character
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
slip_parse_char(char c) { slip_parse_char(char c) {
if (!slip_inpkt) { if (c == SLIP_END) {
if (c == SLIP_START) { // either start or end of packet, process whatever we may have accumulated
if (slip_len > 0) console_process(slip_buf, slip_len); if (slip_len > 0) {
slip_reset(); if (slip_inpkt) slip_process(); else console_process(slip_buf, slip_len);
slip_inpkt = true;
DBG("SLIP: start\n");
return;
} }
slip_reset();
//slip_inpkt = true;
DBG("SLIP: start or end\n");
} else if (slip_escaped) { } else if (slip_escaped) {
// prev char was SLIP_REPL // prev char was SLIP_ESC
c = SLIP_ESC(c); if (c == SLIP_ESC_END) c = SLIP_END;
if (c == SLIP_ESC_ESC) c = SLIP_ESC;
if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c;
slip_escaped = false; slip_escaped = false;
} else if (slip_inpkt && c == SLIP_ESC) {
slip_escaped = true;
} else { } else {
switch (c) { if (slip_len == 0 && slip_printable(c)) {
case SLIP_REPL: // start of packet and it's a printable character, we're gonna assume that this is console text
slip_escaped = true; slip_inpkt = false;
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");
//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;
} }
if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c;
} }
if (slip_len < SLIP_MAX) slip_buf[slip_len++] = c;
} }
// callback with a buffer of characters that have arrived on the uart // callback with a buffer of characters that have arrived on the uart

Loading…
Cancel
Save