Jump to content

SkySurveyBanner.jpg.21855908fce40597655603b6c9af720d.jpg

DIY Moon Phase Dial


Gina

Recommended Posts

  • Replies 1.1k
  • Created
  • Last Reply

12 hours elapsed and still within the second, maybe half a second :)  I reckon better than 1 part in 80,000 :)  I think this pretty much confirms that the non-gearbox stepper motor is doing the job :)

The other question is how accurately the time can be set without resorting to a position sensor on the seconds and that the timimg for this relies on the Nano's clock in addition to the minute hand Hall sensor.  I can probably get the minute sensing accurate to 5 or 10s and I could check the Nano clock accuracy by measuring the number of μs in a cycle of the RTC 1Hz square wave. eg. read micros on first positive edge of square wave and stop on next positive edge, subtract and print result.  Accurate to 4μs.

Quote

micros()

Description

Returns the number of microseconds since the Arduino board began running the current program. This number will overflow (go back to zero), after approximately 70 minutes. On 16 MHz Arduino boards (e.g. Duemilanove and Nano), this function has a resolution of four microseconds (i.e. the value returned is always a multiple of four).

With that accuracy I could use a half cycle as used for timing ATM.

Link to comment
Share on other sites

Using two half cycles should give greater accuracy.

void loop(){
  if (timeIsSet) { return; } //  break out once time is set
  while (digitalRead(2) == lastSqWave) {} ;  // wait for first clock edge
  lastSqWave = digitalRead(2);
  long time1 = micros();  //  save time of first clock edge
  while (digitalRead(2) == lastSqWave) {} ;  // wait for next clock edge
  lastSqWave = digitalRead(2);
  while (digitalRead(2) == lastSqWave) {} ;  // wait for next clock edge
  long time2 = micros(); //  save time of second clock edge
  Serial.print(" One second in "); Serial.print(time2 - time1); Serial.println(" micros");
  timeIsSet = true;
}

Using ten lots of this gives

Quote

 One second in 1000136 micros
 One second in 999556 micros
 One second in 999604 micros
 One second in 999596 micros
 One second in 999604 micros
 One second in 999604 micros
 One second in 999596 micros
 One second in 999600 micros
 One second in 999600 micros
 One second in 999600 micros

Repeat

Quote

 One second in 1000136 micros
 One second in 999560 micros
 One second in 999604 micros
 One second in 999608 micros
 One second in 999600 micros
 One second in 999600 micros
 One second in 999608 micros
 One second in 999600 micros
 One second in 999600 micros
 One second in 999608 micros

And again

Quote

 One second in 1000136 micros
 One second in 999556 micros
 One second in 999604 micros
 One second in 999600 micros
 One second in 999596 micros
 One second in 999612 micros
 One second in 999596 micros
 One second in 999600 micros
 One second in 999608 micros
 One second in 999604 micros

Why the first run is different from the others is a puzzle.  But the timing is within 5 parts in 10,000 or 1 in 2,000

Link to comment
Share on other sites

If the clock needs changing by 6 hours to set the time, the time taken is going to be 6 x 3600 / 150 = 144s so an error of 1 in 2000 is going to be a fraction of a second and the Nano clock error is negligible.  That's the Nano timing error out of the way and that just leaves the accuracy of detecting the minute hand position.

Link to comment
Share on other sites

Hi Gina,

My maths is very rusty but looking at your timing data and ditching the first value in each set - completely legit, honest guv! you end up with an average variation of 9.2 parts per million - slightly better than 1 part in one hundred thousand.  

I think that should do nicely:hello2:

Regards, Hugh

Link to comment
Share on other sites

Thank you Hugh :)  Yes, It's looking good :)

By commenting out some code, I can see how many seconds before the mark the minute hand is detected and apply correction.  It's 20s.  That sets the clock to exactly 12 o'clock, to the second.    With 64 microsteps to the second I just multiply the number of seconds by 64 and advance by that amount.

Then the number of microsteps required to set the clock to the right time (to the nearest second) is calculated, the "extra time" added on and the stepper motor sent the appropriate number of STEP pulses.

Link to comment
Share on other sites

I've been tittivating the speeds and microstepping has made a huge difference.  Not only is the motion much smoother and quieter but I can get much higher speeds (rather surprisingly) and better accuracy.  When advancing the clock to 12 o'clock it rotates at 20s an hour no bother giving a maximum time of 4m.

Link to comment
Share on other sites

Found that the RTC had lost it real time so have had to re-send the time to it via USB and Serial Monitor using this sketch

