Pages: [1]   Go Down
Author Topic: MIDI-In Controlling Multiple PWMs Simultaneously  (Read 1502 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 9
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm working on a MIDI controlled drum set player. I have an arduino reading midi data on one channel from a sequencer to trigger short pulses on the 6 pwm pins. Each pwm pin drives a different solenoid to activate a drumstick on each drum of a drum set.

The one pin (or midi note) that behaves differently is G#1 (expressed through pwm10). This note controls a solenoid that (when charged) lifts the hi-hat to the open position. A G#1 note with a volume > 1 lifts the hi-hat. It stays lifted (charged) until a G#1 with a volume of 1 is played. That closes the hi-hat.

My problem (and I think it's a code problem) is that once the pwm10 gets charged (by a G#1 w/Volume of over 1), none of the other notes (for the 5 other PWM pins) are responded to until the pwm10 turns off (by a G#1 w/Volume equal to 1).

I'm rather new to programming, so any insight into what might be causing my problem would be greatly appreciated.

Code:
  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 --
// 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 onTime3 = 0; // pwm duration depending on velocity - for each pin
  int onTime5 = 0; // pwm duration depending on velocity - for each pin
  int onTime6 = 0; // pwm duration depending on velocity - for each pin
  int onTime9 = 0; // pwm duration depending on velocity - for each pin
  int onTime11 = 0; // pwm duration depending on velocity - for each pin
  int liftForce = 255; // duty cycle for the hi-hat lifter - 255 max
  int hitForce = 0;
  unsigned long pwmStart3 = 0;
  unsigned long pwmStart5 = 0;
  unsigned long pwmStart6 = 0;
  unsigned long pwmStart9 = 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 >= onTime3)  // 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 >= onTime5)  // 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 >= onTime6)  // 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 >= onTime9)  // 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 >= onTime11)  // 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 - added less than
     analogWrite(myPin, liftForce); // power lifter on
     }
     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
     analogWrite(myPin, hitForce); // play it if it is one of our notes.
     
     if(myPin == 3) {
      pwmStart3 = millis(); // store the pwm start time for whichever pin the note belongs to
      onTime3 = ((25475 / 127) - ((75 * hitForce) / 127)); // this accounts for 50ms onTime at velocity 127 and 200ms at velocity 1
     }
     if(myPin == 5) {
      pwmStart5 = millis();
      onTime5 = ((25475 / 127) - ((75 * hitForce) / 127));
     }
     if(myPin == 6) {
      pwmStart6 = millis();
     onTime6 = ((25475 / 127) - ((75 * hitForce) / 127));
     }
     if(myPin == 9) {
      pwmStart9 = millis();
      onTime9 = ((25475 / 127) - ((75 * hitForce) / 127));
     }
     if(myPin == 11) {
      pwmStart11 = millis();
      onTime11 = ((25475 / 127) - ((75 * hitForce) / 127));
     }
     if(down == HIGH) digitalWrite(strobe, HIGH); // strobe high to data indicator led 
    }
   
  } 
 
 }


Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 208
Posts: 8821
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The code looks OK to me so my guess is that the HiHat lifter is using too much power. Do the other solenoids work properly if you disconnect the HiHat lifter?
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

Brazil
Offline Offline
God Member
*****
Karma: 3
Posts: 616
Wusik Dot Com
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Indeed, maybe try a good 12V power-adaptor?

Wk
Logged


Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 208
Posts: 8821
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It may be that the HiHat lifter doesn't need full power to stay up.  Try turning on the solenoid, delaying a short time, and changing the PWM output to a lower 'holding' value:

Code:
    if(myPin == 10 && velocity > 1) { // if it's the high-hat lifter on switch - added less than
     analogWrite(myPin, liftForce); // power lifter on
     delay(500);  //  2nd: Reduce this delay till the lifter fails, then increase by...  25%?  for safety factor
     analogWrite(myPin, 255);  //  1st:  Reduce this value till the HiHat won't stay up, then increase by...  25%?  for safety factor
     }
     if(myPin == 10 && velocity == 1) { // if it's the high-hat lifter off switch
     analogWrite(myPin, 0); // turn lifter off
     }
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

0
Offline Offline
Newbie
*
Karma: 0
Posts: 9
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Great troubleshooting tips, guys.

