mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-01-30 21:51:52 +00:00
Support encrypted meters and added vcc offset
This commit is contained in:
@@ -242,6 +242,7 @@ uint8_t AmsConfiguration::getMeterType() {
|
||||
}
|
||||
|
||||
void AmsConfiguration::setMeterType(uint8_t meterType) {
|
||||
meterChanged |= config.meterType != meterType;
|
||||
config.meterType = meterType;
|
||||
}
|
||||
|
||||
@@ -269,6 +270,24 @@ void AmsConfiguration::setProductionCapacity(uint8_t productionCapacity) {
|
||||
config.productionCapacity = productionCapacity;
|
||||
}
|
||||
|
||||
uint8_t* AmsConfiguration::getMeterEncryptionKey() {
|
||||
return config.meterEncryptionKey;
|
||||
}
|
||||
|
||||
void AmsConfiguration::setMeterEncryptionKey(uint8_t* meterEncryptionKey) {
|
||||
meterChanged |= strcmp((char*) config.meterEncryptionKey, (char*) meterEncryptionKey);
|
||||
memcpy(config.meterEncryptionKey, meterEncryptionKey, 16);
|
||||
}
|
||||
|
||||
uint8_t* AmsConfiguration::getMeterAuthenticationKey() {
|
||||
return config.meterAuthenticationKey;
|
||||
}
|
||||
|
||||
void AmsConfiguration::setMeterAuthenticationKey(uint8_t* meterAuthenticationKey) {
|
||||
meterChanged |= strcmp((char*) config.meterAuthenticationKey, (char*) meterAuthenticationKey);
|
||||
memcpy(config.meterAuthenticationKey, meterAuthenticationKey, 16);
|
||||
}
|
||||
|
||||
bool AmsConfiguration::isSubstituteMissing() {
|
||||
return config.substituteMissing;
|
||||
}
|
||||
@@ -294,6 +313,14 @@ void AmsConfiguration::clearMeter() {
|
||||
setSendUnknown(false);
|
||||
}
|
||||
|
||||
bool AmsConfiguration::isMeterChanged() {
|
||||
return meterChanged;
|
||||
}
|
||||
|
||||
void AmsConfiguration::ackMeterChanged() {
|
||||
meterChanged = false;
|
||||
}
|
||||
|
||||
|
||||
bool AmsConfiguration::isDebugTelnet() {
|
||||
return config.debugTelnet;
|
||||
@@ -388,6 +415,7 @@ uint8_t AmsConfiguration::getHanPin() {
|
||||
|
||||
void AmsConfiguration::setHanPin(uint8_t hanPin) {
|
||||
if(!pinUsed(hanPin)) {
|
||||
meterChanged |= config.hanPin != hanPin;
|
||||
config.hanPin = hanPin;
|
||||
}
|
||||
}
|
||||
@@ -482,8 +510,16 @@ void AmsConfiguration::setVccPin(uint8_t vccPin) {
|
||||
}
|
||||
}
|
||||
|
||||
double AmsConfiguration::getVccOffset() {
|
||||
return config.vccOffset > 0 ? config.vccOffset / 100.0 : 0;
|
||||
}
|
||||
|
||||
void AmsConfiguration::setVccOffset(double vccOffset) {
|
||||
config.vccOffset = vccOffset == 0 ? 0 : max(-350, min((int)(vccOffset * 100.0), 350));
|
||||
}
|
||||
|
||||
double AmsConfiguration::getVccMultiplier() {
|
||||
return config.vccMultiplier / 1000.0;
|
||||
return config.vccMultiplier > 0 ? config.vccMultiplier / 1000.0 : 0;
|
||||
}
|
||||
|
||||
void AmsConfiguration::setVccMultiplier(double vccMultiplier) {
|
||||
@@ -491,7 +527,7 @@ void AmsConfiguration::setVccMultiplier(double vccMultiplier) {
|
||||
}
|
||||
|
||||
double AmsConfiguration::getVccBootLimit() {
|
||||
return config.vccBootLimit / 10.0;
|
||||
return config.vccBootLimit > 0 ? config.vccBootLimit / 10.0 : 0;
|
||||
}
|
||||
|
||||
void AmsConfiguration::setVccBootLimit(double vccBootLimit) {
|
||||
@@ -532,12 +568,9 @@ bool AmsConfiguration::hasConfig() {
|
||||
EEPROM.end();
|
||||
}
|
||||
switch(configVersion) {
|
||||
case 71:
|
||||
case 72:
|
||||
case 75:
|
||||
case 80:
|
||||
case 81:
|
||||
case 82:
|
||||
case 83:
|
||||
return true;
|
||||
default:
|
||||
configVersion = 0;
|
||||
@@ -557,13 +590,13 @@ bool AmsConfiguration::load() {
|
||||
int cs = EEPROM.read(address);
|
||||
address++;
|
||||
switch(cs) {
|
||||
case 80: // v1.1
|
||||
success = loadConfig80(address);
|
||||
break;
|
||||
case 81: // v1.2
|
||||
success = loadConfig81(address);
|
||||
break;
|
||||
case 82: // v1.3
|
||||
success = loadConfig82(address);
|
||||
break;
|
||||
case 83: // v1.4
|
||||
EEPROM.get(address, config);
|
||||
success = true;
|
||||
break;
|
||||
@@ -572,85 +605,65 @@ bool AmsConfiguration::load() {
|
||||
|
||||
if(config.apPin >= 0)
|
||||
pinMode(config.apPin, INPUT_PULLUP);
|
||||
meterChanged = true;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool AmsConfiguration::loadConfig80(int address) {
|
||||
char* temp;
|
||||
bool AmsConfiguration::loadConfig82(int address) {
|
||||
ConfigObject82 config82;
|
||||
EEPROM.get(address, config82);
|
||||
config.boardType = config82.boardType;
|
||||
strcpy(config.wifiSsid, config82.wifiSsid);
|
||||
strcpy(config.wifiPassword, config82.wifiPassword);
|
||||
strcpy(config.wifiIp, config82.wifiIp);
|
||||
strcpy(config.wifiGw, config82.wifiGw);
|
||||
strcpy(config.wifiSubnet, config82.wifiSubnet);
|
||||
strcpy(config.wifiDns1, config82.wifiDns1);
|
||||
strcpy(config.wifiDns2, config82.wifiDns2);
|
||||
strcpy(config.wifiHostname, config82.wifiHostname);
|
||||
strcpy(config.mqttHost, config82.mqttHost);
|
||||
config.mqttPort = config82.mqttPort;
|
||||
strcpy(config.mqttClientId, config82.mqttClientId);
|
||||
strcpy(config.mqttPublishTopic, config82.mqttPublishTopic);
|
||||
strcpy(config.mqttSubscribeTopic, config82.mqttSubscribeTopic);
|
||||
strcpy(config.mqttUser, config82.mqttUser);
|
||||
strcpy(config.mqttPassword, config82.mqttPassword);
|
||||
config.mqttPayloadFormat = config82.mqttPayloadFormat;
|
||||
config.mqttSsl = config82.mqttSsl;
|
||||
config.authSecurity = config82.authSecurity;
|
||||
strcpy(config.authUser, config82.authUser);
|
||||
strcpy(config.authPassword, config82.authPassword);
|
||||
|
||||
config.meterType = config82.meterType;
|
||||
config.distributionSystem = config82.distributionSystem;
|
||||
config.mainFuse = config82.mainFuse;
|
||||
config.productionCapacity = config82.productionCapacity;
|
||||
config.substituteMissing = config82.substituteMissing;
|
||||
config.sendUnknown = config82.sendUnknown;
|
||||
|
||||
address += readString(address, &temp);
|
||||
setWifiSsid(temp);
|
||||
address += readString(address, &temp);
|
||||
setWifiPassword(temp);
|
||||
address += readString(address, &temp);
|
||||
setWifiIp(temp);
|
||||
address += readString(address, &temp);
|
||||
setWifiGw(temp);
|
||||
address += readString(address, &temp);
|
||||
setWifiSubnet(temp);
|
||||
config.debugTelnet = config82.debugTelnet;
|
||||
config.debugSerial = config82.debugSerial;
|
||||
config.debugLevel = config82.debugLevel;
|
||||
|
||||
bool mqtt = false;
|
||||
address += readBool(address, &mqtt);
|
||||
if(mqtt) {
|
||||
address += readString(address, &temp);
|
||||
setMqttHost(temp);
|
||||
int port;
|
||||
address += readInt(address, &port);
|
||||
setMqttPort(port);
|
||||
address += readString(address, &temp);
|
||||
setMqttClientId(temp);
|
||||
address += readString(address, &temp);
|
||||
setMqttPublishTopic(temp);
|
||||
address += readString(address, &temp);
|
||||
setMqttSubscribeTopic(temp);
|
||||
config.hanPin = config82.hanPin;
|
||||
config.apPin = config82.apPin;
|
||||
config.ledPin = config82.ledPin;
|
||||
config.ledInverted = config82.ledInverted;
|
||||
config.ledPinRed = config82.ledPinRed;
|
||||
config.ledPinGreen = config82.ledPinGreen;
|
||||
config.ledPinBlue = config82.ledPinBlue;
|
||||
config.ledRgbInverted = config82.ledRgbInverted;
|
||||
config.tempSensorPin = config82.tempSensorPin;
|
||||
config.vccPin = config82.vccPin;
|
||||
config.vccMultiplier = config82.vccMultiplier;
|
||||
config.vccBootLimit = config82.vccBootLimit;
|
||||
|
||||
bool secure = false;
|
||||
address += readBool(address, &secure);
|
||||
if (secure)
|
||||
{
|
||||
address += readString(address, &temp);
|
||||
setMqttUser(temp);
|
||||
address += readString(address, &temp);
|
||||
setMqttPassword(temp);
|
||||
} else {
|
||||
setMqttUser("");
|
||||
setMqttPassword("");
|
||||
}
|
||||
} else {
|
||||
clearMqtt();
|
||||
}
|
||||
setMqttPayloadFormat(0);
|
||||
|
||||
byte b;
|
||||
address += readByte(address, &b);
|
||||
setAuthSecurity(b);
|
||||
if (b > 0) {
|
||||
address += readString(address, &temp);
|
||||
setAuthUser(temp);
|
||||
address += readString(address, &temp);
|
||||
setAuthPassword(temp);
|
||||
} else {
|
||||
clearAuth();
|
||||
}
|
||||
|
||||
int i;
|
||||
address += readInt(address, &i);
|
||||
setMeterType(i);
|
||||
address += readInt(address, &i);
|
||||
setDistributionSystem(i);
|
||||
address += readInt(address, &i);
|
||||
setMainFuse(i);
|
||||
address += readInt(address, &i);
|
||||
setProductionCapacity(i);
|
||||
|
||||
ackWifiChange();
|
||||
|
||||
setDebugLevel(3); // 3=Info
|
||||
setDebugTelnet(false);
|
||||
setDebugSerial(false);
|
||||
|
||||
return true;
|
||||
config.domoELIDX = config82.domoELIDX;
|
||||
config.domoVL1IDX = config82.domoVL1IDX;
|
||||
config.domoVL2IDX = config82.domoVL2IDX;
|
||||
config.domoVL3IDX = config82.domoVL3IDX;
|
||||
config.domoCL1IDX = config82.domoCL1IDX;
|
||||
}
|
||||
|
||||
bool AmsConfiguration::loadConfig81(int address) {
|
||||
@@ -852,13 +865,13 @@ void AmsConfiguration::print(Print* debugger)
|
||||
debugger->printf("Vcc multiplier: %f\r\n", this->getVccMultiplier());
|
||||
debugger->printf("Vcc boot limit: %f\r\n", this->getVccBootLimit());
|
||||
|
||||
//if(this->getDomoELIDX() > 0) {
|
||||
debugger->printf("Domoticz ELIDX: %i\r\n", this->getDomoELIDX());
|
||||
debugger->printf("Domoticz VL1IDX: %i\r\n", this->getDomoVL1IDX());
|
||||
debugger->printf("Domoticz VL2IDX: %i\r\n", this->getDomoVL2IDX());
|
||||
debugger->printf("Domoticz VL3IDX: %i\r\n", this->getDomoVL3IDX());
|
||||
debugger->printf("Domoticz CL1IDX: %i\r\n", this->getDomoCL1IDX());
|
||||
//}
|
||||
if(this->getDomoELIDX() > 0) {
|
||||
debugger->printf("Domoticz ELIDX: %i\r\n", this->getDomoELIDX());
|
||||
debugger->printf("Domoticz VL1IDX: %i\r\n", this->getDomoVL1IDX());
|
||||
debugger->printf("Domoticz VL2IDX: %i\r\n", this->getDomoVL2IDX());
|
||||
debugger->printf("Domoticz VL3IDX: %i\r\n", this->getDomoVL3IDX());
|
||||
debugger->printf("Domoticz CL1IDX: %i\r\n", this->getDomoCL1IDX());
|
||||
}
|
||||
|
||||
debugger->println("-----------------------------------------------");
|
||||
}
|
||||
|
||||
@@ -9,6 +9,63 @@ struct ConfigObject {
|
||||
char wifiPassword[64];
|
||||
char wifiIp[15];
|
||||
char wifiGw[15];
|
||||
char wifiSubnet[15];
|
||||
char wifiDns1[15];
|
||||
char wifiDns2[15];
|
||||
char wifiHostname[32];
|
||||
char mqttHost[128];
|
||||
uint16_t mqttPort;
|
||||
char mqttClientId[32];
|
||||
char mqttPublishTopic[64];
|
||||
char mqttSubscribeTopic[64];
|
||||
char mqttUser[64];
|
||||
char mqttPassword[64];
|
||||
uint8_t mqttPayloadFormat;
|
||||
bool mqttSsl;
|
||||
uint8_t authSecurity;
|
||||
char authUser[64];
|
||||
char authPassword[64];
|
||||
|
||||
uint8_t meterType;
|
||||
uint8_t distributionSystem;
|
||||
uint8_t mainFuse;
|
||||
uint8_t productionCapacity;
|
||||
uint8_t meterEncryptionKey[16];
|
||||
uint8_t meterAuthenticationKey[16];
|
||||
bool substituteMissing;
|
||||
bool sendUnknown;
|
||||
|
||||
bool debugTelnet;
|
||||
bool debugSerial;
|
||||
uint8_t debugLevel;
|
||||
|
||||
uint8_t hanPin;
|
||||
uint8_t apPin;
|
||||
uint8_t ledPin;
|
||||
bool ledInverted;
|
||||
uint8_t ledPinRed;
|
||||
uint8_t ledPinGreen;
|
||||
uint8_t ledPinBlue;
|
||||
bool ledRgbInverted;
|
||||
uint8_t tempSensorPin;
|
||||
uint8_t vccPin;
|
||||
int16_t vccOffset;
|
||||
uint16_t vccMultiplier;
|
||||
uint8_t vccBootLimit;
|
||||
|
||||
uint16_t domoELIDX;
|
||||
uint16_t domoVL1IDX;
|
||||
uint16_t domoVL2IDX;
|
||||
uint16_t domoVL3IDX;
|
||||
uint16_t domoCL1IDX;
|
||||
};
|
||||
|
||||
struct ConfigObject82 {
|
||||
uint8_t boardType;
|
||||
char wifiSsid[32];
|
||||
char wifiPassword[64];
|
||||
char wifiIp[15];
|
||||
char wifiGw[15];
|
||||
char wifiSubnet[15];
|
||||
char wifiDns1[15];
|
||||
char wifiDns2[15];
|
||||
@@ -129,12 +186,19 @@ public:
|
||||
void setMainFuse(uint8_t mainFuse);
|
||||
uint8_t getProductionCapacity();
|
||||
void setProductionCapacity(uint8_t productionCapacity);
|
||||
uint8_t* getMeterEncryptionKey();
|
||||
void setMeterEncryptionKey(uint8_t* meterEncryptionKey);
|
||||
uint8_t* getMeterAuthenticationKey();
|
||||
void setMeterAuthenticationKey(uint8_t* meterAuthenticationKey);
|
||||
bool isSubstituteMissing();
|
||||
void setSubstituteMissing(bool substituteMissing);
|
||||
bool isSendUnknown();
|
||||
void setSendUnknown(bool sendUnknown);
|
||||
void clearMeter();
|
||||
|
||||
bool isMeterChanged();
|
||||
void ackMeterChanged();
|
||||
|
||||
bool isDebugTelnet();
|
||||
void setDebugTelnet(bool debugTelnet);
|
||||
bool isDebugSerial();
|
||||
@@ -166,6 +230,8 @@ public:
|
||||
void setTempSensorPin(uint8_t tempSensorPin);
|
||||
uint8_t getVccPin();
|
||||
void setVccPin(uint8_t vccPin);
|
||||
double getVccOffset();
|
||||
void setVccOffset(double vccOffset);
|
||||
double getVccMultiplier();
|
||||
void setVccMultiplier(double vccMultiplier);
|
||||
double getVccBootLimit();
|
||||
@@ -220,6 +286,8 @@ private:
|
||||
0, // Distribution system
|
||||
0, // Main fuse
|
||||
0, // Production capacity
|
||||
{}, // Encryption key
|
||||
{}, // Authentication key
|
||||
false, // Substitute
|
||||
false, // Send unknown
|
||||
false, // Debug telnet
|
||||
@@ -235,6 +303,7 @@ private:
|
||||
true, // Inverted
|
||||
0xFF, // Temp sensor
|
||||
0xFF, // Vcc
|
||||
0, // Offset
|
||||
100, // Multiplier
|
||||
0, // Boot limit
|
||||
//Domoticz
|
||||
@@ -243,16 +312,16 @@ private:
|
||||
0, // VL2IDX
|
||||
0, // VL3IDX
|
||||
0 // CL1IDX
|
||||
// 786 bytes
|
||||
// 822 bytes
|
||||
};
|
||||
bool wifiChanged, mqttChanged, domoChanged;
|
||||
bool wifiChanged, mqttChanged, meterChanged = true, domoChanged;
|
||||
|
||||
const int EEPROM_SIZE = 790; // Config size + 4 bytes for config version
|
||||
const int EEPROM_CHECK_SUM = 82; // Used to check if config is stored. Change if structure changes
|
||||
const int EEPROM_SIZE = 1024; // Config size + 4 bytes for config version
|
||||
const int EEPROM_CHECK_SUM = 83; // Used to check if config is stored. Change if structure changes
|
||||
const int EEPROM_CONFIG_ADDRESS = 0;
|
||||
|
||||
bool loadConfig80(int address);
|
||||
bool loadConfig81(int address);
|
||||
bool loadConfig82(int address);
|
||||
|
||||
int readString(int pAddress, char* pString[]);
|
||||
int readInt(int pAddress, int *pValue);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Kaifa.h"
|
||||
#include "Aidon.h"
|
||||
#include "Kamstrup.h"
|
||||
#include "Omnipower.h"
|
||||
|
||||
AmsData::AmsData() {}
|
||||
|
||||
@@ -20,7 +21,9 @@ AmsData::AmsData(int meterType, bool substituteMissing, HanReader& hanReader) {
|
||||
case METER_TYPE_KAMSTRUP:
|
||||
extractFromKamstrup(hanReader, listSize, substituteMissing);
|
||||
break;
|
||||
|
||||
case METER_TYPE_OMNIPOWER:
|
||||
extractFromOmnipower(hanReader, listSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,6 +261,31 @@ void AmsData::extractFromKamstrup(HanReader& hanReader, int listSize, bool subst
|
||||
}
|
||||
}
|
||||
|
||||
void AmsData::extractFromOmnipower(HanReader& hanReader, int listSize) {
|
||||
switch(listSize) {
|
||||
case (int)Omnipower::DLMS:
|
||||
meterTimestamp = hanReader.getTime( (int)Omnipower_DLMS::MeterClock);
|
||||
activeImportCounter = ((double) hanReader.getInt((int)Omnipower_DLMS::CumulativeActiveImportEnergy)) / 100;
|
||||
activeExportCounter = ((double) hanReader.getInt((int)Omnipower_DLMS::CumulativeActiveExportEnergy)) / 100;
|
||||
reactiveImportCounter = ((double) hanReader.getInt((int)Omnipower_DLMS::CumulativeReactiveImportEnergy)) / 100;
|
||||
reactiveExportCounter = ((double) hanReader.getInt((int)Omnipower_DLMS::CumulativeReactiveExportEnergy)) / 100;
|
||||
listId = hanReader.getString( (int)Omnipower_DLMS::ListVersionIdentifier);
|
||||
activeImportPower = hanReader.getInt( (int)Omnipower_DLMS::ActiveImportPower);
|
||||
reactiveImportPower = hanReader.getInt( (int)Omnipower_DLMS::ReactiveImportPower);
|
||||
activeExportPower = hanReader.getInt( (int)Omnipower_DLMS::ActiveExportPower);
|
||||
reactiveExportPower = hanReader.getInt( (int)Omnipower_DLMS::ReactiveExportPower);
|
||||
l1current = ((double) hanReader.getInt((int)Omnipower_DLMS::CurrentL1)) / 100;
|
||||
l2current = ((double) hanReader.getInt((int)Omnipower_DLMS::CurrentL2)) / 100;
|
||||
l3current = ((double) hanReader.getInt((int)Omnipower_DLMS::CurrentL3)) / 100;
|
||||
l1voltage = hanReader.getInt( (int)Omnipower_DLMS::VoltageL1);
|
||||
l2voltage = hanReader.getInt( (int)Omnipower_DLMS::VoltageL2);
|
||||
l3voltage = hanReader.getInt( (int)Omnipower_DLMS::VoltageL3);
|
||||
listType = 3;
|
||||
break;
|
||||
}
|
||||
threePhase = l3voltage != 0;
|
||||
}
|
||||
|
||||
void AmsData::apply(AmsData& other) {
|
||||
this->lastUpdateMillis = other.getLastUpdateMillis();
|
||||
this->packageTimestamp = other.getPackageTimestamp();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#define METER_TYPE_KAIFA 1
|
||||
#define METER_TYPE_AIDON 2
|
||||
#define METER_TYPE_KAMSTRUP 3
|
||||
#define METER_TYPE_OMNIPOWER 4
|
||||
|
||||
class AmsData {
|
||||
public:
|
||||
@@ -62,6 +63,7 @@ private:
|
||||
void extractFromKaifa(HanReader& hanReader, int listSize);
|
||||
void extractFromAidon(HanReader& hanReader, int listSize, bool substituteMissing);
|
||||
void extractFromKamstrup(HanReader& hanReader, int listSize, bool substituteMissing);
|
||||
void extractFromOmnipower(HanReader& hanReader, int listSize);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -50,6 +50,7 @@ ADC_MODE(ADC_VCC);
|
||||
#include "Aidon.h"
|
||||
#include "Kaifa.h"
|
||||
#include "Kamstrup.h"
|
||||
#include "Omnipower.h"
|
||||
|
||||
#include "Uptime.h"
|
||||
|
||||
@@ -74,7 +75,6 @@ MQTTClient mqtt(512);
|
||||
HanReader hanReader;
|
||||
|
||||
Stream *hanSerial;
|
||||
int hanSerialPin = 0;
|
||||
|
||||
void setup() {
|
||||
if(config.hasConfig()) {
|
||||
@@ -147,10 +147,14 @@ void setup() {
|
||||
hw.ledBlink(LED_BLUE, 1);
|
||||
|
||||
if(config.getHanPin() == 3) {
|
||||
if(config.getMeterType() == 3) {
|
||||
Serial.begin(2400, SERIAL_8N1);
|
||||
} else {
|
||||
Serial.begin(2400, SERIAL_8E1);
|
||||
switch(config.getMeterType()) {
|
||||
case METER_TYPE_KAMSTRUP:
|
||||
case METER_TYPE_OMNIPOWER:
|
||||
Serial.begin(2400, SERIAL_8N1);
|
||||
break;
|
||||
default:
|
||||
Serial.begin(2400, SERIAL_8E1);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Serial.begin(115200);
|
||||
@@ -377,8 +381,9 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
if(hanSerialPin != config.getHanPin()) {
|
||||
if(config.isMeterChanged()) {
|
||||
setupHanPort(config.getHanPin(), config.getMeterType());
|
||||
config.ackMeterChanged();
|
||||
}
|
||||
|
||||
if(now - lastRead > 100) {
|
||||
@@ -390,8 +395,8 @@ void loop() {
|
||||
delay(1); // Needed for auto modem sleep
|
||||
}
|
||||
|
||||
void setupHanPort(int pin, int meterType) {
|
||||
debugI("Setting up HAN on pin %d for meter type %d", pin, meterType);
|
||||
void setupHanPort(int pin, int newMeterType) {
|
||||
debugI("Setting up HAN on pin %d for meter type %d", pin, newMeterType);
|
||||
|
||||
HardwareSerial *hwSerial = NULL;
|
||||
if(pin == 3) {
|
||||
@@ -414,33 +419,41 @@ void setupHanPort(int pin, int meterType) {
|
||||
if(hwSerial != NULL) {
|
||||
debugD("Hardware serial");
|
||||
Serial.flush();
|
||||
if(meterType == 3) {
|
||||
hwSerial->begin(2400, SERIAL_8N1);
|
||||
} else {
|
||||
hwSerial->begin(2400, SERIAL_8E1);
|
||||
switch(newMeterType) {
|
||||
case METER_TYPE_KAMSTRUP:
|
||||
case METER_TYPE_OMNIPOWER:
|
||||
hwSerial->begin(2400, SERIAL_8N1);
|
||||
break;
|
||||
default:
|
||||
hwSerial->begin(2400, SERIAL_8E1);
|
||||
break;
|
||||
}
|
||||
hanSerialPin = pin;
|
||||
hanSerial = hwSerial;
|
||||
} else {
|
||||
debugD("Software serial");
|
||||
Serial.flush();
|
||||
SoftwareSerial *swSerial = new SoftwareSerial(pin);
|
||||
|
||||
if(meterType == 3) {
|
||||
swSerial->begin(2400, SWSERIAL_8N1);
|
||||
} else {
|
||||
swSerial->begin(2400, SWSERIAL_8E1);
|
||||
switch(newMeterType) {
|
||||
case METER_TYPE_KAMSTRUP:
|
||||
case METER_TYPE_OMNIPOWER:
|
||||
swSerial->begin(2400, SWSERIAL_8N1);
|
||||
break;
|
||||
default:
|
||||
swSerial->begin(2400, SWSERIAL_8E1);
|
||||
break;
|
||||
}
|
||||
hanSerialPin = pin;
|
||||
hanSerial = swSerial;
|
||||
|
||||
Serial.begin(115200);
|
||||
}
|
||||
|
||||
hanReader.setup(hanSerial, &Debug);
|
||||
hanReader.setEncryptionKey(config.getMeterEncryptionKey());
|
||||
hanReader.setAuthenticationKey(config.getMeterAuthenticationKey());
|
||||
|
||||
// Compensate for the known Kaifa bug
|
||||
hanReader.compensateFor09HeaderBug = (config.getMeterType() == 1);
|
||||
hanReader.compensateFor09HeaderBug = (newMeterType == 1);
|
||||
|
||||
// Empty buffer before starting
|
||||
while (hanSerial->available() > 0) {
|
||||
@@ -770,33 +783,41 @@ void readHanPort() {
|
||||
}
|
||||
} else {
|
||||
// Auto detect meter if not set
|
||||
for(int i = 1; i <= 3; i++) {
|
||||
for(int i = 1; i <= 4; i++) {
|
||||
String list;
|
||||
switch(i) {
|
||||
case 1:
|
||||
case METER_TYPE_KAIFA:
|
||||
list = hanReader.getString((int) Kaifa_List1Phase::ListVersionIdentifier);
|
||||
break;
|
||||
case 2:
|
||||
case METER_TYPE_AIDON:
|
||||
list = hanReader.getString((int) Aidon_List1Phase::ListVersionIdentifier);
|
||||
break;
|
||||
case 3:
|
||||
case METER_TYPE_KAMSTRUP:
|
||||
list = hanReader.getString((int) Kamstrup_List1Phase::ListVersionIdentifier);
|
||||
break;
|
||||
case METER_TYPE_OMNIPOWER:
|
||||
list = hanReader.getString((int) Omnipower_DLMS::ListVersionIdentifier);
|
||||
break;
|
||||
}
|
||||
if(!list.isEmpty()) {
|
||||
list.toLowerCase();
|
||||
if(list.startsWith("kfm")) {
|
||||
config.setMeterType(1);
|
||||
config.setMeterType(METER_TYPE_KAIFA);
|
||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Detected Kaifa meter");
|
||||
break;
|
||||
} else if(list.startsWith("aidon")) {
|
||||
config.setMeterType(2);
|
||||
config.setMeterType(METER_TYPE_AIDON);
|
||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Detected Aidon meter");
|
||||
break;
|
||||
} else if(list.startsWith("kamstrup")) {
|
||||
config.setMeterType(3);
|
||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Detected Kamstrup meter");
|
||||
break;
|
||||
switch(i) {
|
||||
case METER_TYPE_KAMSTRUP:
|
||||
config.setMeterType(METER_TYPE_KAMSTRUP);
|
||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Detected Kamstrup meter");
|
||||
break;
|
||||
case METER_TYPE_OMNIPOWER:
|
||||
config.setMeterType(METER_TYPE_OMNIPOWER);
|
||||
if(Debug.isActive(RemoteDebug::INFO)) debugI("Detected Kamstrup meter");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ void HwTools::setVccPin(int vccPin) {
|
||||
}
|
||||
}
|
||||
|
||||
void HwTools::setVccOffset(double vccOffset) {
|
||||
this->vccOffset = vccOffset;
|
||||
}
|
||||
|
||||
void HwTools::setVccMultiplier(double vccMultiplier) {
|
||||
this->vccMultiplier = vccMultiplier;
|
||||
}
|
||||
@@ -43,7 +47,7 @@ double HwTools::getVcc() {
|
||||
#endif
|
||||
}
|
||||
|
||||
return volts > 0.0 ? volts * vccMultiplier : 0.0;
|
||||
return vccOffset + (volts > 0.0 ? volts * vccMultiplier : 0.0);
|
||||
}
|
||||
|
||||
double HwTools::getTemperature() {
|
||||
|
||||
@@ -22,6 +22,7 @@ class HwTools {
|
||||
public:
|
||||
void setTempSensorPin(int tempSensorPin);
|
||||
void setVccPin(int vccPin);
|
||||
void setVccOffset(double vccOffset);
|
||||
void setVccMultiplier(double vccMultiplier);
|
||||
double getVcc();
|
||||
double getTemperature();
|
||||
@@ -38,6 +39,7 @@ private:
|
||||
uint8_t vccPin = -1;
|
||||
uint8_t ledPin = -1, ledPinRed = -1, ledPinGreen = -1, ledPinBlue = -1;
|
||||
bool ledInverted, ledRgbInverted;
|
||||
double vccOffset = 0.0;
|
||||
double vccMultiplier = 1.0;
|
||||
bool tempSensorInit, hasTempSensor;
|
||||
OneWire *oneWire;
|
||||
|
||||
@@ -127,7 +127,7 @@ void AmsWebServer::indexHtml() {
|
||||
for(int i = 0; i<255; i++) {
|
||||
html.replace("${config.boardType" + String(i) + "}", config->getBoardType() == i ? "selected" : "");
|
||||
}
|
||||
for(int i = 0; i<4; i++) {
|
||||
for(int i = 0; i<5; i++) {
|
||||
html.replace("${config.meterType" + String(i) + "}", config->getMeterType() == i ? "selected" : "");
|
||||
}
|
||||
html.replace("${config.wifiSsid}", config->getWifiSsid());
|
||||
@@ -218,7 +218,7 @@ void AmsWebServer::configMeterHtml() {
|
||||
server.sendHeader("Pragma", "no-cache");
|
||||
|
||||
html.replace("${config.meterType}", String(config->getMainFuse()));
|
||||
for(int i = 0; i<4; i++) {
|
||||
for(int i = 0; i<5; i++) {
|
||||
html.replace("${config.meterType" + String(i) + "}", config->getMeterType() == i ? "selected" : "");
|
||||
}
|
||||
html.replace("${config.distributionSystem}", String(config->getDistributionSystem()));
|
||||
@@ -230,6 +230,31 @@ void AmsWebServer::configMeterHtml() {
|
||||
html.replace("${config.mainFuse" + String(i) + "}", config->getMainFuse() == i ? "selected" : "");
|
||||
}
|
||||
html.replace("${config.productionCapacity}", String(config->getProductionCapacity()));
|
||||
|
||||
byte encryptionKey[16];
|
||||
memcpy(encryptionKey, config->getMeterEncryptionKey(), 16);
|
||||
String encryptionKeyHex = "0x";
|
||||
for(int i = 0; i < sizeof(encryptionKey); i++) {
|
||||
if(encryptionKey[i] < 0x10) {
|
||||
encryptionKeyHex += '0';
|
||||
}
|
||||
encryptionKeyHex += String(encryptionKey[i], HEX);
|
||||
}
|
||||
encryptionKeyHex.toUpperCase();
|
||||
html.replace("${config.meterEncryptionKey}", encryptionKeyHex);
|
||||
|
||||
byte authenticationKey[16];
|
||||
memcpy(authenticationKey, config->getMeterAuthenticationKey(), 16);
|
||||
String authenticationKeyHex = "0x";
|
||||
for(int i = 0; i < sizeof(authenticationKey); i++) {
|
||||
if(authenticationKey[i] < 0x10) {
|
||||
authenticationKeyHex += '0';
|
||||
}
|
||||
authenticationKeyHex += String(authenticationKey[i], HEX);
|
||||
}
|
||||
authenticationKeyHex.toUpperCase();
|
||||
html.replace("${config.meterAuthenticationKey}", authenticationKeyHex);
|
||||
|
||||
html.replace("${config.substituteMissing}", config->isSubstituteMissing() ? "checked" : "");
|
||||
html.replace("${config.sendUnknown}", config->isSendUnknown() ? "checked" : "");
|
||||
|
||||
@@ -560,6 +585,7 @@ void AmsWebServer::handleSetup() {
|
||||
config->setVccPin(0xFF);
|
||||
|
||||
config->setBoardType(server.arg("board").toInt());
|
||||
config->setVccOffset(0.0);
|
||||
config->setVccMultiplier(1.0);
|
||||
config->setVccBootLimit(0);
|
||||
switch(config->getBoardType()) {
|
||||
@@ -665,6 +691,26 @@ void AmsWebServer::handleSave() {
|
||||
config->setProductionCapacity(server.arg("productionCapacity").toInt());
|
||||
config->setSubstituteMissing(server.hasArg("substituteMissing") && server.arg("substituteMissing") == "true");
|
||||
config->setSendUnknown(server.hasArg("sendUnknown") && server.arg("sendUnknown") == "true");
|
||||
|
||||
String encryptionKeyHex = server.arg("meterEncryptionKey");
|
||||
if(!encryptionKeyHex.isEmpty()) {
|
||||
encryptionKeyHex.replace("0x", "");
|
||||
uint8_t encryptionKey[16];
|
||||
for(int i = 0; i < 32; i += 2) {
|
||||
encryptionKey[i/2] = strtol(encryptionKeyHex.substring(i, i+2).c_str(), 0, 16);
|
||||
}
|
||||
config->setMeterEncryptionKey(encryptionKey);
|
||||
}
|
||||
|
||||
String authenticationKeyHex = server.arg("meterAuthenticationKey");
|
||||
if(!authenticationKeyHex.isEmpty()) {
|
||||
authenticationKeyHex.replace("0x", "");
|
||||
uint8_t authenticationKey[16];
|
||||
for(int i = 0; i < 32; i += 2) {
|
||||
authenticationKey[i/2] = strtol(authenticationKeyHex.substring(i, i+2).c_str(), 0, 16);
|
||||
}
|
||||
config->setMeterAuthenticationKey(authenticationKey);
|
||||
}
|
||||
}
|
||||
|
||||
if(server.hasArg("wifiConfig") && server.arg("wifiConfig") == "true") {
|
||||
@@ -741,6 +787,7 @@ void AmsWebServer::handleSave() {
|
||||
config->setApPin(server.hasArg("apPin") && !server.arg("apPin").isEmpty() ? server.arg("apPin").toInt() : 0xFF);
|
||||
config->setTempSensorPin(server.hasArg("tempSensorPin") && !server.arg("tempSensorPin").isEmpty() ?server.arg("tempSensorPin").toInt() : 0xFF);
|
||||
config->setVccPin(server.hasArg("vccPin") && !server.arg("vccPin").isEmpty() ? server.arg("vccPin").toInt() : 0xFF);
|
||||
config->setVccOffset(server.hasArg("vccOffset") && !server.arg("vccOffset").isEmpty() ? server.arg("vccOffset").toDouble() : 0.0);
|
||||
config->setVccMultiplier(server.hasArg("vccMultiplier") && !server.arg("vccMultiplier").isEmpty() ? server.arg("vccMultiplier").toDouble() : 1.0);
|
||||
config->setVccBootLimit(server.hasArg("vccBootLimit") && !server.arg("vccBootLimit").isEmpty() ? server.arg("vccBootLimit").toDouble() : 0.0);
|
||||
|
||||
@@ -778,6 +825,7 @@ void AmsWebServer::handleSave() {
|
||||
hw->setLedRgb(config->getLedPinRed(), config->getLedPinGreen(), config->getLedPinBlue(), config->isLedRgbInverted());
|
||||
hw->setTempSensorPin(config->getTempSensorPin());
|
||||
hw->setVccPin(config->getVccPin());
|
||||
hw->setVccOffset(config->getVccOffset());
|
||||
hw->setVccMultiplier(config->getVccMultiplier());
|
||||
}
|
||||
} else {
|
||||
@@ -813,6 +861,7 @@ void AmsWebServer::configSystemHtml() {
|
||||
html.replace("${config.tempSensorPin}", config->getTempSensorPin() == 0xFF ? "" : String(config->getTempSensorPin()));
|
||||
html.replace("${config.vccPin}", config->getVccPin() == 0xFF ? "" : String(config->getVccPin()));
|
||||
|
||||
html.replace("${config.vccOffset}", config->getVccOffset() > 0 ? String(config->getVccOffset(), 2) : "");
|
||||
html.replace("${config.vccMultiplier}", config->getVccMultiplier() > 0 ? String(config->getVccMultiplier(), 2) : "");
|
||||
html.replace("${config.vccBootLimit}", config->getVccBootLimit() > 0.0 ? String(config->getVccBootLimit(), 1) : "");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user