Button activated motor with 3 second run time

Hi,
I'm doing a project for school and my team needs some help.
Our goal is to push a button that will then run a motor for 3 seconds then stop. Holding down the button shouldn't do anything to affect the motor, unless it's released and pressed again. We aren't sure whether it is the code, wiring or both. Our current code is:

const int buttonPin = 9;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
  millis();
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
  
  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if ((buttonState == HIGH) && millis() <=3000){
    digitalWrite(ledPin, HIGH); 
    delay(3000);
    digitalWrite(ledPin, LOW);
  }
else {
    // turn LED off:
    digitalWrite(ledPin, LOW);
  }
}

and our wiring follows the diagram on https://www.arduino.cc/en/Tutorial/Button)

Thanks for any help.

1 Like

This is not the right way to use millis()

if ((buttonState == HIGH) && millis() <=3000){

unless the purpose is only to respond during the first 3000 msecs after the Arduino starts. After that time millis() will not be less than 3000 for about 49 days.

Have a look at how millis() is used to manage timing in Several things at a time.

...R

team member of the original poster here.
Our new code is this:

const int buttonPin = 9;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin
unsigned long currentMillis = 0;
unsigned long runDuration=3000;
unsigned long previousButtonMillis = 0; // time when button press last checked
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
  currentMillis=millis();
  updateOnBoardState();
  }

void updateOnBoardState() {  
  if (buttonState == HIGH) {
         // if the Led is off, we must wait for the interval to expire before turning it on
     if (currentMillis - previousButtonMillis <= runDuration) {
         // time isnt up, so change the state to HIGH
      digitalWrite(ledPin,HIGH);
         // and save the time when we made the change
      previousButtonMillis = currentMillis;
     }  else 
        digitalWrite(ledPin,LOW);  
   }
 
 else {  // i.e. if onBoardLedState is LOW
  digitalWrite(ledPin,LOW);
  previousButtonMillis = currentMillis;
   } 
 }

I don't see the problem. When I press the button the motor just goes on forever. Is there something wrong with
previusButtonMillis = currentMillis ?
That seems like the most likely mistake to me. Everything's from the recommended post

If you add a Serial.print() to display the value (currentMillis - previousButtonMillis) what do you see?

Try this

int button = 5;
int led = 3;
byte buttonstate;

void setup(){
pinMode(3,OUTPUT);
pinMode(5,INPUT);

 Serial.begin( 9600 );
}
void loop(){
 buttonstate=digitalRead(button);
 if (buttonstate==HIGH){
   for (int i=0;i<4;i++){
       if (i>=1 && i<4){
          digitalWrite(led,HIGH);
          Serial.println(i);
       }
       delay(950);
    }
 }
 else {
    digitalWrite(led,LOW);
 }
 
}

I added 10 seconds for led to be HIGH, and to stop the led (or motor) in that period(10sec) push the button again.

int button = 5;
int led = 3;
byte buttonstate;
byte buttonStop;

void setup(){
pinMode(3,OUTPUT);
pinMode(5,INPUT);

 Serial.begin( 9600 );
}
void loop(){
 buttonstate=digitalRead(button);
 if (buttonstate==HIGH){
   for (int i=0;i<11;i++){
       
       if (i>=1 && i<11){
          buttonStop=digitalRead(button);
       if (buttonStop==HIGH){
        digitalWrite(led,LOW);
        i=11;
        }
          digitalWrite(led,HIGH);
          Serial.println(i);
       }
       delay(950);
    }
 }
 else {
    digitalWrite(led,LOW);
 }
 
}

Our new code is this:

const int buttonPin = 9;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin
unsigned long currentMillis = 0;
unsigned long runDuration=3000;
unsigned long previousButtonMillis = 0; // time when button press last checked
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
  currentMillis=millis();
  updateOnBoardState();
  }

void updateOnBoardState() {  
  if ((buttonState == HIGH) && (currentMillis - previousButtonMillis <= runDuration)){
         // if the Led is off, we must wait for the interval to expire before turning it on
     if (currentMillis - previousButtonMillis <= runDuration) {
         // time isnt up, so change the state to HIGH
      digitalWrite(ledPin,HIGH);
         // and save the time when we made the change
     }  else 
        digitalWrite(ledPin,LOW);  
        Serial.println(currentMillis-previousButtonMillis);
   }
 
 else {  // i.e. if onBoardLedState is LOW
  digitalWrite(ledPin,LOW);
  previousButtonMillis = currentMillis;
   } 
 
 }

with the serial.println it goes from 0 to 3000 then resets as we want, but only the first time the button is pressed does the motor stop at 3000. every other time its pressed it stops after a couple loops of 3000, generally 2-4. (We removed currentmillis=previousbuttonmillis from the second if)

