Introduction
Bluetooth Low Energy (BLE) Mesh is revolutionizing how devices communicate in large-scale IoT deployments. By extending BLE’s many‑to‑one model into a true many‑to‑many mesh, devices can relay messages across multiple hops—greatly expanding coverage and reliability. In this guide, you’ll learn how to build a BLE Mesh network using the ESP32 and ESP-IDF, controlling three LEDs wired to GPIO2, GPIO4, and GPIO5. We’ll use Espressif’s OnOff Server example as our firmware base, and the nRF Mesh mobile app for provisioning and control.
Whether you’re building a smart‑lighting system, sensor grid, or just exploring mesh topologies, this step‑by‑step tutorial has you covered. By the end, you’ll have a working mesh of ESP32 nodes that turn LEDs on/off via smartphone commands.
Table of Contents
- Understanding BLE Mesh Networking
- Components Required
- Setting Up the Development Environment
- Exploring the ESP-IDF OnOff Server Example
- Hardware Setup and Circuit Design
- Configuring the Firmware
- Building and Flashing the Firmware
- Provisioning and Controlling the Nodes
- Troubleshooting Common Issues
- Advanced Concepts and Further Reading
- Conclusion
1. Understanding BLE Mesh Networking
What Is BLE Mesh?
BLE Mesh extends Bluetooth Low Energy from simple point‑to‑point links into a true many‑to‑many network. In a BLE Mesh:
- Nodes communicate with each other directly or via relays.
- Messages can hop across multiple nodes, extending range.
- The network is self‑healing: if one node fails, messages reroute automatically.
This architecture makes BLE Mesh ideal for applications such as smart lighting, building automation, industrial sensor networks, and asset tracking. Unlike classic BLE (central/peripheral), BLE Mesh doesn’t require a central coordinator; any node can act as a relay or proxy, making the system highly scalable and robust.
Core Concepts
- Provisioning: Securely adding a node to the mesh, assigning it addresses and keys.
- Elements & Models: Each node can have one or more elements (logical units). Each element supports models—standardized interfaces (e.g., Generic OnOff) that define behavior.
- Configuration Server & Client Models: Used during setup to configure nodes (e.g., set publication addresses).
- Generic OnOff Model: A foundation model that turns an output (like an LED) on/off.
By mastering these concepts, you’ll be able to build flexible, large‑scale BLE Mesh deployments.
2. Components Required
Component | Description | Buy Link |
---|---|---|
ESP32 Dev Board | Dual‑core MCU with Wi‑Fi & BLE | ESP32‑WROOM‑32 (38‑Pin) |
5 mm Red LEDs (×3) | Visual indicators for On/Off commands | 5 mm Red LEDs |
Breadboard | Solderless prototyping board | 400‑Tie‑Points Mini Breadboard |
330 Ω Resistors (×3) | Current‑limiting resistors for LEDs | 330 Ohm Resistors |
Jumper Wires | For connections between ESP32 and breadboard | — |
USB Cable | For programming and powering the ESP32 | Micro USB Cable |
Android Smartphone | To run the nRF Mesh mobile app for provisioning/control | nRF Mesh app (Google Play) |
Gather these parts before proceeding. The total cost is modest, and all items can be purchased via the provided ElecSynergy links.
3. Setting Up the Development Environment
Before writing any code, ensure your machine is ready for ESP32 development.
3.1 Install ESP-IDF
- Clone the ESP-IDF repo (with submodules):
git clone --recursive https://github.com/espressif/esp-idf.git
- Run the installer (Linux/macOS):
cd esp-idf ./install.sh
- Export environment variables:
. ./export.sh
This setsIDF_PATH
and updates yourPATH
. - Verify by running:
idf.py --version
3.2 Required Tools
- Python 3.6+ (used by ESP-IDF scripts)
- CMake & Ninja (build system)
- Serial Terminal (e.g.,
miniterm
, PuTTY) foridf.py monitor
output
Once these are installed, you’re ready to build BLE Mesh examples.
4. Exploring the ESP-IDF OnOff Server Example
Espressif provides a ready‑made BLE Mesh example: OnOff Server, which you’ll adapt to control three LEDs.
4.1 Locating the Example
cd $IDF_PATH/examples/bluetooth/esp_ble_mesh/onoff_models/onoff_server
This directory contains:
main/
– application sourceCMakeLists.txt
– build configurationKconfig.projbuild
– menuconfig settings
4.2 How It Works
The OnOff Server example implements:
- Generic OnOff Server model: Responds to on/off messages.
- Provisioning logic: Handles mesh provisioning via callbacks.
- LED control stub: A placeholder function toggles a single LED.
We’ll extend this stub to manage three LEDs on GPIO2, GPIO4, and GPIO5.
Refer to Espressif’s BLE Mesh guide for more details on models and provisioning citeturn0search8.
5. Hardware Setup and Circuit Design
With the code base in place, let’s wire up the ESP32 and LEDs.
5.1 Pin Assignments
LED | ESP32 GPIO | Resistor | Breadboard Connection |
---|---|---|---|
LED 1 | GPIO2 | 330 Ω | Row A (Anode) → GPIO2; Row B (Cathode) → GND |
LED 2 | GPIO4 | 330 Ω | Row C (Anode) → GPIO4; Row D (Cathode) → GND |
LED 3 | GPIO5 | 330 Ω | Row E (Anode) → GPIO5; Row F (Cathode) → GND |
- Insert the breadboard and ESP32 so their rails align.
- Place each LED in its own row.
- Connect a 330 Ω resistor in series with each LED anode.
- Use jumper wires to connect resistors to GPIO2, GPIO4, GPIO5.
- Tie all LED cathodes to a common GND rail.
Visually verify no shorts exist. Power the ESP32 via USB; all LEDs should be off initially.
6. Configuring the Firmware
Now modify the OnOff Server example to:
- Initialize three GPIOs.
- Control each LED based on incoming mesh messages.
6.1 Define GPIOs
In main/board.h
, at the top:
#define LED1_GPIO 2
#define LED2_GPIO 4
#define LED3_GPIO 5
6.2 Initialize GPIOs
Replace the single‑LED init with:
struct _led_state led_state[3] = {
{ LED_OFF, LED_OFF, LED_R, "red" },
{ LED_OFF, LED_OFF, LED_G, "green" },
{ LED_OFF, LED_OFF, LED_B, "blue" },
};
static void board_led_init(void)
{
for (int i = 0; i < 3; i++) {
gpio_reset_pin(led_state[i].pin);
gpio_set_direction(led_state[i].pin, GPIO_MODE_OUTPUT);
gpio_set_level(led_state[i].pin, LED_OFF);
led_state[i].previous = LED_OFF;
}
}
6.3 Extend the OnOff Handler
The example’s Generic OnOff Server callback receives a message with:
- element index (which LED)
- onoff value (0 = off, 1 = on)
Locate the example_handle_gen_onoff_msg()
function and update:
static void example_handle_gen_onoff_msg(esp_ble_mesh_model_t *model,
esp_ble_mesh_msg_ctx_t *ctx,
esp_ble_mesh_server_recv_gen_onoff_set_t *set)
{
esp_ble_mesh_gen_onoff_srv_t *srv = (esp_ble_mesh_gen_onoff_srv_t *)model->user_data;
switch (ctx->recv_op) {
case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_GET:
esp_ble_mesh_server_model_send_msg(model, ctx,
ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, sizeof(srv->state.onoff), &srv->state.onoff);
break;
case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET:
case ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK:
if (set->op_en == false) {
srv->state.onoff = set->onoff;
} else {
/* TODO: Delay and state transition */
srv->state.onoff = set->onoff;
}
if (ctx->recv_op == ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_SET) {
esp_ble_mesh_server_model_send_msg(model, ctx,
ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS, sizeof(srv->state.onoff), &srv->state.onoff);
}
esp_ble_mesh_model_publish(model, ESP_BLE_MESH_MODEL_OP_GEN_ONOFF_STATUS,
sizeof(srv->state.onoff), &srv->state.onoff, ROLE_NODE);
example_change_led_state(model, ctx, srv->state.onoff);
break;
default:
break;
}
}
Download the full code here.
6.4 Update CMakeLists.txt
No changes needed if you’re still in the same folder; just ensure main/onoff_server.c
includes your modifications.
7. Building and Flashing the Firmware
With code updated, it’s time to build and flash.
- Set target (if not already):
idf.py set-target esp32
- Menuconfig (optional):
idf.py menuconfig
- Under Component config → Bluetooth Mesh, set Number of Elements to 3.
- Configure other mesh parameters as needed (TTL, relay, proxy).
- Build:
idf.py build
- Flash & Monitor:
idf.py -p /dev/ttyUSB0 flash monitor
You should see log output for mesh initialization:
I (1234) BLE_MESH: [BLE Mesh Initialized]
I (2345) BLE_MESH: Provisioning started
If you enabled proxy, you can connect via a standard BLE browser; otherwise use the provisioning flow.
8. Provisioning and Controlling the Nodes
8.1 Install nRF Mesh App
- Android: nRF Mesh on Google Play
- iOS: [nRF Mesh on App Store]
The nRF Mesh app allows you to provision, configure, and control nodes citeturn0search1.
8.2 Provisioning Steps
- Launch the nRF Mesh app and create a new network.
- Tap “+” to scan for unprovisioned devices. Your ESP32 nodes appear as “ESP_BLE_MESH”.
- Select a node (it will blink its LED to identify).
- Provision with No OOB or your chosen method.
- Repeat for all three nodes.
After provisioning, each node has:
- A unicast address (e.g., 0x0001, 0x0002, 0x0003).
- A network key and application key for secure messaging.
8.3 Binding and Publishing
For each node:
- Bind the Generic OnOff Server model to the App Key.
- Set Publication: Configure each server to publish its state changes to a group address (e.g., 0xC000).
- Subscribe: Optionally, have nodes subscribe to that group so they see each other’s messages.
8.4 Sending On/Off Commands
In the app’s Control tab:
- Select Generic OnOff.
- Choose Unicast to target a single node, or Group to broadcast.
- Tap ON or OFF.
You should see the corresponding LED turn on/off in real time.
9. Troubleshooting Common Issues
9.1 No Devices Found During Provisioning
- Ensure Bluetooth is enabled on your phone.
- Confirm ESP32 logs show “Advertising”.
- If using Proxy, ensure GATT Proxy is enabled in
menuconfig
.
9.2 LED Doesn’t Respond
- Double‑check GPIO wiring and resistor placement.
- Verify
example_led_onoff_set()
uses correctelement_index
. - Look for errors in the serial log (
idf.py monitor
).
9.3 Mesh Messages Not Relaying
- Enable Relay in
menuconfig
under Mesh Feature. - Increase TTL (Time To Live) if nodes are far apart.
- Ensure nodes subscribe/publish to correct addresses.
9.4 Provisioning Fails or Times Out
- Power‑cycle the ESP32 to reset advertising state.
- Use No OOB provisioning first; add security later.
- Ensure your App Key is the same for all nodes.
10. Advanced Concepts and Further Reading
10.1 Proxy Support
Enable GATT Proxy to allow non‑mesh BLE devices (like phones) to interact with the mesh via a single node.
10.2 Fast Provisioning
ESP-IDF supports Fast Provisioning for batch‑adding multiple nodes quickly. Explore the fast_prov examples.
10.3 Vendor Models
Create custom models to carry proprietary data. The vendor_models example shows how to define your own model IDs and payloads.
10.4 Coexistence with Wi‑Fi
ESP32 can run BLE Mesh alongside Wi‑Fi. The wifi_coexist example demonstrates maintaining mesh performance while streaming data over Wi‑Fi.
11. Conclusion
In this tutorial, you’ve learned how to build a BLE Mesh network using ESP32 and ESP-IDF, leveraging the official OnOff Server example to control three LEDs on GPIO2, GPIO4, and GPIO5. You saw how to:
- Set up your development environment
- Modify and extend the mesh example for multiple elements
- Wire hardware on a breadboard
- Build, flash, and monitor firmware
- Provision nodes and send on/off commands via the nRF Mesh app
BLE Mesh offers a powerful, scalable solution for IoT networks. With ESP32 and ESP-IDF, you have a cost‑effective platform to prototype and deploy robust mesh systems.
If you enjoyed this guide, don’t forget to subscribe to our YouTube channel for the full video demo and more projects. Visit our store to grab the components and start building today!
📽️ Watch the Full Demo
👉 BLE Mesh LED Control with ESP32 – YouTube Tutorial
🛒 Buy the Components
- ESP32‑WROOM‑32 (38‑Pin)
- 5 mm Red LEDs
- 400‑Tie‑Points Breadboard
- 330Ohm 0.25W Resistors
- Micro USB Cable
Ready to build your own BLE Mesh network?
Share your projects and questions in the comments below or reach out on our social channels. Happy building!
Also, check out our other playlist Rasa Chatbot, Internet of things, Docker, Python Programming, Machine Learning, Natural Language Processing, MQTT, Tech News, ESP-IDF etc.
Become a member of our social family on youtube here.
Stay tuned and Happy Learning. ✌🏻😃
Happy coding, and may your NLP endeavors be both enlightening and rewarding! ❤️🔥🚀🛠️🏡💡