DS3231 RTC programming Hr,Min,Sec while device is active in a program

Hi,

I am using an Arduino Uno to measure the voltage of a cell as it discharges. The cell is discharged via the Arduino while the DS3231 RTC keeps track of elapsed discharge time in Hrs, Min, Sec. The time is sent to an I2C 2x16 LCD display as Hrs, Min, Sec. together with an indication of the cell being over or under the 3V set point.

Once the cell is under 3V the Arduino disables the discharge. The cell voltage rises above 3V and the discharge is enabled. This cycle will occur many times until the cell output stays below 3V.

I have the code working and have managed to capture and display the time the cell first discharges below 3V and I have been able to stop the RTC at 30 seconds by continuously writing 42H to the seconds register. According to the spec sheet the registers are in BCD format. Writing the actual seconds
give erratic results...perhaps time delays within the chip, Arduino etc....??

Once the cell rises above 3V writing 42H to the seconds reg stops, the clock continues counting up while the cell discharge is active. This operation is fine but gives a small discharge time error as I cannot stop and start the rtc at the precise second the voltage dropped below 3V.

I would like to stop and start the DS3231 withing the second by writing back the actual second to the seconds register.....but this gives erratic readings, so I have not bothered writing any code to convert the hex to bcd. Perhaps it is not possible to stop the seconds reg time at "any time" between 00 and 59 ??.

However it is interesting to note that by continuously writing a 42H to the seconds reg causes it to stop at exactly 30seconds and the min and hours do not advance. The error is small....I have been at this for weeks.

F.W.I.W writing other codes to the DS3231 seconds reg gives the results below.

Write Ds3231 seconds read
Wire.write(10); 14
Wire.write(20); 14
Wire.write(42); 30
*Wire.write (tm.Second); erratic reading

*Writing the actual seconds give erratic readings.

If there is any interest I can post the code. Thank you

Why do you want to stop the RTC?

the DS3231 RTC keeps track of elapsed discharge time in Hrs, Min, Sec.

Stopping and starting the the ds3231 to us it as an elapsed time totalizer is an "unusual" use. There are certainly less polite terms for what you are doing.

The ds3231 is particularly unsuited to what you are doing because it does not have a way to stop the oscillator while running with Vcc. The ds1307 does allow you to do that.

Calculating and storing elapsed time while meeting some condition is best done with variables, the millis() timer, and the EEPROM.

If there is any interest I can post the code. Thank you

Please post your code.

First read How to Use this Forum and make sure that the code is formatted using Auto Format (Control +T) and posted with code tags.

Hi,

By stopping the clock when cell is below 3V and starting the clock it when rises above 3V will
give me the total time the cell has remained above 3V.

For example, this is a simple thing to do with a regular electric clock that runs on a 1.5V battery.

Simply set the clock at 12am. When the cell is above 3V connect the battery and the clock runs.
When cell goes below 3V disconnect the battery and the clock stops. Finally the cell will stay below 3V and the clock will show total discharge time = total time the cell has been over 3V (however the clock will have a max time of 12hrs).

Biasing an op amp at +3V and having it drive a mosfet to connect/disconnect the 1.5V battery
from the clock will work.

In essence, this is what I am trying to achieve with the Arduino and the DS3231.

Thanks for the reply.

That’s a hacky / kludgy way to do it. Much better to just note the time when the test starts and again when it ends. Subtract the former from that latter and – viola, you have the time duration. Best to use “Unix time” (or similar) for this as the math is easy. It will give the duration in seconds. From there, it’s a trivial matter to convert into hours, minutes, and seconds.

Use the TimeLib.h to track time with internal millis() and set its setSyncProvider() function to true it up with the RTC every 10 minutes or so.

Hi,

I totally agree using the DS3231 rtc for this purpose is rather "disrespectful" of such an accurate device....please forgive :slight_smile:

However the low cost was very attractive. In regard to stopping the devices clock.

This is not what I am actually trying to do. After pouring over data sheets I felt that it was not possible to stop the clock for my application. So I though I could achieve the same results by writing the current seconds to the seconds register and in that manner appear to stop the clock.....maybe not so easy.

