Starting/stopping DC motor using interrupts

So for a project I need to be able to start and stop a fan with a pushbutton, and adjust the speed with a pot. I put the code together and wired it all up, but when I run the program, the motor will start but will not always turn off. Sometimes pushing the button will register, most of the time it won't. Attached is the code. I fear the motor may be causing some sort of feedback to the button because when I physically remove the motor or comment out functions to power it up, the button works everytime. If there doesn't seem to be a problem with the code, then does anyone have any suggestions for putting the circuit together?

//code for motor control w/ pot and button to start/stop


int motor_pin = 3; // PWM pin motor control
int pot_pin = A0;  //pot connected to motor control
int start_button = 0;  //button to start/stop on pin 2
int toggle_on = false;  //button click switches state
int led_pin = 9;

void setup() { 
  Serial.begin(9600);
  //pinMode( motor_pin, OUTPUT );
  pinMode(led_pin, OUTPUT);
    attachInterrupt( start_button, handle_click, RISING);  // Register interrupt handler
}

void loop() {
  int duty, reading;
  reading = (analogRead(pot_pin)); // read potentiometer
  duty = map(reading,0,1023,0,255); // rescale to 8-bit
  duty = constrain(duty,40,255); // be safe
  
    if ( toggle_on ) {
    Serial.print (duty);
    Serial.println("   on");
    analogWrite(motor_pin,duty);
    digitalWrite(led_pin, HIGH);
    } 
    
    Serial.println(toggle_on);
    if(!toggle_on) {
    digitalWrite(motor_pin, LOW);
    digitalWrite(led_pin, LOW);
  }
}



void  handle_click()  {
  static unsigned long last_interrupt_time = 0;       //  Zero only when code first runs

  unsigned long interrupt_time = millis();             //  Read the clock
  if ( interrupt_time - last_interrupt_time >= 200 ) {  //  Only count clicks separated by 200 msec
    toggle_on = !toggle_on;
  }
  last_interrupt_time = interrupt_time;
}

I'm not sure if it has anything to do with your problem, but you're Serial.printing on EVERY itteration of the loop. This is likely causing an overflow of the UARTs buffer.

Try making your print statements less frequently, perhaps once every half second.

eg

//code for motor control w/ pot and button to start/stop
int motor_pin = 3; // PWM pin motor control
int pot_pin = A0;  //pot connected to motor control
int start_button = 0;  //button to start/stop on pin 2
int toggle_on = false;  //button click switches state
int led_pin = 9;

void setup() { 
  Serial.begin(9600);
  //pinMode( motor_pin, OUTPUT );
  pinMode(led_pin, OUTPUT);
    attachInterrupt( start_button, handle_click, RISING);  // Register interrupt handler
}

unsigned long lastReport=0;

void loop() {
int t=micros();

  int duty, reading;
  reading = (analogRead(pot_pin)); // read potentiometer
  duty = map(reading,0,1023,0,255); // rescale to 8-bit
  duty = constrain(duty,40,255); // be safe
  
    if ( toggle_on ) {
    analogWrite(motor_pin,duty);
    digitalWrite(led_pin, HIGH);
    } 
    
    if(!toggle_on) {
    analogWrite(motor_pin, 0);
    digitalWrite(led_pin, LOW);
  }
  
if ((t-lastReport) > 500)
  {switch(toggle_on)
    {case LOW:
       Serial.println("Motor is OFF");
       break;
     case HIGH:
    Serial.print (duty);
    Serial.println("   on");     
    }
  lastReport=t;
  }  
}

void  handle_click()  {
  static unsigned long last_interrupt_time = 0;       //  Zero only when code first runs

  unsigned long interrupt_time = millis();             //  Read the clock
  if ( interrupt_time - last_interrupt_time >= 200 ) {  //  Only count clicks separated by 200 msec
    toggle_on = !toggle_on;
  }
  last_interrupt_time = interrupt_time;
}

BTW I've also changed the ditigalWrite LOW to analogWrite 0.