Tonight, I will simply disconnect the wires to the hi-hat lifter solenoid. If everything starts performing normally, then I will know (maybe*) my solenoid power source is too weak. (I think it's 24v, 10 amp.) I will then first try to fix with johnwasser's power-saving tip.

A detail I should have mentioned on the hardware side is that the PMWs are controlling n-channel enhancing mosfets - which, in turn, are releasing current from the larger power supply to the solenoids. (sorry about the omission, I was pretty sure it was just a code issue).

My first question about johnwasser's tip that includes a 500ms (or less) delay is:
Won't that delay mean I'll be missing data that comes in during that delay? For example, if the MIDI data prescribes for a bass drum hit on the same beat as the hi-hat is to be lifted, won't I possibly miss the bass drum hit coming in right after (during the delay)?

*If I'm understanding it correctly, WilliamK's suggestion addresses a problem that had not occurred to me. Can my arduino have enough juice to function but not enough to be firing all 6 pmw's at once? I have been afraid of over-supplying the arduino. Could my larger power source (solenoid driver) be adequate while my arduino needs more board power?? What's a max rating for the board power supply?
Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 208
Posts: 8821
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

With the mosfets and massive power supply you should not be having a problem with power.  smiley-sad

The half-second initial delay is just to be certain that the HiHat lifter always lifts.. that way you can tune the holding current first.  I hope that a lift time of a few milliseconds will be enough.  With the delay your input stream will not be processed for that time.  If the lift time is long enough that there is a noticeable lag you can always use the same sort of pulse-timing logic you use on the other pins to time the reduction to hold current.

In case 0 of your switch statement, did you intend to drop through into case 1?  It's unusual not to have a 'break' at the end of each case so if you intend to drop through you should add a comment.  

Code:
  if(pwmStart3 > 0 && now - pwmStart3 >= onTime3)  // Check how long the pin has been firing
  {
    analogWrite(3, 0);   // Turn the PWM off if it has been long enough - onTime
    pwmStart3 = 0;  //  ADD THIS or the analogWrite will happen every time through the loop after the timer expires.
  }

Those are the only things I see so far.  I don't think either would cause the symptom you describe.
« Last Edit: May 05, 2011, 11:49:41 am by johnwasser » Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

0
Offline Offline
Newbie
*
Karma: 0
Posts: 9
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The troubleshooting revealed that the problem lies with power. The 24v/10A Mean Well power source isn't cutting it for multiple solenoids firing at the same time.

( I went ahead and implemented johnwasser's 2 phase solution for cutting back on longterm power drain from the hi-hat lifter. - As well a the suggested code clean-ups)

One or more of these must be happening:
1. the solenoids are taking way more current(?) than I thought to do their job
2. the solenoid power source isn't doing what it's rated to do
3. my circuit is highly inefficient
4. the board doesn't have enough current to have more than 2 pins PMWing 100% at the same time (could that even be it?)

My typical solenoids:
http://www.testco-inc.com/ledex/195206-128
http://www.testco-inc.com/ledex/195206-228

and for the one lifter:
http://www.testco-inc.com/ledex/129415-026


I'll try to find some way of testing my Mean Well to determine what it can really do.


The solenoid bit. Is this the way I should be doing it?








Logged

Massachusetts, USA
Offline Offline
Tesla Member
***
Karma: 208
Posts: 8821
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The lifter solenoid  Ledex 129415-026 looks like it can run off 24 volts if you don't keep it on for more than about 40 seconds and let it rest a couple of minutes between lifts.  Looks like at 28V it draws about 2.75 Amps so at 24 volts it will be lower (maybe 2.5?)

Have you thought of using a spring to lift the HiHat and a solenoid to pull it down?  That would reduce the time that solenoid was activated.
Logged

Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

0
Offline Offline
Newbie
*
Karma: 0
Posts: 9
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I got a 24v 25A power source. That fixed all my problems.

I have recently been working on the permanent build for this project. I think this might have been a good project to etch a pcb for.

I've been mounting 2 atmega328's, an array of 12 separate power mosfets, and all the support components to perfboard. That is a ridiculous amount of soldering and wires to fit in. Maybe it's just the perspective of this being my first project.

My RadioShack soldering-helper magnifying-glass station has already broken. I'm seeing the wisdom of investing in a much nicer apparatus for soldering - hopefully one with very bright lighting.
Logged

Pages: [1]   Go Up
Jump to: