Compare commits

...

19 Commits

Author SHA1 Message Date
Gunnar Skjold
b9fe6cab83 Improved on automatic clearing of graph 2023-05-02 14:21:58 +02:00
Gunnar Skjold
e3f41fdb4c Fixed issue introduced in previous commit 2023-05-02 14:02:40 +02:00
Gunnar Skjold
9e5cb48101 Improved update and clearing of hour/day in data storage for graphs 2023-05-02 13:39:16 +02:00
Gunnar Skjold
259d8424a3 Merge pull request #550 from dbeinder/version_lib
Speedup build
2023-05-02 10:51:26 +02:00
david-beinder
a6ae86abb8 Remove redundant 'typedef'
'typedef' is not needed in C++ and generates a warning
2023-05-02 10:48:54 +02:00
david-beinder
a23abf626b Convert Arduino sketch to C++ 2023-05-02 10:48:54 +02:00
david-beinder
849eac1c0f Move version constants into its own compile-unit
Speeds up the build by moving those constants into a separate compile-unit.
Currently, since the version header is auto regenerated on each build,
all modules that include it, also have to be recompiled every time.
2023-05-02 10:48:53 +02:00
Gunnar Skjold
13dda2bb19 Added decimals on month plot if number under 10. Also added mouse-over with better precision 2023-05-02 10:40:53 +02:00
Gunnar Skjold
eea1782280 Fixed segfault when enabling verbose debug with fixed price 2023-05-02 10:20:54 +02:00
Gunnar Skjold
d729d0c5bd Fixed update of data in month plot after entering new month 2023-05-02 10:15:31 +02:00
Gunnar Skjold
72cc0996e1 Merge branch 'master' of github.com:UtilitechAS/amsreader-firmware 2023-04-29 06:40:32 +02:00
Gunnar Skjold
b1ad844bc6 Fixed reactive energy sensors in HA 2023-04-29 06:40:25 +02:00
Gunnar Skjold
4977ad3471 Merge pull request #548 from dbeinder/fixedprice
Add backup, restore, clearing of 'fixedPrice' setting
2023-04-29 06:16:02 +02:00
david-beinder
a3dcd2cfb2 Add backup, restore, clearing of 'fixedPrice' 2023-04-28 22:46:44 +02:00
Gunnar Skjold
c1701c8ee9 Fixed c3 bootloader 2023-04-28 19:32:54 +02:00
Gunnar Skjold
1e88148971 Fixed flash script 2023-04-28 18:42:49 +02:00
Gunnar Skjold
cef7ed15cb Merge pull request #543 from UtilitechAS/dependabot/npm_and_yarn/lib/SvelteUi/app/yaml-2.2.2
Bump yaml from 2.1.1 to 2.2.2 in /lib/SvelteUi/app
2023-04-28 18:37:53 +02:00
dependabot[bot]
7c93537fef Bump yaml from 2.1.1 to 2.2.2 in /lib/SvelteUi/app
Bumps [yaml](https://github.com/eemeli/yaml) from 2.1.1 to 2.2.2.
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v2.1.1...v2.2.2)

---
updated-dependencies:
- dependency-name: yaml
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-25 20:32:45 +00:00
Gunnar Skjold
8b97544a2c Remove old Kaifa special case code 2023-04-25 11:53:58 +02:00
37 changed files with 264 additions and 173 deletions

2
.gitignore vendored
View File

@@ -7,7 +7,7 @@
.vscode .vscode
.pio .pio
platformio-user.ini platformio-user.ini
/lib/AmsConfiguration/include/version.h /lib/FirmwareVersion/src/generated_version.h
/src/web/root /src/web/root
/src/AmsToMqttBridge.ino.cpp /src/AmsToMqttBridge.ino.cpp
/test /test

View File

