When button is pressed in loop question

hey guys, so i have a motor, mosfet and a button, my question is, when i press the button it will turn the motor on, but it will stay on while the button is pressed, i need it so when the button is pressed then it would only turn the motor on for a period of time, eg, 1000ms how would i go about doing that?, currently using a simulator as im waiting on parts…

int B = 2;  // Trigger microswitch
int outPin = 2;
int L = 9;  // Mosfet
int potIn = A0;
int speedVal;
unsigned long startTime = millis();
unsigned long  wait=millis();
int passFlag = 0;
//______________ Setup __________
void setup()
{
  
//INPUT_PULLUP
 pinMode(outPin, INPUT_PULLUP);
  pinMode(L, OUTPUT);
  Serial.begin(9600);
 digitalWrite(outPin,1);
}
//____________ Loop ______________________
void loop()
{

   if (digitalRead(outPin) == 0){
{             
        
 digitalWrite(L,HIGH); 
 delay(200);        
 digitalWrite(L,LOW);  
  delay(1000);  
if (digitalRead(outPin) == 1);     
     digitalWrite(L,LOW); 
     
     }
     
  
     //   startTime = millis(); 
 // delay(2000); 
        // while (digitalRead(B) == LOW)
     
    // while((millis()-wait)<1500)
      // startTime = millis(); 
//  speedVal = analogRead(potIn); 
 // speedVal = map(speedVal, 0, 1023, 0, 255);  
  // Write PWM to transistor
  //analogWrite(outPin, speedVal); 
//  delay(20); 
   
}
          }

       

here is my current set up:

Imgur

ignore the commented code, it will eventuality be a TOF sensor and it will run the motor in a “burst” between a value and allways on between another,

Use a state machine. Here’s a completely untested example:

enum ApplicationState {
  WaitForPress,
  MotorRunning
};

static void startMotor() {
  // start the motor here
}

static void stopMotor() {
  // stop the motor here
}

static bool isButtonPressed() {
  // determine if the button is pressed here
}

static enum ApplicationState currentState = WaitForPress;

void setup() {
  // put your setup code here, to run once:
  
}

void loop() {

  static uint32_t startTime;
  
  switch (currentState) {

    case WaitForPress:
    if (isButtonPressed()) {
      startMotor();
      startTime = millis();
      currentState = MotorRunning;
    }
    break;

    case MotorRunning:
    if (millis() - startTime >= 1000) {
      stopMotor();
      currentState = WaitForPress;
    }
    break;
    
  }
}

Besides being a nice way of programming complex behavior, they can also help create an illusion of multitasking if written in the right way.

2 Likes

brilliant explanation and example to do things in “parallel”.

brilliant thanks ill see if i can get it to work, i come from vb.net and still learning this code, i was thinking i could put i into a “void” of its own? well i would need 2 voids with the motor code, as when im using the TOF i would need to call the different “states”

Thanks for the help guys really appreciate it

In VB, you have “functions” which return a value and “subs” which do not return anything.

In C, there are only functions and you must always give a return type when you declare the function. If you don’t want to return anything, equivalent to a “sub”, you put return type as void.

So they are called “functions”, not “voids”.

Update i got it working, but its not doing what i need it to, it will wait for 2000ms then turn motor on, and it will stay on intill i let go of the button, how would i control how long the motor is on and off for?
example

button in pressed down
turn motor on for 1000ms
Turn motor off

and if i leave the button pressed down it wont “loop”, i did have it working in an old project, but the code is messy and wanting to start again

int outPin = 2;
int L = 9;  // Mosfet
int potIn = A0;
int buttonState = 0;   

enum ApplicationState {
  WaitForPress,
  MotorRunning
};

static void startMotor() {
  // start the motor here
  digitalWrite(L,LOW);
}

static void stopMotor() {
  digitalWrite(L,HIGH);  
  // stop the motor here
}

static bool isButtonPressed() {
  // determine if the button is pressed here
  if (digitalRead(outPin) == 0); 

}

static enum ApplicationState currentState = WaitForPress;

void setup() {
  // put your setup code here, to run once:
   pinMode(outPin, INPUT_PULLUP);
  pinMode(L, OUTPUT);
  digitalWrite(L,LOW);  
  
  
}

void loop() {
buttonState = digitalRead(outPin);
  static uint32_t startTime;
  
  switch (currentState) {

    case WaitForPress:
    if (isButtonPressed()== 1) {
      startMotor();
      startTime = millis();
      currentState = MotorRunning;
    }
    break;

    case MotorRunning:
    if (millis() - startTime >= 2000) {
      stopMotor();
      currentState = WaitForPress;
    }
    break;
    
  }
}

State machines are a good way of coding complex behaviours, I agree, but this project is so simple that I feel using state machines and multiple functions would just make it more difficult to read & understand! Keep it simple.

You need to trigger the motor to start when the button changes from being not pressed to being pressed. Right now you are triggering the motor any time the button is pressed.

To do that, you need a variable to remember if the button was pressed last time you checked it. Then you can write code to check if “the button wasn’t pressed before, but it is now”

Blockquote

What is easier to read and understand is entirely subjective I suppose. I don’t find state machines any harder to understand. While this could certainly easily be coded in another way as well, states machines are easily extendable which in my opinion makes it worthwhile to use them from the beginning.

In this case it’s also good practice with a simple example.

You are not returning the state of the button.

It should be:

static bool isButtonPressed() {
    return (digitalRead(outPin) == LOW);
}

The function returns true if the signal is low. I figure this is correct since you configure the pin as an input with a pullup.

Thanks for the replys, i got it working to what ineed somehow?, i went back and had a look at my old code, and integrated it into the code you helped me with, this is as far i can go intill i get my arduino replacement, here is the code :

int outPin = 2;
int L = 9;  // Mosfet
int potIn = A0;
int buttonState = 0;   
unsigned long startTime = millis();
enum ApplicationState {
  WaitForPress,
  MotorRunning
};

static void startMotor() {
  // start the motor here
  digitalWrite(L,LOW);
}

static void stopMotor() {
  digitalWrite(L,HIGH);  
  // stop the motor here
}

static bool isButtonPressed() {
  // determine if the button is pressed here
 // if (digitalRead(outPin) == 0); 
 return (digitalRead(outPin) == LOW);
}

static enum ApplicationState currentState = WaitForPress;

void setup() {
  // put your setup code here, to run once:
   pinMode(outPin, INPUT_PULLUP);
  pinMode(L, OUTPUT);
  digitalWrite(L,LOW);  
  
  
}

void loop()
{ 
   if (digitalRead(outPin) == 1){  
delay(100);// How Long to Keep the motor running for
    startTime = millis();   
    //get the start time before entering the loop
    while ( digitalRead(outPin) == LOW && millis() < (startTime + 400)) {
      digitalWrite(L, HIGH );           
    }   
  }
  buttonState = digitalRead(outPin); 
  {                 
if (digitalRead(outPin) == 1);     
     digitalWrite(L,LOW);     
     }      
          }

I expanded a bit on my example. Again it’s not tested in any way, so it might need some tweaking. I keep pushing the state machine thing, since it’s one of the most useful concepts you can learn while writing code for microcontrollers. Or in any type of programming really.

It might seem a little daunting at first, but when you’ve tried it a few times it really isn’t that bad.

// Here we define the states that each task be in. There's no requirement that the
// states are defined as an enum, but it's very readable if they are.
enum MotorTaskState {
  WaitForPress,
  MotorRunning,
  WaitForRelease
};

enum BlinkyTaskState {
	LedOff,
	LedOn
};

static const int buttonPin = 2;

static const int motorPin = 9;

static const int ledPin = LED_BUILTIN;

static const int ledOffTime = 800;

static const int ledOnTime = 200;;

// The following functions might seem a little superfluous since they're
// only a single line. But I personally like that you can call a function
// with a name that describes what it does. And using the inline keyword
// there's no function call overhead anyway.
static inline void startMotor() {
	// Make sure that a high signal turns on the motor.
	digitalWrite(motorPin, HIGH);
}

static inline void stopMotor() {
	// Make sure that a low signal turns off the motor.
	digitalWrite(motorPin, LOW);
}

static inline bool isButtonPressed() {
	// Make sure that the signal is low when the button is pressed.
	return (digitalRead(buttonPin) == LOW);
}

static inline void turnLedOn() {
	digitalWrite(ledPin, HIGH);
}

static inline void turnLedOff() {
	digitalWrite(ledPin, LOW);
}

