Feature: SDM power meter: poll asynchronously

This commit is contained in:
Bernhard Kirchen 2024-05-23 23:31:30 +02:00
parent 8a46ba9541
commit 347dd67684
2 changed files with 97 additions and 50 deletions

View File

@ -1,7 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <atomic>
#include <mutex> #include <mutex>
#include <condition_variable>
#include "Configuration.h" #include "Configuration.h"
#include "PowerMeterProvider.h" #include "PowerMeterProvider.h"
#include "SDM.h" #include "SDM.h"
@ -20,16 +22,20 @@ public:
~PowerMeterSerialSdm(); ~PowerMeterSerialSdm();
bool init() final; bool init() final;
void loop() final; void loop() final { } // polling is performed asynchronously
float getPowerTotal() const final; float getPowerTotal() const final;
bool isDataValid() const final; bool isDataValid() const final;
void doMqttPublish() const final; void doMqttPublish() const final;
private: private:
static void pollingLoopHelper(void* context);
std::atomic<bool> _taskDone;
void pollingLoop();
Phases _phases; Phases _phases;
PowerMeterSerialSdmConfig const _cfg; PowerMeterSerialSdmConfig const _cfg;
uint32_t _lastPoll; uint32_t _lastPoll = 0;
float _phase1Power = 0.0; float _phase1Power = 0.0;
float _phase2Power = 0.0; float _phase2Power = 0.0;
@ -40,9 +46,14 @@ private:
float _energyImport = 0.0; float _energyImport = 0.0;
float _energyExport = 0.0; float _energyExport = 0.0;
mutable std::mutex _mutex; mutable std::mutex _valueMutex;
static char constexpr _sdmSerialPortOwner[] = "SDM power meter"; static char constexpr _sdmSerialPortOwner[] = "SDM power meter";
std::unique_ptr<HardwareSerial> _upSdmSerial = nullptr; std::unique_ptr<HardwareSerial> _upSdmSerial = nullptr;
std::unique_ptr<SDM> _upSdm = nullptr; std::unique_ptr<SDM> _upSdm = nullptr;
TaskHandle_t _taskHandle = nullptr;
bool _stopPolling;
mutable std::mutex _pollingMutex;
std::condition_variable _cv;
}; };

View File

@ -6,6 +6,19 @@
PowerMeterSerialSdm::~PowerMeterSerialSdm() PowerMeterSerialSdm::~PowerMeterSerialSdm()
{ {
_taskDone = false;
std::unique_lock<std::mutex> lock(_pollingMutex);
_stopPolling = true;
lock.unlock();
_cv.notify_all();
if (_taskHandle != nullptr) {
while (!_taskDone) { delay(10); }
_taskHandle = nullptr;
}
if (_upSdmSerial) { if (_upSdmSerial) {
_upSdmSerial->end(); _upSdmSerial->end();
_upSdmSerial = nullptr; _upSdmSerial = nullptr;
@ -34,12 +47,20 @@ bool PowerMeterSerialSdm::init()
SERIAL_8N1, pin.powermeter_rx, pin.powermeter_tx); SERIAL_8N1, pin.powermeter_rx, pin.powermeter_tx);
_upSdm->begin(); _upSdm->begin();
std::unique_lock<std::mutex> lock(_pollingMutex);
_stopPolling = false;
lock.unlock();
uint32_t constexpr stackSize = 3072;
xTaskCreate(PowerMeterSerialSdm::pollingLoopHelper, "PM:SDM",
stackSize, this, 1/*prio*/, &_taskHandle);
return true; return true;
} }
float PowerMeterSerialSdm::getPowerTotal() const float PowerMeterSerialSdm::getPowerTotal() const
{ {
std::lock_guard<std::mutex> l(_mutex); std::lock_guard<std::mutex> l(_valueMutex);
return _phase1Power + _phase2Power + _phase3Power; return _phase1Power + _phase2Power + _phase3Power;
} }
@ -51,7 +72,7 @@ bool PowerMeterSerialSdm::isDataValid() const
void PowerMeterSerialSdm::doMqttPublish() const void PowerMeterSerialSdm::doMqttPublish() const
{ {
std::lock_guard<std::mutex> l(_mutex); std::lock_guard<std::mutex> l(_valueMutex);
mqttPublish("power1", _phase1Power); mqttPublish("power1", _phase1Power);
mqttPublish("power2", _phase2Power); mqttPublish("power2", _phase2Power);
mqttPublish("power3", _phase3Power); mqttPublish("power3", _phase3Power);
@ -62,14 +83,30 @@ void PowerMeterSerialSdm::doMqttPublish() const
mqttPublish("export", _energyExport); mqttPublish("export", _energyExport);
} }
void PowerMeterSerialSdm::loop() void PowerMeterSerialSdm::pollingLoopHelper(void* context)
{ {
if (!_upSdm) { return; } auto pInstance = static_cast<PowerMeterSerialSdm*>(context);
pInstance->pollingLoop();
pInstance->_taskDone = true;
vTaskDelete(nullptr);
}
if ((millis() - _lastPoll) < (_cfg.PollingInterval * 1000)) { void PowerMeterSerialSdm::pollingLoop()
return; {
std::unique_lock<std::mutex> lock(_pollingMutex);
while (!_stopPolling) {
auto elapsedMillis = millis() - _lastPoll;
auto intervalMillis = _cfg.PollingInterval * 1000;
if (_lastPoll > 0 && elapsedMillis < intervalMillis) {
auto sleepMs = intervalMillis - elapsedMillis;
_cv.wait_for(lock, std::chrono::milliseconds(sleepMs),
[this] { return _stopPolling; }); // releases the mutex
continue;
} }
_lastPoll = millis();
uint8_t addr = _cfg.Address; uint8_t addr = _cfg.Address;
// reading takes a "very long" time as each readVal() is a synchronous // reading takes a "very long" time as each readVal() is a synchronous
@ -92,7 +129,7 @@ void PowerMeterSerialSdm::loop()
} }
{ {
std::lock_guard<std::mutex> l(_mutex); std::lock_guard<std::mutex> l(_valueMutex);
_phase1Power = static_cast<float>(phase1Power); _phase1Power = static_cast<float>(phase1Power);
_phase2Power = static_cast<float>(phase2Power); _phase2Power = static_cast<float>(phase2Power);
_phase3Power = static_cast<float>(phase3Power); _phase3Power = static_cast<float>(phase3Power);
@ -103,9 +140,8 @@ void PowerMeterSerialSdm::loop()
_energyExport = static_cast<float>(energyExport); _energyExport = static_cast<float>(energyExport);
} }
gotUpdate();
MessageOutput.printf("[PowerMeterSerialSdm] TotalPower: %5.2f\r\n", getPowerTotal()); MessageOutput.printf("[PowerMeterSerialSdm] TotalPower: %5.2f\r\n", getPowerTotal());
_lastPoll = millis(); gotUpdate();
}
} }