#include <ArduinoJson.h>
#include <TimeLib.h>
#define JSON_DOC_ALLOC 200
const char* fieldData = "data";
const char* fieldRefreshInterval = "ri";
const char* fieldLastRefreshed = "lr";
const char* fieldDirty = "d";
const char* fieldTimestamp = "ts";
class DataManager {
public:
DataManager() {}
// Add a variable to root of type int.
// @var: the name of the variable (document)
// @key: the key / name
// @val: the initial value
// @refresh_interval: the frequency, in sec, to force update the value even if value is unchanged
void addValueInt(const String& var, const String& key, const int val, const long refresh_interval = 0) {
JsonObject doc = root.createNestedObject(var);
JsonObject data = doc.createNestedObject(fieldData);
data[key] = val;
doc[fieldRefreshInterval] = refresh_interval;
if (refresh_interval) {
doc[fieldLastRefreshed] = now();
}
doc[fieldDirty] = 1;
}
// Add a variable to root of type String.
// @var: the name of the variable (document)
// @key: the key / name
// @val: the initial value
// @refresh_interval: the frequency, in sec, to force update the value even if value is unchanged
void addValueString(const String& var, const String& key, const String& val, const long refresh_interval = 0) {
JsonObject doc = root.createNestedObject(var);
JsonObject data = doc.createNestedObject(fieldData);
data[key] = val;
doc[fieldRefreshInterval] = refresh_interval;
if (refresh_interval) {
doc[fieldLastRefreshed] = now();
}
doc[fieldDirty] = 1;
}
// Update an int variable.
void updateValueInt(const String& var, const String& key, const int val) {
JsonObject doc = root[var];
JsonObject data = doc[fieldData];
if (data[key] != val) {
data[key] = val;
if (doc[fieldRefreshInterval]) {
doc[fieldLastRefreshed] = now();
}
doc[fieldDirty] = 1;
}
}
// Update a String variable.
void updateValueString(const String& var, const String& key, const String& val) {
JsonObject doc = root[var];
JsonObject data = doc[fieldData];
if (data[key] != val) {
data[key] = val;
if (doc[fieldRefreshInterval]) {
doc[fieldLastRefreshed] = now();
}
doc[fieldDirty] = 1;
}
}
// Get the JSON serialized representation of var.
// @var: the name of the variable
// @add_timestamp: add 'ts' field with current time epoch
// @mark_flushed: clear the 'd' dirty field of the variable value
String serializeVar(const String& var, const bool add_timestamp = false, const bool mark_flushed = false) {
String s;
JsonObject doc = root[var];
if (add_timestamp) {
JsonObject data = doc[fieldData];
DynamicJsonDocument d(data.memoryUsage() + 8); // Timestamp occupies 8 spaces.
d = data;
d[fieldTimestamp] = now();
serializeJson(d, s);
} else {
serializeJson(doc[fieldData], s);
}
if (mark_flushed) {
doc[fieldDirty] = 0;
}
return s;
}
// Specifies the update callback for the object.
// @callback: a function with arguments String var, String serialData
void setUpdateCallback(void (*callback)(const String, const String)) {
update_callback = callback;
}
// Run the periodic functions of the class.
void poll() {
JsonObject root_ = root.as<JsonObject>(); // Iterator is only defined for JsonObject & Array.
for (JsonPair kv : root_) {
const String key = String(kv.key().c_str());
JsonObject doc = root[key];
// Check if refresh is due.
bool update = false;
if (doc[fieldDirty]) {
update = true;
} else if (doc[fieldLastRefreshed]) {
if (now() >= (long) doc[fieldRefreshInterval] + (long) doc[fieldLastRefreshed]) {
update = true;
doc[fieldLastRefreshed] = now();
}
}
// Run the update.
if (update) {
String serialData = serializeVar(key, true, true);
if (update_callback) {
(*update_callback)(key, serialData);
} else {
Serial.println("DEBUG: " + key + "=" + serialData);
}
}
}
}
private:
// Root document for JSON
// Variable name is stored at the root level. Cannot start with underscore (_).
// `data` subfield stores the actual data
// `ri` subfield stores the refresh interval
// `lr` subfield stores the last refresh timestamp if refresh interval is set
// `d` subfield indicates whether the data is dirty and needs to be flushed
DynamicJsonDocument root = DynamicJsonDocument(200);
// Function pointer to send update.
void (*update_callback)(const String, const String);
};
DataManager d;
void print_callback(const String var, const String serialData) {
Serial.println("Sending: " + var + "=" + serialData);
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
while (!Serial);
long t = 1678528333; // Epoch for Mar 11 3:52AM CT
// May be optional depending on WifiNINA implementation.
setTime(t);
Serial.println(now());
// Serial.println(year());
// Serial.println(month());
// Serial.println(day());
// Serial.println(hour());
// Serial.println(minute());
// Serial.println(root.memoryUsage());
d.setUpdateCallback(&print_callback);
// d.addValueInt("ktc1", "temp", 1234, 0);
d.addValueString("ktc1", "temp", "1234", 1);
// Serial.println(root.memoryUsage());
// serializeJson(root, Serial);
// Serial.println();
d.poll();
// String data;
// serializeJson(doc, data);
// Serial.println(data);
delay(2000);
//d.updateValueInt("ktc1", "temp", 2345);
d.updateValueString("ktc1", "temp", "2345");
// serializeJson(root, Serial);
// Serial.println();
d.poll();
delay(2000);
// serializeJson(root, Serial);
// Serial.println();
// d.updateValueInt("ktc1", "temp", 2345);
// d.updateValueInt("ktc1", "temp", 2345);
d.updateValueString("ktc1", "temp", "2345");
d.updateValueString("ktc1", "temp", "2345");
d.poll();
Serial.println("Enter loop");
}
void loop() {
// put your main code here, to run repeatedly:
}