static void MotorTask() {
	
	// "Task" functions are called periodically. The motor task that you want the microcontroller to perform
	// is broken into 3 distinct states that the state machine transition between based on some input.
	// Each state is kept short on purpose such that it allows other tasks to run as frequently as possible.
	
	// This variable is used for storing the current state of the task.
	// The simplified explanation of what the static keyword in this case, is that it
	// the value is kept even when the function returns.
	static enum MotorTaskState currentState = WaitForPress;
	
	// This variable is used for storing the time that some specific action/event/transition.
	static uint32_t t = 0;
	
	// Keep in mind that only one case is entered each time the function is called.
	switch (currentState) {
		
		// Here we check for a button press. If a button press occurs we start the motor,
		// store the time the motor was started and transition the state machine to the MotorRunning state.
		case WaitForPress:
		if (isButtonPressed()) {
			startMotor();
			t = millis();
			currentState = MotorRunning;
		}
		break;
		
		// In this state we check whether a second has passed. If a second has passed we stop the motor and
		// transition to the WaitForRelease state.
		case MotorRunning:
		if (millis() - t >= 1000) {
			stopMotor();
			currentState = WaitForPress;
		}
		break;
		
		// I don't know whether this is a project requirement but it sounded a bit like it. We check whether the
		// button is released before transitioning back to the WaitForPress state and starting all over again.
		case WaitForRelease:
		if (!isButtonPressed()) {
			currentState = WaitForPress;
		}
		break;
	}
}

static void BlinkyTask() {
	
	// This function is just to illustrate that you can parallel tasks this way.
	static enum BlinkyTaskState currentState = LedOff;
	
	static uint32_t t = 0;
	
	switch (currentState) {
		
		// If more than 'ledOffTime' time has passed, turn on the LED and transition to the LedOn state.
		case LedOff:
		if (millis() - t >= ledOffTime) {
			turnLedOn();
			currentState = LedOn;
		}
		break;
		
		// If more than 'ledOffTime + ledOnTime' time has passed, increment the timing variable by that amount
		// and transition to the LedOff state.
		case LedOn:
		if (millis() - t >= ledOffTime + ledOnTime) {
			turnLedOff();
			t += ledOffTime + ledOnTime;
			currentState = LedOff;
		}
		break;
		
	}
}

void setup() {
	pinMode(buttonPin, INPUT_PULLUP);
	pinMode(motorPin, OUTPUT);
	pinMode(ledPin, OUTPUT);
}

void loop() {
	
	// Here we just repeatedly call the task functions. If the execution time of the functions is kept short, they should
	// appear to be executing in parallel.
	MotorTask();
	BlinkyTask();
}

Hi,

Have you at least tried compiling your code?
Do you have any hardware to try it on?

Tom… :grinning: :+1: :coffee: :australia:

Whipping together example code is quick while digging through drawers takes time. This was the amount of my free time I decided to put in. The point was not delivering a finished project but to give a base which he could build on.

It compiles fine.

Brill, thank you, i have got some books on there way to help, you have been good help

so iv cleaned up the code a bit more and converted the pot value to a percentage: its running smoothly but i still cant get my head around how to stop the motor while the pot value is between 55 and 90
i want to be able to specify how long the motor is running while its inside that value, i just cant work it out even by reading the code supplied, at the moment, while its between 55 and 90, it will start, /stop, start/stop, when i just want it to do Start then STOP. not repeat it while between the value 55 and 90

int outPin = 2;
int L = 9;  // Mosfet
int potIn = A0;
int potPin = A0;
int sensorValue = 0;
int percent; // variable used to store the percentage value
int buttonState = 0;
unsigned long startTime = millis();
//______________________________________//
enum ApplicationState {
  WaitForPress,
  MotorRunning
};
static void startMotor() {
  // start the motor here
  digitalWrite(L,HIGH);
}
static void stopMotor() {
  digitalWrite(L,LOW);  
  // stop the motor here
}
static enum ApplicationState currentState = WaitForPress;
//________________________________________//

void setup()
{
 pinMode(outPin, INPUT_PULLUP);
  pinMode(L, OUTPUT);
  digitalWrite(L,LOW);    
  pinMode(A0, INPUT);
   Serial.begin(9600);
}
//______________________________________//
void loop()
{
  int sensorValue = analogRead(A0);
  float value = sensorValue * (100 / 1023.0);
  Serial.println(value);
  //\\__________________________________//\\
    

  
value = map(sensorValue, 0, 1023, 0, 100); // convert potentiometer reading to a percentage  
  delay(10); // Delay a little bit to improve simulation performance
  
  
     if ( (value > 55)&&(value < 90)){                      
        startMotor();
          delay(100);
        stopMotor();  
   delay(1000); 
   // }   
  }

  
  
else if ((value > 20) &&(value < 40)){ 
  startMotor();
   delay(100);
      }  
  
  
else if (value > 10){
   stopMotor();  
   delay(100);
      }
}
//______________________________________//

again here is the code:
i get the static void functions now, and that will help in the future projects.

if i use

if ( (value > 55)&&(value < 90)){    
   while (millis() < (startTime + 400 )) {          
 startMotor();
         delay(100);
       stopMotor();  
 }
}

it works but only for a short time at the very start if i then change the value outside 55 - 90, then go back into it it wont run, that part again

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.