DSMR with encrypted payload

This commit is contained in:
Gunnar Skjold
2024-12-14 10:54:02 +01:00
parent 42e0ca963c
commit 61d4728ffa
16 changed files with 224 additions and 184 deletions

View File

@@ -14,5 +14,6 @@ String toHex(uint8_t* in);
String toHex(uint8_t* in, uint16_t size);
void fromHex(uint8_t *out, String in, uint16_t size);
bool stripNonAscii(uint8_t* in, uint16_t size, bool extended = false);
void debugPrint(uint8_t *buffer, uint16_t start, uint16_t length, Print* debugger);
#endif

View File

@@ -45,4 +45,20 @@ bool stripNonAscii(uint8_t* in, uint16_t size, bool extended) {
}
memset(in+size-1, 0, 1); // Make sure the last character is null-terminator
return ret;
}
void debugPrint(uint8_t *buffer, uint16_t start, uint16_t length, Print* debugger) {
for (uint16_t i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debugger->print(F("0"));
debugger->print(buffer[i], HEX);
debugger->print(F(" "));
if ((i - start + 1) % 16 == 0)
debugger->println(F(""));
else if ((i - start + 1) % 4 == 0)
debugger->print(F(" "));
yield(); // Let other get some resources too
}
debugger->println(F(""));
}

View File

@@ -9,15 +9,19 @@
#include "Arduino.h"
#include "DataParser.h"
#include "GcmParser.h"
class DSMRParser {
public:
int8_t parse(uint8_t *buf, DataParserContext &ctx, bool verified);
DSMRParser(GCMParser* gcmParser) { this->gcmParser = gcmParser; };
int8_t parse(uint8_t *buf, DataParserContext &ctx, bool verified, Print* debugger);
uint16_t getCrc();
uint16_t getCrcCalc();
private:
uint16_t crc;
uint16_t crc_calc;
GCMParser* gcmParser;
};
#endif

View File

