🚀 Introducing ESP32 Bluetooth OTA Firmware Update Library

🚀 Introducing ESP32 Bluetooth OTA Firmware Update Library

Publish Date: Aug 21
0 0

Wirelessly update ESP32 firmware using Bluetooth Low Energy (BLE) with this Arduino-compatible library for IoT and robotics projects.

Updating ESP32 devices in the field can be challenging without USB or Wi-Fi access. Traditional Wi-Fi-based OTA (Over-The-Air) updates require network connectivity, but what if your project is offline? My ESP32 BLE OTA Library simplifies Bluetooth firmware updates for Arduino and PlatformIO, enabling wireless updates via mobile apps or BLE devices.

View on GitHub | Explore Wiki

Table of Contents

Why Bluetooth OTA for ESP32? 🔧

This Arduino-compatible library enables ESP32 OTA tutorials for IoT, robotics, and consumer electronics projects. Key benefits include:

  • No Wi-Fi Required: Updates work offline, ideal for remote devices.
  • Simple Integration: Add OTA with minimal code in Arduino IDE or PlatformIO.
  • Configurable UUIDs: Customize BLE service and characteristic UUIDs.
  • Cross-Platform: Supports Android, iOS, and desktop BLE clients.
  • Secure & Reliable: Ensures data integrity with progress monitoring and error recovery.

Installation 📦

Install via Arduino Library Manager:

  1. Open Arduino IDE.
  2. Go to Tools > Manage Libraries.
  3. Search for BLE OTA Update.
  4. Select the latest version and click Install.

For PlatformIO, add to platformio.ini:

lib_deps = Raghav117/bluetooth_ota_firmware_update
Enter fullscreen mode Exit fullscreen mode

Arduino Library Manager showing BLE OTA Update library installation

How to Use 🖥️

This Bluetooth firmware update Arduino example shows how to set up OTA and control an LED.

Basic Setup

Include the library and define custom UUIDs:

#include <BLEOtaUpdate.h>

const char* CUSTOM_SERVICE_UUID = "12345678-1234-1234-1234-123456789ABC";
const char* CUSTOM_OTA_CHAR_UUID = "87654321-4321-4321-4321-CBA987654321";
const char* CUSTOM_COMMAND_CHAR_UUID = "11111111-2222-3333-4444-555555555555";
const char* CUSTOM_STATUS_CHAR_UUID = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE";

BLEOtaUpdate bleOta(CUSTOM_SERVICE_UUID, CUSTOM_OTA_CHAR_UUID, CUSTOM_COMMAND_CHAR_UUID, CUSTOM_STATUS_CHAR_UUID);
const int LED_PIN = 2;
Enter fullscreen mode Exit fullscreen mode

UUID Purposes:

  • CUSTOM_SERVICE_UUID: Groups OTA-related characteristics (container).
  • CUSTOM_OTA_CHAR_UUID: Transfers firmware binary in chunks.
  • CUSTOM_COMMAND_CHAR_UUID: Sends non-OTA commands (e.g., "LED_ON").
  • CUSTOM_STATUS_CHAR_UUID: Reports OTA progress and status.

Callbacks

Set up callbacks to monitor OTA and handle commands:

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);

  // Set callbacks
  bleOta.setOtaProgressCallback(onOtaProgress);
  bleOta.setOtaStatusCallback(onOtaStatus);
  bleOta.setCommandCallback(onCommand);
  bleOta.setConnectionCallback(onConnection);

  // Start OTA service
  bleOta.begin("ESP32-OTA-Device");
  Serial.println("Ready for OTA update via Bluetooth!");
}

void onOtaProgress(int progress) {
  Serial.printf("OTA Progress: %d%%\n", progress);
  digitalWrite(LED_PIN, progress % 2); // Blink LED
}

void onOtaStatus(OtaStatus status) {
  if (status == OTA_COMPLETED) {
    Serial.println("OTA Update Finished!");
    digitalWrite(LED_PIN, HIGH);
  } else if (status == OTA_FAILED) {
    Serial.println("OTA Update Failed!");
    digitalWrite(LED_PIN, LOW);
  }
}

void onCommand(String cmd) {
  Serial.print("Received Command: ");
  Serial.println(cmd);
  if (cmd == "LED_ON") digitalWrite(LED_PIN, HIGH);
  else if (cmd == "LED_OFF") digitalWrite(LED_PIN, LOW);
}

void onConnection(bool connected) {
  Serial.println(connected ? "Device connected!" : "Device disconnected!");
}
Enter fullscreen mode Exit fullscreen mode
  • Serial.begin(115200): Starts serial communication for logs.
  • pinMode(LED_PIN, OUTPUT): Configures LED for feedback.
  • Callbacks: Monitor progress, status, commands, and connections.

