Clean up Archos-TBH code style

This commit is contained in:
Christian W. Zuckschwerdt 2020-01-09 13:19:24 +01:00
parent b5519f028f
commit 8ed6eaf35c
10 changed files with 261 additions and 277 deletions

View file

@ -219,6 +219,7 @@ Read the Test Data section at the bottom.
[141] Auriol HG02832, HG05124A-DCF, Rubicson 48957 temperature/humidity sensor
[142] Fine Offset Electronics/ECOWITT WH51 Soil Moisture Sensor
[143] Holman Industries iWeather WS5029 weather station (older PWM)
[144] TBH weather sensor
* Disabled by default, use -R n or -G

View file

@ -330,6 +330,7 @@ stop_after_successful_events false
protocol 141 # Auriol HG02832, HG05124A-DCF, Rubicson 48957 temperature/humidity sensor
protocol 142 # Fine Offset Electronics/ECOWITT WH51 Soil Moisture Sensor
protocol 143 # Holman Industries iWeather WS5029 weather station (older PWM)
protocol 144 # TBH weather sensor
## Flex devices (command line option "-X")

View file

@ -151,7 +151,7 @@
DECL(auriol_hg02832) \
DECL(fineoffset_WH51) \
DECL(holman_ws5029pwm) \
DECL(tbh) \
DECL(archos_tbh) \
/* Add new decoders here. */

View file

@ -556,6 +556,9 @@ Fine Offset Electronics/ECOWITT WH51 Soil Moisture Sensor
.TP
[ \fB143\fI\fP ]
Holman Industries iWeather WS5029 weather station (older PWM)
.TP
[ \fB144\fI\fP ]
TBH weather sensor
* Disabled by default, use \-R n or \-G
.SS "Input device selection"

View file

