Is it possible to sync RTC with RTC widget?

The project I am building hinges on the ability to maintain an in circuit clock, and I use the DS3231 Chronodot RTC. When I compile the sketch, the compiler catches what the time is on the uploading machine (PC), and starts a counting. Over time, RTCs are known to lose a few seconds here and there, but I was hoping that it is possible to program to refresh the time signature from an RTC widget on Blynk. Does anybody know if this is possible, and if yes, can you point me to some literature to get me going? Blynk has already vastly expanded the potential of my project so it is very cool if something like this is not possible, but I was hoping to have cake and eat it too, so to speak.

TYIA

@myggle Blynk’s RTC widget works fine so it should be able to update your clock.

The Adafruit page for your product mentions the Paul Stoffregen library that Blynk uses.
His GitHub is at https://github.com/PaulStoffregen/Time

I think it’s a simple as adding these few extra lines of code:

#include <DS1307RTC.h>  // or similar library required for time_t format 

void timedfunction(){
    // get time then
    time_t t = now();
    RTC.set(t);
}

Post your regular Chronodot sketch and I will check it over against the Blynk RTC sketch and the addition above.

I don’t use a Chronodot sketch, but rather RTCLib, a DS1307 library. The sketch that I took the lines of code from is the ds1307 sketch;

// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include <Wire.h>
#include "RTClib.h"

#if defined(ARDUINO_ARCH_SAMD)  // for Zero, output on USB Serial console
   #define Serial SerialUSB
#endif

RTC_DS1307 rtc;

char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

void setup () {
  while (!Serial);  // for Leonardo/Micro/Zero

  Serial.begin(57600);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }

  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }
}

