BLE MIDI

Dependencies:   BLE_API mbed nRF51822

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*  
00002  *  Copyright (c) 2016 Samuele Cornell  
00003  *
00004  *  Permission is hereby granted, free of charge, to any person obtaining a copy
00005  *  of this software and associated documentation files (the "Software"), to deal
00006  *  in the Software without restriction, including without limitation the rights
00007  *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  *  copies of the Software, and to permit persons to whom the Software is
00009  *  furnished to do so, subject to the following conditions:
00010  *
00011  *  The above copyright notice and this permission notice shall be included in all
00012  *  copies or substantial portions of the Software.
00013  *
00014  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00019  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00020  *  SOFTWARE.
00021  *
00022  */
00023  
00024  /*
00025  *   Thanks to Matthias Frick and his project blidino https://github.com/sieren/blidino 
00026  *   which also uses RedBearslab nRF51822 and implements a USB-MIDI to BLE-MIDI bridge. 
00027  *   His work made this possible and served as the main inspiration for this project . 
00028  *   The content of BLEMIDI_MIDI_Parser.h file is for the most part taken from his blidino project.  
00029  */
00030 
00031 #include "mbed.h"
00032 
00033 #include "ble/BLE.h"
00034 
00035 #include "LEDService.h"
00036 
00037 Serial UART(UART_TX_PIN,UART_RX_PIN) ; //UART_RX_PIN,BUFSERIAL_LENGHT  
00038 
00039 #if ONLY_MIDI_to_BLEMIDI==0 
00040 
00041 #include "BLEMIDI_MIDI_Parser.h" 
00042 
00043 #endif
00044 
00045 #if ONLY_BLEMIDI_to_MIDI==0   
00046 
00047 #include "MIDI_BLEMIDI_Parser.h"
00048 
00049 #endif
00050 
00051 
00052 
00053 #define TXRX_BUF_LEN                    20 
00054 
00055 #define RX_BUF_LEN                      256 
00056 
00057 
00058 #if LIGHT_SHOW==1
00059 DigitalOut redled(P0_20);  // sets the two LEDS on the MIDI Shield as outputs, these will be used as a visual feedback for debug 
00060 DigitalOut greenled(P0_19); //
00061 #endif 
00062 
00063 
00064 /******************************************************************************************************************************
00065 *INITIALIZE VARIABLES, BUFFERS and BLE-MIDI Service and Characteristic
00066 ******************************************************************************************************************************/
00067 
00068 Ticker sendBLEMIDI_Ticker ; 
00069 Ticker sendData_Ticker ; 
00070  
00071 Timer t; // timer used for BLE-MIDI timestamps 
00072 
00073 bool isConnected; 
00074 
00075 
00076 ////////////////////////////////////////////
00077 bool sendBLEMIDI_flag = false ; 
00078 
00079 ////////////
00080 
00081 
00082 BLEDevice ble; // BLE_API  
00083 
00084 static Gap::ConnectionParams_t connectionParams;
00085 
00086 
00087 // MIDI BLE Service and Characteristic UUID ( see Apple BLE-MIDI Spec. and Midi manufacter Association BLE-MIDI Spec.) 
00088 
00089 static const uint8_t service_uuid[] = {0x03, 0xB8, 0x0E, 0x5A, 0xED, 0xE8, 0x4B, 0x33, 0xA7, 0x51, 0x6C, 0xE3, 0x4E, 0xC4, 0xC7, 0};
00090 static const uint8_t characteristic_uuid[]   = {0x77, 0x72, 0xE5, 0xDB, 0x38, 0x68, 0x41, 0x12, 0xA1, 0xA9, 0xF2, 0x66, 0x9D, 0x10, 0x6B, 0xF3};
00091 
00092 static const uint8_t service_uuid_rev[] = {0, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51, 0xA7, 0x33, 0x4B, 0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03};
00093 
00094 uint8_t txPayload[TXRX_BUF_LEN] = {0,};
00095 
00096 
00097 GattCharacteristic  midiCharacteristic(characteristic_uuid, txPayload, 0, 20,GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE |
00098                                                    GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE |
00099                                                                    GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY |
00100                                                                      GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
00101 
00102 GattCharacteristic *midiChars[] = {&midiCharacteristic};
00103 GattService BLEMIDIService(service_uuid, midiChars,sizeof(midiChars) / sizeof(GattCharacteristic *));
00104  
00105  
00106                                 
00107 /*********************************************************************************************************************
00108 MIDI to BLE-MIDI  
00109 *********************************************************************************************************************/
00110 
00111                                 
00112 #if ONLY_BLEMIDI_to_MIDI==0     
00113                
00114 
00115 void sendBLEMIDI_Callback (void)
00116 {
00117 
00118     sendBLEMIDI_flag = true ; 
00119     
00120     /**** 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 
00121     doing the MIDI to BLEMIDI task in the main, the callback only set a flag which would be checked in the main loop. 
00122     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 )  
00123     *****/
00124 }
00125 
00126 
00127 
00128 void sendBLEMIDI(void) 
00129 {
00130 
00131  
00132 // MIDI::MIDI_to_BLEMIDI_Parser() parses incoming MIDI events from the UART (see MIDI_BLEMIDI_Parser.h) 
00133     if(isConnected == true && MIDI::MIDI_to_BLEMIDI_Parser()==true) {
00134 
00135         // a valid MIDI message has been parsed from the UART buffer
00136 
00137 #if LIGHT_SHOW==1
00138         greenled = !(greenled);
00139 #endif
00140 
00141         uint8_t BLEmidi_out[SysExMaxSize] ;
00142         uint8_t SysEx_Array_lenght ;
00143 
00144         uint16_t ticks = t.read_us() & 0x1fff; // read timer for timestamps 
00145 
00146         if(MIDI::mMessage.sysexArray[0] == MIDI::SystemExclusive)   { // message is  SysEx
00147 
00148 
00149             SysEx_Array_lenght = MIDI::mMessage.getSysExSize() ; // get SysEx message lenght
00150 
00151             uint8_t position = 0; // position for SysexArray 
00152 
00153             // header
00154            
00155             BLEmidi_out[position++] = 0x80 | ((ticks >> 7) & 0x3f); // header & timestampHigh
00156             BLEmidi_out[position++] = 0x80 | (ticks & 0x7f); // timestampLow
00157 
00158             for (int i = 0; i < SysEx_Array_lenght; i++) {
00159                 if (i == SysEx_Array_lenght - 1) {
00160                     // modify last byte
00161                     BLEmidi_out[position++] = 0x80 | (ticks & 0x7f);
00162 
00163                     if (position == 20) {
00164                         
00165                         
00166                         ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out, 20);
00167 
00168                         position = 0;
00169                         // header
00170                         BLEmidi_out[position++] = 0x80 | (ticks >> 7) & 0x3f;
00171                     }
00172                 }
00173                 BLEmidi_out[position++] = MIDI::mMessage.sysexArray[i];
00174                 if (position == 20) {
00175                   ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out, 20);
00176                 
00177                     position = 0;
00178                     // header
00179                     BLEmidi_out[position++] = 0x80 | (ticks >> 7) & 0x3f;
00180                 }
00181 
00182                 ticks = t.read_us() & 0x1fff;
00183             }
00184 
00185             if (position > 0) {
00186                 // send remains
00187              ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out, position);
00188              
00189             }
00190             
00191            
00192             MIDI::mMessage.sysexArray[0]= 0 ; // reset
00193 
00194         } 
00195 
00196 
00197         // message is not SysEx
00198         
00199         
00200         else {
00201             
00202             
00203             if(MIDI::mMessage.data1 == 0 ) { // no data1 only status 
00204             
00205             BLEmidi_out[0] = 0x80 | ((ticks >> 7) & 0x3f);
00206             BLEmidi_out[1] = 0x80 | (ticks & 0x7f);
00207             BLEmidi_out[2] = MIDI::mMessage.channel+MIDI::mMessage.type;
00208         
00209              ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out , 3);
00210             
00211             }
00212             
00213             if(MIDI::mMessage.data2 == 0 ) { // no data2 
00214             
00215             BLEmidi_out[0] = 0x80 | ((ticks >> 7) & 0x3f);
00216             BLEmidi_out[1] = 0x80 | (ticks & 0x7f);
00217             BLEmidi_out[2] = MIDI::mMessage.channel+MIDI::mMessage.type;
00218             BLEmidi_out[3] = MIDI::mMessage.data1 ;
00219 
00220             ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out , 4);
00221             
00222             } 
00223             
00224             if(MIDI::mMessage.data2 != 0 ) { 
00225             
00226             BLEmidi_out[0] = 0x80 | ((ticks >> 7) & 0x3f);
00227             BLEmidi_out[1] = 0x80 | (ticks & 0x7f);
00228             BLEmidi_out[2] = MIDI::mMessage.channel+MIDI::mMessage.type;
00229             BLEmidi_out[3] = MIDI::mMessage.data1 ;
00230             BLEmidi_out[4] = MIDI::mMessage.data2 ;
00231 
00232             ble.updateCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(), BLEmidi_out , 5);
00233             
00234             } 
00235 
00236         }// end else
00237 
00238     }// outer if
00239 
00240 // invalid message or not connected
00241 
00242 }
00243 
00244 #endif 
00245 
00246 
00247 /******************************************************************************************************************************
00248 * BLE CALLBACKS 
00249 ******************************************************************************************************************************/
00250  
00251                                 
00252 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) 
00253 {
00254     //device disconnected 
00255     
00256     isConnected = false ; 
00257     
00258 #if ONLY_BLEMIDI_to_MIDI==0   
00259    sendBLEMIDI_Ticker.detach() ; // stop Ticker to save energy 
00260 #endif
00261     
00262     ble.startAdvertising(); // start advertising
00263 }
00264 
00265 
00266 void connectionCallback(const Gap::ConnectionCallbackParams_t* params) {
00267 
00268 
00269 isConnected=true ; 
00270 
00271 // try update conn.parameters 
00272 
00273 connectionParams.minConnectionInterval        = Config::minConnectionInterval;
00274 connectionParams.maxConnectionInterval        = Config::maxConnectionInterval;
00275 connectionParams.slaveLatency                 = Config::slaveLatency;
00276 connectionParams.connectionSupervisionTimeout = Config::supervisionTimeout;
00277 
00278 ble.updateConnectionParams(params->handle ,&connectionParams);
00279 
00280 
00281 //start timers here
00282 #if ONLY_BLEMIDI_to_MIDI==0    
00283 sendBLEMIDI_Ticker.attach( sendBLEMIDI_Callback, SENDBLEMIDI_INTERVAL); // every SENDBLEMIDI_INTERVAL seconds calls sendBLEMIDI_Callback (ISR)   
00284 
00285 t.start();  // start the timer used for BLEMIDI timestamps  
00286 
00287 #endif 
00288 
00289 } 
00290 
00291 
00292 /****************************************************************************************************************************
00293 BLE-MIDI to MIDI 
00294 ****************************************************************************************************************************/
00295 
00296 
00297 #if ONLY_MIDI_to_BLEMIDI==0 
00298 
00299 void parseIncoming(uint8_t *buffer, uint16_t bytesRead) {  // parse BLE-MIDI Events that have been written on the MIDI Characteristic 
00300   for (int i = 1; i < bytesRead; i++)
00301   {
00302     parseMidiEvent(buffer[0], buffer[i]); // parse and send through UART the MIDI Events received through BLE (see BLE_MIDI_Parser.h)
00303   }
00304 }
00305                                 
00306 
00307 
00308 void onDataWritten(const GattWriteCallbackParams *Handler) // this functions is called within an ISR every time data has been written on nRF51822 GATT Server MIDI Characteristic   
00309 {
00310 
00311 #if LIGHT_SHOW==1
00312     redled = !(redled) ;
00313 #endif
00314 
00315     uint8_t buf[TXRX_BUF_LEN];
00316     uint16_t bytesRead;
00317     if (Handler->handle == midiCharacteristic.getValueAttribute().getHandle()) {
00318         ble.readCharacteristicValue(midiCharacteristic.getValueAttribute().getHandle(),
00319                                     buf, &bytesRead);
00320         //parseIncoming(buf, bytesRead);
00321        
00322        for (int i = 0; i < bytesRead; i++){ 
00323         UART.putc(buf[i]);
00324  
00325         }     
00326        
00327     }
00328 
00329 }
00330   
00331 #endif  
00332 
00333 
00334 /**************************************
00335 MAIN 
00336 ***************************************/
00337 
00338 int main(void)
00339 {
00340 
00341 
00342 #if LIGHT_SHOW==1
00343     redled = 1;
00344     greenled = 1;
00345 #endif
00346 
00347 
00348 
00349     UART.baud(31250) ; // set UART baud rate to 31250 (MIDI standard)
00350 
00351     ble.init();
00352     ble.onDisconnection(disconnectionCallback);
00353     ble.onConnection(connectionCallback) ;
00354 
00355 #if ONLY_MIDI_to_BLEMIDI==0
00356     ble.onDataWritten(onDataWritten);
00357 #endif
00358     
00359     //conn. parameters ( rejected on iOS/OSX  see Apple BLE peripheral design guidelines but can work on Android ) 
00360     
00361     connectionParams.minConnectionInterval        = Config::minConnectionInterval;
00362     connectionParams.maxConnectionInterval        = Config::maxConnectionInterval;
00363     connectionParams.slaveLatency                 = Config::slaveLatency;
00364     connectionParams.connectionSupervisionTimeout = Config::supervisionTimeout;
00365     ble.setPreferredConnectionParams(&connectionParams);
00366     ble.getPreferredConnectionParams(&connectionParams);
00367     
00368     
00369     /* setup advertising */
00370     ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED);
00371     ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME,
00372                                      (const uint8_t *)"LLFX", sizeof("LLFX") - 1);
00373     ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
00374                                      (const uint8_t *)service_uuid_rev, sizeof(service_uuid_rev));
00375 
00376     ble.accumulateScanResponse(GapAdvertisingData::SHORTENED_LOCAL_NAME,
00377                                (const uint8_t *)"LLFX", sizeof("LLFX") - 1);
00378     ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,(const uint8_t *)service_uuid_rev, sizeof(service_uuid_rev));
00379     ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00380 
00381     /* 100ms; in multiples of 0.625ms. */
00382     ble.setAdvertisingInterval(160);
00383 
00384     // set adv_timeout, in seconds
00385     ble.setAdvertisingTimeout(0);
00386     ble.addService(BLEMIDIService);
00387 
00388     //Set Device Name
00389     ble.setDeviceName((const uint8_t *)"LLFX");
00390 
00391     ble.startAdvertising();
00392     
00393   
00394     while(1)   { //main loop
00395 
00396 #if ONLY_BLEMIDI_to_MIDI==0    
00397             if(sendBLEMIDI_flag == true ) { // check if the flag is set 
00398 
00399             sendBLEMIDI_flag=false ;
00400             sendBLEMIDI() ; // parse MIDI Events from UART and send them over BLE
00401 
00402         }
00403 #endif
00404 
00405         ble.waitForEvent(); // sleep 
00406 
00407     } // end main loop
00408 
00409 } // end main