Skip to content

Commit bf3e9b7

Browse files
authored
Add support for TPMS Shenzhen EGQ Q85 and Improve Abarth 124 Spider (#3514)
1 parent 980529c commit bf3e9b7

3 files changed

Lines changed: 116 additions & 45 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
241241
[153] Cotech 36-7959, SwitchDocLabs FT020T wireless weather station with USB
242242
[154] Standard Consumption Message Plus (SCMplus)
243243
[155] Fine Offset Electronics WH1080/WH3080 Weather Station (FSK)
244-
[156] Abarth 124 Spider TPMS
244+
[156] Abarth 124 Spider and Shenzhen EGQ Q85 TPMS
245245
[157] Missil ML0757 weather station
246246
[158] Sharp SPC775 weather station
247247
[159] Insteon

conf/rtl_433.example.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ convert si
394394
protocol 153 # Cotech 36-7959, SwitchDocLabs FT020T wireless weather station with USB
395395
protocol 154 # Standard Consumption Message Plus (SCMplus)
396396
protocol 155 # Fine Offset Electronics WH1080/WH3080 Weather Station (FSK)
397-
protocol 156 # Abarth 124 Spider TPMS
397+
protocol 156 # Abarth 124 Spider and Shenzhen EGQ Q85 TPMS
398398
protocol 157 # Missil ML0757 weather station
399399
protocol 158 # Sharp SPC775 weather station
400400
protocol 159 # Insteon

src/devices/tpms_abarth124.c

Lines changed: 114 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,138 @@
1-
/** @file
2-
VDO Type TG1C FSK 9 byte Manchester encoded checksummed TPMS data.
1+
/** @file
2+
VDO TPMS Type TG1C and Q85.
3+
4+
Copyright (C) TTigges for (VDO Type TG1C via) Abarth 124 Spider TPMS decoded
5+
Protocol similar (and based on) Jansite Solar TPMS by Andreas Spiess and Christian W. Zuckschwerdt
6+
Copyright (C) 2024 Bruno OCTAU (ProfBoc75) Add Shenzhen EGQ Q85 support
37
48
This program is free software; you can redistribute it and/or modify
59
it under the terms of the GNU General Public License as published by
610
the Free Software Foundation; either version 2 of the License, or
711
(at your option) any later version.
812
*/
913

10-
/**
11-
(VDO Type TG1C via) Abarth 124 Spider TPMS decoded by TTigges
12-
Protocol similar (and based on) Jansite Solar TPMS by Andreas Spiess and Christian W. Zuckschwerdt
14+
#include "decoder.h"
1315

14-
OEM Sensor is said to be a VDO Type TG1C, available in different cars,
15-
e.g.: Abarth 124 Spider, some Fiat 124 Spider, some Mazda MX-5 ND (and NC?) and probably some other Mazdas.
16-
Mazda reference/part no.: BHB637140A
17-
VDO reference/part no.: A2C1132410180
16+
/**
17+
VDO TPMS Type TG1C and Q85.
18+
19+
VDO Type TG1C:
20+
- FSK 9 byte Manchester encoded, checksummed for 8 bytes TPMS data.
21+
22+
Q85 from Shenzhen EGQ Cloud technology CO,LTD:
23+
- FSK 12 byte Manchester encoded, checksummed for 8 bytes TPMS data + CRC/16 CCITT-FASLE for 10 bytes DATA.
24+
25+
TG1C (Abarth-124Spider):
26+
- OEM Sensor is said to be a VDO Type TG1C, available in different cars,
27+
- e.g.: Abarth 124 Spider, some Fiat 124 Spider, some Mazda MX-5 ND (and NC?) and probably some other Mazdas.
28+
- Mazda reference/part no.: BHB637140A
29+
- VDO reference/part no.: A2C1132410180
30+
- Compatible with aftermarket sensors, e.g. Aligator sens.it RS3
31+
- Working Temperature: -50°C to 125°C
32+
- Working Frequency: 433.92MHz+-38KHz
33+
- Tire monitoring range value: 0kPa-350kPa+-7kPa (to be checked, VDO says 450/900kPa)
34+
35+
Q85 (Shenzhen-EGQ-Q85):
36+
- Analysis asked by \@js-3972 in issue #2556
37+
- TPMS from Amazon here: https://www.amazon.com/dp/B0BK8SHDRZ?psc=1&ref=ppx_yo2ov_dt_b_product_details
38+
- Air pressure setting range: 0.1~6.0BA / 1.45~87psi
39+
- Temperature setting range: 60°C~110°C / 140ºF~230ºF
40+
- Working temperature: -20°C~80°C / -68ºF~176ºF
41+
- Storage temperature: -30°C~85°C / 86ºF~185ºF
42+
- Power-on voltage: DC 5V
43+
- Frequency: 433.92MHz±20.00MHZ (remark: more probably ±20.00KHZ than MHZ)
44+
- Very similar to Abarth 124Spider, message lengh is bigger including a 0x40 then a CRC/16 CCITT-FALSE, (little-endian)
45+
- Temperature (°C) offset is 55 C
46+
- Pressure (KPa) is divided by 3.
47+
48+
Common payload:
49+
- The preamble is 0xaa..aa9 (or 0x55..556 depending on polarity)
1850
19-
Compatible with aftermarket sensors, e.g. Aligator sens.it RS3
51+
Data layout (nibbles):
2052
21-
// Working Temperature: -50°C to 125°C
22-
// Working Frequency: 433.92MHz+-38KHz
23-
// Tire monitoring range value: 0kPa-350kPa+-7kPa (to be checked, VDO says 450/900kPa)
53+
Byte 00 01 02 03 04 05 06 07 08 09 10 11
54+
TG1C II II II II ?? PP TT SS CC
55+
Q85 II II II II ?? PP TT SS CC FF CR CR
2456
25-
Data layout (nibbles):
26-
II II II II ?? PP TT SS CC
2757
- I: 32 bit ID
2858
- ?: 4 bit unknown (seems to change with status)
2959
- ?: 4 bit unknown (seems static)
30-
- P: 8 bit Pressure (multiplied by 1.38 = kPa)
31-
- T: 8 bit Temperature (deg. C offset by 50)
60+
- P: 8 bit Pressure (multiplied by 1.38 = kPa for TG1C, by 3 for Q85)
61+
- T: 8 bit Temperature (deg. C offset by 50 for TG1C, by 55 for Q85)
3262
- S: Status? (first nibble seems static, second nibble seems to change with status)
33-
- C: 8 bit Checksum (Checksum8 XOR on bytes 0 to 8)
34-
- The preamble is 0xaa..aa9 (or 0x55..556 depending on polarity)
63+
- CC: 8 bit Checksum (Checksum8 XOR on bytes 0 to 8)
64+
- F: 8 bit unknown (Q85 only, seems fixed = 0x40)
65+
- CR: 16 bit CRC/16 CCITT-FASLE. little-endian format (Q85 only, on bytes 0 to 9).
3566
*/
3667

37-
#include "decoder.h"
68+
#define MODEL_TG1C 1
69+
#define MODEL_Q85 2
3870

39-
static int tpms_abarth124_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos)
71+
static int tpms_abarth124_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos, int type)
4072
{
4173
bitbuffer_t packet_bits = {0};
42-
bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 72);
74+
char id_str[4 * 2 + 1];
75+
char flags[1 * 2 + 1];
76+
int data_len;
77+
uint16_t crc_little_endian, crc_result;
78+
79+
// 9 byte or 12 byte
80+
if (type == MODEL_TG1C) {
81+
data_len = 72;
82+
} else if (type == MODEL_Q85) {
83+
data_len = 96;
84+
} else {
85+
decoder_log(decoder, 2, __func__, "Unknown model");
86+
return 0; //return 0 instead of DECODE_ABORT_LENGTH, to avoid exit error : gave invalid return value -x: notify maintainer;
87+
}
88+
89+
bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, data_len);
4390

