Added remote debugging

This commit is contained in:
Gunnar Skjold 2020-03-24 20:17:44 +01:00
parent d747c84a14
commit 73d00f786a
11 changed files with 346 additions and 243 deletions

View File

@ -4,11 +4,11 @@ HanReader::HanReader()
{
}
void HanReader::setup(Stream *hanPort, Stream *debugPort)
void HanReader::setup(Stream *hanPort, RemoteDebug *debug)
{
han = hanPort;
bytesRead = 0;
debug = debugPort;
debugger = debug;
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
@ -23,26 +23,22 @@ void HanReader::setup(Stream *hanPort)
setup(hanPort, NULL);
}
bool HanReader::read(byte data)
{
if (reader.Read(data))
{
bool HanReader::read(byte data) {
if (reader.Read(data)) {
bytesRead = reader.GetRawData(buffer, 0, 512);
if (debug)
{
debug->print("Got valid DLMS data (");
debug->print(bytesRead);
debug->println(" bytes):");
debugPrint(buffer, 0, bytesRead);
if (debugger->isActive(RemoteDebug::INFO)) {
printI("Got valid DLMS data (%d bytes)", bytesRead);
if (debugger->isActive(RemoteDebug::DEBUG)) {
debugPrint(buffer, 0, bytesRead);
}
}
/*
Data should start with E6 E7 00 0F
and continue with four bytes for the InvokeId
*/
if (bytesRead < 9)
{
if (debug) debug->println("Invalid HAN data: Less than 9 bytes received");
if (bytesRead < 9) {
printW("Invalid HAN data: Less than 9 bytes received");
return false;
}
else if (
@ -52,39 +48,35 @@ bool HanReader::read(byte data)
buffer[3] != 0x0F
)
{
if (debug) debug->println("Invalid HAN data: Start should be E6 E7 00 0F");
printW("Invalid HAN data: Start should be E6 E7 00 0F");
return false;
}
listSize = getInt(0, buffer, 0, bytesRead);
if (debug) debug->print("HAN data is valid, listSize: ");
if (debug) debug->println(listSize);
printI("HAN data is valid, listSize: %d", listSize);
return true;
}
return false;
}
void HanReader::debugPrint(byte *buffer, int start, int length)
{
for (int i = start; i < start + length; i++)
{
void HanReader::debugPrint(byte *buffer, int start, int length) {
for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debug->print("0");
debug->print(buffer[i], HEX);
debug->print(" ");
debugger->print("0");
debugger->print(buffer[i], HEX);
debugger->print(" ");
if ((i - start + 1) % 16 == 0)
debug->println("");
debugger->println("");
else if ((i - start + 1) % 4 == 0)
debug->print(" ");
debugger->print(" ");
yield(); // Let other get some resources too
}
debug->println("");
debugger->println("");
}
bool HanReader::read()
{
bool HanReader::read() {
while(han->available()) {
if(read(han->read())) {
return true;
@ -93,44 +85,37 @@ bool HanReader::read()
return false;
}
int HanReader::getListSize()
{
int HanReader::getListSize() {
return listSize;
}
time_t HanReader::getPackageTime()
{
time_t HanReader::getPackageTime() {
int packageTimePosition = dataHeader
+ (compensateFor09HeaderBug ? 1 : 0);
return getTime(buffer, packageTimePosition, bytesRead);
}
time_t HanReader::getTime(int objectId)
{
time_t HanReader::getTime(int objectId) {
return getTime(objectId, buffer, 0, bytesRead);
}
int HanReader::getInt(int objectId)
{
int HanReader::getInt(int objectId) {
return getInt(objectId, buffer, 0, bytesRead);
}
String HanReader::getString(int objectId)
{
String HanReader::getString(int objectId) {
return getString(objectId, buffer, 0, bytesRead);
}
int HanReader::findValuePosition(int dataPosition, byte *buffer, int start, int length)
{
int HanReader::findValuePosition(int dataPosition, byte *buffer, int start, int length) {
// The first byte after the header gives the length
// of the extended header information (variable)
int headerSize = dataHeader + (compensateFor09HeaderBug ? 1 : 0);
int firstData = headerSize + buffer[headerSize] + 1;
for (int i = start + firstData; i<length; i++)
{
for (int i = start + firstData; i<length; i++) {
if (dataPosition-- == 0)
return i;
else if (buffer[i] == 0x00) // null
@ -153,37 +138,26 @@ int HanReader::findValuePosition(int dataPosition, byte *buffer, int start, int
i += 1;
else if (buffer[i] == 0x16) // enum (1 bytes)
i += 1;
else
{
if (debug)
{
debug->print("Unknown data type found: 0x");
debug->println(buffer[i], HEX);
}
else {
printW("Unknown data type found: 0x%s", String(buffer[i], HEX).c_str());
return 0; // unknown data type found
}
}
if (debug)
{
debug->print("Passed the end of the data. Length was: ");
debug->println(length);
}
printD("Passed the end of the data. Length was: %d", length);
return 0;
}
time_t HanReader::getTime(int dataPosition, byte *buffer, int start, int length)
{
time_t HanReader::getTime(int dataPosition, byte *buffer, int start, int length) {
// TODO: check if the time is represented always as a 12 byte string (0x09 0x0C)
int timeStart = findValuePosition(dataPosition, buffer, start, length);
timeStart += 1;
return getTime(buffer, start + timeStart, length - timeStart);
}
time_t HanReader::getTime(byte *buffer, int start, int length)
{
time_t HanReader::getTime(byte *buffer, int start, int length) {
int pos = start;
int dataLength = buffer[pos++];
@ -208,25 +182,19 @@ time_t HanReader::getTime(byte *buffer, int start, int length)
} else if(dataLength == 0) {
return (time_t)0L;
} else {
if(debug) {
debug->print("Unknown time length: ");
debug->println(dataLength);
}
printW("Unknown time length: %d", dataLength);
// Date format not supported
return (time_t)0L;
}
}
int HanReader::getInt(int dataPosition, byte *buffer, int start, int length)
{
int HanReader::getInt(int dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0)
{
if (valuePosition > 0) {
int value = 0;
int bytes = 0;
switch (buffer[valuePosition++])
{
switch (buffer[valuePosition++]) {
case 0x10:
bytes = 2;
break;
@ -250,8 +218,7 @@ int HanReader::getInt(int dataPosition, byte *buffer, int start, int length)
break;
}
for (int i = valuePosition; i < valuePosition + bytes; i++)
{
for (int i = valuePosition; i < valuePosition + bytes; i++) {
value = value << 8 | buffer[i];
}
@ -260,17 +227,42 @@ int HanReader::getInt(int dataPosition, byte *buffer, int start, int length)
return 0;
}
String HanReader::getString(int dataPosition, byte *buffer, int start, int length)
{
String HanReader::getString(int dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0)
{
if (valuePosition > 0) {
String value = String("");
for (int i = valuePosition + 2; i < valuePosition + buffer[valuePosition + 1] + 2; i++)
{
for (int i = valuePosition + 2; i < valuePosition + buffer[valuePosition + 1] + 2; i++) {
value += String((char)buffer[i]);
}
return value;
}
return String("");
}
void HanReader::printD(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), args);
va_end(args);
}
void HanReader::printI(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), args);
va_end(args);
}
void HanReader::printW(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), args);
va_end(args);
}
void HanReader::printE(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), args);
va_end(args);
}

View File

@ -10,6 +10,7 @@
#include "DlmsReader.h"
#include <Timezone.h>
#include "RemoteDebug.h"
class HanReader
{
@ -19,7 +20,7 @@ public:
HanReader();
void setup(Stream *hanPort);
void setup(Stream *hanPort, Stream *debugPort);
void setup(Stream *hanPort, RemoteDebug *debug);
bool read();
bool read(byte data);
int getListSize();
@ -29,7 +30,7 @@ public:
time_t getTime(int objectId);
private:
Stream *debug;
RemoteDebug* debugger;
Stream *han;
byte buffer[512];
int bytesRead;
@ -47,6 +48,11 @@ private:
time_t toUnixTime(int year, int month, int day, int hour, int minute, int second);
void debugPrint(byte *buffer, int start, int length);
void printD(String fmt, ...);
void printI(String fmt, ...);
void printW(String fmt, ...);
void printE(String fmt, ...);
};

View File

@ -4,7 +4,7 @@ extra_configs = platformio-user.ini
[common]
framework = arduino
lib_deps = HanReader@1.0.1, ArduinoJson@6.14.1, MQTT@2.4.7, DallasTemperature@3.8.1, EspSoftwareSerial@6.7.1, Base64@1.0.0
lib_deps = HanReader@1.0.1, ArduinoJson@6.14.1, MQTT@2.4.7, DallasTemperature@3.8.1, EspSoftwareSerial@6.7.1, Base64@1.0.0, RemoteDebug@3.0.5
[env:hw1esp12e]
platform = espressif8266@2.3.3

View File

@ -241,6 +241,30 @@ void AmsConfiguration::setProductionCapacity(int productionCapacity) {
this->productionCapacity = productionCapacity;
}
bool AmsConfiguration::isDebugTelnet() {
return this->debugTelnet;
}
void AmsConfiguration::setDebugTelnet(bool debugTelnet) {
this->debugTelnet = debugTelnet;
}
bool AmsConfiguration::isDebugSerial() {
return this->debugSerial;
}
void AmsConfiguration::setDebugSerial(bool debugSerial) {
this->debugSerial = debugSerial;
}
int AmsConfiguration::getDebugLevel() {
return this->debugLevel;
}
void AmsConfiguration::setDebugLevel(int debugLevel) {
this->debugLevel = debugLevel;
}
bool AmsConfiguration::hasConfig() {
if(configVersion == 0) {
@ -338,6 +362,10 @@ bool AmsConfiguration::loadConfig72(int address) {
ackWifiChange();
setDebugLevel(5); // 5=Error
setDebugTelnet(false);
setDebugSerial(false);
return true;
}
@ -405,6 +433,10 @@ bool AmsConfiguration::loadConfig75(int address) {
ackWifiChange();
setDebugLevel(5); // 5=Error
setDebugTelnet(false);
setDebugSerial(false);
return true;
}
@ -476,6 +508,10 @@ bool AmsConfiguration::loadConfig80(int address) {
ackWifiChange();
setDebugLevel(5); // 5=Error
setDebugTelnet(false);
setDebugSerial(false);
return true;
}
@ -557,6 +593,15 @@ bool AmsConfiguration::loadConfig81(int address) {
address += readInt(address, &i);
setProductionCapacity(i);
bool debugTelnet = false;
address += readBool(address, &debugTelnet);
setDebugTelnet(debugTelnet);
bool debugSerial = false;
address += readBool(address, &debugSerial);
setDebugSerial(debugSerial);
address += readInt(address, &i);
setDebugLevel(i);
ackWifiChange();
return true;
@ -612,6 +657,10 @@ bool AmsConfiguration::save() {
address += saveInt(address, mainFuse);
address += saveInt(address, productionCapacity);
address += saveBool(address, debugTelnet);
address += saveBool(address, debugSerial);
address += saveInt(address, debugLevel);
bool success = EEPROM.commit();
EEPROM.end();
@ -704,7 +753,7 @@ template <class T> int AmsConfiguration::readAnything(int ee, T& value) {
return i;
}
void AmsConfiguration::print(Stream* debugger)
void AmsConfiguration::print(Print* debugger)
{
debugger->println("Configuration:");
debugger->println("-----------------------------------------------");

View File

@ -70,7 +70,14 @@ public:
int getProductionCapacity();
void setProductionCapacity(int productionCapacity);
void print(Stream* debugger);
bool isDebugTelnet();
void setDebugTelnet(bool debugTelnet);
bool isDebugSerial();
void setDebugSerial(bool debugSerial);
int getDebugLevel();
void setDebugLevel(int debugLevel);
void print(Print* debugger);
protected:
@ -103,6 +110,9 @@ private:
int meterType, distributionSystem, mainFuse, productionCapacity;
bool debugTelnet, debugSerial;
int debugLevel;
const int EEPROM_SIZE = 512;
const int EEPROM_CHECK_SUM = 81; // Used to check if config is stored. Change if structure changes
const int EEPROM_CONFIG_ADDRESS = 0;

View File

@ -169,7 +169,6 @@ void AmsData::extractFromAidon(HanReader& hanReader, int listSize) {
l2voltage = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL2)) / 10;
l3voltage = ((double) hanReader.getInt( (int)Aidon_List3PhaseIT::VoltageL3)) / 10;
//l2current = ((activeImportPower * sqrt(3)) - (l1voltage * l1current) - (l3voltage * l3current)) / l2voltage;
threePhase = true;
break;
}
}

View File

@ -16,22 +16,19 @@
#include "Update.h"
#endif
#define RGB_RED 1
#define RGB_GREEN 2
#define RGB_YELLOW 3
#define RGB_ON 1
#define RGB_OFF 0
// Build settings for custom hardware by Roar Fredriksen
#if HW_ROARFRED
#define LED_PIN 2 // The blue on-board LED of the ESP8266 custom AMS board
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN 0
#if DEBUG_MODE
#if SOFTWARE_SERIAL
#include <SoftwareSerial.h>
SoftwareSerial *hanSerial = new SoftwareSerial(3);
#else
HardwareSerial *hanSerial = &Serial;
#endif
#else
HardwareSerial *hanSerial = &Serial;
#endif
// Build settings for Wemos Lolin D32
#elif defined(ARDUINO_LOLIN_D32)

View File

@ -38,18 +38,21 @@ ADC_MODE(ADC_VCC);
#include "Uptime.h"
#define WEBSOCKET_DISABLED true
#include "RemoteDebug.h"
HwTools hw;
DNSServer dnsServer;
AmsConfiguration config;
AmsWebServer ws;
RemoteDebug Debug;
AmsWebServer ws(&Debug);
WiFiClient *client;
MQTTClient mqtt(384);
Stream* debugger = NULL;
MQTTClient mqtt(512);
HanReader hanReader;
@ -58,41 +61,43 @@ void setup() {
config.load();
}
#if DEBUG_MODE
#if HW_ROARFRED
#if SOFTWARE_SERIAL
SoftwareSerial *ser = new SoftwareSerial(-1, 1);
ser->begin(115200, SWSERIAL_8N1);
debugger = ser;
#else
HardwareSerial *ser = &Serial;
if(config.getMeterType() == 3) {
ser->begin(2400, SERIAL_8N1);
Serial.begin(2400, SERIAL_8N1);
} else {
ser->begin(2400, SERIAL_8E1);
Serial.begin(2400, SERIAL_8E1);
}
#endif
#else
HardwareSerial *ser = &Serial;
ser->begin(115200, SERIAL_8N1);
Serial.begin(115200);
#endif
debugger = ser;
if(config.hasConfig()) {
Debug.begin(config.getWifiHostname(), (uint8_t) config.getDebugLevel());
if(config.getAuthSecurity() > 0) {
Debug.setPassword(config.getAuthPassword());
}
Debug.setSerialEnabled(config.isDebugSerial());
if(!config.isDebugTelnet()) {
Debug.stop();
}
} else {
#if DEBUG_MODE
Debug.begin("localhost", RemoteDebug::DEBUG);
Debug.setSerialEnabled(true);
#endif
}
double vcc = hw.getVcc();
if (debugger) {
debugger->println("");
debugger->println("Started...");
debugger->print("Voltage: ");
debugger->print(vcc);
debugger->println("V");
if (Debug.isActive(RemoteDebug::INFO)) {
debugI("AMS bridge started");
debugI("Voltage: %dV", vcc);
}
if (vcc > 0 && vcc < 3.1) {
if(debugger) {
debugger->println("Voltage is too low, sleeping");
debugger->flush();
if(Debug.isActive(RemoteDebug::INFO)) {
debugI("Votltage is too low, sleeping");
Debug.flush();
}
ESP.deepSleep(10000000); //Deep sleep to allow output cap to charge up
}
@ -115,14 +120,14 @@ void setup() {
if(SPIFFS.begin()) {
bool flashed = false;
if(SPIFFS.exists("/firmware.bin")) {
if(debugger) debugger->println("Found firmware");
if(Debug.isActive(RemoteDebug::INFO)) debugI("Found firmware");
#if defined(ESP8266)
WiFi.setSleepMode(WIFI_LIGHT_SLEEP);
WiFi.forceSleepBegin();
#endif
int i = 0;
while(hw.getVcc() < 3.3 && i < 3) {
if(debugger) debugger->println(" vcc not optimal, light sleep 10s");
if(Debug.isActive(RemoteDebug::INFO)) debugI(" vcc not optimal, light sleep 10s");
#if defined(ESP8266)
delay(10000);
#elif defined(ESP32)
@ -132,13 +137,13 @@ void setup() {
i++;
}
if(debugger) debugger->println(" flashing");
if(Debug.isActive(RemoteDebug::INFO)) debugI(" flashing");
File firmwareFile = SPIFFS.open("/firmware.bin", "r");
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if (!Update.begin(maxSketchSpace, U_FLASH)) {
if(debugger) {
debugger->println("Unable to start firmware update");
Update.printError(*debugger);
if(Debug.isActive(RemoteDebug::ERROR)) {
debugE("Unable to start firmware update");
Update.printError(Serial);
}
} else {
while (firmwareFile.available()) {
@ -153,9 +158,9 @@ void setup() {
}
SPIFFS.end();
if(flashed) {
if(debugger) {
debugger->println("Firmware update complete, restarting");
debugger->flush();
if(Debug.isActive(RemoteDebug::INFO)) {
debugI("Firmware update complete, restarting");
Debug.flush();
}
#if defined(ESP8266)
ESP.reset();
@ -177,27 +182,27 @@ void setup() {
}
if(config.hasConfig()) {
if(debugger) config.print(debugger);
if(Debug.isActive(RemoteDebug::INFO)) config.print(&Debug);
WiFi_connect();
client = new WiFiClient();
} else {
if(debugger) {
debugger->println("No configuration, booting AP");
if(Debug.isActive(RemoteDebug::INFO)) {
debugI("No configuration, booting AP");
}
swapWifiMode();
}
#if SOFTWARE_SERIAL
if(debugger) debugger->println("HAN has software serial");
if(Debug.isActive(RemoteDebug::DEBUG)) debugD("HAN has software serial");
if(config.getMeterType() == 3) {
hanSerial->begin(2400, SWSERIAL_8N1);
} else {
hanSerial->begin(2400, SWSERIAL_8E1);
}
#else
if(debugger) {
debugger->println("HAN has hardware serial");
debugger->flush();
if(Debug.isActive(RemoteDebug::DEBUG)) {
debugD("HAN has hardware serial");
Debug.flush();
}
if(config.getMeterType() == 3) {
hanSerial->begin(2400, SERIAL_8N1);
@ -209,7 +214,7 @@ void setup() {
#endif
#endif
hanReader.setup(hanSerial, debugger);
hanReader.setup(hanSerial, &Debug);
// Compensate for the known Kaifa bug
hanReader.compensateFor09HeaderBug = (config.getMeterType() == 1);
@ -219,7 +224,7 @@ void setup() {
hanSerial->read();
}
ws.setup(&config, debugger, &mqtt);
ws.setup(&config, &mqtt);
#if HAS_RGB_LED
//Signal startup by blinking red / green / yellow
@ -249,6 +254,7 @@ unsigned long lastErrorBlink = 0;
int lastError = 0;
void loop() {
Debug.handle();
unsigned long now = millis();
if(AP_BUTTON_PIN != INVALID_BUTTON_PIN) {
if (digitalRead(AP_BUTTON_PIN) == LOW) {
@ -292,13 +298,13 @@ void loop() {
} else {
if(!wifiConnected) {
wifiConnected = true;
if(debugger) {
debugger->println("Successfully connected to WiFi!");
debugger->println(WiFi.localIP());
if(!config.getWifiHostname().isEmpty()) {
MDNS.begin(config.getWifiHostname().c_str());
MDNS.addService("http", "tcp", 80);
}
if(Debug.isActive(RemoteDebug::INFO)) {
debugI("Successfully connected to WiFi!");
debugI("IP: %s", WiFi.localIP().toString().c_str());
}
if(!config.getWifiHostname().isEmpty()) {
MDNS.begin(config.getWifiHostname().c_str());
MDNS.addService("http", "tcp", 80);
}
}
if (!config.getMqttHost().isEmpty()) {
@ -388,14 +394,14 @@ void swapWifiMode() {
yield();
if (mode != WIFI_AP || !config.hasConfig()) {
if(debugger) debugger->println("Swapping to AP mode");
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to AP mode");
WiFi.softAP("AMS2MQTT");
WiFi.mode(WIFI_AP);
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(53, "*", WiFi.softAPIP());
} else {
if(debugger) debugger->println("Swapping to STA mode");
if(Debug.isActive(RemoteDebug::INFO)) debugI("Swapping to STA mode");
WiFi_connect();
}
delay(500);
@ -405,12 +411,8 @@ void swapWifiMode() {
void mqttMessageReceived(String &topic, String &payload)
{
if (debugger) {
debugger->println("Incoming MQTT message:");
debugger->print("[");
debugger->print(topic);
debugger->print("] ");
debugger->println(payload);
if (Debug.isActive(RemoteDebug::DEBUG)) {
debugD("Incoming MQTT message: [%s] %s", topic.c_str(), payload.c_str());
}
// Do whatever needed here...
@ -428,11 +430,7 @@ void readHanPort() {
lastSuccessfulRead = millis();
if(config.getMeterType() > 0) {
#if HAS_RGB_LED
rgb_led(RGB_GREEN, 1);
#else
led_on();
#endif
rgb_led(RGB_GREEN, 2);
AmsData data(config.getMeterType(), hanReader);
ws.setData(data);
@ -441,10 +439,11 @@ void readHanPort() {
if(config.getMqttPayloadFormat() == 0) {
StaticJsonDocument<512> json;
hanToJson(json, data, hw, temperature);
if (debugger) {
debugger->print("Sending data to MQTT: ");
serializeJsonPretty(json, *debugger);
debugger->println();
if (Debug.isActive(RemoteDebug::INFO)) {
debugI("Sending data to MQTT");
if (Debug.isActive(RemoteDebug::DEBUG)) {
serializeJsonPretty(json, Debug);
}
}
String msg;
@ -507,12 +506,6 @@ void readHanPort() {
mqtt.loop();
delay(10);
}
#if HAS_RGB_LED
rgb_led(RGB_GREEN, 0);
#else
led_off();
#endif
} else {
// Auto detect meter if not set
for(int i = 1; i <= 3; i++) {
@ -532,15 +525,15 @@ void readHanPort() {
list.toLowerCase();
if(list.startsWith("kfm")) {
config.setMeterType(1);
if(debugger) debugger->println("Detected Kaifa meter");
if(Debug.isActive(RemoteDebug::INFO)) debugI("Detected Kaifa meter");
break;
} else if(list.startsWith("aidon")) {
config.setMeterType(2);
if(debugger) debugger->println("Detected Aidon meter");
if(Debug.isActive(RemoteDebug::INFO)) debugI("Detected Aidon meter");
break;
} else if(list.startsWith("kamstrup")) {
config.setMeterType(3);
if(debugger) debugger->println("Detected Kamstrup meter");
if(Debug.isActive(RemoteDebug::INFO)) debugI("Detected Kamstrup meter");
break;
}
}
@ -552,7 +545,7 @@ void readHanPort() {
// Switch parity if meter is still not detected
if(config.getMeterType() == 0 && millis() - lastSuccessfulRead > 10000) {
lastSuccessfulRead = millis();
if(debugger) debugger->println("No data for current setting, switching parity");
if(Debug.isActive(RemoteDebug::DEBUG)) debugD("No data for current setting, switching parity");
#if SOFTWARE_SERIAL
if(even) {
hanSerial->begin(2400, SWSERIAL_8N1);
@ -579,12 +572,7 @@ void WiFi_connect() {
}
lastWifiRetry = millis();
if (debugger) {
debugger->println();
debugger->println();
debugger->print("Connecting to WiFi network ");
debugger->println(config.getWifiSsid());
}
if (Debug.isActive(RemoteDebug::INFO)) debugI("Connecting to WiFi network: %s", config.getWifiSsid().c_str());
if (WiFi.status() != WL_CONNECTED) {
MDNS.end();
@ -617,7 +605,7 @@ void WiFi_connect() {
unsigned long lastMqttRetry = -10000;
void MQTT_connect() {
if(config.getMqttHost().isEmpty()) {
if(debugger) debugger->println("No MQTT config");
if(Debug.isActive(RemoteDebug::WARNING)) debugW("No MQTT config");
return;
}
if(millis() - lastMqttRetry < 5000) {
@ -625,12 +613,8 @@ void MQTT_connect() {
return;
}
lastMqttRetry = millis();
if(debugger) {
debugger->print("Connecting to MQTT: ");
debugger->print(config.getMqttHost());
debugger->print(", port: ");
debugger->print(config.getMqttPort());
debugger->println();
if(Debug.isActive(RemoteDebug::INFO)) {
debugI("Connecting to MQTT %s:%d", config.getMqttHost().c_str(), config.getMqttPort());
}
mqtt.disconnect();
@ -641,13 +625,13 @@ void MQTT_connect() {
// Connect to a unsecure or secure MQTT server
if ((config.getMqttUser().isEmpty() && mqtt.connect(config.getMqttClientId().c_str())) ||
(!config.getMqttUser().isEmpty() && mqtt.connect(config.getMqttClientId().c_str(), config.getMqttUser().c_str(), config.getMqttPassword().c_str()))) {
if (debugger) debugger->println("\nSuccessfully connected to MQTT!");
if (Debug.isActive(RemoteDebug::INFO)) debugI("Successfully connected to MQTT!");
config.ackMqttChange();
// Subscribe to the chosen MQTT topic, if set in configuration
if (!config.getMqttSubscribeTopic().isEmpty()) {
mqtt.subscribe(config.getMqttSubscribeTopic());
if (debugger) debugger->printf(" Subscribing to [%s]\r\n", config.getMqttSubscribeTopic().c_str());
if (Debug.isActive(RemoteDebug::INFO)) debugI(" Subscribing to [%s]\r\n", config.getMqttSubscribeTopic().c_str());
}
if(config.getMqttPayloadFormat() == 0) {
@ -656,9 +640,8 @@ void MQTT_connect() {
sendSystemStatusToMqtt();
}
} else {
if (debugger) {
debugger->print(" failed, ");
debugger->println(" trying again in 5 seconds");
if (Debug.isActive(RemoteDebug::ERROR)) {
debugI("Failed to connect to MQTT");
}
}
yield();
@ -689,8 +672,8 @@ void sendMqttData(String data)
// Send the json over MQTT
mqtt.publish(config.getMqttPublishTopic(), msg.c_str());
if (debugger) debugger->print("sendMqttData: ");
if (debugger) debugger->println(data);
if (Debug.isActive(RemoteDebug::INFO)) debugI("Sending MQTT data");
if (Debug.isActive(RemoteDebug::DEBUG)) debugD("[%s]", data.c_str());
}
unsigned long lastSystemDataSent = -10000;
@ -724,21 +707,21 @@ void rgb_led(int color, int mode) {
#endif
int blinkduration = 50; // milliseconds
switch (mode) {
case 0: //OFF
case RGB_OFF: //OFF
digitalWrite(LEDPIN_RGB_RED, HIGH);
digitalWrite(LEDPIN_RGB_GREEN, HIGH);
break;
case 1: //ON
case RGB_ON: //ON
switch (color) {
case 1: //Red
case RGB_RED: //Red
digitalWrite(LEDPIN_RGB_RED, LOW);
digitalWrite(LEDPIN_RGB_GREEN, HIGH);
break;
case 2: //Green
case RGB_GREEN: //Green
digitalWrite(LEDPIN_RGB_RED, HIGH);
digitalWrite(LEDPIN_RGB_GREEN, LOW);
break;
case 3: //Yellow
case RGB_YELLOW: //Yellow
digitalWrite(LEDPIN_RGB_RED, LOW);
digitalWrite(LEDPIN_RGB_GREEN, LOW);
break;
@ -746,9 +729,9 @@ void rgb_led(int color, int mode) {
break;
default: // Blink
for(int i = 1; i < mode; i++) {
rgb_led(color, 1);
rgb_led(color, RGB_ON);
delay(blinkduration);
rgb_led(color, 0);
rgb_led(color, RGB_OFF);
if(i != mode)
delay(blinkduration);
}

View File

@ -13,10 +13,12 @@
#include "Base64.h"
AmsWebServer::AmsWebServer(RemoteDebug* Debug) {
this->debugger = Debug;
}
void AmsWebServer::setup(AmsConfiguration* config, Stream* debugger, MQTTClient* mqtt) {
void AmsWebServer::setup(AmsConfiguration* config, MQTTClient* mqtt) {
this->config = config;
this->debugger = debugger;
this->mqtt = mqtt;
server.on("/", std::bind(&AmsWebServer::indexHtml, this));
@ -61,7 +63,7 @@ void AmsWebServer::setData(AmsData& data) {
bool AmsWebServer::checkSecurity(byte level) {
bool access = WiFi.getMode() == WIFI_AP || !config->hasConfig() || config->getAuthSecurity() < level;
if(!access && config->getAuthSecurity() >= level && server.hasHeader("Authorization")) {
println(" forcing web security");
printD(" forcing web security");
String expectedAuth = String(config->getAuthUser()) + ":" + String(config->getAuthPassword());
String providedPwd = server.header("Authorization");
@ -73,13 +75,12 @@ bool AmsWebServer::checkSecurity(byte level) {
int decodedLength = Base64.decodedLength(inputString, inputStringLength);
char decodedString[decodedLength];
Base64.decode(decodedString, inputString, inputStringLength);
print("Received auth: ");
println(decodedString);
printD("Received auth: %s", decodedString);
access = String(decodedString).equals(expectedAuth);
}
if(!access) {
println(" no access, requesting user/pass");
printD(" no access, requesting user/pass");
server.sendHeader("WWW-Authenticate", "Basic realm=\"Secure Area\"");
server.setContentLength(0);
server.send(401, "text/html", "");
@ -88,7 +89,7 @@ bool AmsWebServer::checkSecurity(byte level) {
}
void AmsWebServer::indexHtml() {
println("Serving /index.html over http...");
printD("Serving /index.html over http...");
if(!checkSecurity(2))
return;
@ -155,7 +156,7 @@ void AmsWebServer::indexHtml() {
}
void AmsWebServer::configMeterHtml() {
println("Serving /config-meter.html over http...");
printD("Serving /config-meter.html over http...");
if(!checkSecurity(1))
return;
@ -189,7 +190,7 @@ void AmsWebServer::configMeterHtml() {
}
void AmsWebServer::configWifiHtml() {
println("Serving /config-wifi.html over http...");
printD("Serving /config-wifi.html over http...");
if(!checkSecurity(1))
return;
@ -219,7 +220,7 @@ void AmsWebServer::configWifiHtml() {
}
void AmsWebServer::configMqttHtml() {
println("Serving /config-mqtt.html over http...");
printD("Serving /config-mqtt.html over http...");
if(!checkSecurity(1))
return;
@ -253,7 +254,7 @@ void AmsWebServer::configMqttHtml() {
}
void AmsWebServer::configWebHtml() {
println("Serving /config-web.html over http...");
printD("Serving /config-web.html over http...");
if(!checkSecurity(1))
return;
@ -280,7 +281,7 @@ void AmsWebServer::configWebHtml() {
}
void AmsWebServer::bootCss() {
println("Serving /boot.css over http...");
printD("Serving /boot.css over http...");
String css = String((const __FlashStringHelper*) BOOT_CSS);
@ -291,7 +292,7 @@ void AmsWebServer::bootCss() {
}
void AmsWebServer::gaugemeterJs() {
println("Serving /gaugemeter.js over http...");
printD("Serving /gaugemeter.js over http...");
String js = String((const __FlashStringHelper*) GAUGEMETER_JS);
@ -302,7 +303,7 @@ void AmsWebServer::gaugemeterJs() {
}
void AmsWebServer::dataJson() {
println("Serving /data.json over http...");
printD("Serving /data.json over http...");
if(!checkSecurity(2))
return;
@ -500,11 +501,26 @@ void AmsWebServer::handleSave() {
}
}
println("Saving configuration now...");
if(server.hasArg("sysConfig") && server.arg("sysConfig") == "true") {
config->setDebugTelnet(server.hasArg("debugTelnet") && server.arg("debugTelnet") == "true");
config->setDebugSerial(server.hasArg("debugSerial") && server.arg("debugSerial") == "true");
config->setDebugLevel(server.arg("debugLevel").toInt());
debugger->stop();
debugger->begin(config->getWifiHostname(), (uint8_t) config->getDebugLevel());
if(config->getAuthSecurity() > 0) {
debugger->setPassword(config->getAuthPassword());
}
debugger->setSerialEnabled(config->isDebugSerial());
if(!config->isDebugTelnet()) {
debugger->stop();
}
}
if (debugger) config->print(debugger);
printI("Saving configuration now...");
if (debugger->isActive(RemoteDebug::DEBUG)) config->print(debugger);
if (config->save()) {
println("Successfully saved.");
printI("Successfully saved.");
if(config->isWifiChanged()) {
performRestart = true;
server.sendHeader("Location","/restart-wait");
@ -514,14 +530,14 @@ void AmsWebServer::handleSave() {
server.send (302, "text/plain", "");
}
} else {
println("Error saving configuration");
printE("Error saving configuration");
String html = "<html><body><h1>Error saving configuration!</h1></form>";
server.send(500, "text/html", html);
}
}
void AmsWebServer::configSystemHtml() {
println("Serving /config-system.html over http...");
printD("Serving /config-system.html over http...");
if(!checkSecurity(1))
return;
@ -533,6 +549,13 @@ void AmsWebServer::configSystemHtml() {
html.replace("boot.css", BOOTSTRAP_URL);
}
html.replace("${config.debugTelnet}", config->isDebugTelnet() ? "checked" : "");
html.replace("${config.debugSerial}", config->isDebugSerial() ? "checked" : "");
html.replace("${config.debugLevel}", String(config->getDebugLevel()));
for(int i = 0; i<=RemoteDebug::ANY; i++) {
html.replace("${config.debugLevel" + String(i) + "}", config->getDebugLevel() == i ? "selected" : "");
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
@ -551,11 +574,11 @@ void AmsWebServer::configSystemUpload() {
if(!filename.endsWith(".bin")) {
server.send(500, "text/plain", "500: couldn't create file");
} else if (!SPIFFS.begin()) {
println("An Error has occurred while mounting SPIFFS");
printE("An Error has occurred while mounting SPIFFS");
String html = "<html><body><h1>Error uploading!</h1></form>";
server.send(500, "text/html", html);
} else {
print("handleFileUpload Name: "); println(filename.c_str());
printD("handleFileUpload Name: %s", filename.c_str());
firmwareFile = SPIFFS.open("/firmware.bin", "w");
filename = String();
}
@ -566,7 +589,7 @@ void AmsWebServer::configSystemUpload() {
if(firmwareFile) {
firmwareFile.close();
SPIFFS.end();
print("handleFileUpload Size: "); println(String(upload.totalSize).c_str());
printD("handleFileUpload Size: %d", upload.totalSize);
performRestart = true;
server.sendHeader("Location","/restart-wait");
server.send(303);
@ -577,7 +600,7 @@ void AmsWebServer::configSystemUpload() {
}
void AmsWebServer::restartWaitHtml() {
println("Serving /restart-wait.html over http...");
printD("Serving /restart-wait.html over http...");
if(!checkSecurity(1))
return;
@ -604,7 +627,7 @@ void AmsWebServer::restartWaitHtml() {
yield();
if(performRestart) {
SPIFFS.end();
println("Firmware uploaded, rebooting");
printI("Firmware uploaded, rebooting");
delay(1000);
#if defined(ESP8266)
ESP.reset();
@ -620,19 +643,30 @@ void AmsWebServer::isAliveCheck() {
server.send(200);
}
size_t AmsWebServer::print(const char* text)
{
if (debugger) debugger->print(text);
void AmsWebServer::printD(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(String("(AmsWebServer)" + fmt + "\n").c_str(), args);
va_end(args);
}
size_t AmsWebServer::println(const char* text)
{
if (debugger) debugger->println(text);
void AmsWebServer::printI(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(String("(AmsWebServer)" + fmt + "\n").c_str(), args);
va_end(args);
}
size_t AmsWebServer::print(const Printable& data)
{
if (debugger) debugger->print(data);
void AmsWebServer::printW(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf(String("(AmsWebServer)" + fmt + "\n").c_str(), args);
va_end(args);
}
size_t AmsWebServer::println(const Printable& data)
{
if (debugger) debugger->println(data);
void AmsWebServer::printE(String fmt, ...) {
va_list args;
va_start(args, fmt);
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(String("(AmsWebServer)" + fmt + "\n").c_str(), args);
va_end(args);
}

View File

@ -9,6 +9,7 @@
#include "HwTools.h"
#include "AmsData.h"
#include "Uptime.h"
#include "RemoteDebug.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
@ -30,17 +31,18 @@
class AmsWebServer {
public:
void setup(AmsConfiguration* config, Stream* debugger, MQTTClient* mqtt);
AmsWebServer(RemoteDebug* Debug);
void setup(AmsConfiguration* config, MQTTClient* mqtt);
void loop();
void setData(AmsData& data);
private:
RemoteDebug* debugger;
int maxPwr = 0;
HwTools hw;
AmsConfiguration* config;
AmsData data;
Stream* debugger;
MQTTClient* mqtt;
File firmwareFile;
bool performRestart = false;
@ -70,11 +72,10 @@ private:
void restartWaitHtml();
void isAliveCheck();
size_t print(const char* text);
size_t println(const char* text);
size_t print(const Printable& data);
size_t println(const Printable& data);
void printD(String fmt, ...);
void printI(String fmt, ...);
void printW(String fmt, ...);
void printE(String fmt, ...);
};
#endif

View File

@ -5,6 +5,7 @@
<title>AMS reader - System configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
</head>
<body class="bg-light">
<main role="main" class="container">
@ -37,8 +38,37 @@
</li>
</ul>
</header>
<form method="post" action="/save">
<input type="hidden" name="sysConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-md-3">
<label><input type="checkbox" name="debugTelnet" value="true" ${config.debugTelnet}/> Telnet debugger</label>
</div>
<div class="col-md-3">
<label><input type="checkbox" name="debugSerial" value="true" ${config.debugSerial}/> Serial debugger</label>
</div>
<div class="col-md-5">
<div class="row form-group">
<label class="col-6">Debug level</label>
<div class="col-6">
<select class="form-control" name="debugLevel">
<option value="2" ${config.debugLevel2}>Debug</option>
<option value="3" ${config.debugLevel3}>Info</option>
<option value="4" ${config.debugLevel4}>Warning</option>
<option value="5" ${config.debugLevel5}>Error</option>
</select>
</div>
</div>
</div>
<div class="col-md-1 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div>
</div>
</form>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="authConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="alert alert-warning">!!WARNING!!<br/>Do not use this unless you know what you are doing. Uploading the wrong image could cause your device to stop working. Use with extreme caution!</div>
<div class="row">
@ -50,6 +80,9 @@
</div>
</div>
</div>
<div class="col-md-8 text-right">
<button class="btn btn-primary">Upload</button>
</div>
</div>
</div>
<hr/>
@ -58,7 +91,6 @@
<a href="/" class="btn btn-outline-secondary">Back</a>
</div>
<div class="col-6 text-right">
<button class="btn btn-primary">Upload</button>
</div>
</div>
</form>