Generalized-Core-Counter 3.20
Particle-based generalized core counter firmware
Loading...
Searching...
No Matches
MyPersistentData.cpp
Go to the documentation of this file.
1
34
35#include "MyPersistentData.h"
36
37// Forward declaration for safe diagnostic publishing (defined in Generalized-Core-Counter.cpp)
38bool publishDiagnosticSafe(const char* eventName, const char* data, PublishFlags flags = PRIVATE);
39
40// ******************* SysStatus Storage Object **********************
41//
42// ********************************************************************
43
44const char *persistentDataPathSystem = "/usr/sysStatus.dat";
45
47
48// [static]
50 if (!_instance) {
52 }
53 return *_instance;
54}
55
56sysStatusData::sysStatusData() : StorageHelperRK::PersistentDataFile(persistentDataPathSystem, &sysData.sysHeader, sizeof(SysData), SYS_DATA_MAGIC, SYS_DATA_VERSION) {
57
58};
59
62
65 // .withLogData(true)
66 .withSaveDelayMs(100)
67 .load();
68
69 // Log.info("sizeof(SysData): %u", sizeof(SysData));
70}
71
73 sysStatus.flush(false);
74}
75
76bool sysStatusData::validate(size_t dataSize) {
77 bool valid = PersistentDataFile::validate(dataSize);
78 if (valid) {
79 // If test1 < 0 or test1 > 100, then the data is invalid
80
81 // openTime is an hour-of-day in local time (0-23)
82 if (sysStatus.get_openTime() > 23) {
83 Log.info("data not valid open time =%d", sysStatus.get_openTime());
84 valid = false;
85 }
86
87 // closeTime is allowed to be 24 as a sentinel for "always open"
88 if (sysStatus.get_closeTime() > 24) {
89 Log.info("data not valid close time =%d", sysStatus.get_closeTime());
90 valid = false;
91 }
92
93 // Last connection duration sanity check (seconds)
94 if (sysStatus.get_lastConnectionDuration() > 900) {
95 Log.info("data not valid last connection duration =%d", sysStatus.get_lastConnectionDuration());
96 valid = false;
97 }
98 }
99 Log.info("sysStatus data is %s",(valid) ? "valid": "not valid");
100 return valid;
101}
102
104 PersistentDataFile::initialize();
105
106 const char message[26] = "Loading System Defaults";
107 Log.info(message);
108 if (Particle.connected()) publishDiagnosticSafe("Mode", message, PRIVATE);
109 Log.info("Loading system defaults");
110 sysStatus.set_structuresVersion(1);
111 sysStatus.set_verboseMode(false);
112 sysStatus.set_lowBatteryMode(false);
113 sysStatus.set_solarPowerMode(true);
114 sysStatus.set_lowPowerMode(false); // Legacy flag - kept for storage compatibility
115 sysStatus.set_timeZoneStr("SGT-8"); // Default to Singapore Time (POSIX TZ string for UTC+8, no DST)
116 sysStatus.set_sensorType(1); // PIR sensor
117 sysStatus.set_openTime(0);
118 sysStatus.set_closeTime(24); // New standard with v20
119 sysStatus.set_lastConnectionDuration(0); // New measure
120 sysStatus.set_lastDailyCleanup(0); // No cleanup has run yet
121
122 // ********** Operating Mode Defaults **********
123 sysStatus.set_countingMode(COUNTING); // Default to counting mode
124 sysStatus.set_operatingMode(CONNECTED); // Default to connected mode
125 sysStatus.set_occupancyDebounceMs(0); // Default 0 ms; only used in OCCUPANCY mode
126 sysStatus.set_connectedReportingIntervalSec(300); // Default 5 minutes when connected
127 sysStatus.set_lowPowerReportingIntervalSec(3600); // Default 1 hour when in low power
128 sysStatus.set_connectAttemptBudgetSec(300); // Default 300s (5 minutes) max connect attempt per wake
129 sysStatus.set_cloudDisconnectBudgetSec(15); // Default 15s max wait for cloud disconnect
130 sysStatus.set_modemOffBudgetSec(30); // Default 30s max wait for modem power-down
131}
132
134 return getValue<uint8_t>(offsetof(SysData, structuresVersion));
135}
136
138 setValue<uint8_t>(offsetof(SysData, structuresVersion), value);
139}
140
142 return getValue<bool>(offsetof(SysData,verboseMode));
143}
144
146 setValue<bool>(offsetof(SysData, verboseMode), value);
147}
148
150 return getValue<bool>(offsetof(SysData,solarPowerMode ));
151}
153 setValue<bool>(offsetof(SysData, solarPowerMode), value);
154}
155
157 return getValue<bool>(offsetof(SysData,lowPowerMode ));
158}
160 setValue<bool>(offsetof(SysData, lowPowerMode), value);
161}
162
164 return getValue<bool>(offsetof(SysData, lowBatteryMode));
165}
167 setValue<bool>(offsetof(SysData, lowBatteryMode), value);
168}
169
171 return getValue<uint8_t>(offsetof(SysData,resetCount));
172}
173void sysStatusData::set_resetCount(uint8_t value) {
174 setValue<uint8_t>(offsetof(SysData, resetCount), value);
175}
176
178 String result;
179 getValueString(offsetof(SysData, timeZoneStr), sizeof(SysData::timeZoneStr), result);
180 return result;
181}
182
183bool sysStatusData::set_timeZoneStr(const char *str) {
184 return setValueString(offsetof(SysData, timeZoneStr), sizeof(SysData::timeZoneStr), str);
185}
186
188 return getValue<uint8_t>(offsetof(SysData,openTime));
189}
190void sysStatusData::set_openTime(uint8_t value) {
191 setValue<uint8_t>(offsetof(SysData, openTime), value);
192}
193
195 return getValue<uint8_t>(offsetof(SysData,closeTime));
196}
197void sysStatusData::set_closeTime(uint8_t value) {
198 setValue<uint8_t>(offsetof(SysData, closeTime), value);
199}
200
202 return getValue<time_t>(offsetof(SysData,lastReport));
203}
205 setValue<time_t>(offsetof(SysData, lastReport), value);
206}
207
209 return getValue<time_t>(offsetof(SysData,lastConnection));
210}
212 setValue<time_t>(offsetof(SysData, lastConnection), value);
213}
214
216 return getValue<uint16_t>(offsetof(SysData,lastConnectionDuration));
217}
219 setValue<uint16_t>(offsetof(SysData, lastConnectionDuration), value);
220}
221
223 return getValue<time_t>(offsetof(SysData,lastHookResponse));
224}
226 setValue<time_t>(offsetof(SysData, lastHookResponse), value);
227}
228
230 return getValue<uint8_t>(offsetof(SysData,sensorType));
231}
232void sysStatusData::set_sensorType(uint8_t value) {
233 setValue<uint8_t>(offsetof(SysData, sensorType), value);
234}
235
237 return getValue<bool>(offsetof(SysData,updatesPending));
238}
240 setValue<bool>(offsetof(SysData,updatesPending), value);
241}
242
244 return getValue<uint16_t>(offsetof(SysData,reportingInterval));
245}
247 setValue<uint16_t>(offsetof(SysData, reportingInterval), value);
248}
249
251 return getValue<bool>(offsetof(SysData,disconnectedMode));
252}
254 setValue<bool>(offsetof(SysData,disconnectedMode), value);
255}
256
258 return getValue<bool>(offsetof(SysData,serialConnected));
259}
261 setValue<bool>(offsetof(SysData,serialConnected), value);
262}
263
265 return getValue<time_t>(offsetof(SysData,lastDailyCleanup));
266}
268 setValue<time_t>(offsetof(SysData, lastDailyCleanup), value);
269}
270
272 return getValue<time_t>(offsetof(SysData,lastTimeSync));
273}
275 setValue<time_t>(offsetof(SysData, lastTimeSync), value);
276}
277
278// ********** Operating Mode Configuration Get/Set Functions **********
279
281 return getValue<uint8_t>(offsetof(SysData,countingMode));
282}
284 setValue<uint8_t>(offsetof(SysData,countingMode), value);
285}
286
288 return getValue<uint8_t>(offsetof(SysData,operatingMode));
289}
291 setValue<uint8_t>(offsetof(SysData,operatingMode), value);
292}
293
295 return getValue<uint32_t>(offsetof(SysData,occupancyDebounceMs));
296}
298 setValue<uint32_t>(offsetof(SysData,occupancyDebounceMs), value);
299}
300
302 return getValue<uint16_t>(offsetof(SysData,connectedReportingIntervalSec));
303}
305 setValue<uint16_t>(offsetof(SysData,connectedReportingIntervalSec), value);
306}
307
309 return getValue<uint16_t>(offsetof(SysData,lowPowerReportingIntervalSec));
310}
312 setValue<uint16_t>(offsetof(SysData,lowPowerReportingIntervalSec), value);
313}
314
316 return getValue<uint16_t>(offsetof(SysData,connectAttemptBudgetSec));
317}
319 setValue<uint16_t>(offsetof(SysData,connectAttemptBudgetSec), value);
320}
321
323 return getValue<uint16_t>(offsetof(SysData,cloudDisconnectBudgetSec));
324}
326 setValue<uint16_t>(offsetof(SysData,cloudDisconnectBudgetSec), value);
327}
328
330 return getValue<uint16_t>(offsetof(SysData,modemOffBudgetSec));
331}
333 setValue<uint16_t>(offsetof(SysData,modemOffBudgetSec), value);
334}
335
336// End of sysStatusData class
337
338// ***************** Sensor Config Storage Object *******************
339//
340// ********************************************************************
341
342const char *persistentDataPathSensor = "/usr/sensor.dat";
343
345
346// [static]
348 if (!_instance) {
350 }
351 return *_instance;
352}
353
355};
356
359
362 // .withLogData(true)
363 .withSaveDelayMs(250)
364 .load();
365}
366
368 sensorConfig.flush(false);
369}
370
371bool sensorConfigData::validate(size_t dataSize) {
372 bool valid = PersistentDataFile::validate(dataSize);
373 if (valid) {
374 if (sensorConfig.get_threshold1() > 100 || sensorConfig.get_threshold2() > 100) {
375 Log.info("Sensor config: thresholds not valid (threshold1=%d, threshold2=%d)",
376 sensorConfig.get_threshold1(), sensorConfig.get_threshold2());
377 valid = false;
378 }
379 }
380 Log.info("Sensor config is %s", (valid) ? "valid" : "not valid");
381 return valid;
382}
383
385 PersistentDataFile::initialize();
386
387 Log.info("Current Data Initialized");
388
389 // If you manually update fields here, be sure to update the hash
390 updateHash();
391}
392
394 return getValue<uint16_t>(offsetof(SensorData, threshold1));
395}
396
398 setValue<uint16_t>(offsetof(SensorData, threshold1), value);
399}
400
402 return getValue<uint16_t>(offsetof(SensorData, threshold2));
403}
404
406 setValue<uint16_t>(offsetof(SensorData, threshold2), value);
407}
409 return getValue<uint16_t>(offsetof(SensorData, pollingRate));
410}
411
413 setValue<uint16_t>(offsetof(SensorData, pollingRate), value);
414} // End of sensorConfigData class
415
416
417
418
419// ***************** Current Status Storage Object *******************
420//
421// ********************************************************************
422
423const char *persistentDataPathCurrent = "/usr/current.dat";
424
426
427// [static]
434
437
440
442 current
443 // .withLogData(true)
444 .withSaveDelayMs(250)
445 .load();
446}
447
449 current.flush(false);
450}
451
452void currentStatusData::resetEverything() { // The device is waking up in a new day or is a new install
453 current.set_lastCountTime(Time.now());
454 sysStatus.set_resetCount(0); // Reset the reset count as well
455
456 // ********** Reset Counting Mode Fields **********
457 current.set_hourlyCount(0);
458 current.set_dailyCount(0);
459
460 // ********** Reset Occupancy Mode Fields **********
461 current.set_occupied(false);
462 current.set_lastOccupancyEvent(0);
463 current.set_occupancyStartTime(0);
464 current.set_totalOccupiedSeconds(0);
465}
466
467bool currentStatusData::validate(size_t dataSize) {
468 bool valid = PersistentDataFile::validate(dataSize);
469 if (valid) {
470 // Basic sanity checks on data
471 if (current.get_hourlyCount() > 10000 || current.get_dailyCount() > 100000) {
472 Log.info("Current: counts appear invalid, resetting");
473 current.set_hourlyCount(0);
474 current.set_dailyCount(0);
475 valid = false;
476 }
477 }
478 Log.info("Current data is %s", (valid) ? "valid" : "not valid");
479 return valid;
480}
481
483 PersistentDataFile::initialize();
484
485 Log.info("Current Data Initialized");
486
488
489 // If you manually update fields here, be sure to update the hash
490 updateHash();
491}
492
494 return getValue<uint16_t>(offsetof(CurrentData, faceNumber));
495}
496
498 setValue<uint16_t>(offsetof(CurrentData, faceNumber), value);
499}
500
502 return getValue<uint16_t>(offsetof(CurrentData, faceScore));
503}
504
506 setValue<uint16_t>(offsetof(CurrentData, faceScore), value);
507}
509 return getValue<uint16_t>(offsetof(CurrentData, gestureType));
510}
511
513 setValue<uint16_t>(offsetof(CurrentData, gestureType), value);
514}
515
517 return getValue<uint16_t>(offsetof(CurrentData, gestureScore));
518}
519
521 setValue<uint16_t>(offsetof(CurrentData, gestureScore), value);
522}
523
525 return getValue<time_t>(offsetof(CurrentData, lastCountTime));
526}
527
529 setValue<time_t>(offsetof(CurrentData, lastCountTime), value);
530}
531
533 return getValue<float>(offsetof(CurrentData, internalTempC));
534}
535
537 setValue<float>(offsetof(CurrentData, internalTempC), value);
538}
539
541 return getValue<float>(offsetof(CurrentData, externalTempC));
542}
543
545 setValue<float>(offsetof(CurrentData, externalTempC), value);
546}
547
549 return getValue<int8_t>(offsetof(CurrentData, alertCode));
550}
551
553 setValue<int8_t>(offsetof(CurrentData, alertCode), value);
554}
555
557 // lastAlertTime is stored as time_t in CurrentData; retrieve with correct type
558 return getValue<time_t>(offsetof(CurrentData,lastAlertTime));
559}
560
562 setValue<time_t>(offsetof(CurrentData,lastAlertTime),value);
563}
564
565// Local helper to convert an alert code into a coarse severity bucket.
566// Higher numbers indicate more severe conditions.
567static int getAlertSeverity(int8_t code) {
568 if (code <= 0) {
569 return 0; // no alert
570 }
571
572 // Map known codes into tiers. This is intentionally simple and can be
573 // extended as new alert codes are added over time.
574 switch (code) {
575 case 14: // out-of-memory
576 case 15: // modem / disconnect failure
577 case 16: // repeated sleep failures (HIBERNATE / ULP)
578 case 20: // PMIC thermal shutdown (critical battery/charging fault)
579 case 21: // PMIC charge timeout / stuck charging
580 return 3; // critical
581
582 case 23: // PMIC battery fault (general)
583 case 30: // connectivity timeout with radio up
584 case 31: // failed to connect to cloud
585 case 32: // connect taking too long
586 case 40: // repeated webhook failures
587 case 41: // configuration/ledger apply failure
588 case 42: // data ledger publish failure
589 case 43: // publish queue not drained before forced sleep
590 return 2; // major
591 default:
592 return 1; // minor / warning
593 }
594}
595
597 if (value <= 0) {
598 return; // ignore attempts to "raise" a non-alert here
599 }
600
601 int8_t existing = get_alertCode();
602 if (getAlertSeverity(value) > getAlertSeverity(existing)) {
603 set_alertCode(value);
604 set_lastAlertTime(Time.now());
605 }
606}
607
609 return getValue<float>(offsetof(CurrentData,stateOfCharge));
610}
612 setValue<float>(offsetof(CurrentData, stateOfCharge), value);
613}
614
616 return getValue<uint8_t>(offsetof(CurrentData, batteryState));
617}
619 setValue<uint8_t>(offsetof(CurrentData, batteryState), value);
620}
621
622// ********** Counting Mode Get/Set Functions **********
623
625 return getValue<uint16_t>(offsetof(CurrentData, hourlyCount));
626}
628 setValue<uint16_t>(offsetof(CurrentData, hourlyCount), value);
629}
630
632 return getValue<uint16_t>(offsetof(CurrentData, dailyCount));
633}
635 setValue<uint16_t>(offsetof(CurrentData, dailyCount), value);
636}
637
638// ********** Occupancy Mode Get/Set Functions **********
639
641 return getValue<bool>(offsetof(CurrentData, occupied));
642}
644 setValue<bool>(offsetof(CurrentData, occupied), value);
645}
646
648 return getValue<uint32_t>(offsetof(CurrentData, lastOccupancyEvent));
649}
651 setValue<uint32_t>(offsetof(CurrentData, lastOccupancyEvent), value);
652}
653
655 return getValue<time_t>(offsetof(CurrentData, occupancyStartTime));
656}
658 setValue<time_t>(offsetof(CurrentData, occupancyStartTime), value);
659}
660
662 return getValue<uint32_t>(offsetof(CurrentData, totalOccupiedSeconds));
663}
665 setValue<uint32_t>(offsetof(CurrentData, totalOccupiedSeconds), value);
666}
667
668// End of currentStatusData class
bool publishDiagnosticSafe(const char *eventName, const char *data, PublishFlags flags=PRIVATE)
Publish a state transition to the log handler.
bool publishDiagnosticSafe(const char *eventName, const char *data, PublishFlags flags=PRIVATE)
Publish a state transition to the log handler.
const char * persistentDataPathSensor
const char * persistentDataPathCurrent
const char * persistentDataPathSystem
Persistent Data Storage Structures - EEPROM/Retained Memory Management.
#define sensorConfig
#define sysStatus
@ COUNTING
#define current
@ CONNECTED
void set_gestureScore(uint16_t value)
time_t get_lastAlertTime() const
uint16_t get_gestureScore() const
uint32_t get_lastOccupancyEvent() const
float get_internalTempC() const
void setup()
Perform setup operations; call this from global application setup().
static currentStatusData & instance()
Gets the singleton instance of this class, allocating it if necessary.
void resetEverything()
Resets the current and hourly counts.
void set_internalTempC(float value)
void initialize()
Will reinitialize data if it is found not to be valid.
void raiseAlert(int8_t value)
Raise an alert, keeping the highest severity code when multiple occur.
uint32_t get_totalOccupiedSeconds() const
void set_faceNumber(uint16_t value)
float get_stateOfCharge() const
uint16_t get_dailyCount() const
int8_t get_alertCode() const
uint16_t get_hourlyCount() const
time_t get_occupancyStartTime() const
void set_batteryState(uint8_t value)
static const uint16_t CURRENT_DATA_VERSION
void set_gestureType(uint16_t value)
currentStatusData()
The constructor is protected because the class is a singleton.
float get_externalTempC() const
uint8_t get_batteryState() const
void set_externalTempC(float value)
void set_alertCode(int8_t value)
static currentStatusData * _instance
Singleton instance of this class.
time_t get_lastCountTime() const
void set_lastCountTime(time_t value)
static const uint32_t CURRENT_DATA_MAGIC
void set_lastOccupancyEvent(uint32_t value)
void set_occupied(bool value)
bool validate(size_t dataSize)
Validates values and, if valid, checks that data is in the correct range.
void loop()
Perform application loop operations; call this from global application loop().
void set_hourlyCount(uint16_t value)
uint16_t get_faceScore() const
void set_dailyCount(uint16_t value)
virtual ~currentStatusData()
The destructor is protected because the class is a singleton and cannot be deleted.
void set_faceScore(uint16_t value)
void set_totalOccupiedSeconds(uint32_t value)
uint16_t get_gestureType() const
void set_stateOfCharge(float value)
uint16_t get_faceNumber() const
For the Get functions, used to retrieve the value of the variable.
void set_lastAlertTime(time_t value)
void set_occupancyStartTime(time_t value)
static sensorConfigData & instance()
Gets the singleton instance of this class, allocating it if necessary.
uint16_t get_pollingRate() const
static sensorConfigData * _instance
Singleton instance of this class.
static const uint32_t SENSOR_DATA_MAGIC
virtual ~sensorConfigData()
The destructor is protected because the class is a singleton and cannot be deleted.
uint16_t get_threshold1() const
For the Get functions, used to retrieve the value of the variable.
void setup()
Perform setup operations; call this from global application setup().
sensorConfigData()
The constructor is protected because the class is a singleton.
bool validate(size_t dataSize)
Validates values and, if valid, checks that data is in the correct range.
void set_pollingRate(uint16_t value)
void set_threshold2(uint16_t value)
void initialize()
Will reinitialize data if it is found not to be valid.
void set_threshold1(uint16_t value)
void loop()
Perform application loop operations; call this from global application loop().
static const uint16_t SENSOR_DATA_VERSION
uint16_t get_threshold2() const
This class is a singleton; you do not create one as a global, on the stack, or with new.
time_t get_lastReport() const
uint16_t get_connectAttemptBudgetSec() const
void set_modemOffBudgetSec(uint16_t value)
bool get_solarPowerMode() const
time_t get_lastHookResponse() const
void set_lastConnectionDuration(uint16_t value)
uint8_t get_operatingMode() const
time_t get_lastDailyCleanup() const
bool set_timeZoneStr(const char *str)
uint8_t get_resetCount() const
void set_connectAttemptBudgetSec(uint16_t value)
bool get_lowPowerMode() const
void set_lastHookResponse(time_t value)
void set_cloudDisconnectBudgetSec(uint16_t value)
uint8_t get_structuresVersion() const
For the Get functions, used to retrieve the value of the variable.
uint16_t get_reportingInterval() const
uint8_t get_sensorType() const
void set_lastReport(time_t value)
void set_serialConnected(bool value)
static sysStatusData & instance()
Gets the singleton instance of this class, allocating it if necessary.
uint16_t get_lastConnectionDuration() const
void set_disconnectedMode(bool value)
void set_lowPowerReportingIntervalSec(uint16_t value)
void set_resetCount(uint8_t value)
void set_countingMode(uint8_t value)
void set_solarPowerMode(bool value)
void set_connectedReportingIntervalSec(uint16_t value)
void set_lowBatteryMode(bool value)
sysStatusData()
The constructor is protected because the class is a singleton.
void set_updatesPending(bool value)
bool get_disconnectedMode() const
void set_closeTime(uint8_t value)
static sysStatusData * _instance
Singleton instance of this class.
uint32_t get_occupancyDebounceMs() const
static const uint32_t SYS_DATA_MAGIC
void set_openTime(uint8_t value)
uint16_t get_cloudDisconnectBudgetSec() const
void loop()
Perform application loop operations; call this from global application loop().
uint16_t get_modemOffBudgetSec() const
String get_timeZoneStr() const
bool get_lowBatteryMode() const
void set_structuresVersion(uint8_t value)
void initialize()
Will reinitialize data if it is found not to be valid.
uint16_t get_connectedReportingIntervalSec() const
void set_sensorType(uint8_t value)
void set_lowPowerMode(bool value)
void setup()
Perform setup operations; call this from global application setup().
void set_reportingInterval(uint16_t value)
uint8_t get_closeTime() const
static const uint16_t SYS_DATA_VERSION
bool get_serialConnected() const
virtual ~sysStatusData()
The destructor is protected because the class is a singleton and cannot be deleted.
void set_occupancyDebounceMs(uint32_t value)
void set_verboseMode(bool value)
bool get_updatesPending() const
bool get_verboseMode() const
void set_lastDailyCleanup(time_t value)
uint8_t get_openTime() const
void set_operatingMode(uint8_t value)
time_t get_lastTimeSync() const
time_t get_lastConnection() const
void set_lastConnection(time_t value)
void set_lastTimeSync(time_t value)
bool validate(size_t dataSize)
Validates values and, if valid, checks that data is in the correct range.
uint8_t get_countingMode() const
uint16_t get_lowPowerReportingIntervalSec() const