Complete IoT monitoring platform for Acuvim II power meters via ESP32. Firmware (Phases 1-7): - ESP32-WROVER-B (TTGO T-Call v1.4) with RS485 Modbus RTU - WiFi STA+AP concurrent mode with GSM/GPRS failover - Transport abstraction layer with 4 priority modes - MQTT protocol with 20 commands, LWT, QoS, exponential backoff - SD card offline buffering with JSONL rotation and non-blocking drain - OTA firmware updates with dual partition rollback protection - Watchdog timer, crash loop detection, Acuvim health monitoring - Captive portal provisioning with AP mode Console backend (Phase 8): - .NET 10 minimal API with PostgreSQL + EF Core - JWT authentication, SignalR real-time updates - MQTTnet 5.x bridge service with health monitoring - Device, telemetry, firmware, alert, group management - Rate limiting, security headers, Swagger/OpenAPI Frontend (Phase 9): - React 18 + TypeScript + Vite with Ant Design 5 - ECharts telemetry visualization, TanStack Query - SignalR live updates, device management UI - Dashboard, fleet management, firmware deployment Testing & Production (Phase 10): - 28 firmware unit tests (Modbus, JSON, config, version) - 23 xUnit backend tests (device, telemetry, command, alert) - Docker Compose with nginx, TLS MQTT, PostgreSQL - Production deployment, commissioning, and troubleshooting docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
71 lines
1.7 KiB
C++
71 lines
1.7 KiB
C++
#ifndef SD_MANAGER_H
|
|
#define SD_MANAGER_H
|
|
|
|
#include <Arduino.h>
|
|
#include <SD.h>
|
|
#include <SPI.h>
|
|
#include "pin_config.h"
|
|
#include "acuvim_registers.h"
|
|
|
|
class MqttClient;
|
|
|
|
class SdManager {
|
|
public:
|
|
SdManager();
|
|
|
|
bool begin(const char* deviceId);
|
|
bool isAvailable() { return _mounted; }
|
|
|
|
void setRetentionDays(uint16_t days) { _retentionDays = days; }
|
|
|
|
bool bufferTelemetry(const AcuvimData& data);
|
|
uint32_t getQueuedCount() { return _queuedCount; }
|
|
bool hasQueuedData() { return _queuedCount > 0; }
|
|
|
|
bool drainBatch(MqttClient& mqtt, int batchSize = 5);
|
|
|
|
uint64_t getTotalSpace();
|
|
uint64_t getUsedSpace();
|
|
uint64_t getFreeSpace();
|
|
uint32_t getFileCount();
|
|
uint32_t cleanup(uint32_t maxAgeDays = 0);
|
|
uint16_t getRetentionDays() { return _retentionDays; }
|
|
|
|
void loop();
|
|
|
|
private:
|
|
SPIClass _hspi;
|
|
bool _mounted;
|
|
char _deviceId[32];
|
|
uint16_t _retentionDays;
|
|
|
|
String _currentFileName;
|
|
uint32_t _recordsInCurrentFile;
|
|
uint32_t _currentFileSize;
|
|
uint32_t _queuedCount;
|
|
|
|
bool _drainActive;
|
|
String _drainFilePath;
|
|
uint32_t _drainByteOffset;
|
|
|
|
unsigned long _lastCardCheckMs;
|
|
unsigned long _lastCleanupMs;
|
|
|
|
static const unsigned long CARD_CHECK_INTERVAL_MS = 30000;
|
|
static const unsigned long CLEANUP_INTERVAL_MS = 3600000;
|
|
static const uint32_t MAX_FILE_SIZE = 500 * 1024;
|
|
static const uint32_t MAX_RECORDS_PER_FILE = 5000;
|
|
|
|
void ensureDirectories();
|
|
String generateFileName();
|
|
bool rotateFile();
|
|
bool shouldRotate();
|
|
void checkCard();
|
|
uint32_t countExistingRecords();
|
|
String getDateString();
|
|
uint32_t getEpochTime();
|
|
String findOldestFile(const char* dir);
|
|
};
|
|
|
|
#endif
|