Skip to content

Commit

Permalink
Add support for TFA Stratos 30.3151 and improve Fineoffset WH1050 (#2549
Browse files Browse the repository at this point in the history
)
  • Loading branch information
ProfBoc75 authored Jul 23, 2023
1 parent 406918d commit d616b69
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 50 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
[243] Celsia CZC1 Thermostat
[244] Fine Offset Electronics WS90 weather station
[245]* ThermoPro TX-2C Thermometer
[246] TFA 30.3151 Weather Station
* Disabled by default, use -R n or a conf file to enable
Expand Down
1 change: 1 addition & 0 deletions include/rtl_433_devices.h
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@
DECL(celsia_czc1) \
DECL(fineoffset_ws90) \
DECL(thermopro_tx2c) \
DECL(tfa_303151) \

/* Add new decoders here. */

Expand Down
151 changes: 101 additions & 50 deletions src/devices/fineoffset_wh1050.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/** @file
Fine Offset WH1050 Weather Station.
Fine Offset WH1050 and TFA 30.3151 Weather Station.
2016 Nicola Quiriti ('ovrheat')
Modifications 2016 by Don More
2023 Bruno OCTAU (ProfBoc75) for TFA 30.3151 FSK
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
Expand All @@ -12,9 +13,11 @@
*/

#include "decoder.h"
#define TYPE_OOK 1
#define TYPE_FSK 2

/**
Fine Offset WH1050 Weather Station.
/** @fn in fineoffset_wh1050_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned bitpos, int type)
Fine Offset WH1050 and TFA 30.3151 Weather Station.
This module is a cut-down version of the WH1080 decoder.
The WH1050 sensor unit is like the WH1080 unit except it has no
Expand Down Expand Up @@ -44,35 +47,43 @@ around the minute 59 of the even hours the sensor's TX stops sending weather dat
After around 3-4 minutes of silence it starts to send just time data for some minute, then it starts again with
weather data as usual.
TFA 30.3151 Sensor is FSK version and decodes here. See issue #2538: Preamble is aaaa2dd4 and Temperature is not offset and rain gauge is 0.5 mm by pulse.
To recognize which message is received (weather or time) you can use the 'msg_type' field on json output:
- msg_type 0 = weather data
- msg_type 1 = time data
Weather data - Message layout and example:
AA BC CD DD EE FF GG HH HH II
{80} ff 5f 51 93 48 00 00 12 46 aa
Preamble{8} : 0xFF - OOK Version
or Preamble{40} : 0xAAAAAA2DD4 - FSK Version
Byte Position : 00 01 02 03 04 05 06 07 08
Payload{72} : BC CD DD EE FF GG HH HH II
Sample{72} : 5f 51 93 48 00 00 12 46 aa
- A : 8 bits : Preamble 0xFF
- B : 4 bits : ?? - seems to be 0x5 for whether data, 0x6 for time data
- C : 8 bits : Id, changes when reset (e.g., 0xF5)
- D : 1 bit : msg_type - 0, for whether data
- D : 1 bit : Battery, 0 = ok, 1 = low (e.g, OK)
- D : 10 bits : Temperature in Celsius, offset 400, scaled by 10 (e.g., 0.3 degrees C)
- D : 10 bits : Temperature in Celsius, [offset 400 only for OOK Version], scaled by 10 (e.g., 0.3 degrees C)
- E : 8 bits : Relative humidity, percent (e.g., 72%)
- F : 8 bits : Wind speed average in m/s, scaled by 1/0.34 (e.g., 0 m/s)
- G : 8 bits : Wind speed gust in m/s, scaled by 1/0.34 (e.g., 0 m/s)
- H : 16 bits : Total rainfall in units of 0.3mm, since reset (e.g., 1403.4 mm)
- H : 16 bits : Total rainfall in units of 0.3mm (OOK version) or 0.5mm (FSK version), since reset (e.g., 1403.4 mm)
- I : 8 bits : CRC, poly 0x31, init 0x00 (excluding preamble)
Time data - Message layout and example:
AA BC CD DE FG HI JK LM NO PP
{80} ff 69 0a 96 02 41 23 43 27 df
Preamble{8} : 0xFF - OOK Version
or Preamble{40} : 0xAAAAAA2DD4 - FSK Version
Byte Position : 00 01 02 03 04 05 06 07 08
Payload{72} : BC CD DE FG HI JK LM NO PP
Sample{72} : 69 0a 96 02 41 23 43 27 df
- A : 8 bits : Preamble 0xFF
- B : 4 bits : ?? - seems to be 0x5 for whether data, seems 0x6 for time data
- C : 8 bits : Id, changes when reset (e.g., 0xF5)
- C : 8 bits : Id, changes when reset (e.g., 0x90)
- D : 1 bit : msg_type - 1, for time data
- D : 1 bit : Battery, 0 = ok, 1 = low (e.g, OK)
- D : 4 bits : ??
Expand All @@ -92,38 +103,14 @@ Time data - Message layout and example:
- P : 8 bits : CRC, poly 0x31, init 0x00 (excluding preamble)
*/
static int fineoffset_wh1050_decode(r_device *decoder, bitbuffer_t *bitbuffer)
static int fineoffset_wh1050_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned bitpos, int type)
{
data_t *data;
uint8_t br[9];
float temperature, rain;

if (bitbuffer->num_rows != 1) {
return DECODE_ABORT_EARLY;
}
bitbuffer_extract_bytes(bitbuffer, 0, bitpos, br, 9 * 8);

/* The normal preamble for WH1050 is 8 1s (0xFF) followed by 4 0s
for a total 80 bit message.
(The 4 0s is not confirmed to be preamble but seems to be zero on most devices)
Digitech XC0346 (and possibly other models) only sends 7 1 bits not 8 (0xFE)
for some reason (maybe transmitter module is slow to wake up), for a total
79 bit message.
In both cases, we extract the 72 bits after the preamble.
*/
unsigned bits = bitbuffer->bits_per_row[0];
uint8_t preamble_byte = bitbuffer->bb[0][0];
if (bits == 79 && preamble_byte == 0xfe) {
bitbuffer_extract_bytes(bitbuffer, 0, 7, br, 72);
} else if (bits == 80 && preamble_byte == 0xff) {
bitbuffer_extract_bytes(bitbuffer, 0, 8, br, 72);
} else {
return DECODE_ABORT_LENGTH;
}

// If you calculate the CRC over all 10 bytes including the preamble
// byte (always 0xFF), then CRC_INIT is 0xFF. But we compare the preamble
// byte and then discard it.
if (crc8(br, 9, 0x31, 0x00)) {
return DECODE_FAIL_MIC; // crc mismatch
}
Expand All @@ -134,26 +121,33 @@ static int fineoffset_wh1050_decode(r_device *decoder, bitbuffer_t *bitbuffer)
if (msg_type == 0) {
// GETTING WEATHER SENSORS DATA
int temp_raw = ((br[1] & 0x03) << 8) | br[2];
float temperature = (temp_raw - 400) * 0.1f;
int rain_raw = (br[6] << 8) | br[7];
if (type == TYPE_OOK) {
temperature = (temp_raw - 400) * 0.1f;
rain = rain_raw * 0.3f;
}
else {
temperature = temp_raw * 0.1f;
rain = rain_raw * 0.5f;
}
int humidity = br[3];
float speed = (br[4] * 0.34f) * 3.6f; // m/s -> km/h
float gust = (br[5] * 0.34f) * 3.6f; // m/s -> km/h
int rain_raw = (br[6] << 8) | br[7];
float rain = rain_raw * 0.3f;
int device_id = (br[0] << 4 & 0xf0) | (br[1] >> 4);
int battery_low = br[1] & 0x04;

/* clang-format off */
data = data_make(
"model", "", DATA_STRING, "Fineoffset-WH1050",
"id", "Station ID", DATA_FORMAT, "%04X", DATA_INT, device_id,
"model", "", DATA_COND, type == TYPE_OOK, DATA_STRING, "Fineoffset-WH1050",
"model", "", DATA_COND, type == TYPE_FSK, DATA_STRING, "TFA-303151",
"id", "Station ID", DATA_FORMAT, "%02X", DATA_INT, device_id,
"msg_type", "Msg type", DATA_INT, msg_type,
"battery_ok", "Battery", DATA_INT, !battery_low,
"temperature_C", "Temperature", DATA_FORMAT, "%.01f C", DATA_DOUBLE, temperature,
"humidity", "Humidity", DATA_FORMAT, "%u %%", DATA_INT, humidity,
"wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.02f", DATA_DOUBLE, speed,
"wind_max_km_h", "Wind gust", DATA_FORMAT, "%.02f", DATA_DOUBLE, gust,
"rain_mm", "Total rainfall", DATA_FORMAT, "%.01f", DATA_DOUBLE, rain,
"wind_avg_km_h", "Wind avg speed", DATA_FORMAT, "%.02f km/h", DATA_DOUBLE, speed,
"wind_max_km_h", "Wind gust", DATA_FORMAT, "%.02f km/h ", DATA_DOUBLE, gust,
"rain_mm", "Total rainfall", DATA_FORMAT, "%.01f mm", DATA_DOUBLE, rain,
"mic", "Integrity", DATA_STRING, "CRC",
NULL);
/* clang-format on */
Expand All @@ -175,8 +169,9 @@ static int fineoffset_wh1050_decode(r_device *decoder, bitbuffer_t *bitbuffer)

/* clang-format off */
data = data_make(
"model", "", DATA_STRING, "Fineoffset-WH1050",
"id", "Station ID", DATA_INT, device_id,
"model", "", DATA_COND, type == TYPE_OOK, DATA_STRING, "Fineoffset-WH1050",
"model", "", DATA_COND, type == TYPE_FSK, DATA_STRING, "TFA-303151",
"id", "Station ID", DATA_FORMAT, "%02X", DATA_INT, device_id,
"msg_type", "Msg type", DATA_INT, msg_type,
"battery_ok", "Battery", DATA_INT, !battery_low,
"radio_clock", "Radio Clock", DATA_STRING, clock_str,
Expand All @@ -189,6 +184,52 @@ static int fineoffset_wh1050_decode(r_device *decoder, bitbuffer_t *bitbuffer)
return 1;
}

/**
Fineoffset or TFA OOK/FSK protocol.
@sa fineoffset_wh1050_decode()
*/
static int fineoffset_wh1050_callback(r_device *decoder, bitbuffer_t *bitbuffer)
{
unsigned bitpos = 0;
int events = 0;

if (bitbuffer->num_rows != 1) {
return DECODE_ABORT_EARLY;
}

/* The normal preamble for WH1050 is 8 1s (0xFF) followed by 4 0s
for a total 80 bit message.
(The 4 0s is not confirmed to be preamble but seems to be zero on most devices)
Digitech XC0346 (and possibly other models) only sends 7 1 bits not 8 (0xFE)
for some reason (maybe transmitter module is slow to wake up), for a total
79 bit message.
In both cases, we extract the 72 bits after the preamble.
For FSK version TFA 30.3151 the preamble is aaaaaa2dd4 and message payload is 6 times repeats (gap, preamble, message, gap, ... ) in one row and 754 bits.
gap is 11 bits long, preamble need to be searched into a while loop to get the repeated message
*/

unsigned bits = bitbuffer->bits_per_row[0];
uint8_t preamble_byte = bitbuffer->bb[0][0]; // for OOK
uint8_t const preamble_fsk[] = {0xAA, 0x2D, 0xD4}; // part of preamble and sync word for FSK
if (bits == 79 && preamble_byte == 0xfe) {
fineoffset_wh1050_decode(decoder, bitbuffer, 7, TYPE_OOK);
} else if (bits == 80 && preamble_byte == 0xff) {
fineoffset_wh1050_decode(decoder, bitbuffer, 8, TYPE_OOK);
} else if (bits > 112 && bits < 760) {
while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, preamble_fsk, sizeof(preamble_fsk) * 8)) + 72 <=
bitbuffer->bits_per_row[0]) {
events += fineoffset_wh1050_decode(decoder, bitbuffer, bitpos + sizeof(preamble_fsk) * 8, TYPE_FSK);
bitpos += 123;
}
} else {
return DECODE_ABORT_LENGTH;
}
return events;
}

static char const *const output_fields[] = {
"model",
"id",
Expand All @@ -210,6 +251,16 @@ r_device const fineoffset_wh1050 = {
.short_width = 544,
.long_width = 1524,
.reset_limit = 10520,
.decode_fn = &fineoffset_wh1050_decode,
.decode_fn = &fineoffset_wh1050_callback,
.fields = output_fields,
};

r_device const tfa_303151 = {
.name = "TFA 30.3151 Weather Station",
.modulation = FSK_PULSE_PCM,
.short_width = 60,
.long_width = 60,
.reset_limit = 2500,
.decode_fn = &fineoffset_wh1050_callback,
.fields = output_fields,
};

0 comments on commit d616b69

Please sign in to comment.