BLE Asset Tracking with ESP32: A Comprehensive Guide

Ibrahim Bin Mansur
5 min readAug 15, 2024

--

In the rapidly evolving world of IoT and smart devices, Bluetooth Low Energy (BLE) has emerged as a game-changing technology. Its low power consumption and widespread adoption make it ideal for various applications, including asset tracking. In this article, we’ll dive deep into creating a robust BLE-based asset tracking system using ESP32 microcontrollers.

The Setup

Our asset tracking system consists of two main components:

  1. Beacons: ESP32 devices that broadcast their presence using BLE.
  2. Central Device: An ESP32 that scans for beacons, calculates distances, and determines its position using trilateration.

Let’s explore the implementation details of each component.

The Beacon

Each beacon is an ESP32 device programmed to continuously advertise its presence using the iBeacon protocol. Here’s a detailed look at the beacon code:

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLEBeacon.h>

#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d"
#define BEACON_MAJOR 1 // Change this for each beacon
#define BEACON_MINOR 1 // Change this for each beacon

BLEAdvertising *pAdvertising;
BLEBeacon myBeacon;

void setup() {
Serial.begin(115200);
Serial.println("ESP32 BLE Beacon");
BLEDevice::init("ESP32 Beacon");
BLEServer *pServer = BLEDevice::createServer();
BLEBeacon oBeacon = BLEBeacon();
oBeacon.setManufacturerId(0x4C00); // Apple's company ID
oBeacon.setProximityUUID(BLEUUID(BEACON_UUID));
oBeacon.setMajor(BEACON_MAJOR);
oBeacon.setMinor(BEACON_MINOR);
oBeacon.setSignalPower(-59);

BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();

oAdvertisementData.setFlags(0x04); // BR_EDR_NOT_SUPPORTED 0x04

std::string strServiceData = "";

strServiceData += (char)26; // Len
strServiceData += (char)0xFF; // Type
strServiceData += oBeacon.getData();
oAdvertisementData.addData(strServiceData);

pAdvertising = BLEDevice::getAdvertising();
pAdvertising->setAdvertisementData(oAdvertisementData);
pAdvertising->setScanResponseData(oScanResponseData);
pAdvertising->start();
Serial.printf("Beacon %d started\n", BEACON_MINOR);
// Print debug information
Serial.printf("Beacon started with UUID: %s, Major: %d, Minor: %d\n",
BEACON_UUID, BEACON_MAJOR, BEACON_MINOR);

// Print out the raw manufacturer data
std::string beaconData = oBeacon.getData();
Serial.print("Raw manufacturer data: ");
for(int i = 0; i < beaconData.length(); i++) {
Serial.printf("%02X", (unsigned char)beaconData[i]);
}
Serial.println();
}

void loop() {
delay(1000);
}

Key points about the beacon code:

  1. We set a unique UUID for all beacons in the system, with different major and minor values to distinguish individual beacons.
  2. The signal power is set to -59 dBm, which is the reference signal strength at 1 meter distance.
  3. The beacon continuously advertises its presence once started.

The Central Device

The central device is the brain of our asset tracking system. It scans for beacons, estimates distances based on signal strength, and uses trilateration to calculate its position. Here’s a detailed look at the central device code:

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#include <map>
#include <math.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

#define SCAN_TIME 5 // seconds
#define MEASURED_POWER -59 // Calibrated TX power at 1 meter
#define N 2 // Path loss exponent (2 for free space)

const char* softAP_ssid = "ESP32 Asset Tracking";
const char* softAP_password = "";

AsyncWebServer server(80);

struct Beacon {
double x;
double y;
double distance;
};

std::map<int, Beacon> beacons = {
{1, {0.0, 0.0, 0.0}},
{2, {5.0, 0.0, 0.0}},
{3, {2.5, 5.0, 0.0}}
};

double central_x = 0.0;
double central_y = 0.0;


class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
// ... (Beacon detection and distance estimation code)
}
};


void trilateration() {
// ... (Trilateration calculation code)
}


