HAN serial autodetect

This commit is contained in:
Gunnar Skjold 2022-12-13 21:05:42 +01:00
parent c4eaf8184b
commit 7ea4fe881c
6 changed files with 65 additions and 25 deletions

View File

@ -218,8 +218,8 @@ bool AmsConfiguration::setMeterConfig(MeterConfig& config) {
}
void AmsConfiguration::clearMeter(MeterConfig& config) {
config.baud = 2400;
config.parity = 11; // 8E1
config.baud = 0;
config.parity = 0;
config.invert = false;
config.distributionSystem = 0;
config.mainFuse = 0;

View File

@ -225,6 +225,7 @@
<span>Serial configuration</span>
<div class="flex">
<select name="mb" bind:value={configuration.m.b} class="in-f">
<option value={0} disabled={configuration.m.b != 0}>Autodetect</option>
<option value={2400}>2400</option>
<option value={4800}>4800</option>
<option value={9600}>9600</option>
@ -233,7 +234,8 @@
<option value={57600}>57600</option>
<option value={115200}>115200</option>
</select>
<select name="mp" bind:value={configuration.m.p} class="in-l">
<select name="mp" bind:value={configuration.m.p} class="in-l" disabled={configuration.m.b == 0}>
<option value={0} disabled={configuration.m.b != 0}>-</option>
<option value={2}>7N1</option>
<option value={3}>8N1</option>
<option value={10}>7E1</option>

View File

@ -104,6 +104,9 @@ export function hanError(err) {
case -51: return "Authentication failed";
case -52: return "Decryption failed";
case -53: return "Encryption key invalid";
case 90: return "No HAN data received last 30s";
case 98: return "Exception in code, debugging necessary";
case 99: return "Autodetection failed";
}
if(err < 0) return "Unspecified error "+err;
return "";

View File

@ -60,7 +60,7 @@ private:
bool performRestart = false;
bool performUpgrade = false;
bool rebootForUpgrade = false;
String priceRegion;
String priceRegion = "";
static const uint16_t BufferSize = 2048;
char* buf;

View File

@ -273,16 +273,6 @@ void AmsWebServer::sysinfoJson() {
ESP.restart();
#endif
performRestart = false;
} else {
time_t now = time(nullptr);
if(now < BUILD_EPOCH && server.hasArg(F("t"))) {
time_t clientTime = server.arg(F("t")).toInt();
if(clientTime > BUILD_EPOCH) {
timeval tv { clientTime, 0};
settimeofday(&tv, nullptr);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("Internal clock synchronized with client\n");
}
}
}
}
@ -290,7 +280,7 @@ void AmsWebServer::dataJson() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /data.json over http...\n");
uint64_t millis = millis64();
if(!checkSecurity(2))
if(!checkSecurity(2, true))
return;
float vcc = hw->getVcc();
@ -1322,6 +1312,9 @@ void AmsWebServer::handleSave() {
void AmsWebServer::reboot() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("Serving /reboot over http...\n");
if(!checkSecurity(1))
return;
server.send(200, MIME_JSON, "{\"reboot\":true}");
server.handleClient();
@ -1407,13 +1400,13 @@ void AmsWebServer::upgrade() {
void AmsWebServer::firmwareHtml() {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(PSTR("Serving /firmware.html over http..."));
if(!checkSecurity(1))
return;
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
server.sendHeader(HEADER_EXPIRES, EXPIRES_OFF);
if(!checkSecurity(1))
return;
server.setContentLength(FIRMWARE_HTML_LEN);
server.send_P(200, MIME_HTML, FIRMWARE_HTML);
}
@ -1979,15 +1972,15 @@ void AmsWebServer::configFileDownload() {
EnergyAccountingConfig eac;
config->getEnergyAccountingConfig(eac);
EnergyAccountingData ead = ea->getData();
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("energyaccounting %d %d %.2f %.2f %.2f %.2f %d %.2f %d %.2f %d %.2f %d %.2f %d %.2f"),
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("energyaccounting %d %d %.2f %d %d %.2f %d %d %d %.2f %d %.2f %d %.2f %d %.2f %d %.2f"),
ead.version,
ead.month,
ead.costYesterday / 10.0,
ead.costThisMonth / 1.0,
ead.costLastMonth / 1.0,
ead.costThisMonth,
ead.costLastMonth,
ead.incomeYesterday / 10.0,
ead.incomeThisMonth / 1.0,
ead.incomeLastMonth / 1.0,
ead.incomeThisMonth,
ead.incomeLastMonth,
ead.peaks[0].day,
ead.peaks[0].value / 100.0,
ead.peaks[1].day,

View File

@ -233,9 +233,9 @@ void setup() {
break;
}
#if defined(ESP32)
Serial.begin(meterConfig.baud, serialConfig, -1, -1, meterConfig.invert);
Serial.begin(meterConfig.baud == 0 ? 2400 : meterConfig.baud: , serialConfig, -1, -1, meterConfig.invert);
#else
Serial.begin(meterConfig.baud, serialConfig, SERIAL_FULL, 1, meterConfig.invert);
Serial.begin(meterConfig.baud == 0 ? 2400 : meterConfig.baud, serialConfig, SERIAL_FULL, 1, meterConfig.invert);
#endif
}
@ -404,6 +404,13 @@ unsigned long lastSysupdate = 0;
unsigned long lastErrorBlink = 0;
int lastError = 0;
bool meterAutodetect = false;
unsigned long meterAutodetectLastChange = 0;
uint8_t meterAutoIndex = 0;
uint32_t bauds[] = { 2400, 2400, 115200, 115200 };
uint8_t parities[] = { 11, 3, 3, 3 };
bool inverts[] = { false, false, false, true };
void loop() {
Debug.handle();
unsigned long now = millis();
@ -593,6 +600,31 @@ void loop() {
}
} catch(const std::exception& e) {
debugE("Exception in readHanPort (%s)", e.what());
meterState.setLastError(98);
}
try {
if(meterState.getLastError() > 0) {
if(now - meterAutodetectLastChange > 15000 && (meterConfig.baud == 0 || meterConfig.parity == 0)) {
meterAutodetect = true;
meterAutoIndex++; // Default is to try the first one in setup()
debugI("Meter serial autodetect, swapping to: %d, %d, %s", bauds[meterAutoIndex], parities[meterAutoIndex], inverts[meterAutoIndex] ? "true" : "false");
if(meterAutoIndex >= 4) meterAutoIndex = 0;
setupHanPort(gpioConfig.hanPin, bauds[meterAutoIndex], parities[meterAutoIndex], inverts[meterAutoIndex]);
meterAutodetectLastChange = now;
}
} else if(meterAutodetect) {
meterAutoIndex--; // Last one worked, so lets use that
debugI("Meter serial autodetected, saving: %d, %d, %s", bauds[meterAutoIndex], parities[meterAutoIndex], inverts[meterAutoIndex] ? "true" : "false");
meterAutodetect = false;
meterConfig.baud = bauds[meterAutoIndex];
meterConfig.parity = parities[meterAutoIndex];
meterConfig.invert = inverts[meterAutoIndex];
config.setMeterConfig(meterConfig);
setupHanPort(gpioConfig.hanPin, meterConfig.baud, meterConfig.parity, meterConfig.invert);
}
} catch(const std::exception& e) {
debugE("Exception in meter autodetect (%s)", e.what());
meterState.setLastError(99);
}
delay(1); // Needed for auto modem sleep
@ -606,6 +638,15 @@ void loop() {
void setupHanPort(uint8_t pin, uint32_t baud, uint8_t parityOrdinal, bool invert) {
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);
if(baud == 0) {
baud = bauds[meterAutoIndex];
parityOrdinal = parities[meterAutoIndex];
invert = inverts[meterAutoIndex];
}
if(parityOrdinal == 0) {
parityOrdinal = 3; // 8N1
}
HardwareSerial *hwSerial = NULL;
if(pin == 3 || pin == 113) {
hwSerial = &Serial;
@ -723,6 +764,7 @@ void errorBlink() {
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
if(meterState.getLastError() == 0) meterState.setLastError(90);
return;
}
break;