BLE MIDI
Dependencies: BLE_API mbed nRF51822
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
Generated on Mon Aug 1 2022 10:22:45 by
1.7.2