using millis() with midi-notes

dear folks,

sorry for sparing any decent code-example - i'm at the very beginning of learning and can't wrap my mind around this issue being so difficult :confused:

i'm (simply?) trying to set any of 11 digital pins HIGH for a very short period of time (~40ms) when a NoteOn is received via midi. i've got the midi-part running, and actually a version that partially works for NoteOns and NoteOffs, how ever this appears of no use in the given scenario..

can anybody help or point me towards the right direction? i've found various examples on setting (up to 2) pins HIGH via millis() in a given interval, how ever nothing that's event-based.

i'd be gracious!

//variables setup

byte incomingByte;
byte note;
byte velocity;

int c = 2;
int d = 3;
int e = 4;
int f = 5;
int g = 6;
int a = 7;
int h = 8;
int c2 = 9;
int d2 = 10;
int e2 = 11;
int f2 = 11;

//0 =note off ; 1=note on ; 2= nada
int action=2; 


//setup: declaring iputs and outputs and begin serial 
void setup() {
  pinMode(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(12,OUTPUT);
  
  //start serial with midi baudrate 31250 or 38400 for debugging
  Serial.begin(31250);        
}

//loop: wait for serial data, and interpret the message 
void loop () {
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();
    
    // wait for as status-byte, channel 16, note on or off
    
    
    
    if (incomingByte== 159){ // note on message starting starting
      action=1;
      
      
      
    }else if (incomingByte== 143){ // note off message starting
      action=0;
      
      
      
    }else if ( (action==0)&&(note==0) ){ // if we received a "note off", we wait for which note (databyte)
      note=incomingByte;
      playNote(note, 0);
      note=0;
      velocity=0;
      action=2;
      
      
    }else if ( (action==1)&&(note==0) ){ // if we received a "note on", we wait for the note (databyte)
      note=incomingByte;
      
      
      
    }else if ( (action==1)&&(note!=0) ){ // ...and then the velocity
      velocity=incomingByte;
      playNote(note, velocity);
      note=0;
      velocity=0;
      action=0;
    }else{
      //nada
    }
  }
}



