From dcea0814efc53def17404a4b199d47bbbb69f100 Mon Sep 17 00:00:00 2001 From: "Christian W. Zuckschwerdt" Date: Fri, 10 Dec 2021 21:12:38 +0100 Subject: [PATCH] minor: Clean up LaCrosse-TX34/35-IT --- README.md | 1 + conf/rtl_433.example.conf | 1 + src/devices/lacrosse_tx34.c | 93 +++++++++--------------- src/devices/lacrosse_tx35.c | 133 ++++++++++++++++------------------- src/devices/m_bus.c | 1 + vs15/rtl_433.vcxproj | 1 + vs15/rtl_433.vcxproj.filters | 3 + 7 files changed, 102 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index 36747665..8397fdff 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md). [203] Porsche Boxster/Cayman TPMS [204] Jasco/GE Choice Alert Security Devices [205] Telldus weather station FT0385R sensors + [206] LaCrosse TX34-IT rain gauge * Disabled by default, use -R n or -G diff --git a/conf/rtl_433.example.conf b/conf/rtl_433.example.conf index e30e4769..7e52a9fd 100644 --- a/conf/rtl_433.example.conf +++ b/conf/rtl_433.example.conf @@ -426,6 +426,7 @@ stop_after_successful_events false protocol 203 # Porsche Boxster/Cayman TPMS protocol 204 # Jasco/GE Choice Alert Security Devices protocol 205 # Telldus weather station FT0385R sensors + protocol 206 # LaCrosse TX34-IT rain gauge ## Flex devices (command line option "-X") diff --git a/src/devices/lacrosse_tx34.c b/src/devices/lacrosse_tx34.c index d0a96523..314f767f 100644 --- a/src/devices/lacrosse_tx34.c +++ b/src/devices/lacrosse_tx34.c @@ -25,9 +25,9 @@ The LaCrosse "IT+" family share some specifications: Frame format: ------------- -| 1010 1010 | preamble (up to the first two bits may be lost) +| 1010 1010 | preamble (some bits may be lost) ------------- -| 0010 1101 | 0x2DD4: LaCrosse IT frame identifier +| 0010 1101 | 0x2dd4: sync word | 1101 0100 | ------------- | MMMM DDDD | MMMM: sensor model (5 for rain gauge, 9 for thermo/hydro...) @@ -35,7 +35,7 @@ Frame format: | GGGG GGGG | N: new battery (on for about 420 minutes after startup) | GGGG GGGG | W: weak battery (on when battery voltage < 2 volts) ------------- GGGGGGGGGGGGGGGG: bucket tipping counter -| CCCC CCCC | CCCCCCCC: CRC8 on previous 4 bytes +| CCCC CCCC | CCCCCCCC: CRC8 (poly 0x31 init 0x00) on previous 4 bytes ------------- This decoder decodes generic LaCrosse IT+ frames and filters TX34 ones. @@ -44,85 +44,61 @@ Could be merged with existing TX29 decoder... or not. #include "decoder.h" -#define LACROSSE_TX34_CRC_POLY 0x31 -#define LACROSSE_TX34_CRC_INIT 0x00 -#define LACROSSE_TX34_PREAMBLE_BITS 22 #define LACROSSE_TX34_ITMODEL 5 #define LACROSSE_TX34_PAYLOAD_BITS 40 #define LACROSSE_TX34_RAIN_FACTOR 0.222 static int lacrosse_tx34_callback(r_device *decoder, bitbuffer_t *bitbuffer) { - data_t *data; - int row; - uint8_t payload[5]; - uint8_t r_crc, c_crc; - uint8_t sensor_id, new_bat, weak_bat; - uint16_t rain_tick; - float rain_mm; - int events; + // 20 bits preamble (shifted left): 1010b 0x2DD4 + uint8_t const preamble[] = {0xa2, 0xdd, 0x40}; - // 22 bits preamble (shifted left): 101010b 0x2DD4 - static const uint8_t preamble[] = {0xA8, 0xB7, 0x50}; - - // process rows - events = 0; - for (row = 0; row <= bitbuffer->num_rows; ++row) { + // process all rows + int events = 0; + for (int row = 0; row < bitbuffer->num_rows; ++row) { // search for preamble - unsigned start_pos = bitbuffer_search(bitbuffer, row, 0, preamble, - LACROSSE_TX34_PREAMBLE_BITS); - if (start_pos == bitbuffer->bits_per_row[row]) + unsigned start_pos = bitbuffer_search(bitbuffer, row, 0, preamble, 20) + 20; + if (start_pos + LACROSSE_TX34_PAYLOAD_BITS > bitbuffer->bits_per_row[row]) continue; // preamble not found - unsigned payload_bits = bitbuffer->bits_per_row[row] - start_pos - - LACROSSE_TX34_PREAMBLE_BITS; - if (payload_bits < LACROSSE_TX34_PAYLOAD_BITS) - continue; // probably truncated frame - if (decoder->verbose) - fprintf(stderr, - "LaCrosse IT frame detected (%d bits payload)\n", - payload_bits); - + if (decoder->verbose > 1) + fprintf(stderr, "%s: LaCrosse IT frame detected\n", __func__); // get payload - bitbuffer_extract_bytes(bitbuffer, row, - start_pos + LACROSSE_TX34_PREAMBLE_BITS, - payload, LACROSSE_TX34_PAYLOAD_BITS); + uint8_t b[5]; + bitbuffer_extract_bytes(bitbuffer, row, start_pos, b, LACROSSE_TX34_PAYLOAD_BITS); // verify CRC - r_crc = payload[4]; - c_crc = crc8(&payload[0], 4, - LACROSSE_TX34_CRC_POLY, LACROSSE_TX34_CRC_INIT); + int r_crc = b[4]; + int c_crc = crc8(b, 4, 0x31, 0x00); if (r_crc != c_crc) { // bad CRC: reject IT frame if (decoder->verbose) - fprintf(stderr, - "LaCrosse IT frame bad CRC: calculated %02x, " - "received %02x\n", - c_crc, r_crc); + fprintf(stderr, "%s: LaCrosse IT frame bad CRC: calculated %02x, received %02x\n", __func__, c_crc, r_crc); continue; } // check model - if (((payload[0] & 0xF0) >> 4) != LACROSSE_TX34_ITMODEL) + if (((b[0] & 0xF0) >> 4) != LACROSSE_TX34_ITMODEL) continue; // not a rain gauge... // decode payload - sensor_id = ((payload[0] & 0x0F) << 2) + (payload[1] >> 6); - new_bat = (payload[1] & 0x20) >> 5; - weak_bat = (payload[1] && 0x10) >> 4; - rain_tick = (payload[2] << 8) + payload[3]; - rain_mm = rain_tick * LACROSSE_TX34_RAIN_FACTOR; + int sensor_id = ((b[0] & 0x0F) << 2) | (b[1] >> 6); + int new_batt = (b[1] & 0x20) >> 5; + int low_batt = (b[1] & 0x10) >> 4; + int rain_tick = (b[2] << 8) | b[3]; + float rain_mm = rain_tick * LACROSSE_TX34_RAIN_FACTOR; + /* clang-format off */ - data = data_make( - "model", "", DATA_STRING, "LaCrosse-TX34IT", - "id", "", DATA_INT, sensor_id, - "battery_ok", "Battery", DATA_INT, 1 - weak_bat, - "newbattery", "New battery", DATA_INT, new_bat, - "rain_mm", "Total rain", DATA_DOUBLE, rain_mm, - "rain_raw", "Raw rain", DATA_INT, rain_tick, - "mic", "Integrity", DATA_STRING, "CRC", - NULL - ); + data_t *data = data_make( + "model", "", DATA_STRING, "LaCrosse-TX34IT", + "id", "", DATA_INT, sensor_id, + "battery_ok", "Battery", DATA_INT, !low_batt, + "newbattery", "New battery", DATA_INT, new_batt, + "rain_mm", "Total rain", DATA_DOUBLE, rain_mm, + "rain_raw", "Raw rain", DATA_INT, rain_tick, + "mic", "Integrity", DATA_STRING, "CRC", + NULL); /* clang-format on */ + decoder_output_data(decoder, data); events++; } @@ -147,6 +123,5 @@ r_device lacrosse_tx34 = { .long_width = 58, .reset_limit = 4000, .decode_fn = &lacrosse_tx34_callback, - .disabled = 0, .fields = output_fields, }; diff --git a/src/devices/lacrosse_tx35.c b/src/devices/lacrosse_tx35.c index 5f95b3da..8382c7c5 100644 --- a/src/devices/lacrosse_tx35.c +++ b/src/devices/lacrosse_tx35.c @@ -1,5 +1,5 @@ /** @file - LaCrosse/StarMétéo/Conrad TX35 protocol. + LaCrosse/StarMeteo/Conrad TX35 protocol. 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 @@ -9,16 +9,16 @@ */ /** Generic decoder for LaCrosse "IT+" (instant transmission) protocol. -Param device29or35 contain "29" or "35" depending of the device. +Param device29or35 must be "29" or "35" depending of the device. -LaCrosse/StarMétéo/Conrad TX35DTH-IT, TFA Dostmann 30.3155 Temperature/Humidity Sensors. -LaCrosse/StarMétéo/Conrad TX29-IT, TFA Dostmann 30.3159.IT Temperature Sensors. -Tune to 868240000Hz +LaCrosse/StarMeteo/Conrad TX35DTH-IT, TFA Dostmann 30.3155 Temperature/Humidity Sensors. +LaCrosse/StarMeteo/Conrad TX29-IT, TFA Dostmann 30.3159.IT Temperature Sensors. +Found at 868240000Hz. LaCrosse TX25U Temperature/Temperature Probe at 915 MHz -Protocol -======== +## Protocol + Example data : https://github.com/merbanan/rtl_433_tests/tree/master/tests/lacrosse/06/gfile-tx29.cu8 a a 2 d d 4 9 2 8 4 4 8 6 a e c @@ -29,9 +29,9 @@ Example data : https://github.com/merbanan/rtl_433_tests/tree/master/tests/lacro ~~~~~~~~~ 1st byte preamble, sequence 10B repeated 4 times (see below) ~~~~~~~~~~~~~~~~~~~ bytes 2 and 3 - brand identifier, always 0x2dd4 + sync word of 0x2dd4 ~~~~ 1st nibble of bytes 4 - datalength (always 9) in nibble, including this field and crc + sensor model (always 9) ~~~~ ~~ 2nd nibble of bytes 4 and 1st and 2nd bits of byte 5 Random device id (6 bits) ~ 3rd bits of byte 5 @@ -46,10 +46,10 @@ Example data : https://github.com/merbanan/rtl_433_tests/tree/master/tests/lacro humidity, in%. If == 0x6a : no humidity sensor If == 0x7d : temperature is actually second probe temperature channel ~~~~ ~~~~ byte 8 - crc8 of bytes + crc8 (poly 0x31 init 0x00) of bytes + +## Developer's comments -Developer's comments -==================== I have noticed that depending of the device, the message received has different length. It seems some sensor send a long preamble (33 bits, 0 / 1 alternated), and some send only six bits as the preamble. I own 3 sensors TX29, and two of them send a long preamble. @@ -58,9 +58,9 @@ So this decoder synchronize on the following sequence: 1010 1000 1011 0111 0101 0010 01-- A 8 B 7 5 2 4 -- 0 - 5 : short preabmle [101010B] -- 6 - 14 : brand identifier [2DD4h] -- 15 - 19 : datalength [9] +- 0 - 5 : short preamble [101010B] +- 6 - 14 : sync word [2DD4h] +- 15 - 19 : sensor model [9] Short preamble example (sampling rate - 1Mhz): https://github.com/merbanan/rtl_433_tests/tree/master/tests/lacrosse/06/gfile-tx29-short-preamble.cu8. @@ -79,56 +79,46 @@ There's no way to distinguish between the TX35 and TX25U models #define LACROSSE_TX29_NOHUMIDSENSOR 0x6a // Sensor do not support humidity #define LACROSSE_TX25_PROBE_FLAG 0x7d // Humidity flag to indicate probe temperature channel -#define LACROSSE_TX35_CRC_POLY 0x31 -#define LACROSSE_TX35_CRC_INIT 0x00 #define LACROSSE_TX29_MODEL 29 // Model number #define LACROSSE_TX35_MODEL 35 static int lacrosse_it(r_device *decoder, bitbuffer_t *bitbuffer, int device29or35) { - data_t *data; - int brow; - uint8_t out[5]; - int r_crc, c_crc; - int sensor_id, newbatt, battery_low; - int humidity; - float temp_c; + // 4 bits of preamble, sync word 2dd4, sensor model 9: 24 bit + uint8_t const preamble[] = {0xa2, 0xdd, 0x49}; + int events = 0; - static const uint8_t preamble[] = { - 0xa8, - 0xb7, - 0x52, - 0x40, - }; - - for (brow = 0; brow < bitbuffer->num_rows; ++brow) { + for (int row = 0; row < bitbuffer->num_rows; ++row) { // Validate message and reject it as fast as possible : check for preamble - unsigned int start_pos = bitbuffer_search(bitbuffer, brow, 0, preamble, 26); + unsigned int start_pos = bitbuffer_search(bitbuffer, row, 0, preamble, 24); // no preamble detected, move to the next row - if (start_pos == bitbuffer->bits_per_row[brow]) + if (start_pos >= bitbuffer->bits_per_row[row]) continue; // DECODE_ABORT_EARLY if (decoder->verbose) - fprintf(stderr, "LaCrosse TX29/35 detected, buffer is %d bits length, device is TX%d\n", bitbuffer->bits_per_row[brow], device29or35); + fprintf(stderr, "%s: LaCrosse TX29/35 detected, buffer is %d bits length, device is TX%d\n", __func__, bitbuffer->bits_per_row[row], device29or35); // remove preamble and keep only five octets - bitbuffer_extract_bytes(bitbuffer, brow, start_pos+22, out, 40); + uint8_t b[5]; + bitbuffer_extract_bytes(bitbuffer, row, start_pos + 20, b, 40); - // Check message integrity (CRC/Checksum/parity) - r_crc = out[4]; - c_crc = crc8(&out[0], 4, LACROSSE_TX35_CRC_POLY, LACROSSE_TX35_CRC_INIT); + // Check message integrity + int r_crc = b[4]; + int c_crc = crc8(b, 4, 0x31, 0x00); if (r_crc != c_crc) { if (decoder->verbose) - fprintf(stderr, "LaCrosse TX29/35 bad CRC: calculated %02x, received %02x\n", c_crc, r_crc); + fprintf(stderr, "%s: LaCrosse TX29/35 bad CRC: calculated %02x, received %02x\n", __func__, c_crc, r_crc); // reject row continue; // DECODE_FAIL_MIC } // message "envelope" has been validated, start parsing data - sensor_id = ((out[0] & 0x0f) << 2) | (out[1] >> 6); - temp_c = 10.0 * (out[1] & 0x0f) + 1.0 * ((out[2] >> 4) & 0x0f) + 0.1 * (out[2] & 0x0f) - 40.0; - newbatt = (out[1] >> 5) & 1; - battery_low = out[3] >> 7; - humidity = out[3] & 0x7f; + int sensor_id = ((b[0] & 0x0f) << 2) | (b[1] >> 6); + float temp_c = 10.0 * (b[1] & 0x0f) + 1.0 * ((b[2] >> 4) & 0x0f) + 0.1 * (b[2] & 0x0f) - 40.0; + int new_batt = (b[1] >> 5) & 1; + int battery_low = b[3] >> 7; + int humidity = b[3] & 0x7f; + + data_t *data; if ((humidity == LACROSSE_TX29_NOHUMIDSENSOR) || (humidity == LACROSSE_TX25_PROBE_FLAG)) { if (humidity == LACROSSE_TX25_PROBE_FLAG) sensor_id += 0x40; // Change ID to distinguish between the main and probe channels @@ -137,18 +127,19 @@ static int lacrosse_it(r_device *decoder, bitbuffer_t *bitbuffer, int device29or "model", "", DATA_STRING, (device29or35 == 29 ? "LaCrosse-TX29IT" : "LaCrosse-TX35DTHIT"), "id", "", DATA_INT, sensor_id, "battery_ok", "Battery", DATA_INT, !battery_low, - "newbattery", "NewBattery", DATA_INT, newbatt, + "newbattery", "NewBattery", DATA_INT, new_batt, "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, "mic", "Integrity", DATA_STRING, "CRC", NULL); /* clang-format on */ - } else { + } + else { /* clang-format off */ data = data_make( "model", "", DATA_STRING, (device29or35 == 29 ? "LaCrosse-TX29IT" : "LaCrosse-TX35DTHIT"), "id", "", DATA_INT, sensor_id, "battery_ok", "Battery", DATA_INT, !battery_low, - "newbattery", "NewBattery", DATA_INT, newbatt, + "newbattery", "NewBattery", DATA_INT, new_batt, "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, "mic", "Integrity", DATA_STRING, "CRC", @@ -181,36 +172,34 @@ static int lacrossetx35_callback(r_device *decoder, bitbuffer_t *bitbuffer) } static char *output_fields[] = { - "model", - "id", - "battery_ok", - "newbattery", - "temperature_C", - "humidity", - "mic", - NULL, + "model", + "id", + "battery_ok", + "newbattery", + "temperature_C", + "humidity", + "mic", + NULL, }; // Receiver for the TX29 and TX25U device r_device lacrosse_tx29 = { - .name = "LaCrosse TX29IT, TFA Dostmann 30.3159.IT Temperature sensor", - .modulation = FSK_PULSE_PCM, - .short_width = 55, // 58 us for TX34-IT - .long_width = 55, // 58 us for TX34-IT - .reset_limit = 4000, - .decode_fn = &lacrossetx29_callback, - .disabled = 0, - .fields = output_fields, + .name = "LaCrosse TX29IT, TFA Dostmann 30.3159.IT Temperature sensor", + .modulation = FSK_PULSE_PCM, + .short_width = 55, // 58 us for TX34-IT + .long_width = 55, // 58 us for TX34-IT + .reset_limit = 4000, + .decode_fn = &lacrossetx29_callback, + .fields = output_fields, }; // Receiver for the TX35 device r_device lacrosse_tx35 = { - .name = "LaCrosse TX35DTH-IT, TFA Dostmann 30.3155 Temperature/Humidity sensor", - .modulation = FSK_PULSE_PCM, - .short_width = 105, - .long_width = 105, - .reset_limit = 4000, - .decode_fn = &lacrossetx35_callback, - .disabled = 0, - .fields = output_fields, + .name = "LaCrosse TX35DTH-IT, TFA Dostmann 30.3155 Temperature/Humidity sensor", + .modulation = FSK_PULSE_PCM, + .short_width = 105, + .long_width = 105, + .reset_limit = 4000, + .decode_fn = &lacrossetx35_callback, + .fields = output_fields, }; diff --git a/src/devices/m_bus.c b/src/devices/m_bus.c index f7d27b72..81e15fd3 100644 --- a/src/devices/m_bus.c +++ b/src/devices/m_bus.c @@ -1146,6 +1146,7 @@ static int m_bus_mode_s_callback(r_device *decoder, bitbuffer_t *bitbuffer) return 1; } +// NOTE: we'd need to add "value_types_tab X unit_names X n" fields static char *output_fields[] = { "model", "mode", diff --git a/vs15/rtl_433.vcxproj b/vs15/rtl_433.vcxproj index dbac0e7e..1a0ebd94 100644 --- a/vs15/rtl_433.vcxproj +++ b/vs15/rtl_433.vcxproj @@ -263,6 +263,7 @@ COPY ..\..\libusb\MS64\dll\libusb*.dll $(TargetDir) + diff --git a/vs15/rtl_433.vcxproj.filters b/vs15/rtl_433.vcxproj.filters index 87baba94..0edcfa9c 100644 --- a/vs15/rtl_433.vcxproj.filters +++ b/vs15/rtl_433.vcxproj.filters @@ -526,6 +526,9 @@ Source Files\devices + + Source Files\devices + Source Files\devices