025 – ESP32 MicroPython: ESP32 Bluetooth Low Energy

In this article, I will discussed how you can use ESP32 BLE capability using MicroPython.
Bluetooth Low Energy or BLE is very popular on most devices today because of its low power consumption. That is achieve by constantly switching to sleep mode then once in a while wakes up to process Bluetooth functions. With its low power requirements, it is best suited for battery operated applications.
As of this writing, Bluetooth Classic or the traditional Bluetooth communication is not yet supported by MicroPython community so we will just use the BLE instead. By following the example source code given below, you can implement both direction of communication using BLE.
BILL OF MATERIALS:
- ESP32 development board.
VIDEO DEMONSTRATION:
SOURCE CODE:
1. Example # 1: Demo sending and receiving BLE request:
from machine import Pin
from machine import Timer
from time import sleep_ms
import ubluetooth
ble_msg = ""
class ESP32_BLE():
def __init__(self, name):
# Create internal objects for the onboard LED
# blinking when no BLE device is connected
# stable ON when connected
self.led = Pin(2, Pin.OUT)
self.timer1 = Timer(0)
self.name = name
self.ble = ubluetooth.BLE()
self.ble.active(True)
self.disconnected()
self.ble.irq(self.ble_irq)
self.register()
self.advertiser()
def connected(self):
self.led.value(1)
self.timer1.deinit()
def disconnected(self):
self.timer1.init(period=100, mode=Timer.PERIODIC, callback=lambda t: self.led.value(not self.led.value()))
def ble_irq(self, event, data):
global ble_msg
if event == 1: #_IRQ_CENTRAL_CONNECT:
# A central has connected to this peripheral
self.connected()
elif event == 2: #_IRQ_CENTRAL_DISCONNECT:
# A central has disconnected from this peripheral.
self.advertiser()
self.disconnected()
elif event == 3: #_IRQ_GATTS_WRITE:
# A client has written to this characteristic or descriptor.
buffer = self.ble.gatts_read(self.rx)
ble_msg = buffer.decode('UTF-8').strip()
def register(self):
# Nordic UART Service (NUS)
NUS_UUID = '6E400001-B5A3-F393-E0A9-E50E24DCCA9E'
RX_UUID = '6E400002-B5A3-F393-E0A9-E50E24DCCA9E'
TX_UUID = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E'
BLE_NUS = ubluetooth.UUID(NUS_UUID)
BLE_RX = (ubluetooth.UUID(RX_UUID), ubluetooth.FLAG_WRITE)
BLE_TX = (ubluetooth.UUID(TX_UUID), ubluetooth.FLAG_NOTIFY)
BLE_UART = (BLE_NUS, (BLE_TX, BLE_RX,))
SERVICES = (BLE_UART, )
((self.tx, self.rx,), ) = self.ble.gatts_register_services(SERVICES)
def send(self, data):
self.ble.gatts_notify(0, self.tx, data + 'n')
def advertiser(self):
name = bytes(self.name, 'UTF-8')
adv_data = bytearray('x02x01x02') + bytearray((len(name) + 1, 0x09)) + name
self.ble.gap_advertise(100, adv_data)
print(adv_data)
print("rn")
# adv_data
# raw: 0x02010209094553503332424C45
# b'x02x01x02ttESP32BLE'
#
# 0x02 - General discoverable mode
# 0x01 - AD Type = 0x01
# 0x02 - value = 0x02
# https://jimmywongiot.com/2019/08/13/advertising-payload-format-on-ble/
# https://docs.silabs.com/bluetooth/latest/general/adv-and-scanning/bluetooth-adv-data-basics
led = Pin(2, Pin.OUT)
but = Pin(0, Pin.IN)
ble = ESP32_BLE("ESP32BLE")
def buttons_irq(pin):
led.value(not led.value())
ble.send('LED state will be toggled.')
print('LED state will be toggled.')
but.irq(trigger=Pin.IRQ_FALLING, handler=buttons_irq)
while True:
if ble_msg == 'read_LED':
print(ble_msg)
ble_msg = ""
print('LED is ON.' if led.value() else 'LED is OFF')
ble.send('LED is ON.' if led.value() else 'LED is OFF')
sleep_ms(100)
2. Example # 2, demo continuous sending of BLE message. In this example is_ble_connected global variable is used. This will represent if a device is connected on the BLE device. If you try to send a BLE message when no device is connected, it will throw an error OS Error: -128.:
from machine import Pin
from machine import Timer
from time import sleep_ms
import ubluetooth
ble_msg = ""
is_ble_connected = False
class ESP32_BLE():
def __init__(self, name):
# Create internal objects for the onboard LED
# blinking when no BLE device is connected
# stable ON when connected
self.led = Pin(2, Pin.OUT)
self.timer1 = Timer(0)
self.name = name
self.ble = ubluetooth.BLE()
self.ble.active(True)
self.disconnected()
self.ble.irq(self.ble_irq)
self.register()
self.advertiser()
def connected(self):
global is_ble_connected
is_ble_connected = True
self.led.value(1)
self.timer1.deinit()
def disconnected(self):
global is_ble_connected
is_ble_connected = False
self.timer1.init(period=100, mode=Timer.PERIODIC, callback=lambda t: self.led.value(not self.led.value()))
def ble_irq(self, event, data):
global ble_msg
if event == 1: #_IRQ_CENTRAL_CONNECT:
# A central has connected to this peripheral
self.connected()
elif event == 2: #_IRQ_CENTRAL_DISCONNECT:
# A central has disconnected from this peripheral.
self.advertiser()
self.disconnected()
elif event == 3: #_IRQ_GATTS_WRITE:
# A client has written to this characteristic or descriptor.
buffer = self.ble.gatts_read(self.rx)
ble_msg = buffer.decode('UTF-8').strip()
def register(self):
# Nordic UART Service (NUS)
NUS_UUID = '6E400001-B5A3-F393-E0A9-E50E24DCCA9E'
RX_UUID = '6E400002-B5A3-F393-E0A9-E50E24DCCA9E'
TX_UUID = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E'
BLE_NUS = ubluetooth.UUID(NUS_UUID)
BLE_RX = (ubluetooth.UUID(RX_UUID), ubluetooth.FLAG_WRITE)
BLE_TX = (ubluetooth.UUID(TX_UUID), ubluetooth.FLAG_NOTIFY)
BLE_UART = (BLE_NUS, (BLE_TX, BLE_RX,))
SERVICES = (BLE_UART, )
((self.tx, self.rx,), ) = self.ble.gatts_register_services(SERVICES)
def send(self, data):
self.ble.gatts_notify(0, self.tx, data + 'n')
def advertiser(self):
name = bytes(self.name, 'UTF-8')
adv_data = bytearray('x02x01x02') + bytearray((len(name) + 1, 0x09)) + name
self.ble.gap_advertise(100, adv_data)
print(adv_data)
print("rn")
# adv_data
# raw: 0x02010209094553503332424C45
# b'x02x01x02ttESP32BLE'
#
# 0x02 - General discoverable mode
# 0x01 - AD Type = 0x01
# 0x02 - value = 0x02
# https://jimmywongiot.com/2019/08/13/advertising-payload-format-on-ble/
# https://docs.silabs.com/bluetooth/latest/general/adv-and-scanning/bluetooth-adv-data-basics
ble = ESP32_BLE("ESP32BLE")
while True:
if is_ble_connected:
ble.send('Hello')
sleep_ms(1000)
Added Septemter 4, 2021.
REFERENCES AND CREDITS:
1. MicroPython BLE documents:
2. Micropython BLE examples:
Good to see such a nice blog post Best Application Services in USA
Hi,
Thanks for sharing.
The code below works and toggles the led's state.
However, the 'print' statement is not shown in Thonny. Why is that?
Thanks again.
import machine
button = machine.Pin(0, machine.Pin.IN)
led = machine.Pin(1, machine.Pin.OUT)
def buttons_irq(pin):
print('LED state will be toggled.')
led.value(not led.value())
button.irq(trigger=machine.Pin.IRQ_FALLING, handler=buttons_irq)
I just want to confirm if based from that code, does the on board led toggles if you pressed the BOOT button?
Hi. I am getting "Connection failed: gatt status 133" when I try connect from mobile via Serial Bluetooth Terminal. Did you met with this kind of error?
Hi @Arube, there are 2 examples given in the source code. What of that you use? I believe the error happens when you are sending messages through BLE while there is no BLE device connected to receive the said message.
George: Thanks for this tutorial! I'm trying to replicate it on an ESP32 board in Thonny, but getting "ImportError: no module named 'ubluetooth' "
(My board is flashed w/ micropython)
What troubleshooting should I try?
nevermind. I re-flashed micropython on my esp32 and now it works.
it works great!
Great post George! Thanks a lot for sharing, I'll test it and let you know, I'm trying to port a C++ sketch from Arduino IDE for ESP32, to uPython, and BT was the more dificult to find. Cheers!
Hi ! Thanks for this tutorial, and others on this website that helped me a lot !
However I'm stucked on this one : the python program starts, running, the ESP32BLE device is showing on potentials shared devices of the Android Bluetooth configuration, but it is not detected in Serial Bluetooth app (BLE section).
I've red elsewhere that ESP32 is not always detected as Low Energy device on Android. Maybe a configuration missing ?
Hello,
the Android application Open Camera https://opencamera.org.uk/help.html#remote_control allows shooting via BLE. According to the guide it is sufficient to send the key "5" I tried with your code by replacing the word "Hello" but it does not work.
Can you give me some advice? Thank you
Thank You, your code works great. How can I modify it to connect two ESP32 each other with BLE?