arduino-audio-tools
AudioBLEClientESP32.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 
10 namespace audio_tools {
11 
12 class AudioBLEClient;
13 static AudioBLEClient *selfAudioBLEClient = nullptr;
14 
23 class AudioBLEClient : public AudioBLEStream,
24  public BLEClientCallbacks,
25  public BLEAdvertisedDeviceCallbacks {
26 public:
27  AudioBLEClient(int mtu = BLE_MTU) : AudioBLEStream(mtu) {
28  selfAudioBLEClient = this;
29  max_transfer_size = mtu;
30  }
31 
33  bool begin(const char *localName, int seconds) {
34  TRACEI();
35  // Init BLE device
36  BLEDevice::init(localName);
37 
38  // Retrieve a Scanner and set the callback we want to use to be informed
39  // when we have detected a new device.
40  BLEScan *pBLEScan = BLEDevice::getScan();
41  pBLEScan->setAdvertisedDeviceCallbacks(this);
42  pBLEScan->setActiveScan(true);
43  pBLEScan->start(seconds);
44  return true;
45  }
46 
47  void end() override {
48  TRACEI();
49  flush();
50  BLEDevice::deinit();
51  }
52 
53  size_t readBytes(uint8_t *data, size_t len) override {
54  TRACED();
55  setupBLEClient();
56  if (!is_client_connected || !is_client_set_up)
57  return 0;
58  if (!ch01_char->canRead())
59  return 0;
60  // changed to auto to be version independent (it changed from std::string to
61  // String)
62  auto str = ch01_char->readValue();
63  if (str.length() > 0) {
64  memcpy(data, str.c_str(), str.length());
65  }
66  return str.length();
67  }
68 
69  int available() override { return BLE_MTU - BLE_MTU_OVERHEAD; }
70 
71  size_t write(const uint8_t *data, size_t len) override {
72  TRACED();
73  setupBLEClient();
74  if (!is_client_connected || !is_client_set_up)
75  return 0;
76  if (!ch02_char->canWrite()){
77  return 0;
78  }
79 
80  if (is_framed){
81  writeChannel2Characteristic(data, len);
82  delay(1);
83  } else {
84  // send only data with max mtu
85  for (int j=0; j<len; j++){
86  write_buffer.write(data[j]);
87  if (write_buffer.isFull()){
88  writeChannel2Characteristic(write_buffer.data(), write_buffer.available());
89  write_buffer.reset();
90  }
91  }
92  }
93  return len;
94  }
95 
96  int availableForWrite() override {
97  return is_framed ? (BLE_MTU - BLE_MTU_OVERHEAD) : DEFAULT_BUFFER_SIZE;
98  }
99 
100  bool connected() override {
101  if (!setupBLEClient()) {
102  LOGE("setupBLEClient failed");
103  }
104  return is_client_connected;
105  }
106 
107  void setWriteThrottle(int ms){
108  write_throttle = ms;
109  }
110 
111  void setConfirmWrite(bool flag){
112  write_confirmation_flag = flag;
113  }
114 
115 protected:
116  // client
117  BLEClient *p_client = nullptr;
118  BLEAdvertising *p_advertising = nullptr;
119  BLERemoteService *p_remote_service = nullptr;
120  BLEAddress *p_server_address = nullptr;
121  BLERemoteCharacteristic *ch01_char = nullptr; // read
122  BLERemoteCharacteristic *ch02_char = nullptr; // write
123  BLERemoteCharacteristic *info_char = nullptr;
124  BLEAdvertisedDevice advertised_device;
125  BLEUUID BLUEID_AUDIO_SERVICE_UUID{BLE_AUDIO_SERVICE_UUID};
126  BLEUUID BLUEID_CH1_UUID{BLE_CH1_UUID};
127  BLEUUID BLUEID_CH2_UUID{BLE_CH2_UUID};
128  BLEUUID BLUEID_INFO_UUID{BLE_INFO_UUID};
129  SingleBuffer<uint8_t> write_buffer{0};
130  int write_throttle = 0;
131  bool write_confirmation_flag = false;
132 
133  volatile bool is_client_connected = false;
134  bool is_client_set_up = false;
135 
136  void onConnect(BLEClient *pClient) override {
137  TRACEI();
138  is_client_connected = true;
139  }
140 
141  void onDisconnect(BLEClient *pClient) override {
142  TRACEI();
143  is_client_connected = false;
144  };
145 
146  void writeAudioInfoCharacteristic(AudioInfo info) override {
147  TRACEI();
148  // send update via BLE
149  info_char->writeValue((uint8_t *)&info, sizeof(AudioInfo));
150  }
151 
152  void writeChannel2Characteristic(const uint8_t*data, size_t len){
153  if (ch02_char->canWrite()) {
154  ch02_char->writeValue((uint8_t *)data, len, write_confirmation_flag);
155  delay(write_throttle);
156  }
157  }
158 
159  bool readAudioInfoCharacteristic(){
160  if (!info_char->canRead())
161  return false;
162  auto str = info_char->readValue();
163  if (str.length() > 0) {
164  setAudioInfo((const uint8_t*)str.c_str(), str.length());
165  return true;
166  }
167  return false;
168  }
169 
170  // Scanning Results
171  void onResult(BLEAdvertisedDevice advertisedDevice) override {
172  TRACEI();
173  // Check if the name of the advertiser matches
174  if (advertisedDevice.haveServiceUUID() &&
175  advertisedDevice.isAdvertisingService(BLUEID_AUDIO_SERVICE_UUID)) {
176  LOGI("Service '%s' found!", BLE_AUDIO_SERVICE_UUID);
177  // save advertised_device in class variable
178  advertised_device = advertisedDevice;
179  // Scan can be stopped, we found what we are looking for
180  advertised_device.getScan()->stop();
181  }
182  delay(10);
183  }
184 
185  static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic,
186  uint8_t *pData, size_t length, bool isNotify) {
187  TRACEI();
188  if (pBLERemoteCharacteristic->getUUID().toString() ==
189  selfAudioBLEClient->BLE_INFO_UUID) {
190  selfAudioBLEClient->setAudioInfo(pData, length);
191  }
192  }
193 
194  bool setupBLEClient() {
195  if (is_client_set_up)
196  return true;
197 
198  TRACEI();
199 
200  // setup buffer
201  if (write_buffer.size()==0){
202  write_buffer.resize(getMTU() - BLE_MTU_OVERHEAD);
203  }
204 
205  if (p_client == nullptr)
206  p_client = BLEDevice::createClient();
207 
208  // onConnect and on onDisconnect support
209  p_client->setClientCallbacks(this);
210 
211  // Connect to the remove BLE Server.
212  LOGI("Connecting to %s ...",
213  advertised_device.getAddress().toString().c_str());
214  // p_client->connect(advertised_device.getAddress(),BLE_ADDR_TYPE_RANDOM);
215  p_client->connect(&advertised_device);
216  if (!p_client->isConnected()) {
217  LOGE("Connect failed");
218  return false;
219  }
220  LOGI("Connected to %s ...",
221  advertised_device.getAddress().toString().c_str());
222 
223  LOGI("Setting mtu to %d", max_transfer_size);
224  assert(max_transfer_size > 0);
225  p_client->setMTU(max_transfer_size);
226 
227  // Obtain a reference to the service we are after in the remote BLE
228  // server.
229  if (p_remote_service == nullptr) {
230  p_remote_service = p_client->getService(BLUEID_AUDIO_SERVICE_UUID);
231  if (p_remote_service == nullptr) {
232  LOGE("Failed to find our service UUID: %s", BLE_AUDIO_SERVICE_UUID);
233  return (false);
234  }
235  }
236 
237  if (ch01_char == nullptr) {
238  ch01_char = p_remote_service->getCharacteristic(BLUEID_CH1_UUID);
239  if (ch01_char == nullptr) {
240  LOGE("Failed to find char. UUID: %s", BLE_CH1_UUID);
241  return false;
242  }
243  }
244 
245  if (ch02_char == nullptr) {
246  ch02_char = p_remote_service->getCharacteristic(BLUEID_CH2_UUID);
247  if (ch02_char == nullptr) {
248  LOGE("Failed to find char. UUID: %s", BLE_CH2_UUID);
249  return false;
250  }
251  }
252 
253  if (is_audio_info_active && info_char == nullptr) {
254  info_char = p_remote_service->getCharacteristic(BLUEID_INFO_UUID);
255  if (info_char == nullptr) {
256  LOGE("Failed to find char. UUID: %s", BLE_INFO_UUID);
257  return false;
258  }
259  info_char->registerForNotify(notifyCallback);
260  readAudioInfoCharacteristic();
261 
262  }
263  LOGI("Connected to server: %s", is_client_connected ? "true" : "false");
264  is_client_set_up = true;
265  is_client_connected = true;
266  return is_client_connected;
267  }
268 
269  int getMTU() override { return BLE_MTU; }
270 
271 
272 };
273 
274 } // namespace audio_tools
bool begin(const char *localName, int seconds)
starts a BLE client
Definition: AudioBLEClientESP32.h:33
void setAudioInfo(AudioInfo info)
Defines the input AudioInfo.
Definition: AudioBLEStream.h:27
T * data()
Provides address of actual data.
Definition: Buffers.h:252
bool write(T sample) override
write add an entry to the buffer
Definition: Buffers.h:194
int available() override
provides the number of entries that are available to read
Definition: Buffers.h:219
bool isFull() override
checks if the buffer is full
Definition: Buffers.h:226
void reset() override
clears the buffer
Definition: Buffers.h:254
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:823