Help! 4 x SG90 Servos with 3 x push buttons

I am trying to set up 4 x sg90 servos on a Arduino Nano.
I don't want anything to happen until a button is pressed the servos each have a set set of actions that i want to run when a button is pressed.
Once the actions have completed i want it to wait until i press a button then go whatever action is set, if that makes sense.

I have tried many times but either the buttons don't work properly or the servos just start without any input from the buttons without being pushed.

he is the best version i have come up with so far, can someone please have a look and try and correct my errors.

Here is a link to my wokwi sim on the project: Sweep.ino - Wokwi Arduino and ESP32 Simulator


#include <Servo.h>

Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;

#define button 2 //attaches button to pin 2
#define button2 3 //attaches button to pin 3
#define button3 4 //attaches button to pin 3

int buttonState = 0; //buttonState stores state of input

const byte positions[] = {0, 90,};
unsigned long period = 500;
unsigned long startTime = 0;
int greenLED = 11;

void setup()
{
  pinMode (greenLED, OUTPUT);
  pinMode(button, INPUT_PULLUP); //sets button as input
  buttonState = digitalRead(button);
  buttonState = digitalRead(button2);
  buttonState = digitalRead(button3);
}


void loop()
{
  
  digitalWrite (greenLED, HIGH);
  delay (100);
  digitalWrite (greenLED, LOW);
  delay (100);

  servo1.attach(5);
  servo2.attach(6);
  servo3.attach(9);
  servo4.attach(10);
  
  buttonState = digitalRead(button); //reads state of buttonunsigned long currentTime = millis();
  unsigned long currentTime = millis();
  if ((currentTime - startTime >= period) && (buttonState == LOW))
  {
    servo1.write(90);
    servo2.write(90);
    servo3.write(90);
    servo4.write(90);
    delay(1200);
    servo1.write(0);  //run this servo sequence
    delay(1200);
    servo1.write(90);
    servo2.write(0);
    delay(1200);
    servo2.write(90);
    servo3.write(0);
    delay(1200);
    servo3.write(90);
    servo4.write(0);
    delay(1200);
    servo4.write(90);
    delay(1200);
    servo1.write(0);  //run this servo sequence
    delay(1200);
    servo1.write(90);
    servo2.write(0);
    delay(1200);
    servo2.write(90);
    servo3.write(0);
    delay(1200);
    servo3.write(90);
    servo4.write(0);
    delay(1200);
    servo4.write(90);
    delay(1200);
  };
  if (buttonState == HIGH) { //button is not pressed 
  }
  buttonState = digitalRead(button2); //reads state of buttonunsigned long currentTime = millis();
  if ((currentTime - startTime >= period) && (buttonState == LOW))
  {

    servo1.write(90);
    servo2.write(90);
    servo3.write(90);
    servo4.write(90);
    delay(8000);
    servo1.write(0);
    servo2.write(0);
    servo3.write(0);
    servo4.write(0);
    delay(1000);
  };
  if (buttonState == HIGH) { //button is not pressed 
  }
  buttonState = digitalRead(button3); //reads state of buttonunsigned long currentTime = millis();
  if ((currentTime - startTime >= period) && (buttonState == LOW))
  {
    servo1.write(90);
    servo2.write(90);
    servo3.write(90);
    servo4.write(90);
    delay(800);
    servo2.write(0);  //run this servo sequence
    delay(800);
    servo2.write(90);
    servo4.write(0);
    delay(800);
    servo4.write(90);
    servo1.write(0);
    delay(800);
    servo1.write(90);
    servo3.write(0);
    delay(800);
    servo3.write(90);
    delay(800);
    servo2.write(0);  
    delay(800);
    servo2.write(90);
    servo4.write(0);
    delay(800);
    servo4.write(90);
    servo1.write(0);
    delay(800);
    servo1.write(90);
    servo3.write(0);
    delay(800);
    servo3.write(90);
    delay(800);
  };
  if (buttonState == HIGH) { //button is not pressed 
  }
}

Why do the servo attaches in the loop function?

I lost count of the number of 800 millisecond delays, but period is only 500 milliseconds.

If you want to use multiple buttons each button must be setup with pinMode()
Your code only sets up button but not button2 and button3

If your three buttons do different things you should give them more specific names than just button1,2,3

as the setup-function is executed only one single time reading in buttonstate must not be done inside the function setup

servo.attach should be done inside the setup-function

You are using if.conditions like

if ((currentTime - startTime >= period) 

but the variables currentTime and StartTime get never updated or assigned a value

variable starttime never gets assigned a value.

You are mixing timing with delay() with timing based on function millis() which is a bad idea

It is

essential

  • to start with a small code that does just one thing.
    if this one thing works
    add only one small thing and test again if it works

repeat this pattern.

The longer you wait with testing the more places where a single bug might hide and the more bugs could hide in your code.
adding a small piece of code and re-Test new keeps the number of lines where the bug might be small and will be found pretty fast.

trying to be real fast turns out to slow you down.

So walk back to start.
With which small part of the code would you like to start?
best regards Stefan

I have added debug-output to your code and added the serial-Monitor-Section to your JSON-file at Wokwi
to make visible what is happening
If you posted a link to your poject and I modified it. Can you see the modifications?
best regards Stefan

Are you powering your servos with a separate 4.8-6V power supply, capable of providing at least 3 Amperes?

If not, that is a serious problem. Don't forget to connect all the grounds!

well the WOKI-Simulation the TO linked to in his first post has no problem with driving the servos.

But your comment is a good idea to improve this simulator to come closer to reality.
I will write an Issue or feature-request on GitHub

best regards Stefan

Here is a code-version that has debug-output added


#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVar,myInterval);
// myVar can be any variable or expression that is defined in scope
// myInterval is the time-interval which must pass by before the next
// print is executed


#include <Servo.h>

Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;

#define button 2 //attaches button to pin 2
#define button2 3 //attaches button to pin 3
#define button3 4 //attaches button to pin 3

int buttonState = 0; //buttonState stores state of input

const byte positions[] = {0, 90,};
unsigned long period = 500;
unsigned long startTime = 0;
int greenLED = 11;

void setup() {
  Serial.begin(115200);
  Serial.println("Setup Start");
  
  pinMode (greenLED, OUTPUT);
  pinMode(button, INPUT_PULLUP); //sets button as input
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);

  servo1.attach(5);
  servo2.attach(6);
  servo3.attach(9);
  servo4.attach(10);
}


void loop() {  
  digitalWrite (greenLED, HIGH);
  delay (100);
  digitalWrite (greenLED, LOW);
  delay (100);
  
  buttonState = digitalRead(button); //reads state of buttonunsigned long currentTime = millis();
  unsigned long currentTime = millis();
  dbgi("top of loop",currentTime,1000);

  dbg("result of currentTime - startTime is:",currentTime - startTime);
  if ((currentTime - startTime >= period) && (buttonState == LOW))
  {
    dbg("if ((currentTime - startTime >= period) && (buttonState == LOW)) is true",1);
    dbg("now I'm busy with the first sequence",0);
    servo1.write(90);
    servo2.write(0);
    servo3.write(90);
    servo4.write(90);
    dbg("delay(1000)",0)
    delay(1000);
    servo2.write(90);
    delay(500);

    servo1.write(0);  //run this servo sequence
    delay(1200);
    servo1.write(90);
    servo2.write(0);
    delay(1200);
    servo2.write(90);
    servo3.write(0);
    delay(1200);
    servo3.write(90);
    servo4.write(0);
    delay(1200);
    servo4.write(90);
    delay(1200);
    servo1.write(0);  //run this servo sequence
    delay(1200);
    servo1.write(90);
    servo2.write(0);
    delay(1200);
    servo2.write(90);
    servo3.write(0);
    delay(1200);
    servo3.write(90);
    servo4.write(0);
    delay(1200);
    servo4.write(90);
    delay(1200);
    dbg("sequence 1: it took many seconds caused by all those delays to come o this point",0);
  }

  if (buttonState == HIGH) { //button is not pressed 
  }

  buttonState = digitalRead(button2); //reads state of buttonunsigned long currentTime = millis();
  if ((currentTime - startTime >= period) && (buttonState == LOW))
  {
    dbg("now I'm busy with the second sequence",0);
    servo1.write(0);
    servo2.write(90);
    servo3.write(90);
    servo4.write(90);
    delay(1000);
    servo1.write(90);
    delay(500);

    servo1.write(90);
    servo2.write(90);
    servo3.write(90);
    servo4.write(90);
    delay(8000);
    servo1.write(0);
    servo2.write(0);
    servo3.write(0);
    servo4.write(0);
    delay(1000);
    dbg("sequence 2: it took many seconds caused by all those delays to come o this point",0);
  }

  if (buttonState == HIGH) { //button is not pressed 
  }

  buttonState = digitalRead(button3); //reads state of buttonunsigned long currentTime = millis();
  if ((currentTime - startTime >= period) && (buttonState == LOW))
  {
    dbg("now I'm busy with the third sequence",0);
    servo1.write(90);
    servo2.write(90);
    servo3.write(0);
    servo4.write(90);
    delay(1000);
    servo3.write(90);
    delay(500);

    servo2.write(0);  //run this servo sequence
    delay(800);
    servo2.write(90);
    servo4.write(0);
    delay(800);
    servo4.write(90);
    servo1.write(0);
    delay(800);
    servo1.write(90);
    servo3.write(0);
    delay(800);
    servo3.write(90);
    delay(800);
    servo2.write(0);  
    delay(800);
    servo2.write(90);
    servo4.write(0);
    delay(800);
    servo4.write(90);
    servo1.write(0);
    delay(800);
    servo1.write(90);
    servo3.write(0);
    delay(800);
    servo3.write(90);
    delay(800);
    dbg("sequence 3: it took many seconds caused by all those delays to come o this point",0);
  }
  if (buttonState == HIGH) { //button is not pressed 
  }
}

