Compare commits

..

45 Commits

Author SHA1 Message Date
Gunnar Skjold
8d448533c7 Fixed config export for esp32 2022-12-19 18:36:04 +01:00
Gunnar Skjold
43cb9a0000 Fixed DSMR CRC check 2022-12-19 18:14:12 +01:00
Gunnar Skjold
8f057e687c Merge pull request #387 from lassebm/lg-e360-fixes
Landis+Gyr E360 fixes
2022-12-19 16:45:52 +01:00
Lasse Bang Mikkelsen
5b4f680114 Align Landis+Gyr naming 2022-12-17 12:38:18 +01:00
Lasse Bang Mikkelsen
fabdfbadf4 Add support for Landis+Gyr meters using "LGF" manufacturer ID 2022-12-16 20:20:40 +01:00
Lasse Bang Mikkelsen
afa47ea633 Use list type 4 when phase active import/export power is available 2022-12-16 20:19:15 +01:00
Gunnar Skjold
c40e20c8e9 Extract active import/export per phase from DSMR 2022-12-15 18:25:13 +01:00
Gunnar Skjold
6b0d540f39 Added 300 baud 2022-12-07 11:51:45 +01:00
Gunnar Skjold
33bd3da310 Various fix for realtime values 2022-12-06 15:45:43 +01:00
Gunnar Skjold
a239e1a63d Fix on special case for Kaifa MA304T3 2022-12-06 08:28:30 +01:00
Gunnar Skjold
1ef5703971 Fixed changing mdns settings 2022-12-01 18:06:19 +01:00
Gunnar Skjold
538de5ea99 Increased range of multipliers 2022-11-08 18:41:31 +01:00
Gunnar Skjold
042e2bcc85 Fixed power saving 2022-11-07 14:57:53 +01:00
Gunnar Skjold
775e5a0881 Support different size numbers in L&G payload 2022-11-01 18:41:43 +01:00
Gunnar Skjold
6c0d5fcc09 Some adjustments to wifi power saving option 2022-10-31 10:09:07 +01:00
Gunnar Skjold
69d8fa9254 Made wifi sleep mode configurable 2022-10-27 19:17:50 +02:00
Gunnar Skjold
c38c305bab Fixed threshold update via config file 2022-10-21 19:21:55 +02:00
Gunnar Skjold
bfee2a1d64 Fixed month average calculation 2022-10-21 18:43:30 +02:00
Gunnar Skjold
ec7edae9a1 Updated issue templates 2022-10-21 17:43:33 +02:00
Gunnar Skjold
d8e265b7ac Fixed JSON errors 2022-10-20 20:12:33 +02:00
Gunnar Skjold
1987cddab7 Some more cleanup 2022-10-20 20:11:13 +02:00
Gunnar Skjold
e18be5f97c Better memory optimization 2022-10-19 20:58:57 +02:00
Gunnar Skjold
537597d6d1 Expanding size of daily and monthly data points 2022-10-19 20:30:29 +02:00
Gunnar Skjold
a89013cec3 Fixed config file upload 2022-10-19 20:24:21 +02:00
Gunnar Skjold
9c7a0cb7ff Fixed graph error 2022-10-19 19:59:41 +02:00
Gunnar Skjold
ade12199b9 Reclaiming some heap space 2022-10-19 19:54:26 +02:00
Gunnar Skjold
39b68aca51 Removed invalid code 2022-10-18 18:30:26 +02:00
Gunnar Skjold
8ac1e034b1 Reverted WiFi_connect method 2022-10-17 13:09:28 +02:00
Gunnar Skjold
f446dff865 Fixed LED blink delay 2022-10-17 13:09:04 +02:00
Gunnar Skjold
34ebe9601a Changed blink behaviour 2022-10-16 18:31:11 +02:00
Gunnar Skjold
fa299198fc Fixed all build warnings 2022-10-13 20:33:59 +02:00
Gunnar Skjold
6d81b0a856 Fixed all build warnings 2022-10-13 20:24:56 +02:00
Gunnar Skjold
dd095da97b Reduced footprint of GPIO config page 2022-10-13 19:27:25 +02:00
Gunnar Skjold
1b6ce203b7 Better debug output whem receiving unknown data 2022-10-13 17:40:44 +02:00
Gunnar Skjold
2850be4e48 Merge pull request #322 from tbarnekov/extend_power_info
Populate Active Import and Export per phase and export to HA
2022-10-12 20:34:09 +02:00
Gunnar Skjold
7eca31de84 Fixed build errors 2022-10-12 20:01:55 +02:00
Gunnar Skjold
a64f960cc7 Added Pow-U+ profile 2022-10-12 19:30:00 +02:00
Gunnar Skjold
ce3a47a7e6 Fixed error blinks and improved WiFi reconnect 2022-10-12 19:25:48 +02:00
Thomas Barnekov
8395e1dc77 Merge remote-tracking branch 'origin/master' into extend_power_info 2022-10-10 17:06:09 +02:00
Thomas Barnekov
39a4761415 Add DOCTYPE to html to fix quirks mode 2022-10-10 16:59:43 +02:00
Gunnar Skjold
867ab9d6c2 Build variable for upgrade URL 2022-10-07 19:35:30 +02:00
Thomas Barnekov
5e03e3d3c2 Move per-phase power consumption and power factor values to new ha4.json 2022-10-07 13:40:29 +02:00
Thomas Barnekov
adb5050621 Merge branch 'gskjold:master' into extend_power_info 2022-10-06 17:31:43 +02:00
Thomas Barnekov
7cd52d5689 Add individual power reading for all phases (include in HA) 2022-10-06 00:35:30 +02:00
Thomas Barnekov
ad78ff3082 Fix implicit cast and non-const char pointer warnings 2022-10-05 22:42:43 +02:00
51 changed files with 1339 additions and 876 deletions

View File

@@ -33,6 +33,7 @@ If applicable, add screenshots to help explain your problem.
**Relevant firmware information:** **Relevant firmware information:**
- Version: [e.g. 2.1.0] - Version: [e.g. 2.1.0]
- MQTT: [yes/no] - MQTT: [yes/no]
- MQTT payload type: [e.g. JSON]
- HAN GPIO: [e.g. GPIO5] - HAN GPIO: [e.g. GPIO5]
- HAN baud and parity: [e.g. 2400 8E1] - HAN baud and parity: [e.g. 2400 8E1]
- Temperature sensors [e.g. 3xDS18B20] - Temperature sensors [e.g. 3xDS18B20]

View File

@@ -20,6 +20,7 @@ A clear and concise description of what the problem is.
**Relevant firmware information:** **Relevant firmware information:**
- Version: [e.g. 2.1.0] - Version: [e.g. 2.1.0]
- MQTT: [yes/no] - MQTT: [yes/no]
- MQTT payload type: [e.g. JSON]
- HAN GPIO: [e.g. GPIO5] - HAN GPIO: [e.g. GPIO5]
- HAN baud and parity: [e.g. 2400 8E1] - HAN baud and parity: [e.g. 2400 8E1]
- Temperature sensors [e.g. 3xDS18B20] - Temperature sensors [e.g. 3xDS18B20]

View File

@@ -24,6 +24,7 @@ bool AmsConfiguration::getWiFiConfig(WiFiConfig& config) {
EEPROM.begin(EEPROM_SIZE); EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_WIFI_START, config); EEPROM.get(CONFIG_WIFI_START, config);
EEPROM.end(); EEPROM.end();
if(config.sleep > 2) config.sleep = 1;
return true; return true;
} else { } else {
clearWifi(config); clearWifi(config);
@@ -33,6 +34,7 @@ bool AmsConfiguration::getWiFiConfig(WiFiConfig& config) {
bool AmsConfiguration::setWiFiConfig(WiFiConfig& config) { bool AmsConfiguration::setWiFiConfig(WiFiConfig& config) {
WiFiConfig existing; WiFiConfig existing;
if(config.sleep > 2) config.sleep = 1;
if(getWiFiConfig(existing)) { if(getWiFiConfig(existing)) {
wifiChanged |= strcmp(config.ssid, existing.ssid) != 0; wifiChanged |= strcmp(config.ssid, existing.ssid) != 0;
wifiChanged |= strcmp(config.psk, existing.psk) != 0; wifiChanged |= strcmp(config.psk, existing.psk) != 0;
@@ -45,6 +47,7 @@ bool AmsConfiguration::setWiFiConfig(WiFiConfig& config) {
} }
wifiChanged |= strcmp(config.hostname, existing.hostname) != 0; wifiChanged |= strcmp(config.hostname, existing.hostname) != 0;
wifiChanged |= config.power != existing.power; wifiChanged |= config.power != existing.power;
wifiChanged |= config.sleep != existing.sleep;
} else { } else {
wifiChanged = true; wifiChanged = true;
} }
@@ -70,6 +73,7 @@ void AmsConfiguration::clearWifi(WiFiConfig& config) {
#endif #endif
strcpy(config.hostname, (String("ams-") + String(chipId, HEX)).c_str()); strcpy(config.hostname, (String("ams-") + String(chipId, HEX)).c_str());
config.mdns = true; config.mdns = true;
config.sleep = 0xFF;
} }
void AmsConfiguration::clearWifiIp(WiFiConfig& config) { void AmsConfiguration::clearWifiIp(WiFiConfig& config) {
@@ -510,6 +514,7 @@ bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config)
if(config.thresholds[9] != 255) { if(config.thresholds[9] != 255) {
clearEnergyAccountingConfig(config); clearEnergyAccountingConfig(config);
} }
if(config.hours > 5) config.hours = 5;
return true; return true;
} else { } else {
return false; return false;
@@ -549,6 +554,7 @@ void AmsConfiguration::clearEnergyAccountingConfig(EnergyAccountingConfig& confi
config.thresholds[7] = 100; config.thresholds[7] = 100;
config.thresholds[8] = 150; config.thresholds[8] = 150;
config.thresholds[9] = 255; config.thresholds[9] = 255;
config.hours = 3;
} }
bool AmsConfiguration::isEnergyAccountingChanged() { bool AmsConfiguration::isEnergyAccountingChanged() {
@@ -669,6 +675,14 @@ bool AmsConfiguration::hasConfig() {
configVersion = 0; configVersion = 0;
return false; return false;
} }
case 95:
configVersion = -1; // Prevent loop
if(relocateConfig95()) {
configVersion = 96;
} else {
configVersion = 0;
return false;
}
case EEPROM_CHECK_SUM: case EEPROM_CHECK_SUM:
return true; return true;
default: default:
@@ -743,7 +757,7 @@ bool AmsConfiguration::relocateConfig86() {
} }
bool AmsConfiguration::relocateConfig87() { bool AmsConfiguration::relocateConfig87() {
MeterConfig87 meter87; MeterConfig87 meter87 = {0,0,0,0,0,0,0};
MeterConfig meter; MeterConfig meter;
EEPROM.begin(EEPROM_SIZE); EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_METER_START_87, meter87); EEPROM.get(CONFIG_METER_START_87, meter87);
@@ -846,6 +860,23 @@ bool AmsConfiguration::relocateConfig94() {
return ret; return ret;
} }
bool AmsConfiguration::relocateConfig95() {
MeterConfig meter;
MeterConfig95 meter95;
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(CONFIG_METER_START, meter);
EEPROM.get(CONFIG_METER_START, meter95);
meter.wattageMultiplier = meter95.wattageMultiplier;
meter.voltageMultiplier = meter95.voltageMultiplier;
meter.amperageMultiplier = meter95.amperageMultiplier;
meter.accumulatedMultiplier = meter95.accumulatedMultiplier;
EEPROM.put(CONFIG_METER_START, meter);
EEPROM.put(EEPROM_CONFIG_ADDRESS, 96);
bool ret = EEPROM.commit();
EEPROM.end();
return ret;
}
bool AmsConfiguration::save() { bool AmsConfiguration::save() {
EEPROM.begin(EEPROM_SIZE); EEPROM.begin(EEPROM_SIZE);
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM); EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM);

View File

@@ -4,7 +4,7 @@
#include "Arduino.h" #include "Arduino.h"
#define EEPROM_SIZE 1024*3 #define EEPROM_SIZE 1024*3
#define EEPROM_CHECK_SUM 95 // Used to check if config is stored. Change if structure changes #define EEPROM_CHECK_SUM 96 // Used to check if config is stored. Change if structure changes
#define EEPROM_CONFIG_ADDRESS 0 #define EEPROM_CONFIG_ADDRESS 0
#define EEPROM_TEMP_CONFIG_ADDRESS 2048 #define EEPROM_TEMP_CONFIG_ADDRESS 2048
@@ -54,7 +54,8 @@ struct WiFiConfig {
char hostname[32]; char hostname[32];
bool mdns; bool mdns;
uint8_t power; uint8_t power;
}; // 210 uint8_t sleep;
}; // 211
struct MqttConfig86 { struct MqttConfig86 {
char host[128]; char host[128];
@@ -87,6 +88,23 @@ struct WebConfig {
}; // 129 }; // 129
struct MeterConfig { struct MeterConfig {
uint32_t baud;
uint8_t parity;
bool invert;
uint8_t distributionSystem;
uint8_t mainFuse;
uint8_t productionCapacity;
uint8_t encryptionKey[16];
uint8_t authenticationKey[16];
uint32_t wattageMultiplier;
uint32_t voltageMultiplier;
uint32_t amperageMultiplier;
uint32_t accumulatedMultiplier;
uint8_t source;
uint8_t parser;
}; // 50
struct MeterConfig95 {
uint32_t baud; uint32_t baud;
uint8_t parity; uint8_t parity;
bool invert; bool invert;
@@ -269,6 +287,7 @@ private:
bool relocateConfig92(); // 2.0.3 bool relocateConfig92(); // 2.0.3
bool relocateConfig93(); // 2.1.0 bool relocateConfig93(); // 2.1.0
bool relocateConfig94(); // 2.1.4 bool relocateConfig94(); // 2.1.4
bool relocateConfig95(); // 2.1.13
void saveToFs(); void saveToFs();
bool loadFromFs(uint8_t version); bool loadFromFs(uint8_t version);

View File

@@ -45,6 +45,12 @@ void AmsData::apply(AmsData& other) {
this->l1PowerFactor = other.getL1PowerFactor(); this->l1PowerFactor = other.getL1PowerFactor();
this->l2PowerFactor = other.getL2PowerFactor(); this->l2PowerFactor = other.getL2PowerFactor();
this->l3PowerFactor = other.getL3PowerFactor(); this->l3PowerFactor = other.getL3PowerFactor();
this->l1activeImportPower = other.getL1ActiveImportPower();
this->l2activeImportPower = other.getL2ActiveImportPower();
this->l3activeImportPower = other.getL3ActiveImportPower();
this->l1activeExportPower = other.getL1ActiveExportPower();
this->l2activeExportPower = other.getL2ActiveExportPower();
this->l3activeExportPower = other.getL3ActiveExportPower();
case 3: case 3:
this->meterTimestamp = other.getMeterTimestamp(); this->meterTimestamp = other.getMeterTimestamp();
this->activeImportCounter = other.getActiveImportCounter(); this->activeImportCounter = other.getActiveImportCounter();
@@ -161,6 +167,30 @@ float AmsData::getL3PowerFactor() {
return this->l3PowerFactor; return this->l3PowerFactor;
} }
float AmsData::getL1ActiveImportPower() {
return this->l1activeImportPower;
}
float AmsData::getL2ActiveImportPower() {
return this->l2activeImportPower;
}
float AmsData::getL3ActiveImportPower() {
return this->l3activeImportPower;
}
float AmsData::getL1ActiveExportPower() {
return this->l1activeExportPower;
}
float AmsData::getL2ActiveExportPower() {
return this->l2activeExportPower;
}
float AmsData::getL3ActiveExportPower() {
return this->l3activeExportPower;
}
double AmsData::getActiveImportCounter() { double AmsData::getActiveImportCounter() {
return this->activeImportCounter; return this->activeImportCounter;
} }

View File

@@ -10,7 +10,7 @@ enum AmsType {
AmsTypeKaifa = 0x02, AmsTypeKaifa = 0x02,
AmsTypeKamstrup = 0x03, AmsTypeKamstrup = 0x03,
AmsTypeIskra = 0x08, AmsTypeIskra = 0x08,
AmsTypeLandis = 0x09, AmsTypeLandisGyr = 0x09,
AmsTypeSagemcom = 0x0A, AmsTypeSagemcom = 0x0A,
AmsTypeLng = 0x0B, AmsTypeLng = 0x0B,
AmsTypeCustom = 0x88, AmsTypeCustom = 0x88,
@@ -54,6 +54,14 @@ public:
float getL2PowerFactor(); float getL2PowerFactor();
float getL3PowerFactor(); float getL3PowerFactor();
float getL1ActiveImportPower();
float getL2ActiveImportPower();
float getL3ActiveImportPower();
float getL1ActiveExportPower();
float getL2ActiveExportPower();
float getL3ActiveExportPower();
double getActiveImportCounter(); double getActiveImportCounter();
double getReactiveImportCounter(); double getReactiveImportCounter();
double getActiveExportCounter(); double getActiveExportCounter();
@@ -71,6 +79,8 @@ protected:
time_t meterTimestamp = 0; time_t meterTimestamp = 0;
uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0; uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0; float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
float l1activeImportPower = 0, l2activeImportPower = 0, l3activeImportPower = 0;
float l1activeExportPower = 0, l2activeExportPower = 0, l3activeExportPower = 0;
float powerFactor = 0, l1PowerFactor = 0, l2PowerFactor = 0, l3PowerFactor = 0; float powerFactor = 0, l1PowerFactor = 0, l2PowerFactor = 0, l3PowerFactor = 0;
double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0; double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0;
bool threePhase = false, twoPhase = false, counterEstimated = false; bool threePhase = false, twoPhase = false, counterEstimated = false;

View File

@@ -21,7 +21,7 @@ bool AmsDataStorage::update(AmsData* data) {
} }
time_t now = time(nullptr); time_t now = time(nullptr);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Time is: %lld\n", (int64_t) now); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Time is: %lu\n", (int32_t) now);
if(tz == NULL) { if(tz == NULL) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Timezone is missing\n"); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Timezone is missing\n");
return false; return false;
@@ -30,18 +30,18 @@ bool AmsDataStorage::update(AmsData* data) {
if(data->getMeterTimestamp() > BUILD_EPOCH) { if(data->getMeterTimestamp() > BUILD_EPOCH) {
now = data->getMeterTimestamp(); now = data->getMeterTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Using meter timestamp, which is: %lld\n", (int64_t) now); debugger->printf("(AmsDataStorage) Using meter timestamp, which is: %lu\n", (int32_t) now);
} }
} else if(data->getPackageTimestamp() > BUILD_EPOCH) { } else if(data->getPackageTimestamp() > BUILD_EPOCH) {
now = data->getPackageTimestamp(); now = data->getPackageTimestamp();
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Using package timestamp, which is: %lld\n", (int64_t) now); debugger->printf("(AmsDataStorage) Using package timestamp, which is: %lu\n", (int32_t) now);
} }
} }
} }
if(now < BUILD_EPOCH) { if(now < BUILD_EPOCH) {
if(debugger->isActive(RemoteDebug::VERBOSE)) { if(debugger->isActive(RemoteDebug::VERBOSE)) {
debugger->printf("(AmsDataStorage) Invalid time: %lld\n", (int64_t) now); debugger->printf("(AmsDataStorage) Invalid time: %lu\n", (int32_t) now);
} }
return false; return false;
} }
@@ -63,7 +63,7 @@ bool AmsDataStorage::update(AmsData* data) {
return true; return true;
} else { } else {
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last day update: %lld\n", (int64_t) day.lastMeterReadTime); debugger->printf("(AmsDataStorage) Last day update: %lu\n", (int32_t) day.lastMeterReadTime);
} }
tmElements_t last; tmElements_t last;
breakTime(day.lastMeterReadTime, last); breakTime(day.lastMeterReadTime, last);
@@ -86,7 +86,7 @@ bool AmsDataStorage::update(AmsData* data) {
month.lastMeterReadTime = now; month.lastMeterReadTime = now;
} else { } else {
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last month update: %lld\n", (int64_t) month.lastMeterReadTime); debugger->printf("(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);
@@ -156,7 +156,7 @@ bool AmsDataStorage::update(AmsData* data) {
setHourExport(last.Hour, exp); setHourExport(last.Hour, exp);
if(debugger->isActive(RemoteDebug::INFO)) { if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Estimated usage for hour %u: %.1f - %.1f (%lld)\n", last.Hour, imp, exp, (int64_t) cur); debugger->printf("(AmsDataStorage) Estimated usage for hour %u: %.1f - %.1f (%lu)\n", last.Hour, imp, exp, (int32_t) cur);
} }
day.activeImport += imp; day.activeImport += imp;
@@ -199,7 +199,7 @@ bool AmsDataStorage::update(AmsData* data) {
breakTime(tz->toLocal(month.lastMeterReadTime), last); breakTime(tz->toLocal(month.lastMeterReadTime), last);
month.lastMeterReadTime = month.lastMeterReadTime - (last.Hour * 3600) - (last.Minute * 60) - last.Second; month.lastMeterReadTime = month.lastMeterReadTime - (last.Hour * 3600) - (last.Minute * 60) - last.Second;
if(debugger->isActive(RemoteDebug::DEBUG)) { if(debugger->isActive(RemoteDebug::DEBUG)) {
debugger->printf("(AmsDataStorage) Last month read after resetting to midnight: %lld\n", (int64_t) month.lastMeterReadTime); debugger->printf("(AmsDataStorage) Last month read after resetting to midnight: %lu\n", (int32_t) month.lastMeterReadTime);
} }
float hrs = (now - month.lastMeterReadTime) / 3600.0; float hrs = (now - month.lastMeterReadTime) / 3600.0;
@@ -224,7 +224,7 @@ bool AmsDataStorage::update(AmsData* data) {
setDayExport(last.Day, exp); setDayExport(last.Day, exp);
if(debugger->isActive(RemoteDebug::INFO)) { if(debugger->isActive(RemoteDebug::INFO)) {
debugger->printf("(AmsDataStorage) Estimated usage for day %u: %.1f - %.1f (%lld)\n", last.Day, imp, exp, (int64_t) cur); debugger->printf("(AmsDataStorage) Estimated usage for day %u: %.1f - %.1f (%lu)\n", last.Day, imp, exp, (int32_t) cur);
} }
month.activeImport += imp; month.activeImport += imp;
@@ -383,11 +383,11 @@ bool AmsDataStorage::isDayHappy() {
tmElements_t tm, last; tmElements_t tm, last;
if(now < day.lastMeterReadTime) { if(now < day.lastMeterReadTime) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day %lld < %lld\n", (int64_t) now, (int64_t) day.lastMeterReadTime); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day %lu < %lu\n", (int32_t) now, (int32_t) day.lastMeterReadTime);
return false; return false;
} }
if(now-day.lastMeterReadTime > 3600) { if(now-day.lastMeterReadTime > 3600) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day %lld - %lld > 3600\n", (int64_t) now, (int64_t) day.lastMeterReadTime); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Day %lu - %lu > 3600\n", (int32_t) now, (int32_t) day.lastMeterReadTime);
return false; return false;
} }
breakTime(tz->toLocal(now), tm); breakTime(tz->toLocal(now), tm);
@@ -411,11 +411,11 @@ bool AmsDataStorage::isMonthHappy() {
tmElements_t tm, last; tmElements_t tm, last;
if(now < month.lastMeterReadTime) { if(now < month.lastMeterReadTime) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month %lld < %lld\n", (int64_t) now, (int64_t) month.lastMeterReadTime); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month %lu < %lu\n", (int32_t) now, (int32_t) month.lastMeterReadTime);
return false; return false;
} }
if(now-month.lastMeterReadTime > 86400) { if(now-month.lastMeterReadTime > 86400) {
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month %lld - %lld > 3600\n", (int64_t) now, (int64_t) month.lastMeterReadTime); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(AmsDataStorage) Month %lu - %lu > 3600\n", (int32_t) now, (int32_t) month.lastMeterReadTime);
return false; return false;
} }
breakTime(tz->toLocal(now), tm); breakTime(tz->toLocal(now), tm);

