|
14 | 14 | #include <bluefruit.h>
|
15 | 15 | #include <MIDI.h>
|
16 | 16 |
|
17 |
| -// To test: |
18 |
| -// - Run this sketch and open the Serial Monitor |
19 |
| -// - Open the iGrand Piano Free app |
20 |
| -// - Open the midimittr app on your phone and under Clients select "Bluefruit52" |
21 |
| -// - When you see the 'Connected' label switch to the Routing panel |
22 |
| -// - Set the Destination to 'iGrand Piano' |
23 |
| -// - Switch to the iGrand Piano Free app and you should see notes playing one by one |
24 |
| - |
25 | 17 | BLEDis bledis;
|
26 | 18 | BLEMidi blemidi;
|
27 | 19 |
|
28 |
| -// Create MIDI instance using ble service as physical layer |
| 20 | +// Create a new instance of the Arduino MIDI Library, |
| 21 | +// and attach BluefruitLE MIDI as the transport. |
29 | 22 | MIDI_CREATE_BLE_INSTANCE(blemidi);
|
30 | 23 |
|
| 24 | +// Variable that holds the current position in the sequence. |
| 25 | +int position = 0; |
| 26 | + |
| 27 | +// Store example melody as an array of note values |
| 28 | +byte note_sequence[] = { |
| 29 | + 74,78,81,86,90,93,98,102,57,61,66,69,73,78,81,85,88,92,97,100,97,92,88,85,81,78, |
| 30 | + 74,69,66,62,57,62,66,69,74,78,81,86,90,93,97,102,97,93,90,85,81,78,73,68,64,61, |
| 31 | + 56,61,64,68,74,78,81,86,90,93,98,102 |
| 32 | +}; |
| 33 | + |
| 34 | +// Variable to hold the last time we sent a note |
| 35 | +unsigned long previousSend = 0; |
| 36 | + |
31 | 37 | void setup()
|
32 | 38 | {
|
| 39 | + |
33 | 40 | Serial.begin(115200);
|
34 |
| - Serial.println("Bluefruit52 BLEMIDI Example"); |
| 41 | + Serial.println("Adafruit Bluefruit52 MIDI over Bluetooth LE Example"); |
35 | 42 |
|
36 | 43 | Bluefruit.begin();
|
37 |
| - Bluefruit.setName("Bluefruit52"); |
| 44 | + Bluefruit.setName("Bluefruit52 MIDI"); |
38 | 45 |
|
39 |
| - // Setup the BLE LED to be enabled on CONNECT |
40 |
| - // Note: This is actually the default behaviour, but provided |
41 |
| - // here in case you want to control this manually via PIN 19 |
42 |
| - Bluefruit.autoConnLed(true); |
43 |
| - |
44 |
| - Bluefruit.setConnectCallback(connect_callback); |
45 |
| - Bluefruit.setDisconnectCallback(disconnect_callback); |
46 |
| - |
| 46 | + // Setup the on board blue LED to be enabled on CONNECT |
| 47 | + Bluefruit.autoConnLed(true); |
47 | 48 |
|
48 | 49 | // Configure and Start Device Information Service
|
49 | 50 | bledis.setManufacturer("Adafruit Industries");
|
50 | 51 | bledis.setModel("Bluefruit Feather52");
|
51 | 52 | bledis.begin();
|
52 |
| - |
53 |
| - /* Start BLE MIDI |
54 |
| - * Note: Apple requires BLE device must have min connection interval >= 20m |
55 |
| - * ( The smaller the connection interval the faster we could send data). |
56 |
| - * However for HID and MIDI device, Apple could accept min connection interval |
57 |
| - * up to 11.25 ms. Therefore BLEMidi::start() will try to set the min and max |
58 |
| - * connection interval to 11.25 ms and 15 ms respectively for best performance. |
59 |
| - */ |
60 |
| - blemidi.setWriteCallback(midi_write_callback); |
61 |
| - blemidi.begin(); |
62 |
| - |
63 |
| - // Initialize MIDI, listen to all channels |
| 53 | + |
| 54 | + // Initialize MIDI, and listen to all MIDI channels |
64 | 55 | MIDI.begin(MIDI_CHANNEL_OMNI);
|
65 | 56 |
|
66 |
| - // Set up Advertising Packet |
67 |
| - setupAdv(); |
| 57 | + // Attach the handleNoteOn function to the MIDI Library. It will |
| 58 | + // be called whenever the Bluefruit receives MIDI Note On messages. |
| 59 | + MIDI.setHandleNoteOn(handleNoteOn); |
68 | 60 |
|
69 |
| - // Start Advertising |
70 |
| - Bluefruit.Advertising.start(); |
71 |
| -} |
| 61 | + // Do the same for MIDI Note Off messages. |
| 62 | + MIDI.setHandleNoteOff(handleNoteOff); |
72 | 63 |
|
73 |
| -void setupAdv(void) |
74 |
| -{ |
75 |
| - //Bluefruit.Advertising.addTxPower(); |
| 64 | + // Set General Discoverable Mode flag |
76 | 65 | Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
| 66 | + |
| 67 | + // Advertise TX Power |
77 | 68 | Bluefruit.Advertising.addTxPower();
|
78 | 69 |
|
79 |
| - // Include bleuart 128-bit uuid |
| 70 | + // Advertise BLE MIDI Service |
80 | 71 | Bluefruit.Advertising.addService(blemidi);
|
81 | 72 |
|
82 |
| - // There is no room for Name in Advertising packet |
83 |
| - // Use Scan response for Name |
| 73 | + // Advertise device name in the Scan Response |
84 | 74 | Bluefruit.ScanResponse.addName();
|
85 |
| -} |
86 | 75 |
|
87 |
| -void midi_write_callback(void) |
88 |
| -{ |
89 |
| - MIDI.read(); |
| 76 | + // Start Advertising |
| 77 | + Bluefruit.Advertising.start(); |
| 78 | + |
90 | 79 | }
|
91 | 80 |
|
92 |
| -void loop() |
| 81 | +void handleNoteOn(byte channel, byte pitch, byte velocity) |
93 | 82 | {
|
94 |
| - if ( Bluefruit.connected() && blemidi.notifyEnabled() ) |
95 |
| - { |
96 |
| - MIDI.sendNoteOn(42, 127, 1); // Send a Note (pitch 42, velo 127 on channel 1) |
97 |
| - delay(1000); // Wait for a second |
98 |
| - MIDI.sendNoteOff(42, 0, 1); // Stop the note |
99 |
| - } |
| 83 | + // Log when a note is pressed. |
| 84 | + Serial.printf("Note on: channel = %d, pitch = %d, velocity - %d", channel, pitch, velocity); |
| 85 | + Serial.println(); |
100 | 86 | }
|
101 | 87 |
|
102 |
| -void connect_callback(void) |
| 88 | +void handleNoteOff(byte channel, byte pitch, byte velocity) |
103 | 89 | {
|
104 |
| - Serial.println("Connected"); |
| 90 | + // Log when a note is released. |
| 91 | + Serial.printf("Note off: channel = %d, pitch = %d, velocity - %d", channel, pitch, velocity); |
| 92 | + Serial.println(); |
105 | 93 | }
|
106 | 94 |
|
107 |
| -void disconnect_callback(uint8_t reason) |
| 95 | +void loop() |
108 | 96 | {
|
109 |
| - (void) reason; |
110 |
| - |
111 |
| - Serial.println("Disconnected"); |
112 |
| - Serial.println("Bluefruit will auto start advertising (default)"); |
| 97 | + |
| 98 | + // Don't continue if we aren't connected. |
| 99 | + if (! Bluefruit.connected()) { |
| 100 | + return; |
| 101 | + } |
| 102 | + |
| 103 | + // Don't continue if the connected device isn't ready to receive messages. |
| 104 | + if (! blemidi.notifyEnabled()) { |
| 105 | + return; |
| 106 | + } |
| 107 | + |
| 108 | + // read any new MIDI messages |
| 109 | + MIDI.read(); |
| 110 | + |
| 111 | + // Store the current time |
| 112 | + unsigned long now = millis(); |
| 113 | + |
| 114 | + // Check if enough time has passed since last send |
| 115 | + if (now - previousSend < 286) { |
| 116 | + return; |
| 117 | + } |
| 118 | + |
| 119 | + // Setup variables for the current and previous |
| 120 | + // positions in the note sequence. |
| 121 | + int current = position; |
| 122 | + int previous = position - 1; |
| 123 | + |
| 124 | + // If we currently are at position 0, set the |
| 125 | + // previous position to the last note in the sequence. |
| 126 | + if (previous < 0) { |
| 127 | + previous = sizeof(note_sequence) - 1; |
| 128 | + } |
| 129 | + |
| 130 | + // Send Note On for current position at full velocity (127) on channel 1. |
| 131 | + MIDI.sendNoteOn(note_sequence[current], 127, 1); |
| 132 | + |
| 133 | + // Send Note Off for previous note. |
| 134 | + MIDI.sendNoteOff(note_sequence[previous], 0, 1); |
| 135 | + |
| 136 | + // Increment position |
| 137 | + position++; |
| 138 | + |
| 139 | + // If we are at the end of the sequence, start over. |
| 140 | + if (position >= sizeof(note_sequence)) { |
| 141 | + position = 0; |
| 142 | + } |
| 143 | + |
| 144 | + // Log the send time |
| 145 | + previousSend = now; |
| 146 | + |
113 | 147 | }
|
0 commit comments