4491
// make sure we decoded the expected number of bits
45-
if (packet_bits.bits_per_row[0] < 72) {
46-
// decoder_logf(decoder, 0, __func__, "bitpos=%u start_pos=%u = %u", bitpos, start_pos, (start_pos - bitpos));
47-
return 0; // DECODE_FAIL_SANITY;
92+
if (packet_bits.bits_per_row[0] < data_len) {
93+
decoder_log(decoder, 2, __func__, "Packet too short");
94+
return 0; //DECODE_FAIL_SANITY;
4895
}
4996

5097
uint8_t *b = packet_bits.bb[0];
5198

52-
// check checksum (checksum8 xor)
53-
int const checksum = xor_bytes(b, 9);
99+
// check checksum (checksum8 xor) same for both type model
100+
int checksum = xor_bytes(b, 9);
54101
if (checksum != 0) {
55-
return 0; // DECODE_FAIL_MIC;
102+
decoder_log(decoder, 2, __func__, "Checksum failed");
103+
return 0; //DECODE_FAIL_MIC;
56104
}
57105

58-
int const pressure = b[5];
59-
int const temperature = b[6];
60-
int const status = b[7];
61-
// int const checksum = b[8];
62-
63-
char flags[1 * 2 + 1];
64-
snprintf(flags, sizeof(flags), "%02x", b[4]);
65-
char id_str[4 * 2 + 1];
66-
snprintf(id_str, sizeof(id_str), "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]);
106+
int press_raw = b[5];
107+
int temp_raw = b[6];
108+
int status = b[7];
109+
sprintf(flags, "%02x", b[4]);
110+
sprintf(id_str, "%02x%02x%02x%02x", b[0], b[1], b[2], b[3]);
111+
112+
if (type == MODEL_Q85) {
113+
// check CRC for 0 to 9 byte only if type Q85, CRC 16 CCITT-FALSE little-endian
114+
crc_little_endian = (b[11] << 8 ) | b[10];
115+
crc_result = crc16(b, 10, 0x1021,0xffff); // CRC-16 CCITT-FALSE
116+
if (crc_result != crc_little_endian) {
117+
decoder_log(decoder, 2, __func__, "CRC Failed");
118+
return 0; //DECODE_FAIL_MIC;
119+
}
120+
}
67121

68122
/* clang-format off */
69123
data_t *data = data_make(
70-
"model", "", DATA_STRING, "Abarth-124Spider",
124+
"model", "", DATA_COND, type == MODEL_TG1C, DATA_STRING, "Abarth-124Spider",
125+
"model", "", DATA_COND, type == MODEL_Q85, DATA_STRING, "Shenzhen-EGQQ85",
71126
"type", "", DATA_STRING, "TPMS",
72127
"id", "", DATA_STRING, id_str,
73128
"flags", "", DATA_STRING, flags,
74-
"pressure_kPa", "Pressure", DATA_FORMAT, "%.0f kPa", DATA_DOUBLE, (float)pressure * 1.38,
75-
"temperature_C", "Temperature", DATA_FORMAT, "%.0f C", DATA_DOUBLE, (float)temperature - 50.0,
129+
"pressure_kPa", "Pressure", DATA_COND, type == MODEL_TG1C, DATA_FORMAT, "%.0f kPa", DATA_DOUBLE, (float)press_raw * 1.38,
130+
"pressure_kPa", "Pressure", DATA_COND, type == MODEL_Q85, DATA_FORMAT, "%.0f kPa", DATA_DOUBLE, (float)press_raw * 3,
131+
"temperature_C", "Temperature", DATA_COND, type == MODEL_TG1C, DATA_FORMAT, "%.0f C", DATA_DOUBLE, (float)temp_raw - 50.0,
132+
"temperature_C", "Temperature", DATA_COND, type == MODEL_Q85, DATA_FORMAT, "%.0f C", DATA_DOUBLE, (float)temp_raw - 55.0,
76133
"status", "", DATA_INT, status,
77-
"mic", "Integrity", DATA_STRING, "CHECKSUM",
134+
"mic", "Integrity", DATA_COND, type == MODEL_TG1C, DATA_STRING, "CHECKSUM",
135+
"mic", "Integrity", DATA_COND, type == MODEL_Q85, DATA_STRING, "CRC",
78136
NULL);
79137
/* clang-format on */
80138

@@ -90,12 +148,25 @@ static int tpms_abarth124_callback(r_device *decoder, bitbuffer_t *bitbuffer)
90148

91149
unsigned bitpos = 0;
92150
int events = 0;
151+
int type = 0;
93152

94153
bitbuffer_invert(bitbuffer);
154+
unsigned bits = bitbuffer->bits_per_row[0];
155+
156+
// Define the type model , around 195 bits for TG1C MC encoded (result is 72 bits), around 353 bits for Q85 MC encoded (result is 96 bits)
157+
if (bits > 150 && bits < 210) {
158+
type = MODEL_TG1C;
159+
} else if ( bits > 210 && bits < 400) {
160+
type = MODEL_Q85;
161+
} else {
162+
decoder_log(decoder, 2, __func__, "Length does not match");
163+
return DECODE_ABORT_LENGTH;
164+
}
165+
95166
// Find a preamble with enough bits after it that it could be a complete packet
96167
while ((bitpos = bitbuffer_search(bitbuffer, 0, bitpos, preamble_pattern, 24)) + 80 <=
97168
bitbuffer->bits_per_row[0]) {
98-
events += tpms_abarth124_decode(decoder, bitbuffer, 0, bitpos + 24);
169+
events += tpms_abarth124_decode(decoder, bitbuffer, 0, bitpos + 24, type);
99170
bitpos += 2;
100171
}
101172

@@ -116,7 +187,7 @@ static char const *const output_fields[] = {
116187
};
117188

118189
r_device const tpms_abarth124 = {
119-
.name = "Abarth 124 Spider TPMS",
190+
.name = "Abarth 124 Spider and Shenzhen EGQ Q85 TPMS",
120191
.modulation = FSK_PULSE_PCM,
121192
.short_width = 52, // 12-13 samples @250k
122193
.long_width = 52, // FSK

0 commit comments

Comments
 (0)