// Copyright 2015 by Thorsten von Eicken, see LICENSE.txt #include #include "uart.h" #include "cgi.h" #include "config.h" #include "log.h" // Web log for the esp8266 to replace outputting to uart1. // The web log has a 1KB circular in-memory buffer which os_printf prints into and // the HTTP handler simply displays the buffer content on a web page. // see console.c for invariants (same here) #define BUF_MAX (1400) static char log_buf[BUF_MAX]; static int log_wr, log_rd; static int log_pos; static bool log_no_uart; // start out printing to uart static bool log_newline; // at start of a new line // called from wifi reset timer to turn UART on when we loose wifi and back off // when we connect to wifi AP. Here this is gated by the flash setting void ICACHE_FLASH_ATTR log_uart(bool enable) { if (!enable && !log_no_uart && flashConfig.log_mode != LOG_MODE_ON) { // we're asked to turn uart off, and uart is on, and the flash setting isn't always-on #if 1 os_printf("Turning OFF uart log\n"); os_delay_us(4*1000L); // time for uart to flush log_no_uart = !enable; #endif } else if (enable && log_no_uart && flashConfig.log_mode != LOG_MODE_OFF) { // we're asked to turn uart on, and uart is off, and the flash setting isn't always-off log_no_uart = !enable; os_printf("Turning ON uart log\n"); } } static void ICACHE_FLASH_ATTR log_write(char c) { log_buf[log_wr] = c; log_wr = (log_wr+1) % BUF_MAX; if (log_wr == log_rd) { log_rd = (log_rd+1) % BUF_MAX; // full, eat first char log_pos++; } } #if 0 static char ICACHE_FLASH_ATTR log_read(void) { char c = 0; if (log_rd != log_wr) { c = log_buf[log_rd]; log_rd = (log_rd+1) % BUF_MAX; } return c; } #endif static void ICACHE_FLASH_ATTR log_write_char(char c) { // Uart output unless disabled if (!log_no_uart) { if (log_newline) { char buff[16]; int l = os_sprintf(buff, "%6d> ", (system_get_time()/1000)%1000000); for (int i=0; iconn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. 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 < log_pos) { start = 0; } else if (start >= log_pos+log_len) { start = log_len; } else { start = start - log_pos; } } // start outputting len = os_sprintf(buff, "{\"len\":%d, \"start\":%d, \"text\": \"", log_len-start, log_pos+start); int rd = (log_rd+start) % BUF_MAX; while (len < 2040 && rd != log_wr) { uint8_t c = log_buf[rd]; if (c == '\\' || c == '"') { buff[len++] = '\\'; buff[len++] = c; } 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; } static char *dbg_mode[] = { "auto", "off", "on" }; int ICACHE_FLASH_ATTR ajaxLogDbg(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, "mode", buff, sizeof(buff)); if (len > 0) { int8_t mode = -1; if (os_strcmp(buff, "auto") == 0) mode = LOG_MODE_AUTO; if (os_strcmp(buff, "off") == 0) mode = LOG_MODE_OFF; if (os_strcmp(buff, "on") == 0) mode = LOG_MODE_ON; if (mode >= 0) { flashConfig.log_mode = mode; if (mode != LOG_MODE_AUTO) log_uart(mode == LOG_MODE_ON); status = configSave() ? 200 : 400; } } else if (connData->requestType == HTTPD_METHOD_GET) { status = 200; } jsonHeader(connData, status); os_sprintf(buff, "{\"mode\": \"%s\"}", dbg_mode[flashConfig.log_mode]); httpdSend(connData, buff, -1); return HTTPD_CGI_DONE; } void ICACHE_FLASH_ATTR logInit() { log_no_uart = flashConfig.log_mode == LOG_MODE_OFF; // ON unless set to always-off log_wr = 0; log_rd = 0; os_install_putc1((void *)log_write_char); }