MIDI controller to Arduino to Relays Help

Hello, I sure could use a helping hand. Arduino code is Greek to me and although I’ve managed to decipher some things I’m still far from where I need to be.

Here’s my project in a nutshell… I’m in a band and my drummer triggers sound effects at various times through an Alesis Samplepad which has four drum pads on it and a MIDI out port. I want a stage prop to be actuated in sync with the sound effect.

The prop is a cannon equipped with a bright light and a fog machine to simulate a cannon shot. I have a grip on the 120v side of things but where I need help is in controlling the relays the way I need to.

I’ve gotten pretty far along but I still need a little help. Here’s my current status…. I have a MIDI shield, an Arduino Uno and an eight channel relay board. (see links below) This is my first attempt at any sort of Arduino project so keep in mind that I’m pretty new to this and I’ve been trying to feel my way through it. In hindsight I should have just built the MIDI input circuit because at this point I’m not able to plug the MIDI shield into the Arduino anyway but I’ve managed to make it work

I’ve done a lot of experimenting and trial and error and I’ve managed to get everything communicating somewhat. I’m currently using a sketch someone wrote to control a homemade MIDI Glockenspiel.

MIDI Glockenspiel Sketch

Video of someone using this sketch as I did with the keyboard.

Initially I used a MIDI keybord as a controller instead of the Alesis Samplepad and eventually I got it all working as well as possible with the sketch I’m using. With the keyboard I’m able to hit the white keys C5 through C6 and trigger each of the eight relays. I know that’s a lot of detail but I want you to know that my setup is working the way it should with regards to the sketch I’m using and that everything is connected properly.

My current sketch is giving me something close to what I need but it needs amended or replaced all together. As it is now, when I send a note on message with a key the relay actuates and holds in as long as I hold the key down and then releases when I release the key.

I need to be able to send a single note on message from the Alesis drum pad and automatically hold the relay on for a length of time ie: 1 to 3 seconds before releasing it. From what I understand I should be able to change a number in the code to indicate how many milliseconds I want it to hold in.

I may also want to hold either the light or the fog machine on for slightly different times to adjust the effect. In this case I would need to be able to assign two relays to one drum pad but control their durations separately. (Not sure if this can be done but it would be ideal)

So to sum it all up… Drummer hits pad – Sound goes to PA system – MIDI “Note On” message goes to Arduino – Arduino tells relay 1 to actuate for an adjustable duration - At the same time Arduino tells relay 2 to actuate for its own adjustable duration.

There should be plenty of time between hits especially since there are currently two cannons. Yes, relays 3&4 would be for cannon 2 and relays 5-8 could be for adding two more cannon props in the future.

Each of the four drum pads can be assigned one MIDI note number and the samplepad itself is fixed at MIDI channel 10.

I have replaced the keyboard with the Alesis Sample pad and I managed to change the code to reflect the fact that the Samplepad is set for MIDI channel 10. I have also programed each pad to its own MIDI note number 60, 62, 64 and 65 which corresponds notes C5, D5, E5 and F5.

I have one problem with the Samplepad communicating. When I hit a pad the pin 13 led on the Arduino blinks but it’s very dim and the relay does not operate. Each pad has a sensitivity setting of 1 to 8 and all of them are set to 8. I’ve tried it at all levels and the LED is still dim like it’s not a strong enough signal. I hope that’s something that can be adjusted in the code.

Here’s some info from the Samplepad’s MIDI Implementation Chart: Basic Channel – 10 Mode Messages – 0-127 Altered - ************** Velocity Note On – 99H,V=1 -127 Note Off - (99H,V=0)

I realize the sketch I’m using probably has many things I don’t need and lacks several things I do need. I’ve searched high and low and I can’t seem to find anyone who is doing the same thing I’m trying to do or a ready-made sketch containing all of my needs. Could anyone please help solve my problems or at least steer me in the right direction? Any help would be greatly appreciated! Thank you so much

Alesis Samplepad

Olimex MIDI Shield

IEIK Arduino UNO

8 Channel Relay Board

Could you attach the sketch?

Hello and thank you for replying. :slight_smile: I tried to include the sketch in my first post but there were too many characters so I had to cut it out. This version is the one I’ve changed to look for MIDI channel 10 which is what the Samplepad needs. Other than that it hasn’t been altered from the original. I’ll only be using Normally Open contacts on the relays so it isn’t really necessary to have them all energized from the beginning, just something to keep in mind.

/* Midi Glock - Mike Cook April 2008

  • based on code by kuki

  • listen for MIDI serial data, and fire solenoids for individual notes

#####################################################################################################################################################

HARDWARE NOTE:
The MIDI Socket is connected to arduino RX through an opto-isolator to invert the MIDI signal and seperate the circuits of individual instruments.
Connect the 8 solenoids to pin2 to pin9 on your arduino and pin 13 to the drive enabling monostable.

####################################################################################################################################################
*/

