Skip to content
Merged
Changes from all 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
104 changes: 64 additions & 40 deletions src/devices/steelmate.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

Copyright (C) 2016 Benjamin Larsson
Copyright (C) 2016 John Jore
Copyright (C) 2025 Bruno OCTAU (ProfBoc75)

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
Expand All @@ -12,7 +13,23 @@
/**
Steelmate TPMS FSK protocol.

Packet payload: 9 bytes.
Reference:

- model TP-S15

Brand:

- Steelmate
- R-Lake

S.a. issue #3200 Pressure issue :

- The originally guessed formula was : Pressure in PSI scale 2, but more the pressure is important more the value diverged between the TPMS display and rtl_433.
- New analysis : Based on data collected by \@e100 + the technical specification ( 0~7.9Bar ) + analysis by \@e100 and refined by \@ProfBoc75, the pressure is given in Bar at scale 32.

Packet payload:

- 9 bytes.

Bytes 2 to 9 are inverted Manchester with swapped MSB/LSB:

Expand All @@ -24,9 +41,12 @@ Bytes 2 to 9 are inverted Manchester with swapped MSB/LSB:
- S = sync, (0x00)
- A = preamble, (0x01)
- I = id, 0xc3f0
- P = Pressure as double the PSI, 0x14 = 10 PSI
- T = Temperature in Fahrenheit, 0x4a = 74 'F
- B = Battery as half the millivolt, 0x8e = 2.84 V
- P = Pressure in Bar, scale 32, 0xA0 / 32 = 5 Bar, or 0xA0 * 3.125 = 500 kPA, see issue #3200
- T = Temperature in Celcius + 50, 0x4a = 24 'C
- B = Battery, where mV = 3900-(value*10). E.g 0x8e becomes 3900-(1420) = 2480mV.
- This calculation is approximate fit from sample data, any improvements are welcome.
- > If this field is set to 0xFF, a "fast leak" alarm is triggered.
- > If this field is set to 0xFE, a "slow leak" alarm is triggered.
- C = Checksum, adding bytes 2 to 7 modulo 256 = byte 8,(0x01+0xc3+0xf0+0x14+0x4a+0x8e) modulus 256 = 0xa0

*/
Expand All @@ -35,58 +55,61 @@ Bytes 2 to 9 are inverted Manchester with swapped MSB/LSB:

static int steelmate_callback(r_device *decoder, bitbuffer_t *bitbuffer)
{
uint8_t const preamble_pattern[] = {0x00, 0x00, 0x7f}; // inverted, raw value is 0x5a
//Loop through each row of data
for (int row = 0; row < bitbuffer->num_rows; row++)
{
for (int row = 0; row < bitbuffer->num_rows; row++) {
//Payload is inverted Manchester encoded, and reversed MSB/LSB order
uint8_t *b = bitbuffer->bb[row];
unsigned row_len = bitbuffer->bits_per_row[row];

//Length must be 72 bits to be considered a valid packet
if (bitbuffer->bits_per_row[row] != 72)
//Length must be 72, 73, 208 or 209 bits to be considered a valid packet
if (row_len != 72 && row_len != 73 && row_len != 209 && row_len != 208)
continue; // DECODE_ABORT_LENGTH

//Valid preamble? (Note, the data is still wrong order at this point. Correct pre-amble: 0x00 0x00 0x01)
if (b[0] != 0x00 || b[1] != 0x00 || b[2] != 0x7f)
unsigned bitpos = bitbuffer_search(bitbuffer, row, 0, preamble_pattern, 24);
if (bitpos > row_len - 72)
continue; // DECODE_ABORT_EARLY
bitbuffer_invert(bitbuffer);
uint8_t b[9];
bitbuffer_extract_bytes(bitbuffer, row, bitpos, b, 72);

//Preamble
uint8_t preamble = ~reverse8(b[2]);
reflect_bytes(b, 9);

//Sensor ID
uint8_t id1 = ~reverse8(b[3]);
uint8_t id2 = ~reverse8(b[4]);
//Checksum is a sum of all the other values
if ((add_bytes(&b[2], 6) & 0xFF) != b[8])
continue; // DECODE_FAIL_MIC

//Pressure is stored as twice the PSI
uint8_t p1 = ~reverse8(b[5]);
//Sensor ID
uint8_t id1 = b[3];
uint8_t id2 = b[4];

//Temperature is stored in Fahrenheit. Note that the datasheet claims operational to -40'C, but can only express values from -17.8'C
uint8_t tempFahrenheit = ~reverse8(b[6]);
//Pressure is stored as 32 * the Bar
uint8_t p1 = b[5];

//Battery voltage is stored as half the mV
uint8_t tmpbattery_mV = ~reverse8(b[7]);
//Temperature is sent as degrees Celcius + 50.
int temp_c = b[6] - 50;

//Checksum is a sum of all the other values
uint8_t payload_checksum = ~reverse8(b[8]);
uint8_t calculated_checksum = preamble + id1 + id2 + p1 + tempFahrenheit + tmpbattery_mV;
if (payload_checksum != calculated_checksum)
continue; // DECODE_FAIL_MIC
//Battery voltage is stored as 100*(3.9v-<volt>).
uint8_t b1 = b[7];
int battery_mv = 3900 - b1 * 10;

int sensor_id = (id1 << 8) | id2;
float pressure_psi = p1 * 0.5f;
int battery_mV = tmpbattery_mV * 2;
float pressure_kpa = p1 * 3.125f; // as guessed in #3200

char sensor_idhex[7];
snprintf(sensor_idhex, sizeof(sensor_idhex), "0x%04x", sensor_id);

/* clang-format off */
data_t *data = data_make(
"type", "", DATA_STRING, "TPMS",
"model", "", DATA_STRING, "Steelmate",
"id", "", DATA_STRING, sensor_idhex,
"pressure_PSI", "", DATA_DOUBLE, pressure_psi,
"temperature_F", "", DATA_DOUBLE, (float)tempFahrenheit,
"battery_mV", "", DATA_INT, battery_mV,
"mic", "Integrity", DATA_STRING, "CHECKSUM",
"type", "", DATA_STRING, "TPMS",
"model", "", DATA_STRING, "Steelmate",
"id", "", DATA_STRING, sensor_idhex,
"pressure_kPa", "", DATA_FORMAT, "%.0f kPa", DATA_DOUBLE, pressure_kpa,
"temperature_C", "", DATA_FORMAT, "%d C", DATA_INT, temp_c,
"battery_mV", "", DATA_COND, b1 < 0xFE, DATA_INT, battery_mv,
"alarm", "", DATA_COND, b1 == 0xFF, DATA_STRING, "fast leak",
"alarm", "", DATA_COND, b1 == 0xFE, DATA_STRING, "slow leak",
"mic", "Integrity", DATA_STRING, "CHECKSUM",
NULL);
/* clang-format on */

Expand All @@ -103,19 +126,20 @@ static char const *const output_fields[] = {
"type",
"model",
"id",
"pressure_PSI",
"temperature_F",
"pressure_kPa",
"temperature_C",
"battery_mV",
"alarm",
"mic",
NULL,
};

r_device const steelmate = {
.name = "Steelmate TPMS",
.modulation = FSK_PULSE_MANCHESTER_ZEROBIT,
.short_width = 12 * 4,
.long_width = 0,
.reset_limit = 27 * 4,
.short_width = 50,
.long_width = 50,
.reset_limit = 120,
.decode_fn = &steelmate_callback,
.fields = output_fields,
};
Loading