Add support for TFA Stratos 30.3151 and improve Fineoffset WH1050 (#2549)
This commit is contained in:
parent
406918d35f
commit
d616b692a8
3 changed files with 103 additions and 50 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -253,6 +253,7 @@
|
|||
DECL(celsia_czc1) \
|
||||
DECL(fineoffset_ws90) \
|
||||
DECL(thermopro_tx2c) \
|
||||
DECL(tfa_303151) \
|
||||
|
||||
/* Add new decoders here. */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 : ??
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue