Skip to content

Commit 89d24f9

Browse files
authored
Merge pull request #4 from tobozo/1.2.3
1.2.3
2 parents 7dc3c81 + 9445a96 commit 89d24f9

File tree

5 files changed

+175
-39
lines changed

5 files changed

+175
-39
lines changed

assets/ugly_logo.png

2.63 KB
Loading
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#include <ArduinoJson.h>
2+
#include <YAMLDuino.h>
3+
#include <M5Stack.h>
4+
5+
6+
const char* yaml_example_str = R"_YAML_STRING_(
7+
8+
my_setting: "false"
9+
10+
flag1: "true"
11+
flag2: "true"
12+
13+
settings1:
14+
just_a_string: "I am a string"
15+
integer: 12345
16+
float: 12.3323
17+
18+
settings2:
19+
nope: ["n","o","p","e"]
20+
21+
22+
)_YAML_STRING_";
23+
24+
25+
const char* config_file = "/config.yml";
26+
const char* nodename = "my_setting"; // property name in the config
27+
const bool default_value = false; // default value for property
28+
bool current_value = default_value;
29+
bool config_loaded = false; // prevent updates if config isn't loaded
30+
DynamicJsonDocument json_doc(2048);
31+
JsonObject myConfig; // json accessor
32+
33+
34+
bool writeTestYaml( fs::FS &fs, const char* path )
35+
{
36+
fs::File file = fs.open( path, FILE_WRITE );
37+
if( !file ) {
38+
Serial.println("Can't open file for writing");
39+
return false;
40+
}
41+
size_t written = file.write( (const uint8_t*)yaml_example_str, strlen( yaml_example_str ) );
42+
file.close();
43+
//Serial.printf("Example file created (%d bytes)\n", written);
44+
return written > 0;
45+
}
46+
47+
48+
bool loadYamlConfig()
49+
{
50+
fs::File file = SD.open( config_file );
51+
if( !file ) {
52+
Serial.println("Can't open test file for writing :-(");
53+
return false;
54+
}
55+
auto err = deserializeYml( json_doc, file ); // convert yaml to json
56+
file.close();
57+
if( err ) {
58+
Serial.printf("Unable to deserialize YAML to JsonDocument: %s\n", err.c_str() );
59+
return false;
60+
}
61+
myConfig = json_doc.as<JsonObject>();
62+
current_value = myConfig[nodename].as<String>() == "true";
63+
//serializeJson( myConfig, Serial );
64+
//Serial.println();
65+
return true;
66+
}
67+
68+
69+
bool saveYamlConfig()
70+
{
71+
fs::File file = SD.open( config_file, FILE_WRITE);
72+
if( !file ) {
73+
Serial.println("Can't open file for writing");
74+
return false;
75+
}
76+
const size_t bytes_out = serializeYml( myConfig, file );
77+
file.close();
78+
//Serial.printf("Written %d bytes\n", bytes_out );
79+
return bytes_out > 0;
80+
}
81+
82+
83+
bool toggleYamlProperty()
84+
{
85+
if( !config_loaded ) return false;
86+
//Serial.printf("Initial value: [%s] = %s\n", nodename, current_value ? "true" : "false" );
87+
current_value = !current_value;
88+
Serial.printf("New value: [%s] = %s\n", nodename, current_value ? "true" : "false" );
89+
// ArduinoJson @bool is borked up so we use a string
90+
myConfig[nodename] = current_value ? "true" : "false";
91+
return saveYamlConfig();
92+
}
93+
94+
95+
void setup()
96+
{
97+
M5.begin();
98+
99+
if( M5.BtnA.isPressed() ) {
100+
SD.remove( config_file );
101+
Serial.println("Deleted config file");
102+
while( M5.BtnA.isPressed() ) { M5.update(); } // wait for release
103+
ESP.restart();
104+
}
105+
106+
_load_config:
107+
config_loaded = loadYamlConfig();
108+
109+
if( !config_loaded ) {
110+
Serial.printf("Ceating config file %s\n", config_file );
111+
if( !writeTestYaml( SD, config_file ) ) {
112+
Serial.println("Could not create config file, aborting");
113+
while(1) vTaskDelay(1);
114+
}
115+
// write succeeded, reload config
116+
goto _load_config;
117+
}
118+
119+
Serial.printf("Current config value: [%s] = %s\n", nodename, current_value ? "true" : "false" );
120+
}
121+
122+
123+
124+
void loop()
125+
{
126+
M5.update();
127+
128+
if( M5.BtnB.wasPressed() ) {
129+
if( !toggleYamlProperty() ) {
130+
Serial.println("Failed to save property");
131+
}
132+
}
133+
}
134+

