2

I have designed and 3d printed a clock.

I am controlling the clock using a Arduino sketch running on a Attiny2313. The stepper motor I am using is a 12v 1:12 ratio stepper. The total number of steps per revolution are 288.

The problem I am facing is that I am unable to get the time right. I have got the steps per revolution of the minute ring bang on, but some how the motor gains 10/11 minutes every 24 hours.

I have used a lot of the code from the stepper motor library. I am not using the stepper motor library as the code gets larger than what can fit into the 2k attiny memory.

Any help is appreciated.

int number_of_steps = 288;
int motor_pin_1 = 9;
int motor_pin_2 = 10;
int motor_pin_3 = 11;
int motor_pin_4 = 12;
int ledpin = 13;
int delayTime = 4;
int buttonPin = 4;
int buttonState = 0; 
int enablepin = 8;
int whatspeed = 40;
int last_step_time =0;
int step_number = 0;
[color=red]int blinkDelay = 939; [/color]
int step_delay = 60L * 1000L / number_of_steps / whatspeed;
int step_direction = 0;
void setup() {


 pinMode(motor_pin_1, OUTPUT);
 pinMode(motor_pin_2, OUTPUT);
 pinMode(motor_pin_3, OUTPUT);
 pinMode(motor_pin_4, OUTPUT);
 pinMode(buttonPin,INPUT);
 pinMode(ledpin, OUTPUT);
 pinMode(enablepin,OUTPUT);

}

void loop() {
 buttonState = digitalRead(buttonPin);
 if (buttonState ==HIGH){
   enablemotor();
   movestep(20);
   digitalWrite(ledpin,HIGH);
 }
 else{
   digitalWrite(ledpin,LOW);

   for (int xx = 0; xx<59;xx ++){

     enablemotor();
     movestep(42);
     //delay(100);
     motoroff();
     blinkk();
   }
   enablemotor();
   movestep(89);//85;

   motoroff();
   blinkk();
  //delay(3000);
 }
}



void enablemotor(){
 digitalWrite(enablepin,HIGH);
}


void movestep(int steps_to_move){
 int steps_left = abs(steps_to_move);

 if (steps_to_move > 0) { 
   step_direction = 1;
 }
 if (steps_to_move < 0) { 
   step_direction = 0;
 }


 while(steps_left > 0) {
   // if (millis() - last_step_time >= step_delay ) {
   //last_step_time = millis();
   if ( step_direction == 1 ){
     step_number++;
     delay(step_delay);
     if (step_number == number_of_steps) {
       step_number = 0;
     }
   }
   else {
     if (step_number ==0){
       step_number = number_of_steps;
     }
     step_number--;
   }
   steps_left--;


   stepmotor(step_number % 4);


   //} 
 }
}





void stepmotor(int thisStep){

 switch (thisStep) {
 case 0:    // 1010
   digitalWrite(motor_pin_1, HIGH);
   digitalWrite(motor_pin_2, LOW);
   digitalWrite(motor_pin_3, HIGH);
   digitalWrite(motor_pin_4, LOW);
   break;
 case 1:    // 0110
   digitalWrite(motor_pin_1, LOW);
   digitalWrite(motor_pin_2, HIGH);
   digitalWrite(motor_pin_3, HIGH);
   digitalWrite(motor_pin_4, LOW);
   break;
 case 2:    //0101
   digitalWrite(motor_pin_1, LOW);
   digitalWrite(motor_pin_2, HIGH);
   digitalWrite(motor_pin_3, LOW);
   digitalWrite(motor_pin_4, HIGH);
   break;
 case 3:    //1001
   digitalWrite(motor_pin_1, HIGH);
   digitalWrite(motor_pin_2, LOW);
   digitalWrite(motor_pin_3, LOW);
   digitalWrite(motor_pin_4, HIGH);
   break;
 } 

}

void blinkk(){
 for (int i = 0 ; i<30;i++){
   buttonState = digitalRead(buttonPin);
   if (buttonState ==HIGH){
     break;
   }
   else{
     digitalWrite(ledpin, HIGH);   // set the LED on
     delay(blinkDelay);  // 968           // wait for a second
     digitalWrite(ledpin, LOW);    // set the LED off
     delay(blinkDelay); //968
   } 
 }
}

void motoroff(){
 digitalWrite(enablepin,LOW);
 digitalWrite(motor_pin_1, LOW);
 digitalWrite(motor_pin_2, LOW);
 digitalWrite(motor_pin_3, LOW);
 digitalWrite(motor_pin_4, LOW);
  • 3
    What is your clock source? – Ignacio Vazquez-Abrams Mar 24 '15 at 6:05
  • I guess the value for blinkDelay is too low. How did you even get that value? Even so, the clock of the ATTiny2313 isn't that accurate. Better get an RTC chip of sorts. – Gerben Mar 24 '15 at 15:07
2

It is very difficult to accurately control timings with delay(), because you have to account for the time needed to execute the rest of the code, including interrupts. You can easily have better accuracy by using millis() like this:

static unsigned long last_step_time;
unsigned long now = millis();
if (now - last_step_time >= step_delay) {
    do_one_step();
    last_step_time += step_delay;
}

This is almost what you have commented out in your own code, except that millis() should be called only once per iteration, hence the now variable.

This may still have less than perfect accuracy due to your clock source. If your Arduino is clocked from a quartz you should be able to get decent timing accuracy. If it is clocked from a ceramic oscillator you are out of luck. In any case, you can always improve the accuracy by calibrating your clock: you measure its drift and correct for it in software. Then you have only to worry about thermal fluctuations of the clock frequency, which should not be an issue with a quarts.

For best accuracy, you can use either an RTC or a watch crystal connected directly to your ATmega chip. This last solution is only possible if you go for a bare microcontroller, without the Arduino board. See this answer for more details about this option, as well as the way to correct for the drift in software.

| improve this answer | |
  • the millis i used in my code was acting weird. the clock worked for 288 steps and then the step delay value would become zero and the motor would stall . i couldnt figure out what was wrong and went back to using delay. i will try your code . – ekaggrat Mar 25 '15 at 8:03
  • 1
    @ekaggrat: Beware I just edited a mistake in the code above. last_step_time should be incremented by step_delay instead of becoming now, because it is intended to contain the time when the motor should have been pulsed, irrespective of whether the pulse has been delayed by some interrupt. – Edgar Bonet Mar 25 '15 at 9:17
0

You should consider using hardware timers (millis() uses this, see answer of edgarBonnet.

Or you might want to use a Real Time (Calendar) Clock chip, which often has even better accuracy (some even adjust to temperature).

Also a possibility is using a GPS module, and using the GPS time which should be extremely accurate and will not drift (since it's synchronized).

Post on AVR timer precision (hardware timers): http://www.seanet.com/~karllunt/interval.html

RTC (Real Time Clock) example: http://www.instructables.com/id/Real-Time-Clock-DS1302/

GPS example: http://blog.arduino.cc/2014/01/06/simple-arduino-micro-gps-clock-project-arduinomicromonday/

| improve this answer | |
  • millis() is based on a hardware timer, and is as accurate as your primary clock source. – Edgar Bonet May 24 '15 at 13:24
  • @EdgarBonet I was unaware of that, edited it in my answer. Though, you could say the primary clock source isn't 'accurate'. It'll drift in time by temperature differences, some RTC's even adjust for this. And (somehow) synchronizing (gps?) would be the best way to go. – Paul May 26 '15 at 9:18
  • Yes. By “as accurate as” I mean: no more accurate, no less accurate. It’s synonymous with “as inaccurate as”. – Edgar Bonet May 26 '15 at 12:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.