support telnet protocol for microcontroller reset; wifi & console tweaks

pull/7/head
Thorsten von Eicken 10 years ago
parent 504f994214
commit 79495f6b42
  1. 124
      serial/serbridge.c
  2. 3
      serial/serbridge.h
  3. 10
      serial/uart.c
  4. 1
      serial/uart.h
  5. 28
      user/cgi.c
  6. 1
      user/cgi.h
  7. 41
      user/cgiwifi.c
  8. 26
      user/console.c
  9. 1
      user/console.h

@ -89,6 +89,96 @@ static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) {
sendtxbuffer(conn); // send possible new data in txbuffer sendtxbuffer(conn); // send possible new data in txbuffer
} }
// Telnet protocol characters
#define IAC 255 // escape
#define WILL 251 // negotiation
#define SB 250 // subnegotiation begin
#define SE 240 // subnegotiation end
#define ComPortOpt 44 // COM port options
#define SetControl 5 // Set control lines
#define DTR_ON 8 // used here to reset microcontroller
#define DTR_OFF 9
#define RTS_ON 11 // used here to signal ISP (in-system-programming) to uC
#define RTS_OFF 12
// telnet state machine states
enum { TN_normal, TN_iac, TN_will, TN_start, TN_end, TN_comPort, TN_setControl };
// process a buffer-full on a telnet connection and return the ending telnet state
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:
os_printf("MCU reset\n");
GPIO_OUTPUT_SET(MCU_RESET, 0);
os_delay_us(100L);
break;
case DTR_OFF:
GPIO_OUTPUT_SET(MCU_RESET, 1);
os_delay_us(100L);
break;
case RTS_ON:
os_printf("MCU ISP\n");
GPIO_OUTPUT_SET(MCU_ISP, 0);
os_delay_us(100L);
break;
case RTS_OFF:
GPIO_OUTPUT_SET(MCU_ISP, 1);
os_delay_us(100L);
break;
}
state = TN_end;
break;
}
}
return state;
}
// Receive callback // Receive callback
static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) { static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) {
serbridgeConnData *conn = serbridgeFindConnData(arg); serbridgeConnData *conn = serbridgeFindConnData(arg);
@ -108,25 +198,38 @@ static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned sh
(len == 2 && strncmp(data, "?\n", 2) == 0) || (len == 2 && strncmp(data, "?\n", 2) == 0) ||
(len == 3 && strncmp(data, "?\r\n", 3) == 0)) { (len == 3 && strncmp(data, "?\r\n", 3) == 0)) {
os_printf("MCU Reset=%d ISP=%d\n", MCU_RESET, MCU_ISP); os_printf("MCU Reset=%d ISP=%d\n", MCU_RESET, MCU_ISP);
os_delay_us(2*1000L); // time for os_printf to happen
// send reset to arduino/ARM // send reset to arduino/ARM
GPIO_OUTPUT_SET(MCU_RESET, 0); GPIO_OUTPUT_SET(MCU_RESET, 0);
os_delay_us(100L); os_delay_us(100L);
GPIO_OUTPUT_SET(MCU_ISP, 0); GPIO_OUTPUT_SET(MCU_ISP, 0);
os_delay_us(1000L); os_delay_us(100L);
GPIO_OUTPUT_SET(MCU_RESET, 1); GPIO_OUTPUT_SET(MCU_RESET, 1);
os_delay_us(100L); os_delay_us(100L);
GPIO_OUTPUT_SET(MCU_ISP, 1); GPIO_OUTPUT_SET(MCU_ISP, 1);
os_delay_us(1000L); os_delay_us(1000L);
//uart0_tx_buffer(data, len);
//conn->skip_chars = 2;
conn->conn_mode = cmAVR; conn->conn_mode = cmAVR;
//return;
// 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 { } else {
conn->conn_mode = cmTransparent; conn->conn_mode = cmTransparent;
} }
} }
uart0_tx_buffer(data, len); // 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);
}
} }
// Error callback (it's really poorly named, it's not a "connection reconnected" callback, // Error callback (it's really poorly named, it's not a "connection reconnected" callback,
@ -175,7 +278,7 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) {
connData[i].conn=conn; connData[i].conn=conn;
connData[i].txbufferlen = 0; connData[i].txbufferlen = 0;
connData[i].readytosend = true; connData[i].readytosend = true;
connData[i].skip_chars = 0; connData[i].telnet_state = 0;
connData[i].conn_mode = cmInit; connData[i].conn_mode = cmInit;
espconn_regist_recvcb(conn, serbridgeRecvCb); espconn_regist_recvcb(conn, serbridgeRecvCb);
@ -192,14 +295,7 @@ serbridgeUartCb(char *buf, int length) {
for (int i = 0; i < MAX_CONN; ++i) { for (int i = 0; i < MAX_CONN; ++i) {
if (connData[i].conn) { if (connData[i].conn) {
s++; s++;
if (connData[i].skip_chars == 0) { espbuffsend(&connData[i], buf, length);
espbuffsend(&connData[i], buf, length);
} else if (connData[i].skip_chars >= length) {
connData[i].skip_chars -= length;
} else { // connData[i].skip_chars < length
espbuffsend(&connData[i], buf+connData[i].skip_chars, length-connData[i].skip_chars);
connData[i].skip_chars = 0;
}
} }
} }
} }

