// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt #include #include "uart.h" #include "cgi.h" #include "uart.h" #include "serbridge.h" #include "serled.h" #include "config.h" #include "console.h" // Microcontroller console capturing the last 1024 characters received on the uart so // they can be shown on a web page // Buffer to hold concole contents. // Invariants: // - console_rd==console_wr <=> buffer empty // - *console_rd == next char to read // - *console_wr == next char to write // - 0 <= console_xx < BUF_MAX // - (console_wr+1)%BUF_MAX) == console_rd <=> buffer full #define BUF_MAX (1024) static char console_buf[BUF_MAX]; static int console_wr, console_rd; static int console_pos; // offset since reset of buffer static void ICACHE_FLASH_ATTR console_write(char c) { console_buf[console_wr] = c; console_wr = (console_wr+1) % BUF_MAX; if (console_wr == console_rd) { // full, we write anyway and loose the oldest char console_rd = (console_rd+1) % BUF_MAX; // full, eat first char console_pos++; } } #if 0 // return previous character in console, 0 if at start static char ICACHE_FLASH_ATTR console_prev(void) { if (console_wr == console_rd) return 0; return console_buf[(console_wr-1+BUF_MAX)%BUF_MAX]; } #endif void ICACHE_FLASH_ATTR console_write_char(char c) { //if (c == '\n' && console_prev() != '\r') console_write('\r'); // does more harm than good console_write(c); } int ICACHE_FLASH_ATTR ajaxConsoleReset(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. jsonHeader(connData, 200); console_rd = console_wr = console_pos = 0; serbridgeReset(); return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR ajaxConsoleClear(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. jsonHeader(connData, 200); //reset buffer console_rd = console_wr = console_pos = 0; return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR ajaxConsoleBaud(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. char buff[512]; int len, status = 400; len = httpdFindArg(connData->getArgs, "rate", buff, sizeof(buff)); if (len > 0) { int rate = atoi(buff); if (rate >= 300 && rate <= 1000000) { uart0_baud(rate); flashConfig.baud_rate = rate; status = configSave() ? 200 : 400; } } else if (connData->requestType == HTTPD_METHOD_GET) { status = 200; } jsonHeader(connData, status); os_sprintf(buff, "{\"rate\": %d}", flashConfig.baud_rate); httpdSend(connData, buff, -1); return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR ajaxConsoleFormat(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. char buff[16]; int len, status = 400; len = httpdFindArg(connData->getArgs, "fmt", buff, sizeof(buff)); if (len >= 3) { int c = buff[0]; if (c >= '5' && c <= '8') flashConfig.data_bits = c - '5' + FIVE_BITS; if (buff[1] == 'N') flashConfig.parity = NONE_BITS; if (buff[1] == 'E') flashConfig.parity = EVEN_BITS; if (buff[1] == 'O') flashConfig.parity = ODD_BITS; if (buff[2] == '1') flashConfig.stop_bits = ONE_STOP_BIT; if (buff[2] == '2') flashConfig.stop_bits = TWO_STOP_BIT; uart0_config(flashConfig.data_bits, flashConfig.parity, flashConfig.stop_bits, UART_NO_INVERT); status = configSave() ? 200 : 400; } else if (connData->requestType == HTTPD_METHOD_GET) { status = 200; } jsonHeader(connData, status); os_sprintf(buff, "{\"fmt\": \"%c%c%c\"}", flashConfig.data_bits + '5', flashConfig.parity ? 'E' : 'N', flashConfig.stop_bits ? '2': '1'); httpdSend(connData, buff, -1); return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR ajaxConsoleSend(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. char buff[2048]; int len, status = 400; // figure out where to start in buffer based on URI param len = httpdFindArg(connData->getArgs, "text", buff, sizeof(buff)); if (len > 0) { serledFlash(50); // short blink on serial LED uart0_tx_buffer(buff, len); status = 200; } jsonHeader(connData, status); return HTTPD_CGI_DONE; } int ICACHE_FLASH_ATTR ajaxConsole(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. char buff[2048]; int len; // length of text in buff int console_len = (console_wr+BUF_MAX-console_rd) % BUF_MAX; // num chars in console_buf int start = 0; // offset onto console_wr to start sending out chars jsonHeader(connData, 200); // figure out where to start in buffer based on URI param len = httpdFindArg(connData->getArgs, "start", buff, sizeof(buff)); if (len > 0) { start = atoi(buff); if (start < console_pos) { start = 0; } else if (start >= console_pos+console_len) { start = console_len; } else { start = start - console_pos; } } // start outputting len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", console_len-start, console_pos+start); int rd = (console_rd+start) % BUF_MAX; while (len < 2040 && rd != console_wr) { uint8_t c = console_buf[rd]; if (c == '\\' || c == '"') { buff[len++] = '\\'; buff[len++] = c; } else if (c == '\r') { // this is crummy, but browsers display a newline for \r\n sequences } else if (c < ' ') { len += os_sprintf(buff+len, "\\u%04x", c); } else { buff[len++] = c; } rd = (rd + 1) % BUF_MAX; } os_strcpy(buff+len, "\"}"); len+=2; httpdSend(connData, buff, len); return HTTPD_CGI_DONE; } void ICACHE_FLASH_ATTR consoleInit() { console_wr = 0; console_rd = 0; }