mirror of https://github.com/merbanan/rtl_433.git
238 lines
7.1 KiB
C
238 lines
7.1 KiB
C
/** @file
|
|
FSK 8 byte Manchester encoded TPMS with simple checksum.
|
|
|
|
Copyright (C) 2017 Christian W. Zuckschwerdt <zany@triq.net>
|
|
|
|
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.
|
|
*/
|
|
/**
|
|
FSK 8 byte Manchester encoded TPMS with simple checksum.
|
|
Seen on Ford Fiesta, Focus, Kuga, Escape, Transit...
|
|
|
|
Seen on 315.00 MHz (United States).
|
|
|
|
Seen on 433.92 MHz.
|
|
Likely VDO-Sensors, Type "S180084730Z", built by "Continental Automotive GmbH".
|
|
|
|
Typically a transmission is sent 4 times. Sometimes the T/P values
|
|
differ (slightly) among those.
|
|
|
|
Sensor has 3 modes:
|
|
moving: while being driven
|
|
atrest: once after stopping, and every 6h thereafter (for months)
|
|
learn: 12 transmissions, caused by using learn tool
|
|
|
|
Packet nibbles:
|
|
|
|
II II II II PP TT FF CC
|
|
|
|
- I = ID
|
|
- P = Pressure, as PSI * 4
|
|
- T = Temperature, as C + 56, except:
|
|
When 0x80 is on, value is not temperature, meaning the full 8
|
|
bits is not temperature, and the lower 7 bits is also not
|
|
temperature. Pattern of low 7 bits in this case seems more like
|
|
codepoints than a measurement.
|
|
- F = Flags:
|
|
0x80 not seen
|
|
0x40 ON for vehicle moving
|
|
Is strongly correlated with 0x80 being set in TT
|
|
0x20: 9th bit of pressure. Seen on Transit very high pressure, otherwise not.
|
|
0x10: not seen
|
|
|
|
0x08: ON for learn
|
|
0x04: ON for moving (0x08 and 0x04 both OFF for at rest)
|
|
0x02: ~always NOT 0x01 (meaning of 0x3 not understood, but MOVING
|
|
tends to have 0x02)
|
|
0x01: about 19% of samples
|
|
- C = Checksum, SUM bytes 0 to 6 = byte 7
|
|
*/
|
|
|
|
#include "decoder.h"
|
|
|
|
static int tpms_ford_decode(r_device *decoder, bitbuffer_t *bitbuffer, unsigned row, unsigned bitpos)
|
|
{
|
|
bitbuffer_t packet_bits = {0};
|
|
uint8_t *b;
|
|
unsigned id;
|
|
int code;
|
|
float pressure_psi;
|
|
int temperature_c, temperature_valid;
|
|
int psibits;
|
|
int moving;
|
|
int learn;
|
|
int unknown;
|
|
int unknown_3;
|
|
|
|
bitbuffer_manchester_decode(bitbuffer, row, bitpos, &packet_bits, 160);
|
|
|
|
// require 64 data bits
|
|
if (packet_bits.bits_per_row[0] < 64) {
|
|
return 0;
|
|
}
|
|
b = packet_bits.bb[0];
|
|
|
|
if (((b[0] + b[1] + b[2] + b[3] + b[4] + b[5] + b[6]) & 0xff) != b[7]) {
|
|
return 0;
|
|
}
|
|
|
|
id = (unsigned)b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3];
|
|
|
|
/* Extract and log code to aid in debugging. */
|
|
code = b[4] << 16 | b[5] << 8 | b[6];
|
|
|
|
/*
|
|
* Formula is a combination of regression and plausible, observed
|
|
* from roughly 31 to 36 psi. (The bit at byte6-0x20 is shifted
|
|
* to 0x100.)
|
|
*/
|
|
psibits = (((b[6] & 0x20) << 3) | b[4]);
|
|
pressure_psi = psibits * 0.25f;
|
|
|
|
/*
|
|
* Working theory is that temperature bits are temp + 56,
|
|
* encoding -56 to 71 C. Validated as close around 15 C.
|
|
*/
|
|
if ((b[5] & 0x80) == 0x80) {
|
|
temperature_valid = 0;
|
|
/* Avoid uninitialized warning due to DATA_COND. */
|
|
temperature_c = -1000.0;
|
|
}
|
|
else {
|
|
temperature_valid = 1;
|
|
temperature_c = (b[5] & 0x7f) - 56;
|
|
}
|
|
|
|
/*
|
|
* Set up syndrome of unexpected bits. The point is to have a
|
|
* variable unknown which is zero if this packet matches the
|
|
* code's understanding, and to be non-zero if anything is unusual,
|
|
* to aid finding logged packets for manual study.
|
|
*/
|
|
unknown = 0;
|
|
|
|
/* Examine moving, learn and normal bits. */
|
|
learn = moving = 0;
|
|
switch (b[6] & 0x4c) {
|
|
case 0x8:
|
|
/* In response to learn tool */
|
|
learn = 1;
|
|
break;
|
|
|
|
case 0x4:
|
|
/* At rest. */
|
|
break;
|
|
|
|
case 0x44:
|
|
/* Moving. */
|
|
moving = 1;
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* These three bits taken together do not match a known
|
|
* pattern. Therefore set all of them as the unknown
|
|
* syndrome.
|
|
*/
|
|
unknown = (b[6] & 0x4c);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* We've accounted for
|
|
* 0x40(moving) 0x20(temp) 0x8(learn) 04(normal)
|
|
* 0x3(separate, next)
|
|
* so that leaves 0x80 and 0x10, which are expected to be 0.
|
|
*/
|
|
unknown |= (b[6] & 0x90);
|
|
|
|
/* Low-order 2 bits are variously 01, 10. */
|
|
unknown_3 = b[6] & 0x3;
|
|
|
|
char id_str[9];
|
|
snprintf(id_str, sizeof(id_str), "%08x", id);
|
|
char code_str[7];
|
|
snprintf(code_str, sizeof(code_str), "%06x", code);
|
|
char unknown_str[3];
|
|
snprintf(unknown_str, sizeof(unknown_str), "%02x", unknown);
|
|
char unknown_3_str[2];
|
|
snprintf(unknown_3_str, sizeof(unknown_3_str), "%01x", unknown_3);
|
|
|
|
/* clang-format off */
|
|
data_t *data = data_make(
|
|
"model", "", DATA_STRING, "Ford",
|
|
"type", "", DATA_STRING, "TPMS",
|
|
"id", "", DATA_STRING, id_str,
|
|
"pressure_PSI", "Pressure", DATA_FORMAT, "%.2f PSI", DATA_DOUBLE, pressure_psi,
|
|
"temperature_C", "Temperature", DATA_COND, temperature_valid, DATA_FORMAT, "%.1f C", DATA_DOUBLE, (float)temperature_c,
|
|
"moving", "Moving", DATA_INT, moving,
|
|
"learn", "Learn", DATA_INT, learn,
|
|
"code", "", DATA_STRING, code_str,
|
|
"unknown", "", DATA_STRING, unknown_str,
|
|
"unknown_3", "", DATA_STRING, unknown_3_str,
|
|
"mic", "Integrity", DATA_STRING, "CHECKSUM",
|
|
NULL);
|
|
/* clang-format on */
|
|
|
|
decoder_output_data(decoder, data);
|
|
return 1;
|
|
}
|
|
|
|
/** @sa tpms_ford_decode() */
|
|
static int tpms_ford_callback(r_device *decoder, bitbuffer_t *bitbuffer)
|
|
{
|
|
// full preamble is 55 55 55 56 (inverted: aa aa aa a9)
|
|
uint8_t const preamble_pattern[2] = {0xaa, 0xa9}; // 16 bits
|
|
|
|
int row;
|
|
unsigned bitpos;
|
|
int ret = 0;
|
|
int events = 0;
|
|
|
|
bitbuffer_invert(bitbuffer);
|
|
|
|
for (row = 0; row < bitbuffer->num_rows; ++row) {
|
|
bitpos = 0;
|
|
// Find a preamble with enough bits after it that it could be a complete packet
|
|
while ((bitpos = bitbuffer_search(bitbuffer, row, bitpos,
|
|
preamble_pattern, 16)) + 144 <=
|
|
bitbuffer->bits_per_row[row]) {
|
|
ret = tpms_ford_decode(decoder, bitbuffer, row, bitpos + 16);
|
|
if (ret > 0)
|
|
events += ret;
|
|
bitpos += 15;
|
|
}
|
|
}
|
|
|
|
return events > 0 ? events : ret;
|
|
}
|
|
|
|
static char const *const output_fields[] = {
|
|
"model",
|
|
"type",
|
|
"id",
|
|
"flags",
|
|
"pressure_PSI",
|
|
"temperature_C",
|
|
"moving",
|
|
"learn",
|
|
"code",
|
|
"unknown",
|
|
"unknown_3",
|
|
"mic",
|
|
NULL,
|
|
};
|
|
|
|
r_device const tpms_ford = {
|
|
.name = "Ford TPMS",
|
|
.modulation = FSK_PULSE_PCM,
|
|
.short_width = 52, // 12-13 samples @250k
|
|
.long_width = 52, // FSK
|
|
.reset_limit = 150, // Maximum gap size before End Of Message [us].
|
|
.decode_fn = &tpms_ford_callback,
|
|
.fields = output_fields,
|
|
};
|