Setting PWM Duration for Multiple Pins/Solenoids

Programming novice here.

I’ve built a working version of Mike Cook’s Midi Glockenspiel, except mine plays drums (with different mechanics). MIDI Glockenspiel

I’m now attempting to use an analogWrite method instead of digital HIGH/LOW so I can utilize the MIDI velocity data. Each time I fire a solenoid, I want to produce a 50 millisecond long PWM (of varying duty cycles).

Since I am controlling six independent solenoids, I figured I couldn’t use “delay” to control the PWM duration as it would conflict with the data listening and firing of the other solenoids/drums. I’ve studied the BlinkWithoutDelay example to help wrap my mind around using “millis()” as a way to time the PWM duration.

What I’m having a hard time imagining is what the code would look like to keep track of the analogWrite for 6 different pins simultaneously.

This is the code I have now. It is lacking code to control the analogWrite duration. Any help on getting me going in the right direction would be greatly appreciated.

//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 = 36;  // 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 for our drums define pin numbers:- 
  byte playArray[] =     {  3,  0,  5,  0,  0,  0,  6,  9,  10,  11  }; 
// corresponding to note 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 - for base note = 24 or C1 
int strobe = 13;   // select the pin for the monostable 
int channel = 15; // MIDI channel to respond to (in this case channel 16) change this to change the channel number 
                 // MIDI channel = the value in 'channel' + 1 
 
//setup: declaring inputs and outputs and begin serial  
void setup()  {  
  pinMode(strobe,OUTPUT);   // declare the strobe pin as output 
  state = 0;  // initialize 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 24 & 33 
 if(note>=baseNote && note<(baseNote + 10)) { 
   byte myPin=playArray[note-baseNote]; // to get our pwm pin number 
   if(myPin != 0)  { 
     analogWrite(myPin, velocity*2); // play it if it is one of our notes. Also, does this multiplier work? 
     if(down == HIGH) digitalWrite(strobe, HIGH); // strobe high to data indicator led  
    } 
  }  
 
 }

What I'm having a hard time imagining is what the code would look like to keep track of the analogWrite for 6 different pins simultaneously.

You've got a piece of paper, a pencil, and a stopwatch. It is your job to manually perform the task. How would you do it?

If the job seems hard with 6 solenoids, imagine having just one. Figure out the action for one solenoid. Then, use arrays and for loops wherever you deal with one solenoid, to deal with 6 of them.

So what do you need to do. Something (I'm not sure what, in your system) tells you that it is time to fire a solenoid. So, you set the PWM duty cycle for the pin, and write down the time. Periodically, you check the stop watch, and compare the current time to the time that you started the PWM pin firing. If it is time to shut it off, shut it off. Otherwise, do nothing.

On the Arduino, "periodically" is every pass through loop. Writing down when something happened is an assignment.

unsigned long pwmStart = 0;
int onTime = 50; // or whatever value you need

void loop()
{
   unsigned long now = millis();
   if(pwmStart > 0 && now - pwmStart >= onTime)
   {
      // Turn the PWM off
   }

   // If something happens that requires PWMing the pin
   {
      analogWrite(pin, dutyCycle);
      pwmStart = millis();
   }
}

Using arrays and for loops will allow this code to operate for more than one pin.

Thanks, PaulS. I’ve implemented (what is hopefully) a solution based on the code you suggested. I did not use an array or for loops, but I think what I did might work. I haven’t built the circuitry yet to test. Does this seem reasonable?

//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 = 36;  // 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 for our drums define pin numbers:- 
  byte playArray[] =     {  3,  0,  5,  0,  0,  0,  6,  9,  10,  11  }; 
// corresponding to note 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 - for base note = 24 or C1 
int strobe = 13;   // select the pin for the monostable 
int channel = 15; // MIDI channel to respond to (in this case channel 16) change this to change the channel number 
                 // MIDI channel = the value in 'channel' + 1 
  int onTime = 50; // pwm duration - 50 milliseconds
  int liftForce = 255; // duty cycle for the hi-hat lifter
  int hitForce = 0;
  unsigned long pwmStart3 = 0;
  unsigned long pwmStart5 = 0;
  unsigned long pwmStart6 = 0;
  unsigned long pwmStart9 = 0;
  unsigned long pwmStart10 = 0;
  unsigned long pwmStart11 = 0;

  
//setup: declaring inputs and outputs and begin serial  
void setup()  {  
  pinMode(strobe,OUTPUT);   // declare the strobe pin as output 
  state = 0;  // initialize 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 ()  { 
 
   unsigned long now = millis();  // Get current time
   if(pwmStart3 > 0 && now - pwmStart3 >= onTime)  // Check how long the pin has been firing
  {
     analogWrite(3, 0);   // Turn the PWM off if it has been long enough - onTime
   }
   
   if(pwmStart5 > 0 && now - pwmStart5 >= onTime)  // Check how long the pin has been firing
  {
     analogWrite(5, 0);   // Turn the PWM off if it has been long enough - onTime
   }
   
   if(pwmStart6 > 0 && now - pwmStart6 >= onTime)  // Check how long the pin has been firing
  {
     analogWrite(6, 0);   // Turn the PWM off if it has been long enough - onTime
   }
   
   if(pwmStart9 > 0 && now - pwmStart9 >= onTime)  // Check how long the pin has been firing
  {
     analogWrite(9, 0);   // Turn the PWM off if it has been long enough - onTime
   }
   
   if(pwmStart11 > 0 && now - pwmStart11 >= onTime)  // Check how long the pin has been firing
  {
     analogWrite(11, 0);   // Turn the PWM off if it has been long enough - onTime
   }
    
  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 24 & 33 
 if(note>=baseNote && note<(baseNote + 10)) { 
   byte myPin=playArray[note-baseNote]; // to get our pwm pin number 
   if(myPin == 10 && velocity > 1) { // if it's the high-hat lifter on switch
     analogWrite(myPin, liftForce); // power lifter
     }
     if(myPin == 10 && velocity == 1) { // if it's the high-hat lifter off switch
     analogWrite(myPin, 0); // turn lifter off
     }
     
   hitForce = (velocity * 2); 
   if(myPin != 0 || myPin != 10)  {  // if it's not zero or ten - Is this how this is suposed to be writtten?
     analogWrite(myPin, hitForce); // play it if it is one of our notes. Also, does this multiplier work? 
     
     if(myPin == 3) {
      pwmStart3 = millis(); // store the pwm start time for whichever pin the note belongs to
     }
     if(myPin == 5) {
      pwmStart5 = millis(); 
     }
     if(myPin == 6) {
      pwmStart6 = millis(); 
     }
     if(myPin == 9) {
      pwmStart9 = millis(); 
     }
     if(myPin == 11) {
      pwmStart11 = millis(); 
     }
     if(down == HIGH) digitalWrite(strobe, HIGH); // strobe high to data indicator led  
    } 
  }  
 
 }

I haven't built the circuitry yet to test. Does this seem reasonable?

No, not really. Oh, wait, you mean the code. The code looks like it should work, although, since you have an array of PWM pin numbers, an array of times would have been trivial to add, and a loop to test each time vs. now would have been easier to implement than copy/pasting/editing the code to deal with one instance. Less code = fewer places for bugs to hide.