Skip to content

Commit a25931f

Browse files
authored
Add support for Maverick-XR50 BBQ meat thermometer (#3293)
1 parent c60f574 commit a25931f

File tree

5 files changed

+190
-0
lines changed

5 files changed

+190
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
366366
[278] ThermoPro TX-7B Outdoor Thermometer Hygrometer
367367
[279] Nexus, CRX, Prego sauna temperature sensor
368368
[280] Homelead HG9901 (Geevon, Dr.Meter, Royal Gardineer) soil moisture/temp/light level sensor
369+
[281] Maverick XR-50 BBQ Sensor
369370
370371
* Disabled by default, use -R n or a conf file to enable
371372

conf/rtl_433.example.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ convert si
519519
protocol 278 # ThermoPro TX-7B Outdoor Thermometer Hygrometer
520520
protocol 279 # Nexus, CRX, Prego sauna temperature sensor
521521
protocol 280 # Homelead HG9901 (Geevon, Dr.Meter, Royal Gardineer) soil moisture/temp/light level sensor
522+
protocol 281 # Maverick XR-50 BBQ Sensor
522523

523524
## Flex devices (command line option "-X")
524525

include/rtl_433_devices.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@
288288
DECL(thermopro_tx7b) \
289289
DECL(nexus_sauna) \
290290
DECL(homelead_hg9901) \
291+
DECL(maverick_xr50) \
291292

292293
/* Add new decoders here. */
293294

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ add_library(r_433 STATIC
178178
devices/maverick_et73.c
179179
devices/maverick_et73x.c
180180
devices/maverick_xr30.c
181+
devices/maverick_xr50.c
181182
devices/mebus.c
182183
devices/megacode.c
183184
devices/missil_ml0757.c

src/devices/maverick_xr50.c

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/** @file
2+
Maverick XR-50 BBQ Sensor Europe Version
3+
4+
Copyright (C) 2025 Luca Pinasco
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+
Maverick XR-50 BBQ Sensor.
16+
17+
Examples:
18+
19+
555555555555555a5545ba8100de0008343e9e001234821e000b543e9e0014a0ce4d401555400000
20+
555555555555555a5545ba8100de0008347d1e0008347d1e0008347d1e0008347d02c01555400000
21+
555555555555555a5545ba86811a5c2d5cc13a5e2d5cc1d85c89743e985b89883e80801555400
22+
23+
align preamble sync word:
24+
25+
PP PP PP PP PP SS SS SS SS
26+
.. aa aa aa aa d2 aa 2d d4
27+
28+
Data layout after sync word:
29+
30+
Byte Position 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 TT TT TT TT TT TT
31+
Sample 34 08 d2 e1 6a e6 09 d2 f1 6a e6 0e c2 e4 4b a1 f4 c2 dc 4c 41 f4 04 00 aa aa 00 0
32+
Sample 08 06 f0 00 41 a1 f4 f0 00 91 a4 10 f0 00 5a a1 f4 f0 00 a5 06 72 6a 00 aa aa 00 00 00
33+
Sample 08 06 f0 00 41 a3 e8 f0 00 41 a3 e8 f0 00 41 a3 e8 f0 00 41 a3 e8 16 00 aa aa 00 00 00
34+
Sample 08 06 f0 00 41 a1 f4 f0 00 91 a4 10 f0 00 5a a1 f4 f0 00 a5 06 72 6a 00 aa aa 00 00 00 [no probe]
35+
Layout II II FT TT HH HL LL FT TT HH HL LL FT TT HH HL LL FT TT HH HL LL CC TT TT TT TT TT TT
36+
[Probe 1 ][Probe 2 ][Probe 3 ][Probe 4 ]
37+
38+
- II:{16} Probably ID, 0x0806 or 0x3408 from above samples
39+
- F:{4} Some flags, depends on the presence of probe, if temp below, within or above range
40+
0xF = No probe, in that case, the TTT = 0x000
41+
0xD = Temp below low temp value
42+
0xC = Temp within the range, between low and high temp values set.
43+
0x? all flags not yet guessed
44+
45+
- TTT:{12} Actual probe Temperature in Celsius, offset 500, scale 10
46+
- HHH:{12} High Temperature set, Celsius, offset 500, scale 10
47+
- LLL:{12} Low Temperature set, Celsius, offset 500, scale 10
48+
- CC :{8} CRC 8, poly 0x31, init 0x00, final XOR 0x00, from all previous 22 bytes.
49+
*/
50+
static int maverick_xr50_decode(r_device *decoder, bitbuffer_t *bitbuffer)
51+
{
52+
uint8_t const preamble[] = { 0xd2, 0xaa, 0x2d, 0xd4 };
53+
54+
uint8_t b[23];
55+
56+
if (bitbuffer->num_rows > 1) {
57+
decoder_logf(decoder, 1, __func__, "Too many rows: %d", bitbuffer->num_rows);
58+
return DECODE_FAIL_SANITY;
59+
}
60+
61+
int msg_len = bitbuffer->bits_per_row[0];
62+
int start_pos = bitbuffer_search(bitbuffer,0, 0, preamble, sizeof(preamble) * 8);
63+
64+
if (start_pos >= msg_len ) {
65+
decoder_log(decoder, 3, __func__, "Sync word not found");
66+
return DECODE_ABORT_LENGTH;
67+
}
68+
69+
if ((msg_len - start_pos ) < 184 ) {
70+
decoder_logf(decoder, 1, __func__, "Packet too short: %d bits", msg_len);
71+
return DECODE_ABORT_LENGTH;
72+
}
73+
74+
start_pos += sizeof(preamble) * 8;
75+
76+
// Need 23 bytes, last bit are useless trailing bits
77+
bitbuffer_extract_bytes(bitbuffer, 0, start_pos, b, 23 * 8);
78+
79+
if (crc8(b, 23, 0x31, 0x00)) {
80+
decoder_logf(decoder, 1, __func__, "CRC Error, found: %02x, expected: %02x", b[22], crc8(b, 22, 0x31, 0x00));
81+
return DECODE_FAIL_MIC;
82+
}
83+
84+
decoder_log_bitrow(decoder, 1, __func__, b, 23 * 8, "MSG");
85+
86+
int id = b[0] << 8 | b[1];
87+
88+
int p_1_flags = (b[2] & 0xf0) >> 4;
89+
int p_1_temp_raw = ((b[2] & 0x0f) << 8) | b[3];
90+
int p_1_high_raw = (b[4] << 4) | ((b[5] & 0xf0) >> 4);
91+
int p_1_low_raw = ((b[5] & 0x0f) << 8) | b[6];
92+
93+
int p_2_flags = (b[7] & 0xf0) >> 4;
94+
int p_2_temp_raw = ((b[7] & 0x0f) << 8) | b[8];
95+
int p_2_high_raw = (b[9] << 4) | ((b[10] & 0xf0) >> 4);
96+
int p_2_low_raw = ((b[10] & 0x0f) << 8) | b[11];
97+
98+
int p_3_flags = (b[12] & 0xf0) >> 4;
99+
int p_3_temp_raw = ((b[12] & 0x0f) << 8) | b[13];
100+
int p_3_high_raw = (b[14] << 4) | ((b[15] & 0xf0) >> 4);
101+
int p_3_low_raw = ((b[15] & 0x0f) << 8) | b[16];
102+
103+
int p_4_flags = (b[17] & 0xf0) >> 4;
104+
int p_4_temp_raw = ((b[17] & 0x0f) << 8) | b[18];
105+
int p_4_high_raw = (b[19] << 4) | ((b[20] & 0xf0) >> 4);
106+
int p_4_low_raw = ((b[20] & 0x0f) << 8) | b[21];
107+
108+
float p1_temp = (p_1_temp_raw - 500) * 0.1f;
109+
float p1_set_high = (p_1_high_raw - 500) * 0.1f;
110+
float p1_set_low = (p_1_low_raw - 500) * 0.1f;
111+
112+
float p2_temp = (p_2_temp_raw - 500) * 0.1f;
113+
float p2_set_high = (p_2_high_raw - 500) * 0.1f;
114+
float p2_set_low = (p_2_low_raw - 500) * 0.1f;
115+
116+
float p3_temp = (p_3_temp_raw - 500) * 0.1f;
117+
float p3_set_high = (p_3_high_raw - 500) * 0.1f;
118+
float p3_set_low = (p_3_low_raw - 500) * 0.1f;
119+
120+
float p4_temp = (p_4_temp_raw - 500) * 0.1f;
121+
float p4_set_high = (p_4_high_raw - 500) * 0.1f;
122+
float p4_set_low = (p_4_low_raw - 500) * 0.1f;
123+
124+
/* clang-format off */
125+
data_t *data = data_make(
126+
"model", "", DATA_STRING, "Maverick-XR50",
127+
"id", "", DATA_FORMAT, "%04x", DATA_INT, id,
128+
"probe_1_flags", "Flags Probe 1", DATA_FORMAT, "%1x", DATA_INT, p_1_flags,
129+
"temperature_1_C", "Temperature 1", DATA_COND, p_1_temp_raw != 0, DATA_FORMAT, "%.1f C", DATA_DOUBLE, p1_temp,
130+
"setpoint_high_1_C", "Setpoint 1 high", DATA_FORMAT, "%.1f C", DATA_DOUBLE, p1_set_high,
131+
"setpoint_low_1_C", "Setpoint 1 low", DATA_FORMAT, "%.1f C", DATA_DOUBLE, p1_set_low,
132+
"probe_2_flags", "Flags Probe 2", DATA_FORMAT, "%1x", DATA_INT, p_2_flags,
133+
"temperature_2_C", "Temperature 2", DATA_COND, p_2_temp_raw != 0, DATA_FORMAT, "%.1f C", DATA_DOUBLE, p2_temp,
134+
"setpoint_high_2_C", "Setpoint 2 high", DATA_FORMAT, "%.1f C", DATA_DOUBLE, p2_set_high,
135+
"setpoint_low_2_C", "Setpoint 2 low", DATA_FORMAT, "%.1f C", DATA_DOUBLE, p2_set_low,
136+
"probe_3_flags", "Flags Probe 3", DATA_FORMAT, "%1x", DATA_INT, p_3_flags,
137+
"temperature_3_C", "Temperature 3", DATA_COND, p_3_temp_raw != 0, DATA_FORMAT, "%.1f C", DATA_DOUBLE, p3_temp,
138+
"setpoint_high_3_C", "Setpoint 3 high", DATA_FORMAT, "%.1f C", DATA_DOUBLE, p3_set_high,
139+
"setpoint_low_3_C", "Setpoint 3 low", DATA_FORMAT, "%.1f C", DATA_DOUBLE, p3_set_low,
140+
"probe_4_flags", "Flags Probe 4", DATA_FORMAT, "%1x", DATA_INT, p_4_flags,
141+
"temperature_4_C", "Temperature 4", DATA_COND, p_4_temp_raw != 0, DATA_FORMAT, "%.1f C", DATA_DOUBLE, p4_temp,
142+
"setpoint_high_4_C", "Setpoint 4 high", DATA_FORMAT, "%.1f C", DATA_DOUBLE, p4_set_high,
143+
"setpoint_low_4_C", "Setpoint 4 low", DATA_FORMAT, "%.1f C", DATA_DOUBLE, p4_set_low,
144+
"mic", "Integrity", DATA_STRING, "CRC",
145+
NULL
146+
);
147+
148+
149+
/* clang-format on */
150+
151+
decoder_output_data(decoder, data);
152+
return 1;
153+
}
154+
155+
static char const *const output_fields[] = {
156+
"model",
157+
"id",
158+
"probe_1_flags",
159+
"temperature_1_C",
160+
"setpoint_high_1_C",
161+
"setpoint_low_1_C",
162+
"probe_2_flags",
163+
"temperature_2_C",
164+
"setpoint_high_2_C",
165+
"setpoint_low_2_C",
166+
"probe_3_flags",
167+
"temperature_3_C",
168+
"setpoint_high_3_C",
169+
"setpoint_low_3_C",
170+
"probe_4_flags",
171+
"temperature_4_C",
172+
"setpoint_high_4_C",
173+
"setpoint_low_4_C",
174+
"mic",
175+
NULL,
176+
};
177+
178+
r_device const maverick_xr50 = {
179+
.name = "Maverick XR-50 BBQ Sensor",
180+
.modulation = FSK_PULSE_PCM,
181+
.short_width = 107,
182+
.long_width = 107,
183+
.reset_limit = 2200,
184+
.decode_fn = &maverick_xr50_decode,
185+
.fields = output_fields,
186+
};

0 commit comments

Comments
 (0)