I tried your code. It didn't seem to make any difference in the performance. I'll be a little more specific in the problem. When the board is reset, it prints 0 (from Serial.println(toggle_on), outside any if loops). In KenF's program, it prints "Motor is OFF" from his if loop. Pressing the button changes toggle_on and starts the if loop, printing everything there. The motor speed can be controlled from the pot. Sometimes when the pot is turned all the way down, the program reverts back and the motor stops and "0" is printing. In any other position, sometimes the button will work, sometimes it won't. I believe the motor is causing some sort of feedback into the system. Right now, the motor is in parallel to a snubber diode and is attached to a transistor. The collector goes to the ground strip, the base to a resistor and then pin 3, and the emitter to the motor and diode. I'll try to attach a breadboard diagram with Fritzing once I make it.

Why are you using interrupts? They are only for when your timing is critical and measured in microseconds.
A human hand pushing a switch will take about 500,000 microseconds, hardly time critical in terms of a 16MHz microcontroller.

How have you connected the motor?

Here's a picture of the fan schematic. The capacitor is actually attached to the motor.

FritzFan.PNG

i See the problem. You're driving the motor from the 5v of the UNO. That's going to kill the voltage regulator on the UNO.

If you remove the red wire that goes to the collector of that transistor and pass it to an alternative power supply (maybe a pack of AA batteries or a usb charger) and then connect the GND of your external power supply to the GND of the arduino, you'll probably get better results.

suchas.jpg

Part of the coding problem - independent of the circuit - is that you are attempting to use interrupts.

Clearly you have not a clue of what interrupts are for!

Paul__B, I'm building the code using what I've learned in class so far. Teachers suggested using interrupts and that's all I know. Is there a better way I could do this? I tried drawing code from the SeveralThingsAtTheSameTime code posted by Robin2. Using a user defined function to switch the state of a variable when the button was pressed didn't seem to work. Do you at least agree with KenF that a separate power source is needed? Could you offer a solution to my problem?

knaving:
Paul__B, I'm building the code using what I've learned in class so far.

Aha! So, this is another college assignment, eh? So we are not ethically supposed to give you the full solution, but may (and will be happy to) guide you. Of course, you should expect your teachers to be monitoring these forums when they set such an assignment. :grin:

knaving:
Teachers suggested using interrupts and that's all I know.

Oops! Teachers aren't "the full quid" then. Well, you might get away with some gross plagiarism. Maybe. :grinning:

knaving:
Is there a better way I could do this?

Well, there's a proper way to do it for a start. I haven't examined Robin's code precisely, but offer a core routine which you can try to demonstrate how a toggle button with proper debouncing should behave. Do try this out. Obviously it does not use interrupts in the code itself (but - the hidden libraries actually do but in a correct fashion!)

Note the usual considerations - you must not use digital pins 0 or 1 as you appear to have, for anything; I will leave you to research why. I have allocated the button to pin 10 Next, you do not have pushbuttons connected to Vcc, you connect them to ground and then you do not require a pull-up as it is built into the MCU if you simply enable it. My code will toggle the "L" LED.

// Button toggle with extreme reliability!

const int led1Pin =  13;    // LED pin number
const int button1 =  10;
int led1State = LOW;        // initialise the LED
char bstate1 = 0;
unsigned long bcount1 = 0; // button debounce timer.  Replicate as necessary.

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check
// Routines by Paul__B of Arduino Forum
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
// Routines by Paul__B of Arduino Forum
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
  switch (*butnstate) {               // Odd states if was pressed, >= 2 if debounce in progress
  case 0: // Button up so far, 
    if (button == HIGH) return false; // Nothing happening!
    else { 
      *butnstate = 2;                 // record that is now pressed
      *marker = millis();             // note when was pressed
      return false;                   // and move on
    }

  case 1: // Button down so far, 
    if (button == LOW) return false; // Nothing happening!
    else { 
      *butnstate = 3;                 // record that is now released
      *marker = millis();             // note when was released
      return false;                   // and move on
    }

  case 2: // Button was up, now down.
    if (button == HIGH) {
      *butnstate = 0;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 1;               // jackpot!  update the state
        return true;                  // because we have the desired event!
      }
      else 
        return false;                 // not done yet; just move on
    }

  case 3: // Button was down, now up.
    if (button == LOW) {
      *butnstate = 1;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 0;               // Debounced; update the state
        return false;                 // but it is not the event we want
      }
      else 
        return false;                 // not done yet; just move on
    }
  default:                            // Error; recover anyway
    {  
      *butnstate = 0;
      return false;                   // Definitely false!
    }
  }
}

void setup() {
  pinMode(led1Pin, OUTPUT);      
  pinMode(button1, INPUT);      
  digitalWrite(button1,HIGH);        // internal pullup all versions
}

void loop() {
  // Toggle LED if button debounced
  if (butndown(digitalRead(button1), &bcount1, &bstate1, 10UL )) {
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW; 
    } 
    digitalWrite(led1Pin, led1State);
  } 
}

knaving:
Do you at least agree with KenF that a separate power source is needed? Could you offer a solution to my problem?

You absolutely must not expect the Arduino regulator to power anything other than a simple indicator LED - and even that with care. Of course you need a separate supply for the motor.

You would only have to add your potentiometer reading and PWM code into the example I gave and alter the output pin allocation to completely solve your problem as stated. Another hint - only alter the AnalogWrite if the potentiometer value actually changes - and even then it would probably be a good idea to average it over several readings.

Awesome, this is great. Thank you Paul. I got it all working. For the sake of education, why would I not want to use interrupts here? And why would I want to average the potentiometer readings?
Now all I have to do is program a servo to work with everything else. I'm assuming I'll want to draw power for the servo from the 6V battery pack I have the motor attached to also?

"Now all I have to do is program a servo to work with everything else"

Pin interrupts & a positioning pot could indeed be used for the control of a SERVO motor .....but you left out that little nugget of information till now.

knaving:
Awesome, this is great. Thank you Paul. I got it all working.

So now post your working code for us to look at.

knaving:
For the sake of education, why would I not want to use interrupts here?

I did explain that in my other reference. As did Henry_Best. Perhaps most significantly because interrupts and contact bounce are a very bad combination.

knaving:
And why would I want to average the potentiometer readings?

To reduce jitter caused by minor variations from one reading to the next. Perhaps I should have explained that I mean a "moving average" which is a little more challenging, essentially do not change the setting (PWM) value unless the input reading changes significantly.

knaving:
Now all I have to do is program a servo to work with everything else.

As I understand it, the servo library uses either timers or timers and interrupts to work "in the background" for a given set servo value.

knaving:
I'm assuming I'll want to draw power for the servo from the 6V battery pack I have the motor attached to also?

Yes.

Hello i would like to ask what kind capacitor, diode did u use? and you use also PNP transistor?

Newbie0027:
Hello i would like to ask what kind capacitor, diode did u use? and you use also PNP transistor?

Newbie, You'll notice that this was a school assignment from last year. I doubt very much that knaving will be back. From the circuit he's posted I'd imagine it was an NPN transistor. The diode is probably a run of the mill, generic diode. (just about any diode will work here).

KenF:
Newbie, You'll notice that this was a school assignment from last year. I doubt very much that knaving will be back. From the circuit he's posted I'd imagine it was an NPN transistor. The diode is probably a run of the mill, generic diode. (just about any diode will work here).

Thank you sir kenf, by the way sir. i would like to ask question, can i use the push button to control the DC motor like when i press the button it will clockwise and stop a specific point and stay for how many minutes or seconds, and when i press it again it will counter-clockwise then stop until it reach the 0 degree.

To achieve this would require a much more sophisticated circuit but YES it could be achieved.

What I would suggest, however, is that you post this as a new thread. By continuing here, your questions will only be noticed by those of us that responded to the original thread (and we know at least one of those is now MIA :slight_smile:

Click here to start.

thanks sir.. :smiley: