LaCrosse TX29-IT / TX35DTH-IT weather sensor (#479)

* 1st experiment

* First try

* First working decoder of LaCrosse TX29/35

* Merge TX29 and TX35

* Be compatible with rules

* Finalization

* oups

* Code improvement
This commit is contained in:
psa-jforestier
2017-02-01 09:38:40 +01:00
committed by Benjamin Larsson
parent 744bccd505
commit a9a574fe4f
5 changed files with 230 additions and 6 deletions

View File

@@ -42,7 +42,7 @@
#define MINIMAL_BUF_LENGTH 512
#define MAXIMAL_BUF_LENGTH (256 * 16384)
#define MAX_PROTOCOLS 73
#define MAX_PROTOCOLS 75
#define SIGNAL_GRABBER_BUFFER (12 * DEFAULT_BUF_LENGTH)
/* Supported modulation types */

View File

@@ -76,8 +76,9 @@
DECL(maverick_et73x) \
DECL(rftech) \
DECL(lacrosse_TX141TH_Bv2) \
DECL(acurite_00275rm)
DECL(acurite_00275rm) \
DECL(lacrosse_tx35) \
DECL(lacrosse_tx29)
typedef struct {
char name[256];

View File

@@ -24,7 +24,7 @@ add_executable(rtl_433
pulse_detect.c
rtl_433.c
util.c
devices/acurite.c
devices/acurite.c
devices/alecto.c
devices/ambient_weather.c
devices/blyss.c
@@ -80,8 +80,8 @@ add_executable(rtl_433
devices/steelmate.c
devices/schraeder.c
devices/elro_db286a.c
devices/efergy_optical.c
devices/hondaremote.c
devices/efergy_optical.c
devices/hondaremote.c
devices/new_template.c
devices/radiohead_ask.c
devices/kerui.c
@@ -89,6 +89,7 @@ add_executable(rtl_433
devices/honeywell.c
devices/maverick_et73x.c
devices/rftech.c
devices/lacrosse_tx35.c
)

View File

@@ -42,6 +42,7 @@ rtl_433_SOURCES = baseband.c \
devices/lacrosse.c \
devices/lacrosse_TX141TH_Bv2.c \
devices/lacrossews.c \
devices/lacrosse_tx35.c \
devices/lightwave_rf.c \
devices/mebus.c \
devices/newkaku.c \

221
src/devices/lacrosse_tx35.c Normal file
View File

@@ -0,0 +1,221 @@
/*
* LaCrosse/StarMétéo/Conrad TX35DTH-IT, TX29-IT, Temperature and Humidity Sensors.
* Tune to 868240000Hz
*
*
Protocol
========
Example Data (gfile-tx29.data : https://github.com/merbanan/rtl_433_tests/tree/master/tests/lacrosse/06)
a a 2 d d 4 9 2 8 4 4 8 6 a e c
Bits :
1010 1010 0010 1101 1101 0100 1001 0010 1000 0100 0100 1000 0110 1010 1110 1100
Bytes num :
----1---- ----2---- ----3---- ----4---- ----5---- ----6---- ----7---- ----8----
~~~~~~~~~ 1st byte
preamble, always "0xaa"
~~~~~~~~~~~~~~~~~~~ bytes 2 and 3
brand identifier, always 0x2dd4
~~~~ 1st nibble of bytes 4
datalength (always 9) in nibble, including this field and crc
~~~~ ~~ 2nd nibble of bytes 4 and 1st and 2nd bits of byte 5
Random device id (6 bits)
~ 3rd bits of byte 5
new battery indicator
~ 4th bits of byte 5
unkown, unused
~~~~ ~~~~ ~~~~ 2nd nibble of byte 5 and byte 6
temperature, in bcd *10 +40
~ 1st bit of byte 7
weak battery
~~~ ~~~~ 2-8 bits of byte 7
humidity, in%. If == 0x6a : no humidity sensor
~~~~ ~~~~ byte 8
crc8 of bytes
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
one byte as the preamble. I own 3 sensors TX29, and two of them send a long preamble.
So this decoder synchronize on the 0xaa 0x2d 0xd4 preamble, so many 0xaa can occurs before.
Also, I added 0x9 in the preamble (the weather data length), because this decoder only handle
this type of message.
TX29 and TX35 share the same protocol, but pulse are different length, thus this decoder
handle the two signal and we use two r_device struct (only differing by the pulse width)
How to make a decoder : https://enavarro.me/ajouter-un-decodeur-ask-a-rtl_433.html
*/
#include "rtl_433.h"
#include "util.h"
#include "data.h"
#define LACROSSE_TX29_NOHUMIDSENSOR 0x6a // Sensor do not support humidty
#define LACROSSE_TX35_CRC_POLY 0x31
#define LACROSSE_TX35_CRC_INIT 0x00
#define LACROSSE_TX29_MODEL 29; // Model number
#define LACROSSE_TX35_MODEL 35;
/**
** Generic decoder for LaCrosse "IT+" (instant transmission) protocol
** Param device29or35 contain "29" or "35" depending of the device.
**/
static int lacrosse_it(bitbuffer_t *bitbuffer, uint8_t device29or35) {
char time_str[LOCAL_TIME_BUFLEN];
uint8_t *bb;
uint16_t brow, row_nbytes;
uint8_t msg_type, r_crc, c_crc;
uint8_t sensor_id, newbatt, battery_low;
uint8_t humidity; // in %. If > 100 : no humidity sensor
float temp_c; // in °C
int valid = 0;
data_t *data;
int events = 0;
static const uint8_t preamble[] = {
0xaa, // preamble
0x2d, // brand identifer
0xd4, // brand identifier
0x90, // data length (this decoder work only with data length of 9, so we hardcode it on the preamble)
};
uint8_t out[8] = {0}; // array of byte to decode
local_time_str(0, time_str);
for (brow = 0; brow < bitbuffer->num_rows; ++brow) {
bb = bitbuffer->bb[brow];
// Validate message and reject it as fast as possible : check for preamble
unsigned int start_pos = bitbuffer_search(bitbuffer, brow, 0, preamble, 28);
if(start_pos == bitbuffer->bits_per_row[brow])
continue; // no preamble detected, move to the next row
if (debug_output >= 1)
fprintf(stderr, "LaCrosse TX29/35 detected, buffer is %d bits length, device is TX%d\n", bitbuffer->bits_per_row[brow], device29or35);
// remove preamble and keep only 64 bits
bitbuffer_extract_bytes(bitbuffer, brow, start_pos, out, 64);
/*
* Check message integrity (CRC/Checksum/parity)
* Normally, it is computed on the whole message, from byte 0 (preamble) to byte 6,
* but preamble is always the same, so we can speed the process by doing a crc check
* only on byte 3,4,5,6
*/
r_crc = out[7];
c_crc = crc8(&out[3], 4, LACROSSE_TX35_CRC_POLY, LACROSSE_TX35_CRC_INIT);
if (r_crc != c_crc) {
// example debugging output
if (debug_output >= 1)
fprintf(stderr, "%s LaCrosse TX29/35 bad CRC: calculated %02x, received %02x\n",
time_str, c_crc, r_crc);
// reject row
continue;
}
/*
* Now that message "envelope" has been validated,
* start parsing data.
*/
sensor_id = ((out[3] & 0x0f) << 2) | ((out[4]>>6) & 0x03);
temp_c = 10.0 * (out[4] & 0x0f) + 1.0 *((out[5]>>4) & 0x0f) + 0.1 * (out[5] & 0x0f) - 40.0;
newbatt = (out[4] >> 5) & 1;
battery_low = (out[6]>>7) & 1;
humidity = 1 * (out[6] & 0x7F); // Bit 1 to 7 of byte 6
if (humidity == LACROSSE_TX29_NOHUMIDSENSOR) {
data = data_make("time", "", DATA_STRING, time_str,
"brand", "", DATA_STRING, "LaCrosse",
"model", "", DATA_STRING, (device29or35 == 29 ? "TX29-IT" : "TX35DTH-IT"),
"id", "", DATA_INT, sensor_id,
"battery", "Battery", DATA_STRING, battery_low ? "LOW" : "OK",
"newbattery", "NewBattery", DATA_INT, newbatt,
"temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c,
"crc", "CRC", DATA_STRING, "OK",
NULL);
}
else {
data = data_make("time", "", DATA_STRING, time_str,
"brand", "", DATA_STRING, "LaCrosse",
"model", "", DATA_STRING, (device29or35 == 29 ? "TX29-IT" : "TX35DTH-IT"),
"id", "", DATA_INT, sensor_id,
"battery", "Battery", DATA_STRING, battery_low ? "LOW" : "OK",
"newbattery", "NewBattery", DATA_INT, newbatt,
"temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c,
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity,
"crc", "CRC", DATA_STRING, "OK",
NULL);
}
// humidity = -1; // The TX29-IT sensor do not have humidity. It is replaced by a special value
data_acquired_handler(data);
events++;
}
return events;
}
/**
** Wrapper for the TX29 device
**/
static int lacrossetx29_callback(bitbuffer_t *bitbuffer) {
return lacrosse_it(bitbuffer, LACROSSE_TX29_MODEL);
}
/**
** Wrapper for the TX35 device
**/
static int lacrossetx35_callback(bitbuffer_t *bitbuffer) {
return lacrosse_it(bitbuffer, LACROSSE_TX35_MODEL);
}
static char *output_fields_TX35[] = {
"time",
"brand",
"model",
"id",
"battery",
"newbattery",
"status",
"temperature_C",
"humidity",
"crc",
NULL
};
static char *output_fields_TX29[] = {
"time",
"brand",
"model",
"id",
"battery",
"newbattery",
"status",
"temperature_C",
"crc",
NULL
};
// Receiver for the TX29 device
r_device lacrosse_tx29 = {
.name = "LaCrosse TX29IT Temperature snsor",
.modulation = FSK_PULSE_PCM,
.short_limit = 55,
.long_limit = 55,
.reset_limit = 4000,
.json_callback = &lacrossetx29_callback,
.disabled = 0,
.demod_arg = 0,
.fields = output_fields_TX29,
};
// Receiver for the TX35 device
r_device lacrosse_tx35 = {
.name = "LaCrosse TX35DTH-IT Temperature sensor",
.modulation = FSK_PULSE_PCM,
.short_limit = 105,
.long_limit = 105,
.reset_limit = 4000,
.json_callback = &lacrossetx35_callback,
.disabled = 0,
.demod_arg = 0,
.fields = output_fields_TX35,
};