Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 98 additions & 24 deletions src/ESP32/ESP32.ino
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <PubSubClient.h>
#include "time.h"
#include "esp_task_wdt.h"
#include "esp_timer.h"
#include "config.h"
#include "secret.h"
#include "ota.h"
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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()
Expand Down Expand Up @@ -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,
Expand All @@ -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();
Expand Down Expand Up @@ -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*)&currentDiagData, sizeof(DiagData), false);
currentDiagData.loopMaxMs = 0;
}
Expand Down
Loading