examples/test/test.ino

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ blah:
2525
prop4: wat
2626
integer: 12345
2727
float: 12.3323
28-
inline_json_for_the_haters: { "hello":"json", "nested":[3,2,1,"moon"] }
28+
inline_json_for_the_haters: { "hello":"json", "nested":[3,2,"1","moon"] }
2929
whatever:
3030
nope: ["n","o","p","e"]
3131
last: "true"
@@ -56,7 +56,7 @@ const char* json_example_str = R"_JSON_STRING_(
5656
],
5757
"integer": 12345,
5858
"float": 12.3323,
59-
"inline_json_for_the_haters": { "hello": "json", "nested": [ 3, 2, 1, "moon" ] }
59+
"inline_json_for_the_haters": { "hello": "json", "nested": [ 3, 2, "1", "moon" ] }
6060
},
6161
"whatever": { "nope": [ "n", "o", "p", "e" ] },
6262
"last": "true"

src/ArduinoYaml.cpp

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ YAMLParser::~YAMLParser()
109109
{
110110
yaml_parser_delete(&_parser);
111111
yaml_emitter_delete(&_emitter);
112-
yaml_document_delete(&_document);
112+
#if !defined ESP8266 // TODO: fix mem leak with esp8266
113+
yaml_document_delete(&_document);
114+
#endif
113115
}
114116

115117

@@ -219,63 +221,60 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
219221

220222
#if defined HAS_ARDUINOJSON
221223