Example Workflow

A minimal example for beginners:

#include <BLEOtaUpdate.h>

BLEOtaUpdate bleOta;
const int LED_PIN = 2;

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  bleOta.setOtaStatusCallback([](OtaStatus status) {
    Serial.println(status == OTA_COMPLETED ? "OTA Success!" : "OTA Failed!");
    digitalWrite(LED_PIN, status == OTA_COMPLETED ? HIGH : LOW);
  });
  bleOta.begin("ESP32-OTA-Device");
}

void loop() {
  // No code needed; OTA is handled by the library
}
Enter fullscreen mode Exit fullscreen mode

More examples (minimal, advanced, UUID customization) are in the Examples Folder.

Writing a Cross-Platform OTA Client 📱

Create a BLE client (e.g., mobile app) to update ESP32 firmware. MTU (Maximum Transmission Unit) is the max data size per BLE packet; chunk size splits firmware for transfer.

Step 0: Define UUIDs

Match UUIDs with ESP32 firmware:

const SERVICE_UUID = "12345678-1234-5678-9ABC-DEF012345678"; // OTA Service
const OTA_CHAR_UUID = "87654321-4321-8765-CBA9-FEDCBA987654"; // Firmware transfer
const COMMAND_CHAR_UUID = "11111111-2222-3333-4444-555555555555"; // Commands
const STATUS_CHAR_UUID = "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"; // Status
Enter fullscreen mode Exit fullscreen mode

Step 1: Enable Bluetooth & Permissions

enableBluetooth();
requestPermissions();
Enter fullscreen mode Exit fullscreen mode

Step 2: Scan for OTA-Enabled Device

startScan({ services: [SERVICE_UUID] });
const device = await userSelectsDeviceFromResults();
Enter fullscreen mode Exit fullscreen mode

Step 3: Connect to Device

await device.connect({ timeout: 15000 });
await device.requestMtu(247); // Max packet size
Enter fullscreen mode Exit fullscreen mode

Step 4: Discover Services & Characteristics

const service = await device.getPrimaryService(SERVICE_UUID);
const otaChar = await service.getCharacteristic(OTA_CHAR_UUID);
const cmdChar = await service.getCharacteristic(COMMAND_CHAR_UUID);
const stsChar = await service.getCharacteristic(STATUS_CHAR_UUID);
Enter fullscreen mode Exit fullscreen mode

Step 5: Select Firmware File

const firmware = await pickFile({ extensions: [".bin"] });
if (!firmware) throw new Error("No firmware selected");
Enter fullscreen mode Exit fullscreen mode

Step 6: Compute Chunk Size

const chunkSize = (device.currentMtu > 0 ? device.currentMtu : 23) - 3;
Enter fullscreen mode Exit fullscreen mode

Step 7: Start OTA Transfer

await otaChar.writeWithoutResponse(new TextEncoder().encode("OPEN"));
await otaChar.writeWithoutResponse(
  new DataView(new ArrayBuffer(4)).setUint32(0, firmware.length, false).buffer
);
Enter fullscreen mode Exit fullscreen mode

Step 8: Send Firmware in Chunks

for (const chunk of splitArray(firmware.bytes, chunkSize)) {
  await otaChar.writeWithoutResponse(chunk);
  await new Promise(resolve => setTimeout(resolve, 25)); // Avoid overflow
}
await otaChar.writeWithResponse(new TextEncoder().encode("DONE"));
Enter fullscreen mode Exit fullscreen mode

Bonus: Sending Non-OTA Commands

Send custom commands:

await cmdChar.writeWithoutResponse(new TextEncoder().encode("LED_ON"));
await cmdChar.writeWithoutResponse(new TextEncoder().encode("SPEED_80"));
Enter fullscreen mode Exit fullscreen mode

Summary:

  • Scan, connect, discover services.
  • Select and split firmware into chunks.
  • Send OTA, confirm completion, disconnect.
  • Use Command Characteristic for device control (e.g., LEDs, motors).

See a full OTA client in the GitHub Examples.

Conclusion 🏁

Bluetooth OTA transforms IoT development by enabling wireless ESP32 firmware updates without USB or Wi-Fi. This library simplifies the process with:

  • Ready-to-use OTA service and characteristics.
  • Flexible UUID configuration.
  • Command-based control alongside OTA.

Whether building smart gadgets, robotics, or prototypes, this library keeps your ESP32 devices up-to-date and future-proof. Try it using the GitHub repo examples.

Contributing 🤝

Feedback and contributions are welcome! To contribute:

  1. Open an issue on GitHub.
  2. Discuss on X or Arduino Forums.
  3. Submit a pull request.

Check the Changelog for updates.

Further Reading 📖


Comments 0 total

    Add comment