X-Git-Url: https://git.rvb.name/rtl-433.git/blobdiff_plain/ca13278b24eb61443559bcb61e64627fba3d8823..6d15c6f967221af825cf84e3ed12b96c763b127b:/src/devices/lacrosse_TX141TH_Bv2.c diff --git a/src/devices/lacrosse_TX141TH_Bv2.c b/src/devices/lacrosse_TX141TH_Bv2.c new file mode 100755 index 0000000..78c286d --- /dev/null +++ b/src/devices/lacrosse_TX141TH_Bv2.c @@ -0,0 +1,225 @@ +/* LaCrosse Color Forecast Station (model C85845), or other LaCrosse product + * utilizing the remote temperature/humidity sensor TX141TH-Bv2 transmitting + * in the 433.92 MHz band. Product pages: + * http://www.lacrossetechnology.com/c85845-color-weather-station/ + * http://www.lacrossetechnology.com/tx141th-bv2-temperature-humidity-sensor + * + * The TX141TH-Bv2 protocol is OOK modulated PWM with fixed period of 625 us + * for data bits, preambled by four long startbit pulses of fixed period equal + * to ~1666 us. Hence, it is similar to Bresser Thermo-/Hygro-Sensor 3CH (bresser_3ch.c + * included in this source code) with the exception that OOK_PULSE_PWM_TERNARY + * modulation type is technically more correct than OOK_PULSE_PWM_RAW. + * + * A single data packet looks as follows: + * 1) preamble - 833 us high followed by 833 us low, repeated 4 times: + * ---- ---- ---- ---- + * | | | | | | | | + * ---- ---- ---- ---- + * 2) a train of 40 data pulses with fixed 625 us period follows immediately: + * --- -- -- --- --- -- --- + * | | | | | | | | | | | | | | + * -- --- --- -- -- --- -- .... + * A logical 1 is 417 us of high followed by 208 us of low. + * A logical 0 is 208 us of high followed by 417 us of low. + * Thus, in the pictorial example above the bits are 1 0 0 1 1 0 1 .... + * + * The TX141TH-Bv2 sensor sends 12 of identical packets, one immediately following + * the other, in a single burst. These 12-packet bursts repeat every 50 seconds. At + * the end of the last packet there are two 833 us pulses ("post-amble"?). + * + * The data is grouped in 5 bytes / 10 nybbles + * [id] [id] [flags] [temp] [temp] [temp] [humi] [humi] [chk] [chk] + * + * The "id" is an 8 bit random integer generated when the sensor powers up for the + * first time; "flags" are 4 bits for battery low indicator, test button press, + * and channel; "temp" is 12 bit unsigned integer which encodes temperature in degrees + * Celsius as follows: + * temp_c = temp/10 - 50 + * to account for the -40 C -- 60 C range; "humi" is 8 bit integer indicating + * relative humidity in %. The method of calculating "chk", the presumed 8-bit checksum + * remains a complete mystery at the moment of this writing, and I am not totally sure + * if the last is any kind of CRC. I've run reveng 1.4.4 on exemplary data with all + * available CRC algorithms and found no match. Be my guest if you want to + * solve it - for example, if you figure out why the following two pairs have identical + * checksums you'll become a hero: + * + * 0x87 0x02 0x3c 0x3b 0xe1 + * 0x87 0x02 0x7d 0x37 0xe1 + * + * 0x87 0x01 0xc3 0x31 0xd8 + * 0x87 0x02 0x28 0x37 0xd8 + * + * Developer's comment 1: because of our choice of the OOK_PULSE_PWM_TERNARY type, the input + * array of bits will look like this: + * bitbuffer:: Number of rows: 25 + * [00] {0} : + * [01] {0} : + * [02] {0} : + * [03] {0} : + * [04] {40} 87 02 67 39 f6 + * [05] {0} : + * [06] {0} : + * [07] {0} : + * [08] {40} 87 02 67 39 f6 + * [09] {0} : + * [10] {0} : + * [11] {0} : + * [12] {40} 87 02 67 39 f6 + * [13] {0} : + * [14] {0} : + * [15] {0} : + * [16] {40} 87 02 67 39 f6 + * [17] {0} : + * [18] {0} : + * [19] {0} : + * [20] {40} 87 02 67 39 f6 + * [21] {0} : + * [22] {0} : + * [23] {0} : + * [24] {280} 87 02 67 39 f6 87 02 67 39 f6 87 02 67 39 f6 87 02 67 39 f6 87 02 67 39 f6 87 02 67 39 f6 87 02 67 39 f6 + * which is a direct consequence of two factors: (1) pulse_demod_pwm_ternary() always assuming + * only one startbit, and (2) bitbuffer_add_row() not adding rows beyond BITBUF_ROWS. This is + * OK because the data is clearly processable and the unique pattern minimizes the chance of + * confusion with other sensors, particularly Bresser 3CH. + * + * Developer's comment 2: with unknown CRC (see above) the obvious way of checking the data + * integrity is making use of the 12 packet repetition. In principle, transmission errors are + * be relatively rare, thus the most frequent packet (statistical mode) should represent + * the true data. Therefore, in the fisrt part of the callback routine the mode is determined + * for the first 4 bytes of the data compressed into a single 32-bit integer. Since the packet + * count is small, no sophisticated mode algorithm is necessary; a simple array of + * structures is sufficient. The added bonus is that relative count enables us to determine + * the quality of radio transmission. + * + * Copyright (C) 2017 Robert Fraczkiewicz (aromring@gmail.com) + * 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. + * + */ +#include "data.h" +#include "rtl_433.h" +#include "util.h" + +#define LACROSSE_TX141TH_BITLEN 40 +#define LACROSSE_TX141TH_BYTELEN 5 // = LACROSSE_TX141TH_BITLEN / 8 +#define LACROSSE_TX141TH_PACKETCOUNT 12 + +typedef struct { + int32_t data; // First 4 data bytes compressed into 32-bit integer + int8_t count; // Count +} data_and_count; + +static int lacrosse_tx141th_bv2_callback(bitbuffer_t *bitbuffer) { + bitrow_t *bb = bitbuffer->bb; + data_t *data; + char time_str[LOCAL_TIME_BUFLEN]; + local_time_str(0, time_str); + int i,j,k,nbytes,npacket,kmax; + uint8_t id=0,status=0,battery_low=0,test=0,humidity=0,maxcount; + uint16_t temp_raw=0; + float temp_f=0.0; + data_and_count dnc[LACROSSE_TX141TH_PACKETCOUNT] = {0}; + + if (debug_output) { + bitbuffer_print(bitbuffer); + } + + npacket=0; // Number of unique packets + for(i=0; ibits_per_row[i]; + if(j>=LACROSSE_TX141TH_BITLEN) { + nbytes=j/8; + for(j=0;j1) { + for(k=0;kmaxcount) { + maxcount=dnc[k].count; + kmax=k; + } + } + } + + // Unpack the data bytes back to eliminate dependence on the platform endiannes! + uint8_t *bytes=(uint8_t*)(&(dnc[kmax].data)); + id=bytes[0]; + status=bytes[1]; + battery_low=(status & 0x80) >> 7; + test=(status & 0x40) >> 6; + temp_raw=((status & 0x0F) << 8) + bytes[2]; + temp_f = 9.0*((float)temp_raw)/50.0-58.0; // Temperature in F + humidity = bytes[3]; + + if (0==id || 0==humidity || humidity > 100 || temp_f < -40.0 || temp_f > 140.0) { + if (debug_output) { + fprintf(stderr, "LaCrosse TX141TH-Bv2 data error\n"); + } + return 0; + } + + data = data_make("time", "Date and time", DATA_STRING, time_str, + "temperature", "Temperature in deg F", DATA_FORMAT, "%.2f F", DATA_DOUBLE, temp_f, + "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, + "id", "Sensor ID", DATA_FORMAT, "%02x", DATA_INT, id, + "model", "", DATA_STRING, "LaCrosse TX141TH-Bv2 sensor", + "battery", "Battery", DATA_STRING, battery_low ? "LOW" : "OK", + "test", "Test?", DATA_STRING, test ? "Yes" : "No", + NULL); + data_acquired_handler(data); + + return 1; + +} + +static char *output_fields[] = { + "time", + "temperature", + "humidity", + "id", + "model", + "battery", + "test", + NULL +}; + +r_device lacrosse_TX141TH_Bv2 = { + .name = "LaCrosse TX141TH-Bv2 sensor", + .modulation = OOK_PULSE_PWM_TERNARY, + .short_limit = 312, // short pulse is ~208 us, long pulse is ~417 us + .long_limit = 625, // long gap (with short pulse) is ~417 us, sync gap is ~833 us + .reset_limit = 1500, // maximum gap is 1250 us (long gap + longer sync gap on last repeat) + .json_callback = &lacrosse_tx141th_bv2_callback, + .disabled = 0, + .demod_arg = 2, // Longest pulses are startbits + .fields = output_fields, +}; +