arduino-audio-tools
AudioBLEServerESP32.h
1 #pragma once
2 
3 #include "AudioBLEStream.h"
4 #include "ConstantsESP32.h"
5 //#include <BLE2902.h>
6 #include <BLEDevice.h>
7 #include <BLEServer.h>
8 #include <BLEUtils.h>
9 #include "AudioTools/CoreAudio/AudioBasic/StrView.h"
10 
11 namespace audio_tools {
20 class AudioBLEServer : public AudioBLEStream,
21  public BLECharacteristicCallbacks,
22  public BLEServerCallbacks {
23 public:
24  AudioBLEServer(int mtu = BLE_MTU) : AudioBLEStream(mtu) {}
25 
26  // starts a BLE server with the indicated name
27  bool begin(const char *name) {
28  TRACEI();
29  ble_server_name = name;
30  BLEDevice::init(name);
31 
32  p_server = BLEDevice::createServer();
33  p_server->setCallbacks(this);
34 
35  setupBLEService();
36  p_advertising = BLEDevice::getAdvertising();
37  p_advertising->addServiceUUID(BLE_AUDIO_SERVICE_UUID);
38  BLEDevice::startAdvertising();
39  return true;
40  }
41 
42  void end() override {
43  TRACEI();
44  flush();
45  BLEDevice::deinit();
46  }
47 
48  size_t readBytes(uint8_t *data, size_t len) override {
49  TRACED();
50  size_t read_size = getReadSize(len);
51  return receive_buffer.readArray(data, read_size);
52  }
53 
54  int available() override {
55  if (is_framed)
56  return receive_sizes.peek();
57  return this->receive_buffer.available();
58  }
59 
60  size_t write(const uint8_t *data, size_t dataSize) override {
61  LOGD("AudioBLEStream::write: %d", dataSize);
62  if (!connected()) {
63  return 0;
64  }
65  if (is_framed && availableForWrite() < dataSize) {
66  return 0;
67  }
68  return transmit_buffer.writeArray(data, dataSize);
69  }
70 
71  int availableForWrite() override {
72  int result = transmit_buffer.availableForWrite();
73  // make sure we copy always a consistent amount of data
74  if (result < DEFAULT_BUFFER_SIZE) result = 0;
75  return result ;
76  }
77 
78  bool connected() override { return p_server->getConnectedCount() > 0; }
79 
80 protected:
81  // server
82  BLEServer *p_server = nullptr;
83  BLEService *p_service = nullptr;
84  BLEAdvertising *p_advertising = nullptr;
85  BLECharacteristic *ch01_char;
86  BLECharacteristic *ch02_char;
87  BLECharacteristic *info_char;
88  BLEDescriptor ch01_desc{"2901"};
89  BLEDescriptor ch02_desc{"2901"};
90  BLEDescriptor info_desc{"2901"};
91  RingBuffer<uint8_t> receive_buffer{0};
92  RingBuffer<uint16_t> receive_sizes{0};
93  RingBuffer<uint8_t> transmit_buffer{0};
94  RingBuffer<uint16_t> transmit_buffer_sizes{0};
95 
96  virtual void receiveAudio(const uint8_t *data, size_t size) {
97  while (receive_buffer.availableForWrite() < size) {
98  // wait for ringbuffer to get freed up
99  delay(10);
100  }
101  if (is_framed)
102  receive_sizes.write(size);
103  receive_buffer.writeArray(data, size);
104  }
105 
106  void writeAudioInfoCharacteristic(AudioInfo info) {
107  TRACEI();
108  // send update via BLE
109  StrView str = toStr(info);
110  LOGI("AudioInfo: %s", str.c_str());
111  info_char->setValue((uint8_t *)str.c_str(), str.length() + 1);
112  info_char->notify();
113  }
114 
115  int getMTU() override {
116  TRACED();
117  if (max_transfer_size == 0) {
118  int peer_max_transfer_size =
119  p_server->getPeerMTU(p_server->getConnId()) - BLE_MTU_OVERHEAD;
120  max_transfer_size = std::min(BLE_MTU - BLE_MTU, peer_max_transfer_size);
121 
122  LOGI("max_transfer_size: %d", max_transfer_size);
123  }
124  return max_transfer_size;
125  }
126 
127  void setupBLEService() {
128  TRACEI();
129  // characteristic property is what the other device does.
130 
131  if (p_service == nullptr) {
132  p_service = p_server->createService(BLE_AUDIO_SERVICE_UUID);
133 
134  ch01_char = p_service->createCharacteristic(
135  BLE_CH1_UUID, BLECharacteristic::PROPERTY_READ );
136  ch01_desc.setValue("Channel 1");
137  ch01_char->addDescriptor(&ch01_desc);
138  ch01_char->setCallbacks(this);
139 
140  ch02_char = p_service->createCharacteristic(
141  BLE_CH2_UUID, BLECharacteristic::PROPERTY_WRITE);
142  ch02_desc.setValue("Channel 2");
143  ch02_char->addDescriptor(&ch02_desc);
144  ch02_char->setCallbacks(this);
145 
146  // optional setup of audio info notifications
147  if (is_audio_info_active && info_char == nullptr) {
148 
149  info_char = p_service->createCharacteristic(
150  BLE_INFO_UUID, BLECharacteristic::PROPERTY_NOTIFY |
151  BLECharacteristic::PROPERTY_READ |
152  BLECharacteristic::PROPERTY_NOTIFY |
153  BLECharacteristic::PROPERTY_INDICATE);
154  info_desc.setValue("Audio Info");
155  info_char->addDescriptor(&info_desc);
156  info_char->setCallbacks(this);
157 
158  }
159 
160  p_service->start();
161 
162  getMTU();
163 
164  if (info_char != nullptr) {
165  writeAudioInfoCharacteristic(info);
166  }
167  }
168  }
169 
170  void onConnect(BLEServer *pServer) override {
171  TRACEI();
172  }
173 
174  void onDisconnect(BLEServer *pServer) override {
175  TRACEI();
176  BLEDevice::startAdvertising();
177  }
178 
180  void onWrite(BLECharacteristic *pCharacteristic) override {
181  TRACED();
182  setupRXBuffer();
183  // changed to auto to be version independent (it changed from std::string to String)
184  auto value = pCharacteristic->getValue();
185  if (pCharacteristic->getUUID().toString() == BLE_INFO_UUID) {
186  setAudioInfo((uint8_t *)&value[0], value.length());
187  } else {
188  receiveAudio((uint8_t *)&value[0], value.length());
189  }
190  }
191 
193  void onRead(BLECharacteristic *pCharacteristic) override {
194  TRACED();
195  // changed to auto to be version independent (it changed from std::string to String)
196  auto uuid = pCharacteristic->getUUID().toString();
197  if (uuid == BLE_CH1_UUID || uuid == BLE_CH2_UUID) {
198  setupTXBuffer();
199  int len = std::min(getMTU() - BLE_MTU_OVERHEAD, (int)transmit_buffer.available());
200  if (is_framed) {
201  len = transmit_buffer_sizes.read();
202  }
203  LOGD("%s: len: %d, buffer: %d", uuid.c_str(), len,
204  transmit_buffer.size());
205  uint8_t tmp[len];
206  transmit_buffer.readArray(tmp, len);
207  pCharacteristic->setValue(tmp, len);
208  }
209  }
210 
211  void setupTXBuffer() {
212  if (transmit_buffer.size() == 0) {
213  LOGI("Setting transmit_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU());
214  transmit_buffer.resize(TX_BUFFER_SIZE);
215  if (is_framed) {
216  transmit_buffer_sizes.resize(TX_COUNT);
217  }
218  }
219  }
220 
221  void setupRXBuffer() {
222  if (receive_buffer.size() == 0) {
223  LOGI("Setting receive_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU());
224  receive_buffer.resize(RX_BUFFER_SIZE);
225  if (is_framed) {
226  receive_sizes.resize(RX_COUNT);
227  }
228  }
229  }
230 
231  size_t getReadSize(size_t dataSize) {
232  size_t read_size = dataSize;
233  if (is_framed) {
234  read_size = 0;
235  if (receive_sizes.available() > 0) {
236  read_size = receive_sizes.read();
237  }
238  if (dataSize < read_size) {
239  LOGE("read size too small: %d - it must be >= %d", dataSize, read_size);
240  return 0;
241  }
242  if (receive_buffer.available() < read_size) {
243  LOGE("missing data in buffer");
244  return 0;
245  }
246  }
247  return read_size;
248  }
249 };
250 
251 
252 } // namespace audio_tools
void onRead(BLECharacteristic *pCharacteristic) override
provide the next batch of audio data
Definition: AudioBLEServerESP32.h:193
void onWrite(BLECharacteristic *pCharacteristic) override
store the next batch of data
Definition: AudioBLEServerESP32.h:180
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
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:823