diff --git a/.gitignore b/.gitignore index e462231..e90d818 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ esp-link.sdf espfs/mkespfsimage/mman-win32/libmman.a .localhistory/ tools/ +local.conf *.tgz diff --git a/Makefile b/Makefile index 78342c1..e235338 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,12 @@ # `VERBOSE=1 make ...` will print debug info # `ESP_HOSTNAME=my.esp.example.com make wiflash` is an easy way to override a variable # +# optional local configuration file +-include local.conf # Makefile heavily adapted to esp-link and wireless flashing by Thorsten von Eicken # Lots of work, in particular to support windows, by brunnels # Original from esphttpd and others... -#VERBOSE=1 +# VERBOSE=1 # --------------- toolchain configuration --------------- @@ -21,7 +23,7 @@ XTENSA_TOOLS_ROOT ?= $(abspath ../esp-open-sdk/xtensa-lx106-elf/bin)/ # Base directory of the ESP8266 SDK package, absolute # Typically you'll download from Espressif's BBS, http://bbs.espressif.com/viewforum.php?f=5 -SDK_BASE ?= $(abspath ../esp_iot_sdk_v1.4.0) +SDK_BASE ?= $(abspath ../esp_iot_sdk_v1.4.1_pre7) # Esptool.py path and port, only used for 1-time serial flashing # Typically you'll use https://github.com/themadinventor/esptool @@ -30,11 +32,29 @@ ESPTOOL ?= $(abspath ../esp-open-sdk/esptool/esptool.py) ESPPORT ?= /dev/ttyUSB0 ESPBAUD ?= 460800 -# The Wifi station configuration can be hard-coded here, which makes esp-link come up in STA+AP -# mode trying to connect to the specified AP *only* if the flash wireless settings are empty! -# This happens on a full serial flash and avoids having to hunt for the AP... -# STA_SSID ?= -# STA_PASS ?= +# --------------- chipset configuration --------------- + +# Pick your flash size: "512KB" or "4MB" +FLASH_SIZE ?= 4MB + +ifeq ("$(FLASH_SIZE)","512KB") +# Winbond 25Q40 512KB flash, typ for esp-01 thru esp-11 +ESP_SPI_SIZE ?= 0 # 0->512KB +ESP_FLASH_MODE ?= 0 # 0->QIO +ESP_FLASH_FREQ_DIV ?= 0 # 0->40Mhz +ESP_FLASH_MAX ?= 241664 # max bin file for 512KB flash: 236KB + +else +# Winbond 25Q32 4MB flash, typ for esp-12 +# Here we're using two partitions of approx 0.5MB because that's what's easily available in terms +# of linker scripts in the SDK. Ideally we'd use two partitions of approx 1MB, the remaining 2MB +# cannot be used for code. +ESP_SPI_SIZE ?= 4 # 6->4MB (1MB+1MB) or 4->4MB (512KB+512KB) +ESP_FLASH_MODE ?= 0 # 0->QIO, 2->DIO +ESP_FLASH_FREQ_DIV ?= 15 # 15->80Mhz +ESP_FLASH_MAX ?= 503808 # max bin file for 512KB flash partition: 492KB +#ESP_FLASH_MAX ?= 1028096 # max bin file for 1MB flash partition: 1004KB +endif # hostname or IP address for wifi flashing ESP_HOSTNAME ?= esp-link @@ -63,7 +83,7 @@ LED_SERIAL_PIN ?= 14 CHANGE_TO_STA ?= yes # Optional Modules -MODULES ?= mqtt rest +MODULES ?= mqtt rest syslog # --------------- esphttpd config options --------------- @@ -178,6 +198,10 @@ ifneq (,$(findstring rest,$(MODULES))) CFLAGS += -DREST endif +ifneq (,$(findstring syslog,$(MODULES))) + CFLAGS += -DSYSLOG +endif + # which modules (subdirectories) of the project to include in compiling LIBRARIES_DIR = libraries MODULES += espfs httpd user serial cmd esp-link @@ -185,7 +209,7 @@ MODULES += $(foreach sdir,$(LIBRARIES_DIR),$(wildcard $(sdir)/*)) EXTRA_INCDIR = include . # libraries used in this project, mainly provided by the SDK -LIBS = c gcc hal phy pp net80211 wpa main lwip +LIBS = c gcc hal phy pp net80211 wpa main lwip # compiler flags using during compilation of source files CFLAGS += -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \ @@ -335,7 +359,7 @@ flash: all $(Q) $(ESPTOOL) --port $(ESPPORT) --baud $(ESPBAUD) write_flash -fs $(ET_FS) -ff $(ET_FF) \ 0x00000 "$(SDK_BASE)/bin/boot_v1.4(b1).bin" 0x01000 $(FW_BASE)/user1.bin \ $(ET_BLANK) $(SDK_BASE)/bin/blank.bin - + tools/$(HTML_COMPRESSOR): $(Q) mkdir -p tools ifeq ($(OS),Windows_NT) diff --git a/esp-link/config.c b/esp-link/config.c index 2b756c1..f962fdc 100644 --- a/esp-link/config.c +++ b/esp-link/config.c @@ -9,21 +9,31 @@ FlashConfig flashConfig; FlashConfig flashDefault = { - 33, 0, 0, - MCU_RESET_PIN, MCU_ISP_PIN, LED_CONN_PIN, LED_SERIAL_PIN, - 115200, - "esp-link\0", // hostname - 0, 0x00ffffff, 0, // static ip, netmask, gateway - 0, // log mode - 0, // swap uart (don't by default) - 1, 0, // tcp_enable, rssi_enable - "\0", // api_key - 0, 0, 0, // slip_enable, mqtt_enable, mqtt_status_enable - 2, 1, // mqtt_timeout, mqtt_clean_session - 1883, 60, // mqtt port, mqtt_keepalive - "\0", "\0", "\0", "\0", "\0", // mqtt host, client_id, user, password, status-topic - "\0", // system description - 1, // rx_pullup + .seq=33, .magic=0, .crc=0, + .reset_pin = MCU_RESET_PIN, .isp_pin = MCU_ISP_PIN, + .conn_led_pin = LED_CONN_PIN, .ser_led_pin = LED_SERIAL_PIN, + .baud_rate = 115200, + .hostname = "esp-link\0", + .staticip = 0, + .netmask = 0x00ffffff, + .gateway = 0, + .log_mode = 0, + .swap_uart = 0, + .tcp_enable = 1, .rssi_enable = 0, + .api_key = "\0", + .slip_enable = 0, .mqtt_enable = 0, .mqtt_status_enable = 0, + .mqtt_timeout = 2, .mqtt_clean_session = 1, + .mqtt_port = 1883, .mqtt_keepalive = 60, + .mqtt_host = "\0", .mqtt_clientid = "\0", + .mqtt_username= "\0", .mqtt_password = "\0", .mqtt_status_topic = "\0", + .sys_descr = "\0", + .rx_pullup = 1, + .sntp_server = "\0", + .syslog_host = "\0", + .syslog_minheap= 8192, + .syslog_filter = 7, + .syslog_showtick= 1, + .syslog_showdate= 0, }; typedef union { diff --git a/esp-link/config.h b/esp-link/config.h index 9b20df3..4ad37b7 100644 --- a/esp-link/config.h +++ b/esp-link/config.h @@ -25,6 +25,12 @@ typedef struct { char mqtt_status_topic[32]; char sys_descr[129]; // system description int8_t rx_pullup; // internal pull-up on RX pin + char sntp_server[32]; + char syslog_host[32]; + uint16_t syslog_minheap; // min. heap to allow queuing + uint8_t syslog_filter; // min. severity + uint8_t syslog_showtick; // show system tick (µs) + uint8_t syslog_showdate; // populate SYSLOG date field } FlashConfig; extern FlashConfig flashConfig; diff --git a/esp-link/main.c b/esp-link/main.c index 3719d9c..2dc1dfa 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -30,10 +30,18 @@ #include "config.h" #include "log.h" #include +#include "syslog.h" +#include "sntp.h" static int ICACHE_FLASH_ATTR cgiSystemInfo(HttpdConnData *connData); static int ICACHE_FLASH_ATTR cgiSystemSet(HttpdConnData *connData); +#define NOTICE(format, ...) do { \ + LOG_NOTICE(format, ## __VA_ARGS__ ); \ + os_printf(format "\n", ## __VA_ARGS__); \ +} while ( 0 ) + + /* This is the main url->function dispatching data struct. In short, it's a struct with various URLs plus their handlers. The handlers can @@ -102,6 +110,17 @@ static char *flash_maps[] = { "2MB:1024/1024", "4MB:1024/1024" }; +// enable SNTP client... +static void ICACHE_FLASH_ATTR enableSNTP() { + if (flashConfig.sntp_server[0]) { + NOTICE("SNTP timesource set to %s", flashConfig.sntp_server); + sntp_stop(); + sntp_setservername(0, flashConfig.sntp_server); + // sntp_set_timezone(flashConfig.timezone); /* stay with GMT... */ + sntp_init(); + } +} + // Cgi to return various System information static int ICACHE_FLASH_ATTR cgiSystemInfo(HttpdConnData *connData) { char buff[1024]; @@ -115,13 +134,18 @@ static int ICACHE_FLASH_ATTR cgiSystemInfo(HttpdConnData *connData) { os_sprintf(buff, "{\"name\": \"%s\", \"reset cause\": \"%d=%s\", " "\"size\": \"%s\"," "\"id\": \"0x%02lX 0x%04lX\"," "\"partition\": \"%s\"," "\"slip\": \"%s\"," "\"mqtt\": \"%s/%s\"," "\"baud\": \"%ld\"," - "\"description\": \"%s\"" "}", + "\"description\": \"%s\"," + "\"syslog\": \"%s\"," + "\"sntp\": \"%s\" " + "}", flashConfig.hostname, rst_info->reason, rst_codes[rst_info->reason], flash_maps[system_get_flash_size_map()], fid & 0xff, (fid&0xff00)|((fid>>16)&0xff), part_id ? "user2.bin" : "user1.bin", flashConfig.slip_enable ? "enabled" : "disabled", flashConfig.mqtt_enable ? "enabled" : "disabled", - mqttState(), flashConfig.baud_rate, flashConfig.sys_descr + mqttState(), flashConfig.baud_rate, flashConfig.sys_descr, + flashConfig.syslog_host, + flashConfig.sntp_server ); jsonHeader(connData, 200); @@ -137,7 +161,27 @@ static int ICACHE_FLASH_ATTR cgiSystemSet(HttpdConnData *connData) { int8_t n = getStringArg(connData, "name", flashConfig.hostname, sizeof(flashConfig.hostname)); int8_t d = getStringArg(connData, "description", flashConfig.sys_descr, sizeof(flashConfig.sys_descr)); - if (n < 0 || d < 0) return HTTPD_CGI_DONE; // getStringArg has produced an error response + int8_t t = getStringArg(connData, "sntp", flashConfig.sntp_server, sizeof(flashConfig.sntp_server)); + int8_t l = getStringArg(connData, "syslog", flashConfig.syslog_host, sizeof(flashConfig.syslog_host)); + if (n < 0 || d < 0 || t < 0 || l < 0) return HTTPD_CGI_DONE; // getStringArg has produced an error response + + // set defaults for syslog server + // we also should add a config or services web page... + if (l > 0) { + // set defaults for syslog server + syslog_init(flashConfig.syslog_host); + flashConfig.syslog_minheap= 8192; + flashConfig.syslog_filter = SYSLOG_PRIO_DEBUG; + flashConfig.syslog_showtick= 1; // show ESP µs Ticker in log message + flashConfig.syslog_showdate= 0; // Synology does a log rotate if timestamp is in the past, so we simply don't send it + os_printf("flashConfig: syslog: %s, minheap: %d, filter: %d, showtick: %d, showdate: %d\n", + flashConfig.syslog_host, flashConfig.syslog_minheap, flashConfig.syslog_filter, flashConfig.syslog_showtick, flashConfig.syslog_showdate); + } + + // (re)start SNTP client if server setting is changed + if (t > 0) { + enableSNTP(); + } if (n > 0) { // schedule hostname change-over @@ -225,18 +269,24 @@ void user_init(void) { #endif struct rst_info *rst_info = system_get_rst_info(); - os_printf("Reset cause: %d=%s\n", rst_info->reason, rst_codes[rst_info->reason]); - os_printf("exccause=%d epc1=0x%x epc2=0x%x epc3=0x%x excvaddr=0x%x depc=0x%x\n", + NOTICE("Reset cause: %d=%s", rst_info->reason, rst_codes[rst_info->reason]); + NOTICE("exccause=%d epc1=0x%x epc2=0x%x epc3=0x%x excvaddr=0x%x depc=0x%x", rst_info->exccause, rst_info->epc1, rst_info->epc2, rst_info->epc3, rst_info->excvaddr, rst_info->depc); uint32_t fid = spi_flash_get_id(); - os_printf("Flash map %s, manuf 0x%02lX chip 0x%04lX\n", flash_maps[system_get_flash_size_map()], + NOTICE("Flash map %s, manuf 0x%02lX chip 0x%04lX", flash_maps[system_get_flash_size_map()], fid & 0xff, (fid&0xff00)|((fid>>16)&0xff)); + NOTICE("** esp-link ready"); + + enableSNTP(); - os_printf("** esp-link ready\n"); #ifdef MQTT + NOTICE("initializing MQTT"); mqtt_client_init(); #endif + NOTICE("initializing user application"); app_init(); + + NOTICE("waiting for work to do..."); } diff --git a/esp-link/task.c b/esp-link/task.c new file mode 100644 index 0000000..879a694 --- /dev/null +++ b/esp-link/task.c @@ -0,0 +1,78 @@ +/* + * task.c + * + * Copyright 2015 Susi's Strolch + * + * For license information see projects "License.txt" + * + * Not sure if it's save to use ICACHE_FLASH_ATTR, so we're running from RAM + */ + +#undef USRTASK_DBG + +#include "esp8266.h" +#include + +#define MAXUSRTASKS 8 + +#ifdef USRTASK_DBG +#define DBG_USRTASK(format, ...) os_printf(format, ## __VA_ARGS__) +#else +#define DBG_USRTASK(format, ...) do { } while(0) +#endif + +LOCAL os_event_t *_task_queue = NULL; // system_os_task queue +LOCAL os_task_t *usr_task_queue = NULL; // user task queue + +// it seems save to run the usr_event_handler from RAM, so no ICACHE_FLASH_ATTR here... + +LOCAL void usr_event_handler(os_event_t *e) +{ + DBG_USRTASK("usr_event_handler: event %p (sig=%d, par=%p)\n", e, (int)e->sig, (void *)e->par); + if (usr_task_queue[e->sig] == NULL || e->sig < 0 || e->sig >= MAXUSRTASKS) { + os_printf("usr_event_handler: task %d %s\n", (int)e->sig, + usr_task_queue[e->sig] == NULL ? "not registered" : "out of range"); + return; + } + (usr_task_queue[e->sig])(e); +} + +LOCAL void init_usr_task() { + if (_task_queue == NULL) + _task_queue = (os_event_t *)os_zalloc(sizeof(os_event_t) * _task_queueLen); + + if (usr_task_queue == NULL) + usr_task_queue = (os_task_t *)os_zalloc(sizeof(os_task_t) * MAXUSRTASKS); + + system_os_task(usr_event_handler, _taskPrio, _task_queue, _task_queueLen); +} + +// public functions +bool post_usr_task(uint8_t task, os_param_t par) +{ + return system_os_post(_taskPrio, task, par); +} + +uint8_t register_usr_task (os_task_t event) +{ + int task; + + DBG_USRTASK("register_usr_task: %p\n", event); + if (_task_queue == NULL || usr_task_queue == NULL) + init_usr_task(); + + for (task = 0; task < MAXUSRTASKS; task++) { + if (usr_task_queue[task] == event) + return task; // task already registered - bail out... + } + + for (task = 0; task < MAXUSRTASKS; task++) { + if (usr_task_queue[task] == NULL) { + DBG_USRTASK("register_usr_task: assign task #%d\n", task); + usr_task_queue[task] = event; + break; + } + } + return task; +} + diff --git a/esp-link/task.h b/esp-link/task.h new file mode 100644 index 0000000..2dfd5d8 --- /dev/null +++ b/esp-link/task.h @@ -0,0 +1,20 @@ +/* + * task.h + * + * Copyright 2015 Susi's Strolch + * + * For license information see projects "License.txt" + * + * + */ + +#ifndef USRTASK_H +#define USRTASK_H + +#define _taskPrio 1 +#define _task_queueLen 64 + +uint8_t register_usr_task (os_task_t event); +bool post_usr_task(uint8_t task, os_param_t par); + +#endif diff --git a/html/favicon.ico b/html/favicon.ico index bf372a1..fe24a7b 100755 Binary files a/html/favicon.ico and b/html/favicon.ico differ diff --git a/html/home.html b/html/home.html index ef99429..1341903 100644 --- a/html/home.html +++ b/html/home.html @@ -27,6 +27,20 @@ SLIP status MQTT status Serial baud + Syslog Server +
+ + + +
+ + SNTP Server +
+ + + +
+
@@ -145,6 +159,8 @@ onLoad(function() { makeAjaxInput("system", "description"); makeAjaxInput("system", "name"); + makeAjaxInput("system", "sntp"); + makeAjaxInput("system", "syslog"); fetchPins(); getWifiInfo(); getSystemInfo(); diff --git a/include/espmissingincludes.h b/include/espmissingincludes.h index bf4c9d5..2c35965 100644 --- a/include/espmissingincludes.h +++ b/include/espmissingincludes.h @@ -3,6 +3,10 @@ #include #include +#include +#include +#include +#include //Missing function prototypes in include folders. Gcc will warn on these if we don't define 'em anywhere. //MOST OF THESE ARE GUESSED! but they seem to work and shut up the compiler. @@ -43,14 +47,17 @@ void ets_update_cpu_frequency(int freqmhz); #define DEBUG_SDK false #endif +int ets_vsprintf(char *str, const char *format, va_list argptr); +int ets_vsnprintf(char *buffer, size_t sizeOfBuffer, const char *format, va_list argptr); int os_snprintf(char *str, size_t size, const char *format, ...) __attribute__((format(printf, 3, 4))); int os_printf_plus(const char *format, ...) __attribute__((format(printf, 1, 2))); #undef os_printf -#define os_printf(format, ...) \ - system_set_os_print(true); \ - os_printf_plus(format, ## __VA_ARGS__); \ - system_set_os_print(DEBUG_SDK); // int os_printf(const char *format, ...) +#define os_printf(format, ...) do { \ + system_set_os_print(true); \ + os_printf_plus(format, ## __VA_ARGS__); \ + system_set_os_print(DEBUG_SDK); \ + } while (0) // memory allocation functions are "different" due to memory debugging functionality diff --git a/serial/serbridge.c b/serial/serbridge.c index 8d400fd..696d635 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -10,6 +10,7 @@ #include "console.h" #include "slip.h" #include "cmd.h" +#include "syslog.h" #define SKIP_AT_RESET @@ -404,11 +405,12 @@ serbridgeConnectCb(void *arg) #ifdef SERBR_DBG os_printf("Accept port %d, conn=%p, pool slot %d\n", conn->proto.tcp->local_port, conn, i); #endif - + syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_NOTICE, "esp-link", "Accept port %d, conn=%p, pool slot %d\n", conn->proto.tcp->local_port, conn, i); if (i==MAX_CONN) { #ifdef SERBR_DBG os_printf("Aiee, conn pool overflow!\n"); #endif + syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_WARNING, "esp-link", "Aiee, conn pool overflow!\n"); espconn_disconnect(conn); return; } diff --git a/serial/uart.c b/serial/uart.c index 8e61f7e..f7f07fa 100644 --- a/serial/uart.c +++ b/serial/uart.c @@ -18,6 +18,7 @@ * Heavily modified and enhanced by Thorsten von Eicken in 2015 */ #include "esp8266.h" +#include "task.h" #include "uart.h" #ifdef UART_DBG @@ -26,14 +27,10 @@ #define DBG_UART(format, ...) do { } while(0) #endif -#define recvTaskPrio 1 -#define recvTaskQueueLen 64 +LOCAL uint8_t uart_recvTaskNum; // UartDev is defined and initialized in rom code. extern UartDevice UartDev; - -os_event_t recvTaskQueue[recvTaskQueueLen]; - #define MAX_CB 4 static UartRecv_cb uart_recv_cb[4]; @@ -208,7 +205,7 @@ uart0_rx_intr_handler(void *para) { //DBG_UART("stat:%02X",*(uint8 *)UART_INT_ENA(uart_no)); ETS_UART_INTR_DISABLE(); - system_os_post(recvTaskPrio, 0, 0); + post_usr_task(uart_recvTaskNum, 0); } } @@ -284,7 +281,7 @@ uart_init(UartBautRate uart0_br, UartBautRate uart1_br) // install uart1 putc callback os_install_putc1((void *)uart0_write_char); - system_os_task(uart_recvTask, recvTaskPrio, recvTaskQueue, recvTaskQueueLen); + uart_recvTaskNum = register_usr_task(uart_recvTask); } void ICACHE_FLASH_ATTR diff --git a/syslog/syslog.c b/syslog/syslog.c new file mode 100644 index 0000000..ca22130 --- /dev/null +++ b/syslog/syslog.c @@ -0,0 +1,392 @@ +/* + * syslog.c + * + * + * Copyright 2015 Susi's Strolch + * + * For license information see projects "License.txt" + * + */ + +#include +#include "config.h" +#include +#include +#include "task.h" + +extern void * mem_trim(void *m, size_t s); // not well documented... + +#define SYSLOG_DBG +#ifdef SYSLOG_DBG +#define DBG_SYSLOG(format, ...) os_printf(format, ## __VA_ARGS__) +#else +#define DBG_SYSLOG(format, ...) do { } while(0) +#endif + +#define WIFI_CHK_INTERVAL 1000 // ms to check Wifi statis +LOCAL os_timer_t wifi_chk_timer; + +LOCAL struct espconn syslog_espconn; +LOCAL uint32_t syslog_msgid = 1; +LOCAL uint8_t syslog_task = 0; + +LOCAL syslog_host_t syslogHost; +LOCAL syslog_entry_t *syslogQueue = NULL; + +static enum syslog_state syslogState = SYSLOG_NONE; + +LOCAL void ICACHE_FLASH_ATTR syslog_add_entry(syslog_entry_t *entry); +LOCAL void ICACHE_FLASH_ATTR syslog_chk_wifi_stat(void); +LOCAL void ICACHE_FLASH_ATTR syslog_udp_sent_cb(void *arg); +#ifdef SYSLOG_UDP_RECV +LOCAL void ICACHE_FLASH_ATTR syslog_udp_recv_cb(void *arg, char *pusrdata, unsigned short length); +#endif + +#define syslog_send_udp() post_usr_task(syslog_task,0) + +/****************************************************************************** + * FunctionName : syslog_chk_wifi_stat + * Description : check whether get ip addr or not + * Parameters : none + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +syslog_chk_wifi_stat(void) +{ + struct ip_info ipconfig; + DBG_SYSLOG("syslog_chk_wifi_stat: state: %d ", syslogState); + + //disarm timer first + os_timer_disarm(&wifi_chk_timer); + + //try to get ip info of ESP8266 station + wifi_get_ip_info(STATION_IF, &ipconfig); + int wifi_status = wifi_station_get_connect_status(); + if (wifi_status == STATION_GOT_IP && ipconfig.ip.addr != 0) + { + if (syslogState == SYSLOG_WAIT) { // waiting for initialization + DBG_SYSLOG("connected, initializing UDP socket\n"); + syslog_init(flashConfig.syslog_host); + } + } else { + if ((wifi_status == STATION_WRONG_PASSWORD || + wifi_status == STATION_NO_AP_FOUND || + wifi_status == STATION_CONNECT_FAIL)) { + syslogState = SYSLOG_ERROR; + os_printf("*** connect failure!!!\n"); + } else { + DBG_SYSLOG("re-arming timer...\n"); + os_timer_setfn(&wifi_chk_timer, (os_timer_func_t *)syslog_chk_wifi_stat, NULL); + os_timer_arm(&wifi_chk_timer, WIFI_CHK_INTERVAL, 0); + } + } +} + +LOCAL void ICACHE_FLASH_ATTR +syslog_udp_send_event(os_event_t *events) { + if (syslogQueue == NULL) + syslogState = SYSLOG_READY; + else { + int res = 0; + syslog_espconn.proto.udp->remote_port = syslogHost.port; // ESP8266 udp remote port + os_memcpy(&syslog_espconn.proto.udp->remote_ip, &syslogHost.addr.addr, 4); // ESP8266 udp remote IP + res = espconn_send(&syslog_espconn, (uint8_t *)syslogQueue->datagram, syslogQueue->datagram_len); + if (res != 0) { + os_printf("syslog_udp_send: error %d\n", res); + } + } +} + +/****************************************************************************** + * FunctionName : syslog_compose + * Description : compose a syslog_entry_t from va_args + * Parameters : va_args + * Returns : the malloced syslog_entry_t + ******************************************************************************/ +LOCAL syslog_entry_t ICACHE_FLASH_ATTR * +syslog_compose(uint8_t facility, uint8_t severity, const char *tag, const char *fmt, ...) +{ + syslog_entry_t *se = os_zalloc(sizeof (syslog_entry_t) + 1024); // allow up to 1k datagram + char *p = se->datagram; + uint32_t tick = WDEV_NOW(); // 0 ... 4294.967295s + + // The Priority value is calculated by first multiplying the Facility + // number by 8 and then adding the numerical value of the Severity. + p += os_sprintf(p, "<%d> ", facility * 8 + severity); + + // strftime doesn't work as expected - or adds 8k overhead. + // so let's do poor man conversion - format is fixed anyway + if (flashConfig.syslog_showdate == 0) + p += os_sprintf(p, "- "); + else { + time_t now = NULL; + struct tm *tp = NULL; + + // create timestamp: FULL-DATE "T" PARTIAL-TIME "Z": 'YYYY-mm-ddTHH:MM:SSZ ' + // as long as realtime_stamp is 0 we use tick div 10⁶ as date + now = (realtime_stamp == 0) ? (tick / 1000000) : realtime_stamp; + tp = gmtime(&now); + + p += os_sprintf(p, "%4d-%02d-%02dT%02d:%02d:%02dZ ", + tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + } + + // add HOSTNAME APP-NAME PROCID MSGID + if (flashConfig.syslog_showtick) + p += os_sprintf(p, "%s %s %lu.%06lu %lu ", flashConfig.hostname, tag, tick / 1000000, tick % 1000000, syslog_msgid++); + else + p += os_sprintf(p, "%s %s - %lu ", flashConfig.hostname, tag, syslog_msgid++); + + // append syslog message + va_list arglist; + va_start(arglist, fmt); + p += ets_vsprintf(p, fmt, arglist ); + va_end(arglist); + + se->datagram_len = p - se->datagram; + se = mem_trim(se, sizeof(syslog_entry_t) + se->datagram_len + 1); + return se; +} + +/****************************************************************************** + * FunctionName : syslog_add_entry + * Description : add a syslog_entry_t to the syslogQueue + * Parameters : entry: the syslog_entry_t + * Returns : none + ******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +syslog_add_entry(syslog_entry_t *entry) +{ + syslog_entry_t *pse = syslogQueue; + + // append msg to syslog_queue + if (pse == NULL) + syslogQueue = entry; + else { + while (pse->next != NULL) + pse = pse->next; + pse->next = entry; // append msg to syslog queue + } + + // ensure we have sufficient heap for the rest of the system + if (system_get_free_heap_size() < syslogHost.min_heap_size) { + if (syslogState != SYSLOG_HALTED) { + os_printf("syslog_add_entry: Warning: queue filled up, halted\n"); + entry->next = syslog_compose(SYSLOG_FAC_SYSLOG, SYSLOG_PRIO_CRIT, "-", "queue filled up, halted"); + if (syslogState == SYSLOG_READY) + syslog_send_udp(); + syslogState = SYSLOG_HALTED; + } + } +} + +/****************************************************************************** + * FunctionName : syslog_sent_cb + * Description : udp sent successfully + * fetch next syslog package, free old message + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none + ******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +syslog_udp_sent_cb(void *arg) +{ + struct espconn *pespconn = arg; + (void) pespconn; + + // datagram is delivered - free and advance queue + syslog_entry_t *pse = syslogQueue; + syslogQueue = syslogQueue -> next; + os_free(pse); + + if (syslogQueue != NULL) + syslog_send_udp(); + else + syslogState = SYSLOG_READY; +} + + /***************************************************************************** + * FunctionName : syslog_recv_cb + * Description : Processing the received udp packet + * Parameters : arg -- Additional argument to pass to the callback function + * pusrdata -- The received data (or NULL when the connection has been closed!) + * length -- The length of received data + * Returns : none + ******************************************************************************/ +#ifdef SYSLOG_UDP_RECV +LOCAL void ICACHE_FLASH_ATTR +syslog_udp_recv_cb(void *arg, char *pusrdata, unsigned short length) +{ + DBG_SYSLOG("syslog_udp_recv_cb: %p, %p, %d\n", arg, pusrdata, length); +} +#endif + +/****************************************************************************** + * + ******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +syslog_gethostbyname_cb(const char *name, ip_addr_t *ipaddr, void *arg) +{ + struct espconn *pespconn = (struct espconn *)arg; + (void) pespconn; + if (ipaddr != NULL) { + syslogHost.addr.addr = ipaddr->addr; + syslogState = SYSLOG_SENDING; + syslog_send_udp(); + } else { + syslogState = SYSLOG_ERROR; + DBG_SYSLOG("syslog_gethostbyname_cb: state=SYSLOG_ERROR\n"); + } +} + + /****************************************************************************** + * FunctionName : initSyslog + * Description : Initialize the syslog library + * Parameters : hostname -- the syslog server (host:port) + * host: IP-Addr | hostname + * Returns : none + *******************************************************************************/ +void ICACHE_FLASH_ATTR +syslog_init(char *syslog_server) +{ + char host[32], *port = &host[0]; + + syslog_task = register_usr_task(syslog_udp_send_event); + syslogHost.min_heap_size = flashConfig.syslog_minheap; + syslogHost.port = 514; + syslogState = SYSLOG_WAIT; + + os_strncpy(host, syslog_server, 32); + while (*port && *port != ':') // find port delimiter + port++; + if (*port) { + *port++ = '\0'; + syslogHost.port = atoi(port); + } + + wifi_set_broadcast_if(STATIONAP_MODE); // send UDP broadcast from both station and soft-AP interface + syslog_espconn.type = ESPCONN_UDP; + syslog_espconn.proto.udp = (esp_udp *)os_zalloc(sizeof(esp_udp)); + syslog_espconn.proto.udp->local_port = espconn_port(); // set a available port +#ifdef SYSLOG_UDP_RECV + espconn_regist_recvcb(&syslog_espconn, syslog_udp_recv_cb); // register a udp packet receiving callback +#endif + espconn_regist_sentcb(&syslog_espconn, syslog_udp_sent_cb); // register a udp packet sent callback + espconn_create(&syslog_espconn); // create udp + + if (UTILS_StrToIP((const char *)host, (void*)&syslogHost.addr)) { + syslogState = SYSLOG_SENDING; + syslog_send_udp(); + } else { + static struct espconn espconn_ghbn; + espconn_gethostbyname(&espconn_ghbn, host, &syslogHost.addr, syslog_gethostbyname_cb); + // syslog_send_udp is called by syslog_gethostbyname_cb() + } +#ifdef SYSLOG_UDP_RECV + DBG_SYSLOG("syslog_init: host: %s, port: %d, lport: %d, recvcb: %p, sentcb: %p, state: %d\n", + host, syslogHost.port, syslog_espconn.proto.udp->local_port, + syslog_udp_recv_cb, syslog_udp_sent_cb, syslogState ); +#else + DBG_SYSLOG("syslog_init: host: %s, port: %d, lport: %d, rsentcb: %p, state: %d\n", + host, syslogHost.port, syslog_espconn.proto.udp->local_port, + syslog_udp_sent_cb, syslogState ); +#endif +} + + /****************************************************************************** + * FunctionName : syslog + * Description : compose and queue a new syslog message + * Parameters : facility + * severity + * tag + * message + * ... + * + * SYSLOG-MSG = HEADER SP STRUCTURED-DATA [SP MSG] + + HEADER = PRI VERSION SP TIMESTAMP SP HOSTNAME + SP APP-NAME SP PROCID SP MSGID + PRI = "<" PRIVAL ">" + PRIVAL = 1*3DIGIT ; range 0 .. 191 + VERSION = NONZERO-DIGIT 0*2DIGIT + HOSTNAME = NILVALUE / 1*255PRINTUSASCII + + APP-NAME = NILVALUE / 1*48PRINTUSASCII + PROCID = NILVALUE / 1*128PRINTUSASCII + MSGID = NILVALUE / 1*32PRINTUSASCII + + TIMESTAMP = NILVALUE / FULL-DATE "T" FULL-TIME + FULL-DATE = DATE-FULLYEAR "-" DATE-MONTH "-" DATE-MDAY + DATE-FULLYEAR = 4DIGIT + DATE-MONTH = 2DIGIT ; 01-12 + DATE-MDAY = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on + ; month/year + FULL-TIME = PARTIAL-TIME TIME-OFFSET + PARTIAL-TIME = TIME-HOUR ":" TIME-MINUTE ":" TIME-SECOND + [TIME-SECFRAC] + TIME-HOUR = 2DIGIT ; 00-23 + TIME-MINUTE = 2DIGIT ; 00-59 + TIME-SECOND = 2DIGIT ; 00-59 + TIME-SECFRAC = "." 1*6DIGIT + TIME-OFFSET = "Z" / TIME-NUMOFFSET + TIME-NUMOFFSET = ("+" / "-") TIME-HOUR ":" TIME-MINUTE + + + STRUCTURED-DATA = NILVALUE / 1*SD-ELEMENT + SD-ELEMENT = "[" SD-ID *(SP SD-PARAM) "]" + SD-PARAM = PARAM-NAME "=" %d34 PARAM-VALUE %d34 + SD-ID = SD-NAME + PARAM-NAME = SD-NAME + PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and + ; ']' MUST be escaped. + SD-NAME = 1*32PRINTUSASCII + ; except '=', SP, ']', %d34 (") + + MSG = MSG-ANY / MSG-UTF8 + MSG-ANY = *OCTET ; not starting with BOM + MSG-UTF8 = BOM UTF-8-STRING + BOM = %xEF.BB.BF + UTF-8-STRING = *OCTET ; UTF-8 string as specified + ; in RFC 3629 + + OCTET = %d00-255 + SP = %d32 + PRINTUSASCII = %d33-126 + NONZERO-DIGIT = %d49-57 + DIGIT = %d48 / NONZERO-DIGIT + NILVALUE = "-" + * + * TIMESTAMP: realtime_clock == 0 ? timertick / 10⁶ : realtime_clock + * HOSTNAME hostname + * APPNAME: ems-esp-link + * PROCID: timertick + * MSGID: NILVALUE + * + * Returns : none + *******************************************************************************/ +void ICACHE_FLASH_ATTR +syslog(uint8_t facility, uint8_t severity, const char *tag, const char *fmt, ...) +{ + DBG_SYSLOG("syslog: state=%d ", syslogState); + if (syslogState == SYSLOG_ERROR || + syslogState == SYSLOG_HALTED) + return; + + // compose the syslog message + void *arg = __builtin_apply_args(); + void *res = __builtin_apply((void*)syslog_compose, arg, 128); + syslog_entry_t *se = *(syslog_entry_t **)res; + + // and append it to the message queue + syslog_add_entry(se); + + if (syslogState == SYSLOG_READY) { + syslogState = SYSLOG_SENDING; + syslog_send_udp(); + } + + if (syslogState == SYSLOG_NONE) { + syslogState = SYSLOG_WAIT; + syslog_chk_wifi_stat(); // fire the timer to check the Wifi connection status + } +} diff --git a/syslog/syslog.h b/syslog/syslog.h new file mode 100644 index 0000000..f13d383 --- /dev/null +++ b/syslog/syslog.h @@ -0,0 +1,110 @@ +/* + * syslog.h + * + * + * Copyright 2015 Susi's Strolch + * + * For license information see projects "License.txt" + * + * part of syslog.c - client library + * + */ + + +#ifndef _SYSLOG_H +#define _SYSLOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum syslog_state { + SYSLOG_NONE, // not initialized + SYSLOG_WAIT, // initialized, waiting for Wifi + SYSLOG_READY, // Wifi established, ready to send + SYSLOG_SENDING, // UDP package on the air + SYSLOG_HALTED, // heap full, discard message + SYSLOG_ERROR, +}; + +enum syslog_priority { + SYSLOG_PRIO_EMERG, /* system is unusable */ + SYSLOG_PRIO_ALERT, /* action must be taken immediately */ + SYSLOG_PRIO_CRIT, /* critical conditions */ + SYSLOG_PRIO_ERR, /* error conditions */ + SYSLOG_PRIO_WARNING, /* warning conditions */ + SYSLOG_PRIO_NOTICE, /* normal but significant condition */ + SYSLOG_PRIO_INFO, /* informational */ + SYSLOG_PRIO_DEBUG, /* debug-level messages */ +}; + +enum syslog_facility { + SYSLOG_FAC_KERN, /* kernel messages */ + SYSLOG_FAC_USER, /* random user-level messages */ + SYSLOG_FAC_MAIL, /* mail system */ + SYSLOG_FAC_DAEMON, /* system daemons */ + SYSLOG_FAC_AUTH, /* security/authorization messages */ + SYSLOG_FAC_SYSLOG, /* messages generated internally by syslogd */ + SYSLOG_FAC_LPR, /* line printer subsystem */ + SYSLOG_FAC_NEWS, /* network news subsystem */ + SYSLOG_FAC_UUCP, /* UUCP subsystem */ + SYSLOG_FAC_CRON, /* clock daemon */ + SYSLOG_FAC_AUTHPRIV,/* security/authorization messages (private) */ + SYSLOG_FAC_FTP, /* ftp daemon */ + SYSLOG_FAC_LOCAL0, /* reserved for local use */ + SYSLOG_FAC_LOCAL1, /* reserved for local use */ + SYSLOG_FAC_LOCAL2, /* reserved for local use */ + SYSLOG_FAC_LOCAL3, /* reserved for local use */ + SYSLOG_FAC_LOCAL4, /* reserved for local use */ + SYSLOG_FAC_LOCAL5, /* reserved for local use */ + SYSLOG_FAC_LOCAL6, /* reserved for local use */ + SYSLOG_FAC_LOCAL7, /* reserved for local use */ +}; + +#define MINIMUM_HEAP_SIZE 8192 +#define REG_READ(_r) (*(volatile uint32 *)(_r)) +#define WDEV_NOW() REG_READ(0x3ff20c00) + +extern uint32_t realtime_stamp; // 1sec NTP ticker + +typedef struct syslog_host_t syslog_host_t; +struct syslog_host_t { + uint32_t min_heap_size; // minimum allowed heap size when buffering + ip_addr_t addr; + uint16_t port; +}; + +// buffered syslog event - f.e. if network stack isn't up and running +typedef struct syslog_entry_t syslog_entry_t; +struct syslog_entry_t { + syslog_entry_t *next; + uint16_t datagram_len; + char datagram[]; +}; + +syslog_host_t syslogserver; + +void ICACHE_FLASH_ATTR syslog_init(char *syslog_server); +void ICACHE_FLASH_ATTR syslog(uint8_t facility, uint8_t severity, const char tag[], const char message[], ...); + +// some convenience macros +#ifdef SYSLOG +// extern char *esp_link_version; // in user_main.c +#define LOG_DEBUG(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_DEBUG, "esp_link", format, ## __VA_ARGS__ ) +#define LOG_NOTICE(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_NOTICE, "esp_link", format, ## __VA_ARGS__ ) +#define LOG_INFO(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_INFO, "esp_link", format, ## __VA_ARGS__ ) +#define LOG_WARN(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_WARNING, "esp_link", format, ## __VA_ARGS__ ) +#define LOG_ERR(format, ...) syslog(SYSLOG_FAC_USER, SYSLOG_PRIO_ERR, "esp_link", format, ## __VA_ARGS__ ) +#else +#define LOG_DEBUG(format, ...) do { } while(0) +#define LOG_NOTICE(format, ...) do { } while(0) +#define LOG_WARN(format, ...) do { } while(0) +#define LOG_INFO(format, ...) do { } while(0) +#define LOG_ERR(format, ...) do { } while(0) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _SYSLOG_H */