View File

@@ -7,20 +7,20 @@
struct DayDataPoints { struct DayDataPoints {
uint8_t version; uint8_t version;
int16_t hImport[24]; uint16_t hImport[24];
time_t lastMeterReadTime; time_t lastMeterReadTime;
uint32_t activeImport; uint32_t activeImport;
uint32_t activeExport; uint32_t activeExport;
int16_t hExport[24]; uint16_t hExport[24];
}; // 112 bytes }; // 112 bytes
struct MonthDataPoints { struct MonthDataPoints {
uint8_t version; uint8_t version;
int16_t dImport[31]; uint16_t dImport[31];
time_t lastMeterReadTime; time_t lastMeterReadTime;
uint32_t activeImport; uint32_t activeImport;
uint32_t activeExport; uint32_t activeExport;
int16_t dExport[31]; uint16_t dExport[31];
}; // 141 bytes }; // 141 bytes
class AmsDataStorage { class AmsDataStorage {

View File

@@ -1,7 +1,7 @@
#ifndef _AMSTOMQTTBRIDGE_H #ifndef _AMSTOMQTTBRIDGE_H
#define _AMSTOMQTTBRIDGE_H #define _AMSTOMQTTBRIDGE_H
#define WIFI_CONNECTION_TIMEOUT 30000; #define WIFI_CONNECTION_TIMEOUT 30000
#define INVALID_BUTTON_PIN 0xFFFFFFFF #define INVALID_BUTTON_PIN 0xFFFFFFFF

View File

@@ -119,7 +119,6 @@ DLMSParser *dlmsParser = NULL;
DSMRParser *dsmrParser = NULL; DSMRParser *dsmrParser = NULL;
void setup() { void setup() {
WiFiConfig wifi;
Serial.begin(115200); Serial.begin(115200);
if(!config.getGpioConfig(gpioConfig)) { if(!config.getGpioConfig(gpioConfig)) {
@@ -246,7 +245,6 @@ void setup() {
} }
Debug.setSerialEnabled(true); Debug.setSerialEnabled(true);
DebugConfig debug;
delay(1); delay(1);
float vcc = hw.getVcc(); float vcc = hw.getVcc();
@@ -309,7 +307,7 @@ void setup() {
} }
debugI(" flashing"); debugI(" flashing");
File firmwareFile = LittleFS.open(FILE_FIRMWARE, "r"); File firmwareFile = LittleFS.open(FILE_FIRMWARE, (char*) "r");
debugD(" firmware size: %d", firmwareFile.size()); debugD(" firmware size: %d", firmwareFile.size());
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
debugD(" available: %d", maxSketchSpace); debugD(" available: %d", maxSketchSpace);
@@ -360,7 +358,7 @@ void setup() {
NtpConfig ntp; NtpConfig ntp;
if(config.getNtpConfig(ntp)) { if(config.getNtpConfig(ntp)) {
configTime(ntp.offset*10, ntp.summerOffset*10, ntp.enable ? strlen(ntp.server) > 0 ? ntp.server : "pool.ntp.org" : ""); // Add NTP server by default if none is configured configTime(ntp.offset*10, ntp.summerOffset*10, ntp.enable ? strlen(ntp.server) > 0 ? ntp.server : (char*) F("pool.ntp.org") : (char*) F("")); // Add NTP server by default if none is configured
sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0); sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0);
ntpEnabled = ntp.enable; ntpEnabled = ntp.enable;
TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6}; TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6};
@@ -436,6 +434,10 @@ void loop() {
} }
} }
if(now > 10000 && now - lastErrorBlink > 3000) {
errorBlink();
}
// Only do normal stuff if we're not booted as AP // Only do normal stuff if we're not booted as AP
if (WiFi.getMode() != WIFI_AP) { if (WiFi.getMode() != WIFI_AP) {
if (WiFi.status() != WL_CONNECTED) { if (WiFi.status() != WL_CONNECTED) {
@@ -471,7 +473,7 @@ void loop() {
if(strlen(wifi.hostname) > 0 && wifi.mdns) { if(strlen(wifi.hostname) > 0 && wifi.mdns) {
debugD("mDNS is enabled, using host: %s", wifi.hostname); debugD("mDNS is enabled, using host: %s", wifi.hostname);
if(MDNS.begin(wifi.hostname)) { if(MDNS.begin(wifi.hostname)) {
MDNS.addService("http", "tcp", 80); MDNS.addService(F("http"), F("tcp"), 80);
} else { } else {
debugE("Failed to set up mDNS!"); debugE("Failed to set up mDNS!");
} }
@@ -506,10 +508,6 @@ void loop() {
MDNS.update(); MDNS.update();
#endif #endif
if(now > 10000 && now - lastErrorBlink > 3000) {
errorBlink();
}
if (mqttEnabled || config.isMqttChanged()) { if (mqttEnabled || config.isMqttChanged()) {
if(mqtt == NULL || !mqtt->connected() || config.isMqttChanged()) { if(mqtt == NULL || !mqtt->connected() || config.isMqttChanged()) {
MQTT_connect(); MQTT_connect();
@@ -591,7 +589,7 @@ void loop() {
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) { if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
mqttHandler->publishTemperatures(&config, &hw); mqttHandler->publishTemperatures(&config, &hw);
} }
debugD("Used %d ms to update temperature", millis()-start); debugD("Used %ld ms to update temperature", millis()-start);
} }
if(now - lastSysupdate > 10000) { if(now - lastSysupdate > 10000) {
if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) { if(mqtt != NULL && mqttHandler != NULL && WiFi.getMode() != WIFI_AP && WiFi.status() == WL_CONNECTED && mqtt->connected() && !topic.isEmpty()) {
@@ -609,7 +607,7 @@ void loop() {
} }
void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert) { void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert) {
if(Debug.isActive(RemoteDebug::INFO)) Debug.printf("(setupHanPort) Setting up HAN on pin %d with baud %d and parity %d\n", pin, baud, parityOrdinal); if(Debug.isActive(RemoteDebug::INFO)) Debug.printf((char*) F("(setupHanPort) Setting up HAN on pin %d with baud %d and parity %d\n"), pin, baud, parityOrdinal);
HardwareSerial *hwSerial = NULL; HardwareSerial *hwSerial = NULL;
if(pin == 3 || pin == 113) { if(pin == 3 || pin == 113) {
@@ -660,14 +658,11 @@ void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert
#if defined(CONFIG_IDF_TARGET_ESP32S2) #if defined(CONFIG_IDF_TARGET_ESP32S2)
hwSerial->begin(baud, serialConfig, -1, -1, invert); hwSerial->begin(baud, serialConfig, -1, -1, invert);
hwSerial->setRxBufferSize(768);
uart_set_pin(UART_NUM_1, UART_PIN_NO_CHANGE, pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); uart_set_pin(UART_NUM_1, UART_PIN_NO_CHANGE, pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
#elif defined(ESP32) #elif defined(ESP32)
hwSerial->begin(baud, serialConfig, -1, -1, invert); hwSerial->begin(baud, serialConfig, -1, -1, invert);
hwSerial->setRxBufferSize(768);
#else #else
hwSerial->begin(baud, serialConfig, SERIAL_FULL, 1, invert); hwSerial->begin(baud, serialConfig, SERIAL_FULL, 1, invert);
hwSerial->setRxBufferSize(768);
#endif #endif
#if defined(ESP8266) #if defined(ESP8266)
@@ -725,22 +720,25 @@ void errorBlink() {
if(lastError == 3) if(lastError == 3)
lastError = 0; lastError = 0;
lastErrorBlink = millis(); lastErrorBlink = millis();
for(;lastError < 3;lastError++) { while(lastError < 3) {
switch(lastError) { switch(lastError++) {
case 0: case 0:
if(lastErrorBlink - meterState.getLastUpdateMillis() > 30000) { if(lastErrorBlink - meterState.getLastUpdateMillis() > 30000) {
debugW("No HAN data received last 30s, single blink");
hw.ledBlink(LED_RED, 1); // If no message received from AMS in 30 sec, blink once hw.ledBlink(LED_RED, 1); // If no message received from AMS in 30 sec, blink once
return; return;
} }
break; break;
case 1: case 1:
if(mqttEnabled && mqtt != NULL && mqtt->lastError() != 0) { if(mqttEnabled && mqtt != NULL && mqtt->lastError() != 0) {
debugW("MQTT connection not available, double blink");
hw.ledBlink(LED_RED, 2); // If MQTT error, blink twice hw.ledBlink(LED_RED, 2); // If MQTT error, blink twice
return; return;
} }
break; break;
case 2: case 2:
if(WiFi.getMode() != WIFI_AP && WiFi.status() != WL_CONNECTED) { if(WiFi.getMode() != WIFI_AP && WiFi.status() != WL_CONNECTED) {
debugW("WiFi not connected, tripe blink");
hw.ledBlink(LED_RED, 3); // If WiFi not connected, blink three times hw.ledBlink(LED_RED, 3); // If WiFi not connected, blink three times
return; return;
} }
@@ -764,14 +762,14 @@ void swapWifiMode() {
if (mode != WIFI_AP || !config.hasConfig()) { if (mode != WIFI_AP || !config.hasConfig()) {
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to AP mode"); if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to AP mode");
WiFi.softAP("AMS2MQTT"); WiFi.softAP((char*) F("AMS2MQTT"));
WiFi.mode(WIFI_AP); WiFi.mode(WIFI_AP);
if(dnsServer == NULL) { if(dnsServer == NULL) {
dnsServer = new DNSServer(); dnsServer = new DNSServer();
} }
dnsServer->setErrorReplyCode(DNSReplyCode::NoError); dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
dnsServer->start(53, "*", WiFi.softAPIP()); dnsServer->start(53, (char*) F("*"), WiFi.softAPIP());
} else { } else {
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to STA mode"); if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to STA mode");
if(dnsServer != NULL) { if(dnsServer != NULL) {
@@ -812,7 +810,7 @@ bool readHanPort() {
hanBuffer[len++] = hanSerial->read(); hanBuffer[len++] = hanSerial->read();
ctx.length = len; ctx.length = len;
pos = unwrapData((uint8_t *) hanBuffer, ctx); pos = unwrapData((uint8_t *) hanBuffer, ctx);
if(pos >= 0) { if(ctx.type > 0 && pos >= 0) {
if(ctx.type == DATA_TAG_DLMS) { if(ctx.type == DATA_TAG_DLMS) {
debugV("Received valid DLMS at %d", pos); debugV("Received valid DLMS at %d", pos);
} else if(ctx.type == DATA_TAG_DSMR) { } else if(ctx.type == DATA_TAG_DSMR) {
@@ -827,6 +825,12 @@ bool readHanPort() {
} }
if(pos == DATA_PARSE_INCOMPLETE) { if(pos == DATA_PARSE_INCOMPLETE) {
return false; return false;
} else if(pos == DATA_PARSE_UNKNOWN_DATA) {
debugV("Unknown data payload:");
len = len + hanSerial->readBytes(hanBuffer+len, BUF_SIZE_HAN-len);
debugPrint(hanBuffer, 0, len);
len = 0;
return false;
} }
if(pos == DATA_PARSE_INTERMEDIATE_SEGMENT) { if(pos == DATA_PARSE_INTERMEDIATE_SEGMENT) {
@@ -972,13 +976,13 @@ void printHanReadError(int pos) {
void debugPrint(byte *buffer, int start, int length) { void debugPrint(byte *buffer, int start, int length) {
for (int i = start; i < start + length; i++) { for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10) if (buffer[i] < 0x10)
Debug.print("0"); Debug.print(F("0"));
Debug.print(buffer[i], HEX); Debug.print(buffer[i], HEX);
Debug.print(" "); Debug.print(F(" "));
if ((i - start + 1) % 16 == 0) if ((i - start + 1) % 16 == 0)
Debug.println(""); Debug.println("");
else if ((i - start + 1) % 4 == 0) else if ((i - start + 1) % 4 == 0)
Debug.print(" "); Debug.print(F(" "));
yield(); // Let other get some resources too yield(); // Let other get some resources too
} }
@@ -1051,7 +1055,6 @@ void WiFi_connect() {
} }
#endif #endif
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.setSleep(WIFI_PS_MAX_MODEM);
#if defined(ESP32) #if defined(ESP32)
if(wifi.power >= 195) if(wifi.power >= 195)
WiFi.setTxPower(WIFI_POWER_19_5dBm); WiFi.setTxPower(WIFI_POWER_19_5dBm);
@@ -1093,7 +1096,7 @@ void WiFi_connect() {
if(strlen(wifi.dns2) > 0) { if(strlen(wifi.dns2) > 0) {
dns2.fromString(wifi.dns2); dns2.fromString(wifi.dns2);
} else if(dns1.toString().isEmpty()) { } else if(dns1.toString().isEmpty()) {
dns2.fromString("208.67.220.220"); // Add OpenDNS as second by default if nothing is configured dns2.fromString(F("208.67.220.220")); // Add OpenDNS as second by default if nothing is configured
} }
if(!WiFi.config(ip, gw, sn, dns1, dns2)) { if(!WiFi.config(ip, gw, sn, dns1, dns2)) {
debugE("Static IP configuration is invalid, not using"); debugE("Static IP configuration is invalid, not using");
@@ -1112,6 +1115,19 @@ void WiFi_connect() {
WiFi.setAutoReconnect(true); WiFi.setAutoReconnect(true);
WiFi.persistent(true); WiFi.persistent(true);
if(WiFi.begin(wifi.ssid, wifi.psk)) { if(WiFi.begin(wifi.ssid, wifi.psk)) {
if(wifi.sleep <= 2) {
switch(wifi.sleep) {
case 0:
WiFi.setSleep(WIFI_PS_NONE);
break;
case 1:
WiFi.setSleep(WIFI_PS_MIN_MODEM);
break;
case 2:
WiFi.setSleep(WIFI_PS_MAX_MODEM);
break;
}
}
yield(); yield();
} else { } else {
if (Debug.isActive(RemoteDebug::ERROR)) debugI("Unable to start WiFi"); if (Debug.isActive(RemoteDebug::ERROR)) debugI("Unable to start WiFi");
@@ -1121,16 +1137,16 @@ void WiFi_connect() {
void mqttMessageReceived(String &topic, String &payload) { void mqttMessageReceived(String &topic, String &payload) {
debugI("Received message for topic %s", topic.c_str() ); debugI("Received message for topic %s", topic.c_str() );
if(meterConfig.source == METER_SOURCE_MQTT) { //if(meterConfig.source == METER_SOURCE_MQTT) {
DataParserContext ctx = {payload.length()/2}; //DataParserContext ctx = {static_cast<uint8_t>(payload.length()/2)};
fromHex(hanBuffer, payload, ctx.length); //fromHex(hanBuffer, payload, ctx.length);
uint16_t pos = unwrapData(hanBuffer, ctx); //uint16_t pos = unwrapData(hanBuffer, ctx);
// TODO: Run through DLMS/DMSR parser and apply AmsData // TODO: Run through DLMS/DMSR parser and apply AmsData
} //}
} }
int16_t unwrapData(uint8_t *buf, DataParserContext &context) { int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
int16_t ret; int16_t ret = 0;
bool doRet = false; bool doRet = false;
uint16_t end = BUF_SIZE_HAN; uint16_t end = BUF_SIZE_HAN;
uint8_t tag = (*buf); uint8_t tag = (*buf);
@@ -1170,7 +1186,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
if(res >= 0) doRet = true; if(res >= 0) doRet = true;
break; break;
default: default:
debugE("Ended up in default case while unwrapping..."); debugE("Ended up in default case while unwrapping...(tag %02X)", tag);
return DATA_PARSE_UNKNOWN_DATA; return DATA_PARSE_UNKNOWN_DATA;
} }
lastTag = tag; lastTag = tag;
@@ -1321,7 +1337,7 @@ void MQTT_connect() {
if(LittleFS.exists(FILE_MQTT_CA)) { if(LittleFS.exists(FILE_MQTT_CA)) {
debugI("Found MQTT CA file (%dkb free heap)", ESP.getFreeHeap()); debugI("Found MQTT CA file (%dkb free heap)", ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_CA, "r"); file = LittleFS.open(FILE_MQTT_CA, (char*) "r");
#if defined(ESP8266) #if defined(ESP8266)
BearSSL::X509List *serverTrustedCA = new BearSSL::X509List(file); BearSSL::X509List *serverTrustedCA = new BearSSL::X509List(file);
mqttSecureClient->setTrustAnchors(serverTrustedCA); mqttSecureClient->setTrustAnchors(serverTrustedCA);
@@ -1334,12 +1350,12 @@ void MQTT_connect() {
if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) { if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) {
#if defined(ESP8266) #if defined(ESP8266)
debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap()); debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_CERT, "r"); file = LittleFS.open(FILE_MQTT_CERT, (char*) "r");
BearSSL::X509List *serverCertList = new BearSSL::X509List(file); BearSSL::X509List *serverCertList = new BearSSL::X509List(file);
file.close(); file.close();
debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap()); debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_KEY, "r"); file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file); BearSSL::PrivateKey *serverPrivKey = new BearSSL::PrivateKey(file);
file.close(); file.close();
@@ -1347,12 +1363,12 @@ void MQTT_connect() {
mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey); mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey);
#elif defined(ESP32) #elif defined(ESP32)
debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap()); debugI("Found MQTT certificate file (%dkb free heap)", ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_CERT, "r"); file = LittleFS.open(FILE_MQTT_CERT, (char*) "r");
mqttSecureClient->loadCertificate(file, file.size()); mqttSecureClient->loadCertificate(file, file.size());
file.close(); file.close();
debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap()); debugI("Found MQTT key file (%dkb free heap)", ESP.getFreeHeap());
file = LittleFS.open(FILE_MQTT_KEY, "r"); file = LittleFS.open(FILE_MQTT_KEY, (char*) "r");
mqttSecureClient->loadPrivateKey(file, file.size()); mqttSecureClient->loadPrivateKey(file, file.size());
file.close(); file.close();
#endif #endif
@@ -1375,7 +1391,7 @@ void MQTT_connect() {
#if defined(ESP8266) #if defined(ESP8266)
if(mqttSecureClient) { if(mqttSecureClient) {
time_t epoch = time(nullptr); time_t epoch = time(nullptr);
debugD("Setting NTP time %i for secure MQTT connection", epoch); debugD("Setting NTP time %lu for secure MQTT connection", epoch);
mqttSecureClient->setX509Time(epoch); mqttSecureClient->setX509Time(epoch);
} }
#endif #endif
@@ -1418,7 +1434,7 @@ void configFileParse() {
return; return;
} }
File file = LittleFS.open(FILE_CFG, "r"); File file = LittleFS.open(FILE_CFG, (char*) "r");
bool lSys = false; bool lSys = false;
bool lWiFi = false; bool lWiFi = false;
@@ -1448,195 +1464,196 @@ void configFileParse() {
char* buf = (char*) commonBuffer; char* buf = (char*) commonBuffer;
memset(buf, 0, 1024); memset(buf, 0, 1024);
while((size = file.readBytesUntil('\n', buf, 1024)) > 0) { while((size = file.readBytesUntil('\n', buf, 1024)) > 0) {
if(strncmp(buf, "boardType ", 10) == 0) { if(strncmp_P(buf, PSTR("boardType "), 10) == 0) {
if(!lSys) { config.getSystemConfig(sys); lSys = true; }; if(!lSys) { config.getSystemConfig(sys); lSys = true; };
sys.boardType = String(buf+10).toInt(); sys.boardType = String(buf+10).toInt();
} else if(strncmp(buf, "ssid ", 5) == 0) { } else if(strncmp_P(buf, PSTR("ssid "), 5) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
memcpy(wifi.ssid, buf+5, size-5); memcpy(wifi.ssid, buf+5, size-5);
} else if(strncmp(buf, "psk ", 4) == 0) { } else if(strncmp_P(buf, PSTR("psk "), 4) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
memcpy(wifi.psk, buf+4, size-4); memcpy(wifi.psk, buf+4, size-4);
} else if(strncmp(buf, "ip ", 3) == 0) { } else if(strncmp_P(buf, PSTR("ip "), 3) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
memcpy(wifi.ip, buf+3, size-3); memcpy(wifi.ip, buf+3, size-3);
} else if(strncmp(buf, "gateway ", 8) == 0) { } else if(strncmp_P(buf, PSTR("gateway "), 8) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
memcpy(wifi.gateway, buf+8, size-8); memcpy(wifi.gateway, buf+8, size-8);
} else if(strncmp(buf, "subnet ", 7) == 0) { } else if(strncmp_P(buf, PSTR("subnet "), 7) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
memcpy(wifi.subnet, buf+7, size-7); memcpy(wifi.subnet, buf+7, size-7);
} else if(strncmp(buf, "dns1 ", 5) == 0) { } else if(strncmp_P(buf, PSTR("dns1 "), 5) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
memcpy(wifi.dns1, buf+5, size-5); memcpy(wifi.dns1, buf+5, size-5);
} else if(strncmp(buf, "dns2 ", 5) == 0) { } else if(strncmp_P(buf, PSTR("dns2 "), 5) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
memcpy(wifi.dns2, buf+5, size-5); memcpy(wifi.dns2, buf+5, size-5);
} else if(strncmp(buf, "hostname ", 9) == 0) { } else if(strncmp_P(buf, PSTR("hostname "), 9) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
memcpy(wifi.hostname, buf+9, size-9); memcpy(wifi.hostname, buf+9, size-9);
} else if(strncmp(buf, "mdns ", 5) == 0) { } else if(strncmp_P(buf, PSTR("mdns "), 5) == 0) {
if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; }; if(!lWiFi) { config.getWiFiConfig(wifi); lWiFi = true; };
wifi.mdns = String(buf+5).toInt() == 1;; wifi.mdns = String(buf+5).toInt() == 1;;
} else if(strncmp(buf, "mqttHost ", 9) == 0) { } else if(strncmp_P(buf, PSTR("mqttHost "), 9) == 0) {
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; }; if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
memcpy(mqtt.host, buf+9, size-9); memcpy(mqtt.host, buf+9, size-9);
} else if(strncmp(buf, "mqttPort ", 9) == 0) { } else if(strncmp_P(buf, PSTR("mqttPort "), 9) == 0) {
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; }; if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
mqtt.port = String(buf+9).toInt(); mqtt.port = String(buf+9).toInt();
} else if(strncmp(buf, "mqttClientId ", 13) == 0) { } else if(strncmp_P(buf, PSTR("mqttClientId "), 13) == 0) {
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; }; if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
memcpy(mqtt.clientId, buf+13, size-13); memcpy(mqtt.clientId, buf+13, size-13);
} else if(strncmp(buf, "mqttPublishTopic ", 17) == 0) { } else if(strncmp_P(buf, PSTR("mqttPublishTopic "), 17) == 0) {
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; }; if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
memcpy(mqtt.publishTopic, buf+17, size-17); memcpy(mqtt.publishTopic, buf+17, size-17);
} else if(strncmp(buf, "mqttUsername ", 13) == 0) { } else if(strncmp_P(buf, PSTR("mqttUsername "), 13) == 0) {
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; }; if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
memcpy(mqtt.username, buf+13, size-13); memcpy(mqtt.username, buf+13, size-13);
} else if(strncmp(buf, "mqttPassword ", 13) == 0) { } else if(strncmp_P(buf, PSTR("mqttPassword "), 13) == 0) {
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; }; if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
memcpy(mqtt.password, buf+13, size-13); memcpy(mqtt.password, buf+13, size-13);
} else if(strncmp(buf, "mqttPayloadFormat ", 18) == 0) { } else if(strncmp_P(buf, PSTR("mqttPayloadFormat "), 18) == 0) {
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; }; if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
mqtt.payloadFormat = String(buf+18).toInt(); mqtt.payloadFormat = String(buf+18).toInt();
} else if(strncmp(buf, "mqttSsl ", 8) == 0) { } else if(strncmp_P(buf, PSTR("mqttSsl "), 8) == 0) {
if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; }; if(!lMqtt) { config.getMqttConfig(mqtt); lMqtt = true; };
mqtt.ssl = String(buf+8).toInt() == 1;; mqtt.ssl = String(buf+8).toInt() == 1;;
} else if(strncmp(buf, "webSecurity ", 12) == 0) { } else if(strncmp_P(buf, PSTR("webSecurity "), 12) == 0) {
if(!lWeb) { config.getWebConfig(web); lWeb = true; }; if(!lWeb) { config.getWebConfig(web); lWeb = true; };
web.security = String(buf+12).toInt(); web.security = String(buf+12).toInt();
} else if(strncmp(buf, "webUsername ", 12) == 0) { } else if(strncmp_P(buf, PSTR("webUsername "), 12) == 0) {
if(!lWeb) { config.getWebConfig(web); lWeb = true; }; if(!lWeb) { config.getWebConfig(web); lWeb = true; };
memcpy(web.username, buf+12, size-12); memcpy(web.username, buf+12, size-12);
} else if(strncmp(buf, "webPassword ", 12) == 0) { } else if(strncmp_P(buf, PSTR("webPassword "), 12) == 0) {
if(!lWeb) { config.getWebConfig(web); lWeb = true; }; if(!lWeb) { config.getWebConfig(web); lWeb = true; };
memcpy(web.username, buf+12, size-12); memcpy(web.username, buf+12, size-12);
} else if(strncmp(buf, "meterBaud ", 10) == 0) { } else if(strncmp_P(buf, PSTR("meterBaud "), 10) == 0) {
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; }; if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
meter.baud = String(buf+10).toInt(); meter.baud = String(buf+10).toInt();
} else if(strncmp(buf, "meterParity ", 12) == 0) { } else if(strncmp_P(buf, PSTR("meterParity "), 12) == 0) {
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; }; if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
if(strncmp(buf+12, "7N1", 3) == 0) meter.parity = 2; if(strncmp_P(buf+12, PSTR("7N1"), 3) == 0) meter.parity = 2;
if(strncmp(buf+12, "8N1", 3) == 0) meter.parity = 3; if(strncmp_P(buf+12, PSTR("8N1"), 3) == 0) meter.parity = 3;
if(strncmp(buf+12, "7E1", 3) == 0) meter.parity = 10; if(strncmp_P(buf+12, PSTR("7E1"), 3) == 0) meter.parity = 10;
if(strncmp(buf+12, "8E1", 3) == 0) meter.parity = 11; if(strncmp_P(buf+12, PSTR("8E1"), 3) == 0) meter.parity = 11;
} else if(strncmp(buf, "meterInvert ", 12) == 0) { } else if(strncmp_P(buf, PSTR("meterInvert "), 12) == 0) {
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; }; if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
meter.invert = String(buf+12).toInt() == 1;; meter.invert = String(buf+12).toInt() == 1;;
} else if(strncmp(buf, "meterDistributionSystem ", 24) == 0) { } else if(strncmp_P(buf, PSTR("meterDistributionSystem "), 24) == 0) {
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; }; if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
meter.distributionSystem = String(buf+24).toInt(); meter.distributionSystem = String(buf+24).toInt();
} else if(strncmp(buf, "meterMainFuse ", 14) == 0) { } else if(strncmp_P(buf, PSTR("meterMainFuse "), 14) == 0) {
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; }; if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
meter.mainFuse = String(buf+14).toInt(); meter.mainFuse = String(buf+14).toInt();
} else if(strncmp(buf, "meterProductionCapacity ", 24) == 0) { } else if(strncmp_P(buf, PSTR("meterProductionCapacity "), 24) == 0) {
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; }; if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
meter.productionCapacity = String(buf+24).toInt(); meter.productionCapacity = String(buf+24).toInt();
} else if(strncmp(buf, "meterEncryptionKey ", 19) == 0) { } else if(strncmp_P(buf, PSTR("meterEncryptionKey "), 19) == 0) {
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; }; if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
fromHex(meter.encryptionKey, String(buf+19), 16); fromHex(meter.encryptionKey, String(buf+19), 16);
} else if(strncmp(buf, "meterAuthenticationKey ", 23) == 0) { } else if(strncmp_P(buf, PSTR("meterAuthenticationKey "), 23) == 0) {
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; }; if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
fromHex(meter.authenticationKey, String(buf+19), 16); fromHex(meter.authenticationKey, String(buf+19), 16);
} else if(strncmp(buf, "gpioHanPin ", 11) == 0) { } else if(strncmp_P(buf, PSTR("gpioHanPin "), 11) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.hanPin = String(buf+11).toInt(); gpio.hanPin = String(buf+11).toInt();
} else if(strncmp(buf, "gpioApPin ", 10) == 0) { } else if(strncmp_P(buf, PSTR("gpioApPin "), 10) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.apPin = String(buf+10).toInt(); gpio.apPin = String(buf+10).toInt();
} else if(strncmp(buf, "gpioLedPin ", 11) == 0) { } else if(strncmp_P(buf, PSTR("gpioLedPin "), 11) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.ledPin = String(buf+11).toInt(); gpio.ledPin = String(buf+11).toInt();
} else if(strncmp(buf, "gpioLedInverted ", 16) == 0) { } else if(strncmp_P(buf, PSTR("gpioLedInverted "), 16) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.ledInverted = String(buf+16).toInt() == 1; gpio.ledInverted = String(buf+16).toInt() == 1;
} else if(strncmp(buf, "gpioLedPinRed ", 14) == 0) { } else if(strncmp_P(buf, PSTR("gpioLedPinRed "), 14) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.ledPinRed = String(buf+14).toInt(); gpio.ledPinRed = String(buf+14).toInt();
} else if(strncmp(buf, "gpioLedPinGreen ", 16) == 0) { } else if(strncmp_P(buf, PSTR("gpioLedPinGreen "), 16) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.ledPinGreen = String(buf+16).toInt(); gpio.ledPinGreen = String(buf+16).toInt();
} else if(strncmp(buf, "gpioLedPinBlue ", 15) == 0) { } else if(strncmp_P(buf, PSTR("gpioLedPinBlue "), 15) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.ledPinBlue = String(buf+15).toInt(); gpio.ledPinBlue = String(buf+15).toInt();
} else if(strncmp(buf, "gpioLedRgbInverted ", 19) == 0) { } else if(strncmp_P(buf, PSTR("gpioLedRgbInverted "), 19) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.ledRgbInverted = String(buf+19).toInt() == 1; gpio.ledRgbInverted = String(buf+19).toInt() == 1;
} else if(strncmp(buf, "gpioTempSensorPin ", 18) == 0) { } else if(strncmp_P(buf, PSTR("gpioTempSensorPin "), 18) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.tempSensorPin = String(buf+18).toInt(); gpio.tempSensorPin = String(buf+18).toInt();
} else if(strncmp(buf, "gpioTempAnalogSensorPin ", 24) == 0) { } else if(strncmp_P(buf, PSTR("gpioTempAnalogSensorPin "), 24) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.tempAnalogSensorPin = String(buf+24).toInt(); gpio.tempAnalogSensorPin = String(buf+24).toInt();
} else if(strncmp(buf, "gpioVccPin ", 11) == 0) { } else if(strncmp_P(buf, PSTR("gpioVccPin "), 11) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.vccPin = String(buf+11).toInt(); gpio.vccPin = String(buf+11).toInt();
} else if(strncmp(buf, "gpioVccOffset ", 14) == 0) { } else if(strncmp_P(buf, PSTR("gpioVccOffset "), 14) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.vccOffset = String(buf+14).toDouble() * 100; gpio.vccOffset = String(buf+14).toDouble() * 100;
} else if(strncmp(buf, "gpioVccMultiplier ", 18) == 0) { } else if(strncmp_P(buf, PSTR("gpioVccMultiplier "), 18) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.vccMultiplier = String(buf+18).toDouble() * 1000; gpio.vccMultiplier = String(buf+18).toDouble() * 1000;
} else if(strncmp(buf, "gpioVccBootLimit ", 17) == 0) { } else if(strncmp_P(buf, PSTR("gpioVccBootLimit "), 17) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.vccBootLimit = String(buf+17).toDouble() * 10; gpio.vccBootLimit = String(buf+17).toDouble() * 10;
} else if(strncmp(buf, "gpioVccResistorGnd ", 19) == 0) { } else if(strncmp_P(buf, PSTR("gpioVccResistorGnd "), 19) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.vccResistorGnd = String(buf+19).toInt(); gpio.vccResistorGnd = String(buf+19).toInt();
} else if(strncmp(buf, "gpioVccResistorVcc ", 19) == 0) { } else if(strncmp_P(buf, PSTR("gpioVccResistorVcc "), 19) == 0) {
if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; }; if(!lGpio) { config.getGpioConfig(gpio); lGpio = true; };
gpio.vccResistorVcc = String(buf+19).toInt(); gpio.vccResistorVcc = String(buf+19).toInt();
} else if(strncmp(buf, "domoticzElidx ", 14) == 0) { } else if(strncmp_P(buf, PSTR("domoticzElidx "), 14) == 0) {
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; }; if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
domo.elidx = String(buf+14).toInt(); domo.elidx = String(buf+14).toInt();
} else if(strncmp(buf, "domoticzVl1idx ", 15) == 0) { } else if(strncmp_P(buf, PSTR("domoticzVl1idx "), 15) == 0) {
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; }; if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
domo.vl1idx = String(buf+15).toInt(); domo.vl1idx = String(buf+15).toInt();
} else if(strncmp(buf, "domoticzVl2idx ", 15) == 0) { } else if(strncmp_P(buf, PSTR("domoticzVl2idx "), 15) == 0) {
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; }; if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
domo.vl2idx = String(buf+15).toInt(); domo.vl2idx = String(buf+15).toInt();
} else if(strncmp(buf, "domoticzVl3idx ", 15) == 0) { } else if(strncmp_P(buf, PSTR("domoticzVl3idx "), 15) == 0) {
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; }; if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
domo.vl3idx = String(buf+15).toInt(); domo.vl3idx = String(buf+15).toInt();
} else if(strncmp(buf, "domoticzCl1idx ", 15) == 0) { } else if(strncmp_P(buf, PSTR("domoticzCl1idx "), 15) == 0) {
if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; }; if(!lDomo) { config.getDomoticzConfig(domo); lDomo = true; };
domo.cl1idx = String(buf+15).toInt(); domo.cl1idx = String(buf+15).toInt();
} else if(strncmp(buf, "ntpEnable ", 10) == 0) { } else if(strncmp_P(buf, PSTR("ntpEnable "), 10) == 0) {
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
ntp.enable = String(buf+10).toInt() == 1; ntp.enable = String(buf+10).toInt() == 1;
} else if(strncmp(buf, "ntpDhcp ", 8) == 0) { } else if(strncmp_P(buf, PSTR("ntpDhcp "), 8) == 0) {
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
ntp.dhcp = String(buf+8).toInt() == 1; ntp.dhcp = String(buf+8).toInt() == 1;
} else if(strncmp(buf, "ntpOffset ", 10) == 0) { } else if(strncmp_P(buf, PSTR("ntpOffset "), 10) == 0) {
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
ntp.offset = String(buf+10).toInt() / 10; ntp.offset = String(buf+10).toInt() / 10;
} else if(strncmp(buf, "ntpSummerOffset ", 16) == 0) { } else if(strncmp_P(buf, PSTR("ntpSummerOffset "), 16) == 0) {
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
ntp.summerOffset = String(buf+16).toInt() / 10; ntp.summerOffset = String(buf+16).toInt() / 10;
} else if(strncmp(buf, "ntpServer ", 10) == 0) { } else if(strncmp_P(buf, PSTR("ntpServer "), 10) == 0) {
if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; }; if(!lNtp) { config.getNtpConfig(ntp); lNtp = true; };
memcpy(ntp.server, buf+10, size-10); memcpy(ntp.server, buf+10, size-10);
} else if(strncmp(buf, "entsoeToken ", 12) == 0) { } else if(strncmp_P(buf, PSTR("entsoeToken "), 12) == 0) {
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; }; if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
memcpy(entsoe.token, buf+12, size-12); memcpy(entsoe.token, buf+12, size-12);
} else if(strncmp(buf, "entsoeArea ", 11) == 0) { } else if(strncmp_P(buf, PSTR("entsoeArea "), 11) == 0) {
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; }; if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
memcpy(entsoe.area, buf+11, size-11); memcpy(entsoe.area, buf+11, size-11);
} else if(strncmp(buf, "entsoeCurrency ", 15) == 0) { } else if(strncmp_P(buf, PSTR("entsoeCurrency "), 15) == 0) {
if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; }; if(!lEntsoe) { config.getEntsoeConfig(entsoe); lEntsoe = true; };
memcpy(entsoe.currency, buf+15, size-15); memcpy(entsoe.currency, buf+15, size-15);
} else if(strncmp(buf, "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).toDouble() * 1000; entsoe.multiplier = String(buf+17).toDouble() * 1000;
} else if(strncmp(buf, "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;
char * pch = strtok (buf+11," "); char * pch = strtok (buf+11," ");
while (pch != NULL) { while (pch != NULL && i < 10) {
eac.thresholds[i++] = String(pch).toInt(); eac.thresholds[i++] = String(pch).toInt();
pch = strtok (NULL, " "); pch = strtok (NULL, " ");
} }
} else if(strncmp(buf, "dayplot ", 8) == 0) { eac.hours = String(pch).toInt();
} else if(strncmp_P(buf, PSTR("dayplot "), 8) == 0) {
int i = 0; int i = 0;
DayDataPoints day = { 4 }; // Use a version we know the multiplier of the data points DayDataPoints day = { 4 }; // Use a version we know the multiplier of the data points
char * pch = strtok (buf+8," "); char * pch = strtok (buf+8," ");
@@ -1659,7 +1676,7 @@ void configFileParse() {
} }
ds.setDayData(day); ds.setDayData(day);
sDs = true; sDs = true;
} else if(strncmp(buf, "monthplot ", 10) == 0) { } else if(strncmp_P(buf, PSTR("monthplot "), 10) == 0) {
int i = 0; int i = 0;
MonthDataPoints month = { 5 }; // Use a version we know the multiplier of the data points MonthDataPoints month = { 5 }; // Use a version we know the multiplier of the data points
char * pch = strtok (buf+10," "); char * pch = strtok (buf+10," ");
@@ -1682,7 +1699,7 @@ void configFileParse() {
} }
ds.setMonthData(month); ds.setMonthData(month);
sDs = true; sDs = true;
} else if(strncmp(buf, "energyaccounting ", 17) == 0) { } else if(strncmp_P(buf, PSTR("energyaccounting "), 17) == 0) {
uint8_t i = 0; uint8_t i = 0;
EnergyAccountingData ead = { 4, 0, EnergyAccountingData ead = { 4, 0,
0, 0, 0, 0, 0, 0,
@@ -1747,6 +1764,7 @@ void configFileParse() {
if(lDomo) config.setDomoticzConfig(domo); if(lDomo) config.setDomoticzConfig(domo);
if(lNtp) config.setNtpConfig(ntp); if(lNtp) config.setNtpConfig(ntp);
if(lEntsoe) config.setEntsoeConfig(entsoe); if(lEntsoe) config.setEntsoeConfig(entsoe);
if(lEac) config.setEnergyAccountingConfig(eac);
if(sDs) ds.save(); if(sDs) ds.save();
if(sEa) ea.save(); if(sEa) ea.save();
config.save(); config.save();

View File

@@ -42,7 +42,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
if(!init) { if(!init) {
currentHour = local.Hour; currentHour = local.Hour;
currentDay = local.Day; currentDay = local.Day;
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing data at %lld\n", (int64_t) now); if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing data at %lu\n", (int32_t) now);
if(!load()) { if(!load()) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Unable to load existing data\n"); if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) Unable to load existing data\n");
data = { 4, local.Month, data = { 4, local.Month,
@@ -57,24 +57,27 @@ bool EnergyAccounting::update(AmsData* amsData) {
for(uint8_t i = 0; i < 5; i++) { for(uint8_t i = 0; i < 5; i++) {
debugger->printf("(EnergyAccounting) Peak hour from day %d: %d\n", data.peaks[i].day, data.peaks[i].value*10); debugger->printf("(EnergyAccounting) Peak hour from day %d: %d\n", data.peaks[i].day, data.peaks[i].value*10);
} }
debugger->printf("(EnergyAccounting) Loaded cost yesterday: %d, this month: %d, last month: %d\n", data.costYesterday / 10.0, data.costThisMonth, data.costLastMonth); debugger->printf("(EnergyAccounting) Loaded cost yesterday: %.2f, this month: %d, last month: %d\n", data.costYesterday / 10.0, data.costThisMonth, data.costLastMonth);
} }
init = true; init = true;
} }
if(!initPrice && eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) { if(!initPrice && eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing prices at %lld\n", (int64_t) now); if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EnergyAccounting) Initializing prices at %lu\n", (int32_t) now);
calcDayCost(); calcDayCost();
} }
if(local.Hour != currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) { if(local.Hour != currentHour && (amsData->getListType() >= 3 || local.Minute == 1)) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New local hour %d\n", local.Hour); if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EnergyAccounting) New local hour %d\n", local.Hour);
tmElements_t oneHrAgo; tmElements_t oneHrAgo, oneHrAgoLocal;
breakTime(now-3600, oneHrAgo); breakTime(now-3600, oneHrAgo);
uint16_t val = ds->getHourImport(oneHrAgo.Hour) / 10; uint16_t val = ds->getHourImport(oneHrAgo.Hour) / 10;
ret |= updateMax(val, local.Day);
breakTime(tz->toLocal(now-3600), oneHrAgoLocal);
ret |= updateMax(val, oneHrAgoLocal.Day);
currentHour = local.Hour; // Need to be defined here so that day cost is correctly calculated
if(local.Hour > 0) { if(local.Hour > 0) {
calcDayCost(); calcDayCost();
} }
@@ -143,9 +146,9 @@ void EnergyAccounting::calcDayCost() {
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) { if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
if(initPrice) costDay = 0; if(initPrice) costDay = 0;
for(int i = 0; i < currentHour; i++) { for(int i = 0; i < currentHour; i++) {
float price = eapi->getValueForHour(i - currentHour); float price = eapi->getValueForHour(i - local.Hour);
if(price == ENTSOE_NO_VALUE) break; if(price == ENTSOE_NO_VALUE) break;
breakTime(now - ((currentHour - i) * 3600), utc); breakTime(now - ((local.Hour - i) * 3600), utc);
int16_t wh = ds->getHourImport(utc.Hour); int16_t wh = ds->getHourImport(utc.Hour);
costDay += price * (wh / 1000.0); costDay += price * (wh / 1000.0);
} }
@@ -161,9 +164,10 @@ double 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; if(now < BUILD_EPOCH) return 0;
tmElements_t utc; tmElements_t utc, local;
breakTime(tz->toLocal(now), local);
for(int i = 0; i < currentHour; i++) { for(int i = 0; i < currentHour; i++) {
breakTime(now - ((currentHour - i) * 3600), utc); breakTime(now - ((local.Hour - i) * 3600), utc);
ret += ds->getHourImport(utc.Hour) / 1000.0; ret += ds->getHourImport(utc.Hour) / 1000.0;
} }
return ret + getUseThisHour(); return ret + getUseThisHour();
@@ -237,7 +241,7 @@ float EnergyAccounting::getMonthMax() {
uint32_t maxHour = 0.0; uint32_t maxHour = 0.0;
bool included[5] = { false, false, false, false, false }; bool included[5] = { false, false, false, false, false };
while(count < config->hours && count <= 5) { for(uint8_t x = 0;x < min((uint8_t) 5, config->hours); x++) {
uint8_t maxIdx = 0; uint8_t maxIdx = 0;
uint16_t maxVal = 0; uint16_t maxVal = 0;
for(uint8_t i = 0; i < 5; i++) { for(uint8_t i = 0; i < 5; i++) {
@@ -248,8 +252,10 @@ float EnergyAccounting::getMonthMax() {
maxIdx = i; maxIdx = i;
} }
} }
included[maxIdx] = true; if(maxVal > 0) {
count++; included[maxIdx] = true;
count++;
}
} }
for(uint8_t i = 0; i < 5; i++) { for(uint8_t i = 0; i < 5; i++) {
@@ -265,7 +271,7 @@ float EnergyAccounting::getPeak(uint8_t num) {
uint8_t count = 0; uint8_t count = 0;
bool included[5] = { false, false, false, false, false }; bool included[5] = { false, false, false, false, false };
while(count < config->hours && count <= 5) { for(uint8_t x = 0;x < min((uint8_t) 5, config->hours); x++) {
uint8_t maxIdx = 0; uint8_t maxIdx = 0;
uint16_t maxVal = 0; uint16_t maxVal = 0;
for(uint8_t i = 0; i < 5; i++) { for(uint8_t i = 0; i < 5; i++) {
@@ -275,8 +281,10 @@ float EnergyAccounting::getPeak(uint8_t num) {
maxIdx = i; maxIdx = i;
} }
} }
included[maxIdx] = true; if(maxVal > 0) {
count++; included[maxIdx] = true;
count++;
}
} }
uint8_t pos = 0; uint8_t pos = 0;

View File

@@ -373,8 +373,7 @@ bool HwTools::ledBlink(uint8_t color, uint8_t blink) {
if(!ledOn(color)) return false; if(!ledOn(color)) return false;
delay(50); delay(50);
ledOff(color); ledOff(color);
if(i != blink) delay(200);
delay(50);
} }
return true; return true;
} }

View File

@@ -1,16 +1,10 @@
#include "IEC6205621.h" #include "IEC6205621.h"
#include "ams/crc.h"
IEC6205621::IEC6205621(const char* p) { IEC6205621::IEC6205621(const char* p) {
if(strlen(p) < 16) if(strlen(p) < 16)
return; return;
String payload(p+1); String payload(p+1);
int crc_pos = payload.lastIndexOf("!");
String crc = payload.substring(crc_pos+1, crc_pos+5);
//uint16_t crc_calc = crc16_x25((uint8_t*) (payload.startsWith("/") ? p+1 : p), crc_pos);
//Serial.printf("CRC %s :: %04X\n", crc.c_str(), crc_calc);
lastUpdateMillis = millis(); lastUpdateMillis = millis();
listId = payload.substring(payload.startsWith("/") ? 1 : 0, payload.indexOf("\n")); listId = payload.substring(payload.startsWith("/") ? 1 : 0, payload.indexOf("\n"));
@@ -28,11 +22,14 @@ IEC6205621::IEC6205621(const char* p) {
meterType = AmsTypeIskra; meterType = AmsTypeIskra;
listId = listId.substring(0,5); listId = listId.substring(0,5);
} else if(listId.startsWith("XMX")) { } else if(listId.startsWith("XMX")) {
meterType = AmsTypeLandis; meterType = AmsTypeLandisGyr;
listId = listId.substring(0,6); listId = listId.substring(0,6);
} else if(listId.startsWith("Ene") || listId.startsWith("EST")) { } else if(listId.startsWith("Ene") || listId.startsWith("EST")) {
meterType = AmsTypeSagemcom; meterType = AmsTypeSagemcom;
listId = listId.substring(0,4); listId = listId.substring(0,4);
} else if(listId.startsWith("LGF")) {
meterType = AmsTypeLandisGyr;
listId = listId.substring(0,4);
} else { } else {
meterType = AmsTypeUnknown; meterType = AmsTypeUnknown;
listId = listId.substring(0,4); listId = listId.substring(0,4);
@@ -79,6 +76,14 @@ IEC6205621::IEC6205621(const char* p) {
l1current = extractDouble(payload, "31.7.0"); l1current = extractDouble(payload, "31.7.0");
l2current = extractDouble(payload, "51.7.0"); l2current = extractDouble(payload, "51.7.0");
l3current = extractDouble(payload, "71.7.0"); l3current = extractDouble(payload, "71.7.0");
l1activeImportPower = extractDouble(payload, "21.7.0");
l2activeImportPower = extractDouble(payload, "41.7.0");
l3activeImportPower = extractDouble(payload, "61.7.0");
l1activeExportPower = extractDouble(payload, "22.7.0");
l2activeExportPower = extractDouble(payload, "42.7.0");
l3activeExportPower = extractDouble(payload, "62.7.0");
if(l1voltage > 0 || l2voltage > 0 || l3voltage > 0) if(l1voltage > 0 || l2voltage > 0 || l3voltage > 0)
listType = 2; listType = 2;
@@ -128,6 +133,9 @@ IEC6205621::IEC6205621(const char* p) {
l2current = (((activeImportPower - activeExportPower) * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage; l2current = (((activeImportPower - activeExportPower) * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage;
} }
} }
if (l1activeImportPower > 0 || l2activeImportPower > 0 || l3activeImportPower > 0 || l1activeExportPower > 0 || l2activeExportPower > 0 || l3activeExportPower > 0)
listType = 4;
} }
String IEC6205621::extract(String payload, String obis) { String IEC6205621::extract(String payload, String obis) {

View File

@@ -98,8 +98,10 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
} }
if(listType >= 2 && memcmp(meterModel.c_str(), "MA304T3", 7) == 0) { if(listType >= 2 && memcmp(meterModel.c_str(), "MA304T3", 7) == 0) {
l2current = (((activeImportPower - activeExportPower) * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage;
l2voltage = sqrt(pow(l1voltage - l3voltage * cos(60 * (PI/180)), 2) + pow(l3voltage * sin(60 * (PI/180)),2)); l2voltage = sqrt(pow(l1voltage - l3voltage * cos(60 * (PI/180)), 2) + pow(l3voltage * sin(60 * (PI/180)),2));
if(l2voltage > 0) {
l2current = (((activeImportPower - activeExportPower) * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage;
}
} }
if(listType == 3) { if(listType == 3) {
@@ -296,6 +298,38 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
l3PowerFactor = val; l3PowerFactor = val;
} }
val = getNumber(AMS_OBIS_ACTIVE_IMPORT_L1, sizeof(AMS_OBIS_ACTIVE_IMPORT_L1), ((char *) (d)));
if (val != NOVALUE) {
listType = 4;
l1activeImportPower = val;
}
val = getNumber(AMS_OBIS_ACTIVE_IMPORT_L2, sizeof(AMS_OBIS_ACTIVE_IMPORT_L2), ((char *) (d)));
if (val != NOVALUE) {
listType = 4;
l2activeImportPower = val;
}
val = getNumber(AMS_OBIS_ACTIVE_IMPORT_L3, sizeof(AMS_OBIS_ACTIVE_IMPORT_L3), ((char *) (d)));
if (val != NOVALUE) {
listType = 4;
l3activeImportPower = val;
}
val = getNumber(AMS_OBIS_ACTIVE_EXPORT_L1, sizeof(AMS_OBIS_ACTIVE_EXPORT_L1), ((char *) (d)));
if (val != NOVALUE) {
listType = 4;
l1activeExportPower = val;
}
val = getNumber(AMS_OBIS_ACTIVE_EXPORT_L2, sizeof(AMS_OBIS_ACTIVE_EXPORT_L2), ((char *) (d)));
if (val != NOVALUE) {
listType = 4;
l2activeExportPower = val;
}
val = getNumber(AMS_OBIS_ACTIVE_EXPORT_L3, sizeof(AMS_OBIS_ACTIVE_EXPORT_L3), ((char *) (d)));
if (val != NOVALUE) {
listType = 4;
l3activeExportPower = val;
}
if(meterType == AmsTypeKamstrup) { if(meterType == AmsTypeKamstrup) {
if(listType >= 3) { if(listType >= 3) {
activeImportCounter *= 10; activeImportCounter *= 10;
@@ -336,12 +370,14 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
if(mid != NULL) { if(mid != NULL) {
switch(mid->base.type) { switch(mid->base.type) {
case CosemTypeString: case CosemTypeString:
memcpy(&meterId, mid->str.data, mid->str.length); memcpy(str, mid->oct.data, mid->oct.length);
meterId[mid->str.length] = 0; str[mid->oct.length] = 0x00;
meterId = String(str);
break; break;
case CosemTypeOctetString: case CosemTypeOctetString:
memcpy(&meterId, mid->oct.data, mid->oct.length); memcpy(str, mid->str.data, mid->str.length);
meterId[mid->oct.length] = 0; str[mid->str.length] = 0x00;
meterId = String(str);
break; break;
} }
} }

View File

@@ -49,6 +49,12 @@ private:
uint8_t AMS_OBIS_POWER_FACTOR_L1[4] = { 33, 7, 0, 255 }; uint8_t AMS_OBIS_POWER_FACTOR_L1[4] = { 33, 7, 0, 255 };
uint8_t AMS_OBIS_POWER_FACTOR_L2[4] = { 53, 7, 0, 255 }; uint8_t AMS_OBIS_POWER_FACTOR_L2[4] = { 53, 7, 0, 255 };
uint8_t AMS_OBIS_POWER_FACTOR_L3[4] = { 73, 7, 0, 255 }; uint8_t AMS_OBIS_POWER_FACTOR_L3[4] = { 73, 7, 0, 255 };
uint8_t AMS_OBIS_ACTIVE_IMPORT_L1[4] = { 21, 7, 0, 255 };
uint8_t AMS_OBIS_ACTIVE_IMPORT_L2[4] = { 41, 7, 0, 255 };
uint8_t AMS_OBIS_ACTIVE_IMPORT_L3[4] = { 61, 7, 0, 255 };
uint8_t AMS_OBIS_ACTIVE_EXPORT_L1[4] = { 22, 7, 0, 255 };
uint8_t AMS_OBIS_ACTIVE_EXPORT_L2[4] = { 42, 7, 0, 255 };
uint8_t AMS_OBIS_ACTIVE_EXPORT_L3[4] = { 62, 7, 0, 255 };
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
#include "LNG.h" #include "LNG.h"
#include "lwip/def.h" #include "lwip/def.h"
#include "ams/Cosem.h" #include "ams/ntohll.h"
LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger) { LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger) {
LngHeader* h = (LngHeader*) payload; LngHeader* h = (LngHeader*) payload;
@@ -11,9 +11,10 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
uint8_t* ptr = (uint8_t*) &h[1]; uint8_t* ptr = (uint8_t*) &h[1];
uint8_t* data = ptr + (18*h->arrayLength); // Skip descriptors uint8_t* data = ptr + (18*h->arrayLength); // Skip descriptors
uint16_t o170 = 0, o270 = 0; uint64_t o170 = 0, o270 = 0;
uint16_t o181 = 0, o182 = 0; uint64_t o180 = 0, o280 = 0;
uint16_t o281 = 0, o282 = 0; uint64_t o181 = 0, o182 = 0;
uint64_t o281 = 0, o282 = 0;
LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr; LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr;
for(uint8_t x = 0; x < h->arrayLength-1; x++) { for(uint8_t x = 0; x < h->arrayLength-1; x++) {
ptr = (uint8_t*) &descriptor[1]; ptr = (uint8_t*) &descriptor[1];
@@ -24,39 +25,41 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
if(descriptor->obis[2] == 1) { if(descriptor->obis[2] == 1) {
if(descriptor->obis[3] == 7) { if(descriptor->obis[3] == 7) {
if(descriptor->obis[4] == 0) { if(descriptor->obis[4] == 0) {
o170 = ntohl(item->dlu.data); o170 = getNumber(item);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o170);
} }
} else if(descriptor->obis[3] == 8) { } else if(descriptor->obis[3] == 8) {
if(descriptor->obis[4] == 0) { if(descriptor->obis[4] == 0) {
activeImportCounter = ntohl(item->dlu.data) / 1000.0; o180 = getNumber(item);
listType = listType >= 3 ? listType : 3; listType = listType >= 3 ? listType : 3;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o180);
activeImportCounter = o180 / 1000.0;
} else if(descriptor->obis[4] == 1) { } else if(descriptor->obis[4] == 1) {
o181 = ntohl(item->dlu.data); o181 = getNumber(item);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o181);
} else if(descriptor->obis[4] == 2) { } else if(descriptor->obis[4] == 2) {
o182 = ntohl(item->dlu.data); o182 = getNumber(item);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o182);
} }
} }
} else if(descriptor->obis[2] == 2) { } else if(descriptor->obis[2] == 2) {
if(descriptor->obis[3] == 7) { if(descriptor->obis[3] == 7) {
if(descriptor->obis[4] == 0) { if(descriptor->obis[4] == 0) {
o270 = ntohl(item->dlu.data); o270 = getNumber(item);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o270);
} }
} else if(descriptor->obis[3] == 8) { } else if(descriptor->obis[3] == 8) {
if(descriptor->obis[4] == 0) { if(descriptor->obis[4] == 0) {
activeExportCounter = ntohl(item->dlu.data) / 1000.0; o280 = getNumber(item);
listType = listType >= 3 ? listType : 3; listType = listType >= 3 ? listType : 3;
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o280);
activeExportCounter = o280 / 1000.0;
} else if(descriptor->obis[4] == 1) { } else if(descriptor->obis[4] == 1) {
o281 = ntohl(item->dlu.data); o281 = getNumber(item);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o281);
} else if(descriptor->obis[4] == 2) { } else if(descriptor->obis[4] == 2) {
o282 = ntohl(item->dlu.data); o282 = getNumber(item);
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o282);
} }
} }
} else if(descriptor->obis[2] == 96) { } else if(descriptor->obis[2] == 96) {
@@ -101,11 +104,49 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
if((*data) == 0x09) { if((*data) == 0x09) {
data += (*(data+1))+2; data += (*(data+1))+2;
} else { } else if((*data) == 0x15) {
data += 9;
} else if((*data) == 0x06) {
data += 5; data += 5;
} else if((*data) == 0x12) {
data += 3;
} }
lastUpdateMillis = millis(); lastUpdateMillis = millis();
} }
} }
} }
uint64_t LNG::getNumber(CosemData* item) {
if(item != NULL) {
uint64_t ret = 0.0;
switch(item->base.type) {
case CosemTypeLongSigned: {
int16_t i16 = ntohs(item->ls.data);
return i16;
}
case CosemTypeLongUnsigned: {
uint16_t u16 = ntohs(item->lu.data);
return u16;
}
case CosemTypeDLongSigned: {
int32_t i32 = ntohl(item->dlu.data);
return i32;
}
case CosemTypeDLongUnsigned: {
uint32_t u32 = ntohl(item->dlu.data);
return u32;
}
case CosemTypeLong64Signed: {
int64_t i64 = ntohll(item->l64s.data);
return i64;
}
case CosemTypeLong64Unsigned: {
uint64_t u64 = ntohll(item->l64u.data);
return u64;
}
}
return ret;
}
return 0.0;
}

View File

@@ -4,6 +4,7 @@
#include "AmsData.h" #include "AmsData.h"
#include "AmsConfiguration.h" #include "AmsConfiguration.h"
#include "ams/DataParser.h" #include "ams/DataParser.h"
#include "ams/Cosem.h"
#include "RemoteDebug.h" #include "RemoteDebug.h"
struct LngHeader { struct LngHeader {
@@ -25,6 +26,7 @@ struct LngObisDescriptor {
class LNG : public AmsData { class LNG : public AmsData {
public: public:
LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger); LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger);
uint64_t getNumber(CosemData* item);
}; };
#endif #endif

View File

@@ -1,5 +1,8 @@
#include "Uptime.h" #include "Uptime.h"
uint32_t _uptime_last_value = 0;
uint32_t _uptime_rollovers = 0;
uint64_t millis64() { uint64_t millis64() {
uint32_t new_low32 = millis(); uint32_t new_low32 = millis();
if (new_low32 < _uptime_last_value) _uptime_rollovers++; if (new_low32 < _uptime_last_value) _uptime_rollovers++;

View File

@@ -3,8 +3,6 @@
#include "Arduino.h" #include "Arduino.h"
static uint32_t _uptime_last_value = 0;
static uint32_t _uptime_rollovers = 0;
uint64_t millis64(); uint64_t millis64();
#endif #endif

View File

@@ -1,10 +1,12 @@
#include "DsmrParser.h" #include "DsmrParser.h"
#include "crc.h"
#include "hexutils.h"
#include "lwip/def.h"
int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) { int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) {
uint16_t crcPos = 0; uint16_t crcPos = 0;
bool reachedEnd = verified; bool reachedEnd = verified;
uint8_t lastByte = 0x00; uint8_t lastByte = 0x00;
int c = 0;
for(int pos = 0; pos < ctx.length; pos++) { for(int pos = 0; pos < ctx.length; pos++) {
uint8_t b = *(buf+pos); uint8_t b = *(buf+pos);
if(pos == 0 && b != '/') return DATA_PARSE_BOUNDRY_FLAG_MISSING; if(pos == 0 && b != '/') return DATA_PARSE_BOUNDRY_FLAG_MISSING;
@@ -15,8 +17,13 @@ int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) {
if(!reachedEnd) return DATA_PARSE_INCOMPLETE; if(!reachedEnd) return DATA_PARSE_INCOMPLETE;
buf[ctx.length+1] = '\0'; buf[ctx.length+1] = '\0';
if(crcPos > 0) { if(crcPos > 0) {
// TODO: CRC uint16_t crc_calc = crc16(buf, crcPos);
Serial.printf("CRC: %s\n", buf+crcPos); uint16_t crc = 0x0000;
fromHex((uint8_t*) &crc, String((char*) buf+crcPos), 2);
crc = ntohs(crc);
if(crc != crc_calc)
return DATA_PARSE_FOOTER_CHECKSUM_ERROR;
} }
return DATA_PARSE_OK; return DATA_PARSE_OK;
} }

View File

@@ -27,7 +27,7 @@ int8_t GCMParser::parse(uint8_t *d, DataParserContext &ctx) {
memcpy(ctx.system_title, ptr, systemTitleLength); memcpy(ctx.system_title, ptr, systemTitleLength);
memcpy(initialization_vector, ctx.system_title, systemTitleLength); memcpy(initialization_vector, ctx.system_title, systemTitleLength);
int len; int len = 0;
int headersize = 2 + systemTitleLength; int headersize = 2 + systemTitleLength;
ptr += systemTitleLength; ptr += systemTitleLength;
if(((*ptr) & 0xFF) == 0x81) { if(((*ptr) & 0xFF) == 0x81) {

View File

@@ -5,8 +5,6 @@
int8_t HDLCParser::parse(uint8_t *d, DataParserContext &ctx) { int8_t HDLCParser::parse(uint8_t *d, DataParserContext &ctx) {
int len; int len;
uint8_t flag = *d;
uint8_t* ptr; uint8_t* ptr;
if(ctx.length < 3) if(ctx.length < 3)
return DATA_PARSE_INCOMPLETE; return DATA_PARSE_INCOMPLETE;

View File

@@ -1,7 +1,6 @@
#include "LlcParser.h" #include "LlcParser.h"
int8_t LLCParser::parse(uint8_t *buf, DataParserContext &ctx) { int8_t LLCParser::parse(uint8_t *buf, DataParserContext &ctx) {
LLCHeader* llc = (LLCHeader*) buf;
ctx.length -= 3; ctx.length -= 3;
return 3; return 3;
} }

View File

@@ -5,8 +5,6 @@ int8_t MBUSParser::parse(uint8_t *d, DataParserContext &ctx) {
int headersize = 3; int headersize = 3;
int footersize = 1; int footersize = 1;
uint8_t flag = *d;
uint8_t* ptr; uint8_t* ptr;
// https://m-bus.com/documentation-wired/06-application-layer // https://m-bus.com/documentation-wired/06-application-layer

View File

@@ -10,3 +10,20 @@ uint16_t crc16_x25(const uint8_t* p, int len)
return (~crc << 8) | (~crc >> 8 & 0xff); return (~crc << 8) | (~crc >> 8 & 0xff);
} }
uint16_t crc16 (const uint8_t *p, int len) {
uint16_t crc = 0;
while (len--) {
int i;
crc ^= *p++;
for (i = 0 ; i < 8 ; ++i) {
if (crc & 1)
crc = (crc >> 1) ^ 0xa001;
else
crc = (crc >> 1);
}
}
return crc;
}

View File

@@ -4,6 +4,7 @@
#include "Arduino.h" #include "Arduino.h"
#include <stdint.h> #include <stdint.h>
uint16_t crc16(const uint8_t* p, int len);
uint16_t crc16_x25(const uint8_t* p, int len); uint16_t crc16_x25(const uint8_t* p, int len);
#endif #endif

View File

@@ -22,7 +22,7 @@ void DnbCurrParser::flush() {
} }
size_t DnbCurrParser::write(const uint8_t *buffer, size_t size) { size_t DnbCurrParser::write(const uint8_t *buffer, size_t size) {
for(int i = 0; i < size; i++) { for(size_t i = 0; i < size; i++) {
write(buffer[i]); write(buffer[i]);
} }
return size; return size;

View File

@@ -5,6 +5,10 @@ EntsoeA44Parser::EntsoeA44Parser() {
for(int i = 0; i < 24; i++) points[i] = ENTSOE_NO_VALUE; for(int i = 0; i < 24; i++) points[i] = ENTSOE_NO_VALUE;
} }
EntsoeA44Parser::~EntsoeA44Parser() {
}
char* EntsoeA44Parser::getCurrency() { char* EntsoeA44Parser::getCurrency() {
return currency; return currency;
} }
@@ -35,7 +39,7 @@ void EntsoeA44Parser::flush() {
} }
size_t EntsoeA44Parser::write(const uint8_t *buffer, size_t size) { size_t EntsoeA44Parser::write(const uint8_t *buffer, size_t size) {
for(int i = 0; i < size; i++) { for(size_t i = 0; i < size; i++) {
write(buffer[i]); write(buffer[i]);
} }
return size; return size;

View File

@@ -14,6 +14,7 @@
class EntsoeA44Parser: public Stream { class EntsoeA44Parser: public Stream {
public: public:
EntsoeA44Parser(); EntsoeA44Parser();
virtual ~EntsoeA44Parser();
char* getCurrency(); char* getCurrency();
char* getMeasurementUnit(); char* getMeasurementUnit();

View File

@@ -106,7 +106,7 @@ bool EntsoeApi::loop() {
if(midnightMillis == 0) { if(midnightMillis == 0) {
uint32_t curDayMillis = (((((tm.Hour * 60) + tm.Minute) * 60) + tm.Second) * 1000); uint32_t curDayMillis = (((((tm.Hour * 60) + tm.Minute) * 60) + tm.Second) * 1000);
midnightMillis = now + (SECS_PER_DAY * 1000) - curDayMillis; midnightMillis = now + (SECS_PER_DAY * 1000) - curDayMillis;
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Setting midnight millis %lu\n", midnightMillis); if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Setting midnight millis %llu\n", midnightMillis);
currentDay = tm.Day; currentDay = tm.Day;
return false; return false;
} else if(now > midnightMillis && currentDay != tm.Day) { } else if(now > midnightMillis && currentDay != tm.Day) {

View File

@@ -15,6 +15,7 @@ public:
this->mqtt = mqtt; this->mqtt = mqtt;
this->json = buf; this->json = buf;
}; };
virtual ~AmsMqttHandler() {};
virtual bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea); virtual bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
virtual bool publishTemperatures(AmsConfiguration*, HwTools*); virtual bool publishTemperatures(AmsConfiguration*, HwTools*);

View File

@@ -6,6 +6,7 @@
#include "web/root/ha1_json.h" #include "web/root/ha1_json.h"
#include "web/root/ha2_json.h" #include "web/root/ha2_json.h"
#include "web/root/ha3_json.h" #include "web/root/ha3_json.h"
#include "web/root/ha4_json.h"
#include "web/root/jsonsys_json.h" #include "web/root/jsonsys_json.h"
#include "web/root/jsonprices_json.h" #include "web/root/jsonprices_json.h"
#include "web/root/hadiscover_json.h" #include "web/root/hadiscover_json.h"
@@ -33,8 +34,8 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
snprintf_P(json, BufferSize, HA1_JSON, snprintf_P(json, BufferSize, HA1_JSON,
data->getActiveImportPower() data->getActiveImportPower()
); );
return mqtt->publish(topic + "/power", json); mqtt->publish(topic + "/power", json);
} else if(data->getListType() >= 2) { // publish power counts and volts/amps } else if(data->getListType() <= 3) { // publish power counts and volts/amps
snprintf_P(json, BufferSize, HA3_JSON, snprintf_P(json, BufferSize, HA3_JSON,
data->getListId().c_str(), data->getListId().c_str(),
data->getMeterId().c_str(), data->getMeterId().c_str(),
@@ -48,15 +49,63 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
data->getL3Current(), data->getL3Current(),
data->getL1Voltage(), data->getL1Voltage(),
data->getL2Voltage(), data->getL2Voltage(),
data->getL3Voltage()
);
mqtt->publish(topic + "/power", json);
} else if(data->getListType() == 4) { // publish power counts and volts/amps/phase power and PF
snprintf_P(json, BufferSize, HA4_JSON,
data->getListId().c_str(),
data->getMeterId().c_str(),
meterModel.c_str(),
data->getActiveImportPower(),
data->getL1ActiveImportPower(),
data->getL2ActiveImportPower(),
data->getL3ActiveImportPower(),
data->getReactiveImportPower(),
data->getActiveExportPower(),
data->getL1ActiveExportPower(),
data->getL2ActiveExportPower(),
data->getL3ActiveExportPower(),
data->getReactiveExportPower(),
data->getL1Current(),
data->getL2Current(),
data->getL3Current(),
data->getL1Voltage(),
data->getL2Voltage(),
data->getL3Voltage(), data->getL3Voltage(),
data->getPowerFactor() == 0 ? 1 : data->getPowerFactor(), data->getPowerFactor() == 0 ? 1 : data->getPowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL1PowerFactor(), data->getPowerFactor() == 0 ? 1 : data->getL1PowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL2PowerFactor(), data->getPowerFactor() == 0 ? 1 : data->getL2PowerFactor(),
data->getPowerFactor() == 0 ? 1 : data->getL3PowerFactor() data->getPowerFactor() == 0 ? 1 : data->getL3PowerFactor()
); );
return mqtt->publish(topic + "/power", json); mqtt->publish(topic + "/power", json);
} }
return false;}
String peaks = "";
uint8_t peakCount = ea->getConfig()->hours;
if(peakCount > 5) peakCount = 5;
for(uint8_t i = 1; i <= peakCount; i++) {
if(!peaks.isEmpty()) peaks += ",";
peaks += String(ea->getPeak(i), 2);
}
snprintf_P(json, BufferSize, REALTIME_JSON,
ea->getMonthMax(),
peaks.c_str(),
ea->getCurrentThreshold(),
ea->getUseThisHour(),
ea->getCostThisHour(),
ea->getProducedThisHour(),
ea->getUseToday(),
ea->getCostToday(),
ea->getProducedToday(),
ea->getUseThisMonth(),
ea->getCostThisMonth(),
ea->getProducedThisMonth()
);
mqtt->publish(topic + "/realtime", json);
return true;
}
bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) { bool HomeAssistantMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
int count = hw->getTempSensorCount(); int count = hw->getTempSensorCount();
@@ -92,7 +141,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
time_t now = time(nullptr); time_t now = time(nullptr);
float min1hr, min3hr, min6hr; float min1hr = 0.0, min3hr = 0.0, min6hr = 0.0;
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1; int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
float min = INT16_MAX, max = INT16_MIN; float min = INT16_MAX, max = INT16_MIN;
float values[24]; float values[24];
@@ -142,7 +191,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
} }
char ts1hr[21]; char ts1hr[24];
if(min1hrIdx > -1) { if(min1hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min1hrIdx); time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts); //Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
@@ -150,7 +199,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
breakTime(ts, tm); breakTime(ts, tm);
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour); sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
} }
char ts3hr[21]; char ts3hr[24];
if(min3hrIdx > -1) { if(min3hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min3hrIdx); time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts); //Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
@@ -158,7 +207,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
breakTime(ts, tm); breakTime(ts, tm);
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour); sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
} }
char ts6hr[21]; char ts6hr[24];
if(min6hrIdx > -1) { if(min6hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min6hrIdx); time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts); //Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
@@ -187,7 +236,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
ts3hr, ts3hr,
ts6hr ts6hr
); );
return mqtt->publish(topic + "/prices", json); return mqtt->publish(topic + "/prices", json, true, 0);
} }
bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) { bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, EnergyAccounting* ea) {
@@ -217,28 +266,48 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, Energ
#endif #endif
String haUrl = "http://" + haUID + ".local/"; String haUrl = "http://" + haUID + ".local/";
// Could this be necessary? haUID.replace("-", "_"); // Could this be necessary? haUID.replace("-", "_");
uint8_t peakCount = ea->getConfig()->hours;
if(peakCount > 5) peakCount = 5;
for(int i=0;i<17;i++){ uint8_t peaks = 0;
for(int i=0;i<HA_SENSOR_COUNT;i++) {
HomeAssistantSensor sensor = HA_SENSORS[i];
String uid = String(sensor.path);
uid.replace(".", "");
uid.replace("[", "");
uid.replace("]", "");
uid.replace("'", "");
String uom = String(sensor.uom);
if(strncmp(sensor.devcl, "monetary", 8) == 0) {
if(eapi == NULL) continue;
uom = String(eapi->getCurrency());
}
if(strncmp(sensor.path, "peaks[", 6) == 0) {
if(peaks >= peakCount) continue;
peaks++;
}
snprintf_P(json, BufferSize, HADISCOVER_JSON, snprintf_P(json, BufferSize, HADISCOVER_JSON,
FPSTR(HA_NAMES[i]), sensor.name,
topic.c_str(), FPSTR(HA_TOPICS[i]), topic.c_str(), sensor.topic,
haUID.c_str(), FPSTR(HA_PARAMS[i]), haUID.c_str(), uid.c_str(),
haUID.c_str(), FPSTR(HA_PARAMS[i]), haUID.c_str(), uid.c_str(),
FPSTR(HA_UOM[i]), uom.c_str(),
FPSTR(HA_PARAMS[i]), sensor.path,
FPSTR(HA_DEVCL[i]), sensor.devcl,
haUID.c_str(), haUID.c_str(),
haName.c_str(), haName.c_str(),
haModel.c_str(), haModel.c_str(),
VERSION, VERSION,
haManuf.c_str(), haManuf.c_str(),
haUrl.c_str(), haUrl.c_str(),
strlen_P(HA_STACL[i]) > 0 ? ", \"stat_cla\" :" : "", strlen_P(sensor.stacl) > 0 ? ", \"stat_cla\" :" : "",
strlen_P(HA_STACL[i]) > 0 ? (char *) FPSTR(HA_STACL[i]) : "" strlen_P(sensor.stacl) > 0 ? (char *) FPSTR(sensor.stacl) : ""
); );
mqtt->publish(haTopic + haUID + "_" + FPSTR(HA_PARAMS[i]) + "/config", json, true, 0); mqtt->publish(haTopic + haUID + "_" + uid.c_str() + "/config", json, true, 0);
} }
autodiscoverInit = true; autodiscoverInit = true;
} }
if(listType>0) sequence++; if(listType>0) sequence++;
return true;} return true;
}

View File

@@ -3,12 +3,79 @@
#include "Arduino.h" #include "Arduino.h"
const char* HA_TOPICS[17] PROGMEM = {"/state", "/state", "/state", "/power", "/power", "/power", "/power", "/power", "/power", "/power", "/power", "/power", "/power", "/energy", "/energy", "/energy", "/energy"}; struct HomeAssistantSensor {
const char* HA_NAMES[17] PROGMEM = {"Status", "Supply volt", "Temperature", "Active import", "Reactive import", "Active export", "Reactive export", "L1 current", "L2 current", "L3 current", const char* name;
"L1 voltage", "L2 voltage", "L3 voltage", "Accumulated active import", "Accumulated active export", "Accumulated reactive import", "Accumulated reactive export"}; const char* topic;
const char* HA_PARAMS[17] PROGMEM = {"rssi", "vcc", "temp", "P", "Q", "PO", "QO", "I1", "I2", "I3", "U1", "U2", "U3", "tPI", "tPO", "tQI", "tQO"}; const char* path;
const char* HA_UOM[17] PROGMEM = {"dBm", "V", "C", "W", "W", "W", "W", "A", "A", "A", "V", "V", "V", "kWh", "kWh", "kWh", "kWh"}; const char* uom;
const char* HA_DEVCL[17] PROGMEM = {"signal_strength", "voltage", "temperature", "power", "power", "power", "power", "current", "current", "current", "voltage", "voltage", "voltage", "energy", "energy", "energy", "energy"}; const char* devcl;
const char* HA_STACL[17] PROGMEM = {"", "", "", "\"measurement\"", "\"measurement\"", "\"measurement\"", "\"measurement\"", "", "", "", "", "", "", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\"", "\"total_increasing\""}; const char* stacl;
};
#endif
const uint8_t HA_SENSOR_COUNT PROGMEM = 60;
HomeAssistantSensor HA_SENSORS[HA_SENSOR_COUNT] PROGMEM = {
{"Status", "/state", "rssi", "dBm", "signal_strength", "\"measurement\""},
{"Supply volt", "/state", "vcc", "V", "voltage", "\"measurement\""},
{"Temperature", "/state", "temp", "C", "temperature", "\"measurement\""},
{"Active import", "/power", "P", "W", "power", "\"measurement\""},
{"L1 active import", "/power", "P1", "W", "power", "\"measurement\""},
{"L2 active import", "/power", "P2", "W", "power", "\"measurement\""},
{"L3 active import", "/power", "P3", "W", "power", "\"measurement\""},
{"Reactive import", "/power", "Q", "VAr", "reactive_power", "\"measurement\""},
{"Active export", "/power", "PO", "W", "power", "\"measurement\""},
{"L1 active export", "/power", "PO1", "W", "power", "\"measurement\""},
{"L2 active export", "/power", "PO2", "W", "power", "\"measurement\""},
{"L3 active export", "/power", "PO3", "W", "power", "\"measurement\""},
{"Reactive export", "/power", "QO", "VAr", "reactive_power", "\"measurement\""},
{"L1 current", "/power", "I1", "A", "current", "\"measurement\""},
{"L2 current", "/power", "I2", "A", "current", "\"measurement\""},
{"L3 current", "/power", "I3", "A", "current", "\"measurement\""},
{"L1 voltage", "/power", "U1", "V", "voltage", "\"measurement\""},
{"L2 voltage", "/power", "U2", "V", "voltage", "\"measurement\""},
{"L3 voltage", "/power", "U3", "V", "voltage", "\"measurement\""},
{"Accumulated active import", "/energy", "tPI", "kWh", "energy", "\"total_increasing\""},
{"Accumulated active export", "/energy", "tPO", "kWh", "energy", "\"total_increasing\""},
{"Accumulated reactive import","/energy", "tQI", "kVArh","energy", "\"total_increasing\""},
{"Accumulated reactive export","/energy", "tQO", "kVArh","energy", "\"total_increasing\""},
{"Power factor", "/power", "PF", "", "power_factor", "\"measurement\""},
{"L1 power factor", "/power", "PF1", "", "power_factor", "\"measurement\""},
{"L2 power factor", "/power", "PF2", "", "power_factor", "\"measurement\""},
{"L3 power factor", "/power", "PF3", "", "power_factor", "\"measurement\""},
{"Price current hour", "/prices", "prices['0']", "", "monetary", ""},
{"Price next hour", "/prices", "prices['1']", "", "monetary", ""},
{"Price in two hour", "/prices", "prices['2']", "", "monetary", ""},
{"Price in three hour", "/prices", "prices['3']", "", "monetary", ""},
{"Price in four hour", "/prices", "prices['4']", "", "monetary", ""},
{"Price in five hour", "/prices", "prices['5']", "", "monetary", ""},
{"Price in six hour", "/prices", "prices['6']", "", "monetary", ""},
{"Price in seven hour", "/prices", "prices['7']", "", "monetary", ""},
{"Price in eight hour", "/prices", "prices['8']", "", "monetary", ""},
{"Price in nine hour", "/prices", "prices['9']", "", "monetary", ""},
{"Price in ten hour", "/prices", "prices['10']", "", "monetary", ""},
{"Price in eleven hour", "/prices", "prices['11']", "", "monetary", ""},
{"Minimum price ahead", "/prices", "prices.min", "", "monetary", ""},
{"Maximum price ahead", "/prices", "prices.max", "", "monetary", ""},
{"Cheapest 1hr period ahead", "/prices", "prices.cheapest1hr","", "timestamp", ""},
{"Cheapest 3hr period ahead", "/prices", "prices.cheapest3hr","", "timestamp", ""},
{"Cheapest 6hr period ahead", "/prices", "prices.cheapest6hr","", "timestamp", ""},
{"Month max", "/realtime","max", "kWh", "energy", "\"total_increasing\""},
{"Tariff threshold", "/realtime","threshold", "kWh", "energy", "\"total_increasing\""},
{"Current hour used", "/realtime","hour.use", "kWh", "energy", "\"total_increasing\""},
{"Current hour cost", "/realtime","hour.cost", "", "monetary", "\"total_increasing\""},
{"Current hour produced", "/realtime","hour.produced", "kWh", "energy", "\"total_increasing\""},
{"Current day used", "/realtime","day.use", "kWh", "energy", "\"total_increasing\""},
{"Current day cost", "/realtime","day.cost", "", "monetary", "\"total_increasing\""},
{"Current day produced", "/realtime","day.produced", "kWh", "energy", "\"total_increasing\""},
{"Current month used", "/realtime","month.use", "kWh", "energy", "\"total_increasing\""},
{"Current month cost", "/realtime","month.cost", "", "monetary", "\"total_increasing\""},
{"Current month produced", "/realtime","month.produced", "kWh", "energy", "\"total_increasing\""},
{"Current month peak 1", "/realtime","peaks[0]", "kWh", "energy", ""},
{"Current month peak 2", "/realtime","peaks[1]", "kWh", "energy", ""},
{"Current month peak 3", "/realtime","peaks[2]", "kWh", "energy", ""},
{"Current month peak 4", "/realtime","peaks[3]", "kWh", "energy", ""},
{"Current month peak 5", "/realtime","peaks[4]", "kWh", "energy", ""},
};
#endif

View File

@@ -111,8 +111,14 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
data->getMeterId().c_str(), data->getMeterId().c_str(),
meterModel.c_str(), meterModel.c_str(),
data->getActiveImportPower(), data->getActiveImportPower(),
data->getL1ActiveImportPower(),
data->getL2ActiveImportPower(),
data->getL3ActiveImportPower(),
data->getReactiveImportPower(), data->getReactiveImportPower(),
data->getActiveExportPower(), data->getActiveExportPower(),
data->getL1ActiveExportPower(),
data->getL2ActiveExportPower(),
data->getL3ActiveExportPower(),
data->getReactiveExportPower(), data->getReactiveExportPower(),
data->getL1Current(), data->getL1Current(),
data->getL2Current(), data->getL2Current(),
@@ -143,8 +149,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) { bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
int count = hw->getTempSensorCount(); int count = hw->getTempSensorCount();
if(count < 2) if(count < 2) {
return false; return false;
}
snprintf(json, 24, "{\"temperatures\":{"); snprintf(json, 24, "{\"temperatures\":{");
@@ -173,7 +180,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
time_t now = time(nullptr); time_t now = time(nullptr);
float min1hr, min3hr, min6hr; float min1hr = 0.0, min3hr = 0.0, min6hr = 0.0;
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1; int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
float min = INT16_MAX, max = INT16_MIN; float min = INT16_MAX, max = INT16_MIN;
float values[24]; float values[24];
@@ -223,7 +230,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
} }
char ts1hr[21]; char ts1hr[24];
if(min1hrIdx > -1) { if(min1hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min1hrIdx); time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts); //Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
@@ -231,7 +238,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
breakTime(ts, tm); breakTime(ts, tm);
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour); sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
} }
char ts3hr[21]; char ts3hr[24];
if(min3hrIdx > -1) { if(min3hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min3hrIdx); time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts); //Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
@@ -239,7 +246,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
breakTime(ts, tm); breakTime(ts, tm);
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour); sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
} }
char ts6hr[21]; char ts6hr[24];
if(min6hrIdx > -1) { if(min6hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min6hrIdx); time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts); //Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);

