controling a servo with mills

I need a servo to slowly toggle from 30 to 150 then back 6 hours later over and over using mills. I have tried several different sketches using a shorter time. Some have started out like they are going to work then they go crazy.

servoTest33.ino (1.45 KB)

Here is the code I'm trying to make work.

#include <Servo.h> 
 unsigned long interval=5000L; // the time we need to wait
unsigned long previousMillis=0; // millis() returns an unsigned long.
 
Servo myservo;  // create servo object to control a servo  
 
int pos = 0;    // variable to store the servo position 
int pos1 = 0; 
void setup() 
 {
  myservo.attach(6);  // attaches the servo on pin 9 to the servo object 
 }
 
void loop() 
 {
 unsigned long currentMillis = millis(); // grab current time
 
 // check if "interval" time has passed (5000 milliseconds)
 if ((unsigned long)(currentMillis - previousMillis) >= interval) 

{ 
  for (pos = 30; pos < 150; pos ++)  // goes from 30 degrees to 150 degrees 
{                                // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
}
                          
  previousMillis = millis();
 
  } 
 {
 unsigned long currentMillis = millis(); // grab current time
 
 // check if "interval" time has passed (1000 milliseconds)
 if ((unsigned long)(currentMillis - previousMillis) >= interval) 


  for (pos1 = 150; pos1>=30; pos1-=1)     // goes from 150 degrees to 30 degrees 
    {                              
    myservo.write(pos1);              // tell servo to go to position in variable 'pos' 
    delay(15);     // waits 15ms for the servo to reach the position 
    previousMillis = millis();
 }
  } 
 }

Better make sure you tell the pre processor the right format
unsigned long interval=5000[color=red]U[/color]L // U for unsigned

So which pin do you really use?

myservo.attach([color=red]6[/color]);  // attaches the servo on pin [color=red]9[/color] to the servo object

Your are defining twice the currentMillis variable

unsigned long currentMillis = millis(); // grab current time
...
unsigned long currentMillis = millis(); // grab current time

In your code once you enter the first if when time is up, you are totally synchronous in your for loop... nothing else can happen while you are ticking your servo

Presumably you meant this thread to continue this one?

I'm not sure what testing (unsigned long) will do:

if ((unsigned long)(currentMillis - previousMillis) >= interval)

