Increasing two different servo PWM's with different delay's

I am basically trying to simulate a real clock with two servos, one for each hand (Hours and Minutes).
The reason for using servos is I have multiple buttons. You press one and the clock hands instantly zips to a particular time and starts running at a normal clock time. You press another and it zips to that time and starts running etc.

Normal servos can only do 180 degrees so I am using sail winch servos to allow me a couple of rotations however they work on the basis that 600pwm is all the way left and 2500pwm is approx three/four rotations to the right. I have calculated through testing that increasing the microseconds from 1000 to 1253 gives me one full rotation on the servo (this can be done anywhere within the 600-2500 range. I picked the 1000 point because it seems to be the most reliable area in the range.

So, to get one minute on the minute hand in a minute of normal time, matching a normal clock, the servo has to move 4pwm per minute (3.9 actually but it has been rounded up) I have this working in the code shown

Simultaneous to that I need the hour hand to move. This should move 1.5pwm per minute to simulate moving from say midnight to 1 over the space of an hour, however I have rounded this up to 3pwm per two minutes due to my belief that I can't use fractions of a pwm.

What I am asking is how can I have two different servos moving incrementally by different amounts with different delays.

Also I wanted to check if it is possible to increase the microseconds in decimal fractions such as 1.5 or 3.9.

Also I wanted to check if it is possible to increase the microseconds in decimal fractions such as 1.5 or 3.9.

You could using floating point, or you could use a fixed point representation.

I realise this is very complicated for what is essentially a simple idea.

Here is the 'wrong code version' of what I am trying to do

Case 0:
MinutePulse = MinutePulse + 4; //(increases the value by 4 every loop)
ServoA.WriteMicroseconds(MinutePulse) //writes value to servo
Delay (60000); // delay for 60 seconds before moving again

// In the same case 0 I need the following to happen within the same program loop (simultaneously)

HourPulse = HourPulse + 3; //(increases the value by 3 every loop)
ServoB.WriteMicroseconds(HourPulse) //writes value to servo
Delay (120000); // delay for 120 seconds before moving again

AWOL:

Also I wanted to check if it is possible to increase the microseconds in decimal fractions such as 1.5 or 3.9.

You could using floating point, or you could use a fixed point representation.

Sorry, I am fairly new to this and still learning, how would I do this, and would this cause problems with the servo, or are they ok with reading a signal of say 3.9?

Thanks

Here is the 'wrong code version' of what I am trying to do

Case 0:

I'm sorry, but a case statement without the enclosing switch statement is useless. I can't figure out under what conditions you expect this code to be executed.

An alternative aproach might be to use millis to tell you how much time has elapsed and calculate hours and minutes values from that. Then use the map function to map 0-59 into 0-180 (or however you have to tweak it for your servos)and write it to the servo. For the simple case that would give you time since power up. When you go to your setpoints, you'll have to add in the appropriate (unsigned long) number of milliseconds. No worries about timing or delays.

PaulS:

Here is the 'wrong code version' of what I am trying to do

Case 0:

I'm sorry, but a case statement without the enclosing switch statement is useless. I can't figure out under what conditions you expect this code to be executed.

Sorry I cut out most of the stuff that was not relevant to what I am trying to get across. I was trying to explain the fact that I am trying to have two different delays running simultaneously within the same loop. I have many switch/ case statements working perfectly within my code at the moment, I should have left the 'case' out to avoid confusion.

I should have left the 'case' out to avoid confusion.

I don't think that would have helped. I still need to understand under what circumstances you want to change the speed of the motors.

I was trying to explain the fact that I am trying to have two different delays running simultaneously within the same loop.

By now, you should know that you can't do this. You should not have ANY delays(). Look at the blink without delay example to see how to write delay()less code.

By now, you should know that you can't do this. You should not have ANY delays(). Look at the blink without delay example to see how to write delay()less code.

I do know this, hence the reason for asking for an alternative method. I dont really see how I could use 'millis' to help here, can you suggest how?.

My understanding is that Millis counts the time since the program started, however my program waits for inputs and runs other switch/modes before running my 'clocktime loop'

The absolute simplest explanation I can think of what I trying to achieve' is:

Within one continous loop

every 60 seconds - increase servoA pwm by 4
every 120 seconds - increase servoB pwm by 3

looped forever, (or until reset)

I am trying to understand the code for timing/updating the pwm on the two servos within the same executed loop.

The Blink Without Delay sample shows you how to time a particular event using millis(). It requires you track the required timing yourself, and execute the specific task when the appropriate amount of time has passed.

This is really the only method of timing multiple independent events.

flotilla:

By now, you should know that you can't do this. You should not have ANY delays(). Look at the blink without delay example to see how to write delay()less code.

I do know this, hence the reason for asking for an alternative method. I dont really see how I could use 'millis' to help here, can you suggest how?.

My understanding is that Millis counts the time since the program started, however my program waits for inputs and runs other switch/modes before running my 'clocktime loop'

The absolute simplest explanation I can think of what I trying to achieve' is:

Within one continous loop

every 60 seconds - increase servoA pwm by 4
every 120 seconds - increase servoB pwm by 3

looped forever, (or until reset)

I am trying to understand the code for timing/updating the pwm on the two servos within the same executed loop.

There are tons of different ways to do this, but here's a simple one for you.

int servoA = 0;
int servoB = 0;
int counter = 0;
void loop() {


	delay(60);
	servoA += 4;
	
	counter++;

	if (count == 2) {
		servoB += 3;
		counter=0;
	}	
}

Obviously instead of incrementing variables you'll have to increment the PWMs themselves.

Edit: Fixed a missing semicolon. I don't actually have the editor in front of me, so I just typed that up in notepad.

Just wanted to thank you all for your patience and help. I managed to get it working with the 'millis' method
as suggested. After staring at it for an hour it finally clicked into place

My code for that particular section is below for anyone who is interested (majority omitted).

Thanks Again!

(Setup declarations)

unsigned long currentMillisA = millis();  //Storage for Timer
unsigned long currentMillisB = millis();  //Storage for Timer 

long previousMillisA = 0;        // Millis Counter Store
long previousMillisB = 0;        // Millis Counter Store

long StepperAinterval = 60000; // 60 seconds break time          
long StepperBinterval = 120000; // 120 seconds break time

(inside void loop())

case 7: // Commence Time
 ////////////
 
 if (MinutePulse >= 2500) mode = 10; //servo safety to stop it over-turning
 if (HourPulse >= 2500) mode = 10; 
 //////////// 
  
  if(CurrentMillisA - previousMillisA > StepperAinterval) {
    MinutePulse = MinutePulse + 4;
    previousMillisA = CurrentMillisA;
    MinuteHand.writeMicroseconds(MinutePulse);
    Serial.print("MINUTE PWM VALUE IS SET TO ");
    Serial.println(MinutePulse);
  }
  if(CurrentMillisB - previousMillisB > StepperBinterval) {
    HourPulse = HourPulse + 3;
    previousMillisB = CurrentMillisB;
    Serial.print("HOUR PWM VALUE IS SET TO ");
    Serial.println(HourPulse);
    HourHand.writeMicroseconds(HourPulse);
  }

    
break;

Within one continous loop

every 60 seconds - increase servoA pwm by 4
every 120 seconds - increase servoB pwm by 3

I'm really curious why you want the motor to run faster and faster, instead of a constant speed. Every clock I own has a motor that runs at a constant speed. I can't help but think I am missing something.

PaulS:

Within one continous loop

every 60 seconds - increase servoA pwm by 4
every 120 seconds - increase servoB pwm by 3

I'm really curious why you want the motor to run faster and faster, instead of a constant speed. Every clock I own has a motor that runs at a constant speed. I can't help but think I am missing something.

Hi, Increasing the PWM is not increasing the speed, it is increasing the position of the motor around the 360 degrees, for example: on the minute hand
1600pwm is midnight 1604 is one minute past 1608- two minutes past and so on. So adding 4 to the microseconds every minute is moving the servo by one minute on the clock, every minute.

The need for a different speed on the hour hand is due to it having to only cover 1/12th of the 360 degrees in an hour. My maths above for the hour hand were wrong however. It is actually 3 increments every 6 minutes, not every two.

The clock is for a theatre show where there are 6 scene changes in the show. At each scene change the stage crew press a button and the time zips to the next scene time then starts ticking away until the next button is pressed, moving it on to the next scene.

I will post the full code on a seperate post for anyone who is interested. It is not perfect and probably a little messy (I still class myself as a complete newbie to all this) It works very well at the moment and keeps proper time to within a minute which is fine for my purposes.

Hi, Increasing the PWM is not increasing the speed, it is increasing the position of the motor around the 360 degrees

My apologies. For some reason, I had it in my head that you were using continuous rotation servos.

My full code for community purposes and anyone who is interested. Be gentle! I know there are probably easier ways to do this.

#include <Servo.h>
#include <SoftwareSerial.h>

Servo MinuteHand;
Servo HourHand;

// SWITCH PINS

byte setupAswitch = A0;
byte setupBswitch = A1;
byte setupCswitch = A2;
byte setupDswitch = A3;
byte setupEswitch = A4;
byte setupFswitch = A5;
byte ResetSwitch = 2;




//  SERVO PINS

int MinuteHandPin = 9;     // Control pin for servo motor
int HourHandPin = 10;

//  STORAGE VALUES

int MinutePulse; // Storage value for counting time, MinuteHand
int HourPulse; // Storage value for counting time, HourHand

long previousMillisA = 0;        // Millis Counter Store
long previousMillisB = 0;        // Millis Counter Store

long StepperAinterval = 60000; // 60 seconds break time between MINUTE movements          
long StepperBinterval = 360000; // 6 minute break time between HOUR movements

byte mode; //

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void setup() {
  Serial.begin(9600);         // connect to the serial port
  Serial.println("**********    SETTING UP CLOCK CIRCUIT    **********"); //saying how it is
  
 //  SWITCH SETUP
 pinMode      (setupAswitch, INPUT); // set up the various push switches
 digitalWrite (setupAswitch, HIGH);  // turn on internl pullup
 pinMode      (setupBswitch, INPUT);
 digitalWrite (setupBswitch, HIGH);
 pinMode      (setupCswitch, INPUT);
 digitalWrite (setupCswitch, HIGH);
 pinMode      (setupDswitch, INPUT);
 digitalWrite (setupDswitch, HIGH);
 pinMode      (setupEswitch, INPUT);
 digitalWrite (setupEswitch, HIGH);
 pinMode      (setupFswitch, INPUT);
 digitalWrite (setupFswitch, HIGH);
 pinMode      (ResetSwitch, INPUT);
 digitalWrite (ResetSwitch, HIGH);
 
 // SERVO SETUP
 MinuteHand.attach(MinuteHandPin);  //Connects servo to relevent pin 
 HourHand.attach(HourHandPin);  

 // SET BOTH SERVOS TO 'MIDNIGHT'
 
 MinuteHand.writeMicroseconds(1600);  /// SET to 12.00
 HourHand.writeMicroseconds(1600);  /// SET to 12.00

 // START MILLIS TIMER
 
 //unsigned long CurrentMillisA = millis();  //Storage for Timer NOT SURE IF RIGHT ONE!!!
 
Serial.println("WAITING FOR INPUT"); 
}


void loop() {
  
unsigned long CurrentMillisA = millis();  //Storage for Timer
  
if ( mode == 0 && digitalRead (setupAswitch) == LOW){ mode = 1;}
if ( mode == 0 && digitalRead (setupBswitch) == LOW){ mode = 2;}
if ( mode == 0 && digitalRead (setupCswitch) == LOW){ mode = 3;}
if ( mode == 0 && digitalRead (setupDswitch) == LOW){ mode = 4;}
if ( mode == 0 && digitalRead (setupEswitch) == LOW){ mode = 5;}
if ( mode == 0 && digitalRead (setupFswitch) == LOW){ mode = 6;}

if ( mode == 0 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 1 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 2 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 3 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 4 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 5 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 6 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 7 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all

////////////////////////////////////////////////////////////////////////////////////////
////PROGRAM VARIABLES/SERVO SETUPS

switch (mode){

case 0:  // Do Nothing and Wait for input

break;

case 1:  // Set Clock start position A
 MinutePulse = 1600;  // sets the stored Minute value to match the time
 HourPulse = 1600;   //sets the stored Hour value to match the time set
 MinuteHand.writeMicroseconds(MinutePulse); // sets Time A min
 HourHand.writeMicroseconds(HourPulse);  // sets Time A hour
 Serial.print("HOUR PWM VALUE IS SET TO ");
 Serial.println(HourPulse);
 Serial.print("MINUTE PWM VALUE IS SET TO ");
 Serial.println(MinutePulse);
 delay (1000);
 Serial.println("TICKING 1");
 previousMillisA = 0; //Reset Millis Counter A
 previousMillisB = 0; //Reset Millis Counter B
 mode= 7; 
break;


case 2:  // Set Clock start position B
 MinutePulse = 1600;  // sets the stored Minute value to match the time
 HourPulse = 1600;   //sets the stored Hour value to match the time set
 MinuteHand.writeMicroseconds(MinutePulse); // sets Time A min
 HourHand.writeMicroseconds(HourPulse);  // sets Time A hour
 Serial.print("HOUR PWM VALUE IS SET TO ");
 Serial.println(HourPulse);
 Serial.print("MINUTE PWM VALUE IS SET TO ");
 Serial.println(MinutePulse);
 delay (1000);
 Serial.println("TICKING 2");
 previousMillisA = 0; //Reset Millis Counter A
 previousMillisB = 0; //Reset Millis Counter B
 mode= 7; 
break;


case 3:  // Set Clock start position C
 MinutePulse = 1600;  // sets the stored Minute value to match the time
 HourPulse = 1600;   //sets the stored Hour value to match the time set
 MinuteHand.writeMicroseconds(MinutePulse); // sets Time A min
 HourHand.writeMicroseconds(HourPulse);  // sets Time A hour
 Serial.print("HOUR PWM VALUE IS SET TO ");
 Serial.println(HourPulse);
 Serial.print("MINUTE PWM VALUE IS SET TO ");
 Serial.println(MinutePulse);
 delay (1000);
 Serial.println("TICKING 3");
 previousMillisA = 0; //Reset Millis Counter A
 previousMillisB = 0; //Reset Millis Counter B
 mode= 7; 
break;


case 4:  // Set Clock start position D
 MinutePulse = 1600;  // sets the stored Minute value to match the time
 HourPulse = 1600;   //sets the stored Hour value to match the time set
 MinuteHand.writeMicroseconds(MinutePulse); // sets Time A min
 HourHand.writeMicroseconds(HourPulse);  // sets Time A hour
 Serial.print("HOUR PWM VALUE IS SET TO ");
 Serial.println(HourPulse);
 Serial.print("MINUTE PWM VALUE IS SET TO ");
 Serial.println(MinutePulse);
 delay (1000);
 Serial.println("TICKING 4");
 previousMillisA = 0; //Reset Millis Counter A
 previousMillisB = 0; //Reset Millis Counter B
 mode= 7; 
break;

case 5:  // Set Clock start position E
 MinutePulse = 1600;  // sets the stored Minute value to match the time
 HourPulse = 1600;   //sets the stored Hour value to match the time set
 MinuteHand.writeMicroseconds(MinutePulse); // sets Time A min
 HourHand.writeMicroseconds(HourPulse);  // sets Time A hour
 Serial.print("HOUR PWM VALUE IS SET TO ");
 Serial.println(HourPulse);
 Serial.print("MINUTE PWM VALUE IS SET TO ");
 Serial.println(MinutePulse);
 delay (1000);
 Serial.println("TICKING 5");
 previousMillisA = 0; //Reset Millis Counter A
 previousMillisB = 0; //Reset Millis Counter B
 mode= 7; 
break;


case 6:  // Set Clock start position F
 MinutePulse = 1600;  // sets the stored Minute value to match the time
 HourPulse = 1600;   //sets the stored Hour value to match the time set
 MinuteHand.writeMicroseconds(MinutePulse); // sets Time A min
 HourHand.writeMicroseconds(HourPulse);  // sets Time A hour
 Serial.print("HOUR PWM VALUE IS SET TO ");
 Serial.println(HourPulse);
 Serial.print("MINUTE PWM VALUE IS SET TO ");
 Serial.println(MinutePulse);
 delay (1000);
 Serial.println("TICKING 6");
 previousMillisA = 0; //Reset Millis Counter A
 previousMillisB = 0; //Reset Millis Counter B
 mode= 7; 
break;


case 7: // Commence Time
 
 if (MinutePulse >= 2495) mode = 10; //servo safety to stop it over-turning
 if (HourPulse >= 2495) mode = 10; 
 // Serial.println(CurrentMillisA);
 // Serial.println(CurrentMillisB);
  if(CurrentMillisA - previousMillisA > StepperAinterval) {
    previousMillisA = CurrentMillisA;
    MinutePulse = MinutePulse + 4;
    Serial.print("MINUTE PWM VALUE IS SET TO ");
    Serial.println(MinutePulse);
    MinuteHand.writeMicroseconds(MinutePulse);
  }
  if(CurrentMillisA - previousMillisB > StepperBinterval) {
    previousMillisB = CurrentMillisA;
    HourPulse = HourPulse + 2;
    Serial.print("HOUR PWM VALUE IS SET TO ");
    Serial.println(HourPulse);
    HourHand.writeMicroseconds(HourPulse);
  }

    

case 9:  //  RESET THE PROGRAM
Serial.println("RESETTING & JOGGING");
 MinuteHand.writeMicroseconds(1700); // Jogs the motor to ensure a 'midnight'
 HourHand.writeMicroseconds(1700);
 delay (1000);
 MinuteHand.writeMicroseconds(1600); // Jogs the motor
 HourHand.writeMicroseconds(1600);
MinutePulse = 1600;  // resets values
HourPulse = 1600;
Serial.print("HOUR PWM VALUE IS RESET TO MIDNIGHT");
Serial.println(HourPulse);
Serial.print("MINUTE PWM VALUE IS RESET TO MIDNIGHT");
Serial.println(MinutePulse);
delay (1000);
Serial.println("WAITING FOR INPUT");
mode = 0;
break;

case 10:  //  SERVO OVERREACH
Serial.println("******   BWEEP SERVO OVERREACH   *******");
Serial.print("SERVO OR PWM VALUE IS ");
Serial.println(HourPulse);
Serial.print("SERVO OR PWM VALUE IS ");
Serial.println(MinutePulse);
delay (3000);
Serial.println("WAITING FOR INPUT");
MinutePulse = 1600;  // resets values to midnight
HourPulse = 1600;
mode = 0;
break;


} // end switch

} // end loop
long StepperAinterval = 60000; // 60 seconds break time between MINUTE movements          
long StepperBinterval = 360000; // 6 minute break time between HOUR movements

These should be:

long StepperAinterval = 60000UL; // 60 seconds break time between MINUTE movements          
long StepperBinterval = 360000UL; // 6 minute break time between HOUR movements
 MinuteHand.attach(MinuteHandPin);  //Connects servo to relevent pin 
 HourHand.attach(HourHandPin);

Good names. It is very easy to see what you are doing.

 // SET BOTH SERVOS TO 'MIDNIGHT'
 
 MinuteHand.writeMicroseconds(1600);  /// SET to 12.00
 HourHand.writeMicroseconds(1600);  /// SET to 12.00

I'm not a fan of magic numbers. I'd do something like this:

 // Set initial position for servos
#define MIDNITE 1600
 // SET BOTH SERVOS TO 'MIDNIGHT'
 
 MinuteHand.writeMicroseconds(MIDNITE);  /// SET to 12.00
 HourHand.writeMicroseconds(MIDNITE);  /// SET to 12.00

That way, if you need to change the value (to 1601 or 1599, for instance), you only need to do it in one place.

if ( mode == 0 && digitalRead (setupAswitch) == LOW){ mode = 1;}
if ( mode == 0 && digitalRead (setupBswitch) == LOW){ mode = 2;}
if ( mode == 0 && digitalRead (setupCswitch) == LOW){ mode = 3;}
if ( mode == 0 && digitalRead (setupDswitch) == LOW){ mode = 4;}
if ( mode == 0 && digitalRead (setupEswitch) == LOW){ mode = 5;}
if ( mode == 0 && digitalRead (setupFswitch) == LOW){ mode = 6;}

Using a nested if test would be better:

if(mode == 0)
{
   if (digitalRead (setupAswitch) == LOW){ mode = 1;}
   if (digitalRead (setupBswitch) == LOW){ mode = 2;}
   if (digitalRead (setupCswitch) == LOW){ mode = 3;}
   if (digitalRead (setupDswitch) == LOW){ mode = 4;}
   if (digitalRead (setupEswitch) == LOW){ mode = 5;}
   if (digitalRead (setupFswitch) == LOW){ mode = 6;}
}
if ( mode == 0 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 1 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 2 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 3 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 4 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 5 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 6 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all
if ( mode == 7 && digitalRead (ResetSwitch) == LOW){ mode = 9;} //reset all

Again, a little restructuring here leads to more readable code:

if(digitalRead (ResetSwitch) == LOW)
{
   if(mode > 0 && mode <= 7)
   {
      mode = 9;
   }
}

(and a lot less of it...)

I don't see how cases 2 through 6 differ from each other. Can you point out what I missed?

Thanks Paul, that is very helpful

PaulS:

long StepperAinterval = 60000; // 60 seconds break time between MINUTE movements          

long StepperBinterval = 360000; // 6 minute break time between HOUR movements



These should be:


long StepperAinterval = 60000UL; // 60 seconds break time between MINUTE movements         
long StepperBinterval = 360000UL; // 6 minute break time between HOUR movements

Just wondered what the UL is for? It seems to work fine without it.

One issue I do have however is that when Case 7 starts it is not always exactly 60 seconds before the first value is written to the motor, it seems to be random as to how long after pressing the switch to the first write, I am assuming it is something to do with the millis count not being zero when the case 7 starts to run.

After the first write it does stick strictly to the right timings for each motor so it is not too much of an issue and it is never seems to be more than a minute, Just a bit annoying.

Case 1-6 are all the same because the different 'start times' have not been entered yet. When I get the timings for the show these will all be set to different start points.

Thanks again for your help

Just wondered what the UL is for? It seems to work fine without it.

Unsigned Long. By default, literal constants are interpreted as ints. Since neither value IS an int, it is better to tell the compiler that the value isn't an int, by telling it what it is. That's what the UL on the end does.

One issue I do have however is that when Case 7 starts it is not always exactly 60 seconds before the first value is written to the motor, it seems to be random as to how long after pressing the switch to the first write, I am assuming it is something to do with the millis count not being zero when the case 7 starts to run.

Set PreviousMillsA and PreviousMillisB to CurrentMillisA, instead of 0, in the cases 2 to 6. This will ensure that nothing happens in case 7 until exactly 60 seconds has elapsed.

Case 1-6 are all the same because the different 'start times' have not been entered yet. When I get the timings for the show these will all be set to different start points.

There is a lot of duplicate code in the 6 cases. Having a series of if/else if/else statements to handle the differences (future) would shorten the code considerably.