Using millis for time and during that time move a servo

Hello fellow programmers!

I will first explaine what I wish to achieve. Then introduce the problem that accrue.

Function
I want to build a machine that rock my babys cot like a charm. My plan is to have three settings. I will choose the settings with a button. One click at the button will start the rocking for e.g. five minutes, a secon push at the same button will start the rocking for e.g. fifteen minutes and a third push will stop everything.

Got the servo moving
Because I am a neewbie I did start of with tutorials from Youtube. I got the servo to go back and forth like i wanted it with this code.

#include <Servo.h>
Servo servo1;
int S1Pos1=0;
int ButtonState = 0;  
int ButtonOld = 0;    
int ButtonPoll = 0;   
int ServoOn = 0;     
int ServoOff = 0;     
#define Button 3      

void setup() {
{
  servo1.attach(9);
}
  pinMode(3, INPUT);  

}

void loop()

{
  for(S1Pos1 = 0; S1Pos1 < 180; S1Pos1 += 3)
  {
  servo1.write(S1Pos1);
  delay(10);
  }
  for(S1Pos1 = 180; S1Pos1>=1; S1Pos1-=1)
  {
  servo1.write(S1Pos1);
  delay(10);
  }
}

Three states and timing
I wanted different states så I found this code on youtube with lamps and added Millis to have timing on the lamps. The function works like charm.

/* Multiple state handling
 *  2019-08-11 */
 
int S1 = A1;
int D1 = 13;
int D2 = 12;
int D3 = 11;

int state = 0;
int old = 0;
int buttonPoll = 0;

unsigned long previousMillis = 0;
const long interval1 = 30000;
const long interval2 = 60000;



void setup() {
  // put your setup code here, to run once:
  pinMode(S1, INPUT);
  pinMode(D1, OUTPUT);
  pinMode(D2, OUTPUT);
  pinMode(D3, OUTPUT);

  digitalWrite(D1,HIGH);
  digitalWrite(D2,HIGH);
  digitalWrite(D3,HIGH);
}

void loop() {
  
unsigned long currentMillis = millis();   
  
                                    //debouncing routine
    buttonPoll = digitalRead(S1);      
  if(buttonPoll == 1){          
    delay(50);                      
    buttonPoll = digitalRead(S1);    
  if(buttonPoll == 0){          
      state = old + 1;             
      previousMillis = currentMillis;
  }}
  else{                           
    delay(100);                     
  }

//______________________________________________________________________________________
//TIME

    
  if ((currentMillis - previousMillis >= interval1) && (state==1)) {
    state = 0;
    }

  if ((currentMillis - previousMillis >= interval2) && (state==2)) {
    state = 0;
    }
                                      
                                    
                                 
   switch (state) {                //react to button press & state
    case 1:                         // if state is 1
      digitalWrite(D1, LOW);
      digitalWrite(D2, HIGH);
      digitalWrite(D3, HIGH);
      old = state;
    break;

    case 2:                         // if state is 2
      digitalWrite(D1, HIGH);
      digitalWrite(D2, LOW);
      digitalWrite(D3, HIGH);
      old = state;
    break;

    case 3:                         // if state is 3
      digitalWrite(D1, HIGH);
      digitalWrite(D2, HIGH);
      digitalWrite(D3, LOW);
      old = state;
    break;

    default:                         
      digitalWrite(D1, HIGH);
      digitalWrite(D2, HIGH);
      digitalWrite(D3, HIGH);
      old = 0;
    break;    
    }
    
}

Combination to get my rocking-program

When then programs aboved worked I felt like a badass programmer (altough I had “borrow” the code from world wide web). In my naive dream with my new confident I thought this should be easy to combined.

int D1 = 13;
int D2 = 12;
int D3 = 11;



 
#include <Servo.h>             
Servo servo1;               
int ServoPos1;               

                                
#define Button A1             

                              
int ButtonState=0;         
int ButtonOld=0;               
int ButtonPoll=0;              

                              
unsigned long previousMillis=0;
const long interval1=3000;     
const long interval2=60000;    


void setup() {

  pinMode(D1, OUTPUT);
  pinMode(D2, OUTPUT);
  pinMode(D3, OUTPUT);

  digitalWrite(D1,HIGH);
  digitalWrite(D2,HIGH);
  digitalWrite(D3,HIGH); 
 
  servo1.attach(9);             
  pinMode(Button, INPUT);      
}

void loop()

