added serial in scripting

master
Martin Ger 2017-11-30 21:54:38 +01:00
rodzic d0bb72c67d
commit ffab86c7e8
15 zmienionych plików z 186 dodań i 36 usunięć

Wyświetl plik

@ -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.

Wyświetl plik

@ -34,8 +34,9 @@ In general, scripts conform to the following BNF:
mqttconnect |
timer <num> |
alarm <num> |
gpio_interrupt <num> (pullup|nopullup) |
topic (local|remote) <topic-id> |
gpio_interrupt <num> (pullup|nopullup) |
serial |
http_response
<action> ::= publish (local|remote) <topic-id> <expr> [retained] |
@ -49,6 +50,7 @@ In general, scripts conform to the following BNF:
gpio_pinmode <num> (input|output) [pullup] |
gpio_out <num> <expr> |
gpio_pwm <num> <num> |
serial_out <expr> |
if <expr> then <action> [else <action>] endif |
print <expr> | println <expr> |
system <expr> |
@ -56,13 +58,14 @@ In general, scripts conform to the following BNF:
<expr> ::= <val> | <val> <op> <expr> | (<expr>) | not (<expr>) |
retained_topic(<expr>) | substr(<expr>,<num>,<num>) |
csvstr(<expr>,<num>,<char>) | json_parse (<expr>,<expr>)
csvstr(<expr>,<num>,<char>) | eatwhite (<expr>) |
json_parse (<expr>,<expr>)
<op> := '=' | '>' | gte | str_ge | str_gte | '+' | '-' | '*' | '|' | div
<val> := <string> | <const> | #<hex-string> | $[any ASCII]* | @<num> |
gpio_in(<num>) | $adc | $this_item | $this_data | $this_gpio |
$this_http_code | $this_http_body | $timestamp | $weekday
gpio_in(<num>) | $adc | $this_item | $this_data | $this_serial |
$this_gpio | $this_http_code | $this_http_body | $timestamp | $weekday
<string> := "[any ASCII]*" | [any ASCII]*
@ -111,6 +114,11 @@ alarm <num>
```
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 <num> (pullup|nopullup)
```
@ -177,6 +185,11 @@ gpio_pwm <num> <num>
```
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 <expr>
```
Sends the given expression to serial port.
```
system <expr>
```
@ -214,7 +227,12 @@ Extracts characters from a string. The two constant numbers give the starting po
```
csvstr(<expr>,<num>,<char>)
```
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(<expr>)
```
Eliminates all whitespaces from a string.
```
json_parse (<expr>,<expr>)

Wyświetl plik

@ -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);

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -1,2 +1,2 @@
65c1e77051ecf1d65eb38017df1096f84b5daa4a 0x00000.bin
a4ce86573e9a5a60ae6665a47d624522e054a0b2 0x10000.bin
f8fcbc261888a7fe6c4d2db2e9b9ce2b13de7a29 0x00000.bin
476c9ae67db3a4117c410d9b664315f83590c9d8 0x10000.bin

Wyświetl plik

@ -229,5 +229,6 @@ void UART_init_console(UartBautRate uart0_br,
ringbuf_t rxbuffer,
ringbuf_t txBuffer);
int UART_Echo(uint8 echo);
#endif

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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);

Wyświetl plik

@ -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<str_data_len; i++){
if (!isspace(str[i])) {
tmp_buffer[j] = str[i];
j++;
}
}
tmp_buffer[j] = '\0';
*data_len = j;
*data = tmp_buffer;
*data_type = STRING_T;
}
}
else if (is_token(next_token, "substr")) {
lang_debug("val substr\r\n");
@ -1216,7 +1279,6 @@ int ICACHE_FLASH_ATTR parse_expression(int next_token, char **data, int *data_le
int16_t num = atoi(my_token[next_token]);
if (num == 0)
return syntax_error(next_token, "value > 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)

Wyświetl plik

@ -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);

Wyświetl plik

@ -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

Wyświetl plik

@ -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