/*----------------------------------------------------------------------*
 * Display the date and time from a DS3231 or DS3232 RTC every second.  *
 * Display the temperature once per minute. (The DS3231 does a          *
 * temperature conversion once every 64 seconds. This is also the       *
 * default for the DS3232.)                                             *
 *                                                                      *
 * Set the date and time by entering the following on the Arduino       *
 * serial monitor:                                                      *
 *    year,month,day,hour,minute,second,                                *
 *                                                                      *
 * Where                                                                *
 *    year can be two or four digits,                                   *
 *    month is 1-12,                                                    *
 *    day is 1-31,                                                      *
 *    hour is 0-23, and                                                 *
 *    minute and second are 0-59.                                       *
 *                                                                      *
 * Entering the final comma delimiter (after "second") will avoid a     *
 * one-second timeout and will allow the RTC to be set more accurately. *
 *                                                                      *
 * No validity checking is done, invalid values or incomplete syntax    *
 * in the input will result in an incorrect RTC setting.                *
 *                                                                      *
 * Jack Christensen 08Aug2013                                           *
 *                                                                      *
 * Tested with Arduino 1.0.5, Arduino Uno, DS3231/Chronodot, DS3232.    *
 *                                                                      *
 * This work is licensed under the Creative Commons Attribution-        *
 * ShareAlike 3.0 Unported License. To view a copy of this license,     *
 * visit http://creativecommons.org/licenses/by-sa/3.0/ or send a       *
 * letter to Creative Commons, 171 Second Street, Suite 300,            *
 * San Francisco, California, 94105, USA.                               *
 *----------------------------------------------------------------------*/ 
 
#include <DS3232RTC.h>        //http://github.com/JChristensen/DS3232RTC
#include <Streaming.h>        //http://arduiniana.org/libraries/streaming/
#include <Time.h>             //http://playground.arduino.cc/Code/Time
#include <Wire.h>             //http://arduino.cc/en/Reference/Wire

void setup(void)
{
    Serial.begin(9600);
    
    //setSyncProvider() causes the Time library to synchronize with the
    //external RTC by calling RTC.get() every five minutes by default.
    setSyncProvider(RTC.get);
    Serial << F("RTC Sync");
    if (timeStatus() != timeSet) Serial << F(" FAIL!");
    Serial << endl;
}

void loop(void)
{
    static time_t tLast;
    time_t t;
    tmElements_t tm;

    //check for input to set the RTC, minimum length is 12, i.e. yy,m,d,h,m,s
    if (Serial.available() >= 12) {
        //note that the tmElements_t Year member is an offset from 1970,
        //but the RTC wants the last two digits of the calendar year.
        //use the convenience macros from Time.h to do the conversions.
        int y = Serial.parseInt();
        if (y >= 100 && y < 1000)
            Serial << F("Error: Year must be two digits or four digits!") << endl;
        else {
            if (y >= 1000)
                tm.Year = CalendarYrToTm(y);
            else    //(y < 100)
                tm.Year = y2kYearToTm(y);
            tm.Month = Serial.parseInt();
            tm.Day = Serial.parseInt();
            tm.Hour = Serial.parseInt();
            tm.Minute = Serial.parseInt();
            tm.Second = Serial.parseInt();
            t = makeTime(tm);
            RTC.set(t);        //use the time_t value to ensure correct weekday is set
            setTime(t);        
            Serial << F("RTC set to: ");
            printDateTime(t);
            Serial << endl;
            //dump any extraneous input
            while (Serial.available() > 0) Serial.read();
        }
    }
    
    t = now();
    if (t != tLast) {
        tLast = t;
        printDateTime(t);
        if (second(t) == 0) {
            float c = RTC.temperature() / 4.;
            float f = c * 9. / 5. + 32.;
            Serial << F("  ") << c << F(" C  ") << f << F(" F");
        }
        Serial << endl;
    }
}

//print date and time to Serial
void printDateTime(time_t t)
{
    printDate(t);
    Serial << ' ';
    printTime(t);
}

//print time to Serial
void printTime(time_t t)
{
    printI00(hour(t), ':');
    printI00(minute(t), ':');
    printI00(second(t), ' ');
}

//print date to Serial
void printDate(time_t t)
{
    printI00(day(t), 0);
    Serial << monthShortStr(month(t)) << _DEC(year(t));
}

//Print an integer in "00" format (with leading zero),
//followed by a delimiter character to Serial.
//Input value assumed to be between 0 and 99.
void printI00(int val, char delim)
{
    if (val < 10) Serial << '0';
    Serial << _DEC(val);
    if (delim > 0) Serial << delim;
    return;
}

Now to upload the clock sketch and see if everything works...

Link to comment
Share on other sites

RESULT!  :)