void setup() {
Serial.begin(115200);
Serial.println("ESP32 S3 BLE Central");
BLEDevice::init("ESP32 S3 Central");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->setInterval(100);
pBLEScan->setWindow(99);
// Set up Soft AP
WiFi.softAP(softAP_ssid, softAP_password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
// Set up web server
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html);
});
server.on("/data", HTTP_GET, [](AsyncWebServerRequest *request){
// ... (JSON data response code)
});
server.begin();
}

void loop() {
Serial.println("Starting BLE scan...");
BLEScan* pBLEScan = BLEDevice::getScan();
BLEScanResults foundDevices = pBLEScan->start(10, false);
Serial.print("Devices found: ");
Serial.println(foundDevices.getCount());
Serial.println("Scan done!");

if (foundDevices.getCount() > 0) {
trilateration();
} else {
Serial.println("No devices found, skipping trilateration");
}

pBLEScan->clearResults();
delay(1000);
}

Key features of the central device:

  1. BLE Scanning: The device continuously scans for BLE advertisements from beacons.
  2. Distance Estimation: When a beacon is detected, the central device estimates its distance using the RSSI (Received Signal Strength Indicator):
double distance = pow(10, (MEASURED_POWER - rssi) / (10 * N));
  1. Trilateration: Using the estimated distances from three beacons, the central device calculates its position:
void trilateration() {
double x1 = beacons[1].x;
double y1 = beacons[1].y;
double r1 = beacons[1].distance;

double x2 = beacons[2].x;
double y2 = beacons[2].y;
double r2 = beacons[2].distance;

double x3 = beacons[3].x;
double y3 = beacons[3].y;
double r3 = beacons[3].distance;

// ... (calculation code)
central_x = (C*E - F*B) / (E*A - B*D);
central_y = (C*D - A*F) / (B*D - A*E);
Serial.printf("Estimated position: (%.2f, %.2f)\n", central_x, central_y);
}
  1. Web Interface: The central device sets up a web server, allowing users to view the tracking results in real-time through a web browser. The HTML and JavaScript for this interface are embedded in the code:
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>ESP32 Asset Tracking</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: Arial; margin:0px auto; padding: 20px; }
canvas { width: 100%; max-width: 800px; height: auto; }
#position { font-size: 1.2em; font-weight: bold; }
</style>
</head>
<body>
<h2>ESP32 Asset Tracking</h2>
<p>Estimated position: <span id="position"></span></p>
<canvas id="positionChart"></canvas>
<script>
// ... (JavaScript code for real-time updates and chart rendering)
</script>
</body>
</html>
)rawliteral";

Putting It All Together

When the system is up and running:

  1. The beacons continuously broadcast their presence using the iBeacon protocol.
  2. The central device scans for these beacons every 10 seconds.
  3. For each detected beacon, the central device estimates its distance based on the received signal strength.
  4. If at least three beacons are detected, the central device uses trilateration to determine its position.
  5. The calculated position is made available through a web interface, which users can access by connecting to the ESP32’s Wi-Fi access point.

Video

Challenges and Considerations

While this system provides a robust starting point for BLE-based asset tracking, there are several factors to consider for real-world applications:

  1. Signal Interference: Walls, furniture, and other obstacles can affect signal strength and distance estimates. The path loss exponent (N) might need adjustment based on the environment.
  2. Battery Life: Continuous broadcasting and scanning can drain battery quickly. Implementing power-saving strategies, such as reducing the advertising frequency for beacons or the scanning frequency for the central device, is crucial for long-term deployment.
  3. Accuracy: The accuracy of position estimates can vary based on factors like beacon placement and environmental conditions. More sophisticated algorithms or sensor fusion techniques might be necessary for higher accuracy.
  4. Scalability: The current implementation assumes a fixed number of beacons. For larger-scale deployments, you might need to implement dynamic beacon discovery and management.
  5. Security: The current implementation doesn’t include any security measures. For sensitive applications, you should implement encryption and authentication mechanisms.

Conclusion

BLE asset tracking with ESP32 offers a flexible and cost-effective solution for various applications, from inventory management to indoor navigation. By understanding the basics outlined in this article, you can start building your own tracking systems and exploring more advanced features.

Remember, the key to a successful IoT project is iterative development and testing. Start with this basic setup, and then expand and refine based on your specific needs and constraints. Consider factors like environmental interference, power consumption, and accuracy requirements as you develop your solution.

--

--

No responses yet