From 79495f6b4244d8c4c9199ea5153399a10fc6706f Mon Sep 17 00:00:00 2001 From: Thorsten von Eicken Date: Mon, 25 May 2015 17:10:41 -0700 Subject: [PATCH] support telnet protocol for microcontroller reset; wifi & console tweaks --- serial/serbridge.c | 124 ++++++++++++++++++++++++++++++++++++++++----- serial/serbridge.h | 3 +- serial/uart.c | 10 ++-- serial/uart.h | 1 + user/cgi.c | 28 +++++++++- user/cgi.h | 1 + user/cgiwifi.c | 41 ++++++++------- user/console.c | 26 +++++++++- user/console.h | 1 + 9 files changed, 198 insertions(+), 37 deletions(-) diff --git a/serial/serbridge.c b/serial/serbridge.c index 12cc5a0..1bce1fa 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -89,6 +89,96 @@ static void ICACHE_FLASH_ATTR serbridgeSentCb(void *arg) { 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 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 static void ICACHE_FLASH_ATTR serbridgeRecvCb(void *arg, char *data, unsigned short len) { 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 == 3 && strncmp(data, "?\r\n", 3) == 0)) { 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 GPIO_OUTPUT_SET(MCU_RESET, 0); os_delay_us(100L); GPIO_OUTPUT_SET(MCU_ISP, 0); - os_delay_us(1000L); + os_delay_us(100L); GPIO_OUTPUT_SET(MCU_RESET, 1); os_delay_us(100L); GPIO_OUTPUT_SET(MCU_ISP, 1); os_delay_us(1000L); - //uart0_tx_buffer(data, len); - //conn->skip_chars = 2; 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 { 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, @@ -175,7 +278,7 @@ static void ICACHE_FLASH_ATTR serbridgeConnectCb(void *arg) { connData[i].conn=conn; connData[i].txbufferlen = 0; connData[i].readytosend = true; - connData[i].skip_chars = 0; + connData[i].telnet_state = 0; connData[i].conn_mode = cmInit; espconn_regist_recvcb(conn, serbridgeRecvCb); @@ -192,14 +295,7 @@ serbridgeUartCb(char *buf, int length) { for (int i = 0; i < MAX_CONN; ++i) { if (connData[i].conn) { s++; - if (connData[i].skip_chars == 0) { - 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; - } + espbuffsend(&connData[i], buf, length); } } } diff --git a/serial/serbridge.h b/serial/serbridge.h index 1d1998a..22dc4a1 100644 --- a/serial/serbridge.h +++ b/serial/serbridge.h @@ -20,6 +20,7 @@ enum connModes { cmARM, // ARM (LPC8xx) programming cmEcho, // simply echo characters (used for debugging latency) cmCommand, // AT command mode + cmTelnet, // use telnet escape sequences for programming mode }; struct serbridgeConnData { @@ -28,7 +29,7 @@ struct serbridgeConnData { char *txbuffer; // buffer for the data to send uint16 txbufferlen; // length of data in txbuffer 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); diff --git a/serial/uart.c b/serial/uart.c index 1ec63cf..240cf3d 100644 --- a/serial/uart.c +++ b/serial/uart.c @@ -104,7 +104,7 @@ uart_config(uint8 uart_no) * Parameters : uint8 TxChar - character to tx * Returns : OK *******************************************************************************/ -static STATUS +STATUS uart_tx_one_char(uint8 uart, uint8 c) { //Wait until there is room in the FIFO @@ -124,13 +124,13 @@ uart_tx_one_char(uint8 uart, uint8 c) void ICACHE_FLASH_ATTR 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); } void ICACHE_FLASH_ATTR 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); } /****************************************************************************** @@ -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)) { 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); } diff --git a/serial/uart.h b/serial/uart.h index 1fb6a37..1b769bc 100644 --- a/serial/uart.h +++ b/serial/uart.h @@ -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_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 // of bytes are received. A small number of callbacks can be added and they are all called diff --git a/user/cgi.c b/user/cgi.c index fbcd714..afeaa3a 100644 --- a/user/cgi.c +++ b/user/cgi.c @@ -69,7 +69,9 @@ int ICACHE_FLASH_ATTR tplCounter(HttpdConnData *connData, char *token, void **ar char buff[256]; 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); } else if (os_strcmp(token, "counter")==0) { hitCounter++; @@ -96,3 +98,27 @@ int ICACHE_FLASH_ATTR printNav(char *buff) { //os_printf("nav: %s\n", buff); 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; + } +} + diff --git a/user/cgi.h b/user/cgi.h index 3cd8441..371e525 100644 --- a/user/cgi.h +++ b/user/cgi.h @@ -7,5 +7,6 @@ int cgiLed(HttpdConnData *connData); int tplLed(HttpdConnData *connData, char *token, void **arg); int tplCounter(HttpdConnData *connData, char *token, void **arg); int printNav(char *buff); +int ICACHE_FLASH_ATTR printSysInfo(char *buff, char *token); #endif diff --git a/user/cgiwifi.c b/user/cgiwifi.c index c5ccc7c..d468ff2 100644 --- a/user/cgiwifi.c +++ b/user/cgiwifi.c @@ -16,6 +16,7 @@ Cgi/template routines for the /wifi url. #include "cgiwifi.h" #include "cgi.h" #include "status.h" +#include "console.h" //Enable this to disallow any changes in AP settings //#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", "beacon_timeout", "no_ap_found" }; +static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; + static char* ICACHE_FLASH_ATTR wifiGetReason(void) { if (wifiReason <= 24) return wifiReasons[wifiReason]; 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. static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { int x = wifi_station_get_connect_status(); - int m = wifi_get_opmode(); - os_printf("Wifi check: mode=%d status=%d\n", m, x); + int m = wifi_get_opmode() & 0x3; + os_printf("Wifi check: mode=%s status=%d\n", wifiMode[m], x); if (x == STATION_GOT_IP) { if (m != 1) { @@ -236,9 +239,17 @@ static void ICACHE_FLASH_ATTR resetTimerCb(void *arg) { wifi_set_opmode(1); os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } - } else if (m != 3) { - os_printf("Wifi connect failed. Going into STA+AP mode..\n"); - wifi_set_opmode(3); + os_printf("Turning off uart console\n"); + os_delay_us(4*1000L); // time for uart to flush + 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); } } @@ -369,10 +380,8 @@ int ICACHE_FLASH_ATTR tplWlan(HttpdConnData *connData, char *token, void **arg) os_strcpy(buff, "Unknown"); if (os_strcmp(token, "WiFiMode")==0) { - x=wifi_get_opmode(); - if (x==1) os_strcpy(buff, "Client"); - if (x==2) os_strcpy(buff, "SoftAP"); - if (x==3) os_strcpy(buff, "STA+AP"); + x = wifi_get_opmode() & 0x3; + os_strcpy(buff, wifiMode[x]); } else if (os_strcmp(token, "currSsid")==0) { os_strcpy(buff, (char*)stconf.ssid); } 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 // so we can revert to STA+AP mode if we can't connect. void ICACHE_FLASH_ATTR wifiInit() { - int x = wifi_get_opmode(); - os_printf("Wifi init, mode=%d\n", x); + int x = wifi_get_opmode() & 0x3; + os_printf("Wifi init, mode=%s\n", wifiMode[x]); wifi_set_phy_mode(2); wifi_set_event_handler_cb(wifiHandleEventCb); - if (x == 1) { - // STA-only mode, reset into STA+AP after a timeout - os_timer_disarm(&resetTimer); - os_timer_setfn(&resetTimer, resetTimerCb, NULL); - os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); - } + // check on the wifi in a few seconds to see whether we need to switch mode + os_timer_disarm(&resetTimer); + os_timer_setfn(&resetTimer, resetTimerCb, NULL); + os_timer_arm(&resetTimer, RESET_TIMEOUT, 0); } diff --git a/user/console.c b/user/console.c index 77c9e1f..955d1ad 100644 --- a/user/console.c +++ b/user/console.c @@ -10,6 +10,20 @@ #define BUF_MAX (1024) static char console_buf[BUF_MAX]; 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 console_write(char c) { @@ -34,7 +48,17 @@ console_read(void) { static void ICACHE_FLASH_ATTR 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'); console_write(c); } diff --git a/user/console.h b/user/console.h index bcb8d67..b51d3a3 100644 --- a/user/console.h +++ b/user/console.h @@ -4,6 +4,7 @@ #include "httpd.h" void consoleInit(void); +void ICACHE_FLASH_ATTR console_uart(bool enable); int tplConsole(HttpdConnData *connData, char *token, void **arg); #endif