Changed step delay from 125μs to 100μs when setting the time and the clock is now set to the right time to the nearest second :)

56ec3969861d5_MoonClockSerialOutput2016-

Link to comment
Share on other sites

This is the sketch.  It wants a bit of tidying up to remove debugging code.  It will also want code added to cater for the GMT-BST "change the clocks".  But I'm going to leave it running for a while to check that it maintains correct time.

// Filename :- Moon_Clock_v2_with_NEMA16_Microstepping 01_2016-03-18_1700
// Arduino test sketch for noise on RTC square wave input.
// Software timing from RTC on pin 2 using polling
// Everything to do with the seconds motor has been removed
// moveBackward has been removed
//
#include <DS3232RTC.h>    //http://github.com/JChristensen/DS3232RTC
#include <Time.h>         //http://www.arduino.cc/playground/Code/Time  
#include <Wire.h>         //http://arduino.cc/en/Reference/Wire (included with Arduino IDE)
//
int timeError =  20;  //  Amount of time setting error to be corrected (seconds)
boolean timeIsSet = false;
int accel = 0;  // number of steps to accellerate to full speed
long steps, h, units, extra ; // time set variables;
int lastSqWave = 0, lastHallMinute = 1 ; //  Save logic states of square wave and Hall sensor for minute hand
int dirPin = 10;  // DIRECTION pin
int stepPin = 11; // STEP pin
int msPin = 12;  // Microstepping pin
void setup() {
  Serial.begin (9600);     // Enable Serial Monitor via USB
  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(msPin, OUTPUT);
  digitalWrite(msPin, 1);  //  Set 16x microstepping mode
  pinMode(2,INPUT_PULLUP);   //  One sec timing pin
  setSyncProvider(RTC.get);  // the function to get the time from the RTC
  if(timeStatus() != timeSet) 
      Serial.println(" Unable to sync with the RTC");
  else
      Serial.println(" RTC has set the system time");
  RTC.squareWave(SQWAVE_1_HZ);    // 1 Hz square wave            
}
//
void stepForward(void){
  digitalWrite(stepPin, 1);
  delayMicroseconds(5);     // Make STEP pulse at least 5μs long
  digitalWrite(stepPin, 0);
}
//
void fastForward(long steps, int wait) {  
  for (long i = steps; i >= 0; i--) { stepForward(); delayMicroseconds(wait); }  // step then wait before next step
}
//
void fastBackward(long steps, int wait) {  
  digitalWrite(dirPin, 1);
  delayMicroseconds(5);     // Wait before sending STEP pulses
  for (long i = steps; i >= 0; i--) { stepForward(); delayMicroseconds(wait); }  // step then wait 2ms before next step
  digitalWrite(dirPin, 0);
}
//
void runClock() {  // subroutine called on both edges of the RTC square wave
// Send 32 μsteps to motor every half second
  for (int i = 31; i >= 0; i--) { stepForward(); delayMicroseconds(15000); }
}
//
void displayTime(void)  // Digital time display on Serial Monitor
{
    // digital clock display of the time
    Serial.print(' ');
    Serial.print(hour());
    printDigits(minute());
    printDigits(second());
    Serial.print(' ');
    Serial.println(); 
}

void printDigits(int digits) {
    // utility function for digital clock display: prints preceding colon and leading 0
    Serial.print(':');
    if(digits < 10)
        Serial.print('0');
    Serial.print(digits); }
//
void loop(){
  if (timeIsSet) { 
    int val = digitalRead(2);  // read logic level of 1Hz square wave
    if (val != lastSqWave) { lastSqWave = val; runClock(); }  //  Call runClock on both edges of RTC square wave
    val = (analogRead(A7) < 500);  // Read logic level of Hall sensor for minute hand
    if (val != lastHallMinute) { lastHallMinute = val; if (val) { displayTime();} }
    } 
// return;  
  if (timeIsSet) { return; } //  break out once time is set
  Serial.println(" Moon Clock v2 - NEMA16 microstepping");
  Serial.println(" Setting hands to 12 o'clock");
//  Serial.println(analogRead(A6));
  while (analogRead((A6) > 500) && (accel  < 2000)) { fastForward(1,125); accel ++ ; } // move fast forward until 12 o'clock sensed with hour hand
  Serial.println(" Speeding up");
  while (analogRead(A6) > 500) { fastForward(1,0); } // move forward at full speed until 12 o'clock sensed with hour hand
  Serial.println(" Slowing down");
  while (analogRead(A7) > 500) { fastForward(1,125); } // move forward until 12 o'clock sensed with minute hand
  fastForward(timeError*64,125);   // correct error - seconds x 4 steps/s x 16x microstepping
  h = hour(); if (h >= 12) { h = h - 12; }  // convert 24hr clock to 12hr
  units = h * 3600;
  units += minute() * 60;
  units += second();
  steps = units * 64;
  extra = steps / 150;
  
  steps = steps + extra;  Serial.print(" Moving hands to ");
  Serial.print(h); Serial.print(":");
  Serial.print(minute()); Serial.print(" using ");
  Serial.print(steps);
  Serial.print(" steps - including ");
  Serial.print(extra);
  Serial.println(" extra steps");
  fastForward(steps, 100);
  timeIsSet = true;
}
// End

 

