-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add General Motors TPMS #3191
Add General Motors TPMS #3191
Changes from 3 commits
4611155
493f9b2
aaa43a0
f9ac647
9bbc7e4
3e3678c
2a16a4f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/** @file | ||
General Motors Aftermarket TPMS | ||
|
||
Copyright (C) 2025 Eric Blevins | ||
|
||
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. | ||
*/ | ||
|
||
#include "decoder.h" | ||
#include "r_util.h" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. include only decoder.h if possible. |
||
#include <stdio.h> | ||
#include <inttypes.h> // Needed for PRIX64 | ||
|
||
static uint8_t const preamble_pattern[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move this into the decoder function. |
||
|
||
/** | ||
Data was detected an initially captured using: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. first line should be the same as line 2 |
||
|
||
rtl_433 -X 'n=name,m=OOK_MC_ZEROBIT,s=120,l=0,r=15600' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. four spaces indent for code lines |
||
|
||
These sensors react to a signal from an EL-50448 relearn tool. | ||
They enter into learn mode upon receiving the signal and transmit. | ||
If they remain at 0PSI they will not transmit unless receiving signal | ||
from the relearn tool. | ||
If they have pressure they transmit every 2 minutes and will exit | ||
the learn mode after one cycle. | ||
Any sudden changes in pressure triggers immediate signal. | ||
These do not seem capable of detection motion. | ||
|
||
|
||
|
||
130 bits | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. indent |
||
AAAAAAAAAAAASSSSDDDDIIIIIIPPTTCCX | ||
0000000000004c90007849176600536d0 | ||
Data layout: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no indent for header lines |
||
|
||
AAAAAAAAAAAASSSSDDDDIIIIIIPPTTCCX | ||
|
||
- A: preamble 0x000000000000 | ||
- S: Status | ||
- D: Device type or prefix | ||
- I: Device uniquie identifier | ||
- P: Pressure | ||
- T: Temperature | ||
- C: CheckSum, modulo 256 | ||
|
||
The only status data detected is learn mode and low battery. | ||
Bit 5 of status indicates low battery when set to 1 | ||
Bits 0,1,8 are set to 0 to indicate learn mode and 1 for operational mode. | ||
The sensors drop to learn mode when detecting a large pressure drop | ||
or when activated with the learning tool | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. end sentences with a dot |
||
|
||
In learn mode with zero pressure they only transmit when activated by | ||
the learning tool. | ||
Once presurized they will transmit in learn mode and within a couple | ||
minutes switch to sending in operatioinal mode every teo minutes. | ||
|
||
*/ | ||
|
||
static int tpms_gm_decode(r_device *decoder, bitbuffer_t *bitbuffer) | ||
{ | ||
if (bitbuffer->num_rows != 1) { | ||
return DECODE_ABORT_EARLY; | ||
} | ||
|
||
if (bitbuffer->bits_per_row[0] != 130) { | ||
return DECODE_ABORT_LENGTH; | ||
} | ||
|
||
int pos = bitbuffer_search(bitbuffer, 0, 0, preamble_pattern, sizeof(preamble_pattern) * 8); | ||
if (pos < 0) { | ||
return DECODE_ABORT_EARLY; | ||
} | ||
|
||
// Buffer for extracted bytes | ||
uint8_t b[17] = {0}; | ||
bitbuffer_extract_bytes(bitbuffer, 0, 0, b, 130); | ||
|
||
// Checksum skips preamble | ||
uint8_t computed_checksum = 0; | ||
for (int i = 6; i < 15; i++) { | ||
computed_checksum += b[i]; | ||
} | ||
if ((computed_checksum % 256) != b[15]) { | ||
return DECODE_FAIL_MIC; | ||
} | ||
|
||
// Convert ID to an integer | ||
uint64_t sensor_id = ((uint64_t)b[8] << 32) | ((uint64_t)b[9] << 24) | (b[10] << 16) | (b[11] << 8) | b[12]; | ||
uint16_t status = (b[6] << 8) | b[7]; | ||
|
||
uint8_t pressure_raw = b[13]; | ||
uint8_t temperature_raw = b[14]; | ||
|
||
// Adding 3.75 made my sensors accurate accurate | ||
// But I think it might be best to allow the user to | ||
// to add their own offset when consuming the data | ||
float pressure_kpa = (pressure_raw * 2.75); | ||
float pressure_psi = kpa2psi(pressure_kpa); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. output native units, don't convert in decoders |
||
float temperature_c = temperature_raw - 60; | ||
|
||
// Extract bits correctly based on little-endian order | ||
int bit8 = (status >> 8) & 1; | ||
int bit1 = (status >> 1) & 1; | ||
int bit0 = (status >> 0) & 1; | ||
|
||
// Status bits | ||
int learn_mode = ((bit8 == 0) && (bit1 == 0) && (bit0 == 0)) ? 1 : 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't |
||
int battery_ok = !((status >> 5) & 1); | ||
|
||
char status_hex[7]; // 6 chars + null terminator for "0xFFFF" | ||
snprintf(status_hex, sizeof(status_hex), "0x%04X", status); | ||
|
||
/* clang-format off */ | ||
data_t *data = data_make( | ||
"model", "", DATA_STRING, "GM Aftermarket TPMS", | ||
"type", "", DATA_STRING, "TPMS", | ||
"id", "", DATA_INT, sensor_id, | ||
"status", "", DATA_STRING, status_hex, | ||
"learn_mode", "", DATA_INT, learn_mode, | ||
"battery_ok", "", DATA_INT, battery_ok, | ||
"pressure_kPa", "", DATA_DOUBLE, pressure_kpa, | ||
"pressure_PSI", "", DATA_DOUBLE, pressure_psi, | ||
"temperature_C", "", DATA_DOUBLE, temperature_c, | ||
"mic", "Integrity", DATA_STRING, "CHECKSUM", | ||
NULL); | ||
|
||
/* clang-format on */ | ||
|
||
decoder_output_data(decoder, data); | ||
return 1; | ||
} | ||
|
||
/** Output fields for rtl_433 */ | ||
static char const *const output_fields[] = { | ||
"model", | ||
"type", | ||
"id", | ||
"status", | ||
"learn_mode", | ||
"battery_ok", | ||
"pressure_kPa", | ||
"pressure_PSI", | ||
"temperature_C", | ||
"mic", | ||
NULL, | ||
}; | ||
|
||
r_device const tpms_gm = { | ||
.name = "GM Aftermarket TPMS", | ||
.modulation = OOK_PULSE_MANCHESTER_ZEROBIT, | ||
.short_width = 120, | ||
.long_width = 0, | ||
.reset_limit = 15600, | ||
.decode_fn = &tpms_gm_decode, | ||
.fields = output_fields, | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
End the first line with a dot.