Skip to content

Commit

Permalink
Graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieucarbou committed Jun 21, 2024
1 parent cb94964 commit 31f2c7b
Show file tree
Hide file tree
Showing 15 changed files with 114 additions and 72 deletions.
6 changes: 4 additions & 2 deletions include/YaSolR.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ extern Mycila::ZCD zcd;
extern Mycila::CircularBuffer<float, YASOLR_GRAPH_POINTS> gridPowerHistory;
extern Mycila::CircularBuffer<float, YASOLR_GRAPH_POINTS> routedPowerHistory;
extern Mycila::CircularBuffer<float, YASOLR_GRAPH_POINTS> routerTHDiHistory;
extern Mycila::CircularBuffer<uint32_t, YASOLR_GRAPH_POINTS> timeHistory;

extern Mycila::TaskManager ioTaskManager;
extern Mycila::Task haDiscoveryTask;
Expand All @@ -104,7 +105,8 @@ extern Mycila::Task profilerTask;
extern Mycila::Task resetTask;
extern Mycila::Task restartTask;
extern Mycila::Task routerDebugTask;
extern Mycila::Task dashboardTask;
extern Mycila::Task dashboardCards;
extern Mycila::Task dashboardCharts;
#ifdef APP_MODEL_TRIAL
extern Mycila::Task trialTask;
#endif
Expand All @@ -123,7 +125,7 @@ extern Mycila::Task routingTask;
extern Mycila::Task bootTask;
extern Mycila::Task initConfigTask;
extern Mycila::Task initCoreTask;
extern Mycila::Task initDashboardTask;
extern Mycila::Task initdashboardCards;
extern Mycila::Task initEventsTask;
extern Mycila::Task initLoggingTask;
extern Mycila::Task initMqttSubscribersTask;
Expand Down
2 changes: 1 addition & 1 deletion include/YaSolRDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@
#define YASOLR_RELAY_TYPE_NO "NO"
#define YASOLR_SERIAL_BAUDRATE 115200
#define YASOLR_WEEK_DAYS "sun,mon,tue,wed,thu,fri,sat"
#define YASOLR_GRAPH_POINTS 30
#define YASOLR_GRAPH_POINTS 60

// pinout

