diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 9be72a70..76669404 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -301,7 +301,6 @@ bool longPressActive = false; bool wifiConnected = false; unsigned long lastTemperatureRead = 0; -unsigned long lastSuccessfulRead = 0; unsigned long lastErrorBlink = 0; int lastError = 0; @@ -558,7 +557,7 @@ void errorBlink() { for(;lastError < 3;lastError++) { switch(lastError) { case 0: - if(lastErrorBlink - lastSuccessfulRead > 30000) { + if(lastErrorBlink - meterState.getLastUpdateMillis() > 30000) { hw.ledBlink(LED_RED, 1); // If no message received from AMS in 30 sec, blink once return; } @@ -645,11 +644,12 @@ void readHanPort() { hanSerial->readBytes(buf, BUF_SIZE); return; } + CosemDateTime timestamp; AmsData data; if(currentMeterType == 1) { size_t len = hanSerial->readBytes(buf, BUF_SIZE); // TODO: read one byte at the time. This blocks up the GUI if(len > 0) { - int pos = HDLC_validate((uint8_t *) buf, len, hc); + int pos = HDLC_validate((uint8_t *) buf, len, hc, ×tamp); if(pos == HDLC_ENCRYPTION_CONFIG_MISSING) { hc = new HDLCConfig(); memcpy(hc->encryption_key, meterConfig.encryptionKey, 16); @@ -671,7 +671,7 @@ void readHanPort() { } if(pos >= 0) { debugI("Valid HDLC, start at %d", pos); - data = IEC6205675(((char *) (buf)) + pos, meterState.getMeterType()); + data = IEC6205675(((char *) (buf)) + pos, meterState.getMeterType(), timestamp); } else { debugW("Invalid HDLC, returned with %d", pos); currentMeterType = 0; diff --git a/src/IEC6205675.cpp b/src/IEC6205675.cpp index da8398fa..99f1e2e6 100644 --- a/src/IEC6205675.cpp +++ b/src/IEC6205675.cpp @@ -1,11 +1,13 @@ #include "IEC6205675.h" #include "lwip/def.h" -IEC6205675::IEC6205675(const char* d, uint8_t useMeterType) { +IEC6205675::IEC6205675(const char* d, uint8_t useMeterType, CosemDateTime packageTimestamp) { uint32_t u32; int32_t s32; char str[64]; + this->packageTimestamp = getTimestamp(packageTimestamp); + u32 = getUnsignedNumber(AMS_OBIS_ACTIVE_IMPORT, sizeof(AMS_OBIS_ACTIVE_IMPORT), ((char *) (d))); if(u32 == 0xFFFFFFFF) { CosemData* data = getCosemDataAt(1, ((char *) (d))); @@ -94,20 +96,7 @@ IEC6205675::IEC6205675(const char* d, uint8_t useMeterType) { case CosemTypeOctetString: { if(data->oct.length == 0x0C) { AmsOctetTimestamp* ts = (AmsOctetTimestamp*) data; - tmElements_t tm; - tm.Year = ntohs(ts->year) - 1970; - tm.Month = ts->month; - tm.Day = ts->dayOfMonth; - tm.Hour = ts->hour; - tm.Minute = ts->minute; - tm.Second = ts->second; - - time_t time = makeTime(tm); - int16_t deviation = ntohs(ts->deviation); - if(deviation >= -720 && deviation <= 720) { - time -= deviation * 60; - } - meterTimestamp = time; + meterTimestamp = getTimestamp(ts->dt); } } } @@ -419,23 +408,28 @@ time_t IEC6205675::getTimestamp(uint8_t* obis, int matchlength, const char* ptr) case CosemTypeOctetString: { if(item->oct.length == 0x0C) { AmsOctetTimestamp* ts = (AmsOctetTimestamp*) item; - tmElements_t tm; - tm.Year = ntohs(ts->year) - 1970; - tm.Month = ts->month; - tm.Day = ts->dayOfMonth; - tm.Hour = ts->hour; - tm.Minute = ts->minute; - tm.Second = ts->second; - - time_t time = makeTime(tm); - int16_t deviation = ntohs(ts->deviation); - if(deviation >= -720 && deviation <= 720) { - time -= deviation * 60; - } - return time; + //Serial.printf("\nYear: %d, Month: %d, Day: %d, Hour: %d, Minutes %d, Second: %d, Deviation: %d\n", ntohs(ts->dt.year), ts->dt.month, ts->dt.dayOfMonth, ts->dt.hour, ts->dt.minute, ts->dt.second, ntohs(ts->dt.deviation)); + return getTimestamp(ts->dt); } } } } return 0; } + +time_t IEC6205675::getTimestamp(CosemDateTime timestamp) { + tmElements_t tm; + tm.Year = ntohs(timestamp.year) - 1970; + tm.Month = timestamp.month; + tm.Day = timestamp.dayOfMonth; + tm.Hour = timestamp.hour; + tm.Minute = timestamp.minute; + tm.Second = timestamp.second; + + time_t time = makeTime(tm); + int16_t deviation = ntohs(timestamp.deviation); + if(deviation >= -720 && deviation <= 720) { + time -= deviation * 60; + } + return time; +} diff --git a/src/IEC6205675.h b/src/IEC6205675.h index a28f053a..7bfde259 100644 --- a/src/IEC6205675.h +++ b/src/IEC6205675.h @@ -5,21 +5,13 @@ #include "ams/hdlc.h" struct AmsOctetTimestamp { - uint16_t year; - uint8_t month; - uint8_t dayOfMonth; - uint8_t dayOfWeek; - uint8_t hour; - uint8_t minute; - uint8_t second; - uint8_t hundredths; - int16_t deviation; - uint8_t status; + uint8_t type; + CosemDateTime dt; } __attribute__((packed)); class IEC6205675 : public AmsData { public: - IEC6205675(const char* payload, uint8_t useMeterType); + IEC6205675(const char* payload, uint8_t useMeterType, CosemDateTime packageTimestamp); private: CosemData* getCosemDataAt(uint8_t index, const char* ptr); @@ -28,6 +20,7 @@ private: uint32_t getSignedNumber(uint8_t* obis, int matchlength, const char* ptr); uint32_t getUnsignedNumber(uint8_t* obis, int matchlength, const char* ptr); time_t getTimestamp(uint8_t* obis, int matchlength, const char* ptr); + time_t getTimestamp(CosemDateTime timestamp); uint8_t AMS_OBIS_VERSION[6] = { 1, 1, 0, 2, 129, 255 }; uint8_t AMS_OBIS_METER_MODEL[4] = { 96, 1, 1, 255 }; diff --git a/src/ams/hdlc.cpp b/src/ams/hdlc.cpp index 6985bdd9..c52bd009 100644 --- a/src/ams/hdlc.cpp +++ b/src/ams/hdlc.cpp @@ -15,7 +15,7 @@ void mbus_hexdump(const uint8_t* buf, int len) { printf("]\n"); } -int HDLC_validate(const uint8_t* d, int len, HDLCConfig* config) { +int HDLC_validate(const uint8_t* d, int len, HDLCConfig* config, CosemDateTime* timestamp) { //mbus_hexdump(d, len); HDLCHeader* h = (HDLCHeader*) d; @@ -74,13 +74,18 @@ int HDLC_validate(const uint8_t* d, int len, HDLCConfig* config) { ptr += sizeof *adpu; // ADPU timestamp - // TODO : extract and return CosemData* dateTime = (CosemData*) ptr; if(dateTime->base.type == CosemTypeOctetString) { + if(dateTime->base.length == 0x0C) { + memcpy(timestamp, ptr+1, dateTime->base.length); + } ptr += 2 + dateTime->base.length; } else if(dateTime->base.type == CosemTypeNull) { ptr++; + } else if(dateTime->base.type == CosemTypeDateTime) { + memcpy(timestamp, ptr, dateTime->base.length); } else if(dateTime->base.type == 0x0C) { // Kamstrup bug... + memcpy(timestamp, ptr, dateTime->base.length); ptr += 13; } else { return -99; diff --git a/src/ams/hdlc.h b/src/ams/hdlc.h index dc709951..f87df6af 100644 --- a/src/ams/hdlc.h +++ b/src/ams/hdlc.h @@ -51,7 +51,8 @@ enum CosemType { CosemTypeString = 0x0A, CosemTypeDLongUnsigned = 0x06, CosemTypeLongSigned = 0x10, - CosemTypeLongUnsigned = 0x12 + CosemTypeLongUnsigned = 0x12, + CosemTypeDateTime = 0x19 }; struct CosemBasic { @@ -80,6 +81,20 @@ struct CosemLongSigned { int16_t data; } __attribute__((packed)); +struct CosemDateTime { + uint8_t type; + uint16_t year; + uint8_t month; + uint8_t dayOfMonth; + uint8_t dayOfWeek; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t hundredths; + int16_t deviation; + uint8_t status; +} __attribute__((packed)); + typedef union { struct CosemBasic base; struct CosemString str; @@ -87,9 +102,10 @@ typedef union { struct CosemLongUnsigned lu; struct CosemDLongUnsigned dlu; struct CosemLongSigned ls; + struct CosemDateTime dt; } CosemData; void mbus_hexdump(const uint8_t* buf, int len); -int HDLC_validate(const uint8_t* d, int len, HDLCConfig* config); +int HDLC_validate(const uint8_t* d, int len, HDLCConfig* config, CosemDateTime* timestamp); #endif