224+
222225
// yaml_node_t deconstructor => JsonObject
223226
void deserializeYml_JsonObject( yaml_document_t* document, yaml_node_t* yamlNode, JsonObject &jsonNode, YAMLParser::JNestingType_t nt, const char *nodename, int depth )
224227
{
225-
// Prevent "use after free" situations in JsonObject key
226-
// ArduinoJson only aliases const char* so use char* to force a copy
227-
char* _nodename = (char*)malloc( strlen(nodename)+1 );
228-
strcpy(_nodename, nodename);
229-
230228
switch (yamlNode->type) {
231-
case YAML_NO_NODE: /*YAML_LOG_v("YAML_NO_NODE");*/ break;
232229
case YAML_SCALAR_NODE:
233230
{
234231
double number;
235232
char* scalar;
236233
char* end;
237-
bool is_string;
238234
scalar = (char *)yamlNode->data.scalar.value;
239235
number = strtod(scalar, &end);
240-
is_string = (end == scalar || *end);
236+
bool is_string = (end == scalar || *end);
241237
switch( nt ) {
242238
case YAMLParser::SEQ_KEY:
243239
{
244-
if(is_string) jsonNode[_nodename].add( scalar );
245-
else jsonNode[_nodename].add( number );
240+
JsonArray array = jsonNode[nodename];
241+
if(is_string) array.add( scalar );
242+
else array.add( number );
243+
//YAML_LOG_d("[SEQ][%s][%d] => %s(%s)", nodename, array.size()-1, is_string?"string":"number", is_string?scalar:String(number).c_str() );
246244
}
247245
break;
248246
case YAMLParser::MAP_KEY:
249-
if(is_string) jsonNode[_nodename] = scalar;
250-
else jsonNode[_nodename] = number;
247+
if(is_string) jsonNode[nodename] = scalar;
248+
else jsonNode[nodename] = number;
249+
//YAML_LOG_d("[MAP][%d][%s] => %s(%s)", jsonNode.size()-1, nodename, is_string?"string":"number", is_string?scalar:String(number).c_str() );
251250
break;
252251
default: YAML_LOG_e("Error invalid nesting type"); break;
253252
}
254253
}
255254
break;
256255
case YAML_SEQUENCE_NODE:
257256
{
258-
jsonNode.createNestedArray(_nodename);
257+
JsonArray tmpArray = jsonNode.createNestedArray(nodename);
259258
yaml_node_item_t * item_i;
259+
yaml_node_t *itemNode;
260+
String _nodename;
260261
for (item_i = yamlNode->data.sequence.items.start; item_i < yamlNode->data.sequence.items.top; ++item_i) {
261-
yaml_node_t *itemNode = yaml_document_get_node(document, *item_i);
262+
itemNode = yaml_document_get_node(document, *item_i);
262263
if( itemNode->type == YAML_MAPPING_NODE ) { // array of anonymous objects
263-
JsonObject tmpObj = jsonNode[_nodename].createNestedObject(); // insert empty nested object
264-
deserializeYml_JsonObject( document, itemNode, tmpObj, YAMLParser::SEQ_KEY, "root", depth+1 ); // go recursive using temporary node name
265-
jsonNode[_nodename][jsonNode[_nodename].size()-1].set( tmpObj["root"] ); // remove temporary name and make object anonymous
264+
JsonObject tmpObj = tmpArray.createNestedObject(); // insert empty nested object
265+
_nodename = ROOT_NODE + String( nodename ) + String( tmpArray.size() ); // generate a temporary nodename
266+
deserializeYml_JsonObject( document, itemNode, tmpObj, YAMLParser::SEQ_KEY, _nodename.c_str(), depth+1 ); // go recursive using temporary node name
267+
jsonNode[nodename][tmpArray.size()-1] = tmpObj[_nodename]; // remove temporary name and make object anonymous
266268
} else { // array of sequences or values
267-
deserializeYml_JsonObject( document, itemNode, jsonNode, YAMLParser::SEQ_KEY, _nodename, depth+1 );
269+
deserializeYml_JsonObject( document, itemNode, jsonNode, YAMLParser::SEQ_KEY, nodename, depth+1 );
268270
}
269271
}
272+
//YAML_LOG_d("[ARR][%s] has %d items", nodename, tmpArray.size() );
270273
}
271274
break;
272275
case YAML_MAPPING_NODE:
273276
{
274-
jsonNode.createNestedObject(_nodename);
275-
JsonObject tmpObj = jsonNode[_nodename];
276-
if( nt == YAMLParser::NONE ) {
277-
jsonNode = tmpObj; // topmost object, nodename is empty so apply reparenting
278-
}
277+
JsonObject tmpNode = jsonNode.createNestedObject(nodename);
279278
yaml_node_pair_t* pair_i;
280279
yaml_node_t* key;
281280
yaml_node_t* value;
@@ -286,18 +285,20 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
286285
YAML_LOG_w("Mapping key is not scalar (line %lu, val=%s).", key->start_mark.line, (const char*)value->data.scalar.value);
287286
continue;
288287
}
289-
deserializeYml_JsonObject( document, value, tmpObj, YAMLParser::MAP_KEY, (const char*)key->data.scalar.value, depth+1 );
288+
deserializeYml_JsonObject( document, value, tmpNode, YAMLParser::MAP_KEY, (const char*)key->data.scalar.value, depth+1 );
289+
}
290+
if( nt == YAMLParser::NONE ) {
291+
//YAML_LOG_d("Root node has %d items", jsonNode.size() );
292+
} else {
293+
//YAML_LOG_d("[KEY][%s] has %d items", nodename, jsonNode.size() );
290294
}
291295
}
292296
break;
293-
default:
294-
YAML_LOG_w("Unknown node type (line %lu).", yamlNode->start_mark.line);
295-
break;
297+
case YAML_NO_NODE: YAML_LOG_e("YAML_NO_NODE"); break;
298+
default: YAML_LOG_w("Unknown node type (line %lu).", yamlNode->start_mark.line); break;
296299
}
297-
free(_nodename);
298300
}
299301

300-
301302
// JsonVariant deconstructor => YAML stream
302303
size_t serializeYml_JsonVariant( JsonVariant root, Stream &out, int depth, YAMLParser::JNestingType_t nt )
303304
{
@@ -362,7 +363,7 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
362363
{
363364
YAMLToArduinoJson *parser = new YAMLToArduinoJson();
364365
JsonObject tmpObj = parser->toJson( src ); // decode yaml stream/string
365-
dest_doc.set( tmpObj );
366+
dest_doc.set( tmpObj[ROOT_NODE] );
366367
delete parser;
367368
return DeserializationError::Ok;
368369
}
@@ -372,7 +373,7 @@ void YAMLParser::handle_emitter_error(yaml_emitter_t *e)
372373
{
373374
YAMLToArduinoJson *parser = new YAMLToArduinoJson();
374375
JsonObject tmpObj = parser->toJson( src ); // decode yaml stream/string
375-
dest_doc.set( tmpObj );
376+
dest_doc.set( tmpObj[ROOT_NODE] );
376377
delete parser;
377378
return DeserializationError::Ok;
378379
}

src/ArduinoYaml.hpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,22 +110,24 @@ class YAMLParser
110110
#include <ArduinoJson.h>
111111

112112
// deconstructors
113-
void deserializeYml_JsonObject( yaml_document_t* document, yaml_node_t* yamlNode, JsonObject &jsonNode, YAMLParser::JNestingType_t nt=YAMLParser::NONE, const char *nodename="", int depth=0 );
113+
#define ROOT_NODE "_root_"
114+
void deserializeYml_JsonObject( yaml_document_t* document, yaml_node_t* yamlNode, JsonObject &jsonNode, YAMLParser::JNestingType_t nt=YAMLParser::NONE, const char *nodename=ROOT_NODE, int depth=0 );
114115
size_t serializeYml_JsonVariant( JsonVariant root, Stream &out, int depth_level, YAMLParser::JNestingType_t nt );
115116

116117
class YAMLToArduinoJson : public YAMLParser
117118
{
118119
public:
119120
YAMLToArduinoJson() {};
120-
~YAMLToArduinoJson() { if( _doc) delete _doc; };
121-
void setJsonDocument( const size_t capacity ) { _doc = new DynamicJsonDocument(capacity); _root = _doc->to<JsonObject>(); };
121+
~YAMLToArduinoJson() { if( _doc) delete _doc; }
122+
void setJsonDocument( const size_t capacity ) { _doc = new DynamicJsonDocument(capacity); _root = _doc->to<JsonObject>(); /*_root.createNestedObject(ROOT_NODE);*/ }
122123
JsonObject& getJsonObject() { return _root; }
123124
void toJson() {
124125
yaml_node_t * node;
125126
if( !_doc || _root.isNull() ) {
126127
YAML_LOG_e("No destination JsonObject defined.");
127128
return;
128129
}
130+
// YAML_LOG_i("JsonDocument capacity: %d (%d/%d yaml r/w)", _doc->capacity(), bytesRead(), bytesWritten() );
129131
// dafuq is that if( a=b, !a ) notation ??
130132
if (node = yaml_document_get_root_node(getDocument()), !node) {
131133
YAML_LOG_e("No document defined.");
@@ -171,8 +173,7 @@ class YAMLParser
171173
DeserializationError deserializeYml( JsonObject &dest_obj, T &src)
172174
{
173175
YAMLToArduinoJson *parser = new YAMLToArduinoJson();
174-
JsonObject tmpObj = parser->toJson( src ); // decode yaml stream/string
175-
dest_obj = tmpObj;
176+
dest_obj = parser->toJson( src ); // decode yaml stream/string
176177
size_t capacity = parser->bytesWritten()*2;
177178
delete parser;
178179
if( capacity == 0 ) {

0 commit comments

Comments
 (0)