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
118189r_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