diff --git a/README.md b/README.md index 3d4e6b3..adeec91 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ General commands: - reset [factory]: resets the esp, 'factory' optionally resets WiFi params to default values (works on a locked device only from serial console) - set speed [80|160]: sets the CPU clock frequency (default 80 Mhz) - set config_port _portno_: sets the port number of the console login (default is 7777, 0 disables remote console config) -- set config_access _mode_: controls the networks that allow config access (0: no access, 1: only internal, 2: only external, 3: both (default)) +- set config_access [0|1|2|3]: controls the networks that allow config access (0: no access, 1: only internal, 2: only external, 3: both (default)) - set bitrate [bps]: sets the serial bitrate (default 115200 bps) -- set system_output [0|1]: enables/disables system info/warning output to serial port (default: on=1) +- set system_output [0|1|2]: configures systems handling of the serial port (0: none/script, 1: cli commands/responses, 2: cli and info/warnings (default)). Mode 0 means, that any serial input is forwarded to the scripting engine if enabled - quit: terminates a remote session WiFi and network related commands: @@ -57,7 +57,7 @@ WiFi and network related commands: - set ip dhcp: configures dynamic IP address for the ESP in the uplink network, default - set netmask _netmask_: sets a static netmask for the uplink network - set gw _gw-addr_: sets a static gateway address in the uplink network -- set mdns_mode [0|1|2]: selects, which interface should be announced via mDNS (0=none (default), 1 = STA, 2 = SoftAP) +- set mdns_mode [0|1|2]: selects, which interface should be announced via mDNS (0: none (default), 1: STA, 2: SoftAP) - scan: does a scan for APs While the user interface looks similar to my esp_wifi_repeater at https://github.com/martin-ger/esp_wifi_repeater this does NO NAT routing. AP and STA network are stricly separated and there is no routing in between. The only possible connection via both networks is the uMQTT broker that listens on both interfaces. diff --git a/SCRIPTING.md b/SCRIPTING.md index 072f3a5..89e5c40 100644 --- a/SCRIPTING.md +++ b/SCRIPTING.md @@ -34,8 +34,9 @@ In general, scripts conform to the following BNF: mqttconnect | timer | alarm | - gpio_interrupt (pullup|nopullup) | topic (local|remote) | + gpio_interrupt (pullup|nopullup) | + serial | http_response ::= publish (local|remote) [retained] | @@ -49,6 +50,7 @@ In general, scripts conform to the following BNF: gpio_pinmode (input|output) [pullup] | gpio_out | gpio_pwm | + serial_out | if then [else ] endif | print | println | system | @@ -56,13 +58,14 @@ In general, scripts conform to the following BNF: ::= | | () | not () | retained_topic() | substr(,,) | - csvstr(,,) | json_parse (,) + csvstr(,,) | eatwhite () | + json_parse (,) := '=' | '>' | gte | str_ge | str_gte | '+' | '-' | '*' | '|' | div := | | # | $[any ASCII]* | @ | - gpio_in() | $adc | $this_item | $this_data | $this_gpio | - $this_http_code | $this_http_body | $timestamp | $weekday + gpio_in() | $adc | $this_item | $this_data | $this_serial | + $this_gpio | $this_http_code | $this_http_body | $timestamp | $weekday := "[any ASCII]*" | [any ASCII]* @@ -111,6 +114,11 @@ alarm ``` This event happens when the time-of-day stored in the alarm with the given number is reached. It happens once per day. Alarm times are given as "hh:mm:ss" and are only available if NTP is enabled. Make sure to set the correct "ntp_timezone" to adjust UTC to your location. +``` +serial +``` +This event happens when the "system_output" mode is set to 0 and a carriage return (CR)-terminated string has been received from the serial input. Instead of interpreting it as cli command it is forwarded to the scripting engine. The input value is availabe via the special variable _$this_serial_. + ``` gpio_interrupt (pullup|nopullup) ``` @@ -177,6 +185,11 @@ gpio_pwm ``` Defines the GPIO pin num as PWM output and sets the PWM duty cycle to the given value. The value should be in the range from 0-1000 (0 = off, 1000 = full duty). By default the PWM frequency is 1000Hz. It can be changed with the _pwm_period_ config parameter. +``` +serial_out +``` +Sends the given expression to serial port. + ``` system ``` @@ -214,7 +227,12 @@ Extracts characters from a string. The two constant numbers give the starting po ``` csvstr(,,) ``` -Extracts strings from a CSV-list (correctly a string with a delimiter character). The constant number gives the position (first is postion 1) and the char is the delimiter. Examples: csvstr("one,two,three", 2, ",") is "two", csvstr("system/test/1", 2, "/") is "test" +Extracts strings from a CSV-list (correctly a string with a delimiter character). The constant number gives the position (first is postion 1) and the char is the delimiter. Examples: csvstr("one,two,three", 2, ",") is "two", csvstr("system/test/1", 2, "/") is "test". + +``` +eatwhite() +``` +Eliminates all whitespaces from a string. ``` json_parse (,) diff --git a/driver/new_uart.c b/driver/new_uart.c index dde2a24..5f8b373 100644 --- a/driver/new_uart.c +++ b/driver/new_uart.c @@ -29,6 +29,7 @@ #ifdef _ENABLE_CONSOLE_INTEGRATION uint8_t linked_to_console = 0; + uint8_t echo_on = 1; #endif extern UartDevice UartDev; @@ -68,6 +69,7 @@ void ICACHE_FLASH_ATTR UART_init_console(UartBautRate uart0_br, rxBuff = rxbuffer; txBuff = txBuffer; linked_to_console = 1; + echo_on = 1; uart_config(UART0); UART_SetPrintPort(UART0); @@ -224,6 +226,11 @@ int UART_Recv(uint8 uart_no, char *buffer, int max_buf_len) return index; } +int ICACHE_FLASH_ATTR UART_Echo(uint8 echo) +{ + echo_on = echo; +} + int UART_Send(uint8 uart_no, char *buffer, int len) { int index = 0; @@ -311,7 +318,9 @@ static void uart0_rx_intr_handler(void *para) //if (ch == '\r') ch = '\n'; ringbuf_memcpy_into(rxBuff, &ch, 1); #if _ENABLE_CONSOLE_INTEGRATION == 1 - uart_tx_one_char(uart_no, ch); + if (echo_on){ + uart_tx_one_char(uart_no, ch); + } if (ch == '\r') { system_os_post(0, SIG_CONSOLE_RX, 0); diff --git a/firmware/0x00000.bin b/firmware/0x00000.bin index c050421..d427239 100644 Binary files a/firmware/0x00000.bin and b/firmware/0x00000.bin differ diff --git a/firmware/0x10000.bin b/firmware/0x10000.bin index 0692a08..84aa6f0 100644 Binary files a/firmware/0x10000.bin and b/firmware/0x10000.bin differ diff --git a/firmware/sha1sums b/firmware/sha1sums index e83aaef..42cccb8 100644 --- a/firmware/sha1sums +++ b/firmware/sha1sums @@ -1,2 +1,2 @@ -65c1e77051ecf1d65eb38017df1096f84b5daa4a 0x00000.bin -a4ce86573e9a5a60ae6665a47d624522e054a0b2 0x10000.bin +f8fcbc261888a7fe6c4d2db2e9b9ce2b13de7a29 0x00000.bin +476c9ae67db3a4117c410d9b664315f83590c9d8 0x10000.bin diff --git a/include/driver/uart.h b/include/driver/uart.h index b9bf4ed..49b5f0c 100644 --- a/include/driver/uart.h +++ b/include/driver/uart.h @@ -229,5 +229,6 @@ void UART_init_console(UartBautRate uart0_br, ringbuf_t rxbuffer, ringbuf_t txBuffer); +int UART_Echo(uint8 echo); #endif diff --git a/user/cli.c b/user/cli.c index d43d3dc..d3ec61b 100644 --- a/user/cli.c +++ b/user/cli.c @@ -263,9 +263,10 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) { os_sprintf(response, "Serial bitrate: %d\r\n", config.bit_rate); to_console(response); - if (!config.system_output) - to_console("System output: off\r\n"); - + if (config.system_output < SYSTEM_OUTPUT_INFO) { + os_sprintf(response, "System output: %s\r\n", config.system_output==SYSTEM_OUTPUT_NONE?"none":"command reply"); + to_console(response); + } goto command_handled_2; } @@ -768,7 +769,7 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) { if (strcmp(tokens[1],"system_output") == 0) { config.system_output = atoi(tokens[2]); - os_sprintf(response, "System output %s\r\n", config.system_output?"on":"off"); + os_sprintf(response, "System output set to %d\r\n", config.system_output); goto command_handled; } diff --git a/user/config_flash.c b/user/config_flash.c index 8ca08ae..c57482c 100644 --- a/user/config_flash.c +++ b/user/config_flash.c @@ -31,7 +31,7 @@ void config_load_default(sysconfig_p config) { config->my_netmask.addr = 0; // use DHCP config->my_gw.addr = 0; // use DHCP - config->system_output = 1; + config->system_output = SYSTEM_OUTPUT_INFO; config->bit_rate = 115200; config->mdns_mode = 0; // no mDNS diff --git a/user/config_flash.h b/user/config_flash.h index 0ec743b..6d2444e 100644 --- a/user/config_flash.h +++ b/user/config_flash.h @@ -15,6 +15,10 @@ #define MAGIC_NUMBER 0x015005fd +#define SYSTEM_OUTPUT_INFO 2 +#define SYSTEM_OUTPUT_CMD 1 +#define SYSTEM_OUTPUT_NONE 0 + typedef struct { // To check if the structure is initialized or not in flash diff --git a/user/global.h b/user/global.h index 75792f2..8706b16 100644 --- a/user/global.h +++ b/user/global.h @@ -43,3 +43,6 @@ void script_connected_cb(void *arg); void console_handle_command(struct espconn *pespconn); void to_console(char *str); +void do_command(char *t1, char *t2, char *t3); +void con_print(uint8_t *str); +void serial_out(uint8_t *str); diff --git a/user/lang.c b/user/lang.c index 6ef412b..20aebf4 100644 --- a/user/lang.c +++ b/user/lang.c @@ -2,10 +2,8 @@ #include "mem.h" #include "osapi.h" #include "lang.h" -#include "user_config.h" -#include "config_flash.h" -#include "mqtt/mqtt_topics.h" -#include "mqtt/mqtt_retainedlist.h" +#include "global.h" + #ifdef NTP #include "ntp.h" #endif @@ -30,10 +28,6 @@ #define lang_log(...) {if (lang_logging){char log_buffer[256]; os_sprintf (log_buffer, "%s: ", get_timestr()); con_print(log_buffer); os_sprintf (log_buffer, __VA_ARGS__); con_print(log_buffer);}} //#define lang_log //os_printf -extern uint8_t *my_script; -extern void do_command(char *t1, char *t2, char *t3); -extern void con_print(uint8_t *str); - static char EOT[] = "end of text"; #define len_check(x) \ if (interpreter_status==SYNTAX_CHECK && next_token+(x) >= max_token) \ @@ -72,6 +66,9 @@ char *interpreter_data; int interpreter_data_len; int interpreter_timer; int interpreter_timestamp; +bool in_serial_statement; +char *interpreter_serial_data; +int interpreter_serial_data_len; #ifdef GPIO bool in_gpio_statement; int interpreter_gpio; @@ -464,6 +461,7 @@ int ICACHE_FLASH_ATTR parse_statement(int next_token) { while ((next_token = syn_chk ? next_token : search_token(next_token, "on")) < max_token) { in_topic_statement = false; + in_serial_statement = false; #ifdef GPIO in_gpio_statement = false; #endif @@ -588,6 +586,16 @@ int ICACHE_FLASH_ATTR parse_event(int next_token, bool * happend) { } return next_token + 2; } + + if (is_token(next_token, "serial")) { + lang_debug("event serial\r\n"); + in_serial_statement = true; + + *happend = (interpreter_status == SERIAL_INPUT); + if (*happend) + lang_log("on serial\r\n"); + return next_token + 1; + } #ifdef GPIO if (is_token(next_token, "gpio_interrupt")) { lang_debug("event gpio\r\n"); @@ -632,7 +640,7 @@ int ICACHE_FLASH_ATTR parse_event(int next_token, bool * happend) { return next_token + 1; } #endif - return syntax_error(next_token, "'init', 'mqttconnect', 'topic', 'gpio_interrupt', 'alarm', 'http_response', or 'timer' expected"); + return syntax_error(next_token, "'init', 'mqttconnect', 'topic', 'gpio_interrupt', 'serial', 'alarm', 'http_response', or 'timer' expected"); } int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) { @@ -662,6 +670,20 @@ int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) { } } + else if (is_token(next_token, "serial_out")) { + char *p_char; + int p_len; + Value_Type p_type; + + len_check(1); + if ((next_token = parse_expression(next_token + 1, &p_char, &p_len, &p_type, doit)) == -1) + return -1; + if (doit) { + lang_log("serial_out '%s'\r\n", p_char); + serial_out(p_char); + } + } + else if (is_token(next_token, "system")) { char *p_char; int p_len; @@ -1131,6 +1153,47 @@ int ICACHE_FLASH_ATTR parse_expression(int next_token, char **data, int *data_le } } } + + else if (is_token(next_token, "eatwhite")) { + lang_debug("val eatwhite\r\n"); + + len_check(3); + if (syn_chk && !is_token(next_token+1, "(")) + return syntax_error(next_token+1, "expected '('"); + + char *str_data; + int str_data_len; + Value_Type str_data_type; + // parse path string + if ((next_token = parse_expression(next_token + 2, &str_data, &str_data_len, &str_data_type, doit)) == -1) + return -1; + if (!doit) + str_data_len = 0; + char str[str_data_len+1]; + if (doit) + os_strcpy(str, str_data); + + if (syn_chk && !is_token(next_token, ")")) + return syntax_error(next_token, "expected ')'"); + next_token++; + + if (doit) { + int i, j; + + for (i=0, j=0; i 0 expected"); - num--; next_token++; if (syn_chk && !is_token(next_token, ",")) @@ -1237,7 +1299,7 @@ int ICACHE_FLASH_ATTR parse_expression(int next_token, char **data, int *data_le int i; char *p, *q; - for (i=0, p=q=str_data; i<=num; p++) { + for (i=0, p=q=str; i<=num; p++) { if (*p == ch || *p == '\0') { i++; if (i > num) { @@ -1496,6 +1558,17 @@ int ICACHE_FLASH_ATTR parse_value(int next_token, char **data, int *data_len, Va *data_type = STRING_T; return next_token + 1; } + + else if (is_token(next_token, "$this_serial")) { + lang_debug("val $this_serial\r\n"); + + if (!in_serial_statement) + return syntax_error(next_token, "undefined $this_serial"); + *data = interpreter_serial_data; + *data_len = interpreter_serial_data_len; + *data_type = DATA_T; + return next_token + 1; + } #ifdef GPIO else if (is_token(next_token, "$this_gpio")) { lang_debug("val $this_gpio\r\n"); @@ -1735,6 +1808,19 @@ int ICACHE_FLASH_ATTR interpreter_topic_received(const char *topic, const char * return parse_statement(0); } +int ICACHE_FLASH_ATTR interpreter_serial_input(const char *data, int data_len) { + if (!script_enabled) + return -1; + + lang_debug("interpreter_topic_received\r\n"); + + interpreter_status = SERIAL_INPUT; + interpreter_serial_data = (char *)data; + interpreter_serial_data_len = data_len; + + return parse_statement(0); +} + #ifdef HTTPC void ICACHE_FLASH_ATTR interpreter_http_reply(char *response_body, int http_status, char *response_headers, int body_size) { if (!script_enabled) diff --git a/user/lang.h b/user/lang.h index 23fa1db..202ac59 100644 --- a/user/lang.h +++ b/user/lang.h @@ -3,8 +3,7 @@ #include "mqtt/mqtt_server.h" - -typedef enum {SYNTAX_CHECK, CONFIG, INIT, MQTT_CLIENT_CONNECT, WIFI_CONNECT, TOPIC_LOCAL, TOPIC_REMOTE, TIMER, GPIO_INT, ALARM, HTTP_RESPONSE} Interpreter_Status; +typedef enum {SYNTAX_CHECK, CONFIG, INIT, MQTT_CLIENT_CONNECT, WIFI_CONNECT, TOPIC_LOCAL, TOPIC_REMOTE, TIMER, SERIAL_INPUT, GPIO_INT, ALARM, HTTP_RESPONSE} Interpreter_Status; typedef enum {STRING_T, DATA_T} Value_Type; typedef struct _var_entry_t { @@ -43,6 +42,7 @@ int interpreter_init(); int interpreter_mqtt_connect(void); int interpreter_wifi_connect(void); int interpreter_topic_received(const char *topic, const char *data, int data_len, bool local); +int interpreter_serial_input(const char *data, int data_len); void init_timestamps(uint8_t *curr_time); void check_timestamps(uint8_t *curr_time); diff --git a/user/user_config.h b/user/user_config.h index f938713..118824b 100644 --- a/user/user_config.h +++ b/user/user_config.h @@ -113,7 +113,7 @@ #define MAX_RETAINED_LEN 0x1000 -typedef enum {SIG_DO_NOTHING=0, SIG_START_SERVER=1, SIG_UART0, SIG_TOPIC_RECEIVED, SIG_SCRIPT_LOADED, SIG_SCRIPT_HTTP_LOADED, SIG_CONSOLE_TX_RAW, SIG_CONSOLE_TX, SIG_CONSOLE_RX} USER_SIGNALS; +typedef enum {SIG_DO_NOTHING=0, SIG_START_SERVER=1, SIG_UART0, SIG_TOPIC_RECEIVED, SIG_SCRIPT_LOADED, SIG_SCRIPT_HTTP_LOADED, SIG_CONSOLE_TX_RAW, SIG_CONSOLE_TX, SIG_SERIAL_TX, SIG_CONSOLE_RX} USER_SIGNALS; #define LOCAL_ACCESS 0x01 #define REMOTE_ACCESS 0x02 diff --git a/user/user_main.c b/user/user_main.c index bb18b08..3cb62a0 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -44,6 +44,8 @@ static void user_procTask(os_event_t * events); static os_timer_t ptimer; +static int system_output; + /* Some stats */ uint64_t t_old; @@ -254,7 +256,7 @@ void ICACHE_FLASH_ATTR free_script(void) { } #endif /* SCRIPTED */ -void ICACHE_FLASH_ATTR console_send_response(struct espconn *pespconn) { +void ICACHE_FLASH_ATTR console_send_response(struct espconn *pespconn, bool serial_force) { char payload[MAX_CON_SEND_SIZE]; uint16_t len = ringbuf_bytes_used(console_tx_buffer); @@ -268,7 +270,8 @@ void ICACHE_FLASH_ATTR console_send_response(struct espconn *pespconn) { client_sent_pending = true; } } else { - UART_Send(0, &payload, len); + if (system_output >= SYSTEM_OUTPUT_CMD || serial_force) + UART_Send(0, payload, len); } } @@ -277,6 +280,11 @@ void ICACHE_FLASH_ATTR con_print(uint8_t *str) { system_os_post(user_procTaskPrio, SIG_CONSOLE_TX_RAW, (ETSParam) console_conn); } +void ICACHE_FLASH_ATTR serial_out(uint8_t *str) { + ringbuf_memcpy_into(console_tx_buffer, str, os_strlen(str)); + system_os_post(user_procTaskPrio, SIG_SERIAL_TX, (ETSParam) NULL); +} + bool ICACHE_FLASH_ATTR delete_retainedtopics() { clear_retainedtopics(); blob_zero(RETAINED_SLOT, MAX_RETAINED_LEN); @@ -351,7 +359,7 @@ static void ICACHE_FLASH_ATTR tcp_client_sent_cb(void *arg) { struct espconn *pespconn = (struct espconn *)arg; client_sent_pending = false; - console_send_response(pespconn); + console_send_response(pespconn, false); } @@ -472,10 +480,11 @@ static void ICACHE_FLASH_ATTR user_procTask(os_event_t * events) { { ringbuf_memcpy_into(console_tx_buffer, "CMD>", 4); } + case SIG_CONSOLE_TX_RAW: { struct espconn *pespconn = (struct espconn *)events->par; - console_send_response(pespconn); + console_send_response(pespconn, false); if (pespconn != 0 && remote_console_disconnect) espconn_disconnect(pespconn); @@ -483,10 +492,24 @@ static void ICACHE_FLASH_ATTR user_procTask(os_event_t * events) { } break; + case SIG_SERIAL_TX: + { + console_send_response(NULL, true); + } + break; + case SIG_CONSOLE_RX: { struct espconn *pespconn = (struct espconn *)events->par; - console_handle_command(pespconn); + if (pespconn == 0 && system_output == SYSTEM_OUTPUT_NONE) { + int bytes_count = ringbuf_bytes_used(console_rx_buffer); + char data[bytes_count+1]; + ringbuf_memcpy_from(data, console_rx_buffer, bytes_count); + data[bytes_count] = '\0'; + interpreter_serial_input(data, bytes_count); + } else { + console_handle_command(pespconn); + } } break; @@ -761,11 +784,16 @@ void user_init() { // Set bit rate to config value uart_div_modify(0, UART_CLK_FREQ / config.bit_rate); - if (!config.system_output) { + system_output = config.system_output; + if (system_output < SYSTEM_OUTPUT_INFO) { // all system output to /dev/null system_set_os_print(0); os_install_putc1(void_write_char); } + if (system_output < SYSTEM_OUTPUT_CMD) { + // disable UART echo + UART_Echo(0); + } // Configure the AP and start it, if required