kopia lustrzana https://github.com/martin-ger/esp_mqtt
alarm instead of clock events
rodzic
290f29dc5b
commit
2f49d98f0a
27
SCRIPTING.md
27
SCRIPTING.md
|
@ -33,7 +33,7 @@ In general, scripts conform to the following BNF:
|
|||
wificonnect |
|
||||
mqttconnect |
|
||||
timer <num> |
|
||||
clock <timestamp> |
|
||||
alarm <num> |
|
||||
gpio_interrupt <num> (pullup|nopullup) |
|
||||
topic (local|remote) <topic-id> |
|
||||
http_response
|
||||
|
@ -42,6 +42,7 @@ In general, scripts conform to the following BNF:
|
|||
subscribe (local|remote) <topic-id> |
|
||||
unsubscribe (local|remote) <topic-id> |
|
||||
settimer <num> <expr> |
|
||||
setalarm <num> <expr> |
|
||||
setvar ($[any ASCII]* | @<num>) = <expr> |
|
||||
http_get <expr> |
|
||||
http_post <expr> <expr> |
|
||||
|
@ -65,8 +66,6 @@ In general, scripts conform to the following BNF:
|
|||
<string> := "[any ASCII]*" | [any ASCII]*
|
||||
|
||||
<num> := [0-9]*
|
||||
|
||||
<timestamp> := hh:mm:ss
|
||||
```
|
||||
|
||||
## Statements
|
||||
|
@ -107,9 +106,9 @@ timer <num>
|
|||
This event happens when the timer with the given number expires. Timers are set in millisecond units, but their expiration might be delayed by other interrupts and events, e.g. network traffic. Thus their accuracy is limited. Timers are not reloading automatically. I.e. if you need a permanently running timer, reload the expired timer in the "on timer" clause.
|
||||
|
||||
```
|
||||
clock <timestamp>
|
||||
alarm <num>
|
||||
```
|
||||
This event happens when the time-of-day value given in the event has been reached. It happens once per day. Timestamps 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.
|
||||
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.
|
||||
|
||||
```
|
||||
gpio_interrupt <num> (pullup|nopullup)
|
||||
|
@ -138,6 +137,11 @@ settimer <num> <expr>
|
|||
```
|
||||
(Re-)initializes the timer "num" with a value given in milliseconds. Timers are not reloading automatically. I.e. if you need a permanently running timer, reload the expired timer in the "on timer" clause.
|
||||
|
||||
```
|
||||
setalarm <num> <expr>
|
||||
```
|
||||
(Re-)initializes the alarm "num" with a value given as "hh:mm:ss" (it can also be just "hh:mm" or even "hh"). Alarm values are compared lexicographically with the current clock time.
|
||||
|
||||
```
|
||||
setvar ($[any ASCII]* | @<num>) = <expr>
|
||||
```
|
||||
|
@ -267,7 +271,7 @@ This operator concatenates the left and the right operator as strings. Useful e.
|
|||
Comments start with a ’%' anywhere in a line and reach until the end of this line.
|
||||
|
||||
## Sample
|
||||
Here is a demo of a script to give you an idea of the power of the scripting feature. This script controls a Sonoff switch module. It connects to a remote MQTT broker and in parallel offers locally its own. The device has a number stored in the variable $device_number. On both brokers it subscribes to a topic named '/martinshome/switch/($device_number)/command', where it receives commands, and it publishes the topic '/martinshome/switch/($device_number)/status' with the current state of the switch relay. It understands the commands 'on','off', 'toggle', and 'blink'. Blinking is realized via a timer event. Local status is stored in the two variables $relay_status and $blink (blinking on/off). The 'on gpio_interrupt' clause reacts on pressing the pushbutton of the Sonoff and simply toggles the switch (and stops blinking). The last two 'on clock' clauses implement a daily on and off period:
|
||||
Here is a demo of a script to give you an idea of the power of the scripting feature. This script controls a Sonoff switch module. It connects to a remote MQTT broker and in parallel offers locally its own. The device has a number stored in the variable $device_number. On both brokers it subscribes to a topic named '/martinshome/switch/($device_number)/command', where it receives commands, and it publishes the topic '/martinshome/switch/($device_number)/status' with the current state of the switch relay. It understands the commands 'on','off', 'toggle', and 'blink'. Blinking is realized via a timer event. Local status is stored in the two variables $relay_status and $blink (blinking on/off). The 'on gpio_interrupt' clause reacts on pressing the pushbutton of the Sonoff and simply toggles the switch (and stops blinking):
|
||||
|
||||
```
|
||||
% Config params, overwrite any previous settings from the commandline
|
||||
|
@ -377,17 +381,6 @@ do
|
|||
publish local $command_topic "toggle"
|
||||
settimer 1 500
|
||||
endif
|
||||
|
||||
|
||||
% Switch on in the evening
|
||||
on clock 19:30:00
|
||||
do
|
||||
publish local $command_topic "on"
|
||||
|
||||
% Switch off at night
|
||||
on clock 01:00:00
|
||||
do
|
||||
publish local $command_topic "off"
|
||||
```
|
||||
|
||||
|
||||
|
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -1,2 +1,2 @@
|
|||
3fa016262b70ebe0dd23d7e99c34df7d7e6ae5b7 0x00000.bin
|
||||
bfe86bb5e16d735add5eda99d77c90598d8b59e3 0x10000.bin
|
||||
11cc1192f65e4ee3c3b1c447b216d63f59c3a990 0x00000.bin
|
||||
9c255dfd53c2790e94718e94fb47774791831f3f 0x10000.bin
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
% Timeclock example for using alarms
|
||||
% Publish time values (format hh:mm:ss) on the "/timeclock/on_time" and "/timeclock/on_time" topics
|
||||
% to the brocker and the build-in LED will switch at the given times.
|
||||
|
||||
% Config params, overwrite any previous settings from the commandline
|
||||
config ntp_server 1.pool.ntp.org
|
||||
config ntp_timezone 1
|
||||
|
||||
% Now the initialization, this is done once after booting
|
||||
on init
|
||||
do
|
||||
% Command prefix
|
||||
setvar $command_prefix = "/timeclock"
|
||||
% Command topic
|
||||
setvar $command_topic = $command_prefix | "/#"
|
||||
|
||||
% local subscriptions once in 'init'
|
||||
subscribe local $command_topic
|
||||
|
||||
% Now the events, checked whenever something happens
|
||||
|
||||
% Is there a remote command?
|
||||
on topic local $command_topic
|
||||
do
|
||||
if ($this_topic = $command_prefix | "/on-time") then
|
||||
println "Received on command: " | $this_data
|
||||
setalarm 1 $this_data
|
||||
endif
|
||||
|
||||
if ($this_topic = $command_prefix | "/off-time") then
|
||||
println "Received off command: " | $this_data
|
||||
setalarm 2 $this_data
|
||||
endif
|
||||
|
||||
if ($this_topic = $command_prefix | "/switch") then
|
||||
println "Received switch command: " | $this_data
|
||||
gpio_out 2 not($this_data)
|
||||
endif
|
||||
|
||||
% Switch on
|
||||
on alarm 1
|
||||
do
|
||||
println "Switch on at " | $timestamp
|
||||
gpio_out 2 0
|
||||
|
||||
% Switch off
|
||||
on alarm 2
|
||||
do
|
||||
println "Switch off at " | $timestamp
|
||||
gpio_out 2 1
|
94
user/lang.c
94
user/lang.c
|
@ -40,9 +40,10 @@ if (interpreter_status==SYNTAX_CHECK && next_token+(x) >= max_token) \
|
|||
return syntax_error(next_token+(x), EOT)
|
||||
#define syn_chk (interpreter_status==SYNTAX_CHECK)
|
||||
|
||||
typedef enum {INVALID = 0, HAPPENED, NOT_HAPPENED, UNDEFINED} Alarm_State;
|
||||
typedef struct _timestamp_entry_t {
|
||||
uint8_t *ts;
|
||||
bool happened;
|
||||
uint8_t ts[9];
|
||||
Alarm_State state;
|
||||
} timestamp_entry_t;
|
||||
|
||||
#ifdef GPIO
|
||||
|
@ -70,8 +71,7 @@ char *interpreter_topic;
|
|||
char *interpreter_data;
|
||||
int interpreter_data_len;
|
||||
int interpreter_timer;
|
||||
char *interpreter_timestamp;
|
||||
int ts_counter;
|
||||
int interpreter_timestamp;
|
||||
#ifdef GPIO
|
||||
bool in_gpio_statement;
|
||||
int interpreter_gpio;
|
||||
|
@ -121,37 +121,33 @@ static void ICACHE_FLASH_ATTR lang_timers_timeout(void *arg) {
|
|||
parse_statement(0);
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR init_timestamps(uint8_t * curr_time) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ts_counter; i++) {
|
||||
if (os_strcmp(curr_time, timestamps[i].ts) >= 0) {
|
||||
timestamps[i].happened = true;
|
||||
} else {
|
||||
timestamps[i].happened = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_FLASH_ATTR check_timestamps(uint8_t * curr_time) {
|
||||
int i;
|
||||
|
||||
if (!script_enabled)
|
||||
return;
|
||||
for (i = 0; i < ts_counter; i++) {
|
||||
|
||||
for (i = 0; i < MAX_TIMESTAMPS; i++) {
|
||||
if (timestamps[i].state == INVALID)
|
||||
continue;
|
||||
|
||||
if (os_strcmp(curr_time, timestamps[i].ts) >= 0) {
|
||||
if (timestamps[i].happened)
|
||||
if (timestamps[i].state == UNDEFINED) {
|
||||
timestamps[i].state = HAPPENED;
|
||||
continue;
|
||||
}
|
||||
if (timestamps[i].state == HAPPENED)
|
||||
continue;
|
||||
lang_debug("timerstamp %s happened\r\n", timestamps[i].ts);
|
||||
|
||||
interpreter_topic = interpreter_data = "";
|
||||
interpreter_data_len = 0;
|
||||
interpreter_status = CLOCK;
|
||||
interpreter_timestamp = timestamps[i].ts;
|
||||
interpreter_status = ALARM;
|
||||
interpreter_timestamp = i;
|
||||
parse_statement(0);
|
||||
timestamps[i].happened = true;
|
||||
timestamps[i].state = HAPPENED;
|
||||
} else {
|
||||
timestamps[i].happened = false;
|
||||
timestamps[i].state = NOT_HAPPENED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -578,6 +574,20 @@ int ICACHE_FLASH_ATTR parse_event(int next_token, bool * happend) {
|
|||
}
|
||||
return next_token + 2;
|
||||
}
|
||||
|
||||
if (is_token(next_token, "alarm")) {
|
||||
lang_debug("event alarm\r\n");
|
||||
|
||||
len_check(1);
|
||||
uint32_t timer_no = atoi(my_token[next_token + 1]);
|
||||
if (timer_no == 0 || timer_no > MAX_TIMESTAMPS)
|
||||
return syntax_error(next_token + 1, "invalid alarm number");
|
||||
if (interpreter_status == ALARM && interpreter_timestamp == --timer_no) {
|
||||
lang_log("on alarm %s\r\n", my_token[next_token + 1]);
|
||||
*happend = true;
|
||||
}
|
||||
return next_token + 2;
|
||||
}
|
||||
#ifdef GPIO
|
||||
if (is_token(next_token, "gpio_interrupt")) {
|
||||
lang_debug("event gpio\r\n");
|
||||
|
@ -611,22 +621,6 @@ int ICACHE_FLASH_ATTR parse_event(int next_token, bool * happend) {
|
|||
return next_token + 3;
|
||||
}
|
||||
#endif
|
||||
if (is_token(next_token, "clock")) {
|
||||
lang_debug("event clock\r\n");
|
||||
|
||||
len_check(1);
|
||||
if (syn_chk && os_strlen(my_token[next_token + 1]) != 8)
|
||||
return syntax_error(next_token, "invalid timestamp");
|
||||
if (syn_chk) {
|
||||
if (ts_counter >= MAX_TIMESTAMPS)
|
||||
return syntax_error(next_token, "too many timestamps");
|
||||
timestamps[ts_counter++].ts = my_token[next_token + 1];
|
||||
}
|
||||
*happend = (interpreter_status == CLOCK && os_strcmp(interpreter_timestamp, my_token[next_token + 1]) == 0);
|
||||
if (*happend)
|
||||
lang_log("on clock %s\r\n", my_token[next_token + 1]);
|
||||
return next_token + 2;
|
||||
}
|
||||
#ifdef HTTPC
|
||||
if (is_token(next_token, "http_response")) {
|
||||
lang_debug("event http_response\r\n");
|
||||
|
@ -638,7 +632,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', 'clock', 'http_response', or 'timer' expected");
|
||||
return syntax_error(next_token, "'init', 'mqttconnect', 'topic', 'gpio_interrupt', 'alarm', 'http_response', or 'timer' expected");
|
||||
}
|
||||
|
||||
int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) {
|
||||
|
@ -848,6 +842,27 @@ int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) {
|
|||
}
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "setalarm")) {
|
||||
len_check(2);
|
||||
uint32_t alarm_no = atoi(my_token[next_token + 1]);
|
||||
if (alarm_no == 0 || alarm_no > MAX_TIMESTAMPS)
|
||||
return syntax_error(next_token + 1, "invalid alarm number");
|
||||
|
||||
char *timer_char;
|
||||
int timer_len;
|
||||
Value_Type timer_type;
|
||||
if ((next_token = parse_expression(next_token + 2, &timer_char, &timer_len, &timer_type, doit)) == -1)
|
||||
return -1;
|
||||
|
||||
if (doit) {
|
||||
lang_log("setalarm %d %d\r\n", alarm_no, timer_char);
|
||||
|
||||
alarm_no--;
|
||||
os_strncpy(timestamps[alarm_no].ts, timer_char, 9);
|
||||
timestamps[alarm_no].state = UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
else if (is_token(next_token, "setvar")) {
|
||||
len_check(3);
|
||||
bool flash_var = false;
|
||||
|
@ -1484,7 +1499,6 @@ int ICACHE_FLASH_ATTR interpreter_syntax_check() {
|
|||
interpreter_topic = interpreter_data = "";
|
||||
interpreter_data_len = 0;
|
||||
os_bzero(×tamps, sizeof(timestamps));
|
||||
ts_counter = 0;
|
||||
#ifdef GPIO
|
||||
gpio_counter = 0;
|
||||
#ifdef GPIO_PWM
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "mqtt/mqtt_server.h"
|
||||
|
||||
|
||||
typedef enum {SYNTAX_CHECK, CONFIG, INIT, MQTT_CLIENT_CONNECT, WIFI_CONNECT, TOPIC_LOCAL, TOPIC_REMOTE, TIMER, GPIO_INT, CLOCK, HTTP_RESPONSE} Interpreter_Status;
|
||||
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 {STRING_T, DATA_T} Value_Type;
|
||||
|
||||
typedef struct _var_entry_t {
|
||||
|
|
|
@ -41,7 +41,6 @@ struct espconn *downloadCon;
|
|||
struct espconn *scriptcon;
|
||||
uint8_t *load_script;
|
||||
uint32_t load_size;
|
||||
bool timestamps_init;
|
||||
#endif
|
||||
|
||||
/* System Task, for signals refer to user_config.h */
|
||||
|
@ -1406,10 +1405,6 @@ void ICACHE_FLASH_ATTR timer_func(void *arg) {
|
|||
uint8_t *timestr = get_timestr();
|
||||
MQTT_local_publish("$SYS/broker/time", get_timestr(config.ntp_timezone), 8, 0, 0);
|
||||
#ifdef SCRIPTED
|
||||
if (!timestamps_init) {
|
||||
init_timestamps(timestr);
|
||||
timestamps_init = true;
|
||||
}
|
||||
check_timestamps(timestr);
|
||||
#endif
|
||||
}
|
||||
|
@ -1833,7 +1828,6 @@ void user_init() {
|
|||
system_os_task(user_procTask, user_procTaskPrio, user_procTaskQueue, user_procTaskQueueLen);
|
||||
|
||||
#ifdef SCRIPTED
|
||||
timestamps_init = false;
|
||||
interpreter_init();
|
||||
#endif
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue