🚧 SD - Start building out json->pb parser, allow debug passthru for parsing a json string hardcoded

This commit is contained in:
brentru 2024-11-04 16:00:44 -05:00
parent 30bab50b8e
commit 6bb1ab2815
4 changed files with 178 additions and 33 deletions

View file

@ -255,6 +255,9 @@ public:
bool got_checkin_response; ///< True if a checkin response was received, False
///< otherwise.
// PoC - offline mode TODO
wippersnapper_digitalio_DigitalIOAdd _offline_msg_DigitalIOAdd;
private:
void _initV2();

View file

@ -0,0 +1,38 @@
# 1 "/var/folders/ff/dmzflvf52tq9kzvt6g8jglxw0000gn/T/tmpxjfs2kc1"
#include <Arduino.h>
# 1 "/Users/brentrubell/Documents/Arduino/libraries/Adafruit_Wippersnapper_Arduino/src/Wippersnapper_demo_wokwi.ino"
# 14 "/Users/brentrubell/Documents/Arduino/libraries/Adafruit_Wippersnapper_Arduino/src/Wippersnapper_demo_wokwi.ino"
#define IO_USERNAME "brubell"
#define IO_KEY "YOUR_AIO_KEY"
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASS ""
#define WS_DEBUG
#define API_PIN 0
#include "ws_manager.h"
Wippersnapper_Manager manager;
Wippersnapper_WiFiV2 wipper(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS, "io.adafruit.com", 8883);
void setup();
void loop();
#line 31 "/Users/brentrubell/Documents/Arduino/libraries/Adafruit_Wippersnapper_Arduino/src/Wippersnapper_demo_wokwi.ino"
void setup() {
manager.checkAPIVersion(API_PIN);
manager.provision();
Serial.begin(115200);
Serial.println("Adafruit Wippersnapper API Manager Demo");
Serial.print("Running Wippersnapper API Version: ");
Serial.println(manager.getAPIVersion());
manager.connect();
}
void loop() {
manager.run();
}

View file

@ -58,13 +58,20 @@ bool ws_sdcard::parseConfigFile() {
JsonDocument doc;
// TODO: Change max input length to fit an expected/max json size
int max_input_len = 512;
int max_input_len = 1024;
// Attempt to de-serialize the JSON document
DeserializationError error;
#ifdef ONLINE_MODE_DEBUG
// Read the config file from the serial input buffer
error = deserializeJson(doc, _serialInput.c_str(), max_input_len);
if (!_use_test_data) {
// Read the config file from the serial input buffer
WS_DEBUG_PRINTLN("Reading JSON config file...");
error = deserializeJson(doc, _serialInput.c_str(), max_input_len);
} else {
// Read the config file from the test JSON string
WS_DEBUG_PRINTLN("Reading test JSON data...");
error = deserializeJson(doc, json_test_data, max_input_len);
}
#else
// Read the config file from the SD card
WS_DEBUG_PRINTLN("Reading config file...");
@ -76,22 +83,83 @@ bool ws_sdcard::parseConfigFile() {
// print the error because it is not possible to continue running in offline
// mode without a valid config file
if (error) {
WS_DEBUG_PRINTLN("deserializeJson() failed: " + String(error.c_str()));
// TODO: Maybe this func should contain a bool return type for failure at
// this point
WS_DEBUG_PRINTLN("deserializeJson() failed, error code: " +
String(error.c_str()));
return false;
}
// Parse the "components" array
// Parse the "components" array into a JsonObject
JsonObject components = doc["components"][0];
// TODO: This is a list so we'll need to refactor the following to loop thru,
// parse and dispatch each component individually
// Parse the PB API type
const char *component_api_type =
components["componentAPI"]; // ie: "analogio", "digitalio", etc.
if (component_api_type == nullptr) {
WS_DEBUG_PRINTLN("No component API type found in JSON string!");
return false;
} else {
WS_DEBUG_PRINTLN("Component API type found: " + String(component_api_type));
}
// TODO- maybe a Switch case to handle the different component API types but
// for now just a simple if-else is OK
if (strcmp(component_api_type, "digitalio") == 0) {
// TODO - dispatch to create digitalio component protobuf message
// Create a new digitalio add protobuf message
WsV2._offline_msg_DigitalIOAdd =
wippersnapper_digitalio_DigitalIOAdd_init_default;
// Parse pinName
strcpy(WsV2._offline_msg_DigitalIOAdd.pin_name, components["pinName"]);
// Parse direction
const char *direction = components["direction"];
if (strcmp(direction, "INPUT") == 0) {
WsV2._offline_msg_DigitalIOAdd.gpio_direction =
wippersnapper_digitalio_DigitalIODirection_DIGITAL_IO_DIRECTION_INPUT;
} else if (strcmp(direction, "INPUT-PULLUP") == 0) {
WsV2._offline_msg_DigitalIOAdd.gpio_direction =
wippersnapper_digitalio_DigitalIODirection_DIGITAL_IO_DIRECTION_INPUT_PULL_UP;
} else if (strcmp(direction, "OUTPUT") == 0) {
WsV2._offline_msg_DigitalIOAdd.gpio_direction =
wippersnapper_digitalio_DigitalIODirection_DIGITAL_IO_DIRECTION_OUTPUT;
} else { // Unknown direction, bail out
WS_DEBUG_PRINTLN("Unknown digital pin direction found: " +
String(direction));
return false;
}
// Determine the sample mode
bool is_timer_sample_mode = false;
const char *sample_mode = components["sampleMode"];
if (strcmp(sample_mode, "TIMER") == 0)
is_timer_sample_mode = true;
if (is_timer_sample_mode) {
// If we're sampling periodically, parse the period
WsV2._offline_msg_DigitalIOAdd.period = components["timer"];
// and set the sample mode
WsV2._offline_msg_DigitalIOAdd.sample_mode =
wippersnapper_digitalio_DigitalIOSampleMode_DIGITAL_IO_SAMPLE_MODE_TIMER;
} else {
// set the sample mode for event
WsV2._offline_msg_DigitalIOAdd.sample_mode =
wippersnapper_digitalio_DigitalIOSampleMode_DIGITAL_IO_SAMPLE_MODE_EVENT;
}
// Print out the contents of the DigitalIOADD message
WS_DEBUG_PRINTLN("DigitalIOAdd message:");
WS_DEBUG_PRINTLN("Pin Name: " +
String(WsV2._offline_msg_DigitalIOAdd.pin_name));
WS_DEBUG_PRINTLN("Direction: " + String(direction));
WS_DEBUG_PRINTLN("Sample Mode: " + String(sample_mode));
WS_DEBUG_PRINTLN("Period: " +
String(WsV2._offline_msg_DigitalIOAdd.period));
return true;
} else if (strcmp(component_api_type, "analogio") == 0) {
// TODO - dispatch to create analogio component protobuf message
} else {
@ -107,39 +175,72 @@ bool ws_sdcard::parseConfigFile() {
// Returns true if input points to a valid JSON string
bool ws_sdcard::validateJson(const char *input) {
JsonDocument doc, filter;
return deserializeJson(doc, input, DeserializationOption::Filter(filter)) ==
DeserializationError::Ok;
}
// Note: using this for VALID json:
// {"temperature": 22.5, "humidity": 60}
// Note: using this for INVALID json:
// {"temperature": 22.5, "humidity": 60,
DeserializationError error =
deserializeJson(doc, input, DeserializationOption::Filter(filter));
WS_DEBUG_PRINTLN("Error: " + String(error.c_str()));
return error == DeserializationError::Ok;
// return deserializeJson(doc, input, DeserializationOption::Filter(filter))
// ==
// DeserializationError::Ok;
}
// Waits for incoming config file and parses it
// TODO: Split out parsing into parseConfigFile() and just read here
bool ws_sdcard::waitForSerialConfig() {
_serialInput = ""; // Clear the serial input buffer
WS_DEBUG_PRINTLN("Waiting for incoming JSON string...");
// Wait for incoming serial data
while (true) {
// Check if there is data available to read
if (Serial.available() > 0) {
// Read and append to _serialInput
char c = Serial.read();
_serialInput += c;
// Check for EoL or end of JSON string
if (c == '\n' || c == '}') {
break;
// We provide three ways to use this function:
// 1. Use a SD card with a JSON config file
// 2. Provide a JSON string via the hardware's serial input
// 3. Use a test JSON string - for debugging purposes ONLY
json_test_data =
"{\"components\":[{\"componentAPI\":\"analogio\",\"name\":\"Analog "
"Pin\",\"pinName\":\"A18\",\"type\":\"analog_pin\",\"mode\":\"ANALOG\","
"\"direction\":\"INPUT\",\"sampleMode\":\"TIMER\",\"analogReadMode\":"
"\"PIN_VALUE\",\"period\":30,\"isPin\":true}]}\\n\r\n";
_use_test_data = true;
_serialInput = ""; // Clear the serial input buffer
if (!_use_test_data) {
WS_DEBUG_PRINTLN("Waiting for incoming JSON string...");
while (true) {
// Check if there is data available to read
if (Serial.available() > 0) {
// Read and append to _serialInput
char c = Serial.read();
_serialInput += c;
// Check for EoL or end of JSON string
// Read the TODO/Note below!
// NOTE: This is checking for a \n delimeter from the serial
// and that wont be present in non-serial application
// Parse JSON normally if not using serial and inspect this condition!
if (c == '\n') {
break;
}
}
}
}
// Print out the received JSON string
WS_DEBUG_PRINT("[Debug] JSON string received: ");
if (_use_test_data) {
WS_DEBUG_PRINTLN("[from json test data]");
WS_DEBUG_PRINTLN(json_test_data);
} else {
WS_DEBUG_PRINTLN(_serialInput);
}
// Attempt to validate the string as JSON
if (!validateJson(_serialInput.c_str())) {
WS_DEBUG_PRINTLN("Invalid JSON string received!");
return false;
if (!_use_test_data) {
if (!validateJson(_serialInput.c_str())) {
WS_DEBUG_PRINTLN("Invalid JSON string received!");
return false;
}
} else {
if (!validateJson(json_test_data)) {
WS_DEBUG_PRINTLN("Invalid JSON string received!");
return false;
}
}
WS_DEBUG_PRINTLN("Valid JSON string received!");

View file

@ -38,10 +38,13 @@ public:
bool validateJson(const char *input);
private:
bool _is_sd_card_inserted; ///< True if an SD card is inserted, False
///< otherwise.
SdFat _sd; ///< SD object from Adafruit SDFat library
String _serialInput; ///< Serial input buffer
bool _is_sd_card_inserted; ///< True if an SD card is inserted, False
///< otherwise.
SdFat _sd; ///< SD object from Adafruit SDFat library
String _serialInput; ///< Serial input buffer
const char *json_test_data; ///< Json test data
bool _use_test_data; ///< True if sample data is being used to test, instead
///< of serial input, False otherwise.
};
extern Wippersnapper_V2 WsV2;
#endif // WS_SDCARD_H