Binary Clock - using RTC

Hi All

I am using a modified version of the binary clock code from here (http://www.instructables.com/id/Arduino-Powered-Binary-Clock/?ALLSTEPS)

I am using it with an RTC instead of the little up/down buttons that you have to use each time. Currently I read the time from the RTC only in the setup() function and the code just takes it from there - looping around and checking if 1000ms have passed each time.

This strikes me as being rather inefficient. Is there a way of using interrupts to have the chip idling most of the time, and only "waking up" once a second to update the time if required?

I believe I can configure the RTC module to output a 1Hz square wave on it's currently unused "SQW" pin - could that drive the interrupt or could I save a wire and use an internal timer?

Looking forward to your hints!

My code FYI:

#include <Wire.h>
#include <RTClib.h>

/*
An open-source binary clock for Arduino. 
Based on the code from by Rob Faludi (http://www.faludi.com)
Code under (cc) by Daniel Spillere Andrade, www.danielandrade.net
http://creativecommons.org/license/cc-gpl
*/

RTC_DS1307 rtc;
int second, minute, hour;
int munit,hunit;

void setup() {
  
  for (int i=1; i <= 13; i++) {
    pinMode(i, OUTPUT);
  }

  Wire.begin();
  rtc.begin();
  
  DateTime now = rtc.now();
  second = now.second();
  minute = now.minute();
  hour = now.hour();
}

void loop() {

static unsigned long lastTick = 0; // set up a local variable to hold the last time we moved forward one second
// (static variables are initialized once and keep their values between function calls)
// move forward one second every 1000 milliseconds

if (millis() - lastTick >= 1000) {
 lastTick = millis();
 second++;
}

// move forward one minute every 60 seconds
 if (second >= 60) {
 minute++;
 second = 0; // reset seconds to zero
}

// move forward one hour every 60 minutes
if (minute >=60) {
 hour++;
 minute = 0; // reset minutes to zero
}

if (hour >=24) {
 hour=0;
 minute = 0; // reset minutes to zero
}

 munit = minute%10; //sets the variable munit and hunit for the unit digits
 hunit = hour%10;

 // minute units
 // LED 1 binary 1
 if(munit == 1 || munit == 3 || munit == 5 || munit == 7 || munit == 9) {
   digitalWrite(1, HIGH);
 } else {
   digitalWrite(1,LOW);
 }
 
 // LED 2 binary 2
 if(munit == 2 || munit == 3 || munit == 6 || munit == 7) {
   digitalWrite(2, HIGH);
 } else {
   digitalWrite(2,LOW);
 }
 
 //LED 3 binary 4
 if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {
   digitalWrite(3, HIGH);
 } else {
   digitalWrite(3,LOW);
 }
 
 //LED 4 binary 8
 if(munit == 8 || munit == 9) {
   digitalWrite(4, HIGH);
 } else {
   digitalWrite(4,LOW);
 }

 //minutes tens
 if((minute >= 10 && minute < 20) || (minute >= 30 && minute < 40) || (minute >= 50 && minute < 60)) {
   digitalWrite(5, HIGH);
 } else {
   digitalWrite(5,LOW);
 }
 
 if(minute >= 20 && minute < 40) {
   digitalWrite(6, HIGH);
 } else {
   digitalWrite(6,LOW);
 }
 
 if(minute >= 40 && minute < 60) {
   digitalWrite(7, HIGH);
 } else {
   digitalWrite(7,LOW);
 }

 //hour units
 if(hunit == 1 || hunit == 3 || hunit == 5 || hunit == 7 || hunit == 9) {
   digitalWrite(8, HIGH);
 } else {
   digitalWrite(8,LOW);
 }
 
 if(hunit == 2 || hunit == 3 || hunit == 6 || hunit == 7) {
   digitalWrite(9, HIGH);
 } else {
   digitalWrite(9,LOW);
 }
 
 if(hunit == 4 || hunit == 5 || hunit == 6 || hunit == 7) {
   digitalWrite(10, HIGH);
 } else {
   digitalWrite(10,LOW);
 }
 
 if(hunit == 8 || hunit == 9) {
   digitalWrite(11, HIGH);
 } else {
   digitalWrite(11,LOW);
 }

 //hour
 if(hour >= 10 && hour < 20) {
   digitalWrite(12, HIGH);
 } else {
   digitalWrite(12,LOW);
 }
 
 if(hour >= 20 && hour < 24) {
   digitalWrite(13, HIGH);
 } else {
   digitalWrite(13,LOW);
 }
}

Yes. Enable the square wave output and set it 1Hz rate. Output is open drain, so need a pullup resistor.

If want to save a wire, compare the maintained time to the RTC time every so often and adust maintained time if it is off.

Thank you

Just looking thought the rtclib library and it doesn't seem to implement the configuration of the square wave output. Is this something I'd have to do on each boot or is it set once (maybe using the Wire lib?) somehow and forget about it?

Cheers
James

Answered my own question thanks to a bit of searching:

#define DS1307_I2C_ADDRESS 0x68

In setup()

Wire.beginTransmission(DS1307_I2C_ADDRESS);
Wire.write(0x07); // move pointer to SQW address
Wire.write(B00010000); // set bit for SQW enable and 1Hz output
Wire.endTransmission();

CrossRoads:
Yes. Enable the square wave output and set it 1Hz rate. Output is open drain, so need a pullup resistor.

If want to save a wire, compare the maintained time to the RTC time every so often and adust maintained time if it is off.

You can save the resistor by enabling internal pull-up...

Depends on how sharp of a low to high transition you can live with. 4.7K will snap it up; internal ~20K will get up there muh slower.

Right, I have the SQW pin generating the 1Hz wave using a 2.2K pull up and this seems to work - I can implement a simple blinking light when the interrupt service routine is triggered.

However, I can't get the ISR to execute my code - I get no lights lit at all!

Am I trying to do to much inside the ISR? All my logic is there, and loop(){} is empty. Is this a bad idea?

SQW output is going to digital pin 2 (INT0) so I have moved the LED that was there to digital 0.

#include <Wire.h>
#include <RTClib.h>
#define DS1307_I2C_ADDRESS 0x68

/*
An open-source binary clock for Arduino. 
Based on the code from by Rob Faludi (http://www.faludi.com)
Code under (cc) by Daniel Spillere Andrade, www.danielandrade.net
http://creativecommons.org/license/cc-gpl
*/

RTC_DS1307 rtc;
int second, minute, hour;
int munit,hunit;

void setup() {
  
  for (int i=0; i <= 13; i++) {
    pinMode(i, OUTPUT);
  }

  //pinMode(13, OUTPUT);
  pinMode(2, INPUT);

  Wire.begin();
  rtc.begin();
  
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0x07); // move pointer to SQW address
  Wire.write(B00010000); // Set up 1Hz SQW output
  Wire.endTransmission();
  
//  DateTime now = rtc.now();
//  second = now.second();
//  minute = now.minute();
//  hour = now.hour();
  
  attachInterrupt(0, isr0, FALLING);
  
}

void isr0() {

  DateTime now = rtc.now();
  second = now.second();
  minute = now.minute();
  hour = now.hour();
  
  munit = minute%10; //sets the variable munit and hunit for the unit digits
  hunit = hour%10;

 // minute units
 // LED 1 binary 1
 if(munit == 1 || munit == 3 || munit == 5 || munit == 7 || munit == 9) {
   digitalWrite(1, HIGH);
 } else {
   digitalWrite(1,LOW);
 }
 
 // LED 2 binary 2
 if(munit == 2 || munit == 3 || munit == 6 || munit == 7) {
   digitalWrite(0, HIGH);
 } else {
   digitalWrite(0,LOW);
 }
 
 //LED 3 binary 4
 if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {
   digitalWrite(3, HIGH);
 } else {
   digitalWrite(3,LOW);
 }
 
 //LED 4 binary 8
 if(munit == 8 || munit == 9) {
   digitalWrite(4, HIGH);
 } else {
   digitalWrite(4,LOW);
 }

 //minutes tens
 if((minute >= 10 && minute < 20) || (minute >= 30 && minute < 40) || (minute >= 50 && minute < 60)) {
   digitalWrite(5, HIGH);
 } else {
   digitalWrite(5,LOW);
 }
 
 if(minute >= 20 && minute < 40) {
   digitalWrite(6, HIGH);
 } else {
   digitalWrite(6,LOW);
 }
 
 if(minute >= 40 && minute < 60) {
   digitalWrite(7, HIGH);
 } else {
   digitalWrite(7,LOW);
 }

 //hour units
 if(hunit == 1 || hunit == 3 || hunit == 5 || hunit == 7 || hunit == 9) {
   digitalWrite(8, HIGH);
 } else {
   digitalWrite(8,LOW);
 }
 
 if(hunit == 2 || hunit == 3 || hunit == 6 || hunit == 7) {
   digitalWrite(9, HIGH);
 } else {
   digitalWrite(9,LOW);
 }
 
 if(hunit == 4 || hunit == 5 || hunit == 6 || hunit == 7) {
   digitalWrite(10, HIGH);
 } else {
   digitalWrite(10,LOW);
 }
 
 if(hunit == 8 || hunit == 9) {
   digitalWrite(11, HIGH);
 } else {
   digitalWrite(11,LOW);
 }

 //hour
 if(hour >= 10 && hour < 20) {
   digitalWrite(12, HIGH);
 } else {
   digitalWrite(12,LOW);
 }
  
  
}

void loop() {

  
}

I doubt this call will work inside an interrupt:

DateTime now = rtc.now();

It would seem not!

I've worked around the issue. Now all the logic is back in the main loop(), but only gets executed when the ISR sets a flag.

void isr0() {
  isrflag = 1;
}

void loop() {
  while (isrflag == 0) { // loop around until the ISR sets isrflag
  }
  //Do stuff here
}