@ -20,6 +20,7 @@ enum connModes {
cmARM, // ARM (LPC8xx) programming cmARM, // ARM (LPC8xx) programming
cmEcho, // simply echo characters (used for debugging latency) cmEcho, // simply echo characters (used for debugging latency)
cmCommand, // AT command mode cmCommand, // AT command mode
cmTelnet, // use telnet escape sequences for programming mode
}; };
struct serbridgeConnData { struct serbridgeConnData {
@ -28,7 +29,7 @@ struct serbridgeConnData {
char *txbuffer; // buffer for the data to send char *txbuffer; // buffer for the data to send
uint16 txbufferlen; // length of data in txbuffer uint16 txbufferlen; // length of data in txbuffer
bool readytosend; // true, if txbuffer can send by espconn_sent bool readytosend; // true, if txbuffer can send by espconn_sent
uint8 skip_chars; // number of chars to skip from uart, used in Arduino reset sequence uint8_t telnet_state;
}; };
void ICACHE_FLASH_ATTR serbridgeInit(int port); void ICACHE_FLASH_ATTR serbridgeInit(int port);

@ -104,7 +104,7 @@ uart_config(uint8 uart_no)
* Parameters : uint8 TxChar - character to tx * Parameters : uint8 TxChar - character to tx
* Returns : OK * Returns : OK
*******************************************************************************/ *******************************************************************************/
static STATUS STATUS
uart_tx_one_char(uint8 uart, uint8 c) uart_tx_one_char(uint8 uart, uint8 c)
{ {
//Wait until there is room in the FIFO //Wait until there is room in the FIFO
@ -124,13 +124,13 @@ uart_tx_one_char(uint8 uart, uint8 c)
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
uart1_write_char(char c) uart1_write_char(char c)
{ {
if (c == '\n') uart_tx_one_char(UART1, '\r'); //if (c == '\n') uart_tx_one_char(UART1, '\r');
uart_tx_one_char(UART1, c); uart_tx_one_char(UART1, c);
} }
void ICACHE_FLASH_ATTR void ICACHE_FLASH_ATTR
uart0_write_char(char c) uart0_write_char(char c)
{ {
if (c == '\n') uart_tx_one_char(UART0, '\r'); //if (c == '\n') uart_tx_one_char(UART0, '\r');
uart_tx_one_char(UART0, c); uart_tx_one_char(UART0, c);
} }
/****************************************************************************** /******************************************************************************
@ -184,6 +184,10 @@ uart0_rx_intr_handler(void *para)
if(UART_FRM_ERR_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_FRM_ERR_INT_ST)) if(UART_FRM_ERR_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_FRM_ERR_INT_ST))
{ {
os_printf("FRM_ERR\r\n"); os_printf("FRM_ERR\r\n");
//clear rx and tx fifo
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST);
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST);
// reset interrupt
WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR); WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR);
} }

@ -14,6 +14,7 @@ void ICACHE_FLASH_ATTR uart_init(UartBautRate uart0_br, UartBautRate uart1_br);
void ICACHE_FLASH_ATTR uart0_tx_buffer(char *buf, uint16 len); void ICACHE_FLASH_ATTR uart0_tx_buffer(char *buf, uint16 len);
void ICACHE_FLASH_ATTR uart0_write_char(char c); void ICACHE_FLASH_ATTR uart0_write_char(char c);
STATUS uart_tx_one_char(uint8 uart, uint8 c);
// Add a receive callback function, this is called on the uart receive task each time a chunk // Add a receive callback function, this is called on the uart receive task each time a chunk
// of bytes are received. A small number of callbacks can be added and they are all called // of bytes are received. A small number of callbacks can be added and they are all called

@ -69,7 +69,9 @@ int ICACHE_FLASH_ATTR tplCounter(HttpdConnData *connData, char *token, void **ar
char buff[256]; char buff[256];
if (token==NULL) return HTTPD_CGI_DONE; if (token==NULL) return HTTPD_CGI_DONE;
if (os_strcmp(token, "topnav")==0) { if (printSysInfo(buff, token) > 0) {
// awesome...
} else if (os_strcmp(token, "topnav")==0) {
printNav(buff); printNav(buff);
} else if (os_strcmp(token, "counter")==0) { } else if (os_strcmp(token, "counter")==0) {
hitCounter++; hitCounter++;
@ -96,3 +98,27 @@ int ICACHE_FLASH_ATTR printNav(char *buff) {
//os_printf("nav: %s\n", buff); //os_printf("nav: %s\n", buff);
return len; return len;
} }
#define TOKEN(x) (os_strcmp(token, x) == 0)
// Handle system information variables and print their value, returns the number of
// characters appended to buff
int ICACHE_FLASH_ATTR printSysInfo(char *buff, char *token) {
if (TOKEN("si_chip_id")) {
return os_sprintf(buff, "0x%x", system_get_chip_id());
} else if (TOKEN("si_freeheap")) {
return os_sprintf(buff, "%dKB", system_get_free_heap_size()/1024);
} else if (TOKEN("si_uptime")) {
uint32 t = system_get_time() / 1000000; // in seconds
return os_sprintf(buff, "%dd%dh%dm%ds", t/(24*3600), (t/(3600))%24, (t/60)%60, t%60);
} else if (TOKEN("si_boot_version")) {
return os_sprintf(buff, "%d", system_get_boot_version());
} else if (TOKEN("si_boot_address")) {
return os_sprintf(buff, "0x%x", system_get_userbin_addr());
} else if (TOKEN("si_cpu_freq")) {
return os_sprintf(buff, "%dMhz", system_get_cpu_freq());
} else {
return 0;
}
}

@ -7,5 +7,6 @@ int cgiLed(HttpdConnData *connData);
int tplLed(HttpdConnData *connData, char *token, void **arg); int tplLed(HttpdConnData *connData, char *token, void **arg);
int tplCounter(HttpdConnData *connData, char *token, void **arg); int tplCounter(HttpdConnData *connData, char *token, void **arg);
int printNav(char *buff); int printNav(char *buff);
int ICACHE_FLASH_ATTR printSysInfo(char *buff, char *token);
#endif #endif

@ -16,6 +16,7 @@ Cgi/template routines for the /wifi url.
#include "cgiwifi.h" #include "cgiwifi.h"
#include "cgi.h" #include "cgi.h"
#include "status.h" #include "status.h"
#include "console.h"
//Enable this to disallow any changes in AP settings //Enable this to disallow any changes in AP settings
//#define DEMO_MODE //#define DEMO_MODE
@ -33,6 +34,8 @@ static char *wifiReasons[] = {
"unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected", "unsupp_rsn_ie_version", "invalid_rsn_ie_cap", "802_1x_auth_failed", "cipher_suite_rejected",
"beacon_timeout", "no_ap_found" }; "beacon_timeout", "no_ap_found" };
static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" };
static char* ICACHE_FLASH_ATTR wifiGetReason(void) { static char* ICACHE_FLASH_ATTR wifiGetReason(void) {
if (wifiReason <= 24) return wifiReasons[wifiReason]; if (wifiReason <= 24) return wifiReasons[wifiReason];
if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+25]; if (wifiReason >= 200 && wifiReason <= 201) return wifiReasons[wifiReason-200+25];
@ -226,8 +229,8 @@ static ETSTimer resetTimer;
//the connect succeeds, this gets the module in STA-only mode. //the connect succeeds, this gets the module in STA-only mode.
static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) {
int x = wifi_station_get_connect_status(); int x = wifi_station_get_connect_status();
int m = wifi_get_opmode(); int m = wifi_get_opmode() & 0x3;
os_printf("Wifi check: mode=%d status=%d\n", m, x); os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x);
if (x == STATION_GOT_IP) { if (x == STATION_GOT_IP) {
if (m != 1) { if (m != 1) {
@ -236,9 +239,17 @@ static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) {
wifi_set_opmode(1); wifi_set_opmode(1);
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
} }
} else if (m != 3) { os_printf("Turning off uart console\n");
os_printf("Wifi connect failed. Going into STA+AP mode..\n"); os_delay_us(4*1000L); // time for uart to flush
wifi_set_opmode(3); console_uart(false);
// no more resetTimer at this point, gotta use physical reset to recover if in trouble
} else {
if (m != 3) {
os_printf("Wifi connect failed. Going into STA+AP mode..\n");
wifi_set_opmode(3);
}
console_uart(true);
os_printf("Enabling/continuing uart console\n");
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
} }
} }
@ -369,10 +380,8 @@ int ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg)
os_strcpy(buff, "Unknown"); os_strcpy(buff, "Unknown");
if (os_strcmp(token, "WiFiMode")==0) { if (os_strcmp(token, "WiFiMode")==0) {
x=wifi_get_opmode(); x = wifi_get_opmode() & 0x3;
if (x==1) os_strcpy(buff, "Client"); os_strcpy(buff, wifiMode[x]);
if (x==2) os_strcpy(buff, "SoftAP");
if (x==3) os_strcpy(buff, "STA+AP");
} else if (os_strcmp(token, "currSsid")==0) { } else if (os_strcmp(token, "currSsid")==0) {
os_strcpy(buff, (char*)stconf.ssid); os_strcpy(buff, (char*)stconf.ssid);
} else if (os_strcmp(token, "currStatus")==0) { } else if (os_strcmp(token, "currStatus")==0) {
@ -409,15 +418,13 @@ int ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg)
// Init the wireless, which consists of setting a timer if we expect to connect to an AP // Init the wireless, which consists of setting a timer if we expect to connect to an AP
// so we can revert to STA+AP mode if we can't connect. // so we can revert to STA+AP mode if we can't connect.
void ICACHE_FLASH_ATTR wifiInit() { void ICACHE_FLASH_ATTR wifiInit() {
int x = wifi_get_opmode(); int x = wifi_get_opmode() & 0x3;
os_printf("Wifi init, mode=%d\n", x); os_printf("Wifi init, mode=%s\n", wifiMode[x]);
wifi_set_phy_mode(2); wifi_set_phy_mode(2);
wifi_set_event_handler_cb(wifiHandleEventCb); wifi_set_event_handler_cb(wifiHandleEventCb);
if (x == 1) { // check on the wifi in a few seconds to see whether we need to switch mode
// STA-only mode, reset into STA+AP after a timeout os_timer_disarm(&resetTimer);
os_timer_disarm(&resetTimer); os_timer_setfn(&resetTimer, resetTimerCb, NULL);
os_timer_setfn(&resetTimer, resetTimerCb, NULL); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
os_timer_arm(&resetTimer, RESET_TIMEOUT, 0);
}
} }

@ -10,6 +10,20 @@
#define BUF_MAX (1024) #define BUF_MAX (1024)
static char console_buf[BUF_MAX]; static char console_buf[BUF_MAX];
static int console_wr, console_rd; static int console_wr, console_rd;
static bool console_no_uart; // start out printing to uart
static bool console_newline; // at start of a new line
void ICACHE_FLASH_ATTR
console_uart(bool enable) {
if (!enable && !console_no_uart) {
os_printf("Turning OFF uart console\n");
os_delay_us(4*1000L); // time for uart to flush
console_no_uart = !enable;
} else if (enable && console_no_uart) {
console_no_uart = !enable;
os_printf("Turning ON uart console\n");
}
}
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
console_write(char c) { console_write(char c) {
@ -34,7 +48,17 @@ console_read(void) {
static void ICACHE_FLASH_ATTR static void ICACHE_FLASH_ATTR
console_write_char(char c) { console_write_char(char c) {
uart0_write_char(c); // Uart output unless disabled
if (!console_no_uart) {
if (console_newline) {
uart0_write_char('>');
uart0_write_char(' ');
console_newline = false;
}
uart0_write_char(c);
console_newline = c == '\n';
}
// Store in console buffer
if (c == '\n') console_write('\r'); if (c == '\n') console_write('\r');
console_write(c); console_write(c);
} }

@ -4,6 +4,7 @@
#include "httpd.h" #include "httpd.h"
void consoleInit(void); void consoleInit(void);
void ICACHE_FLASH_ATTR console_uart(bool enable);
int tplConsole(HttpdConnData *connData, char *token, void **arg); int tplConsole(HttpdConnData *connData, char *token, void **arg);
#endif #endif

Loading…
Cancel
Save