BLE MIDI

Dependencies:   BLE_API mbed nRF51822

Files at this revision

API Documentation at this revision

Comitter:
niklasjakob
Date:
Mon Jun 12 17:12:31 2017 +0000
Commit message:
BLE MIDI BRIDGE TEST

Changed in this revision

BLE_API.lib Show annotated file Show diff for this revision Revisions of this file
LEDService.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
nRF51822.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BLE_API.lib	Mon Jun 12 17:12:31 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#65474dc93927
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LEDService.h	Mon Jun 12 17:12:31 2017 +0000
@@ -0,0 +1,72 @@
+
+/*
+ *  Copyright (c) 2016 Samuele Cornell  
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a copy
+ *  of this software and associated documentation files (the "Software"), to deal
+ *  in the Software without restriction, including without limitation the rights
+ *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *  copies of the Software, and to permit persons to whom the Software is
+ *  furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in all
+ *  copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ *  SOFTWARE.
+ *
+ */
+
+#define UART_RX_PIN USBRX// RX  UART Pin  
+
+#define UART_TX_PIN USBTX// TX UART Pin 
+
+// i have used DFRobot MIDI Shield but there are other commercially available similar products, also it is possible to interface directly a MIDI cable via an optocoupler (several projects are already available online ) 
+
+#define LIGHT_SHOW 1 // 0 if visual feedback isn't needed  (saves energy )
+
+#define TX_LED D6 // the state of this led will change whenever a message is to be sent over ble-midi
+
+#define RX_LED D7 // the state of this led will change whenever a message received over ble-midi
+
+
+/*******************************************************************************************************
+PERFORMANCE TWEAKS
+*******************************************************************************************************/
+
+
+#define BUFSERIAL_LENGHT 256 // define the software circular buffer lenght used for the UART. 
+
+
+#define ONLY_MIDI_to_BLEMIDI 1 // if only unidirectional MIDI to BLE-MIDI is desired set this to 1  
+
+
+#define ONLY_BLEMIDI_to_MIDI 1 // if only unidirectional BLE-MIDI to MIDI is desired set this to 1 
+
+// unidirectional operation allows to save energy. It also leads to better performance as if ONLY MIDI to BLE MIDI is required, for example it is  possible to shorten the SENDBLE_INTERVAL
+// without reliability issues (to a certain extent).   
+
+#define SENDBLEMIDI_INTERVAL 0.01 // this defines how frequently MIDI Events from the UART are polled, parsed and then sent via BLE-MIDI. 
+                                 // a lower value means less latency but it also increase energy comsuption and if is set too low can cause reliability issues in MIDI to BLE-MIDI operation (especially for long SysEx messages).  
+
+
+/***************************************************************************************************************
+CONNECTION PARAMETERS 
+***************************************************************************************************************/
+
+namespace Config { 
+
+// 
+const int minConnectionInterval = 6; // (1.25 ms units)  
+const int maxConnectionInterval = 15; // (1.25 ms units)
+const int slaveLatency          = 0;
+const int supervisionTimeout    = 500; // (10 ms units)
+
+
+
+} 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Jun 12 17:12:31 2017 +0000
@@ -0,0 +1,409 @@
+/*  
+ *  Copyright (c) 2016 Samuele Cornell  
+ *
+ *  Permission is hereby granted, free of charge, to any person obtaining a copy
+ *  of this software and associated documentation files (the "Software"), to deal
+ *  in the Software without restriction, including without limitation the rights
+ *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ *  copies of the Software, and to permit persons to whom the Software is
+ *  furnished to do so, subject to the following conditions:
+ *
+ *  The above copyright notice and this permission notice shall be included in all
+ *  copies or substantial portions of the Software.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ *  SOFTWARE.
+ *
+ */
+ 
+ /*
+ *   Thanks to Matthias Frick and his project blidino https://github.com/sieren/blidino 
+ *   which also uses RedBearslab nRF51822 and implements a USB-MIDI to BLE-MIDI bridge. 
+ *   His work made this possible and served as the main inspiration for this project . 
+ *   The content of BLEMIDI_MIDI_Parser.h file is for the most part taken from his blidino project.  
+ */
+
+#include "mbed.h"
+
+#include "ble/BLE.h"
+
+#include "LEDService.h"
+
+Serial UART(UART_TX_PIN,UART_RX_PIN) ; //UART_RX_PIN,BUFSERIAL_LENGHT  
+
+#if ONLY_MIDI_to_BLEMIDI==0 
+
+#include "BLEMIDI_MIDI_Parser.h" 
+
+#endif
+
+#if ONLY_BLEMIDI_to_MIDI==0   
+
+#include "MIDI_BLEMIDI_Parser.h"
+
+#endif
+
+
+
+#define TXRX_BUF_LEN                    20 
+
+#define RX_BUF_LEN                      256 
+
+
+#if LIGHT_SHOW==1
+DigitalOut redled(P0_20);  // sets the two LEDS on the MIDI Shield as outputs, these will be used as a visual feedback for debug 
+DigitalOut greenled(P0_19); //
+#endif 
+
+
+/******************************************************************************************************************************
+*INITIALIZE VARIABLES, BUFFERS and BLE-MIDI Service and Characteristic
+******************************************************************************************************************************/
+
+Ticker sendBLEMIDI_Ticker ; 
+Ticker sendData_Ticker ; 
+ 
+Timer t; // timer used for BLE-MIDI timestamps 
+
+bool isConnected; 
+
+
+////////////////////////////////////////////
+bool sendBLEMIDI_flag = false ; 
+
+////////////
+
+
+BLEDevice ble; // BLE_API  
+
+static Gap::ConnectionParams_t connectionParams;
+
+
+// MIDI BLE Service and Characteristic UUID ( see Apple BLE-MIDI Spec. and Midi manufacter Association BLE-MIDI Spec.) 
+
+static const uint8_t service_uuid[] = {0x03, 0xB8, 0x0E, 0x5A, 0xED, 0xE8, 0x4B, 0x33, 0xA7, 0x51, 0x6C, 0xE3, 0x4E, 0xC4, 0xC7, 0};
+static const uint8_t characteristic_uuid[]   = {0x77, 0x72, 0xE5, 0xDB, 0x38, 0x68, 0x41, 0x12, 0xA1, 0xA9, 0xF2, 0x66, 0x9D, 0x10, 0x6B, 0xF3};
+
+static const uint8_t service_uuid_rev[] = {0, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51, 0xA7, 0x33, 0x4B, 0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03};
+
+uint8_t txPayload[TXRX_BUF_LEN] = {0,};
+
+
+GattCharacteristic  midiCharacteristic(characteristic_uuid, txPayload, 0, 20,GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE |
+                                                   GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE |
+                                                                   GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY |
+                                                                     GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
+
+GattCharacteristic *midiChars[] = {&midiCharacteristic};
+GattService BLEMIDIService(service_uuid, midiChars,sizeof(midiChars) / sizeof(GattCharacteristic *));
+ 
+ 
+                                
+/*********************************************************************************************************************
+MIDI to BLE-MIDI  
+*********************************************************************************************************************/
+
+                                
+#if ONLY_BLEMIDI_to_MIDI==0     
+               
+
+void sendBLEMIDI_Callback (void)
+{
+
+    sendBLEMIDI_flag = true ; 
+    
+    /**** This callback is called within an ISR with frequency set by the sendBLEMIDI_Ticker, because it is called in an Interrupt context it is preferably 
+    doing the MIDI to BLEMIDI task in the main, the callback only set a flag which would be checked in the main loop. 
+    This is because all the BLEMIDI to MIDI conversion is already performed within Interrupt context. ( thus as a consequence BLEMIDI to MIDI has higher priority )  
+    *****/
+}
+
+
+
+void sendBLEMIDI(void) 
+{
+
+ 
+// MIDI::MIDI_to_BLEMIDI_Parser() parses incoming MIDI events from the UART (see MIDI_BLEMIDI_Parser.h) 
+    if(isConnected == true && MIDI::MIDI_to_BLEMIDI_Parser()==true) {
+
+        // a valid MIDI message has been parsed from the UART buffer
+
+#if LIGHT_SHOW==1
+        greenled = !(greenled);
+#endif
+
+        uint8_t BLEmidi_out[SysExMaxSize] ;
+        uint8_t SysEx_Array_lenght ;
+
+        uint16_t ticks = t.read_us() & 0x1fff; // read timer for timestamps 
+
+        if(MIDI::mMessage.sysexArray[0] == MIDI::SystemExclusive)   { // message is  SysEx
+
+
+            SysEx_Array_lenght = MIDI::mMessage.getSysExSize() ; // get SysEx message lenght
+
+            uint8_t position = 0; // position for SysexArray 
+
+            // header
+           
+            BLEmidi_out[position++] = 0x80 | ((ticks >> 7) & 0x3f); // header & timestampHigh
+            BLEmidi_out[position++] = 0x80 | (ticks & 0x7f); // timestampLow
+
+            for (int i = 0; i < SysEx_Array_lenght; i++) {
+                if (i == SysEx_Array_lenght - 1) {
+                    // modify last byte
+                    BLEmidi_out[position++] = 0x80 | (ticks & 0x7f);
+
+                    if (position == 20) {
+                        
+                        
+                        ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out, 20);
+
+                        position = 0;
+                        // header
+                        BLEmidi_out[position++] = 0x80 | (ticks >> 7) & 0x3f;
+                    }
+                }
+                BLEmidi_out[position++] = MIDI::mMessage.sysexArray[i];
+                if (position == 20) {
+                  ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out, 20);
+                
+                    position = 0;
+                    // header
+                    BLEmidi_out[position++] = 0x80 | (ticks >> 7) & 0x3f;
+                }
+
+                ticks = t.read_us() & 0x1fff;
+            }
+
+            if (position > 0) {
+                // send remains
+             ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out, position);
+             
+            }
+            
+           
+            MIDI::mMessage.sysexArray[0]= 0 ; // reset
+
+        } 
+
+
+        // message is not SysEx
+        
+        
+        else {
+            
+            
+            if(MIDI::mMessage.data1 == 0 ) { // no data1 only status 
+            
+            BLEmidi_out[0] = 0x80 | ((ticks >> 7) & 0x3f);
+            BLEmidi_out[1] = 0x80 | (ticks & 0x7f);
+            BLEmidi_out[2] = MIDI::mMessage.channel+MIDI::mMessage.type;
+        
+             ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out , 3);
+            
+            }
+            
+            if(MIDI::mMessage.data2 == 0 ) { // no data2 
+            
+            BLEmidi_out[0] = 0x80 | ((ticks >> 7) & 0x3f);
+            BLEmidi_out[1] = 0x80 | (ticks & 0x7f);
+            BLEmidi_out[2] = MIDI::mMessage.channel+MIDI::mMessage.type;
+            BLEmidi_out[3] = MIDI::mMessage.data1 ;
+
+            ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out , 4);
+            
+            } 
+            
+            if(MIDI::mMessage.data2 != 0 ) { 
+            
+            BLEmidi_out[0] = 0x80 | ((ticks >> 7) & 0x3f);
+            BLEmidi_out[1] = 0x80 | (ticks & 0x7f);
+            BLEmidi_out[2] = MIDI::mMessage.channel+MIDI::mMessage.type;
+            BLEmidi_out[3] = MIDI::mMessage.data1 ;
+            BLEmidi_out[4] = MIDI::mMessage.data2 ;
+
+            ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out , 5);
+            
+            } 
+
+        }// end else
+
+    }// outer if
+
+// invalid message or not connected
+
+}
+
+#endif 
+
+
+/******************************************************************************************************************************
+* BLE CALLBACKS 
+******************************************************************************************************************************/
+ 
+                                
+void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) 
+{
+    //device disconnected 
+    
+    isConnected = false ; 
+    
+#if ONLY_BLEMIDI_to_MIDI==0   
+   sendBLEMIDI_Ticker.detach() ; // stop Ticker to save energy 
+#endif
+    
+    ble.startAdvertising(); // start advertising
+}
+
+
+void connectionCallback(const Gap::ConnectionCallbackParams_t* params) {
+
+
+isConnected=true ; 
+
+// try update conn.parameters 
+
+connectionParams.minConnectionInterval        = Config::minConnectionInterval;
+connectionParams.maxConnectionInterval        = Config::maxConnectionInterval;
+connectionParams.slaveLatency                 = Config::slaveLatency;
+connectionParams.connectionSupervisionTimeout = Config::supervisionTimeout;
+
+ble.updateConnectionParams(params->handle ,&connectionParams);
+
+
+//start timers here
+#if ONLY_BLEMIDI_to_MIDI==0    
+sendBLEMIDI_Ticker.attach( sendBLEMIDI_Callback, SENDBLEMIDI_INTERVAL); // every SENDBLEMIDI_INTERVAL seconds calls sendBLEMIDI_Callback (ISR)   
+
+t.start();  // start the timer used for BLEMIDI timestamps  
+
+#endif 
+
+} 
+
+
+/****************************************************************************************************************************
+BLE-MIDI to MIDI 
+****************************************************************************************************************************/
+
+
+#if ONLY_MIDI_to_BLEMIDI==0 
+
+void parseIncoming(uint8_t *buffer, uint16_t bytesRead) {  // parse BLE-MIDI Events that have been written on the MIDI Characteristic 
+  for (int i = 1; i < bytesRead; i++)
+  {
+    parseMidiEvent(buffer[0], buffer[i]); // parse and send through UART the MIDI Events received through BLE (see BLE_MIDI_Parser.h)
+  }
+}
+                                
+
+
+void onDataWritten(const GattWriteCallbackParams *Handler) // this functions is called within an ISR every time data has been written on nRF51822 GATT Server MIDI Characteristic   
+{
+
+#if LIGHT_SHOW==1
+    redled = !(redled) ;
+#endif
+
+    uint8_t buf[TXRX_BUF_LEN];
+    uint16_t bytesRead;
+    if (Handler->handle == midiCharacteristic.getValueAttribute().getHandle()) {
+        ble.readCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(),
+                                    buf, &bytesRead);
+        //parseIncoming(buf, bytesRead);
+       
+       for (int i = 0; i < bytesRead; i++){ 
+        UART.putc(buf[i]);
+ 
+        }     
+       
+    }
+
+}
+  
+#endif  
+
+
+/**************************************
+MAIN 
+***************************************/
+
+int main(void)
+{
+
+
+#if LIGHT_SHOW==1
+    redled = 1;
+    greenled = 1;
+#endif
+
+
+
+    UART.baud(31250) ; // set UART baud rate to 31250 (MIDI standard)
+
+    ble.init();
+    ble.onDisconnection(disconnectionCallback);
+    ble.onConnection(connectionCallback) ;
+
+#if ONLY_MIDI_to_BLEMIDI==0
+    ble.onDataWritten(onDataWritten);
+#endif
+    
+    //conn. parameters ( rejected on iOS/OSX  see Apple BLE peripheral design guidelines but can work on Android ) 
+    
+    connectionParams.minConnectionInterval        = Config::minConnectionInterval;
+    connectionParams.maxConnectionInterval        = Config::maxConnectionInterval;
+    connectionParams.slaveLatency                 = Config::slaveLatency;
+    connectionParams.connectionSupervisionTimeout = Config::supervisionTimeout;
+    ble.setPreferredConnectionParams(&connectionParams);
+    ble.getPreferredConnectionParams(&connectionParams);
+    
+    
+    /* setup advertising */
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
+                                     (const uint8_t *)"LLFX", sizeof("LLFX") - 1);
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
+                                     (const uint8_t *)service_uuid_rev, sizeof(service_uuid_rev));
+
+    ble.accumulateScanResponse(GapAdvertisingData::SHORTENED_LOCAL_NAME,
+                               (const uint8_t *)"LLFX", sizeof("LLFX") - 1);
+    ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,(const uint8_t *)service_uuid_rev, sizeof(service_uuid_rev));
+    ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+
+    /* 100ms; in multiples of 0.625ms. */
+    ble.setAdvertisingInterval(160);
+
+    // set adv_timeout, in seconds
+    ble.setAdvertisingTimeout(0);
+    ble.addService(BLEMIDIService);
+
+    //Set Device Name
+    ble.setDeviceName((const uint8_t *)"LLFX");
+
+    ble.startAdvertising();
+    
+  
+    while(1)   { //main loop
+
+#if ONLY_BLEMIDI_to_MIDI==0    
+            if(sendBLEMIDI_flag == true ) { // check if the flag is set 
+
+            sendBLEMIDI_flag=false ;
+            sendBLEMIDI() ; // parse MIDI Events from UART and send them over BLE
+
+        }
+#endif
+
+        ble.waitForEvent(); // sleep 
+
+    } // end main loop
+
+} // end main
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Mon Jun 12 17:12:31 2017 +0000
@@ -0,0 +1,1 @@
+https://mbed.org/users/mbed_official/code/mbed/builds/0f02307a0877
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF51822.lib	Mon Jun 12 17:12:31 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#c90ae1400bf2