I did manage to do this but only reliably within 30 seconds of the actual time. If I must I can live with this error, I am beginner Arduino programmer.

A much better way would be to use the 1 second interrupt from the device and make a sec, min, hour, counter. Then I could easily stop and start the clock by disconnecting the alarm from the counter via software. However as a project I went down perhaps this wrong path.

Thanks for the reply.

After pouring over data sheets

Haven’t we all done that! Sigh.
Even worse if it’s inside a computer!
Usually coffee, cola or water.

gb2019:
A much better way would be to use the 1 second interrupt from the device and make a sec, min, hour, counter.

No. A much better way is what I told you in Reply #4.

That's why I took the time to write it up for you.

Hi,

I am trying to get the hang of using this help site for the first time.....how does one index to the individual message one is trying to reply to. Or does it go out generally and the reader has to sort it all out?

I am replying to reply #4 Posted by gfvalvo - Today at 01:22 am reply 4
re That's why I took the time to write it up for you.

Thanks for your reply gfvalvo. You mention Best to use "Unix time" (or similar) for this as the math is easy. It will give the duration in seconds. From there, it's a trivial matter to convert into hours, minutes, and seconds.

This cell measuring device must be stand alone and not connected to the net or any other computer etc.

Thanks

gb2019:
This cell measuring device must be stand alone and not connected to the net or any other computer etc.

As related to my suggestion, Unix Time has nothing to do with being on a network. It is simply the way computers have been keeping time for ~50 years. By using a 32-bit integers, you can determine the time duration between two events by simple subtraction.

I too recommend that you follow the approach suggested by gfvalvo.

Use the TimeLib.h to track time with internal millis() and set its setSyncProvider() function to true it up with the RTC every 10 minutes or so.

A good place to start is with choice of a DS3231 libarary. I strongly recommend using Jack Christensens DS3232RTC.h. It works with the DS3231 and is designed to integrate with the time library.

The other library to install is the Michael Margolis Time library.

Both are available through the library manager.

Set yourself up with these libraries. Run the DS3232 example TimeRTC. It shows how to use the time library synched with the RTC. There are also examples in the library of how to set the time on the RTC.

The time library command which returns "unix time" is now().

If what you want is a clock which you can stop and start at will, then the DS3231 is probably not the right tool for the job.
Actually, for what you're doing, it looks like you don't need an external RTC at all. The Arduino's built in micros() timer should suffice.

I believe that something like this will do the job for you. I haven't tested this code, though, so I can't be 100% sure that it will work properly.

For details on how the code is supposed to work, please read the comments within it.
Notice that I have not written code to make this work with your i2c LCD display. That is because I have never used such a display, and so I don't feel confident writing code for it.

// variable declarations for clock
byte hh=0; byte mi=0; byte ss=0; // hours, minutes, seconds
unsigned int dddd=0; // days (for if you want a calendar)
unsigned long lastTimeUpdate=0; // time that the clock was last updated
// ... and, to allow us to stop and start the clock,
byte clockRunning = 1;
// To pause the clock, set clockRunning to 0.
// To get the clock running again, set clockRunning to 1.

char buf[20]; // buffer for output

void setup() {
  Serial.begin(115200); // set baud rate for Serial
}

void loop() {
  
  // This next part is what makes our clock keep time.
  // First, we check if it is time to update the clock.
  // (We only update the clock once per second.)
  if ((micros() - lastTimeUpdate) >= 1000000UL) {
    
    // We add the value of clockRunning to the count of seconds.
    // If clockRunning is 1 (for a running clock), then this will advance the clock one second.
    // If clockRunning is 0 (for a paused clock), then this will advance the clock zero seconds.
    // (Of course, to "advance zero seconds" means to not advance at all!)     
    ss += clockRunning;
    
    // We do any necessary arithmetic for the minutes, hours, and days.
    if (ss >= 60) {
      ss -= 60;
      mi += 1;
    }
    if (mi >= 60) {
      mi -= 60;
      hh += 1;
    }
    if (hh >= 24) {
      hh -= 24;
      dddd += 1;
    }
    
    // And we schedule the next update for one second later.
    lastTimeUpdate += 1000000UL;  
  }
  
  // A clock isn't much use if you have no way of seeing the time.
  // So...
  // This next line composes a string showing the elapsed time:
  sprintf(buf,"%03dd %02dh %02dm %02ds", dddd, hh, mi, ss);
  // (That string should look something like this: "000d 00h 00m 00s")
  // And this next line writes that string to the Serial monitor:
  Serial.print(buf);
  
  // You can also write that string to an LCD display if you wish.
  // But, I'm not sure how you have your display wired up,
  // so I'll skip that part for now.  
}

