Adding DS2417 RTC to existing analog clock code

Hi

I have already written code for an analog clock that uses a stepper motor for the output. The code works but it losses accuracy over a few week.

The circuit board size means that space is a premium so the smallest RTC I can find is the DS2417.

I have never used a RTC and have no idea on where to start with adding it to my code to make the accuracy better.

Here is my original clock code

#define SP1 8
#define SP2 9
#define SP3 10
#define SP4 11

#define SET_PIN 7



#include "myPushSwitch.h"

// Set input has internal pullup; LOW when active; no double-click; no double-click delay; use default debounce time
PushSwitch setSwitch(SET_PIN, INPUT_PULLUP, LOW, false);



const uint32_t ONE_MINUTE = 60 * 1000UL;
const uint32_t MAX_SET_STEP_INTERVAL = 1000UL;
const uint32_t MIN_SET_STEP_INTERVAL = 50UL;
const uint32_t SET_STEP_DELTA = 100UL;



void parkMotor()
{
  // step 0
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, LOW);
  digitalWrite(SP3, LOW);
  digitalWrite(SP4, LOW);
}

void stepMotor(uint16_t tickLength)
{
  // step 0
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, HIGH);
  digitalWrite(SP3, HIGH);
  digitalWrite(SP4, HIGH);
  delayMicroseconds(tickLength);
  // step 1
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, HIGH);
  digitalWrite(SP3, HIGH);
  digitalWrite(SP4, LOW);
  delayMicroseconds(tickLength);
  // step 2
  digitalWrite(SP1, HIGH);
  digitalWrite(SP2, HIGH);
  digitalWrite(SP3, HIGH);
  digitalWrite(SP4, LOW);
  delayMicroseconds(tickLength);
  // step 3
  digitalWrite(SP1, HIGH);
  digitalWrite(SP2, LOW);
  digitalWrite(SP3, LOW);
  digitalWrite(SP4, LOW);
  delayMicroseconds(tickLength);
  // step 4
  digitalWrite(SP1, HIGH);
  digitalWrite(SP2, LOW);
  digitalWrite(SP3, LOW);
  digitalWrite(SP4, HIGH);
  delayMicroseconds(tickLength);
  // step 5
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, LOW);
  digitalWrite(SP3, LOW);
  digitalWrite(SP4, HIGH);
  delayMicroseconds(tickLength);
  // step 6
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, HIGH);
  digitalWrite(SP3, HIGH);
  digitalWrite(SP4, HIGH);
  delayMicroseconds(tickLength);
  // idle
  parkMotor();
}



uint32_t lastStepTime;
uint32_t stepInterval;



void setup()
{
  Serial.begin(115200);
  Serial.println("program started");

  pinMode(SP1, OUTPUT);
  pinMode(SP2, OUTPUT);
  pinMode(SP3, OUTPUT);
  pinMode(SP4, OUTPUT);
  parkMotor();
  setSwitch.begin();

  lastStepTime = millis();
  stepInterval = ONE_MINUTE;

}

void loop()
{

  switchEvent event = setSwitch.update();
  if (event == switchPressed)
  {
    Serial.print("switch pressed - ");
    stepInterval = MAX_SET_STEP_INTERVAL;
    Serial.println(stepInterval);
  }
  else if (event == switchReleased)
  {
    lastStepTime = millis();
    stepInterval = ONE_MINUTE;
    Serial.println("switch released");
  }

  if (millis() - lastStepTime > stepInterval)
  {
    lastStepTime = millis();
    Serial.println("tick");
    stepMotor(6000);
    switchState state = setSwitch.read();
    if (state == switchWasPressed)
    {
      Serial.print("switch still pressed - ");
      if (stepInterval > (MIN_SET_STEP_INTERVAL + SET_STEP_DELTA))
      {
        stepInterval -= SET_STEP_DELTA;

      }
      else
      {
        stepInterval = MIN_SET_STEP_INTERVAL;
      }
      Serial.println(stepInterval);
    }
  }

}

Any help would be appreciated.

Thank You

You don't start adding it to your code. You will first have to figure out how to work with the DS2417; write test sketches till you understand and next integrate.

Found a related question with some code; might work for you.

http://www.technoblogy.com/show?1MG3

Might help ...

Hi All

I've found some code for anther 1 wire RTC and changed some bits to get the uno to send and receive from the DS2417.

#include <OneWire.h>

// DS2417 Real Time Clock
OneWire ds(A0);  // on pin A0

void setup(void) {
  Serial.begin(9600);
   pinMode(LED_BUILTIN, OUTPUT);
}

