Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions localazy/source/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,16 @@
"pulse": "Pulse",
"title": "Communication"
},
"c1218": {
"extended": "Read extended table 28",
"idle": "Daily idle time (s, 0 disables)",
"idle_start": "Daily idle start",
"logoff": "Logoff interval (s)",
"terminate": "Terminate session after each read",
"password": "Password",
"userid": "User ID",
"username": "Username"
},
"encrypted": "Meter is encrypted",
"fuse": "Main fuse",
"inverted": "inverted",
Expand Down Expand Up @@ -254,6 +264,7 @@
"94": "Serial frame error",
"95": "Serial parity error",
"96": "RX error",
"97": "ANSI C12.18 communication error",
"98": "Exception in code, debugging necessary",
"99": "Autodetection failed"
},
Expand Down
37 changes: 37 additions & 0 deletions src/AmsConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,43 @@ void AmsConfiguration::clearMeter(MeterConfig& config) {
config.bufferSize = 1; // 64 bytes
}

bool AmsConfiguration::getC1218Config(C1218Config& config) {
static_assert(CONFIG_C1218_START + sizeof(C1218Config) <= EEPROM_SIZE, "C12.18 config exceeds EEPROM");
EEPROM.get(CONFIG_C1218_START, config);
if(config.magic != C1218_CONFIG_MAGIC && config.magic != C1218_CONFIG_MAGIC_LEGACY && config.magic != C1218_CONFIG_MAGIC_LEGACY_V0) {
clearC1218Config(config);
return false;
}
config.username[10] = 0;
config.password[20] = 0;
if(config.magic != C1218_CONFIG_MAGIC) {
config.terminateSession = false;
config.logoffInterval = 300;
config.idleStartSeconds = 7800;
config.idleSeconds = 0;
}
return true;
}

bool AmsConfiguration::setC1218Config(C1218Config& config) {
C1218Config existing;
getC1218Config(existing);
config.magic = C1218_CONFIG_MAGIC;
config.username[10] = 0;
config.password[20] = 0;
meterChanged |= memcmp(&config, &existing, sizeof(C1218Config)) != 0;
EEPROM.put(CONFIG_C1218_START, config);
return meterChanged;
}

void AmsConfiguration::clearC1218Config(C1218Config& config) {
memset(&config, 0, sizeof(C1218Config));
config.userId = 1;
config.extendedTable28 = true;
config.logoffInterval = 300;
config.idleStartSeconds = 7800;
}

bool AmsConfiguration::isMeterChanged() {
return meterChanged;
}
Expand Down
21 changes: 20 additions & 1 deletion src/AmsConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
#define CONFIG_CLOUD_START 1742
#define CONFIG_UPGRADE_INFO_START 1934
#define CONFIG_ZC_START 2000
#define CONFIG_C1218_START 2096

#define C1218_CONFIG_MAGIC 0x121A
#define C1218_CONFIG_MAGIC_LEGACY 0x1219
#define C1218_CONFIG_MAGIC_LEGACY_V0 0x1218

#define CONFIG_METER_START_103 32
#define CONFIG_UPGRADE_INFO_START_103 216
Expand Down Expand Up @@ -161,6 +166,18 @@ struct MeterConfig {
uint8_t txPin;
}; // 65

struct C1218Config {
uint16_t magic;
uint16_t userId;
char username[11];
char password[21];
bool extendedTable28;
bool terminateSession;
uint16_t logoffInterval;
uint32_t idleStartSeconds;
uint16_t idleSeconds;
};

