4. Chapter BLE

This chapter mainly introduces how to make simple data transmission through BLE of ESP32-S3 WROOM and mobile phones.

4.1. Project 4.1 Bluetooth Low Energy Data Passthrough

4.1.1. Component List

ESP32-S3 WROOM x1

USB cable x1

Chapter01_00

Chapter01_01

4.1.2. Component knowledge

ESP32-S3’s integrated Bluetooth function Bluetooth is a short-distance communication system, which can be divided into two types, namely Bluetooth Low Energy(BLE) and Classic Bluetooth. There are two modes for simple data transmission: master mode and slave mode.

4.1.2.1. Master mode

In this mode, works are done in the master device and it can connect with a slave device. And we can search and select slave devices nearby to connect with. When a device initiates connection request in master mode, it requires information of the other Bluetooth devices including their address and pairing passkey. After finishing pairing, it can connect with them directly.

4.1.2.2. Slave mode

The Bluetooth module in slave mode can only accept connection request from a host computer, but cannot initiate a connection request. After connecting with a host device, it can send data to or receive from the host device.

Bluetooth devices can make data interaction with each other, as one is in master mode and the other in slave mode. When they are making data interaction, the Bluetooth device in master mode searches and selects devices nearby to connect to. When establishing connection, they can exchange data. When mobile phones exchange data with ESP32-S3, they are usually in master mode and ESP32-S3 in slave mode.

../../../_images/Chapter04_00.png

4.1.3. Circuit

Connect Freenove ESP32-S3 to the computer using the Type C cable.

../../../_images/Chapter04_01.png

4.1.3.1. Lightblue

If you can’t install Serial Bluetooth on your phone, try LightBlue.If you do not have this software installed on your phone, you can refer to this link:

https://apps.apple.com/us/app/lightblue/id557428110

../../../_images/Chapter04_02.png

4.1.4. Code

Move the program folder “Freenove_ESP32_S3_WROOM_Board_Lite/Python/Python_Codes” to disk(D) in advance with the path of “D:/Micropython_Codes”.

Open “Thonny”, click “This computer” -> “D:” -> “Micropython_Codes” -> “04.1_BLE”. Select “ble_advertising.py”, right click your mouse to select “Upload to /”, wait for “ble_advertising.py” to be uploaded to ESP32-S3 and then double click “BLE.py”.

4.1.4.1. 04.1_BLE

../../../_images/Chapter04_16.png

Click run for BLE.py.

../../../_images/Chapter04_17.png

Turn ON Bluetooth on your phone, and open the Lightblue APP.

../../../_images/Chapter04_18.png

In the Scan page, swipe down to refresh the name of Bluetooth that the phone searches for. Click ESP32S3.

../../../_images/Chapter04_19.png

After Bluetooth is connect successfully, Shell will printer the information.

../../../_images/Chapter04_20.png

Click “Receive”. Select the appropriate Data format in the box to the right of Data Format. For example, HEX for hexadecimal, utf-string for character, Binary for Binary, etc. Then click SUBSCRIBE.

../../../_images/Chapter04_21.png

You can type “Hello” in Shell and press “Enter” to send.

../../../_images/Chapter04_22.png

And then you can see the mobile Bluetooth has received the message.

../../../_images/Chapter04_23.png

Similarly, you can select “Send” on your phone. Set Data format, and then enter anything in the sending box and click Write to send.

../../../_images/Chapter04_24.png

You can check the message from Bluetooth in “Shell”.

../../../_images/Chapter04_25.png

And now data can be transferred between your mobile phone and computer via ESP32S3.

The following is the program code:

  1# This example demonstrates a UART periperhal.
  2
  3import bluetooth
  4import random
  5import struct
  6import time
  7from ble_advertising import advertising_payload
  8
  9from micropython import const
 10
 11_IRQ_CENTRAL_CONNECT = const(1)
 12_IRQ_CENTRAL_DISCONNECT = const(2)
 13_IRQ_GATTS_WRITE = const(3)
 14
 15_FLAG_READ = const(0x0002)
 16_FLAG_WRITE_NO_RESPONSE = const(0x0004)
 17_FLAG_WRITE = const(0x0008)
 18_FLAG_NOTIFY = const(0x0010)
 19
 20_UART_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
 21_UART_TX = (
 22    bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"),
 23    _FLAG_READ | _FLAG_NOTIFY,
 24)
 25_UART_RX = (
 26    bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"),
 27    _FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE,
 28)
 29_UART_SERVICE = (
 30    _UART_UUID,
 31    (_UART_TX, _UART_RX),
 32)
 33
 34
 35class BLESimplePeripheral:
 36    def __init__(self, ble, name="ESP32S3"):
 37        self._ble = ble
 38        self._ble.active(True)
 39        self._ble.irq(self._irq)
 40        ((self._handle_tx, self._handle_rx),) = self._ble.gatts_register_services((_UART_SERVICE,))
 41        self._connections = set()   
 42        self._write_callback = None
 43        self._payload = advertising_payload(name=name, services=[_UART_UUID])
 44        self._advertise()
 45
 46    def _irq(self, event, data):
 47        # Track connections so we can send notifications.
 48        if event == _IRQ_CENTRAL_CONNECT:
 49            conn_handle, _, _ = data
 50            print("New connection", conn_handle)
 51            print("\nThe BLE connection is successful.")
 52            self._connections.add(conn_handle)
 53        elif event == _IRQ_CENTRAL_DISCONNECT:
 54            conn_handle, _, _ = data
 55            print("Disconnected", conn_handle)
 56            self._connections.remove(conn_handle)
 57            # Start advertising again to allow a new connection.
 58            self._advertise()
 59        elif event == _IRQ_GATTS_WRITE:
 60            conn_handle, value_handle = data
 61            value = self._ble.gatts_read(value_handle)
 62            if value_handle == self._handle_rx and self._write_callback:
 63                self._write_callback(value)
 64
 65    def send(self, data):
 66        for conn_handle in self._connections:
 67            self._ble.gatts_notify(conn_handle, self._handle_tx, data)
 68
 69    def is_connected(self):
 70        return len(self._connections) > 0
 71
 72    def _advertise(self, interval_us=500000):
 73        print("Starting advertising")
 74        self._ble.gap_advertise(interval_us, adv_data=self._payload)
 75
 76    def on_write(self, callback):
 77        self._write_callback = callback
 78
 79
 80def demo():
 81    ble = bluetooth.BLE()
 82    p = BLESimplePeripheral(ble)
 83
 84    def on_rx(rx_data):
 85        print("\nRX", rx_data)
 86
 87    p.on_write(on_rx)
 88
 89    print("Please use LightBlue to connect to ESP32S3.")
 90
 91    while True:
 92        if p.is_connected():
 93            # Short burst of queued notifications.
 94            tx_data = input("Enter anything: ")
 95            print("Send: ", tx_data)
 96            p.send(tx_data)
 97
 98
 99if __name__ == "__main__":
100    demo()

Define the specified UUID number for BLE vendor.

1_UART_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
2_UART_TX = (
3    bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"),
4    _FLAG_READ | _FLAG_NOTIFY,
5)
6_UART_RX = (
7    bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"),
8    _FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE,
9)

Write an _irq function to manage BLE interrupt events.

 1def _irq(self, event, data):
 2    # Track connections so we can send notifications.
 3    if event == _IRQ_CENTRAL_CONNECT:
 4        conn_handle, _, _ = data
 5        print("New connection", conn_handle)
 6        print("\nThe BLE connection is successful.")
 7        self._connections.add(conn_handle)
 8    elif event == _IRQ_CENTRAL_DISCONNECT:
 9        conn_handle, _, _ = data
10        print("Disconnected", conn_handle)
11        self._connections.remove(conn_handle)
12        # Start advertising again to allow a new connection.
13        self._advertise()
14    elif event == _IRQ_GATTS_WRITE:
15        conn_handle, value_handle = data
16        value = self._ble.gatts_read(value_handle)
17        if value_handle == self._handle_rx and self._write_callback:
18            self._write_callback(value)

Initialize the BLE function and name it.

1def __init__(self, ble, name="ESP32S3"):

When the mobile phone send data to ESP32-S3 via BLE Bluetooth, it will print them out with serial port; When the serial port of ESP32-S3 receive data, it will send them to mobile via BLE Bluetooth.

 1def demo():
 2    ble = bluetooth.BLE()
 3    p = BLESimplePeripheral(ble)
 4
 5    def on_rx(rx_data):
 6        print("\nRX", rx_data)
 7
 8    p.on_write(on_rx)
 9
10    print("Please use LightBlue to connect to ESP32S3.")
11
12    while True:
13        if p.is_connected():
14            # Short burst of queued notifications.
15            tx_data = input("Enter anything: ")
16            print("Send: ", tx_data)
17            p.send(tx_data)

4.2. Project 4.2 Bluetooth Control LED

In this section, we will control the LED with Bluetooth.

4.2.1. Component List

ESP32-S3 WROOM x1

USB cable x1

Chapter01_00

Chapter01_01

4.2.2. Code

