mirror of
https://github.com/merbanan/rtl_433.git
synced 2026-04-22 18:46:58 -04:00
minor: Clean up LaCrosse-TX34/35-IT
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user