{


 unsigned long currentMillis = millis();   
  
                                           
    ButtonPoll = digitalRead(Button);      
  if(ButtonPoll == 1){                  
    delay(50);                            
    ButtonPoll = digitalRead(Button); 
  if(ButtonPoll == 0){                    
      ButtonState = ButtonOld + 1;       
      previousMillis = currentMillis;      
  }}
  else{                                    
    delay(100);                           
  }



  if ((currentMillis - previousMillis <= interval1) && (ButtonState==1)) {
    for(ServoPos1 = 0; ServoPos1 < 180; ServoPos1 += 3)
        {
          servo1.write(ServoPos1);
          delay(10);
        }
      for(ServoPos1 = 180; ServoPos1>=1; ServoPos1-=1)
        { 
          servo1.write(ServoPos1);
          delay(10);
        }
      ButtonOld = ButtonState;
    }

  if ((currentMillis - previousMillis >= interval2) && (ButtonState==2)) {
      ButtonState = 0;
    }

But what is the problem?
The problem that occurs is that the servo will start from 0 then move to pos 180 then back to 0 ONCE. Not back and forth for the time I have set.

Can someone please tell me why this aint working?

Apology
English is not my native language, but I hope some of it will make sense. I’m also sorry that the programs is uncomment. I did comment the programs, but in Swedish… I will from now comment everything in english.

What you are doing with the buttons and the ButtonState variable looks wrong to me.

With buttons you get bounce on press and release. Bounce is sparks jumping across a very tiny gap. So for any change in the pin state we can watch the state for no change over a small time (debounce interval) and that is the stable state, bounce is done.

With buttons we want to detect state changes and use those, not pin state. Not “is button up or down?” but rather “has button just become stable after press or release?”.

We detect these things over many runs through void loop() using “do many things at once” techniques shown here and on the web.

Likewise with the rocking, don’t do it all in one pass through void loop(), I can help you there, it will let the button -always- be read (as far as human can tell) and let many other things be done in future versions.

I did a little cleanup of your code to make it easier to read and use less RAM.
In next post I will show you one other way to debounce a button.

// cradle rock

#include <Servo.h>
Servo servo1;
int ServoPos1;

const byte D1 = 13;  // small numbers can use 1 byte instead of 2 byte int
const byte D2 = 12;  // cost is constant, never changes
const byte D3 = 11;

#define ButtonPin A1

byte ButtonState = 0;
byte ButtonOld = 0;
byte ButtonPoll = 0;

unsigned long previousMillis = 0;
const unsigned long interval1 = 3000;   // keep time variables all unsigned
const unsigned long interval2 = 60000;  // even if only for habit


void setup() 
{
  pinMode(D1, OUTPUT);
  pinMode(D2, OUTPUT);
  pinMode(D3, OUTPUT);

  digitalWrite(D1, HIGH);
  digitalWrite(D2, HIGH);
  digitalWrite(D3, HIGH);

  servo1.attach(9);
  pinMode(ButtonPin, INPUT);
}

void loop()  // I put { on the same level as the } below it.
{            // it is easier to tell which ones should belong together

  // I got rid of currentMillis because often by the time it is used it is "old".
  // the millis() function is very close to as fast as reading a UL variable.
  
  // button task  -- task code runs every time in loop()
  ButtonPoll = digitalRead(ButtonPin);
  if (ButtonPoll == 1) 
  {
    delay(50);     // will the user release the button just in this time?
    ButtonPoll = digitalRead(ButtonPin);  
    
    if (ButtonPoll == 0) // if button is up now, no rocking below?
    {
      ButtonState = ButtonOld + 1;
      previousMillis = millis();
    }
  }
  else // what is this for?
  {
    delay(100);
  }

  // servo move task
  if ((millis() - previousMillis <= interval1) && (ButtonState == 1)) 
  {
    for (ServoPos1 = 0; ServoPos1 < 180; ServoPos1 += 3)
    {
      servo1.write(ServoPos1);
      delay(10);
    }
    for (ServoPos1 = 180; ServoPos1 >= 1; ServoPos1 -= 1)
    {
      servo1.write(ServoPos1);
      delay(10);
    }
    ButtonOld = ButtonState;
  }

  // this is a task too
  if ((millis() - previousMillis >= interval2) && (ButtonState == 2)) {
    ButtonState = 0;
  }

}

Here is a sketch with button and blinking led. The button is pin 7 but you can change that.

The code is “advanced” because it uses bits and bit-logic and 16 bit time variables. I did that to make the button use less memory, it matters more when there are lots of buttons.

You can grab the button code, variables and the buttonTask() function. To use, when buttonHistory is 128 the button has been pressed and become stable while 127 means button released.
With buttonHistory updated twice a milli, it takes 3.5ms to determine “stable” -after- the last state change however long that takes (with “dirty” button, maybe 20ms bounce first and “clean” button only 2ms… I use a jumper to test).

This is non-blocking code able to “do many things at once”.

PS – this code is for button wired between pin and GND. There is no need to wire in a resistor, the Arduino chip has the resistor built-in and never enough current flows to hurt the pin at all, the internal resistor is 20K Ohms or more!

// add-a-sketch_button 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 30/2018 by GFS. Compiled on Arduino IDE 1.6.9.
// Update May 6/2018, Aug 11, 2018

/*  Button Debounce Example

  --- for this example connect a button between pin 7 and GND
  --- or stick a jumper in pin 7 and while holding the board steady
  --- tap the free end onto the grounded USB port box to press.
  --- Press and hold the Button to toggle led13 blinking.

  Yes I'm using a 16 bit micros timer to time fractions of millis as micros.
  The button reader only reads 1 button per call so as to not block void loop().
  Each button has a history byte that holds the last 8 reads with 256 possible
  states but only 4 of them being significant.
  0 is the button held down
  255 is the button left up
  127 is the buton changing from up to down, button just released.
  128 is the button changing from down to up, button just pressed.
  everything else is to be ignored as bounce.
*/

// button vars
byte buttonPin = 7;
byte buttonHistory;
word markButtonTime;        // 16-bit micros timers
const word waitButtonTime = 500; // micros

// added sketch task, on-off blinker vars
byte ledState, ledPin = 13; // use byte for small values, int cost 2 bytes
word startBlink, waitBlink; // 16 bit millis is good to time 65.535 seconds

void buttonTask()   // yup, this is my new button debounce compromise method.
{ // read twice per milli, bits 0 to 6 all same sets the state
  if ( word( micros()) - markButtonTime >= waitButtonTime ) // read occaisoinally
  {
    buttonHistory <<= 1; // if you don't know <<= look it up in the Arduino Reference
    // keep a browser open to that page when you use the IDE.
    buttonHistory += digitalRead( buttonPin ); // read history streams through buttonHistory
    markButtonTime = micros(); // gets the low 16 bits of micros(), time to 60 ms + margin
  }
  /*        buttonHistory bits read as values:
    0 is button held down, 8 reads of 0 in a row
    255 is button left up, 8 reads of 1 in a row
    127 is buton changing from up to down, 7 1's and 1 0 = just released
    128 is button changing from down to up, 7 0's and 1 1 = just pressed.
    everything else is to be ignored as bounce.
    Understand that 7 same bits in a row counts as pin state stable.
  */
}

void OnOffBlinker() // only blinks if there's a wait time, can be switched on/off
{
  if ( waitBlink > 0 ) // this is the on/off switch
  {
    // word( millis()) gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startBlink >= waitBlink ) // difference in time by subtracting start from end
    {
      ledState = !ledState;  // ! is NOT: not_0/true becomes 0/false else 0 becomes 1.
      digitalWrite( ledPin, ledState ); // the led changes state.
      startBlink += waitBlink; // next blink starts when it should, where diff > wait.
    }
  }
  else if ( ledState > 0 ) // waitBlink == 0 turns blinking off
  {
    digitalWrite( ledPin, ledState = LOW ); //  make sure the led is OFF
  } // yes, you can set a variable during calculation in C, the write here is LOW.
}

void setup()
{
  Serial.begin( 115200 );
  for ( byte i = 0; i < 66; i++ )  Serial.println();
  Serial.println( F( "\n  Button Debounce Example, free by GoForSmoke\n" ));
  Serial.println( F( "\n-- for this example connect a button between pin 7 and GND" ));
  Serial.println( F( "--- or stick a jumper in pin 7 and while holding the board steady" ));
  Serial.println( F( "--- tap the free end onto the grounded USB port box to press." ));

  pinMode( buttonPin, INPUT_PULLUP );
  pinMode( ledPin, OUTPUT );

  buttonHistory = 255;
  waitBlink = 500;
};


void loop()
{
  buttonTask();
  /*
    0 is the button held down
    255 is the button left up
    127 is the buton changing from up to down, button just released.
    128 is the button changing from down to up, button just pressed.
    everything else is to be ignored as bounce.
  */

  switch ( buttonHistory ) 
  {
    case 128 : // pin is HIGH in bit 7, LOW for 7 reads, up to down detected
      buttonHistory = 0; // change detected, make it into no change now
      Serial.print( F( "press detected     " ));
      Serial.println( millis());
      if ( waitBlink == 0 ) // toggle action tied to button press
      {
        waitBlink = 500; // makes the blinking start
        startBlink = millis(); // gets the low 16 bits
      }
      else
      {
        waitBlink = 0; // makes the blinking stop
      }
      break;
    case 127 : // pin is LOW in bit 7, HIGH for 7 reads, down to up detected
      buttonHistory = 255; // change detected, make it into no change now
      Serial.print( F( "release detected   " ));
      Serial.println( millis());
      break;
  }

  OnOffBlinker();
}

Here is the address of a tutorial by Nick Gammon covering non-blocking “do many things” code for beginners:
http://gammon.com.au/blink