Compare commits
69 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
929c2fc049 | |
|
|
8e740fb499 | |
|
|
8dee1f7d25 | |
|
|
498d192e97 | |
|
|
8b7eb46277 | |
|
|
ada4b3ce57 | |
|
|
9f04bcfdd7 | |
|
|
40fd2c241c | |
|
|
741e713ebf | |
|
|
c2815355d7 | |
|
|
564c5a9dde | |
|
|
4b455f695e | |
|
|
a8cb8f3582 | |
|
|
354c75ed60 | |
|
|
39ce9ff6d6 | |
|
|
656d7376fe | |
|
|
b1e968f492 | |
|
|
def06b13ef | |
|
|
ebe18daaea | |
|
|
dad42b4dde | |
|
|
5c42e14704 | |
|
|
36999ecf28 | |
|
|
3f9b48cb1a | |
|
|
f8236efa5f | |
|
|
4724511fa8 | |
|
|
c05b5301c5 | |
|
|
5698c0c4ae | |
|
|
dbeadf5a7f | |
|
|
950ff4e219 | |
|
|
e0afefb9a2 | |
|
|
927b3ac8be | |
|
|
b3df11a0ea | |
|
|
6ea32b340f | |
|
|
f9ad1e5b83 | |
|
|
6045d2270c | |
|
|
3b08fec7da | |
|
|
697e0d40b0 | |
|
|
f1e0982e6b | |
|
|
3c8f54c459 | |
|
|
c87819b42e | |
|
|
ab1dd1567f | |
|
|
841754ce96 | |
|
|
ad26e7b4ed | |
|
|
31817fba8e | |
|
|
6d00baaf53 | |
|
|
586207d747 | |
|
|
895089174b | |
|
|
4e52f70062 | |
|
|
5a0fd77bc6 | |
|
|
43d9cfbc35 | |
|
|
49f7ec93db | |
|
|
5f7d493acd | |
|
|
665f343d74 | |
|
|
60cfe28c45 | |
|
|
b830648af4 | |
|
|
65d96380b8 | |
|
|
b8831e4d3e | |
|
|
75069b93ea | |
|
|
aa0f6bc44a | |
|
|
e0fb12b9c0 | |
|
|
37ac074af7 | |
|
|
0cc25e9ced | |
|
|
30d101bca6 | |
|
|
f4927cd4ea | |
|
|
4aee6708ca | |
|
|
cc1d4ef1e1 | |
|
|
d8746c2967 | |
|
|
ab02fbc108 | |
|
|
9b41d732ef |
|
|
@ -0,0 +1,10 @@
|
|||
name: build
|
||||
on: [pull_request, push]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Build on Arduino CLI
|
||||
run: bash ci/build-arduino.sh
|
||||
|
|
@ -3,3 +3,7 @@ examples/.DS_Store
|
|||
src/.DS_Store
|
||||
.vscode/settings.json
|
||||
.vscode/c_cpp_properties.json
|
||||
test/msvc/.vs
|
||||
test/msvc/x64
|
||||
test/msvc/ConsoleApplication2.vcxproj.user
|
||||
test/msvc/ConsoleApplication2.vcxproj.filters
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
# Arduino BLE-MIDI Transport
|
||||
[](https://www.ardu-badge.com/BLE-MIDI)
|
||||
|
||||
This library implements the BLE-MIDI transport layer for the [FortySevenEffects Arduino MIDI Library](https://github.com/FortySevenEffects/arduino_midi_library)
|
||||
|
||||
## Installation
|
||||
|
|
@ -9,7 +11,7 @@ When installing this library from the Arduino IDE, the dependency be downloaded
|
|||
When manually installing this library, you have to manually download [Arduino MIDI Library](https://github.com/FortySevenEffects/arduino_midi_library) from github and install it in the same directory as this library - without this additional install, this library will not be able to compile.
|
||||
|
||||
When using `ESP32` consider using NimBLE (`NimBLE-Arduino`).
|
||||
When using the `Arduino NANO 33 BLE`, you have to install `ArduinoBLE`
|
||||
When using the `Arduino NANO 33 BLE` or `Arduino NANO RP2040 Connect`, you must install `ArduinoBLE`
|
||||
|
||||
## Usage
|
||||
### Basic / Default
|
||||
|
|
@ -63,8 +65,9 @@ void OnDisconnected() {
|
|||
will create a instance named `BLEMIDI` and listens to incoming MIDI.
|
||||
|
||||
## Tested boards/modules
|
||||
- ESP32 (OOB BLE and NimBLE)
|
||||
- Arduino NANO 33 BLE
|
||||
- ESP32 (OOB BLE and NimBLE)
|
||||
- Arduino NANO 33 BLE
|
||||
- Arduino NANO RP2040 Connect
|
||||
|
||||
## Other Transport protocols:
|
||||
The libraries below the same calling mechanism (API), making it easy to interchange the transport layer.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
# Enable the globstar shell option
|
||||
shopt -s globstar
|
||||
# Make sure we are inside the github workspace
|
||||
cd $GITHUB_WORKSPACE
|
||||
# Create directories
|
||||
mkdir $HOME/Arduino
|
||||
mkdir $HOME/Arduino/libraries
|
||||
# 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
|
||||
|
||||
# Install Arduino AVR core
|
||||
arduino-cli core install arduino:avr
|
||||
arduino-cli core install arduino:samd
|
||||
# arduino-cli core install esp8266:esp8266
|
||||
arduino-cli core install esp32:esp32
|
||||
|
||||
# List the boards
|
||||
arduino-cli board list
|
||||
|
||||
# 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 NimBLE-Arduino
|
||||
|
||||
# Compile all *.ino files for the Arduino Uno
|
||||
# for f in **/AVR_*.ino ; do
|
||||
# arduino-cli compile -b arduino:avr:uno $f
|
||||
# done
|
||||
|
||||
# Compile all *.ino files for the Arduino Uno
|
||||
# for f in **/SAMD_*.ino ; do
|
||||
# arduino-cli compile -b arduino:samd:mkrzero $f
|
||||
# done
|
||||
|
||||
# Compile all *.ino files for the Arduino Uno
|
||||
# for f in **/ESP8266_*.ino ; do
|
||||
# arduino-cli compile -b arduino:esp8266:??? $f
|
||||
# done
|
||||
|
||||
# Compile all *.ino files for the Arduino Uno
|
||||
for f in **/*.ino ; do
|
||||
arduino-cli compile -b arduino:esp32:??? $f
|
||||
done
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include <BLEMIDI_Transport.h>
|
||||
|
||||
#include <hardware/BLEMIDI_ESP32_NimBLE.h>
|
||||
//#include <hardware/BLEMIDI_ESP32.h>
|
||||
//#include <hardware/BLEMIDI_ESP32_NimBLE.h>
|
||||
#include <hardware/BLEMIDI_ESP32.h>
|
||||
//#include <hardware/BLEMIDI_nRF52.h>
|
||||
//#include <hardware/BLEMIDI_ArduinoBLE.h>
|
||||
|
||||
|
|
@ -11,7 +11,9 @@ 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()
|
||||
{
|
||||
|
|
@ -20,11 +22,22 @@ void setup()
|
|||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
|
||||
BLEMIDI.setHandleConnected(OnConnected);
|
||||
BLEMIDI.setHandleDisconnected(OnDisconnected);
|
||||
BLEMIDI.setHandleConnected([]() {
|
||||
isConnected = true;
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
});
|
||||
|
||||
MIDI.setHandleNoteOn(OnNoteOn);
|
||||
MIDI.setHandleNoteOff(OnNoteOff);
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
@ -38,38 +51,6 @@ void loop()
|
|||
{
|
||||
t0 = millis();
|
||||
|
||||
MIDI.sendNoteOn (60, 100, 1); // note 60, velocity 127 on channel 1
|
||||
MIDI.sendNoteOn (60, 100, 1); // note 60, velocity 100 on channel 1
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
// Event handlers for incoming MIDI messages
|
||||
// ====================================================================================
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Device connected
|
||||
// -----------------------------------------------------------------------------
|
||||
void OnConnected() {
|
||||
isConnected = true;
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Device disconnected
|
||||
// -----------------------------------------------------------------------------
|
||||
void OnDisconnected() {
|
||||
isConnected = false;
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Received note on
|
||||
// -----------------------------------------------------------------------------
|
||||
void OnNoteOn(byte channel, byte note, byte velocity) {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Received note off
|
||||
// -----------------------------------------------------------------------------
|
||||
void OnNoteOff(byte channel, byte note, byte velocity) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* --------------------------------------------------------
|
||||
* This example shows how to use client MidiBLE
|
||||
* Client BLEMIDI works im a similar way Server (Common) BLEMIDI, but with some exception.
|
||||
*
|
||||
* The most importart exception is read() method. This function works as usual, but
|
||||
* now it manages machine-states BLE connection too. The
|
||||
* read() function must be called several times continuously in order to scan BLE device
|
||||
* and connect with the server. In this example, read() is called in a "multitask function of
|
||||
* FreeRTOS", but it can be called in loop() function as usual.
|
||||
*
|
||||
* Some BLEMIDI_CREATE_INSTANCE() are added in MidiBLE-Client to be able to choose a specific server to connect
|
||||
* or to connect to the first server which has the MIDI characteristic. You can choose the server by typing in the name field
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @auth RobertoHE
|
||||
* --------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BLEMIDI_Transport.h>
|
||||
|
||||
#include <hardware/BLEMIDI_Client_ESP32.h>
|
||||
|
||||
//#include <hardware/BLEMIDI_ESP32_NimBLE.h>
|
||||
//#include <hardware/BLEMIDI_ESP32.h>
|
||||
//#include <hardware/BLEMIDI_nRF52.h>
|
||||
//#include <hardware/BLEMIDI_ArduinoBLE.h>
|
||||
|
||||
BLEMIDI_CREATE_DEFAULT_INSTANCE(); //Connect to first server found
|
||||
|
||||
//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
|
||||
|
||||
#ifndef LED_BUILTIN
|
||||
#define LED_BUILTIN 2 //modify for match with yout board
|
||||
#endif
|
||||
|
||||
void ReadCB(void *parameter); //Continuos Read function (See FreeRTOS multitasks)
|
||||
|
||||
unsigned long t0 = millis();
|
||||
bool isConnected = false;
|
||||
|
||||
/**
|
||||
* -----------------------------------------------------------------------------
|
||||
* When BLE is connected, LED will turn on (indicating 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);
|
||||
MIDI.begin(MIDI_CHANNEL_OMNI);
|
||||
|
||||
BLEMIDI.setHandleConnected([]()
|
||||
{
|
||||
Serial.println("---------CONNECTED---------");
|
||||
isConnected = true;
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
});
|
||||
|
||||
BLEMIDI.setHandleDisconnected([]()
|
||||
{
|
||||
Serial.println("---------NOT CONNECTED---------");
|
||||
isConnected = false;
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
});
|
||||
|
||||
MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity)
|
||||
{
|
||||
Serial.print("NoteON: CH: ");
|
||||
Serial.print(channel);
|
||||
Serial.print(" | ");
|
||||
Serial.print(note);
|
||||
Serial.print(", ");
|
||||
Serial.println(velocity);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
});
|
||||
MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity)
|
||||
{
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
});
|
||||
|
||||
xTaskCreatePinnedToCore(ReadCB, //See FreeRTOS for more multitask info
|
||||
"MIDI-READ",
|
||||
3000,
|
||||
NULL,
|
||||
1,
|
||||
NULL,
|
||||
1); //Core0 or Core1
|
||||
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, LOW);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
void loop()
|
||||
{
|
||||
//MIDI.read(); // This function is called in the other task
|
||||
|
||||
if (isConnected && (millis() - t0) > 1000)
|
||||
{
|
||||
t0 = millis();
|
||||
|
||||
MIDI.sendNoteOn(60, 100, 1); // note 60, velocity 100 on channel 1
|
||||
vTaskDelay(250/portTICK_PERIOD_MS);
|
||||
MIDI.sendNoteOff(60, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called by xTaskCreatePinnedToCore() to perform a multitask execution.
|
||||
* In this task, read() is called every millisecond (approx.).
|
||||
* read() function performs connection, reconnection and scan-BLE functions.
|
||||
* Call read() method repeatedly to perform a successfull connection with the server
|
||||
* in case connection is lost.
|
||||
*/
|
||||
void ReadCB(void *parameter)
|
||||
{
|
||||
// Serial.print("READ Task is started on core: ");
|
||||
// Serial.println(xPortGetCoreID());
|
||||
for (;;)
|
||||
{
|
||||
MIDI.read();
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS); //Feed the watchdog of FreeRTOS.
|
||||
//Serial.println(uxTaskGetStackHighWaterMark(NULL)); //Only for debug. You can see the watermark of the free resources assigned by the xTaskCreatePinnedToCore() function.
|
||||
}
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
name=BLE-MIDI
|
||||
version=2.1.0
|
||||
version=2.2
|
||||
author=lathoub
|
||||
maintainer=lathoub <lathoub@gmail.com>
|
||||
sentence=BLE-MIDI I/Os for Arduino
|
||||
paragraph=MIDI over Bluetooth Low Energy (BLE-MIDI) 1.0 for Arduino
|
||||
category=Communication
|
||||
url=https://github.com/lathoub/Arduino-BLE-MIDI
|
||||
architectures=esp32,samd,megaavr,mbed,nrf52
|
||||
architectures=esp32,samd,megaavr,mbed,apollo3,mbed_nano,mbed_portenta
|
||||
includes=BLEMIDI_Transport.h
|
||||
depends=MIDI Library, NimBLE-Arduino, ArduinoBLE
|
||||
|
|
|
|||
|
|
@ -2,13 +2,6 @@
|
|||
|
||||
#include "BLEMIDI_Namespace.h"
|
||||
|
||||
// As specified in
|
||||
// Specification for MIDI over Bluetooth Low Energy (BLE-MIDI)
|
||||
// Version 1.0a, NOvember 1, 2015
|
||||
// 3. BLE Service and Characteristics Definitions
|
||||
#define SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700"
|
||||
#define CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3"
|
||||
|
||||
#if ARDUINO
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -12,7 +12,18 @@
|
|||
|
||||
BEGIN_BLEMIDI_NAMESPACE
|
||||
|
||||
template<class T, class _Settings = DefaultSettings>
|
||||
using namespace MIDI_NAMESPACE;
|
||||
|
||||
// As specified in
|
||||
// Specification for MIDI over Bluetooth Low Energy (BLE-MIDI)
|
||||
// Version 1.0a, November 1, 2015
|
||||
// 3. BLE Service and Characteristics Definitions
|
||||
static const char *const SERVICE_UUID = "03b80e5a-ede8-4b33-a751-6ce34ec4c700";
|
||||
static const char *const CHARACTERISTIC_UUID = "7772e5db-3868-4112-a1a9-f2669d106bf3";
|
||||
|
||||
#define MIDI_TYPE 0x80
|
||||
|
||||
template <class T, class _Settings = DefaultSettings>
|
||||
class BLEMIDI_Transport
|
||||
{
|
||||
typedef _Settings Settings;
|
||||
|
|
@ -29,16 +40,16 @@ private:
|
|||
uint8_t mTimestampLow;
|
||||
|
||||
private:
|
||||
T mBleClass;
|
||||
T mBleClass;
|
||||
|
||||
public:
|
||||
BLEMIDI_Transport(const char* deviceName)
|
||||
{
|
||||
BLEMIDI_Transport(const char *deviceName)
|
||||
{
|
||||
strncpy(mDeviceName, deviceName, sizeof(mDeviceName));
|
||||
|
||||
mRxIndex = 0;
|
||||
mTxIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static const bool thruActivated = false;
|
||||
|
|
@ -48,6 +59,11 @@ public:
|
|||
mBleClass.begin(mDeviceName, this);
|
||||
}
|
||||
|
||||
void end()
|
||||
{
|
||||
mBleClass.end();
|
||||
}
|
||||
|
||||
bool beginTransmission(MIDI_NAMESPACE::MidiType type)
|
||||
{
|
||||
getMidiTimestamp(&mTxBuffer[0], &mTxBuffer[1]);
|
||||
|
|
@ -70,20 +86,20 @@ public:
|
|||
|
||||
void endTransmission()
|
||||
{
|
||||
if (mTxBuffer[mTxIndex - 1] == 0xF7)
|
||||
if (mTxBuffer[mTxIndex - 1] == SystemExclusiveEnd)
|
||||
{
|
||||
if (mTxIndex >= sizeof(mTxBuffer))
|
||||
{
|
||||
mBleClass.write(mTxBuffer, mTxIndex - 1);
|
||||
|
||||
mTxIndex = 1; // keep header
|
||||
mTxIndex = 1; // keep header
|
||||
mTxBuffer[mTxIndex++] = mTimestampLow; // or generate new ?
|
||||
}
|
||||
else
|
||||
{
|
||||
mTxBuffer[mTxIndex - 1] = mTimestampLow; // or generate new ?
|
||||
}
|
||||
mTxBuffer[mTxIndex++] = 0xF7;
|
||||
mTxBuffer[mTxIndex++] = SystemExclusiveEnd;
|
||||
}
|
||||
|
||||
mBleClass.write(mTxBuffer, mTxIndex);
|
||||
|
|
@ -99,10 +115,10 @@ public:
|
|||
{
|
||||
uint8_t byte;
|
||||
auto success = mBleClass.available(&byte);
|
||||
if (!success) return mRxIndex;
|
||||
if (!success)
|
||||
return mRxIndex;
|
||||
|
||||
mRxBuffer[mRxIndex++] = byte;
|
||||
|
||||
return mRxIndex;
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +133,6 @@ protected:
|
|||
The header byte contains the topmost 6 bits of timing information for MIDI events in the BLE
|
||||
packet. The remaining 7 bits of timing information for individual MIDI messages encoded in a
|
||||
packet is expressed by timestamp bytes.
|
||||
|
||||
Timestamp Byte
|
||||
bit 7 Set to 1.
|
||||
bits 6-0 timestampLow: Least Significant 7 bits of timestamp information.
|
||||
|
|
@ -152,33 +167,46 @@ protected:
|
|||
and the MSB of both bytes are set to indicate that this is a header byte.
|
||||
Both bytes are placed into the first two position of an array in preparation for a MIDI message.
|
||||
*/
|
||||
static void getMidiTimestamp (uint8_t *header, uint8_t *timestamp)
|
||||
static void getMidiTimestamp(uint8_t *header, uint8_t *timestamp)
|
||||
{
|
||||
auto currentTimeStamp = millis() & 0x01FFF;
|
||||
|
||||
*header = ((currentTimeStamp >> 7) & 0x3F) | 0x80; // 6 bits plus MSB
|
||||
*timestamp = (currentTimeStamp & 0x7F) | 0x80; // 7 bits plus MSB
|
||||
*header = ((currentTimeStamp >> 7) & 0x3F) | 0x80; // 6 bits plus MSB
|
||||
*timestamp = (currentTimeStamp & 0x7F) | 0x80; // 7 bits plus MSB
|
||||
}
|
||||
|
||||
static void setMidiTimestamp (uint8_t header, uint8_t *timestamp)
|
||||
static uint16_t setMidiTimestamp(uint8_t header, uint8_t timestamp)
|
||||
{
|
||||
auto timestampHigh = 0x3f & header;
|
||||
auto timestampLow = 0x7f & timestamp;
|
||||
return (timestampLow + (timestampHigh << 7));
|
||||
}
|
||||
|
||||
public:
|
||||
// callbacks
|
||||
void(*_connectedCallback)() = nullptr;
|
||||
void(*_disconnectedCallback)() = nullptr;
|
||||
// callbacks
|
||||
void (*_connectedCallback)() = nullptr;
|
||||
void (*_disconnectedCallback)() = nullptr;
|
||||
|
||||
BLEMIDI_Transport &setName(const char *deviceName)
|
||||
{
|
||||
strncpy(mDeviceName, deviceName, sizeof(mDeviceName));
|
||||
return *this;
|
||||
};
|
||||
|
||||
public:
|
||||
void setHandleConnected(void(*fptr)()) {
|
||||
_connectedCallback = fptr;
|
||||
}
|
||||
BLEMIDI_Transport &setHandleConnected(void (*fptr)())
|
||||
{
|
||||
_connectedCallback = fptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void setHandleDisconnected(void(*fptr)()) {
|
||||
_disconnectedCallback = fptr;
|
||||
}
|
||||
BLEMIDI_Transport &setHandleDisconnected(void (*fptr)())
|
||||
{
|
||||
_disconnectedCallback = fptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
The general form of a MIDI message follows:
|
||||
n-byte MIDI Message
|
||||
Byte 0 MIDI message Status byte, Bit 7 is Set to 1.
|
||||
|
|
@ -208,83 +236,179 @@ public:
|
|||
MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved
|
||||
from other messages – except for System Exclusive messages.
|
||||
*/
|
||||
void receive(byte* buffer, size_t length)
|
||||
{
|
||||
|
||||
/**
|
||||
* If #define RUNNING_ENABLE is commented/disabled, it will transform all incoming runningStatus messages in full midi messages.
|
||||
* Else, it will put in the buffer the same info that it had received (runningStatus will be not transformated).
|
||||
* It recommend not use runningStatus by default. Only use if parser accepts runningStatus and your application has a so high transmission rate.
|
||||
*/
|
||||
#define RUNNING_ENABLE
|
||||
|
||||
void receive(byte *buffer, size_t length)
|
||||
{
|
||||
// Pointers used to search through payload.
|
||||
byte lPtr = 0;
|
||||
byte rPtr = 0;
|
||||
int lPtr = 0;
|
||||
int rPtr = 0;
|
||||
|
||||
// lastStatus used to capture runningStatus
|
||||
byte lastStatus;
|
||||
// Decode first packet -- SHALL be "Full MIDI message"
|
||||
lPtr = 2; //Start at first MIDI status -- SHALL be "MIDI status"
|
||||
// previousStatus used to continue a runningStatus interrupted by a timeStamp or a System Message.
|
||||
byte previousStatus = InvalidType;
|
||||
|
||||
byte headerByte = buffer[lPtr++];
|
||||
|
||||
auto timestampHigh = 0x3f & headerByte;
|
||||
|
||||
byte timestampByte = buffer[lPtr++];
|
||||
uint16_t timestamp = 0;
|
||||
|
||||
bool sysExContinuation = false;
|
||||
bool runningStatusContinuation = false;
|
||||
|
||||
if (timestampByte >= MIDI_TYPE) // if bit 7 is 1, it's a timestampByte
|
||||
{
|
||||
timestamp = setMidiTimestamp(headerByte, timestampByte);
|
||||
// what do to with the timestamp?
|
||||
}
|
||||
else // if bit 7 is 0, it's the Continuation of a previous SysEx
|
||||
{
|
||||
sysExContinuation = true;
|
||||
lPtr--; // the second byte is part of the SysEx
|
||||
}
|
||||
|
||||
//While statement contains incrementing pointers and breaks when buffer size exceeded.
|
||||
while (true)
|
||||
{
|
||||
lastStatus = buffer[lPtr];
|
||||
|
||||
if( (buffer[lPtr] < 0x80))
|
||||
return; // Status message not present, bail
|
||||
if (previousStatus == InvalidType)
|
||||
{
|
||||
if ((lastStatus < MIDI_TYPE) && !sysExContinuation)
|
||||
return; // Status message not present and it is not a runningStatus continuation, bail
|
||||
}
|
||||
else if (lastStatus < MIDI_TYPE)
|
||||
{
|
||||
lastStatus = previousStatus;
|
||||
runningStatusContinuation = true;
|
||||
}
|
||||
|
||||
// Point to next non-data byte
|
||||
rPtr = lPtr;
|
||||
while( (buffer[rPtr + 1] < 0x80) && (rPtr < (length - 1)) )
|
||||
while ((buffer[rPtr + 1] < MIDI_TYPE) && (rPtr < (length - 1)))
|
||||
rPtr++;
|
||||
if (buffer[rPtr + 1] == 0xF7) rPtr++;
|
||||
|
||||
// look at l and r pointers and decode by size.
|
||||
if( rPtr - lPtr < 1 ) {
|
||||
// Time code or system
|
||||
mBleClass.add(lastStatus);
|
||||
} else if( rPtr - lPtr < 2 ) {
|
||||
mBleClass.add(lastStatus);
|
||||
mBleClass.add(buffer[lPtr + 1]);
|
||||
} else if( rPtr - lPtr < 3 ) {
|
||||
mBleClass.add(lastStatus);
|
||||
mBleClass.add(buffer[lPtr + 1]);
|
||||
mBleClass.add(buffer[lPtr + 2]);
|
||||
} else {
|
||||
// Too much data
|
||||
if (!runningStatusContinuation)
|
||||
{
|
||||
// If not System Common or System Real-Time, send it as running status
|
||||
switch(buffer[lPtr] & 0xF0)
|
||||
|
||||
auto midiType = lastStatus & 0xF0;
|
||||
if (sysExContinuation)
|
||||
midiType = SystemExclusive;
|
||||
|
||||
switch (midiType)
|
||||
{
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
case 0xA0:
|
||||
case 0xB0:
|
||||
case 0xE0:
|
||||
case NoteOff:
|
||||
case NoteOn:
|
||||
case AfterTouchPoly:
|
||||
case ControlChange:
|
||||
case PitchBend:
|
||||
#ifdef RUNNING_ENABLE
|
||||
mBleClass.add(lastStatus);
|
||||
#endif
|
||||
for (auto i = lPtr; i < rPtr; i = i + 2)
|
||||
{
|
||||
#ifndef RUNNING_ENABLE
|
||||
mBleClass.add(lastStatus);
|
||||
#endif
|
||||
mBleClass.add(buffer[i + 1]);
|
||||
mBleClass.add(buffer[i + 2]);
|
||||
}
|
||||
break;
|
||||
case 0xC0:
|
||||
case 0xD0:
|
||||
case ProgramChange:
|
||||
case AfterTouchChannel:
|
||||
#ifdef RUNNING_ENABLE
|
||||
mBleClass.add(lastStatus);
|
||||
#endif
|
||||
for (auto i = lPtr; i < rPtr; i = i + 1)
|
||||
{
|
||||
#ifndef RUNNING_ENABLE
|
||||
mBleClass.add(lastStatus);
|
||||
#endif
|
||||
mBleClass.add(buffer[i + 1]);
|
||||
}
|
||||
break;
|
||||
case 0xF0:
|
||||
mBleClass.add(buffer[lPtr]);
|
||||
case SystemExclusive:
|
||||
mBleClass.add(lastStatus);
|
||||
for (auto i = lPtr; i < rPtr; i++)
|
||||
mBleClass.add(buffer[i + 1]);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef RUNNING_ENABLE
|
||||
auto midiType = lastStatus & 0xF0;
|
||||
switch (midiType)
|
||||
{
|
||||
case NoteOff:
|
||||
case NoteOn:
|
||||
case AfterTouchPoly:
|
||||
case ControlChange:
|
||||
case PitchBend:
|
||||
//3 bytes full Midi -> 2 bytes runningStatus
|
||||
for (auto i = lPtr; i <= rPtr; i = i + 2)
|
||||
{
|
||||
mBleClass.add(lastStatus);
|
||||
mBleClass.add(buffer[i]);
|
||||
mBleClass.add(buffer[i + 1]);
|
||||
}
|
||||
break;
|
||||
case ProgramChange:
|
||||
case AfterTouchChannel:
|
||||
//2 bytes full Midi -> 1 byte runningStatus
|
||||
for (auto i = lPtr; i <= rPtr; i = i + 1)
|
||||
{
|
||||
mBleClass.add(lastStatus);
|
||||
mBleClass.add(buffer[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#else
|
||||
mBleClass.add(lastStatus);
|
||||
for (auto i = lPtr; i <= rPtr; i++)
|
||||
mBleClass.add(buffer[i]);
|
||||
#endif
|
||||
runningStatusContinuation = false;
|
||||
}
|
||||
|
||||
if (++rPtr >= length)
|
||||
return; // end of packet
|
||||
|
||||
if (lastStatus < SystemExclusive) //exclude System Message. They must not be RunningStatus
|
||||
{
|
||||
previousStatus = lastStatus;
|
||||
}
|
||||
|
||||
timestampByte = buffer[rPtr++];
|
||||
if (timestampByte >= MIDI_TYPE) // is bit 7 set?
|
||||
{
|
||||
timestamp = setMidiTimestamp(headerByte, timestampByte);
|
||||
// what do to with the timestamp?
|
||||
}
|
||||
|
||||
// Point to next status
|
||||
lPtr = rPtr + 2;
|
||||
if(lPtr >= length)
|
||||
lPtr = rPtr;
|
||||
if (lPtr >= length)
|
||||
return; //end of packet
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
struct MySettings : public MIDI_NAMESPACE::DefaultSettings
|
||||
|
|
@ -293,4 +417,3 @@ struct MySettings : public MIDI_NAMESPACE::DefaultSettings
|
|||
};
|
||||
|
||||
END_BLEMIDI_NAMESPACE
|
||||
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
// Headers for ESP32 BLE
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BEGIN_BLEMIDI_NAMESPACE
|
||||
|
||||
class BLEMIDI_Client_ESP32
|
||||
{
|
||||
private:
|
||||
BLEClient* _client = nullptr;
|
||||
|
||||
BLEMIDI<class BLEMIDI_Client_ESP32>* _bleMidiTransport = nullptr;
|
||||
|
||||
public:
|
||||
BLEMIDI_Client_ESP32()
|
||||
{
|
||||
}
|
||||
|
||||
bool begin(const char*, BLEMIDI<class BLEMIDI_Client_ESP32>*);
|
||||
|
||||
void write(uint8_t* data, uint8_t length)
|
||||
{
|
||||
_characteristic->setValue(data, length);
|
||||
_characteristic->notify();
|
||||
}
|
||||
|
||||
void receive(uint8_t* buffer, size_t length)
|
||||
{
|
||||
// Post the items to the back of the queue
|
||||
// (drop the first 2 items)
|
||||
for (size_t i = 2; i < length; i++)
|
||||
xQueueSend(_bleMidiTransport->mRxQueue, &buffer[i], portMAX_DELAY);
|
||||
}
|
||||
|
||||
void connected()
|
||||
{
|
||||
if (_bleMidiTransport->_connectedCallback)
|
||||
_bleMidiTransport->_connectedCallback();
|
||||
}
|
||||
|
||||
void disconnected()
|
||||
{
|
||||
if (_bleMidiTransport->_disconnectedCallback)
|
||||
_bleMidiTransport->_disconnectedCallback();
|
||||
}
|
||||
};
|
||||
|
||||
class MyClientCallbacks: public BLEClientCallbacks {
|
||||
public:
|
||||
MyClientCallbacks(BLEMIDI_Client_ESP32* bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32) {
|
||||
}
|
||||
|
||||
protected:
|
||||
BLEMIDI_Client_ESP32* _bluetoothEsp32 = nullptr;
|
||||
|
||||
void onConnect(BLEClient*) {
|
||||
if (_bluetoothEsp32)
|
||||
_bluetoothEsp32->connected();
|
||||
};
|
||||
|
||||
void onDisconnect(BLEClient*) {
|
||||
if (_bluetoothEsp32)
|
||||
_bluetoothEsp32->disconnected();
|
||||
}
|
||||
};
|
||||
|
||||
bool BLEMIDI_Client_ESP32::begin(const char* deviceName, BLEMIDI<class BLEMIDI_ESP32>* bleMidiTransport)
|
||||
{
|
||||
_bleMidiTransport = bleMidiTransport;
|
||||
|
||||
BLEDevice::init(deviceName);
|
||||
|
||||
_client = BLEDevice::createClient();
|
||||
_client->setCallbacks(new MyClientCallbacks(this));
|
||||
|
||||
// Retrieve a Scanner and set the callback we want to use to be informed when we
|
||||
// have detected a new device. Specify that we want active scanning and start the
|
||||
// scan to run for 5 seconds.
|
||||
pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(this));
|
||||
pBLEScan->setInterval(1349);
|
||||
pBLEScan->setWindow(449);
|
||||
pBLEScan->setActiveScan(true);
|
||||
doScan = true;
|
||||
pBLEScan->start(10, scanCompleteCB);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
END_BLEMIDI_NAMESPACE
|
||||
|
|
@ -75,6 +75,11 @@ public:
|
|||
|
||||
bool begin(const char*, BLEMIDI_Transport<class BLEMIDI_ArduinoBLE>*);
|
||||
|
||||
void end()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void write(uint8_t* buffer, size_t length)
|
||||
{
|
||||
// TODO: test length
|
||||
|
|
@ -165,6 +170,7 @@ protected:
|
|||
{
|
||||
if (_bleMidiTransport->_disconnectedCallback)
|
||||
_bleMidiTransport->_disconnectedCallback();
|
||||
|
||||
_central = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,636 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
#############################################
|
||||
########## USER DEFINES BEGINNING ###########
|
||||
####### Modify only these parameters ########
|
||||
#############################################
|
||||
*/
|
||||
|
||||
/*
|
||||
##### BLE DEVICE NAME #####
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 + <NameServer/addrServer> + BLEMIDI_CLIENT_NAME_SUBFIX
|
||||
*
|
||||
* example:
|
||||
* BLEMIDI_CLIENT_NAME_PREFIX "Client-"
|
||||
* <NameServer/addrServer> "AX-Edge"
|
||||
* BLEMIDI_CLIENT_NAME_SUBFIX "-Midi1"
|
||||
*
|
||||
* Result: "Client-AX-Edge-Midi1"
|
||||
*/
|
||||
#define BLEMIDI_CLIENT_NAME_PREFIX "C-"
|
||||
#define BLEMIDI_CLIENT_NAME_SUBFIX ""
|
||||
|
||||
/**
|
||||
* When client tries to connect to the first midi server found:
|
||||
*/
|
||||
#define BLEMIDI_CLIENT_DEFAULT_NAME "BLEMIDI-CLIENT"
|
||||
#endif //Not modify
|
||||
|
||||
/*
|
||||
###### 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
|
||||
*/
|
||||
|
||||
#define BLEMIDI_TX_PWR 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
|
||||
|
||||
/** 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");
|
||||
|
||||
/** return the passkey to send to the server */
|
||||
return passkey;
|
||||
};
|
||||
|
||||
/*
|
||||
###### 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 <NimBLEDevice.h>
|
||||
|
||||
BEGIN_BLEMIDI_NAMESPACE
|
||||
|
||||
#ifdef BLEMIDI_CLIENT_BOND
|
||||
#define BLEMIDI_CLIENT_BOND_DUMMY BLE_SM_PAIR_AUTHREQ_BOND
|
||||
#else
|
||||
#define BLEMIDI_CLIENT_BOND_DUMMY 0x00
|
||||
#endif
|
||||
|
||||
#ifdef BLEMIDI_CLIENT_MITM
|
||||
#define BLEMIDI_CLIENT_MITM_DUMMY BLE_SM_PAIR_AUTHREQ_MITM
|
||||
#else
|
||||
#define BLEMIDI_CLIENT_MITM_DUMMY 0x00
|
||||
#endif
|
||||
|
||||
#ifdef BLEMIDI_CLIENT_PAIR
|
||||
#define BLEMIDI_CLIENT_PAIR_DUMMY BLE_SM_PAIR_AUTHREQ_SC
|
||||
#else
|
||||
#define BLEMIDI_CLIENT_PAIR_DUMMY 0x00
|
||||
#endif
|
||||
|
||||
/** Set the security method.
|
||||
* bonding
|
||||
* man in the middle protection
|
||||
* pair. secure connections
|
||||
*
|
||||
* More info in nimBLE lib
|
||||
*/
|
||||
#define BLEMIDI_CLIENT_SECURITY_AUTH (BLEMIDI_CLIENT_BOND_DUMMY | BLEMIDI_CLIENT_MITM_DUMMY | BLEMIDI_CLIENT_PAIR_DUMMY)
|
||||
|
||||
/** Define a class to handle the callbacks when advertisments are received */
|
||||
class AdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks
|
||||
{
|
||||
public:
|
||||
NimBLEAdvertisedDevice advDevice;
|
||||
bool doConnect = false;
|
||||
bool scanDone = false;
|
||||
bool specificTarget = false;
|
||||
bool enableConnection = false;
|
||||
std::string nameTarget;
|
||||
|
||||
protected:
|
||||
void onResult(NimBLEAdvertisedDevice *advertisedDevice)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/** Define a funtion to handle the callbacks when scan ends */
|
||||
void scanEndedCB(NimBLEScanResults results);
|
||||
|
||||
/** Define the class that performs Client Midi (nimBLE) */
|
||||
class BLEMIDI_Client_ESP32
|
||||
{
|
||||
private:
|
||||
BLEClient *_client = nullptr;
|
||||
BLEAdvertising *_advertising = nullptr;
|
||||
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.
|
||||
|
||||
BLEMIDI_Transport<class BLEMIDI_Client_ESP32> *_bleMidiTransport = nullptr;
|
||||
|
||||
bool specificTarget = false;
|
||||
|
||||
friend class AdvertisedDeviceCallbacks;
|
||||
friend class MyClientCallbacks;
|
||||
friend class MIDI_NAMESPACE::MidiInterface<BLEMIDI_Transport<BLEMIDI_Client_ESP32>, MySettings>; //
|
||||
|
||||
AdvertisedDeviceCallbacks myAdvCB;
|
||||
|
||||
protected:
|
||||
QueueHandle_t mRxQueue;
|
||||
|
||||
public:
|
||||
BLEMIDI_Client_ESP32()
|
||||
{
|
||||
}
|
||||
|
||||
bool begin(const char *, BLEMIDI_Transport<class BLEMIDI_Client_ESP32> *);
|
||||
|
||||
bool end()
|
||||
{
|
||||
myAdvCB.enableConnection = false;
|
||||
xQueueReset(mRxQueue);
|
||||
_client->disconnect();
|
||||
_client = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void write(uint8_t *data, uint8_t length)
|
||||
{
|
||||
if (!myAdvCB.enableConnection)
|
||||
return;
|
||||
if (_characteristic == NULL)
|
||||
return;
|
||||
|
||||
if (firstTimeSend)
|
||||
{
|
||||
_characteristic->writeValue(data, length, true);
|
||||
firstTimeSend = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_characteristic->writeValue(data, length, !_characteristic->canWriteNoResponse()))
|
||||
firstTimeSend = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool available(byte *pvBuffer);
|
||||
|
||||
void add(byte value)
|
||||
{
|
||||
// called from BLE-MIDI, to add it to a buffer here
|
||||
xQueueSend(mRxQueue, &value, portMAX_DELAY / 2);
|
||||
}
|
||||
|
||||
protected:
|
||||
void receive(uint8_t *buffer, size_t length)
|
||||
{
|
||||
// forward the buffer so that it can be parsed
|
||||
_bleMidiTransport->receive(buffer, length);
|
||||
}
|
||||
|
||||
void connectCallbacks(MIDI_NAMESPACE::MidiInterface<BLEMIDI_Transport<BLEMIDI_Client_ESP32>, MySettings> *MIDIcallback);
|
||||
|
||||
void connected()
|
||||
{
|
||||
if (_bleMidiTransport->_connectedCallback)
|
||||
{
|
||||
_bleMidiTransport->_connectedCallback();
|
||||
}
|
||||
firstTimeSend = true;
|
||||
}
|
||||
|
||||
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 */
|
||||
class MyClientCallbacks : public BLEClientCallbacks
|
||||
{
|
||||
public:
|
||||
MyClientCallbacks(BLEMIDI_Client_ESP32 *bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
BLEMIDI_Client_ESP32 *_bluetoothEsp32 = nullptr;
|
||||
|
||||
uint32_t onPassKeyRequest()
|
||||
{
|
||||
return userOnPassKeyRequest();
|
||||
};
|
||||
|
||||
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);
|
||||
vTaskDelay(1);
|
||||
if (_bluetoothEsp32)
|
||||
{
|
||||
_bluetoothEsp32->connected();
|
||||
}
|
||||
};
|
||||
|
||||
void onDisconnect(BLEClient *pClient)
|
||||
{
|
||||
//Serial.print(pClient->getPeerAddress().toString().c_str());
|
||||
//Serial.println(" 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
|
||||
|
||||
//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)
|
||||
{ /** 1.25ms units */
|
||||
return false;
|
||||
}
|
||||
else if (params->itvl_max > BLEMIDI_CLIENT_COMM_MAX_INTERVAL)
|
||||
{ /** 1.25ms units */
|
||||
return false;
|
||||
}
|
||||
else if (params->latency > BLEMIDI_CLIENT_COMM_LATENCY)
|
||||
{ /** Number of intervals allowed to skip */
|
||||
return false;
|
||||
}
|
||||
else if (params->supervision_timeout > BLEMIDI_CLIENT_COMM_TIMEOUT)
|
||||
{ /** 10ms units */
|
||||
return false;
|
||||
}
|
||||
pClient->updateConnParams(params->itvl_min, params->itvl_max, params->latency, params->supervision_timeout);
|
||||
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
##########################################
|
||||
############# IMPLEMENTATION #############
|
||||
##########################################
|
||||
*/
|
||||
|
||||
bool BLEMIDI_Client_ESP32::begin(const char *deviceName, BLEMIDI_Transport<class BLEMIDI_Client_ESP32> *bleMidiTransport)
|
||||
{
|
||||
_bleMidiTransport = bleMidiTransport;
|
||||
|
||||
std::string strDeviceName(deviceName);
|
||||
if (strDeviceName == "") // Connect to the first midi server found
|
||||
{
|
||||
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
|
||||
{
|
||||
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());
|
||||
|
||||
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
|
||||
|
||||
NimBLEDevice::setSecurityIOCap(BLEMIDI_CLIENT_SECURITY_CAP); // Attention, it may need a passkey
|
||||
NimBLEDevice::setSecurityAuth(BLEMIDI_CLIENT_SECURITY_AUTH);
|
||||
|
||||
/** Optional: set the transmit power, default is 3db */
|
||||
NimBLEDevice::setPower(BLEMIDI_TX_PWR); /** +9db */
|
||||
|
||||
myAdvCB.enableConnection = true;
|
||||
scan();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BLEMIDI_Client_ESP32::available(byte *pvBuffer)
|
||||
{
|
||||
if (myAdvCB.enableConnection)
|
||||
{
|
||||
if (_client == nullptr || !_client->isConnected()) //Try to connect/reconnect
|
||||
{
|
||||
if (myAdvCB.doConnect)
|
||||
{
|
||||
myAdvCB.doConnect = false;
|
||||
if (!connect())
|
||||
{
|
||||
scan();
|
||||
}
|
||||
}
|
||||
else if (myAdvCB.scanDone)
|
||||
{
|
||||
scan();
|
||||
}
|
||||
}
|
||||
// return 1 byte from the Queue
|
||||
return xQueueReceive(mRxQueue, (void *)pvBuffer, 0); // return immediately when the queue is empty
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Notification receiving handler callback */
|
||||
void BLEMIDI_Client_ESP32::notifyCB(NimBLERemoteCharacteristic *pRemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify)
|
||||
{
|
||||
if (this->_characteristic == pRemoteCharacteristic) //Redundant protection
|
||||
{
|
||||
receive(pData, length);
|
||||
}
|
||||
}
|
||||
|
||||
void BLEMIDI_Client_ESP32::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
|
||||
// scan to run for 3 seconds.
|
||||
myAdvCB.scanDone = true;
|
||||
NimBLEScan *pBLEScan = BLEDevice::getScan();
|
||||
if (!pBLEScan->isScanning())
|
||||
{
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(&myAdvCB);
|
||||
pBLEScan->setInterval(600);
|
||||
pBLEScan->setWindow(500);
|
||||
pBLEScan->setActiveScan(true);
|
||||
|
||||
Serial.println("Scanning...");
|
||||
pBLEScan->start(1, scanEndedCB);
|
||||
}
|
||||
};
|
||||
|
||||
bool BLEMIDI_Client_ESP32::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)
|
||||
{
|
||||
if (_client == NimBLEDevice::getClientByPeerAddress(myAdvCB.advDevice.getAddress()))
|
||||
{
|
||||
if (_client->connect(&myAdvCB.advDevice, false))
|
||||
{
|
||||
if (_characteristic->canNotify())
|
||||
{
|
||||
if (_characteristic->subscribe(true, std::bind(&BLEMIDI_Client_ESP32::notifyCB, this, _1, _2, _3, _4)))
|
||||
{
|
||||
//Re-connection SUCCESS
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/** 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 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");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create and setup a new client
|
||||
_client = BLEDevice::createClient();
|
||||
|
||||
_client->setClientCallbacks(new MyClientCallbacks(this), false);
|
||||
|
||||
_client->setConnectionParams(BLEMIDI_CLIENT_COMM_MIN_INTERVAL, BLEMIDI_CLIENT_COMM_MAX_INTERVAL, BLEMIDI_CLIENT_COMM_LATENCY, BLEMIDI_CLIENT_COMM_TIMEOUT);
|
||||
|
||||
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
|
||||
_client->setConnectTimeout(15);
|
||||
|
||||
if (!_client->connect(&myAdvCB.advDevice))
|
||||
{
|
||||
/** 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");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_client->isConnected())
|
||||
{
|
||||
//Serial.println("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());
|
||||
|
||||
/*
|
||||
Serial.print("RSSI: ");
|
||||
Serial.println(_client->getRssi());
|
||||
*/
|
||||
|
||||
/** Now we can read/write/subscribe the charateristics of the services we are interested in */
|
||||
pSvc = _client->getService(SERVICE_UUID);
|
||||
if (pSvc) /** make sure it's not null */
|
||||
{
|
||||
_characteristic = pSvc->getCharacteristic(CHARACTERISTIC_UUID);
|
||||
|
||||
if (_characteristic) /** make sure it's not null */
|
||||
{
|
||||
if (_characteristic->canNotify())
|
||||
{
|
||||
if (_characteristic->subscribe(true, std::bind(&BLEMIDI_Client_ESP32::notifyCB, this, _1, _2, _3, _4)))
|
||||
{
|
||||
//Connection SUCCESS
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If anything fails, disconnect and delete client
|
||||
_client->disconnect();
|
||||
NimBLEDevice::deleteClient(_client);
|
||||
_client = nullptr;
|
||||
return false;
|
||||
};
|
||||
|
||||
/** Callback to process the results of the last scan or restart it */
|
||||
void scanEndedCB(NimBLEScanResults results)
|
||||
{
|
||||
// Serial.println("Scan Ended");
|
||||
}
|
||||
|
||||
END_BLEMIDI_NAMESPACE
|
||||
|
||||
/*! \brief Create an instance for ESP32 named <DeviceName>, and adviertise it like "Prefix + <DeviceName> + Subfix"
|
||||
It will try to connect to a specific server with equal name or addr than <DeviceName>. If <DeviceName> is "", it will connect to first midi server
|
||||
*/
|
||||
#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \
|
||||
BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_Client_ESP32> BLE##Name(DeviceName); \
|
||||
MIDI_NAMESPACE::MidiInterface<BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_Client_ESP32>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_Client_ESP32> &)BLE##Name);
|
||||
|
||||
/*! \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() \
|
||||
BLEMIDI_CREATE_INSTANCE("", MIDI)
|
||||
|
|
@ -11,11 +11,11 @@ BEGIN_BLEMIDI_NAMESPACE
|
|||
class BLEMIDI_ESP32
|
||||
{
|
||||
private:
|
||||
BLEServer* _server = nullptr;
|
||||
BLEAdvertising* _advertising = nullptr;
|
||||
BLECharacteristic* _characteristic = nullptr;
|
||||
BLEServer *_server = nullptr;
|
||||
BLEAdvertising *_advertising = nullptr;
|
||||
BLECharacteristic *_characteristic = nullptr;
|
||||
|
||||
BLEMIDI_Transport<class BLEMIDI_ESP32>* _bleMidiTransport = nullptr;
|
||||
BLEMIDI_Transport<class BLEMIDI_ESP32> *_bleMidiTransport = nullptr;
|
||||
|
||||
friend class MyServerCallbacks;
|
||||
friend class MyCharacteristicCallbacks;
|
||||
|
|
@ -24,19 +24,24 @@ protected:
|
|||
QueueHandle_t mRxQueue;
|
||||
|
||||
public:
|
||||
BLEMIDI_ESP32()
|
||||
BLEMIDI_ESP32()
|
||||
{
|
||||
}
|
||||
|
||||
bool begin(const char*, BLEMIDI_Transport<class BLEMIDI_ESP32>*);
|
||||
bool begin(const char *, BLEMIDI_Transport<class BLEMIDI_ESP32> *);
|
||||
|
||||
void write(uint8_t* buffer, size_t length)
|
||||
void end()
|
||||
{
|
||||
Serial.println("end");
|
||||
}
|
||||
|
||||
void write(uint8_t *buffer, size_t length)
|
||||
{
|
||||
_characteristic->setValue(buffer, length);
|
||||
_characteristic->notify();
|
||||
}
|
||||
|
||||
bool available(byte* pvBuffer)
|
||||
bool available(byte *pvBuffer)
|
||||
{
|
||||
return xQueueReceive(mRxQueue, pvBuffer, 0); // return immediately when the queue is empty
|
||||
}
|
||||
|
|
@ -48,65 +53,77 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
void receive(uint8_t* buffer, size_t length)
|
||||
{
|
||||
void receive(uint8_t *buffer, size_t length)
|
||||
{
|
||||
// parse the incoming buffer
|
||||
_bleMidiTransport->receive(buffer, length);
|
||||
}
|
||||
}
|
||||
|
||||
void connected()
|
||||
{
|
||||
if (_bleMidiTransport->_connectedCallback)
|
||||
_bleMidiTransport->_connectedCallback();
|
||||
}
|
||||
void connected()
|
||||
{
|
||||
if (_bleMidiTransport->_connectedCallback)
|
||||
_bleMidiTransport->_connectedCallback();
|
||||
}
|
||||
|
||||
void disconnected()
|
||||
{
|
||||
if (_bleMidiTransport->_disconnectedCallback)
|
||||
_bleMidiTransport->_disconnectedCallback();
|
||||
}
|
||||
void disconnected()
|
||||
{
|
||||
if (_bleMidiTransport->_disconnectedCallback)
|
||||
_bleMidiTransport->_disconnectedCallback();
|
||||
|
||||
end();
|
||||
}
|
||||
};
|
||||
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
class MyServerCallbacks : public BLEServerCallbacks
|
||||
{
|
||||
public:
|
||||
MyServerCallbacks(BLEMIDI_ESP32* bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32) {
|
||||
MyServerCallbacks(BLEMIDI_ESP32 *bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
BLEMIDI_ESP32* _bluetoothEsp32 = nullptr;
|
||||
BLEMIDI_ESP32 *_bluetoothEsp32 = nullptr;
|
||||
|
||||
void onConnect(BLEServer*) {
|
||||
void onConnect(BLEServer *)
|
||||
{
|
||||
if (_bluetoothEsp32)
|
||||
_bluetoothEsp32->connected();
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer*) {
|
||||
void onDisconnect(BLEServer *server)
|
||||
{
|
||||
if (_bluetoothEsp32)
|
||||
_bluetoothEsp32->disconnected();
|
||||
}
|
||||
|
||||
server->getAdvertising()->start();
|
||||
}
|
||||
};
|
||||
|
||||
class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {
|
||||
class MyCharacteristicCallbacks : public BLECharacteristicCallbacks
|
||||
{
|
||||
public:
|
||||
MyCharacteristicCallbacks(BLEMIDI_ESP32* bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32 ) {
|
||||
MyCharacteristicCallbacks(BLEMIDI_ESP32 *bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
BLEMIDI_ESP32* _bluetoothEsp32 = nullptr;
|
||||
BLEMIDI_ESP32 *_bluetoothEsp32 = nullptr;
|
||||
|
||||
void onWrite(BLECharacteristic * characteristic) {
|
||||
void onWrite(BLECharacteristic *characteristic)
|
||||
{
|
||||
std::string rxValue = characteristic->getValue();
|
||||
if (rxValue.length() > 0) {
|
||||
_bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length());
|
||||
if (rxValue.length() > 0)
|
||||
{
|
||||
_bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool BLEMIDI_ESP32::begin(const char* deviceName, BLEMIDI_Transport<class BLEMIDI_ESP32>* bleMidiTransport)
|
||||
bool BLEMIDI_ESP32::begin(const char *deviceName, BLEMIDI_Transport<class BLEMIDI_ESP32> *bleMidiTransport)
|
||||
{
|
||||
_bleMidiTransport = bleMidiTransport;
|
||||
_bleMidiTransport = bleMidiTransport;
|
||||
|
||||
BLEDevice::init(deviceName);
|
||||
|
||||
|
|
@ -122,41 +139,42 @@ bool BLEMIDI_ESP32::begin(const char* deviceName, BLEMIDI_Transport<class BLEMID
|
|||
|
||||
// Create a BLE Characteristic
|
||||
_characteristic = service->createCharacteristic(
|
||||
BLEUUID(CHARACTERISTIC_UUID),
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_WRITE_NR
|
||||
);
|
||||
BLEUUID(CHARACTERISTIC_UUID),
|
||||
BLECharacteristic::PROPERTY_READ |
|
||||
BLECharacteristic::PROPERTY_WRITE |
|
||||
BLECharacteristic::PROPERTY_NOTIFY |
|
||||
BLECharacteristic::PROPERTY_WRITE_NR);
|
||||
// Add CCCD 0x2902 to allow notify
|
||||
_characteristic->addDescriptor(new BLE2902());
|
||||
|
||||
_characteristic->setCallbacks(new MyCharacteristicCallbacks(this));
|
||||
|
||||
auto _security = new BLESecurity();
|
||||
_security->setAuthenticationMode(ESP_LE_AUTH_BOND);
|
||||
|
||||
// Start the service
|
||||
service->start();
|
||||
|
||||
auto advertisementData = BLEAdvertisementData();
|
||||
advertisementData.setFlags(0x04);
|
||||
advertisementData.setCompleteServices(BLEUUID(SERVICE_UUID));
|
||||
advertisementData.setName(deviceName);
|
||||
|
||||
// Start advertising
|
||||
_advertising = _server->getAdvertising();
|
||||
_advertising->setAdvertisementData(advertisementData);
|
||||
_advertising->addServiceUUID(service->getUUID());
|
||||
_advertising->setAppearance(0x00);
|
||||
_advertising->start();
|
||||
|
||||
Serial.println("begin");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! \brief Create an instance for ESP32 named <DeviceName>
|
||||
/*! \brief Create an instance for ESP32 named <DeviceName>
|
||||
*/
|
||||
#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \
|
||||
BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32> BLE##Name(DeviceName); \
|
||||
MIDI_NAMESPACE::MidiInterface<BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32> &)BLE##Name);
|
||||
#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \
|
||||
BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32> BLE##Name(DeviceName); \
|
||||
MIDI_NAMESPACE::MidiInterface<BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32> &)BLE##Name);
|
||||
|
||||
/*! \brief Create a default instance for ESP32 named BLE-MIDI
|
||||
/*! \brief Create a default instance for ESP32 named BLE-MIDI
|
||||
*/
|
||||
#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \
|
||||
BLEMIDI_CREATE_INSTANCE("BLE-MIDI", MIDI)
|
||||
BLEMIDI_CREATE_INSTANCE("Esp32-BLE-MIDI", MIDI)
|
||||
|
||||
END_BLEMIDI_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ BEGIN_BLEMIDI_NAMESPACE
|
|||
class BLEMIDI_ESP32_NimBLE
|
||||
{
|
||||
private:
|
||||
BLEServer* _server = nullptr;
|
||||
BLEAdvertising* _advertising = nullptr;
|
||||
BLECharacteristic* _characteristic = nullptr;
|
||||
BLEServer *_server = nullptr;
|
||||
BLEAdvertising *_advertising = nullptr;
|
||||
BLECharacteristic *_characteristic = nullptr;
|
||||
|
||||
BLEMIDI_Transport<class BLEMIDI_ESP32_NimBLE>* _bleMidiTransport = nullptr;
|
||||
BLEMIDI_Transport<class BLEMIDI_ESP32_NimBLE> *_bleMidiTransport = nullptr;
|
||||
|
||||
friend class MyServerCallbacks;
|
||||
friend class MyCharacteristicCallbacks;
|
||||
|
|
@ -21,22 +21,26 @@ protected:
|
|||
QueueHandle_t mRxQueue;
|
||||
|
||||
public:
|
||||
BLEMIDI_ESP32_NimBLE()
|
||||
BLEMIDI_ESP32_NimBLE()
|
||||
{
|
||||
}
|
||||
|
||||
bool begin(const char*, BLEMIDI_Transport<class BLEMIDI_ESP32_NimBLE>*);
|
||||
bool begin(const char *, BLEMIDI_Transport<class BLEMIDI_ESP32_NimBLE> *);
|
||||
|
||||
void write(uint8_t* buffer, size_t length)
|
||||
void end()
|
||||
{
|
||||
}
|
||||
|
||||
void write(uint8_t *buffer, size_t length)
|
||||
{
|
||||
_characteristic->setValue(buffer, length);
|
||||
_characteristic->notify();
|
||||
}
|
||||
|
||||
bool available(byte* pvBuffer)
|
||||
bool available(byte *pvBuffer)
|
||||
{
|
||||
// return 1 byte from the Queue
|
||||
return xQueueReceive(mRxQueue, (void*)pvBuffer, 0); // return immediately when the queue is empty
|
||||
return xQueueReceive(mRxQueue, (void *)pvBuffer, 0); // return immediately when the queue is empty
|
||||
}
|
||||
|
||||
void add(byte value)
|
||||
|
|
@ -46,65 +50,73 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
void receive(uint8_t* buffer, size_t length)
|
||||
{
|
||||
void receive(uint8_t *buffer, size_t length)
|
||||
{
|
||||
// forward the buffer so it can be parsed
|
||||
_bleMidiTransport->receive(buffer, length);
|
||||
}
|
||||
}
|
||||
|
||||
void connected()
|
||||
{
|
||||
if (_bleMidiTransport->_connectedCallback)
|
||||
_bleMidiTransport->_connectedCallback();
|
||||
}
|
||||
void connected()
|
||||
{
|
||||
if (_bleMidiTransport->_connectedCallback)
|
||||
_bleMidiTransport->_connectedCallback();
|
||||
}
|
||||
|
||||
void disconnected()
|
||||
{
|
||||
if (_bleMidiTransport->_disconnectedCallback)
|
||||
_bleMidiTransport->_disconnectedCallback();
|
||||
}
|
||||
void disconnected()
|
||||
{
|
||||
if (_bleMidiTransport->_disconnectedCallback)
|
||||
_bleMidiTransport->_disconnectedCallback();
|
||||
}
|
||||
};
|
||||
|
||||
class MyServerCallbacks: public BLEServerCallbacks {
|
||||
class MyServerCallbacks : public BLEServerCallbacks
|
||||
{
|
||||
public:
|
||||
MyServerCallbacks(BLEMIDI_ESP32_NimBLE* bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32) {
|
||||
MyServerCallbacks(BLEMIDI_ESP32_NimBLE *bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
BLEMIDI_ESP32_NimBLE* _bluetoothEsp32 = nullptr;
|
||||
BLEMIDI_ESP32_NimBLE *_bluetoothEsp32 = nullptr;
|
||||
|
||||
void onConnect(BLEServer*) {
|
||||
void onConnect(BLEServer *)
|
||||
{
|
||||
if (_bluetoothEsp32)
|
||||
_bluetoothEsp32->connected();
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer*) {
|
||||
void onDisconnect(BLEServer *)
|
||||
{
|
||||
if (_bluetoothEsp32)
|
||||
_bluetoothEsp32->disconnected();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {
|
||||
class MyCharacteristicCallbacks : public BLECharacteristicCallbacks
|
||||
{
|
||||
public:
|
||||
MyCharacteristicCallbacks(BLEMIDI_ESP32_NimBLE* bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32 ) {
|
||||
MyCharacteristicCallbacks(BLEMIDI_ESP32_NimBLE *bluetoothEsp32)
|
||||
: _bluetoothEsp32(bluetoothEsp32)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
BLEMIDI_ESP32_NimBLE* _bluetoothEsp32 = nullptr;
|
||||
BLEMIDI_ESP32_NimBLE *_bluetoothEsp32 = nullptr;
|
||||
|
||||
void onWrite(BLECharacteristic * characteristic) {
|
||||
void onWrite(BLECharacteristic *characteristic)
|
||||
{
|
||||
std::string rxValue = characteristic->getValue();
|
||||
if (rxValue.length() > 0) {
|
||||
_bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length());
|
||||
if (rxValue.length() > 0)
|
||||
{
|
||||
_bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool BLEMIDI_ESP32_NimBLE::begin(const char* deviceName, BLEMIDI_Transport<class BLEMIDI_ESP32_NimBLE>* bleMidiTransport)
|
||||
bool BLEMIDI_ESP32_NimBLE::begin(const char *deviceName, BLEMIDI_Transport<class BLEMIDI_ESP32_NimBLE> *bleMidiTransport)
|
||||
{
|
||||
_bleMidiTransport = bleMidiTransport;
|
||||
_bleMidiTransport = bleMidiTransport;
|
||||
|
||||
BLEDevice::init(deviceName);
|
||||
|
||||
|
|
@ -114,45 +126,45 @@ bool BLEMIDI_ESP32_NimBLE::begin(const char* deviceName, BLEMIDI_Transport<class
|
|||
|
||||
_server = BLEDevice::createServer();
|
||||
_server->setCallbacks(new MyServerCallbacks(this));
|
||||
_server->advertiseOnDisconnect(true);
|
||||
|
||||
// Create the BLE Service
|
||||
auto service = _server->createService(BLEUUID(SERVICE_UUID));
|
||||
|
||||
// Create a BLE Characteristic
|
||||
_characteristic = service->createCharacteristic(
|
||||
BLEUUID(CHARACTERISTIC_UUID),
|
||||
NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::WRITE |
|
||||
NIMBLE_PROPERTY::NOTIFY |
|
||||
NIMBLE_PROPERTY::WRITE_NR
|
||||
);
|
||||
BLEUUID(CHARACTERISTIC_UUID),
|
||||
NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::WRITE |
|
||||
NIMBLE_PROPERTY::NOTIFY |
|
||||
NIMBLE_PROPERTY::WRITE_NR);
|
||||
|
||||
_characteristic->setCallbacks(new MyCharacteristicCallbacks(this));
|
||||
|
||||
auto _security = new NimBLESecurity();
|
||||
_security->setAuthenticationMode(ESP_LE_AUTH_BOND);
|
||||
|
||||
// Start the service
|
||||
service->start();
|
||||
|
||||
auto advertisementData = BLEAdvertisementData();
|
||||
advertisementData.setFlags(0x04);
|
||||
advertisementData.setCompleteServices(BLEUUID(SERVICE_UUID));
|
||||
advertisementData.setName(deviceName);
|
||||
|
||||
// Start advertising
|
||||
_advertising = _server->getAdvertising();
|
||||
_advertising->setAdvertisementData(advertisementData);
|
||||
_advertising->addServiceUUID(service->getUUID());
|
||||
_advertising->setAppearance(0x00);
|
||||
_advertising->start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! \brief Create an instance for ESP32 named <DeviceName>
|
||||
/*! \brief Create an instance for ESP32 named <DeviceName>
|
||||
*/
|
||||
#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \
|
||||
BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32_NimBLE> BLE##Name(DeviceName); \
|
||||
MIDI_NAMESPACE::MidiInterface<BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32_NimBLE>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32_NimBLE> &)BLE##Name);
|
||||
#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \
|
||||
BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32_NimBLE> BLE##Name(DeviceName); \
|
||||
MIDI_NAMESPACE::MidiInterface<BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32_NimBLE>, BLEMIDI_NAMESPACE::MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDI_Transport<BLEMIDI_NAMESPACE::BLEMIDI_ESP32_NimBLE> &)BLE##Name);
|
||||
|
||||
/*! \brief Create a default instance for ESP32 named BLE-MIDI
|
||||
/*! \brief Create a default instance for ESP32 named BLE-MIDI
|
||||
*/
|
||||
#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \
|
||||
BLEMIDI_CREATE_INSTANCE("Esp32-BLE-MIDI", MIDI)
|
||||
BLEMIDI_CREATE_INSTANCE("Esp32-NimBLE-MIDI", MIDI)
|
||||
|
||||
END_BLEMIDI_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ public:
|
|||
|
||||
bool begin(const char*, BLEMIDI_NAMESPACE::BLEMIDI_Transport<class BLEMIDI_nRF52>*);
|
||||
|
||||
void end()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void write(uint8_t* buffer, size_t length)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue