BLE Direct Tire Pressure Monitoring System (TPMS) Display Using ESP32
by MusaW in Circuits > Arduino
16270 Views, 136 Favorites, 0 Comments
BLE Direct Tire Pressure Monitoring System (TPMS) Display Using ESP32
What is TPMS?
The purpose of the tire pressure monitoring system (TPMS) in your vehicle is to warn you that at least one or more tires are significantly under-inflated, possibly creating unsafe driving conditions. The TPMS low tire pressure indicator is a yellow symbol that illuminates on the dashboard instrument panel in the shape of a tire cross-section (that resembles a horseshoe) with an exclamation point.
That indicator light in your vehicle has a history. It’s a history rooted in years of uncertainty about proper tire pressure and many serious car accidents that might have been avoided had drivers known their air pressure was low. Even now, it’s estimated that a substantial number of vehicles hit the road each day with underinflated tires. However, proper tire maintenance with the aid of a TPMS can and does help prevent many serious accidents.
How Does it Work?
Direct TPMS uses pressure monitoring sensors within each tire that monitor specific pressure levels.
Sensors in a direct TPMS may even provide tire temperature readings. The direct tire pressure monitoring system sends all of this data to a centralized control module where it’s analyzed, interpreted, and, if tire pressure is lower than it should be, transmitted directly to your dashboard where the indicator light illuminates. A direct tire pressure monitor usually sends all of this data wirelessly. Each sensor has a unique serial number. This is how the system not only distinguishes between itself and systems on other vehicles but also among pressure readings for each individual tire.
"from bridgestonetire.com"
What is BLE TMPS?
in short, it is a TPMS device that uses wireless communication with low-energy Bluetooth technology.
there are many universal BLE TPMS devices on the market that we can easily find. and the cheap ones usually have the same raw data reading protocol. The chap ones usually require an auxiliary application to be able to read pressure, temperature, and other data. This time I will create a display screen that can describe the data and place it on your motorcycle dashboard.
Using Arduino code (ESP32) to read the tire pressures, temperatures, battery levels, and alarms from the BLE ZEEPIN TPMS Sensors, TP630, etc.
The TPMS BLE data format
The devices cannot be connected or paired to and the devices do not receive any incoming BLE data. All data is broadcast as part of the "Manufacturer data" portion of the BLE advertisement. Manufacturer data looks like this:
000180EACA108A78E36D0000E60A00005B00
And now let's analyze in depth the received data:
bytes 0 and 1 -->0001 Manufacturer (see https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/)
byte 2 -->80 Sensor Number (80:1, 81:2, 82:3, 83:4, ...)
bytes 3 and 4 -->EACA Address Prefix
bytes 5, 6 and 7--> 108A78 Sensor Address --> usually listed on the TPMS box
bytes 8, 9, 10 and 11 -->E36D0000 Tire pressure (in kPA)
bytes 12, 13, 14 and 15 -->E60A0000 Tire Temperature (in Celsius)
byte 16 -->5B Battery Percentage
byte 17 -->00 Alarm Flag (00: Ok, 01: No pressure)
TPMS BLE "manufacturer data" format "000180eaca108a78e36d0000e60a00005b00" 0001 Manufacturer (0001: TomTom) 80 Sensor Number (80:1, 81:2, 82:3, 83:4, ..) 80eaca108a78 Sensor Address e36d0000 Pressure e60a0000 Temperature 5b Battery percentage 00 Alarm Flag (00: OK, 01: No Pressure Alarm) How calculate Sensor Address: (Sensor number):EA:CA:(Code binding reported in the leaflet) - i.e. 80:EA:CA:10:8A:78
Reference ra6070
Supplies
- 1 x 1,3-inch SPI TFT display ST7798
- 1 x Wemos ESP32 Mini
- Some cables as needed
- Case for mounting on the motorbike
- DC Step-down Buck Converter 3A
- 2 x Cheap Universal BLE TPMS sensors, buy from alibaba.com , from Amazon.com
- I use ST7798 because it's easy and it has a fast refresh rate
- Wemos ESP32 Mini has a small size, so it will fit in my case
- use a small cable so it fits in the case
- I use, a used motorcycle TPMS case for it because it's waterproof and strong enough
- Voltage regulator from 12v/24v to 5v for powering your ESP32 and Display
Preparing Your Arduino IDE
Follow preparation Step 1 until Step 3 from here?
ESP32 Programming
Change the knownAddresses[] list with your TPMS Device address. If you were not sure about your device address, you can use an application like Light blue explorer to look for it.
Flash the WEMOS ESP32 MINI with this code BLE_TPMS_ilte.ino, download from here or Here
//TFT #include <TFT_eSPI.h> TFT_eSPI tft = TFT_eSPI(); // Invoke library #include "splash.h" #include "BG.h" #include "alert.h" #include "noalert.h" // BLE Service #include "BLEDevice.h" BLEScan* pBLEScan; BLEClient* pClient; // Variables static BLEAddress *pServerAddress; // TPMS BLE SENSORS known addresses String knownAddresses[] = { "80:ea:ca:11:54:32" , "81:ea:ca:21:53:b7"}; // Change this with your TPMS Address String Rss01 ="888";String Bat01 ="888";String Tem01 ="88";String Pre01 ="88"; String Rss02 ="888";String Bat02 ="888";String Tem02 ="88";String Pre02 ="88"; boolean NewDat = true;boolean isAlt = true; static void notifyCallback( BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) { } class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { void onResult(BLEAdvertisedDevice Device){ pServerAddress = new BLEAddress(Device.getAddress()); bool known = false; bool Master = false; String ManufData = Device.toString().c_str(); for (int i = 0; i < (sizeof(knownAddresses) / sizeof(knownAddresses[0])); i++) { if (strcmp(pServerAddress->toString().c_str(), knownAddresses[i].c_str()) == 0) known = true; } if (known) { String instring=retmanData(ManufData, 0); NewDat= true; if (instring.substring(4,6)=="80") { Rss01=Device.getRSSI(); Bat01=(returnBatt(instring)); Tem01=(returnData(instring,12)/100.0); Pre01=(returnData(instring,8)/100000.0*14.5038); } else if (instring.substring(4,6)=="81") { Rss02=Device.getRSSI(); Bat02=(returnBatt(instring)); Tem02=(returnData(instring,12)/100.0); Pre02=(returnData(instring,8)/100000.0*14.5038); } if (returnAlarm(instring)) { isAlt = true; } else { isAlt = false; } Device.getScan()->stop(); delay(100); } } }; void setup() { delay(100); //TFT Init tft.begin (); // initialize a ST7789 chip tft.setSwapBytes (true); tft.setRotation(1); tft.fillScreen(TFT_BLACK); // BLE Init tft.setCursor(0, 0, 2); tft.setTextColor(TFT_WHITE,TFT_BLACK); tft.setTextSize(2); BLEDevice::init(""); pClient = BLEDevice::createClient(); pBLEScan = BLEDevice::getScan(); pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setActiveScan(true); tft.pushImage (0,0,240,240,splash); } void loop() { BLEScanResults scanResults = pBLEScan->start(5); if (NewDat) { tft.pushImage (0,0,240,240,background); tft.setTextColor(TFT_YELLOW); tft.setTextFont(7);tft.setTextSize(1); tft.setCursor(34, 115);tft.println (Pre01); tft.setCursor(139, 115);tft.println (Pre02); tft.setTextColor(TFT_WHITE);tft.setTextFont(4); tft.setCursor(45, 180);tft.println (Tem01); tft.setCursor(153, 180);tft.println (Tem02); tft.setTextColor(TFT_WHITE);tft.setTextFont(2); tft.setCursor(45, 207);tft.println (Bat01); tft.setCursor(90, 207);tft.println (Rss01); tft.setCursor(150, 207);tft.println (Bat02); tft.setCursor(195, 207);tft.println (Rss02); if (isAlt){ tft.pushImage(78, 13, 80, 71, alert); } else { tft.pushImage(78, 13, 80, 71, noalert); } NewDat=false; } } // FUNCTIONS String retmanData(String txt, int shift) { // Return only manufacturer data string int start=txt.indexOf("data: ")+6+shift; return txt.substring(start,start+(36-shift)); } byte retByte(String Data,int start) { // Return a single byte from string int sp=(start)*2; char *ptr; return strtoul(Data.substring(sp,sp+2).c_str(),&ptr, 16); } long returnData(String Data,int start) { // Return a long value with little endian conversion return retByte(Data,start)|retByte(Data,start+1)<<8|retByte(Data,start+2)<<16|retByte(Data,start+3)<<24; } int returnBatt(String Data) { // Return battery percentage return retByte(Data,16); } int returnAlarm(String Data) { // Return battery percentage return retByte(Data,17); }