fbpixel
Tags: , , ,

In this tutorial, we’ll learn how to manage and test BLE (Bluetooth Low Energy) communication on an ESP32 with MicroPython.

Equipment

  • An ESP32 module
  • A computer with Python installed
  • USB cable for ESP32-computer connection
  • An Android device

IDE environment and configuration

To communicate and program your ESP32 in Python, you can follow this previous tutorial for using MicroPython.

You can also install the BLE Terminal application on your Android phone to test BLE communication.

Activation du Bluetooth LE

To activate BLE on your EPS32, copy the following micropython code into the Thonny IDE editor and export it to your module. In this example, we use the bluetooth library (alternative option ubluetooth)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import bluetooth #https://docs.micropython.org/en/latest/library/bluetooth.html
import ubinascii
def main():
BLE = bluetooth.BLE()
BLE.active(True)
#Advertise
name = bytes("ESP32BLEmPy", 'UTF-8')
adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
BLE.gap_advertise(100, adv_data)
#get MAC address
mac = BLE.config('mac')[1]
print("device MAC address is: "+ubinascii.hexlify(mac).decode())
#print("device MAC address is: "+mac.hex())
if __name__ == "__main__":
main()
import bluetooth #https://docs.micropython.org/en/latest/library/bluetooth.html import ubinascii def main(): BLE = bluetooth.BLE() BLE.active(True) #Advertise name = bytes("ESP32BLEmPy", 'UTF-8') adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name BLE.gap_advertise(100, adv_data) #get MAC address mac = BLE.config('mac')[1] print("device MAC address is: "+ubinascii.hexlify(mac).decode()) #print("device MAC address is: "+mac.hex()) if __name__ == "__main__": main()
import bluetooth #https://docs.micropython.org/en/latest/library/bluetooth.html
import ubinascii

def main():
    BLE = bluetooth.BLE()
    BLE.active(True)

    #Advertise
    name = bytes("ESP32BLEmPy", 'UTF-8')
    adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
    BLE.gap_advertise(100, adv_data)

    #get MAC address
    mac = BLE.config('mac')[1]
    print("device MAC address is: "+ubinascii.hexlify(mac).decode())
    #print("device MAC address is: "+mac.hex())



if __name__ == "__main__":
    main()

N.B.: it is possible to retrieve the MAC address in hexadecimal format using the hex() function or the ubinascii library.

Once the code has been loaded and the announcement activated, you can find the device in the BLE Terminal application.

esp32-ble-device-advertised BLE management on an ESP32 with MicroPython

IMPORTANT: annunciation must be activated after each disconnection in order to reconnect the ESP32.

Register services

BLE communication is based on the concept of services and features with certain access rights (read, write, notify). There are default services (Nordic UART service (NUS), Heart rate (HR) or you can create your own services with unique UUID identifiers.

A service has a unique UUID and can contain different features. Each feature is defined by a unique UUID and different access rights.

  • The bluetooth.UUID function is used to define service addresses and features.
  • bluetooth.FLAG_READ gives read access to the client
  • bluetooth.FLAG_NOTIFY notifies the customer without any action on his part
  • bluetooth.FLAG_WRITE gives write access to the client
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#register
HR_UUID = bluetooth.UUID(0x180D)
HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
HR_SERVICE = (HR_UUID, (HR_CHAR,),)
UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b")
SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,)
MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),)
SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE)
( (hr,), (tx, rx,), (sen,cmd,), ) = BLE.gatts_register_services(SERVICES)
BLE.gatts_write(sen, str(43.256), False) #init val to be read
#register HR_UUID = bluetooth.UUID(0x180D) HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) HR_SERVICE = (HR_UUID, (HR_CHAR,),) UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E') UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,) UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),) MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b") SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,) MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),) SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE) ( (hr,), (tx, rx,), (sen,cmd,), ) = BLE.gatts_register_services(SERVICES) BLE.gatts_write(sen, str(43.256), False) #init val to be read
    #register
    HR_UUID = bluetooth.UUID(0x180D)
    HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
    HR_SERVICE = (HR_UUID, (HR_CHAR,),)
    UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
    UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
    UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
    UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
    
    MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b")
    SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
    CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,)
    MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),)
    
    SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE)
    ( (hr,), (tx, rx,), (sen,cmd,), ) = BLE.gatts_register_services(SERVICES)
    
    BLE.gatts_write(sen, str(43.256), False) #init val to be read

N.B: advertising must be stopped before registering services.