void loop(void) {
  byte i;
  byte present = 0;
  byte data[8];
  byte addr[8];

  if ( !ds.search(addr)) {
      Serial.print("No more addresses found.\n");
      ds.reset_search();
      delay(500);  // for readability
      return;
  }

  Serial.print("ROM: ");
  for( i = 0; i < 8; i++) {
    Serial.print(addr[i], HEX);
    Serial.print(" ");
  }

  if ( OneWire::crc8( addr, 7) != addr[7]) {
      Serial.print("CRC is not valid!\n");
      return;
  }

  if ( addr[0] != 0x27) {
      Serial.print("\t\tDevice is not a DS1904 family device.\n");
      return;
  }


  // write!
  Serial.println("writing to RTC...");
  present = ds.reset();
  ds.select(addr);
  ds.write(0x99,1);   // write RTC - this is the write code
  ds.write(0xAC);  //This is the control byte.  AC in hex = 10101100
                   //read the datasheet and you will see that this is important
                   //to start the internal osc's... Or to make the clock start
                   //counting seconds.  --ril3y
 // ds.write(0x00);  //0x02 is a random time set it with your own
 // ds.write(0x03);  //same with this
//  ds.write(0x05);  //this
 // ds.write(0x08);  //and this
  present = ds.reset();
  delay(1000);     // unknown if wait needed


  // read!
  present = ds.reset();
  ds.select(addr);
  ds.write(0x66,1);   // read RTC

  Serial.print("PR: ");
  Serial.print(present, HEX);
  for ( i = 0; i < 5; i++) {
    data[i] = ds.read();
  }
  Serial.print(" CTRL BYTE:  ");
  Serial.print(data[0], BIN);
  Serial.print("\n\ttime:  ");
  for ( i = 1; i < 5; i++) {
    Serial.print(data[i], HEX);
    Serial.print(" ");
    }


  Serial.println();
  
}

My problems now are using the data that the RTC is giving me to control the time.

My first thought was to tell the uno to turn on the LED when a minute had past then turn it off after another minute.

The time is being output as HEX

How would I convert that into something I could see on serial monitor

Thank You

The data that you get is a 32bit counter value. So you need to convert your 4 bytes to an unsigned long.

First thing to do is to determine if you get the least significant byte first or te most significant byte first. Of the 4 bytes that you receive from

  for ( i = 1; i < 5; i++) {
    Serial.print(data[i], HEX);
    Serial.print(" ");
    }

Which one changes most frequently?

Once you know that, you can put it in the correct order in an unsigned long.

After that you need Paul Stoffregen's time library that will allow you to convert the unsigned long to a standard tm structure with year, month, day, hour etc. and the other way around.

The time is being output as HEX

What does that data look like? What does the datasheet for the RTC say about converting that data to useful information?

The datasheet says the data is LSB first.

The data on serial monitor shows

E2 0 0 0
E4 0 0 0
E6 0 0 0

And so on

This is the serial monitor output

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: 66 1020 00 00 0
No more addresses found.

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: 68 1040 00 00 0
No more addresses found.

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: 69 1050 00 00 0
No more addresses found.

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: 6B 1070 00 00 0
No more addresses found.

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: 6C 1080 00 00 0
No more addresses found.

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: 6E 1100 00 00 0
No more addresses found.

You can add the following to the end of your loop()

void loop()
{
  ...
  ...

  // pointer to an uint32_t
  uint32_t *counterValue;
  // counterValue points to the second data byte and treats it and the following bytes as an uint32_t
  counterValue = (uint32_t*)&data[1];
  // print the result
  Serial.print("counterValue = "); Serial.println(*counterValue, HEX);
}

You should see E2, E4, E6, ... FE, 100, 102, 104 etc

Sterretje I added the code you said and this is what I'm getting from the serial monitor.

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: C 1 0 0
counterValue = 10C
No more addresses found.

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: D 1 0 0
counterValue = 10D
No more addresses found.

ROM: 27 C6 B3 44 0 0 0 9E writing to RTC...
PR: 1 CTRL BYTE: 10101100
time: F 1 0 0
counterValue = 10F
No more addresses found.

How would I convert the counter value into seconds that I can see counting up.

The number that you see is 'seconds' according to the datasheet.

REAL-TIME CLOCK
The real-time clock is a 32-bit binary counter. It is incremented once per second. The real-time clock can
accumulate 136 years of seconds before rolling over. Time/date is represented by the number of seconds since a reference point, which is determined by the user. For example, 12:00 a.m., January 1, 1970 could be a reference point.

For demonstration, you can print millis() next to it and should see that both increment by the same amount.

So you can use that countValue instead of millis() except that it goes 1000 times slower; just give it a better name.

Ok so if that HEX is the time, how do I use that to control something, like turn on the LED after 60 seconds

Since it's counting seconds, you wait until it increments 60 times and then do your "something".

ullisees:
How would I convert the counter value into seconds that I can see counting up.

If you change this

 Serial.println(*counterValue, HEX);

to this

 Serial.println(*counterValue, DEC);

you should then see the seconds counting up.

@sterretje:
I know that, when you use pointers like that, it looks slick, but that might be too advanced for a project such as this one. It also appears that you are relying on the compiler to implement the uint32_t datatype as LSB first, which might or might not be justified.

Maybe better to do something like this instead:

// convert bytes in data[] array to a more useful form
uint32_t secondsCount;
secondsCount = (((((data[4]*256UL)+data[3])*256UL)+data[2])*256UL)+data[1];

