Added JSON parsing

pull/16/head
Martin Ger 2017-10-03 23:09:07 +02:00
rodzic 4ace989d53
commit a71414f77e
10 zmienionych plików z 203 dodań i 6 usunięć

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

107
user/json_path.c 100644
Wyświetl plik

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

4
user/json_path.h 100644
Wyświetl plik

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

Wyświetl plik

@ -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");

Wyświetl plik

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

Wyświetl plik

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