esp32-ble-custom-services BLE management on an ESP32 with MicroPython

Define event functions

To manage the BLE module correctly, we will create callback functions to detect and act on various events.

To do this, we need to know the different event identifiers

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
from micropython import const
_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)
_IRQ_GATTS_READ_REQUEST = const(4)
_IRQ_SCAN_RESULT = const(5)
_IRQ_SCAN_DONE = const(6)
_IRQ_PERIPHERAL_CONNECT = const(7)
_IRQ_PERIPHERAL_DISCONNECT = const(8)
_IRQ_GATTC_SERVICE_RESULT = const(9)
_IRQ_GATTC_SERVICE_DONE = const(10)
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
_IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
_IRQ_GATTC_DESCRIPTOR_RESULT = const(13)
_IRQ_GATTC_DESCRIPTOR_DONE = const(14)
_IRQ_GATTC_READ_RESULT = const(15)
_IRQ_GATTC_READ_DONE = const(16)
_IRQ_GATTC_WRITE_DONE = const(17)
_IRQ_GATTC_NOTIFY = const(18)
_IRQ_GATTC_INDICATE = const(19)
_IRQ_GATTS_INDICATE_DONE = const(20)
_IRQ_MTU_EXCHANGED = const(21)
_IRQ_L2CAP_ACCEPT = const(22)
_IRQ_L2CAP_CONNECT = const(23)
_IRQ_L2CAP_DISCONNECT = const(24)
_IRQ_L2CAP_RECV = const(25)
_IRQ_L2CAP_SEND_READY = const(26)
_IRQ_CONNECTION_UPDATE = const(27)
_IRQ_ENCRYPTION_UPDATE = const(28)
_IRQ_GET_SECRET = const(29)
_IRQ_SET_SECRET = const(30)
from micropython import const _IRQ_CENTRAL_CONNECT = const(1) _IRQ_CENTRAL_DISCONNECT = const(2) _IRQ_GATTS_WRITE = const(3) _IRQ_GATTS_READ_REQUEST = const(4) _IRQ_SCAN_RESULT = const(5) _IRQ_SCAN_DONE = const(6) _IRQ_PERIPHERAL_CONNECT = const(7) _IRQ_PERIPHERAL_DISCONNECT = const(8) _IRQ_GATTC_SERVICE_RESULT = const(9) _IRQ_GATTC_SERVICE_DONE = const(10) _IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) _IRQ_GATTC_CHARACTERISTIC_DONE = const(12) _IRQ_GATTC_DESCRIPTOR_RESULT = const(13) _IRQ_GATTC_DESCRIPTOR_DONE = const(14) _IRQ_GATTC_READ_RESULT = const(15) _IRQ_GATTC_READ_DONE = const(16) _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) _IRQ_GATTS_INDICATE_DONE = const(20) _IRQ_MTU_EXCHANGED = const(21) _IRQ_L2CAP_ACCEPT = const(22) _IRQ_L2CAP_CONNECT = const(23) _IRQ_L2CAP_DISCONNECT = const(24) _IRQ_L2CAP_RECV = const(25) _IRQ_L2CAP_SEND_READY = const(26) _IRQ_CONNECTION_UPDATE = const(27) _IRQ_ENCRYPTION_UPDATE = const(28) _IRQ_GET_SECRET = const(29) _IRQ_SET_SECRET = const(30)
from micropython import const

_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)
_IRQ_GATTS_READ_REQUEST = const(4)
_IRQ_SCAN_RESULT = const(5)
_IRQ_SCAN_DONE = const(6)
_IRQ_PERIPHERAL_CONNECT = const(7)
_IRQ_PERIPHERAL_DISCONNECT = const(8)
_IRQ_GATTC_SERVICE_RESULT = const(9)
_IRQ_GATTC_SERVICE_DONE = const(10)
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
_IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
_IRQ_GATTC_DESCRIPTOR_RESULT = const(13)
_IRQ_GATTC_DESCRIPTOR_DONE = const(14)
_IRQ_GATTC_READ_RESULT = const(15)
_IRQ_GATTC_READ_DONE = const(16)
_IRQ_GATTC_WRITE_DONE = const(17)
_IRQ_GATTC_NOTIFY = const(18)
_IRQ_GATTC_INDICATE = const(19)
_IRQ_GATTS_INDICATE_DONE = const(20)
_IRQ_MTU_EXCHANGED = const(21)
_IRQ_L2CAP_ACCEPT = const(22)
_IRQ_L2CAP_CONNECT = const(23)
_IRQ_L2CAP_DISCONNECT = const(24)
_IRQ_L2CAP_RECV = const(25)
_IRQ_L2CAP_SEND_READY = const(26)
_IRQ_CONNECTION_UPDATE = const(27)
_IRQ_ENCRYPTION_UPDATE = const(28)
_IRQ_GET_SECRET = const(29)
_IRQ_SET_SECRET = const(30)

