Skip to content

Commit

Permalink
Feature: HTTP+JSON power meter: poll asynchronously
Browse files Browse the repository at this point in the history
  • Loading branch information
schlimmchen committed Jun 27, 2024
1 parent f6f62a6 commit 8a46ba9
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 13 deletions.
19 changes: 17 additions & 2 deletions include/PowerMeterHttpJson.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <atomic>
#include <array>
#include <variant>
#include <memory>
#include <condition_variable>
#include <mutex>
#include <stdint.h>
#include "HttpGetter.h"
#include "Configuration.h"
Expand All @@ -17,8 +20,10 @@ class PowerMeterHttpJson : public PowerMeterProvider {
explicit PowerMeterHttpJson(PowerMeterHttpJsonConfig const& cfg)
: _cfg(cfg) { }

~PowerMeterHttpJson();

bool init() final;
void loop() final;
void loop() final { } // polling is performed asynchronously
float getPowerTotal() const final;
bool isDataValid() const final;
void doMqttPublish() const final;
Expand All @@ -28,11 +33,21 @@ class PowerMeterHttpJson : public PowerMeterProvider {
poll_result_t poll();

private:
static void pollingLoopHelper(void* context);
std::atomic<bool> _taskDone;
void pollingLoop();

PowerMeterHttpJsonConfig const _cfg;

uint32_t _lastPoll;
uint32_t _lastPoll = 0;

mutable std::mutex _valueMutex;
power_values_t _powerValues;

std::array<std::unique_ptr<HttpGetter>, POWERMETER_HTTP_JSON_MAX_VALUES> _httpGetters;

TaskHandle_t _taskHandle = nullptr;
bool _stopPolling;
mutable std::mutex _pollingMutex;
std::condition_variable _cv;
};
71 changes: 60 additions & 11 deletions src/PowerMeterHttpJson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@
#include <base64.h>
#include <ESPmDNS.h>

PowerMeterHttpJson::~PowerMeterHttpJson()
{
_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;
}
}

bool PowerMeterHttpJson::init()
{
for (uint8_t i = 0; i < POWERMETER_HTTP_JSON_MAX_VALUES; i++) {
Expand All @@ -32,24 +48,54 @@ bool PowerMeterHttpJson::init()
return false;
}

std::unique_lock<std::mutex> lock(_pollingMutex);
_stopPolling = false;
lock.unlock();

uint32_t constexpr stackSize = 3072;
xTaskCreate(PowerMeterHttpJson::pollingLoopHelper, "PM:HTTP+JSON",
stackSize, this, 1/*prio*/, &_taskHandle);

return true;
}

void PowerMeterHttpJson::loop()
void PowerMeterHttpJson::pollingLoopHelper(void* context)
{
if ((millis() - _lastPoll) < (_cfg.PollingInterval * 1000)) {
return;
}
auto pInstance = static_cast<PowerMeterHttpJson*>(context);
pInstance->pollingLoop();
pInstance->_taskDone = true;
vTaskDelete(nullptr);
}

_lastPoll = millis();
void PowerMeterHttpJson::pollingLoop()
{
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;
}

auto res = poll();
if (std::holds_alternative<String>(res)) {
MessageOutput.printf("[PowerMeterHttpJson] %s\r\n", std::get<String>(res).c_str());
return;
}
_lastPoll = millis();

lock.unlock(); // polling can take quite some time
auto res = poll();
lock.lock();

gotUpdate();
if (std::holds_alternative<String>(res)) {
MessageOutput.printf("[PowerMeterHttpJson] %s\r\n", std::get<String>(res).c_str());
continue;
}

MessageOutput.printf("[PowerMeterHttpJson] New total: %.2f\r\n", getPowerTotal());

gotUpdate();
}
}

PowerMeterHttpJson::poll_result_t PowerMeterHttpJson::poll()
Expand Down Expand Up @@ -113,13 +159,15 @@ PowerMeterHttpJson::poll_result_t PowerMeterHttpJson::poll()
if (cfg.SignInverted) { cache[i] *= -1; }
}

std::unique_lock<std::mutex> lock(_valueMutex);
_powerValues = cache;
return cache;
}

float PowerMeterHttpJson::getPowerTotal() const
{
float sum = 0.0;
std::unique_lock<std::mutex> lock(_valueMutex);
for (auto v: _powerValues) { sum += v; }
return sum;
}
Expand All @@ -132,6 +180,7 @@ bool PowerMeterHttpJson::isDataValid() const

void PowerMeterHttpJson::doMqttPublish() const
{
std::unique_lock<std::mutex> lock(_valueMutex);
mqttPublish("power1", _powerValues[0]);
mqttPublish("power2", _powerValues[1]);
mqttPublish("power3", _powerValues[2]);
Expand Down

0 comments on commit 8a46ba9

Please sign in to comment.