IR sensors detect the movement of the pedal keys and Arduino Due generates MIDI messages.
Devices and components
Arduino due
Software and tools
Arduino IDE
Project description
1. The project
2. Starting point
3. Design
4. Wiring
MIDI signals from a pedal board
This code will turn an Arduino Due into a USB MIDI host, which will translate IR sensor signals into MIDI note events.
1#include <MIDIUSB.h>
2#include <ArduinoQueue.h>
3
4/*
5Program for MIDIfying a 30-key organ pedal board with Arduino Due as Midi controller and IR-sensors TCRT5000
6Original code by Prof. David Musker, June 2020
7Adapted by Johannes Barkowsky, April 2022
8Free for use if source is acknowledged
9*/
10
11//=================================================================
12const byte KeyboardSize = 30; // size of pedalboard here 30 keys, can be set to 27, 25 or whatever
13const byte StartNotePin = 2; // first digital input pin for note lowest note is set to pin 2, leaving digital pin 1 and 0 for other use
14 // so 30 pins now are pin3 to 32. StartNotePin can be set to 5 or whatever. It moves all pins up then.
15const byte NoteOff = B10000000;
16const byte NoteOn = B10010000;
17
18const byte MidiChannel = 3; // 3 sets Midi channel to channel 4; according to midi_Defs.h a 0 translates to MIDI_CHANNEL_OMNI, not sure though
19const byte LowestNote = 24; // Midi note number of lowest note, set to C1
20
21const byte KeyPin [KeyboardSize] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29};
22
23//=================================================================
24unsigned long KeyReadTime = 0;
25unsigned long DebounceTime = 20; // 20 ms in real life for notes - might be set lower
26unsigned long LastMIDISentTime; // track when last MIDI message sent
27unsigned long MidiLag = 50; // delay between MIDI messages - greater than 25 ms and less than say 500 ms to avoid latency
28
29byte NoteCount = 0; // notes on
30byte NoteCountOld = 0; // notes on at last scan
31byte NotesChanged = 0; // note changes from last scan
32byte Velocity;
33
34bool KeyState[KeyboardSize] = {true}; // orig. false, but set here to true, because IR-sensors are HIGH, when pedals are up
35bool NoteState[KeyboardSize] [3] = {false}; // current and last note states and a change flag
36unsigned long KeyLastTime [KeyboardSize] = {0}; // times since last key transitions
37
38//Queue creation:
39ArduinoQueue<midiEventPacket_t> MidiChannelNoteQueue(20); // 20 here is the maximum number of items
40
41//=================================================================
42void setup () {
43delay(1000); // remedy for Arduino Due R3 startup reset issue - perhaps unnecessary
44
45Serial.begin(31250); // MIDI transmission speed is 31250 baud
46 for (int i = 0; i < KeyboardSize; i++) {
47 pinMode((StartNotePin+i), INPUT_PULLUP); // digital input pins with pullup resistor
48 }
49}
50
51//=================================================================
52void loop () {
53
54KeyRead();
55NoteRead();
56NoteSend();
57TransmitMIDI();
58
59} //main loop ends here
60
61//=================================================================
62void KeyRead() {
63KeyReadTime = millis();
64 for (int i = 0; i < KeyboardSize; i++) { // note reading loop
65 KeyState[i] = !digitalRead(KeyPin[i]+StartNotePin); // indirect addressing via keypin array; keystate is here reversed (!), because IR-sensors are HIGH by default
66 }
67}
68
69//=================================================================
70void NoteRead() {
71NoteCountOld = NoteCount;
72NotesChanged = 0;
73 for (int i = 0; i < KeyboardSize; i++ ) {
74 if ((KeyReadTime - KeyLastTime [i]) > DebounceTime) { // only look at key state if it is more than DebounceTime since last change in note, otherwise skip
75 KeyLastTime [i] = KeyReadTime;
76 NoteState[i][3] = false;
77 NoteState[i][2] = NoteState[i][1]; // update previous note state
78 if (((KeyState[i] == false) && (NoteState[i][1] == true)) || ((KeyState[i] == true) && (NoteState[i][1] == false))){ // key state has changed
79 NoteState[i][1] = KeyState[i]; // update current note state
80 NoteState[i][3] = true; // set a changed state flag
81 NotesChanged++;
82 if (NoteState[i][1] == true) { // note is on
83 NoteCount++; // increment number of notes on
84 }
85 else {NoteCount--;} // decrement number of notes on
86 }
87 }
88 }
89}
90/*
91At the end of this loop, we have:
92NoteState[i][1] - current note states,
93NoteState[i][2] - previous note states, and
94NoteState[i][3] - change flags
95NoteCount - a count of the number of notes ON
96NotesChanged - a count of the number of notes changed
97*/
98
99//=================================================================
100void NoteSend() {
101if (NotesChanged > 0) { // something has changed
102 for (int i = 0; i < (KeyboardSize); i++) {
103 if (NoteState[i][3] == true){ // note state has changed
104 if (NoteState[i][1] == true){ // note is ON
105 sendNoteOn((i+ LowestNote), MidiChannel, 127); // velocity is here fixed at 127
106 }
107 else {
108 sendNoteOff((i+ LowestNote), MidiChannel, 0);} // note is OFF
109 }
110 }
111 }
112}
113
114//=================================================================
115void sendNoteOn (byte Pitch, byte MidiChannel, byte Velocity) {
116 MIDImessage(NoteOn, MidiChannel, Pitch, Velocity);
117}
118
119//=================================================================
120void sendNoteOff (byte Pitch, byte MidiChannel, byte Velocity) {
121 MIDImessage(NoteOff, MidiChannel, Pitch, Velocity);
122}
123
124//=================================================================
125void MIDImessage(int byte1, int byte2, int byte3, int byte4) { // sends a 3 byte MIDI message via the serial port and queues it
126 // one could use the Arduino Midi library command instead
127 byte cmd = (byte1 + byte2);
128 Serial.write(cmd); // send note on or note off command
129 Serial.write(byte3); // send pitch
130 Serial.write(byte4); //send velocity
131 midiEventPacket_t midiMsg = {cmd >> 4, cmd, byte3, byte4};
132 MidiChannelNoteQueue.enqueue(midiMsg);
133}
134
135//=================================================================
136void TransmitMIDI() {
137if ((micros() - LastMIDISentTime) > MidiLag) { // send only if the delay time has expired
138 if (!MidiChannelNoteQueue.isEmpty()) { // something is in the main channel queue
139 MidiUSB.sendMIDI(MidiChannelNoteQueue.dequeue());
140 MidiUSB.flush();
141 LastMIDISentTime = micros();
142 }
143 }
144}
145
Note: Content and images are from: https://projecthub.arduino.cc/, with some modifications.
If you want it removed due to copyright reasons, please leave a comment. Thank you.
I want to share this article more widely so that everyone knows about Arduino and your project.