//variables setup

byte incomingByte;
byte note;
byte velocity;
int noteDown = LOW;
int state=0; // state machine variable 0 = command waiting : 1 = note waitin : 2 = velocity waiting
int baseNote = 60; // lowest note
// use different values of baseNote to select the MIDI octiave
// 24 for octiave 1 – 36 for octiave 2 – 48 for octiave 3 – 60 for octiave 4 – 72 for octiave 5
// 84 for octiave 6 – 96 for octiave 7

// play only notes in the key of C (that is no sharps or flats) define pin numbers:-
byte playArray = { 2, 0, 3, 0, 4, 5, 0, 6, 0, 7, 0, 8, 9 };
// corrisponding to note 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48 - for base note = 36 or C2
int strobe = 13; // select the pin for the monostable
int channel = 9; // MIDI channel to respond to (in this case channel 10) chnage this to change the channel number
// MIDI channel = the value in ‘channel’ + 1

//setup: declaring iputs and outputs and begin serial
void setup() {
pinMode(strobe,OUTPUT); // declare the strobe pin as output
pinMode(2,OUTPUT); // declare the solenoid’s pins as outputs
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
pinMode(8,OUTPUT);
pinMode(9,OUTPUT);
state = 0; // initilise state machine variable
//start serial with MIDI baudrate 31250 or 38400 for debugging
Serial.begin(31250);
digitalWrite(strobe,LOW);
}

//loop: wait for serial data, and interpret the message
void loop () {

if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
digitalWrite(strobe,LOW); // clear any previous strobe
switch (state){
case 0:
// look for as status-byte, our channel, note on
if (incomingByte== (144 | channel)){
noteDown = HIGH;
state=1;
}
// look for as status-byte, our channel, note off
if (incomingByte== (128 | channel)){
noteDown = LOW;
state=1;
}

case 1:
// get the note to play or stop
if(incomingByte < 128) {
note=incomingByte;
state=2;
}
else{
state = 0; // reset state machine as this should be a note number
}
break;

case 2:
// get the velocity
if(incomingByte < 128) {
playNote(note, incomingByte, noteDown); // fire off the solenoid
}
state = 0; // reset state machine to start
}
}
}

void playNote(byte note, byte velocity, int down){
// if velocity = 0 on a ‘Note ON’ command, treat it as a note off
if ((down == HIGH) && (velocity == 0)){
down = LOW;
}
//since we can’t play all notes we only action notes between 36 & 49
if(note>=baseNote && note<(baseNote + 13)){
byte myPin=playArray[note-baseNote]; // to get a pin number between 2 and 9
if(myPin != 0) {
digitalWrite(myPin, down); // play it if it is one of our notes
if(down == HIGH) digitalWrite(strobe, HIGH); // strobe high to monostable for note on
}
}

}

I tried to include the sketch in my first post but there were too many characters so I had to cut it out

You can attach the sketch as a file. Open up the 'Attachments and other options' at the base of the edit box when you are editing your post. Also, while I am passing on tips, you can also put URLs in 'link' tags so that they provide a click through, making it easier for your readers. The link tags are at the top of the edit box and look line 3 links in a chain.

From what I understand I should be able to change a number in the code to indicate how many milliseconds I want it to hold in

Not possible. The code activates the relay on MIDI Note ON and deactivates it on a Note OFF. This also explains the 'dim' LED - it flicks on and off very quickly. The code can be changed to do what you want but it involves additional logic per I/O pin to track the time when the pin note was turned on and also clearly the Note OFF needs to be ignored.

So to sum it all up… Drummer hits pad - Sound goes to PA system - MIDI "Note On" message goes to Arduino - Arduino tells relay 1 to actuate for an adjustable duration - At the same time Arduino tells relay 2 to actuate for its own adjustable duration.

This is quite different from what the code does now. Ignoring the delay, at the moment Note On and output relay are 1-to-1. Code can be changed, but you do not have any of this in there at the moment.

I clearly do not have your setup, but I will try and modify this sketch to give you something that is closer to what you need. You will need to test and debug the result.

You're awesome Marco! I can't tell you enough how much I appreciate your offer of help!

I followed your advice and modified my original post to have clickable links. I also added a video of someone with similar equipment using the Glockenspiel sketch to do what I did with my Keyboard. My set up worked exactly the same as seen in the video. Here it is again... Video

Thank you very much, John

Here is the code modified. I have allowed for 2 pins per note, but is easily changed by the constant.

There is a ‘strobe’ variable and associated code that I think is probably not needed in your setup - you may want to consider removing all that.

