arduino-audio-tools
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 
8 namespace audio_tools {
9 
10 class AudioBLEServer;
11 class AudioBLEServer *selfAudioBLEServer = nullptr;
12 
26 public:
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 
102 protected:
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:41
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition: Buffers.h:65
virtual T read()
reads a single value
Definition: Buffers.h:309
virtual int availableForWrite()
provides the number of entries that are available to write
Definition: Buffers.h:369
virtual int available()
provides the number of entries that are available to read
Definition: Buffers.h:366
virtual bool write(T data)
write add an entry to the buffer
Definition: Buffers.h:347
virtual size_t size()
Returns the maximum capacity of the buffer.
Definition: Buffers.h:383
virtual T peek()
peeks the actual entry from the buffer
Definition: Buffers.h:320
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: AudioConfig.h:821