← SD Card Datalogging on an ESP32 PLC with Daily Files
Textile monitoring (weaving)ESP32 PLCSDDatalogging
SD Card Datalogging on an ESP32 PLC with Daily Files — full example
SD card datalogging on an ESP32 PLC with one JSON file per day: RTC-named files, hot remount, offline buffering and recovery of pending machine data.
Complete, runnable program for the ESP32 PLC (sd-daily-file-datalogging.ino): wiring header, requirements and integration notes included.
Download the full project pack — freeThis example + the related ones + bill of materials
Read-only preview.
/*
* COMPLETE EXAMPLE — SD datalogging with daily file and offline buffering
*
* Hardware: ESP32 PLC (Industrial Shields, with microSD slot + RTC)
* Based on: textile monitoring project, modul_SD.h
*
* Wiring / hardware:
* microSD formatted FAT32, in the PLC slot
* RTC integrated chip of the PLC (valid time even without network)
*
* Offline-first architecture:
* 1. Every JSON message is appended to /YYYY-MM-DD.json (one file per day)
* 2. The file name comes from the hardware RTC -> automatic rotation
* at midnight without extra logic
* 3. The SD write ALWAYS happens before the network send: if MQTT
* or WiFi go down, the data stays on the card
* 4. Inventory function that lists the pending files so the HTTP
* upload module can recover them later
*
* Works together with other catalog examples:
* - rtc-ntp-synchronization.ino (correct time for the file names)
* - sd-file-upload-http-post.ino (recovery)
* - mqtt-events-sd-buffering.ino (source of the messages)
*/
#include
#include // integrated RTC of the PLC (Industrial Shields library)
const int PLC_ID = 1;
bool sdAvailable = false;
uint32_t tDemo = 0;
uint32_t demoCounter = 0;
// ------------------------------------------------- Daily file name
// The RTC rules: without a valid time, data goes to a fallback file
String dailyFileName() {
if (!RTC.read()) return "/error_date.json";
char n[20];
sprintf(n, "/%04d-%02d-%02d.json", RTC.getYear(), RTC.getMonth(), RTC.getMonthDay());
return String(n);
}
// ------------------------------------------------- Append write
// Returns true if the record was persisted; the caller decides whether to retry
bool saveToSD(const String &json) {
if (!sdAvailable) {
sdAvailable = SD.begin(); // hot remount retry
if (!sdAvailable) return false;
}
File f = SD.open(dailyFileName(), FILE_APPEND);
if (!f) { sdAvailable = false; return false; }
f.println(json);
f.close();
return true;
}
// ------------------------------------------------- History inventory
// Lists the .json files in the root: this is what the MQTT "list" command
// returns and what the HTTP upload module walks to recover pending days
void listFiles() {
File root = SD.open("/");
if (!root) return;
Serial.println("--- Files on SD ---");
File f = root.openNextFile();
while (f) {
if (!f.isDirectory() && String(f.name()).endsWith(".json")) {
Serial.print(f.name());
Serial.print(" ");
Serial.print(f.size());
Serial.println(" bytes");
}
f = root.openNextFile();
}
root.close();
}
void setup() {
Serial.begin(115200);
RTC.begin(); // in production: synchronized via NTP
sdAvailable = SD.begin();
Serial.println(sdAvailable ? "SD mounted" : "SD NOT available");
listFiles(); // which days are still pending upload
}
void loop() {
// Demo: generate one record every 5 s, like the production snapshot would
if (millis() - tDemo >= 5000) {
tDemo = millis();
demoCounter++;
String json = "{\"id\":" + String(PLC_ID) +
",\"ts\":" + String(RTC.getTime()) +
",\"sample\":" + String(demoCounter) + "}";
if (saveToSD(json)) {
Serial.println("OK -> " + dailyFileName() + " : " + json);
} else {
Serial.println("SD FAILURE (check card): " + json);
// In production the message is also attempted over MQTT, so that
// SD and network cover each other (two-way offline-first)
}
}
delay(20);
}
Download the full project pack — freeThis example + the related ones + bill of materials