Re Posted by cattledog- Today at 03:01 am …holy smokes, I hope that was not your real time!

Thank you for taking the time to present this code…it looks like it might do the job. I will give it a try
and keep you posted.

The “explicit comments are really appreciated”. In my Arduino wanderings on the net there are scant comments as to the actual function of the code.

Perhaps the creator does not really want understanding …magic maybe?, or perhaps they want it to appear as a “dark art” that only they can fathom.

I yearn for the times I could simply call a subroutine know exactly what it did and return to the main
program, the flow was natural.

Now I must contend with the curse of the “curly braces”…click on the bracket and somewhere deep down there in the depths of the program I find the closing bracket :frowning:

However, it is amazing how much a $5.00 Arduino can do.

Thanks cattledog.

Sorry… I thanked the wrong person…though how bad can that be?

My response was to odometer - Today at 05:54 am

Thanks, odometer

and to all.

gb2019:
In my Arduino wanderings on the net there are scant comments as to the actual function of the code.

Perhaps the creator does not really want understanding ...magic maybe?, or perhaps they want it to appear as a "dark art" that only they can fathom.

I think the answer is much simpler than that - Writing code is fun. Writing documentation and comments isn't. The authors presumably provided enough comments to help them understand and (hopefully) maintain the code ---- given the level of their knowledge and programming skill.

They make it available for others to use, FOR FREE. Given that you (presumably) didn't pay to have the code written, you don't have much room to complain that it's not documented to your level of knowledge and programming skill.

“They make it available for others to use, FOR FREE. Given that you (presumably) didn’t pay to have the code written,
you don’t have much room to complain that it’s not documented to your level of knowledge and programming skill.”

Good reality check, as one tends to forget the fact that it’s all free.

I am sure it is difficult for an experienced programmer to understand the view from down here.

:slight_smile:

gfvalvo:
They make it available for others to use, FOR FREE.

If it’s free, but you can’t get it to work, then it’s worth exactly what you paid for it.

Actually, even “free” code isn’t really free, if you factor in the user’s time and effort in getting it to work for him/herself.

@gb2019
I took another look at your post at the start of this thread, and in it, I noticed this:

gb2019:
F.W.I.W writing other codes to the DS3231 seconds reg gives the results below.

Write Ds3231 seconds read
Wire.write(10); 14
Wire.write(20); 14
Wire.write(42); 30
*Wire.write (tm.Second); erratic reading

*Writing the actual seconds give erratic readings.

and, in the same post, this:

I would like to stop and start the DS3231 withing the second by writing back the actual second to the seconds register…but this gives erratic readings, so I have not bothered writing any code to convert the hex to bcd.

Most likely, the erratic readings were precisely because you did not bother to convert hex to BCD.

gb2019:
If there is any interest I can post the code. Thank you

cattledog:
Please post your code.

You never did post your code.
If you had posted your code, I would have been able to give you better help: for example, I could have re-written the code in reply #11 so that it would better work with your project.

Re: DS3231 RTC programming Hr,Min,Sec while device is active in a program
#17 Aug 04, 2019, 10:04 am @gb2019
I took another look at your post at the start of this thread, and in it, I noticed this:

Hi,

I did try converting to BCD but did not help.

As requested here is the code. I have pretty well accepted it as it is, but if anyone can show me how to stop and restart the clock, it would be great. This has taken an inordinate amount of my time…months. It seems RTC.read™ creates a string that contains the time data. I tried to understand how it does this as I thought I could fool with the string behind the scenes but was unable to find where it was located.

