Skip to content

Commit f2ae5a3

Browse files
committed
Add i2s audio streaming example
1 parent 6f48063 commit f2ae5a3

6 files changed

Lines changed: 465 additions & 76 deletions

File tree

05_advanced/05_06_bluetooth.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ This code is very long so only excerpts will be shown here. Also the module 'bl
4848
```python
4949
def demo():
5050
ble = bluetooth.BLE()
51-
p = BLESimplePeripheral(ble)
51+
p = BLEBasePeripheral(ble)
5252

5353
def on_rx(rx_data):
5454
print("\nRX", rx_data)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from ble_peripheral import BLEBasePeripheral
2+
3+
"""
4+
Streams packets over bluetooth with the format:
5+
[sequence byte][command byte][audio bytes]
6+
"""
7+
class BLEAudioPeripheral(BLEBasePeripheral):
8+
def __init__(self, ble, name, on_command=None):
9+
BLEBasePeripheral.__init__(self, ble, name)
10+
self.streaming = False
11+
self.dropped = 0
12+
self._on_command = on_command
13+
self.on_write(self.on_command)
14+
15+
def on_command(self, command):
16+
if self._on_command:
17+
self._on_command(command)
18+
19+
if command == b"1":
20+
print("Streaming started")
21+
self.dropped = 0
22+
self.streaming = True
23+
elif command == b"0":
24+
print("Streaming stopped")
25+
self.streaming = False
26+
27+
def send_audio(self, samples, count, command=0):
28+
chunk = self.max_bytes() - 1 # one byte is used for command
29+
start = 0
30+
while start < count:
31+
end = min(start + chunk, count)
32+
packet = bytes([command]) + bytes(memoryview(samples)[start:end])
33+
try:
34+
self.send(packet)
35+
except:
36+
self.dropped += 1
37+
start = end

05_advanced/code/ble_micro.py

Lines changed: 2 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,12 @@
11
# This example demonstrates a UART periperhal.
22

33
import bluetooth
4-
import random
5-
import struct
6-
import time
7-
from ble_advertising import advertising_payload
8-
9-
from 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-
35-
class 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
4+
from ble_peripheral import BLEBasePeripheral
785

796

807
def demo():
818
ble = bluetooth.BLE()
82-
p = BLESimplePeripheral(ble)
9+
p = BLEBasePeripheral(ble)
8310

8411
def on_rx(rx_data):
8512
print("\nRX", rx_data)

05_advanced/code/ble_peripheral.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import bluetooth
2+
import random
3+
import struct
4+
import time
5+
from ble_advertising import advertising_payload
6+
7+
from micropython import const
8+
9+
_IRQ_CENTRAL_CONNECT = const(1)
10+
_IRQ_CENTRAL_DISCONNECT = const(2)
11+
_IRQ_GATTS_WRITE = const(3)
12+
_IRQ_MTU_EXCHANGED = const(21)
13+
14+
_FLAG_READ = const(0x0002)
15+
_FLAG_WRITE_NO_RESPONSE = const(0x0004)
16+
_FLAG_WRITE = const(0x0008)
17+
_FLAG_NOTIFY = const(0x0010)
18+
19+
_UART_UUID = bluetooth.UUID("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
20+
_UART_TX = (
21+
bluetooth.UUID("6E400003-B5A3-F393-E0A9-E50E24DCCA9E"),
22+
_FLAG_READ | _FLAG_NOTIFY,
23+
)
24+
_UART_RX = (
25+
bluetooth.UUID("6E400002-B5A3-F393-E0A9-E50E24DCCA9E"),
26+
_FLAG_WRITE | _FLAG_WRITE_NO_RESPONSE,
27+
)
28+
_UART_SERVICE = (
29+
_UART_UUID,
30+
(_UART_TX, _UART_RX),
31+
)
32+
33+
_PREFERRED_MTU = const(517)
34+
_DEFAULT_MTU = const(23)
35+
36+
class BLEBasePeripheral:
37+
def __init__(self, ble, name="ESP32S3"):
38+
self._ble = ble
39+
self._ble.active(True)
40+
self._ble.config(mtu=_PREFERRED_MTU)
41+
self._ble.irq(self._irq)
42+
((self._handle_tx, self._handle_rx),) = self._ble.gatts_register_services((_UART_SERVICE,))
43+
self._connections = set()
44+
self._mtu = _DEFAULT_MTU
45+
self._write_callback = None
46+
self._payload = advertising_payload(name=name)
47+
self._name = name
48+
self._advertise()
49+
50+
def _irq(self, event, data):
51+
# Track connections so we can send notifications.
52+
if event == _IRQ_CENTRAL_CONNECT:
53+
conn_handle, _, _ = data
54+
print("Connected to", conn_handle)
55+
self._connections.add(conn_handle)
56+
elif event == _IRQ_CENTRAL_DISCONNECT:
57+
conn_handle, _, _ = data
58+
print("Disconnected from ", conn_handle)
59+
self._connections.remove(conn_handle)
60+
# Start advertising again to allow a new connection.
61+
self._advertise()
62+
elif event == _IRQ_MTU_EXCHANGED:
63+
conn_handle, mtu = data
64+
self._mtu = mtu
65+
print("Packet size agreed:", mtu, "bytes")
66+
elif event == _IRQ_GATTS_WRITE:
67+
conn_handle, value_handle = data
68+
value = self._ble.gatts_read(value_handle)
69+
if value_handle == self._handle_rx and self._write_callback:
70+
self._write_callback(value)
71+
72+
def send(self, data):
73+
for conn_handle in self._connections:
74+
self._ble.gatts_notify(conn_handle, self._handle_tx, data)
75+
76+
def is_connected(self):
77+
return len(self._connections) > 0
78+
79+
def _advertise(self, interval_us=100000):
80+
print("Advertising as", self._name)
81+
self._ble.gap_advertise(interval_us, adv_data=self._payload)
82+
83+
def on_write(self, callback):
84+
self._write_callback = callback
85+
86+
def max_bytes(self):
87+
# 3 bytes of every packet are used by Bluetooth itself.
88+
return self._mtu - 3

0 commit comments

Comments
 (0)