kopia lustrzana https://github.com/martin-ger/esp_mqtt
added gpio_pinmode, gpio_in, and brackets
rodzic
9d8f70aeae
commit
8205b1cf04
41
README.md
41
README.md
|
@ -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.
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
484
user/lang.c
484
user/lang.c
|
@ -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(×tamps, 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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue