minor: Clean up LaCrosse-TX34/35-IT

This commit is contained in:
Christian W. Zuckschwerdt
2021-12-10 21:12:38 +01:00
parent 804cad03ab
commit dcea0814ef
7 changed files with 102 additions and 131 deletions

View File

@@ -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

View File

@@ -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")

View File

@@ -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,
};

View File

@@ -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,
};

View File

@@ -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",

View File

@@ -263,6 +263,7 @@ COPY ..\..\libusb\MS64\dll\libusb*.dll $(TargetDir)</Command>
<ClCompile Include="..\src\devices\lacrosse_r1.c" />
<ClCompile Include="..\src\devices\lacrosse_th3.c" />
<ClCompile Include="..\src\devices\lacrosse_tx141x.c" />
<ClCompile Include="..\src\devices\lacrosse_tx34.c" />
<ClCompile Include="..\src\devices\lacrosse_tx35.c" />
<ClCompile Include="..\src\devices\lacrosse_wr1.c" />
<ClCompile Include="..\src\devices\lacrosse_ws7000.c" />

View File

@@ -526,6 +526,9 @@
<ClCompile Include="..\src\devices\lacrosse_tx141x.c">
<Filter>Source Files\devices</Filter>
</ClCompile>
<ClCompile Include="..\src\devices\lacrosse_tx34.c">
<Filter>Source Files\devices</Filter>
</ClCompile>
<ClCompile Include="..\src\devices\lacrosse_tx35.c">
<Filter>Source Files\devices</Filter>
</ClCompile>