You have to add a section to the JSON-file on Wokwi too to let WOKWI show the serial monitor

the JSON-sesction looks like this

,
  "serialMonitor": {
  "display": "auto",
  "newline": "lf"
},

here is the complete JSON-file

{
  "version": 1,
  "author": "Uri Shaked",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-uno", "id": "uno", "top": 200, "left": 20, "attrs": {} },
    {
      "type": "wokwi-servo",
      "id": "servo",
      "top": 200,
      "left": 400,
      "attrs": { "hornColor": "black" }
    },
    { "type": "wokwi-servo", "id": "servo1", "top": 282.52, "left": 399.08, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo2", "top": 369.93, "left": 395, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo3", "top": 458.17, "left": 395, "attrs": {} },
    {
      "type": "wokwi-pushbutton",
      "id": "btn1",
      "top": 425.94,
      "left": 7.86,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton",
      "id": "btn2",
      "top": 478.22,
      "left": 1.32,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-pushbutton",
      "id": "btn3",
      "top": 533.4,
      "left": 47.63,
      "attrs": { "color": "green" }
    }
  ],
  "serialMonitor": {
  "display": "auto",
  "newline": "lf"
},

  "connections": [
    [ "uno:5V", "servo:V+", "red", [ "v20", "h0", "*", "h-20" ] ],
    [ "uno:GND.1", "servo:GND", "black", [ "v-28", "h240", "*", "h-20" ] ],
    [ "servo:PWM", "uno:5", "orange", [ "h-101", "v-81", "h-71" ] ],
    [ "servo1:GND", "uno:GND.2", "black", [ "h-12", "v85", "h-197" ] ],
    [ "servo1:PWM", "uno:6", "orange", [ "h-96", "v-180", "h-83" ] ],
    [ "servo1:V+", "uno:5V", "red", [ "h-34", "v66", "h-185" ] ],
    [ "servo2:V+", "uno:5V", "red", [ "h0" ] ],
    [ "servo2:PWM", "uno:9", "orange", [ "h-86", "v-296", "h-127" ] ],
    [ "servo3:GND", "uno:GND.3", "black", [ "h0" ] ],
    [ "servo3:V+", "uno:5V", "red", [ "h0" ] ],
    [ "servo3:PWM", "uno:10", "orange", [ "h-281", "v-372", "h62" ] ],
    [ "servo2:GND", "uno:GND.3", "black", [ "h0" ] ],
    [ "btn1:2.l", "uno:2", "green", [ "h-10", "v-333", "h268" ] ],
    [ "btn1:1.r", "uno:GND.2", "green", [ "v8", "h119" ] ],
    [ "btn2:1.r", "uno:GND.1", "green", [ "v-323", "h59" ] ],
    [ "btn2:2.l", "uno:3", "green", [ "h0" ] ],
    [ "btn3:2.l", "uno:4", "green", [ "h0" ] ],
    [ "btn3:1.r", "uno:GND.3", "green", [ "v2", "h97" ] ]
  ]
}

The debug-output shows you what is really going on and this will guide you to find the bug
best regards Stefan

@essentialled
I would suggest to implement switch debouncing first by including the following codes in the sketch. Debouncing is needed to allow settling time for the closed switch as the switch, being a mecanical one, makes hundreds of make-break before settling to the final close position.

#include<Debounce.h>
Debounce Button1(2);  //Switch connected at DPin-2 with internal pull-up is seen as object named Button1.
Debounce Button2(3);
Debounce Button3(4);

void setup()
{
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  //---Test Codes----------
  while (!Button1.read() != LOW)
  {
    ;  //wait until s=Button1 is closed
  }
  bool n = !Button1.read();
  Serial.println(n, BIN); //n = 0 means Button1 is closed
  Serial.println("Button1 is closed.");
}

void loop()
{

}

Yes the servos will be running off their own supply, this is not shown on wokwi.

Excellent Thank You, With the help of everyone here i think i can solve the issue. I'm going to build up the real thing now i have some working code.

thank you again for all your help.

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