Link to comment
Share on other sites

I'm not sure I believe it but it looks like I have actually got this clock working properly and keeping time :)  It's certainly taken a long long time!

Still a few things to do to finish it.

  1. Case including moon dial hood
  2. Box for electronics
  3. Lighting for inside case - both hardware and code to control it
  4. Code to implement the hour change for GMT to BST and back
  5. Whatever I've forgotten :D
Link to comment
Share on other sites

Here's the latest log.  The variation in the RTC time readings is due to movement of the main axle and of the minutes tube on it.  Checking the seconds hand against my radio controlled digital wall clock, the time is accurate to the second.  Clock has been running continuously for over 14 hours.  My assessment is that the clock is now running with perfect timekeeping from the RTC - any error will be down to the RTC :)

56ed1b980588b_MoonClockSerialOutput2016-

Link to comment
Share on other sites

Looking at the lighting control, there's a ULN2003A taking output from pins D3 to D9 and ATM all outputs are free so I could use 6 with 2 separate RGB LED strings for more interesting lighting effects :D  Only 4 of the 7 outputs are PWM though so some limitation on effects.

Quote

PWM: 3, 5, 6, 9, 10, and 11. Provide 8-bit PWM output with the analogWrite() function.

As well as time controlled lighting effects I could vary the illumination with ambient light levels by adding an LDR on the case and connecting it to one of the several unused analogue inputs but that's something to consider for later.

Initially I shall probably set constant lighting and add special effects later - I want to get the clock usable ASAP.

Link to comment
Share on other sites

Working on the case and electronics box.  Since there are problems with my DIY Titan 3D printer that need solving, I've decided to make up the moon dial cowl from thin aluminium sheet for the curved part and 6mm plywood for the back.  I could then cover the inside with black velour flock paper.  The electronics box has been designed and waiting for the 3D printer bed to heat up ready for printing.

Link to comment
Share on other sites

Here's a couple of photos of the case with LED light strings and the electronics box just place in it (I shall need to see where it fits before screwing it onto the back of the box).

56ed7bd7c4321_CasewithLEDStrips01.thumb.56ed7c7d8fb29_CasewithLEDStrips03.thumb.

Link to comment
Share on other sites

To prevent the LEDs shining out into the room and the observer's eyes I shall need some opaque strips in front of the strings so that just the clock parts are lit.

Link to comment
Share on other sites

Here's a photo of the clock with just the front LED string lit and with black insulation tape applied to the outer edge of the dial.  I think this will be sufficient and I don't think the back LED string will add anything.  Looks like it needs another layer of tape to stop the LEDs shining through.

56eda45c51d12_CasewithFrontLEDString01.t

Link to comment
Share on other sites

Yes, much better - better than 2 parts per million.  But I may want to go better than that and may add a radio time code receiver.

Another possibility is to use the mains which is regulated to keep time as accurately as Greenwich.

Link to comment
Share on other sites

I have a problem with the new lighting strip stopping the pauls for the moon phase drive lifting enough to work so I'll have to either remove the LED strip there or alter the moon phase system to bring it a bit lower down.  Neither of these options are particularly easy nor terribly difficult.

Link to comment
Share on other sites

Cured the problem with the moon phase drive pauls and also mounted the rear bearing onto the case.  The main axle no longer flops around :)  So that's two more little jobs done - gradually getting there :)

Link to comment
Share on other sites

Regarding the LED string illumination, PWM outputs are available on D3, D5 and D6 so I've connected red to D3, green to D5 and blue to D6.

Link to comment
Share on other sites

With things back together I found the LED in the moon wasn't working :(  After much testing I found the problem was a break in an insulated wire - wire broken inside the insulation.  Had to dismantle the moon phase dial completely <grumble>.  Now to put it all back together and connect up again...

Link to comment
Share on other sites

Back together and the moon LED is working but somehow I've got a short to earth on the 12v line on the board.  I guess it's going to need some high powered inspection of the board for solder whiskers.  Disconnected the hall sensors and it's not that.  Has to be on the circuit board itself.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. By using this site, you agree to our Terms of Use.