Added usage plots and ADC reading for Vcc

This commit is contained in:
Gunnar Skjold
2021-11-27 20:16:26 +01:00
parent 6f09f523e4
commit eb59245118
27 changed files with 1422 additions and 615 deletions

View File

@@ -2,7 +2,7 @@
bool AmsConfiguration::getSystemConfig(SystemConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_SYSTEM_START, config);
EEPROM.end();
return true;
@@ -12,7 +12,7 @@ bool AmsConfiguration::getSystemConfig(SystemConfig& config) {
}
bool AmsConfiguration::setSystemConfig(SystemConfig& config) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_SYSTEM_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -21,7 +21,7 @@ bool AmsConfiguration::setSystemConfig(SystemConfig& config) {
bool AmsConfiguration::getWiFiConfig(WiFiConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_WIFI_START, config);
EEPROM.end();
return true;
@@ -45,7 +45,7 @@ bool AmsConfiguration::setWiFiConfig(WiFiConfig& config) {
} else {
wifiChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_WIFI_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -85,7 +85,7 @@ void AmsConfiguration::ackWifiChange() {
bool AmsConfiguration::getMqttConfig(MqttConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_MQTT_START, config);
EEPROM.end();
return true;
@@ -110,7 +110,7 @@ bool AmsConfiguration::setMqttConfig(MqttConfig& config) {
} else {
mqttChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_MQTT_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -143,7 +143,7 @@ void AmsConfiguration::ackMqttChange() {
bool AmsConfiguration::getWebConfig(WebConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_WEB_START, config);
EEPROM.end();
return true;
@@ -154,7 +154,7 @@ bool AmsConfiguration::getWebConfig(WebConfig& config) {
}
bool AmsConfiguration::setWebConfig(WebConfig& config) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_WEB_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -169,7 +169,7 @@ void AmsConfiguration::clearAuth(WebConfig& config) {
bool AmsConfiguration::getMeterConfig(MeterConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_METER_START, config);
EEPROM.end();
return true;
@@ -193,7 +193,7 @@ bool AmsConfiguration::setMeterConfig(MeterConfig& config) {
} else {
meterChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_METER_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -221,7 +221,7 @@ void AmsConfiguration::ackMeterChanged() {
bool AmsConfiguration::getDebugConfig(DebugConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_DEBUG_START, config);
EEPROM.end();
return true;
@@ -232,7 +232,7 @@ bool AmsConfiguration::getDebugConfig(DebugConfig& config) {
}
bool AmsConfiguration::setDebugConfig(DebugConfig& config) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_DEBUG_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -247,7 +247,7 @@ void AmsConfiguration::clearDebug(DebugConfig& config) {
bool AmsConfiguration::getDomoticzConfig(DomoticzConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_DOMOTICZ_START, config);
EEPROM.end();
return true;
@@ -269,7 +269,7 @@ bool AmsConfiguration::setDomoticzConfig(DomoticzConfig& config) {
domoChanged = true;
}
mqttChanged = domoChanged;
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_DOMOTICZ_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -310,7 +310,7 @@ bool AmsConfiguration::pinUsed(uint8_t pin, GpioConfig& config) {
bool AmsConfiguration::getGpioConfig(GpioConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_GPIO_START, config);
EEPROM.end();
return true;
@@ -366,7 +366,7 @@ bool AmsConfiguration::setGpioConfig(GpioConfig& config) {
if(config.apPin >= 0)
pinMode(config.apPin, INPUT_PULLUP);
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_GPIO_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -388,11 +388,13 @@ void AmsConfiguration::clearGpio(GpioConfig& config) {
config.vccOffset = 0;
config.vccMultiplier = 1000;
config.vccBootLimit = 0;
config.vccResistorGnd = 0;
config.vccResistorVcc = 0;
}
bool AmsConfiguration::getNtpConfig(NtpConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_NTP_START, config);
EEPROM.end();
return true;
@@ -419,7 +421,7 @@ bool AmsConfiguration::setNtpConfig(NtpConfig& config) {
} else {
ntpChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_NTP_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -444,7 +446,7 @@ void AmsConfiguration::clearNtp(NtpConfig& config) {
bool AmsConfiguration::getEntsoeConfig(EntsoeConfig& config) {
if(hasConfig()) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_ENTSOE_START, config);
EEPROM.end();
return true;
@@ -463,7 +465,7 @@ bool AmsConfiguration::setEntsoeConfig(EntsoeConfig& config) {
} else {
entsoeChanged = true;
}
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(CONFIG_ENTSOE_START, config);
bool ret = EEPROM.commit();
EEPROM.end();
@@ -486,7 +488,7 @@ void AmsConfiguration::ackEntsoeChange() {
}
void AmsConfiguration::clear() {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
MeterConfig meter;
clearMeter(meter);
EEPROM.put(CONFIG_METER_START, meter);
@@ -522,7 +524,7 @@ void AmsConfiguration::clear() {
bool AmsConfiguration::hasConfig() {
if(configVersion == 0) {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
configVersion = EEPROM.read(EEPROM_CONFIG_ADDRESS);
EEPROM.end();
}
@@ -553,6 +555,14 @@ bool AmsConfiguration::hasConfig() {
configVersion = 0;
return false;
}
case 88:
configVersion = -1; // Prevent loop
if(relocateConfig88()) {
configVersion = 89;
} else {
configVersion = 0;
return false;
}
case EEPROM_CHECK_SUM:
return true;
default:
@@ -567,7 +577,7 @@ int AmsConfiguration::getConfigVersion() {
}
void AmsConfiguration::loadTempSensors() {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
TempSensorConfig* tempSensors[32];
int address = EEPROM_TEMP_CONFIG_ADDRESS;
int c = 0;
@@ -606,7 +616,7 @@ void AmsConfiguration::saveTempSensors() {
bool AmsConfiguration::loadConfig83(int address) {
ConfigObject83 c;
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(address, c);
EntsoeConfig entsoe {"", "", "", 1000};
@@ -644,7 +654,9 @@ bool AmsConfiguration::loadConfig83(int address) {
c.vccPin,
c.vccOffset,
c.vccMultiplier,
c.vccBootLimit
c.vccBootLimit,
0,
0
};
EEPROM.put(CONFIG_GPIO_START, gpio);
@@ -715,7 +727,7 @@ bool AmsConfiguration::loadConfig83(int address) {
bool AmsConfiguration::relocateConfig86() {
MqttConfig86 mqtt86;
MqttConfig mqtt;
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_MQTT_START_86, mqtt86);
strcpy(mqtt.host, mqtt86.host);
mqtt.port = mqtt86.port;
@@ -736,7 +748,7 @@ bool AmsConfiguration::relocateConfig86() {
bool AmsConfiguration::relocateConfig87() {
MeterConfig87 meter87;
MeterConfig meter;
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_METER_START_87, meter87);
if(meter87.type < 5) {
meter.baud = 2400;
@@ -757,8 +769,38 @@ bool AmsConfiguration::relocateConfig87() {
return ret;
}
bool AmsConfiguration::relocateConfig88() {
GpioConfig88 gpio88;
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.get(CONFIG_GPIO_START_88, gpio88);
GpioConfig gpio {
gpio88.hanPin,
gpio88.apPin,
gpio88.ledPin,
gpio88.ledInverted,
gpio88.ledPinRed,
gpio88.ledPinGreen,
gpio88.ledPinBlue,
gpio88.ledRgbInverted,
gpio88.tempSensorPin,
gpio88.tempAnalogSensorPin,
gpio88.vccPin,
gpio88.vccOffset,
gpio88.vccMultiplier,
gpio88.vccBootLimit,
0,
0
};
EEPROM.put(CONFIG_GPIO_START, gpio);
EEPROM.put(EEPROM_CONFIG_ADDRESS, 89);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::save() {
EEPROM.begin(EEPROM_SIZE);
EEPROM.begin(SPI_FLASH_SEC_SIZE);
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM);
saveTempSensors();
bool success = EEPROM.commit();
@@ -938,9 +980,17 @@ void AmsConfiguration::print(Print* debugger)
debugger->printf("Temperature pin: %i\r\n", gpio.tempSensorPin);
debugger->printf("Temp analog pin: %i\r\n", gpio.tempAnalogSensorPin);
debugger->printf("Vcc pin: %i\r\n", gpio.vccPin);
if(gpio.vccMultiplier > 0) {
debugger->printf("Vcc multiplier: %f\r\n", gpio.vccMultiplier / 1000.0);
}
if(gpio.vccOffset > 0) {
debugger->printf("Vcc offset: %f\r\n", gpio.vccOffset / 100.0);
}
if(gpio.vccBootLimit > 0) {
debugger->printf("Vcc boot limit: %f\r\n", gpio.vccBootLimit / 10.0);
}
debugger->printf("GND resistor: %i\r\n", gpio.vccResistorGnd);
debugger->printf("Vcc resistor: %i\r\n", gpio.vccResistorVcc);
debugger->println("");
delay(10);
Serial.flush();

View File

@@ -3,17 +3,16 @@
#include <EEPROM.h>
#include "Arduino.h"
#define EEPROM_SIZE 1024 * 3
#define EEPROM_CHECK_SUM 88 // Used to check if config is stored. Change if structure changes
#define EEPROM_CHECK_SUM 89 // Used to check if config is stored. Change if structure changes
#define EEPROM_CONFIG_ADDRESS 0
#define EEPROM_TEMP_CONFIG_ADDRESS 2048
#define CONFIG_SYSTEM_START 8
#define CONFIG_WIFI_START 16
#define CONFIG_METER_START 224
#define CONFIG_GPIO_START 266
#define CONFIG_WEB_START 648
#define CONFIG_DEBUG_START 824
#define CONFIG_GPIO_START 832
#define CONFIG_DOMOTICZ_START 856
#define CONFIG_NTP_START 872
#define CONFIG_ENTSOE_START 944
@@ -21,6 +20,7 @@
#define CONFIG_MQTT_START_86 224
#define CONFIG_METER_START_87 784
#define CONFIG_GPIO_START_88 832
struct SystemConfig {
@@ -111,6 +111,25 @@ struct GpioConfig {
int16_t vccOffset;
uint16_t vccMultiplier;
uint8_t vccBootLimit;
uint16_t vccResistorGnd;
uint16_t vccResistorVcc;
}; // 20
struct GpioConfig88 {
uint8_t hanPin;
uint8_t apPin;
uint8_t ledPin;
bool ledInverted;
uint8_t ledPinRed;
uint8_t ledPinGreen;
uint8_t ledPinBlue;
bool ledRgbInverted;
uint8_t tempSensorPin;
uint8_t tempAnalogSensorPin;
uint8_t vccPin;
int16_t vccOffset;
uint16_t vccMultiplier;
uint8_t vccBootLimit;
}; // 16
struct DomoticzConfig {
@@ -295,6 +314,7 @@ private:
bool loadConfig83(int address);
bool relocateConfig86();
bool relocateConfig87();
bool relocateConfig88(); // dev 1.6
int readString(int pAddress, char* pString[]);
int readInt(int pAddress, int *pValue);

308
src/AmsDataStorage.cpp Normal file
View File

@@ -0,0 +1,308 @@
#include "AmsDataStorage.h"
#include <lwip/apps/sntp.h>
#include "EEPROM.h"
#include "LittleFS.h"
#include "AmsStorage.h"
AmsDataStorage::AmsDataStorage(RemoteDebug* debugger) {
day.version = 3;
month.version = 4;
this->debugger = debugger;
}
void AmsDataStorage::setTimezone(Timezone* tz) {
this->tz = tz;
}
bool AmsDataStorage::update(AmsData* data) {
if(data->getListType() != 3) return false;
time_t now = time(nullptr);
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Time is: %d\n", now);
}
if(now < EPOCH_2021_01_01) {
now = data->getMeterTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) New time is: %d\n", now);
}
}
if(now-day.lastMeterReadTime < 3590) {
if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) It is only %d seconds since last update, ignoring\n", (now-day.lastMeterReadTime));
}
return false;
}
tmElements_t tm;
breakTime(now, tm);
if(tm.Minute > 5) {
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Already %d minutes into the hour, ignoring\n", tm.Minute);
}
return false;
}
int16_t val = (day.activeImport == 0 ? 0 : ((data->getActiveImportCounter()*1000) - day.activeImport) - ((data->getActiveExportCounter()*1000) - day.activeExport)) / 10;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Usage for hour %d: %d", tm.Hour, val);
}
if(tm.Hour == 1) {
day.h00 = val;
} else if(tm.Hour == 2) {
day.h01 = val;
} else if(tm.Hour == 3) {
day.h02 = val;
} else if(tm.Hour == 4) {
day.h03 = val;
} else if(tm.Hour == 5) {
day.h04 = val;
} else if(tm.Hour == 6) {
day.h05 = val;
} else if(tm.Hour == 7) {
day.h06 = val;
} else if(tm.Hour == 8) {
day.h07 = val;
} else if(tm.Hour == 9) {
day.h08 = val;
} else if(tm.Hour == 10) {
day.h09 = val;
} else if(tm.Hour == 11) {
day.h10 = val;
} else if(tm.Hour == 12) {
day.h11 = val;
} else if(tm.Hour == 13) {
day.h12 = val;
} else if(tm.Hour == 14) {
day.h13 = val;
} else if(tm.Hour == 15) {
day.h14 = val;
} else if(tm.Hour == 16) {
day.h15 = val;
} else if(tm.Hour == 17) {
day.h16 = val;
} else if(tm.Hour == 18) {
day.h17 = val;
} else if(tm.Hour == 19) {
day.h18 = val;
} else if(tm.Hour == 20) {
day.h19 = val;
} else if(tm.Hour == 21) {
day.h20 = val;
} else if(tm.Hour == 22) {
day.h21 = val;
} else if(tm.Hour == 23) {
day.h22 = val;
} else if(tm.Hour == 0) {
day.h23 = val;
}
day.activeImport = data->getActiveImportCounter()*1000;
day.activeExport = data->getActiveExportCounter()*1000;
day.lastMeterReadTime = now;
// Update month plot
if(tz != NULL) {
time_t local = tz->toLocal(now);
breakTime(local, tm);
}
if(tm.Hour == 0) {
val = (month.activeImport == 0 ? 0 : ((data->getActiveImportCounter()*1000) - month.activeImport) - ((data->getActiveExportCounter()*1000) - month.activeExport)) / 10;
if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Usage for day %d: %d", tm.Day, val);
}
if(tm.Day == 1) {
time_t yesterday = now-3600;
breakTime(yesterday, tm);
if(tm.Day == 29) {
month.d28 = val;
} else if(tm.Day == 30) {
month.d29 = val;
} else if(tm.Day == 31) {
month.d30 = val;
}
} else if(tm.Day == 2) {
month.d01 = val;
} else if(tm.Day == 3) {
month.d02 = val;
} else if(tm.Day == 4) {
month.d03 = val;
} else if(tm.Day == 5) {
month.d04 = val;
} else if(tm.Day == 6) {
month.d05 = val;
} else if(tm.Day == 7) {
month.d06 = val;
} else if(tm.Day == 8) {
month.d07 = val;
} else if(tm.Day == 9) {
month.d08 = val;
} else if(tm.Day == 10) {
month.d09 = val;
} else if(tm.Day == 11) {
month.d10 = val;
} else if(tm.Day == 12) {
month.d11 = val;
} else if(tm.Day == 13) {
month.d12 = val;
} else if(tm.Day == 14) {
month.d13 = val;
} else if(tm.Day == 15) {
month.d14 = val;
} else if(tm.Day == 16) {
month.d15 = val;
} else if(tm.Day == 17) {
month.d16 = val;
} else if(tm.Day == 18) {
month.d17 = val;
} else if(tm.Day == 19) {
month.d18 = val;
} else if(tm.Day == 20) {
month.d19 = val;
} else if(tm.Day == 21) {
month.d20 = val;
} else if(tm.Day == 22) {
month.d21 = val;
} else if(tm.Day == 23) {
month.d22 = val;
} else if(tm.Day == 24) {
month.d23 = val;
} else if(tm.Day == 25) {
month.d24 = val;
} else if(tm.Day == 26) {
month.d25 = val;
} else if(tm.Day == 27) {
month.d26 = val;
} else if(tm.Day == 28) {
month.d27 = val;
} else if(tm.Day == 29) {
month.d28 = val;
} else if(tm.Day == 30) {
month.d29 = val;
} else if(tm.Day == 31) {
month.d30 = val;
}
month.activeImport = data->getActiveImportCounter()*1000;
month.activeExport = data->getActiveExportCounter()*1000;
}
return true;
}
DayDataPoints AmsDataStorage::getDayDataPoints() {
return day;
}
MonthDataPoints AmsDataStorage::getMonthDataPoints() {
return month;
}
bool AmsDataStorage::load(AmsData* meterState) {
if(!LittleFS.begin()) {
printE("Unable to load LittleFS");
return false;
}
bool ret = false;
if(LittleFS.exists(FILE_DAYPLOT)) {
File file = LittleFS.open(FILE_DAYPLOT, "r");
char buf[file.size()];
file.readBytes(buf, file.size());
DayDataPoints* day = (DayDataPoints*) buf;
file.close();
if(day->version == 3) {
memcpy(&this->day, day, sizeof(this->day));
ret = true;
} else {
ret = false;
}
}
if(LittleFS.exists(FILE_MONTHPLOT)) {
File file = LittleFS.open(FILE_MONTHPLOT, "r");
char buf[file.size()];
file.readBytes(buf, file.size());
MonthDataPoints* month = (MonthDataPoints*) buf;
file.close();
if(month->version == 3) { // dev-1.6
month->d25 = month->d26;
month->d26 = month->d27;
month->d27 = month->d28;
month->d28 = month->d29;
month->d29 = month->d30;
month->d30 = month->d31;
month->version = 4;
}
if(month->version == 4) {
memcpy(&this->month, month, sizeof(this->month));
ret = true;
} else {
ret = false;
}
}
LittleFS.end();
return ret;
}
bool AmsDataStorage::save() {
if(!LittleFS.begin()) {
printE("Unable to load LittleFS");
return false;
}
{
File file = LittleFS.open(FILE_DAYPLOT, "w");
char buf[sizeof(day)];
memcpy(buf, &day, sizeof(day));
for(int i = 0; i < sizeof(day); i++) {
file.write(buf[i]);
}
file.close();
}
{
File file = LittleFS.open(FILE_MONTHPLOT, "w");
char buf[sizeof(month)];
memcpy(buf, &month, sizeof(month));
for(int i = 0; i < sizeof(month); i++) {
file.write(buf[i]);
}
file.close();
}
LittleFS.end();
return true;
}
void AmsDataStorage::printD(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(String("(AmsDataStorage)" + fmt + "\n").c_str(), args);
va_end(args);
}
void AmsDataStorage::printI(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(String("(AmsDataStorage)" + fmt + "\n").c_str(), args);
va_end(args);
}
void AmsDataStorage::printW(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf(String("(AmsDataStorage)" + fmt + "\n").c_str(), args);
va_end(args);
}
void AmsDataStorage::printE(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(String("(AmsDataStorage)" + fmt + "\n").c_str(), args);
va_end(args);
}

101
src/AmsDataStorage.h Normal file
View File

@@ -0,0 +1,101 @@
#ifndef _AMSDATASTORAGE_H
#define _AMSDATASTORAGE_H
#include "Arduino.h"
#include "AmsData.h"
#include "RemoteDebug.h"
#include "Timezone.h"
#define EPOCH_2021_01_01 1609459200
struct DayDataPoints {
uint8_t version;
int16_t h00;
int16_t h01;
int16_t h02;
int16_t h03;
int16_t h04;
int16_t h05;
int16_t h06;
int16_t h07;
int16_t h08;
int16_t h09;
int16_t h10;
int16_t h11;
int16_t h12;
int16_t h13;
int16_t h14;
int16_t h15;
int16_t h16;
int16_t h17;
int16_t h18;
int16_t h19;
int16_t h20;
int16_t h21;
int16_t h22;
int16_t h23;
time_t lastMeterReadTime;
uint32_t activeImport;
uint32_t activeExport;
}; // 37 bytes
struct MonthDataPoints {
uint8_t version;
int16_t d01;
int16_t d02;
int16_t d03;
int16_t d04;
int16_t d05;
int16_t d06;
int16_t d07;
int16_t d08;
int16_t d09;
int16_t d10;
int16_t d11;
int16_t d12;
int16_t d13;
int16_t d14;
int16_t d15;
int16_t d16;
int16_t d17;
int16_t d18;
int16_t d19;
int16_t d20;
int16_t d21;
int16_t d22;
int16_t d23;
int16_t d24;
int16_t d25;
int16_t d26;
int16_t d27;
int16_t d28;
int16_t d29;
int16_t d30;
int16_t d31;
time_t lastMeterReadTime;
uint32_t activeImport;
uint32_t activeExport;
}; // 75 bytes
class AmsDataStorage {
public:
AmsDataStorage(RemoteDebug*);
void setTimezone(Timezone*);
bool update(AmsData*);
DayDataPoints getDayDataPoints();
MonthDataPoints getMonthDataPoints();
bool load(AmsData*);
bool save();
private:
Timezone* tz;
DayDataPoints day;
MonthDataPoints month;
RemoteDebug* debugger;
void printD(String fmt, ...);
void printI(String fmt, ...);
void printW(String fmt, ...);
void printE(String fmt, ...);
};
#endif

View File

@@ -7,4 +7,7 @@
#define FILE_MQTT_CERT "/mqtt-cert.pem"
#define FILE_MQTT_KEY "/mqtt-key.pem"
#define FILE_DAYPLOT "/dayplot.bin"
#define FILE_MONTHPLOT "/monthplot.bin"
#endif

View File

@@ -5,8 +5,6 @@
#define INVALID_BUTTON_PIN 0xFFFFFFFF
#define EPOCH_2021_01_01 1609459200
#define MAX_PEM_SIZE 4096
#include <SoftwareSerial.h>

View File

@@ -18,14 +18,11 @@
#include "AmsToMqttBridge.h"
#include "AmsStorage.h"
#include "AmsDataStorage.h"
#include <MQTT.h>
#include <DNSServer.h>
#include <lwip/apps/sntp.h>
#if defined(ESP8266)
ADC_MODE(ADC_VCC);
#endif
#include "HwTools.h"
#include "entsoe/EntsoeApi.h"
@@ -74,6 +71,8 @@ String topic = "ams";
AmsData meterState;
bool ntpEnabled = false;
AmsDataStorage ds(&Debug);
void setup() {
WiFiConfig wifi;
Serial.begin(115200);
@@ -143,7 +142,7 @@ void setup() {
SerialConfig serialConfig;
#elif defined(ESP32)
uint32_t serialConfig;
#endif;
#endif
switch(meterConfig.parity) {
case 2:
serialConfig = SERIAL_7N1;
@@ -186,10 +185,10 @@ void setup() {
}
float vccBootLimit = gpioConfig.vccBootLimit == 0 ? 0 : gpioConfig.vccBootLimit / 10.0;
if(vccBootLimit > 0 && (gpioConfig.apPin == 0xFF || digitalRead(gpioConfig.apPin) == HIGH)) { // Skip if user is holding AP button while booting (HIGH = button is released)
if(vccBootLimit > 2.5 && vccBootLimit < 3.3 && (gpioConfig.apPin == 0xFF || digitalRead(gpioConfig.apPin) == HIGH)) { // Skip if user is holding AP button while booting (HIGH = button is released)
if (vcc < vccBootLimit) {
if(Debug.isActive(RemoteDebug::INFO)) {
debugI("Voltage is too low, sleeping");
Debug.printf("(setup) Voltage is too low (%.2f < %.2f), sleeping\n", vcc, vccBootLimit);
Serial.flush();
}
ESP.deepSleep(10000000); //Deep sleep to allow output cap to charge up
@@ -214,7 +213,7 @@ void setup() {
if(hasFs) {
bool flashed = false;
if(LittleFS.exists(FILE_FIRMWARE)) {
if (digitalRead(gpioConfig.apPin) == HIGH) {
if (gpioConfig.apPin == 0xFF || digitalRead(gpioConfig.apPin) == HIGH) {
if(Debug.isActive(RemoteDebug::INFO)) debugI("Found firmware");
#if defined(ESP8266)
WiFi.setSleepMode(WIFI_LIGHT_SLEEP);
@@ -286,7 +285,10 @@ void setup() {
TimeChangeRule dst = {"DST", Last, Sun, Mar, 2, (ntp.offset + ntp.summerOffset) / 6};
tz = new Timezone(dst, std);
ws.setTimezone(tz);
ds.setTimezone(tz);
}
ds.load(&meterState);
} else {
if(Debug.isActive(RemoteDebug::INFO)) {
debugI("No configuration, booting AP");
@@ -294,7 +296,7 @@ void setup() {
swapWifiMode();
}
ws.setup(&config, &gpioConfig, &meterConfig, &meterState, &mqtt);
ws.setup(&config, &gpioConfig, &meterConfig, &meterState, &ds, &mqtt);
}
int buttonTimer = 0;
@@ -472,7 +474,7 @@ void loop() {
}
void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert) {
debugI("Setting up HAN on pin %d with baud %d and parity %d", pin, baud, parityOrdinal);
if(Debug.isActive(RemoteDebug::INFO)) Debug.printf("(setupHanPort) Setting up HAN on pin %d with baud %d and parity %d\n", pin, baud, parityOrdinal);
HardwareSerial *hwSerial = NULL;
if(pin == 3) {
@@ -630,6 +632,7 @@ void mqttMessageReceived(String &topic, String &payload)
// Ideas could be to query for values or to initiate OTA firmware update
}
int len = 0;
uint8_t buf[BUF_SIZE];
HDLCConfig* hc = NULL;
int currentMeterType = -1;
@@ -648,12 +651,22 @@ void readHanPort() {
hanSerial->readBytes(buf, BUF_SIZE);
return;
}
CosemDateTime timestamp;
CosemDateTime timestamp = {0};
AmsData data;
if(currentMeterType == 1) {
size_t len = hanSerial->readBytes(buf, BUF_SIZE); // TODO: read one byte at the time. This blocks up the GUI
while(hanSerial->available()) {
buf[len++] = hanSerial->read();
}
if(len > 0) {
int pos = HDLC_validate((uint8_t *) buf, len, hc, &timestamp);
if(pos == HDLC_FRAME_INCOMPLETE) {
if(len >= BUF_SIZE) {
hanSerial->readBytes(buf, BUF_SIZE);
len = 0;
debugI("Buffer overflow, resetting");
}
return;
}
if(pos == HDLC_ENCRYPTION_CONFIG_MISSING) {
hc = new HDLCConfig();
memcpy(hc->encryption_key, meterConfig.encryptionKey, 16);
@@ -673,6 +686,7 @@ void readHanPort() {
debugPrint(hc->authentication_tag, 0, 8);
}
}
len = 0;
if(pos >= 0) {
debugI("Valid HDLC, start at %d", pos);
data = IEC6205675(((char *) (buf)) + pos, meterState.getMeterType(), timestamp);
@@ -726,6 +740,14 @@ void readHanPort() {
mqtt.loop();
delay(10);
}
if(ds.update(&data)) {
debugI("Saving day plot");
ds.save();
} else if(data.getListType() == 3) {
debugE("Unable to update day plot");
}
meterState.apply(data);
}
}

View File

@@ -1,4 +1,7 @@
#include "HwTools.h"
#if defined(ESP8266)
ADC_MODE(ADC_VCC);
#endif
void HwTools::setup(GpioConfig* config, AmsConfiguration* amsConf) {
this->config = config;
@@ -14,18 +17,35 @@ void HwTools::setup(GpioConfig* config, AmsConfiguration* amsConf) {
config->tempSensorPin = 0xFF;
}
if(config->vccPin > 0 && config->vccPin < 40) {
getAdcChannel(config->vccPin, voltAdc);
if(voltAdc.unit != 0xFF) {
#if defined(ESP32)
if(voltAdc.unit == ADC_UNIT_1) {
voltAdcChar = (esp_adc_cal_characteristics_t*) calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_value_t adcVal = esp_adc_cal_characterize((adc_unit_t) voltAdc.unit, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_12, 1100, voltAdcChar);
adc1_config_channel_atten((adc1_channel_t) voltAdc.channel, ADC_ATTEN_DB_6);
} else if(voltAdc.unit == ADC_UNIT_2) {
voltAdcChar = (esp_adc_cal_characteristics_t*) calloc(1, sizeof(esp_adc_cal_characteristics_t));
esp_adc_cal_value_t adcVal = esp_adc_cal_characterize((adc_unit_t) voltAdc.unit, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_12, 1100, voltAdcChar);
adc2_config_channel_atten((adc2_channel_t) voltAdc.channel, ADC_ATTEN_DB_6);
}
#endif
} else {
pinMode(config->vccPin, INPUT);
}
} else {
voltAdc.unit = 0xFF;
voltAdc.channel = 0xFF;
config->vccPin = 0xFF;
}
if(config->tempAnalogSensorPin > 0 && config->tempAnalogSensorPin < 40) {
pinMode(config->tempAnalogSensorPin, INPUT);
} else {
config->tempAnalogSensorPin = 0xFF;
}
if(config->vccPin > 0 && config->vccPin < 40) {
pinMode(config->vccPin, INPUT);
} else {
config->vccPin = 0xFF;
}
if(config->ledPin > 0 && config->ledPin < 40) {
pinMode(config->ledPin, OUTPUT);
ledOff(LED_INTERNAL);
@@ -55,19 +75,128 @@ void HwTools::setup(GpioConfig* config, AmsConfiguration* amsConf) {
}
}
void HwTools::getAdcChannel(uint8_t pin, AdcConfig& config) {
config.unit = 0xFF;
config.channel = 0xFF;
#if defined(ESP32)
switch(pin) {
case ADC1_CHANNEL_0_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_0;
break;
case ADC1_CHANNEL_1_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_1;
break;
case ADC1_CHANNEL_2_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_2;
break;
case ADC1_CHANNEL_3_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_3;
break;
case ADC1_CHANNEL_4_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_4;
break;
case ADC1_CHANNEL_5_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_5;
break;
case ADC1_CHANNEL_6_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_6;
break;
case ADC1_CHANNEL_7_GPIO_NUM:
config.unit = ADC_UNIT_1;
config.channel = ADC1_CHANNEL_7;
break;
case ADC2_CHANNEL_0_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_0;
break;
case ADC2_CHANNEL_1_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_1;
break;
case ADC2_CHANNEL_2_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_2;
break;
case ADC2_CHANNEL_3_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_3;
break;
case ADC2_CHANNEL_4_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_4;
break;
case ADC2_CHANNEL_5_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_5;
break;
case ADC2_CHANNEL_6_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_6;
break;
case ADC2_CHANNEL_7_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_7;
break;
case ADC2_CHANNEL_8_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_8;
break;
case ADC2_CHANNEL_9_GPIO_NUM:
config.unit = ADC_UNIT_2;
config.channel = ADC2_CHANNEL_9;
break;
}
#endif
}
double HwTools::getVcc() {
double volts = 0.0;
if(config->vccPin != 0xFF) {
#if defined(ESP8266)
volts = (analogRead(config->vccPin) / 1024.0) * 3.3;
uint32_t x = 0;
for (int i = 0; i < 10; i++) {
x += analogRead(config->vccPin);
}
volts = x / 10240;
#elif defined(ESP32)
volts = (analogRead(config->vccPin) / 4095.0) * 3.3;
if(voltAdc.unit != 0xFF) {
uint32_t x = 0;
for (int i = 0; i < 10; i++) {
if(voltAdc.unit == ADC_UNIT_1) {
x += adc1_get_raw((adc1_channel_t) voltAdc.channel);
} else if(voltAdc.unit == ADC_UNIT_2) {
int v = 0;
adc2_get_raw((adc2_channel_t) voltAdc.channel, ADC_WIDTH_BIT_12, &v);
x += v;
}
}
x = x / 10;
uint32_t voltage = esp_adc_cal_raw_to_voltage(x, voltAdcChar);
volts = voltage / 1000.0;
} else {
uint32_t x = 0;
for (int i = 0; i < 10; i++) {
x += analogRead(config->vccPin);
}
volts = x / 40950;
}
#endif
} else {
#if defined(ESP8266)
volts = ((double) ESP.getVcc()) / 1024.0;
volts = ESP.getVcc() / 1024.0;
#endif
}
if(config->vccResistorGnd > 0 && config->vccResistorVcc > 0) {
volts *= ((double) (config->vccResistorGnd + config->vccResistorVcc) / config->vccResistorGnd);
}
float vccOffset = config->vccOffset / 100.0;
float vccMultiplier = config->vccMultiplier / 1000.0;

View File

@@ -7,6 +7,9 @@
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#include <driver/adc.h>
#include <esp_adc_cal.h>
#include <soc/adc_channel.h>
#endif
#include <DallasTemperature.h>
@@ -26,6 +29,11 @@ struct TempSensorData {
bool changed;
};
struct AdcConfig {
uint8_t unit;
uint8_t channel;
};
class HwTools {
public:
void setup(GpioConfig*, AmsConfiguration*);
@@ -43,6 +51,10 @@ public:
HwTools() {};
private:
AdcConfig voltAdc, tempAdc;
#if defined(ESP32)
esp_adc_cal_characteristics_t* voltAdcChar, tempAdcChar;
#endif
GpioConfig* config;
AmsConfiguration* amsConf;
bool tempSensorInit;
@@ -53,6 +65,7 @@ private:
bool writeLedPin(uint8_t color, uint8_t state);
bool isSensorAddressEqual(uint8_t a[8], uint8_t b[8]);
void getAdcChannel(uint8_t pin, AdcConfig&);
};
#endif

View File

@@ -1,5 +1,6 @@
#include "IEC6205675.h"
#include "lwip/def.h"
#include "Timezone.h"
IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, CosemDateTime packageTimestamp) {
uint32_t u32;
@@ -261,9 +262,21 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, CosemDateTime packag
}
}
time_t ts = getTimestamp(AMS_OBIS_METER_TIMESTAMP, sizeof(AMS_OBIS_METER_TIMESTAMP), ((char *) (d)));
if(ts > 0) {
meterTimestamp = ts;
CosemData* meterTs = findObis(AMS_OBIS_METER_TIMESTAMP, sizeof(AMS_OBIS_METER_TIMESTAMP), ((char *) (d)));
if(meterTs != NULL) {
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120};
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60};
Timezone tz(CEST, CET);
AmsOctetTimestamp* amst = (AmsOctetTimestamp*) meterTs;
time_t ts = getTimestamp(amst->dt);
if(meterType == AmsTypeKamstrup || meterType == AmsTypeAidon) {
this->packageTimestamp = tz.toUTC(ts);
this->meterTimestamp = tz.toUTC(ts);
Serial.printf("\nKamstrup/Aidon time: %d\n", meterTimestamp);
} else {
meterTimestamp = ts;
}
}
u32 = getUnsignedNumber(AMS_OBIS_POWER_FACTOR, sizeof(AMS_OBIS_POWER_FACTOR), ((char *) (d)));
@@ -425,7 +438,6 @@ time_t IEC6205675::getTimestamp(uint8_t* obis, int matchlength, const char* ptr)
case CosemTypeOctetString: {
if(item->oct.length == 0x0C) {
AmsOctetTimestamp* ts = (AmsOctetTimestamp*) item;
//Serial.printf("\nYear: %d, Month: %d, Day: %d, Hour: %d, Minutes %d, Second: %d, Deviation: %d\n", ntohs(ts->dt.year), ts->dt.month, ts->dt.dayOfMonth, ts->dt.hour, ts->dt.minute, ts->dt.second, ntohs(ts->dt.deviation));
return getTimestamp(ts->dt);
}
}
@@ -436,13 +448,17 @@ time_t IEC6205675::getTimestamp(uint8_t* obis, int matchlength, const char* ptr)
time_t IEC6205675::getTimestamp(CosemDateTime timestamp) {
tmElements_t tm;
tm.Year = ntohs(timestamp.year) - 1970;
uint16_t year = ntohs(timestamp.year);
if(year < 1970) return 0;
tm.Year = year - 1970;
tm.Month = timestamp.month;
tm.Day = timestamp.dayOfMonth;
tm.Hour = timestamp.hour;
tm.Minute = timestamp.minute;
tm.Second = timestamp.second;
Serial.printf("\nY: %d, M: %d, D: %d, h: %d, m: %d, s: %d, deviation: 0x%2X, status: 0x%1X\n", tm.Year, tm.Month, tm.Day, tm.Hour, tm.Minute, tm.Second, timestamp.deviation, timestamp.status);
time_t time = makeTime(tm);
int16_t deviation = ntohs(timestamp.deviation);
if(deviation >= -720 && deviation <= 720) {

View File

@@ -15,13 +15,15 @@ void mbus_hexdump(const uint8_t* buf, int len) {
printf("]\n");
}
int HDLC_validate(const uint8_t* d, int len, HDLCConfig* config, CosemDateTime* timestamp) {
int HDLC_validate(const uint8_t* d, int length, HDLCConfig* config, CosemDateTime* timestamp) {
//mbus_hexdump(d, len);
HDLCHeader* h = (HDLCHeader*) d;
// Length field (11 lsb of format)
len = (ntohs(h->format) & 0x7FF) + 2;
int len = (ntohs(h->format) & 0x7FF) + 2;
if(len > length)
return -4;
HDLCFooter* f = (HDLCFooter*) (d + len - sizeof *f);
@@ -81,11 +83,12 @@ int HDLC_validate(const uint8_t* d, int len, HDLCConfig* config, CosemDateTime*
}
ptr += 2 + dateTime->base.length;
} else if(dateTime->base.type == CosemTypeNull) {
timestamp = 0;
ptr++;
} else if(dateTime->base.type == CosemTypeDateTime) {
memcpy(timestamp, ptr, dateTime->base.length);
} else if(dateTime->base.type == 0x0C) { // Kamstrup bug...
memcpy(timestamp, ptr, dateTime->base.length);
memcpy(timestamp, ptr, 0x0C);
ptr += 13;
} else {
return -99;

View File

@@ -5,6 +5,8 @@
#include <stdint.h>
#define HDLC_FLAG 0x7E
#define HDLC_BOUNDRY_FLAG_MISSING -1
#define HDLC_FRAME_INCOMPLETE -4
#define HDLC_ENCRYPTION_CONFIG_MISSING -90
struct HDLCConfig {

View File

@@ -179,9 +179,9 @@ bool EntsoeApi::retrieve(const char* url, Stream* doc) {
client.setBufferSizes(bufSize, bufSize);
}
*/
client.setInsecure();
#endif
client.setInsecure();
HTTPClient https;
https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);

View File

@@ -30,9 +30,9 @@
#include "root/restart_html.h"
#include "root/restartwait_html.h"
#include "root/boot_css.h"
#include "root/gaugemeter_js.h"
#include "root/github_svg.h"
#include "root/upload_html.h"
#include "root/firmware_html.h"
#include "root/delete_html.h"
#include "root/reset_html.h"
#include "root/temperature_html.h"
@@ -41,6 +41,8 @@
#include "root/data_json.h"
#include "root/tempsensor_json.h"
#include "root/lowmem_html.h"
#include "root/dayplot_json.h"
#include "root/monthplot_json.h"
#include "base64.h"
@@ -49,11 +51,12 @@ AmsWebServer::AmsWebServer(RemoteDebug* Debug, HwTools* hw) {
this->hw = hw;
}
void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, MeterConfig* meterConfig, AmsData* meterState, MQTTClient* mqtt) {
void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, MeterConfig* meterConfig, AmsData* meterState, AmsDataStorage* ds, MQTTClient* mqtt) {
this->config = config;
this->gpioConfig = gpioConfig;
this->meterConfig = meterConfig;
this->meterState = meterState;
this->ds = ds;
this->mqtt = mqtt;
char jsuri[32];
@@ -73,9 +76,10 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter
server.on("/domoticz",HTTP_GET, std::bind(&AmsWebServer::configDomoticzHtml, this));
server.on("/entsoe",HTTP_GET, std::bind(&AmsWebServer::configEntsoeHtml, this));
server.on("/boot.css", HTTP_GET, std::bind(&AmsWebServer::bootCss, this));
server.on("/gaugemeter.js", HTTP_GET, std::bind(&AmsWebServer::gaugemeterJs, this));
server.on("/github.svg", HTTP_GET, std::bind(&AmsWebServer::githubSvg, this));
server.on("/data.json", HTTP_GET, std::bind(&AmsWebServer::dataJson, this));
server.on("/dayplot.json", HTTP_GET, std::bind(&AmsWebServer::dayplotJson, this));
server.on("/monthplot.json", HTTP_GET, std::bind(&AmsWebServer::monthplotJson, this));
server.on("/save", HTTP_POST, std::bind(&AmsWebServer::handleSave, this));
@@ -660,13 +664,6 @@ void AmsWebServer::bootCss() {
server.send_P(200, "text/css", BOOT_CSS);
}
void AmsWebServer::gaugemeterJs() {
printD("Serving /gaugemeter.js over http...");
server.sendHeader("Cache-Control", "public, max-age=3600");
server.send_P(200, "application/javascript", GAUGEMETER_JS);
}
void AmsWebServer::githubSvg() {
printD("Serving /github.svg over http...");
@@ -782,6 +779,95 @@ void AmsWebServer::dataJson() {
server.send(200, "application/json", json);
}
void AmsWebServer::dayplotJson() {
printD("Serving /dayplot.json over http...");
DayDataPoints d = ds->getDayDataPoints();
char json[384];
snprintf_P(json, sizeof(json), DAYPLOT_JSON,
d.h00 / 100.0,
d.h01 / 100.0,
d.h02 / 100.0,
d.h03 / 100.0,
d.h04 / 100.0,
d.h05 / 100.0,
d.h06 / 100.0,
d.h07 / 100.0,
d.h08 / 100.0,
d.h09 / 100.0,
d.h10 / 100.0,
d.h11 / 100.0,
d.h12 / 100.0,
d.h13 / 100.0,
d.h14 / 100.0,
d.h15 / 100.0,
d.h16 / 100.0,
d.h17 / 100.0,
d.h18 / 100.0,
d.h19 / 100.0,
d.h20 / 100.0,
d.h21 / 100.0,
d.h22 / 100.0,
d.h23 / 100.0
);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.setContentLength(strlen(json));
server.send(200, "application/json", json);
}
void AmsWebServer::monthplotJson() {
printD("Serving /monthplot.json over http...");
MonthDataPoints m = ds->getMonthDataPoints();
char json[512];
snprintf_P(json, sizeof(json), MONTHPLOT_JSON,
m.d01 / 100.0,
m.d02 / 100.0,
m.d03 / 100.0,
m.d04 / 100.0,
m.d05 / 100.0,
m.d06 / 100.0,
m.d07 / 100.0,
m.d08 / 100.0,
m.d09 / 100.0,
m.d10 / 100.0,
m.d11 / 100.0,
m.d12 / 100.0,
m.d13 / 100.0,
m.d14 / 100.0,
m.d15 / 100.0,
m.d16 / 100.0,
m.d17 / 100.0,
m.d18 / 100.0,
m.d19 / 100.0,
m.d20 / 100.0,
m.d21 / 100.0,
m.d22 / 100.0,
m.d23 / 100.0,
m.d24 / 100.0,
m.d25 / 100.0,
m.d26 / 100.0,
m.d27 / 100.0,
m.d28 / 100.0,
m.d29 / 100.0,
m.d30 / 100.0,
m.d31 / 100.0
);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.setContentLength(strlen(json));
server.send(200, "application/json", json);
}
void AmsWebServer::handleSetup() {
printD("Handling setup method from http");
@@ -796,9 +882,6 @@ void AmsWebServer::handleSetup() {
config->clear();
config->clearGpio(*gpioConfig);
config->clearMeter(*meterConfig);
switch(sys.boardType) {
case 0: // roarfred
gpioConfig->hanPin = 3;
@@ -922,10 +1005,6 @@ void AmsWebServer::handleSetup() {
printD("Unable to set web config");
success = false;
}
if(!config->setMeterConfig(*meterConfig)) {
printD("Unable to set meter config");
success = false;
}
if(!config->setGpioConfig(*gpioConfig)) {
printD("Unable to set GPIO config");
success = false;
@@ -1066,6 +1145,8 @@ void AmsWebServer::handleSave() {
gpioConfig->vccOffset = server.hasArg("vccOffset") && !server.arg("vccOffset").isEmpty() ? server.arg("vccOffset").toFloat() * 100 : 0;
gpioConfig->vccMultiplier = server.hasArg("vccMultiplier") && !server.arg("vccMultiplier").isEmpty() ? server.arg("vccMultiplier").toFloat() * 1000 : 1000;
gpioConfig->vccBootLimit = server.hasArg("vccBootLimit") && !server.arg("vccBootLimit").isEmpty() ? server.arg("vccBootLimit").toFloat() * 10 : 0;
gpioConfig->vccResistorGnd = server.hasArg("vccResistorGnd") && !server.arg("vccResistorGnd").isEmpty() ? server.arg("vccResistorGnd").toInt() : 0;
gpioConfig->vccResistorVcc = server.hasArg("vccResistorVcc") && !server.arg("vccResistorVcc").isEmpty() ? server.arg("vccResistorVcc").toInt() : 0;
config->setGpioConfig(*gpioConfig);
}
@@ -1201,6 +1282,9 @@ void AmsWebServer::configGpioHtml() {
html.replace("${config.vccMultiplier}", gpioConfig->vccMultiplier > 0 ? String(gpioConfig->vccMultiplier / 1000.0, 2) : "");
html.replace("${config.vccBootLimit}", gpioConfig->vccBootLimit > 0 ? String(gpioConfig->vccBootLimit / 10.0, 1) : "");
html.replace("${config.vccResistorGnd}", gpioConfig->vccResistorGnd > 0 ? String(gpioConfig->vccResistorGnd) : "");
html.replace("${config.vccResistorVcc}", gpioConfig->vccResistorVcc > 0 ? String(gpioConfig->vccResistorVcc) : "");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
@@ -1372,7 +1456,21 @@ void AmsWebServer::firmwareHtml() {
if(!checkSecurity(1))
return;
uploadHtml("Firmware", "/firmware", "system");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
String html = String((const __FlashStringHelper*) FIRMWARE_HTML);
#if defined(ESP8266)
html.replace("{chipset}", "ESP8266");
#elif defined(ESP32)
html.replace("{chipset}", "ESP32");
#endif
server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN);
server.send_P(200, "text/html", HEAD_HTML);
server.sendContent(html);
server.sendContent_P(FOOT_HTML);
}
void AmsWebServer::firmwareUpload() {
@@ -1519,7 +1617,7 @@ void AmsWebServer::restartWaitHtml() {
yield();
if(performRestart) {
LittleFS.end();
ds->save();
printI("Rebooting");
delay(1000);
#if defined(ESP8266)

View File

@@ -8,6 +8,7 @@
#include "AmsConfiguration.h"
#include "HwTools.h"
#include "AmsData.h"
#include "AmsDataStorage.h"
#include "Uptime.h"
#include "RemoteDebug.h"
#include "entsoe/EntsoeApi.h"
@@ -29,7 +30,7 @@
class AmsWebServer {
public:
AmsWebServer(RemoteDebug* Debug, HwTools* hw);
void setup(AmsConfiguration*, GpioConfig*, MeterConfig*, AmsData*, MQTTClient*);
void setup(AmsConfiguration*, GpioConfig*, MeterConfig*, AmsData*, AmsDataStorage* ds, MQTTClient*);
void loop();
void setTimezone(Timezone* tz);
void setMqttEnabled(bool);
@@ -47,6 +48,7 @@ private:
MeterConfig* meterConfig;
WebConfig webConfig;
AmsData* meterState;
AmsDataStorage* ds;
MQTTClient* mqtt;
bool uploading = false;
File file;
@@ -76,9 +78,10 @@ private:
void configGpioHtml();
void configDebugHtml();
void bootCss();
void gaugemeterJs();
void githubSvg();
void dataJson();
void dayplotJson();
void monthplotJson();
void handleSetup();
void handleSave();