Code compiles cleanly but has not been tested at all.

Any questions, either PM me or come back to this forum entry.

Rockstar.ino (5.59 KB)

It's late and I need to get to bed but I wanted to tell you that I just tried your version of the sketch and it appears to be working! Pads 1-4 actuate relays 1-4. they seem to hold for about one second each before snapping back. Can you please tell me where in the code I'll need to change it to make note actuate 2 relays and where I can try different time durations? It was so nice of you to do this, I really cant thank you enough!! :)

The only other thing I can think of at this time is that it would be nice if all relay coils were in the de-energized state at all times until a note on message was received. As it stands now, when I send power to the Arduino all relays actuate and stay energized until a note on message is received. I would have to connect my prop to the normally closed contacts for it to work correctly which would cause the 120v to my prop to be energized until I send power to the Arduino. I would always have to remember to energize the Arduino first before energizing the 120v side. I could live with this but if it's an easy fix it would be nice.

Thanks again, John

it would be nice if all relay coils were in the de-energized state at all times

Not sure what you mean by this as most relays have a NC and NO which act in opposite directions. However, the new code has been changed so that you specify which the ENERGISED_STATE is (HIGH or LOW - look for it in the code near the top) and the code adapts to use the right states when switching.

The amount of delay for the first pin and the second pins (or more if you need them) are defined in

// The time in milliseconds for each pin for a notre to be active
// change these values for different timeouts
const uint32_t  timePinActive[MAX_PINS_PER_NOTE] = { 1000, 3000 };

Change the values 1000 and 3000 (milliseconds) to whatever suits. If you need to add a third or more pins per note, change the definition of MAX_PINS_PER_NOTE and add entries to this table for additional pin delays.

The rest of the code is driven by a data table. This data structure

struct playData_t
{
  uint8_t   pin[MAX_PINS_PER_NOTE];   // outputs for the pins
  uint32_t  timeStart[MAX_PINS_PER_NOTE]; // time the outputs were activated
};

defines one line in a table of data for each note. pin is an array of pin numbers that are activated for each note and timeStart is the time when they were activated, one per note. The time allows the code to track when the pin needs to be de-energised, using the timer values defined as explained above. The way to read this is

pin[x] uses timeStart[x] which times out when it has been on for timePinActive[x] milliseconds.

So once we have defined what one line in the data table looks like, this

struct playData_t playArray [] = 
{
//   pin[] timeStart[]
  { {2, 0}, {0L, 0L} },
  { {0, 0}, {0L, 0L} },
...
  { {8, 0}, {0L, 0L} },
  { {9, 0}, {0L, 0L} },
};

defines the table of data, one line at a time. The initialisation values are given in the order they are declared in the definition of each line. So {2, 0} are the pin numbers and {0L, 0L} are the timer start values. You will only ever need to change the pin number as you allocate more pins for a note (0 means the pin is not used). So if you want the first note to trigger pins 10 and 11, the first entry needs to be {10, 11} instead of {2, 0}. Again, changing MAX_PINS_PER_NOTE allows you to define more entries for each pin, but you must include ALL the data in the table, using 0 for what you do not need activated.

Hopefully that explains enough to keep you going.

Rockstar.ino (5.94 KB)

Marco, you are my favorite person in the whole world right now!! You really came through, this is everything I asked for! It works perfect. ;D Thank you so much my friend!!

I changed "Energized state" to "LOW" and that gave me what I was looking for. Apparently you figured out what I meant. Now when I power up the Arduino all relays stay at their normal state until a note on message is received. The way it was before, upon powering up the Arduino, all relays would click and their LED's would stay lit until the Note On message was sent.

I successfully configured the pins and assigned two pins with different off times to each of four notes. Awesome! now I can play around with times and see what works best foe the props.

There is still one thing I don't quite understand though. Its the section with {0L, 0L}. Am I understanding correctly that editing this section allows for adjusting the start time of each pin? So when the Note On message is received the relay would not actuate exactly with the sound effect but be delayed for a pre-defined amount of time? If so, how do I edit {0L, 0L} to achieve this? I tried putting millisecond numbers in there but it didn't seem to make a difference. I may not need that particular functionality but it definitely would be an added bonus and give me even more flexibility.

Am I understanding correctly that editing this section allows for adjusting the start time of each pin?

Not understanding correctly.

The code is programmed to set the output as soon as note on is received. These variables records the time when the corresponding output is set on so that it knows when the timeout takes effect. It is a working register and not a configuration parameter, so in the table it should always be zero.

Setting an initial delay requires adding some setup data and some logic to handle that initial delay. None of the exists at this stage.

Okay, that clears it up. I don't think that's something I would need anyway.