@ -33,6 +33,7 @@ add_executable(rtl_433
devices/ambient_weather.c
devices/ambientweather_tx8300.c
devices/ambientweather_wh31e.c
devices/archos_tbh.c
devices/auriol_hg02832.c
devices/blyss.c
devices/brennenstuhl_rcs_2044.c
@ -126,7 +127,6 @@ add_executable(rtl_433
devices/solight_te44.c
devices/springfield.c
devices/steelmate.c
devices/tbh.c
devices/tfa_30_3196.c
devices/tfa_pool_thermometer.c
devices/tfa_twin_plus_30.3049.c

View file

@ -34,6 +34,7 @@ rtl_433_SOURCES = abuf.c \
devices/ambient_weather.c \
devices/ambientweather_tx8300.c \
devices/ambientweather_wh31e.c \
devices/archos_tbh.c \
devices/auriol_hg02832.c \
devices/blyss.c \
devices/brennenstuhl_rcs_2044.c \

249
src/devices/archos_tbh.c Normal file
View file

@ -0,0 +1,249 @@
/** @file
Decoder for TBH Archos devices.
Copyright (c) 2019 duc996 <duc_996@gmx.net>
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.
*/
/**
Decoder for devices from the TBH project (https://www.projet-tbh.fr)
- Modulation: FSK PCM
- Frequency: 433.93MHz +-10kHz
- 212 us symbol/bit time
There exist several device types (power, meteo, gaz,...)
Payload format:
- Synchro {32} 0xaaaaaaaa
- Preamble {32} 0xd391d391
- Length {8}
- Payload {n}
- Checksum {16} CRC16 poly=0x8005 init=0xffff
To get raw data:
./rtl_433 -f 433901000 -X n=tbh,m=FSK_PCM,s=212,l=212,r=3000
The application data is obfuscated by doing data[n] xor data[n-1] xor info[n%16].
Payload foramt:
- Device id {32}
- Frame type {8}
- Frame Data {x}
Frame types:
- Raw data 1
- Weather 2
- Battery level 3
- Battery low 4
Weather frame format:
- Type {8} 02
- Temperature {16} unsigned in 0.1 Celsius steps
- Humidity {16} unsigned rel%
Raw data frame (power index):
- Version {8}
- Index {24}
- Timestamp {34}
- MaxPower {16}
- some additinal data ???
- CRC8 poly=0x7 the crc includes a length byte at the beginning
*/
#include "decoder.h"
static int archos_tbh_decode(r_device *decoder, bitbuffer_t *bitbuffer)
{
uint8_t const preamble[] = {
/*0xaa, 0xaa, */ 0xaa, 0xaa, // preamble
0xd3, 0x91, 0xd3, 0x91 // sync word
};
data_t *data;
if (bitbuffer->num_rows != 1) {
return DECODE_ABORT_EARLY;
}
int row = 0;
// Validate message and reject it as fast as possible : check for preamble
unsigned start_pos = bitbuffer_search(bitbuffer, row, 0, preamble, sizeof (preamble) * 8);
if (start_pos == bitbuffer->bits_per_row[row]) {
return DECODE_ABORT_EARLY; // no preamble detected
}
// check min length
if (bitbuffer->bits_per_row[row] < 12 * 8) { //sync(4) + preamble(4) + len(1) + data(1) + crc(2)
return DECODE_ABORT_LENGTH;
}
uint8_t len;
bitbuffer_extract_bytes(bitbuffer, row, start_pos + sizeof (preamble) * 8, &len, 8);
if (len > 60) {
if (decoder->verbose)
fprintf(stderr, "%s: packet to large (%d bytes), drop it\n", __func__, len);
return DECODE_ABORT_LENGTH;
}
uint8_t frame[62] = {0}; //TODO check max size, I have no idea, arbitrary limit of 60 bytes + 2 bytes crc
frame[0] = len;
// Get frame (len don't include the length byte and the crc16 bytes)
bitbuffer_extract_bytes(bitbuffer, row,
start_pos + (sizeof (preamble) + 1) * 8,
&frame[1], (len + 2) * 8);
if (decoder->verbose > 1) {
bitrow_printf(frame, (len + 1) * 8, "%s: frame data: ", __func__);
}
uint16_t crc = crc16(frame, len + 1, 0x8005, 0xffff);
if ((frame[len + 1] << 8 | frame[len + 2]) != crc) {
if (decoder->verbose) {
fprintf(stderr, "%s: CRC invalid %04x != %04x\n", __func__,
frame[len + 1] << 8 | frame[len + 2], crc);
}
return DECODE_FAIL_MIC;
}
uint8_t const info[] = {
0x19, 0xF8, 0x28, 0x30, 0x6d, 0x0c, 0x94, 0x54,
0x22, 0xf2, 0x37, 0xc9, 0x66, 0xa3, 0x97, 0x57
};
uint8_t payload[62] = {0};
payload[0] = frame[1] ^ info[0];
for (int i = 1; i < len; ++i) {
payload[i] = frame[i] ^ frame[i + 1] ^ info[i % sizeof (info)];
}
if (decoder->verbose > 1) {
bitrow_printf(payload, len * 8, "%s: frame data: ", __func__);
}
uint8_t type = payload[4];
uint32_t id = payload[0] | payload[1] << 8 | payload[2] << 16 | (uint32_t)(payload[3]) << 24;
if (type == 1) {
// raw data
if (decoder->verbose)
fprintf(stderr, "%s: raw data from ID: %08x\n", __func__, id);
payload[4] = len - 4; //write len for crc (len - 4b ID)
if (decoder->verbose > 1) {
bitrow_printf(&payload[4], (len - 4) * 8, "%s: data: ", __func__);
}
uint8_t c = crc8(&payload[4], len - 5, 0x07, 0x00);
if (c != payload[len - 1]) {
fprintf(stderr, "%s: crc error\n", __func__);
return DECODE_FAIL_MIC;
}
uint32_t idx = payload[6] << 16 | payload[7] << 8 | payload[8];
uint32_t ts = payload[9] << 16 | payload[10] << 8 | payload[11];
uint32_t maxPower = payload[12] << 8 | payload[13];
if (decoder->verbose > 1)
fprintf(stderr, "%s: index: %d, timestamp: %d, maxPower: %d\n", __func__,
idx, ts, maxPower);
/* clang-format off */
data = data_make(
"model", "", DATA_STRING, "Archos-TBH",
"id", "Station ID", DATA_FORMAT, "%08X", DATA_INT, id,
"power_idx", "Power index", DATA_FORMAT, "%d", DATA_INT, idx,
"power_max", "Power max", DATA_FORMAT, "%d", DATA_INT, maxPower,
"timestamp", "Timestamp", DATA_FORMAT, "%d s", DATA_INT, ts / 8,
"mic", "Integrity", DATA_STRING, "CRC",
NULL);
/* clang-format on */
decoder_output_data(decoder, data);
return 1;
}
else if (type == 2) {
// temp and humidity
int temp_raw = (payload[6] << 8 | payload[5]) - 2732;
float temp_c = temp_raw * 0.1;
int humidity = payload[7];
/* clang-format off */
data = data_make(
"model", "", DATA_STRING, "Archos-TBH",
"id", "Station ID", DATA_FORMAT, "%08X", DATA_INT, id,
"temperature_C", "Temperature", DATA_FORMAT, "%.01f °C", DATA_DOUBLE, temp_c,
"humidity", "Humidity", DATA_FORMAT, "%d %%", DATA_INT, humidity,
"mic", "Integrity", DATA_STRING, "CRC",
NULL);
/* clang-format on */
decoder_output_data(decoder, data);
return 1;
}
else if (type == 3) {
// bat level, 0-100%
int batt_level = payload[5];
/* clang-format off */
data = data_make(
"model", "", DATA_STRING, "Archos-TBH",
"id", "Station ID", DATA_FORMAT, "%08X", DATA_INT, id,
"battery_ok", "Battery level", DATA_FORMAT, "%0.2f", DATA_DOUBLE, batt_level * 0.01,
"mic", "Integrity", DATA_STRING, "CRC",
NULL);
/* clang-format on */
decoder_output_data(decoder, data);
return 1;
}
else if (type == 4) {
// battery low
/* clang-format off */
data = data_make(
"model", "", DATA_STRING, "Archos-TBH",
"id", "Station ID", DATA_FORMAT, "%08X", DATA_INT, id,
"battery_ok", "Battery level", DATA_INT, 0, // fixed
"mic", "Integrity", DATA_STRING, "CRC",
NULL);
/* clang-format on */
decoder_output_data(decoder, data);
return 1;
}
else {
if (decoder->verbose)
fprintf(stderr, "%s: unknown frame received\n", __func__);
return 0;
}
}
static char *output_fields[] = {
"model",
"id",
"battery_ok",
"temperature_C",
"humidity",
"power_idx",
"power_max",
"timestamp",
"mic",
NULL,
};
r_device archos_tbh = {
.name = "TBH weather sensor",
.modulation = FSK_PULSE_PCM,
.short_width = 212,
.long_width = 212,
.reset_limit = 3000,
.decode_fn = &archos_tbh_decode,
.disabled = 0,
.fields = output_fields,
};

View file

@ -1,275 +0,0 @@
/** @file
Decoder for TBH Archos devices.
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.
*/
/**
Decoder for devices from the TBH project (https://www.projet-tbh.fr)
- Modulation: FSK PCM
- Frequency: 433.93MHz +-10kHz
- 212 us symbol/bit time
There exist several device types (power, meteo, gaz,...)
Payload format:
- Synchro {32} 0xaaaaaaaa
- Preamble {32} 0xd391d391
- Length {8}
- Payload {n}
- Checksum {16} CRC16 poly=0x8005 init=0xffff
To get raw data:
./rtl_433 -f 433901000 -X n=tbh,m=FSK_PCM,s=212,l=212,r=217088
The application data is obfuscated by doing data[n] xor data[n-1] xor info[n%16].
Payload foramt:
- Device id {32}
- Frame type {8}
- Frame Data {x}
Frame types:
- Raw data 1
- Weather 2
- Battery level 3
- Battery low 4
Weather frame format:
- Type {8} 02
- Temperature {16} unsigned in 0.1 Kelvin steps
- Humidity {16} unsigned %
Raw data frame (power index):
- Version {8}
- Index {24}
- Timestamp {34}
- MaxPower {16}
- some additinal data ???
- CRC8 poly=0x7 the crc includes a length byte at the beginning
*/
#include "decoder.h"
static int
tbh_decode (r_device * decoder, bitbuffer_t * bitbuffer)
{
uint8_t *b;
data_t *data;
int row;
static uint8_t const preamble[] = {
/*0xaa, 0xaa, */ 0xaa, 0xaa, // preamble
0xd3, 0X91, 0xd3, 0x91 // sync word
};
if (!decoder || !bitbuffer) {
return DECODE_ABORT_EARLY;
}
if (bitbuffer->num_rows != 1) {
return DECODE_ABORT_EARLY;
}
for (row = 0; row < bitbuffer->num_rows; ++row) {
// Validate message and reject it as fast as possible : check for preamble
unsigned start_pos =
bitbuffer_search (bitbuffer, row, 0, preamble, sizeof (preamble) * 8);
if (decoder->verbose)
fprintf (stderr, "start pos: %d\n", start_pos);
if (start_pos == bitbuffer->bits_per_row[row])
continue; // no preamble detected, move to the next row
// min length
if (bitbuffer->bits_per_row[row] < 12 * 8) { //sync(4) + preamble(4) + len(1) + data(1) + crc(2)
return DECODE_ABORT_EARLY;
}
if (decoder->verbose)
fprintf (stderr, "sync and preamble found\n");
uint8_t len;
bitbuffer_extract_bytes (bitbuffer, row,
start_pos + sizeof (preamble) * 8, &len, 8);
if (decoder->verbose)
fprintf (stderr, "got packet with %d bytes\n", len);
if (len > 60) {
if (decoder->verbose)
fprintf (stderr, "packet to large (%d bytes), drop it\n", len);
continue;
}
uint8_t frame[62] = { 0 }; //TODO check max size, I have no idea, arbitrary limit of 60 bytes + 2 bytes crc
frame[0] = len;
// Get frame (len don't include the length byte and the crc16 bytes)
bitbuffer_extract_bytes (bitbuffer, row,
start_pos + (sizeof (preamble) + 1) * 8,
&frame[1], (len + 2) * 8);
if (decoder->verbose) {
fprintf (stderr, "frame data: ");
for (int i = 0; i < len + 1; i++) {
fprintf (stderr, " %02x", frame[i]);
}
fprintf (stderr, "\n");
}
uint16_t crc;
crc = crc16 (frame, len + 1, 0x8005, 0xffff);
if (decoder->verbose)
fprintf (stderr, "got CRC %04x\n", crc);
if ((frame[len + 1] << 8 | frame[len + 2]) != crc) {
if (decoder->verbose) {
fprintf (stderr, "CRC invalid %04x != %04x\n",
frame[len + 1] << 8 | frame[len + 2], crc);
}
continue;
}
else {
if (decoder->verbose)
fprintf (stderr, "CRC OK\n");
}
static uint8_t const info[] = {
0x19, 0xF8, 0x28, 0x30, 0x6d, 0x0c, 0x94, 0x54,
0x22, 0xf2, 0x37, 0xc9, 0x66, 0xa3, 0x97, 0x57
};
uint8_t payload[62] = { 0 };
payload[0] = frame[1] ^ info[0];
for (int i = 1; i < len; i++) {
payload[i] = frame[i] ^ frame[i + 1] ^ info[i % sizeof (info)];
}
if (decoder->verbose) {
fprintf (stderr, "payload: ");
for (int i = 0; i < len; i++) {
fprintf (stderr, " %02x", payload[i]);
}
fprintf (stderr, "\n");
}
uint8_t type = payload[4];
uint32_t id =
payload[0] | payload[1] << 8 | payload[2] << 16 | payload[3] << 24;
switch (type) {
case 1:
// raw data
if (decoder->verbose)
fprintf (stderr, "raw data from ID: %08x\n", id);
payload[4] = len - 4; //write len for crc (len - 4b ID)
if (decoder->verbose) {
fprintf (stderr, "data: ");
for (int i = 4; i < len; i++) {
fprintf (stderr, " %02x", payload[i]);
}
fprintf (stderr, "\n");
}
uint8_t c = crc8(&payload[4], len-5, 0x07, 0x00);
if (c != payload[len-1]) {
fprintf (stderr, "crc error\n");
continue;
}
uint32_t idx = payload[6] << 16 | payload[7] << 8 | payload[8];
uint32_t ts = payload[9] << 16 | payload[10] << 8 | payload[11];
uint32_t maxPower = payload[12] << 8 | payload[13];
if (decoder->verbose)
fprintf (stderr, "index: %d, timestamp: %d, maxPower: %d\n", idx, ts, maxPower);
data = data_make ("model", "", DATA_STRING, "TBH",
"id", "StationID", DATA_FORMAT, "%08X", DATA_INT, id,
"power_idx", "Power index", DATA_FORMAT, "%d", DATA_INT, idx,
"power_max", "Power max", DATA_FORMAT, "%d", DATA_INT, maxPower,
"timestamp", "Timestamp", DATA_FORMAT, "%d s", DATA_INT, ts/8,
NULL);
decoder_output_data (decoder, data);
break;
case 2:
{
// temp and hym
float temp = (payload[6] << 8 | payload[5]) - 2732;
temp /= 10;
int hym = payload[7];
if (decoder->verbose)
fprintf (stderr, "ID: %08x info: %.1f°C %d%%\n", id, temp, hym);
data = data_make ("model", "", DATA_STRING, "TBH weather",
"id", "StationID", DATA_FORMAT, "%08X", DATA_INT,
id, "temperature_C", "Temperature", DATA_FORMAT,
"%.01f °C", DATA_DOUBLE, temp,
"humidity", "Humidity", DATA_FORMAT, "%d %%", DATA_INT,
hym, NULL);
decoder_output_data (decoder, data);
}
break;
case 3:
if (decoder->verbose)
fprintf (stderr, "bat level received\n");
// bat level, 0-100%
data = data_make ("model", "", DATA_STRING, "TBH",
"id", "StationID", DATA_FORMAT, "%08X", DATA_INT, id,
"battery_level", "Battery level", DATA_FORMAT, "%d %%",
DATA_INT, payload[5], NULL);
decoder_output_data (decoder, data);
break;
case 4:
if (decoder->verbose)
fprintf (stderr, "bat low received\n");
data = data_make ("model", "", DATA_STRING, "TBH",
"id", "StationID", DATA_FORMAT, "%08X", DATA_INT, id,
"battery", "", DATA_STRING, "LOW", NULL);
decoder_output_data (decoder, data);
// bat low
break;
default:
if (decoder->verbose)
fprintf (stderr, "unknown frame received\n");
break;
}
}
return 1;
}
static char *output_fields[] = {
"model",
"id",
"temperature_C",
"humidity",
"battery",
"battery_level",
"power_idx",
"power_max",
"timestamp",
NULL,
};
r_device tbh = {
.name = "TBH weather sensor",
.modulation = FSK_PULSE_PCM,
.short_width = 212,
.long_width = 212,
.reset_limit = 217088,
.decode_fn = &tbh_decode,
.disabled = 0,
.fields = output_fields,
};

View file

@ -149,6 +149,7 @@
<ClCompile Include="..\src\devices\ambient_weather.c" />
<ClCompile Include="..\src\devices\ambientweather_tx8300.c" />
<ClCompile Include="..\src\devices\ambientweather_wh31e.c" />
<ClCompile Include="..\src\devices\archos_tbh.c" />
<ClCompile Include="..\src\devices\auriol_hg02832.c" />
<ClCompile Include="..\src\devices\blyss.c" />
<ClCompile Include="..\src\devices\brennenstuhl_rcs_2044.c" />

View file

@ -208,6 +208,9 @@
<ClCompile Include="..\src\devices\ambientweather_wh31e.c">
<Filter>Source Files\devices</Filter>
</ClCompile>
<ClCompile Include="..\src\devices\archos_tbh.c">
<Filter>Source Files\devices</Filter>
</ClCompile>
<ClCompile Include="..\src\devices\auriol_hg02832.c">
<Filter>Source Files\devices</Filter>
</ClCompile>