Introduction: BLE Direct Tire Pressure Monitoring System (TPMS) Display Using ESP32

About: Designer, Artist, Engineer

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. 1 x 1,3-inch SPI TFT display ST7798
  2. 1 x Wemos ESP32 Mini
  3. Some cables as needed
  4. Case for mounting on the motorbike
  5. DC Step-down Buck Converter 3A
  6. 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

Step 1: Preparing Your Arduino IDE

Follow preparation Step 1 until Step 3 from here?

Step 2: 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);
}

Step 3: How to Edit the Image

Follow Step 7 from here?

Electronics Contest

Participated in the
Electronics Contest