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);
Paolo Zanchi
  • 951
  • 8
  • 22
ekaggrat
  • 21
  • 1

2 Answers2

1

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.

Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81
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/

aaa
  • 2,715
  • 2
  • 25
  • 40