diff --git a/Makefile b/Makefile index b4edfba..414366a 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ EXTRA_INCDIR = include LIB_MODULES = mqtt # libraries used in this project, mainly provided by the SDK -LIBS = c gcc hal pp phy net80211 lwip wpa main ssl +LIBS = c gcc hal pp phy net80211 lwip wpa main ssl json # compiler flags using during compilation of source files CFLAGS = -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -DUSE_OPTIMIZE_PRINTF -Desp8266 diff --git a/README.md b/README.md index 7480aba..eb3ea94 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # esp_uMQTT_broker An 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 to local GPIO pins, react on GPIO interrupts, and drive GPIO pins with PWM. +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 parse JSON structures, write to local GPIO pins, react on timers and GPIO interrupts, drive GPIO pins with PWM, and do basic HTTP GET requests. Find a video that explains the ideas and the architecture of the project at: https://www.youtube.com/watch?v=0K9q4IuB_oA @@ -230,6 +230,6 @@ The *MqttConnectCallback* function does a similar check for the connection, but - tuanpmt for esp_mqtt (https://github.com/tuanpmt/esp_mqtt ) - eadf for esp8266_easygpio (https://github.com/eadf/esp8266_easygpio ) - Stefan BrĂ¼ns for ESP8266_new_pwm (https://github.com/StefanBruens/ESP8266_new_pwm ) +- Martin d'Allens for esphttpclient (https://github.com/Caerbannog/esphttpclient ) - Ian Craggs for mqtt_topic -- Martin d'Allens for esphttpclient - many others contributing to open software (for the ESP8266) diff --git a/SCRIPTING.md b/SCRIPTING.md index b39703a..d45a26d 100644 --- a/SCRIPTING.md +++ b/SCRIPTING.md @@ -51,7 +51,7 @@ In general, scripts conform to the following BNF: system | - ::= | | () | not () + ::= | | () | not () | json_parse (,) := '=' | '>' | gte | str_ge | str_gte | '+' | '-' | '*' | '|' | div @@ -181,7 +181,29 @@ if then [else ] endif Classic "if then else" expression. Sequences of actions must be terminated with the (optional) "else" and the "endif". Can be nested. # Expressions -Expressions evaluate to a (string) value. A single constant, a string, or a variable are the basic expressions. Expressions can be combined by operators. If more than one operator is used in an expression, all expressions are stricly evaluated from left to right. CAUTION: arithmetical preceedence does not (yet) apply automatically like in other programming languages. However, the preceedence can be fully controlled by brackets. "not(_expr_)" interpretes the expr as boolean and inverts the result. +Expressions evaluate to a (string) value. A single constant, a string, or a variable are the basic expressions. Expressions can be combined by operators. If more than one operator is used in an expression, all expressions are stricly evaluated from left to right. CAUTION: arithmetical preceedence does not (yet) apply automatically like in other programming languages. However, the preceedence can be fully controlled by brackets. + +``` +not() +``` +Interpretes the argument expression as boolean and inverts the result. + +``` +json_parse (,) +``` +Parses a JSON structure. The first argument expression is interpreted as JSON path, i.g. a string with names or numbers separated by "." characters. The second argument expression is interpreted as a JSON structure and the result of the expression is the data field of the JSON structure that is identified by the path (or an empty string if not found). + +Example - give in the variable $json the following JSON structure: +``` +{ +"name": + { "first":"John", + "last":"Snow" } +"age":30, +"cars":[ "Ford", "BMW", "Fiat" ] +} +``` +"json_parse("name.first", $json)" results in "John", "json_parse("cars.1", $json)" results in "BMW". # Values A constant, a string, or a variable are values. Optionally, strings and constans can be put in quotes, like e.g. "A String" or "-10". This is especially useful for strings containing a whitespace or an operator. Any single character can be quotet using the '\\' escape character, like e.g. A\ String (equals "A String"). diff --git a/firmware/0x00000.bin b/firmware/0x00000.bin index 621548a..cc6487c 100644 Binary files a/firmware/0x00000.bin and b/firmware/0x00000.bin differ diff --git a/firmware/0x10000.bin b/firmware/0x10000.bin index 2d6f031..ada706e 100644 Binary files a/firmware/0x10000.bin and b/firmware/0x10000.bin differ diff --git a/user/json_path.c b/user/json_path.c new file mode 100644 index 0000000..a2f8cfb --- /dev/null +++ b/user/json_path.c @@ -0,0 +1,107 @@ +#include "c_types.h" +#include "mem.h" +#include "ets_sys.h" +#include "osapi.h" +#include "os_type.h" + +#include "user_interface.h" +#include "string.h" + +#include "json/jsonparse.h" + +bool ICACHE_FLASH_ATTR find_next_pair(struct jsonparse_state *state, char *name, int level) { + int json_type; + + //os_printf ("name: %s level: %d\r\n", name, level); + while(json_type = jsonparse_next(state)) { + //os_printf ("json_type: %d json_level: %d\r\n", json_type, state->depth); + if (state->depth < level-1) + return false; + + if (state->depth == level && json_type == JSON_TYPE_PAIR_NAME && + jsonparse_strcmp_value(state, name) == 0) + return true; + } + return false; +} + +bool ICACHE_FLASH_ATTR find_index(struct jsonparse_state *state, int index, int level) { + int json_type; + + //os_printf ("index: %d level: %d\r\n", index, level); + index++; + while(index > 0 && (json_type = jsonparse_next(state))) { + //os_printf ("json_type: %d json_level: %d index: %d\r\n", json_type, state->depth, index); + if (state->depth < level-1) + return false; + + if (state->depth == level && (json_type == JSON_TYPE_ARRAY || json_type == ',')) { + index--; + } + } + return index == 0; +} + +void ICACHE_FLASH_ATTR json_path(char *json, char *path, char *buf, int *buf_size) +{ + char *p; + char *ids[JSONPARSE_MAX_DEPTH]; + char tmppath[strlen(path)+1]; + int i_max = 0; + + os_strcpy(tmppath, path); + ids[i_max++] = tmppath; + for (p=tmppath; *p != '\0'; p++) { + if (*p == '.') { + *p = '\0'; + ids[i_max++] = p+1; + } + if (*p == '[') { + *p = '\0'; + ids[i_max++] = p+1; + } + } + + int i; + int level = 1; + struct jsonparse_state state; + int json_type; + bool hit; + int array_count = -1; + jsonparse_setup(&state, json, os_strlen(json)); + + if (*buf_size > 0) + buf[0] = '\0'; + + for (i = 0, hit = true; hit && i