added gpio_pinmode, gpio_in, and brackets

pull/16/head
Martin Ger 2017-07-26 11:59:11 +02:00
rodzic 9d8f70aeae
commit 8205b1cf04
10 zmienionych plików z 467 dodań i 315 usunięć

Wyświetl plik

@ -3,6 +3,8 @@ A basic MQTT Broker/Client with scripting support on the ESP8266
This program enables the ESP8266 to become the central node in a small distributed IoT system. It implements an MQTT Broker and a simple scripted rule engine with event/action statements that links together the MQTT sensors and actors. It can act as STA, as AP, or as both and it can connect to another MQTT broker (i.e. in the cloud). Here it can act as bridge and forward and rewrite topics in both directions.
Also it can write on local GPIO pins and react on GPIO interrupts.
# Usage
In the user directory there is the main program that serves as a stand-alone MQTT broker, client and bridge. The program starts with the following default configuration:
@ -85,28 +87,40 @@ do
setvar $1=0
setvar $2=0
setvar $3=10
gpio_pinmode 5 input pullup % configure GPIO 5 as input
% Now the events, checked whenever something happens
% Here a remote republish, of any local topic starting with "/test/"
% Here a remote re-publish, of any local topic starting with "/test/"
on topic local /test/#
do
publish remote $this_topic $this_data
% Now a check for local GPIOs
on gpio_interrupt 4 pullup
do
println "New state GPIO 4: " | $this_gpio
publish local /t/gpio4 $this_gpio
% When timer 1 expires, do some stuff
on timer 1
do
% publish a timestamp locally
publish local /t/time $timestamp
% publish the current status of GPIO 5 as two byte binary val
if gpio_in(5)=0 then
publish local /t/gpio5 #0000
endif
if gpio_in(5)=1 then
publish local /t/gpio5 #0001
endif
% Let the LED on GPIO 2 blink
gpio_out 2 $1
setvar $1 = not $1
setvar $1 = not($1)
% Count occurences in var $2
% Count timer 1 ticks in var $2
setvar $2=$2+1
% And if we have reached 10, print that to the console
% And each time if we have reached 10, print that to the console
if $2 = $3 then
println "We have reached "|$2| " at " |$timestamp
setvar $3=$2+10
@ -128,23 +142,30 @@ In general, scripts have the following BNF:
config <param> <value> |
<statement> <statement>
<event> ::= init | timer <num> | clock <timestamp> | topic (local|remote) <topic-id>
<event> ::= init |
mqttconnect |
timer <num> |
clock <timestamp> |
gpio_interrupt <num> (pullup|nopullup) |
topic (local|remote) <topic-id>
<action> ::= publish (local|remote) <topic-id> <val> [retained] |
subscribe (local|remote) <topic-id> |
unsubscribe (local|remote) <topic-id> |
settimer <num> <num> |
setvar $<num> = <expr> |
gpio_pinmode <num> [pullup]
gpio_out <num> <expr> |
if <expr> then <action> endif |
print <expr> | println <expr>
<action> <action>
<expr> ::= <val> <op> <expr> | not <expr>
<expr> ::= <val> <op> <expr> | (<expr>) | not (<expr>)
<op> := '=' | '>' | gte | str_ge | str_gte | '+' | '-' | '/' | '*' | div
<op> := '=' | '>' | gte | str_ge | str_gte | '+' | '-' | '*' | '|' | div
<val> := <string> | <const> | #<hex-string> | $<num> | $this_item | $this_data | $timestamp
<val> := <string> | <const> | #<hex-string> | $<num> | gpio_in(<num>) |
$this_item | $this_data | $this_gpio | $timestamp
<string> := "[any ASCII]*" | [any ASCII]*

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -26,7 +26,8 @@
#include "os_type.h"
#include "osapi.h"
#include "mem.h"
#include "string.h"
/*
char *_strtok_r(char *s, const char *delim, char **last);
char *_strchr(const char *s, int c) {
@ -35,8 +36,8 @@ char *_strchr(const char *s, int c) {
return 0;
return (char *)s;
}
char *_strdup(char *src) {
*/
char ICACHE_FLASH_ATTR *_strdup(char *src) {
char *str;
char *p;
int len = 0;
@ -56,21 +57,21 @@ char *_strdup(char *src) {
* @param aName the topic name string
* @return boolean value indicating whether the topic name is valid
*/
int Topics_isValidName(char *aName) {
int ICACHE_FLASH_ATTR Topics_isValidName(char *aName) {
int rc = true;
char *c = NULL;
int length = os_strlen(aName);
char *hashpos = _strchr(aName, '#'); /* '#' wildcard can be only at the beginning or the end of a topic */
char *hashpos = strchr(aName, '#'); /* '#' wildcard can be only at the beginning or the end of a topic */
if (hashpos != NULL) {
char *second = _strchr(hashpos + 1, '#');
char *second = strchr(hashpos + 1, '#');
if ((hashpos != aName && hashpos != aName + (length - 1)) || second != NULL)
rc = false;
}
/* '#' or '+' only next to a slash separator or end of name */
for (c = "#+"; *c != '\0'; ++c) {
char *pos = _strchr(aName, *c);
char *pos = strchr(aName, *c);
while (pos != NULL) {
if (pos > aName) { /* check previous char is '/' */
if (*(pos - 1) != '/')
@ -80,7 +81,7 @@ int Topics_isValidName(char *aName) {
if (*(pos + 1) != '/')
rc = false;
}
pos = _strchr(pos + 1, *c);
pos = strchr(pos + 1, *c);
}
}
@ -93,7 +94,7 @@ int Topics_isValidName(char *aName) {
* @param astr the character string to reverse
* @return pointer to the reversed string which was reversed in place
*/
char *_strrev(char *astr) {
char ICACHE_FLASH_ATTR *_strrev(char *astr) {
char *forwards = astr;
int len = os_strlen(astr);
if (len > 1) {
@ -112,8 +113,8 @@ char *_strrev(char *astr) {
* @param topic the topic name string
* @return boolean value indicating whether the topic contains a wildcard or not
*/
int Topics_hasWildcards(char *topic) {
return (_strchr(topic, '+') != NULL) || (_strchr(topic, '#') != NULL);
int ICACHE_FLASH_ATTR Topics_hasWildcards(char *topic) {
return (strchr(topic, '+') != NULL) || (strchr(topic, '#') != NULL);
}
/**
@ -122,7 +123,7 @@ int Topics_hasWildcards(char *topic) {
* @param topic a topic name string that must not contain wildcards
* @return boolean value indicating whether topic matches wildTopic
*/
int Topics_matches(char *wildTopic, int wildcards, char *topic) {
int ICACHE_FLASH_ATTR Topics_matches(char *wildTopic, int wildcards, char *topic) {
int rc = false;
char *last1 = NULL, *last2 = NULL;
char *pwild = NULL, *pmatch = NULL;
@ -174,8 +175,8 @@ int Topics_matches(char *wildTopic, int wildcards, char *topic) {
topic = (char *)_strdup(topic);
}
pwild = _strtok_r(wildTopic, TOPIC_LEVEL_SEPARATOR, &last1);
pmatch = _strtok_r(topic, TOPIC_LEVEL_SEPARATOR, &last2);
pwild = strtok_r(wildTopic, TOPIC_LEVEL_SEPARATOR, &last1);
pmatch = strtok_r(topic, TOPIC_LEVEL_SEPARATOR, &last2);
/* Step through the subscription, level by level */
while (pwild != NULL) {
@ -191,8 +192,8 @@ int Topics_matches(char *wildTopic, int wildcards, char *topic) {
break;
} else
break; /* No more tokens to match against further tokens in the wildcard stream... */
pwild = _strtok_r(NULL, TOPIC_LEVEL_SEPARATOR, &last1);
pmatch = _strtok_r(NULL, TOPIC_LEVEL_SEPARATOR, &last2);
pwild = strtok_r(NULL, TOPIC_LEVEL_SEPARATOR, &last1);
pmatch = strtok_r(NULL, TOPIC_LEVEL_SEPARATOR, &last2);
}
/* All tokens up to here matched, and we didn't end in #. If there
@ -201,7 +202,7 @@ int Topics_matches(char *wildTopic, int wildcards, char *topic) {
if (pmatch == NULL && pwild == NULL)
rc = true;
/* Now free the memory allocated in strdup() */
/* Now free the memory allocated in _strdup() */
os_free(wildTopic);
os_free(topic);
exit:
@ -216,7 +217,7 @@ int Topics_matches(char *wildTopic, int wildcards, char *topic) {
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
int test() {
int ICACHE_FLASH_ATTR test() {
int i;
struct {

Wyświetl plik

@ -1,85 +0,0 @@
/*-
* Copyright (c) 1998 Softweyr LLC. All rights reserved.
*
* strtok_r, from Berkeley strtok
* Oct 13, 1998 by Wes Peters <wes@softweyr.com>
*
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notices, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notices, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Softweyr LLC, the
* University of California, Berkeley, and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY SOFTWEYR LLC, THE REGENTS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTWEYR LLC, THE
* REGENTS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "os_type.h"
#include "osapi.h"
#include "mem.h"
char *_strtok_r(char *s, const char *delim, char **last) {
char *spanp, *tok;
int c, sc;
if (s == NULL && (s = *last) == NULL)
return (NULL);
/*
* Skip (span) leading delimiters (s += strspn(s, delim), sort of).
*/
cont:
c = *s++;
for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
if (c == sc)
goto cont;
}
if (c == 0) { /* no non-delimiter characters */
*last = NULL;
return (NULL);
}
tok = s - 1;
/*
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
* Note that delim must have one NUL; we stop if we see that, too.
*/
for (;;) {
c = *s++;
spanp = (char *)delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0)
s = NULL;
else
s[-1] = '\0';
*last = s;
return (tok);
}
} while (sc != 0);
}
/* NOTREACHED */
}

Wyświetl plik

@ -1,9 +1,8 @@
% Config params, overwrite any previous settings from the commandline
config ap_ssid MyAP
config ap_password stupidPassword
config ntp_server 1.de.pool.ntp.org
config mqtt_host martinshome.fritz.box
config speed 160
config ntp_server 1.pool.ntp.org
config mqtt_host 192.168.1.20
% Now the initialization, this is done once after booting
on init
@ -14,41 +13,42 @@ do
setvar $1=0
setvar $2=0
setvar $3=10
% This is done each time the ESP (re-)connects to another MQTT broker
on mqttconnect
do
println "(Re-)connected to client"
subscribe remote $SYS/broker/time
gpio_pinmode 5 input pullup % configure GPIO 5 as input
% Now the events, checked whenever something happens
% Here a remote republish, of any local topic starting with "/test/"
% Here a remote re-publish, of any local topic starting with "/test/"
on topic local /test/#
do
publish remote $this_topic $this_data
% Print the time when published from remote
on topic remote $SYS/broker/time
% Now a check for local GPIOs
on gpio_interrupt 4 pullup
do
println "Remote time: "|$this_data
println "New state GPIO 4: " | $this_gpio
publish local /t/gpio4 $this_gpio
% When timer 1 expires, do some stuff
on timer 1
do
% publish a timestamp locally
publish local /t/time $timestamp
% publish the current status of GPIO 5 as two byte binary val
if gpio_in(5)=0 then
publish local /t/gpio5 #0000
endif
if gpio_in(5)=1 then
publish local /t/gpio5 #0001
endif
% Let the LED on GPIO 2 blink
gpio_out 2 $1
setvar $1 = not $1
setvar $1 = not($1)
% Count occurences in var $2
% Count timer 1 ticks in var $2
setvar $2=$2+1
% And if we have reached 10, print that to the console
% And each time if we have reached 10, print that to the console
if $2 = $3 then
println "We have reached *"|$2| "* at " |$timestamp
println "We have reached "|$2| " at " |$timestamp
setvar $3=$2+10
endif

Wyświetl plik

@ -21,9 +21,6 @@ if (interpreter_status==SYNTAX_CHECK && next_token+(x) >= max_token) \
return syntax_error(next_token+(x), EOT)
#define syn_chk (interpreter_status==SYNTAX_CHECK)
#define ON "\xf0"
#define CONFIG "\xf1"
typedef struct _var_entry_t {
uint8_t data[MAX_VAR_LEN];
uint32_t data_len;
@ -35,17 +32,30 @@ typedef struct _timestamp_entry_t {
bool happened;
} timestamp_entry_t;
#ifdef GPIO
typedef struct _gpio_entry_t {
uint8_t no;
os_timer_t inttimer;
bool val;
} gpio_entry_t;
static gpio_entry_t gpios[MAX_GPIOS];
#endif
char **my_token;
int max_token;
bool script_enabled = false;
bool in_topic_statement;
bool in_gpio_statement;
Interpreter_Status interpreter_status;
char *interpreter_topic;
char *interpreter_data;
int interpreter_data_len;
int interpreter_timer;
char *interpreter_timestamp;
int interpreter_gpio;
int interpreter_gpioval;
int ts_counter;
int gpio_counter;
static os_timer_t timers[MAX_TIMERS];
static var_entry_t vars[MAX_VARS];
@ -101,6 +111,70 @@ void ICACHE_FLASH_ATTR check_timestamps(uint8_t * curr_time) {
}
}
#ifdef GPIO
// GPIO int timer - debouncing
void ICACHE_FLASH_ATTR inttimer_func(void *arg){
gpio_entry_t *my_gpio_entry = (gpio_entry_t *)arg;
interpreter_gpioval = easygpio_inputGet(my_gpio_entry->no);
//os_printf("GPIO %d %d\r\n", my_gpio_entry->no, interpreter_gpioval);
// Reactivate interrupts for GPIO
gpio_pin_intr_state_set(GPIO_ID_PIN(my_gpio_entry->no), GPIO_PIN_INTR_ANYEDGE);
if (script_enabled) {
lang_debug("interpreter GPIO %d %d\r\n", my_gpio_entry->no, interpreter_gpioval);
interpreter_status = GPIO_INT;
interpreter_topic = interpreter_data = "";
interpreter_data_len = 0;
interpreter_gpio = my_gpio_entry->no;
my_gpio_entry->val = interpreter_gpioval;
parse_statement(0);
}
}
// Interrupt handler - this function will be executed on any edge of a GPIO
LOCAL void gpio_intr_handler(void *arg)
{
gpio_entry_t *my_gpio_entry = (gpio_entry_t *)arg;
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
if (gpio_status & BIT(my_gpio_entry->no)) {
// Disable interrupt for GPIO
gpio_pin_intr_state_set(GPIO_ID_PIN(my_gpio_entry->no), GPIO_PIN_INTR_DISABLE);
// Clear interrupt status for GPIO
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & BIT(my_gpio_entry->no));
// Start the timer
os_timer_setfn(&my_gpio_entry->inttimer, inttimer_func, my_gpio_entry);
os_timer_arm(&my_gpio_entry->inttimer, 50, 0);
}
}
void ICACHE_FLASH_ATTR init_gpios() {
int i;
if (!script_enabled)
return;
for (i = 0; i < gpio_counter; i++) {
gpio_pin_intr_state_set(GPIO_ID_PIN(gpios[i].no), GPIO_PIN_INTR_ANYEDGE);
}
}
void ICACHE_FLASH_ATTR stop_gpios() {
int i;
for (i = 0; i < gpio_counter; i++) {
gpio_pin_intr_state_set(GPIO_ID_PIN(gpios[i].no), GPIO_PIN_INTR_DISABLE);
}
}
#endif
void ICACHE_FLASH_ATTR test_tokens(void) {
int i;
@ -119,34 +193,26 @@ int ICACHE_FLASH_ATTR text_into_tokens(char *str) {
lang_debug("lexxer preprocessing (prog_len: %d)\r\n", os_strlen(str));
for (p = q = str; *p != 0; p++) {
// special case "on" keyword - replace by special token ON (0xf0)
if (!in_token && *p == 'o' && *(p + 1) == 'n' && *(p + 2) <= ' ') {
*q++ = '\xf0';
p += 1;
continue;
}
// special case "config" keyword - replace by special token CONFIG (0xf1)
if (!in_token && *p == 'c' && *(p + 1) == 'o' && *(p + 2) == 'n'
&& *(p + 3) == 'f' && *(p + 4) == 'i' && *(p + 5) == 'g' && *(p + 6) <= ' ') {
*q++ = '\xf1';
p += 5;
continue;
}
if (*p == '\\') {
// next char is quoted, copy that - skip this one
if (*(p + 1) != 0)
*q++ = *++p;
} else if (*p == '\"') {
} else if (*p == '"') {
// string quotation
if (in_token)
*q++ = 1; // end of string
else
*q++ = *p; // start of string - copy "
in_token = !in_token;
*q++ = 1;
} else if (*p == '%' && !in_token) {
// comment till eol
for (; *p != 0; p++)
if (*p == '\n')
break;
} else if (*p <= ' ' && !in_token) {
}
else if (*p <= ' ' && !in_token) {
// mark this as whitespace
*q++ = 1;
} else if (*p == '|' && !in_token) {
@ -165,8 +231,14 @@ int ICACHE_FLASH_ATTR text_into_tokens(char *str) {
// mark this as div
*q++ = 6;
} else if (*p == '>' && !in_token) {
// mark this as div
// mark this as gt
*q++ = 7;
} else if (*p == '(' && !in_token) {
// mark this as bracket open
*q++ = 8;
} else if (*p == ')' && !in_token) {
// mark this as bracket close
*q++ = 9;
} else {
*q++ = *p;
}
@ -251,6 +323,16 @@ int ICACHE_FLASH_ATTR text_into_tokens(char *str) {
*p = '\0';
in_token = false;
}
else if (*p == 8) {
my_token[token_count++] = "(";
*p = '\0';
in_token = false;
}
else if (*p == 9) {
my_token[token_count++] = ")";
*p = '\0';
in_token = false;
}
else {
if (!in_token) {
my_token[token_count++] = p;
@ -289,10 +371,6 @@ int ICACHE_FLASH_ATTR syntax_error(int i, char *message) {
os_sprintf(tmp_buffer, "Error (%s) at >>", message);
for (j = i; j < i + 5 && j < max_token; j++) {
int pos = os_strlen(tmp_buffer);
if (is_token(j, ON))
my_token[j] = "on";
if (is_token(j, CONFIG))
my_token[j] = "config";
if (sizeof(tmp_buffer) - pos - 2 > os_strlen(my_token[j])) {
os_sprintf(tmp_buffer + pos, "%s ", my_token[j]);
}
@ -306,11 +384,11 @@ int ICACHE_FLASH_ATTR parse_statement(int next_token) {
uint32_t start = system_get_time();
while ((next_token = syn_chk ? next_token : search_token(next_token, ON)) < max_token) {
while ((next_token = syn_chk ? next_token : search_token(next_token, "on")) < max_token) {
in_topic_statement = false;
in_topic_statement = in_gpio_statement = false;
if (is_token(next_token, ON)) {
if (is_token(next_token, "on")) {
lang_debug("statement on\r\n");
if ((next_token = parse_event(next_token + 1, &event_happened)) == -1)
@ -322,7 +400,7 @@ int ICACHE_FLASH_ATTR parse_statement(int next_token) {
return syntax_error(next_token, "'do' expected");
if ((next_token = parse_action(next_token + 1, event_happened)) == -1)
return -1;
} else if (is_token(next_token, CONFIG)) {
} else if (is_token(next_token, "config")) {
next_token += 3;
} else {
return syntax_error(next_token, "'on' or 'config' expected");
@ -392,8 +470,36 @@ int ICACHE_FLASH_ATTR parse_event(int next_token, bool * happend) {
}
return next_token + 2;
}
#ifdef GPIO
if (is_token(next_token, "gpio_interrupt")) {
lang_debug("event gpio\r\n");
else if (is_token(next_token, "clock")) {
in_gpio_statement = true;
len_check(2);
uint32_t gpio_no = atoi(my_token[next_token + 1]);
if (syn_chk) {
if (gpio_no > 16)
return syntax_error(next_token + 1, "invalid gpio number");
if (!is_token(next_token+2, "pullup") && !is_token(next_token+2, "nopullup"))
return syntax_error(next_token + 2, "expected 'pullup' or 'nopullup'");
int pullup = is_token(next_token+2, "pullup") ? EASYGPIO_PULLUP : EASYGPIO_NOPULL;
if (gpio_counter >= MAX_GPIOS)
return syntax_error(next_token, "too many gpio_interrupt");
gpios[gpio_counter].no = gpio_no;
easygpio_pinMode(gpio_no, pullup, EASYGPIO_INPUT);
easygpio_attachInterrupt(gpio_no, pullup, gpio_intr_handler, &gpios[gpio_counter]);
gpio_counter++;
}
if (interpreter_status == GPIO_INT && interpreter_gpio == gpio_no) {
lang_info("gpio %s interrupt\r\n", my_token[next_token + 1]);
*happend = true;
}
return next_token + 3;
}
#endif
if (is_token(next_token, "clock")) {
lang_debug("event clock\r\n");
len_check(1);
@ -408,13 +514,13 @@ int ICACHE_FLASH_ATTR parse_event(int next_token, bool * happend) {
return next_token + 2;
}
return syntax_error(next_token, "'init', 'mqttconnect', 'topic', 'clock', or 'timer' expected");
return syntax_error(next_token, "'init', 'mqttconnect', 'topic', 'gpio_interrupt', 'clock', or 'timer' expected");
}
int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) {
while (next_token < max_token && !is_token(next_token, ON)
&& !is_token(next_token, CONFIG) && !is_token(next_token, "endif")) {
while (next_token < max_token && !is_token(next_token, "on")
&& !is_token(next_token, "config") && !is_token(next_token, "endif")) {
bool is_nl = false;
if (doit) {
@ -450,7 +556,7 @@ int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) {
len_check(3);
if ((next_token = parse_value(next_token + 2, &topic, &topic_len, &topic_type)) == -1)
return -1;
if ((next_token = parse_value(next_token, &data, &data_len, &data_type)) == -1)
if ((next_token = parse_expression(next_token, &data, &data_len, &data_type, doit)) == -1)
return -1;
if (next_token < max_token && is_token(next_token, "retained")) {
retained = true;
@ -458,8 +564,6 @@ int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) {
}
if (doit) {
if (data_type == STRING_T && data_len > 0)
data_len--;
if (topic_type != STRING_T || Topics_hasWildcards(topic)) {
os_printf("invalid topic string\r\n");
return next_token;
@ -606,6 +710,33 @@ int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) {
}
}
#ifdef GPIO
else if (is_token(next_token, "gpio_pinmode")) {
len_check(2);
uint32_t gpio_no = atoi(my_token[next_token + 1]);
if (gpio_no > 16)
return syntax_error(next_token + 1, "invalid gpio number");
int pullup = EASYGPIO_NOPULL;
int inout = EASYGPIO_OUTPUT;
if (is_token(next_token+2, "input")) {
inout = EASYGPIO_INPUT;
if (is_token(next_token+3, "pullup")) {
pullup = EASYGPIO_PULLUP;
next_token++;
}
else if (syn_chk && !is_token(next_token+2, "output"))
return syntax_error(next_token + 2, "expected 'input' or 'output'");
}
if (doit) {
lang_info("gpio_pinmode %d %s\r\n", gpio_no, inout == EASYGPIO_INPUT ? "input" : "output");
easygpio_pinMode(gpio_no, pullup, inout);
}
next_token += 3;
}
else if (is_token(next_token, "gpio_out")) {
len_check(2);
@ -639,140 +770,150 @@ int ICACHE_FLASH_ATTR parse_action(int next_token, bool doit) {
int ICACHE_FLASH_ATTR parse_expression(int next_token, char **data, int *data_len, Value_Type * data_type, bool doit) {
if (is_token(next_token, "not")) {
len_check(1);
lang_debug("expr not\r\n");
if ((next_token = parse_expression(next_token + 1, data, data_len, data_type, doit)) == -1)
len_check(3);
if (syn_chk && !is_token(next_token+1, "("))
return syntax_error(next_token, "expected '('");
if ((next_token = parse_expression(next_token + 2, data, data_len, data_type, doit)) == -1)
return -1;
if (syn_chk && !is_token(next_token, ")"))
return syntax_error(next_token, "expected ')'");
next_token++;
*data = atoi(*data) ? "0" : "1";
*data_len = 1;
*data_type = STRING_T;
}
#ifdef GPIO
else if (is_token(next_token, "gpio_in")) {
lang_debug("val gpio_in\r\n");
len_check(3);
if (syn_chk && !is_token(next_token+1, "("))
return syntax_error(next_token, "expected '('");
uint32_t gpio_no = atoi(my_token[next_token + 2]);
if (gpio_no > 16)
return syntax_error(next_token+2, "invalid gpio number");
if (syn_chk && !is_token(next_token+3, ")"))
return syntax_error(next_token+3, "expected ')'");
next_token += 4;
*data = "0";
*data_len = 1;
*data_type = STRING_T;
if (easygpio_inputGet(gpio_no)) {
*data = "1";
}
}
#endif
else if (is_token(next_token, "(")) {
lang_debug("expr (\r\n");
len_check(2);
if ((next_token = parse_expression(next_token + 1, data, data_len, data_type, doit)) == -1)
return -1;
if (!is_token(next_token, ")"))
return syntax_error(next_token, "expected ')'");
next_token++;
}
else {
if ((next_token = parse_value(next_token, data, data_len, data_type)) == -1)
return -1;
}
// if it is not some kind of binary operation - finished
if (!is_token(next_token, "=")
&& !is_token(next_token, "+")
&& !is_token(next_token, "-")
&& !is_token(next_token, "*")
&& !is_token(next_token, "div")
&& !is_token(next_token, "|")
&& !is_token(next_token, ">")
&& !is_token(next_token, "gte")
&& !is_token(next_token, "str_gt")
&& !is_token(next_token, "str_gte"))
return next_token;
// if it is not some kind of binary operation - finished
if (!is_token(next_token, "=")
&& !is_token(next_token, "+")
&& !is_token(next_token, "-")
&& !is_token(next_token, "*")
&& !is_token(next_token, "div")
&& !is_token(next_token, "|")
&& !is_token(next_token, ">")
&& !is_token(next_token, "gte")
&& !is_token(next_token, "str_gt")
&& !is_token(next_token, "str_gte"))
return next_token;
// okay, it is an operation
int op = next_token;
// okay, it is an operation
int op = next_token;
char *r_data;
int r_data_len;
Value_Type r_data_type;
char *r_data;
int r_data_len;
Value_Type r_data_type;
// parse second operand
if ((next_token = parse_expression(next_token + 1, &r_data, &r_data_len, &r_data_type, doit)) == -1)
return -1;
//os_printf("l:%s(%d) r:%s(%d)\r\n", *data, *data_len, r_data, r_data_len);
// parse second operand
if ((next_token = parse_expression(next_token + 1, &r_data, &r_data_len, &r_data_type, doit)) == -1)
return -1;
//os_printf("l:%s(%d) r:%s(%d)\r\n", *data, *data_len, r_data, r_data_len);
if (!doit)
return next_token;
if (!doit)
return next_token;
*data_type = STRING_T;
if (is_token(op, "=")) {
*data = os_strcmp(*data, r_data) ? "0" : "1";
} else if (is_token(op, "+")) {
os_sprintf(tmp_buffer, "%d", atoi(*data) + atoi(r_data));
*data = tmp_buffer;
*data_len = os_strlen(tmp_buffer);
} else if (is_token(op, "-")) {
os_sprintf(tmp_buffer, "%d", atoi(*data) - atoi(r_data));
*data = tmp_buffer;
*data_len = os_strlen(tmp_buffer);
} else if (is_token(op, "*")) {
os_sprintf(tmp_buffer, "%d", atoi(*data) * atoi(r_data));
*data = tmp_buffer;
*data_len = os_strlen(tmp_buffer);
} else if (is_token(op, "div")) {
os_sprintf(tmp_buffer, "%d", atoi(*data) / atoi(r_data));
*data = tmp_buffer;
*data_len = os_strlen(tmp_buffer);
} else if (is_token(op, "|")) {
uint16_t len = os_strlen(*data) + os_strlen(r_data);
char catbuf[len+1];
os_sprintf(catbuf, "%s%s", *data, r_data);
if (len > sizeof(tmp_buffer)-1) {
len = sizeof(tmp_buffer);
catbuf[len] = '\0';
}
*data_len = len;
os_memcpy(tmp_buffer, catbuf, *data_len + 1);
*data = tmp_buffer;
} else if (is_token(op, ">")) {
*data = atoi(*data) > atoi(r_data) ? "1" : "0";
*data_len = 1;
} else if (is_token(op, "gte")) {
*data = atoi(*data) >= atoi(r_data) ? "1" : "0";
*data_len = 1;
} else if (is_token(op, "str_gt")) {
*data = os_strcmp(*data, r_data) > 0 ? "1" : "0";
*data_len = 1;
} else if (is_token(op, "str_gte")) {
*data = os_strcmp(*data, r_data) >= 0 ? "1" : "0";
*data_len = 1;
*data_type = STRING_T;
if (is_token(op, "=")) {
*data = os_strcmp(*data, r_data) ? "0" : "1";
} else if (is_token(op, "+")) {
os_sprintf(tmp_buffer, "%d", atoi(*data) + atoi(r_data));
*data = tmp_buffer;
*data_len = os_strlen(tmp_buffer);
} else if (is_token(op, "-")) {
os_sprintf(tmp_buffer, "%d", atoi(*data) - atoi(r_data));
*data = tmp_buffer;
*data_len = os_strlen(tmp_buffer);
} else if (is_token(op, "*")) {
os_sprintf(tmp_buffer, "%d", atoi(*data) * atoi(r_data));
*data = tmp_buffer;
*data_len = os_strlen(tmp_buffer);
} else if (is_token(op, "div")) {
os_sprintf(tmp_buffer, "%d", atoi(*data) / atoi(r_data));
*data = tmp_buffer;
*data_len = os_strlen(tmp_buffer);
} else if (is_token(op, "|")) {
uint16_t len = os_strlen(*data) + os_strlen(r_data);
char catbuf[len+1];
os_sprintf(catbuf, "%s%s", *data, r_data);
if (len > sizeof(tmp_buffer)-1) {
len = sizeof(tmp_buffer);
catbuf[len] = '\0';
}
*data_len = len;
os_memcpy(tmp_buffer, catbuf, *data_len + 1);
*data = tmp_buffer;
} else if (is_token(op, ">")) {
*data = atoi(*data) > atoi(r_data) ? "1" : "0";
*data_len = 1;
} else if (is_token(op, "gte")) {
*data = atoi(*data) >= atoi(r_data) ? "1" : "0";
*data_len = 1;
} else if (is_token(op, "str_gt")) {
*data = os_strcmp(*data, r_data) > 0 ? "1" : "0";
*data_len = 1;
} else if (is_token(op, "str_gte")) {
*data = os_strcmp(*data, r_data) >= 0 ? "1" : "0";
*data_len = 1;
}
return next_token;
}
int ICACHE_FLASH_ATTR parse_value(int next_token, char **data, int *data_len, Value_Type * data_type) {
if (is_token(next_token, "$this_data")) {
lang_debug("val $this_data\r\n");
if (!in_topic_statement)
return syntax_error(next_token, "undefined $this_data");
*data = interpreter_data;
*data_len = interpreter_data_len;
*data_type = DATA_T;
return next_token + 1;
}
if (my_token[next_token][0] == '"') {
lang_debug("val str(%s)\r\n", &my_token[next_token][1]);
else if (is_token(next_token, "$this_topic")) {
lang_debug("val $this_topic\r\n");
if (!in_topic_statement)
return syntax_error(next_token, "undefined $this_topic");
*data = interpreter_topic;
*data_len = os_strlen(interpreter_topic) + 1;
*data = &my_token[next_token][1];
*data_len = os_strlen(&my_token[next_token][1]);
*data_type = STRING_T;
return next_token + 1;
}
#ifdef NTP
else if (is_token(next_token, "$timestamp")) {
lang_debug("val $timestamp\r\n");
if (ntp_sync_done())
*data = get_timestr();
else
*data = "99:99:99";
*data_len = os_strlen(*data) + 1;
*data_type = STRING_T;
return next_token + 1;
}
#endif
/* else if (my_token[next_token][0] == '\'') {
char *p = &(my_token[next_token][1]);
int *b = (int*)&val_buf[0];
*b = atoi(htonl(p));
*data = val_buf;
*data_len = sizeof(int);
return next_token+1;
}
*/
else if (my_token[next_token][0] == '$' && my_token[next_token][1] <= '9') {
lang_debug("val var\r\n");
uint32_t var_no = atoi(&(my_token[next_token][1]));
if (var_no == 0 || var_no > MAX_VARS)
return syntax_error(next_token + 1, "invalid var number");
@ -785,20 +926,19 @@ int ICACHE_FLASH_ATTR parse_value(int next_token, char **data, int *data_len, Va
}
else if (my_token[next_token][0] == '#') {
lang_debug("val hexbinary\r\n");
// Convert it in place to binary once during syntax check
// Format: Byte 0: '#', Byte 1: len (max 255), Byte 2 to len: binary data, Byte len+1: 0
if (syn_chk) {
int i, j, len = os_strlen(my_token[next_token]);
int i, j, len = os_strlen(my_token[next_token])-1;
uint8_t a, *p = &(my_token[next_token][1]);
if (len < 3)
return syntax_error(next_token, "hexbinary too short");
if (len == 0 || len % 2)
return syntax_error(next_token, "number of hexdigits must be multiple of 2");
if (len > 511)
return syntax_error(next_token, "hexbinary too long");
for (i = 0, j = 1; i < len - 1; i += 2, j++) {
for (i = 0, j = 1; i < len; i += 2, j++) {
if (p[i] <= '9')
a = p[i] - '0';
else
@ -811,18 +951,67 @@ int ICACHE_FLASH_ATTR parse_value(int next_token, char **data, int *data_len, Va
p[j] = a;
}
p[j] = '\0';
p[0] = (uint8_t) j - 2;
p[0] = (uint8_t) j - 1;
}
*data = &my_token[next_token][2];
*data_len = my_token[next_token][1];
*data_type = DATA_T;
return next_token + 1;
}
else if (is_token(next_token, "$this_data")) {
lang_debug("val $this_data\r\n");
if (!in_topic_statement)
return syntax_error(next_token, "undefined $this_data");
*data = interpreter_data;
*data_len = interpreter_data_len;
*data_type = DATA_T;
return next_token + 1;
}
else if (is_token(next_token, "$this_topic")) {
lang_debug("val $this_topic\r\n");
if (!in_topic_statement)
return syntax_error(next_token, "undefined $this_topic");
*data = interpreter_topic;
*data_len = os_strlen(interpreter_topic);
*data_type = STRING_T;
return next_token + 1;
}
#ifdef GPIO
else if (is_token(next_token, "$this_gpio")) {
lang_debug("val $this_gpio\r\n");
if (!in_gpio_statement)
return syntax_error(next_token, "undefined $this_gpio");
*data = interpreter_gpioval == 0 ? "0" : "1";
*data_len = 1;
*data_type = STRING_T;
return next_token + 1;
}
#endif
#ifdef NTP
else if (is_token(next_token, "$timestamp")) {
lang_debug("val $timestamp\r\n");
if (ntp_sync_done())
*data = get_timestr();
else
*data = "99:99:99";
*data_len = os_strlen(*data);
*data_type = STRING_T;
return next_token + 1;
}
#endif
else {
lang_debug("val num/str(%s)\r\n", my_token[next_token]);
*data = my_token[next_token];
*data_len = os_strlen(my_token[next_token]) + 1;
*data_len = os_strlen(my_token[next_token]);
*data_type = STRING_T;
return next_token + 1;
}
@ -837,13 +1026,14 @@ int ICACHE_FLASH_ATTR interpreter_syntax_check() {
interpreter_data_len = 0;
os_bzero(&timestamps, sizeof(timestamps));
ts_counter = 0;
gpio_counter = 0;
return parse_statement(0);
}
int ICACHE_FLASH_ATTR interpreter_config() {
int next_token = 0;
while ((next_token = search_token(next_token, CONFIG)) < max_token) {
while ((next_token = search_token(next_token, "config")) < max_token) {
lang_debug("statement config\r\n");
len_check(2);

Wyświetl plik

@ -5,7 +5,7 @@ extern bool mqtt_enabled, mqtt_connected;
uint8_t tmp_buffer[128];
uint32_t loop_time;
typedef enum {SYNTAX_CHECK, CONFIG, INIT, MQTT_CLIENT_CONNECT, TOPIC_LOCAL, TOPIC_REMOTE, TIMER, CLOCK} Interpreter_Status;
typedef enum {SYNTAX_CHECK, CONFIG, INIT, MQTT_CLIENT_CONNECT, TOPIC_LOCAL, TOPIC_REMOTE, TIMER, GPIO_INT, CLOCK} Interpreter_Status;
typedef enum {STRING_T, DATA_T} Value_Type;
int text_into_tokens(char *str);
@ -29,3 +29,6 @@ int interpreter_topic_received(const char *topic, const char *data, int data_len
void init_timestamps(uint8_t *curr_time);
void check_timestamps(uint8_t *curr_time);
void init_gpios();
void stop_gpios();

Wyświetl plik

@ -36,9 +36,10 @@ typedef enum {SIG_DO_NOTHING=0, SIG_START_SERVER=1, SIG_UART0, SIG_SCRIPT_LOADED
#define MAX_SCRIPT_SIZE 0x1000
#define MAX_TIMERS 4
#define MAX_GPIOS 3
#define MAX_VARS 6
#define MAX_VAR_LEN 64
#define MAX_TIMESTAMPS 6
#define MAX_TIMESTAMPS 6
//
// Define this if you want to have NTP support.

Wyświetl plik

@ -2,7 +2,6 @@
#include "mem.h"
#include "ets_sys.h"
#include "osapi.h"
#include "gpio.h"
#include "os_type.h"
#include "user_interface.h"
@ -194,53 +193,65 @@ void ICACHE_FLASH_ATTR free_script(void) {
}
#endif /* SCRIPTED */
int parse_str_into_tokens(char *str, char **tokens, int max_tokens) {
char *p, *q;
int token_count = 0;
bool in_token = false;
int ICACHE_FLASH_ATTR parse_str_into_tokens(char *str, char **tokens, int max_tokens)
{
char *p, *q, *end;
int token_count = 0;
bool in_token = false;
// preprocessing
for (p = q = str; *p != 0; p++) {
if (*p == '\\') {
// next char is quoted, copy it skip, this one
if (*(p + 1) != 0)
*q++ = *++p;
// preprocessing
for (p = q = str; *p != 0; p++) {
if (*(p) == '%' && *(p+1) != 0 && *(p+2) != 0) {
// quoted hex
uint8_t a;
p++;
if (*p <= '9')
a = *p - '0';
else
a = toupper(*p) - 'A' + 10;
a <<= 4;
p++;
if (*p <= '9')
a += *p - '0';
else
a += toupper(*p) - 'A' + 10;
*q++ = a;
} else if (*p == '\\' && *(p+1) != 0) {
// next char is quoted - just copy it, skip this one
*q++ = *++p;
} else if (*p == 8) {
// backspace - delete previous char
if (q != str)
q--;
// backspace - delete previous char
if (q != str) q--;
} else if (*p <= ' ') {
// mark this as whitespace
*q++ = 1;
// mark this as whitespace
*q++ = 0;
} else {
*q++ = *p;
*q++ = *p;
}
}
}
*q = 0;
end = q;
*q = 0;
// cut into tokens
for (p = str; *p != 0; p++) {
if (*p == 1) {
if (in_token) {
*p = 0;
// cut into tokens
for (p = str; p != end; p++) {
if (*p == 0) {
if (in_token) {
in_token = false;
}
}
} else {
if (*p & 0x80)
*p &= 0x7f;
if (!in_token) {
if (!in_token) {
tokens[token_count++] = p;
if (token_count == max_tokens)
return token_count;
return token_count;
in_token = true;
}
}
}
}
return token_count;
}
return token_count;
}
void console_send_response(struct espconn *pespconn) {
void ICACHE_FLASH_ATTR console_send_response(struct espconn *pespconn) {
char payload[MAX_CON_SEND_SIZE];
uint16_t len = ringbuf_bytes_used(console_tx_buffer);
@ -586,6 +597,9 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) {
}
if (strcmp(tokens[1], "delete") == 0) {
#ifdef GPIO
stop_gpios();
#endif
script_enabled = false;
if (my_script != NULL)
free_script();
@ -600,6 +614,9 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) {
goto command_handled;
}
// delete and disable existing script
#ifdef GPIO
stop_gpios();
#endif
script_enabled = false;
if (my_script != NULL)
free_script();
@ -1175,8 +1192,9 @@ void ICACHE_FLASH_ATTR user_init() {
console_rx_buffer = ringbuf_new(MAX_CON_CMD_SIZE);
console_tx_buffer = ringbuf_new(MAX_CON_SEND_SIZE);
#ifdef GPIO
gpio_init();
#endif
init_long_systime();
UART_init_console(BIT_RATE_115200, 0, console_rx_buffer, console_tx_buffer);
@ -1284,6 +1302,9 @@ void ICACHE_FLASH_ATTR user_init() {
#ifdef SCRIPTED
timestamps_init = false;
interpreter_init();
#ifdef GPIO
init_gpios();
#endif
#endif
// Start the timer