I like to challenge myself regularly in order to keep my mind active and open to new information. One of my long-running ideas is to create an “infotainment” system for my car that is capable of displaying and logging vehicle sensors in addition to the standard media-playing functionality. Many of the vehicle sensors can be accessed through the OBD2 interface, but my car doesn’t have a factory tire-pressure monitoring system. This means I need to get a bit more creative to acquire that data. Enter my attempt to reverse-engineer some standard TPMS sensors.
Reverse-engineering tire-pressure monitoring system (TPMS) sensors takes a combination of research, educated guessing, and experimentation. Wikipedia has a good article for background information on TPMS: https://en.wikipedia.org/wiki/Tire-pressure_monitoring_system
For more information on what hardware is needed and what software to use, see: https://github.com/jboone/tpms
This is also the software which I will be using for this post.
Overview of the steps:
- Determine if your vehicle has direct or indirect TPMS
- Assuming direct TPMS, determine the sensor radio frequency band (likely 315MHz or 433MHz)
- Get a software radio receiver that can capture complex radio spectrum data at your target frequency
- Capture data while driving, ideally in an area with few other vehicles to minimize interference
- Examine capture files, looking for transmissions that are clear and strong
The following steps will require repeated experimentation:
- Pick a clear capture file to determine the modulation and approximate bandwidth
- Determine symbol rate by measuring time between bits
- Determine preamble (this is one of the more difficult steps to get right)
- Use these parameters to try to decode more data for more sample points
This process will only work with direct TPMS. Indirect TPMS doesn’t have sensors in the tires, so there is no information transmitted and nothing to capture using this method.
There are many options for software-defined radios. The most commonly available ones are based on the Realtek R820T2 chipset.
Examining capture files:
Once you have identified the radio encoding parameters that your sensors use, you can continue to work at determining the packet structure. This requires a fair amount of guesswork, but can be made easier by collecting data under controlled conditions. For example, identifying the pressure field can be done by capturing data while changing the tire pressure. Generally, TPMS sensors will go to sleep when the vehicle is parked, but rapidly changing pressure should put them into an alarm state. This can also help you determine how the sensor values correspond to actual tire pressure. By removing and re-adding air from the tire in recorded steps (and pausing in between), you should see a clear trend in the sensor data. Note that changing the pressure will also affect the temperature slightly.
Here is an example of the graphed data when varying pressure in a single tire.
When examining all your captured and decoded data, the sensor ID field will have 4-5 distinct values depending on how many sensors your vehicle has. Pressure and temperature will both vary while driving. There may also be a few status fields and a CRC/checksum. It is likely that fields will be 8 bits wide, so that’s a good guess to start.
I only have 3 functioning sensors, but here are some examples of the interesting fields that I was able to decipher:
bits[00,32]: sensor ID
bits[32,40]: status flags?
bits[40,48]: pressure (psi * 2.72 or so)
bits[48,56]: temperature (units not yet determined)
bits[56,64]: more status flags?
bits[64,72]: checksum (initial value: 0x00, polynomial: 0x01, XOR value: 0x00)
I’ve been able to identify all the information that I consider to be important (sensor ID, pressure, and temperature), but there is still experimentation to be done for the other values. So far, this process has taken me a few weeks of poking and prodding. If you decide to take on a similar challenge, I wish you the best of luck!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
(py-tpms) mitchel@ubuntu:~/Development/py-tpms$ packet_stats.py --rangestats 00,32 < all_decoded.txt Range 0:32 ca19265a 3390645850 11001010000110010010011001011010: 124 **************************************************************************************************************************** ca19266c 3390645868 11001010000110010010011001101100: 98 ************************************************************************************************** ca19269b 3390645915 11001010000110010010011010011011: 124 **************************************************************************************************************************** (py-tpms) mitchel@ubuntu:~/Development/py-tpms$ packet_stats.py --rangestats 32,40 < all_decoded.txt Range 32:40 1 1 00000001: 64 **************************************************************** 2 2 00000010: 62 ************************************************************** 3 3 00000011: 78 ****************************************************************************** c1 193 11000001: 45 ********************************************* c2 194 11000010: 50 ************************************************** c3 195 11000011: 47 *********************************************** (py-tpms) mitchel@ubuntu:~/Development/py-tpms$ packet_stats.py --rangestats 40,48 < all_decoded.txt Range 40:48 55 85 01010101: 1 * 56 86 01010110: 7 ******* 57 87 01010111: 6 ****** 58 88 01011000: 15 *************** 59 89 01011001: 8 ******** 5a 90 01011010: 9 ********* 5b 91 01011011: 8 ******** 5c 92 01011100: 10 ********** 5d 93 01011101: 15 *************** 5e 94 01011110: 18 ****************** 5f 95 01011111: 8 ******** 60 96 01100000: 13 ************* 61 97 01100001: 6 ****** 66 102 01100110: 5 ***** 67 103 01100111: 3 *** 68 104 01101000: 5 ***** 69 105 01101001: 7 ******* 6a 106 01101010: 9 ********* 6b 107 01101011: 11 *********** 6c 108 01101100: 9 ********* 6d 109 01101101: 5 ***** 6e 110 01101110: 19 ******************* 6f 111 01101111: 18 ****************** 70 112 01110000: 8 ******** 71 113 01110001: 8 ******** 72 114 01110010: 23 *********************** 73 115 01110011: 7 ******* 74 116 01110100: 12 ************ 75 117 01110101: 6 ****** 76 118 01110110: 10 ********** 77 119 01110111: 11 *********** 78 120 01111000: 11 *********** 79 121 01111001: 7 ******* 7a 122 01111010: 9 ********* 7b 123 01111011: 2 ** 7c 124 01111100: 1 * 7d 125 01111101: 2 ** 7e 126 01111110: 7 ******* 7f 127 01111111: 7 ******* (py-tpms) mitchel@ubuntu:~/Development/py-tpms$ packet_stats.py --rangestats 48,56 < all_decoded.txt Range 48:56 2c 44 00101100: 3 *** 2d 45 00101101: 16 **************** 2e 46 00101110: 17 ***************** 2f 47 00101111: 11 *********** 30 48 00110000: 12 ************ 31 49 00110001: 9 ********* 32 50 00110010: 17 ***************** 33 51 00110011: 7 ******* 34 52 00110100: 12 ************ 35 53 00110101: 14 ************** 36 54 00110110: 21 ********************* 37 55 00110111: 18 ****************** 38 56 00111000: 11 *********** 39 57 00111001: 12 ************ 3a 58 00111010: 15 *************** 3b 59 00111011: 6 ****** 3c 60 00111100: 16 **************** 3d 61 00111101: 12 ************ 3e 62 00111110: 19 ******************* 3f 63 00111111: 5 ***** 40 64 01000000: 14 ************** 41 65 01000001: 9 ********* 42 66 01000010: 6 ****** 43 67 01000011: 5 ***** 44 68 01000100: 12 ************ 45 69 01000101: 18 ****************** 46 70 01000110: 11 *********** 47 71 01000111: 13 ************* 48 72 01001000: 5 ***** (py-tpms) mitchel@ubuntu:~/Development/py-tpms$ packet_stats.py --rangestats 56,64 < all_decoded.txt Range 56:64 1 1 00000001: 1 * 3 3 00000011: 2 ** 4 4 00000100: 2 ** 13 19 00010011: 2 ** 15 21 00010101: 1 * 17 23 00010111: 2 ** 19 25 00011001: 3 *** 1c 28 00011100: 3 *** 1d 29 00011101: 2 ** 21 33 00100001: 1 * 29 41 00101001: 2 ** 35 53 00110101: 2 ** 3a 58 00111010: 1 * 3e 62 00111110: 5 ***** 40 64 01000000: 2 ** 43 67 01000011: 1 * 4a 74 01001010: 3 *** 4c 76 01001100: 2 ** 52 82 01010010: 1 * 59 89 01011001: 3 *** 5b 91 01011011: 4 **** 61 97 01100001: 3 *** 6d 109 01101101: 2 ** 6f 111 01101111: 1 * 74 116 01110100: 1 * 7e 126 01111110: 1 * 80 128 10000000: 1 * 84 132 10000100: 1 * 85 133 10000101: 2 ** 88 136 10001000: 2 ** 8b 139 10001011: 2 ** 8c 140 10001100: 2 ** 92 146 10010010: 2 ** 95 149 10010101: 1 * 96 150 10010110: 5 ***** 99 153 10011001: 2 ** 9a 154 10011010: 3 *** 9b 155 10011011: 1 * 9c 156 10011100: 2 ** 9d 157 10011101: 2 ** a1 161 10100001: 3 *** a3 163 10100011: 4 **** a4 164 10100100: 1 * a8 168 10101000: 1 * ab 171 10101011: 1 * b3 179 10110011: 1 * b8 184 10111000: 2 ** b9 185 10111001: 1 * ba 186 10111010: 1 * bb 187 10111011: 1 * bc 188 10111100: 3 *** c0 192 11000000: 1 * c2 194 11000010: 2 ** c5 197 11000101: 2 ** c7 199 11000111: 1 * ca 202 11001010: 2 ** d1 209 11010001: 2 ** d3 211 11010011: 2 ** d4 212 11010100: 3 *** d5 213 11010101: 3 *** d9 217 11011001: 1 * da 218 11011010: 1 * db 219 11011011: 2 ** e0 224 11100000: 2 ** e3 227 11100011: 2 ** e4 228 11100100: 2 ** fe 254 11111110: 1 * ff 255 11111111: 217 ************************************************************************************************************************************************************************************************************************* |
Dmitry says
Hi Mitchel!
Which one Partnumber of your TPMS-sensor?
Do you have any progress after month of experiments?
Mitchel says
Hi Dmitry,
I don’t actually know the part number for my sensors. I made a guess based on the compatible replacements from various auto parts stores.
I’ve started working on using a hardware radio to do the decoding for me. Unfortunately, I’ve been working on other projects as well so I haven’t made much progress on that. I will definitely post about it when I do!