Skip to content
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

Merged
merged 7 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions conf/rtl_433.example.conf
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ convert si
protocol 272 # Landis & Gyr Gridstream Power Meters 19.2k
protocol 273 # Landis & Gyr Gridstream Power Meters 38.4k
protocol 274 # Revolt ZX-7717 power meter
protocol 275 # GM TPMS

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

Expand Down
1 change: 1 addition & 0 deletions include/rtl_433_devices.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@
DECL(gridstream192) \
DECL(gridstream384) \
DECL(revolt_zx7717) \
DECL(tpms_gm) \

/* Add new decoders here. */

Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ add_library(r_433 STATIC
devices/tpms_eezrv.c
devices/tpms_elantra2012.c
devices/tpms_ford.c
devices/tpms_gm.c
devices/tpms_hyundai_vdo.c
devices/tpms_jansite.c
devices/tpms_jansite_solar.c
Expand Down
161 changes: 161 additions & 0 deletions src/devices/tpms_gm.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/** @file
General Motors Aftermarket TPMS
Copy link
Collaborator

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.


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"
Copy link
Collaborator

Choose a reason for hiding this comment

The 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};
Copy link
Collaborator

Choose a reason for hiding this comment

The 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:
Copy link
Collaborator

Choose a reason for hiding this comment

The 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'
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent

AAAAAAAAAAAASSSSDDDDIIIIIIPPTTCCX
0000000000004c90007849176600536d0
Data layout:
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The 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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't ? 1 : 0 on a bool

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,
};

Loading