Compare commits

...

12 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
15 changed files with 128 additions and 42 deletions

View File

@@ -675,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:
@@ -852,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
@@ -88,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;
@@ -270,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

@@ -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,

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

@@ -1391,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 %lld for secure MQTT connection", epoch); debugD("Setting NTP time %lu for secure MQTT connection", epoch);
mqttSecureClient->setX509Time(epoch); mqttSecureClient->setX509Time(epoch);
} }
#endif #endif

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,
@@ -63,18 +63,21 @@ bool EnergyAccounting::update(AmsData* amsData) {
} }
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();

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);
@@ -80,6 +77,14 @@ IEC6205621::IEC6205621(const char* p) {
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) {

View File

@@ -1,4 +1,7 @@
#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;
@@ -14,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

@@ -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

@@ -110,7 +110,7 @@ bool EntsoeApi::loop() {
currentDay = tm.Day; currentDay = tm.Day;
return false; return false;
} else if(now > midnightMillis && currentDay != tm.Day) { } else if(now > midnightMillis && currentDay != tm.Day) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Rotating price objects at %lld\n", t); if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Rotating price objects at %lu\n", t);
if(today != NULL) delete today; if(today != NULL) delete today;
if(tomorrow != NULL) { if(tomorrow != NULL) {
today = tomorrow; today = tomorrow;

View File

@@ -329,8 +329,8 @@ void AmsWebServer::configMeterHtml() {
case AmsTypeIskra: case AmsTypeIskra:
manufacturer = F("Iskra"); manufacturer = F("Iskra");
break; break;
case AmsTypeLandis: case AmsTypeLandisGyr:
manufacturer = F("Landis + Gyro"); manufacturer = F("Landis+Gyr");
break; break;
case AmsTypeSagemcom: case AmsTypeSagemcom:
manufacturer = F("Sagemcom"); manufacturer = F("Sagemcom");
@@ -347,6 +347,7 @@ void AmsWebServer::configMeterHtml() {
html.replace(F("{mod}"), meterState->getMeterModel()); html.replace(F("{mod}"), meterState->getMeterModel());
html.replace(F("{mid}"), meterState->getMeterId()); html.replace(F("{mid}"), meterState->getMeterId());
html.replace(F("{b}"), String(meterConfig->baud)); html.replace(F("{b}"), String(meterConfig->baud));
html.replace(F("{b300}"), meterConfig->baud == 300 ? F("selected") : F(""));
html.replace(F("{b2400}"), meterConfig->baud == 2400 ? F("selected") : F("")); html.replace(F("{b2400}"), meterConfig->baud == 2400 ? F("selected") : F(""));
html.replace(F("{b4800}"), meterConfig->baud == 4800 ? F("selected") : F("")); html.replace(F("{b4800}"), meterConfig->baud == 4800 ? F("selected") : F(""));
html.replace(F("{b9600}"), meterConfig->baud == 9600 ? F("selected") : F("")); html.replace(F("{b9600}"), meterConfig->baud == 9600 ? F("selected") : F(""));
@@ -1264,6 +1265,7 @@ void AmsWebServer::handleSave() {
if(server.hasArg(F("h")) && !server.arg(F("h")).isEmpty()) { if(server.hasArg(F("h")) && !server.arg(F("h")).isEmpty()) {
strcpy(wifi.hostname, server.arg(F("h")).c_str()); strcpy(wifi.hostname, server.arg(F("h")).c_str());
} }
wifi.mdns = server.arg(F("m")) == F("true");
wifi.power = server.arg(F("w")).toFloat() * 10; wifi.power = server.arg(F("w")).toFloat() * 10;
wifi.sleep = server.arg(F("z")).toInt(); wifi.sleep = server.arg(F("z")).toInt();
config->setWiFiConfig(wifi); config->setWiFiConfig(wifi);
@@ -2304,9 +2306,9 @@ void AmsWebServer::configFileDownload() {
if(ds != NULL) { if(ds != NULL) {
DayDataPoints day = ds->getDayData(); DayDataPoints day = ds->getDayData();
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dayplot %d %lld %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"), server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("dayplot %d %lu %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
day.version, day.version,
(int64_t) day.lastMeterReadTime, (int32_t) day.lastMeterReadTime,
day.activeImport, day.activeImport,
ds->getHourImport(0), ds->getHourImport(0),
ds->getHourImport(1), ds->getHourImport(1),
@@ -2366,9 +2368,9 @@ void AmsWebServer::configFileDownload() {
} }
MonthDataPoints month = ds->getMonthData(); MonthDataPoints month = ds->getMonthData();
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("monthplot %d %lld %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"), server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("monthplot %d %lu %lu %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"),
month.version, month.version,
(int64_t) month.lastMeterReadTime, (int32_t) month.lastMeterReadTime,
month.activeImport, month.activeImport,
ds->getDayImport(1), ds->getDayImport(1),
ds->getDayImport(2), ds->getDayImport(2),

View File

@@ -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

@@ -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>