Expand Down
10 changes: 10 additions & 0 deletions include/YaSolRWebsite.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#ifdef APP_MODEL_PRO
#define PUSH_BUTTON_CARD_CB ()
#else
#define LINE_CHART BAR_CHART
#define ENERGY_CARD GENERIC_CARD
#define PUSH_BUTTON_CARD BUTTON_CARD
#define PUSH_BUTTON_CARD_CB (int32_t value)
Expand All @@ -22,6 +23,7 @@ namespace YaSolR {
void initLayout();
void initCards();
void updateCards();
void updateCharts();

private:
// statistics
Expand Down Expand Up @@ -82,6 +84,14 @@ namespace YaSolR {
Card _gridPower = Card(&dashboard, ENERGY_CARD, YASOLR_LBL_044, "W");
Card _routerDS18State = Card(&dashboard, TEMPERATURE_CARD, YASOLR_LBL_045, "°C");

int _historyX[YASOLR_GRAPH_POINTS] = {0};
float _gridPowerHistoryY[YASOLR_GRAPH_POINTS] = {0};
float _routedPowerHistoryY[YASOLR_GRAPH_POINTS] = {0};
float _routerTHDiHistoryY[YASOLR_GRAPH_POINTS] = {0};
Chart _gridPowerHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_044 " (Watts)");
Chart _routedPowerHistory = Chart(&dashboard, LINE_CHART, YASOLR_LBL_036 " (Watts)");
Chart _routerTHDiHistory = Chart(&dashboard, BAR_CHART, YASOLR_LBL_039 " (%)");

#ifdef APP_MODEL_PRO
// tabs icons:
// https://en.wikipedia.org/wiki/List_of_Unicode_characters#Miscellaneous_Symbols
Expand Down
17 changes: 16 additions & 1 deletion lib/MycilaCircularBuffer/MycilaCircularBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
#pragma once

#include <stddef.h>

#include <functional>
#include <limits>

namespace Mycila {
template <typename T, int N>
template <typename T, size_t N>
class CircularBuffer {
public:
CircularBuffer() { reset(); }

T const& operator[](size_t index) const { return _buffer[(_index + index) % N]; }

size_t count() const { return _count; };
T avg() const { return _count == 0 ? 0 : _sum / _count; }
T last() const { return _last; }
Expand All @@ -37,6 +41,17 @@ namespace Mycila {
return current;
};

size_t copy(T* dest, size_t size) const { // NOLINT (build/include_what_you_use)
size_t i = 0;
size_t j = _index;
while (i < size && i < _count) {
dest[i++] = _buffer[j++];
if (j == N)
j = 0;
}
return i;
}

void reset() {
_sum = 0;
_last = 0;
Expand Down
5 changes: 2 additions & 3 deletions lib/MycilaRouter/MycilaRouterOutput.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,17 @@ namespace Mycila {
const char* getStateName() const;
const char* getName() const { return _name; }

bool isEnabled() const { return _dimmer->isEnabled(); }
void listen(RouterOutputStateCallback callback) { _callback = callback; }

#ifdef MYCILA_JSON_SUPPORT
void toJson(const JsonObject& root) const {
RouterOutputMetrics metrics;
getMetrics(metrics);

root["enabled"] = isEnabled();
root["enabled"] = isDimmerEnabled();
root["state"] = getStateName();
root["bypass"] = isBypassOn() ? "on" : "off";
root["online"] = isDimmerEnabled();
root["online"] = isDimmerConnected();

_dimmer->toJson(root["dimmer"].to<JsonObject>());
_temperatureSensor->toJson(root["ds18"].to<JsonObject>());
Expand Down
4 changes: 2 additions & 2 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ build_flags =
-D DASH_DEFAULT_CARD_SIZE_XL=6
-D DASH_DEFAULT_CARD_SIZE_XS=12
-D DASH_DEFAULT_CARD_SIZE_XXL=3
-D DASH_JSON_SIZE=2048
-D DASH_JSON_SIZE=4096
-D ELEGANTOTA_USE_ASYNC_WEBSERVER=1
; Fla gto disable mDNS which takes a lot of memory
; Flag to disable mDNS which takes a lot of memory
; 25.6 KB …espressif__mdns/mdns.c
; -D ESPCONNECT_NO_MDNS
; Flag to disable secure clients
Expand Down
33 changes: 31 additions & 2 deletions src/Website.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@

#define HIDDEN_PWD "********"

#ifdef APP_MODEL_PRO
static const ChartSize chartSize = {.xs = 12, .sm = 12, .md = 12, .lg = 12, .xl = 12, .xxl = 12};
#endif

void YaSolR::WebsiteClass::initLayout() {
logger.debug(TAG, "Initializing layout...");

#ifdef APP_MODEL_PRO
dashboard.setChartAnimations(false);

// graphs
_gridPowerHistory.setSize(chartSize);
_routedPowerHistory.setSize(chartSize);
_routerTHDiHistory.setSize(chartSize);

// output 1 (status)
_output1State.setTab(&_output1Tab);
_output1DS18State.setTab(&_output1Tab);
Expand Down Expand Up @@ -634,6 +645,24 @@ void YaSolR::WebsiteClass::updateCards() {
#endif
}

void YaSolR::WebsiteClass::updateCharts() {
// graphs
for (size_t i = 0; i < YASOLR_GRAPH_POINTS; i++)
_historyX[i] = static_cast<int>(timeHistory[i] / 1000);

gridPowerHistory.copy(_gridPowerHistoryY, YASOLR_GRAPH_POINTS);
routedPowerHistory.copy(_routedPowerHistoryY, YASOLR_GRAPH_POINTS);
for (size_t i = 0; i < YASOLR_GRAPH_POINTS; i++)
_routerTHDiHistoryY[i] = routerTHDiHistory[i] * 100;

_gridPowerHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
_gridPowerHistory.updateY(_gridPowerHistoryY, YASOLR_GRAPH_POINTS);
_routedPowerHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
_routedPowerHistory.updateY(_routedPowerHistoryY, YASOLR_GRAPH_POINTS);
_routerTHDiHistory.updateX(_historyX, YASOLR_GRAPH_POINTS);
_routerTHDiHistory.updateY(_routerTHDiHistoryY, YASOLR_GRAPH_POINTS);
}

void YaSolR::WebsiteClass::_sliderConfig(Card& card, const char* key) {
card.attachCallback([key, &card](int value) {
config.set(key, String(value));
Expand Down Expand Up @@ -727,7 +756,7 @@ void YaSolR::WebsiteClass::_outputBypassSwitch(Card& card, Mycila::RouterOutput&
}
card.update(output.isBypassOn());
dashboard.refreshCard(&card);
dashboardTask.requestEarlyRun();
dashboardCards.requestEarlyRun();
});
}

Expand All @@ -738,7 +767,7 @@ void YaSolR::WebsiteClass::_outputDimmerSlider(Card& card, Mycila::RouterOutput&
}
card.update(output.getDimmerDuty());
dashboard.refreshCard(&card);
dashboardTask.requestEarlyRun();
dashboardCards.requestEarlyRun();
});
}

Expand Down
8 changes: 5 additions & 3 deletions src/init/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ Mycila::Task initConfigTask("Init Config", [](void* params) {
// Tasks configuration
carouselTask.setEnabledWhen([]() { return display.isEnabled(); });
carouselTask.setIntervalSupplier([]() { return config.get(KEY_DISPLAY_SPEED).toInt() * Mycila::TaskDuration::SECONDS; });
dashboardTask.setEnabledWhen([]() { return ESPConnect.isConnected() && dashboard.hasClient() && !dashboard.isAsyncAccessInProgress(); });
dashboardTask.setInterval(751 * Mycila::TaskDuration::MILLISECONDS);
dashboardCards.setEnabledWhen([]() { return ESPConnect.isConnected() && dashboard.hasClient() && !dashboard.isAsyncAccessInProgress() && !routingTask.isRunning(); });
dashboardCards.setInterval(751 * Mycila::TaskDuration::MILLISECONDS);
dashboardCharts.setEnabledWhen([]() { return ESPConnect.isConnected() && dashboard.hasClient() && !dashboard.isAsyncAccessInProgress() && !routingTask.isRunning(); });
dashboardCharts.setInterval(2 * Mycila::TaskDuration::SECONDS);
displayTask.setEnabledWhen([]() { return display.isEnabled(); });
displayTask.setInterval(293 * Mycila::TaskDuration::MILLISECONDS);
ds18Task.setEnabledWhen([]() { return ds18Sys.isEnabled() || ds18O1.isEnabled() || ds18O2.isEnabled(); });
Expand All @@ -28,7 +30,7 @@ Mycila::Task initConfigTask("Init Config", [](void* params) {
relayTask.setInterval(7 * Mycila::TaskDuration::SECONDS);
routerDebugTask.setInterval(5 * Mycila::TaskDuration::SECONDS);
routerTask.setInterval(537 * Mycila::TaskDuration::MILLISECONDS);
routingTask.setEnabledWhen([]() { return output1.isAutoDimmerEnabled() || output2.isAutoDimmerEnabled(); });
routingTask.setEnabledWhen([]() { return output1.isDimmerEnabled() || output2.isDimmerEnabled(); });
#ifdef APP_MODEL_TRIAL
trialTask.setInterval(30 * Mycila::TaskDuration::SECONDS);
#endif
Expand Down
3 changes: 2 additions & 1 deletion src/init/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ Mycila::Task initCoreTask("Init Core", [](void* params) {

// coreTaskManager
carouselTask.setManager(coreTaskManager);
dashboardTask.setManager(coreTaskManager);
dashboardCards.setManager(coreTaskManager);
dashboardCharts.setManager(coreTaskManager);
displayTask.setManager(coreTaskManager);
ds18Task.setManager(coreTaskManager);
lightsTask.setManager(coreTaskManager);
Expand Down
2 changes: 1 addition & 1 deletion src/init/Dashboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <YaSolR.h>
#include <YaSolRWebsite.h>

Mycila::Task initDashboardTask("Init Dashboard", [](void* params) {
Mycila::Task initdashboardCards("Init Dashboard", [](void* params) {
logger.info(TAG, "Initializing dashboard...");
YaSolR::Website.initLayout();
YaSolR::Website.initCards();
Expand Down
6 changes: 4 additions & 2 deletions src/init/Debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Mycila::Task initLoggingTask("Init Logging", [](void* params) {

if (debug) {
// Enable profiling for some FOREVER tasks
dashboardTask.enableProfiling(10, Mycila::TaskTimeUnit::MILLISECONDS);
dashboardCards.enableProfiling(10, Mycila::TaskTimeUnit::MILLISECONDS);
dashboardCharts.enableProfiling(10, Mycila::TaskTimeUnit::MILLISECONDS);
displayTask.enableProfiling(10, Mycila::TaskTimeUnit::MILLISECONDS);
jsyTask.enableProfiling(10, Mycila::TaskTimeUnit::MILLISECONDS);
mqttPublishTask.enableProfiling(10, Mycila::TaskTimeUnit::MILLISECONDS);
Expand All @@ -32,7 +33,8 @@ Mycila::Task initLoggingTask("Init Logging", [](void* params) {
routerTask.enableProfiling(10, Mycila::TaskTimeUnit::MILLISECONDS);
routingTask.enableProfiling(10, Mycila::TaskTimeUnit::MILLISECONDS);
} else {
dashboardTask.disableProfiling();
dashboardCards.disableProfiling();
dashboardCharts.disableProfiling();
displayTask.disableProfiling();
jsyTask.disableProfiling();
mqttPublishTask.disableProfiling();
Expand Down
3 changes: 2 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ Mycila::RouterRelay routerRelay2(relay2);
Mycila::CircularBuffer<float, YASOLR_GRAPH_POINTS> gridPowerHistory;
Mycila::CircularBuffer<float, YASOLR_GRAPH_POINTS> routedPowerHistory;
Mycila::CircularBuffer<float, YASOLR_GRAPH_POINTS> routerTHDiHistory;
Mycila::CircularBuffer<uint32_t, YASOLR_GRAPH_POINTS> timeHistory;

AsyncWebServer webServer(80);
ESPDash dashboard = ESPDash(&webServer, "/dashboard", false);
Expand All @@ -56,7 +57,7 @@ void setup() {
initWebTask.forceRun();
initRestApiTask.forceRun();
initMqttSubscribersTask.forceRun();
initDashboardTask.forceRun();
initdashboardCards.forceRun();

assert( coreTaskManager.asyncStart(1024 * 4, 1, 1, 100, true)); // NOLINT
assert( ioTaskManager.asyncStart(1024 * 5, 1, 1, 100, false)); // NOLINT
Expand Down
7 changes: 6 additions & 1 deletion src/tasks/Dashboard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
#include <YaSolR.h>
#include <YaSolRWebsite.h>

Mycila::Task dashboardTask("Dashboard", [](void* params) {
Mycila::Task dashboardCards("Dashboard Cards", [](void* params) {
YaSolR::Website.updateCards();
dashboard.sendUpdates();
});

Mycila::Task dashboardCharts("Dashboard Charts", [](void* params) {
YaSolR::Website.updateCharts();
dashboard.sendUpdates();
});
72 changes: 21 additions & 51 deletions src/tasks/MQTT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,35 +35,9 @@ Mycila::Task mqttPublishStaticTask("MQTT Static", Mycila::TaskType::ONCE, [](voi
mqtt.publish(baseTopic + "/system/network/wifi/mac_address", ESPConnect.getMACAddress(ESPConnectMode::STA), true);
yield();

if (!output1.isEnabled()) {
mqtt.publish(baseTopic + "/router/output1/bypass", YASOLR_STATE(output1.isBypassOn()), true);
mqtt.publish(baseTopic + "/router/output1/state", output1.getStateName());
mqtt.publish(baseTopic + "/router/output1/temperature", String(ds18O1.getValidTemperature(), 1), true);
mqtt.publish(baseTopic + "/router/output1/dimmer/duty", String(dimmerO1.getPowerDuty()), true);
mqtt.publish(baseTopic + "/router/output1/dimmer/duty_cycle", String(dimmerO1.getPowerDutyCycle() * 100), true);
mqtt.publish(baseTopic + "/router/output1/dimmer/state", YASOLR_STATE(dimmerO1.isOn()), true);
mqtt.publish(baseTopic + "/router/output1/relay/state", YASOLR_STATE(bypassRelayO1.isOn()), true);
mqtt.publish(baseTopic + "/router/output1/relay/switch_count", String(bypassRelayO1.getSwitchCount()), true);
yield();
}

if (!output2.isEnabled()) {
mqtt.publish(baseTopic + "/router/output2/bypass", YASOLR_STATE(output2.isBypassOn()), true);
mqtt.publish(baseTopic + "/router/output2/state", output2.getStateName());
mqtt.publish(baseTopic + "/router/output2/temperature", String(ds18O2.getValidTemperature(), 1), true);
mqtt.publish(baseTopic + "/router/output2/dimmer/duty", String(dimmerO2.getPowerDuty()), true);
mqtt.publish(baseTopic + "/router/output2/dimmer/duty_cycle", String(dimmerO2.getPowerDutyCycle() * 100), true);
mqtt.publish(baseTopic + "/router/output2/dimmer/state", YASOLR_STATE(dimmerO2.isOn()), true);
mqtt.publish(baseTopic + "/router/output2/relay/state", YASOLR_STATE(bypassRelayO2.isOn()), true);
mqtt.publish(baseTopic + "/router/output2/relay/switch_count", String(bypassRelayO2.getSwitchCount()), true);
yield();
}

if (!relay1.isEnabled()) {
mqtt.publish(baseTopic + "/router/relay1/state", YASOLR_STATE(relay1.isOn()), true);
mqtt.publish(baseTopic + "/router/relay1/switch_count", String(relay1.getSwitchCount()), true);
yield();
}
mqtt.publish(baseTopic + "/router/relay1/state", YASOLR_STATE(relay1.isOn()), true);
mqtt.publish(baseTopic + "/router/relay1/switch_count", String(relay1.getSwitchCount()), true);
yield();

if (!relay2.isEnabled()) {
mqtt.publish(baseTopic + "/router/relay2/state", YASOLR_STATE(relay2.isOn()), true);
Expand Down Expand Up @@ -145,29 +119,25 @@ Mycila::Task mqttPublishTask("MQTT", [](void* params) {
mqtt.publish(baseTopic + "/router/virtual_grid_power", String(gridMetrics.power - routerMetrics.power, 3));
yield();

if (output1.isEnabled()) {
mqtt.publish(baseTopic + "/router/output1/bypass", YASOLR_STATE(output1.isBypassOn()));
mqtt.publish(baseTopic + "/router/output1/state", output1.getStateName());
mqtt.publish(baseTopic + "/router/output1/temperature", String(ds18O1.getValidTemperature(), 1));
mqtt.publish(baseTopic + "/router/output1/dimmer/duty", String(dimmerO1.getPowerDuty()));
mqtt.publish(baseTopic + "/router/output1/dimmer/duty_cycle", String(dimmerO1.getPowerDutyCycle() * 100));
mqtt.publish(baseTopic + "/router/output1/dimmer/state", YASOLR_STATE(dimmerO1.isOn()));
mqtt.publish(baseTopic + "/router/output1/relay/state", YASOLR_STATE(bypassRelayO1.isOn()));
mqtt.publish(baseTopic + "/router/output1/relay/switch_count", String(bypassRelayO1.getSwitchCount()));
yield();
}
mqtt.publish(baseTopic + "/router/output1/bypass", YASOLR_STATE(output1.isBypassOn()));
mqtt.publish(baseTopic + "/router/output1/state", output1.getStateName());
mqtt.publish(baseTopic + "/router/output1/temperature", String(ds18O1.getValidTemperature(), 1));
mqtt.publish(baseTopic + "/router/output1/dimmer/duty", String(dimmerO1.getPowerDuty()));
mqtt.publish(baseTopic + "/router/output1/dimmer/duty_cycle", String(dimmerO1.getPowerDutyCycle() * 100));
mqtt.publish(baseTopic + "/router/output1/dimmer/state", YASOLR_STATE(dimmerO1.isOn()));
mqtt.publish(baseTopic + "/router/output1/relay/state", YASOLR_STATE(bypassRelayO1.isOn()));
mqtt.publish(baseTopic + "/router/output1/relay/switch_count", String(bypassRelayO1.getSwitchCount()));
yield();

if (output2.isEnabled()) {
mqtt.publish(baseTopic + "/router/output2/bypass", YASOLR_STATE(output2.isBypassOn()));
mqtt.publish(baseTopic + "/router/output2/state", output2.getStateName());
mqtt.publish(baseTopic + "/router/output2/temperature", String(ds18O2.getValidTemperature(), 1));
mqtt.publish(baseTopic + "/router/output2/dimmer/duty", String(dimmerO2.getPowerDuty()));
mqtt.publish(baseTopic + "/router/output2/dimmer/duty_cycle", String(dimmerO2.getPowerDutyCycle() * 100));
mqtt.publish(baseTopic + "/router/output2/dimmer/state", YASOLR_STATE(dimmerO2.isOn()));
mqtt.publish(baseTopic + "/router/output2/relay/state", YASOLR_STATE(bypassRelayO2.isOn()));
mqtt.publish(baseTopic + "/router/output2/relay/switch_count", String(bypassRelayO2.getSwitchCount()));
yield();
}
mqtt.publish(baseTopic + "/router/output2/bypass", YASOLR_STATE(output2.isBypassOn()));
mqtt.publish(baseTopic + "/router/output2/state", output2.getStateName());
mqtt.publish(baseTopic + "/router/output2/temperature", String(ds18O2.getValidTemperature(), 1));
mqtt.publish(baseTopic + "/router/output2/dimmer/duty", String(dimmerO2.getPowerDuty()));
mqtt.publish(baseTopic + "/router/output2/dimmer/duty_cycle", String(dimmerO2.getPowerDutyCycle() * 100));
mqtt.publish(baseTopic + "/router/output2/dimmer/state", YASOLR_STATE(dimmerO2.isOn()));
mqtt.publish(baseTopic + "/router/output2/relay/state", YASOLR_STATE(bypassRelayO2.isOn()));
mqtt.publish(baseTopic + "/router/output2/relay/switch_count", String(bypassRelayO2.getSwitchCount()));
yield();

if (relay1.isEnabled()) {
mqtt.publish(baseTopic + "/router/relay1/state", YASOLR_STATE(relay1.isOn()));
Expand Down
Loading

0 comments on commit 31f2c7b

Please sign in to comment.