// ============================================
// Complete Structa Usage Example
// ============================================
#include <Arduino.h>
#include "structa.h"
#include "structa_config.h" // MCU presets
#include "structa_buffer.h" // Time-series buffering
#include "structa_memory.h" // Enhanced memory tracking
// ======================================================
// Example 1: Simple Sensor Reading with Buffer
// ======================================================
#define SENSOR_FIELDS(field) \
field(float, temperature, META_RANGE(-40.0, 125.0)) \
field(float, humidity, META_RANGE(0.0, 100.0)) \
field(int, battery, META_RANGE(0, 100)) \
field(String, sensorId, META_STRLEN(3, 20))
DEFINE_STRUCTA(SensorReading, SENSOR_FIELDS)
// Create a buffer for 20 readings
StructaBuffer<SensorReading, 20> sensorBuffer;
void handleFullBuffer(const String& json) {
Serial.println("Buffer full! Sending data...");
Serial.println(json);
// Here you would send to server, save to SD, etc.
// sendToServer(json);
// saveToSD(json);
}
void onSensorAdd(const SensorReading& data, size_t index) {
Serial.print("Added reading #");
Serial.print(index);
Serial.print(" - Temp: ");
Serial.println(data.temperature);
}
void setupSensorExample() {
// Configure buffer
sensorBuffer.enableAutoSerialize(handleFullBuffer);
sensorBuffer.onAdd = onSensorAdd;
Serial.println("Sensor buffer ready!");
}
void loopSensorExample() {
// Simulate sensor reading every 5 seconds
static unsigned long lastRead = 0;
if (millis() - lastRead >= 5000) {
SensorReading reading;
reading.temperature = random(200, 300) / 10.0; // 20.0 - 30.0
reading.humidity = random(400, 600) / 10.0; // 40.0 - 60.0
reading.battery = random(50, 100);
reading.sensorId = "SENSOR_001";
bool bufferFull = sensorBuffer.add(reading);
if (!bufferFull) {
Serial.print("Buffer: ");
Serial.print(sensorBuffer.size());
Serial.print("/");
Serial.print(sensorBuffer.capacity());
Serial.print(" (");
Serial.print(sensorBuffer.fillPercentage());
Serial.println("%)");
}
lastRead = millis();
}
}
// ======================================================
// Example 2: Timestamped Buffer
// ======================================================
TimestampedBuffer<SensorReading, 10> timestampedBuffer;
void setupTimestampedExample() {
Serial.println("Timestamped buffer ready!");
}
void loopTimestampedExample() {
static unsigned long lastRead = 0;
if (millis() - lastRead >= 3000) {
SensorReading reading;
reading.temperature = random(200, 300) / 10.0;
reading.humidity = random(400, 600) / 10.0;
reading.battery = random(50, 100);
reading.sensorId = "SENSOR_002";
// Automatically adds timestamp
timestampedBuffer.add(TimestampedData<SensorReading>(reading));
if (timestampedBuffer.isFull()) {
String json = timestampedBuffer.serializeAll();
Serial.println("Timestamped data:");
Serial.println(json);
timestampedBuffer.clear();
}
lastRead = millis();
}
}
// ======================================================
// Example 3: Rate-Limited Buffer (avoid flooding)
// ======================================================
RateLimitedBuffer<SensorReading, 15> rateLimitedBuffer(2000); // Max one per 2 seconds
void setupRateLimitedExample() {
Serial.println("Rate-limited buffer ready!");
}
void loopRateLimitedExample() {
// Try to add reading (will be rate-limited)
SensorReading reading;
reading.temperature = random(200, 300) / 10.0;
reading.humidity = random(400, 600) / 10.0;
reading.battery = random(50, 100);
reading.sensorId = "SENSOR_003";
bool added = rateLimitedBuffer.addIfReady(reading);
if (added) {
Serial.println("Reading added (rate limit passed)");
}
}
// ======================================================
// Example 4: Batch Serialization (for large buffers)
// ======================================================
StructaBuffer<SensorReading, 50> largeBuffer;
void handleBatch(const String& batchJson) {
Serial.println("--- Batch ---");
Serial.println(batchJson);
// Send this batch to server
}
void setupBatchExample() {
// Fill buffer with dummy data
for (int i = 0; i < 50; i++) {
SensorReading reading;
reading.temperature = random(200, 300) / 10.0;
reading.humidity = random(400, 600) / 10.0;
reading.battery = random(50, 100);
reading.sensorId = "SENSOR_004";
largeBuffer.add(reading);
}
Serial.println("Buffer filled with 50 readings");
}
void loopBatchExample() {
static bool sent = false;
if (!sent && largeBuffer.isFull()) {
Serial.println("Sending in batches of 10...");
largeBuffer.serializeBatch(10, handleBatch);
sent = true;
}
}
// ======================================================
// Example 5: With Metadata
// ======================================================
void setupMetadataExample() {
// Fill buffer
for (int i = 0; i < 5; i++) {
SensorReading reading;
reading.temperature = 20.0 + i;
reading.humidity = 50.0 + i;
reading.battery = 90 - i;
reading.sensorId = "SENSOR_005";
sensorBuffer.add(reading);
}
}
void loopMetadataExample() {
static bool sent = false;
if (!sent) {
// Serialize with metadata
String json = sensorBuffer.serializeWithMetadata("DEVICE_ESP32_001", "temperature_humidity");
Serial.println("Data with metadata:");
Serial.println(json);
/*
Output:
{
"deviceId": "DEVICE_ESP32_001",
"type": "temperature_humidity",
"count": 5,
"timestamp": 12345,
"capacity": 20,
"data": [ ... ]
}
*/
sent = true;
}
}
// ======================================================
// Example 6: MCU Configuration
// ======================================================
void setupConfigExample() {
// Print detected configuration
StructaSettings::printConfig();
// Override for specific needs
// StructaSettings::applyPreset(MCU_ATMEGA328P);
// StructaSettings::setDefaultBufferSize(128);
// Or custom configuration
// StructaSettings::setBufferSizes(256, 1024, 128, 2048);
}
// ======================================================
// Example 7: Memory Tracking
// ======================================================
#define USER_FIELDS(field) \
field(String, username, META_STRLEN(3, 20)) \
field(int, age, META_RANGE(18, 100)) \
field(bool, active, META_NONE())
DEFINE_STRUCTA(User, USER_FIELDS)
void setupMemoryExample() {
// Track memory for User struct
for (int i = 0; i < 10; i++) {
User user;
user.username = "user" + String(i);
user.age = 25 + i;
user.active = true;
StructMemoryTracker<User>::recordAllocation(256);
String json = user.serialize();
StructMemoryTracker<User>::recordDeallocation(256);
}
// Print stats
StructMemoryTracker<User>::printStats("User");
// Add to global monitor
auto stats = StructMemoryTracker<User>::getStats();
GlobalMemoryMonitor::updateStruct("User", stats.current, stats.peak, stats.allocations);
}
void loopMemoryExample() {
static bool printed = false;
if (!printed) {
GlobalMemoryMonitor::printGlobalStats();
printed = true;
}
}
// ======================================================
// Main Setup & Loop
// ======================================================
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println("\n\n");
Serial.println("╔════════════════════════════════════════╗");
Serial.println("║ STRUCTA COMPLETE EXAMPLE ║");
Serial.println("╚════════════════════════════════════════╝");
Serial.println();
// Initialize examples
setupConfigExample();
Serial.println();
setupSensorExample();
setupMemoryExample();
Serial.println("\nAll examples initialized!");
Serial.println("Running sensor buffer example...\n");
}
void loop() {
// Run sensor buffer example
loopSensorExample();
// Uncomment to test other examples:
//loopTimestampedExample();
//loopRateLimitedExample();
//loopBatchExample();
//loopMetadataExample();
//loopMemoryExample();
}
// ======================================================
// Usage Patterns
// ======================================================
/*
PATTERN 1: Simple buffering with auto-send
───────────────────────────────────────────
StructaBuffer<SensorReading, 20> buffer;
buffer.enableAutoSerialize(handleFullBuffer);
In loop:
buffer.add(reading);
PATTERN 2: Manual control
───────────────────────────────────────────
StructaBuffer<SensorReading, 20> buffer;
In loop:
if (buffer.add(reading)) {
String json = buffer.serializeAll();
sendData(json);
buffer.clear();
}
PATTERN 3: Time-based sending
───────────────────────────────────────────
StructaBuffer<SensorReading, 100> buffer;
unsigned long lastSend = 0;
In loop:
buffer.add(reading);
if (millis() - lastSend >= 60000) { // Every minute
String json = buffer.serializeAll();
sendData(json);
buffer.clear();
lastSend = millis();
}
PATTERN 4: Batch sending for large datasets
───────────────────────────────────────────
StructaBuffer<SensorReading, 100> buffer;
When ready to send:
buffer.serializeBatch(10, [](const String& batch) {
sendData(batch);
});
PATTERN 5: Store and forward (offline)
───────────────────────────────────────────
StructaBuffer<SensorReading, 50> buffer;
if (WiFi.connected()) {
String json = buffer.serializeWithMetadata("DEVICE_001", "sensors");
if (sendToServer(json)) {
buffer.clear();
}
} else {
// Keep buffering until connection restored
buffer.add(reading);
}
*/