Yeah I let that one go as Casting is above > in operator precedence so does the "right" thing (although It's useless as both values are unsigned long so the difference is an unsigned long and compared with an unsigned long)

(And if it were behaving the other way ((unsigned long) true) and ((unsigned long) false) would still be evaluated as true and false respectively....)

Please Ask moderator to merge your two posts - this is confusing

Please document if the move from 30 to 150 needs to take 6 hours or if the pause is for 6 hours once you get to 150. I suppose same is true in the other direction?

Threads merged as requested.

Do you want it to sweep out from 30 to 150 over 6 hours then snap back? Or go from 30 .. 150 ..30 over 6 hours or go from 30..150..30 in 12 hours?

-jim lee

I'm guessing there and back slowly over 6 hours.

I'd write it like this.

#include <Servo.h> 
#include <multiMap.h>
#include <timeObj.h>


Servo myservo;  // create servo object to control a servo  
multiMap servoMapper; // Will map minutes to servo position
timeObj servoTimer(60000);  // 1 minute timer.

int pos;
int minute;

void setup() {
  myservo.attach(6);                      // attaches the servo on pin 9 to the servo object 
  servoMapper.addPoint(0,30);             // Add the 0 point to the mapper.
  servoMapper.addPoint(180,150);          // 1/2 way point..
  servoMapper.addPoint(360,30);            // Add the 360 minute point to the mapper.
  minute = 0;                             // We are at minute 0 now..
  pos = round(servoMapper.Map(minute));   // Calculate initial position.
  myservo.write(pos);                     // Send Mr servo on its way.
  servoTimer.start();                     // Start the timer.
 }

 
void loop() 
 {
    if (servoTimer.ding()) {                  // Our minute is up..
      minute++;                               // Bump the minute count.
      if (minute>360) {                       // We go over?
        minute = 0;                           // Then make it 0
      }
      pos = round(servoMapper.Map(minute));   // Calculate the new position.
      myservo.write(pos);                     // Send the servo there.
      servoTimer.stepTime();                  // Reset the timer.
    }
    // Do whatever else you need to do..
}

If you're intereted I can give you a link to the lib files you'd need to do this.

-jim lee

Thanks for the replies. Sorry that I wasn't clear about what I was trying to do. I'm using it to turn the eggs in an incubator, so it needs to go from 30 to 150 in about one second and then stay there for six hours then return to 30 in about one to two seconds so it doesn't shake the eggs too hard and then wait there for six hours, over and over.
I can make it work if I take out the 'FOR" statement but then it jerks the eggs instead of gently rolling them.

I have it working now using the "delay"function but I had to use another arduino to operate the heat and humidity and I was hoping that if I could get this working that I would then try using a button to send it to 90 and wait there untill the button was pressed again so it would not turn while the eggs are hatching.
I know that I'm in over my head but I have sent several hours reading and trying different changes. The challenge of learning something new is more rewarding than the process is frustrating.

I'm using pin 9 for the servo. I read that pin 9 & 10 would not work correctly when using the Servo library so I tried changing it and forgot to change it back.

Thanks,
Kenny

It's a pretty simple state machine, you could update your code to something like below.

if you want to test, set your console to 115200 bauds and if you don't want to wait 6 hours to see things moving around change the variable SixHours into 6000 for example to have something happen every 6 seconds.

the TickPeriod variable defines how long in microseconds to wait between two consecutive "ticks" of the servo when turning. if you want the to turn in 1 second by having done 180 ticks then TickPeriod should be 1s / 180 = 5555 micro sec (Assuming your servo has such speed capability otherwise you'll see erratic movements, then move in 2s for example)

The code includes some Serial.print to see what's going on, of course you need to remove that

have a look and ask if you have questions. (It might not work as I've not tested at all :slight_smile: )

#include <Servo.h>
Servo eggTurnServo;

const byte eggTurnServoPin = 6;
const int minAngle = 0;
const int maxAngle = 180;
int currentAngle;         // variable to store the current servo position

enum {ServoIdleCW, ServoIdleCCW, ServoCW, ServoCCW} servoState;

const unsigned long TickPeriod = 1000000ul/180; // in microseconds 180 steps in 1s
const unsigned long SixHours = 6 * 60 * 60 * 1000ul; // six hours in ms. Careful use ul in the expression

unsigned long lastEggTickTime;
unsigned long lastEggFullTurnTime;

void setup() {
  eggTurnServo.attach(eggTurnServoPin);
  currentAngle = minAngle;
  eggTurnServo.write(currentAngle);
  lastEggFullTurnTime = 0;
  servoState = ServoIdleCW; // idle, will turn CW next time

  Serial.begin(115200);
}


void loop() {

  if ((servoState == ServoIdleCW) || (servoState == ServoIdleCCW)) {
    if (millis() - lastEggFullTurnTime >= SixHours) {
      // time to start the rotation, pick the right direction
      servoState = (servoState == ServoIdleCW) ? ServoCW : ServoCCW;
      lastEggTickTime = millis();
      // ----- DEBUG -------
      Serial.print(lastEggTickTime);
      if (servoState == ServoCW) Serial.println("\tStart Moving CW");
      else Serial.println("\tStart Moving CCW");
      // ------------------
    }
  }

  if ((servoState == ServoCW) || (servoState == ServoCCW)) {
    if (micros() - lastEggTickTime >= TickPeriod) {
      // we are moving, progress to the next position based on direction, don't go beyond limits!
      if (servoState == ServoCW) {
        if (++currentAngle <= maxAngle) {
          eggTurnServo.write(currentAngle);
          // ----- DEBUG -------
          if (currentAngle % 10 == 0) Serial.print("."); // print a dot every 10°
          // ------------------
        } else {
          // we finished turning CW, we pause, next will be CCW
          currentAngle = maxAngle;
          servoState = ServoIdleCCW;
          lastEggFullTurnTime = millis();
          // ----- DEBUG -------
          Serial.print(" Done CW\nAngle=");
          Serial.println(currentAngle);
          Serial.print("Time=");
          Serial.println(lastEggFullTurnTime);
          // ------------------
        }
      } else {
        if (--currentAngle >= minAngle) {
          eggTurnServo.write(currentAngle);
          // ----- DEBUG -------
          if (currentAngle % 10 == 0) Serial.print("."); // print a dot every 10°
          // ------------------
        } else {
          // we finished turning CCW, we pause, next will be CW
          currentAngle = minAngle;
          servoState = ServoIdleCW;
          lastEggFullTurnTime = millis();
          // ----- DEBUG -------
          Serial.print(" Done CCW\nAngle=");
          Serial.println(currentAngle);
          Serial.print("Time=");
          Serial.println(lastEggFullTurnTime);
          // ------------------
        }
      }
      lastEggTickTime += TickPeriod;
    }
  }

  // HERE YOU CAN DO OTHER STUFF AS LONG AS IT IS SHORTER THAN TickPeriod



}

Did that work? (curious if I broke any egg ;D :disappointed_relieved: )

Thanks for your reply.
The timing on how and when to turn works but it still turns fast. I can change the six hours to seconds easy enough but I'm still not sure how to slow the servo movement down.

Thanks,
Kenny

Kennygregory:
Thanks for your reply.
The timing on how and when to turn works but it still turns fast. I can change the six hours to seconds easy enough but I'm still not sure how to slow the servo movement down.

Thanks,
Kenny

cf what I said:

the TickPeriod variable defines how long in microseconds to wait between two consecutive "ticks" of the servo when turning. if you want the to turn in 1 second by having done 180 ticks then TickPeriod should be 1s / 180 = 5555 micro sec (Assuming your servo has such speed capability otherwise you'll see erratic movements, then move in 2s for example)

the TickPeriod is basically the number of microseconds you need to wait between 2 small movements of the Servo. the Servo will perform 180 ticks total. so if you want the turn to take 5 seconds, then you put in TickPeriod the value 5000000ul / 180 (don't forget the ul or the pre-processor will do math in small integers (16 bits) and not get you the right result)