diff --git a/examples/simple_sensor/SensorMesh.cpp b/examples/simple_sensor/SensorMesh.cpp index df568ddf..fa08b156 100644 --- a/examples/simple_sensor/SensorMesh.cpp +++ b/examples/simple_sensor/SensorMesh.cpp @@ -237,8 +237,8 @@ void SensorMesh::sendAlert(const char* text) { } } -void SensorMesh::alertIfLow(Trigger& t, float value, float threshold, const char* text) { - if (value < threshold) { +void SensorMesh::alertIf(bool condition, Trigger& t, const char* text) { + if (condition) { if (!t.triggered) { t.triggered = true; t.time = getRTCClock()->getCurrentTime(); @@ -252,65 +252,6 @@ void SensorMesh::alertIfLow(Trigger& t, float value, float threshold, const char } } -void SensorMesh::alertIfHigh(Trigger& t, float value, float threshold, const char* text) { - if (value > threshold) { - if (!t.triggered) { - t.triggered = true; - t.time = getRTCClock()->getCurrentTime(); - sendAlert(text); - } - } else { - if (t.triggered) { - t.triggered = false; - // TODO: apply debounce logic - } - } -} - -void SensorMesh::recordData(TimeSeriesData& data, float value) { - uint32_t now = getRTCClock()->getCurrentTime(); - if (now >= data.last_timestamp + data.interval_secs) { - data.last_timestamp = now; - - data.data[data.next] = value; // append to cycle table - data.next = (data.next + 1) % data.num_slots; - } -} - -void SensorMesh::calcDataMinMaxAvg(const TimeSeriesData& data, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) { - int i = data.next, n = data.num_slots; - uint32_t ago = data.interval_secs * data.num_slots; - int num_values = 0; - float total = 0.0f; - - dest->_channel = channel; - dest->_lpp_type = lpp_type; - - // start at earliest recording, through to most recent - while (n > 0) { - n--; - i = (i + 1) % data.num_slots; - if (ago >= end_secs_ago && ago < start_secs_ago) { - float v = data.data[i]; - num_values++; - total += v; - if (num_values == 1) { - dest->_max = dest->_min = v; - } else { - if (v < dest->_min) dest->_min = v; - if (v > dest->_max) dest->_max = v; - } - } - ago -= data.interval_secs; - } - // calc average - if (num_values > 0) { - dest->_avg = total / num_values; - } else { - dest->_avg = NAN; - } -} - float SensorMesh::getAirtimeBudgetFactor() const { return _prefs.airtime_factor; } diff --git a/examples/simple_sensor/SensorMesh.h b/examples/simple_sensor/SensorMesh.h index 0f94b128..e8b4d88f 100644 --- a/examples/simple_sensor/SensorMesh.h +++ b/examples/simple_sensor/SensorMesh.h @@ -3,6 +3,8 @@ #include // needed for PlatformIO #include +#include "TimeSeriesData.h" + #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #include #elif defined(RP2040_PLATFORM) @@ -90,33 +92,7 @@ protected: Trigger() { triggered = false; time = 0; } }; - void alertIfLow(Trigger& t, float value, float threshold, const char* text); - void alertIfHigh(Trigger& t, float value, float threshold, const char* text); - - class TimeSeriesData { - public: - float* data; - int num_slots, next; - uint32_t last_timestamp; - uint32_t interval_secs; - - TimeSeriesData(float* array, int num, uint32_t secs) : num_slots(num), data(array), last_timestamp(0), next(0), interval_secs(secs) { - memset(data, 0, sizeof(float)*num); - } - TimeSeriesData(int num, uint32_t secs) : num_slots(num), last_timestamp(0), next(0), interval_secs(secs) { - data = new float[num]; - memset(data, 0, sizeof(float)*num); - } - }; - - void recordData(TimeSeriesData& data, float value); - - struct MinMaxAvg { - float _min, _max, _avg; - uint8_t _lpp_type, _channel; - }; - - void calcDataMinMaxAvg(const TimeSeriesData& data, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type); + void alertIf(bool condition, Trigger& t, const char* text); virtual void onSensorDataRead() = 0; // for app to implement virtual int querySeriesData(uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg dest[], int max_num) = 0; // for app to implement diff --git a/examples/simple_sensor/TimeSeriesData.cpp b/examples/simple_sensor/TimeSeriesData.cpp new file mode 100644 index 00000000..ff7daa25 --- /dev/null +++ b/examples/simple_sensor/TimeSeriesData.cpp @@ -0,0 +1,45 @@ +#include "TimeSeriesData.h" + +void TimeSeriesData::recordData(mesh::RTCClock* clock, float value) { + uint32_t now = clock->getCurrentTime(); + if (now >= last_timestamp + interval_secs) { + last_timestamp = now; + + data[next] = value; // append to cycle table + next = (next + 1) % num_slots; + } +} + +void TimeSeriesData::calcDataMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const { + int i = next, n = num_slots; + uint32_t ago = clock->getCurrentTime() - last_timestamp; + int num_values = 0; + float total = 0.0f; + + dest->_channel = channel; + dest->_lpp_type = lpp_type; + + // start at most recet recording, back-track through to oldest + while (n > 0) { + n--; + i = (i + num_slots - 1) % num_slots; // go back by one + if (ago >= end_secs_ago && ago < start_secs_ago) { // filter by the desired time range + float v = data[i]; + num_values++; + total += v; + if (num_values == 1) { + dest->_max = dest->_min = v; + } else { + if (v < dest->_min) dest->_min = v; + if (v > dest->_max) dest->_max = v; + } + } + ago += interval_secs; + } + // calc average + if (num_values > 0) { + dest->_avg = total / num_values; + } else { + dest->_avg = NAN; + } +} diff --git a/examples/simple_sensor/TimeSeriesData.h b/examples/simple_sensor/TimeSeriesData.h new file mode 100644 index 00000000..ea9e823b --- /dev/null +++ b/examples/simple_sensor/TimeSeriesData.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +struct MinMaxAvg { + float _min, _max, _avg; + uint8_t _lpp_type, _channel; +}; + +class TimeSeriesData { + float* data; + int num_slots, next; + uint32_t last_timestamp; + uint32_t interval_secs; + +public: + TimeSeriesData(float* array, int num, uint32_t secs) : num_slots(num), data(array), last_timestamp(0), next(0), interval_secs(secs) { + memset(data, 0, sizeof(float)*num); + } + TimeSeriesData(int num, uint32_t secs) : num_slots(num), last_timestamp(0), next(0), interval_secs(secs) { + data = new float[num]; + memset(data, 0, sizeof(float)*num); + } + + void recordData(mesh::RTCClock* clock, float value); + void calcDataMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const; +}; + diff --git a/examples/simple_sensor/main.cpp b/examples/simple_sensor/main.cpp index 500efef4..03d5cb5b 100644 --- a/examples/simple_sensor/main.cpp +++ b/examples/simple_sensor/main.cpp @@ -21,12 +21,12 @@ protected: void onSensorDataRead() override { float batt_voltage = getVoltage(TELEM_CHANNEL_SELF); - recordData(battery_data, batt_voltage); // record battery - alertIfLow(low_batt, batt_voltage, 3.4f, "Battery low!"); + battery_data.recordData(getRTCClock(), batt_voltage); // record battery + alertIf(batt_voltage < 3.4f, low_batt, "Battery low!"); } int querySeriesData(uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg dest[], int max_num) override { - calcDataMinMaxAvg(battery_data, start_secs_ago, end_secs_ago, &dest[0], TELEM_CHANNEL_SELF, LPP_VOLTAGE); + battery_data.calcDataMinMaxAvg(getRTCClock(), start_secs_ago, end_secs_ago, &dest[0], TELEM_CHANNEL_SELF, LPP_VOLTAGE); return 1; } /* ======================================================================= */