alarm instead of clock events

master
Martin Ger 2017-11-17 00:11:13 +01:00
rodzic 290f29dc5b
commit 2f49d98f0a
8 zmienionych plików z 117 dodań i 66 usunięć

Wyświetl plik

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

Wyświetl plik

@ -1,2 +1,2 @@
3fa016262b70ebe0dd23d7e99c34df7d7e6ae5b7 0x00000.bin
bfe86bb5e16d735add5eda99d77c90598d8b59e3 0x10000.bin
11cc1192f65e4ee3c3b1c447b216d63f59c3a990 0x00000.bin
9c255dfd53c2790e94718e94fb47774791831f3f 0x10000.bin

Wyświetl plik

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

Wyświetl plik

@ -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(&timestamps, sizeof(timestamps));
ts_counter = 0;
#ifdef GPIO
gpio_counter = 0;
#ifdef GPIO_PWM

Wyświetl plik

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

Wyświetl plik

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