void playNote(byte note, byte velocity){
  int value=LOW;
  if (velocity >0){
      value=HIGH;
  }else{
   value=LOW; 
  }
 
 //since we don't want to "play" all notes we wait for a note between 36 & 44

 if(note==36){
   digitalWrite(c, value);
 } 
 
 else if (note==38){
   digitalWrite(d, value);
 }
 
  else if (note==39){
   digitalWrite(e, value);
 }
 
   else if (note==40){
   digitalWrite(f, value);
 }
 
    else if (note==41){
   digitalWrite(g, value);
 }

    else if (note==42{
   digitalWrite(a, value);
 }

    else if (note==43{
   digitalWrite(h, value);
 }

    else if (note==44{
   digitalWrite(c2, value);
 }

    else if (note==45{
   digitalWrite(d2, value);
 }

    else if (note==46{
   digitalWrite(e2, value);
 }

    else if (note==47{
   digitalWrite(f2, value);
 }

}

That is exactly the sort of code I show you how to do in my book

Basically it is like the blink without delay sketch. You get a note on message and make a note of the value of the millis timer as well as setting the pin high. Then in the loop check to see if the current value in the millis timer minus the time when the note on message was received exceeds the required time for the pin to be high and if it does put the pin low.

Do that with one note only, then. Extend it to all 11 by using an array to hold your variables.

Looks like your code was code I wrote mainly.

hello mike,

thanks a lot for your help! this really got me going :slight_smile: i'm definitely going to get my hands on your book. great!

here's the code i've written. i'm very sure it's far from perfect, how ever it works. i'd be totally gracious if you found the time to give me any input on it, as i'm sure i'm lacking some essential basics. anything that you'd say is a no-go or could be done far more efficiently? (i do how ever understand if you have no time for such guidance)

// midi bytes & stuff
byte incomingByte;
byte note;
byte velocity;
int action=2; //0 =note off ; 1=note on ; 2= nada

// memo: pin arduino - drum hohner
// BD         =  6  (36)
// SN         =  5  (40)
// LOW CONGA  =  2  (41)
// LOW BONGO  =  8  (43)
// HI BONGO   =  7  (44)
// HIHAT      =  10 (46)
// MARACAS    =  11 (42)
// COWBELL    =  4  (39) 
// CLAVE      =  3  (37) 
// CYMBAL     =  9  (52)

// define array of possible notes
int notes[]=                       {36, 40, 41, 43, 44, 46, 42, 39, 37, 52};

// define aaccording pins
int pins[]=                        { 6,  5,  2,  8,  7, 10, 11,  4,  3,  9};

// this array is used as buffer to store pin's high-times
unsigned long pintime[]=           { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0};

// this array defines how long each pin should be HIGH when an according note is received
const unsigned long pininterv[]=   {10, 20, 10, 10, 10, 10, 30, 10, 10, 70};

// buffer for current pinstates
int pinstate[]= {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// simple led-blink sequence when turning the machine on
int delayseq[]= {20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180};
 


//setup: declaring iputs and outputs and begin serial 
void setup() {
  pinMode(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
  pinMode(8,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  pinMode(13,OUTPUT);

// set pintimes to 0 when startup
  for (int i=0; i<=9; i++) {
    pintime[i]=millis ();
    digitalWrite(pins[i], LOW);
}

//start serial with midi baudrate 31250 or 38400 for debugging
  Serial.begin(31250);   

// blink LED13 to show that the machine is working
for (int i=0; i<=15; i++) {
  digitalWrite (13, HIGH ) ; 
  delay(delayseq[i]);
  digitalWrite (13, LOW ) ; 
  delay(delayseq[i]);
}

}


//loop: wait for serial data, and interpret the message 
void loop () {
  
  if (Serial.available() > 0) {
    // read the incoming byte:
    incomingByte = Serial.read();
    
           // wait for as status-byte, channel 16, note on or off 
            if (incomingByte== 159){ // note on message starting starting
              action=1;

                  // note off message starting
                  }else if (incomingByte== 143){ 
                    action=0;
    
                  // if we received a "note off", we wait for which note (databyte)
                  }else if ( (action==0)&&(note==0) ){ 
                    note=incomingByte;
                    playNote(note, 0);
                    note=0;
                    velocity=0;
                    action=2;
    
                    // if we received a "note on", we wait for the note (databyte)
                    }else if ( (action==1)&&(note==0) ){ 
                      note=incomingByte;
                      
                    // ...and then the velocity 
                    }else if ( (action==1)&&(note!=0) ){ 
                      velocity=incomingByte;
                      playNote(note, velocity);
                      note=0;
                      velocity=0;
                      action=0;
                    }else{
                      //nada
                    }
       }


// cycle though the array and check for values
    for (int i=0; i<10; i++) {
               if ( ((millis () - pintime[i]) <= pininterv[i]) && (pinstate[i]==1) ) {
                 digitalWrite (pins[i], HIGH ) ;
                 digitalWrite (13, HIGH ) ;
                 }else {
                 pinstate[i] = 0 ;
                 digitalWrite (pins[i], LOW ) ;
                 digitalWrite (13, LOW ) ;
                 pintime[i] = 0;
                }
    }


}


// MAIN NOTE FUNCTION, HANDLES ALL PINS IN CONJUNCTION WITH for-loop in the main/void loop
void playNote(byte note, byte velocity) {
    if ((velocity > 0) && (note > 35) && (note < 53) ){
         for (int i=0; i<10; i++) {
             if (note == notes[i]) {
             pinstate[i]= 1;
             pintime[i] = millis () ; 
             break;
             }
         }} else {
               for (int i=0; i<10; i++) {
               if (note == notes[i]) {
               pinstate[i]= 0;
               break;
               }
               }
         }
         
}

OK that looks OK, just a few minor things I would change.
In the setup function in place of all those pinMode calls I would just do this:-

for(int i=0; i<10; i++) pinMode(pins[i], OUTPUT);
   pinMode(13,OUTPUT);

The other thing I would do is to add the putting the pin high into the playNote function just after you set the pintime array element to millis. Then their is no need to do it every time round the loop function in the "cycle though the array and check for values" section, you just do it once. This makes the "cycle though the array and check for values" section a lot shorter and saves on processing time. Of course you need to change the condition in the if statement here.
Note there is no requirement to include an else with every if, if you are not going to do anything with it just leave the else out.

Finally I would split the loop function into two. One with the read and process the MIDI input and the other the "cycle though the array and check for values" section. Now your new loop function just calls these two functions. This makes it more modular and easier to read.