Here is the somewhat kludgy code with liberal use of web links to help me and perhaps you…be kind:-) BTW I know the use of “goto” is discouraged, but according to the Arduino site is can be useful for small programs.

Having come from an assembly language background I like it a lot better than curly brackets as it tells you where you are heading. The code is over documented…just the way I like it!

Regards.

//Orig code KEVIN BAINES: Arduino Tutorial: 20x4 I2C Character LCD display with Arduino Uno
//DS1307RTC Library DS1307RTC Library, For Accessing Real Time Clock (RTC) Chips
//PrintTime ThinkJavaCode/PrintTime.java at master · AllenDowney/ThinkJavaCode · GitHub
//file name VdischTime.ino compiles with Arduno 1.8.8
//processor - Arduno Uno
//RTC timing device DS3231
//LCD display - 2 line 16 char with I2C communication

//Software executes as below

//1 Power on.
//2 Software time and date and function is displayed on the I2C 2 line 16 char display
//3 Connect charged cell (which must be above 1V) to A0 UNO pin 23.
//4 Press switch to start discharge at time t0.
//5 Display indicates voltage and elapsed discharge time since t0.
//6 When cell discharges to below 1V cell discharge stops and RTC stops with time = (t1).
//7 Cell voltage rises above 1V, discharge resumes and RTC continues timing up from t1.
//8 Cell again discharges below 1V. Cell discharge stops. This cycle will repeat many
// times until cell eventually stays below 1V. The LCD display “should” show total of all
// discharge times…but this is the problem, it does not as I have been unable to stop and
// restart the DS3231 RTC for every discharge cycle.
// I have ordered some DS 1307 which should be easier to stop.
// For accuracy I would like to stay with the DS3231 or the DS1307

// general info web sites
// curly braces correct use of https://forum.arduino.cc/index.php?topic=367219.0
// Arduino Code & Syntax Overview https://programmingelectronics.com/tutorial-3-arduino-ide-and-sketch-overview/

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DS1307RTC.h>
#include <LiquidCrystal_SR3W.h>
#include <DS3231.h>

//The DS3231 has a default I2C address of 0x68 and cannot be changed
//see RTClib - Arduino Libraries

const int en = 2, rw = 1, rs = 0, d4 = 4, d5 = 5, d6 = 6, d7 = 7, bl = 3;
int Z;

int inPin = 8; // input pin 8 for pushbutton (processor pin 14)
int val = 0; // variable for reading the pin status
int pin12 = 12; // pin 12 for complement to pin 13
int pin13 = 13; // pin 13 for the on board LED

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
DS3231 rtc(SDA, SCL);// Init the DS3231 using the hardware interface

void setup()
{
// Setup Serial connection

Serial.begin(9600); // serial connection speed to development platform
digitalWrite(inPin, HIGH); // enable pin 8 push button pullup resistor
pinMode(13, OUTPUT); // initialize digital pin LED_BUILTIN as an output
digitalWrite(13, LOW); // turn off led
pinMode(12, OUTPUT); // initialize digital pin 12 as output
digitalWrite(12, HIGH); // inverse level of above
rtc.begin();
lcd.begin(16,2);

lcd.clear(); // Clear the display

// show software version number, date, and function

lcd.setCursor ( 0, 0 ); // 1st line full left
lcd.print ("file V33.ino ");

lcd.setCursor ( 0, 2 ); // 2nd line full left
lcd.print ("09/08/19 ");
lcd.print (“12.09PM”);

delay(5000);

lcd.setCursor ( 0, 0 ); // 1st line full left
lcd.print ("Measures time ");

lcd.setCursor ( 0, 2 ); // 2nd line full left
lcd.print ("taken to disch. ");

delay(5000);

lcd.clear(); // Clear the display
lcd.setCursor ( 0, 0 ); // 1st line full left
lcd.print ("NIMH cell down ");

lcd.setCursor ( 0, 2 ); // 2nd line full left
lcd.print ("to 1.00V ");
lcd.setBacklight(HIGH); // Backlight on

delay(5000);

lcd.setCursor ( 0, 0 ); // 1st line full left
lcd.print ("Push switch to ");
lcd.setCursor ( 0, 2 ); // 2nd line full left
lcd.print ("start discharge ");

while (digitalRead (inPin) == HIGH) { // show software info & wait for p.b. switch to go low
}

rtc.setTime(00,00,00); // reset RTC to zero and start discharging cell
}

