snippets = [
// ===== SENSOR READING =====
{
id : "sensor-dht22" ,
title : "DHT22 Temperature & Humidity Reading" ,
category : "Sensors" ,
platform : "Arduino/ESP32" ,
difficulty : "Beginner" ,
tags : ["temperature" , "humidity" , "dht22" , "sensor" ],
description : "Read temperature and humidity from DHT22 sensor with error handling" ,
code : `#include <DHT.h>
#define DHT_PIN 4
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
void setup() {
Serial.begin(115200);
dht.begin();
}
void loop() {
float humidity = dht.readHumidity();
float tempC = dht.readTemperature();
float tempF = dht.readTemperature(true);
// Check for read errors
if (isnan(humidity) || isnan(tempC)) {
Serial.println("DHT read failed!");
delay(2000);
return;
}
// Calculate heat index
float heatIndex = dht.computeHeatIndex(tempC, humidity, false);
Serial.printf("Temp: %.1f°C (%.1f°F) \\ n", tempC, tempF);
Serial.printf("Humidity: %.1f%% \\ n", humidity);
Serial.printf("Heat Index: %.1f°C \\ n", heatIndex);
delay(2000); // DHT22 needs 2s between reads
}` ,
explanation : "DHT22 requires a 2-second minimum delay between readings. Always check for NaN values as the sensor can fail reads. The heat index calculation combines temperature and humidity for 'feels like' temperature."
},
{
id : "sensor-analog" ,
title : "Analog Sensor with Averaging" ,
category : "Sensors" ,
platform : "Arduino/ESP32" ,
difficulty : "Beginner" ,
tags : ["analog" , "adc" , "averaging" , "noise" ],
description : "Read analog sensor with multi-sample averaging to reduce noise" ,
code : `const int SENSOR_PIN = 34; // ADC1 pin on ESP32
const int NUM_SAMPLES = 10;
const float V_REF = 3.3;
const int ADC_MAX = 4095; // 12-bit ADC
float readAnalogAveraged(int pin, int samples) {
long sum = 0;
for (int i = 0; i < samples; i++) {
sum += analogRead(pin);
delay(1); // Small delay between samples
}
return (float)sum / samples;
}
float adcToVoltage(float adcValue) {
return (adcValue / ADC_MAX) * V_REF;
}
void setup() {
Serial.begin(115200);
analogReadResolution(12); // ESP32: 9-12 bits
analogSetAttenuation(ADC_11db); // 0-3.3V range
}
void loop() {
float avgReading = readAnalogAveraged(SENSOR_PIN, NUM_SAMPLES);
float voltage = adcToVoltage(avgReading);
Serial.printf("ADC: %.1f, Voltage: %.3fV \\ n", avgReading, voltage);
delay(100);
}` ,
explanation : "Multi-sample averaging reduces ADC noise significantly. For ESP32, use ADC1 pins (GPIO 32-39) when using Wi-Fi. Attenuation setting determines voltage range: 0dB (0-1.1V), 2.5dB (0-1.5V), 6dB (0-2.2V), 11dB (0-3.3V)."
},
{
id : "sensor-i2c-scan" ,
title : "I2C Device Scanner" ,
category : "Sensors" ,
platform : "Arduino/ESP32" ,
difficulty : "Beginner" ,
tags : ["i2c" , "scanner" , "debug" , "address" ],
description : "Scan I2C bus to find connected device addresses" ,
code : `#include <Wire.h>
void setup() {
Serial.begin(115200);
Wire.begin(); // Default pins: SDA=21, SCL=22 on ESP32
Serial.println("I2C Scanner Starting...");
scanI2C();
}
void scanI2C() {
byte deviceCount = 0;
Serial.println("Scanning...");
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0) {
Serial.printf("Device found at 0x%02X", addr);
// Common device identification
switch(addr) {
case 0x3C: case 0x3D: Serial.print(" (OLED Display)"); break;
case 0x40: Serial.print(" (HDC1080/INA219)"); break;
case 0x48: Serial.print(" (ADS1115/TMP102)"); break;
case 0x50: Serial.print(" (EEPROM)"); break;
case 0x68: Serial.print(" (MPU6050/DS3231)"); break;
case 0x76: case 0x77: Serial.print(" (BME280/BMP280)"); break;
}
Serial.println();
deviceCount++;
}
}
Serial.printf(" \\ nFound %d device(s) \\ n", deviceCount);
}
void loop() {
delay(5000);
scanI2C(); // Rescan periodically
}` ,
explanation : "I2C addresses are 7-bit (1-127). Common ranges: 0x20-0x27 (PCF8574), 0x48-0x4F (ADC), 0x50-0x57 (EEPROM), 0x68-0x6F (RTC/IMU), 0x76-0x77 (pressure sensors)."
},
// ===== MQTT =====
{
id : "mqtt-publish" ,
title : "MQTT Publish with Reconnection" ,
category : "MQTT" ,
platform : "ESP32" ,
difficulty : "Intermediate" ,
tags : ["mqtt" , "publish" , "wifi" , "reconnect" ],
description : "Robust MQTT publishing with automatic Wi-Fi and broker reconnection" ,
code : `#include <WiFi.h>
#include <PubSubClient.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-password";
const char* MQTT_SERVER = "broker.hivemq.com";
const int MQTT_PORT = 1883;
const char* MQTT_CLIENT_ID = "esp32-sensor-01";
const char* MQTT_TOPIC = "iot/sensors/temperature";
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
unsigned long lastPublish = 0;
const long PUBLISH_INTERVAL = 5000;
void connectWiFi() {
if (WiFi.status() == WL_CONNECTED) return;
Serial.printf("Connecting to %s", WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASS);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.printf(" \\ nConnected! IP: %s \\ n", WiFi.localIP().toString().c_str());
} else {
Serial.println(" \\ nWi-Fi connection failed!");
}
}
void connectMQTT() {
while (!mqtt.connected()) {
Serial.print("Connecting to MQTT...");
if (mqtt.connect(MQTT_CLIENT_ID)) {
Serial.println("connected!");
} else {
Serial.printf("failed (rc=%d), retry in 5s \\ n", mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
mqtt.setServer(MQTT_SERVER, MQTT_PORT);
connectWiFi();
connectMQTT();
}
void loop() {
// Maintain connections
if (WiFi.status() != WL_CONNECTED) connectWiFi();
if (!mqtt.connected()) connectMQTT();
mqtt.loop();
// Publish periodically
if (millis() - lastPublish >= PUBLISH_INTERVAL) {
float temperature = 25.5; // Replace with sensor reading
char payload[50];
snprintf(payload, sizeof(payload), "{ \\ "temp \\ ":%.1f}", temperature);
if (mqtt.publish(MQTT_TOPIC, payload)) {
Serial.printf("Published: %s \\ n", payload);
} else {
Serial.println("Publish failed!");
}
lastPublish = millis();
}
}` ,
explanation : "PubSubClient requires mqtt.loop() in every iteration. Always check both Wi-Fi and MQTT connections. Use snprintf for safe string formatting. Client ID must be unique per broker connection."
},
{
id : "mqtt-subscribe" ,
title : "MQTT Subscribe with Callback" ,
category : "MQTT" ,
platform : "ESP32" ,
difficulty : "Intermediate" ,
tags : ["mqtt" , "subscribe" , "callback" , "json" ],
description : "Subscribe to MQTT topics and handle incoming messages with JSON parsing" ,
code : `#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-password";
const char* MQTT_SERVER = "broker.hivemq.com";
const char* MQTT_TOPIC_CMD = "iot/device/+/command"; // + is wildcard
const char* MQTT_TOPIC_STATUS = "iot/device/esp32-01/status";
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
// Callback for incoming messages
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.printf("Message on [%s]: ", topic);
// Convert payload to string
char message[length + 1];
memcpy(message, payload, length);
message[length] = ' \\ 0';
Serial.println(message);
// Parse JSON
StaticJsonDocument<200> doc;
DeserializationError error = deserializeJson(doc, message);
if (error) {
Serial.printf("JSON parse failed: %s \\ n", error.c_str());
return;
}
// Handle commands
const char* command = doc["cmd"];
if (strcmp(command, "led_on") == 0) {
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("LED turned ON");
} else if (strcmp(command, "led_off") == 0) {
digitalWrite(LED_BUILTIN, LOW);
Serial.println("LED turned OFF");
} else if (strcmp(command, "status") == 0) {
publishStatus();
}
}
void publishStatus() {
StaticJsonDocument<200> doc;
doc["device"] = "esp32-01";
doc["uptime"] = millis() / 1000;
doc["rssi"] = Wi-Fi.RSSI();
doc["heap"] = ESP.getFreeHeap();
char buffer[200];
serializeJson(doc, buffer);
mqtt.publish(MQTT_TOPIC_STATUS, buffer);
}
void connectMQTT() {
while (!mqtt.connected()) {
if (mqtt.connect("esp32-01")) {
mqtt.subscribe(MQTT_TOPIC_CMD);
Serial.println("Subscribed to command topic");
} else {
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer(MQTT_SERVER, 1883);
mqtt.setCallback(mqttCallback);
connectMQTT();
}
void loop() {
if (!mqtt.connected()) connectMQTT();
mqtt.loop();
}` ,
explanation : "MQTT wildcards: + matches one level, # matches all remaining levels. Always set callback before connecting. ArduinoJson StaticJsonDocument is stack-allocated (faster, fixed size). Use DynamicJsonDocument for variable-size messages."
},
// ===== HTTP/REST =====
{
id : "http-post-json" ,
title : "HTTP POST with JSON" ,
category : "HTTP" ,
platform : "ESP32" ,
difficulty : "Intermediate" ,
tags : ["http" , "post" , "json" , "rest" , "api" ],
description : "Send sensor data to REST API using HTTP POST with JSON body" ,
code : `#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-password";
const char* API_ENDPOINT = "https://api.example.com/sensor-data";
const char* API_KEY = "your-api-key";
void sendSensorData(float temperature, float humidity) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Wi-Fi not connected!");
return;
}
HTTPClient http;
http.begin(API_ENDPOINT);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", String("Bearer ") + API_KEY);
http.setTimeout(10000); // 10 second timeout
// Build JSON payload
StaticJsonDocument<200> doc;
doc["device_id"] = "esp32-sensor-01";
doc["temperature"] = temperature;
doc["humidity"] = humidity;
doc["timestamp"] = millis();
String payload;
serializeJson(doc, payload);
Serial.printf("Sending: %s \\ n", payload.c_str());
int httpCode = http.POST(payload);
if (httpCode > 0) {
Serial.printf("HTTP Response: %d \\ n", httpCode);
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED) {
String response = http.getString();
Serial.printf("Response: %s \\ n", response.c_str());
}
} else {
Serial.printf("HTTP Error: %s \\ n", http.errorToString(httpCode).c_str());
}
http.end();
}
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" \\ nWi-Fi connected!");
}
void loop() {
float temp = 23.5 + random(-10, 10) / 10.0;
float hum = 55.0 + random(-50, 50) / 10.0;
sendSensorData(temp, hum);
delay(30000); // Send every 30 seconds
}` ,
explanation : "Always call http.end() to free resources. HTTP response codes: 200=OK, 201=Created, 400=Bad Request, 401=Unauthorized, 500=Server Error. Use https:// for secure connections (ESP32 supports TLS)."
},
// ===== DEEP SLEEP =====
{
id : "deep-sleep-timer" ,
title : "Deep Sleep with Timer Wakeup" ,
category : "Power" ,
platform : "ESP32" ,
difficulty : "Intermediate" ,
tags : ["sleep" , "power" , "battery" , "timer" , "wakeup" ],
description : "Ultra-low power deep sleep with timed wakeup for battery-powered sensors" ,
code : `#include <WiFi.h>
#include <esp_sleep.h>
#define uS_TO_S_FACTOR 1000000ULL // Microseconds to seconds
#define SLEEP_DURATION_SEC 300 // 5 minutes
RTC_DATA_ATTR int bootCount = 0; // Survives deep sleep
void printWakeupReason() {
esp_sleep_wakeup_cause_t reason = esp_sleep_get_wakeup_cause();
switch(reason) {
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("Wakeup: Timer");
break;
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("Wakeup: External (RTC_IO)");
break;
case ESP_SLEEP_WAKEUP_EXT1:
Serial.println("Wakeup: External (RTC_CNTL)");
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
Serial.println("Wakeup: Touchpad");
break;
default:
Serial.printf("Wakeup: Other (%d) \\ n", reason);
}
}
void doWork() {
// Quick Wi-Fi connect
WiFi.begin("ssid", "password");
WiFi.setSleep(false); // Disable modem sleep for faster connection
int timeout = 0;
while (WiFi.status() != WL_CONNECTED && timeout < 20) {
delay(500);
timeout++;
}
if (WiFi.status() == WL_CONNECTED) {
// Send data quickly
Serial.printf("Boot #%d - IP: %s \\ n", bootCount, WiFi.localIP().toString().c_str());
// ... send sensor data ...
}
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}
void goToSleep() {
Serial.printf("Sleeping for %d seconds... \\ n", SLEEP_DURATION_SEC);
Serial.flush();
esp_sleep_enable_timer_wakeup(SLEEP_DURATION_SEC * uS_TO_S_FACTOR);
esp_deep_sleep_start();
// Code below this never runs
}
void setup() {
Serial.begin(115200);
delay(100);
bootCount++;
Serial.printf(" \\ n=== Boot #%d === \\ n", bootCount);
printWakeupReason();
doWork();
goToSleep();
}
void loop() {
// Never reached in deep sleep mode
}` ,
explanation : "RTC_DATA_ATTR variables persist across deep sleep. ESP32 deep sleep current: ~10uA. Timer wakeup has ~150ms boot time. Use esp_wifi_start() with stored credentials for faster reconnection (<1s vs ~3s)."
},
{
id : "deep-sleep-gpio" ,
title : "Deep Sleep with GPIO Wakeup" ,
category : "Power" ,
platform : "ESP32" ,
difficulty : "Intermediate" ,
tags : ["sleep" , "power" , "gpio" , "interrupt" , "wakeup" ],
description : "Wake from deep sleep on button press or sensor trigger" ,
code : `#include <esp_sleep.h>
#define WAKEUP_PIN GPIO_NUM_33 // RTC GPIO
#define LED_PIN 2
RTC_DATA_ATTR int wakeupCount = 0;
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
pinMode(WAKEUP_PIN, INPUT_PULLUP);
wakeupCount++;
Serial.printf("Wakeup #%d \\ n", wakeupCount);
// Blink to show we're awake
for (int i = 0; i < 3; i++) {
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(100);
}
// Check wakeup cause
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
Serial.println("Woken by button press!");
// Handle button press event
handleButtonWakeup();
}
// Configure wakeup on LOW signal (button pressed)
esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, 0); // 0 = LOW level
// Optional: Also set timer as backup
esp_sleep_enable_timer_wakeup(3600 * 1000000ULL); // 1 hour
Serial.println("Going to sleep...");
Serial.flush();
// Disable peripherals for lowest power
esp_wifi_stop();
esp_bt_controller_disable();
esp_deep_sleep_start();
}
void handleButtonWakeup() {
// Debounce
delay(50);
if (digitalRead(WAKEUP_PIN) == LOW) {
Serial.println("Button confirmed pressed");
// Do something useful
}
}
void loop() {
// Never reached
}` ,
explanation : "EXT0 wakeup uses one RTC GPIO (0,2,4,12-15,25-27,32-39). EXT1 can use multiple pins with bitmask. Wakeup level: 0=LOW, 1=HIGH. Internal pullups work during deep sleep on RTC GPIOs only."
},
// ===== BLE =====
{
id : "ble-beacon" ,
title : "BLE iBeacon Transmitter" ,
category : "BLE" ,
platform : "ESP32" ,
difficulty : "Intermediate" ,
tags : ["ble" , "beacon" , "ibeacon" , "advertising" ],
description : "Broadcast as an iBeacon for proximity detection and indoor positioning" ,
code : `#include <BLEDevice.h>
#include <BLEBeacon.h>
// iBeacon UUID - generate your own at uuidgenerator.net
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d"
#define MAJOR 1
#define MINOR 1
#define TX_POWER -59 // Calibrated at 1 meter
BLEAdvertising *pAdvertising;
void setBeacon() {
BLEBeacon beacon;
beacon.setManufacturerId(0x4C00); // Apple
beacon.setProximityUUID(BLEUUID(BEACON_UUID));
beacon.setMajor(MAJOR);
beacon.setMinor(MINOR);
beacon.setSignalPower(TX_POWER);
BLEAdvertisementData advData;
advData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED
std::string strBeacon = "";
strBeacon += (char)26; // Length
strBeacon += (char)0xFF; // Type: Manufacturer Specific
strBeacon += beacon.getData();
advData.addData(strBeacon);
pAdvertising->setAdvertisementData(advData);
// Scan response with device name
BLEAdvertisementData scanResponse;
scanResponse.setName("IoT-Beacon-01");
pAdvertising->setScanResponseData(scanResponse);
}
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE Beacon...");
BLEDevice::init("IoT-Beacon");
pAdvertising = BLEDevice::getAdvertising();
setBeacon();
pAdvertising->setAdvertisementType(ADV_TYPE_NONCONN_IND);
pAdvertising->start();
Serial.println("Beacon active!");
Serial.printf("UUID: %s \\ n", BEACON_UUID);
Serial.printf("Major: %d, Minor: %d \\ n", MAJOR, MINOR);
}
void loop() {
delay(1000);
}` ,
explanation : "iBeacon uses UUID (128-bit), Major (16-bit), Minor (16-bit) for identification. TX_POWER is RSSI at 1 meter, used for distance estimation. Calibrate TX_POWER by measuring actual RSSI at 1m."
},
// ===== LoRa =====
{
id : "lora-transmit" ,
title : "LoRa Point-to-Point Transmit" ,
category : "LoRa" ,
platform : "ESP32" ,
difficulty : "Intermediate" ,
tags : ["lora" , "wireless" , "long-range" , "transmit" ],
description : "Send data over LoRa with configurable spreading factor and bandwidth" ,
code : `#include <SPI.h>
#include <LoRa.h>
// Pin definitions for common modules
// TTGO LoRa32: SS=18, RST=14, DIO0=26
// Heltec Wi-Fi LoRa: SS=18, RST=14, DIO0=26
#define LORA_SS 18
#define LORA_RST 14
#define LORA_DIO0 26
#define LORA_FREQ 915E6 // 915MHz for US, 868E6 for EU
struct SensorPacket {
uint8_t nodeId;
uint16_t packetNum;
float temperature;
float humidity;
uint16_t battery; // mV
} __attribute__((packed));
uint16_t packetCounter = 0;
void setup() {
Serial.begin(115200);
SPI.begin(5, 19, 27, LORA_SS); // SCK, MISO, MOSI, SS
LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0);
if (!LoRa.begin(LORA_FREQ)) {
Serial.println("LoRa init failed!");
while (1);
}
// Configure for long range
LoRa.setSpreadingFactor(10); // 7-12, higher = longer range
LoRa.setSignalBandwidth(125E3); // 125kHz standard
LoRa.setCodingRate4(5); // 4/5 to 4/8
LoRa.setTxPower(20); // 2-20 dBm
LoRa.enableCrc();
Serial.println("LoRa Transmitter Ready");
Serial.printf("Frequency: %.1f MHz, SF: %d \\ n",
LORA_FREQ/1E6, LoRa.getSpreadingFactor());
}
void sendPacket() {
SensorPacket packet;
packet.nodeId = 1;
packet.packetNum = packetCounter++;
packet.temperature = 25.5;
packet.humidity = 60.0;
packet.battery = 3700;
LoRa.beginPacket();
LoRa.write((uint8_t*)&packet, sizeof(packet));
LoRa.endPacket();
Serial.printf("Sent packet #%d (%d bytes) \\ n",
packet.packetNum, sizeof(packet));
}
void loop() {
sendPacket();
delay(30000); // Respect duty cycle limits
}` ,
explanation : "LoRa settings trade-off: Higher SF = longer range but slower. SF7@125kHz = 5.5kbps, SF12@125kHz = 0.3kbps. EU868 has 1% duty cycle limit. Use packed structs for efficient binary payloads."
},
// ===== JSON HANDLING =====
{
id : "json-serialize" ,
title : "JSON Serialization Best Practices" ,
category : "Data" ,
platform : "Arduino/ESP32" ,
difficulty : "Beginner" ,
tags : ["json" , "serialize" , "data" , "format" ],
description : "Properly serialize sensor data to JSON with nested objects and arrays" ,
code : `#include <ArduinoJson.h>
void createSensorJson() {
// Calculate required size: https://arduinojson.org/v6/assistant/
StaticJsonDocument<512> doc;
// Basic fields
doc["device_id"] = "sensor-node-01";
doc["firmware"] = "1.2.3";
doc["timestamp"] = millis();
// Nested object for location
JsonObject location = doc.createNestedObject("location");
location["lat"] = 37.7749;
location["lon"] = -122.4194;
location["accuracy"] = 10.5;
// Nested object for readings
JsonObject readings = doc.createNestedObject("readings");
readings["temperature"] = 23.5;
readings["humidity"] = 65.2;
readings["pressure"] = 1013.25;
// Array of alerts
JsonArray alerts = doc.createNestedArray("alerts");
alerts.add("low_battery");
alerts.add("high_temp");
// Array of historical values
JsonArray history = doc.createNestedArray("temp_history");
float temps[] = {22.1, 22.5, 23.0, 23.5};
for (float t : temps) {
history.add(t);
}
// Serialize to string
String output;
serializeJson(doc, output);
Serial.println("Compact:");
Serial.println(output);
// Pretty print
Serial.println(" \\ nPretty:");
serializeJsonPretty(doc, Serial);
// Check memory usage
Serial.printf(" \\ n \\ nMemory used: %d/%d bytes \\ n",
doc.memoryUsage(), doc.capacity());
}
void parseSensorJson(const char* json) {
StaticJsonDocument<512> doc;
DeserializationError error = deserializeJson(doc, json);
if (error) {
Serial.printf("Parse error: %s \\ n", error.c_str());
return;
}
// Safe access with defaults
const char* deviceId = doc["device_id"] | "unknown";
float temp = doc["readings"]["temperature"] | -999.0;
float lat = doc["location"]["lat"] | 0.0;
// Check if field exists
if (doc.containsKey("alerts")) {
JsonArray alerts = doc["alerts"];
Serial.printf("Alerts: %d \\ n", alerts.size());
for (const char* alert : alerts) {
Serial.printf(" - %s \\ n", alert);
}
}
Serial.printf("Device: %s, Temp: %.1f, Lat: %.4f \\ n",
deviceId, temp, lat);
}
void setup() {
Serial.begin(115200);
delay(1000);
createSensorJson();
Serial.println(" \\ n--- Parsing ---");
const char* testJson = "{ \\ "device_id \\ ": \\ "test \\ ", \\ "readings \\ ":{ \\ "temperature \\ ":25.5}}";
parseSensorJson(testJson);
}
void loop() {}` ,
explanation : "StaticJsonDocument is stack-allocated with fixed capacity. Use DynamicJsonDocument for heap allocation when size varies. The | operator provides default values for missing fields. Always check DeserializationError."
},
// ===== OTA UPDATE =====
{
id : "ota-basic" ,
title : "Over-the-Air (OTA) Updates" ,
category : "Updates" ,
platform : "ESP32" ,
difficulty : "Advanced" ,
tags : ["ota" , "update" , "firmware" , "wireless" ],
description : "Enable wireless firmware updates for deployed IoT devices" ,
code : `#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-password";
const char* OTA_HOSTNAME = "esp32-sensor-01";
const char* OTA_PASSWORD = "update123"; // Optional
void setupOTA() {
ArduinoOTA.setHostname(OTA_HOSTNAME);
ArduinoOTA.setPassword(OTA_PASSWORD);
ArduinoOTA.onStart([]() {
String type = (ArduinoOTA.getCommand() == U_FLASH)
? "firmware" : "filesystem";
Serial.printf("OTA Start: %s \\ n", type.c_str());
});
ArduinoOTA.onEnd([]() {
Serial.println(" \\ nOTA Complete!");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%% \\ r", (progress * 100) / total);
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("OTA Error[%u]: ", error);
switch (error) {
case OTA_AUTH_ERROR: Serial.println("Auth Failed"); break;
case OTA_BEGIN_ERROR: Serial.println("Begin Failed"); break;
case OTA_CONNECT_ERROR: Serial.println("Connect Failed"); break;
case OTA_RECEIVE_ERROR: Serial.println("Receive Failed"); break;
case OTA_END_ERROR: Serial.println("End Failed"); break;
}
});
ArduinoOTA.begin();
Serial.printf("OTA Ready! Hostname: %s.local \\ n", OTA_HOSTNAME);
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.printf(" \\ nConnected: %s \\ n", WiFi.localIP().toString().c_str());
setupOTA();
}
void loop() {
ArduinoOTA.handle(); // Must be called regularly!
// Your normal code here
delay(10);
}` ,
explanation : "Upload via Arduino IDE: Tools > Port > Network ports. Or use espota.py script. OTA requires ~50% free flash (app runs while updating). Use partitions with OTA support (e.g., min_spiffs). ArduinoOTA.handle() must be called frequently."
},
// ===== WATCHDOG =====
{
id : "watchdog-timer" ,
title : "Watchdog Timer for Reliability" ,
category : "Reliability" ,
platform : "ESP32" ,
difficulty : "Intermediate" ,
tags : ["watchdog" , "reliability" , "crash" , "recovery" ],
description : "Automatic reset if code hangs or crashes" ,
code : `#include <esp_task_wdt.h>
#define WDT_TIMEOUT_SEC 30 // Reset if no feed for 30s
void setup() {
Serial.begin(115200);
// Configure watchdog
esp_task_wdt_init(WDT_TIMEOUT_SEC, true); // true = panic on timeout
esp_task_wdt_add(NULL); // Add current task to watchdog
Serial.printf("Watchdog configured: %ds timeout \\ n", WDT_TIMEOUT_SEC);
// Check if last reset was from watchdog
esp_reset_reason_t reason = esp_reset_reason();
if (reason == ESP_RST_TASK_WDT || reason == ESP_RST_WDT) {
Serial.println("WARNING: Last reset was from watchdog!");
// Log this event, maybe reduce workload
}
}
void doSensorWork() {
// Simulate sensor reading
Serial.println("Reading sensors...");
delay(1000);
// Feed watchdog after successful operation
esp_task_wdt_reset();
}
void doNetworkWork() {
// Simulate network operation
Serial.println("Sending data...");
delay(2000);
// Feed watchdog
esp_task_wdt_reset();
}
void loop() {
static unsigned long lastRun = 0;
if (millis() - lastRun > 5000) {
lastRun = millis();
doSensorWork();
doNetworkWork();
// Uncomment to test watchdog (will reset after WDT_TIMEOUT_SEC)
// while(1) { delay(100); }
}
// Feed watchdog in main loop too
esp_task_wdt_reset();
delay(100);
}` ,
explanation : "Watchdog resets ESP32 if not fed within timeout. Place esp_task_wdt_reset() after each major operation. Don't set timeout too short - network operations can take seconds. Check reset reason to detect and log watchdog events."
},
// ===== DATA STRUCTURES =====
{
id : "ring-buffer" ,
title : "Ring Buffer for Sensor Data" ,
category : "Data" ,
platform : "Arduino/ESP32" ,
difficulty : "Intermediate" ,
tags : ["buffer" , "queue" , "circular" , "memory" ],
description : "Efficient circular buffer for storing recent sensor readings" ,
code : `template<typename T, size_t SIZE>
class RingBuffer {
private:
T buffer[SIZE];
size_t head = 0;
size_t tail = 0;
size_t count = 0;
public:
bool push(const T& item) {
buffer[head] = item;
head = (head + 1) % SIZE;
if (count < SIZE) {
count++;
} else {
tail = (tail + 1) % SIZE; // Overwrite oldest
}
return true;
}
bool pop(T& item) {
if (count == 0) return false;
item = buffer[tail];
tail = (tail + 1) % SIZE;
count--;
return true;
}
bool peek(T& item) const {
if (count == 0) return false;
item = buffer[tail];
return true;
}
T& operator[](size_t index) {
return buffer[(tail + index) % SIZE];
}
size_t size() const { return count; }
size_t capacity() const { return SIZE; }
bool isEmpty() const { return count == 0; }
bool isFull() const { return count == SIZE; }
float average() const {
if (count == 0) return 0;
float sum = 0;
for (size_t i = 0; i < count; i++) {
sum += buffer[(tail + i) % SIZE];
}
return sum / count;
}
void clear() {
head = tail = count = 0;
}
};
// Usage example
RingBuffer<float, 60> temperatureHistory; // Last 60 readings
void setup() {
Serial.begin(115200);
}
void loop() {
// Simulate sensor reading
float temp = 20.0 + random(0, 100) / 10.0;
temperatureHistory.push(temp);
Serial.printf("Temp: %.1f, Buffer: %d/%d, Avg: %.1f \\ n",
temp,
temperatureHistory.size(),
temperatureHistory.capacity(),
temperatureHistory.average());
delay(1000);
}` ,
explanation : "Ring buffers are O(1) for push/pop and use fixed memory. Perfect for storing N most recent readings. When full, oldest data is automatically overwritten. Use for rolling averages, trend detection, and buffering before transmission."
}
]
// Extract unique values
categories = [... new Set (snippets. map (s => s. category ))]. sort ()
platforms = [... new Set (snippets. map (s => s. platform ))]. sort ()
difficulties = ["Beginner" , "Intermediate" , "Advanced" ]
viewof filterCategory = Inputs. select (["All Categories" , ... categories], {
label : "Category:" ,
value : "All Categories"
})
viewof filterPlatform = Inputs. select (["All Platforms" , ... platforms], {
label : "Platform:" ,
value : "All Platforms"
})
viewof filterDifficulty = Inputs. select (["All Levels" , ... difficulties], {
label : "Difficulty:" ,
value : "All Levels"
})
viewof searchTerm = Inputs. text ({
label : "Search:" ,
placeholder : "mqtt, sensor, sleep, json..."
})