DRAFT BLE Arduino

This commit is contained in:
pschatzmann 2023-11-10 09:36:41 +01:00
parent dcb88c85fc
commit 8629540499
8 changed files with 2897 additions and 274 deletions

View File

@ -15,10 +15,8 @@
#include "Sandbox/BLE/AudioBLE.h"
AudioInfo info(16000, 1, 16);
SineWaveGenerator<int16_t>
sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000
GeneratedSoundStream<int16_t>
sound(sineWave); // Stream generated from sine wave
SineWaveGenerator<int16_t> sineWave(32000);
GeneratedSoundStream<int16_t> sound(sineWave);
Throttle throttle(sound);
AudioBLEClient ble;
ADPCMEncoder adpcm(AV_CODEC_ID_ADPCM_IMA_WAV);

View File

@ -1374,7 +1374,7 @@ class Throttle : public AudioStream {
uint64_t durationUsEff = micros() - start_time;
uint64_t durationUsToBe = getDelayUs(sum_frames);
int64_t waitUs = durationUsToBe - durationUsEff + cfg.correction_us;
LOGI("wait us: %ld", (long) waitUs);
LOGI("wait us: %ld", static_cast<long>(waitUs));
if (waitUs > 0) {
int64_t waitMs = waitUs / 1000;
if (waitMs > 0) delay(waitMs);

View File

@ -1,9 +1,12 @@
#pragma once
#ifdef ESP32
//# include "AudioBLEServer.h"
//# include "AudioBLEClient.h"
# include "AudioBLEServerESP32.h"
# include "AudioBLEClientESP32.h"
#else
# include "AudioBLEServer.h"
# include "AudioBLEClient.h"
#else
# error Device not supported
#endif

View File

@ -1,10 +1,7 @@
#pragma once
#include "AudioBLEStream.h"
//#include <BLE2902.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <ArduinoBLE.h>
namespace audio_tools {
@ -19,9 +16,7 @@ static AudioBLEClient *selfAudioBLEClient = nullptr;
* @copyright GPLv3
*/
class AudioBLEClient : public AudioBLEStream,
public BLEClientCallbacks,
public BLEAdvertisedDeviceCallbacks {
class AudioBLEClient : public AudioBLEStream {
public:
AudioBLEClient(int mtu = BLE_MTU) : AudioBLEStream(mtu) {
selfAudioBLEClient = this;
@ -32,59 +27,52 @@ public:
bool begin(const char *localName, int seconds) {
TRACEI();
// Init BLE device
BLEDevice::init(localName);
// Retrieve a Scanner and set the callback we want to use to be informed
// when we have detected a new device.
BLEScan *pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(this);
pBLEScan->setActiveScan(true);
pBLEScan->start(seconds);
BLE.begin();
BLE.setLocalName(localName);
BLE.scanForUuid(BLE_AUDIO_SERVICE_UUID);
return true;
}
void end() override {
TRACEI();
flush();
BLEDevice::deinit();
BLE.end();
}
size_t readBytes(uint8_t *data, size_t dataSize) override {
TRACED();
setupBLEClient();
if (!is_client_connected || !is_client_set_up)
if (!setupBLEClient()) {
return 0;
if (!ch01_char->canRead())
return 0;
// changed to auto to be version independent (it changed from std::string to
// String)
auto str = ch01_char->readValue();
if (str.length() > 0) {
memcpy(data, str.c_str(), str.length());
}
return str.length();
if (!ch01_char.canRead())
return 0;
return ch01_char.readValue(data, dataSize);
}
int available() override { return BLE_MTU - BLE_MTU_OVERHEAD; }
size_t write(const uint8_t *data, size_t dataSize) override {
TRACED();
setupBLEClient();
if (!is_client_connected || !is_client_set_up)
return 0;
if (!ch02_char->canWrite()){
if (!setupBLEClient()) {
return 0;
}
if (is_framed){
if (!ch02_char.canWrite()) {
return 0;
}
if (is_framed) {
writeChannel2Characteristic(data, dataSize);
delay(1);
} else {
// send only data with max mtu
for (int j=0; j<dataSize; j++){
for (int j = 0; j < dataSize; j++) {
write_buffer.write(data[j]);
if (write_buffer.isFull()){
writeChannel2Characteristic(write_buffer.data(), write_buffer.available());
if (write_buffer.isFull()) {
writeChannel2Characteristic(write_buffer.data(),
write_buffer.available());
write_buffer.reset();
}
}
@ -92,177 +80,106 @@ public:
return dataSize;
}
int availableForWrite() override {
return is_framed ? (BLE_MTU - BLE_MTU_OVERHEAD) : DEFAULT_BUFFER_SIZE;
int availableForWrite() override {
return is_framed ? (BLE_MTU - BLE_MTU_OVERHEAD) : DEFAULT_BUFFER_SIZE;
}
bool connected() override {
if (!setupBLEClient()) {
LOGE("setupBLEClient failed");
}
return is_client_connected;
}
bool connected() override { return setupBLEClient(); }
void setWriteThrottle(int ms){
write_throttle = ms;
}
void setWriteThrottle(int ms) { write_throttle = ms; }
void setConfirmWrite(bool flag){
write_confirmation_flag = flag;
}
void setConfirmWrite(bool flag) { write_confirmation_flag = flag; }
protected:
// client
BLEClient *p_client = nullptr;
BLEAdvertising *p_advertising = nullptr;
BLERemoteService *p_remote_service = nullptr;
BLEAddress *p_server_address = nullptr;
BLERemoteCharacteristic *ch01_char = nullptr; // read
BLERemoteCharacteristic *ch02_char = nullptr; // write
BLERemoteCharacteristic *info_char = nullptr;
BLEAdvertisedDevice advertised_device;
BLEUUID BLUEID_AUDIO_SERVICE_UUID{BLE_AUDIO_SERVICE_UUID};
BLEUUID BLUEID_CH1_UUID{BLE_CH1_UUID};
BLEUUID BLUEID_CH2_UUID{BLE_CH2_UUID};
BLEUUID BLUEID_INFO_UUID{BLE_INFO_UUID};
BLEDevice peripheral;
BLECharacteristic ch01_char;
BLECharacteristic ch02_char;
BLECharacteristic info_char;
SingleBuffer<uint8_t> write_buffer{0};
int write_throttle = 0;
bool write_confirmation_flag = false;
volatile bool is_client_connected = false;
bool is_client_set_up = false;
void onConnect(BLEClient *pClient) override {
TRACEI();
is_client_connected = true;
}
void onDisconnect(BLEClient *pClient) override {
TRACEI();
is_client_connected = false;
};
void writeAudioInfoCharacteristic(AudioInfo info) override {
TRACEI();
// send update via BLE
info_char->writeValue((uint8_t *)&info, sizeof(AudioInfo));
info_char.writeValue((uint8_t *)&info, sizeof(AudioInfo));
}
void writeChannel2Characteristic(const uint8_t*data, size_t len){
if (ch02_char->canWrite()) {
ch02_char->writeValue((uint8_t *)data, len, write_confirmation_flag);
void writeChannel2Characteristic(const uint8_t *data, size_t len) {
if (ch02_char.canWrite()) {
ch02_char.writeValue((uint8_t *)data, len, write_confirmation_flag);
delay(write_throttle);
}
}
bool readAudioInfoCharacteristic(){
if (!info_char->canRead())
bool readAudioInfoCharacteristic() {
if (!info_char.canRead())
return false;
auto str = info_char->readValue();
if (str.length() > 0) {
setAudioInfo((const uint8_t*)str.c_str(), str.length());
const uint8_t *str = info_char.value();
int len = info_char.valueLength();
if (len > 0) {
setAudioInfo(str, len);
return true;
}
return false;
}
// Scanning Results
void onResult(BLEAdvertisedDevice advertisedDevice) override {
TRACEI();
// Check if the name of the advertiser matches
if (advertisedDevice.haveServiceUUID() &&
advertisedDevice.isAdvertisingService(BLUEID_AUDIO_SERVICE_UUID)) {
LOGI("Service '%s' found!", BLE_AUDIO_SERVICE_UUID);
// save advertised_device in class variable
advertised_device = advertisedDevice;
// Scan can be stopped, we found what we are looking for
advertised_device.getScan()->stop();
}
delay(10);
}
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic,
uint8_t *pData, size_t length, bool isNotify) {
TRACEI();
if (pBLERemoteCharacteristic->getUUID().toString() ==
selfAudioBLEClient->BLE_INFO_UUID) {
selfAudioBLEClient->setAudioInfo(pData, length);
}
static void onInfoUpdated(BLEDevice central,
BLECharacteristic characteristic) {
selfAudioBLEClient->setAudioInfo(characteristic.value(),
characteristic.valueLength());
}
bool setupBLEClient() {
if (is_client_set_up)
// if we are already connected there is nothing to do
if (peripheral.connected()) {
return true;
}
TRACEI();
// setup buffer
if (write_buffer.size()==0){
if (write_buffer.size() == 0) {
write_buffer.resize(getMTU() - BLE_MTU_OVERHEAD);
}
if (p_client == nullptr)
p_client = BLEDevice::createClient();
// onConnect and on onDisconnect support
p_client->setClientCallbacks(this);
// Connect to the remove BLE Server.
LOGI("Connecting to %s ...",
advertised_device.getAddress().toString().c_str());
// p_client->connect(advertised_device.getAddress(),BLE_ADDR_TYPE_RANDOM);
p_client->connect(&advertised_device);
if (!p_client->isConnected()) {
LOGE("Connect failed");
peripheral = BLE.available();
if (!peripheral) {
return false;
}
LOGI("Connected to %s ...",
advertised_device.getAddress().toString().c_str());
LOGI("Setting mtu to %d", max_transfer_size);
assert(max_transfer_size > 0);
p_client->setMTU(max_transfer_size);
BLE.stopScan();
// Obtain a reference to the service we are after in the remote BLE
// server.
if (p_remote_service == nullptr) {
p_remote_service = p_client->getService(BLUEID_AUDIO_SERVICE_UUID);
if (p_remote_service == nullptr) {
LOGE("Failed to find our service UUID: %s", BLE_AUDIO_SERVICE_UUID);
return (false);
}
if (!peripheral.connect()) {
return false;
}
if (ch01_char == nullptr) {
ch01_char = p_remote_service->getCharacteristic(BLUEID_CH1_UUID);
if (ch01_char == nullptr) {
LOGE("Failed to find char. UUID: %s", BLE_CH1_UUID);
return false;
}
if (peripheral.discoverAttributes()) {
LOGI("Attributes discovered");
} else {
LOGE("Attribute discovery failed!");
peripheral.disconnect();
return false;
}
if (ch02_char == nullptr) {
ch02_char = p_remote_service->getCharacteristic(BLUEID_CH2_UUID);
if (ch02_char == nullptr) {
LOGE("Failed to find char. UUID: %s", BLE_CH2_UUID);
return false;
}
ch01_char = peripheral.characteristic(BLE_CH1_UUID);
if (!ch01_char) {
peripheral.disconnect();
return false;
}
if (is_audio_info_active && info_char == nullptr) {
info_char = p_remote_service->getCharacteristic(BLUEID_INFO_UUID);
if (info_char == nullptr) {
LOGE("Failed to find char. UUID: %s", BLE_INFO_UUID);
return false;
}
info_char->registerForNotify(notifyCallback);
readAudioInfoCharacteristic();
ch02_char = peripheral.characteristic(BLE_CH2_UUID);
if (!ch02_char) {
peripheral.disconnect();
return false;
}
LOGI("Connected to server: %s", is_client_connected ? "true" : "false");
is_client_set_up = true;
is_client_connected = true;
return is_client_connected;
if (is_audio_info_active) {
info_char = peripheral.characteristic(BLE_INFO_UUID);
info_char.setEventHandler(BLEUpdated, onInfoUpdated);
}
return true;
}
};

View File

@ -0,0 +1,269 @@
#pragma once
#include "AudioBLEStream.h"
//#include <BLE2902.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
namespace audio_tools {
class AudioBLEClient;
static AudioBLEClient *selfAudioBLEClient = nullptr;
/**
* @brief A simple BLE client that implements the serial protocol, so that it
* can be used to send and recevie audio. In BLE terminology this is a Central
* @ingroup communications
* @author Phil Schatzmann
* @copyright GPLv3
*/
class AudioBLEClient : public AudioBLEStream,
public BLEClientCallbacks,
public BLEAdvertisedDeviceCallbacks {
public:
AudioBLEClient(int mtu = BLE_MTU) : AudioBLEStream(mtu) {
selfAudioBLEClient = this;
max_transfer_size = mtu;
}
/// starts a BLE client
bool begin(const char *localName, int seconds) {
TRACEI();
// Init BLE device
BLEDevice::init(localName);
// Retrieve a Scanner and set the callback we want to use to be informed
// when we have detected a new device.
BLEScan *pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(this);
pBLEScan->setActiveScan(true);
pBLEScan->start(seconds);
return true;
}
void end() override {
TRACEI();
flush();
BLEDevice::deinit();
}
size_t readBytes(uint8_t *data, size_t dataSize) override {
TRACED();
setupBLEClient();
if (!is_client_connected || !is_client_set_up)
return 0;
if (!ch01_char->canRead())
return 0;
// changed to auto to be version independent (it changed from std::string to
// String)
auto str = ch01_char->readValue();
if (str.length() > 0) {
memcpy(data, str.c_str(), str.length());
}
return str.length();
}
int available() override { return BLE_MTU - BLE_MTU_OVERHEAD; }
size_t write(const uint8_t *data, size_t dataSize) override {
TRACED();
setupBLEClient();
if (!is_client_connected || !is_client_set_up)
return 0;
if (!ch02_char->canWrite()){
return 0;
}
if (is_framed){
writeChannel2Characteristic(data, dataSize);
delay(1);
} else {
// send only data with max mtu
for (int j=0; j<dataSize; j++){
write_buffer.write(data[j]);
if (write_buffer.isFull()){
writeChannel2Characteristic(write_buffer.data(), write_buffer.available());
write_buffer.reset();
}
}
}
return dataSize;
}
int availableForWrite() override {
return is_framed ? (BLE_MTU - BLE_MTU_OVERHEAD) : DEFAULT_BUFFER_SIZE;
}
bool connected() override {
if (!setupBLEClient()) {
LOGE("setupBLEClient failed");
}
return is_client_connected;
}
void setWriteThrottle(int ms){
write_throttle = ms;
}
void setConfirmWrite(bool flag){
write_confirmation_flag = flag;
}
protected:
// client
BLEClient *p_client = nullptr;
BLEAdvertising *p_advertising = nullptr;
BLERemoteService *p_remote_service = nullptr;
BLEAddress *p_server_address = nullptr;
BLERemoteCharacteristic *ch01_char = nullptr; // read
BLERemoteCharacteristic *ch02_char = nullptr; // write
BLERemoteCharacteristic *info_char = nullptr;
BLEAdvertisedDevice advertised_device;
BLEUUID BLUEID_AUDIO_SERVICE_UUID{BLE_AUDIO_SERVICE_UUID};
BLEUUID BLUEID_CH1_UUID{BLE_CH1_UUID};
BLEUUID BLUEID_CH2_UUID{BLE_CH2_UUID};
BLEUUID BLUEID_INFO_UUID{BLE_INFO_UUID};
SingleBuffer<uint8_t> write_buffer{0};
int write_throttle = 0;
bool write_confirmation_flag = false;
volatile bool is_client_connected = false;
bool is_client_set_up = false;
void onConnect(BLEClient *pClient) override {
TRACEI();
is_client_connected = true;
}
void onDisconnect(BLEClient *pClient) override {
TRACEI();
is_client_connected = false;
};
void writeAudioInfoCharacteristic(AudioInfo info) override {
TRACEI();
// send update via BLE
info_char->writeValue((uint8_t *)&info, sizeof(AudioInfo));
}
void writeChannel2Characteristic(const uint8_t*data, size_t len){
if (ch02_char->canWrite()) {
ch02_char->writeValue((uint8_t *)data, len, write_confirmation_flag);
delay(write_throttle);
}
}
bool readAudioInfoCharacteristic(){
if (!info_char->canRead())
return false;
auto str = info_char->readValue();
if (str.length() > 0) {
setAudioInfo((const uint8_t*)str.c_str(), str.length());
return true;
}
return false;
}
// Scanning Results
void onScanResult(BLEAdvertisedDevice advertisedDevice) override {
TRACEI();
// Check if the name of the advertiser matches
if (advertisedDevice.haveServiceUUID() &&
advertisedDevice.isAdvertisingService(BLUEID_AUDIO_SERVICE_UUID)) {
LOGI("Service '%s' found!", BLE_AUDIO_SERVICE_UUID);
// save advertised_device in class variable
advertised_device = advertisedDevice;
// Scan can be stopped, we found what we are looking for
advertised_device.getScan()->stop();
}
delay(10);
}
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic,
uint8_t *pData, size_t length, bool isNotify) {
TRACEI();
if (pBLERemoteCharacteristic->getUUID().toString() ==
selfAudioBLEClient->BLE_INFO_UUID) {
selfAudioBLEClient->setAudioInfo(pData, length);
}
}
bool setupBLEClient() {
if (is_client_set_up)
return true;
TRACEI();
// setup buffer
if (write_buffer.size()==0){
write_buffer.resize(getMTU() - BLE_MTU_OVERHEAD);
}
if (p_client == nullptr)
p_client = BLEDevice::createClient();
// onConnect and on onDisconnect support
p_client->setClientCallbacks(this);
// Connect to the remove BLE Server.
LOGI("Connecting to %s ...",
advertised_device.getAddress().toString().c_str());
// p_client->connect(advertised_device.getAddress(),BLE_ADDR_TYPE_RANDOM);
p_client->connect(&advertised_device);
if (!p_client->isConnected()) {
LOGE("Connect failed");
return false;
}
LOGI("Connected to %s ...",
advertised_device.getAddress().toString().c_str());
LOGI("Setting mtu to %d", max_transfer_size);
assert(max_transfer_size > 0);
p_client->setMTU(max_transfer_size);
// Obtain a reference to the service we are after in the remote BLE
// server.
if (p_remote_service == nullptr) {
p_remote_service = p_client->getService(BLUEID_AUDIO_SERVICE_UUID);
if (p_remote_service == nullptr) {
LOGE("Failed to find our service UUID: %s", BLE_AUDIO_SERVICE_UUID);
return (false);
}
}
if (ch01_char == nullptr) {
ch01_char = p_remote_service->getCharacteristic(BLUEID_CH1_UUID);
if (ch01_char == nullptr) {
LOGE("Failed to find char. UUID: %s", BLE_CH1_UUID);
return false;
}
}
if (ch02_char == nullptr) {
ch02_char = p_remote_service->getCharacteristic(BLUEID_CH2_UUID);
if (ch02_char == nullptr) {
LOGE("Failed to find char. UUID: %s", BLE_CH2_UUID);
return false;
}
}
if (is_audio_info_active && info_char == nullptr) {
info_char = p_remote_service->getCharacteristic(BLUEID_INFO_UUID);
if (info_char == nullptr) {
LOGE("Failed to find char. UUID: %s", BLE_INFO_UUID);
return false;
}
info_char->registerForNotify(notifyCallback);
readAudioInfoCharacteristic();
}
LOGI("Connected to server: %s", is_client_connected ? "true" : "false");
is_client_set_up = true;
is_client_connected = true;
return is_client_connected;
}
};
} // namespace audio_tools

View File

@ -1,55 +1,72 @@
#pragma once
#include "AudioBLEStream.h"
//#include <BLE2902.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <ArduinoBLE.h>
namespace audio_tools {
class AudioBLEServer;
class AudioBLEServer *selfAudioBLEServer = nullptr;
/**
* @brief A simple BLE server that implements the serial protocol, so that it
* can be used to send and recevie audio. In BLE terminologiy this is a Peripheral.
* can be used to send and recevie audio. In BLE terminologiy this is a
* Peripheral.
* This implementation uses the ArduinoBLE library!
* @ingroup communications
* @author Phil Schatzmann
* @copyright GPLv3
*/
class AudioBLEServer : public AudioBLEStream,
public BLECharacteristicCallbacks,
public BLEServerCallbacks {
class AudioBLEServer : public AudioBLEStream {
public:
AudioBLEServer(int mtu = BLE_MTU) : AudioBLEStream(mtu) {}
AudioBLEServer(int mtu = BLE_MTU) : AudioBLEStream(mtu) {
selfAudioBLEServer = this;
}
// starts a BLE server with the indicated name
bool begin(const char *name) {
TRACEI();
ble_server_name = name;
BLEDevice::init(name);
p_server = BLEDevice::createServer();
p_server->setCallbacks(this);
if (!BLE.begin()) {
LOGE("starting BLE failed");
return false;
}
// set the local name peripheral advertises
BLE.setLocalName(ble_server_name);
// assign event handlers for connected, disconnected to peripheral
BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);
// setup serice with characteristics
setupBLEService();
p_advertising = BLEDevice::getAdvertising();
p_advertising->addServiceUUID(BLE_AUDIO_SERVICE_UUID);
BLEDevice::startAdvertising();
// start advertising
BLE.advertise();
return true;
}
void end() override {
TRACEI();
flush();
BLEDevice::deinit();
BLE.end();
}
size_t readBytes(uint8_t *data, size_t dataSize) override {
TRACED();
if (!checkCentralConnected())
return 0;
size_t read_size = getReadSize(dataSize);
return receive_buffer.readArray(data, read_size);
}
int available() override {
if (!checkCentralConnected())
return 0;
if (is_framed)
return receive_sizes.peek();
return this->receive_buffer.available();
@ -57,9 +74,8 @@ public:
size_t write(const uint8_t *data, size_t dataSize) override {
LOGD("AudioBLEStream::write: %d", dataSize);
if (!connected()) {
if (!checkCentralConnected())
return 0;
}
if (is_framed && availableForWrite() < dataSize) {
return 0;
}
@ -67,30 +83,109 @@ public:
}
int availableForWrite() override {
int result = transmit_buffer.availableForWrite();
if (!checkCentralConnected())
return 0;
setupTXBuffer();
int result = transmit_buffer.availableForWrite();
// make sure we copy always a consistent amount of data
if (result < DEFAULT_BUFFER_SIZE) result = 0;
return result ;
if (result < DEFAULT_BUFFER_SIZE)
result = 0;
return result;
}
bool connected() override { return p_server->getConnectedCount() > 0; }
bool connected() override { return checkCentralConnected(); }
protected:
// server
BLEServer *p_server = nullptr;
BLEService *p_service = nullptr;
BLEAdvertising *p_advertising = nullptr;
BLECharacteristic *ch01_char;
BLECharacteristic *ch02_char;
BLECharacteristic *info_char;
BLEDescriptor ch01_desc{"2901"};
BLEDescriptor ch02_desc{"2901"};
BLEDescriptor info_desc{"2901"};
BLEService service{BLE_AUDIO_SERVICE_UUID}; // create service
BLECharacteristic ch01_char{BLE_CH1_UUID, BLERead,
BLE_MTU - BLE_MTU_OVERHEAD};
BLECharacteristic ch02_char{BLE_CH2_UUID, BLEWrite,
BLE_MTU - BLE_MTU_OVERHEAD};
BLECharacteristic info_char{BLE_INFO_UUID, BLERead | BLEWrite | BLENotify,
80};
BLEDescriptor ch01_desc{"2901", "channel 1"};
BLEDescriptor ch02_desc{"2901", "channel 2"};
BLEDescriptor info_desc{"2901", "info"};
RingBuffer<uint8_t> receive_buffer{0};
RingBuffer<uint16_t> receive_sizes{0};
RingBuffer<uint8_t> transmit_buffer{0};
RingBuffer<uint16_t> transmit_buffer_sizes{0};
static void blePeripheralConnectHandler(BLEDevice device) {
selfAudioBLEServer->onConnect(device);
}
static void blePeripheralDisconnectHandler(BLEDevice device) {
selfAudioBLEServer->onDisconnect(device);
}
static void bleOnWrite(BLEDevice device, BLECharacteristic characteristic) {
selfAudioBLEServer->onWrite(characteristic);
}
static void bleOnRead(BLEDevice device, BLECharacteristic characteristic) {
TRACED();
selfAudioBLEServer->onRead(characteristic);
}
void onConnect(BLEDevice device) { TRACEI(); }
void onDisconnect(BLEDevice device) {
TRACEI();
BLE.advertise();
}
/// store the next batch of data
void onWrite(BLECharacteristic characteristic) {
TRACED();
setupRXBuffer();
// changed to auto to be version independent (it changed from std::string to
// String)
if (Str(BLE_INFO_UUID).equals(characteristic.uuid())) {
setAudioInfo((uint8_t *)characteristic.value(),
characteristic.valueLength());
} else {
receiveAudio((uint8_t *)characteristic.value(),
characteristic.valueLength());
}
}
/// provide the next batch of audio data
void onRead(BLECharacteristic characteristic) {
TRACED();
auto uuid = Str(characteristic.uuid());
if (uuid == BLE_CH1_UUID || uuid == BLE_CH2_UUID) {
TRACEI();
setupTXBuffer();
int len = std::min(getMTU() - BLE_MTU_OVERHEAD,
(int)transmit_buffer.available());
if (is_framed) {
len = transmit_buffer_sizes.read();
}
LOGI("%s: len: %d, buffer: %d", uuid.c_str(), len,
transmit_buffer.size());
if (len>0){
assert(len==512);
uint8_t tmp[len];
transmit_buffer.readArray(tmp, len);
characteristic.writeValue(tmp, len);
}
}
}
bool checkCentralConnected() {
BLEDevice central = BLE.central();
// if a central is connected to the peripheral:
if (central)
return central.connected();
return false;
}
virtual void receiveAudio(const uint8_t *data, size_t size) {
while (receive_buffer.availableForWrite() < size) {
// wait for ringbuffer to get freed up
@ -106,16 +201,17 @@ protected:
// send update via BLE
Str str = toStr(info);
LOGI("AudioInfo: %s", str.c_str());
info_char->setValue((uint8_t *)str.c_str(), str.length() + 1);
info_char->notify();
info_char.setValue((uint8_t *)str.c_str(), str.length() + 1);
}
int getMTU() override {
TRACED();
if (max_transfer_size == 0) {
int peer_max_transfer_size =
p_server->getPeerMTU(p_server->getConnId()) - BLE_MTU_OVERHEAD;
max_transfer_size = std::min(BLE_MTU - BLE_MTU, peer_max_transfer_size);
// int peer_max_transfer_size =
// p_server->getPeerMTU(p_server->getConnId()) - BLE_MTU_OVERHEAD;
// max_transfer_size = std::min(BLE_MTU - BLE_MTU,
// peer_max_transfer_size);
max_transfer_size = BLE_MTU - BLE_MTU_OVERHEAD;
LOGI("max_transfer_size: %d", max_transfer_size);
}
@ -124,91 +220,42 @@ protected:
void setupBLEService() {
TRACEI();
// characteristic property is what the other device does.
// set the UUID for the service this peripheral advertises
BLE.setAdvertisedService(service);
if (p_service == nullptr) {
p_service = p_server->createService(BLE_AUDIO_SERVICE_UUID);
ch01_char.addDescriptor(ch01_desc);
ch02_char.addDescriptor(ch02_desc);
ch01_char = p_service->createCharacteristic(
BLE_CH1_UUID, BLECharacteristic::PROPERTY_READ );
ch01_desc.setValue("Channel 1");
ch01_char->addDescriptor(&ch01_desc);
ch01_char->setCallbacks(this);
// add the characteristic to the service
service.addCharacteristic(ch01_char);
service.addCharacteristic(ch02_char);
ch02_char = p_service->createCharacteristic(
BLE_CH2_UUID, BLECharacteristic::PROPERTY_WRITE);
ch02_desc.setValue("Channel 2");
ch02_char->addDescriptor(&ch02_desc);
ch02_char->setCallbacks(this);
// assign event handlers for characteristic
ch02_char.setEventHandler(BLEWritten, bleOnWrite);
ch01_char.setEventHandler(BLERead, bleOnRead);
// optional setup of audio info notifications
if (is_audio_info_active && info_char == nullptr) {
info_char = p_service->createCharacteristic(
BLE_INFO_UUID, BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE);
info_desc.setValue("Audio Info");
info_char->addDescriptor(&info_desc);
info_char->setCallbacks(this);
}
p_service->start();
getMTU();
if (info_char != nullptr) {
writeAudioInfoCharacteristic(info);
}
if (is_audio_info_active) {
info_char.addDescriptor(info_desc);
service.addCharacteristic(info_char);
}
}
void onConnect(BLEServer *pServer) override {
TRACEI();
}
// add service
BLE.addService(service);
void onDisconnect(BLEServer *pServer) override {
TRACEI();
BLEDevice::startAdvertising();
}
/// store the next batch of data
void onWrite(BLECharacteristic *pCharacteristic) override {
TRACED();
setupRXBuffer();
// changed to auto to be version independent (it changed from std::string to String)
auto value = pCharacteristic->getValue();
if (pCharacteristic->getUUID().toString() == BLE_INFO_UUID) {
setAudioInfo((uint8_t *)&value[0], value.length());
} else {
receiveAudio((uint8_t *)&value[0], value.length());
// provide AudioInfo
if (is_audio_info_active) {
writeAudioInfoCharacteristic(info);
}
}
/// provide the next batch of audio data
void onRead(BLECharacteristic *pCharacteristic) override {
TRACED();
// changed to auto to be version independent (it changed from std::string to String)
auto uuid = pCharacteristic->getUUID().toString();
if (uuid == BLE_CH1_UUID || uuid == BLE_CH2_UUID) {
setupTXBuffer();
int len = std::min(getMTU() - BLE_MTU_OVERHEAD, (int)transmit_buffer.available());
if (is_framed) {
len = transmit_buffer_sizes.read();
}
LOGD("%s: len: %d, buffer: %d", uuid.c_str(), len,
transmit_buffer.size());
uint8_t tmp[len];
transmit_buffer.readArray(tmp, len);
pCharacteristic->setValue(tmp, len);
}
// Read callback works only when we provide some initial data
uint8_t tmp[512]={0xFF};
ch01_char.writeValue(tmp, 512, false);
}
void setupTXBuffer() {
if (transmit_buffer.size() == 0) {
LOGI("Setting transmit_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU());
LOGI("Setting transmit_buffer to %d for mtu %d", RX_BUFFER_SIZE,
getMTU());
transmit_buffer.resize(TX_BUFFER_SIZE);
if (is_framed) {
transmit_buffer_sizes.resize(TX_COUNT);
@ -246,5 +293,4 @@ protected:
}
};
} // namespace audio_tools

View File

@ -0,0 +1,250 @@
#pragma once
#include "AudioBLEStream.h"
//#include <BLE2902.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
namespace audio_tools {
/**
* @brief A simple BLE server that implements the serial protocol, so that it
* can be used to send and recevie audio. In BLE terminologiy this is a Peripheral.
* @ingroup communications
* @author Phil Schatzmann
* @copyright GPLv3
*/
class AudioBLEServer : public AudioBLEStream,
public BLECharacteristicCallbacks,
public BLEServerCallbacks {
public:
AudioBLEServer(int mtu = BLE_MTU) : AudioBLEStream(mtu) {}
// starts a BLE server with the indicated name
bool begin(const char *name) {
TRACEI();
ble_server_name = name;
BLEDevice::init(name);
p_server = BLEDevice::createServer();
p_server->setCallbacks(this);
setupBLEService();
p_advertising = BLEDevice::getAdvertising();
p_advertising->addServiceUUID(BLE_AUDIO_SERVICE_UUID);
BLEDevice::startAdvertising();
return true;
}
void end() override {
TRACEI();
flush();
BLEDevice::deinit();
}
size_t readBytes(uint8_t *data, size_t dataSize) override {
TRACED();
size_t read_size = getReadSize(dataSize);
return receive_buffer.readArray(data, read_size);
}
int available() override {
if (is_framed)
return receive_sizes.peek();
return this->receive_buffer.available();
}
size_t write(const uint8_t *data, size_t dataSize) override {
LOGD("AudioBLEStream::write: %d", dataSize);
if (!connected()) {
return 0;
}
if (is_framed && availableForWrite() < dataSize) {
return 0;
}
return transmit_buffer.writeArray(data, dataSize);
}
int availableForWrite() override {
int result = transmit_buffer.availableForWrite();
// make sure we copy always a consistent amount of data
if (result < DEFAULT_BUFFER_SIZE) result = 0;
return result ;
}
bool connected() override { return p_server->getConnectedCount() > 0; }
protected:
// server
BLEServer *p_server = nullptr;
BLEService *p_service = nullptr;
BLEAdvertising *p_advertising = nullptr;
BLECharacteristic *ch01_char;
BLECharacteristic *ch02_char;
BLECharacteristic *info_char;
BLEDescriptor ch01_desc{"2901"};
BLEDescriptor ch02_desc{"2901"};
BLEDescriptor info_desc{"2901"};
RingBuffer<uint8_t> receive_buffer{0};
RingBuffer<uint16_t> receive_sizes{0};
RingBuffer<uint8_t> transmit_buffer{0};
RingBuffer<uint16_t> transmit_buffer_sizes{0};
virtual void receiveAudio(const uint8_t *data, size_t size) {
while (receive_buffer.availableForWrite() < size) {
// wait for ringbuffer to get freed up
delay(10);
}
if (is_framed)
receive_sizes.write(size);
receive_buffer.writeArray(data, size);
}
void writeAudioInfoCharacteristic(AudioInfo info) {
TRACEI();
// send update via BLE
Str str = toStr(info);
LOGI("AudioInfo: %s", str.c_str());
info_char->setValue((uint8_t *)str.c_str(), str.length() + 1);
info_char->notify();
}
int getMTU() override {
TRACED();
if (max_transfer_size == 0) {
int peer_max_transfer_size =
p_server->getPeerMTU(p_server->getConnId()) - BLE_MTU_OVERHEAD;
max_transfer_size = std::min(BLE_MTU - BLE_MTU, peer_max_transfer_size);
LOGI("max_transfer_size: %d", max_transfer_size);
}
return max_transfer_size;
}
void setupBLEService() {
TRACEI();
// characteristic property is what the other device does.
if (p_service == nullptr) {
p_service = p_server->createService(BLE_AUDIO_SERVICE_UUID);
ch01_char = p_service->createCharacteristic(
BLE_CH1_UUID, BLECharacteristic::PROPERTY_READ );
ch01_desc.setValue("Channel 1");
ch01_char->addDescriptor(&ch01_desc);
ch01_char->setCallbacks(this);
ch02_char = p_service->createCharacteristic(
BLE_CH2_UUID, BLECharacteristic::PROPERTY_WRITE);
ch02_desc.setValue("Channel 2");
ch02_char->addDescriptor(&ch02_desc);
ch02_char->setCallbacks(this);
// optional setup of audio info notifications
if (is_audio_info_active && info_char == nullptr) {
info_char = p_service->createCharacteristic(
BLE_INFO_UUID, BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY |
BLECharacteristic::PROPERTY_INDICATE);
info_desc.setValue("Audio Info");
info_char->addDescriptor(&info_desc);
info_char->setCallbacks(this);
}
p_service->start();
getMTU();
if (info_char != nullptr) {
writeAudioInfoCharacteristic(info);
}
}
}
void onConnect(BLEServer *pServer) override {
TRACEI();
}
void onDisconnect(BLEServer *pServer) override {
TRACEI();
BLEDevice::startAdvertising();
}
/// store the next batch of data
void onWrite(BLECharacteristic *pCharacteristic) override {
TRACED();
setupRXBuffer();
// changed to auto to be version independent (it changed from std::string to String)
auto value = pCharacteristic->getValue();
if (pCharacteristic->getUUID().toString() == BLE_INFO_UUID) {
setAudioInfo((uint8_t *)&value[0], value.length());
} else {
receiveAudio((uint8_t *)&value[0], value.length());
}
}
/// provide the next batch of audio data
void onRead(BLECharacteristic *pCharacteristic) override {
TRACED();
// changed to auto to be version independent (it changed from std::string to String)
auto uuid = pCharacteristic->getUUID().toString();
if (uuid == BLE_CH1_UUID || uuid == BLE_CH2_UUID) {
setupTXBuffer();
int len = std::min(getMTU() - BLE_MTU_OVERHEAD, (int)transmit_buffer.available());
if (is_framed) {
len = transmit_buffer_sizes.read();
}
LOGD("%s: len: %d, buffer: %d", uuid.c_str(), len,
transmit_buffer.size());
uint8_t tmp[len];
transmit_buffer.readArray(tmp, len);
pCharacteristic->setValue(tmp, len);
}
}
void setupTXBuffer() {
if (transmit_buffer.size() == 0) {
LOGI("Setting transmit_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU());
transmit_buffer.resize(TX_BUFFER_SIZE);
if (is_framed) {
transmit_buffer_sizes.resize(TX_COUNT);
}
}
}
void setupRXBuffer() {
if (receive_buffer.size() == 0) {
LOGI("Setting receive_buffer to %d for mtu %d", RX_BUFFER_SIZE, getMTU());
receive_buffer.resize(RX_BUFFER_SIZE);
if (is_framed) {
receive_sizes.resize(RX_COUNT);
}
}
}
size_t getReadSize(size_t dataSize) {
size_t read_size = dataSize;
if (is_framed) {
read_size = 0;
if (receive_sizes.available() > 0) {
read_size = receive_sizes.read();
}
if (dataSize < read_size) {
LOGE("read size too small: %d - it must be >= %d", dataSize, read_size);
return 0;
}
if (receive_buffer.available() < read_size) {
LOGE("missing data in buffer");
return 0;
}
}
return read_size;
}
};
} // namespace audio_tools

File diff suppressed because it is too large Load Diff