kopia lustrzana https://github.com/martin-ger/esp_mqtt
Added JSON parsing
rodzic
4ace989d53
commit
a71414f77e
2
Makefile
2
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
|
||||
|
|
|
@ -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)
|
||||
|
|
26
SCRIPTING.md
26
SCRIPTING.md
|
@ -51,7 +51,7 @@ In general, scripts conform to the following BNF:
|
|||
system <expr> |
|
||||
<action> <action>
|
||||
|
||||
<expr> ::= <val> | <val> <op> <expr> | (<expr>) | not (<expr>)
|
||||
<expr> ::= <val> | <val> <op> <expr> | (<expr>) | not (<expr>) | json_parse (<expr>,<expr>)
|
||||
|
||||
<op> := '=' | '>' | gte | str_ge | str_gte | '+' | '-' | '*' | '|' | div
|
||||
|
||||
|
@ -181,7 +181,29 @@ if <expr> then <action> [else <action>] 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(<expr>)
|
||||
```
|
||||
Interpretes the argument expression as boolean and inverts the result.
|
||||
|
||||
```
|
||||
json_parse (<expr>,<expr>)
|
||||
```
|
||||
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").
|
||||
|
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -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<i_max; i++) {
|
||||
if (isdigit(ids[i][0]))
|
||||
hit = find_index(&state, atoi(ids[i]), level);
|
||||
else
|
||||
hit = find_next_pair(&state, ids[i], level);
|
||||
level += 2;
|
||||
}
|
||||
|
||||
if (!hit) {
|
||||
*buf_size = 0;
|
||||
return;
|
||||
}
|
||||
level -= 2;
|
||||
|
||||
while (json_type = jsonparse_next(&state)) {
|
||||
//os_printf ("level: %d json_type: %d json_level: %d\r\n", level, json_type, state.depth);
|
||||
if (state.depth < level) {
|
||||
*buf_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (json_type == JSON_TYPE_STRING || json_type == JSON_TYPE_INT ||
|
||||
json_type == JSON_TYPE_NUMBER) {
|
||||
jsonparse_copy_value(&state, buf, *buf_size);
|
||||
*buf_size = jsonparse_get_len(&state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*buf_size = 0;
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef JSON_PATH_H
|
||||
#define JSON_PATH_H
|
||||
void json_path(char *json, char *path, char *buf, int *buf_size);
|
||||
#endif
|
60
user/lang.c
60
user/lang.c
|
@ -16,6 +16,10 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JSON_PARSE
|
||||
#include "json_path.h"
|
||||
#endif
|
||||
|
||||
#define lang_debug //os_printf
|
||||
|
||||
#define lang_log(...) {if (lang_logging){char log_buffer[256]; os_sprintf (log_buffer, "%s: ", get_timestr()); con_print(log_buffer); os_sprintf (log_buffer, __VA_ARGS__); con_print(log_buffer);}}
|
||||
|
@ -303,6 +307,9 @@ int ICACHE_FLASH_ATTR text_into_tokens(char *str) {
|
|||
} else if (*p == ')' && !in_token) {
|
||||
// mark this as bracket close
|
||||
*q++ = 9;
|
||||
} else if (*p == ',' && !in_token) {
|
||||
// mark this as colon
|
||||
*q++ = 10;
|
||||
} else {
|
||||
*q++ = *p;
|
||||
}
|
||||
|
@ -397,6 +404,11 @@ int ICACHE_FLASH_ATTR text_into_tokens(char *str) {
|
|||
*p = '\0';
|
||||
in_token = false;
|
||||
}
|
||||
else if (*p == 10) {
|
||||
my_token[token_count++] = ",";
|
||||
*p = '\0';
|
||||
in_token = false;
|
||||
}
|
||||
else {
|
||||
if (!in_token) {
|
||||
my_token[token_count++] = p;
|
||||
|
@ -1058,6 +1070,54 @@ int ICACHE_FLASH_ATTR parse_expression(int next_token, char **data, int *data_le
|
|||
*data = "1";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef JSON_PARSE
|
||||
else if (is_token(next_token, "json_parse")) {
|
||||
lang_debug("val json_parse\r\n");
|
||||
|
||||
len_check(5);
|
||||
if (syn_chk && !is_token(next_token+1, "("))
|
||||
return syntax_error(next_token+1, "expected '('");
|
||||
|
||||
char *path_data;
|
||||
int path_data_len;
|
||||
Value_Type path_data_type;
|
||||
// parse path string
|
||||
if ((next_token = parse_expression(next_token + 2, &path_data, &path_data_len, &path_data_type, doit)) == -1)
|
||||
return -1;
|
||||
if (!doit)
|
||||
path_data_len = 0;
|
||||
char path[path_data_len+1];
|
||||
if (doit)
|
||||
os_strcpy(path, path_data);
|
||||
|
||||
if (syn_chk && !is_token(next_token, ","))
|
||||
return syntax_error(next_token, "expected ','");
|
||||
|
||||
char *json_data;
|
||||
int json_data_len;
|
||||
Value_Type json_data_type;
|
||||
// parse json string
|
||||
if ((next_token = parse_expression(next_token + 1, &json_data, &json_data_len, &json_data_type, doit)) == -1)
|
||||
return -1;
|
||||
if (!doit)
|
||||
json_data_len = 0;
|
||||
char json[json_data_len+1];
|
||||
if (doit)
|
||||
os_strcpy(json, json_data);
|
||||
|
||||
if (syn_chk && !is_token(next_token, ")"))
|
||||
return syntax_error(next_token, "expected ')'");
|
||||
|
||||
next_token += 1;
|
||||
|
||||
if (doit) {
|
||||
*data_len = sizeof(tmp_buffer);
|
||||
json_path(json, path, tmp_buffer, data_len);
|
||||
*data = tmp_buffer;
|
||||
*data_type = STRING_T;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (is_token(next_token, "(")) {
|
||||
lang_debug("expr (\r\n");
|
||||
|
|
|
@ -67,6 +67,11 @@
|
|||
#define HTTPC 1
|
||||
//#define HTTPCS 1
|
||||
|
||||
//
|
||||
// Define this if you want to have JSON parse support in scripts.
|
||||
//
|
||||
#define JSON_PARSE 1
|
||||
|
||||
//
|
||||
// Define this if you want to have mDNS support in scripts.
|
||||
//
|
||||
|
|
|
@ -924,7 +924,6 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) {
|
|||
if (strcmp(tokens[1], "network") == 0) {
|
||||
config.network_addr.addr = ipaddr_addr(tokens[2]);
|
||||
ip4_addr4(&config.network_addr) = 0;
|
||||
os_sprintf(response, "Network set to %d.%d.%d.%d/24\r\n", IP2STR(&config.network_addr));
|
||||
goto command_handled;
|
||||
}
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue