renamed Electricity -> Power, added Energy

This commit is contained in:
Patrick Haßel 2024-12-30 12:00:26 +01:00
parent 5790286e32
commit f3d4c68e94
11 changed files with 165 additions and 26 deletions

View File

@ -7,7 +7,8 @@
#define _ false
#define ____ 0
#define HALF 127
#define QUAR 64
#define HALF 128
#define FULL 255
#define countof(x) (sizeof(x) / sizeof(x[0]))

View File

@ -8,7 +8,7 @@ const Color RED = {FULL, ____, ____};
const Color GREEN = {____, FULL, ____};
const Color ORANGE = {FULL, HALF, ____};
const Color ORANGE = {FULL, QUAR, ____};
const Color BLUE = {____, ____, FULL};

View File

@ -99,6 +99,13 @@ bool SYMBOLS[SYMBOL_COUNT][DISPLAY_CHAR_WIDTH * DISPLAY_CHAR_HEIGHT] = {
_, _, _,
_, _, _,
},
{
X, _, _,
_, _, X,
_, X, _,
X, _, _,
_, _, X,
},
// this must always be the last symbol (fallback)
{
X, X, X,

View File

@ -5,8 +5,9 @@
#include "Adafruit_NeoPixel.h"
#include "Vector.h"
#define SYMBOL_COUNT 15
#define SYMBOL_COUNT 16
#define SYMBOL_DASH 13
#define SYMBOL_PERCENT 14
#define DISPLAY_CHAR_WIDTH 3
#define DISPLAY_CHAR_HEIGHT 5
@ -94,12 +95,12 @@ public:
LEFT, RIGHT
};
uint8_t print(int x, int y, double valueDbl, Color colorPositive, Color colorZero, Color colorNegative, ALIGN align = LEFT) {
uint8_t print2(int x, int y, double valueDbl, Color colorPositive, Color colorZero, Color colorNegative, ALIGN align = RIGHT) {
const Color color = valueDbl == 0 ? colorZero : (valueDbl < 0 ? colorNegative : colorPositive);
return print(x, y, valueDbl, color, align);
return print2(x, y, valueDbl, color, align);
}
uint8_t print(int x, int y, double valueDbl, Color color, ALIGN align = LEFT) {
uint8_t print2(int x, int y, double valueDbl, Color color, ALIGN align = RIGHT) {
if (isnan(valueDbl)) {
x -= 3 * (DISPLAY_CHAR_WIDTH + 1) - 1;
x += print(x, y, SYMBOL_DASH, color, true) + 1;
@ -140,7 +141,7 @@ public:
return DISPLAY_CHAR_WIDTH;
}
if (index >= SYMBOL_COUNT) {
Serial.printf("Cannot print symbol #%u.\n", index);
Serial.printf("Cannot print2 symbol #%u.\n", index);
index = SYMBOL_COUNT - 1;
}
bool *symbolBit = SYMBOLS[index];

View File

@ -9,7 +9,8 @@
#include "mode/Starfield/Starfield.h"
#include "mode/Matrix/Matrix.h"
#include "display.h"
#include "mode/Electricity/Electricity.h"
#include "mode/Power/Power.h"
#include "mode/Energy/Energy.h"
ModeId currentModeId = NONE;
@ -109,8 +110,11 @@ void loadNewMode() {
case MATRIX:
mode = new Matrix(display);
break;
case ELECTRICITY:
mode = new Electricity(display);
case POWER:
mode = new Power(display);
break;
case ENERGY:
mode = new Energy(display);
break;
default:
Serial.print("No mode loaded.\n");

71
src/mode/Energy/Energy.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef MODE_ENERGY_H
#define MODE_ENERGY_H
#include "mode/Mode.h"
#include "mqtt.h"
#define POWER_PHOTOVOLTAIC_PRODUCED_BEFORE_METER_CHANGE 287.995
#define PV_COST_TOTAL_EURO 576.52
#define GRID_KWH_EURO 0.33
#define PV_COST_AMORTISATION_KWH ( PV_COST_TOTAL_EURO / GRID_KWH_EURO )
class Energy : public Mode {
private:
int page = 0;
public:
explicit Energy(Display &display) :
Mode(display) {
timer(0, 7000);
}
const char *getName() override {
return "Energy";
}
protected:
void tick(uint8_t index, microseconds_t microseconds) override {
page = (page + 1) % 3;
}
void step(microseconds_t microseconds) override {
if (realtimeChanged) {
markDirty();
}
}
void draw(Display &display) override {
const double produced = getPhotovoltaicEnergyKWh();
const double imported = getGridImportKWh();
const double exported = getGridExportKWh();
const double producedAfterMeterChange = produced - POWER_PHOTOVOLTAIC_PRODUCED_BEFORE_METER_CHANGE;
const double selfAfterMeterChange = producedAfterMeterChange - exported;
const double selfRatio = selfAfterMeterChange / producedAfterMeterChange;
const double selfConsumedKWh = selfRatio * produced;
const double costSaved = selfConsumedKWh * GRID_KWH_EURO;
const double amortisationPercent = selfConsumedKWh / PV_COST_AMORTISATION_KWH * 100;
const uint8_t l = (DISPLAY_CHAR_WIDTH + 1) * 4 - 1;
const uint8_t r = width;
display.clear();
if (page == 0) {
display.print2(l, 0, costSaved, GREEN);
uint8_t x = display.print2(r - DISPLAY_CHAR_WIDTH - 1, 0, amortisationPercent, WHITE);
display.print(x, 0, SYMBOL_PERCENT, WHITE, true);
} else if (page == 1) {
display.print2(l, 0, getPhotovoltaicEnergyKWh(), BLUE);
display.print2(r, 0, selfConsumedKWh, GREEN);
} else {
display.print2(l, 0, imported, ORANGE);
display.print2(r, 0, exported, MAGENTA);
}
}
};
#endif

View File

@ -23,7 +23,8 @@ enum ModeId {
COUNT_DOWN_BARS,
STARFIELD,
MATRIX,
ELECTRICITY,
POWER,
ENERGY,
};
class Mode {

View File

@ -1,5 +1,5 @@
#ifndef MODE_ELECTRICITY_H
#define MODE_ELECTRICITY_H
#ifndef MODE_POWER_H
#define MODE_POWER_H
#include "mode/Mode.h"
#include "mqtt.h"
@ -7,17 +7,17 @@
#pragma clang diagnostic push
#pragma ide diagnostic ignored "UnusedValue"
class Electricity : public Mode {
class Power : public Mode {
public:
explicit Electricity(Display &display) :
explicit Power(Display &display) :
Mode(display) {
// nothing
}
const char *getName() override {
return "Electricity";
return "Power";
}
protected:
@ -30,8 +30,8 @@ protected:
void draw(Display &display) override {
display.clear();
display.print((DISPLAY_CHAR_WIDTH + 1) * 3 - 1, 0, getPhotovoltaicPowerW(), GREEN, Color(), Color(), Display::RIGHT);
display.print(width, 0, getGridPowerW(), ORANGE, WHITE, MAGENTA, Display::RIGHT);
display.print2((DISPLAY_CHAR_WIDTH + 1) * 3 - 1, 0, getPhotovoltaicPowerW(), GREEN);
display.print2(width, 0, getGridPowerW(), ORANGE, WHITE, MAGENTA);
}
};

View File

@ -8,9 +8,11 @@
#define MQTT_MAX_MESSAGE_AGE_MILLIS 11000
#define PHOTOVOLTAIC_POWER "openDTU/pv/ac/power"
#define GRID_POWER "electricity/grid/power/signed/w"
#define PHOTOVOLTAIC_POWER_W "openDTU/pv/ac/power"
#define PHOTOVOLTAIC_ENERGY_KWH "openDTU/pv/ac/yieldtotal"
#define GRID_POWER_W "electricity/grid/power/signed/w"
#define GRID_IMPORT_WH "electricity/grid/energy/import/wh"
#define GRID_EXPORT_WH "electricity/grid/energy/export/wh"
WiFiClient espClient;
@ -24,10 +26,22 @@ double photovoltaicPowerW = NAN;
unsigned long photovoltaicPowerWLast = 0;
double photovoltaicEnergyKWh = NAN;
unsigned long photovoltaicEnergyKWhLast = 0;
double gridPowerW = NAN;
unsigned long gridPowerWLast = 0;
double gridImportKWh = NAN;
unsigned long gridImportKWhLast = 0;
double gridExportKWh = NAN;
unsigned long gridExportKWhLast = 0;
void mqttDisconnect();
void mqttCallback(char *topic, uint8_t *payload, unsigned int length) {
@ -38,12 +52,21 @@ void mqttCallback(char *topic, uint8_t *payload, unsigned int length) {
}
memcpy(message, payload, length);
message[length] = 0;
if (strcmp(PHOTOVOLTAIC_POWER, topic) == 0) {
if (strcmp(PHOTOVOLTAIC_POWER_W, topic) == 0) {
photovoltaicPowerW = strtod(message, nullptr);
photovoltaicPowerWLast = millis();
} else if (strcmp(GRID_POWER, topic) == 0) {
} else if (strcmp(PHOTOVOLTAIC_ENERGY_KWH, topic) == 0) {
photovoltaicEnergyKWh = strtod(message, nullptr);
photovoltaicEnergyKWhLast = millis();
} else if (strcmp(GRID_POWER_W, topic) == 0) {
gridPowerW = strtod(message, nullptr);
gridPowerWLast = millis();
} else if (strcmp(GRID_IMPORT_WH, topic) == 0) {
gridImportKWh = strtod(message, nullptr) / 1000;
gridImportKWhLast = millis();
} else if (strcmp(GRID_EXPORT_WH, topic) == 0) {
gridExportKWh = strtod(message, nullptr) / 1000;
gridExportKWhLast = millis();
}
}
@ -59,8 +82,11 @@ void mqtt_loop() {
if (mqttConnected) {
Serial.printf("Successfully connected mqtt broker at %s:%d\n", "10.0.0.50", 1883);
mqtt.setCallback(mqttCallback);
mqtt.subscribe(PHOTOVOLTAIC_POWER);
mqtt.subscribe(GRID_POWER);
mqtt.subscribe(PHOTOVOLTAIC_POWER_W);
mqtt.subscribe(PHOTOVOLTAIC_ENERGY_KWH);
mqtt.subscribe(GRID_POWER_W);
mqtt.subscribe(GRID_IMPORT_WH);
mqtt.subscribe(GRID_EXPORT_WH);
} else {
Serial.printf("ERROR: Failed to connect MQTT broker at %s:%d\n", "10.0.0.50", 1883);
mqttDisconnect();
@ -86,9 +112,30 @@ double getPhotovoltaicPowerW() {
return photovoltaicPowerW;
}
double getPhotovoltaicEnergyKWh() {
if (millis() - photovoltaicEnergyKWhLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
return NAN;
}
return photovoltaicEnergyKWh;
}
double getGridPowerW() {
if (millis() - gridPowerWLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
return NAN;
}
return gridPowerW;
}
double getGridImportKWh() {
if (millis() - gridImportKWhLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
return NAN;
}
return gridImportKWh;
}
double getGridExportKWh() {
if (millis() - gridExportKWhLast > MQTT_MAX_MESSAGE_AGE_MILLIS) {
return NAN;
}
return gridExportKWh;
}

View File

@ -5,6 +5,12 @@ void mqtt_loop();
double getPhotovoltaicPowerW();
double getPhotovoltaicEnergyKWh();
double getGridPowerW();
double getGridImportKWh();
double getGridExportKWh();
#endif

View File

@ -105,7 +105,8 @@ void web_index() {
server.sendContent(R"(<a onclick="get('/mode?mode=10');">COUNT_DOWN_BARS</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=11');">STARFIELD</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=12');">MATRIX</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=13');">ELECTRICITY</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=13');">POWER</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=14');">ENERGY</a><br>)");
server.sendContent(R"(</p>)");
server.sendContent(R"(<p>)");