struct DebugConfig {
bool telnet;
bool serial;
Expand Down Expand Up @@ -327,6 +344,9 @@ class AmsConfiguration {
bool getMeterConfig(MeterConfig&);
bool setMeterConfig(MeterConfig&);
void clearMeter(MeterConfig&);
bool getC1218Config(C1218Config&);
bool setC1218Config(C1218Config&);
void clearC1218Config(C1218Config&);
void setMeterChanged();
bool isMeterChanged();
void ackMeterChanged();
Expand Down Expand Up @@ -411,4 +431,3 @@ class AmsConfiguration {
void deleteFromFs(uint8_t version);
};
#endif

14 changes: 13 additions & 1 deletion src/AmsData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ void AmsData::apply(AmsData& other) {
strncpy(this->meterId, other.meterId, sizeof(this->meterId) - 1);
this->meterType = other.getMeterType();
strncpy(this->meterModel, other.meterModel, sizeof(this->meterModel) - 1);
strncpy(this->meterManufacturer, other.meterManufacturer, sizeof(this->meterManufacturer) - 1);
this->reactiveImportPower = other.getReactiveImportPower();
this->reactiveExportPower = other.getReactiveExportPower();
this->l1current = other.getL1Current();
Expand Down Expand Up @@ -317,6 +318,17 @@ String AmsData::getMeterModel() {
return String(this->meterModel);
}

String AmsData::getMeterManufacturer() {
return String(this->meterManufacturer);
}

void AmsData::setMeterInfo(uint8_t type, const char* manufacturer, const char* model, const char* id) {
meterType = type;
strlcpy(meterManufacturer, manufacturer, sizeof(meterManufacturer));
strlcpy(meterModel, model, sizeof(meterModel));
strlcpy(meterId, id, sizeof(meterId));
}

time_t AmsData::getMeterTimestamp() {
return this->meterTimestamp;
}
Expand Down Expand Up @@ -484,4 +496,4 @@ void AmsData::setLastError(int8_t lastError) {
} else {
lastErrorCount++;
}
}
}
3 changes: 3 additions & 0 deletions src/AmsData.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class AmsData {
String getMeterId();
uint8_t getMeterType();
String getMeterModel();
String getMeterManufacturer();
void setMeterInfo(uint8_t type, const char* manufacturer, const char* model, const char* id);

time_t getMeterTimestamp();

Expand Down Expand Up @@ -102,6 +104,7 @@ class AmsData {
char listId[32] = {};
char meterId[32] = {};
char meterModel[65] = {};
char meterManufacturer[17] = {};
time_t meterTimestamp = 0;
uint32_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0;
float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0;
Expand Down
16 changes: 15 additions & 1 deletion src/AmsJsonGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ void AmsJsonGenerator::generateConfigurationJson(AmsConfiguration* config, JsonS

MeterConfig meterConfig;
config->getMeterConfig(meterConfig);
C1218Config c1218Config;
config->getC1218Config(c1218Config);

NetworkConfig networkConfig;
config->getNetworkConfig(networkConfig);
Expand Down Expand Up @@ -135,6 +137,18 @@ void AmsJsonGenerator::generateConfigurationJson(AmsConfiguration* config, JsonS
meterConfig.amperageMultiplier == 0.0 ? 1.0 : meterConfig.amperageMultiplier / 1000.0,
meterConfig.accumulatedMultiplier == 0.0 ? 1.0 : meterConfig.accumulatedMultiplier / 1000.0
);
EMIT(PSTR(",\"c\":{\"u\":%u,\"n\":\"%s\",\"p\":\"%s\",\"x\":%s,\"t\":%s,\"l\":%u,\"i\":\"%02u:%02u:%02u\",\"d\":%u}"),
c1218Config.userId,
c1218Config.username,
strlen(c1218Config.password) > 0 ? "***" : "",
c1218Config.extendedTable28 ? "true" : "false",
c1218Config.terminateSession ? "true" : "false",
c1218Config.logoffInterval,
c1218Config.idleStartSeconds / SECS_PER_HOUR,
(c1218Config.idleStartSeconds / SECS_PER_MIN) % SECS_PER_MIN,
c1218Config.idleStartSeconds % SECS_PER_MIN,
c1218Config.idleSeconds
);

EMIT(PSTR("}")); // End of meter

Expand Down Expand Up @@ -304,4 +318,4 @@ void AmsJsonGenerator::generateConfigurationJson(AmsConfiguration* config, JsonS
void AmsJsonGenerator::generateConfigurationJson(AmsConfiguration* config, char* buf, size_t bufSize) {
BufferJsonSink sink(buf, bufSize);
generateConfigurationJson(config, sink);
}
}
36 changes: 35 additions & 1 deletion src/AmsToMqttBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ ADC_MODE(ADC_VCC);
#define METER_PARSER_PASSIVE 0
#define METER_PARSER_PULSE 2
#define METER_PARSER_KAMSTRUP 9
#define METER_PARSER_C1218 18

#define METER_ERROR_UNKNOWN_DATA 89
#define METER_ERROR_NO_DATA 90
Expand Down Expand Up @@ -99,6 +100,9 @@ ADC_MODE(ADC_VCC);
#include "KmpCommunicator.h"
#endif
#include "PulseMeterCommunicator.h"
#if defined(ESP32)
#include "C1218MeterCommunicator.h"
#endif

#include "Uptime.h"
#include "NtpStatus.h"
Expand Down Expand Up @@ -277,6 +281,9 @@ PassiveMeterCommunicator* passiveMc = NULL;
KmpCommunicator* kmpMc = NULL;
#endif
PulseMeterCommunicator* pulseMc = NULL;
#if defined(ESP32)
C1218MeterCommunicator* c1218Mc = NULL;
#endif


bool networkConnected = false;
Expand Down Expand Up @@ -1188,6 +1195,9 @@ void handleMeterConfig() {
if(meterConfig.source == METER_SOURCE_GPIO) {
switch(meterConfig.parser) {
case METER_PARSER_PASSIVE:
#if defined(ESP32)
if(c1218Mc != NULL) { delete c1218Mc; c1218Mc = NULL; }
#endif
if(pulseMc != NULL) {
delete pulseMc;
pulseMc = NULL;
Expand All @@ -1206,6 +1216,9 @@ void handleMeterConfig() {
mc = passiveMc;
break;
case METER_PARSER_KAMSTRUP:
#if defined(ESP32)
if(c1218Mc != NULL) { delete c1218Mc; c1218Mc = NULL; }
#endif
if(pulseMc != NULL) {
delete pulseMc;
pulseMc = NULL;
Expand All @@ -1224,6 +1237,9 @@ void handleMeterConfig() {
#endif
break;
case METER_PARSER_PULSE:
#if defined(ESP32)
if(c1218Mc != NULL) { delete c1218Mc; c1218Mc = NULL; }
#endif
#if defined(AMS_KMP)
if(kmpMc != NULL) {
delete(kmpMc);
Expand All @@ -1242,6 +1258,19 @@ void handleMeterConfig() {

mc = pulseMc;
break;
#if defined(ESP32)
case METER_PARSER_C1218:
if(pulseMc != NULL) { delete pulseMc; pulseMc = NULL; }
if(passiveMc != NULL) { delete passiveMc; passiveMc = NULL; }
#if defined(AMS_KMP)
if(kmpMc != NULL) { delete kmpMc; kmpMc = NULL; }
#endif
if(c1218Mc == NULL) c1218Mc = new C1218MeterCommunicator(&Debug, &config);
c1218Mc->configure(meterConfig, tz);
hwSerial = c1218Mc->getHwSerial();
mc = c1218Mc;
break;
#endif
default:
debugE_P(PSTR("Unknown meter parser selected: %d"), meterConfig.parser);
}
Expand Down Expand Up @@ -1552,6 +1581,11 @@ void connectToNetwork() {
NetworkConfig network;
if(config.getNetworkConfig(network)) {
if(network.mode == 0 || network.mode > 3) network.mode = NETWORK_MODE_WIFI_CLIENT;
if(network.mode != NETWORK_MODE_ETH_CLIENT && strlen(network.ssid) == 0) {
setupMode = false;
toggleSetupMode();
return;
}
if(ch != NULL && ch->getMode() != network.mode) {
delete ch;
ch = NULL;
Expand Down Expand Up @@ -2655,4 +2689,4 @@ void configFileParse() {

void IRAM_ATTR onPulse() {
pulses++;
}
}
29 changes: 26 additions & 3 deletions src/AmsWebServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,9 @@ void AmsWebServer::sysinfoJson() {
String meterModel = meterState->getMeterModel();
if(!meterModel.isEmpty())
meterModel.replace(F("\\"), F("\\\\"));
String meterManufacturer = meterState->getMeterManufacturer();
if(!meterManufacturer.isEmpty())
meterManufacturer.replace(F("\\"), F("\\\\"));

String meterId = meterState->getMeterId();
if(!meterId.isEmpty())
Expand Down Expand Up @@ -662,6 +665,7 @@ void AmsWebServer::sysinfoJson() {
#endif
sys.boardType > 240 && sys.boardType < 250 ? "true" : "false",
meterState->getMeterType(),
meterManufacturer.c_str(),
meterModel.c_str(),
meterId.c_str(),
ui.showImport,
Expand Down Expand Up @@ -1249,6 +1253,7 @@ void AmsWebServer::handleSave() {
config->getSystemConfig(sys);

bool success = true;
if(server.arg(F("s")) == F("true") && server.arg(F("sc")).toInt() != 3 && server.arg(F("ss")).isEmpty()) success = false;
if(server.hasArg(F("v")) && server.arg(F("v")) == F("true")) {
int boardType = server.arg(F("vb")).toInt();
int hanPin = server.arg(F("vh")).toInt();
Expand All @@ -1267,7 +1272,7 @@ void AmsWebServer::handleSave() {
}
}

if(server.hasArg(F("s")) && server.arg(F("s")) == F("true")) {
if(success && server.hasArg(F("s")) && server.arg(F("s")) == F("true")) {
MeterConfig meterConfig;
config->getMeterConfig(meterConfig);

Expand Down Expand Up @@ -1380,6 +1385,24 @@ void AmsWebServer::handleSave() {
meterConfig.amperageMultiplier = server.arg(F("mma")).toDouble() * 1000.0;
meterConfig.accumulatedMultiplier = server.arg(F("mmc")).toDouble() * 1000.0;
config->setMeterConfig(meterConfig);

if(meterConfig.parser == 18) {
C1218Config c1218;
config->getC1218Config(c1218);
c1218.userId = server.arg(F("mcu")).toInt();
strlcpy(c1218.username, server.arg(F("mcn")).c_str(), sizeof(c1218.username));
String password = server.arg(F("mcp"));
if(password != F("***")) strlcpy(c1218.password, password.c_str(), sizeof(c1218.password));
c1218.extendedTable28 = server.hasArg(F("mcx")) && server.arg(F("mcx")) == F("true");
c1218.terminateSession = server.hasArg(F("mct")) && server.arg(F("mct")) == F("true");
c1218.logoffInterval = constrain(server.arg(F("mcl")).toInt(), 1, 86400);
unsigned int hour, minute, second;
if(sscanf(server.arg(F("mci")).c_str(), "%u:%u:%u", &hour, &minute, &second) == 3 && hour < 24 && minute < 60 && second < 60) {
c1218.idleStartSeconds = hour * SECS_PER_HOUR + minute * SECS_PER_MIN + second;
}
c1218.idleSeconds = constrain(server.arg(F("mcd")).toInt(), 0, 86400);
config->setC1218Config(c1218);
}
}

if(server.hasArg(F("w")) && server.arg(F("w")) == F("true")) {
Expand Down Expand Up @@ -1737,9 +1760,9 @@ void AmsWebServer::handleSave() {
debugger->printf_P(PSTR("Saving configuration now...\n"));

// If vendor page and clear all config is selected
if(server.hasArg(F("v")) && server.arg(F("v")) == F("true") && server.hasArg(F("vr")) && server.arg(F("vr")) == F("true")) {
if(success && server.hasArg(F("v")) && server.arg(F("v")) == F("true") && server.hasArg(F("vr")) && server.arg(F("vr")) == F("true")) {
config->clear();
} else if(config->save()) {
} else if(success && config->save()) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
Expand Down
Loading