This works... however it bugs me that the CPU us essentially spinning round at 16Mhz constantly comparing isrflag to 0 when it could be just idle and waiting for an interrupt.

Have I now done it the "Right Way" or is there something more elegant that can have the CPU sleep or idle, or am I barking up the wrong tree entirely?

however it bugs me that the CPU us essentially spinning round at 16Mhz constantly comparing isrflag to 0 when it could be just idle and waiting for an interrupt.

There is no such thing as idling for the CPU, in any computer. It is always working, even if it is doing nothing more than seeing if there is anything to do.

The only change I'd make to your code would be to get rid of the while loop, and, instead only do something if the flag is set:

void loop()
{
  if(isrFlag)
  {
     // Update the display
     isrFlag = false;
  }
}

is there something more elegant that can have the CPU sleep

Now, that is an entirely different question. Sleeping is useful to extend battery life. But, if you are powering a bunch of LEDs by battery, via the Arduino, the savings you'd get by sleeping are minimal.

i just create an account to thanks your code was pretty good y only need to change some little things to the RTC so the arduino could use the values on the leds

is all thanks to you i can present this without using arduinos internal clock thanks you if you want to know what i change y put the code here again

#include <Wire.h>
#include <RTClib.h>


RTC_Millis RTC;
int second, minute, hour;
int munit,hunit;

void setup() {
  
  for (int i=1; i <= 13; i++) {
    pinMode(i, OUTPUT);
  }

  Wire.begin();
  RTC.begin(DateTime(__DATE__, __TIME__));
  
  DateTime now = RTC.now();
  second = now.second();
  minute = now.minute();
  hour = now.hour();
}

void loop() {

static unsigned long lastTick = 0; // set up a local variable to hold the last time we moved forward one second
// (static variables are initialized once and keep their values between function calls)
// move forward one second every 1000 milliseconds

if (millis() - lastTick >= 1000) {
 lastTick = millis();
 second++;
}

// move forward one minute every 60 seconds
 if (second >= 60) {
 minute++;
 second = 0; // reset seconds to zero
}

// move forward one hour every 60 minutes
if (minute >=60) {
 hour++;
 minute = 0; // reset minutes to zero
}

if (hour >=24) {
 hour=0;
 minute = 0; // reset minutes to zero
}

 munit = minute%10; //sets the variable munit and hunit for the unit digits
 hunit = hour%10;

 // minute units
 // LED 1 binary 1
 if(munit == 1 || munit == 3 || munit == 5 || munit == 7 || munit == 9) {
   digitalWrite(1, HIGH);
 } else {
   digitalWrite(1,LOW);
 }
 
 // LED 2 binary 2
 if(munit == 2 || munit == 3 || munit == 6 || munit == 7) {
   digitalWrite(2, HIGH);
 } else {
   digitalWrite(2,LOW);
 }
 
 //LED 3 binary 4
 if(munit == 4 || munit == 5 || munit == 6 || munit == 7) {
   digitalWrite(3, HIGH);
 } else {
   digitalWrite(3,LOW);
 }
 
 //LED 4 binary 8
 if(munit == 8 || munit == 9) {
   digitalWrite(4, HIGH);
 } else {
   digitalWrite(4,LOW);
 }

 //minutes tens
 if((minute >= 10 && minute < 20) || (minute >= 30 && minute < 40) || (minute >= 50 && minute < 60)) {
   digitalWrite(5, HIGH);
 } else {
   digitalWrite(5,LOW);
 }
 
 if(minute >= 20 && minute < 40) {
   digitalWrite(6, HIGH);
 } else {
   digitalWrite(6,LOW);
 }
 
 if(minute >= 40 && minute < 60) {
   digitalWrite(7, HIGH);
 } else {
   digitalWrite(7,LOW);
 }

 //hour units
 if(hunit == 1 || hunit == 3 || hunit == 5 || hunit == 7 || hunit == 9) {
   digitalWrite(8, HIGH);
 } else {
   digitalWrite(8,LOW);
 }
 
 if(hunit == 2 || hunit == 3 || hunit == 6 || hunit == 7) {
   digitalWrite(9, HIGH);
 } else {
   digitalWrite(9,LOW);
 }
 
 if(hunit == 4 || hunit == 5 || hunit == 6 || hunit == 7) {
   digitalWrite(10, HIGH);
 } else {
   digitalWrite(10,LOW);
 }
 
 if(hunit == 8 || hunit == 9) {
   digitalWrite(11, HIGH);
 } else {
   digitalWrite(11,LOW);
 }

 //hour
 if(hour >= 10 && hour < 20) {
   digitalWrite(12, HIGH);
 } else {
   digitalWrite(12,LOW);
 }
 
 if(hour >= 20 && hour < 24) {
   digitalWrite(13, HIGH);
 } else {
   digitalWrite(13,LOW);
 }
}

Moderator edit: Please use CODE TAGS when posting code.
Also your code would be shorter and simpler with some simple binary mask operations.