void loop () {
    DateTime now = rtc.now();
    
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
    Serial.print(") ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    
    Serial.print(" since midnight 1/1/1970 = ");
    Serial.print(now.unixtime());
    Serial.print("s = ");
    Serial.print(now.unixtime() / 86400L);
    Serial.println("d");
    
    // calculate a date which is 7 days and 30 seconds into the future
    DateTime future (now + TimeSpan(7,12,30,6));
    
    Serial.print(" now + 7d + 30s: ");
    Serial.print(future.year(), DEC);
    Serial.print('/');
    Serial.print(future.month(), DEC);
    Serial.print('/');
    Serial.print(future.day(), DEC);
    Serial.print(' ');
    Serial.print(future.hour(), DEC);
    Serial.print(':');
    Serial.print(future.minute(), DEC);
    Serial.print(':');
    Serial.print(future.second(), DEC);
    Serial.println();
    
    Serial.println();
    delay(3000);
}

My project sketch samples the clock and sends its value through Blynk, but also other stuff will be programmed to occur at specific times. I would just feel more comfortable if my circuit’s RTC was frequently being updated via Blynk. This would add a level of redundancy.

@myggle ok for the sketch you are using the time setting is done in this format:

// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); 

So start with Blynk’s RTC sketch and add your extra libraries etc.

The Blynk sketch has a terrible layout for the time with no adjustment for minutes and seconds below 10.

So 5 seconds after 8 minutes passed 7am would show as 7:8:5.

The proper way to do it is:

char Date[16];
char Time[16];

and then in the clockDisplay() section use sprintf as:

  sprintf(Date, "%02d/%02d/%04d",  day(), month(), year());
  sprintf(Time, "%02d:%02d:%02d", hour(), minute(), second());

Same time will then show in a regular format of 07:08:05.

So now you have all the variables to be able to update your Chrondot. Decide on the frequency of updates from Blynk to Chronodot and set up a second timer similar to the clockDisplay() function.

All you need in the function is something like this:

  if(year() != 1970){
    rtc.adjust(DateTime(year(), month(), day(), hour(), minute(), second()));     
  }

Please provide your full working sketch when you have put it together.

Forgive me, but I don’t see how in code the RTC is reading a time stamp from Blynk. In

if(year() != 1970){
    rtc.adjust(DateTime(year(), month(), day(), hour(), minute(), second()));     
  }

… how does the rtc “adjust”?

I already have the elements in the sketch to display these and DHT values on Blynk widgets worked out. Am just trying to refine my use of the RTC by adding a second RTC to the first RTC.

Here’s my project code. I am still learning as I write it, so it’s far from perfect or complete, but the code used in the RTC process to my knowledge is near perfect.

/*
 * This code is a functional hydroponics controller.  Sensors are measured, preset values are
 * executed and devices are turned on and off, but also some devices are on with modulated voltage 
 * for speed control.  The sensors are an internal clock (RTC), atmospheric temperature and humidity
 * sensors (DHT22) and the users smartphone (w/Blynk).  Water quality sensors are also on the wishlist,
 * but this will be added in later.  The example code is already written for these other circuits.  
 * For more info see; https://www.atlas-scientific.com/
 * The devices consist of lights, fans, dehumidifiers, water pumps, water valves and 
 * nutrient dosing pumps.  All of the sensors dictate the usage of each device and the program combined
 * with Blynk extends the gardener's capability in the garden exponentially!
 * */
#include <SPI.h>                    // Used by Blynk
#include <Ethernet.h>               // Used by Blynk
#include <BlynkSimpleEthernet.h>    // Used by Blynk
#include <Wire.h>                   // I2c
#include "DHT.h"                    // DHT data wire connected to I/O pin with a 10k pullup resistor to 5V               
#include <SimpleTimer.h>            // used to run functions at preset intervals
#include "RTClib.h"                 // RealTimeClock Library for DS1307 and DS3231

//  DHT Reference Values - CHANGE THESE TO MANAGE YOUR ROOM'S CLIMATE - //
byte hiMaxTemp = 90;   // temp that triggers heat removal fan on
byte lowMaxTemp = 75;  // temp that triggers heat removal fan off
byte hiMinTemp = 70;   // temp that triggers heater on
byte lowMinTemp = 60;  // temp that triggers heater off
byte hiHum = 75;       // High humidity value that triggers dehumidifier on
byte lowHum = 70;      // Low humidity value that triggers dehumidifier off

//Digital Pin Peristaltic Pump Dosing Heads - 00 Series Pins - PWM/12v motors
#define GHfloraGro 2          //Pump 1 - Primary Nutrient               -  nutePumps[0]
#define GHfloraMicro 3        //Pump 2 - Primary Nutrient               -  nutePumps[1]
#define GHfloraBloom 4        //Pump 3 - Primary Nutrient               -  nutePumps[2]
#define GHfloraBlend 5        //Pump 4 - Metabolic Supplement           -  nutePumps[3]
#define GHarmorSi 6           //Pump 5 - Silica Supplement (raises pH)  -  nutePumps[4]
#define GHcaliMagic 7         //Pump 6 - Calcium/Magnesium Supplement   -  nutePumps[5]
#define GHkoolBloom 8         //Pump 7 - Bloom Supplement               -  nutePumps[6]
#define GHphDown 9            //Pump 8 - pH Lowering Agent              -  nutePumps[7]
int nutePumps[] {GHfloraGro, GHfloraMicro, GHfloraBloom, GHfloraBlend, GHarmorSi, GHcaliMagic, GHkoolBloom, GHphDown};

//Analong Pin Sensor Assignments - Analog Pins
//#define pH A0
//#define PPM A1
//#define waterTemp A2


#define lightA 22             //Relay 1/a  -  Relays[0]
#define lightB 23             //Relay 2/b  -  Relays[1]
#define pumpA 24              //Relay 3/c  -  Relays[2]
#define pumpB 25              //Relay 4/d  -  Relays[3]
#define ROpump 26             //Relay 5/e  -  Relays[4]
#define scrubberFan 27        //Relay 6/f  -  Relays[5]
#define heatVentA 28          //Relay 7/g  -  Relays[6]
#define heatVentB 29          //Relay 8/h  -  Relays[7]
int Relays[] {lightA, lightB, pumpA, pumpB, ROpump, scrubberFan, heatVentA, heatVentB};
//Digital Pin Relay Assignments - 30 Series Pins - 120VAC~ switching
//#define 30                  //2nd bank of 8 relays
//#define 31
//#define 32
//#define 33
//#define 34
//#define 35
//#define 36
//#define 37
//int Relays2[8] {30, 31, 32, 33, 34, 35, 36, 37};

//Digital Pin Valves[] array elements - 40 Series Pins - Hi/Lo Solenoid Valves
#define source 40             //waterValve[0]
#define upperProbes 41        //waterValve[1]
#define upperNuteRail 42      //waterValve[2]
#define siteReturnA 43        //waterValve[3]
#define siteReturnB 44        //waterValve[4]
#define lowerProbes 45        //waterValve[5]
#define lowerNuteRail 46      //waterValve[6]
#define betweenRails 47       //waterValve[7]
#define waste 48              //waterValve[8]
#define mainAccessA 49        //waterValve[9]
#define feedPlantA 50         //waterValve[10]
#define mainAccessB 51        //waterValve[11]
#define feedPlantB 52         //waterValve[12]
int Valves[] {source, upperProbes, upperNuteRail, siteReturnA, siteReturnB, lowerProbes, lowerNuteRail, betweenRails, waste, mainAccessA, feedPlantA, mainAccessB, feedPlantB};

#define TURN_ON 0 // TURN_ON and TURN_OFF are defined to account for Active High relays
#define TURN_OFF 1 // Used to switch relay states for on/off of AC devices   

RTC_DS1307 RTC;
float UTCOffset = -5.0;    // Your timezone relative to UTC (http://en.wikipedia.org/wiki/UTC_offset)

#define BLYNK_PRINT Serial
char auth[] = "PasteEmailedAuthTokenHere";
WidgetLCD lcdA(V2);  //Set LCD widget to Advanced Mode - Widget to display project clock

DHT dhtA(A0, DHT22);     // DHT instance named dhtA, I/O pin and sensor type
DHT dhtB(A1, DHT22);     // DHT instance named dhtB, I/O pin and sensor type

SimpleTimer timer;       // SimpleTimer instance named timer

void setup()
{
  Serial.begin(9600);
  //Serial2.begin() // For other baud rates
  //Serial3.begin() // For other baud rates
  Blynk.begin(auth);
  dhtA.begin();
  dhtB.begin();
  Wire.begin(); 
  RTC.begin();
  RTC.adjust(DateTime(__DATE__, __TIME__));

  //pinMode(nutePumps[8], INPUT_PULLUP);
  //pinMode(Valves[13], INPUT_PULLUP);
  pinMode(Relays[0, 1, 2, 3, 4, 5, 6, 7], OUTPUT);           // 
  //pinMode(lightA, OUTPUT);             //Relay 1/a  -  Relays[0]
  //pinMode(lightB, OUTPUT);             //Relay 2/b  -  Relays[1]
  //pinMode(pumpA, OUTPUT);              //Relay 3/c  -  Relays[2]
  //pinMode(pumpB, OUTPUT);              //Relay 4/d  -  Relays[3]
  //pinMode(ROpump, OUTPUT);             //Relay 5/e  -  Relays[4]
  //pinMode(scrubberFan, OUTPUT);        //Relay 6/f  -  Relays[5]
  //pinMode(heatVentA, OUTPUT);          //Relay 7/g  -  Relays[6]
  //pinMode(heatVentB, OUTPUT);
  pinMode(nutePumps[0, 1, 2, 3, 4, 5, 6, 7], OUTPUT);
  pinMode(Valves[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], OUTPUT);
 
  while (Blynk.connect() == false) {}

  timer.setInterval(10000L, timeRoutine); // 10 second intervals between timed routiness
  timer.setInterval(11100L, climateRoutine); // 11.1 second intervals between climate routines
  //timer.setInterval(xxxxL, testResB);
  //timer.setInterval(xxxxL, testResA);
  //timer.setInterval(xxxxL, testResB);

  delay(2500);
}

void loop()
{
  Blynk.run();
  timer.run();
}

//Functions

void climateRoutine()
{
  byte h1 = dhtA.readHumidity();          // f1 and h1 are fahrenheit and humidity readings
  byte f1 = dhtA.readTemperature(true);   // from DHT/A
  byte h2 = dhtB.readHumidity();          // f2 and h2 are fahrenheit and humidity readings 
  byte f2 = dhtB.readTemperature(true);   // from DHT/B

  Blynk.virtualWrite(V0, f1);      //  Set Virtual Pin 0 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V1, h1);      //  Set Virtual Pin 1 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V3, f2);      //  Set Virtual Pin 2 frequency to PUSH in Blynk app
  Blynk.virtualWrite(V4, h2);      //  Set Virtual Pin 3 frequency to PUSH in Blynk app
  Serial.println(f1);
  Serial.println(h1);
  Serial.println(f2);
  Serial.println(h2);
  //   Tests temp & humidity to see if preset values are exceed.
  //   If exceeded, a relay is trigger high to power an exhaust fan or heater.
  //**************************** REMOVING HEAT *************************************//
  if (f1 >= hiMaxTemp)  //if "f1" is greater than or equal to hiMaxTemp,
  {
    digitalWrite(heatVentA, TURN_ON);  // TURN_ON heatVentA (fan).
    Serial.print("\t");
    Serial.println(F("Exhausting the heat from A")); //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else if (f1 <= lowMaxTemp)  //  or else if "f1" is less than or equal to lowMaxTemp
  {
    digitalWrite(heatVentA, TURN_OFF); //  TURN_OFF relay E.
  }
}

void timeRoutine()
{
  DateTime now = RTC.now();  // reads time at beginning of loop

  byte twelveHour = now.hour() - 12; // Variable used to display 13+ hours in 12 hour format
  byte zeroHour = 12;                // Variable use to convert "0" zero hour to display it as 12:00+
  byte displayHour;
  byte MIN = now.minute();
  byte SEC = now.second();
  char* meridian;

  if (now.hour() == 0)  // First we test if the hour reads "0"
  {
    displayHour = zeroHour;
    meridian = "AM";
  }
  else if (now.hour() >= 13)  // if no, Second we test if the hour reads "13 or more"
  {
    displayHour = twelveHour;
    meridian = "PM";
  }
  else
  {
    displayHour = now.hour();
    meridian = "AM";
  }

  char timeStamp[11];
  char dateStamp[11];
  sprintf(timeStamp, "%02d:%02d:%02d-%02s", displayHour, MIN, SEC, meridian);
  sprintf(dateStamp, "%02d/%02d/%04d", now.month(), now.day(), now.year());
  String ts;
  String ds;
  ts = timeStamp;
  ds = dateStamp;

  lcdA.clear();            //Advanced Mode
  lcdA.print(3, 0, ts);    //Advanced Mode
  lcdA.print(3, 1, ds);    //Advanced Mode
  Serial.println(ts);
  Serial.print(ds);

  //*************** 12/12 BloomA Light - 6AM-6PM**********************************//
  //***** Adjust hours and minutes in accordance with 24 hour time format. *******//
  //***** Create tests where true is ON time and false is OFF time. *************//
  boolean lightAstate = false;
  if (now.hour() >= 6 && now.hour() <= 17) lightAstate = true;
  if (lightAstate == true)
  {
    digitalWrite(lightA, TURN_ON);
    Serial.print("\t");
    Serial.print(F("Bloom A Light On"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else
  {
    digitalWrite(lightA, TURN_OFF);
    Serial.print("\t");
    Serial.println(F("Bloom A Light OFf"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  //*************** 12/12 BloomB Light - 6PM-6AM**********************************//
  //***** Adjust hours and minutes in accordance with 24 hour time format. *******//
  //***** lightB is lit during the opposing 12 hours to lightA. *****************//
  boolean lightBstate = false;
  if (lightAstate == false) lightBstate = true;
  if (lightBstate == true)
  {
    digitalWrite(lightB, TURN_ON);
    Serial.print("\t");
    Serial.print(F("Bloom B Light On"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else
  {
    digitalWrite(lightB, TURN_OFF);
    Serial.print("\t");
    Serial.println(F("Bloom B Light OFf"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  //*********************************** FEED TIMEs - pumpA ******************************************//
  // By testing hour once and minute twice, we isolate the "on" time and the duration it is "on". *****//
  // If the Boolean becomes true, D32 is HIGH until Boolean becomes false again. **********************//
  boolean pumpAstate = false;
  if (now.hour() == 9 && now.minute() >= 0 && now.minute() < 10) pumpAstate = true;    //9:00 am - 10mins
  if (now.hour() == 15 && now.minute() >= 30 && now.minute() < 40) pumpAstate = true;  //3:30 pm - 10mins
  if (now.hour() == 22 && now.minute() >= 30 && now.minute() < 33) pumpAstate = true;  //10:30pm - 03mins
  if (pumpAstate == true)
  {
    digitalWrite(pumpA, TURN_ON);
    Serial.print("\t");
    Serial.println(F("Feeding PlantA"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else
  {
    digitalWrite(pumpA, TURN_OFF);
  }
  //*******************************Feed Times - pumpB *******************************************//
  boolean pumpBstate = false;
  if (now.hour() == 9 && now.minute() >= 0 && now.minute() < 10) pumpBstate = true;    //9:00 am - 10mins
  if (now.hour() == 15 && now.minute() >= 30 && now.minute() < 40) pumpBstate = true;  //3:30 pm - 10mins
  if (now.hour() == 22 && now.minute() >= 30 && now.minute() < 33) pumpBstate = true;  //10:30pm - 03mins
  if (pumpBstate == true)
  {
    digitalWrite(pumpB, TURN_ON);
    Serial.print("\t");
    Serial.println(F("Feeding PlantB"));  //  Text printed to serial monitor
    Serial.print("\t");
    Serial.println();
  }
  else
  {
    digitalWrite(pumpB, TURN_OFF);
  }
}

The reading from the Blynk server is in the standard RTC sketch provided by Blynk.

It uses RTC.begin() so basically you would only want to call this and the sprintf() when you are ready to synchronise the DS3231 with the server time for the first time.

Subsequent synchronisations could be done with the following command, which is referenced in the Blynk sketch, before or after the RTC.begin().

setSyncInterval(interval); // interval is declared in seconds

1 Like