mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-03-11 13:05:31 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b0d540f39 | ||
|
|
33bd3da310 | ||
|
|
a239e1a63d | ||
|
|
1ef5703971 | ||
|
|
538de5ea99 | ||
|
|
042e2bcc85 | ||
|
|
775e5a0881 | ||
|
|
6c0d5fcc09 | ||
|
|
69d8fa9254 | ||
|
|
c38c305bab | ||
|
|
bfee2a1d64 | ||
|
|
ec7edae9a1 | ||
|
|
d8e265b7ac | ||
|
|
1987cddab7 | ||
|
|
e18be5f97c | ||
|
|
537597d6d1 | ||
|
|
a89013cec3 | ||
|
|
9c7a0cb7ff | ||
|
|
ade12199b9 | ||
|
|
39b68aca51 | ||
|
|
8ac1e034b1 | ||
|
|
f446dff865 | ||
|
|
34ebe9601a | ||
|
|
fa299198fc | ||
|
|
6d81b0a856 | ||
|
|
dd095da97b | ||
|
|
1b6ce203b7 | ||
|
|
2850be4e48 | ||
|
|
7eca31de84 | ||
|
|
a64f960cc7 | ||
|
|
ce3a47a7e6 | ||
|
|
8395e1dc77 | ||
|
|
39a4761415 | ||
|
|
867ab9d6c2 | ||
|
|
5e03e3d3c2 | ||
|
|
adb5050621 | ||
|
|
7cd52d5689 | ||
|
|
ad78ff3082 |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -33,6 +33,7 @@ If applicable, add screenshots to help explain your problem.
|
||||
**Relevant firmware information:**
|
||||
- Version: [e.g. 2.1.0]
|
||||
- MQTT: [yes/no]
|
||||
- MQTT payload type: [e.g. JSON]
|
||||
- HAN GPIO: [e.g. GPIO5]
|
||||
- HAN baud and parity: [e.g. 2400 8E1]
|
||||
- Temperature sensors [e.g. 3xDS18B20]
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/support.md
vendored
1
.github/ISSUE_TEMPLATE/support.md
vendored
@@ -20,6 +20,7 @@ A clear and concise description of what the problem is.
|
||||
**Relevant firmware information:**
|
||||
- Version: [e.g. 2.1.0]
|
||||
- MQTT: [yes/no]
|
||||
- MQTT payload type: [e.g. JSON]
|
||||
- HAN GPIO: [e.g. GPIO5]
|
||||
- HAN baud and parity: [e.g. 2400 8E1]
|
||||
- Temperature sensors [e.g. 3xDS18B20]
|
||||
|
||||
@@ -24,6 +24,7 @@ bool AmsConfiguration::getWiFiConfig(WiFiConfig& config) {
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
EEPROM.get(CONFIG_WIFI_START, config);
|
||||
EEPROM.end();
|
||||
if(config.sleep > 2) config.sleep = 1;
|
||||
return true;
|
||||
} else {
|
||||
clearWifi(config);
|
||||
@@ -33,6 +34,7 @@ bool AmsConfiguration::getWiFiConfig(WiFiConfig& config) {
|
||||
|
||||
bool AmsConfiguration::setWiFiConfig(WiFiConfig& config) {
|
||||
WiFiConfig existing;
|
||||
if(config.sleep > 2) config.sleep = 1;
|
||||
if(getWiFiConfig(existing)) {
|
||||
wifiChanged |= strcmp(config.ssid, existing.ssid) != 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 |= config.power != existing.power;
|
||||
wifiChanged |= config.sleep != existing.sleep;
|
||||
} else {
|
||||
wifiChanged = true;
|
||||
}
|
||||
@@ -70,6 +73,7 @@ void AmsConfiguration::clearWifi(WiFiConfig& config) {
|
||||
#endif
|
||||
strcpy(config.hostname, (String("ams-") + String(chipId, HEX)).c_str());
|
||||
config.mdns = true;
|
||||
config.sleep = 0xFF;
|
||||
}
|
||||
|
||||
void AmsConfiguration::clearWifiIp(WiFiConfig& config) {
|
||||
@@ -510,6 +514,7 @@ bool AmsConfiguration::getEnergyAccountingConfig(EnergyAccountingConfig& config)
|
||||
if(config.thresholds[9] != 255) {
|
||||
clearEnergyAccountingConfig(config);
|
||||
}
|
||||
if(config.hours > 5) config.hours = 5;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -549,6 +554,7 @@ void AmsConfiguration::clearEnergyAccountingConfig(EnergyAccountingConfig& confi
|
||||
config.thresholds[7] = 100;
|
||||
config.thresholds[8] = 150;
|
||||
config.thresholds[9] = 255;
|
||||
config.hours = 3;
|
||||
}
|
||||
|
||||
bool AmsConfiguration::isEnergyAccountingChanged() {
|
||||
@@ -669,6 +675,14 @@ bool AmsConfiguration::hasConfig() {
|
||||
configVersion = 0;
|
||||
return false;
|
||||
}
|
||||
case 95:
|
||||
configVersion = -1; // Prevent loop
|
||||
if(relocateConfig95()) {
|
||||
configVersion = 96;
|
||||
} else {
|
||||
configVersion = 0;
|
||||
return false;
|
||||
}
|
||||
case EEPROM_CHECK_SUM:
|
||||
return true;
|
||||
default:
|
||||
@@ -743,7 +757,7 @@ bool AmsConfiguration::relocateConfig86() {
|
||||
}
|
||||
|
||||
bool AmsConfiguration::relocateConfig87() {
|
||||
MeterConfig87 meter87;
|
||||
MeterConfig87 meter87 = {0,0,0,0,0,0,0};
|
||||
MeterConfig meter;
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
EEPROM.get(CONFIG_METER_START_87, meter87);
|
||||
@@ -846,6 +860,23 @@ bool AmsConfiguration::relocateConfig94() {
|
||||
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() {
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "Arduino.h"
|
||||
|
||||
#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_TEMP_CONFIG_ADDRESS 2048
|
||||
|
||||
@@ -54,7 +54,8 @@ struct WiFiConfig {
|
||||
char hostname[32];
|
||||
bool mdns;
|
||||
uint8_t power;
|
||||
}; // 210
|
||||
uint8_t sleep;
|
||||
}; // 211
|
||||
|
||||
struct MqttConfig86 {
|
||||
char host[128];
|
||||
@@ -87,6 +88,23 @@ struct WebConfig {
|
||||
}; // 129
|
||||
|
||||
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;
|
||||
uint8_t parity;
|
||||
bool invert;
|
||||
@@ -269,6 +287,7 @@ private:
|
||||
bool relocateConfig92(); // 2.0.3
|
||||
bool relocateConfig93(); // 2.1.0
|
||||
bool relocateConfig94(); // 2.1.4
|
||||
bool relocateConfig95(); // 2.1.13
|
||||
|
||||
void saveToFs();
|
||||
bool loadFromFs(uint8_t version);
|
||||
|
||||
@@ -45,6 +45,12 @@ void AmsData::apply(AmsData& other) {
|
||||
this->l1PowerFactor = other.getL1PowerFactor();
|
||||
this->l2PowerFactor = other.getL2PowerFactor();
|
||||
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:
|
||||
this->meterTimestamp = other.getMeterTimestamp();
|
||||
this->activeImportCounter = other.getActiveImportCounter();
|
||||
@@ -161,6 +167,30 @@ float AmsData::getL3PowerFactor() {
|
||||
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() {
|
||||
return this->activeImportCounter;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ public:
|
||||
float getL2PowerFactor();
|
||||
float getL3PowerFactor();
|
||||
|
||||
float getL1ActiveImportPower();
|
||||
float getL2ActiveImportPower();
|
||||
float getL3ActiveImportPower();
|
||||
|
||||
float getL1ActiveExportPower();
|
||||
float getL2ActiveExportPower();
|
||||
float getL3ActiveExportPower();
|
||||
|
||||
double getActiveImportCounter();
|
||||
double getReactiveImportCounter();
|
||||
double getActiveExportCounter();
|
||||
@@ -71,6 +79,8 @@ protected:
|
||||
time_t meterTimestamp = 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 l1activeImportPower = 0, l2activeImportPower = 0, l3activeImportPower = 0;
|
||||
float l1activeExportPower = 0, l2activeExportPower = 0, l3activeExportPower = 0;
|
||||
float powerFactor = 0, l1PowerFactor = 0, l2PowerFactor = 0, l3PowerFactor = 0;
|
||||
double activeImportCounter = 0, reactiveImportCounter = 0, activeExportCounter = 0, reactiveExportCounter = 0;
|
||||
bool threePhase = false, twoPhase = false, counterEstimated = false;
|
||||
|
||||
@@ -7,20 +7,20 @@
|
||||
|
||||
struct DayDataPoints {
|
||||
uint8_t version;
|
||||
int16_t hImport[24];
|
||||
uint16_t hImport[24];
|
||||
time_t lastMeterReadTime;
|
||||
uint32_t activeImport;
|
||||
uint32_t activeExport;
|
||||
int16_t hExport[24];
|
||||
uint16_t hExport[24];
|
||||
}; // 112 bytes
|
||||
|
||||
struct MonthDataPoints {
|
||||
uint8_t version;
|
||||
int16_t dImport[31];
|
||||
uint16_t dImport[31];
|
||||
time_t lastMeterReadTime;
|
||||
uint32_t activeImport;
|
||||
uint32_t activeExport;
|
||||
int16_t dExport[31];
|
||||
uint16_t dExport[31];
|
||||
}; // 141 bytes
|
||||
|
||||
class AmsDataStorage {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _AMSTOMQTTBRIDGE_H
|
||||
#define _AMSTOMQTTBRIDGE_H
|
||||
|
||||
#define WIFI_CONNECTION_TIMEOUT 30000;
|
||||
#define WIFI_CONNECTION_TIMEOUT 30000
|
||||
|
||||
#define INVALID_BUTTON_PIN 0xFFFFFFFF
|
||||
|
||||
|
||||
@@ -119,7 +119,6 @@ DLMSParser *dlmsParser = NULL;
|
||||
DSMRParser *dsmrParser = NULL;
|
||||
|
||||
void setup() {
|
||||
WiFiConfig wifi;
|
||||
Serial.begin(115200);
|
||||
|
||||
if(!config.getGpioConfig(gpioConfig)) {
|
||||
@@ -246,7 +245,6 @@ void setup() {
|
||||
}
|
||||
|
||||
Debug.setSerialEnabled(true);
|
||||
DebugConfig debug;
|
||||
delay(1);
|
||||
|
||||
float vcc = hw.getVcc();
|
||||
@@ -309,7 +307,7 @@ void setup() {
|
||||
}
|
||||
|
||||
debugI(" flashing");
|
||||
File firmwareFile = LittleFS.open(FILE_FIRMWARE, "r");
|
||||
File firmwareFile = LittleFS.open(FILE_FIRMWARE, (char*) "r");
|
||||
debugD(" firmware size: %d", firmwareFile.size());
|
||||
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
debugD(" available: %d", maxSketchSpace);
|
||||
@@ -360,7 +358,7 @@ void setup() {
|
||||
|
||||
NtpConfig 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);
|
||||
ntpEnabled = ntp.enable;
|
||||
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
|
||||
if (WiFi.getMode() != WIFI_AP) {
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
@@ -471,7 +473,7 @@ void loop() {
|
||||
if(strlen(wifi.hostname) > 0 && wifi.mdns) {
|
||||
debugD("mDNS is enabled, using host: %s", wifi.hostname);
|
||||
if(MDNS.begin(wifi.hostname)) {
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
MDNS.addService(F("http"), F("tcp"), 80);
|
||||
} else {
|
||||
debugE("Failed to set up mDNS!");
|
||||
}
|
||||
@@ -506,10 +508,6 @@ void loop() {
|
||||
MDNS.update();
|
||||
#endif
|
||||
|
||||
if(now > 10000 && now - lastErrorBlink > 3000) {
|
||||
errorBlink();
|
||||
}
|
||||
|
||||
if (mqttEnabled || config.isMqttChanged()) {
|
||||
if(mqtt == NULL || !mqtt->connected() || config.isMqttChanged()) {
|
||||
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()) {
|
||||
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(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) {
|
||||
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;
|
||||
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)
|
||||
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);
|
||||
#elif defined(ESP32)
|
||||
hwSerial->begin(baud, serialConfig, -1, -1, invert);
|
||||
hwSerial->setRxBufferSize(768);
|
||||
#else
|
||||
hwSerial->begin(baud, serialConfig, SERIAL_FULL, 1, invert);
|
||||
hwSerial->setRxBufferSize(768);
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
@@ -725,22 +720,25 @@ void errorBlink() {
|
||||
if(lastError == 3)
|
||||
lastError = 0;
|
||||
lastErrorBlink = millis();
|
||||
for(;lastError < 3;lastError++) {
|
||||
switch(lastError) {
|
||||
while(lastError < 3) {
|
||||
switch(lastError++) {
|
||||
case 0:
|
||||
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
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(mqttEnabled && mqtt != NULL && mqtt->lastError() != 0) {
|
||||
debugW("MQTT connection not available, double blink");
|
||||
hw.ledBlink(LED_RED, 2); // If MQTT error, blink twice
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
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
|
||||
return;
|
||||
}
|
||||
@@ -764,14 +762,14 @@ void swapWifiMode() {
|
||||
|
||||
if (mode != WIFI_AP || !config.hasConfig()) {
|
||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to AP mode");
|
||||
WiFi.softAP("AMS2MQTT");
|
||||
WiFi.softAP((char*) F("AMS2MQTT"));
|
||||
WiFi.mode(WIFI_AP);
|
||||
|
||||
if(dnsServer == NULL) {
|
||||
dnsServer = new DNSServer();
|
||||
}
|
||||
dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
|
||||
dnsServer->start(53, "*", WiFi.softAPIP());
|
||||
dnsServer->start(53, (char*) F("*"), WiFi.softAPIP());
|
||||
} else {
|
||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to STA mode");
|
||||
if(dnsServer != NULL) {
|
||||
@@ -812,7 +810,7 @@ bool readHanPort() {
|
||||
hanBuffer[len++] = hanSerial->read();
|
||||
ctx.length = len;
|
||||
pos = unwrapData((uint8_t *) hanBuffer, ctx);
|
||||
if(pos >= 0) {
|
||||
if(ctx.type > 0 && pos >= 0) {
|
||||
if(ctx.type == DATA_TAG_DLMS) {
|
||||
debugV("Received valid DLMS at %d", pos);
|
||||
} else if(ctx.type == DATA_TAG_DSMR) {
|
||||
@@ -827,6 +825,12 @@ bool readHanPort() {
|
||||
}
|
||||
if(pos == DATA_PARSE_INCOMPLETE) {
|
||||
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) {
|
||||
@@ -972,13 +976,13 @@ void printHanReadError(int pos) {
|
||||
void debugPrint(byte *buffer, int start, int length) {
|
||||
for (int i = start; i < start + length; i++) {
|
||||
if (buffer[i] < 0x10)
|
||||
Debug.print("0");
|
||||
Debug.print(F("0"));
|
||||
Debug.print(buffer[i], HEX);
|
||||
Debug.print(" ");
|
||||
Debug.print(F(" "));
|
||||
if ((i - start + 1) % 16 == 0)
|
||||
Debug.println("");
|
||||
else if ((i - start + 1) % 4 == 0)
|
||||
Debug.print(" ");
|
||||
Debug.print(F(" "));
|
||||
|
||||
yield(); // Let other get some resources too
|
||||
}
|
||||
@@ -1051,7 +1055,6 @@ void WiFi_connect() {
|
||||
}
|
||||
#endif
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.setSleep(WIFI_PS_MAX_MODEM);
|
||||
#if defined(ESP32)
|
||||
if(wifi.power >= 195)
|
||||
WiFi.setTxPower(WIFI_POWER_19_5dBm);
|
||||
@@ -1093,7 +1096,7 @@ void WiFi_connect() {
|
||||
if(strlen(wifi.dns2) > 0) {
|
||||
dns2.fromString(wifi.dns2);
|
||||
} 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)) {
|
||||
debugE("Static IP configuration is invalid, not using");
|
||||
@@ -1112,6 +1115,19 @@ void WiFi_connect() {
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.persistent(true);
|
||||
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();
|
||||
} else {
|
||||
if (Debug.isActive(RemoteDebug::ERROR)) debugI("Unable to start WiFi");
|
||||
@@ -1121,16 +1137,16 @@ void WiFi_connect() {
|
||||
|
||||
void mqttMessageReceived(String &topic, String &payload) {
|
||||
debugI("Received message for topic %s", topic.c_str() );
|
||||
if(meterConfig.source == METER_SOURCE_MQTT) {
|
||||
DataParserContext ctx = {payload.length()/2};
|
||||
fromHex(hanBuffer, payload, ctx.length);
|
||||
uint16_t pos = unwrapData(hanBuffer, ctx);
|
||||
//if(meterConfig.source == METER_SOURCE_MQTT) {
|
||||
//DataParserContext ctx = {static_cast<uint8_t>(payload.length()/2)};
|
||||
//fromHex(hanBuffer, payload, ctx.length);
|
||||
//uint16_t pos = unwrapData(hanBuffer, ctx);
|
||||
// TODO: Run through DLMS/DMSR parser and apply AmsData
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
||||
int16_t ret;
|
||||
int16_t ret = 0;
|
||||
bool doRet = false;
|
||||
uint16_t end = BUF_SIZE_HAN;
|
||||
uint8_t tag = (*buf);
|
||||
@@ -1170,7 +1186,7 @@ int16_t unwrapData(uint8_t *buf, DataParserContext &context) {
|
||||
if(res >= 0) doRet = true;
|
||||
break;
|
||||
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;
|
||||
}
|
||||
lastTag = tag;
|
||||
@@ -1321,7 +1337,7 @@ void MQTT_connect() {
|
||||
|
||||
if(LittleFS.exists(FILE_MQTT_CA)) {
|
||||
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)
|
||||
BearSSL::X509List *serverTrustedCA = new BearSSL::X509List(file);
|
||||
mqttSecureClient->setTrustAnchors(serverTrustedCA);
|
||||
@@ -1334,12 +1350,12 @@ void MQTT_connect() {
|
||||
if(LittleFS.exists(FILE_MQTT_CERT) && LittleFS.exists(FILE_MQTT_KEY)) {
|
||||
#if defined(ESP8266)
|
||||
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);
|
||||
file.close();
|
||||
|
||||
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);
|
||||
file.close();
|
||||
|
||||
@@ -1347,12 +1363,12 @@ void MQTT_connect() {
|
||||
mqttSecureClient->setClientRSACert(serverCertList, serverPrivKey);
|
||||
#elif defined(ESP32)
|
||||
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());
|
||||
file.close();
|
||||
|
||||
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());
|
||||
file.close();
|
||||
#endif
|
||||
@@ -1375,7 +1391,7 @@ void MQTT_connect() {
|
||||
#if defined(ESP8266)
|
||||
if(mqttSecureClient) {
|
||||
time_t epoch = time(nullptr);
|
||||
debugD("Setting NTP time %i for secure MQTT connection", epoch);
|
||||
debugD("Setting NTP time %lld for secure MQTT connection", epoch);
|
||||
mqttSecureClient->setX509Time(epoch);
|
||||
}
|
||||
#endif
|
||||
@@ -1418,7 +1434,7 @@ void configFileParse() {
|
||||
return;
|
||||
}
|
||||
|
||||
File file = LittleFS.open(FILE_CFG, "r");
|
||||
File file = LittleFS.open(FILE_CFG, (char*) "r");
|
||||
|
||||
bool lSys = false;
|
||||
bool lWiFi = false;
|
||||
@@ -1448,195 +1464,196 @@ void configFileParse() {
|
||||
char* buf = (char*) commonBuffer;
|
||||
memset(buf, 0, 1024);
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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(strncmp(buf+12, "7N1", 3) == 0) meter.parity = 2;
|
||||
if(strncmp(buf+12, "8N1", 3) == 0) meter.parity = 3;
|
||||
if(strncmp(buf+12, "7E1", 3) == 0) meter.parity = 10;
|
||||
if(strncmp(buf+12, "8E1", 3) == 0) meter.parity = 11;
|
||||
} else if(strncmp(buf, "meterInvert ", 12) == 0) {
|
||||
if(strncmp_P(buf+12, PSTR("7N1"), 3) == 0) meter.parity = 2;
|
||||
if(strncmp_P(buf+12, PSTR("8N1"), 3) == 0) meter.parity = 3;
|
||||
if(strncmp_P(buf+12, PSTR("7E1"), 3) == 0) meter.parity = 10;
|
||||
if(strncmp_P(buf+12, PSTR("8E1"), 3) == 0) meter.parity = 11;
|
||||
} else if(strncmp_P(buf, PSTR("meterInvert "), 12) == 0) {
|
||||
if(!lMeter) { config.getMeterConfig(meter); lMeter = true; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
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; };
|
||||
int i = 0;
|
||||
char * pch = strtok (buf+11," ");
|
||||
while (pch != NULL) {
|
||||
while (pch != NULL && i < 10) {
|
||||
eac.thresholds[i++] = String(pch).toInt();
|
||||
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;
|
||||
DayDataPoints day = { 4 }; // Use a version we know the multiplier of the data points
|
||||
char * pch = strtok (buf+8," ");
|
||||
@@ -1659,7 +1676,7 @@ void configFileParse() {
|
||||
}
|
||||
ds.setDayData(day);
|
||||
sDs = true;
|
||||
} else if(strncmp(buf, "monthplot ", 10) == 0) {
|
||||
} else if(strncmp_P(buf, PSTR("monthplot "), 10) == 0) {
|
||||
int i = 0;
|
||||
MonthDataPoints month = { 5 }; // Use a version we know the multiplier of the data points
|
||||
char * pch = strtok (buf+10," ");
|
||||
@@ -1682,7 +1699,7 @@ void configFileParse() {
|
||||
}
|
||||
ds.setMonthData(month);
|
||||
sDs = true;
|
||||
} else if(strncmp(buf, "energyaccounting ", 17) == 0) {
|
||||
} else if(strncmp_P(buf, PSTR("energyaccounting "), 17) == 0) {
|
||||
uint8_t i = 0;
|
||||
EnergyAccountingData ead = { 4, 0,
|
||||
0, 0, 0,
|
||||
@@ -1747,6 +1764,7 @@ void configFileParse() {
|
||||
if(lDomo) config.setDomoticzConfig(domo);
|
||||
if(lNtp) config.setNtpConfig(ntp);
|
||||
if(lEntsoe) config.setEntsoeConfig(entsoe);
|
||||
if(lEac) config.setEnergyAccountingConfig(eac);
|
||||
if(sDs) ds.save();
|
||||
if(sEa) ea.save();
|
||||
config.save();
|
||||
|
||||
@@ -57,7 +57,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
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) 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;
|
||||
}
|
||||
@@ -70,11 +70,14 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
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);
|
||||
|
||||
tmElements_t oneHrAgo;
|
||||
tmElements_t oneHrAgo, oneHrAgoLocal;
|
||||
breakTime(now-3600, oneHrAgo);
|
||||
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) {
|
||||
calcDayCost();
|
||||
}
|
||||
@@ -143,9 +146,9 @@ void EnergyAccounting::calcDayCost() {
|
||||
if(eapi != NULL && eapi->getValueForHour(0) != ENTSOE_NO_VALUE) {
|
||||
if(initPrice) costDay = 0;
|
||||
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;
|
||||
breakTime(now - ((currentHour - i) * 3600), utc);
|
||||
breakTime(now - ((local.Hour - i) * 3600), utc);
|
||||
int16_t wh = ds->getHourImport(utc.Hour);
|
||||
costDay += price * (wh / 1000.0);
|
||||
}
|
||||
@@ -161,9 +164,10 @@ double EnergyAccounting::getUseToday() {
|
||||
float ret = 0.0;
|
||||
time_t now = time(nullptr);
|
||||
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++) {
|
||||
breakTime(now - ((currentHour - i) * 3600), utc);
|
||||
breakTime(now - ((local.Hour - i) * 3600), utc);
|
||||
ret += ds->getHourImport(utc.Hour) / 1000.0;
|
||||
}
|
||||
return ret + getUseThisHour();
|
||||
@@ -237,7 +241,7 @@ float EnergyAccounting::getMonthMax() {
|
||||
uint32_t maxHour = 0.0;
|
||||
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;
|
||||
uint16_t maxVal = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
@@ -248,8 +252,10 @@ float EnergyAccounting::getMonthMax() {
|
||||
maxIdx = i;
|
||||
}
|
||||
}
|
||||
included[maxIdx] = true;
|
||||
count++;
|
||||
if(maxVal > 0) {
|
||||
included[maxIdx] = true;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
@@ -265,7 +271,7 @@ float EnergyAccounting::getPeak(uint8_t num) {
|
||||
uint8_t count = 0;
|
||||
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;
|
||||
uint16_t maxVal = 0;
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
@@ -275,8 +281,10 @@ float EnergyAccounting::getPeak(uint8_t num) {
|
||||
maxIdx = i;
|
||||
}
|
||||
}
|
||||
included[maxIdx] = true;
|
||||
count++;
|
||||
if(maxVal > 0) {
|
||||
included[maxIdx] = true;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t pos = 0;
|
||||
|
||||
@@ -373,8 +373,7 @@ bool HwTools::ledBlink(uint8_t color, uint8_t blink) {
|
||||
if(!ledOn(color)) return false;
|
||||
delay(50);
|
||||
ledOff(color);
|
||||
if(i != blink)
|
||||
delay(50);
|
||||
delay(200);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -98,8 +98,10 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
||||
}
|
||||
|
||||
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));
|
||||
if(l2voltage > 0) {
|
||||
l2current = (((activeImportPower - activeExportPower) * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage;
|
||||
}
|
||||
}
|
||||
|
||||
if(listType == 3) {
|
||||
@@ -296,6 +298,38 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
||||
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(listType >= 3) {
|
||||
activeImportCounter *= 10;
|
||||
@@ -336,12 +370,14 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, MeterConfig* meterCo
|
||||
if(mid != NULL) {
|
||||
switch(mid->base.type) {
|
||||
case CosemTypeString:
|
||||
memcpy(&meterId, mid->str.data, mid->str.length);
|
||||
meterId[mid->str.length] = 0;
|
||||
memcpy(str, mid->oct.data, mid->oct.length);
|
||||
str[mid->oct.length] = 0x00;
|
||||
meterId = String(str);
|
||||
break;
|
||||
case CosemTypeOctetString:
|
||||
memcpy(&meterId, mid->oct.data, mid->oct.length);
|
||||
meterId[mid->oct.length] = 0;
|
||||
memcpy(str, mid->str.data, mid->str.length);
|
||||
str[mid->str.length] = 0x00;
|
||||
meterId = String(str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,12 @@ private:
|
||||
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_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
|
||||
|
||||
85
src/LNG.cpp
85
src/LNG.cpp
@@ -1,6 +1,6 @@
|
||||
#include "LNG.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) {
|
||||
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* data = ptr + (18*h->arrayLength); // Skip descriptors
|
||||
|
||||
uint16_t o170 = 0, o270 = 0;
|
||||
uint16_t o181 = 0, o182 = 0;
|
||||
uint16_t o281 = 0, o282 = 0;
|
||||
uint64_t o170 = 0, o270 = 0;
|
||||
uint64_t o180 = 0, o280 = 0;
|
||||
uint64_t o181 = 0, o182 = 0;
|
||||
uint64_t o281 = 0, o282 = 0;
|
||||
LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr;
|
||||
for(uint8_t x = 0; x < h->arrayLength-1; x++) {
|
||||
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[3] == 7) {
|
||||
if(descriptor->obis[4] == 0) {
|
||||
o170 = ntohl(item->dlu.data);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||
o170 = getNumber(item);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o170);
|
||||
}
|
||||
} else if(descriptor->obis[3] == 8) {
|
||||
if(descriptor->obis[4] == 0) {
|
||||
activeImportCounter = ntohl(item->dlu.data) / 1000.0;
|
||||
o180 = getNumber(item);
|
||||
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) {
|
||||
o181 = ntohl(item->dlu.data);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||
o181 = getNumber(item);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o181);
|
||||
} else if(descriptor->obis[4] == 2) {
|
||||
o182 = ntohl(item->dlu.data);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||
o182 = getNumber(item);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o182);
|
||||
}
|
||||
}
|
||||
} else if(descriptor->obis[2] == 2) {
|
||||
if(descriptor->obis[3] == 7) {
|
||||
if(descriptor->obis[4] == 0) {
|
||||
o270 = ntohl(item->dlu.data);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||
o270 = getNumber(item);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o270);
|
||||
}
|
||||
} else if(descriptor->obis[3] == 8) {
|
||||
if(descriptor->obis[4] == 0) {
|
||||
activeExportCounter = ntohl(item->dlu.data) / 1000.0;
|
||||
o280 = getNumber(item);
|
||||
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) {
|
||||
o281 = ntohl(item->dlu.data);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||
o281 = getNumber(item);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o281);
|
||||
} else if(descriptor->obis[4] == 2) {
|
||||
o282 = ntohl(item->dlu.data);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data));
|
||||
o282 = getNumber(item);
|
||||
if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %lu", o282);
|
||||
}
|
||||
}
|
||||
} else if(descriptor->obis[2] == 96) {
|
||||
@@ -101,11 +104,49 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da
|
||||
|
||||
if((*data) == 0x09) {
|
||||
data += (*(data+1))+2;
|
||||
} else {
|
||||
} else if((*data) == 0x15) {
|
||||
data += 9;
|
||||
} else if((*data) == 0x06) {
|
||||
data += 5;
|
||||
} else if((*data) == 0x12) {
|
||||
data += 3;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "AmsData.h"
|
||||
#include "AmsConfiguration.h"
|
||||
#include "ams/DataParser.h"
|
||||
#include "ams/Cosem.h"
|
||||
#include "RemoteDebug.h"
|
||||
|
||||
struct LngHeader {
|
||||
@@ -25,6 +26,7 @@ struct LngObisDescriptor {
|
||||
class LNG : public AmsData {
|
||||
public:
|
||||
LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger);
|
||||
uint64_t getNumber(CosemData* item);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "Uptime.h"
|
||||
|
||||
uint32_t _uptime_last_value = 0;
|
||||
uint32_t _uptime_rollovers = 0;
|
||||
|
||||
uint64_t millis64() {
|
||||
uint32_t new_low32 = millis();
|
||||
if (new_low32 < _uptime_last_value) _uptime_rollovers++;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
static uint32_t _uptime_last_value = 0;
|
||||
static uint32_t _uptime_rollovers = 0;
|
||||
uint64_t millis64();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,6 @@ int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) {
|
||||
uint16_t crcPos = 0;
|
||||
bool reachedEnd = verified;
|
||||
uint8_t lastByte = 0x00;
|
||||
int c = 0;
|
||||
for(int pos = 0; pos < ctx.length; pos++) {
|
||||
uint8_t b = *(buf+pos);
|
||||
if(pos == 0 && b != '/') return DATA_PARSE_BOUNDRY_FLAG_MISSING;
|
||||
|
||||
@@ -27,7 +27,7 @@ int8_t GCMParser::parse(uint8_t *d, DataParserContext &ctx) {
|
||||
memcpy(ctx.system_title, ptr, systemTitleLength);
|
||||
memcpy(initialization_vector, ctx.system_title, systemTitleLength);
|
||||
|
||||
int len;
|
||||
int len = 0;
|
||||
int headersize = 2 + systemTitleLength;
|
||||
ptr += systemTitleLength;
|
||||
if(((*ptr) & 0xFF) == 0x81) {
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
int8_t HDLCParser::parse(uint8_t *d, DataParserContext &ctx) {
|
||||
int len;
|
||||
|
||||
uint8_t flag = *d;
|
||||
|
||||
uint8_t* ptr;
|
||||
if(ctx.length < 3)
|
||||
return DATA_PARSE_INCOMPLETE;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "LlcParser.h"
|
||||
|
||||
int8_t LLCParser::parse(uint8_t *buf, DataParserContext &ctx) {
|
||||
LLCHeader* llc = (LLCHeader*) buf;
|
||||
ctx.length -= 3;
|
||||
return 3;
|
||||
}
|
||||
@@ -5,8 +5,6 @@ int8_t MBUSParser::parse(uint8_t *d, DataParserContext &ctx) {
|
||||
int headersize = 3;
|
||||
int footersize = 1;
|
||||
|
||||
uint8_t flag = *d;
|
||||
|
||||
uint8_t* ptr;
|
||||
|
||||
// https://m-bus.com/documentation-wired/06-application-layer
|
||||
|
||||
@@ -22,7 +22,7 @@ void DnbCurrParser::flush() {
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
return size;
|
||||
|
||||
@@ -5,6 +5,10 @@ EntsoeA44Parser::EntsoeA44Parser() {
|
||||
for(int i = 0; i < 24; i++) points[i] = ENTSOE_NO_VALUE;
|
||||
}
|
||||
|
||||
EntsoeA44Parser::~EntsoeA44Parser() {
|
||||
|
||||
}
|
||||
|
||||
char* EntsoeA44Parser::getCurrency() {
|
||||
return currency;
|
||||
}
|
||||
@@ -35,7 +39,7 @@ void EntsoeA44Parser::flush() {
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
return size;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
class EntsoeA44Parser: public Stream {
|
||||
public:
|
||||
EntsoeA44Parser();
|
||||
virtual ~EntsoeA44Parser();
|
||||
|
||||
char* getCurrency();
|
||||
char* getMeasurementUnit();
|
||||
|
||||
@@ -106,11 +106,11 @@ bool EntsoeApi::loop() {
|
||||
if(midnightMillis == 0) {
|
||||
uint32_t curDayMillis = (((((tm.Hour * 60) + tm.Minute) * 60) + tm.Second) * 1000);
|
||||
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;
|
||||
return false;
|
||||
} else if(now > midnightMillis && currentDay != tm.Day) {
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Rotating price objects at %lu\n", t);
|
||||
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Rotating price objects at %lld\n", t);
|
||||
if(today != NULL) delete today;
|
||||
if(tomorrow != NULL) {
|
||||
today = tomorrow;
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
this->mqtt = mqtt;
|
||||
this->json = buf;
|
||||
};
|
||||
virtual ~AmsMqttHandler() {};
|
||||
|
||||
virtual bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea);
|
||||
virtual bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "web/root/ha1_json.h"
|
||||
#include "web/root/ha2_json.h"
|
||||
#include "web/root/ha3_json.h"
|
||||
#include "web/root/ha4_json.h"
|
||||
#include "web/root/jsonsys_json.h"
|
||||
#include "web/root/jsonprices_json.h"
|
||||
#include "web/root/hadiscover_json.h"
|
||||
@@ -34,7 +35,7 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
|
||||
data->getActiveImportPower()
|
||||
);
|
||||
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,
|
||||
data->getListId().c_str(),
|
||||
data->getMeterId().c_str(),
|
||||
@@ -48,6 +49,29 @@ bool HomeAssistantMqttHandler::publish(AmsData* data, AmsData* previousState, En
|
||||
data->getL3Current(),
|
||||
data->getL1Voltage(),
|
||||
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->getPowerFactor() == 0 ? 1 : data->getPowerFactor(),
|
||||
data->getPowerFactor() == 0 ? 1 : data->getL1PowerFactor(),
|
||||
@@ -117,7 +141,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
|
||||
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;
|
||||
float min = INT16_MAX, max = INT16_MIN;
|
||||
float values[24];
|
||||
@@ -167,7 +191,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
|
||||
}
|
||||
|
||||
char ts1hr[21];
|
||||
char ts1hr[24];
|
||||
if(min1hrIdx > -1) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
||||
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
||||
@@ -175,7 +199,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
breakTime(ts, tm);
|
||||
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) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
||||
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
||||
@@ -183,7 +207,7 @@ bool HomeAssistantMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
breakTime(ts, tm);
|
||||
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) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
||||
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
||||
@@ -263,13 +287,13 @@ bool HomeAssistantMqttHandler::publishSystem(HwTools* hw, EntsoeApi* eapi, Energ
|
||||
peaks++;
|
||||
}
|
||||
snprintf_P(json, BufferSize, HADISCOVER_JSON,
|
||||
FPSTR(sensor.name),
|
||||
topic.c_str(), FPSTR(sensor.topic),
|
||||
sensor.name,
|
||||
topic.c_str(), sensor.topic,
|
||||
haUID.c_str(), uid.c_str(),
|
||||
haUID.c_str(), uid.c_str(),
|
||||
uom.c_str(),
|
||||
FPSTR(sensor.path),
|
||||
FPSTR(sensor.devcl),
|
||||
sensor.path,
|
||||
sensor.devcl,
|
||||
haUID.c_str(),
|
||||
haName.c_str(),
|
||||
haModel.c_str(),
|
||||
|
||||
@@ -4,23 +4,29 @@
|
||||
#include "Arduino.h"
|
||||
|
||||
struct HomeAssistantSensor {
|
||||
char* name;
|
||||
char* topic;
|
||||
char* path;
|
||||
char* uom;
|
||||
char* devcl;
|
||||
char* stacl;
|
||||
const char* name;
|
||||
const char* topic;
|
||||
const char* path;
|
||||
const char* uom;
|
||||
const char* devcl;
|
||||
const char* stacl;
|
||||
};
|
||||
|
||||
|
||||
const uint8_t HA_SENSOR_COUNT PROGMEM = 50;
|
||||
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\""},
|
||||
@@ -32,6 +38,10 @@ HomeAssistantSensor HA_SENSORS[HA_SENSOR_COUNT] PROGMEM = {
|
||||
{"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", ""},
|
||||
|
||||
@@ -111,8 +111,14 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
|
||||
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(),
|
||||
@@ -143,8 +149,9 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState, EnergyAccou
|
||||
|
||||
bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) {
|
||||
int count = hw->getTempSensorCount();
|
||||
if(count < 2)
|
||||
if(count < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
snprintf(json, 24, "{\"temperatures\":{");
|
||||
|
||||
@@ -173,7 +180,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
|
||||
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;
|
||||
float min = INT16_MAX, max = INT16_MIN;
|
||||
float values[24];
|
||||
@@ -223,7 +230,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
|
||||
}
|
||||
|
||||
char ts1hr[21];
|
||||
char ts1hr[24];
|
||||
if(min1hrIdx > -1) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
||||
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
||||
@@ -231,7 +238,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
breakTime(ts, tm);
|
||||
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) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
||||
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
||||
@@ -239,7 +246,7 @@ bool JsonMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
breakTime(ts, tm);
|
||||
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) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
||||
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
||||
|
||||
@@ -11,6 +11,24 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState, EnergyAccountin
|
||||
}
|
||||
switch(data->getListType()) {
|
||||
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()) {
|
||||
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/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/threshold", String(ea->getCurrentThreshold(), 10), true, 0);
|
||||
@@ -106,7 +126,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
|
||||
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;
|
||||
float min = INT16_MAX, max = INT16_MIN;
|
||||
float values[34];
|
||||
@@ -157,7 +177,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
|
||||
}
|
||||
|
||||
char ts1hr[21];
|
||||
char ts1hr[24];
|
||||
if(min1hrIdx > -1) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min1hrIdx);
|
||||
//Serial.printf("1hr: %d %lu\n", min1hrIdx, ts);
|
||||
@@ -165,7 +185,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
breakTime(ts, tm);
|
||||
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) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min3hrIdx);
|
||||
//Serial.printf("3hr: %d %lu\n", min3hrIdx, ts);
|
||||
@@ -173,7 +193,7 @@ bool RawMqttHandler::publishPrices(EntsoeApi* eapi) {
|
||||
breakTime(ts, tm);
|
||||
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) {
|
||||
time_t ts = now + (SECS_PER_HOUR * min6hrIdx);
|
||||
//Serial.printf("6hr: %d %lu\n", min6hrIdx, ts);
|
||||
|
||||
@@ -2,6 +2,7 @@ static const char HEADER_CACHE_CONTROL[] PROGMEM = "Cache-Control";
|
||||
static const char HEADER_PRAGMA[] PROGMEM = "Pragma";
|
||||
static const char HEADER_EXPIRES[] PROGMEM = "Expires";
|
||||
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_1HR[] PROGMEM = "public, max-age=3600";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -61,7 +61,11 @@ private:
|
||||
bool performRestart = false;
|
||||
bool performUpgrade = false;
|
||||
bool rebootForUpgrade = false;
|
||||
#if defined(AMS2MQTT_FIRMWARE_URL)
|
||||
String customFirmwareUrl = AMS2MQTT_FIRMWARE_URL;
|
||||
#else
|
||||
String customFirmwareUrl;
|
||||
#endif
|
||||
|
||||
static const uint16_t BufferSize = 2048;
|
||||
char* buf;
|
||||
|
||||
@@ -657,7 +657,7 @@ var fetch = function() {
|
||||
|
||||
if(ip) {
|
||||
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";
|
||||
if(v > 1000 && !swatt) {
|
||||
v = (v/1000).toFixed(1);
|
||||
@@ -683,7 +683,7 @@ var fetch = function() {
|
||||
$('.rim').hide();
|
||||
if(xp) {
|
||||
var v = parseInt(json.e);
|
||||
var pct = (v*100)/(om*1000);
|
||||
var pct = Math.min((v*100)/(om*1000), 100);
|
||||
var append = "W";
|
||||
if(v > 1000 && !swatt) {
|
||||
v = (v/1000).toFixed(1);
|
||||
@@ -723,21 +723,21 @@ var fetch = function() {
|
||||
var u1 = parseFloat(json.u1);
|
||||
t += u1;
|
||||
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"];
|
||||
}
|
||||
if(json.u2) {
|
||||
var u2 = parseFloat(json.u2);
|
||||
t += u2;
|
||||
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"];
|
||||
}
|
||||
if(json.u3) {
|
||||
var u3 = parseFloat(json.u3);
|
||||
t += u3;
|
||||
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"];
|
||||
}
|
||||
v = t/c;
|
||||
@@ -762,19 +762,19 @@ var fetch = function() {
|
||||
if(json.i1 || json.u1) {
|
||||
var i1 = parseFloat(json.i1);
|
||||
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"];
|
||||
}
|
||||
if(json.i2 || json.u2) {
|
||||
var i2 = parseFloat(json.i2);
|
||||
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"];
|
||||
}
|
||||
if(json.i3 || json.u3) {
|
||||
var i3 = parseFloat(json.i3);
|
||||
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"];
|
||||
}
|
||||
if(dA) {
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
"v" : %.3f,
|
||||
"r" : %d,
|
||||
"t" : %.2f,
|
||||
"u" : %lu,
|
||||
"m" : %lu,
|
||||
"u" : %u,
|
||||
"m" : %u,
|
||||
"em" : %d,
|
||||
"hm" : %d,
|
||||
"wm" : %d,
|
||||
@@ -53,5 +53,5 @@
|
||||
"p" : %.2f
|
||||
}
|
||||
},
|
||||
"c" : %lu
|
||||
"c" : %u
|
||||
}
|
||||
@@ -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>
|
||||
<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">
|
||||
<h6>GPIO settings</h6>
|
||||
<div class="d-flex flex-row flex-wrap">
|
||||
@@ -8,18 +8,18 @@
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">HAN</span>
|
||||
</div>
|
||||
<select name="hanPin" class="form-control">
|
||||
${options.han}
|
||||
<select name="h" class="form-control">
|
||||
${h}
|
||||
</select>
|
||||
</div>
|
||||
<div class="m-2 input-group input-group-sm" style="width: 150px;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">LED</span>
|
||||
</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">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -27,12 +27,12 @@
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">RGB</span>
|
||||
</div>
|
||||
<input name="ledPinRed" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinRed}"/>
|
||||
<input name="ledPinGreen" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinGreen}"/>
|
||||
<input name="ledPinBlue" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinBlue}"/>
|
||||
<input name="r" type="number" min="2" max="${g}" class="form-control" value="${r}"/>
|
||||
<input name="e" type="number" min="2" max="${g}" class="form-control" value="${e}"/>
|
||||
<input name="b" type="number" min="2" max="${g}" class="form-control" value="${b}"/>
|
||||
<div class="input-group-append" title="Inverted">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,31 +40,31 @@
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">AP button</span>
|
||||
</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 class="m-2 input-group input-group-sm" style="width: 150px;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Temperature</span>
|
||||
</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 class="m-2 input-group input-group-sm" style="width: 150px;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Analog temp</span>
|
||||
</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 class="m-2 input-group input-group-sm" style="width: 100px;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Vcc</span>
|
||||
</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 class="m-2 input-group input-group-sm" style="width: 200px;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">GND resistor</span>
|
||||
</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">
|
||||
<label class="input-group-text">kΩ</label>
|
||||
</div>
|
||||
@@ -73,7 +73,7 @@
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Vcc resistor</span>
|
||||
</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">
|
||||
<label class="input-group-text">kΩ</label>
|
||||
</div>
|
||||
@@ -82,19 +82,19 @@
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Multiplier</span>
|
||||
</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 class="m-2 input-group input-group-sm" style="width: 120px;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Offset</span>
|
||||
</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 class="m-2 input-group input-group-sm" style="width: 130px;">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">Boot limit</span>
|
||||
</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>
|
||||
|
||||
@@ -11,9 +11,5 @@
|
||||
"I3" : %.2f,
|
||||
"U1" : %.2f,
|
||||
"U2" : %.2f,
|
||||
"U3" : %.2f,
|
||||
"PF" : %.2f,
|
||||
"PF1" : %.2f,
|
||||
"PF2" : %.2f,
|
||||
"PF3" : %.2f
|
||||
"U3" : %.2f
|
||||
}
|
||||
|
||||
25
web/ha4.json
Normal file
25
web/ha4.json
Normal 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
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id" : "%s",
|
||||
"name" : "%s",
|
||||
"up" : %lu,
|
||||
"up" : %u,
|
||||
"t" : %lu,
|
||||
"vcc" : %.3f,
|
||||
"rssi": %d,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id" : "%s",
|
||||
"name" : "%s",
|
||||
"up" : %lu,
|
||||
"up" : %u,
|
||||
"t" : %lu,
|
||||
"vcc" : %.3f,
|
||||
"rssi": %d,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id" : "%s",
|
||||
"name" : "%s",
|
||||
"up" : %lu,
|
||||
"up" : %u,
|
||||
"t" : %lu,
|
||||
"vcc" : %.3f,
|
||||
"rssi": %d,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id" : "%s",
|
||||
"name" : "%s",
|
||||
"up" : %lu,
|
||||
"up" : %u,
|
||||
"t" : %lu,
|
||||
"vcc" : %.3f,
|
||||
"rssi": %d,
|
||||
@@ -11,8 +11,14 @@
|
||||
"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,
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<span class="input-group-text">Baud rate</span>
|
||||
</div>
|
||||
<select class="form-control sd" name="b">
|
||||
<option value="300" {b300}>300</option>
|
||||
<option value="2400" {b2400}>2400</option>
|
||||
<option value="4800" {b4800}>4800</option>
|
||||
<option value="9600" {b9600}>9600</option>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<select name="board" class="form-control" required>
|
||||
<option value=""></option>
|
||||
<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="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>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<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-prepend">
|
||||
<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}"/>
|
||||
</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-prepend">
|
||||
<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}"/>
|
||||
</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-prepend">
|
||||
<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}"/>
|
||||
</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-prepend">
|
||||
<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}"/>
|
||||
</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-prepend">
|
||||
<span class="input-group-text">DNS 2</span>
|
||||
@@ -89,7 +89,21 @@
|
||||
</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>
|
||||
<hr/>
|
||||
<div class="row form-group">
|
||||
|
||||
Reference in New Issue
Block a user