|
| 1 | +/* Written by Oren Levy (auxren.com; @auxren) while competing on |
| 2 | + America's Greatest Makers with help from Intel. |
| 3 | + MIDI over BLE info from: https://developer.apple.com/bluetooth/Apple-Bluetooth-Low-Energy-MIDI-Specification.pdf |
| 4 | +
|
| 5 | + This sketch plays a random MIDI note (between 0 and 127) every 400ms. |
| 6 | + For a 'smarter' sketch, check out my Airpeggiator example. |
| 7 | + The Airpeggiator uses the Curie's IMU to allow you to play |
| 8 | + an imaginary harp in the air. I included a quantizer so you can |
| 9 | + select a key and scale so you can jam along with friends. |
| 10 | + https://github.com/auxren/MIDIBLE101/tree/master/Airpeggiator |
| 11 | +
|
| 12 | + I have only tested MIDI over BLE using Apple devices. Android doesn't |
| 13 | + support native MIDI over BLE yet and I haven't had much of a chance |
| 14 | + to test with Windows machines. |
| 15 | +
|
| 16 | + To connect on a Mac, search for Audio MIDI Setup. |
| 17 | + Click 'Window' on the top menu and choose 'Show MIDI Studio'. |
| 18 | + Double click 'Bluetooth' and the bluetooth configuration window |
| 19 | + will pop up. After loading the MIDIBLE sketch on your Arduino 101 |
| 20 | + you should see it advertising as Auxren. Click connect and the device |
| 21 | + will be available as MIDI device in all your audio software like Garageband. |
| 22 | +
|
| 23 | + There are a few ways to connect using an iOS device. One way to to open |
| 24 | + up Garageband. Click on the wrench icon in the upper right and choose 'Advanced' |
| 25 | + Towards the bottom of advanced, you will see 'Bluetooth MIDI devices'. |
| 26 | + You should see your Arduino 101 advertising in the list. Connect to |
| 27 | + your device and it should be available to all other iOS MIDI apps you have. |
| 28 | +
|
| 29 | + To send data, you use the following line: char.setValue(d, n); where char is |
| 30 | + the BLE characteristic (in our case, midiCha), d is the data, and n is the |
| 31 | + number of bytes of data. |
| 32 | + The first 2 bytes of data are the header byte and timestamp byte. If you want, |
| 33 | + you can figure out the timestamping scheme, but I just left it with a generic value |
| 34 | + since I haven't worked on anything timeing sensitive yet (like a sequencer). |
| 35 | + The third, fourth, and fifth bytes are standard MIDI bytes. You can send more bytes |
| 36 | + if you would like as long as it is complies to the standard MIDI spec. |
| 37 | +
|
| 38 | + The MIT License (MIT) |
| 39 | +
|
| 40 | + Permission is hereby granted, free of charge, to any person obtaining a copy |
| 41 | + of this software and associated documentation files (the "Software"), to deal |
| 42 | + in the Software without restriction, including without limitation the rights |
| 43 | + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 44 | + copies of the Software, and to permit persons to whom the Software is |
| 45 | + furnished to do so, subject to the following conditions: |
| 46 | +
|
| 47 | + The above copyright notice and this permission notice shall be included in all |
| 48 | + copies or substantial portions of the Software. |
| 49 | +
|
| 50 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 51 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 52 | + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 53 | + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 54 | + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 55 | + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 56 | + SOFTWARE. |
| 57 | +
|
| 58 | +*/ |
| 59 | +#include <CurieBLE.h> |
| 60 | + |
| 61 | +#define TXRX_BUF_LEN 20 //max number of bytes |
| 62 | +#define RX_BUF_LEN 20 //max number of bytes |
| 63 | +uint8_t rx_buf[RX_BUF_LEN]; |
| 64 | +int rx_buf_num, rx_state = 0; |
| 65 | +uint8_t rx_temp_buf[20]; |
| 66 | +uint8_t outBufMidi[128]; |
| 67 | + |
| 68 | +//Buffer to hold 5 bytes of MIDI data. Note the timestamp is forced |
| 69 | +uint8_t midiData[] = {0x80, 0x80, 0x00, 0x00, 0x00}; |
| 70 | + |
| 71 | +//Loads up buffer with values for note On |
| 72 | +void noteOn(char chan, char note, char vel) //channel 1 |
| 73 | +{ |
| 74 | + midiData[2] = 0x90 + chan; |
| 75 | + midiData[3] = note; |
| 76 | + midiData[4] = vel; |
| 77 | +} |
| 78 | + |
| 79 | +//Loads up buffer with values for note Off |
| 80 | +void noteOff(char chan, char note) //channel 1 |
| 81 | +{ |
| 82 | + midiData[2] = 0x80 + chan; |
| 83 | + midiData[3] = note; |
| 84 | + midiData[4] = 0; |
| 85 | +} |
| 86 | + |
| 87 | +BLEPeripheral midiDevice; // create peripheral instance |
| 88 | + |
| 89 | +BLEService midiSvc("03B80E5A-EDE8-4B33-A751-6CE34EC4C700"); // create service |
| 90 | + |
| 91 | +// create switch characteristic and allow remote device to read and write |
| 92 | +BLECharacteristic midiChar("7772E5DB-3868-4112-A1A9-F2669D106BF3", BLEWrite | BLEWriteWithoutResponse | BLENotify | BLERead, 5); |
| 93 | + |
| 94 | +void setup() { |
| 95 | + Serial.begin(9600); |
| 96 | + |
| 97 | + BLESetup(); |
| 98 | + Serial.println(("Bluetooth device active, waiting for connections...")); |
| 99 | +} |
| 100 | + |
| 101 | +void BLESetup() |
| 102 | +{ |
| 103 | + // set the local name peripheral advertises |
| 104 | + midiDevice.setLocalName("Auxren"); |
| 105 | + midiDevice.setDeviceName("Auxren"); |
| 106 | + |
| 107 | + // set the UUID for the service this peripheral advertises |
| 108 | + midiDevice.setAdvertisedServiceUuid(midiSvc.uuid()); |
| 109 | + |
| 110 | + // add service and characteristic |
| 111 | + midiDevice.addAttribute(midiSvc); |
| 112 | + midiDevice.addAttribute(midiChar); |
| 113 | + |
| 114 | + // assign event handlers for connected, disconnected to peripheral |
| 115 | + midiDevice.setEventHandler(BLEConnected, midiDeviceConnectHandler); |
| 116 | + midiDevice.setEventHandler(BLEDisconnected, midiDeviceDisconnectHandler); |
| 117 | + |
| 118 | + // assign event handlers for characteristic |
| 119 | + midiChar.setEventHandler(BLEWritten, midiCharacteristicWritten); |
| 120 | + // set an initial value for the characteristic |
| 121 | + midiChar.setValue(midiData, 5); |
| 122 | + |
| 123 | + // advertise the service |
| 124 | + midiDevice.begin(); |
| 125 | +} |
| 126 | + |
| 127 | +void loop() { |
| 128 | + |
| 129 | + /*Simple randome note player to test MIDI output |
| 130 | + Plays random note every 400ms |
| 131 | + */ |
| 132 | + int note = random(0, 127); |
| 133 | + //readMIDI(); |
| 134 | + noteOn(0, note, 127); //loads up midiData buffer |
| 135 | + midiChar.setValue(midiData, 5);//midiData); //posts 5 bytes |
| 136 | + delay(200); |
| 137 | + noteOff(0, note); |
| 138 | + midiChar.setValue(midiData, 5);//midiData); //posts 5 bytes |
| 139 | + delay(200); |
| 140 | +} |
| 141 | + |
| 142 | + |
| 143 | +void midiDeviceConnectHandler(BLECentral& central) { |
| 144 | + // central connected event handler |
| 145 | + Serial.print("Connected event, central: "); |
| 146 | + Serial.println(central.address()); |
| 147 | +} |
| 148 | + |
| 149 | +void midiDeviceDisconnectHandler(BLECentral& central) { |
| 150 | + // central disconnected event handler |
| 151 | + Serial.print("Disconnected event, central: "); |
| 152 | + Serial.println(central.address()); |
| 153 | +} |
| 154 | + |
| 155 | +void midiCharacteristicWritten(BLECentral& central, BLECharacteristic& characteristic) { |
| 156 | + // central wrote new value to characteristic, update LED |
| 157 | + Serial.print("Characteristic event, written: "); |
| 158 | +} |
0 commit comments