From a4b34f0019868f2cc67f89e947ed7a1e7be0dac5 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sun, 22 Dec 2024 22:50:35 -0700 Subject: [PATCH 01/14] Added support for bm5 v2.0 12V automotive battery monitor --- include/rtl_433_devices.h | 1 + src/CMakeLists.txt | 1 + src/devices/bm5.c | 119 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 src/devices/bm5.c diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h index 22db39b1e..6b9ec8bea 100644 --- a/include/rtl_433_devices.h +++ b/include/rtl_433_devices.h @@ -282,6 +282,7 @@ DECL(gridstream192) \ DECL(gridstream384) \ DECL(revolt_zx7717) \ + DECL(bm5) \ /* Add new decoders here. */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04e92df91..8b99c4f91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,6 +65,7 @@ add_library(r_433 STATIC devices/baldr_rain.c devices/blueline.c devices/blyss.c + devices/bm5.c devices/brennenstuhl_rcs_2044.c devices/bresser_3ch.c devices/bresser_5in1.c diff --git a/src/devices/bm5.c b/src/devices/bm5.c new file mode 100644 index 000000000..50ea5280b --- /dev/null +++ b/src/devices/bm5.c @@ -0,0 +1,119 @@ +/** @file + BM5 v2.0 12V Automotive Wireless Battery Monitor. + + Copyright (C) 2024 Cameron Murphy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +*/ + +/** +BM5 v2.0 12V Automotive Battery Monitor. Sold as "ANCEL BM200" on Amazon, and "QUICKLYNKS BM5-D" on AliExpress + +The sensor transmits a single message, with all relevant data about every 10 seconds at 433.92 MHz, + +The transmission is inverted from the normal OOK_PULSE_PWM decoder, with a "0" represented as a short pulse of 225us, and a 675us gap, +and a "1" represented as a long 675us pulse, and a 225us gap. The implementaion below initially inverters the buffer to correct for this. + +Each message consists of a preamble (long pulse, plus eight 50% symbol length pulses) sent at double the normal data rate, then a one byte pause (at regular data rate), +then ten bytes of payload, and a one byte CRC. The preamble is decoded as (0x7F 0x80) by rtl_433 (in the native, non-inverted state) due to the initial pulse. + +Payload: + +- I = 3 byte ID +- S = 7 bits for battery State of Health (SOH) - 0 to 100 percent +- C = 1 bit flag for charging system error (!CHARGING on display --probably triggered if running voltage below ~13v) +- s = 7 bits for battery State of Charge (SOC) 0 to 100 percent +- c = 1 bit flag for cranking system error. (!CRANKING indicator on display - triggered if starting voltage drops for too long -- excessive cranking) +- T = 8 byte (signed) for sensor temperature (degrees C, converted if necessary in display) +- V = 16 bits, little endian for current battery voltage (Voltage is displayed as a float with 2 significant digits. The 16 bit int represents this + voltage, multiplied by 1600.) +- v = 16 bits, little endian for previous low voltage during last start. (Is probably used for the algorithm to determine battery health. This value + will be closer to resting voltage for healthy batteries) Same 1600 muliplier as above. +- R = 1 byte Checksum + + msg: IIIIIIIIIIIIIIIIIIIIIIIISSSSSSSCssssssscTTTTTTTTVVVVVVVVVVVVVVVVvvvvvvvvvvvvvvvvRRRRRRRR + ID:24h SOH:7d CRANKING:1b SOC:7d CHARGING:1b TEMP:8s V_CUR:16d V_START:16d CRC:8h + +*/ + +#include "decoder.h" + +static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) +{ + data_t *data; + uint8_t b[11]; + + bitbuffer_invert(bitbuffer); // This device sends data inverted relative to the + // OOK_PWM decoder output. + + if (bitbuffer->num_rows != 1) // Only one message per transmission + return DECODE_ABORT_EARLY; + + //check correct data length + if (bitbuffer->bits_per_row[0] != 88) // 10 bytes dayat + 1 byte checksum) + return DECODE_ABORT_LENGTH; + + bitbuffer_extract_bytes(bitbuffer, 0, 0, b, sizeof(b)*8); + + //check for valid checksum + if (b[10] != add_bytes(&b[0], 10)){ + return DECODE_FAIL_MIC; // failed checksum - invalid message + } + + int id = b[0] << 16 | b[1] << 8 | b[2]; + int soh = b[3] >> 1; // State of Health encoded in 1st 7 bits + int cranking = b[3] & 0x01; // Cranking flag in bit 8 of byte 4 + int soc = b[4] >> 1; // State pf Charge encoded in 1st 7 bits + int charging = b[4] & 0x01; // Charging flag in bit 8 of byte 5 + int temp = b[5]; // Temperature in C, signed char in byte 6 + int volt1 = b[7] << 8 | b[6]; // Current voltage + int volt2 = b[9] << 8 | b[8]; // Previous starting voltage + + float cur_volt = volt1 / 1600.0; // Convert transmitted values to floats + float start_volt = volt2 / 1600.0; + + /* clang-format off */ + data = data_make( + "model", "", DATA_STRING, "BM5 v2.0", + "id", "Device_ID", DATA_FORMAT, "%X", DATA_INT, id, + "soh", "State of Health", DATA_FORMAT, "%d %%", DATA_INT, soh, + "cranking", "Cranking System Error", DATA_INT, cranking, + "soc", "State of Charge", DATA_FORMAT, "%d %%", DATA_INT, soc, + "charging", "Charging System Error", DATA_INT, charging, + "temperature_C", "Temperature", DATA_FORMAT, "%d C", DATA_INT, temp, + "cur_volt", "Current Battery Voltage",DATA_DOUBLE, cur_volt, + "start_volt", "Starting Voltage", DATA_DOUBLE, start_volt, + "mic", "Integrity", DATA_STRING, "CHECKSUM", + NULL); + /* clang-format on */ + + decoder_output_data(decoder, data); + return 1; +} + +static char const *const output_fields[] = { + "model", + "id", + "soh", + "cranking", + "soc", + "charging", + "temperature_C", + "cur_volt", + "start_volt", + "mic", + NULL, +}; + +r_device const bm5 = { + .name = "BM5 v2.0 12V Battery Monitor", + .modulation = OOK_PULSE_PWM, + .short_width = 225, + .long_width = 675, + .reset_limit = 2000, + .decode_fn = &bm5_decode, + .fields = output_fields, +}; From c3208b3c19c256918643b5454c9b113f238c7c25 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Mon, 23 Dec 2024 00:48:38 -0700 Subject: [PATCH 02/14] Fixed Checksum calculation (Needed cast to unsigned char) --- src/devices/bm5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 50ea5280b..58e5369dd 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -59,7 +59,7 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) bitbuffer_extract_bytes(bitbuffer, 0, 0, b, sizeof(b)*8); //check for valid checksum - if (b[10] != add_bytes(&b[0], 10)){ + if ((unsigned char) add_bytes(&b[0], 10) != b[10]){ return DECODE_FAIL_MIC; // failed checksum - invalid message } From cc741605b062dc1c73bfc84021c5bcf5719bf4b0 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Thu, 26 Dec 2024 09:09:29 -0700 Subject: [PATCH 03/14] Fixed reset limit --- src/devices/bm5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 58e5369dd..e6c16cad6 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -113,7 +113,7 @@ r_device const bm5 = { .modulation = OOK_PULSE_PWM, .short_width = 225, .long_width = 675, - .reset_limit = 2000, + .reset_limit = 6000, .decode_fn = &bm5_decode, .fields = output_fields, }; From 7271ef9cb9096e63d9a8afee3f40d138a02edbb1 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 08:56:33 -0700 Subject: [PATCH 04/14] Added formatting to truncate voltage values to 2 decimal places --- src/devices/bm5.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index e6c16cad6..6b4b25c08 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -84,8 +84,8 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) "soc", "State of Charge", DATA_FORMAT, "%d %%", DATA_INT, soc, "charging", "Charging System Error", DATA_INT, charging, "temperature_C", "Temperature", DATA_FORMAT, "%d C", DATA_INT, temp, - "cur_volt", "Current Battery Voltage",DATA_DOUBLE, cur_volt, - "start_volt", "Starting Voltage", DATA_DOUBLE, start_volt, + "cur_volt", "Current Battery Voltage",DATA_FORMAT, "%.2f", DATA_DOUBLE, cur_volt, + "start_volt", "Starting Voltage", DATA_FORMAT, "$.2f", DATA_DOUBLE, start_volt, "mic", "Integrity", DATA_STRING, "CHECKSUM", NULL); /* clang-format on */ From ea6e9e0134d6cdca4da257d49c29e789dceeb252 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 09:03:24 -0700 Subject: [PATCH 05/14] Fixed some typos in readme.md --- src/devices/bm5.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 6b4b25c08..daa0b3efb 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -15,10 +15,13 @@ BM5 v2.0 12V Automotive Battery Monitor. Sold as "ANCEL BM200" on Amazon, and " The sensor transmits a single message, with all relevant data about every 10 seconds at 433.92 MHz, The transmission is inverted from the normal OOK_PULSE_PWM decoder, with a "0" represented as a short pulse of 225us, and a 675us gap, -and a "1" represented as a long 675us pulse, and a 225us gap. The implementaion below initially inverters the buffer to correct for this. +and a "1" represented as a long 675us pulse, and a 225us gap. The implementation below initially inverters the buffer to correct for this. Each message consists of a preamble (long pulse, plus eight 50% symbol length pulses) sent at double the normal data rate, then a one byte pause (at regular data rate), -then ten bytes of payload, and a one byte CRC. The preamble is decoded as (0x7F 0x80) by rtl_433 (in the native, non-inverted state) due to the initial pulse. +then ten bytes of payload, plus a one byte Checksum. The preamble is decoded as (0x7F 0x80) by rtl_433 (in the native, non-inverted state) due to the initial pulse. + +Flex decoder: `rtl_433 -R 0 -X 'n=bm5v2,m=OOK_PWM,s=227,l=675,r=6000,invert'` + Payload: @@ -31,11 +34,11 @@ then ten bytes of payload, and a one byte CRC. The preamble is decoded as (0x7F - V = 16 bits, little endian for current battery voltage (Voltage is displayed as a float with 2 significant digits. The 16 bit int represents this voltage, multiplied by 1600.) - v = 16 bits, little endian for previous low voltage during last start. (Is probably used for the algorithm to determine battery health. This value - will be closer to resting voltage for healthy batteries) Same 1600 muliplier as above. + will be closer to resting voltage for healthy batteries) Same 1600 multiplier as above. - R = 1 byte Checksum msg: IIIIIIIIIIIIIIIIIIIIIIIISSSSSSSCssssssscTTTTTTTTVVVVVVVVVVVVVVVVvvvvvvvvvvvvvvvvRRRRRRRR - ID:24h SOH:7d CRANKING:1b SOC:7d CHARGING:1b TEMP:8s V_CUR:16d V_START:16d CRC:8h + ID:24h SOH:7d CHARGING:1b SOC:7d CRANKING:1b TEMP:8s V_CUR:16d V_START:16d CHECKSUM:8h */ @@ -85,7 +88,7 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) "charging", "Charging System Error", DATA_INT, charging, "temperature_C", "Temperature", DATA_FORMAT, "%d C", DATA_INT, temp, "cur_volt", "Current Battery Voltage",DATA_FORMAT, "%.2f", DATA_DOUBLE, cur_volt, - "start_volt", "Starting Voltage", DATA_FORMAT, "$.2f", DATA_DOUBLE, start_volt, + "start_volt", "Starting Voltage", DATA_FORMAT, "%.2f", DATA_DOUBLE, start_volt, "mic", "Integrity", DATA_STRING, "CHECKSUM", NULL); /* clang-format on */ From 85f26992d762e6364c2faef5c23db243520d352e Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 09:13:59 -0700 Subject: [PATCH 06/14] Added notes concerning rounding vs truncating of voltages in display --- src/devices/bm5.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index daa0b3efb..f0b395bb1 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -32,7 +32,8 @@ Flex decoder: `rtl_433 -R 0 -X 'n=bm5v2,m=OOK_PWM,s=227,l=675,r=6000,invert'` - c = 1 bit flag for cranking system error. (!CRANKING indicator on display - triggered if starting voltage drops for too long -- excessive cranking) - T = 8 byte (signed) for sensor temperature (degrees C, converted if necessary in display) - V = 16 bits, little endian for current battery voltage (Voltage is displayed as a float with 2 significant digits. The 16 bit int represents this - voltage, multiplied by 1600.) + voltage, multiplied by 1600. -- note: The display truncates the voltages to 2 decimal points. I've chosen to round instead of truncate, as this + seems a better representation of the true value.) - v = 16 bits, little endian for previous low voltage during last start. (Is probably used for the algorithm to determine battery health. This value will be closer to resting voltage for healthy batteries) Same 1600 multiplier as above. - R = 1 byte Checksum @@ -75,7 +76,7 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) int volt1 = b[7] << 8 | b[6]; // Current voltage int volt2 = b[9] << 8 | b[8]; // Previous starting voltage - float cur_volt = volt1 / 1600.0; // Convert transmitted values to floats + float cur_volt = volt1 / 1600.0; // Convert transmitted values to floats. Rounded to 2 decimal places in "data_make" below. float start_volt = volt2 / 1600.0; /* clang-format off */ From f1b87febd485d4d20073a28c53524b4a2ca8400b Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 12:31:23 -0700 Subject: [PATCH 07/14] Fixed capitalization and naming conventions to match project style. --- src/devices/bm5.c | 127 ++++++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 54 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index f0b395bb1..95dc4ff5f 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -1,5 +1,5 @@ /** @file - BM5 v2.0 12V Automotive Wireless Battery Monitor. + bm5-v2 12V Automotive Wireless Battery Monitor. Copyright (C) 2024 Cameron Murphy @@ -10,36 +10,52 @@ */ /** -BM5 v2.0 12V Automotive Battery Monitor. Sold as "ANCEL BM200" on Amazon, and "QUICKLYNKS BM5-D" on AliExpress +bm5-v2 12V Automotive Battery Monitor. -The sensor transmits a single message, with all relevant data about every 10 seconds at 433.92 MHz, +Sold as "ANCEL BM200" on Amazon, and "QUICKLYNKS BM5-D" on AliExpress -The transmission is inverted from the normal OOK_PULSE_PWM decoder, with a "0" represented as a short pulse of 225us, and a 675us gap, -and a "1" represented as a long 675us pulse, and a 225us gap. The implementation below initially inverters the buffer to correct for this. +The sensor transmits a single message, with all relevant data about every 1-2 +seconds at 433.92 MHz, -Each message consists of a preamble (long pulse, plus eight 50% symbol length pulses) sent at double the normal data rate, then a one byte pause (at regular data rate), -then ten bytes of payload, plus a one byte Checksum. The preamble is decoded as (0x7F 0x80) by rtl_433 (in the native, non-inverted state) due to the initial pulse. +The transmission is inverted from the normal OOK_PULSE_PWM decoder, with a "0" +represented as a short pulse of 225us, and a 675us gap, and a "1" represented as +a long 675us pulse, and a 225us gap. The implementation below initially +inverters the buffer to correct for this. -Flex decoder: `rtl_433 -R 0 -X 'n=bm5v2,m=OOK_PWM,s=227,l=675,r=6000,invert'` +Each message consists of a preamble (long pulse, plus eight 50% symbol length +pulses) sent at double the normal data rate, then a one byte pause (at regular +data rate), then ten bytes of payload, plus a one byte Checksum. The preamble +is decoded as (0x7F 0x80) by rtl_433 (in the native, non-inverted state) due to +the initial pulse. + +Flex decoder: `rtl_433 -R 0 -X 'n=bm5-v2,m=OOK_PWM,s=227,l=675,r=6000,invert'` Payload: - I = 3 byte ID - S = 7 bits for battery State of Health (SOH) - 0 to 100 percent -- C = 1 bit flag for charging system error (!CHARGING on display --probably triggered if running voltage below ~13v) +- C = 1 bit flag for charging system error (!CHARGING on display --probably +triggered if running voltage below ~13v) - s = 7 bits for battery State of Charge (SOC) 0 to 100 percent -- c = 1 bit flag for cranking system error. (!CRANKING indicator on display - triggered if starting voltage drops for too long -- excessive cranking) -- T = 8 byte (signed) for sensor temperature (degrees C, converted if necessary in display) -- V = 16 bits, little endian for current battery voltage (Voltage is displayed as a float with 2 significant digits. The 16 bit int represents this - voltage, multiplied by 1600. -- note: The display truncates the voltages to 2 decimal points. I've chosen to round instead of truncate, as this - seems a better representation of the true value.) -- v = 16 bits, little endian for previous low voltage during last start. (Is probably used for the algorithm to determine battery health. This value - will be closer to resting voltage for healthy batteries) Same 1600 multiplier as above. +- c = 1 bit flag for cranking system error. (!CRANKING indicator on display - +triggered if starting voltage drops for too long -- excessive cranking) +- T = 8 byte (signed) for sensor temperature (degrees C, converted if necessary +in display) +- V = 16 bits, little endian for current battery voltage (Voltage is displayed +as a float with 2 significant digits. The 16 bit int represents this voltage, +multiplied by 1600. -- note: The display truncates the voltages to 2 decimal +points. I've chosen to round instead of truncate, as this seems a better +representation of the true value.) +- v = 16 bits, little endian for previous low voltage during last start. (Is +probably used for the algorithm to determine battery health. This value will be +closer to resting voltage for healthy batteries) Same 1600 multiplier as above. - R = 1 byte Checksum - msg: IIIIIIIIIIIIIIIIIIIIIIIISSSSSSSCssssssscTTTTTTTTVVVVVVVVVVVVVVVVvvvvvvvvvvvvvvvvRRRRRRRR - ID:24h SOH:7d CHARGING:1b SOC:7d CRANKING:1b TEMP:8s V_CUR:16d V_START:16d CHECKSUM:8h + msg: +IIIIIIIIIIIIIIIIIIIIIIIISSSSSSSCssssssscTTTTTTTTVVVVVVVVVVVVVVVVvvvvvvvvvvvvvvvvRRRRRRRR + ID:24h SOH:7d CHARGING:1b SOC:7d CRANKING:1b TEMP:8s V_CUR:16d V_START:16d +CHECKSUM:8h */ @@ -50,46 +66,49 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) data_t *data; uint8_t b[11]; - bitbuffer_invert(bitbuffer); // This device sends data inverted relative to the - // OOK_PWM decoder output. + bitbuffer_invert(bitbuffer); // This device sends data inverted relative to + // the OOK_PWM decoder output. - if (bitbuffer->num_rows != 1) // Only one message per transmission + if (bitbuffer->num_rows != 1) { // Only one message per transmission return DECODE_ABORT_EARLY; + } - //check correct data length - if (bitbuffer->bits_per_row[0] != 88) // 10 bytes dayat + 1 byte checksum) + // check correct data length + if (bitbuffer->bits_per_row[0] != 88) { // 10 bytes data + 1 byte checksum) return DECODE_ABORT_LENGTH; + } - bitbuffer_extract_bytes(bitbuffer, 0, 0, b, sizeof(b)*8); + bitbuffer_extract_bytes(bitbuffer, 0, 0, b, sizeof(b) * 8); - //check for valid checksum - if ((unsigned char) add_bytes(&b[0], 10) != b[10]){ - return DECODE_FAIL_MIC; // failed checksum - invalid message + // check for valid checksum + return DECODE_FAIL_MIC; // failed checksum - invalid message + if ((unsigned char)add_bytes(&b[0], 10) != b[10]) { } - int id = b[0] << 16 | b[1] << 8 | b[2]; - int soh = b[3] >> 1; // State of Health encoded in 1st 7 bits - int cranking = b[3] & 0x01; // Cranking flag in bit 8 of byte 4 - int soc = b[4] >> 1; // State pf Charge encoded in 1st 7 bits - int charging = b[4] & 0x01; // Charging flag in bit 8 of byte 5 - int temp = b[5]; // Temperature in C, signed char in byte 6 - int volt1 = b[7] << 8 | b[6]; // Current voltage - int volt2 = b[9] << 8 | b[8]; // Previous starting voltage + int id = b[0] << 16 | b[1] << 8 | b[2]; + int soh = b[3] >> 1; // State of Health encoded in 1st 7 bits + int cranking_error = b[3] & 0x01; // Cranking flag in bit 8 of byte 4 + int soc = b[4] >> 1; // State pf Charge encoded in 1st 7 bits + int charging_error = b[4] & 0x01; // Charging flag in bit 8 of byte 5 + int temp = b[5]; // Temperature in C, signed char in byte 6 + int volt1 = b[7] << 8 | b[6]; // Current voltage + int volt2 = b[9] << 8 | b[8]; // Previous starting voltage - float cur_volt = volt1 / 1600.0; // Convert transmitted values to floats. Rounded to 2 decimal places in "data_make" below. - float start_volt = volt2 / 1600.0; + float battery_volt = volt1 * 0.000625f; // Convert transmitted values to floats. Rounded to 2 + // decimal places in "data_make" below. + float starting_volt = volt2 * 0.000625f; /* clang-format off */ data = data_make( - "model", "", DATA_STRING, "BM5 v2.0", - "id", "Device_ID", DATA_FORMAT, "%X", DATA_INT, id, - "soh", "State of Health", DATA_FORMAT, "%d %%", DATA_INT, soh, - "cranking", "Cranking System Error", DATA_INT, cranking, - "soc", "State of Charge", DATA_FORMAT, "%d %%", DATA_INT, soc, - "charging", "Charging System Error", DATA_INT, charging, - "temperature_C", "Temperature", DATA_FORMAT, "%d C", DATA_INT, temp, - "cur_volt", "Current Battery Voltage",DATA_FORMAT, "%.2f", DATA_DOUBLE, cur_volt, - "start_volt", "Starting Voltage", DATA_FORMAT, "%.2f", DATA_DOUBLE, start_volt, + "model", "", DATA_STRING, "BM5-v2", + "device_id", "Device_ID", DATA_FORMAT, "%X", DATA_INT, id, + "health_pct", "State of Health", DATA_FORMAT, "%d %%", DATA_INT, soh, + "cranking_error", "Cranking System Error", DATA_INT, cranking_error, + "charge_pct", "State of Charge", DATA_FORMAT, "%d %%", DATA_INT, soc, + "charging_error", "Charging System Error", DATA_INT, charging_error, + "temperature_C", "Temperature", DATA_FORMAT, "%d C", DATA_INT, temp, + "battery_V", "Current Battery Voltage",DATA_FORMAT, "%.2f", DATA_DOUBLE, battery_volt, + "starting_V", "Starting Voltage", DATA_FORMAT, "%.2f", DATA_DOUBLE, starting_volt, "mic", "Integrity", DATA_STRING, "CHECKSUM", NULL); /* clang-format on */ @@ -100,20 +119,20 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) static char const *const output_fields[] = { "model", - "id", - "soh", - "cranking", - "soc", - "charging", + "device_id", + "health_pct", + "cranking_error", + "charge_pct", + "charging_error", "temperature_C", - "cur_volt", - "start_volt", + "battery_V", + "starting_V", "mic", NULL, }; r_device const bm5 = { - .name = "BM5 v2.0 12V Battery Monitor", + .name = "bm5-v2 12V Battery Monitor", .modulation = OOK_PULSE_PWM, .short_width = 225, .long_width = 675, From 2309a9690bf86045a9bf1b9ebc8c251700ec5bb7 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 14:18:47 -0700 Subject: [PATCH 08/14] Fixed brace location and units on voltage output. --- src/devices/bm5.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 95dc4ff5f..83c066b9f 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -81,8 +81,8 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) bitbuffer_extract_bytes(bitbuffer, 0, 0, b, sizeof(b) * 8); // check for valid checksum - return DECODE_FAIL_MIC; // failed checksum - invalid message if ((unsigned char)add_bytes(&b[0], 10) != b[10]) { + return DECODE_FAIL_MIC; // failed checksum - invalid message } int id = b[0] << 16 | b[1] << 8 | b[2]; @@ -107,8 +107,8 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) "charge_pct", "State of Charge", DATA_FORMAT, "%d %%", DATA_INT, soc, "charging_error", "Charging System Error", DATA_INT, charging_error, "temperature_C", "Temperature", DATA_FORMAT, "%d C", DATA_INT, temp, - "battery_V", "Current Battery Voltage",DATA_FORMAT, "%.2f", DATA_DOUBLE, battery_volt, - "starting_V", "Starting Voltage", DATA_FORMAT, "%.2f", DATA_DOUBLE, starting_volt, + "battery_V", "Current Battery Voltage",DATA_FORMAT, "%.2f V", DATA_DOUBLE, battery_volt, + "starting_V", "Starting Voltage", DATA_FORMAT, "%.2f V", DATA_DOUBLE, starting_volt, "mic", "Integrity", DATA_STRING, "CHECKSUM", NULL); /* clang-format on */ From 63f0e8516eae05c71b012fd150aeb304a5a7edee Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 14:51:33 -0700 Subject: [PATCH 09/14] Added parens around bit shifts, and changed temp to signed int8 data type --- src/devices/bm5.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 83c066b9f..2097f7fe2 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -85,14 +85,14 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) return DECODE_FAIL_MIC; // failed checksum - invalid message } - int id = b[0] << 16 | b[1] << 8 | b[2]; + int id = (b[0] << 16) | (b[1] << 8) | b[2]; int soh = b[3] >> 1; // State of Health encoded in 1st 7 bits int cranking_error = b[3] & 0x01; // Cranking flag in bit 8 of byte 4 int soc = b[4] >> 1; // State pf Charge encoded in 1st 7 bits int charging_error = b[4] & 0x01; // Charging flag in bit 8 of byte 5 - int temp = b[5]; // Temperature in C, signed char in byte 6 - int volt1 = b[7] << 8 | b[6]; // Current voltage - int volt2 = b[9] << 8 | b[8]; // Previous starting voltage + int temp = (int8_t) b[5]; // Temperature in C, signed char in byte 6 + int volt1 = (b[7] << 8) | b[6]; // Current voltage + int volt2 = (b[9] << 8) | b[8]; // Previous starting voltage float battery_volt = volt1 * 0.000625f; // Convert transmitted values to floats. Rounded to 2 // decimal places in "data_make" below. From d06fb700893899164a24b8f728cb9ab341e1f9a6 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 15:01:56 -0700 Subject: [PATCH 10/14] Cleaned up formatting --- src/devices/bm5.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 2097f7fe2..77390acb4 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -86,11 +86,11 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) } int id = (b[0] << 16) | (b[1] << 8) | b[2]; - int soh = b[3] >> 1; // State of Health encoded in 1st 7 bits - int cranking_error = b[3] & 0x01; // Cranking flag in bit 8 of byte 4 - int soc = b[4] >> 1; // State pf Charge encoded in 1st 7 bits - int charging_error = b[4] & 0x01; // Charging flag in bit 8 of byte 5 - int temp = (int8_t) b[5]; // Temperature in C, signed char in byte 6 + int soh = b[3] >> 1; // State of Health encoded in 1st 7 bits + int cranking_error = b[3] & 0x01; // Cranking flag in bit 8 of byte 4 + int soc = b[4] >> 1; // State pf Charge encoded in 1st 7 bits + int charging_error = b[4] & 0x01; // Charging flag in bit 8 of byte 5 + int temp = (int8_t)b[5]; // Temperature in C, signed char in byte 6 int volt1 = (b[7] << 8) | b[6]; // Current voltage int volt2 = (b[9] << 8) | b[8]; // Previous starting voltage From f9480bc2896a41e07e5b7546b02523d27f81e7ea Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 15:03:09 -0700 Subject: [PATCH 11/14] "device_id" -> "id" --- src/devices/bm5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 77390acb4..c06037a10 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -101,7 +101,7 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) /* clang-format off */ data = data_make( "model", "", DATA_STRING, "BM5-v2", - "device_id", "Device_ID", DATA_FORMAT, "%X", DATA_INT, id, + "id", "Device_ID", DATA_FORMAT, "%X", DATA_INT, id, "health_pct", "State of Health", DATA_FORMAT, "%d %%", DATA_INT, soh, "cranking_error", "Cranking System Error", DATA_INT, cranking_error, "charge_pct", "State of Charge", DATA_FORMAT, "%d %%", DATA_INT, soc, From 3b7a3c531adadfdc66dce34ccb8efb4f6a582fb8 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 20:53:35 -0700 Subject: [PATCH 12/14] Fixed transposed error flags, and accounted for sign-magnitude temperature value --- src/devices/bm5.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index c06037a10..0c3d62553 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -40,8 +40,9 @@ triggered if running voltage below ~13v) - s = 7 bits for battery State of Charge (SOC) 0 to 100 percent - c = 1 bit flag for cranking system error. (!CRANKING indicator on display - triggered if starting voltage drops for too long -- excessive cranking) -- T = 8 byte (signed) for sensor temperature (degrees C, converted if necessary +- T = 7 bits for sensor temperature magnitude (degrees C, converted if necessary in display) +- t = 1 bit for temperature sign (0 = positive, 1 = negative) - V = 16 bits, little endian for current battery voltage (Voltage is displayed as a float with 2 significant digits. The 16 bit int represents this voltage, multiplied by 1600. -- note: The display truncates the voltages to 2 decimal @@ -53,7 +54,7 @@ closer to resting voltage for healthy batteries) Same 1600 multiplier as above. - R = 1 byte Checksum msg: -IIIIIIIIIIIIIIIIIIIIIIIISSSSSSSCssssssscTTTTTTTTVVVVVVVVVVVVVVVVvvvvvvvvvvvvvvvvRRRRRRRR +IIIIIIIIIIIIIIIIIIIIIIIISSSSSSSCssssssscTTTTTTTtVVVVVVVVVVVVVVVVvvvvvvvvvvvvvvvvRRRRRRRR ID:24h SOH:7d CHARGING:1b SOC:7d CRANKING:1b TEMP:8s V_CUR:16d V_START:16d CHECKSUM:8h @@ -87,12 +88,17 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) int id = (b[0] << 16) | (b[1] << 8) | b[2]; int soh = b[3] >> 1; // State of Health encoded in 1st 7 bits - int cranking_error = b[3] & 0x01; // Cranking flag in bit 8 of byte 4 + int charging_error = b[3] & 0x01; // Charging error flag in bit 8 of byte 4 int soc = b[4] >> 1; // State pf Charge encoded in 1st 7 bits - int charging_error = b[4] & 0x01; // Charging flag in bit 8 of byte 5 - int temp = (int8_t)b[5]; // Temperature in C, signed char in byte 6 + int cranking_error = b[4] & 0x01; // Cranking error flag in bit 8 of byte 5 + int temp = b[5] >> 1; // Temperature magnitude in degrees C in first 7 bits of byte 6 + int temp_sign = b[5] & 0x01; // Temperature sign in bit 8 of byte 6 int volt1 = (b[7] << 8) | b[6]; // Current voltage int volt2 = (b[9] << 8) | b[8]; // Previous starting voltage + // + if (temp_sign == 1) { + temp = -temp; // Invert temp value + } float battery_volt = volt1 * 0.000625f; // Convert transmitted values to floats. Rounded to 2 // decimal places in "data_make" below. @@ -107,8 +113,8 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) "charge_pct", "State of Charge", DATA_FORMAT, "%d %%", DATA_INT, soc, "charging_error", "Charging System Error", DATA_INT, charging_error, "temperature_C", "Temperature", DATA_FORMAT, "%d C", DATA_INT, temp, - "battery_V", "Current Battery Voltage",DATA_FORMAT, "%.2f V", DATA_DOUBLE, battery_volt, - "starting_V", "Starting Voltage", DATA_FORMAT, "%.2f V", DATA_DOUBLE, starting_volt, + "battery_V", "Current Battery Voltage",DATA_FORMAT, "%.2f V", DATA_DOUBLE, battery_volt, + "starting_V", "Starting Voltage", DATA_FORMAT, "%.2f V", DATA_DOUBLE, starting_volt, "mic", "Integrity", DATA_STRING, "CHECKSUM", NULL); /* clang-format on */ From e29174799c37f9c38d26d01482fd111ad4dd5f80 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sat, 28 Dec 2024 21:44:26 -0700 Subject: [PATCH 13/14] Cast temperature as float to enable auto si->customary conversion --- src/devices/bm5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 0c3d62553..257328438 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -112,7 +112,7 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) "cranking_error", "Cranking System Error", DATA_INT, cranking_error, "charge_pct", "State of Charge", DATA_FORMAT, "%d %%", DATA_INT, soc, "charging_error", "Charging System Error", DATA_INT, charging_error, - "temperature_C", "Temperature", DATA_FORMAT, "%d C", DATA_INT, temp, + "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, (float) temp, "battery_V", "Current Battery Voltage",DATA_FORMAT, "%.2f V", DATA_DOUBLE, battery_volt, "starting_V", "Starting Voltage", DATA_FORMAT, "%.2f V", DATA_DOUBLE, starting_volt, "mic", "Integrity", DATA_STRING, "CHECKSUM", From 9c7a19f3b38cd71501ee3499cb02d83c5aaa5ad4 Mon Sep 17 00:00:00 2001 From: Cameron Murphy Date: Sun, 12 Jan 2025 22:21:41 -0700 Subject: [PATCH 14/14] Fixed missing "id" field --- src/devices/bm5.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/devices/bm5.c b/src/devices/bm5.c index 257328438..5cc8917fd 100644 --- a/src/devices/bm5.c +++ b/src/devices/bm5.c @@ -1,7 +1,7 @@ /** @file bm5-v2 12V Automotive Wireless Battery Monitor. - Copyright (C) 2024 Cameron Murphy + Copyright (C) 2025 Cameron Murphy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -125,7 +125,7 @@ static int bm5_decode(r_device *decoder, bitbuffer_t *bitbuffer) static char const *const output_fields[] = { "model", - "device_id", + "id", "health_pct", "cranking_error", "charge_pct",