We can then use it to define actions for each event

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#event callback function
def ble_irq(event,data):
if event == _IRQ_CENTRAL_CONNECT:
# A central has connected to this peripheral.
conn_handle, addr_type, addr = data
print("BLE device connected successfully")
elif event == _IRQ_CENTRAL_DISCONNECT:
# A central has disconnected from this peripheral.
conn_handle, addr_type, addr = data
print("BLE device disconnected")
adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
BLE.gap_advertise(100, adv_data)
elif event == _IRQ_GATTS_WRITE:
# A client has written to this characteristic or descriptor.
conn_handle, attr_handle = data
print("write event: ",BLE.gatts_read(data[1]).decode('UTF-8').strip())
elif event == _IRQ_GATTS_READ_REQUEST:
# A client has issued a read. Note: this is only supported on STM32.
# Return a non-zero integer to deny the read (see below), or zero (or None)
# to accept the read.
conn_handle, attr_handle = data
print("read event: ",data)
#event callback function def ble_irq(event,data): if event == _IRQ_CENTRAL_CONNECT: # A central has connected to this peripheral. conn_handle, addr_type, addr = data print("BLE device connected successfully") elif event == _IRQ_CENTRAL_DISCONNECT: # A central has disconnected from this peripheral. conn_handle, addr_type, addr = data print("BLE device disconnected") adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name BLE.gap_advertise(100, adv_data) elif event == _IRQ_GATTS_WRITE: # A client has written to this characteristic or descriptor. conn_handle, attr_handle = data print("write event: ",BLE.gatts_read(data[1]).decode('UTF-8').strip()) elif event == _IRQ_GATTS_READ_REQUEST: # A client has issued a read. Note: this is only supported on STM32. # Return a non-zero integer to deny the read (see below), or zero (or None) # to accept the read. conn_handle, attr_handle = data print("read event: ",data)
#event callback function
def ble_irq(event,data):
    if event == _IRQ_CENTRAL_CONNECT:
        # A central has connected to this peripheral.
        conn_handle, addr_type, addr = data
        print("BLE device connected successfully")
    elif event == _IRQ_CENTRAL_DISCONNECT:
        # A central has disconnected from this peripheral.
        conn_handle, addr_type, addr = data
        print("BLE device disconnected")
        adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
        BLE.gap_advertise(100, adv_data)
        
    elif event == _IRQ_GATTS_WRITE:
        # A client has written to this characteristic or descriptor.
        conn_handle, attr_handle = data
        print("write event: ",BLE.gatts_read(data[1]).decode('UTF-8').strip())
    elif event == _IRQ_GATTS_READ_REQUEST:
        # A client has issued a read. Note: this is only supported on STM32.
        # Return a non-zero integer to deny the read (see below), or zero (or None)
        # to accept the read.
        conn_handle, attr_handle = data
        print("read event: ",data)

Results

With this simple code, you can connect to the device and then read and write to the chosen service in the application

esp32-ble-read-write BLE management on an ESP32 with MicroPython

 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
MicroPython v1.22.1 on 2024-01-05; Generic ESP32 module with ESP32
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
device MAC address is: 3c6105315f12
>>> BLE device connected successfully
BLE device disconnected
BLE device connected successfully
write event: hello
read event: (0, 31)
MicroPython v1.22.1 on 2024-01-05; Generic ESP32 module with ESP32 Type "help()" for more information. >>> %Run -c $EDITOR_CONTENT device MAC address is: 3c6105315f12 >>> BLE device connected successfully BLE device disconnected BLE device connected successfully write event: hello read event: (0, 31)
MicroPython v1.22.1 on 2024-01-05; Generic ESP32 module with ESP32

Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT
device MAC address is: 3c6105315f12
>>> BLE device connected successfully
BLE device disconnected
BLE device connected successfully
write event:  hello
read event:  (0, 31)

 

Creation of a MicroPython class to manage ESP32 BLE communication

