diff --git a/src/ESP32/ESP32.ino b/src/ESP32/ESP32.ino index 3a1bdd2..bda16c6 100644 --- a/src/ESP32/ESP32.ino +++ b/src/ESP32/ESP32.ino @@ -3,6 +3,7 @@ #include #include "time.h" #include "esp_task_wdt.h" +#include "esp_timer.h" #include "config.h" #include "secret.h" #include "ota.h" @@ -12,9 +13,14 @@ #endif #define LSSensorPIN1 32 #define LSSensorPIN2 33 -#define PIN_PULL INPUT -#define PIN_EDGE RISING -#define PULSE_DEBOUNCE_MS 84 +#define SAMPLE_INTERVAL_MS 5 +#define ADC_TH_LOW_MV 1000 +#define ADC_TH_HIGH_MV 1800 +#define PULSE_MIN_INTERVAL_MS 30 +#define SAMPLER_CORE 1 +#define SAMPLER_PRIORITY 2 +#define SAMPLER_STACK 4096 +#define SAMPLER_REPORT_ITERS 2000 #define PIN_DIAG_INTERVAL_MS 500UL #define PULSE_INTERVAL 60000UL #define DIAG_INTERVAL 300000UL @@ -29,10 +35,17 @@ PubSubClient mqtt(net); volatile uint32_t counter1 = 0; volatile uint32_t counter2 = 0; -volatile uint32_t lastTime1 = 0; -volatile uint32_t lastTime2 = 0; portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; +bool chHigh1 = false; +bool chHigh2 = false; +uint32_t chLastCount1 = 0; +uint32_t chLastCount2 = 0; + +uint32_t samplerMaxUs = 0; +uint32_t samplerOverruns = 0; +uint16_t samplerStackWords = 0; + char data[32]; unsigned long lastPulseSend = 0; unsigned long lastDiagSend = 0; @@ -51,9 +64,12 @@ struct DiagData { int8_t rssi; uint16_t fwVersion; uint16_t otaFailCount; + uint16_t samplerMaxUs; + uint16_t samplerStackWords; + uint16_t samplerOverruns; }; #pragma pack(pop) -static_assert(sizeof(DiagData) == 18, "DiagData wire layout must stay 18 bytes"); +static_assert(sizeof(DiagData) == 24, "DiagData wire layout must stay 24 bytes"); DiagData currentDiagData; uint16_t otaFailures = 0; @@ -63,28 +79,81 @@ uint16_t heapKb() return (uint16_t)(ESP.getFreeHeap() / 1024); } -void IRAM_ATTR WattMeter1Received() +void ProcessSample(int mv, bool* high, uint32_t* lastCount, volatile uint32_t* counter, uint32_t now) { - uint32_t t = millis(); - portENTER_CRITICAL_ISR(&mux); - if(t - lastTime1 > PULSE_DEBOUNCE_MS) + if(*high) + { + if(mv < ADC_TH_LOW_MV) + { + *high = false; + } + } + else if(mv > ADC_TH_HIGH_MV) { - counter1++; - lastTime1 = t; + *high = true; + if(now - *lastCount > PULSE_MIN_INTERVAL_MS) + { + portENTER_CRITICAL(&mux); + (*counter)++; + portEXIT_CRITICAL(&mux); + *lastCount = now; + } } - portEXIT_CRITICAL_ISR(&mux); } -void IRAM_ATTR WattMeter2Received() +void SamplerTask(void* arg) { - uint32_t t = millis(); - portENTER_CRITICAL_ISR(&mux); - if(t - lastTime2 > PULSE_DEBOUNCE_MS) + TickType_t last = xTaskGetTickCount(); + chHigh1 = analogReadMilliVolts(LSSensorPIN1) > ADC_TH_HIGH_MV; + chHigh2 = analogReadMilliVolts(LSSensorPIN2) > ADC_TH_HIGH_MV; + uint32_t iterCount = 0; + uint32_t durSum = 0; + uint32_t durMax = 0; + uint32_t allTimeMaxUs = 0; + uint32_t overrunCount = 0; + for(;;) { - counter2++; - lastTime2 = t; + int64_t start = esp_timer_get_time(); + uint32_t now = millis(); + int mv1 = analogReadMilliVolts(LSSensorPIN1); + int mv2 = analogReadMilliVolts(LSSensorPIN2); + ProcessSample(mv1, &chHigh1, &chLastCount1, &counter1, now); + ProcessSample(mv2, &chHigh2, &chLastCount2, &counter2, now); + uint32_t dur = (uint32_t)(esp_timer_get_time() - start); + + durSum += dur; + if(dur > durMax) + { + durMax = dur; + } + if(dur > allTimeMaxUs) + { + allTimeMaxUs = dur; + } + if(dur > SAMPLE_INTERVAL_MS * 1000UL) + { + overrunCount++; + Serial.printf("SAMPLER overrun: %lu us\n", (unsigned long)dur); + } + + if(++iterCount >= SAMPLER_REPORT_ITERS) + { + uint16_t hwm = (uint16_t)uxTaskGetStackHighWaterMark(NULL); + uint32_t avg = durSum / iterCount; + portENTER_CRITICAL(&mux); + samplerMaxUs = allTimeMaxUs; + samplerStackWords = hwm; + samplerOverruns = overrunCount; + portEXIT_CRITICAL(&mux); + Serial.printf("SAMPLER avg=%lu us max=%lu us stackHWM=%u words overruns=%lu\n", + (unsigned long)avg, (unsigned long)durMax, hwm, (unsigned long)overrunCount); + iterCount = 0; + durSum = 0; + durMax = 0; + } + + vTaskDelayUntil(&last, pdMS_TO_TICKS(SAMPLE_INTERVAL_MS)); } - portEXIT_CRITICAL_ISR(&mux); } bool SyncTime() @@ -144,16 +213,15 @@ bool Connect() void setup() { currentDiagData.resetReason = (uint8_t)esp_reset_reason(); Serial.begin(115200); - pinMode(LSSensorPIN1, PIN_PULL); - pinMode(LSSensorPIN2, PIN_PULL); + analogReadResolution(12); + analogSetAttenuation(ADC_11db); WiFi.mode(WIFI_STA); WiFi.begin(WifiSSID, WifiPassword); net.setCACert(MQTTCACert); mqtt.setServer(MQTTHost, MQTT_TLS_PORT); mqtt.setBufferSize(256); mqtt.setKeepAlive(60); - attachInterrupt(digitalPinToInterrupt(LSSensorPIN1), WattMeter1Received, PIN_EDGE); - attachInterrupt(digitalPinToInterrupt(LSSensorPIN2), WattMeter2Received, PIN_EDGE); + xTaskCreatePinnedToCore(SamplerTask, "sampler", SAMPLER_STACK, NULL, SAMPLER_PRIORITY, NULL, SAMPLER_CORE); esp_task_wdt_config_t wdtConfig = { .timeout_ms = WDT_TIMEOUT_S * 1000, .idle_core_mask = 0, @@ -177,6 +245,9 @@ void loop() { portENTER_CRITICAL(&mux); uint32_t c1 = counter1; uint32_t c2 = counter2; + uint32_t sMaxUs = samplerMaxUs; + uint16_t sStackWords = samplerStackWords; + uint32_t sOverruns = samplerOverruns; portEXIT_CRITICAL(&mux); bool connected = Connect(); @@ -206,6 +277,9 @@ void loop() { currentDiagData.rssi = (int8_t)WiFi.RSSI(); currentDiagData.fwVersion = (uint16_t)FW_VERSION; currentDiagData.otaFailCount = otaFailures; + currentDiagData.samplerMaxUs = (sMaxUs > 65535UL) ? 65535 : (uint16_t)sMaxUs; + currentDiagData.samplerStackWords = sStackWords; + currentDiagData.samplerOverruns = (sOverruns > 65535UL) ? 65535 : (uint16_t)sOverruns; mqtt.publish(LSSENSOR_DIAG, (const uint8_t*)¤tDiagData, sizeof(DiagData), false); currentDiagData.loopMaxMs = 0; }