diff --git a/README.md b/README.md index a88df194..162db13a 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md). [163] Acurite 590TX Temperature with optional Humidity [164] Security+ 2.0 (Keyfob) [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 diff --git a/conf/rtl_433.example.conf b/conf/rtl_433.example.conf index bff3932d..04c7d8be 100644 --- a/conf/rtl_433.example.conf +++ b/conf/rtl_433.example.conf @@ -347,6 +347,7 @@ stop_after_successful_events false protocol 163 # Acurite 590TX Temperature with optional Humidity protocol 164 # Security+ 2.0 (Keyfob) 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") diff --git a/include/rtl_433_devices.h b/include/rtl_433_devices.h index 49bf3217..ac591461 100644 --- a/include/rtl_433_devices.h +++ b/include/rtl_433_devices.h @@ -173,6 +173,7 @@ DECL(acurite_590tx) \ DECL(secplus_v2) \ DECL(tfa_30_3221) \ + DECL(lacrosse_breezepro) \ /* Add new decoders here. */ #define DECL(name) extern r_device name; diff --git a/man/man1/rtl_433.1 b/man/man1/rtl_433.1 index 59276fc6..3083920a 100644 --- a/man/man1/rtl_433.1 +++ b/man/man1/rtl_433.1 @@ -616,6 +616,9 @@ Security+ 2.0 (Keyfob) .TP [ \fB165\fI\fP ] 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 .SS "Input device selection" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f5ab4ba..461060f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -100,6 +100,7 @@ add_library(r_433 STATIC devices/kerui.c devices/klimalogg.c devices/lacrosse.c + devices/lacrosse_breezepro.c devices/lacrosse_tx141x.c devices/lacrosse_tx35.c devices/lacrosse_ws7000.c diff --git a/src/Makefile.am b/src/Makefile.am index cbfe95f6..39f9e293 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -99,6 +99,7 @@ rtl_433_SOURCES = abuf.c \ devices/kerui.c \ devices/klimalogg.c \ devices/lacrosse.c \ + devices/lacrosse_breezepro.c \ devices/lacrosse_tx141x.c \ devices/lacrosse_tx35.c \ devices/lacrosse_ws7000.c \ diff --git a/src/devices/lacrosse_breezepro.c b/src/devices/lacrosse_breezepro.c index adb13a3e..1776035e 100644 --- a/src/devices/lacrosse_breezepro.c +++ b/src/devices/lacrosse_breezepro.c @@ -1,5 +1,5 @@ /** @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> @@ -9,7 +9,7 @@ (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/ 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/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 utilizes a HopeRF CMT2119A ISM transmitter chip which is capable of 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/AN138%20CMT2219A%20Configuration%20Guideline.pdf) - Protocol Specification: - + 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: -preamble: 7 bytes (when aligned with sync word these are 0xaa) -sync word: 4 bytes (0xd2aa2dd4) -device ID: 3 bytes (matches bar code underside of unit covering pgm port) -x1: 4 bit (unknown, bit 0?00 might be 'battery low') -sequence: 3 bits (0-7, one up per packet, then repeats) -x2: 1 bit (unknown) -celsius: 12 bits (base 400, scale 10, range: -29°C to 60°C) -humidity: 12 bits (10 to 99% relative humidity) -wind speed: 12 bits (0.0 to 178.0 kMh) -wind direction: 12 bits (0 to 359°) -checksum: 8 bits (CRC-8 poly 0x31 init 0x00 over 10 bytes after sync) -end: 32 bytes (0xd2d2d200) - +- warm-up: 7 bytes (0x55, aligned with sync word these are 0xaa) +- preamble/sync 4 bytes 0xd2aa2dd4 (see as 0x695516ea05) +- device id: 3 bytes (matches bar code underside of unit covering pgm port) +- x1: 4 bit (unknown, bit 0?00 might be 'battery low') +- sequence: 3 bits (0-7, one up per packet, then repeats) +- x2: 1 bit (unknown) +- celsius: 12 bits (offset 400, scale 10, range: -29 C to 60 C) +- humidity: 12 bits (10 to 99% relative humidity) +- wind speed: 12 bits (0.0 to 178.0 kMh) +- wind direction: 12 bits (0 to 359 deg) +- checksum: 8 bits (CRC-8 poly 0x31 init 0x00 over 10 bytes after sync) +- trailer: 32 bytes (0xd2d2d200) The sensor generates a packet every 'n' seconds but only transmits if one or more of the following conditions are satified: - temp changes +/- 0.8 degrees C - humidity changes +/- 1% - wind speed changes +/- 0.5 kM/h +- temp changes +/- 0.8 degrees C +- humidity changes +/- 1% +- wind speed changes +/- 0.5 kM/h 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 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" -static const uint8_t sync_word[] = { 0xd2, 0xaa, 0x2d, 0xd4 }; - static int lacrosse_breezepro_decode(r_device *decoder, bitbuffer_t *bitbuffer) { + uint8_t const preamble_pattern[] = { 0xd2, 0xaa, 0x2d, 0xd4 }; + data_t *data; - uint8_t p[32], l, *c, x1, x2, seq, offset, chk; + uint8_t b[11]; 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; if (bitbuffer->bits_per_row[0] < 264) { if (decoder->verbose) { fprintf(stderr, "%s: Wrong packet length: %d\n", __func__, bitbuffer->bits_per_row[0]); - } + } return DECODE_ABORT_LENGTH; } 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) { 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(c, 10, 0x31, 0x00); + chk = crc8(b, 11, 0x31, 0x00); if (chk) { if (decoder->verbose) { 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); } - l = bitbuffer->bits_per_row[0] - offset; - offset += sizeof(sync_word) * 8; - bitbuffer_extract_bytes(bitbuffer, 0, offset, p, l); + id = (b[0] << 16) | (b[1] << 8) | b[2]; + flags = (b[3] & 0xf1); // masks off seq bits + 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]; - x1 = (p[3] & 0xf0) >> 4; - seq = (p[3] & 0x0e) >> 1; - 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; + // base and/or scale adjustments + temp_c = (raw_temp - 400) * 0.1f; + speed_kmh = raw_speed * 0.1f; /* clang-format off */ data = data_make( - "model", "", DATA_STRING, "LaCrosse-LTV-WSDTH01", + "model", "", DATA_STRING, "LaCrosse-BreezePro", "id", "Sensor ID", DATA_FORMAT, "%06x", DATA_INT, id, - "x1", "unknown", DATA_INT, x1, "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, "humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity, "wind_avg_km_h", "Wind speed", DATA_FORMAT, "%.1f km/h", DATA_DOUBLE, speed_kmh, "wind_dir_deg", "Wind direction", DATA_INT, direction, - "mic", "Integrity", DATA_STRING, "CRC8", + "mic", "Integrity", DATA_STRING, "CRC", NULL); /* clang-format on */ @@ -157,26 +157,23 @@ static int lacrosse_breezepro_decode(r_device *decoder, bitbuffer_t *bitbuffer) static char *output_fields[] = { "model", "id", - "x1", - "seq", - "x2", + "seq", + "flags", "temperature_C", "humidity", "wind_avg_km_h", "wind_dir_deg", - "test", "mic", NULL, }; // flex decoder m=FSK_PCM, s=107, l=107, r=5900 r_device lacrosse_breezepro = { - .name = "LaCrosse LTV-WSDTH01 sensor", + .name = "LaCrosse Technology View LTV-WSDTH01 Breeze Pro Wind Sensor", .modulation = FSK_PULSE_PCM, .short_width = 107, .long_width = 107, .reset_limit = 5900, .decode_fn = &lacrosse_breezepro_decode, - .disabled = 0, .fields = output_fields, }; diff --git a/vs15/rtl_433.vcxproj b/vs15/rtl_433.vcxproj index ab0e31f2..b4d18104 100644 --- a/vs15/rtl_433.vcxproj +++ b/vs15/rtl_433.vcxproj @@ -223,6 +223,7 @@ COPY ..\..\libusb\MS64\dll\libusb*.dll $(TargetDir)</Command> <ClCompile Include="..\src\devices\kerui.c" /> <ClCompile Include="..\src\devices\klimalogg.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_tx35.c" /> <ClCompile Include="..\src\devices\lacrosse_ws7000.c" /> diff --git a/vs15/rtl_433.vcxproj.filters b/vs15/rtl_433.vcxproj.filters index 23db32b4..41c5869a 100644 --- a/vs15/rtl_433.vcxproj.filters +++ b/vs15/rtl_433.vcxproj.filters @@ -406,6 +406,9 @@ <ClCompile Include="..\src\devices\lacrosse.c"> <Filter>Source Files\devices</Filter> </ClCompile> + <ClCompile Include="..\src\devices\lacrosse_breezepro.c"> + <Filter>Source Files\devices</Filter> + </ClCompile> <ClCompile Include="..\src\devices\lacrosse_tx141x.c"> <Filter>Source Files\devices</Filter> </ClCompile>