@@ -580,6 +580,7 @@ void AmsConfiguration::clearEntsoe(EntsoeConfig& config) {
strcpy(config.currency, ""); strcpy(config.currency, "");
config.multiplier = 1000; config.multiplier = 1000;
config.enabled = false; config.enabled = false;
config.fixedPrice = 0;
} }
bool AmsConfiguration::isEntsoeChanged() { bool AmsConfiguration::isEntsoeChanged() {

View File

@@ -2,7 +2,7 @@
#include <lwip/apps/sntp.h> #include <lwip/apps/sntp.h>
#include "LittleFS.h" #include "LittleFS.h"
#include "AmsStorage.h" #include "AmsStorage.h"
#include "version.h" #include "FirmwareVersion.h"
AmsDataStorage::AmsDataStorage(RemoteDebug* debugger) { AmsDataStorage::AmsDataStorage(RemoteDebug* debugger) {
day.version = 5; day.version = 5;
@@ -28,20 +28,20 @@ bool AmsDataStorage::update(AmsData* data) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Timezone is missing\n")); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Timezone is missing\n"));
return false; return false;
} }
if(now < BUILD_EPOCH) { if(now < FirmwareVersion::BuildEpoch) {
if(data->getMeterTimestamp() > BUILD_EPOCH) { if(data->getMeterTimestamp() > FirmwareVersion::BuildEpoch) {
now = data->getMeterTimestamp(); now = data->getMeterTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("(AmsDataStorage) Using meter timestamp, which is: %lu\n"), (int32_t) now); debugger->printf_P(PSTR("(AmsDataStorage) Using meter timestamp, which is: %lu\n"), (int32_t) now);
} }
} else if(data->getPackageTimestamp() > BUILD_EPOCH) { } else if(data->getPackageTimestamp() > FirmwareVersion::BuildEpoch) {
now = data->getPackageTimestamp(); now = data->getPackageTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("(AmsDataStorage) Using package timestamp, which is: %lu\n"), (int32_t) now); debugger->printf_P(PSTR("(AmsDataStorage) Using package timestamp, which is: %lu\n"), (int32_t) now);
} }
} }
} }
if(now < BUILD_EPOCH) { if(now < FirmwareVersion::BuildEpoch) {
if(debugger->isActive(RemoteDebug::VERBOSE)) { if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf_P(PSTR("(AmsDataStorage) Invalid time: %lu\n"), (int32_t) now); debugger->printf_P(PSTR("(AmsDataStorage) Invalid time: %lu\n"), (int32_t) now);
} }
@@ -66,13 +66,35 @@ bool AmsDataStorage::update(AmsData* data) {
day.activeExport = exportCounter; day.activeExport = exportCounter;
day.lastMeterReadTime = now; day.lastMeterReadTime = now;
return true; return true;
} else if(day.activeImport == 0 || now - day.lastMeterReadTime > 86400) {
day.activeImport = importCounter;
day.activeExport = exportCounter;
day.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf_P(PSTR("(AmsDataStorage) Too long since last day update, clearing data\n"));
}
for(int i = 0; i<24; i++) {
setHourImport(i, 0);
setHourExport(i, 0);
}
} else { } else {
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("(AmsDataStorage) Last day update: %lu\n"), (int32_t) day.lastMeterReadTime); debugger->printf_P(PSTR("(AmsDataStorage) Last day update: %lu\n"), (int32_t) day.lastMeterReadTime);
} }
tmElements_t last; tmElements_t last;
breakTime(day.lastMeterReadTime, last); breakTime(day.lastMeterReadTime, last);
for(int i = last.Hour; i < utc.Hour; i++) { uint8_t endHour = utc.Hour;
if(last.Hour > utc.Hour){
for(int i = 0; i < utc.Hour; i++) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf_P(PSTR("(AmsDataStorage) Clearing hour: %d\n"), i);
}
setHourImport(i, 0);
setHourExport(i, 0);
}
endHour = 24;
}
for(int i = last.Hour; i < endHour; i++) {
if(debugger->isActive(RemoteDebug::VERBOSE)) { if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf_P(PSTR("(AmsDataStorage) Clearing hour: %d\n"), i); debugger->printf_P(PSTR("(AmsDataStorage) Clearing hour: %d\n"), i);
} }
@@ -89,13 +111,35 @@ bool AmsDataStorage::update(AmsData* data) {
month.activeImport = importCounter; month.activeImport = importCounter;
month.activeExport = exportCounter; month.activeExport = exportCounter;
month.lastMeterReadTime = now; month.lastMeterReadTime = now;
} else if(month.activeImport == 0 || now - month.lastMeterReadTime > 2682000) {
month.activeImport = importCounter;
month.activeExport = exportCounter;
month.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf_P(PSTR("(AmsDataStorage) Too long since last month update, clearing data\n"));
}
for(int i = 1; i<=31; i++) {
setDayImport(i, 0);
setDayExport(i, 0);
}
} else { } else {
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf_P(PSTR("(AmsDataStorage) Last month update: %lu\n"), (int32_t) month.lastMeterReadTime); debugger->printf_P(PSTR("(AmsDataStorage) Last month update: %lu\n"), (int32_t) month.lastMeterReadTime);
} }
tmElements_t last; tmElements_t last;
breakTime(tz->toLocal(month.lastMeterReadTime), last); breakTime(tz->toLocal(month.lastMeterReadTime), last);
for(int i = last.Day; i < ltz.Day; i++) { uint8_t endDay = ltz.Day;
if(last.Day > ltz.Day) {
for(int i = 1; i < ltz.Day; i++) {
if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf_P(PSTR("(AmsDataStorage) Clearing day: %d\n"), i);
}
setDayImport(i, 0);
setDayExport(i, 0);
}
endDay = 31;
}
for(int i = last.Day; i < endDay; i++) {
if(debugger->isActive(RemoteDebug::VERBOSE)) { if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf_P(PSTR("(AmsDataStorage) Clearing day: %d\n"), i); debugger->printf_P(PSTR("(AmsDataStorage) Clearing day: %d\n"), i);
} }
@@ -119,17 +163,6 @@ bool AmsDataStorage::update(AmsData* data) {
day.lastMeterReadTime = now; day.lastMeterReadTime = now;
setHourImport(utcYesterday.Hour, 0); setHourImport(utcYesterday.Hour, 0);
setHourExport(utcYesterday.Hour, 0); setHourExport(utcYesterday.Hour, 0);
} else if(day.activeImport == 0 || now - day.lastMeterReadTime > 86400) {
day.activeImport = importCounter;
day.activeExport = exportCounter;
day.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf_P(PSTR("(AmsDataStorage) Too long since last day update, clearing data\n"));
}
for(int i = 0; i<24; i++) {
setHourImport(i, 0);
setHourExport(i, 0);
}
} else if(now - day.lastMeterReadTime < 4000) { } else if(now - day.lastMeterReadTime < 4000) {
uint32_t imp = importCounter - day.activeImport; uint32_t imp = importCounter - day.activeImport;
uint32_t exp = exportCounter - day.activeExport; uint32_t exp = exportCounter - day.activeExport;
@@ -186,17 +219,6 @@ bool AmsDataStorage::update(AmsData* data) {
month.lastMeterReadTime = now; month.lastMeterReadTime = now;
setDayImport(ltzYesterDay.Day, 0); setDayImport(ltzYesterDay.Day, 0);
setDayExport(ltzYesterDay.Day, 0); setDayExport(ltzYesterDay.Day, 0);
} else if(month.activeImport == 0 || now - month.lastMeterReadTime > 2678400) {
month.activeImport = importCounter;
month.activeExport = exportCounter;
month.lastMeterReadTime = now;
if(debugger->isActive(RemoteDebug::WARNING)) {
debugger->printf_P(PSTR("(AmsDataStorage) Too long since last month update, clearing data\n"));
}
for(int i = 1; i<=31; i++) {
setDayImport(i, 0);
setDayExport(i, 0);
}
} else if(now - month.lastMeterReadTime < 90100 && now - month.lastMeterReadTime > 82700) { // DST days are 23h (82800s) and 25h (90000) } else if(now - month.lastMeterReadTime < 90100 && now - month.lastMeterReadTime > 82700) { // DST days are 23h (82800s) and 25h (90000)
int32_t imp = importCounter - month.activeImport; int32_t imp = importCounter - month.activeImport;
int32_t exp = exportCounter - month.activeExport; int32_t exp = exportCounter - month.activeExport;
@@ -551,7 +573,7 @@ bool AmsDataStorage::isHappy() {
bool AmsDataStorage::isDayHappy() { bool AmsDataStorage::isDayHappy() {
time_t now = time(nullptr); time_t now = time(nullptr);
if(now < BUILD_EPOCH) return false; if(now < FirmwareVersion::BuildEpoch) return false;
tmElements_t tm, last; tmElements_t tm, last;
if(now < day.lastMeterReadTime) { if(now < day.lastMeterReadTime) {
@@ -579,7 +601,7 @@ bool AmsDataStorage::isMonthHappy() {
} }
time_t now = time(nullptr); time_t now = time(nullptr);
if(now < BUILD_EPOCH) return false; if(now < FirmwareVersion::BuildEpoch) return false;
tmElements_t tm, last; tmElements_t tm, last;
if(now < month.lastMeterReadTime) { if(now < month.lastMeterReadTime) {
@@ -589,10 +611,15 @@ bool AmsDataStorage::isMonthHappy() {
breakTime(tz->toLocal(now), tm); breakTime(tz->toLocal(now), tm);
breakTime(tz->toLocal(month.lastMeterReadTime), last); breakTime(tz->toLocal(month.lastMeterReadTime), last);
if(tm.Day > last.Day) { if(tm.Day != last.Day) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Month data day of last timestamp %d > %d\n"), tm.Day, last.Day); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(AmsDataStorage) Month data day of last timestamp %d > %d\n"), tm.Day, last.Day);
return false; return false;
} }
if(now-month.lastMeterReadTime > 90100) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month %lu - %lu > 3600\n", (int32_t) now, (int32_t) month.lastMeterReadTime);
return false;
}
return true; return true;
} }

View File

@@ -81,7 +81,7 @@ public:
EnergyAccountingData getData(); EnergyAccountingData getData();
void setData(EnergyAccountingData&); void setData(EnergyAccountingData&);
void setFixedPrice(float price); void setFixedPrice(float price, String currency);
float getPriceForHour(uint8_t h); float getPriceForHour(uint8_t h);
private: private:
@@ -97,6 +97,7 @@ private:
float produce = 0, incomeHour = 0, incomeDay = 0; float produce = 0, incomeHour = 0, incomeDay = 0;
EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 }; EnergyAccountingData data = { 0, 0, 0, 0, 0, 0 };
float fixedPrice = 0; float fixedPrice = 0;
String currency = "";
void calcDayCost(); void calcDayCost();
bool updateMax(uint16_t val, uint8_t day); bool updateMax(uint16_t val, uint8_t day);

View File

@@ -1,7 +1,7 @@
#include "EnergyAccounting.h" #include "EnergyAccounting.h"
#include "LittleFS.h" #include "LittleFS.h"
#include "AmsStorage.h" #include "AmsStorage.h"
#include "version.h" #include "FirmwareVersion.h"
EnergyAccounting::EnergyAccounting(RemoteDebug* debugger) { EnergyAccounting::EnergyAccounting(RemoteDebug* debugger) {
data.version = 1; data.version = 1;
@@ -33,7 +33,7 @@ bool EnergyAccounting::isInitialized() {
bool EnergyAccounting::update(AmsData* amsData) { bool EnergyAccounting::update(AmsData* amsData) {
if(config == NULL) return false; if(config == NULL) return false;
time_t now = time(nullptr); time_t now = time(nullptr);
if(now < BUILD_EPOCH) return false; if(now < FirmwareVersion::BuildEpoch) return false;
if(tz == NULL) { if(tz == NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) Timezone is missing\n")); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) Timezone is missing\n"));
return false; return false;
@@ -132,7 +132,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
use += kwhi; use += kwhi;
if(price != ENTSOE_NO_VALUE) { if(price != ENTSOE_NO_VALUE) {
float cost = price * kwhi; float cost = price * kwhi;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) and %.4f %s\n"), cost / 100.0, eapi->getCurrency()); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) and %.4f %s\n"), cost / 100.0, currency.c_str());
costHour += cost; costHour += cost;
costDay += cost; costDay += cost;
} }
@@ -142,7 +142,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
produce += kwhe; produce += kwhe;
if(price != ENTSOE_NO_VALUE) { if(price != ENTSOE_NO_VALUE) {
float income = price * kwhe; float income = price * kwhe;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) and %.4f %s\n"), income / 100.0, eapi->getCurrency()); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf_P(PSTR("(EnergyAccounting) and %.4f %s\n"), income / 100.0, currency.c_str());
incomeHour += income; incomeHour += income;
incomeDay += income; incomeDay += income;
} }
@@ -188,7 +188,7 @@ float EnergyAccounting::getUseThisHour() {
float EnergyAccounting::getUseToday() { float EnergyAccounting::getUseToday() {
float ret = 0.0; float ret = 0.0;
time_t now = time(nullptr); time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0.0; if(now < FirmwareVersion::BuildEpoch) return 0.0;
if(tz == NULL) return 0.0; if(tz == NULL) return 0.0;
tmElements_t utc, local; tmElements_t utc, local;
breakTime(tz->toLocal(now), local); breakTime(tz->toLocal(now), local);
@@ -201,7 +201,7 @@ float EnergyAccounting::getUseToday() {
float EnergyAccounting::getUseThisMonth() { float EnergyAccounting::getUseThisMonth() {
time_t now = time(nullptr); time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0.0; if(now < FirmwareVersion::BuildEpoch) return 0.0;
float ret = 0; float ret = 0;
for(uint8_t i = 0; i < currentDay; i++) { for(uint8_t i = 0; i < currentDay; i++) {
ret += ds->getDayImport(i) / 1000.0; ret += ds->getDayImport(i) / 1000.0;
@@ -216,7 +216,7 @@ float EnergyAccounting::getProducedThisHour() {
float EnergyAccounting::getProducedToday() { float EnergyAccounting::getProducedToday() {
float ret = 0.0; float ret = 0.0;
time_t now = time(nullptr); time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0.0; if(now < FirmwareVersion::BuildEpoch) return 0.0;
tmElements_t utc, local; tmElements_t utc, local;
breakTime(tz->toLocal(now), local); breakTime(tz->toLocal(now), local);
for(uint8_t i = 0; i < currentHour; i++) { for(uint8_t i = 0; i < currentHour; i++) {
@@ -228,7 +228,7 @@ float EnergyAccounting::getProducedToday() {
float EnergyAccounting::getProducedThisMonth() { float EnergyAccounting::getProducedThisMonth() {
time_t now = time(nullptr); time_t now = time(nullptr);
if(now < BUILD_EPOCH) return 0.0; if(now < FirmwareVersion::BuildEpoch) return 0.0;
float ret = 0; float ret = 0;
for(uint8_t i = 0; i < currentDay; i++) { for(uint8_t i = 0; i < currentDay; i++) {
ret += ds->getDayExport(i) / 1000.0; ret += ds->getDayExport(i) / 1000.0;
@@ -499,8 +499,9 @@ bool EnergyAccounting::updateMax(uint16_t val, uint8_t day) {
return false; return false;
} }
void EnergyAccounting::setFixedPrice(float price) { void EnergyAccounting::setFixedPrice(float price, String currency) {
this->fixedPrice = price; this->fixedPrice = price;
this->currency = currency;
} }
float EnergyAccounting::getPriceForHour(uint8_t h) { float EnergyAccounting::getPriceForHour(uint8_t h) {

View File

@@ -3,7 +3,7 @@
#include "Uptime.h" #include "Uptime.h"
#include "TimeLib.h" #include "TimeLib.h"
#include "DnbCurrParser.h" #include "DnbCurrParser.h"
#include "version.h" #include "FirmwareVersion.h"
#include "GcmParser.h" #include "GcmParser.h"
@@ -37,7 +37,7 @@ void EntsoeApi::setup(EntsoeConfig& config) {
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
http.setReuse(false); http.setReuse(false);
http.setTimeout(60000); http.setTimeout(60000);
http.setUserAgent("ams2mqtt/" + String(VERSION)); http.setUserAgent("ams2mqtt/" + String(FirmwareVersion::VersionString));
http.useHTTP10(true); http.useHTTP10(true);
#if defined(AMS2MQTT_PRICE_KEY) #if defined(AMS2MQTT_PRICE_KEY)
@@ -135,7 +135,7 @@ bool EntsoeApi::loop() {
if(now < 10000) return false; // Grace period if(now < 10000) return false; // Grace period
time_t t = time(nullptr); time_t t = time(nullptr);
if(t < BUILD_EPOCH) return false; if(t < FirmwareVersion::BuildEpoch) return false;
#ifndef AMS2MQTT_PRICE_KEY #ifndef AMS2MQTT_PRICE_KEY
if(strlen(getToken()) == 0) { if(strlen(getToken()) == 0) {

View File

@@ -0,0 +1,12 @@
#ifndef _FIRMWARE_VERSION_h
#define _FIRMWARE_VERSION_h
class FirmwareVersion {
public:
static long BuildEpoch;
static const char* VersionString;
};
#endif

View File

@@ -0,0 +1,5 @@
#include "FirmwareVersion.h"
#include "generated_version.h"
long FirmwareVersion::BuildEpoch = BUILD_EPOCH;
const char* FirmwareVersion::VersionString = VERSION_STRING;

View File

@@ -3,7 +3,7 @@
#include "Arduino.h" #include "Arduino.h"
typedef struct HomeAssistantSensor { struct HomeAssistantSensor {
const char* name; const char* name;
const char* topic; const char* topic;
const char* path; const char* path;
@@ -15,75 +15,75 @@ typedef struct HomeAssistantSensor {
const uint8_t List1SensorCount PROGMEM = 1; const uint8_t List1SensorCount PROGMEM = 1;
const HomeAssistantSensor List1Sensors[List1SensorCount] PROGMEM = { const HomeAssistantSensor List1Sensors[List1SensorCount] PROGMEM = {
{"Active import", "/power", "P", "W", "power", "\"measurement\""} {"Active import", "/power", "P", "W", "power", "measurement"}
}; };
const uint8_t List2SensorCount PROGMEM = 8; const uint8_t List2SensorCount PROGMEM = 8;
const HomeAssistantSensor List2Sensors[List2SensorCount] PROGMEM = { const HomeAssistantSensor List2Sensors[List2SensorCount] PROGMEM = {
{"Reactive import", "/power", "Q", "var", "reactive_power", "\"measurement\""}, {"Reactive import", "/power", "Q", "var", "reactive_power", "measurement"},
{"Reactive export", "/power", "QO", "var", "reactive_power", "\"measurement\""}, {"Reactive export", "/power", "QO", "var", "reactive_power", "measurement"},
{"L1 current", "/power", "I1", "A", "current", "\"measurement\""}, {"L1 current", "/power", "I1", "A", "current", "measurement"},
{"L2 current", "/power", "I2", "A", "current", "\"measurement\""}, {"L2 current", "/power", "I2", "A", "current", "measurement"},
{"L3 current", "/power", "I3", "A", "current", "\"measurement\""}, {"L3 current", "/power", "I3", "A", "current", "measurement"},
{"L1 voltage", "/power", "U1", "V", "voltage", "\"measurement\""}, {"L1 voltage", "/power", "U1", "V", "voltage", "measurement"},
{"L2 voltage", "/power", "U2", "V", "voltage", "\"measurement\""}, {"L2 voltage", "/power", "U2", "V", "voltage", "measurement"},
{"L3 voltage", "/power", "U3", "V", "voltage", "\"measurement\""} {"L3 voltage", "/power", "U3", "V", "voltage", "measurement"}
}; };
const uint8_t List2ExportSensorCount PROGMEM = 1; const uint8_t List2ExportSensorCount PROGMEM = 1;
const HomeAssistantSensor List2ExportSensors[List2ExportSensorCount] PROGMEM = { const HomeAssistantSensor List2ExportSensors[List2ExportSensorCount] PROGMEM = {
{"Active export", "/power", "PO", "W", "power", "\"measurement\""} {"Active export", "/power", "PO", "W", "power", "measurement"}
}; };
const uint8_t List3SensorCount PROGMEM = 3; const uint8_t List3SensorCount PROGMEM = 3;
const HomeAssistantSensor List3Sensors[List3SensorCount] PROGMEM = { const HomeAssistantSensor List3Sensors[List3SensorCount] PROGMEM = {
{"Accumulated active import", "/energy", "tPI", "kWh", "energy", "\"total_increasing\""}, {"Accumulated active import", "/energy", "tPI", "kWh", "energy", "total_increasing"},
{"Accumulated reactive import","/energy", "tQI", "", "energy", "\"total_increasing\""}, {"Accumulated reactive import","/energy", "tQI", "kvarh","", "total_increasing"},
{"Accumulated reactive export","/energy", "tQO", "", "energy", "\"total_increasing\""} {"Accumulated reactive export","/energy", "tQO", "kvarh","", "total_increasing"}
}; };
const uint8_t List3ExportSensorCount PROGMEM = 1; const uint8_t List3ExportSensorCount PROGMEM = 1;
const HomeAssistantSensor List3ExportSensors[List3ExportSensorCount] PROGMEM = { const HomeAssistantSensor List3ExportSensors[List3ExportSensorCount] PROGMEM = {
{"Accumulated active export", "/energy", "tPO", "kWh", "energy", "\"total_increasing\""} {"Accumulated active export", "/energy", "tPO", "kWh", "energy", "total_increasing"}
}; };
const uint8_t List4SensorCount PROGMEM = 7; const uint8_t List4SensorCount PROGMEM = 7;
const HomeAssistantSensor List4Sensors[List4SensorCount] PROGMEM = { const HomeAssistantSensor List4Sensors[List4SensorCount] PROGMEM = {
{"Power factor", "/power", "PF", "%", "power_factor", "\"measurement\""}, {"Power factor", "/power", "PF", "%", "power_factor", "measurement"},
{"L1 power factor", "/power", "PF1", "%", "power_factor", "\"measurement\""}, {"L1 power factor", "/power", "PF1", "%", "power_factor", "measurement"},
{"L2 power factor", "/power", "PF2", "%", "power_factor", "\"measurement\""}, {"L2 power factor", "/power", "PF2", "%", "power_factor", "measurement"},
{"L3 power factor", "/power", "PF3", "%", "power_factor", "\"measurement\""}, {"L3 power factor", "/power", "PF3", "%", "power_factor", "measurement"},
{"L1 active import", "/power", "P1", "W", "power", "\"measurement\""}, {"L1 active import", "/power", "P1", "W", "power", "measurement"},
{"L2 active import", "/power", "P2", "W", "power", "\"measurement\""}, {"L2 active import", "/power", "P2", "W", "power", "measurement"},
{"L3 active import", "/power", "P3", "W", "power", "\"measurement\""} {"L3 active import", "/power", "P3", "W", "power", "measurement"}
}; };
const uint8_t List4ExportSensorCount PROGMEM = 3; const uint8_t List4ExportSensorCount PROGMEM = 3;
const HomeAssistantSensor List4ExportSensors[List4ExportSensorCount] PROGMEM = { const HomeAssistantSensor List4ExportSensors[List4ExportSensorCount] PROGMEM = {
{"L1 active export", "/power", "PO1", "W", "power", "\"measurement\""}, {"L1 active export", "/power", "PO1", "W", "power", "measurement"},
{"L2 active export", "/power", "PO2", "W", "power", "\"measurement\""}, {"L2 active export", "/power", "PO2", "W", "power", "measurement"},
{"L3 active export", "/power", "PO3", "W", "power", "\"measurement\""} {"L3 active export", "/power", "PO3", "W", "power", "measurement"}
}; };
const uint8_t RealtimeSensorCount PROGMEM = 8; const uint8_t RealtimeSensorCount PROGMEM = 8;
const HomeAssistantSensor RealtimeSensors[RealtimeSensorCount] PROGMEM = { const HomeAssistantSensor RealtimeSensors[RealtimeSensorCount] PROGMEM = {
{"Month max", "/realtime","max", "kWh", "energy", "\"total_increasing\""}, {"Month max", "/realtime","max", "kWh", "energy", "total_increasing"},
{"Tariff threshold", "/realtime","threshold", "kWh", "energy", "\"total_increasing\""}, {"Tariff threshold", "/realtime","threshold", "kWh", "energy", "total_increasing"},
{"Current hour used", "/realtime","hour.use", "kWh", "energy", "\"total_increasing\""}, {"Current hour used", "/realtime","hour.use", "kWh", "energy", "total_increasing"},
{"Current hour cost", "/realtime","hour.cost", "", "monetary", ""}, {"Current hour cost", "/realtime","hour.cost", "", "monetary", ""},
{"Current day used", "/realtime","day.use", "kWh", "energy", "\"total_increasing\""}, {"Current day used", "/realtime","day.use", "kWh", "energy", "total_increasing"},
{"Current day cost", "/realtime","day.cost", "", "monetary", ""}, {"Current day cost", "/realtime","day.cost", "", "monetary", ""},
{"Current month used", "/realtime","month.use", "kWh", "energy", "\"total_increasing\""}, {"Current month used", "/realtime","month.use", "kWh", "energy", "total_increasing"},
{"Current month cost", "/realtime","month.cost", "", "monetary", ""} {"Current month cost", "/realtime","month.cost", "", "monetary", ""}
}; };
const uint8_t RealtimeExportSensorCount PROGMEM = 6; const uint8_t RealtimeExportSensorCount PROGMEM = 6;
const HomeAssistantSensor RealtimeExportSensors[RealtimeExportSensorCount] PROGMEM = { const HomeAssistantSensor RealtimeExportSensors[RealtimeExportSensorCount] PROGMEM = {
{"Current hour produced", "/realtime","hour.produced", "kWh", "energy", "\"total_increasing\""}, {"Current hour produced", "/realtime","hour.produced", "kWh", "energy", "total_increasing"},
{"Current hour income", "/realtime","hour.income", "", "monetary", ""}, {"Current hour income", "/realtime","hour.income", "", "monetary", ""},
{"Current day produced", "/realtime","day.produced", "kWh", "energy", "\"total_increasing\""}, {"Current day produced", "/realtime","day.produced", "kWh", "energy", "total_increasing"},
{"Current day income", "/realtime","day.income", "", "monetary", ""}, {"Current day income", "/realtime","day.income", "", "monetary", ""},
{"Current month produced", "/realtime","month.produced", "kWh", "energy", "\"total_increasing\""}, {"Current month produced", "/realtime","month.produced", "kWh", "energy", "total_increasing"},
{"Current month income", "/realtime","month.income", "", "monetary", ""} {"Current month income", "/realtime","month.income", "", "monetary", ""}
}; };
@@ -102,11 +102,11 @@ const HomeAssistantSensor PriceSensor PROGMEM = {"Price in %02d %s", "/prices",
const uint8_t SystemSensorCount PROGMEM = 2; const uint8_t SystemSensorCount PROGMEM = 2;
const HomeAssistantSensor SystemSensors[SystemSensorCount] PROGMEM = { const HomeAssistantSensor SystemSensors[SystemSensorCount] PROGMEM = {
{"Status", "/state", "rssi", "dBm", "signal_strength", "\"measurement\""}, {"Status", "/state", "rssi", "dBm", "signal_strength", "measurement"},
{"Supply volt", "/state", "vcc", "V", "voltage", "\"measurement\""} {"Supply volt", "/state", "vcc", "V", "voltage", "measurement"}
}; };
const HomeAssistantSensor TemperatureSensor PROGMEM = {"Temperature sensor %s", "/temperatures", "temperatures['%s']", "°C", "temperature", "\"measurement\""}; const HomeAssistantSensor TemperatureSensor PROGMEM = {"Temperature sensor %s", "/temperatures", "temperatures['%s']", "°C", "temperature", "measurement"};
#endif #endif

View File

@@ -5,7 +5,6 @@
"obj_id" : "%s_%s", "obj_id" : "%s_%s",
"unit_of_meas" : "%s", "unit_of_meas" : "%s",
"val_tpl" : "{{ value_json.%s | is_defined }}", "val_tpl" : "{{ value_json.%s | is_defined }}",
"dev_cla" : "%s",
"dev" : { "dev" : {
"ids" : [ "%s" ], "ids" : [ "%s" ],
"name" : "%s", "name" : "%s",
@@ -13,5 +12,5 @@
"sw" : "%s", "sw" : "%s",
"mf" : "%s", "mf" : "%s",
"cu" : "%s" "cu" : "%s"
}%s %s }%s%s%s%s%s%s
} }

View File

@@ -1,7 +1,7 @@
#include "HomeAssistantMqttHandler.h" #include "HomeAssistantMqttHandler.h"
#include "hexutils.h" #include "hexutils.h"
#include "Uptime.h" #include "Uptime.h"
#include "version.h" #include "FirmwareVersion.h"
#include "json/ha1_json.h" #include "json/ha1_json.h"
#include "json/ha2_json.h" #include "json/ha2_json.h"
#include "json/ha3_json.h" #include "json/ha3_json.h"
@@ -329,7 +329,7 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, Energ
hw->getVcc(), hw->getVcc(),
hw->getWifiRssi(), hw->getWifiRssi(),
hw->getTemperature(), hw->getTemperature(),
VERSION FirmwareVersion::VersionString
); );
bool ret = mqtt->publish(topic + "/state", json); bool ret = mqtt->publish(topic + "/state", json);
loop(); loop();
@@ -350,15 +350,18 @@ void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor& sensor)
deviceUid.c_str(), uid.c_str(), deviceUid.c_str(), uid.c_str(),
sensor.uom, sensor.uom,
sensor.path, sensor.path,
sensor.devcl,
deviceUid.c_str(), deviceUid.c_str(),
deviceName.c_str(), deviceName.c_str(),
deviceModel.c_str(), deviceModel.c_str(),
VERSION, FirmwareVersion::VersionString,
manufacturer.c_str(), manufacturer.c_str(),
deviceUrl.c_str(), deviceUrl.c_str(),
strlen_P(sensor.stacl) > 0 ? ", \"stat_cla\" :" : "", strlen_P(sensor.devcl) > 0 ? ",\"dev_cla\":\"" : "",
strlen_P(sensor.stacl) > 0 ? (char *) FPSTR(sensor.stacl) : "" strlen_P(sensor.devcl) > 0 ? (char *) FPSTR(sensor.devcl) : "",
strlen_P(sensor.devcl) > 0 ? "\"" : "",
strlen_P(sensor.stacl) > 0 ? ",\"stat_cla\":\"" : "",
strlen_P(sensor.stacl) > 0 ? (char *) FPSTR(sensor.stacl) : "",
strlen_P(sensor.stacl) > 0 ? "\"" : ""
); );
mqtt->publish(discoveryTopic + deviceUid + "_" + uid.c_str() + "/config", json, true, 0); mqtt->publish(discoveryTopic + deviceUid + "_" + uid.c_str() + "/config", json, true, 0);
loop(); loop();

View File

@@ -1,5 +1,5 @@
#include "JsonMqttHandler.h" #include "JsonMqttHandler.h"
#include "version.h" #include "FirmwareVersion.h"
#include "hexutils.h" #include "hexutils.h"
#include "Uptime.h" #include "Uptime.h"
#include "json/json1_json.h" #include "json/json1_json.h"
@@ -341,7 +341,7 @@ bool JsonMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounti
hw->getVcc(), hw->getVcc(),
hw->getWifiRssi(), hw->getWifiRssi(),
hw->getTemperature(), hw->getTemperature(),
VERSION FirmwareVersion::VersionString
); );
bool ret = mqtt->publish(topic, json); bool ret = mqtt->publish(topic, json);
loop(); loop();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2714,9 +2714,9 @@
} }
}, },
"node_modules/yaml": { "node_modules/yaml": {
"version": "2.1.1", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
"integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">= 14" "node": ">= 14"
@@ -4411,9 +4411,9 @@
"dev": true "dev": true
}, },
"yaml": { "yaml": {
"version": "2.1.1", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
"integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
"dev": true "dev": true
} }
} }

View File

@@ -14,7 +14,8 @@
function point(v) { function point(v) {
return { return {
label: fmtnum(v) +'A', label: fmtnum(v) + 'A',
title: v.toFixed(1) + ' A',
value: isNaN(v) ? 0 : v, value: isNaN(v) ? 0 : v,
color: ampcol(v ? (v)/(max)*100 : 0) color: ampcol(v ? (v)/(max)*100 : 0)
}; };

View File

@@ -61,6 +61,7 @@
<g class='bars'> <g class='bars'>
{#each config.points as point, i} {#each config.points as point, i}
<g>
{#if point.value !== undefined} {#if point.value !== undefined}
<rect <rect
x="{xScale(i) + 2}" x="{xScale(i) + 2}"
@@ -79,9 +80,15 @@
text-anchor="{barWidth < 25 ? 'left' : 'middle'}" text-anchor="{barWidth < 25 ? 'left' : 'middle'}"
fill="{yScale(point.value) > yScale(0)-labelOffset ? point.color : 'white'}" fill="{yScale(point.value) > yScale(0)-labelOffset ? point.color : 'white'}"
transform="rotate({barWidth < 25 ? 90 : 0}, {xScale(i) + (barWidth/2)}, {yScale(point.value) > yScale(0)-labelOffset ? yScale(point.value) - labelOffset : yScale(point.value)+9})" transform="rotate({barWidth < 25 ? 90 : 0}, {xScale(i) + (barWidth/2)}, {yScale(point.value) > yScale(0)-labelOffset ? yScale(point.value) - labelOffset : yScale(point.value)+9})"
>{point.label}</text> >{point.label}</text>
{#if point.title}
<title>{point.title}</title>
{/if}
{/if} {/if}
{/if} {/if}
</g>
<g>
{#if point.value2 > 0.0001} {#if point.value2 > 0.0001}
<rect <rect
x="{xScale(i) + 2}" x="{xScale(i) + 2}"
@@ -100,8 +107,12 @@
fill="{yScale(-point.value2) < yScale(0)+12 ? point.color : 'white'}" fill="{yScale(-point.value2) < yScale(0)+12 ? point.color : 'white'}"
transform="rotate({barWidth < 25 ? 90 : 0}, {xScale(i) + (barWidth/2)}, {yScale(point.value2 - config.y.min) > yScale(0)-12 ? yScale(point.value2 - config.y.min) - 12 : yScale(point.value2 - config.y.min)+9})" transform="rotate({barWidth < 25 ? 90 : 0}, {xScale(i) + (barWidth/2)}, {yScale(point.value2 - config.y.min) > yScale(0)-12 ? yScale(point.value2 - config.y.min) - 12 : yScale(point.value2 - config.y.min)+9})"
>{point.label2}</text> >{point.label2}</text>
{#if point.title2}
<title>{point.title2}</title>
{/if}
{/if} {/if}
{/if} {/if}
</g>
{/each} {/each}
</g> </g>
</svg> </svg>

View File

@@ -26,8 +26,10 @@
}); });
points.push({ points.push({
label: imp.toFixed(1), label: imp.toFixed(1),
title: imp.toFixed(2) + ' kWh',
value: imp*10, value: imp*10,
label2: exp.toFixed(1), label2: exp.toFixed(1),
title2: exp.toFixed(2) + ' kWh',
value2: exp*10, value2: exp*10,
color: '#7c3aed' color: '#7c3aed'
}); });
@@ -46,8 +48,10 @@
}); });
points.push({ points.push({
label: imp.toFixed(1), label: imp.toFixed(1),
title: imp.toFixed(2) + ' kWh',
value: imp*10, value: imp*10,
label2: exp.toFixed(1), label2: exp.toFixed(1),
title2: exp.toFixed(2) + ' kWh',
value2: exp*10, value2: exp*10,
color: '#7c3aed' color: '#7c3aed'
}); });

View File

@@ -116,7 +116,7 @@ export function hanError(err) {
case -51: return "Authentication failed"; case -51: return "Authentication failed";
case -52: return "Decryption failed"; case -52: return "Decryption failed";
case -53: return "Encryption key invalid"; case -53: return "Encryption key invalid";
case 90: return "No HAN data received last 30s"; case 90: return "No HAN data received for at least 30s";
case 91: return "Serial break"; case 91: return "Serial break";
case 92: return "Serial buffer full"; case 92: return "Serial buffer full";
case 93: return "Serial FIFO overflow"; case 93: return "Serial FIFO overflow";

View File

@@ -27,9 +27,11 @@
label: zeropad(i) label: zeropad(i)
}); });
points.push({ points.push({
label: imp.toFixed(0), label: imp.toFixed(imp < 10 ? 1 : 0),
title: imp.toFixed(2) + ' kWh',
value: imp, value: imp,
label2: exp.toFixed(0), label2: exp.toFixed(exp < 10 ? 1 : 0),
title2: exp.toFixed(2) + ' kWh',
value2: exp, value2: exp,
color: '#7c3aed' color: '#7c3aed'
}); });
@@ -46,9 +48,11 @@
label: zeropad(i) label: zeropad(i)
}); });
points.push({ points.push({
label: imp.toFixed(0), label: imp.toFixed(imp < 10 ? 1 : 0),
title: imp.toFixed(2) + ' kWh',
value: imp, value: imp,
label2: exp.toFixed(0), label2: exp.toFixed(exp < 10 ? 1 : 0),
title2: exp.toFixed(2) + ' kWh',
value2: exp, value2: exp,
color: '#7c3aed' color: '#7c3aed'
}); });

View File

@@ -26,8 +26,10 @@
}); });
points.push({ points.push({
label: val > 0 ? val.toFixed(d) : '', label: val > 0 ? val.toFixed(d) : '',
title: val > 0 ? val.toFixed(2) + ' ' + json.currency : '',
value: val > 0 ? Math.abs(val*100) : 0, value: val > 0 ? Math.abs(val*100) : 0,
label2: val < 0 ? val.toFixed(d) : '', label2: val < 0 ? val.toFixed(d) : '',
title2: val < 0 ? val.toFixed(2) + ' ' + json.currency : '',
value2: val < 0 ? Math.abs(val*100) : 0, value2: val < 0 ? Math.abs(val*100) : 0,
color: '#7c3aed' color: '#7c3aed'
}); });

View File

@@ -22,7 +22,7 @@
label: name.slice(-4) label: name.slice(-4)
}); });
points.push({ points.push({
label: val.toFixed(1), label: val.toFixed(1),
value: val, value: val,
color: '#7c3aed' color: '#7c3aed'
}); });

View File

@@ -12,6 +12,7 @@
function point(v) { function point(v) {
return { return {
label: fmtnum(v) + 'V', label: fmtnum(v) + 'V',
title: v.toFixed(1) + ' V',
value: isNaN(v) ? 0 : v, value: isNaN(v) ? 0 : v,
color: voltcol(v ? v : 0) color: voltcol(v ? v : 0)
}; };

View File

@@ -1,5 +1,6 @@
#include "AmsWebServer.h" #include "AmsWebServer.h"
#include "AmsWebHeaders.h" #include "AmsWebHeaders.h"
#include "FirmwareVersion.h"
#include "base64.h" #include "base64.h"
#include "hexutils.h" #include "hexutils.h"
@@ -31,8 +32,6 @@
#include "html/conf_ui_json.h" #include "html/conf_ui_json.h"
#include "html/firmware_html.h" #include "html/firmware_html.h"
#include "version.h"
#if defined(ESP32) #if defined(ESP32)
#include <esp_task_wdt.h> #include <esp_task_wdt.h>
#include <esp_wifi.h> #include <esp_wifi.h>
@@ -64,9 +63,9 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
this->ea = ea; this->ea = ea;
server.on(F("/"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); server.on(F("/"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
snprintf_P(buf, 32, PSTR("/index-%s.js"), VERSION); snprintf_P(buf, 32, PSTR("/index-%s.js"), FirmwareVersion::VersionString);
server.on(buf, HTTP_GET, std::bind(&AmsWebServer::indexJs, this)); server.on(buf, HTTP_GET, std::bind(&AmsWebServer::indexJs, this));
snprintf_P(buf, 32, PSTR("/index-%s.css"), VERSION); snprintf_P(buf, 32, PSTR("/index-%s.css"), FirmwareVersion::VersionString);
server.on(buf, HTTP_GET, std::bind(&AmsWebServer::indexCss, this)); server.on(buf, HTTP_GET, std::bind(&AmsWebServer::indexCss, this));
server.on(F("/configuration"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); server.on(F("/configuration"), HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
@@ -263,7 +262,7 @@ void AmsWebServer::sysinfoJson() {
meterId.replace(F("\\"), F("\\\\")); meterId.replace(F("\\"), F("\\\\"));
int size = snprintf_P(buf, BufferSize, SYSINFO_JSON, int size = snprintf_P(buf, BufferSize, SYSINFO_JSON,
VERSION, FirmwareVersion::VersionString,
#if defined(CONFIG_IDF_TARGET_ESP32S2) #if defined(CONFIG_IDF_TARGET_ESP32S2)
"esp32s2", "esp32s2",
#elif defined(CONFIG_IDF_TARGET_ESP32C3) #elif defined(CONFIG_IDF_TARGET_ESP32C3)
@@ -770,7 +769,6 @@ void AmsWebServer::indexJs() {
return; return;
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1MO); server.sendHeader(HEADER_CACHE_CONTROL, CACHE_1MO);
server.setContentLength(INDEX_JS_LEN);
server.send_P(200, MIME_JS, INDEX_JS); server.send_P(200, MIME_JS, INDEX_JS);
} }
@@ -824,7 +822,7 @@ void AmsWebServer::configurationJson() {
server.setContentLength(CONTENT_LENGTH_UNKNOWN); server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send_P(200, MIME_JSON, PSTR("{\"version\":\"")); server.send_P(200, MIME_JSON, PSTR("{\"version\":\""));
server.sendContent_P(VERSION); server.sendContent_P(FirmwareVersion::VersionString);
server.sendContent_P(PSTR("\",")); server.sendContent_P(PSTR("\","));
snprintf_P(buf, BufferSize, CONF_GENERAL_JSON, snprintf_P(buf, BufferSize, CONF_GENERAL_JSON,
ntpConfig.timezone, ntpConfig.timezone,
@@ -1536,7 +1534,7 @@ void AmsWebServer::upgrade() {
} }
void AmsWebServer::upgradeFromUrl(String url, String nextVersion) { void AmsWebServer::upgradeFromUrl(String url, String nextVersion) {
config->setUpgradeInformation(0xFF, 0xFF, VERSION, nextVersion.c_str()); config->setUpgradeInformation(0xFF, 0xFF, FirmwareVersion::VersionString, nextVersion.c_str());
WiFiClient client; WiFiClient client;
#if defined(ESP8266) #if defined(ESP8266)
@@ -1555,10 +1553,10 @@ void AmsWebServer::upgradeFromUrl(String url, String nextVersion) {
#if defined(ESP8266) #if defined(ESP8266)
ESP8266HTTPUpdate httpUpdate = ESP8266HTTPUpdate(60000); ESP8266HTTPUpdate httpUpdate = ESP8266HTTPUpdate(60000);
String currentVersion = VERSION; String currentVersion = FirmwareVersion::VersionString;
#elif defined(ESP32) #elif defined(ESP32)
HTTPUpdate httpUpdate = HTTPUpdate(60000); HTTPUpdate httpUpdate = HTTPUpdate(60000);
String currentVersion = String(VERSION) + "-" + chipType; String currentVersion = String(FirmwareVersion::VersionString) + "-" + chipType;
#endif #endif
httpUpdate.rebootOnUpdate(false); httpUpdate.rebootOnUpdate(false);
@@ -1566,7 +1564,7 @@ void AmsWebServer::upgradeFromUrl(String url, String nextVersion) {
HTTPUpdateResult ret = httpUpdate.update(client, url, currentVersion); HTTPUpdateResult ret = httpUpdate.update(client, url, currentVersion);
int lastError = httpUpdate.getLastError(); int lastError = httpUpdate.getLastError();
config->setUpgradeInformation(ret, ret == HTTP_UPDATE_OK ? 0 : lastError, VERSION, nextVersion.c_str()); config->setUpgradeInformation(ret, ret == HTTP_UPDATE_OK ? 0 : lastError, FirmwareVersion::VersionString, nextVersion.c_str());
switch(ret) { switch(ret) {
case HTTP_UPDATE_FAILED: case HTTP_UPDATE_FAILED:
debugger->printf_P(PSTR("Update failed\n")); debugger->printf_P(PSTR("Update failed\n"));
@@ -1604,7 +1602,7 @@ void AmsWebServer::firmwarePost() {
if(rebootForUpgrade) { if(rebootForUpgrade) {
server.send(200); server.send(200);
} else { } else {
config->setUpgradeInformation(0xFF, 0xFF, VERSION, ""); config->setUpgradeInformation(0xFF, 0xFF, FirmwareVersion::VersionString, "");
if(server.hasArg(F("url"))) { if(server.hasArg(F("url"))) {
String url = server.arg(F("url")); String url = server.arg(F("url"));
if(!url.isEmpty() && (url.startsWith(F("http://")) || url.startsWith(F("https://")))) { if(!url.isEmpty() && (url.startsWith(F("http://")) || url.startsWith(F("https://")))) {
@@ -1891,7 +1889,7 @@ void AmsWebServer::configFileDownload() {
server.setContentLength(CONTENT_LENGTH_UNKNOWN); server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send_P(200, MIME_PLAIN, PSTR("amsconfig\n")); server.send_P(200, MIME_PLAIN, PSTR("amsconfig\n"));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("version %s\n"), VERSION)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("version %s\n"), FirmwareVersion::VersionString));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("boardType %d\n"), sys.boardType)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("boardType %d\n"), sys.boardType));
if(includeWifi) { if(includeWifi) {
@@ -2019,6 +2017,7 @@ void AmsWebServer::configFileDownload() {
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeArea %s\n"), entsoe.area)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeArea %s\n"), entsoe.area));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeCurrency %s\n"), entsoe.currency)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeCurrency %s\n"), entsoe.currency));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeMultiplier %.3f\n"), entsoe.multiplier / 1000.0)); server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeMultiplier %.3f\n"), entsoe.multiplier / 1000.0));
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("entsoeFixedPrice %.3f\n"), entsoe.fixedPrice / 1000.0));
} }
if(includeThresholds) { if(includeThresholds) {

View File

@@ -2,7 +2,7 @@
extra_configs = platformio-user.ini extra_configs = platformio-user.ini
[common] [common]
lib_deps = EEPROM, LittleFS, DNSServer, https://github.com/256dpi/arduino-mqtt.git, OneWireNg@0.10.0, DallasTemperature@3.9.1, EspSoftwareSerial@6.14.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1, Timezone@1.2.4, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, EntsoePriceApi, EnergyAccounting, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, SvelteUi lib_deps = EEPROM, LittleFS, DNSServer, https://github.com/256dpi/arduino-mqtt.git, OneWireNg@0.10.0, DallasTemperature@3.9.1, EspSoftwareSerial@6.14.1, https://github.com/gskjold/RemoteDebug.git, Time@1.6.1, Timezone@1.2.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, EntsoePriceApi, EnergyAccounting, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, SvelteUi
lib_ignore = OneWire lib_ignore = OneWire
extra_scripts = extra_scripts =
pre:scripts/addversion.py pre:scripts/addversion.py

View File

@@ -2,7 +2,7 @@ import os
import subprocess import subprocess
from time import time from time import time
FILENAME_VERSION_H = 'lib/AmsConfiguration/include/version.h' FILENAME_VERSION_H = 'lib/FirmwareVersion/src/generated_version.h'
version = os.environ.get('GITHUB_TAG') version = os.environ.get('GITHUB_TAG')
if version == None: if version == None:
try: try:
@@ -15,9 +15,7 @@ if version == None:
version = "SNAPSHOT" version = "SNAPSHOT"
hf = """ hf = """
#ifndef VERSION #define VERSION_STRING "{}"
#define VERSION "{}"
#endif
#define BUILD_EPOCH {} #define BUILD_EPOCH {}
""".format(version, round(time())) """.format(version, round(time()))
with open(FILENAME_VERSION_H, 'w+') as f: with open(FILENAME_VERSION_H, 'w+') as f:

View File

@@ -29,7 +29,7 @@ fi
if [ "$1" = "flash" ];then if [ "$1" = "flash" ];then
$esptool --chip esp32 --port $2 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect \ $esptool --chip esp32 --port $2 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect \
0x1000 bootloader_dio_40m.bin \ 0x1000 bootloader.bin \
0x8000 partitions.bin \ 0x8000 partitions.bin \
0xe000 boot_app0.bin \ 0xe000 boot_app0.bin \
0x10000 firmware.bin 0x10000 firmware.bin

View File

@@ -8,7 +8,6 @@ if [ ! -d $build_dir ];then
exit 1 exit 1
fi fi
cp ~/.platformio/packages/framework-arduinoespressif32/tools/sdk/$env/bin/bootloader_dio_40m.bin $build_dir
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin $build_dir cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin $build_dir
chmod +x scripts/$env/flash.sh chmod +x scripts/$env/flash.sh
zip -j $env.zip $build_dir/*.bin scripts/$env/flash.sh zip -j $env.zip $build_dir/*.bin scripts/$env/flash.sh

View File

@@ -29,7 +29,7 @@ fi
if [ "$1" = "flash" ];then if [ "$1" = "flash" ];then
$esptool --chip esp32c3 --port $2 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect \ $esptool --chip esp32c3 --port $2 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect \
0x1000 bootloader_qio_80m.bin \ 0x0000 bootloader.bin \
0x8000 partitions.bin \ 0x8000 partitions.bin \
0xe000 boot_app0.bin \ 0xe000 boot_app0.bin \
0x10000 firmware.bin 0x10000 firmware.bin

View File

@@ -8,7 +8,6 @@ if [ ! -d $build_dir ];then
exit 1 exit 1
fi fi
cp ~/.platformio/packages/framework-arduinoespressif32/tools/sdk/$env/bin/bootloader_qio_80m.bin $build_dir
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin $build_dir cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin $build_dir
chmod +x scripts/$env/flash.sh chmod +x scripts/$env/flash.sh
zip -j $env.zip $build_dir/*.bin scripts/$env/flash.sh zip -j $env.zip $build_dir/*.bin scripts/$env/flash.sh

View File

@@ -29,7 +29,7 @@ fi
if [ "$1" = "flash" ];then if [ "$1" = "flash" ];then
$esptool --chip esp32s2 --port $2 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect \ $esptool --chip esp32s2 --port $2 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect \
0x1000 bootloader_qio_40m.bin \ 0x1000 bootloader.bin \
0x8000 partitions.bin \ 0x8000 partitions.bin \
0xe000 boot_app0.bin \ 0xe000 boot_app0.bin \
0x10000 firmware.bin 0x10000 firmware.bin

View File

@@ -8,7 +8,6 @@ if [ ! -d $build_dir ];then
exit 1 exit 1
fi fi
cp ~/.platformio/packages/framework-arduinoespressif32/tools/sdk/$env/bin/bootloader_qio_40m.bin $build_dir
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin $build_dir cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin $build_dir
chmod +x scripts/$env/flash.sh chmod +x scripts/$env/flash.sh
zip -j $env.zip $build_dir/*.bin scripts/$env/flash.sh zip -j $env.zip $build_dir/*.bin scripts/$env/flash.sh

View File

@@ -29,7 +29,7 @@ fi
if [ "$1" = "flash" ];then if [ "$1" = "flash" ];then
$esptool --chip esp32 --port $2 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect \ $esptool --chip esp32 --port $2 --baud 460800 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect \
0x1000 bootloader_dio_40m.bin \ 0x1000 bootloader.bin \
0x8000 partitions.bin \ 0x8000 partitions.bin \
0xe000 boot_app0.bin \ 0xe000 boot_app0.bin \
0x10000 firmware.bin 0x10000 firmware.bin

View File

@@ -8,7 +8,6 @@ if [ ! -d $build_dir ];then
exit 1 exit 1
fi fi
cp ~/.platformio/packages/framework-arduinoespressif32/tools/sdk/$env/bin/bootloader_dio_40m.bin $build_dir
cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin $build_dir cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin $build_dir
chmod +x scripts/$env/flash.sh chmod +x scripts/$env/flash.sh
zip -j $env.zip $build_dir/*.bin scripts/$env/flash.sh zip -j $env.zip $build_dir/*.bin scripts/$env/flash.sh

View File

@@ -23,6 +23,8 @@
* to Norwegian meters, but may also support data from electricity providers in other countries. * to Norwegian meters, but may also support data from electricity providers in other countries.
*/ */
#include <Arduino.h>
#if defined(ESP8266) #if defined(ESP8266)
ADC_MODE(ADC_VCC); ADC_MODE(ADC_VCC);
#endif #endif
@@ -36,8 +38,7 @@ ADC_MODE(ADC_VCC);
#include <driver/uart.h> #include <driver/uart.h>
#endif #endif
#include "version.h" #include "FirmwareVersion.h"
#include "AmsToMqttBridge.h" #include "AmsToMqttBridge.h"
#include "AmsStorage.h" #include "AmsStorage.h"
#include "AmsDataStorage.h" #include "AmsDataStorage.h"
@@ -133,6 +134,30 @@ LLCParser *llcParser = NULL;
DLMSParser *dlmsParser = NULL; DLMSParser *dlmsParser = NULL;
DSMRParser *dsmrParser = NULL; DSMRParser *dsmrParser = NULL;
void configFileParse();
void swapWifiMode();
void WiFi_connect();
void WiFi_post_connect();
void MQTT_connect();
void handleNtpChange();
void handleDataSuccess(AmsData* data);
void handleTemperature(unsigned long now);
void handleSystem(unsigned long now);
void handleAutodetect(unsigned long now);
void handleButton(unsigned long now);
void handlePriceApi(unsigned long now);
void handleClear(unsigned long now);
void handleEnergyAccountingChanged();
bool readHanPort();
void setupHanPort(GpioConfig& gpioConfig, uint32_t baud, uint8_t parityOrdinal, bool invert);
void rxerr(int err);
int16_t unwrapData(uint8_t *buf, DataParserContext &context);
void errorBlink();
void printHanReadError(int pos);
void debugPrint(byte *buffer, int start, int length);
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@@ -186,7 +211,7 @@ void setup() {
ws.setEntsoeApi(eapi); ws.setEntsoeApi(eapi);
} }
ws.setPriceSettings(entsoe.area, entsoe.currency); ws.setPriceSettings(entsoe.area, entsoe.currency);
ea.setFixedPrice(entsoe.fixedPrice / 1000.0); ea.setFixedPrice(entsoe.fixedPrice / 1000.0, entsoe.currency);
bool shared = false; bool shared = false;
config.getMeterConfig(meterConfig); config.getMeterConfig(meterConfig);
Serial.flush(); Serial.flush();
@@ -309,7 +334,7 @@ void setup() {
} }
flashed = Update.end(true); flashed = Update.end(true);
} }
config.setUpgradeInformation(flashed ? 2 : 0, 0xFF, VERSION, ""); config.setUpgradeInformation(flashed ? 2 : 0, 0xFF, FirmwareVersion::VersionString, "");
firmwareFile.close(); firmwareFile.close();
} else { } else {
debugW_P(PSTR("AP button pressed, skipping firmware update and deleting firmware file.")); debugW_P(PSTR("AP button pressed, skipping firmware update and deleting firmware file."));
@@ -383,6 +408,7 @@ bool wifiConnected = false;
unsigned long lastTemperatureRead = 0; unsigned long lastTemperatureRead = 0;
unsigned long lastSysupdate = 0; unsigned long lastSysupdate = 0;
unsigned long lastErrorBlink = 0; unsigned long lastErrorBlink = 0;
unsigned long lastDataStoreUpdate = 0;
int lastError = 0; int lastError = 0;
bool meterAutodetect = false; bool meterAutodetect = false;
@@ -520,6 +546,9 @@ void loop() {
debugW_P(PSTR("Used %dms to read HAN port (false)"), millis()-start); debugW_P(PSTR("Used %dms to read HAN port (false)"), millis()-start);
} }
} }
if(now > lastDataStoreUpdate && now - lastDataStoreUpdate > 3600000 && !ds.isHappy()) {
handleClear(now);
}
} catch(const std::exception& e) { } catch(const std::exception& e) {
debugE_P(PSTR("Exception in readHanPort (%s)"), e.what()); debugE_P(PSTR("Exception in readHanPort (%s)"), e.what());
meterState.setLastError(METER_ERROR_EXCEPTION); meterState.setLastError(METER_ERROR_EXCEPTION);
@@ -550,6 +579,17 @@ void loop() {
} }
} }
void handleClear(unsigned long now) {
tmElements_t tm;
breakTime(time(nullptr), tm);
if(tm.Minute == 0) {
AmsData nullData;
debugI_P(PSTR("Clearing data that have not been updated"));
ds.update(&nullData);
lastDataStoreUpdate = now;
}
}
void handleEnergyAccountingChanged() { void handleEnergyAccountingChanged() {
EnergyAccountingConfig *eac = ea.getConfig(); EnergyAccountingConfig *eac = ea.getConfig();
config.getEnergyAccountingConfig(*eac); config.getEnergyAccountingConfig(*eac);
@@ -655,7 +695,7 @@ void handlePriceApi(unsigned long now) {
} }
ws.setPriceSettings(entsoe.area, entsoe.currency); ws.setPriceSettings(entsoe.area, entsoe.currency);
config.ackEntsoeChange(); config.ackEntsoeChange();
ea.setFixedPrice(entsoe.fixedPrice / 1000.0); ea.setFixedPrice(entsoe.fixedPrice / 1000.0, entsoe.currency);
} }
} }
@@ -1126,15 +1166,15 @@ void handleDataSuccess(AmsData* data) {
} }
time_t now = time(nullptr); time_t now = time(nullptr);
if(now < BUILD_EPOCH && data->getListType() >= 3) { if(now < FirmwareVersion::BuildEpoch && data->getListType() >= 3) {
if(data->getMeterTimestamp() > BUILD_EPOCH) { if(data->getMeterTimestamp() > FirmwareVersion::BuildEpoch) {
debugI_P(PSTR("Using timestamp from meter")); debugI_P(PSTR("Using timestamp from meter"));
now = data->getMeterTimestamp(); now = data->getMeterTimestamp();
} else if(data->getPackageTimestamp() > BUILD_EPOCH) { } else if(data->getPackageTimestamp() > FirmwareVersion::BuildEpoch) {
debugI_P(PSTR("Using timestamp from meter (DLMS)")); debugI_P(PSTR("Using timestamp from meter (DLMS)"));
now = data->getPackageTimestamp(); now = data->getPackageTimestamp();
} }
if(now > BUILD_EPOCH) { if(now > FirmwareVersion::BuildEpoch) {
timeval tv { now, 0}; timeval tv { now, 0};
settimeofday(&tv, nullptr); settimeofday(&tv, nullptr);
} }
@@ -1143,20 +1183,21 @@ void handleDataSuccess(AmsData* data) {
meterState.apply(*data); meterState.apply(*data);
bool saveData = false; bool saveData = false;
if(!ds.isHappy() && now > BUILD_EPOCH) { if(!ds.isDayHappy() && now > FirmwareVersion::BuildEpoch) {
debugD_P(PSTR("Its time to update data storage")); debugD_P(PSTR("Its time to update data storage"));
tmElements_t tm; tmElements_t tm;
breakTime(now, tm); breakTime(now, tm);
if(tm.Minute == 0) { if(tm.Minute == 0) {
debugV_P(PSTR(" using actual data")); debugV_P(PSTR(" using actual data"));
saveData = ds.update(data); saveData = ds.update(data);
} else if(meterState.getListType() >= 3) { } else if(tm.Minute == 1 && meterState.getListType() >= 3) {
debugV_P(PSTR(" using estimated data")); debugV_P(PSTR(" using estimated data"));
saveData = ds.update(&meterState); saveData = ds.update(&meterState);
} }
if(saveData) { if(saveData) {
debugI_P(PSTR("Saving data")); debugI_P(PSTR("Saving data"));
ds.save(); ds.save();
lastDataStoreUpdate = millis();
} }
} }
@@ -1619,7 +1660,7 @@ void MQTT_connect() {
time_t epoch = time(nullptr); time_t epoch = time(nullptr);
if(mqttConfig.ssl) { if(mqttConfig.ssl) {
if(epoch < BUILD_EPOCH) { if(epoch < FirmwareVersion::BuildEpoch) {
debugI_P(PSTR("NTP not ready for MQTT SSL")); debugI_P(PSTR("NTP not ready for MQTT SSL"));
return; return;
} }
@@ -1967,6 +2008,9 @@ void configFileParse() {
} else if(strncmp_P(buf, PSTR("entsoeMultiplier "), 17) == 0) { } else if(strncmp_P(buf, PSTR("entsoeMultiplier "), 17) == 0) {
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; }; if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
entsoe.multiplier = String(buf+17).toFloat() * 1000; entsoe.multiplier = String(buf+17).toFloat() * 1000;
} else if(strncmp_P(buf, PSTR("entsoeFixedPrice "), 17) == 0) {
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
entsoe.fixedPrice = String(buf+17).toFloat() * 1000;
} else if(strncmp_P(buf, PSTR("thresholds "), 11) == 0) { } else if(strncmp_P(buf, PSTR("thresholds "), 11) == 0) {
if(!lEac) { config.getEnergyAccountingConfig(eac); lEac = true; }; if(!lEac) { config.getEnergyAccountingConfig(eac); lEac = true; };
int i = 0; int i = 0;

View File

@@ -376,19 +376,6 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
break; break;
} }
} }
} else if(meterType == AmsTypeKaifa) {
if(l1current != 0)
l1current /= 1000;
if(l2current != 0)
l2current /= 1000;
if(l3current != 0)
l3current /= 1000;
if(l1voltage != 0)
l1voltage /= 10;
if(l2voltage != 0)
l2voltage /= 10;
if(l3voltage != 0)
l3voltage /= 10;
} }
lastUpdateMillis = millis(); lastUpdateMillis = millis();

View File

@@ -1,5 +0,0 @@
#ifndef VERSION
#define VERSION "538de5e"
#endif
#define BUILD_EPOCH 1668532199