Cleanup LaCrosse LTV-WSDTH01 BreezePro

This commit is contained in:
Christian W. Zuckschwerdt 2020-09-05 18:52:01 +02:00
parent 15c6d6e0a2
commit 9c1945885e
9 changed files with 68 additions and 59 deletions

View file

@ -239,6 +239,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
[163] Acurite 590TX Temperature with optional Humidity [163] Acurite 590TX Temperature with optional Humidity
[164] Security+ 2.0 (Keyfob) [164] Security+ 2.0 (Keyfob)
[165] TFA Dostmann 30.3221.02 T/H Outdoor Sensor [165] TFA Dostmann 30.3221.02 T/H Outdoor Sensor
[166] LaCrosse Technology View LTV-WSDTH01 Breeze Pro Wind Sensor
* Disabled by default, use -R n or -G * Disabled by default, use -R n or -G

View file

@ -347,6 +347,7 @@ stop_after_successful_events false
protocol 163 # Acurite 590TX Temperature with optional Humidity protocol 163 # Acurite 590TX Temperature with optional Humidity
protocol 164 # Security+ 2.0 (Keyfob) protocol 164 # Security+ 2.0 (Keyfob)
protocol 165 # TFA Dostmann 30.3221.02 T/H Outdoor Sensor protocol 165 # TFA Dostmann 30.3221.02 T/H Outdoor Sensor
protocol 166 # LaCrosse Technology View LTV-WSDTH01 Breeze Pro Wind Sensor
## Flex devices (command line option "-X") ## Flex devices (command line option "-X")

View file

@ -173,6 +173,7 @@
DECL(acurite_590tx) \ DECL(acurite_590tx) \
DECL(secplus_v2) \ DECL(secplus_v2) \
DECL(tfa_30_3221) \ DECL(tfa_30_3221) \
DECL(lacrosse_breezepro) \
/* Add new decoders here. */ /* Add new decoders here. */
#define DECL(name) extern r_device name; #define DECL(name) extern r_device name;

View file

@ -616,6 +616,9 @@ Security+ 2.0 (Keyfob)
.TP .TP
[ \fB165\fI\fP ] [ \fB165\fI\fP ]
TFA Dostmann 30.3221.02 T/H Outdoor Sensor TFA Dostmann 30.3221.02 T/H Outdoor Sensor
.TP
[ \fB166\fI\fP ]
LaCrosse Technology View LTV\-WSDTH01 Breeze Pro Wind Sensor
* Disabled by default, use \-R n or \-G * Disabled by default, use \-R n or \-G
.SS "Input device selection" .SS "Input device selection"

View file

@ -100,6 +100,7 @@ add_library(r_433 STATIC
devices/kerui.c devices/kerui.c
devices/klimalogg.c devices/klimalogg.c
devices/lacrosse.c devices/lacrosse.c
devices/lacrosse_breezepro.c
devices/lacrosse_tx141x.c devices/lacrosse_tx141x.c
devices/lacrosse_tx35.c devices/lacrosse_tx35.c
devices/lacrosse_ws7000.c devices/lacrosse_ws7000.c

View file

@ -99,6 +99,7 @@ rtl_433_SOURCES = abuf.c \
devices/kerui.c \ devices/kerui.c \
devices/klimalogg.c \ devices/klimalogg.c \
devices/lacrosse.c \ devices/lacrosse.c \
devices/lacrosse_breezepro.c \
devices/lacrosse_tx141x.c \ devices/lacrosse_tx141x.c \
devices/lacrosse_tx35.c \ devices/lacrosse_tx35.c \
devices/lacrosse_ws7000.c \ devices/lacrosse_ws7000.c \

View file

@ -1,5 +1,5 @@
/** @file /** @file
LaCrosse Breeze Pro LTV-WSDTH01 sensor. LaCrosse Technology View LTV-WSDTH01 Breeze Pro Wind Sensor.
Copyright (C) 2020 Mike Bruski (AJ9X) <michael.bruski@gmail.com> Copyright (C) 2020 Mike Bruski (AJ9X) <michael.bruski@gmail.com>
@ -9,7 +9,7 @@
(at your option) any later version. (at your option) any later version.
*/ */
/** /**
LaCrosse Breeze Pro LTV-WSDTH01 sensor. LaCrosse Technology View LTV-WSDTH01 Breeze Pro Wind Sensor.
LaCrosse Color Forecast Station (model 79400) utilizes the remote temp/ LaCrosse Color Forecast Station (model 79400) utilizes the remote temp/
humidity/wind speed/wind direction sensor LTV-WSDTH01. humidity/wind speed/wind direction sensor LTV-WSDTH01.
@ -18,6 +18,13 @@ Product pages:
https://www.lacrossetechnology.com/products/79400 https://www.lacrossetechnology.com/products/79400
https://www.lacrossetechnology.com/products/ltv-wsdth01 https://www.lacrossetechnology.com/products/ltv-wsdth01
Specifications:
- Wind Speed Range: 0 to 178 kmh
- Degrees of Direction: 360 deg with 16 Cardinal Directions
- Outdoor Temperature Range: -29 C to 60 C
- Outdoor Humidity Range: 10 to 99 %RH
- Update Interval: Every 31 Seconds
Internal inspection of the remote sensor reveals that the device Internal inspection of the remote sensor reveals that the device
utilizes a HopeRF CMT2119A ISM transmitter chip which is capable of utilizes a HopeRF CMT2119A ISM transmitter chip which is capable of
transmitting up to 32 bytes of data on any ISM frequency using OOK transmitting up to 32 bytes of data on any ISM frequency using OOK
@ -37,76 +44,75 @@ CMT2219A.
(http://www.cmostek.com/download/CMT2219A.pdf) (http://www.cmostek.com/download/CMT2219A.pdf)
(http://www.cmostek.com/download/AN138%20CMT2219A%20Configuration%20Guideline.pdf) (http://www.cmostek.com/download/AN138%20CMT2219A%20Configuration%20Guideline.pdf)
Protocol Specification: Protocol Specification:
Data bits are NRZ encoded with logical 1 and 0 bits 106.842us in length. Data bits are NRZ encoded with logical 1 and 0 bits 106.842us in length.
SYN:32h ID:24h ?:4 SEQ:3b ?:1b TEMP:12d HUM:12d WSPD:12d WDIR:12d CHK:8h END:32h SYN:32h ID:24h ?:4b SEQ:3d ?:1b TEMP:12d HUM:12d WSPD:12d WDIR:12d CHK:8h END:32h
Packet length is 264 bits according to inspectrum broken down as follows: Packet length is 264 bits according to inspectrum broken down as follows:
preamble: 7 bytes (when aligned with sync word these are 0xaa) - warm-up: 7 bytes (0x55, aligned with sync word these are 0xaa)
sync word: 4 bytes (0xd2aa2dd4) - preamble/sync 4 bytes 0xd2aa2dd4 (see as 0x695516ea05)
device ID: 3 bytes (matches bar code underside of unit covering pgm port) - device id: 3 bytes (matches bar code underside of unit covering pgm port)
x1: 4 bit (unknown, bit 0?00 might be 'battery low') - x1: 4 bit (unknown, bit 0?00 might be 'battery low')
sequence: 3 bits (0-7, one up per packet, then repeats) - sequence: 3 bits (0-7, one up per packet, then repeats)
x2: 1 bit (unknown) - x2: 1 bit (unknown)
celsius: 12 bits (base 400, scale 10, range: -29°C to 60°C) - celsius: 12 bits (offset 400, scale 10, range: -29 C to 60 C)
humidity: 12 bits (10 to 99% relative humidity) - humidity: 12 bits (10 to 99% relative humidity)
wind speed: 12 bits (0.0 to 178.0 kMh) - wind speed: 12 bits (0.0 to 178.0 kMh)
wind direction: 12 bits (0 to 359°) - wind direction: 12 bits (0 to 359 deg)
checksum: 8 bits (CRC-8 poly 0x31 init 0x00 over 10 bytes after sync) - checksum: 8 bits (CRC-8 poly 0x31 init 0x00 over 10 bytes after sync)
end: 32 bytes (0xd2d2d200) - trailer: 32 bytes (0xd2d2d200)
The sensor generates a packet every 'n' seconds but only transmits if one or The sensor generates a packet every 'n' seconds but only transmits if one or
more of the following conditions are satified: more of the following conditions are satified:
temp changes +/- 0.8 degrees C - temp changes +/- 0.8 degrees C
humidity changes +/- 1% - humidity changes +/- 1%
wind speed changes +/- 0.5 kM/h - wind speed changes +/- 0.5 kM/h
Thus, if there is a gap in sequencing, it is due to bad packet[s] (too short, Thus, if there is a gap in sequencing, it is due to bad packet[s] (too short,
failed CRC) or packet[s] that didn't satisfy at least one of these three failed CRC) or packet[s] that didn't satisfy at least one of these three
conditions. 'n' above varies with temperature. At 0C and above, 'n' is 31. conditions. 'n' above varies with temperature. At 0C and above, 'n' is 31.
Between -17C and 0C, 'n' is 60. Below -17C, 'n' is 360. Between -17C and 0C, 'n' is 60. Below -17C, 'n' is 360.
*/ */
#include "decoder.h" #include "decoder.h"
static const uint8_t sync_word[] = { 0xd2, 0xaa, 0x2d, 0xd4 };
static int lacrosse_breezepro_decode(r_device *decoder, bitbuffer_t *bitbuffer) static int lacrosse_breezepro_decode(r_device *decoder, bitbuffer_t *bitbuffer)
{ {
uint8_t const preamble_pattern[] = { 0xd2, 0xaa, 0x2d, 0xd4 };
data_t *data; data_t *data;
uint8_t p[32], l, *c, x1, x2, seq, offset, chk; uint8_t b[11];
uint32_t id; uint32_t id;
uint16_t raw_temp, humidity, raw_speed, direction; int flags, seq, offset, chk;
int raw_temp, humidity, raw_speed, direction;
float temp_c, speed_kmh; float temp_c, speed_kmh;
if (bitbuffer->bits_per_row[0] < 264) { if (bitbuffer->bits_per_row[0] < 264) {
if (decoder->verbose) { if (decoder->verbose) {
fprintf(stderr, "%s: Wrong packet length: %d\n", __func__, bitbuffer->bits_per_row[0]); fprintf(stderr, "%s: Wrong packet length: %d\n", __func__, bitbuffer->bits_per_row[0]);
} }
return DECODE_ABORT_LENGTH; return DECODE_ABORT_LENGTH;
} }
offset = bitbuffer_search(bitbuffer, 0, 0, offset = bitbuffer_search(bitbuffer, 0, 0,
sync_word, sizeof(sync_word) * 8); preamble_pattern, sizeof(preamble_pattern) * 8);
if (offset == bitbuffer->bits_per_row[0]) { if (offset >= bitbuffer->bits_per_row[0]) {
if (decoder->verbose) { if (decoder->verbose) {
fprintf(stderr, "%s: Sync word not found\n", __func__); fprintf(stderr, "%s: Sync word not found\n", __func__);
} }
return DECODE_FAIL_SANITY; return DECODE_ABORT_EARLY;
} }
offset += sizeof(preamble_pattern) * 8;
bitbuffer_extract_bytes(bitbuffer, 0, offset, b, 11 * 8);
c = bitbuffer->bb[offset]; chk = crc8(b, 11, 0x31, 0x00);
chk = crc8(c, 10, 0x31, 0x00);
if (chk) { if (chk) {
if (decoder->verbose) { if (decoder->verbose) {
fprintf(stderr, "%s: CRC failed!\n", __func__); fprintf(stderr, "%s: CRC failed!\n", __func__);
@ -118,35 +124,29 @@ static int lacrosse_breezepro_decode(r_device *decoder, bitbuffer_t *bitbuffer)
bitbuffer_print(bitbuffer); bitbuffer_print(bitbuffer);
} }
l = bitbuffer->bits_per_row[0] - offset; id = (b[0] << 16) | (b[1] << 8) | b[2];
offset += sizeof(sync_word) * 8; flags = (b[3] & 0xf1); // masks off seq bits
bitbuffer_extract_bytes(bitbuffer, 0, offset, p, l); seq = (b[3] & 0x0e) >> 1;
raw_temp = b[4] << 4 | ((b[5] & 0xf0) >> 4);
humidity = ((b[5] & 0x0f) << 8) | b[6];
raw_speed = b[7] << 4 | ((b[8] & 0xf0) >> 4);
direction = ((b[8] & 0x0f) << 8) | b[9];
id = (p[0] << 16) | (p[1] << 8) | p[2]; // base and/or scale adjustments
x1 = (p[3] & 0xf0) >> 4; temp_c = (raw_temp - 400) * 0.1f;
seq = (p[3] & 0x0e) >> 1; speed_kmh = raw_speed * 0.1f;
x2 = p[3] & 0x01;
raw_temp = p[4] << 4 | ((p[5] & 0xf0) >> 4);
humidity = ((p[5] & 0x0f) << 8) | p[6];
raw_speed = p[7] << 4 | ((p[8] & 0xf0) >> 4);
direction = ((p[8] & 0x0f) << 8) | p[9];
/* base and/or scale adjustments */
temp_c = (float)raw_temp * 0.1 - 40.0;
speed_kmh = (float)raw_speed * 0.1;
/* clang-format off */ /* clang-format off */
data = data_make( data = data_make(
"model", "", DATA_STRING, "LaCrosse-LTV-WSDTH01", "model", "", DATA_STRING, "LaCrosse-BreezePro",
"id", "Sensor ID", DATA_FORMAT, "%06x", DATA_INT, id, "id", "Sensor ID", DATA_FORMAT, "%06x", DATA_INT, id,
"x1", "unknown", DATA_INT, x1,
"seq", "Sequence", DATA_FORMAT, "%01x", DATA_INT, seq, "seq", "Sequence", DATA_FORMAT, "%01x", DATA_INT, seq,
"x2", "unknown", DATA_INT, x2, "flags", "unknown", DATA_INT, flags,
"temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c, "temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, temp_c,
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity,
"wind_avg_km_h", "Wind speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh, "wind_avg_km_h", "Wind speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh,
"wind_dir_deg", "Wind direction", DATA_INT, direction, "wind_dir_deg", "Wind direction", DATA_INT, direction,
"mic", "Integrity", DATA_STRING, "CRC8", "mic", "Integrity", DATA_STRING, "CRC",
NULL); NULL);
/* clang-format on */ /* clang-format on */
@ -157,26 +157,23 @@ static int lacrosse_breezepro_decode(r_device *decoder, bitbuffer_t *bitbuffer)
static char *output_fields[] = { static char *output_fields[] = {
"model", "model",
"id", "id",
"x1", "seq",
"seq", "flags",
"x2",
"temperature_C", "temperature_C",
"humidity", "humidity",
"wind_avg_km_h", "wind_avg_km_h",
"wind_dir_deg", "wind_dir_deg",
"test",
"mic", "mic",
NULL, NULL,
}; };
// flex decoder m=FSK_PCM, s=107, l=107, r=5900 // flex decoder m=FSK_PCM, s=107, l=107, r=5900
r_device lacrosse_breezepro = { r_device lacrosse_breezepro = {
.name = "LaCrosse LTV-WSDTH01 sensor", .name = "LaCrosse Technology View LTV-WSDTH01 Breeze Pro Wind Sensor",
.modulation = FSK_PULSE_PCM, .modulation = FSK_PULSE_PCM,
.short_width = 107, .short_width = 107,
.long_width = 107, .long_width = 107,
.reset_limit = 5900, .reset_limit = 5900,
.decode_fn = &lacrosse_breezepro_decode, .decode_fn = &lacrosse_breezepro_decode,
.disabled = 0,
.fields = output_fields, .fields = output_fields,
}; };

View file

@ -223,6 +223,7 @@ COPY ..\..\libusb\MS64\dll\libusb*.dll $(TargetDir)</Command>
<ClCompile Include="..\src\devices\kerui.c" /> <ClCompile Include="..\src\devices\kerui.c" />
<ClCompile Include="..\src\devices\klimalogg.c" /> <ClCompile Include="..\src\devices\klimalogg.c" />
<ClCompile Include="..\src\devices\lacrosse.c" /> <ClCompile Include="..\src\devices\lacrosse.c" />
<ClCompile Include="..\src\devices\lacrosse_breezepro.c" />
<ClCompile Include="..\src\devices\lacrosse_tx141x.c" /> <ClCompile Include="..\src\devices\lacrosse_tx141x.c" />
<ClCompile Include="..\src\devices\lacrosse_tx35.c" /> <ClCompile Include="..\src\devices\lacrosse_tx35.c" />
<ClCompile Include="..\src\devices\lacrosse_ws7000.c" /> <ClCompile Include="..\src\devices\lacrosse_ws7000.c" />

View file

@ -406,6 +406,9 @@
<ClCompile Include="..\src\devices\lacrosse.c"> <ClCompile Include="..\src\devices\lacrosse.c">
<Filter>Source Files\devices</Filter> <Filter>Source Files\devices</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\src\devices\lacrosse_breezepro.c">
<Filter>Source Files\devices</Filter>
</ClCompile>
<ClCompile Include="..\src\devices\lacrosse_tx141x.c"> <ClCompile Include="..\src\devices\lacrosse_tx141x.c">
<Filter>Source Files\devices</Filter> <Filter>Source Files\devices</Filter>
</ClCompile> </ClCompile>