Hi all
I'm building a DSMR P1 digital meter emulator using an ESP32 programmed through Arduino IDE, but I'm having trouble calculating a valid CRC16 checksum. Perhaps an extra set of eyes might point me in the right direction.
A DSMR P1 meter outputs an ASCII message (the meter "telegram") containing all measured data points. An example of this telegram can be found here. Every line ends with a carriage return (CR), followed by a line feed (LF) (see page 15 here).
The final two bytes of the telegram are a CRC16 value calculated over the preceding characters in the telegram (from “/” to and including “!”), using the polynomial x16+x15+x2+1, no XOR in, no XOR out, computed with least significant bit first. This is also called CRC16-IBM.
I've written the following code which, as far as I can establish, should calculate the correct CRC16. For the example telegram, this should be EF2F, but when testing the code on the ESP32 gives me something else (E47C).
uint16_t crc16(const uint8_t *data) {
uint16_t crc = 0x0000; // initial value
while (*data) {
crc ^= (uint8_t)(*data++); // xor byte into least sig. byte of crc
for (int i = 0; i < 8; i++) {
if (crc & 1)
crc = (crc >> 1) ^ 0xA001;
else
crc >>= 1;
}
}
return crc;
}
void setup() {
Serial.begin(115200);
delay(2000);
const uint8_t data[] = "/ISk5\\2MT382-1000\r\n" //escape the \ in the telegram here
"\r\n"
"1-3:0.2.8(50)\r\n"
"0-0:1.0.0(101209113020W)\r\n"
"0-0:96.1.1(4B384547303034303436333935353037)\r\n"
"1-0:1.8.1(123456.789*kWh)\r\n"
"1-0:1.8.2(123456.789*kWh)\r\n"
"1-0:2.8.1(123456.789*kWh)\r\n"
"1-0:2.8.2(123456.789*kWh)\r\n"
"0-0:96.14.0(0002)\r\n"
"1-0:1.7.0(01.193*kW)\r\n"
"1-0:2.7.0(00.000*kW)\r\n"
"0-0:96.7.21(00004)\r\n"
"0-0:96.7.9(00002)\r\n"
"1-0:99.97.0(2)(0-0:96.7.19)(101208152415W)(0000000240*s)(101208151004W)(0000000301*s)\r\n"
"1-0:32.32.0(00002)\r\n"
"1-0:52.32.0(00001)\r\n"
"1-0:72.32.0(00000)\r\n"
"1-0:32.36.0(00000)\r\n"
"1-0:52.36.0(00003)\r\n"
"1-0:72.36.0(00000)\r\n"
"0-0:96.13.0(303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F303132333435363738393A3B3C3D3E3F)\r\n"
"1-0:32.7.0(220.1*V)\r\n"
"1-0:52.7.0(220.2*V)\r\n"
"1-0:72.7.0(220.3*V)\r\n"
"1-0:31.7.0(001*A)\r\n"
"1-0:51.7.0(002*A)\r\n"
"1-0:71.7.0(003*A)\r\n"
"1-0:21.7.0(01.111*kW)\r\n"
"1-0:41.7.0(02.222*kW)\r\n"
"1-0:61.7.0(03.333*kW)\r\n"
"1-0:22.7.0(04.444*kW)\r\n"
"1-0:42.7.0(05.555*kW)\r\n"
"1-0:62.7.0(06.666*kW)\r\n"
"0-1:24.1.0(003)\r\n"
"0-1:96.1.0(3232323241424344313233343536373839)\r\n"
"0-1:24.2.1(101209112500W)(12785.123*m3)\r\n"
"!"; //should give EF2F as CRC16
uint16_t computedCRC = crc16(data);
Serial.print("Computed CRC16: 0x");
Serial.println(computedCRC, HEX);
}
void loop() {
}
I've tried various things:
- Adding
/0to the end of the telegram - Not escaping/triple escaping the
\character - Using char in stead of uint8_t for the telegram and CRC calculation
- Using a String object, casting it to char using toCharArray
- Memcpy the char array from previous point to a uint8_t array to pass it to the CRC function
I believe the root of the issue lies in the use of the escape character \ (backslash), especially concerning the CR and LF. If I use this code to calculate the CRC16-IBM of a (shorter) ASCII string containing no \n or \r and with the \ double escaped, the returned value matches what I get from online calculators. But when inserting a \n or \r, the value is off.
Perhaps this might also have something to do with char being unsigned on ESP32 but signed on AVR Arduino?
Tl,dr: how to calculate the correct CRC16-IBM from the example ASCII string?