View File

@@ -11,6 +11,24 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
} }
switch(data->getListType()) { switch(data->getListType()) {
case 4: case 4:
if(full || meterState->getL1ActiveImportPower() != data->getL1ActiveImportPower()) {
mqtt->publish(topic + "/meter/import/l1", String(data->getL1ActiveImportPower(), 2));
}
if(full || meterState->getL2ActiveImportPower() != data->getL2ActiveImportPower()) {
mqtt->publish(topic + "/meter/import/l2", String(data->getL2ActiveImportPower(), 2));
}
if(full || meterState->getL3ActiveImportPower() != data->getL3ActiveImportPower()) {
mqtt->publish(topic + "/meter/import/l3", String(data->getL3ActiveImportPower(), 2));
}
if(full || meterState->getL1ActiveExportPower() != data->getL1ActiveExportPower()) {
mqtt->publish(topic + "/meter/export/l1", String(data->getL1ActiveExportPower(), 2));
}
if(full || meterState->getL2ActiveExportPower() != data->getL2ActiveExportPower()) {
mqtt->publish(topic + "/meter/export/l2", String(data->getL2ActiveExportPower(), 2));
}
if(full || meterState->getL3ActiveExportPower() != data->getL3ActiveExportPower()) {
mqtt->publish(topic + "/meter/export/l3", String(data->getL3ActiveExportPower(), 2));
}
if(full || meterState->getPowerFactor() != data->getPowerFactor()) { if(full || meterState->getPowerFactor() != data->getPowerFactor()) {
mqtt->publish(topic + "/meter/powerfactor", String(data->getPowerFactor(), 2)); mqtt->publish(topic + "/meter/powerfactor", String(data->getPowerFactor(), 2));
} }
@@ -74,7 +92,9 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
} }
mqtt->publish(topic + "/realtime/import/hour", String(ea->getUseThisHour(), 3)); mqtt->publish(topic + "/realtime/import/hour", String(ea->getUseThisHour(), 3));
mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2)); mqtt->publish(topic + "/realtime/import/day", String(ea->getUseToday(), 2));
for(uint8_t i = 1; i <= ea->getConfig()->hours; i++) { uint8_t peakCount = ea->getConfig()->hours;
if(peakCount > 5) peakCount = 5;
for(uint8_t i = 1; i <= peakCount; i++) {
mqtt->publish(topic + "/realtime/import/peak/" + String(i, 10), String(ea->getPeak(i), 10), true, 0); mqtt->publish(topic + "/realtime/import/peak/" + String(i, 10), String(ea->getPeak(i), 10), true, 0);
} }
mqtt->publish(topic + "/realtime/import/threshold", String(ea->getCurrentThreshold(), 10), true, 0); mqtt->publish(topic + "/realtime/import/threshold", String(ea->getCurrentThreshold(), 10), true, 0);
@@ -106,7 +126,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
time_t now = time(nullptr); time_t now = time(nullptr);
float min1hr, min3hr, min6hr; float min1hr = 0.0, min3hr = 0.0, min6hr = 0.0;
int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1; int8_t min1hrIdx = -1, min3hrIdx = -1, min6hrIdx = -1;
float min = INT16_MAX, max = INT16_MIN; float min = INT16_MAX, max = INT16_MIN;
float values[34]; float values[34];
@@ -157,7 +177,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
} }
char ts1hr[21]; char ts1hr[24];
if(min1hrIdx > -1) { if(min1hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min1hrIdx); time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts); //Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
@@ -165,7 +185,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
breakTime(ts, tm); breakTime(ts, tm);
sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour); sprintf(ts1hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
} }
char ts3hr[21]; char ts3hr[24];
if(min3hrIdx > -1) { if(min3hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min3hrIdx); time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts); //Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
@@ -173,7 +193,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
breakTime(ts, tm); breakTime(ts, tm);
sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour); sprintf(ts3hr, "%04d-%02d-%02dT%02d:00:00Z", tm.Year+1970, tm.Month, tm.Day, tm.Hour);
} }
char ts6hr[21]; char ts6hr[24];
if(min6hrIdx > -1) { if(min6hrIdx > -1) {
time_t ts = now + (SECS_PER_HOUR * min6hrIdx); time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts); //Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);

View File

@@ -2,6 +2,7 @@ static const char HEADER_CACHE_CONTROL[] PROGMEM = "Cache-Control";
static const char HEADER_PRAGMA[] PROGMEM = "Pragma"; static const char HEADER_PRAGMA[] PROGMEM = "Pragma";
static const char HEADER_EXPIRES[] PROGMEM = "Expires"; static const char HEADER_EXPIRES[] PROGMEM = "Expires";
static const char HEADER_AUTHENTICATE[] PROGMEM = "WWW-Authenticate"; static const char HEADER_AUTHENTICATE[] PROGMEM = "WWW-Authenticate";
static const char HEADER_LOCATION[] PROGMEM = "Location";
static const char CACHE_CONTROL_NO_CACHE[] PROGMEM = "no-cache, no-store, must-revalidate"; static const char CACHE_CONTROL_NO_CACHE[] PROGMEM = "no-cache, no-store, must-revalidate";
static const char CACHE_1HR[] PROGMEM = "public, max-age=3600"; static const char CACHE_1HR[] PROGMEM = "public, max-age=3600";

File diff suppressed because it is too large Load Diff

View File

@@ -61,7 +61,11 @@ private:
bool performRestart = false; bool performRestart = false;
bool performUpgrade = false; bool performUpgrade = false;
bool rebootForUpgrade = false; bool rebootForUpgrade = false;
#if defined(AMS2MQTT_FIRMWARE_URL)
String customFirmwareUrl = AMS2MQTT_FIRMWARE_URL;
#else
String customFirmwareUrl; String customFirmwareUrl;
#endif
static const uint16_t BufferSize = 2048; static const uint16_t BufferSize = 2048;
char* buf; char* buf;

View File

@@ -657,7 +657,7 @@ var fetch = function() {
if(ip) { if(ip) {
var v = parseInt(json.i); var v = parseInt(json.i);
var pct = (v*100)/parseInt(json.im); var pct = Math.min((v*100)/parseInt(json.im), 100);
var append = "W"; var append = "W";
if(v > 1000 && !swatt) { if(v > 1000 && !swatt) {
v = (v/1000).toFixed(1); v = (v/1000).toFixed(1);
@@ -683,7 +683,7 @@ var fetch = function() {
$('.rim').hide(); $('.rim').hide();
if(xp) { if(xp) {
var v = parseInt(json.e); var v = parseInt(json.e);
var pct = (v*100)/(om*1000); var pct = Math.min((v*100)/(om*1000), 100);
var append = "W"; var append = "W";
if(v > 1000 && !swatt) { if(v > 1000 && !swatt) {
v = (v/1000).toFixed(1); v = (v/1000).toFixed(1);
@@ -723,21 +723,21 @@ var fetch = function() {
var u1 = parseFloat(json.u1); var u1 = parseFloat(json.u1);
t += u1; t += u1;
c++; c++;
var pct = (Math.max(parseFloat(json.u1)-195.5, 1)*100/69); var pct = Math.min(Math.max(parseFloat(json.u1)-195.5, 1)*100/69, 100);
arr[r++] = [ds == 1 ? 'L1-L2' : 'L1', u1, "color: " + voltcol(pct) + ";opacity: 0.9;", u1 + "V"]; arr[r++] = [ds == 1 ? 'L1-L2' : 'L1', u1, "color: " + voltcol(pct) + ";opacity: 0.9;", u1 + "V"];
} }
if(json.u2) { if(json.u2) {
var u2 = parseFloat(json.u2); var u2 = parseFloat(json.u2);
t += u2; t += u2;
c++; c++;
var pct = (Math.max(parseFloat(json.u2)-195.5, 1)*100/69); var pct = Math.min(Math.max(parseFloat(json.u2)-195.5, 1)*100/69, 100);
arr[r++] = [ds == 1 ? 'L1-L3' : 'L2', u2, "color: " + voltcol(pct) + ";opacity: 0.9;", u2 + "V"]; arr[r++] = [ds == 1 ? 'L1-L3' : 'L2', u2, "color: " + voltcol(pct) + ";opacity: 0.9;", u2 + "V"];
} }
if(json.u3) { if(json.u3) {
var u3 = parseFloat(json.u3); var u3 = parseFloat(json.u3);
t += u3; t += u3;
c++; c++;
var pct = (Math.max(parseFloat(json.u3)-195.5, 1)*100/69); var pct = Math.min(Math.max(parseFloat(json.u3)-195.5, 1)*100/69, 100);
arr[r++] = [ds == 1 ? 'L2-L3' : 'L3', u3, "color: " + voltcol(pct) + ";opacity: 0.9;", u3 + "V"]; arr[r++] = [ds == 1 ? 'L2-L3' : 'L3', u3, "color: " + voltcol(pct) + ";opacity: 0.9;", u3 + "V"];
} }
v = t/c; v = t/c;
@@ -762,19 +762,19 @@ var fetch = function() {
if(json.i1 || json.u1) { if(json.i1 || json.u1) {
var i1 = parseFloat(json.i1); var i1 = parseFloat(json.i1);
dA = true; dA = true;
var pct = (parseFloat(json.i1)/parseInt(json.mf))*100; var pct = Math.min((parseFloat(json.i1)/parseInt(json.mf))*100, 100);
arr[r++] = ['L1', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i1 + "A"]; arr[r++] = ['L1', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i1 + "A"];
} }
if(json.i2 || json.u2) { if(json.i2 || json.u2) {
var i2 = parseFloat(json.i2); var i2 = parseFloat(json.i2);
dA = true; dA = true;
var pct = (parseFloat(json.i2)/parseInt(json.mf))*100; var pct = Math.min((parseFloat(json.i2)/parseInt(json.mf))*100, 100);
arr[r++] = ['L2', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i2 + "A"]; arr[r++] = ['L2', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i2 + "A"];
} }
if(json.i3 || json.u3) { if(json.i3 || json.u3) {
var i3 = parseFloat(json.i3); var i3 = parseFloat(json.i3);
dA = true; dA = true;
var pct = (parseFloat(json.i3)/parseInt(json.mf))*100; var pct = Math.min((parseFloat(json.i3)/parseInt(json.mf))*100, 100);
arr[r++] = ['L3', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i3 + "A"]; arr[r++] = ['L3', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i3 + "A"];
} }
if(dA) { if(dA) {
@@ -854,7 +854,7 @@ var fetch = function() {
$('.jmt').html("Iskra"); $('.jmt').html("Iskra");
break; break;
case 9: case 9:
$('.jmt').html("Landis"); $('.jmt').html("Landis+Gyr");
break; break;
case 10: case 10:
$('.jmt').html("Sagemcom"); $('.jmt').html("Sagemcom");

View File

@@ -23,8 +23,8 @@
"v" : %.3f, "v" : %.3f,
"r" : %d, "r" : %d,
"t" : %.2f, "t" : %.2f,
"u" : %lu, "u" : %u,
"m" : %lu, "m" : %u,
"em" : %d, "em" : %d,
"hm" : %d, "hm" : %d,
"wm" : %d, "wm" : %d,
@@ -53,5 +53,5 @@
"p" : %.2f "p" : %.2f
} }
}, },
"c" : %lu "c" : %u
} }

View File

@@ -1,6 +1,6 @@
<div class="alert alert-warning">!!WARNING!!<br/>Do not change anything here unless you know exactly what you are doing! Changing things here could cause the device to stop responding</div> <div class="alert alert-warning">!!WARNING!!<br/>Do not change anything here unless you know exactly what you are doing! Changing things here could cause the device to stop responding</div>
<form method="post" action="/save"> <form method="post" action="/save">
<input type="hidden" name="gpioConfig" value="true"/> <input type="hidden" name="gc" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow"> <div class="my-3 p-3 bg-white rounded shadow">
<h6>GPIO settings</h6> <h6>GPIO settings</h6>
<div class="d-flex flex-row flex-wrap"> <div class="d-flex flex-row flex-wrap">
@@ -8,18 +8,18 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">HAN</span> <span class="input-group-text">HAN</span>
</div> </div>
<select name="hanPin" class="form-control"> <select name="h" class="form-control">
${options.han} ${h}
</select> </select>
</div> </div>
<div class="m-2 input-group input-group-sm" style="width: 150px;"> <div class="m-2 input-group input-group-sm" style="width: 150px;">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">LED</span> <span class="input-group-text">LED</span>
</div> </div>
<input name="ledPin" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPin}"/> <input name="l" type="number" min="2" max="${g}" class="form-control" value="${l}"/>
<div class="input-group-append" title="Inverted"> <div class="input-group-append" title="Inverted">
<label class="input-group-text"> <label class="input-group-text">
<input type="checkbox" name="ledInverted" value="true" ${config.ledInverted}/> inv <input type="checkbox" name="i" value="true" ${i}/> inv
</label> </label>
</div> </div>
</div> </div>
@@ -27,12 +27,12 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">RGB</span> <span class="input-group-text">RGB</span>
</div> </div>
<input name="ledPinRed" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinRed}"/> <input name="r" type="number" min="2" max="${g}" class="form-control" value="${r}"/>
<input name="ledPinGreen" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinGreen}"/> <input name="e" type="number" min="2" max="${g}" class="form-control" value="${e}"/>
<input name="ledPinBlue" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinBlue}"/> <input name="b" type="number" min="2" max="${g}" class="form-control" value="${b}"/>
<div class="input-group-append" title="Inverted"> <div class="input-group-append" title="Inverted">
<label class="input-group-text"> <label class="input-group-text">
<input type="checkbox" name="ledRgbInverted" value="true" ${config.ledRgbInverted}/> inv <input type="checkbox" name="n" value="true" ${n}/> inv
</label> </label>
</div> </div>
</div> </div>
@@ -40,31 +40,31 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">AP button</span> <span class="input-group-text">AP button</span>
</div> </div>
<input name="apPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.apPin}"/> <input name="a" type="number" min="0" max="${g}" class="form-control" value="${a}"/>
</div> </div>
<div class="m-2 input-group input-group-sm" style="width: 150px;"> <div class="m-2 input-group input-group-sm" style="width: 150px;">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Temperature</span> <span class="input-group-text">Temperature</span>
</div> </div>
<input name="tempSensorPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.tempSensorPin}"/> <input name="t" type="number" min="0" max="${g}" class="form-control" value="${t}"/>
</div> </div>
<div class="m-2 input-group input-group-sm" style="width: 150px;"> <div class="m-2 input-group input-group-sm" style="width: 150px;">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Analog temp</span> <span class="input-group-text">Analog temp</span>
</div> </div>
<input name="tempAnalogSensorPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.tempAnalogSensorPin}"/> <input name="m" type="number" min="0" max="${g}" class="form-control" value="${m}"/>
</div> </div>
<div class="m-2 input-group input-group-sm" style="width: 100px;"> <div class="m-2 input-group input-group-sm" style="width: 100px;">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Vcc</span> <span class="input-group-text">Vcc</span>
</div> </div>
<input name="vccPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.vccPin}"/> <input name="v" type="number" min="0" max="${g}" class="form-control" value="${v}"/>
</div> </div>
<div class="m-2 input-group input-group-sm" style="width: 200px;"> <div class="m-2 input-group input-group-sm" style="width: 200px;">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">GND resistor</span> <span class="input-group-text">GND resistor</span>
</div> </div>
<input type="number" min="1" max="1000" step="1" class="form-control" name="vccResistorGnd" value="${config.vccResistorGnd}" /> <input type="number" min="1" max="1000" step="1" class="form-control" name="d" value="${d}" />
<div class="input-group-append" title="Inverted"> <div class="input-group-append" title="Inverted">
<label class="input-group-text">k&ohm;</label> <label class="input-group-text">k&ohm;</label>
</div> </div>
@@ -73,7 +73,7 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Vcc resistor</span> <span class="input-group-text">Vcc resistor</span>
</div> </div>
<input type="number" min="1" max="1000" step="1" class="form-control" name="vccResistorVcc" value="${config.vccResistorVcc}" /> <input type="number" min="1" max="1000" step="1" class="form-control" name="s" value="${s}" />
<div class="input-group-append" title="Inverted"> <div class="input-group-append" title="Inverted">
<label class="input-group-text">k&ohm;</label> <label class="input-group-text">k&ohm;</label>
</div> </div>
@@ -82,19 +82,19 @@
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Multiplier</span> <span class="input-group-text">Multiplier</span>
</div> </div>
<input type="number" min="0.1" max="10" step="0.01" class="form-control" name="vccMultiplier" value="${config.vccMultiplier}" /> <input type="number" min="0.1" max="10" step="0.01" class="form-control" name="u" value="${u}" />
</div> </div>
<div class="m-2 input-group input-group-sm" style="width: 120px;"> <div class="m-2 input-group input-group-sm" style="width: 120px;">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Offset</span> <span class="input-group-text">Offset</span>
</div> </div>
<input type="number" min="0.0" max="3.5" step="0.01" class="form-control" name="vccOffset" value="${config.vccOffset}" /> <input type="number" min="0.0" max="3.5" step="0.01" class="form-control" name="o" value="${o}" />
</div> </div>
<div class="m-2 input-group input-group-sm" style="width: 130px;"> <div class="m-2 input-group input-group-sm" style="width: 130px;">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Boot limit</span> <span class="input-group-text">Boot limit</span>
</div> </div>
<input type="number" min="2.5" max="3.5" step="0.1" class="form-control" name="vccBootLimit" value="${config.vccBootLimit}" /> <input type="number" min="2.5" max="3.5" step="0.1" class="form-control" name="c" value="${c}" />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -11,9 +11,5 @@
"I3" : %.2f, "I3" : %.2f,
"U1" : %.2f, "U1" : %.2f,
"U2" : %.2f, "U2" : %.2f,
"U3" : %.2f, "U3" : %.2f
"PF" : %.2f,
"PF1" : %.2f,
"PF2" : %.2f,
"PF3" : %.2f
} }

25
web/ha4.json Normal file
View File

@@ -0,0 +1,25 @@
{
"lv" : "%s",
"id" : "%s",
"type" : "%s",
"P" : %d,
"P1" : %.2f,
"P2" : %.2f,
"P3" : %.2f,
"Q" : %d,
"PO" : %d,
"PO1" : %.2f,
"PO2" : %.2f,
"PO3" : %.2f,
"QO" : %d,
"I1" : %.2f,
"I2" : %.2f,
"I3" : %.2f,
"U1" : %.2f,
"U2" : %.2f,
"U3" : %.2f,
"PF" : %.2f,
"PF1" : %.2f,
"PF2" : %.2f,
"PF3" : %.2f
}

View File

@@ -1,3 +1,4 @@
<!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">

View File

@@ -1,7 +1,7 @@
{ {
"id" : "%s", "id" : "%s",
"name" : "%s", "name" : "%s",
"up" : %lu, "up" : %u,
"t" : %lu, "t" : %lu,
"vcc" : %.3f, "vcc" : %.3f,
"rssi": %d, "rssi": %d,

View File

@@ -1,7 +1,7 @@
{ {
"id" : "%s", "id" : "%s",
"name" : "%s", "name" : "%s",
"up" : %lu, "up" : %u,
"t" : %lu, "t" : %lu,
"vcc" : %.3f, "vcc" : %.3f,
"rssi": %d, "rssi": %d,

View File

@@ -1,7 +1,7 @@
{ {
"id" : "%s", "id" : "%s",
"name" : "%s", "name" : "%s",
"up" : %lu, "up" : %u,
"t" : %lu, "t" : %lu,
"vcc" : %.3f, "vcc" : %.3f,
"rssi": %d, "rssi": %d,

View File

@@ -1,7 +1,7 @@
{ {
"id" : "%s", "id" : "%s",
"name" : "%s", "name" : "%s",
"up" : %lu, "up" : %u,
"t" : %lu, "t" : %lu,
"vcc" : %.3f, "vcc" : %.3f,
"rssi": %d, "rssi": %d,
@@ -11,8 +11,14 @@
"id" : "%s", "id" : "%s",
"type" : "%s", "type" : "%s",
"P" : %d, "P" : %d,
"P1" : %.2f,
"P2" : %.2f,
"P3" : %.2f,
"Q" : %d, "Q" : %d,
"PO" : %d, "PO" : %d,
"PO1" : %.2f,
"PO2" : %.2f,
"PO3" : %.2f,
"QO" : %d, "QO" : %d,
"I1" : %.2f, "I1" : %.2f,
"I2" : %.2f, "I2" : %.2f,

View File

@@ -35,6 +35,7 @@
<span class="input-group-text">Baud rate</span> <span class="input-group-text">Baud rate</span>
</div> </div>
<select class="form-control sd" name="b"> <select class="form-control sd" name="b">
<option value="300" {b300}>300</option>
<option value="2400" {b2400}>2400</option> <option value="2400" {b2400}>2400</option>
<option value="4800" {b4800}>4800</option> <option value="4800" {b4800}>4800</option>
<option value="9600" {b9600}>9600</option> <option value="9600" {b9600}>9600</option>

View File

@@ -26,6 +26,7 @@
<select name="board" class="form-control" required> <select name="board" class="form-control" required>
<option value=""></option> <option value=""></option>
<optgroup label="Custom hardware"> <optgroup label="Custom hardware">
<option value="7" ${config.boardType7}>Pow-U+ (ESP32) from amsleser.no</option>
<option value="6" ${config.boardType6}>Pow-P1 from amsleser.no</option> <option value="6" ${config.boardType6}>Pow-P1 from amsleser.no</option>
<option value="5" ${config.boardType5}>Pow-K+ (ESP32) from amsleser.no</option> <option value="5" ${config.boardType5}>Pow-K+ (ESP32) from amsleser.no</option>
<option value="4" ${config.boardType4}>Pow-U or Pow-K from amsleser.no (GPIO12)</option> <option value="4" ${config.boardType4}>Pow-U or Pow-K from amsleser.no (GPIO12)</option>

View File

@@ -37,7 +37,7 @@
</div> </div>
</div> </div>
<div class="row" id="i"> <div class="row" id="i">
<div class="col-xl-3 col-lg-4 form-group"> <div class="col-xl-3 col-lg-4 col-md-6 form-group">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">IP</span> <span class="input-group-text">IP</span>
@@ -45,7 +45,7 @@
<input type="text" name="i" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{i}"/> <input type="text" name="i" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{i}"/>
</div> </div>
</div> </div>
<div class="col-xl-3 col-lg-4 form-group"> <div class="col-xl-3 col-lg-4 col-md-6 form-group">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Netmask</span> <span class="input-group-text">Netmask</span>
@@ -53,7 +53,7 @@
<input type="text" name="sn" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{sn}"/> <input type="text" name="sn" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{sn}"/>
</div> </div>
</div> </div>
<div class="col-xl-3 col-lg-4 form-group"> <div class="col-xl-3 col-lg-4 col-md-6 form-group">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">Gateway</span> <span class="input-group-text">Gateway</span>
@@ -61,7 +61,7 @@
<input type="text" name="g" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{g}"/> <input type="text" name="g" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{g}"/>
</div> </div>
</div> </div>
<div class="col-xl-4 col-lg-5 form-group"> <div class="col-xl-4 col-lg-5 col-md-6 form-group">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">DNS 1</span> <span class="input-group-text">DNS 1</span>
@@ -69,7 +69,7 @@
<input type="text" name="d1" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{d1}"/> <input type="text" name="d1" class="form-control sip" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" value="{d1}"/>
</div> </div>
</div> </div>
<div class="col-xl-4 col-lg-5 form-group"> <div class="col-xl-4 col-lg-5 col-md-6 form-group">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">
<div class="input-group-prepend"> <div class="input-group-prepend">
<span class="input-group-text">DNS 2</span> <span class="input-group-text">DNS 2</span>
@@ -89,7 +89,21 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="col-lg-3 col-md-4 col-sm-6 form-group">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Power saving</span>
</div>
<select name="z" class="form-control">
<option value="255">Default</option>
<option value="0" {z0}>Off</option>
<option value="1" {z1}>Minimum</option>
<option value="2" {z2}>Maximum</option>
</select>
</div>
</div>
</div>
</div> </div>
<hr/> <hr/>
<div class="row form-group"> <div class="row form-group">