// output the result of that conversion
Serial.print("secondsCount = ");
Serial.println(secondsCount);

It's not about looking slick.

If the sequence is the other way around, my approach indeed does not work and your solution is a matter of moving the indices around.

Odometer that last bit of code shows me the seconds in a format i can see clearly

Now to integrate the DS2417 code into my original clock code.

You will have to learn to understand what the print methods do. In this case, they give a text representation of a number (unsigned long). I used a hexadecimal representation because one can see more easily the relationship with the the 4 bytes; odometer used the decimal representation.

I think I have managed to combined the 2 sets of code.

Here is the code, can anyone see any problems or anywhere it can be improved

#include <OneWire.h>
#define SP1 8
#define SP2 9
#define SP3 10
#define SP4 11

#define SET_PIN 7
OneWire ds(A0);  // DS2417 on pin A0

#include "myPushSwitch.h"

// Set input has internal pullup; LOW when active; no double-click; no double-click delay; use default debounce time
PushSwitch setSwitch(SET_PIN, INPUT_PULLUP, LOW, false);



const uint32_t ONE_MINUTE = 60UL;
const uint32_t MAX_SET_STEP_INTERVAL = 1000UL;
const uint32_t MIN_SET_STEP_INTERVAL = 50UL;
const uint32_t SET_STEP_DELTA = 100UL;
uint32_t secondsCount;


void parkMotor()
{
  // step 0
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, LOW);
  digitalWrite(SP3, LOW);
  digitalWrite(SP4, LOW);
}

void stepMotor(uint16_t tickLength)
{
  // step 0
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, HIGH);
  digitalWrite(SP3, HIGH);
  digitalWrite(SP4, HIGH);
  delayMicroseconds(tickLength);
  // step 1
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, HIGH);
  digitalWrite(SP3, HIGH);
  digitalWrite(SP4, LOW);
  delayMicroseconds(tickLength);
  // step 2
  digitalWrite(SP1, HIGH);
  digitalWrite(SP2, HIGH);
  digitalWrite(SP3, HIGH);
  digitalWrite(SP4, LOW);
  delayMicroseconds(tickLength);
  // step 3
  digitalWrite(SP1, HIGH);
  digitalWrite(SP2, LOW);
  digitalWrite(SP3, LOW);
  digitalWrite(SP4, LOW);
  delayMicroseconds(tickLength);
  // step 4
  digitalWrite(SP1, HIGH);
  digitalWrite(SP2, LOW);
  digitalWrite(SP3, LOW);
  digitalWrite(SP4, HIGH);
  delayMicroseconds(tickLength);
  // step 5
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, LOW);
  digitalWrite(SP3, LOW);
  digitalWrite(SP4, HIGH);
  delayMicroseconds(tickLength);
  // step 6
  digitalWrite(SP1, LOW);
  digitalWrite(SP2, HIGH);
  digitalWrite(SP3, HIGH);
  digitalWrite(SP4, HIGH);
  delayMicroseconds(tickLength);
  // idle
  parkMotor();
}



uint32_t lastStepTime;
uint32_t stepInterval;

void onewire()

{
byte i;
  byte present = 0;
  byte data[8];
  byte addr[8];

  // write!
  Serial.println("writing to RTC...");
  present = ds.reset();
  ds.select(addr);
  ds.write(0x99, 1);   // write RTC - this is the write code
  ds.write(0x3C);  //This is the control byte.  3C in hex = 0011 1100
  present = ds.reset();
 


  // read!
  present = ds.reset();
  ds.select(addr);
  ds.write(0x66,1);   // read RTC

  // convert bytes in data[] array to a more useful form

secondsCount = (((((data[4]*256UL)+data[3])*256UL)+data[2])*256UL)+data[1];

} 

void setup()
{
  pinMode(SP1, OUTPUT);
  pinMode(SP2, OUTPUT);
  pinMode(SP3, OUTPUT);
  pinMode(SP4, OUTPUT);
  parkMotor();
  setSwitch.begin();

  lastStepTime = secondsCount;
  stepInterval = ONE_MINUTE;

}

void loop()
{

  switchEvent event = setSwitch.update();
  if (event == switchPressed)
  {
    stepInterval = MAX_SET_STEP_INTERVAL;
   }
  else if (event == switchReleased)
  {
    lastStepTime = secondsCount;
    stepInterval = ONE_MINUTE;
    }

  if (secondsCount - lastStepTime > stepInterval)
  {
    lastStepTime = secondsCount;
    stepMotor(6000);
    switchState state = setSwitch.read();
    if (state == switchWasPressed)
    {
      if (stepInterval > (MIN_SET_STEP_INTERVAL + SET_STEP_DELTA))
      {
        stepInterval -= SET_STEP_DELTA;

      }
      else
      {
        stepInterval = MIN_SET_STEP_INTERVAL;
      }
      }
  }

}

ullisees:
Here is the code, can anyone see any problems

Your code never does anything with the RTC.

What do i need to change