It’s a good idea to create a class to manage BLE communication that you can reuse in different projects. In the class code, you’ll find all the elements described above

  • BLE initialization and activation
  • definition of callback functions self.ble_irq
  • service registration self.register
  • announcement self.advertise
  • We’ve also added a notification function to update the value of a sensor self.set_sensor

 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class ESP32BLE():
def __init__(self, name):
# Create BLE device management
self.name = name
self.ble = bluetooth.BLE()
self.ble.active(True)
#get MAC address
mac = self.ble.config('mac')[1]
print("device MAC address is: "+mac.hex())
self.ble.irq(self.ble_irq)
self.connections = set()
self.register()
self.advertise()
def ble_irq(self, event, data):
#define event callback functions
if event == _IRQ_CENTRAL_CONNECT:
# A central has connected to this peripheral.
conn_handle, addr_type, addr = data
self.connections.add(conn_handle)
print("BLE device connected successfully")
elif event == _IRQ_CENTRAL_DISCONNECT:
# A central has disconnected from this peripheral.
conn_handle, addr_type, addr = data
self.connections.remove(conn_handle)
print("BLE device disconnected")
self.advertise()
elif event == _IRQ_GATTS_WRITE:
# A client has written to this characteristic or descriptor.
conn_handle, attr_handle = data
print("write event: ",self.ble.gatts_read(data[1]).decode('UTF-8').strip())
elif event == _IRQ_GATTS_READ_REQUEST:
# A client has issued a read. Note: this is only supported on STM32.
# Return a non-zero integer to deny the read (see below), or zero (or None)
# to accept the read.
conn_handle, attr_handle = data
print("read event: ",data)
def register(self):
#define services and characteristics
HR_UUID = bluetooth.UUID(0x180D)
HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
HR_SERVICE = (HR_UUID, (HR_CHAR,),)
UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b")
SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,)
MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),)
SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE)
( (self.hr,), (self.tx, self.rx,), (self.sen,self.cmd,), ) = self.ble.gatts_register_services(SERVICES)
self.ble.gatts_write(self.sen, str(43.256), False)
def advertise(self):
#advertise BLE module with name
name = bytes(self.name, 'UTF-8')
adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
self.ble.gap_advertise(100, adv_data)
def set_sensor(self, data, notify=False):
# Data is sint16 with a resolution of 0.01.
self.ble.gatts_write(self.sen, str(data))
if notify:
for conn_handle in self.connections:
# Notify connected centrals to issue a read.
self.ble.gatts_notify(conn_handle, self.sen)
class ESP32BLE(): def __init__(self, name): # Create BLE device management self.name = name self.ble = bluetooth.BLE() self.ble.active(True) #get MAC address mac = self.ble.config('mac')[1] print("device MAC address is: "+mac.hex()) self.ble.irq(self.ble_irq) self.connections = set() self.register() self.advertise() def ble_irq(self, event, data): #define event callback functions if event == _IRQ_CENTRAL_CONNECT: # A central has connected to this peripheral. conn_handle, addr_type, addr = data self.connections.add(conn_handle) print("BLE device connected successfully") elif event == _IRQ_CENTRAL_DISCONNECT: # A central has disconnected from this peripheral. conn_handle, addr_type, addr = data self.connections.remove(conn_handle) print("BLE device disconnected") self.advertise() elif event == _IRQ_GATTS_WRITE: # A client has written to this characteristic or descriptor. conn_handle, attr_handle = data print("write event: ",self.ble.gatts_read(data[1]).decode('UTF-8').strip()) elif event == _IRQ_GATTS_READ_REQUEST: # A client has issued a read. Note: this is only supported on STM32. # Return a non-zero integer to deny the read (see below), or zero (or None) # to accept the read. conn_handle, attr_handle = data print("read event: ",data) def register(self): #define services and characteristics HR_UUID = bluetooth.UUID(0x180D) HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) HR_SERVICE = (HR_UUID, (HR_CHAR,),) UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E') UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,) UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),) MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b") SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,) MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),) SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE) ( (self.hr,), (self.tx, self.rx,), (self.sen,self.cmd,), ) = self.ble.gatts_register_services(SERVICES) self.ble.gatts_write(self.sen, str(43.256), False) def advertise(self): #advertise BLE module with name name = bytes(self.name, 'UTF-8') adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name self.ble.gap_advertise(100, adv_data) def set_sensor(self, data, notify=False): # Data is sint16 with a resolution of 0.01. self.ble.gatts_write(self.sen, str(data)) if notify: for conn_handle in self.connections: # Notify connected centrals to issue a read. self.ble.gatts_notify(conn_handle, self.sen)
class ESP32BLE():
    def __init__(self, name):
        # Create BLE device management
        self.name = name
        self.ble = bluetooth.BLE()
        self.ble.active(True)
        #get MAC address
        mac = self.ble.config('mac')[1]
        print("device MAC address is: "+mac.hex())
        self.ble.irq(self.ble_irq)
        self.connections = set()
        self.register()
        self.advertise()

    def ble_irq(self, event, data):
        #define event callback functions
        if event == _IRQ_CENTRAL_CONNECT:
            # A central has connected to this peripheral.
            conn_handle, addr_type, addr = data
            self.connections.add(conn_handle)
            print("BLE device connected successfully")
        elif event == _IRQ_CENTRAL_DISCONNECT:
            # A central has disconnected from this peripheral.
            conn_handle, addr_type, addr = data
            self.connections.remove(conn_handle)
            print("BLE device disconnected")
            self.advertise()
        elif event == _IRQ_GATTS_WRITE:
            # A client has written to this characteristic or descriptor.
            conn_handle, attr_handle = data
            print("write event: ",self.ble.gatts_read(data[1]).decode('UTF-8').strip())
        elif event == _IRQ_GATTS_READ_REQUEST:
            # A client has issued a read. Note: this is only supported on STM32.
            # Return a non-zero integer to deny the read (see below), or zero (or None)
            # to accept the read.
            conn_handle, attr_handle = data
            print("read event: ",data)
            
    def register(self):
        #define services and characteristics
        HR_UUID = bluetooth.UUID(0x180D)
        HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
        HR_SERVICE = (HR_UUID, (HR_CHAR,),)
        UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
        UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
        UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
        UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
        
        MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b")
        SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
        CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,)
        MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),)
        
        SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE)
        ( (self.hr,), (self.tx, self.rx,), (self.sen,self.cmd,), ) = self.ble.gatts_register_services(SERVICES)
        
        self.ble.gatts_write(self.sen, str(43.256), False)

    
    def advertise(self):
        #advertise BLE module with name
        name = bytes(self.name, 'UTF-8')
        adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
        self.ble.gap_advertise(100, adv_data)
        
    def set_sensor(self, data, notify=False):
        # Data is sint16 with a resolution of 0.01.
        self.ble.gatts_write(self.sen, str(data))
        if notify:
            for conn_handle in self.connections:
                # Notify connected centrals to issue a read.
                self.ble.gatts_notify(conn_handle, self.sen)

 

esp32-ble-write-notify BLE management on an ESP32 with MicroPython

 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
BLE device disconnected
BLE device connected successfully
write event: hello
read event: (65535, 31)
read event: (0, 31)
read event: (65535, 31)
read event: (0, 31)
read event: (65535, 31)
read event: (0, 31)
write event: hello World
read event: (65535, 31)
read event: (0, 31)
read event: (65535, 31)
read event: (0, 31)
BLE device disconnected BLE device connected successfully write event: hello read event: (65535, 31) read event: (0, 31) read event: (65535, 31) read event: (0, 31) read event: (65535, 31) read event: (0, 31) write event: hello World read event: (65535, 31) read event: (0, 31) read event: (65535, 31) read event: (0, 31)
BLE device disconnected
BLE device connected successfully
write event:  hello
read event:  (65535, 31)
read event:  (0, 31)
read event:  (65535, 31)
read event:  (0, 31)
read event:  (65535, 31)
read event:  (0, 31)
write event:  hello World
read event:  (65535, 31)
read event:  (0, 31)
read event:  (65535, 31)
read event:  (0, 31)

 

Complete BLE ESP32 management code with micropython

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import bluetooth #https://docs.micropython.org/en/latest/library/bluetooth.html
import random
import time
from micropython import const
_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)
_IRQ_GATTS_READ_REQUEST = const(4)
_IRQ_SCAN_RESULT = const(5)
_IRQ_SCAN_DONE = const(6)
_IRQ_PERIPHERAL_CONNECT = const(7)
_IRQ_PERIPHERAL_DISCONNECT = const(8)
_IRQ_GATTC_SERVICE_RESULT = const(9)
_IRQ_GATTC_SERVICE_DONE = const(10)
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
_IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
_IRQ_GATTC_DESCRIPTOR_RESULT = const(13)
_IRQ_GATTC_DESCRIPTOR_DONE = const(14)
_IRQ_GATTC_READ_RESULT = const(15)
_IRQ_GATTC_READ_DONE = const(16)
_IRQ_GATTC_WRITE_DONE = const(17)
_IRQ_GATTC_NOTIFY = const(18)
_IRQ_GATTC_INDICATE = const(19)
_IRQ_GATTS_INDICATE_DONE = const(20)
_IRQ_MTU_EXCHANGED = const(21)
_IRQ_L2CAP_ACCEPT = const(22)
_IRQ_L2CAP_CONNECT = const(23)
_IRQ_L2CAP_DISCONNECT = const(24)
_IRQ_L2CAP_RECV = const(25)
_IRQ_L2CAP_SEND_READY = const(26)
_IRQ_CONNECTION_UPDATE = const(27)
_IRQ_ENCRYPTION_UPDATE = const(28)
_IRQ_GET_SECRET = const(29)
_IRQ_SET_SECRET = const(30)
class ESP32BLE():
def __init__(self, name):
# Create BLE device management
self.name = name
self.ble = bluetooth.BLE()
self.ble.active(True)
#get MAC address
mac = self.ble.config('mac')[1]
print("device MAC address is: "+mac.hex())
self.ble.irq(self.ble_irq)
self.connections = set()
self.register()
self.advertise()
def ble_irq(self, event, data):
#define event callback functions
if event == _IRQ_CENTRAL_CONNECT:
# A central has connected to this peripheral.
conn_handle, addr_type, addr = data
self.connections.add(conn_handle)
print("BLE device connected successfully")
elif event == _IRQ_CENTRAL_DISCONNECT:
# A central has disconnected from this peripheral.
conn_handle, addr_type, addr = data
self.connections.remove(conn_handle)
print("BLE device disconnected")
self.advertise()
elif event == _IRQ_GATTS_WRITE:
# A client has written to this characteristic or descriptor.
conn_handle, attr_handle = data
print("write event: ",self.ble.gatts_read(data[1]).decode('UTF-8').strip())
elif event == _IRQ_GATTS_READ_REQUEST:
# A client has issued a read. Note: this is only supported on STM32.
# Return a non-zero integer to deny the read (see below), or zero (or None)
# to accept the read.
conn_handle, attr_handle = data
print("read event: ",data)
def register(self):
#define services and characteristics
HR_UUID = bluetooth.UUID(0x180D)
HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
HR_SERVICE = (HR_UUID, (HR_CHAR,),)
UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b")
SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,)
MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),)
SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE)
( (self.hr,), (self.tx, self.rx,), (self.sen,self.cmd,), ) = self.ble.gatts_register_services(SERVICES)
self.ble.gatts_write(self.sen, str(43.256), False)
def advertise(self):
#advertise BLE module with name
name = bytes(self.name, 'UTF-8')
adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
self.ble.gap_advertise(100, adv_data)
def set_sensor(self, data, notify=False):
# Data is sint16 with a resolution of 0.01.
self.ble.gatts_write(self.sen, str(data))
if notify:
for conn_handle in self.connections:
# Notify connected centrals to issue a read.
self.ble.gatts_notify(conn_handle, self.sen)
def main():
BLE = ESP32BLE("ESP32BLEmPy")
#simulate sensor
sensorVal=43.256
i=0
while True:
# Write every second, notify every 10 seconds.
i = (i + 1) % 10
BLE.set_sensor(sensorVal, notify=i == 0)
# Random walk the temperature.
sensorVal += random.uniform(-1.5, 1.5)
time.sleep_ms(1000)
if __name__ == "__main__":
main()
import bluetooth #https://docs.micropython.org/en/latest/library/bluetooth.html import random import time from micropython import const _IRQ_CENTRAL_CONNECT = const(1) _IRQ_CENTRAL_DISCONNECT = const(2) _IRQ_GATTS_WRITE = const(3) _IRQ_GATTS_READ_REQUEST = const(4) _IRQ_SCAN_RESULT = const(5) _IRQ_SCAN_DONE = const(6) _IRQ_PERIPHERAL_CONNECT = const(7) _IRQ_PERIPHERAL_DISCONNECT = const(8) _IRQ_GATTC_SERVICE_RESULT = const(9) _IRQ_GATTC_SERVICE_DONE = const(10) _IRQ_GATTC_CHARACTERISTIC_RESULT = const(11) _IRQ_GATTC_CHARACTERISTIC_DONE = const(12) _IRQ_GATTC_DESCRIPTOR_RESULT = const(13) _IRQ_GATTC_DESCRIPTOR_DONE = const(14) _IRQ_GATTC_READ_RESULT = const(15) _IRQ_GATTC_READ_DONE = const(16) _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) _IRQ_GATTS_INDICATE_DONE = const(20) _IRQ_MTU_EXCHANGED = const(21) _IRQ_L2CAP_ACCEPT = const(22) _IRQ_L2CAP_CONNECT = const(23) _IRQ_L2CAP_DISCONNECT = const(24) _IRQ_L2CAP_RECV = const(25) _IRQ_L2CAP_SEND_READY = const(26) _IRQ_CONNECTION_UPDATE = const(27) _IRQ_ENCRYPTION_UPDATE = const(28) _IRQ_GET_SECRET = const(29) _IRQ_SET_SECRET = const(30) class ESP32BLE(): def __init__(self, name): # Create BLE device management self.name = name self.ble = bluetooth.BLE() self.ble.active(True) #get MAC address mac = self.ble.config('mac')[1] print("device MAC address is: "+mac.hex()) self.ble.irq(self.ble_irq) self.connections = set() self.register() self.advertise() def ble_irq(self, event, data): #define event callback functions if event == _IRQ_CENTRAL_CONNECT: # A central has connected to this peripheral. conn_handle, addr_type, addr = data self.connections.add(conn_handle) print("BLE device connected successfully") elif event == _IRQ_CENTRAL_DISCONNECT: # A central has disconnected from this peripheral. conn_handle, addr_type, addr = data self.connections.remove(conn_handle) print("BLE device disconnected") self.advertise() elif event == _IRQ_GATTS_WRITE: # A client has written to this characteristic or descriptor. conn_handle, attr_handle = data print("write event: ",self.ble.gatts_read(data[1]).decode('UTF-8').strip()) elif event == _IRQ_GATTS_READ_REQUEST: # A client has issued a read. Note: this is only supported on STM32. # Return a non-zero integer to deny the read (see below), or zero (or None) # to accept the read. conn_handle, attr_handle = data print("read event: ",data) def register(self): #define services and characteristics HR_UUID = bluetooth.UUID(0x180D) HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) HR_SERVICE = (HR_UUID, (HR_CHAR,),) UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E') UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,) UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),) MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b") SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,) CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,) MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),) SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE) ( (self.hr,), (self.tx, self.rx,), (self.sen,self.cmd,), ) = self.ble.gatts_register_services(SERVICES) self.ble.gatts_write(self.sen, str(43.256), False) def advertise(self): #advertise BLE module with name name = bytes(self.name, 'UTF-8') adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name self.ble.gap_advertise(100, adv_data) def set_sensor(self, data, notify=False): # Data is sint16 with a resolution of 0.01. self.ble.gatts_write(self.sen, str(data)) if notify: for conn_handle in self.connections: # Notify connected centrals to issue a read. self.ble.gatts_notify(conn_handle, self.sen) def main(): BLE = ESP32BLE("ESP32BLEmPy") #simulate sensor sensorVal=43.256 i=0 while True: # Write every second, notify every 10 seconds. i = (i + 1) % 10 BLE.set_sensor(sensorVal, notify=i == 0) # Random walk the temperature. sensorVal += random.uniform(-1.5, 1.5) time.sleep_ms(1000) if __name__ == "__main__": main()
import bluetooth #https://docs.micropython.org/en/latest/library/bluetooth.html
import random
import time
from micropython import const

_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_WRITE = const(3)
_IRQ_GATTS_READ_REQUEST = const(4)
_IRQ_SCAN_RESULT = const(5)
_IRQ_SCAN_DONE = const(6)
_IRQ_PERIPHERAL_CONNECT = const(7)
_IRQ_PERIPHERAL_DISCONNECT = const(8)
_IRQ_GATTC_SERVICE_RESULT = const(9)
_IRQ_GATTC_SERVICE_DONE = const(10)
_IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
_IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
_IRQ_GATTC_DESCRIPTOR_RESULT = const(13)
_IRQ_GATTC_DESCRIPTOR_DONE = const(14)
_IRQ_GATTC_READ_RESULT = const(15)
_IRQ_GATTC_READ_DONE = const(16)
_IRQ_GATTC_WRITE_DONE = const(17)
_IRQ_GATTC_NOTIFY = const(18)
_IRQ_GATTC_INDICATE = const(19)
_IRQ_GATTS_INDICATE_DONE = const(20)
_IRQ_MTU_EXCHANGED = const(21)
_IRQ_L2CAP_ACCEPT = const(22)
_IRQ_L2CAP_CONNECT = const(23)
_IRQ_L2CAP_DISCONNECT = const(24)
_IRQ_L2CAP_RECV = const(25)
_IRQ_L2CAP_SEND_READY = const(26)
_IRQ_CONNECTION_UPDATE = const(27)
_IRQ_ENCRYPTION_UPDATE = const(28)
_IRQ_GET_SECRET = const(29)
_IRQ_SET_SECRET = const(30)


class ESP32BLE():
    def __init__(self, name):
        # Create BLE device management
        self.name = name
        self.ble = bluetooth.BLE()
        self.ble.active(True)
        #get MAC address
        mac = self.ble.config('mac')[1]
        print("device MAC address is: "+mac.hex())
        self.ble.irq(self.ble_irq)
        self.connections = set()
        self.register()
        self.advertise()

    def ble_irq(self, event, data):
        #define event callback functions
        if event == _IRQ_CENTRAL_CONNECT:
            # A central has connected to this peripheral.
            conn_handle, addr_type, addr = data
            self.connections.add(conn_handle)
            print("BLE device connected successfully")
        elif event == _IRQ_CENTRAL_DISCONNECT:
            # A central has disconnected from this peripheral.
            conn_handle, addr_type, addr = data
            self.connections.remove(conn_handle)
            print("BLE device disconnected")
            self.advertise()
        elif event == _IRQ_GATTS_WRITE:
            # A client has written to this characteristic or descriptor.
            conn_handle, attr_handle = data
            print("write event: ",self.ble.gatts_read(data[1]).decode('UTF-8').strip())
        elif event == _IRQ_GATTS_READ_REQUEST:
            # A client has issued a read. Note: this is only supported on STM32.
            # Return a non-zero integer to deny the read (see below), or zero (or None)
            # to accept the read.
            conn_handle, attr_handle = data
            print("read event: ",data)
            
    def register(self):
        #define services and characteristics
        HR_UUID = bluetooth.UUID(0x180D)
        HR_CHAR = (bluetooth.UUID(0x2A37), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
        HR_SERVICE = (HR_UUID, (HR_CHAR,),)
        UART_UUID = bluetooth.UUID('6E400001-B5A3-F393-E0A9-E50E24DCCA9E')
        UART_TX = (bluetooth.UUID('6E400003-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
        UART_RX = (bluetooth.UUID('6E400002-B5A3-F393-E0A9-E50E24DCCA9E'), bluetooth.FLAG_WRITE,)
        UART_SERVICE = (UART_UUID, (UART_TX, UART_RX,),)
        
        MY_UUID = bluetooth.UUID("0bd62591-0b10-431a-982e-bd136821f35b")
        SEN_CHAR = (bluetooth.UUID("0bd62592-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,)
        CMD_CHAR = (bluetooth.UUID("0bd62593-0b10-431a-982e-bd136821f35b"), bluetooth.FLAG_WRITE,)
        MY_SERVICE = (MY_UUID, (SEN_CHAR, CMD_CHAR,),)
        
        SERVICES = (HR_SERVICE, UART_SERVICE, MY_SERVICE)
        ( (self.hr,), (self.tx, self.rx,), (self.sen,self.cmd,), ) = self.ble.gatts_register_services(SERVICES)
        
        self.ble.gatts_write(self.sen, str(43.256), False)

    
    def advertise(self):
        #advertise BLE module with name
        name = bytes(self.name, 'UTF-8')
        adv_data = bytearray(b'\x02\x01\x02') + bytearray((len(name) + 1, 0x09)) + name
        self.ble.gap_advertise(100, adv_data)
        
    def set_sensor(self, data, notify=False):
        # Data is sint16 with a resolution of 0.01.
        self.ble.gatts_write(self.sen, str(data))
        if notify:
            for conn_handle in self.connections:
                # Notify connected centrals to issue a read.
                self.ble.gatts_notify(conn_handle, self.sen)
                
def main():
    BLE = ESP32BLE("ESP32BLEmPy")

    #simulate sensor
    sensorVal=43.256
    i=0
    while True:
        # Write every second, notify every 10 seconds.
        i = (i + 1) % 10
        BLE.set_sensor(sensorVal, notify=i == 0)
        # Random walk the temperature.
        sensorVal += random.uniform(-1.5, 1.5)
        time.sleep_ms(1000)


if __name__ == "__main__":
    main()

Sources