////////////////////////////////// END SHOW FILE INFO /////////////////////////////////

void loop()
{

int sensorValue0 = analogRead(A0); //read A0 UNO pin 23
float voltage0 = sensorValue0 * (5.0 / 1023.0); //Convert reading AD0 (0 - 1023) to voltage (0 - 5V):

tmElements_t tm; // Using a Real Time Clock with Arduino | DroneBot Workshop

if (RTC.read™) { //https://www.pjrc.com/teensy/td_libs_DS1307RTC.html
//Read the current date & time as TimeElements variable.
//See the Time library for TimeElements details. Returns
//true on success, or false if the time could not be read.

lcd.clear(); // Clear the lcd display for next reading of cell voltage

Halt1:

//read analog input

int sensorValue0 = analogRead(A0); //read V of cell on A0 UNO pin 23
float voltage0 = sensorValue0 * (5.0 / 1023.0); //convert reading AD0 (0 - 1023) to a voltage (0 - 5V):
lcd.setCursor ( 0, 1 ); //go to LCD 2nd line
printTime(0,1,tm); //print cell V info @ posn. 0 on line 2
if(voltage0<1.00) //is cell V below 1V on AD0 processor pin 23 ?

//If cell V is above 1.00V continue discharging and printing elapsed time

{

//voltage is below 1.00V stop cell discharge.

digitalWrite(13, LOW); // on board LED off = cell discharge complete
digitalWrite(12, HIGH); // inverse level of above
lcd.setCursor(0, 0); // 1st line full left
lcd.print ("UNDER 1 VOLT "); //is pin
goto Halt1; //display cell discharge time

} //begins at cell voltage check

//###################### cell above 3V execute below to update LCD #####################

//Print elapsed time as cell is above 3V and continue cell discharge

digitalWrite(13, HIGH); // on board LED on = discharging cell
digitalWrite(12, LOW); // inverse of above
lcd.setCursor(0, 0); // 1st line full left
lcd.print ("V IN = ");
lcd.print(voltage0); //print voltage to LCD
lcd.print(“V”);
delay(200); //sets LCD data refresh rate

} //Read the current date & time as TimeElements variable.

} //connects back beginning at void loop

void printTime(int character,int line, tmElements_t tm)

{ //ends at end of program

String seconds,minutes;
lcd.setCursor(character,line); //Structure containing a calendar date and time broken down into its components.
lcd.print(tm.Hour); //http://www.cplusplus.com/reference/ctime/tm/

lcd.print(":");
if(tm.Minute<10)
{
minutes = “0”+String(tm.Minute);
lcd.print(minutes);

}else
{
lcd.print(tm.Minute);

}
lcd.print(":");

if(tm.Second<10) //if tm.Second greater than 10 go to else
{
seconds = “0”+String(tm.Second); //https://www.tutlane.com/tutorial/csharp/csharp-string-format-method

lcd.print(seconds); //seconds units 0 to 9

}else //
{
lcd.print(tm.Second); //seconds tens 1 to 5
}

}

Even if you want to use an external RTC (such as the DS3231) for the sake of accuracy, there is still no need to continually reset the time.

The DS3231 can be configured to give pulses at a rate of 1 pulse per second. (Several other pulse rates are selectable, but that need not concern us.) Simply have the Arduino count these pulses, and you will know how many seconds have passed. Someone is using these pulses in his clock project, here: Stepper motor based clock movement with DS3231 - #10 by peterklopp - Programming Questions - Arduino Forum

There is yet another way to use the DS3231 to measure intervals, and that is the same way as you would use a wall clock to measure intervals. When the interval starts, you look at the clock, and you make a note of the time. When the interval ends, you again make a note of the time. The difference between the two times thus noted is the length of the interval you wish to measure.