diff --git a/README.md b/README.md index 8196a04..02e8494 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ void loop() ``` will create a instance named `BLEMIDI` and listens to incoming MIDI. +#### Namespace collision with ArduinoBLE +Note: error: redefinition of 'class BLEDescriptor' is a namespace collision on class BLEDescriptor between ESP32 BLE and ArduinoBLE. +Solution: remove ArduinoBLE + ### using NimBLE for ESP32 with a custom name and turns LED on when its connected ```cpp diff --git a/ci/build-arduino.sh b/ci/build-arduino.sh index 55a9814..fd30016 100644 --- a/ci/build-arduino.sh +++ b/ci/build-arduino.sh @@ -6,15 +6,16 @@ shopt -s globstar # Make sure we are inside the github workspace cd $GITHUB_WORKSPACE # Create directories -mkdir $HOME/Arduino -mkdir $HOME/Arduino/libraries +mkdir $HOME/Arduino -p +mkdir $HOME/Arduino/libraries -p # Install Arduino IDE export PATH=$PATH:$GITHUB_WORKSPACE/bin curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh arduino-cli config init arduino-cli config set library.enable_unsafe_install true # arduino-cli core update-index --additional-urls https://arduino.esp8266.com/stable/package_esp8266com_index.json -arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json +#arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json +sed -i 's+\[\]+\[https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json\]+g' /home/runner/.arduino15/arduino-cli.yaml arduino-cli core update-index # Install Arduino AVR core @@ -24,13 +25,13 @@ arduino-cli core install arduino:samd arduino-cli core install esp32:esp32 # List the boards -arduino-cli board list +arduino-cli board listall # Link Arduino library ln -s $GITHUB_WORKSPACE $HOME/Arduino/libraries/CI_Test_Library arduino-cli lib install "MIDI library" -arduino-cli lib install ArduinoBLE +#arduino-cli lib install ArduinoBLE arduino-cli lib install NimBLE-Arduino # Compile all *.ino files for the Arduino Uno @@ -48,7 +49,14 @@ arduino-cli lib install NimBLE-Arduino # arduino-cli compile -b arduino:esp8266:??? $f # done +dR=$(pwd) + # Compile all *.ino files for the Arduino Uno for f in **/*.ino ; do - arduino-cli compile -b arduino:esp32:??? $f + echo "Project: $f" + d=$(dirname $(readlink -f $f)) + echo $d + cd $d + arduino-cli compile -b esp32:esp32:esp32 *.ino --clean + cd $dR done diff --git a/examples/CustomerBufferSize/CustomerBufferSize.ino b/examples/CustomerBufferSize/CustomerBufferSize.ino new file mode 100644 index 0000000..8aa7e94 --- /dev/null +++ b/examples/CustomerBufferSize/CustomerBufferSize.ino @@ -0,0 +1,118 @@ +#include + +static uint32_t customPasskeyRequest() +{ + // FILL WITH YOUR CUSTOM AUTH METHOD CODE or PASSKEY + // FOR EXAMPLE: + uint32_t passkey = 123456; + + // Serial.println("Client Passkey Request"); + + /** return the passkey to send to the server */ + return passkey; +}; + + struct CustomBufferSizeSettings : public BLEMIDI_NAMESPACE::DefaultSettings { + //See all options and them explanation in the library. + + /* + ##### BLE DEVICE NAME ##### + */ + //static constexpr char *name = "BleMidiClient"; + /* + ###### TX POWER ##### + */ + //static const esp_power_level_t clientTXPwr = ESP_PWR_LVL_P9; + /* + ###### SECURITY ##### + */ + //static const uint8_t clientSecurityCapabilities = BLE_HS_IO_NO_INPUT_OUTPUT; + //static const bool clientBond = true; + //static const bool clientMITM = false; + //static const bool clientPair = true; + //static constexpr PasskeyRequestCallback userOnPassKeyRequest = customPasskeyRequest; + /* + ###### BLE COMMUNICATION PARAMS ###### + */ + //static const uint16_t commMinInterval = 6; // 7.5ms + //static const uint16_t commMaxInterval = 35; // 40ms + //static const uint16_t commLatency = 0; // + //static const uint16_t commTimeOut = 200; // 2000ms + /* + ###### BLE FORCE NEW CONNECTION ###### + */ + //static const bool forceNewConnection = false; + /* + ###### BLE SUBSCRIPTION: NOTIFICATION & RESPONSE ###### + */ + //static const bool notification = true; + //static const bool response = true; + /* + ###### AND THE OTHER SETTINGS OF MIDI LIBRARY ###### + */ + static const size_t MaxBufferSize = 16; + +}; + +#include +//#include +//#include + +#ifndef LED_BUILTIN +#define LED_BUILTIN 2 +#endif + +BLEMIDI_CREATE_CUSTOM_INSTANCE("Esp32-NimBLE-MIDI", MIDI, CustomBufferSizeSettings); + +unsigned long t0 = millis(); +bool isConnected = false; + + +// ----------------------------------------------------------------------------- +// When BLE connected, LED will turn on (indication that connection was successful) +// When receiving a NoteOn, LED will go out, on NoteOff, light comes back on. +// This is an easy and conveniant way to show that the connection is alive and working. +// ----------------------------------------------------------------------------- +void setup() +{ + Serial.begin(115200); + while (!Serial) {} + Serial.println("booting"); + + MIDI.begin(); + + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LOW); + + BLEMIDI.setHandleConnected([]() { + isConnected = true; + digitalWrite(LED_BUILTIN, HIGH); + }); + + BLEMIDI.setHandleDisconnected([]() { + isConnected = false; + digitalWrite(LED_BUILTIN, LOW); + }); + + MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) { + digitalWrite(LED_BUILTIN, LOW); + }); + MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) { + digitalWrite(LED_BUILTIN, HIGH); + }); +} + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +void loop() +{ + MIDI.read(); + + if (isConnected && (millis() - t0) > 1000) + { + t0 = millis(); + + MIDI.sendNoteOn (60, 100, 1); // note 60, velocity 100 on channel 1 + } +} diff --git a/examples/MidiBle/MidiBle.ino b/examples/MidiBle/MidiBle.ino index b1cfa58..69968b1 100644 --- a/examples/MidiBle/MidiBle.ino +++ b/examples/MidiBle/MidiBle.ino @@ -2,7 +2,6 @@ //#include #include -//#include //#include BLEMIDI_CREATE_DEFAULT_INSTANCE() diff --git a/examples/MidiBle_Client/MidiBle_Client.ino b/examples/MidiBle_Client/MidiBle_Client.ino index 9a44bdf..08efbeb 100644 --- a/examples/MidiBle_Client/MidiBle_Client.ino +++ b/examples/MidiBle_Client/MidiBle_Client.ino @@ -14,10 +14,10 @@ * the name of the server or the BLE address of the server. If you want to connect * to the first MIDI server BLE found by the device, you just have to set the name field empty (""). * - * FOR ADVANCED USERS: Other advanced BLE configurations can be changed in hardware/BLEMIDI_Client_ESP32.h - * #defines in the head of the file (IMPORTANT: Only the first user defines must be modified). These configurations - * are related to security (password, pairing and securityCallback()), communication params, the device name - * and other stuffs. Modify defines at your own risk. + * FOR ADVANCED USERS: Other advanced BLE configurations can be changed with a struct that heritate + * from BLEMIDI_NAMESPACE::DefaultSettingsClient. These configurations are related to + * security (password, pairing and securityCallback()), communication params, the device name + * and other stuffs. Modify those settings at your own risk. * * * @@ -29,17 +29,25 @@ #include #include - //#include //#include -//#include //#include -BLEMIDI_CREATE_DEFAULT_INSTANCE(); //Connect to first server found +#ifndef LED_BUILTIN +#define LED_BUILTIN 2 +#endif + +//See DefaultSettingsClient in hardware/BLEMIDI_Client_ESP32.h for more configurable settings +// If you do not redefine a parameter, it will use the default value for these parameter +struct CustomBufferSizeSettings : public BLEMIDI_NAMESPACE::DefaultSettingsClient { + static const size_t MaxBufferSize = 16; +}; + +BLEMIDI_CREATE_CUSTOM_INSTANCE("Esp32-BLE-MIDI", MIDI, CustomBufferSizeSettings); // Connect to a server named "Esp32-BLE-MIDI" and use CustomBufferSizeSettings as settings of client -//BLEMIDI_CREATE_INSTANCE("",MIDI) //Connect to the first server found -//BLEMIDI_CREATE_INSTANCE("f2:c1:d9:36:e7:6b",MIDI) //Connect to a specific BLE address server -//BLEMIDI_CREATE_INSTANCE("MyBLEserver",MIDI) //Connect to a specific name server +//BLEMIDI_CREATE_INSTANCE("",MIDI) //Connect to the first server found, using default settings +//BLEMIDI_CREATE_INSTANCE("f2:c1:d9:36:e7:6b",MIDI) //Connect to a specific BLE address server, using default settings +//BLEMIDI_CREATE_INSTANCE("MyBLEserver",MIDI) //Connect to a specific name server, using default settings #ifndef LED_BUILTIN #define LED_BUILTIN 2 //modify for match with yout board diff --git a/examples/SysEx_Receive/SysEx_Receive.ino b/examples/SysEx_Receive/SysEx_Receive.ino index 98cc209..79b4684 100644 --- a/examples/SysEx_Receive/SysEx_Receive.ino +++ b/examples/SysEx_Receive/SysEx_Receive.ino @@ -2,9 +2,12 @@ //#include #include -//#include //#include +#ifndef LED_BUILTIN +#define LED_BUILTIN 2 +#endif + BLEMIDI_CREATE_INSTANCE("CustomName", MIDI) bool isConnected = false; diff --git a/examples/SysEx_Send/SysEx_Send.ino b/examples/SysEx_Send/SysEx_Send.ino index b1c28b8..4c5b855 100644 --- a/examples/SysEx_Send/SysEx_Send.ino +++ b/examples/SysEx_Send/SysEx_Send.ino @@ -2,9 +2,12 @@ #include //#include -//#include //#include +#ifndef LED_BUILTIN +#define LED_BUILTIN 2 +#endif + byte sysex4[] = { 0xF0, 0x43, 0x20, 0xF7 }; byte sysex5[] = { 0xF0, 0x43, 0x20, 0x7E, 0xF7 }; byte sysex6[] = { 0xF0, 0x43, 0x20, 0x7E, 0x4C, 0xF7 }; diff --git a/src/BLEMIDI_Settings.h b/src/BLEMIDI_Settings.h index f4c8b5a..5463737 100644 --- a/src/BLEMIDI_Settings.h +++ b/src/BLEMIDI_Settings.h @@ -6,7 +6,7 @@ BEGIN_BLEMIDI_NAMESPACE struct DefaultSettings { - static const size_t MaxBufferSize = 64; + static const short MaxBufferSize = 64; }; END_BLEMIDI_NAMESPACE diff --git a/src/BLEMIDI_Transport.h b/src/BLEMIDI_Transport.h index 45b028a..bc2b2de 100644 --- a/src/BLEMIDI_Transport.h +++ b/src/BLEMIDI_Transport.h @@ -26,13 +26,11 @@ static const char *const CHARACTERISTIC_UUID = "7772e5db-3868-4112-a1a9-f2669d10 template class BLEMIDI_Transport { - typedef _Settings Settings; - private: - byte mRxBuffer[Settings::MaxBufferSize]; + byte mRxBuffer[_Settings::MaxBufferSize]; unsigned mRxIndex = 0; - byte mTxBuffer[Settings::MaxBufferSize]; // minimum 5 bytes + byte mTxBuffer[_Settings::MaxBufferSize]; // minimum 5 bytes unsigned mTxIndex = 0; char mDeviceName[24]; @@ -186,6 +184,7 @@ class BLEMIDI_Transport // callbacks void (*_connectedCallback)() = nullptr; void (*_disconnectedCallback)() = nullptr; + void (*_connectedCallbackDeviceName)(char *) = nullptr; BLEMIDI_Transport &setName(const char *deviceName) { @@ -199,7 +198,13 @@ class BLEMIDI_Transport _connectedCallback = fptr; return *this; } - + + BLEMIDI_Transport &setHandleConnected(void (*fptr)(char*)) + { + _connectedCallbackDeviceName= fptr; + return *this; + } + BLEMIDI_Transport &setHandleDisconnected(void (*fptr)()) { _disconnectedCallback = fptr; @@ -258,10 +263,10 @@ class BLEMIDI_Transport byte headerByte = buffer[lPtr++]; auto timestampHigh = 0x3f & headerByte; - + timestampHigh = timestampHigh; // <-- This line is for avoid Warning message due it is unused byte timestampByte = buffer[lPtr++]; uint16_t timestamp = 0; - + timestamp = timestamp; // <-- This line is for avoid Warning message due it is unused bool sysExContinuation = false; bool runningStatusContinuation = false; diff --git a/src/hardware/BLEMIDI_ArduinoBLE.h b/src/hardware/BLEMIDI_ArduinoBLE.h index 82f3a04..de45b9b 100644 --- a/src/hardware/BLEMIDI_ArduinoBLE.h +++ b/src/hardware/BLEMIDI_ArduinoBLE.h @@ -2,56 +2,57 @@ #include -BLEService midiService(SERVICE_UUID); -BLEStringCharacteristic midiChar(CHARACTERISTIC_UUID, // standard 16-bit characteristic UUID - BLERead | BLEWrite | BLENotify | BLEWriteWithoutResponse, 16); // remote clients will be able to get notifications if this characteristic changes - #define BLE_POLLING BEGIN_BLEMIDI_NAMESPACE +BLEService midiService(SERVICE_UUID); + +BLEStringCharacteristic midiChar(CHARACTERISTIC_UUID, // standard 16-bit characteristic UUID + BLERead | BLEWrite | BLENotify | BLEWriteWithoutResponse, 16); // remote clients will be able to get notifications if this characteristic changes + template class Fifo { public: - const size_t size; //speculative feature, in case it's needed + const size_t size; // speculative feature, in case it's needed - Fifo(): size(rawSize) + Fifo() : size(rawSize) { - flush(); + flush(); } - T dequeue() + T dequeue() { numberOfElements--; nextOut %= size; - return raw[ nextOut++]; + return raw[nextOut++]; }; - - bool enqueue( T element ) + + bool enqueue(T element) { - if ( count() >= rawSize ) + if (count() >= rawSize) return false; numberOfElements++; nextIn %= size; raw[nextIn] = element; - nextIn++; //advance to next index + nextIn++; // advance to next index return true; }; T peek() const { - return raw[ nextOut % size]; + return raw[nextOut % size]; } void flush() { - nextIn = nextOut = numberOfElements = 0; + nextIn = nextOut = numberOfElements = 0; } - // how many elements are currently in the FIFO? - size_t count() { return numberOfElements; } + // how many elements are currently in the FIFO? + size_t count() { return numberOfElements; } private: size_t numberOfElements; @@ -60,60 +61,57 @@ class Fifo { T raw[rawSize]; }; +template class BLEMIDI_ArduinoBLE { -private: - static BLEMIDI_Transport* _bleMidiTransport; - static BLEDevice* _central; +private: + BLEMIDI_Transport, _Settings> *_bleMidiTransport; + BLEDevice *_central; + + BLEService _midiService; + BLECharacteristic _midiChar; + + Fifo mRxBuffer; - Fifo mRxBuffer; + static BLEMIDI_ArduinoBLE<_Settings>* self; public: - BLEMIDI_ArduinoBLE() + BLEMIDI_ArduinoBLE() : _midiService(SERVICE_UUID), + _midiChar(CHARACTERISTIC_UUID, BLERead | BLEWrite | BLENotify | BLEWriteWithoutResponse, _Settings::MaxBufferSize) { + self = this; } - - bool begin(const char*, BLEMIDI_Transport*); - - void end() + + bool begin(const char *, BLEMIDI_Transport, _Settings> *); + + void end() { - } - void write(uint8_t* buffer, size_t length) + void write(uint8_t *buffer, size_t length) { - // TODO: test length - ((BLECharacteristic)midiChar).writeValue(buffer, length); + if (length > 0) + ((BLECharacteristic)_midiChar).writeValue(buffer, length); } - - bool available(byte* pvBuffer) - { - #ifdef BLE_POLLING - if (mRxBuffer.count() > 0) { + bool available(byte *pvBuffer) + { + if (mRxBuffer.count() > 0) + { *pvBuffer = mRxBuffer.dequeue(); - return true; } - poll(); - - if (midiChar.written()) { - // auto buffer = midiChar.value(); - auto length = midiChar.valueLength(); - - if (length > 0) { - auto buffer = midiChar.value().c_str(); - _bleMidiTransport->receive((byte*)buffer, length); - + if (_midiChar.written()) + { + auto length = _midiChar.valueLength(); + if (length > 0) + { + auto buffer = _midiChar.value(); + _bleMidiTransport->receive((byte *)buffer, length); } } return false; -#endif -#ifdef BLE_EVENTS -/ BLE.poll(); - return ; // ?? -#endif } void add(byte value) @@ -123,109 +121,92 @@ class BLEMIDI_ArduinoBLE } protected: - static void receive(const unsigned char* buffer, size_t length) - { - // forward the buffer so it can be parsed - _bleMidiTransport->receive((uint8_t*)buffer, length); - } - - bool poll() - { - BLEDevice central = BLE.central(); - if (!central) { - if (_central) { - BLEMIDI_ArduinoBLE::blePeripheralDisconnectHandler(*_central); - _central = nullptr; - } - return false; - } - - if (!central.connected()) { - return false; - } - - if (nullptr == _central) { - BLEMIDI_ArduinoBLE::blePeripheralConnectHandler(central); - _central = ¢ral; - } - else { - if (*_central != central) { - BLEMIDI_ArduinoBLE::blePeripheralDisconnectHandler(*_central); - BLEMIDI_ArduinoBLE::blePeripheralConnectHandler(central); - _central = ¢ral; - } - } - - return true; + void receive(const unsigned char *buffer, size_t length) + { + if (length > 0) + _bleMidiTransport->receive((uint8_t *)buffer, length); } - static void blePeripheralConnectHandler(BLEDevice central) - { - _central = ¢ral; - if (_bleMidiTransport->_connectedCallback) - _bleMidiTransport->_connectedCallback(); - } - - static void blePeripheralDisconnectHandler(BLEDevice central) - { - if (_bleMidiTransport->_disconnectedCallback) - _bleMidiTransport->_disconnectedCallback(); + void connected() + { + if (_bleMidiTransport->_connectedCallback) + _bleMidiTransport->_connectedCallback(); + } - _central = nullptr; - } + void disconnected() + { + if (_bleMidiTransport->_disconnectedCallback) + _bleMidiTransport->_disconnectedCallback(); - static void characteristicWritten(BLEDevice central, BLECharacteristic characteristic) { - auto buffer = characteristic.value(); - auto length = characteristic.valueLength(); + end(); + } - if (length > 0) - receive(buffer, length); + static void blePeripheralConnectedHandler(BLEDevice central) { + self->connected(); + } + + static void blePeripheralDisconnectedHandler(BLEDevice central) { + self->disconnected(); + } + + static void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) { +// std::string rxValue = characteristic->value(); +// if (rxValue.length() > 0) +// receive((uint8_t *)(rxValue.c_str()), rxValue.length()); } }; -BLEMIDI_Transport* BLEMIDI_ArduinoBLE::_bleMidiTransport = nullptr; -BLEDevice* BLEMIDI_ArduinoBLE::_central = nullptr; +// this pattern of static to instance variable is limited to 1 instance of ArduinoBLE +// for multiple instances, turn this into a vector and lookup +template BLEMIDI_ArduinoBLE<_Settings>* BLEMIDI_ArduinoBLE<_Settings>::self; -bool BLEMIDI_ArduinoBLE::begin(const char* deviceName, BLEMIDI_Transport* bleMidiTransport) +template +bool BLEMIDI_ArduinoBLE<_Settings>::begin(const char *deviceName, BLEMIDI_Transport, _Settings> *bleMidiTransport) { - _bleMidiTransport = bleMidiTransport; + _bleMidiTransport = bleMidiTransport; - if (!BLE.begin()) + // initialize the Bluetooth® Low Energy hardware + if (!BLE.begin()) return false; BLE.setLocalName(deviceName); - BLE.setAdvertisedService(midiService); - midiService.addCharacteristic(midiChar); - BLE.addService(midiService); + BLE.setAdvertisedService(_midiService); + _midiService.addCharacteristic(_midiChar); + BLE.addService(_midiService); -#ifdef BLE_EVENTS - // assign event handlers for connected, disconnected to peripheral - BLE.setEventHandler(BLEConnected, BLEMIDI_ArduinoBLE::blePeripheralConnectHandler); - BLE.setEventHandler(BLEDisconnected, BLEMIDI_ArduinoBLE::blePeripheralDisconnectHandler); + BLE.setEventHandler(BLEConnected, blePeripheralConnectedHandler); + BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectedHandler); - midiChar.setEventHandler(BLEWritten, characteristicWritten); -#endif + _midiChar.setEventHandler(BLEWritten, switchCharacteristicWritten); - /* Start advertising BLE. It will start continuously transmitting BLE - advertising packets and will be visible to remote BLE central devices - until it receives a new connection */ + // set the initial value for the characeristic: + // (when not set, the device will disconnect after 0.5 seconds) + _midiChar.writeValue((uint8_t)0); // TODO: might not be needed, check!! - // start advertising + /* Start advertising BLE. It will start continuously transmitting BLE + advertising packets and will be visible to remote BLE central devices + until it receives a new connection */ BLE.advertise(); - + return true; } - /*! \brief Create an instance for nRF52 named + +/*! \brief Create an instance for ArduinoBLE + */ +#define BLEMIDI_CREATE_CUSTOM_INSTANCE(DeviceName, Name, _Settings) \ + BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> BLE##Name(DeviceName); \ + MIDI_NAMESPACE::MidiInterface, _Settings>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> &)BLE##Name); + +/*! \brief Create an instance for ArduinoBLE */ #define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ -BLEMIDI_NAMESPACE::BLEMIDI_Transport BLE##Name(DeviceName); \ -MIDI_NAMESPACE::MidiInterface, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport &)BLE##Name); + BLEMIDI_CREATE_CUSTOM_INSTANCE(DeviceName, Name, BLEMIDI_NAMESPACE::DefaultSettings); - /*! \brief Create a default instance for nRF52 (Nano 33 BLE) named BLE-MIDI +/*! \brief Create a default instance for ArduinoBLE named BLE-MIDI */ #define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ -BLEMIDI_CREATE_INSTANCE("BLE-MIDI", MIDI) + BLEMIDI_CREATE_INSTANCE("BLE-MIDI", MIDI) END_BLEMIDI_NAMESPACE diff --git a/src/hardware/BLEMIDI_Client_ESP32.h b/src/hardware/BLEMIDI_Client_ESP32.h index d1642fc..b16dd48 100644 --- a/src/hardware/BLEMIDI_Client_ESP32.h +++ b/src/hardware/BLEMIDI_Client_ESP32.h @@ -1,177 +1,141 @@ #pragma once -/* -############################################# -########## USER DEFINES BEGINNING ########### -####### Modify only these parameters ######## -############################################# -*/ +//#define MIDIBLECLIENTVERBOSE -/* -##### BLE DEVICE NAME ##### -*/ +#ifdef MIDIBLECLIENTVERBOSE +#define DEBUGCLIENT(_text_) Serial.println("DbgBC: " + (String)_text_); +#else +#define DEBUGCLIENT(_text_) ; +#endif -/** - * Set always the same name independently of name server - */ -//#define BLEMIDI_CLIENT_FIXED_NAME "BleMidiClient" - -#ifndef BLEMIDI_CLIENT_FIXED_NAME //Not modify -/** - * When client tries to connect to specific server, BLE name is composed as follows: - * BLEMIDI_CLIENT_NAME_PREFIX + + BLEMIDI_CLIENT_NAME_SUBFIX - * - * example: - * BLEMIDI_CLIENT_NAME_PREFIX "Client-" - * "AX-Edge" - * BLEMIDI_CLIENT_NAME_SUBFIX "-Midi1" - * - * Result: "Client-AX-Edge-Midi1" - */ -#define BLEMIDI_CLIENT_NAME_PREFIX "C-" -#define BLEMIDI_CLIENT_NAME_SUBFIX "" +// Headers for ESP32 nimBLE +#include -/** - * When client tries to connect to the first midi server found: - */ -#define BLEMIDI_CLIENT_DEFAULT_NAME "BLEMIDI-CLIENT" -#endif //Not modify +BEGIN_BLEMIDI_NAMESPACE -/* -###### TX POWER ##### -*/ -/** - * Set power transmision - * - * ESP_PWR_LVL_N12 // Corresponding to -12dbm Minimum - * ESP_PWR_LVL_N9 // Corresponding to -9dbm - * ESP_PWR_LVL_N6 // Corresponding to -6dbm - * ESP_PWR_LVL_N3 // Corresponding to -3dbm - * ESP_PWR_LVL_N0 // Corresponding to 0dbm - * ESP_PWR_LVL_P3 // Corresponding to +3dbm - * ESP_PWR_LVL_P6 // Corresponding to +6dbm - * ESP_PWR_LVL_P9 // Corresponding to +9dbm Maximum -*/ +using PasskeyRequestCallback = uint32_t (*)(void); -#define BLEMIDI_TX_PWR ESP_PWR_LVL_P9 +static uint32_t defautlPasskeyRequest() +{ + // FILL WITH YOUR CUSTOM AUTH METHOD CODE or PASSKEY + // FOR EXAMPLE: + uint32_t passkey = 123456; -/* -###### SECURITY ##### -*/ + // Serial.println("Client Passkey Request"); + + /** return the passkey to send to the server */ + return passkey; +}; + +struct DefaultSettingsClient : public BLEMIDI_NAMESPACE::DefaultSettings +{ + + /* + ##### BLE DEVICE NAME ##### + */ + + /** + * Set name of ble device (not affect to connection with server) + * max 16 characters + */ + static constexpr char *name = "BleMidiClient"; -/** Set the IO capabilities of the device, each option will trigger a different pairing method. + /* + ###### TX POWER ##### + */ + + /** + * Set power transmision + * + * ESP_PWR_LVL_N12 // Corresponding to -12dbm Minimum + * ESP_PWR_LVL_N9 // Corresponding to -9dbm + * ESP_PWR_LVL_N6 // Corresponding to -6dbm + * ESP_PWR_LVL_N3 // Corresponding to -3dbm + * ESP_PWR_LVL_N0 // Corresponding to 0dbm + * ESP_PWR_LVL_P3 // Corresponding to +3dbm + * ESP_PWR_LVL_P6 // Corresponding to +6dbm + * ESP_PWR_LVL_P9 // Corresponding to +9dbm Maximum + */ + static const esp_power_level_t clientTXPwr = ESP_PWR_LVL_P9; + + /* + ###### SECURITY ##### + */ + + /** Set the IO capabilities of the device, each option will trigger a different pairing method. * BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing */ -#define BLEMIDI_CLIENT_SECURITY_CAP BLE_HS_IO_NO_INPUT_OUTPUT + static const uint8_t clientSecurityCapabilities = BLE_HS_IO_NO_INPUT_OUTPUT; -/** Set the security method. + /** Set the security method. * bonding * man in the middle protection * pair. secure connections - * + * * More info in nimBLE lib - * - * Uncomment what you need - * These are the default values. - * You can select some simultaneously. */ -#define BLEMIDI_CLIENT_BOND -//#define BLEMIDI_CLIENT_MITM -#define BLEMIDI_CLIENT_PAIR - -/** - * This callback function defines what will be done when server requieres PassKey. - * Add your custom code here. - */ -static uint32_t userOnPassKeyRequest() -{ - //FILL WITH YOUR CUSTOM AUTH METHOD CODE or PASSKEY - //FOR EXAMPLE: - uint32_t passkey = 123456; - - //Serial.println("Client Passkey Request"); + static const bool clientBond = true; + static const bool clientMITM = false; + static const bool clientPair = true; - /** return the passkey to send to the server */ - return passkey; -}; + /** + * This callback function defines what will be done when server requieres PassKey. + * Add your custom code here. + */ + static constexpr PasskeyRequestCallback userOnPassKeyRequest = defautlPasskeyRequest; /* -###### BLE COMMUNICATION PARAMS ###### -*/ - /** Set connection parameters: - * If you only use one connection, put recomended BLE server param communication - * (you may scan it ussing "nRF Connect" app or other similar apps). - * - * If you use more than one connection adjust, for example, settings like 15ms interval, 0 latency, 120ms timout. - * These settings may be safe for 3 clients to connect reliably, set faster values if you have less - * connections. - * - * Min interval (unit: 1.25ms): 12 * 1.25ms = 15 ms, - * Max interval (unit: 1.25ms): 12 * 1.25ms = 15, - * 0 latency (Number of intervals allowed to skip), - * TimeOut (unit: 10ms) 51 * 10ms = 510ms. Timeout should be minimum 100ms. - */ -#define BLEMIDI_CLIENT_COMM_MIN_INTERVAL 6 // 7.5ms -#define BLEMIDI_CLIENT_COMM_MAX_INTERVAL 35 // 40ms -#define BLEMIDI_CLIENT_COMM_LATENCY 0 // -#define BLEMIDI_CLIENT_COMM_TIMEOUT 200 //2000ms - -/* -###### BLE FORCE NEW CONNECTION ###### -*/ - -/** - * This parameter force to skip the "soft-reconnection" and force to create a new connection after a disconnect event. - * "Soft-reconnection" save some time and energy in comparation with performming a new connection, but some BLE devices - * don't support or don't perform correctly a "soft-reconnection" after a disconnection event. - * Uncomment this define if your device doesn't work propertily after a reconnection. - * -*/ -//#define BLEMIDI_FORCE_NEW_CONNECTION - -/** - * -*/ - -/* -############################################# -############ USER DEFINES END ############### -############################################# -*/ - -// Headers for ESP32 nimBLE -#include + ###### BLE COMMUNICATION PARAMS ###### + */ -BEGIN_BLEMIDI_NAMESPACE + /** Set connection parameters: + * If you only use one connection, put recomended BLE server param communication + * (you may scan it ussing "nRF Connect" app or other similar apps). + * + * If you use more than one connection adjust, for example, settings like 15ms interval, 0 latency, 120ms timout. + * These settings may be safe for 3 clients to connect reliably, set faster values if you have less + * connections. + * + * Min interval (unit: 1.25ms): 12 * 1.25ms = 15 ms, + * Max interval (unit: 1.25ms): 12 * 1.25ms = 15, + * 0 latency (Number of intervals allowed to skip), + * TimeOut (unit: 10ms) 51 * 10ms = 510ms. Timeout should be minimum 100ms. + */ + static const uint16_t commMinInterval = 6; // 7.5ms + static const uint16_t commMaxInterval = 35; // 40ms + static const uint16_t commLatency = 0; // + static const uint16_t commTimeOut = 200; // 2000ms -#ifdef BLEMIDI_CLIENT_BOND -#define BLEMIDI_CLIENT_BOND_DUMMY BLE_SM_PAIR_AUTHREQ_BOND -#else -#define BLEMIDI_CLIENT_BOND_DUMMY 0x00 -#endif + /* + ###### BLE FORCE NEW CONNECTION ###### + */ -#ifdef BLEMIDI_CLIENT_MITM -#define BLEMIDI_CLIENT_MITM_DUMMY BLE_SM_PAIR_AUTHREQ_MITM -#else -#define BLEMIDI_CLIENT_MITM_DUMMY 0x00 -#endif + /** + * This parameter force to skip the "soft-reconnection" and force to create a new connection after a disconnect event. + * "Soft-reconnection" save some time and energy in comparation with performming a new connection, but some BLE devices + * don't support or don't perform correctly a "soft-reconnection" after a disconnection event. + * Set to "true" if your device doesn't work propertily after a reconnection. + * + */ + static const bool forceNewConnection = false; -#ifdef BLEMIDI_CLIENT_PAIR -#define BLEMIDI_CLIENT_PAIR_DUMMY BLE_SM_PAIR_AUTHREQ_SC -#else -#define BLEMIDI_CLIENT_PAIR_DUMMY 0x00 -#endif + /* + ###### BLE SUBSCRIPTION: NOTIFICATION & RESPONSE ###### + */ -/** Set the security method. - * bonding - * man in the middle protection - * pair. secure connections - * - * More info in nimBLE lib + /** + * Subscribe in Notification Mode [true] or Indication Mode [false] + * Don't modify this parameter except is completely necessary. */ -#define BLEMIDI_CLIENT_SECURITY_AUTH (BLEMIDI_CLIENT_BOND_DUMMY | BLEMIDI_CLIENT_MITM_DUMMY | BLEMIDI_CLIENT_PAIR_DUMMY) + static const bool notification = true; + /** + * Respond to after a notification message. + * Don't modify this parameter except is completely necessary. + */ + static const bool response = true; +}; /** Define a class to handle the callbacks when advertisments are received */ class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks @@ -187,32 +151,34 @@ class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks protected: void onResult(NimBLEAdvertisedDevice *advertisedDevice) { - if (enableConnection) //not begin() or end() + if (!enableConnection) // not begin() or end() { - Serial.print("Advertised Device found: "); - Serial.println(advertisedDevice->toString().c_str()); - if (advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID))) - { - Serial.println("Found MIDI Service"); - if (!specificTarget || (advertisedDevice->getName() == nameTarget.c_str() || advertisedDevice->getAddress() == nameTarget)) - { - /** Ready to connect now */ - doConnect = true; - /** Save the device reference in a public variable that the client can use*/ - advDevice = *advertisedDevice; - /** stop scan before connecting */ - NimBLEDevice::getScan()->stop(); - } - else - { - Serial.println("Name error"); - } - } - else - { - doConnect = false; - } + return; + } + + DEBUGCLIENT("Advertised Device found: "); + DEBUGCLIENT(advertisedDevice->toString().c_str()); + if (!advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID))) + { + doConnect = false; + return; } + + DEBUGCLIENT("Found MIDI Service"); + if (!(!specificTarget || (advertisedDevice->getName() == nameTarget.c_str() || advertisedDevice->getAddress() == nameTarget))) + { + DEBUGCLIENT("Name error"); + return; + } + + /** Ready to connect now */ + doConnect = true; + /** Save the device reference in a public variable that the client can use*/ + advDevice = *advertisedDevice; + /** stop scan before connecting */ + NimBLEDevice::getScan()->stop(); + + return; }; }; @@ -220,6 +186,7 @@ class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks void scanEndedCB(NimBLEScanResults results); /** Define the class that performs Client Midi (nimBLE) */ +template class BLEMIDI_Client_ESP32 { private: @@ -228,15 +195,12 @@ class BLEMIDI_Client_ESP32 BLERemoteCharacteristic *_characteristic = nullptr; BLERemoteService *pSvc = nullptr; bool firstTimeSend = true; //First writeValue get sends like Write with reponse for clean security flags. After first time, all messages are send like WriteNoResponse for increase transmision speed. - + char connectedDeviceName[24]; + BLEMIDI_Transport *_bleMidiTransport = nullptr; bool specificTarget = false; - friend class AdvertisedDeviceCallbacks; - friend class MyClientCallbacks; - friend class MIDI_NAMESPACE::MidiInterface, MySettings>; // - AdvertisedDeviceCallbacks myAdvCB; protected: @@ -247,7 +211,7 @@ class BLEMIDI_Client_ESP32 { } - bool begin(const char *, BLEMIDI_Transport *); + bool begin(const char *, BLEMIDI_Transport, _Settings> *); bool end() { @@ -256,7 +220,7 @@ class BLEMIDI_Client_ESP32 _client->disconnect(); _client = nullptr; - return !_client->isConnected(); + return true; } void write(uint8_t *data, uint8_t length) @@ -294,100 +258,98 @@ class BLEMIDI_Client_ESP32 _bleMidiTransport->receive(buffer, length); } - void connectCallbacks(MIDI_NAMESPACE::MidiInterface, MySettings> *MIDIcallback); + void notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify); + void scan(); + bool connect(); + +public: void connected() { if (_bleMidiTransport->_connectedCallback) - { _bleMidiTransport->_connectedCallback(); - } firstTimeSend = true; + + if (_bleMidiTransport->_connectedCallbackDeviceName) + { + sprintf(connectedDeviceName, "%s", myAdvCB.advDevice.getName().c_str()); + _bleMidiTransport->_connectedCallbackDeviceName(connectedDeviceName); + } } void disconnected() { if (_bleMidiTransport->_disconnectedCallback) - { _bleMidiTransport->_disconnectedCallback(); - } firstTimeSend = true; } - - void notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify); - - void scan(); - bool connect(); }; /** Define the class that performs interruption callbacks */ +template class MyClientCallbacks : public BLEClientCallbacks { public: - MyClientCallbacks(BLEMIDI_Client_ESP32 *bluetoothEsp32) + MyClientCallbacks(BLEMIDI_Client_ESP32<_Settings> *bluetoothEsp32) : _bluetoothEsp32(bluetoothEsp32) { } protected: - BLEMIDI_Client_ESP32 *_bluetoothEsp32 = nullptr; + BLEMIDI_Client_ESP32<_Settings> *_bluetoothEsp32 = nullptr; uint32_t onPassKeyRequest() { - return userOnPassKeyRequest(); + // if (nullptr != _Settings::userOnPassKeyRequest) + return _Settings::userOnPassKeyRequest(); + // return 0; }; void onConnect(BLEClient *pClient) { - //Serial.println("##Connected##"); - //pClient->updateConnParams(BLEMIDI_CLIENT_COMM_MIN_INTERVAL, BLEMIDI_CLIENT_COMM_MAX_INTERVAL, BLEMIDI_CLIENT_COMM_LATENCY, BLEMIDI_CLIENT_COMM_TIMEOUT); + DEBUGCLIENT("##Connected##"); + // pClient->updateConnParams(_Settings::commMinInterval, _Settings::commMaxInterval, _Settings::commLatency, _Settings::commTimeOut); vTaskDelay(1); if (_bluetoothEsp32) - { _bluetoothEsp32->connected(); - } }; void onDisconnect(BLEClient *pClient) { - //Serial.print(pClient->getPeerAddress().toString().c_str()); - //Serial.println(" Disconnected - Starting scan"); + DEBUGCLIENT(pClient->getPeerAddress().toString().c_str()); + DEBUGCLIENT(" Disconnected - Starting scan"); if (_bluetoothEsp32) { _bluetoothEsp32->disconnected(); -#ifdef BLEMIDI_FORCE_NEW_CONNECTION - // Try reconnection or search a new one - _bluetoothEsp32->scan(); -#endif // BLEMIDI_FORCE_NEW_CONNECTION } -#ifdef BLEMIDI_FORCE_NEW_CONNECTION - // Renew Client - NimBLEDevice::deleteClient(pClient); - NimBLEDevice::createClient(); - pClient = nullptr; -#endif // BLEMIDI_FORCE_NEW_CONNECTION + if (_Settings::forceNewConnection) + { + // Renew Client + NimBLEDevice::deleteClient(pClient); + pClient = nullptr; + } - //Try reconnection or search a new one + // Try reconnection or search a new one NimBLEDevice::getScan()->start(1, scanEndedCB); } bool onConnParamsUpdateRequest(NimBLEClient *pClient, const ble_gap_upd_params *params) { - if (params->itvl_min < BLEMIDI_CLIENT_COMM_MIN_INTERVAL) + if (params->itvl_min < _Settings::commMinInterval) { /** 1.25ms units */ return false; } - else if (params->itvl_max > BLEMIDI_CLIENT_COMM_MAX_INTERVAL) + else if (params->itvl_max > _Settings::commMaxInterval) { /** 1.25ms units */ return false; } - else if (params->latency > BLEMIDI_CLIENT_COMM_LATENCY) + else if (params->latency > _Settings::commLatency) { /** Number of intervals allowed to skip */ return false; } - else if (params->supervision_timeout > BLEMIDI_CLIENT_COMM_TIMEOUT) + else if (params->supervision_timeout > _Settings::commMinInterval) { /** 10ms units */ return false; } @@ -403,46 +365,40 @@ class MyClientCallbacks : public BLEClientCallbacks ########################################## */ -bool BLEMIDI_Client_ESP32::begin(const char *deviceName, BLEMIDI_Transport *bleMidiTransport) +template +bool BLEMIDI_Client_ESP32<_Settings>::begin(const char *deviceName, BLEMIDI_Transport, _Settings> *bleMidiTransport) { _bleMidiTransport = bleMidiTransport; std::string strDeviceName(deviceName); - if (strDeviceName == "") // Connect to the first midi server found + // Connect to the first midi server found + if (strDeviceName == "") { myAdvCB.specificTarget = false; myAdvCB.nameTarget = ""; - -#ifdef BLEMIDI_CLIENT_FIXED_NAME - strDeviceName = BLEMIDI_CLIENT_FIXED_NAME; -#else - strDeviceName = BLEMIDI_CLIENT_DEFAULT_NAME; -#endif } - else // Connect to a specific name or address + // Connect to a specific name or address + else { myAdvCB.specificTarget = true; myAdvCB.nameTarget = strDeviceName; - -#ifdef BLEMIDI_CLIENT_FIXED_NAME - strDeviceName = BLEMIDI_CLIENT_FIXED_NAME; -#else - strDeviceName = BLEMIDI_CLIENT_NAME_PREFIX + strDeviceName + BLEMIDI_CLIENT_NAME_SUBFIX; -#endif } - Serial.println(strDeviceName.c_str()); + static char array[16]; + memcpy(array, _Settings::name, 16); + strDeviceName = array; + DEBUGCLIENT(strDeviceName.c_str()); NimBLEDevice::init(strDeviceName); // To communicate between the 2 cores. // Core_0 runs here, core_1 runs the BLE stack - mRxQueue = xQueueCreate(256, sizeof(uint8_t)); // TODO Settings::MaxBufferSize + mRxQueue = xQueueCreate(_Settings::MaxBufferSize, sizeof(uint8_t)); - NimBLEDevice::setSecurityIOCap(BLEMIDI_CLIENT_SECURITY_CAP); // Attention, it may need a passkey - NimBLEDevice::setSecurityAuth(BLEMIDI_CLIENT_SECURITY_AUTH); + NimBLEDevice::setSecurityIOCap(_Settings::clientSecurityCapabilities); // Attention, it may need a passkey + NimBLEDevice::setSecurityAuth(_Settings::clientBond, _Settings::clientMITM, _Settings::clientPair); /** Optional: set the transmit power, default is 3db */ - NimBLEDevice::setPower(BLEMIDI_TX_PWR); /** +9db */ + NimBLEDevice::setPower(_Settings::clientTXPwr); /** +9db */ myAdvCB.enableConnection = true; scan(); @@ -450,44 +406,47 @@ bool BLEMIDI_Client_ESP32::begin(const char *deviceName, BLEMIDI_Transport +bool BLEMIDI_Client_ESP32<_Settings>::available(byte *pvBuffer) { - if (myAdvCB.enableConnection) + if (!myAdvCB.enableConnection) + { + return false; + } + + // Try to connect/reconnect + if (_client == nullptr || !_client->isConnected()) { - if (_client == nullptr || !_client->isConnected()) //Try to connect/reconnect + if (myAdvCB.doConnect) { - if (myAdvCB.doConnect) - { - myAdvCB.doConnect = false; - if (!connect()) - { - scan(); - } - } - else if (myAdvCB.scanDone) + myAdvCB.doConnect = false; + if (!connect()) { scan(); } } - // return 1 byte from the Queue - return xQueueReceive(mRxQueue, (void *)pvBuffer, 0); // return immediately when the queue is empty - } - else - { - return false; + else if (myAdvCB.scanDone) + { + scan(); + } } + + // return 1 byte from the Queue + return xQueueReceive(mRxQueue, (void *)pvBuffer, 0); // return immediately when the queue is empty } /** Notification receiving handler callback */ -void BLEMIDI_Client_ESP32::notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) +template +void BLEMIDI_Client_ESP32<_Settings>::notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) { - if (this->_characteristic == pRemoteCharacteristic) //Redundant protection + if (this->_characteristic == pRemoteCharacteristic) // Redundant protection { receive(pData, length); } } -void BLEMIDI_Client_ESP32::scan() +template +void BLEMIDI_Client_ESP32<_Settings>::scan() { // Retrieve a Scanner and set the callback you want to use to be informed when a new device is detected. // Specify that you want active scanning and start the @@ -501,61 +460,64 @@ void BLEMIDI_Client_ESP32::scan() pBLEScan->setWindow(500); pBLEScan->setActiveScan(true); - Serial.println("Scanning..."); + DEBUGCLIENT("Scanning..."); pBLEScan->start(1, scanEndedCB); } }; -bool BLEMIDI_Client_ESP32::connect() +template +bool BLEMIDI_Client_ESP32<_Settings>::connect() { using namespace std::placeholders; //<- for bind funtion in callback notification -#ifndef BLEMIDI_FORCE_NEW_CONNECTION - /** Check if we have a client we should reuse first - * Special case when we already know this device - * This saves considerable time and power. - */ - - if (_client) + // Retry to connecto to last one + if (!_Settings::forceNewConnection) { - if (_client == NimBLEDevice::getClientByPeerAddress(myAdvCB.advDevice.getAddress())) + /** Check if we have a client we should reuse first + * Special case when we already know this device + * This saves considerable time and power. + */ + + if (_client) { - if (_client->connect(&myAdvCB.advDevice, false)) + if (_client == NimBLEDevice::getClientByPeerAddress(myAdvCB.advDevice.getAddress())) { - if (_characteristic->canNotify()) + if (_client->connect(&myAdvCB.advDevice, false)) { - if (_characteristic->subscribe(true, std::bind(&BLEMIDI_Client_ESP32::notifyCB, this, _1, _2, _3, _4))) + if (_characteristic->canNotify()) { - //Re-connection SUCCESS - return true; + if (_characteristic->subscribe(_Settings::notification, std::bind(&BLEMIDI_Client_ESP32::notifyCB, this, _1, _2, _3, _4), _Settings::response)) + { + // Re-connection SUCCESS + return true; + } } + /** Disconnect if subscribe failed */ + _client->disconnect(); } - /** Disconnect if subscribe failed */ - _client->disconnect(); + /* If any connection problem exits, delete previous client and try again in the next attemp as new client*/ + NimBLEDevice::deleteClient(_client); + _client = nullptr; + return false; } - /* If any connection problem exits, delete previous client and try again in the next attemp as new client*/ + /*If client does not match, delete previous client and create a new one*/ NimBLEDevice::deleteClient(_client); _client = nullptr; - return false; } - /*If client does not match, delete previous client and create a new one*/ - NimBLEDevice::deleteClient(_client); - _client = nullptr; } -#endif //BLEMIDI_FORCE_NEW_CONNECTION if (NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) { - Serial.println("Max clients reached - no more connections available"); + DEBUGCLIENT("Max clients reached - no more connections available"); return false; } // Create and setup a new client _client = BLEDevice::createClient(); - _client->setClientCallbacks(new MyClientCallbacks(this), false); + _client->setClientCallbacks(new MyClientCallbacks<_Settings>(this), false); - _client->setConnectionParams(BLEMIDI_CLIENT_COMM_MIN_INTERVAL, BLEMIDI_CLIENT_COMM_MAX_INTERVAL, BLEMIDI_CLIENT_COMM_LATENCY, BLEMIDI_CLIENT_COMM_TIMEOUT); + _client->setConnectionParams(_Settings::commMinInterval, _Settings::commMaxInterval, _Settings::commLatency, _Settings::commTimeOut); /** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */ _client->setConnectTimeout(15); @@ -565,28 +527,23 @@ bool BLEMIDI_Client_ESP32::connect() /** Created a client but failed to connect, don't need to keep it as it has no data */ NimBLEDevice::deleteClient(_client); _client = nullptr; - //Serial.println("Failed to connect, deleted client"); + DEBUGCLIENT("Failed to connect, deleted client"); return false; } if (!_client->isConnected()) { - //Serial.println("Failed to connect"); + DEBUGCLIENT("Failed to connect"); _client->disconnect(); NimBLEDevice::deleteClient(_client); _client = nullptr; return false; } - Serial.print("Connected to: "); - Serial.print(myAdvCB.advDevice.getName().c_str()); - Serial.print(" / "); - Serial.println(_client->getPeerAddress().toString().c_str()); + DEBUGCLIENT("Connected to: " + myAdvCB.advDevice.getName().c_str() + " / " + _client->getPeerAddress().toString().c_str()); - /* - Serial.print("RSSI: "); - Serial.println(_client->getRssi()); - */ + DEBUGCLIENT("RSSI: "); + DEBUGCLIENT(_client->getRssi()); /** Now we can read/write/subscribe the charateristics of the services we are interested in */ pSvc = _client->getService(SERVICE_UUID); @@ -598,16 +555,16 @@ bool BLEMIDI_Client_ESP32::connect() { if (_characteristic->canNotify()) { - if (_characteristic->subscribe(true, std::bind(&BLEMIDI_Client_ESP32::notifyCB, this, _1, _2, _3, _4))) + if (_characteristic->subscribe(_Settings::notification, std::bind(&BLEMIDI_Client_ESP32::notifyCB, this, _1, _2, _3, _4), _Settings::response)) { - //Connection SUCCESS + // Connection SUCCESS return true; } } } } - //If anything fails, disconnect and delete client + // If anything fails, disconnect and delete client _client->disconnect(); NimBLEDevice::deleteClient(_client); _client = nullptr; @@ -617,19 +574,25 @@ bool BLEMIDI_Client_ESP32::connect() /** Callback to process the results of the last scan or restart it */ void scanEndedCB(NimBLEScanResults results) { - // Serial.println("Scan Ended"); + // DEBUGCLIENT("Scan Ended"); } END_BLEMIDI_NAMESPACE -/*! \brief Create an instance for ESP32 named , and adviertise it like "Prefix + + Subfix" +/*! \brief Create a custom instance for ESP32 named , and advertise it like "Prefix + + Subfix" + It will try to connect to a specific server with equal name or addr than . If is "", it will connect to first midi server + */ +#define BLEMIDI_CREATE_CUSTOM_INSTANCE(DeviceName, Name, _Settings) \ + BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> BLE##Name(DeviceName); \ + MIDI_NAMESPACE::MidiInterface, _Settings>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> &)BLE##Name); + +/*! \brief Create an instance for ESP32 named , and advertise it like "Prefix + + Subfix" It will try to connect to a specific server with equal name or addr than . If is "", it will connect to first midi server */ -#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ - BLEMIDI_NAMESPACE::BLEMIDI_Transport BLE##Name(DeviceName); \ - MIDI_NAMESPACE::MidiInterface, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport &)BLE##Name); +#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ + BLEMIDI_CREATE_CUSTOM_INSTANCE(DeviceName, Name, BLEMIDI_NAMESPACE::DefaultSettingsClient) -/*! \brief Create a default instance for ESP32 named BLEMIDI-CLIENT. +/*! \brief Create a default instance for ESP32 named BLEMIDI-CLIENT. It will try to connect to first midi ble server found. */ #define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ diff --git a/src/hardware/BLEMIDI_ESP32.h b/src/hardware/BLEMIDI_ESP32.h index f67a419..7c49560 100644 --- a/src/hardware/BLEMIDI_ESP32.h +++ b/src/hardware/BLEMIDI_ESP32.h @@ -6,8 +6,12 @@ #include #include +// Note: error: redefinition of 'class BLEDescriptor' is a namespace collision on class BLEDescriptor between our ESp32 BLE and ArduinoBLE +// Solution: remove ArduinoBLE + BEGIN_BLEMIDI_NAMESPACE +template class BLEMIDI_ESP32 { private: @@ -15,10 +19,10 @@ class BLEMIDI_ESP32 BLEAdvertising *_advertising = nullptr; BLECharacteristic *_characteristic = nullptr; - BLEMIDI_Transport *_bleMidiTransport = nullptr; + BLEMIDI_Transport, _Settings>* _bleMidiTransport = nullptr; - friend class MyServerCallbacks; - friend class MyCharacteristicCallbacks; + template friend class MyServerCallbacks; + template friend class MyCharacteristicCallbacks; protected: QueueHandle_t mRxQueue; @@ -28,7 +32,7 @@ class BLEMIDI_ESP32 { } - bool begin(const char *, BLEMIDI_Transport *); + bool begin(const char *, BLEMIDI_Transport, _Settings> *); void end() { @@ -74,16 +78,17 @@ class BLEMIDI_ESP32 } }; +template class MyServerCallbacks : public BLEServerCallbacks { public: - MyServerCallbacks(BLEMIDI_ESP32 *bluetoothEsp32) + MyServerCallbacks(BLEMIDI_ESP32<_Settings> *bluetoothEsp32) : _bluetoothEsp32(bluetoothEsp32) { } protected: - BLEMIDI_ESP32 *_bluetoothEsp32 = nullptr; + BLEMIDI_ESP32<_Settings> *_bluetoothEsp32 = nullptr; void onConnect(BLEServer *) { @@ -100,20 +105,21 @@ class MyServerCallbacks : public BLEServerCallbacks } }; +template class MyCharacteristicCallbacks : public BLECharacteristicCallbacks { public: - MyCharacteristicCallbacks(BLEMIDI_ESP32 *bluetoothEsp32) + MyCharacteristicCallbacks(BLEMIDI_ESP32<_Settings> *bluetoothEsp32) : _bluetoothEsp32(bluetoothEsp32) { } protected: - BLEMIDI_ESP32 *_bluetoothEsp32 = nullptr; + BLEMIDI_ESP32<_Settings> *_bluetoothEsp32 = nullptr; void onWrite(BLECharacteristic *characteristic) { - std::string rxValue = characteristic->getValue(); + auto rxValue = characteristic->getValue(); if (rxValue.length() > 0) { _bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length()); @@ -121,7 +127,8 @@ class MyCharacteristicCallbacks : public BLECharacteristicCallbacks } }; -bool BLEMIDI_ESP32::begin(const char *deviceName, BLEMIDI_Transport *bleMidiTransport) +template +bool BLEMIDI_ESP32<_Settings>::begin(const char *deviceName, BLEMIDI_Transport, _Settings> *bleMidiTransport) { _bleMidiTransport = bleMidiTransport; @@ -129,10 +136,10 @@ bool BLEMIDI_ESP32::begin(const char *deviceName, BLEMIDI_TransportsetCallbacks(new MyServerCallbacks(this)); + _server->setCallbacks(new MyServerCallbacks<_Settings>(this)); // Create the BLE Service auto service = _server->createService(BLEUUID(SERVICE_UUID)); @@ -147,7 +154,7 @@ bool BLEMIDI_ESP32::begin(const char *deviceName, BLEMIDI_TransportaddDescriptor(new BLE2902()); - _characteristic->setCallbacks(new MyCharacteristicCallbacks(this)); + _characteristic->setCallbacks(new MyCharacteristicCallbacks<_Settings>(this)); auto _security = new BLESecurity(); _security->setAuthenticationMode(ESP_LE_AUTH_BOND); @@ -161,16 +168,19 @@ bool BLEMIDI_ESP32::begin(const char *deviceName, BLEMIDI_TransportsetAppearance(0x00); _advertising->start(); - Serial.println("begin"); - return true; } +/*! \brief Create a customer instance for ESP32 named + */ +#define BLEMIDI_CREATE_CUSTOM_INSTANCE(DeviceName, Name, _Settings) \ + BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> BLE##Name(DeviceName); \ + MIDI_NAMESPACE::MidiInterface, _Settings>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> &)BLE##Name); + /*! \brief Create an instance for ESP32 named */ -#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ - BLEMIDI_NAMESPACE::BLEMIDI_Transport BLE##Name(DeviceName); \ - MIDI_NAMESPACE::MidiInterface, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport &)BLE##Name); +#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ + BLEMIDI_CREATE_CUSTOM_INSTANCE (DeviceName, Name, BLEMIDI_NAMESPACE::DefaultSettings) /*! \brief Create a default instance for ESP32 named BLE-MIDI */ diff --git a/src/hardware/BLEMIDI_ESP32_NimBLE.h b/src/hardware/BLEMIDI_ESP32_NimBLE.h index 75f894d..6914a00 100644 --- a/src/hardware/BLEMIDI_ESP32_NimBLE.h +++ b/src/hardware/BLEMIDI_ESP32_NimBLE.h @@ -5,6 +5,7 @@ BEGIN_BLEMIDI_NAMESPACE +template class BLEMIDI_ESP32_NimBLE { private: @@ -12,10 +13,10 @@ class BLEMIDI_ESP32_NimBLE BLEAdvertising *_advertising = nullptr; BLECharacteristic *_characteristic = nullptr; - BLEMIDI_Transport *_bleMidiTransport = nullptr; + BLEMIDI_Transport, _Settings> *_bleMidiTransport = nullptr; - friend class MyServerCallbacks; - friend class MyCharacteristicCallbacks; + template friend class MyServerCallbacks; + template friend class MyCharacteristicCallbacks; protected: QueueHandle_t mRxQueue; @@ -25,7 +26,7 @@ class BLEMIDI_ESP32_NimBLE { } - bool begin(const char *, BLEMIDI_Transport *); + bool begin(const char *, BLEMIDI_Transport, _Settings> *); void end() { @@ -69,16 +70,17 @@ class BLEMIDI_ESP32_NimBLE } }; +template class MyServerCallbacks : public BLEServerCallbacks { public: - MyServerCallbacks(BLEMIDI_ESP32_NimBLE *bluetoothEsp32) + MyServerCallbacks(BLEMIDI_ESP32_NimBLE<_Settings> *bluetoothEsp32) : _bluetoothEsp32(bluetoothEsp32) { } protected: - BLEMIDI_ESP32_NimBLE *_bluetoothEsp32 = nullptr; + BLEMIDI_ESP32_NimBLE<_Settings> *_bluetoothEsp32 = nullptr; void onConnect(BLEServer *) { @@ -93,16 +95,17 @@ class MyServerCallbacks : public BLEServerCallbacks } }; +template class MyCharacteristicCallbacks : public BLECharacteristicCallbacks { public: - MyCharacteristicCallbacks(BLEMIDI_ESP32_NimBLE *bluetoothEsp32) + MyCharacteristicCallbacks(BLEMIDI_ESP32_NimBLE<_Settings> *bluetoothEsp32) : _bluetoothEsp32(bluetoothEsp32) { } protected: - BLEMIDI_ESP32_NimBLE *_bluetoothEsp32 = nullptr; + BLEMIDI_ESP32_NimBLE<_Settings> *_bluetoothEsp32 = nullptr; void onWrite(BLECharacteristic *characteristic) { @@ -114,18 +117,40 @@ class MyCharacteristicCallbacks : public BLECharacteristicCallbacks } }; -bool BLEMIDI_ESP32_NimBLE::begin(const char *deviceName, BLEMIDI_Transport *bleMidiTransport) +template +bool BLEMIDI_ESP32_NimBLE<_Settings>::begin(const char *deviceName, BLEMIDI_Transport, _Settings> *bleMidiTransport) { _bleMidiTransport = bleMidiTransport; BLEDevice::init(deviceName); + /** + * Set the IO capabilities of the device, each option will trigger a different pairing method. + * BLE_HS_IO_DISPLAY_ONLY - Passkey pairing + * BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing + * BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing + */ + // NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY); // use passkey + // NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison + + /** + * 2 different ways to set security - both calls achieve the same result. + * no bonding, no man in the middle protection, BLE secure connections. + * + * These are the default values, only shown here for demonstration. + */ + // NimBLEDevice::setSecurityAuth(false, false, true); + +// NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC); + + NimBLEDevice::setSecurityAuth(true, false, false); + // To communicate between the 2 cores. // Core_0 runs here, core_1 runs the BLE stack - mRxQueue = xQueueCreate(64, sizeof(uint8_t)); // TODO Settings::MaxBufferSize + mRxQueue = xQueueCreate(_Settings::MaxBufferSize, sizeof(uint8_t)); _server = BLEDevice::createServer(); - _server->setCallbacks(new MyServerCallbacks(this)); + _server->setCallbacks(new MyServerCallbacks<_Settings>(this)); _server->advertiseOnDisconnect(true); // Create the BLE Service @@ -139,10 +164,7 @@ bool BLEMIDI_ESP32_NimBLE::begin(const char *deviceName, BLEMIDI_TransportsetCallbacks(new MyCharacteristicCallbacks(this)); - - auto _security = new NimBLESecurity(); - _security->setAuthenticationMode(ESP_LE_AUTH_BOND); + _characteristic->setCallbacks(new MyCharacteristicCallbacks<_Settings>(this)); // Start the service service->start(); @@ -158,9 +180,14 @@ bool BLEMIDI_ESP32_NimBLE::begin(const char *deviceName, BLEMIDI_Transport */ -#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ - BLEMIDI_NAMESPACE::BLEMIDI_Transport BLE##Name(DeviceName); \ - MIDI_NAMESPACE::MidiInterface, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport &)BLE##Name); +#define BLEMIDI_CREATE_CUSTOM_INSTANCE(DeviceName, Name, _Settings) \ + BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> BLE##Name(DeviceName); \ + MIDI_NAMESPACE::MidiInterface, _Settings>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> &)BLE##Name); + +/*! \brief Create an instance for ESP32 named + */ +#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ + BLEMIDI_CREATE_CUSTOM_INSTANCE (DeviceName, Name, BLEMIDI_NAMESPACE::DefaultSettings) /*! \brief Create a default instance for ESP32 named BLE-MIDI */ diff --git a/src/hardware/BLEMIDI_nRF52.h b/src/hardware/BLEMIDI_nRF52.h index 2210897..06544c2 100644 --- a/src/hardware/BLEMIDI_nRF52.h +++ b/src/hardware/BLEMIDI_nRF52.h @@ -2,27 +2,28 @@ // I N D E V E L O P M E N T -//#include +#include BEGIN_BLEMIDI_NAMESPACE +template class BLEMIDI_nRF52 { private: -// BLEDis bledis; -// BLEMidi blemidi; + BLEDis bledis; + // BLEMidi blemidi; - BLEMIDI_NAMESPACE::BLEMIDI_Transport* _bleMidiTransport; + BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings>* _bleMidiTransport; - friend class MyServerCallbacks; - friend class MyCharacteristicCallbacks; + // template friend class MyServerCallbacks; + // template friend class MyCharacteristicCallbacks; public: BLEMIDI_nRF52() { } - bool begin(const char*, BLEMIDI_NAMESPACE::BLEMIDI_Transport*); + bool begin(const char*, BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings>*); void end() { @@ -61,41 +62,64 @@ class BLEMIDI_nRF52 } }; -bool BLEMIDI_nRF52::begin(const char* deviceName, BLEMIDI_NAMESPACE::BLEMIDI_Transport* bleMidiTransport) +void connect_callback(uint16_t conn_handle) +{ + Serial.println("Connected"); + + // Get the reference to current connection + BLEConnection* connection = Bluefruit.Connection(conn_handle); +} + +void disconnect_callback(uint16_t conn_handle, uint8_t reason) +{ + (void) conn_handle; + (void) reason; + + Serial.println(); + Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX); +} + +template +bool BLEMIDI_nRF52<_Settings>::begin(const char* deviceName, BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings>* bleMidiTransport) { _bleMidiTransport = bleMidiTransport; // Config the peripheral connection with maximum bandwidth // more SRAM required by SoftDevice // Note: All config***() function must be called before begin() -// Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); -// Bluefruit.begin(); -// Bluefruit.setName(deviceName); -// Bluefruit.setTxPower(4); // Check bluefruit.h for supported values + Bluefruit.begin(); + Bluefruit.setName(deviceName); + Bluefruit.setTxPower(4); // Check bluefruit.h for supported values // Setup the on board blue LED to be enabled on CONNECT -// Bluefruit.autoConnLed(true); + Bluefruit.autoConnLed(true); + + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); // Configure and Start Device Information Service -// bledis.setManufacturer("Adafruit Industries"); -// bledis.setModel("Bluefruit Feather52"); - // bledis.begin(); + bledis.setManufacturer("Adafruit Industries"); + bledis.setModel("Bluefruit Feather52"); + bledis.begin(); // Start advertising ---------------------------- // Set General Discoverable Mode flag -// Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); // Advertise TX Power -// Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addTxPower(); // Advertise BLE MIDI Service -// Bluefruit.Advertising.addService(blemidi); + Bluefruit.Advertising.addService(blemidi); + + // blemidi.write((uint8_t)0); // Secondary Scan Response packet (optional) // Since there is no room for 'Name' in Advertising packet -// Bluefruit.ScanResponse.addName(); + Bluefruit.ScanResponse.addName(); /* Start Advertising * - Enable auto advertising if disconnected @@ -106,23 +130,28 @@ bool BLEMIDI_nRF52::begin(const char* deviceName, BLEMIDI_NAMESPACE::BLEMIDI_Tra * For recommended advertising interval * https://developer.apple.com/library/content/qa/qa1931/_index.html */ -// Bluefruit.Advertising.restartOnDisconnect(true); -// Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms -// Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode -// Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds return true; } + /*! \brief Create an instance for nRF52 named + */ +#define BLEMIDI_CREATE_CUSTOM_INSTANCE(DeviceName, Name, _Settings) \ + BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> BLE##Name(DeviceName); \ + MIDI_NAMESPACE::MidiInterface, _Settings>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport, _Settings> &)BLE##Name); + /*! \brief Create an instance for nRF52 named */ #define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ -BLEMIDI_NAMESPACE::BLEMIDI_Transport BLE##Name(DeviceName); \ -MIDI_NAMESPACE::MidiInterface, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport &)BLE##Name); + BLEMIDI_CREATE_CUSTOM_INSTANCE (DeviceName, Name, BLEMIDI_NAMESPACE::DefaultSettings) /*! \brief Create a default instance for nRF52 named BLE-MIDI */ #define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ -BLEMIDI_CREATE_INSTANCE("nRF85BLE-MIDI", MIDI) + BLEMIDI_CREATE_INSTANCE("nRF85BLE-MIDI", MIDI) END_BLEMIDI_NAMESPACE