From 8c8dc25aa11c4f180260ece8447d0e492a76d31b Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Tue, 3 May 2022 23:34:23 +0100 Subject: [PATCH 01/11] WIP --- CMakeLists.txt | 4 ++- src/devices/honeywell_cm921.c | 49 ++++++++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4864ee61d..bd0eac714 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,7 +92,9 @@ if(("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" MATCHES # CMake Debug default for GCC/Clang is "-g -DNDEBUG" # set(CMAKE_C_FLAGS_DEBUG -g3 -O0) # make use of ASAN - set(CMAKE_C_FLAGS_DEBUG "-ggdb -fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer") + + set(CMAKE_C_FLAGS_DEBUG "-ggdb -fno-omit-frame-pointer") + #XXXset(CMAKE_C_FLAGS_DEBUG "-ggdb -fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer") endif() if("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") # make sure we don't accidentally copy more than an int diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index fd7b488bb..d25a3080b 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -16,7 +16,7 @@ Honeywell CM921 Thermostat (subset of Evohome). #include "decoder.h" -// #define _DEBUG +#define _DEBUG static int decode_10to8(uint8_t const *b, int pos, int end, uint8_t *out) { @@ -41,6 +41,7 @@ typedef struct { uint8_t header; uint8_t num_device_ids; uint8_t device_id[4][3]; + uint8_t seq; uint16_t command; uint8_t payload_length; uint8_t payload[256]; @@ -69,7 +70,7 @@ typedef struct { static const dev_map_entry_t device_map[] = { {.t = 1, .s = "CTL"}, // Controller {.t = 2, .s = "UFH"}, // Underfloor heating (HCC80, HCE80) - {.t = 3, .s = " 30"}, // HCW82?? + {.t = 3, .s = "Thm"}, // HCW82 {.t = 4, .s = "TRV"}, // Thermostatic radiator valve (HR80, HR91, HR92) {.t = 7, .s = "DHW"}, // DHW sensor (CS92) {.t = 10, .s = "OTB"}, // OpenTherm bridge (R8810) @@ -122,7 +123,7 @@ static data_t *decode_device_ids(const message_t *msg, data_t *data, int style) #define UNKNOWN_IF(C) do { \ if (C) \ - return data_append(data, "unknown", "", DATA_FORMAT, "%04x", DATA_INT, msg->command, NULL); \ + return data_append(data, "unknown", "", DATA_FORMAT, "%04x", DATA_INT, msg->command); \ } while (0) static data_t *honeywell_cm921_interpret_message(r_device *decoder, const message_t *msg, data_t *data) @@ -132,7 +133,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag // https://www.domoticaforum.eu/viewtopic.php?f=7&t=5806&start=30 // (specifically https://www.domoticaforum.eu/download/file.php?id=1396) - data = decode_device_ids(msg, data, 1); + data = decode_device_ids(msg, data, 0); // XXX IAP data_t *r = data; switch (msg->command) { @@ -234,8 +235,8 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag size_t num_zones = msg->payload_length/3; for (size_t i=0; i < num_zones; i++) { char name[256]; - snprintf(name, sizeof(name), "temperature (zone %u)", msg->payload[3*i]); - int16_t temp = msg->payload[3*i+1] << 8 | msg->payload[3*i+1]; + snprintf(name, sizeof(name), "temp (zone %u)", msg->payload[3*i]); + int16_t temp = msg->payload[3*i+1] << 8 | msg->payload[3*i+2]; data = data_append(data, name, "", DATA_DOUBLE, temp/100.0, NULL); } break; @@ -252,7 +253,9 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag break; } default: /* Unknown command */ - UNKNOWN_IF(1); +//printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX %04x\n",msg->command); + return data_append(data, "unknown", "", DATA_FORMAT, "%04X", DATA_INT, 0xDEAD); + //UNKNOWN_IF(1); } return r; } @@ -289,6 +292,7 @@ static int parse_msg(bitbuffer_t *bmsg, int row, message_t *msg) msg->num_device_ids = msg->header == 0x14 ? 1 : msg->header == 0x18 ? 2 : + msg->header == 0x1a ? 2 : msg->header == 0x1c ? 2 : msg->header == 0x10 ? 2 : msg->header == 0x3c ? 2 : @@ -298,6 +302,11 @@ static int parse_msg(bitbuffer_t *bmsg, int row, message_t *msg) for (unsigned j = 0; j < 3; j++) msg->device_id[i][j] = next(bb, &ipos, num_bytes); + if ( msg->header == 0x1a ) + msg->seq = next(bb, &ipos, num_bytes); + else + msg->seq = 0; + msg->command = (next(bb, &ipos, num_bytes) << 8) | next(bb, &ipos, num_bytes); msg->payload_length = next(bb, &ipos, num_bytes); @@ -390,14 +399,32 @@ static int honeywell_cm921_decode(r_device *decoder, bitbuffer_t *bitbuffer) int pr = parse_msg(&packet, 0, &message); +#ifndef _DEBUG // XXX if (pr <= 0) return pr; +#endif //XXX /* clang-format off */ - data_t *data = data_make( - "model", "", DATA_STRING, "Honeywell-CM921", +data_t *data; +if (pr <= 0) +{ + data = data_make( +//XXX "model", "", DATA_STRING, "Honeywell-CM921", + "mic", "Integrity", DATA_STRING, "CSUM_FAIL", + NULL); + data = add_hex_string(data, "Packet", packet.bb[row], packet.bits_per_row[row] / 8); + data = add_hex_string(data, "CRC", &message.crc, 1); + data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); + + decoder_output_data(decoder, data); + return 1; +} +else +{ + data = data_make( "mic", "Integrity", DATA_STRING, "CHECKSUM", NULL); +} /* clang-format on */ data = honeywell_cm921_interpret_message(decoder, &message, data); @@ -405,6 +432,7 @@ static int honeywell_cm921_decode(r_device *decoder, bitbuffer_t *bitbuffer) #ifdef _DEBUG data = add_hex_string(data, "Packet", packet.bb[row], packet.bits_per_row[row] / 8); data = add_hex_string(data, "Header", &message.header, 1); + data = add_hex_string(data, "Seq", &message.seq, 1); uint8_t cmd[2] = {message.command >> 8, message.command & 0x00FF}; data = add_hex_string(data, "Command", cmd, 2); data = add_hex_string(data, "Payload", message.payload, message.payload_length); @@ -424,10 +452,12 @@ static char *output_fields[] = { #ifdef _DEBUG "Packet", "Header", + "Flags", "Command", "Payload", "Unparsed", "CRC", + "Seq", "# man errors", #endif "unknown", @@ -455,6 +485,7 @@ static char *output_fields[] = { "actuator_run_time", "min_flow_temp", "mic", + "temp (zone 1)", NULL, }; From c20d4342eaeb6d245724d88c3e122c08c43a1b45 Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Sun, 8 May 2022 11:05:29 +0100 Subject: [PATCH 02/11] needs clean up but now handles all packets --- src/devices/honeywell_cm921.c | 270 ++++++++++++++++++++++++++++++---- 1 file changed, 239 insertions(+), 31 deletions(-) diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index d25a3080b..8a41ce37f 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -132,6 +132,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag // https://github.com/Evsdd/The-Evohome-Protocol/wiki // https://www.domoticaforum.eu/viewtopic.php?f=7&t=5806&start=30 // (specifically https://www.domoticaforum.eu/download/file.php?id=1396) + // https://github.com/zxdavb/ramses_protocol data = decode_device_ids(msg, data, 0); // XXX IAP @@ -199,10 +200,19 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag break; } case 0x2309: { - UNKNOWN_IF(msg->payload_length != 3); - data = data_append(data, "zone", "", DATA_INT, msg->payload[0], NULL); - // Observation: CM921 reports a very high setpoint during binding (0x7eff); packet: 143255c1230903017efff7 - data = data_append(data, "setpoint", "", DATA_DOUBLE, ((msg->payload[1] << 8) | msg->payload[2]) / 100.0, NULL); + UNKNOWN_IF(msg->payload_length % 3 != 0); + if (msg->payload_length == 3) { // for single zone use this message for backward compatibility + data = data_append(data, "zone", "", DATA_INT, msg->payload[0], NULL); + // Observation: CM921 reports a very high setpoint during binding (0x7eff); packet: 143255c1230903017efff7 + data = data_append(data, "setpoint", "", DATA_DOUBLE, ((msg->payload[1] << 8) | msg->payload[2]) / 100.0, NULL); + } + else + for (size_t i=0; i < msg->payload_length; i+=3) { + char name[256]; + snprintf(name, sizeof(name), "setpoint (zone %u)", msg->payload[i]); + int16_t temp = msg->payload[i+1] << 8 | msg->payload[i+2]; + data = data_append(data, name, "", DATA_DOUBLE, temp/100.0, NULL); + } break; } case 0x1100: { @@ -252,6 +262,55 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag data = data_append(data, "heat_demand", "", DATA_INT, msg->payload[1], NULL); break; } + case 0x1f09: { + // example "Packet" : "18045ef5045ef51f0903ff077693", "Header" : "18", "Seq" : "00", "Command" : "1f09", "Payload" : "ff0776", + // IAP no idea of the function, appears to contain a temp + UNKNOWN_IF(msg->payload_length != 3); + data = data_append(data, "zone", "", DATA_INT, msg->payload[0], NULL); + // Observation: CM921 reports a very high setpoint during binding (0x7eff); packet: 143255c1230903017efff7 + data = data_append(data, "setpoint", "", DATA_DOUBLE, ((msg->payload[1] << 8) | msg->payload[2]) / 100.0, NULL); + break; + } + case 0x0002: { + // Sent by a thermostat in resposne to a change in the aux wired input + // "Packet" : "1a0da2520da2520300020403010105d1", "Header" : "1a", "Seq" : "03", "Command" : "0002", "Payload" : "03010105" + // byte[2] of payload indicates whether aux input is logic 1 or 0 + // byte[0] is always 3 (remaining length?) + // byte[1] counts between 01 and 02 (seq always remians 0) + // byte[3] is always 5 + UNKNOWN_IF(msg->payload_length != 4); + data = data_append(data, "aux_input", "", DATA_INT, msg->payload[2], NULL); + break; + } + case 0x2d49: { + // Sent by CTL. 3 byte payload. Purpose unknown. + // "Packet" : "18045ef5045ef52d490300b4000d", "Header" : "18", "Seq" : "00", "Command" : "2d49", "Payload" : "00b400" + UNKNOWN_IF(msg->payload_length != 3); + break; + } + case 0x2389: { + // Sent by CTL. 3 byte payload. Purpose unknown. + // "Packet" : "1a0da2520da252b723890302ffe697", "Header" : "1a", "Seq" : "b7", "Command" : "2389", "Payload" : "02ffe6" + UNKNOWN_IF(msg->payload_length != 3); + break; + } + case 0x000a: { + UNKNOWN_IF(msg->payload_length % 6 != 0); + for (size_t i=0; i < msg->payload_length; i+=6) { + char name[256]; + int16_t temp; + snprintf(name, sizeof(name), "flags (zone %u)", msg->payload[i]); + temp = msg->payload[6*i+1]; + data = data_append(data, name, "", DATA_FORMAT, "%02x", DATA_INT, temp, NULL); + snprintf(name, sizeof(name), "temp_low (zone %u)", msg->payload[i]); + temp = msg->payload[6*i+2] << 8 | msg->payload[6*i+3]; + data = data_append(data, name, "", DATA_DOUBLE, temp/100.0, NULL); + snprintf(name, sizeof(name), "temp_high (zone %u)", msg->payload[i]); + temp = msg->payload[6*i+4] << 8 | msg->payload[6*i+5]; + data = data_append(data, name, "", DATA_DOUBLE, temp/100.0, NULL); + } + break; + } default: /* Unknown command */ //printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX %04x\n",msg->command); return data_append(data, "unknown", "", DATA_FORMAT, "%04X", DATA_INT, 0xDEAD); @@ -336,40 +395,74 @@ static int honeywell_cm921_decode(r_device *decoder, bitbuffer_t *bitbuffer) // post=10101100 // each byte surrounded by start/stop bits (0byte1) // then manchester decode. - const uint8_t preamble_pattern[4] = { 0x55, 0x5F, 0xF0, 0x04 }; - const uint8_t preamble_bit_length = 30; + const uint8_t preamble_pattern[4] = { 0x55, 0xff, 0x00, 0x40 }; // { 0x55, 0x5F, 0xF0, 0x04 }; + const uint8_t preamble_bit_length = 26; //30; const int row = 0; // we expect a single row only. + int sep = 0; - if (bitbuffer->num_rows != 1 || bitbuffer->bits_per_row[row] < 60) + if (bitbuffer->num_rows != 1 || bitbuffer->bits_per_row[row] < 60) { + decoder_logf(decoder, 1, __func__, "num_rows=%d, bits_per_row=%d", bitbuffer->num_rows, bitbuffer->bits_per_row[row]); return DECODE_ABORT_LENGTH; + } - decoder_log_bitrow(decoder, 1, __func__, bitbuffer->bb[row], bitbuffer->bits_per_row[row], ""); + decoder_log_bitrow(decoder, 1, __func__, bitbuffer->bb[row], bitbuffer->bits_per_row[row], "RX"); - int preamble_start = bitbuffer_search(bitbuffer, row, 0, preamble_pattern, preamble_bit_length); + int preamble_start = bitbuffer_search(bitbuffer, row, sep, preamble_pattern, preamble_bit_length); int start = preamble_start + preamble_bit_length; int len = bitbuffer->bits_per_row[row] - start; decoder_logf(decoder, 1, __func__, "preamble_start=%d start=%d len=%d", preamble_start, start, len); - if (len < 8) + { + int y; + for(y=0;y<8;y++) { + uint8_t tmp[1000]; + char tstr[200]; + memset(&tmp,0,1000); + bitbuffer_extract_bytes(bitbuffer, 0, y,tmp, bitbuffer->bits_per_row[row]-y); + bitrow_snprint(tmp, bitbuffer->bits_per_row[row]-y, tstr, sizeof (tstr)); + //decoder_logf(decoder, 1, __func__, "AA%d %s",y,tstr); + } + } + if (len < 8 ) + { + decoder_logf(decoder, 1, __func__, "err_runt"); + // continue; return DECODE_ABORT_LENGTH; - int end = start + len; + } + int end = start + len; bitbuffer_t bytes = {0}; int pos = start; while (pos < end) { uint8_t byte = 0; - if (decode_10to8(bitbuffer->bb[row], pos, end, &byte) != 10) + if (decode_10to8(bitbuffer->bb[row], pos, end, &byte) != 10) { + decoder_logf(decoder, 1, __func__, "err_8to10"); break; + } for (unsigned i = 0; i < 8; i++) bitbuffer_add_bit(&bytes, (byte >> i) & 0x1); pos += 10; } + decoder_log_bitrow(decoder, 1, __func__, bytes.bb[0], bytes.bits_per_row[0], "BBB"); + +while( sep < bytes.bits_per_row[row]) +{ + + uint8_t header_pattern[3] = { 0x33, 0x55, 0x53 }; + int start33 = bitbuffer_search(&bytes, row, sep, header_pattern, sizeof(header_pattern)*8); + + if ( start33 != sep ) decoder_logf(decoder, 1, __func__, "Strange start33=%d",start33); + +#if 0 // Skip Manchester breaking header uint8_t header[3] = { 0x33, 0x55, 0x53 }; if (bitrow_get_byte(bytes.bb[row], 0) != header[0] || bitrow_get_byte(bytes.bb[row], 8) != header[1] || bitrow_get_byte(bytes.bb[row], 16) != header[2]) + { + decoder_logf(decoder, 1, __func__, "did not find 33 55 53"); return DECODE_FAIL_SANITY; + } // Find Footer 0x35 (0x55*) int fi = bytes.bits_per_row[row] - 8; @@ -379,33 +472,66 @@ static int honeywell_cm921_decode(r_device *decoder, bitbuffer_t *bitbuffer) fi -= 8; } if (!seen_aa || bitrow_get_byte(bytes.bb[row], fi) != 0x35) + { + decoder_logf(decoder, 1, __func__, "did not find 35 55+"); return DECODE_FAIL_SANITY; + } +#endif + unsigned first_byte = start33+24; + uint8_t seperator_pattern[2] = { 0xff, 0x00 }; + sep = bitbuffer_search(&bytes, row, first_byte, seperator_pattern, sizeof(seperator_pattern)*8); + // if no seperator, this will be the end of the buffer + + if ( sep < bytes.bits_per_row[row] ) + decoder_logf(decoder, 1, __func__, "seperator at %d", sep); - unsigned first_byte = 24; - unsigned end_byte = fi; - unsigned num_bits = end_byte - first_byte; + // Find Footer 0x35 (0x55*) + unsigned fi = sep - 8; + sep = sep + 16; // advance over seperator + + int seen_aa = 0; + while (fi>8 && bitrow_get_byte(bytes.bb[row], fi) == 0x55) { + seen_aa = 1; + fi -= 8; + } + if (!seen_aa || bitrow_get_byte(bytes.bb[row], fi) != 0x35) + { + decoder_logf(decoder, 1, __func__, "did not find 35 55+"); + //return DECODE_FAIL_SANITY; + continue; + } + + + //unsigned end_byte = fi; + //unsigned num_bits = end_byte - first_byte; //unsigned num_bytes = num_bits/8 / 2; bitbuffer_t packet = {0}; - unsigned fpos = bitbuffer_manchester_decode(&bytes, row, first_byte, &packet, num_bits); - unsigned man_errors = num_bits - (fpos - first_byte - 2); + unsigned fpos = bitbuffer_manchester_decode(&bytes, row, first_byte, &packet, fi-first_byte); // decode as much as we can -#ifndef _DEBUG - if (man_errors != 0) - return DECODE_FAIL_SANITY; -#endif + if ( fpos < fi ) + { + decoder_logf(decoder, 1, __func__, "manchester_fail start=%d sep=%d fail_at=%d", first_byte, sep, fpos); + + char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len); + void free( void* ptr ); + + char * p = bitrow_asprint_bits(bytes.bb[0], fpos); + decoder_logf(decoder, 1, __func__, "USED: %s", p); + free(p); + p = bitrow_asprint_bits(bytes.bb[0], bytes.bits_per_row[0]); + decoder_logf(decoder, 1, __func__, "FULL: %s", p); + free(p); + //return DECODE_FAIL_SANITY; + continue; + } + decoder_logf(decoder, 1, __func__, "cool %d bytes",packet.bits_per_row[0]/8); message_t message; - int pr = parse_msg(&packet, 0, &message); -#ifndef _DEBUG // XXX - if (pr <= 0) - return pr; -#endif //XXX - /* clang-format off */ -data_t *data; + data_t *data; if (pr <= 0) { data = data_make( @@ -414,10 +540,11 @@ if (pr <= 0) NULL); data = add_hex_string(data, "Packet", packet.bb[row], packet.bits_per_row[row] / 8); data = add_hex_string(data, "CRC", &message.crc, 1); - data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); +// data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); decoder_output_data(decoder, data); - return 1; + //return 1; + continue; } else { @@ -438,10 +565,11 @@ else data = add_hex_string(data, "Payload", message.payload, message.payload_length); data = add_hex_string(data, "Unparsed", message.unparsed, message.unparsed_length); data = add_hex_string(data, "CRC", &message.crc, 1); - data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); + //data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); #endif decoder_output_data(decoder, data); +} return 1; } @@ -485,7 +613,87 @@ static char *output_fields[] = { "actuator_run_time", "min_flow_temp", "mic", + "aux_input", + "temp (zone 0)", "temp (zone 1)", + "temp (zone 2)", + "temp (zone 3)", + "temp (zone 4)", + "temp (zone 5)", + "temp (zone 6)", + "temp (zone 7)", + "temp (zone 8)", + "temp (zone 9)", + "temp (zone 10)", + "temp (zone 11)", + "temp (zone 12)", + "temp (zone 13)", + "temp (zone 14)", + "temp (zone 15)", + "setpoint (zone 0)", + "setpoint (zone 1)", + "setpoint (zone 2)", + "setpoint (zone 3)", + "setpoint (zone 4)", + "setpoint (zone 5)", + "setpoint (zone 6)", + "setpoint (zone 7)", + "setpoint (zone 8)", + "setpoint (zone 9)", + "setpoint (zone 10)", + "setpoint (zone 11)", + "setpoint (zone 12)", + "setpoint (zone 13)", + "setpoint (zone 14)", + "setpoint (zone 15)", + "temp_low (zone 0)", + "temp_low (zone 1)", + "temp_low (zone 2)", + "temp_low (zone 3)", + "temp_low (zone 4)", + "temp_low (zone 5)", + "temp_low (zone 6)", + "temp_low (zone 7)", + "temp_low (zone 8)", + "temp_low (zone 9)", + "temp_low (zone 10)", + "temp_low (zone 11)", + "temp_low (zone 12)", + "temp_low (zone 13)", + "temp_low (zone 14)", + "temp_low (zone 15)", + "temp_high (zone 0)", + "temp_high (zone 1)", + "temp_high (zone 2)", + "temp_high (zone 3)", + "temp_high (zone 4)", + "temp_high (zone 5)", + "temp_high (zone 6)", + "temp_high (zone 7)", + "temp_high (zone 8)", + "temp_high (zone 9)", + "temp_high (zone 10)", + "temp_high (zone 11)", + "temp_high (zone 12)", + "temp_high (zone 13)", + "temp_high (zone 14)", + "temp_high (zone 15)", + "flags (zone 0)", + "flags (zone 1)", + "flags (zone 2)", + "flags (zone 3)", + "flags (zone 4)", + "flags (zone 5)", + "flags (zone 6)", + "flags (zone 7)", + "flags (zone 8)", + "flags (zone 9)", + "flags (zone 10)", + "flags (zone 11)", + "flags (zone 12)", + "flags (zone 13)", + "flags (zone 14)", + "flags (zone 15)", NULL, }; From 928c504c228e0e316b49bed25a90e1bdf837d930 Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Sun, 15 Oct 2023 16:18:33 +0100 Subject: [PATCH 03/11] updates --- src/data.c | 2 +- src/decoder_util.c | 2 +- src/devices/honeywell_cm921.c | 18 ++++++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/data.c b/src/data.c index d6616ed54..8c365d43b 100644 --- a/src/data.c +++ b/src/data.c @@ -207,7 +207,7 @@ static data_t *vdata_make(data_t *first, const char *key, const char *pretty_key value.v_ptr = va_arg(ap, data_array_t *); break; default: - fprintf(stderr, "vdata_make() bad data type (%d)\n", type); + fprintf(stderr, "vdata_make() bad data type (%d) K%sK *%s*\n", type, key, pretty_key); goto alloc_error; } diff --git a/src/decoder_util.c b/src/decoder_util.c index b6c36e224..4a97e2625 100644 --- a/src/decoder_util.c +++ b/src/decoder_util.c @@ -178,7 +178,7 @@ static char *bitrow_asprint_code(uint8_t const *bitrow, unsigned bit_len) return row_code; } -static char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len) +char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len) { char *row_bits, *p; diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index 8a41ce37f..50e3c70df 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -294,7 +294,8 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag UNKNOWN_IF(msg->payload_length != 3); break; } - case 0x000a: { + case 0x000a: { + // Sent by CTL to UFH. Used for failsafe UNKNOWN_IF(msg->payload_length % 6 != 0); for (size_t i=0; i < msg->payload_length; i+=6) { char name[256]; @@ -311,10 +312,23 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag } break; } - default: /* Unknown command */ + case 0x1060: { + // belived to be batery status, but I have only ever seen 'ffff01' + UNKNOWN_IF(msg->payload_length != 3); + break; + } + case 0x2e04: { + // controller mode + UNKNOWN_IF(msg->payload_length % 8 !=0 ); + break; + } + default: { + /* Unknown command */ //printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX %04x\n",msg->command); return data_append(data, "unknown", "", DATA_FORMAT, "%04X", DATA_INT, 0xDEAD); //UNKNOWN_IF(1); + break; + } } return r; } From 5564efc838eef78291c70b64a60701fe544d1ff0 Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Wed, 18 Oct 2023 01:37:08 +0100 Subject: [PATCH 04/11] fixed for strange sum=1 packets --- src/devices/honeywell_cm921.c | 46 ++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index 8525c77c8..dc7dbb7f7 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -117,13 +117,13 @@ static data_t *decode_device_ids(const message_t *msg, data_t *data, int style) } strcat(ds, buf); } - +//printf("++++%s+++\n",ds); return data_append(data, "ids", "Device IDs", DATA_STRING, ds, NULL); } #define UNKNOWN_IF(C) do { \ if (C) \ - return data_append(data, "unknown", "", DATA_FORMAT, "%04x", DATA_INT, msg->command); \ + return data_append(data, "unknown", "", DATA_FORMAT, "0x%04x", DATA_INT, msg->command, NULL); \ } while (0) static data_t *honeywell_cm921_interpret_message(r_device *decoder, const message_t *msg, data_t *data) @@ -134,7 +134,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag // (specifically https://www.domoticaforum.eu/download/file.php?id=1396) // https://github.com/zxdavb/ramses_protocol - data = decode_device_ids(msg, data, 0); // XXX IAP + data = decode_device_ids(msg, data, 1); // XXX IAP data_t *r = data; switch (msg->command) { @@ -181,7 +181,10 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag break; } case 0x0008: { - UNKNOWN_IF(msg->payload_length != 2); + // Relay Heat Demand + // There seems to be two version of this message, a normal one of length 2, and one with csum==1 of length 0x0d + + UNKNOWN_IF(msg->payload_length != 2); data = data_append(data, "domain_id", "", DATA_INT, msg->payload[0], NULL); data = data_append(data, "demand", "", DATA_DOUBLE, msg->payload[1] * (1 / 200.0F) /* 0xC8 */, NULL); break; @@ -199,6 +202,14 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag } break; } + case 0x3ef1: { + // some kind of actuator message. This always has the bsum == 01. + // len, 3 bytes, counter, ???, csum + // 0c 001784 03 8500ec83ae1ae9db 56 + // 0b 001784 02 4b1c8a136c0910 4f + UNKNOWN_IF(1); + break; + } case 0x2309: { UNKNOWN_IF(msg->payload_length % 3 != 0); if (msg->payload_length == 3) { // for single zone use this message for backward compatibility @@ -322,11 +333,16 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag UNKNOWN_IF(msg->payload_length % 8 !=0 ); break; } + case 0x1fc9: { + // RF Bind + // 0b 01fe ffff 2db9 9b5d 942b 5c c6 + // 0b 0205 ffff 411c e725 1210 c2 6a + UNKNOWN_IF(1); + break; + } default: { /* Unknown command */ -//printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX %04x\n",msg->command); - return data_append(data, "unknown", "", DATA_FORMAT, "%04X", DATA_INT, 0xDEAD); - //UNKNOWN_IF(1); + UNKNOWN_IF(1); break; } } @@ -355,8 +371,10 @@ static int parse_msg(bitbuffer_t *bmsg, int row, message_t *msg) // Checksum: All bytes add up to 0. int bsum = add_bytes(bb, num_bytes) & 0xff; - int checksum_ok = bsum == 0; - msg->crc = bitrow_get_byte(bb, bmsg->bits_per_row[row] - 8); + int checksum_ok = (bsum == 0 || bsum == 1) ? 1 : 0 ; // bizarely, some packets have a csum of 1 +// int checksum_ok = bsum == 0; +// msg->crc = bitrow_get_byte(bb, bmsg->bits_per_row[row] - 8); + msg->crc = bsum; // more useful to record what the sum was if (!checksum_ok) return DECODE_FAIL_MIC; @@ -369,7 +387,9 @@ static int parse_msg(bitbuffer_t *bmsg, int row, message_t *msg) msg->header == 0x1c ? 2 : msg->header == 0x10 ? 2 : msg->header == 0x3c ? 2 : - (msg->header >> 2) & 0x03; // total speculation. + msg->header == 0x8c ? 2 : + msg->header == 0x9c ? 2 : + 2 ; // total speculation. for (unsigned i = 0; i < msg->num_device_ids; i++) for (unsigned j = 0; j < 3; j++) @@ -551,10 +571,11 @@ if (pr <= 0) data = data_make( //XXX "model", "", DATA_STRING, "Honeywell-CM921", "mic", "Integrity", DATA_STRING, "CSUM_FAIL", + "err", "Error", DATA_INT, pr, NULL); data = add_hex_string(data, "Packet", packet.bb[row], packet.bits_per_row[row] / 8); data = add_hex_string(data, "CRC", &message.crc, 1); -// data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); + //data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); decoder_output_data(decoder, data); //return 1; @@ -563,7 +584,7 @@ if (pr <= 0) else { data = data_make( - "mic", "Integrity", DATA_STRING, "CHECKSUM", + "mic", "", DATA_STRING, "CHECKSUM", NULL); } /* clang-format on */ @@ -627,6 +648,7 @@ static char const *const output_fields[] = { "actuator_run_time", "min_flow_temp", "mic", + "err", "aux_input", "temperature (zone 0)", "temperature (zone 1)", From 29c5c8cddbd5ddc469b2337fdd4bb0c62b5e8ca9 Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Thu, 19 Oct 2023 02:09:01 +0100 Subject: [PATCH 05/11] Wcleanup ongoing --- src/decoder_util.c | 2 +- src/devices/honeywell_cm921.c | 58 ++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/decoder_util.c b/src/decoder_util.c index b895b56d4..fff89fa34 100644 --- a/src/decoder_util.c +++ b/src/decoder_util.c @@ -72,7 +72,7 @@ static char *bitrow_asprint_code(uint8_t const *bitrow, unsigned bit_len) return row_code; } -char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len) +static char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len) { char *row_bits, *p; diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index dc7dbb7f7..bfccb389c 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -48,6 +48,7 @@ typedef struct { uint8_t unparsed_length; uint8_t unparsed[256]; uint8_t crc; + uint8_t csum; } message_t; #ifdef _DEBUG @@ -117,7 +118,6 @@ static data_t *decode_device_ids(const message_t *msg, data_t *data, int style) } strcat(ds, buf); } -//printf("++++%s+++\n",ds); return data_append(data, "ids", "Device IDs", DATA_STRING, ds, NULL); } @@ -134,9 +134,26 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag // (specifically https://www.domoticaforum.eu/download/file.php?id=1396) // https://github.com/zxdavb/ramses_protocol - data = decode_device_ids(msg, data, 1); // XXX IAP - + data = decode_device_ids(msg, data, 1); data_t *r = data; + + if (msg->csum == 0x01) { + // some devices send messages with the final byte set such that the byte sum is 0x01 rather than the usual 0x00 + // These messages always have a header of 1c|7c|8c|9c, two addresses, a 2 byte cmd, payload size, payload, csum byte + // not sure whice devices send them, but one of them uses the 10e0 message to identify as a "Jasper EIM" + switch (msg->command) { + case 0x0008: // Relay Demand message with an extra long payload. eg. 0017872b4ba4402a1bc756f7ec + case 0x0502: // mystery long message that come in pairs + case 0x10e0: // Device Info message sent to broadcast address, contains ASCI string + case 0x1100: // Boiler Relay info e.g. 02090002ee7856655a75f2a3fce10f05334453 + case 0x1fc9: // RF bind e.g. 0209ffff692c1e918ecf5f + case 0x3ef0: // Actuator info e.g. 020a001e60ebe52a896c7eaa5451e77ae9440fef + case 0x3ef1: // Actuator unknown 020a001f653c7eab76d7dd91259d61d50048 + default: + UNKNOWN_IF(1); + break; + } + } else { switch (msg->command) { case 0x1030: { UNKNOWN_IF(msg->payload_length != 16); @@ -346,6 +363,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag break; } } + } return r; } @@ -371,10 +389,9 @@ static int parse_msg(bitbuffer_t *bmsg, int row, message_t *msg) // Checksum: All bytes add up to 0. int bsum = add_bytes(bb, num_bytes) & 0xff; - int checksum_ok = (bsum == 0 || bsum == 1) ? 1 : 0 ; // bizarely, some packets have a csum of 1 -// int checksum_ok = bsum == 0; -// msg->crc = bitrow_get_byte(bb, bmsg->bits_per_row[row] - 8); - msg->crc = bsum; // more useful to record what the sum was + int checksum_ok = (bsum == 0 || bsum == 1) ? 1 : 0 ; // bizarely, some packets have a bsum of 1 + msg->crc = bitrow_get_byte(bb, bmsg->bits_per_row[row] - 8); + msg->csum = bsum; // more useful to record what the sum was if (!checksum_ok) return DECODE_FAIL_MIC; @@ -547,16 +564,16 @@ while( sep < bytes.bits_per_row[row]) { decoder_logf(decoder, 1, __func__, "manchester_fail start=%d sep=%d fail_at=%d", first_byte, sep, fpos); - char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len); - void free( void* ptr ); + // char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len); + // void free( void* ptr ); + // char * p = bitrow_asprint_bits(bytes.bb[0], fpos); + // decoder_logf(decoder, 1, __func__, "USED: %s", p); + // free(p); + // p = bitrow_asprint_bits(bytes.bb[0], bytes.bits_per_row[0]); + // decoder_logf(decoder, 1, __func__, "FULL: %s", p); + // free(p); + // return DECODE_FAIL_SANITY; - char * p = bitrow_asprint_bits(bytes.bb[0], fpos); - decoder_logf(decoder, 1, __func__, "USED: %s", p); - free(p); - p = bitrow_asprint_bits(bytes.bb[0], bytes.bits_per_row[0]); - decoder_logf(decoder, 1, __func__, "FULL: %s", p); - free(p); - //return DECODE_FAIL_SANITY; continue; } @@ -569,16 +586,15 @@ while( sep < bytes.bits_per_row[row]) if (pr <= 0) { data = data_make( -//XXX "model", "", DATA_STRING, "Honeywell-CM921", + "model", "", DATA_STRING, "Honeywell-CM921", "mic", "Integrity", DATA_STRING, "CSUM_FAIL", "err", "Error", DATA_INT, pr, NULL); data = add_hex_string(data, "Packet", packet.bb[row], packet.bits_per_row[row] / 8); data = add_hex_string(data, "CRC", &message.crc, 1); - //data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); + data = add_hex_string(data, "csum", &message.csum, 1); decoder_output_data(decoder, data); - //return 1; continue; } else @@ -600,7 +616,7 @@ else data = add_hex_string(data, "Payload", message.payload, message.payload_length); data = add_hex_string(data, "Unparsed", message.unparsed, message.unparsed_length); data = add_hex_string(data, "CRC", &message.crc, 1); - //data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); + data = add_hex_string(data, "csum", &message.csum, 1); #endif decoder_output_data(decoder, data); @@ -620,8 +636,8 @@ static char const *const output_fields[] = { "Payload", "Unparsed", "CRC", + "csum", "Seq", - "# man errors", #endif "unknown", "time_request", From 59d5a3038976477e169fd41655cc056e35b1b143 Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Fri, 20 Oct 2023 01:58:29 +0100 Subject: [PATCH 06/11] pretty clean now --- CMakeLists.txt | 4 +- src/data.c | 2 +- src/devices/honeywell_cm921.c | 148 +++++++++++----------------------- 3 files changed, 47 insertions(+), 107 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f7c1a323..d1ce058e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,9 +93,7 @@ if(("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" MATCHES # CMake Debug default for GCC/Clang is "-g -DNDEBUG" # set(CMAKE_C_FLAGS_DEBUG -g3 -O0) # make use of ASAN - - set(CMAKE_C_FLAGS_DEBUG "-ggdb -fno-omit-frame-pointer") - #XXXset(CMAKE_C_FLAGS_DEBUG "-ggdb -fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_C_FLAGS_DEBUG "-ggdb -fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer") endif() if("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") # make sure we don't accidentally copy more than an int diff --git a/src/data.c b/src/data.c index 6a4e7980f..c13354350 100644 --- a/src/data.c +++ b/src/data.c @@ -207,7 +207,7 @@ static data_t *vdata_make(data_t *first, const char *key, const char *pretty_key value.v_ptr = va_arg(ap, data_array_t *); break; default: - fprintf(stderr, "vdata_make() bad data type (%d) K%sK *%s*\n", type, key, pretty_key); + fprintf(stderr, "vdata_make() bad data type (%d)\n", type); goto alloc_error; } diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index bfccb389c..43491ed2a 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -123,7 +123,7 @@ static data_t *decode_device_ids(const message_t *msg, data_t *data, int style) #define UNKNOWN_IF(C) do { \ if (C) \ - return data_append(data, "unknown", "", DATA_FORMAT, "0x%04x", DATA_INT, msg->command, NULL); \ + return data_append(data, "unknown", "", DATA_FORMAT, "%04x", DATA_INT, msg->command, NULL); \ } while (0) static data_t *honeywell_cm921_interpret_message(r_device *decoder, const message_t *msg, data_t *data) @@ -135,6 +135,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag // https://github.com/zxdavb/ramses_protocol data = decode_device_ids(msg, data, 1); + data_t *r = data; if (msg->csum == 0x01) { @@ -142,7 +143,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag // These messages always have a header of 1c|7c|8c|9c, two addresses, a 2 byte cmd, payload size, payload, csum byte // not sure whice devices send them, but one of them uses the 10e0 message to identify as a "Jasper EIM" switch (msg->command) { - case 0x0008: // Relay Demand message with an extra long payload. eg. 0017872b4ba4402a1bc756f7ec + case 0x0008: // Relay Demand message with a 13 byte payload. eg. 0017872b4ba4402a1bc756f7ec case 0x0502: // mystery long message that come in pairs case 0x10e0: // Device Info message sent to broadcast address, contains ASCI string case 0x1100: // Boiler Relay info e.g. 02090002ee7856655a75f2a3fce10f05334453 @@ -199,7 +200,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag } case 0x0008: { // Relay Heat Demand - // There seems to be two version of this message, a normal one of length 2, and one with csum==1 of length 0x0d + // The version of this message with csum=0 alayws seems to be 2 byte UNKNOWN_IF(msg->payload_length != 2); data = data_append(data, "domain_id", "", DATA_INT, msg->payload[0], NULL); @@ -219,17 +220,9 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag } break; } - case 0x3ef1: { - // some kind of actuator message. This always has the bsum == 01. - // len, 3 bytes, counter, ???, csum - // 0c 001784 03 8500ec83ae1ae9db 56 - // 0b 001784 02 4b1c8a136c0910 4f - UNKNOWN_IF(1); - break; - } case 0x2309: { UNKNOWN_IF(msg->payload_length % 3 != 0); - if (msg->payload_length == 3) { // for single zone use this message for backward compatibility + if (msg->payload_length == 3) { // for single zone use this JSON message for backward compatibility data = data_append(data, "zone", "", DATA_INT, msg->payload[0], NULL); // Observation: CM921 reports a very high setpoint during binding (0x7eff); packet: 143255c1230903017efff7 data = data_append(data, "setpoint", "", DATA_DOUBLE, ((msg->payload[1] << 8) | msg->payload[2]) * (1 / 100.0F), NULL); @@ -291,20 +284,18 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag break; } case 0x1f09: { + // System Sync message. 3 byte message. domain_id then 2 byte countdown in tenths of a second // example "Packet" : "18045ef5045ef51f0903ff077693", "Header" : "18", "Seq" : "00", "Command" : "1f09", "Payload" : "ff0776", - // IAP no idea of the function, appears to contain a temp + // not sure its useful to log this to JSON UNKNOWN_IF(msg->payload_length != 3); - data = data_append(data, "zone", "", DATA_INT, msg->payload[0], NULL); - // Observation: CM921 reports a very high setpoint during binding (0x7eff); packet: 143255c1230903017efff7 - data = data_append(data, "setpoint", "", DATA_DOUBLE, ((msg->payload[1] << 8) | msg->payload[2]) / 100.0, NULL); break; } case 0x0002: { - // Sent by a thermostat in resposne to a change in the aux wired input + // External Sensor. Sent by a thermostat in resposne to a change in the aux wired input // "Packet" : "1a0da2520da2520300020403010105d1", "Header" : "1a", "Seq" : "03", "Command" : "0002", "Payload" : "03010105" - // byte[2] of payload indicates whether aux input is logic 1 or 0 // byte[0] is always 3 (remaining length?) // byte[1] counts between 01 and 02 (seq always remians 0) + // byte[2] of payload indicates whether aux input is logic 1 or 0 // byte[3] is always 5 UNKNOWN_IF(msg->payload_length != 4); data = data_append(data, "aux_input", "", DATA_INT, msg->payload[2], NULL); @@ -323,31 +314,36 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag break; } case 0x000a: { - // Sent by CTL to UFH. Used for failsafe + // Zone Config. Sent by CTL to UFH. Used for failsafe UNKNOWN_IF(msg->payload_length % 6 != 0); for (size_t i=0; i < msg->payload_length; i+=6) { char name[256]; int16_t temp; snprintf(name, sizeof(name), "flags (zone %u)", msg->payload[i]); - temp = msg->payload[6*i+1]; + temp = msg->payload[i+1]; data = data_append(data, name, "", DATA_FORMAT, "%02x", DATA_INT, temp, NULL); snprintf(name, sizeof(name), "temp_low (zone %u)", msg->payload[i]); - temp = msg->payload[6*i+2] << 8 | msg->payload[6*i+3]; + temp = msg->payload[i+2] << 8 | msg->payload[i+3]; data = data_append(data, name, "", DATA_DOUBLE, temp/100.0, NULL); snprintf(name, sizeof(name), "temp_high (zone %u)", msg->payload[i]); - temp = msg->payload[6*i+4] << 8 | msg->payload[6*i+5]; + temp = msg->payload[i+4] << 8 | msg->payload[i+5]; data = data_append(data, name, "", DATA_DOUBLE, temp/100.0, NULL); } break; } case 0x1060: { - // belived to be batery status, but I have only ever seen 'ffff01' + // Battery Status. I only observe 'ffff01', likely because all sensors are mains powered. + // payload[2] 1=OK 0-low UNKNOWN_IF(msg->payload_length != 3); + data = data_append(data, "battery_ok", "", DATA_INT, msg->payload[2], NULL); break; } case 0x2e04: { - // controller mode - UNKNOWN_IF(msg->payload_length % 8 !=0 ); + // Controller Mode. zxdavb lists the payload below but this in no way matches the 16 byte messages I see + // payload[0] Program mode (0=Auto, 1=Heating Off, 2=Eco, 3=Away, 4=Day off, 7=Custom) + // payload[1] hours; payload[2] minutes; payload[3] day; payload[4] month; payload[5] year + // payload[6] Program type (0=Permanent, 1=Timed) + UNKNOWN_IF(1); break; } case 0x1fc9: { @@ -389,9 +385,9 @@ static int parse_msg(bitbuffer_t *bmsg, int row, message_t *msg) // Checksum: All bytes add up to 0. int bsum = add_bytes(bb, num_bytes) & 0xff; - int checksum_ok = (bsum == 0 || bsum == 1) ? 1 : 0 ; // bizarely, some packets have a bsum of 1 + int checksum_ok = (bsum == 0 || bsum == 1) ? 1 : 0 ; // bizarely, some packets have a csum of 1 rather than 0 msg->crc = bitrow_get_byte(bb, bmsg->bits_per_row[row] - 8); - msg->csum = bsum; // more useful to record what the sum was + msg->csum = bsum; // record what the csum was if (!checksum_ok) return DECODE_FAIL_MIC; @@ -446,8 +442,11 @@ static int honeywell_cm921_decode(r_device *decoder, bitbuffer_t *bitbuffer) // post=10101100 // each byte surrounded by start/stop bits (0byte1) // then manchester decode. - const uint8_t preamble_pattern[4] = { 0x55, 0xff, 0x00, 0x40 }; // { 0x55, 0x5F, 0xF0, 0x04 }; - const uint8_t preamble_bit_length = 26; //30; + // Preamble pattern was { 0x55, 0x5F, 0xF0, 0x04 } and 30 bits, but I + // observed the first nibble was frequently corrupted in otherwise perfect packets + + const uint8_t preamble_pattern[4] = { 0x55, 0xff, 0x00, 0x40 }; + const uint8_t preamble_bit_length = 26; const int row = 0; // we expect a single row only. int sep = 0; @@ -462,21 +461,10 @@ static int honeywell_cm921_decode(r_device *decoder, bitbuffer_t *bitbuffer) int start = preamble_start + preamble_bit_length; int len = bitbuffer->bits_per_row[row] - start; decoder_logf(decoder, 1, __func__, "preamble_start=%d start=%d len=%d", preamble_start, start, len); - { - int y; - for(y=0;y<8;y++) { - uint8_t tmp[1000]; - char tstr[200]; - memset(&tmp,0,1000); - bitbuffer_extract_bytes(bitbuffer, 0, y,tmp, bitbuffer->bits_per_row[row]-y); - bitrow_snprint(tmp, bitbuffer->bits_per_row[row]-y, tstr, sizeof (tstr)); - //decoder_logf(decoder, 1, __func__, "AA%d %s",y,tstr); - } - } + if (len < 8 ) { decoder_logf(decoder, 1, __func__, "err_runt"); - // continue; return DECODE_ABORT_LENGTH; } @@ -496,6 +484,7 @@ static int honeywell_cm921_decode(r_device *decoder, bitbuffer_t *bitbuffer) decoder_log_bitrow(decoder, 1, __func__, bytes.bb[0], bytes.bits_per_row[0], "BBB"); + // HCM200d sends multiple commands in the same radio message, separated by an ff00 sequence. Loop over each while( sep < bytes.bits_per_row[row]) { @@ -504,30 +493,6 @@ while( sep < bytes.bits_per_row[row]) if ( start33 != sep ) decoder_logf(decoder, 1, __func__, "Strange start33=%d",start33); -#if 0 - // Skip Manchester breaking header - uint8_t header[3] = { 0x33, 0x55, 0x53 }; - if (bitrow_get_byte(bytes.bb[row], 0) != header[0] || - bitrow_get_byte(bytes.bb[row], 8) != header[1] || - bitrow_get_byte(bytes.bb[row], 16) != header[2]) - { - decoder_logf(decoder, 1, __func__, "did not find 33 55 53"); - return DECODE_FAIL_SANITY; - } - - // Find Footer 0x35 (0x55*) - int fi = bytes.bits_per_row[row] - 8; - int seen_aa = 0; - while (bitrow_get_byte(bytes.bb[row], fi) == 0x55) { - seen_aa = 1; - fi -= 8; - } - if (!seen_aa || bitrow_get_byte(bytes.bb[row], fi) != 0x35) - { - decoder_logf(decoder, 1, __func__, "did not find 35 55+"); - return DECODE_FAIL_SANITY; - } -#endif unsigned first_byte = start33+24; uint8_t seperator_pattern[2] = { 0xff, 0x00 }; sep = bitbuffer_search(&bytes, row, first_byte, seperator_pattern, sizeof(seperator_pattern)*8); @@ -548,32 +513,16 @@ while( sep < bytes.bits_per_row[row]) if (!seen_aa || bitrow_get_byte(bytes.bb[row], fi) != 0x35) { decoder_logf(decoder, 1, __func__, "did not find 35 55+"); - //return DECODE_FAIL_SANITY; continue; } - - //unsigned end_byte = fi; - //unsigned num_bits = end_byte - first_byte; - //unsigned num_bytes = num_bits/8 / 2; - bitbuffer_t packet = {0}; unsigned fpos = bitbuffer_manchester_decode(&bytes, row, first_byte, &packet, fi-first_byte); // decode as much as we can + unsigned man_errors = (fi - first_byte) - (fpos - first_byte - 2); if ( fpos < fi ) { decoder_logf(decoder, 1, __func__, "manchester_fail start=%d sep=%d fail_at=%d", first_byte, sep, fpos); - - // char *bitrow_asprint_bits(uint8_t const *bitrow, unsigned bit_len); - // void free( void* ptr ); - // char * p = bitrow_asprint_bits(bytes.bb[0], fpos); - // decoder_logf(decoder, 1, __func__, "USED: %s", p); - // free(p); - // p = bitrow_asprint_bits(bytes.bb[0], bytes.bits_per_row[0]); - // decoder_logf(decoder, 1, __func__, "FULL: %s", p); - // free(p); - // return DECODE_FAIL_SANITY; - continue; } @@ -581,33 +530,23 @@ while( sep < bytes.bits_per_row[row]) message_t message; int pr = parse_msg(&packet, 0, &message); +#ifndef _DEBUG + if (pr <= 0) + continue; +#endif + /* clang-format off */ data_t *data; -if (pr <= 0) -{ data = data_make( - "model", "", DATA_STRING, "Honeywell-CM921", - "mic", "Integrity", DATA_STRING, "CSUM_FAIL", - "err", "Error", DATA_INT, pr, + "model", "", DATA_STRING, "Honeywell-CM921", NULL); - data = add_hex_string(data, "Packet", packet.bb[row], packet.bits_per_row[row] / 8); - data = add_hex_string(data, "CRC", &message.crc, 1); - data = add_hex_string(data, "csum", &message.csum, 1); - - decoder_output_data(decoder, data); - continue; -} -else -{ - data = data_make( - "mic", "", DATA_STRING, "CHECKSUM", - NULL); -} /* clang-format on */ data = honeywell_cm921_interpret_message(decoder, &message, data); #ifdef _DEBUG + data = data_append(data, "mic", "Integrity", DATA_STRING, pr>0 ? "CHECKSUM" : "CSUM_FAIL", NULL); + data = data_append(data, "err", "Error", DATA_INT, pr, NULL); data = add_hex_string(data, "Packet", packet.bb[row], packet.bits_per_row[row] / 8); data = add_hex_string(data, "Header", &message.header, 1); data = add_hex_string(data, "Seq", &message.seq, 1); @@ -616,6 +555,7 @@ else data = add_hex_string(data, "Payload", message.payload, message.payload_length); data = add_hex_string(data, "Unparsed", message.unparsed, message.unparsed_length); data = add_hex_string(data, "CRC", &message.crc, 1); + data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); data = add_hex_string(data, "csum", &message.csum, 1); #endif @@ -631,15 +571,18 @@ static char const *const output_fields[] = { #ifdef _DEBUG "Packet", "Header", - "Flags", "Command", "Payload", "Unparsed", "CRC", + "# man errors", "csum", "Seq", + "mic", + "err", #endif "unknown", + "Flags", "time_request", "flame_status", "zone", @@ -663,9 +606,8 @@ static char const *const output_fields[] = { "pump_run_time", "actuator_run_time", "min_flow_temp", - "mic", - "err", "aux_input", + "battery_ok", "temperature (zone 0)", "temperature (zone 1)", "temperature (zone 2)", From 819c8f1bbe41a595772acefe181cf36c226abd9d Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Sun, 22 Oct 2023 12:43:40 +0100 Subject: [PATCH 07/11] good shape now --- src/devices/honeywell_cm921.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index 43491ed2a..d8c1173ae 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -16,7 +16,7 @@ Honeywell CM921 Thermostat (subset of Evohome). #include "decoder.h" -#define _DEBUG +//#define _DEBUG static int decode_10to8(uint8_t const *b, int pos, int end, uint8_t *out) { @@ -51,7 +51,6 @@ typedef struct { uint8_t csum; } message_t; -#ifdef _DEBUG static data_t *add_hex_string(data_t *data, const char *name, const uint8_t *buf, size_t buf_sz) { if (buf && buf_sz > 0) { @@ -61,7 +60,6 @@ static data_t *add_hex_string(data_t *data, const char *name, const uint8_t *buf } return data; } -#endif typedef struct { int t; @@ -518,7 +516,10 @@ while( sep < bytes.bits_per_row[row]) bitbuffer_t packet = {0}; unsigned fpos = bitbuffer_manchester_decode(&bytes, row, first_byte, &packet, fi-first_byte); // decode as much as we can + +#ifdef _DEBUG unsigned man_errors = (fi - first_byte) - (fpos - first_byte - 2); +#endif if ( fpos < fi ) { @@ -544,19 +545,23 @@ while( sep < bytes.bits_per_row[row]) data = honeywell_cm921_interpret_message(decoder, &message, data); + data = data_append(data, "mic", "Integrity", DATA_STRING, (pr>0) ? "CHECKSUM" : "CSUM_FAIL", NULL); + + if(1 || decoder->verbose) { + uint8_t cmd[2] = {message.command >> 8, message.command & 0x00FF}; + data = add_hex_string(data, "Command", cmd, 2); + data = add_hex_string(data, "Payload", message.payload, message.payload_length); + data = add_hex_string(data, "csum", &message.csum, 1); + } + #ifdef _DEBUG - data = data_append(data, "mic", "Integrity", DATA_STRING, pr>0 ? "CHECKSUM" : "CSUM_FAIL", NULL); - data = data_append(data, "err", "Error", DATA_INT, pr, NULL); + if (pr<=0) data = data_append(data, "err", "Error", DATA_INT, pr, NULL); data = add_hex_string(data, "Packet", packet.bb[row], packet.bits_per_row[row] / 8); data = add_hex_string(data, "Header", &message.header, 1); data = add_hex_string(data, "Seq", &message.seq, 1); - uint8_t cmd[2] = {message.command >> 8, message.command & 0x00FF}; - data = add_hex_string(data, "Command", cmd, 2); - data = add_hex_string(data, "Payload", message.payload, message.payload_length); data = add_hex_string(data, "Unparsed", message.unparsed, message.unparsed_length); data = add_hex_string(data, "CRC", &message.crc, 1); data = data_append(data, "# man errors", "", DATA_INT, man_errors, NULL); - data = add_hex_string(data, "csum", &message.csum, 1); #endif decoder_output_data(decoder, data); @@ -571,16 +576,16 @@ static char const *const output_fields[] = { #ifdef _DEBUG "Packet", "Header", - "Command", - "Payload", "Unparsed", "CRC", "# man errors", - "csum", "Seq", - "mic", "err", #endif + "Command", + "Payload", + "csum", + "mic", "unknown", "Flags", "time_request", From 34ae721c9ba5041c34658ec15051936a929ba357 Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Sun, 22 Oct 2023 13:09:42 +0100 Subject: [PATCH 08/11] ready to merge --- src/devices/honeywell_cm921.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index d8c1173ae..d785b2775 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -16,7 +16,7 @@ Honeywell CM921 Thermostat (subset of Evohome). #include "decoder.h" -//#define _DEBUG +// #define _DEBUG static int decode_10to8(uint8_t const *b, int pos, int end, uint8_t *out) { @@ -132,7 +132,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag // (specifically https://www.domoticaforum.eu/download/file.php?id=1396) // https://github.com/zxdavb/ramses_protocol - data = decode_device_ids(msg, data, 1); + data = decode_device_ids(msg, data, 1); data_t *r = data; @@ -198,7 +198,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag } case 0x0008: { // Relay Heat Demand - // The version of this message with csum=0 alayws seems to be 2 byte + // The version of this message with csum=0 always seems to be 2 byte UNKNOWN_IF(msg->payload_length != 2); data = data_append(data, "domain_id", "", DATA_INT, msg->payload[0], NULL); @@ -547,7 +547,7 @@ while( sep < bytes.bits_per_row[row]) data = data_append(data, "mic", "Integrity", DATA_STRING, (pr>0) ? "CHECKSUM" : "CSUM_FAIL", NULL); - if(1 || decoder->verbose) { + if(1 || decoder->verbose) { // decoder->verbose never seems to get set so force as these are always helpful uint8_t cmd[2] = {message.command >> 8, message.command & 0x00FF}; data = add_hex_string(data, "Command", cmd, 2); data = add_hex_string(data, "Payload", message.payload, message.payload_length); From 3e80d8a6cda3347b20bdd31163b9f7f580b58c4e Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Sun, 22 Oct 2023 19:45:59 +0100 Subject: [PATCH 09/11] review feedback --- src/devices/honeywell_cm921.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index d785b2775..84f19f7d0 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -383,7 +383,7 @@ static int parse_msg(bitbuffer_t *bmsg, int row, message_t *msg) // Checksum: All bytes add up to 0. int bsum = add_bytes(bb, num_bytes) & 0xff; - int checksum_ok = (bsum == 0 || bsum == 1) ? 1 : 0 ; // bizarely, some packets have a csum of 1 rather than 0 + int checksum_ok = (bsum == 0 || bsum == 1); // bizarely, some packets have a csum of 1 rather than 0 msg->crc = bitrow_get_byte(bb, bmsg->bits_per_row[row] - 8); msg->csum = bsum; // record what the csum was @@ -537,10 +537,7 @@ while( sep < bytes.bits_per_row[row]) #endif /* clang-format off */ - data_t *data; - data = data_make( - "model", "", DATA_STRING, "Honeywell-CM921", - NULL); + data_t *data = data_make( "model", "", DATA_STRING, "Honeywell-CM921", NULL ); /* clang-format on */ data = honeywell_cm921_interpret_message(decoder, &message, data); From 23acd25aeea5f36ca8f6353f1cf4acdc5b07961c Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Sun, 22 Oct 2023 19:49:49 +0100 Subject: [PATCH 10/11] whitespace --- src/devices/honeywell_cm921.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index 84f19f7d0..3dc082e44 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -132,7 +132,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag // (specifically https://www.domoticaforum.eu/download/file.php?id=1396) // https://github.com/zxdavb/ramses_protocol - data = decode_device_ids(msg, data, 1); + data = decode_device_ids(msg, data, 1); data_t *r = data; From 1fc88e46794ed563717d7c56159d3bbba6cf4f6d Mon Sep 17 00:00:00 2001 From: Ian Pratt Date: Sun, 22 Oct 2023 21:07:51 +0100 Subject: [PATCH 11/11] whitespace --- src/devices/honeywell_cm921.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/devices/honeywell_cm921.c b/src/devices/honeywell_cm921.c index 3dc082e44..f00c8c152 100644 --- a/src/devices/honeywell_cm921.c +++ b/src/devices/honeywell_cm921.c @@ -41,7 +41,7 @@ typedef struct { uint8_t header; uint8_t num_device_ids; uint8_t device_id[4][3]; - uint8_t seq; + uint8_t seq; uint16_t command; uint8_t payload_length; uint8_t payload[256]; @@ -139,7 +139,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag if (msg->csum == 0x01) { // some devices send messages with the final byte set such that the byte sum is 0x01 rather than the usual 0x00 // These messages always have a header of 1c|7c|8c|9c, two addresses, a 2 byte cmd, payload size, payload, csum byte - // not sure whice devices send them, but one of them uses the 10e0 message to identify as a "Jasper EIM" + // not sure whice devices send them, but one of them uses the 10e0 message to identify as a "Jasper EIM" switch (msg->command) { case 0x0008: // Relay Demand message with a 13 byte payload. eg. 0017872b4ba4402a1bc756f7ec case 0x0502: // mystery long message that come in pairs @@ -199,8 +199,8 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag case 0x0008: { // Relay Heat Demand // The version of this message with csum=0 always seems to be 2 byte - - UNKNOWN_IF(msg->payload_length != 2); + + UNKNOWN_IF(msg->payload_length != 2); data = data_append(data, "domain_id", "", DATA_INT, msg->payload[0], NULL); data = data_append(data, "demand", "", DATA_DOUBLE, msg->payload[1] * (1 / 200.0F) /* 0xC8 */, NULL); break; @@ -284,14 +284,14 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag case 0x1f09: { // System Sync message. 3 byte message. domain_id then 2 byte countdown in tenths of a second // example "Packet" : "18045ef5045ef51f0903ff077693", "Header" : "18", "Seq" : "00", "Command" : "1f09", "Payload" : "ff0776", - // not sure its useful to log this to JSON + // not sure its useful to log this to JSON UNKNOWN_IF(msg->payload_length != 3); break; } case 0x0002: { // External Sensor. Sent by a thermostat in resposne to a change in the aux wired input // "Packet" : "1a0da2520da2520300020403010105d1", "Header" : "1a", "Seq" : "03", "Command" : "0002", "Payload" : "03010105" - // byte[0] is always 3 (remaining length?) + // byte[0] is always 3 (remaining length?) // byte[1] counts between 01 and 02 (seq always remians 0) // byte[2] of payload indicates whether aux input is logic 1 or 0 // byte[3] is always 5 @@ -311,8 +311,8 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag UNKNOWN_IF(msg->payload_length != 3); break; } - case 0x000a: { - // Zone Config. Sent by CTL to UFH. Used for failsafe + case 0x000a: { + // Zone Config. Sent by CTL to UFH. Used for failsafe UNKNOWN_IF(msg->payload_length % 6 != 0); for (size_t i=0; i < msg->payload_length; i+=6) { char name[256]; @@ -329,7 +329,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag } break; } - case 0x1060: { + case 0x1060: { // Battery Status. I only observe 'ffff01', likely because all sensors are mains powered. // payload[2] 1=OK 0-low UNKNOWN_IF(msg->payload_length != 3); @@ -337,7 +337,7 @@ static data_t *honeywell_cm921_interpret_message(r_device *decoder, const messag break; } case 0x2e04: { - // Controller Mode. zxdavb lists the payload below but this in no way matches the 16 byte messages I see + // Controller Mode. zxdavb lists the payload below but this in no way matches the 16 byte messages I see // payload[0] Program mode (0=Auto, 1=Heating Off, 2=Eco, 3=Away, 4=Day off, 7=Custom) // payload[1] hours; payload[2] minutes; payload[3] day; payload[4] month; payload[5] year // payload[6] Program type (0=Permanent, 1=Timed)