Problem
I am responsible for the road worthiness of a fairly large fleet of mobility devices. How can the tire pressure of the entire fleet be monitored?
Solution (using bicycles as an example)
Use air pressure sensors, receive data from the sensors via SDR, forward sensor data to openHAB via mqtt, decode/transform sensor data within openHAB.
Hardware required
-
SDR receiver and antenna (e.g. https://www.rtl-sdr.com/buy-rtl-sdr-dvb-t-dongles/)
-
tires/tubes with Schrader/American valves
and -
- tire pressure sensors for Schrader/American valves (e.g. https://de.aliexpress.com/item/1005003819238629.html (includes a display unit - not required, but handy for identifying sensor ids)
or
FL/FR/RL/RR Reifen Ventil Externe Inneren Sensor AusrĂŒstungen von Auto TPMS Reifen Druck Ăberwachung System auto Zubehör| | - AliExpress (sensors without display unit)
- tire pressure sensors for Schrader/American valves (e.g. https://de.aliexpress.com/item/1005003819238629.html (includes a display unit - not required, but handy for identifying sensor ids)
-
or a car with pressure sensors supported by
rtl_433
(Citroen, Toyota, Ford, Renault, Dacia, Abarth, Hyundai, Porsche) -
or a car with pressure sensors (433 MHz) not supported by
rtl_433
and plenty of time to analyze the bit patterns
Software required
- rtl_433
- Mosquitto Broker
- openHAB MQTT Binding
- some JS transformations (see below)
Installation
Install required software.
Install rtl_433 as a service:
[Unit]
Description=rtl_433 to MQTT publisher
After=network.target
[Service]
ExecStart=/home/openhabian/rtl_433/rtl_433 -X "n=solar_tpms,m=FSK_PCM,s=52,l=52,r=150,invert,preamble=aaaaa9" -F mqtt://localhost:1883,user=openhabian,pass=<your password>,retain=0,devices=sensors/rtl_433/P[protocol:255]/I[id:9999] -M newmodel -M protocol -M time:iso
Restart=always
RestartSec=15
[Install]
WantedBy=multi-user.target
The custom decoder (-X parameter) is not mandatory for the pressure sensor mentioned in the BOM, but we will later decode the raw data from the pressure sensors just for fun using Blockly and exactly these raw data are provided by this custom decoder.
Create MQTT Things in openHAB:
UID: mqtt:topic:1baf338605:4f44120acc
label: Fahrrad M
thingTypeUID: mqtt:topic
configuration: {}
bridgeUID: mqtt:broker:1baf338605
channels:
- id: Temperatur_vorne
channelTypeUID: mqtt:number
label: Temperatur vorne
description: ""
configuration:
stateTopic: sensors/rtl_433/P156/If5bdXXXX/temperature_C
transformationPattern: JS:SolarTPMS_fix_temperature.js
unit: °C
- id: Druck_vorne
channelTypeUID: mqtt:number
label: Druck vorne
description: ""
configuration:
stateTopic: sensors/rtl_433/P156/If5bdXXXX/pressure_kPa
transformationPattern: JS:SolarTPMS_fix_pressure.js
unit: kPa
- id: Last_seen_vorne
channelTypeUID: mqtt:datetime
label: Last seen vorne
description: ""
configuration:
stateTopic: sensors/rtl_433/P156/If5bdXXXX/time
- id: Temperatur_hinten
channelTypeUID: mqtt:number
label: Temperatur hinten
description: ""
configuration:
stateTopic: sensors/rtl_433/P156/I9c1eXXXX/temperature_C
transformationPattern: JS:SolarTPMS_fix_temperature.js
unit: °C
- id: Druck_hinten
channelTypeUID: mqtt:number
label: Druck hinten
description: ""
configuration:
stateTopic: sensors/rtl_433/P156/I9c1eXXXX/pressure_kPa
transformationPattern: JS:SolarTPMS_fix_pressure.js
unit: kPa
- id: Last_seen_hinten
channelTypeUID: mqtt:datetime
label: Last seen hinten
description: ""
configuration:
stateTopic: sensors/rtl_433/P156/I9c1eXXXX/time
- id: Raw_code_vorne
channelTypeUID: mqtt:string
label: Raw code vorne
description: ""
configuration:
stateTopic: sensors/rtl_433/P255/I9999/rows/0/data XXXX655965696a66
- id: Raw_code_hinten
channelTypeUID: mqtt:string
label: Raw code hinten
description: ""
configuration:
stateTopic: sensors/rtl_433/P255/I9999/rows/0/data XXXXa956a96aa55a
Use mosquitto_sub
or the display unit that came with the sensors to get the sensor ids.
The data packets from the pressure sensors mentioned in the BOM are recognized as rtl_433 type 156 (and occasionally as type 201). The implementation of type 156 within rtl_433 refers to similar sensors and I was able to empirically determine how to transform the values of type 156 into values for the sensors from the BOM:
SolarTPMS_fix_temperature.js
:
( function(i) { return i-5 } ) (input)
SolarTPMS_fix_pressure.js
:
( function(i) { return Math.round( i / 1.38 / 32.6 * 100 ) } ) (input)
Some Results
Quality of the sensors
Transmission about once in 5 minutes (no transmission when not pressurized, AFAICT no motion de-/activation of the sensors, sensor transmission gets triggered by pressure (and temperature?) gradient, temperature resolution: 1 °C, pressure resolution: less than 1 / 32.6 * 100 kPa = 3 kPa = 0.03 bar = 0.435 psi)
Comparison of the temperature data from three sensors (at same location):
The blue one is off by about -3 °C (could be fixed by a sensor specific transformation).
Some fun with Manchester encoding
As mentioned above, the custom decoder (rtl_433 -X option) provides the raw bit patterns. Lets decode the raw data using Blockly:
var raw_code, byte_no, temp, byte, Manchester, start, pressure, nibble_to_hex, temperature, j, i;
var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
// Describe this function...
function decode_byte(raw_code, byte_no) {
Manchester = ['a', '9', '6', '5'];
start = (byte_no - 1) * 4 + 1;
raw_code = raw_code.slice((start - 1), start + 4);
logger.error(raw_code);
byte = 0;
for (j = 4; j >= 1; j--) {
byte = byte + Math.pow(4, 4 - j) * ((Manchester.indexOf(raw_code.charAt((j - 1))) + 1) - 1);
}
return byte;
}
// Describe this function...
function byte_to_hex(byte) {
logger.error(byte);
nibble_to_hex = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
return String(nibble_to_hex[((Math.floor(byte / 16) + 1) - 1)]) + String(nibble_to_hex[((byte % 16 + 1) - 1)]);
}
temp = itemRegistry.getItem('SolarTPMSReceiver_RawCode').getState();
temp += '';
pressure = Math.round(decode_byte(temp, 6) / 0.326);
temperature = decode_byte(temp, 7) - 55;
pressure += ' kPa ';
pressure += String(temperature);
pressure += ' °C id: ';
for (i = 1; i <= 4; i++) {
pressure += String(byte_to_hex(decode_byte(temp, i)));
}
events.postUpdate('SolarTPMSrawcodedecoded_SolarTPMSrawcodedecoded', pressure);