arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
AudioBLEServer.h
1#pragma once
2
3#include "AudioBLEStream.h"
4#include "ConstantsArduino.h"
5#include <ArduinoBLE.h>
6#include "AudioTools/CoreAudio/AudioBasic/StrView.h"
7
8namespace audio_tools {
9
10class AudioBLEServer;
11class AudioBLEServer *selfAudioBLEServer = nullptr;
12
26public:
27 AudioBLEServer(int mtu = 0) : AudioBLEStream(mtu) {
28 selfAudioBLEServer = this;
29 }
30
31 // starts a BLE server with the indicated name
32 bool begin(const char *name) {
33 TRACEI();
34 ble_server_name = name;
35
36 if (!BLE.begin()) {
37 LOGE("starting BLE failed");
38 return false;
39 }
40
41 // set the local name peripheral advertises
42 BLE.setLocalName(ble_server_name);
43
44 // assign event handlers for connected, disconnected to peripheral
45 BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
46 BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
47
48 // setup serice with characteristics
49 setupBLEService();
50
51 // start advertising
52 BLE.advertise();
53
54 return true;
55 }
56
57 void end() override {
58 TRACEI();
59 flush();
60 BLE.end();
61 }
62
63 size_t readBytes(uint8_t *data, size_t len) override {
64 TRACED();
65 if (!checkCentralConnected())
66 return 0;
67 size_t read_size = getReadSize(len);
68 return receive_buffer.readArray(data, read_size);
69 }
70
71 int available() override {
72 if (!checkCentralConnected())
73 return 0;
74 if (is_framed)
75 return receive_sizes.peek();
76 return this->receive_buffer.available();
77 }
78
79 size_t write(const uint8_t *data, size_t len) override {
80 LOGD("AudioBLEStream::write: %d", len);
81 if (!checkCentralConnected())
82 return 0;
83 if (is_framed && availableForWrite() < len) {
84 return 0;
85 }
86 return transmit_buffer.writeArray(data, len);
87 }
88
89 int availableForWrite() override {
90 if (!checkCentralConnected())
91 return 0;
92 setupTXBuffer();
93 int result = transmit_buffer.availableForWrite();
94 // make sure we copy always a consistent amount of data
95 if (result < DEFAULT_BUFFER_SIZE)
96 result = 0;
97 return result;
98 }
99
100 bool connected() override { return checkCentralConnected(); }
101
102protected:
103 // server
104 BLEDevice central;
105 BLEService service{BLE_AUDIO_SERVICE_UUID}; // create service
106 BLECharacteristic ch01_char{BLE_CH1_UUID, BLERead, getMTU()};
107 BLECharacteristic ch02_char{BLE_CH2_UUID, BLEWrite, getMTU()};
108 BLECharacteristic info_char{BLE_INFO_UUID, BLERead | BLEWrite | BLENotify,
109 80};
110 BLEDescriptor ch01_desc{"2901", "channel 1"};
111 BLEDescriptor ch02_desc{"2901", "channel 2"};
112 BLEDescriptor info_desc{"2901", "info"};
113
114 RingBuffer<uint8_t> receive_buffer{0};
115 RingBuffer<uint16_t> receive_sizes{0};
116 RingBuffer<uint8_t> transmit_buffer{0};
117 RingBuffer<uint16_t> transmit_buffer_sizes{0};
118
119 static void blePeripheralConnectHandler(BLEDevice device) {
120 selfAudioBLEServer->onConnect(device);
121 }
122
123 static void blePeripheralDisconnectHandler(BLEDevice device) {
124 selfAudioBLEServer->onDisconnect(device);
125 }
126
127 static void bleOnWrite(BLEDevice device, BLECharacteristic characteristic) {
128 selfAudioBLEServer->onWrite(characteristic);
129 }
130
131 static void bleOnRead(BLEDevice device, BLECharacteristic characteristic) {
132 TRACED();
133 selfAudioBLEServer->onRead(characteristic);
134 }
135
136 void onConnect(BLEDevice device) { TRACEI(); }
137
138 void onDisconnect(BLEDevice device) {
139 TRACEI();
140 BLE.advertise();
141 }
142
144 void onWrite(BLECharacteristic characteristic) {
145 TRACED();
146 setupRXBuffer();
147 // changed to auto to be version independent (it changed from std::string to
148 // String)
149 if (StrView(BLE_INFO_UUID).equals(characteristic.uuid())) {
150 setAudioInfo((uint8_t *)characteristic.value(),
151 characteristic.valueLength());
152 } else {
153 receiveAudio((uint8_t *)characteristic.value(),
154 characteristic.valueLength());
155 }
156 }
157
159 void onRead(BLECharacteristic characteristic) {
160 TRACED();
161 auto uuid = StrView(characteristic.uuid());
162 if (uuid == BLE_CH1_UUID || uuid == BLE_CH2_UUID) {
163 TRACEI();
164 int len = std::min(getMTU(), (int)transmit_buffer.available());
165 if (is_framed) {
166 len = transmit_buffer_sizes.read();
167 }
168 LOGI("%s: len: %d, buffer: %d", uuid.c_str(), len,
169 transmit_buffer.size());
170 if (len > 0) {
171 uint8_t tmp[len];
172 transmit_buffer.peekArray(tmp, len);
173 if (characteristic.writeValue(tmp, len)) {
174 transmit_buffer.readArray(tmp, len);
175 } else {
176 LOGW("writeValue failed")
177 }
178 }
179 }
180 }
181
182 bool checkCentralConnected() {
183 central = BLE.central();
184 // if a central is connected to the peripheral:
185 if (central)
186 return central.connected();
187 return false;
188 }
189
190 virtual void receiveAudio(const uint8_t *data, size_t size) {
191 while (receive_buffer.availableForWrite() < size) {
192 // wait for ringbuffer to get freed up
193 delay(10);
194 }
195 if (is_framed)
196 receive_sizes.write(size);
197 receive_buffer.writeArray(data, size);
198 }
199
200 void writeAudioInfoCharacteristic(AudioInfo info) {
201 TRACEI();
202 // send update via BLE
203 StrView str = toStr(info);
204 LOGI("AudioInfo: %s", str.c_str());
205 info_char.setValue((uint8_t *)str.c_str(), str.length() + 1);
206 }
207
208 int getMTU() override {
209 TRACED();
210 if (max_transfer_size == 0) {
211 // int peer_max_transfer_size =
212 // p_server->getPeerMTU(p_server->getConnId()) - BLE_MTU_OVERHEAD;
213 // max_transfer_size = std::min(BLE_MTU - BLE_MTU,
214 // peer_max_transfer_size);
215 // max_transfer_size = central.mtu() - BLE_MTU_OVERHEAD;
216 max_transfer_size = BLE_MTU - BLE_MTU_OVERHEAD;
217
218 LOGI("max_transfer_size: %d", max_transfer_size);
219 }
220 return max_transfer_size;
221 }
222
223 void setupBLEService() {
224 TRACEI();
225 // set the UUID for the service this peripheral advertises
226 BLE.setAdvertisedService(service);
227
228 ch01_char.addDescriptor(ch01_desc);
229 ch02_char.addDescriptor(ch02_desc);
230
231 // add the characteristic to the service
232 service.addCharacteristic(ch01_char);
233 service.addCharacteristic(ch02_char);
234
235 // assign event handlers for characteristic
236 ch02_char.setEventHandler(BLEWritten, bleOnWrite);
237 ch01_char.setEventHandler(BLERead, bleOnRead);
238
239 if (is_audio_info_active) {
240 info_char.addDescriptor(info_desc);
241 service.addCharacteristic(info_char);
242 }
243
244 // add service
245 BLE.addService(service);
246
247 // provide AudioInfo
248 if (is_audio_info_active) {
249 writeAudioInfoCharacteristic(info);
250 }
251
252 // Read callback works only when we provide some initial data
253 uint8_t tmp[512] = {0xFF};
254 ch01_char.writeValue(tmp, 512, false);
255 }
256
257 void setupTXBuffer() {
258 if (transmit_buffer.size() == 0) {
259 LOGI("Setting transmit_buffer to %d", RX_BUFFER_SIZE);
260 transmit_buffer.resize(TX_BUFFER_SIZE);
261 if (is_framed) {
262 transmit_buffer_sizes.resize(TX_COUNT);
263 }
264 }
265 }
266
267 void setupRXBuffer() {
268 if (receive_buffer.size() == 0) {
269 LOGI("Setting receive_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU());
270 receive_buffer.resize(RX_BUFFER_SIZE);
271 if (is_framed) {
272 receive_sizes.resize(RX_COUNT);
273 }
274 }
275 }
276
277 size_t getReadSize(size_t dataSize) {
278 size_t read_size = dataSize;
279 if (is_framed) {
280 read_size = 0;
281 if (receive_sizes.available() > 0) {
282 read_size = receive_sizes.read();
283 }
284 if (dataSize < read_size) {
285 LOGE("read size too small: %d - it must be >= %d", dataSize, read_size);
286 return 0;
287 }
288 if (receive_buffer.available() < read_size) {
289 LOGE("missing data in buffer");
290 return 0;
291 }
292 }
293 return read_size;
294 }
295};
296
297} // namespace audio_tools
A simple BLE server that implements the serial protocol, so that it can be used to send and recevie a...
Definition AudioBLEServer.h:25
void onWrite(BLECharacteristic characteristic)
store the next batch of data
Definition AudioBLEServer.h:144
void onRead(BLECharacteristic characteristic)
provide the next batch of audio data
Definition AudioBLEServer.h:159
Transmit and receive data via BLE using a Serial API. The following additional experimental features ...
Definition AudioBLEStream.h:19
void setAudioInfo(AudioInfo info)
Defines the input AudioInfo.
Definition AudioBLEStream.h:27
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:37
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:59
Implements a typed Ringbuffer.
Definition Buffers.h:308
bool peek(T &result) override
peeks the actual entry from the buffer
Definition Buffers.h:328
virtual int availableForWrite()
provides the number of entries that are available to write
Definition Buffers.h:380
virtual int available()
provides the number of entries that are available to read
Definition Buffers.h:377
bool read(T &result) override
reads a single value
Definition Buffers.h:315
virtual bool write(T data)
write add an entry to the buffer
Definition Buffers.h:358
virtual size_t size()
Returns the maximum capacity of the buffer.
Definition Buffers.h:394
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10