@@ -18,7 +18,7 @@
class GCMParser {
public:
GCMParser(uint8_t *encryption_key, uint8_t *authentication_key);
int8_t parse(uint8_t *buf, DataParserContext &ctx);
int8_t parse(uint8_t *buf, DataParserContext &ctx, bool hastag = true);
private:
uint8_t encryption_key[16];
uint8_t authentication_key[16];

View File

@@ -9,27 +9,77 @@
#include "hexutils.h"
#include "lwip/def.h"
int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified) {
// verified indicates that this data was encapsulated in something else, so we know this has the correct size etc
int8_t DSMRParser::parse(uint8_t *buf, DataParserContext &ctx, bool verified, Print* debugger) {
uint16_t lenBefore = ctx.length;
uint16_t crcPos = 0;
bool reachedEnd = verified;
uint8_t lastByte = 0x00;
for(int pos = 0; pos < ctx.length; pos++) {
for(uint16_t pos = 0; pos < ctx.length; pos++) {
uint8_t b = *(buf+pos);
if(pos == 0 && b != '/') return DATA_PARSE_BOUNDRY_FLAG_MISSING;
if(pos > 0 && b == '!' && lastByte == '\n') crcPos = pos+1;
if(crcPos > 0 && b == '\n') reachedEnd = true;
if(pos > 0 && b == '!') crcPos = pos+1;
if(crcPos > 0 && b == 0x0A && lastByte == 0x0D) {
reachedEnd = true;
ctx.length = pos;
break;
}
lastByte = b;
}
if(!reachedEnd) return DATA_PARSE_INCOMPLETE;
buf[ctx.length+1] = '\0';
if(crcPos > 0) {
// If we expect data to be encrypted and it was not previously verified, decrypt content
if(gcmParser != NULL && !verified) {
uint8_t* ptr = (uint8_t*) buf;
while(*ptr != 0x0D && *ptr != 0x0A) ptr++;
while(*ptr == 0x0D || *ptr == 0x0A) ptr++;
uint16_t pos = ptr-buf;
DataParserContext gcmCtx = {
DATA_TAG_GCM,
crcPos - pos - 1,
ctx.timestamp
};
if(debugger != NULL) {
debugger->printf_P(PSTR("DSMR wants to decrypt at position %lu, length: %d, payload:\n"), pos, gcmCtx.length);
debugPrint(ptr, 0, gcmCtx.length, debugger);
}
int8_t gcmRet = gcmParser->parse(ptr, gcmCtx, false);
if(gcmRet < 0) {
if(debugger != NULL) {
debugger->printf_P(PSTR(" - Failed! (%d)\n"), gcmRet);
}
return gcmRet;
} else {
if(debugger != NULL) {
debugger->printf_P(PSTR(" - Success! (%d)\n"), gcmRet);
}
ptr += gcmRet;
for(uint16_t i = 0; i < gcmCtx.length; i++) {
buf[pos++] = ptr[i];
}
ptr = buf + crcPos - 1;
crcPos = pos + 1;
while(*ptr != '\0') {
ctx.length = pos;
buf[pos++] = *(ptr++);
}
while(pos < lenBefore) {
buf[pos++] = '\0';
}
}
} else if(crcPos > 0) {
crc_calc = crc16(buf, crcPos);
crc = 0x0000;
fromHex((uint8_t*) &crc, String((char*) buf+crcPos), 2);
crc = ntohs(crc);
if(crc != crc_calc)
if(crc != crc_calc) {
if(debugger != NULL) {
debugger->printf_P(PSTR("CRC incorrrect, %04X != %04X at position %lu\n"), crc, crc_calc, crcPos);
}
return DATA_PARSE_FOOTER_CHECKSUM_ERROR;
}
}
return DATA_PARSE_OK;
}

View File

@@ -17,25 +17,34 @@ GCMParser::GCMParser(uint8_t *encryption_key, uint8_t *authentication_key) {
memcpy(this->authentication_key, authentication_key, 16);
}
int8_t GCMParser::parse(uint8_t *d, DataParserContext &ctx) {
int8_t GCMParser::parse(uint8_t *d, DataParserContext &ctx, bool hastag) {
if(ctx.length < 12) return DATA_PARSE_INCOMPLETE;
uint32_t headersize = 0;
uint8_t* ptr = (uint8_t*) d;
if(*ptr != GCM_TAG) return DATA_PARSE_BOUNDRY_FLAG_MISSING;
ptr++;
if(hastag) {
if(*ptr != GCM_TAG) return DATA_PARSE_BOUNDRY_FLAG_MISSING;
ptr++;
headersize++;
}
// Encrypted APDU
// http://www.weigu.lu/tutorials/sensors2bus/04_encryption/index.html
uint8_t systemTitleLength = *ptr;
ptr++;
headersize++;
uint8_t initialization_vector[12];
memcpy(ctx.system_title, ptr, systemTitleLength);
memcpy(initialization_vector, ctx.system_title, systemTitleLength);
memset(ctx.system_title, 0, 8);
memset(initialization_vector, 0, 12);
if(systemTitleLength > 0) {
memcpy(ctx.system_title, ptr, systemTitleLength);
memcpy(initialization_vector, ctx.system_title, systemTitleLength);
ptr += systemTitleLength;
headersize += systemTitleLength;
}
uint32_t len = 0;
uint32_t headersize = 2 + systemTitleLength;
ptr += systemTitleLength;
if(((*ptr) & 0xFF) == 0x81) {
// 1-byte payload length
ptr++;

View File

@@ -110,7 +110,6 @@ private:
char* pers = "amsreader";
bool init();
void debugPrint(byte *buffer, int start, int length);
String meterManufacturer(uint8_t type) {
switch(type) {

View File

@@ -597,22 +597,6 @@ void CloudConnector::setEnergyAccountingConfig(EnergyAccountingConfig& eac) {
this->lastEac = 0;
}
void CloudConnector::debugPrint(byte *buffer, int start, int length) {
for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debugger->print(F("0"));
debugger->print(buffer[i], HEX);
debugger->print(F(" "));
if ((i - start + 1) % 16 == 0)
debugger->println(F(""));
else if ((i - start + 1) % 4 == 0)
debugger->print(F(" "));
yield(); // Let other get some resources too
}
debugger->println(F(""));
}
String CloudConnector::generateSeed() {
uint8_t key[16];
ESPRandom::uuid4(key);

View File

@@ -89,7 +89,6 @@ protected:
void setupHanPort(uint32_t baud, uint8_t parityOrdinal, bool invert, bool passive = true);
int16_t unwrapData(uint8_t *buf, DataParserContext &context);
void debugPrint(byte *buffer, int start, int length);
void printHanReadError(int pos);
void handleAutodetect(unsigned long now);
};

View File

@@ -8,6 +8,7 @@
#include "Uptime.h"
#include "crc.h"
#include "OBIScodes.h"
#include "hexutils.h"
void KmpCommunicator::configure(MeterConfig& meterConfig) {
this->meterConfig = meterConfig;
@@ -43,7 +44,7 @@ bool KmpCommunicator::loop() {
#endif
{
debugger->printf_P(PSTR(" payload:\n"));
debugPrint(hanBuffer, 0, hanBufferSize);
debugPrint(hanBuffer, 0, hanBufferSize, debugger);
}
}
return ret;

View File

@@ -9,6 +9,7 @@
#include "IEC6205621.h"
#include "LNG.h"
#include "LNG2.h"
#include "hexutils.h"
#if defined(ESP32)
#include <driver/uart.h>
@@ -32,10 +33,23 @@ void PassiveMeterCommunicator::configure(MeterConfig& meterConfig, Timezone* tz)
this->configChanged = false;
this->tz = tz;
setupHanPort(meterConfig.baud, meterConfig.parity, meterConfig.invert);
if(dsmrParser != NULL) {
delete dsmrParser;
dsmrParser = NULL;
}
if(gcmParser != NULL) {
delete gcmParser;
gcmParser = NULL;
}
bool encen = false;
for(uint8_t i = 0; i < 16; i++) {
if(meterConfig.encryptionKey[i] > 0) {
encen = true;
}
}
if(encen) {
gcmParser = new GCMParser(meterConfig.encryptionKey, meterConfig.authenticationKey);
}
}
bool PassiveMeterCommunicator::loop() {
@@ -68,9 +82,9 @@ bool PassiveMeterCommunicator::loop() {
hanSerial->readBytes(hanBuffer, hanBufferSize);
len = 0;
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Buffer overflow, resetting\n"));
if (debugger->isActive(RemoteDebug::INFO))
#endif
debugger->printf_P(PSTR("Buffer overflow, resetting\n"));
return false;
}
hanBuffer[len++] = hanSerial->read();
@@ -80,46 +94,46 @@ debugger->printf_P(PSTR("Buffer overflow, resetting\n"));
switch(ctx.type) {
case DATA_TAG_DLMS:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid DLMS at %d\n"), pos);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid DLMS at %d +%d\n"), pos, ctx.length);
break;
case DATA_TAG_DSMR:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid DSMR at %d\n"), pos);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid DSMR at %d +%d\n"), pos, ctx.length);
break;
case DATA_TAG_SNRM:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid SNMR at %d\n"), pos);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid SNMR at %d +%d\n"), pos, ctx.length);
break;
case DATA_TAG_AARE:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid AARE at %d\n"), pos);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid AARE at %d +%d\n"), pos, ctx.length);
break;
case DATA_TAG_RES:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid Get Response at %d\n"), pos);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid Get Response at %d +%d\n"), pos, ctx.length);
break;
case DATA_TAG_HDLC:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid HDLC at %d\n"), pos);
if (debugger->isActive(RemoteDebug::DEBUG))
#endif
debugger->printf_P(PSTR("Received valid HDLC at %d +%d\n"), pos, ctx.length);
break;
default:
// TODO: Move this so that payload is sent to MQTT
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Unknown tag %02X at pos %d\n"), ctx.type, pos);
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Unknown tag %02X at pos %d\n"), ctx.type, pos);
len = 0;
return false;
}
@@ -129,26 +143,26 @@ debugger->printf_P(PSTR("Unknown tag %02X at pos %d\n"), ctx.type, pos);
end = millis();
if(end-start > 1000) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Used %dms to unwrap HAN data\n"), end-start);
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Used %dms to unwrap HAN data\n"), end-start);
}
if(pos == DATA_PARSE_INCOMPLETE) {
return false;
} else if(pos == DATA_PARSE_UNKNOWN_DATA) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Unknown data received\n"));
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Unknown data received\n"));
lastError = pos;
len = len + hanSerial->readBytes(hanBuffer+len, hanBufferSize-len);
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
{
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
{
debugger->printf_P(PSTR(" payload:\n"));
debugPrint(hanBuffer, 0, len);
debugPrint(hanBuffer, 0, len, debugger);
}
len = 0;
return false;
@@ -164,11 +178,11 @@ if (debugger->isActive(RemoteDebug::VERBOSE))
pt->publishBytes(hanBuffer, len);
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
{
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
{
debugger->printf_P(PSTR(" payload:\n"));
debugPrint(hanBuffer, 0, len);
debugPrint(hanBuffer, 0, len, debugger);
}
while(hanSerial->available()) hanSerial->read(); // Make sure it is all empty, in case we overflowed buffer above
len = 0;
@@ -177,17 +191,17 @@ if (debugger->isActive(RemoteDebug::VERBOSE))
if(ctx.type == 0) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Ended up with context type %d, return code %d and length: %lu/%lu\n"), ctx.type, pos, ctx.length, len);
if (debugger->isActive(RemoteDebug::WARNING))
#endif
debugger->printf_P(PSTR("Ended up with context type %d, return code %d and length: %lu/%lu\n"), ctx.type, pos, ctx.length, len);
lastError = pos;
len = len + hanSerial->readBytes(hanBuffer+len, hanBufferSize-len);
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
{
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
{
debugger->printf_P(PSTR(" payload:\n"));
debugPrint(hanBuffer, 0, len);
debugPrint(hanBuffer, 0, len, debugger);
}
len = 0;
return false;
@@ -226,7 +240,7 @@ debugger->printf_P(PSTR("Using application data:\n"));
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugPrint((byte*) payload, 0, ctx.length);
debugPrint((byte*) payload, 0, ctx.length, debugger);
// Rudimentary detector for L&G proprietary format, this is terrible code... Fix later
if(payload[0] == CosemTypeStructure && payload[2] == CosemTypeArray && payload[1] == payload[3]) {
@@ -351,8 +365,8 @@ int16_t PassiveMeterCommunicator::unwrapData(uint8_t *buf, DataParserContext &co
if(res >= 0) doRet = true;
break;
case DATA_TAG_DSMR:
if(dsmrParser == NULL) dsmrParser = new DSMRParser();
res = dsmrParser->parse(buf, context, lastTag != DATA_TAG_NONE);
if(dsmrParser == NULL) dsmrParser = new DSMRParser(gcmParser);
res = dsmrParser->parse(buf, context, lastTag != DATA_TAG_NONE, debugger->isActive(RemoteDebug::VERBOSE) ? debugger : NULL);
if(res >= 0) doRet = true;
break;
case DATA_TAG_SNRM:
@@ -363,9 +377,9 @@ int16_t PassiveMeterCommunicator::unwrapData(uint8_t *buf, DataParserContext &co
break;
default:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Ended up in default case while unwrapping...(tag %02X)\n"), tag);
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Ended up in default case while unwrapping...(tag %02X)\n"), tag);
return DATA_PARSE_UNKNOWN_DATA;
}
lastTag = tag;
@@ -374,9 +388,9 @@ debugger->printf_P(PSTR("Ended up in default case while unwrapping...(tag %02X)\
}
if(context.length > end) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("Context length %lu > %lu:\n"), context.length, end);
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("Context length %lu > %lu:\n"), context.length, end);
context.type = 0;
context.length = 0;
return false;
@@ -384,78 +398,78 @@ debugger->printf_P(PSTR("Context length %lu > %lu:\n"), context.length, end);
switch(tag) {
case DATA_TAG_HDLC:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("HDLC frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("HDLC frame:\n"));
if(pt != NULL) {
pt->publishBytes(buf, curLen);
}
break;
case DATA_TAG_MBUS:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("MBUS frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("MBUS frame:\n"));
if(pt != NULL) {
pt->publishBytes(buf, curLen);
}
break;
case DATA_TAG_GBT:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("GBT frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("GBT frame:\n"));
break;
case DATA_TAG_GCM:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("GCM frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("GCM frame:\n"));
break;
case DATA_TAG_LLC:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("LLC frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("LLC frame:\n"));
break;
case DATA_TAG_DLMS:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("DLMS frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("DLMS frame:\n"));
break;
case DATA_TAG_DSMR:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("DSMR frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("DSMR frame:\n"));
if(pt != NULL) {
pt->publishString((char*) buf);
}
break;
case DATA_TAG_SNRM:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("SNMR frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("SNMR frame:\n"));
break;
case DATA_TAG_AARE:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("AARE frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("AARE frame:\n"));
break;
case DATA_TAG_RES:
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("RES frame:\n"));
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugger->printf_P(PSTR("RES frame:\n"));
break;
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugPrint(buf, 0, curLen);
if (debugger->isActive(RemoteDebug::VERBOSE))
#endif
debugPrint(buf, 0, curLen, debugger);
if(res == DATA_PARSE_FINAL_SEGMENT) {
if(tag == DATA_TAG_MBUS) {
res = mbusParser->write(buf, context);
@@ -479,28 +493,12 @@ debugPrint(buf, 0, curLen);
tag = (*buf);
}
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Got to end of unwrap method...\n"));
if (debugger->isActive(RemoteDebug::ERROR))
#endif
debugger->printf_P(PSTR("Got to end of unwrap method...\n"));
return DATA_PARSE_UNKNOWN_DATA;
}
void PassiveMeterCommunicator::debugPrint(byte *buffer, int start, int length) {
for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debugger->print(F("0"));
debugger->print(buffer[i], HEX);
debugger->print(F(" "));
if ((i - start + 1) % 16 == 0)
debugger->println(F(""));
else if ((i - start + 1) % 4 == 0)
debugger->print(F(" "));
yield(); // Let other get some resources too
}
debugger->println(F(""));
}
void PassiveMeterCommunicator::printHanReadError(int pos) {
#if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::WARNING))

View File

@@ -129,7 +129,5 @@ private:
PricesContainer* fetchPrices(time_t);
bool retrieve(const char* url, Stream* doc);
float getCurrencyMultiplier(const char* from, const char* to, time_t t);
void debugPrint(byte *buffer, int start, int length);
};
#endif

View File

@@ -540,22 +540,6 @@ debugger->printf(http->getString().c_str());
return NULL;
}
void PriceService::debugPrint(byte *buffer, int start, int length) {
for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debugger->print(F("0"));
debugger->print(buffer[i], HEX);
debugger->print(F(" "));
if ((i - start + 1) % 16 == 0)
debugger->println(F(""));
else if ((i - start + 1) % 4 == 0)
debugger->print(F(" "));
yield(); // Let other get some resources too
}
debugger->println(F(""));
}
int16_t PriceService::getLastError() {
return lastError;
}