Why have you the same test on two successive lines?

  if ((buttonState == HIGH) && (currentMillis - previousButtonMillis <= runDuration)){
         // if the Led is off, we must wait for the interval to expire before turning it on
     if (currentMillis - previousButtonMillis <= runDuration) {

What happens if you simplify the first line to

 if ((buttonState == HIGH) {

It is usual to update the time variable after testing it - like this

if (currentMillis - previousButtonMillis <= runDuration) { 
       previousButtonMillis = currentMillis;

It may be correct not to, but why are you not doing that?

It would help if you write down in English the steps that you want your program to take.

...R

Having the same test twice was just sloppy coding to get both else statements that I now realize i don't need. Whenever I have the line to update the variable in the loop the time stays at 0 according to serial.print and the motor stays on. When I have it update in the else statement it resets properly after the 3 seconds are up in the if part. Also I fixed the issue where it runs for multiples of 3 seconds by putting a short delay at the end of the else statement. Here's a cleaned up version with explanations.

const int buttonPin = 9;
const int ledPin =  13;      // the number of the Motor pin
unsigned long currentMillis = 0;
unsigned long runDuration=3000;
unsigned long previousButtonMillis = 0; // time of previous button press
int buttonState = LOW;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
 //create variable of total time
 currentMillis=millis();
//To make a new void part. I don't know why but it was in the example
  updateOnBoardState();
  }

void updateOnBoardState() {  
  if ((buttonState == HIGH) && (currentMillis - previousButtonMillis <= runDuration)){
      // if the button is pressed and if motor has been running for less then 3 seconds 
      digitalWrite(ledPin,HIGH);
         // activate the motor
        Serial.println(currentMillis-previousButtonMillis);
        //Display how long the motors been running
       
   }
    else {  
    //if the button isnt pressed or the motor has been on more than 3 seconds
      digitalWrite(ledPin,LOW);
      //Turn off motor
      previousButtonMillis = currentMillis;
      // Record the time the motor automaticly shut off after 3 seconds
      delay(100);
     }  }

Now the only problem is getting the motor to stop if the button is still being held down after 3 seconds. I don't know if this is physically possible with our circuit. We have the power running through the switch to the motor with the arduino board in parallel as in this picture.

The post was to large, here's the picture

Image from Reply #8 so we don't have to download it (they are both the same). See this Image Guide

...R

It's a nice photograph but I can't see its value for sorting out this problem. If you want to show us your connections then make a pencil drawing of the circuit and post a photo of the drawing.

You have this in your code

  if ((buttonState == HIGH) && (currentMillis - previousButtonMillis <= runDuration)){
   // [[....]
  }
  else {

  }

What condition is the ELSE intended to deal with? When the button is LOW or when the time is wrong?

If you are using millis() for timing then don't also use delay()

Can you explain again what it is that you want to happen because what you have said in Reply #7 is not clear to me.

...R

The else part is to , i believe, deal with either when the button is low or the time is wrong. Because the if is only true if both statements are true either one being false will activate the else and turn off the motor. As for using millis() and delay() thats the only way i found to make it work. I couldn't make just delay do anything and just millis seems to stop the motor at random times, sometimes it stops at 3 seconds like i want, sometimes less but mostly a seemingly random high number. Putting a small delay before the code checks if there's a new input made the motor stop at 3 seconds every time.

Our end goal is to have the motor run for only 3 seconds no matter how short or long the button is pressed. The only way to get it to run again is to press the button again, releasing and pressing it if it was held down, again running for just 3 seconds and so on.

This is our circuit:

Why is pin 13 connected to GND n your diagram. You risk destroying pin 13 if you output a HIGH on it.

...R

The wire from pin 13 should be going to the other side of the motor, sorry about that.

spoc66:
The wire from pin 13 should be going to the other side of the motor, sorry about that.

That does not make much sense either. Then it would be connected to Pin 9 with a similar risk of damage.

And you should NOT be powering a motor from the Arduino 5v pin, or (even more importantly) from an I/O pin.

Post the correct diagram.

...R

spoc66:
Our end goal is to have the motor run for only 3 seconds no matter how short or long the button is pressed. The only way to get it to run again is to press the button again, releasing and pressing it if it was held down, again running for just 3 seconds and so on.

Then you need edge detection and a variable to 'remember' that an edge was detected.

Well this is a problem. That's what I've been using to test it. Once its working its going to be installed in a small electric car and using its motor and 6v battery. Should I be using this? https://www.arduino.cc/en/Tutorial/TransistorMotorControl

TransistorMotor_bb.png

TransistorMotor_bb.png

@spoc66, the drawing in Reply #11 was much easier to understand than the Fritzing picture. Please do another drawing with the correct connections. It is much too easy to misunderstand the Fritzing picture.

...R