Move the program folder “ Freenove_ESP32_S3_WROOM_Board_Lite/Python/Python_Codes “ to disk(D) in advance with the path of “ D:/Micropython_Codes “.

Open “Thonny”, click “This computer” -> “D:” -> “Micropython_Codes” -> “04.2_ BLE_LED”. Select “ble_advertising.py”, right click your mouse to select “Upload to /”, wait for “ble_advertising.py” to be uploaded to ESP32-S3 and then double click “BLE_LED.py”.

4.2.2.1. 04.2_BLE_LED

../../../_images/Chapter04_26.png

Compile and upload code to ESP32S3. The operation of the APP is the same as 04.1, you only need to change the sending content to “led_on” and “led_off” to operate LEDs on the ESP32S3.

Data sent from mobile APP:

../../../_images/Chapter04_27.png

You can check the message sent by Bluetooth in “Shell”.

../../../_images/Chapter04_28.png

The phenomenon of LED

../../../_images/Chapter04_29.png

Attention: If the sending content isn’t “led_on” or “led_off”, then the state of LED will not change. If the LED is on, when receiving irrelevant content, it keeps on; Correspondingly, if the LED is off, when receiving irrelevant content, it keeps off.

The following is the program code:

 1# This example demonstrates a UART periperhal.
 2
 3import bluetooth
 4import random
 5import struct
 6import time
 7from ble_advertising import advertising_payload
 8from machine import Pin
 9from micropython import const
10
11_IRQ_CENTRAL_CONNECT = const(1)
12_IRQ_CENTRAL_DISCONNECT = const(2)
13_IRQ_GATTS_WRITE = const(3)
14
15_FLAG_READ = const(0x0002)
16_FLAG_WRITE_NO_RESPONSE = const(0x0004)
17_FLAG_WRITE = const(0x0008)
18_FLAG_NOTIFY = const(0x0010)
19
20_UART_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
21_UART_TX = (
22    bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"),
23    _FLAG_READ | _FLAG_NOTIFY,
24)
25_UART_RX = (
26    bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"),
27    _FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE,
28)
29_UART_SERVICE = (
30    _UART_UUID,
31    (_UART_TX, _UART_RX),
32)
33
34
35class BLESimplePeripheral:
36    def __init__(self, ble, name="ESP32S3"):
37        self._ble = ble
38        self._ble.active(True)
39        self._ble.irq(self._irq)
40        ((self._handle_tx, self._handle_rx),) = self._ble.gatts_register_services((_UART_SERVICE,))
41        self._connections = set()   
42        self._write_callback = None
43        self._payload = advertising_payload(name=name, services=[_UART_UUID])
44        self._advertise()
45
46    def _irq(self, event, data):
47        # Track connections so we can send notifications.
48        if event == _IRQ_CENTRAL_CONNECT:
49            conn_handle, _, _ = data
50            print("New connection", conn_handle)
51            print("\nThe BLE connection is successful.")
52            self._connections.add(conn_handle)
53        elif event == _IRQ_CENTRAL_DISCONNECT:
54            conn_handle, _, _ = data
55            print("Disconnected", conn_handle)
56            self._connections.remove(conn_handle)
57            # Start advertising again to allow a new connection.
58            self._advertise()
59        elif event == _IRQ_GATTS_WRITE:
60            conn_handle, value_handle = data
61            value = self._ble.gatts_read(value_handle)
62            if value_handle == self._handle_rx and self._write_callback:
63                self._write_callback(value)
64
65    def send(self, data):
66        for conn_handle in self._connections:
67            self._ble.gatts_notify(conn_handle, self._handle_tx, data)
68
69    def is_connected(self):
70        return len(self._connections) > 0
71
72    def _advertise(self, interval_us=500000):
73        print("Starting advertising")
74        self._ble.gap_advertise(interval_us, adv_data=self._payload)
75
76    def on_write(self, callback):
77        self._write_callback = callback
78
79
80def demo():
81    ble = bluetooth.BLE()
82    p = BLESimplePeripheral(ble)
83    led=Pin(2,Pin.OUT)
84    def on_rx(rx_data):
85        print("Received: ", rx_data)
86        if rx_data == b'led_on':
87            led.value(1)
88        elif rx_data == b'led_off':
89            led.value(0)
90        else:
91            pass
92
93    p.on_write(on_rx)
94
95    print("Please use LightBlue to connect to ESP32S3.")
96
97
98if __name__ == "__main__":
99    demo()

Compare received message with “led_on” and “led_off” and take action accordingly.

1if rx_data == b'led_on':
2    led.value(1)
3elif rx_data == b'led_off':
4    led.value(0)