Skip to content

Commit ffdf657

Browse files
committed
Add support for ThermoPro-TP211B temperature sensor
1 parent 05438e4 commit ffdf657

File tree

6 files changed

+117
-2
lines changed

6 files changed

+117
-2
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
377377
[289] Voltcraft EnergyCount 3000 (ec3k)
378378
[290] Orion Endpoint from Badger Meter, GIF2020OCECNA, water meter, hopping from 904.4 Mhz to 924.6Mhz (-s 1600k)
379379
[291] Geevon TX19-1 outdoor sensor
380+
[292] ThermoPro TP211B Thermometer
380381
381382
* Disabled by default, use -R n or a conf file to enable
382383
@@ -386,7 +387,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
386387
[-d <RTL-SDR USB device index>] (default: 0)
387388
[-d :<RTL-SDR USB device serial (can be set with rtl_eeprom -s)>]
388389
To set gain for RTL-SDR use -g <gain> to set an overall gain in dB.
389-
SoapySDR device driver is available.
390+
SoapySDR device driver is not available.
390391
[-d ""] Open default SoapySDR device
391392
[-d driver=rtlsdr] Open e.g. specific SoapySDR device
392393
To set gain for SoapySDR use -g ELEM=val,ELEM=val,... e.g. -g LNA=20,TIA=8,PGA=2 (for LimeSDR).

conf/rtl_433.example.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ convert si
530530
protocol 289 # Voltcraft EnergyCount 3000 (ec3k)
531531
protocol 290 # Orion Endpoint from Badger Meter, GIF2020OCECNA, water meter, hopping from 904.4 Mhz to 924.6Mhz (-s 1600k)
532532
protocol 291 # Geevon TX19-1 outdoor sensor
533+
protocol 292 # ThermoPro TP211B Thermometer
533534

534535
## Flex devices (command line option "-X")
535536

include/rtl_433_devices.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@
299299
DECL(ec3k) \
300300
DECL(orion_endpoint_2020) \
301301
DECL(geevon_tx19) \
302+
DECL(thermopro_tp211b) \
302303

303304
/* Add new decoders here. */
304305

man/man1/rtl_433.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ RTL\-SDR device driver is available.
168168
To set gain for RTL\-SDR use \-g <gain> to set an overall gain in dB.
169169
.RE
170170
.RS
171-
SoapySDR device driver is available.
171+
SoapySDR device driver is not available.
172172
.RE
173173
.TP
174174
[ \fB\-d\fI ""\fP ]

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ add_library(r_433 STATIC
253253
devices/tfa_twin_plus_30.3049.c
254254
devices/thermopro_tp11.c
255255
devices/thermopro_tp12.c
256+
devices/thermopro_tp211b.c
256257
devices/thermopro_tp28b.c
257258
devices/thermopro_tp82xb.c
258259
devices/thermopro_tx2.c

src/devices/thermopro_tp211b.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/** @file
2+
ThermoPro TP211B Thermometer.
3+
4+
Copyright (C) 2026, Ali Rahimi.
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation; either version 2 of the License, or
9+
(at your option) any later version.
10+
*/
11+
12+
#include "decoder.h"
13+
14+
/**
15+
ThermoPro TP211B 915 MHz FSK temperature sensor.
16+
17+
Based on [this issue](https://github.com/merbanan/rtl_433/issues/3435), and thanks to the
18+
analysis conducted there.
19+
20+
Flex decoder:
21+
22+
rtl_433 -f 915M -X "n=tp211b,m=FSK_PCM,s=105,l=105,r=1500,preamble=552dd4"
23+
24+
Data layout after preamble:
25+
26+
Byte Position 0 1 2 3 4 5 6 7
27+
Sample 01 1e d6 03 6c aa 14 ff
28+
Sample 01 1e d6 02 fa aa c4 1e
29+
II II II fT TT aa CC CC
30+
31+
- III: {24} Sensor ID
32+
- f: {4} Flags or unused, always 0
33+
- TTT: {12} Temperature, raw value, °C = (raw - 500) / 10
34+
- aa: {8} Fixed value 0xAA
35+
- CC: {16} Unknown trailing bytes, possibly checksum
36+
- Followed by trailing d2 d2 d2 d2 d2 00 00 (not used).
37+
*/
38+
39+
static int thermopro_tp211b_decode(r_device *decoder, bitbuffer_t *bitbuffer)
40+
{
41+
uint8_t const preamble_pattern[] = {0x55, 0x2d, 0xd4};
42+
43+
uint8_t b[8];
44+
45+
if (bitbuffer->num_rows > 1) {
46+
decoder_logf(decoder, 1, __func__, "Too many rows: %d", bitbuffer->num_rows);
47+
return DECODE_FAIL_SANITY;
48+
}
49+
const int msg_len = bitbuffer->bits_per_row[0];
50+
51+
int offset = bitbuffer_search(bitbuffer, 0, 0, preamble_pattern, sizeof(preamble_pattern) * 8);
52+
53+
if (offset >= msg_len) {
54+
decoder_log(decoder, 1, __func__, "Sync word not found");
55+
return DECODE_ABORT_EARLY;
56+
}
57+
58+
if ((msg_len - offset) < 64) {
59+
decoder_logf(decoder, 1, __func__, "Packet too short: %d bits", msg_len);
60+
return DECODE_ABORT_LENGTH;
61+
}
62+
63+
offset += sizeof(preamble_pattern) * 8;
64+
bitbuffer_extract_bytes(bitbuffer, 0, offset, b, 8 * 8);
65+
66+
// Sanity check: byte 5 should be fixed 0xAA
67+
if (b[5] != 0xaa) {
68+
decoder_log(decoder, 1, __func__, "Fixed byte mismatch (expected 0xAA at byte 5)");
69+
return DECODE_FAIL_SANITY;
70+
}
71+
72+
if ((!b[0] && !b[1] && !b[2] && !b[3] && !b[4])
73+
|| (b[0] == 0xff && b[1] == 0xff && b[2] == 0xff && b[3] == 0xff && b[4] == 0xff)) {
74+
decoder_log(decoder, 2, __func__, "DECODE_FAIL_SANITY data all 0x00 or 0xFF");
75+
return DECODE_FAIL_SANITY;
76+
}
77+
78+
decoder_log_bitrow(decoder, 2, __func__, b, 64, "MSG");
79+
80+
int id = (b[0] << 16) | (b[1] << 8) | b[2];
81+
int temp_raw = ((b[3] & 0x0f) << 8) | b[4];
82+
float temp_c = (temp_raw - 500) * 0.1f;
83+
84+
/* clang-format off */
85+
data_t *data = data_make(
86+
"model", "", DATA_STRING, "ThermoPro-TP211B",
87+
"id", "Id", DATA_FORMAT, "%06x", DATA_INT, id,
88+
"temperature_C", "Temperature", DATA_FORMAT, "%.1f C", DATA_DOUBLE, (double)temp_c,
89+
NULL);
90+
/* clang-format on */
91+
92+
decoder_output_data(decoder, data);
93+
return 1;
94+
}
95+
96+
static char const *const output_fields[] = {
97+
"model",
98+
"id",
99+
"temperature_C",
100+
NULL,
101+
};
102+
103+
r_device const thermopro_tp211b = {
104+
.name = "ThermoPro TP211B Thermometer",
105+
.modulation = FSK_PULSE_PCM,
106+
.short_width = 105,
107+
.long_width = 105,
108+
.reset_limit = 1500,
109+
.decode_fn = &thermopro_tp211b_decode,
110+
.fields = output_fields,
111+
};

0 commit comments

Comments
 (0)