I've been searching and haven't been able to find an answer to my question. I have one servo and one push button ("normally closed"/"push to make"). When i push the button i want the servo sweep loop to run continuously until the button is pressed again, which makes it stop, then push again to make it start and run continuously until next time the button is pressed and so on...
I realize I have to use millis instead of delay for the servo sweep and ofcourse debounce the button, but how would you do the rest of the stuff?
You need to detect when the button becomes pressed. Look at the StateChangeDetection example in the IDE. When you have detected the button press, set a boolean variable to true. Next time the button becomes pressed set it to false. In loop() sweep the servo when the variable is true.
Hi,
("normally closed"/"push to make").
I think you mean Normally Open, push to make(Close)
Also when you connect your button make sure you have a pull up or pull down resistor with it.
The digital input needs to see a HIGH (5V) or a LOW (0V), the switch can only provide a HIGH or a LOW input, so the resistor takes care of the input when the switch is open.
Google it.
The arduino has a command to turn internal Pull-Up resistors.
Tom...
UKHeliBob:
You need to detect when the button becomes pressed. Look at the StateChangeDetection example in the IDE. When you have detected the button press, set a boolean variable to true. Next time the button becomes pressed set it to false. In loop() sweep the servo when the variable is true.
Okey, I understand it in theory looking at the Led example. But what would the code look like with a servo? Newbie here
TomGeorge:
Hi,
I think you mean Normally Open, push to make(Close)
Also when you connect your button make sure you have a pull up or pull down resistor with it.
The digital input needs to see a HIGH (5V) or a LOW (0V), the switch can only provide a HIGH or a LOW input, so the resistor takes care of the input when the switch is open.
Google it.
The arduino has a command to turn internal Pull-Up resistors.
Tom...
Ops, my bad. I have wired a pulldown resistor for the button, so no worries there
Okey, I understand it in theory looking at the Led example. But what would the code look like with a servo?
Pseudo code
start of loop()
if the button has become pressed
sweepServo = !sweepServo
end if
if sweepServo is true
if it's time to move the servo
move the servo to its next position, reversing it if necessary
end if
end if
end of loop()
#include <Servo.h>
const int buttonPin = 2;
const int ledPin = 13;
Servo servo1;
int buttonPushCounter = 0;
int buttonState = 0;
int lastButtonState = 0;
int pos = 0;
void setup()
{
pinMode(buttonPin, INPUT);
pinMode(ledPin, OUTPUT);
servo1.attach(9);
}
void loop()
{
buttonState = digitalRead(buttonPin);
if (buttonState != lastButtonState)
{
if (buttonState == HIGH) {
buttonPushCounter++;
}
else {
}
}
//Servo sweep 1:
if (buttonPushCounter % 1 == 0)
{
digitalWrite(ledPin, HIGH);
for(pos = 0; pos <= 10; pos += 10)
{
servo1.write(pos);
delay(90);
}
for(pos = 10; pos>=0; pos-=10)
{
servo1.write(pos);
delay(90);
}
}
else
{
digitalWrite(ledPin, LOW);
}
//Servo sweep 2:
if (buttonPushCounter % 2 == 0)
{
digitalWrite(ledPin, HIGH);
for(pos = 0; pos <= 90; pos += 10)
{
servo1.write(pos);
delay(90);
}
for(pos = 90; pos>=0; pos-=10)
{
servo1.write(pos);
delay(90);
}
}
else
{
digitalWrite(ledPin, LOW);
}
lastButtonState = buttonState;
}
Thanks, I got it to work The next challenge for me is to make it incremental. One press for continuously running "Servo sweep 1", and second press for continuously running "Servo sweep 2", and a third press to go back to "Servo sweep 1" and so on. The code above isn't beautiful I know, I deliberately skipped debounce and millis instead of delay to keep it simple until I've got the button increments working, I am aware of the milliseconds of unresponsiveness and potential button press noise when testing .
With my code one press starts running "Servo sweep 1" continuously and the second press starts continuously cycling through "Servo sweep 1" then "Servo sweep 2". I can't figure out why?
Good news that it works, that is always very satisfying. Do you have a pulldown resistor on the switch input ? If not then consider using
pinMode(buttonPin, INPUT_PULLUP);
to activate the built in pullup resistor. You will also need to change the button press detection logic.
Now, keep that code and create an improved version.
Suggestions
Make any variables that will never have a value larger than 255 and don't need to go negative into bytes instead of ints. This save memory. Not important now, but it could be in larger programs.
Change to millis() for the timing. Do it now before the program gets more complicated.
Personally I would use switch/case to handle the logic rather than a series of if as I find it easier to read, understand some moths later, to add to and to maintain. Something like this
start of loop()
if the button has become pressed
update the state variable
save the start time of the state
end if
switch based on state
case 0
if it is time to move the servo (based on start time and current millis())
move the servo
end if
end case
case 1
if it is time to move the servo (based on start time and current millis())
move the servo
end if
end case
end of loop()
#include <Servo.h>
const int buttonPin = 2; //Normally open push button connected with pull down resistor on pin 2
const int ledPin = 13; //led connected to pin 13
Servo myservo; //Servo
int buttonPushCounter = 0; //For storing number of button presses
int buttonState; //Used for debounce
int lastButtonState = LOW; //Used for debounce
long lastDebounceTime = 0; //Used for debounce
long debounceDelay = 50; //Used for debounce
void setup()
{
pinMode(buttonPin, INPUT); //push button on pin 2
pinMode(ledPin, OUTPUT); //led on pin 13
myservo.attach(9); //servo on pin 9
Serial.begin(9600);
}
void loop() {
{
int reading = digitalRead(buttonPin); //read buttonpin to variable "reading"
if (reading != lastButtonState) { //if reading is not equal to lastbuttonstate
lastDebounceTime = millis(); //assign millis to lastDebounceTime
}
if ((millis() - lastDebounceTime) > debounceDelay) { //if millis subtracted by lastDebounceTime is bigger than debouncedelay (50ms)
if (reading != buttonState) { //if buttonstate has changed, that is, if reading is not equal to LOW in first loop
buttonState = reading; //set buttonstate to reading
if (buttonState == HIGH) { //if buttonState has been assigned HIGH from "reading" in previous line
buttonPushCounter++; //increment PushCounter by one
Serial.println("on");
Serial.print("number of button pushes: ");
Serial.println(buttonPushCounter);
}
}
}
lastButtonState = reading; //last in each loop, set reading to LOW
}
switch(buttonPushCounter) //switch...case with regards to buttonPushCounter
{
case 1: //Run continuously as long as buttonPushCounter is 1, and so on...
//Servo sweep code 1 here
break;
case 2:
buttonPushCounter = 0; //Resets buttonPushCounter to 0, so the third press returns to case 1
//Servo sweep code 2 here
break;
} }
Great tips, thanks. I will implement bytes instead of ints later. I've been struggeling for about a week now with figuring out how to control the servo with millis() instead of delays, and I'm really stuck. The code above is what I've accomplished so far, and it seems to work fine.
This is what I'm trying to do now:
Push 1 makes the servo loop through this continuously:
-servo goes from 0 to 30 degrees
-servo waits 300 ms in this position
-servo then returns from 30 degrees to 0 degrees
Push 2 makes the servo loop through this continuously:
-servo goes from 0 to 60 degrees
-servo waits 500 ms in this position
-servo then returns from 60 degrees to 0 degrees
I've done a lot of experimenting with millis() and state variables, but I just can't get the servo to wait in the end positions. Any ideas?
It looks to me like you have 7 states as follows
0 - waiting for input. Servo is stopped at 0 degrees.
1 - moving servo from 0 to 30 degrees. Each time through loop() when in this state test whether a short period of time has elapsed and if so move the servo a little towards 30 degrees. When 30 degrees is reached move to state 2
2 - waiting for 300 mS. Each time through loop() test millis() to determine whether 300 mS has elased. When it has elapsed move to state 3
3 - moving servo from 30 degrees to 0 degrees. Each time through loop() when in this state test whether a short period of time has elapsed and if so move the servo a little towards 0 degrees. When 0 degrees is reached move to state 1
4 - moving servo from 0 to 60 degrees. Each time through loop() when in this state test whether a short period of time has elapsed and if so move the servo a little towards 60 degrees. When 60 degrees is reached move to state 5
5 - waiting for 300 mS. Each time through loop() test millis() to determine whether 300 mS has elased. When it has elapsed move to state 6
6 - moving servo from 60 degrees to 0 degrees. Each time through loop() when in this state test whether a short period of time has elapsed and if so move the servo a little towards 0 degrees. When 0 degrees is reached move to state 4
In the loop() function read the button pin. If the button becomes pressed and you are in state 0 set the state to 1 else set the state to 4
I may have got the details wrong but you should see the idea. Note that as well as changing the state when a condition is met you need to set any entry parameters needed for the target state such as saving the current value of millis() before entering a state that waits for a time period to pass.