B4R Library Software Real Time Clock

This library (called rSoftRtc), is a generic derivative of the device specific library rESP8266rtc v1-03.

It is a software based Real Time Clock.
You don't need any additional hardware modules.
It does NOT run any special background code to keep the RTC alive.
It can generate Alarm Events at required Date/Times.

It also contains several functions to simplify the manipulation of date/times as seconds.
It gets it's timing by calibrating the millis() function available from Arduino.
Generated Alarm Events are managed by the B4R Scheduler class.

B4X:
#Region Project Attributes
    #AutoFlushLogs: True
    #CheckArrayBounds: True
    #StackBufferSize: 300
#End Region
Sub Process_Globals
   Public Serial1 As Serial
   Private DT(8) As Byte
   Private rtc As SoftRtc
End Sub
 
Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
 
    rtc.Initialize("rtc_Alarm",True)
    log("RTC Initialised")
 
    'date .. 10th March 2017   (GMT)
    DT(0) = 21   '21st century (ie 2000)
    DT(1) = 17   'year
    DT(2) = 3     'month
    DT(3) = 10   'day
 
    'time .. 1:26pm  (zero sec)   (GMT)
    DT(4) = 13   'hour (0-23)
    DT(5) = 26   'minute
    DT(6) = 0     'seconds
 
    DT(7) = 0    'not required.
 
    rtc.SetClock(DT,8,0)   'offset for Western Australia. DT +08:00
    log("RTC is set")
 
    Log(rtc.ReadClock(DT))
    Log("RTC: ",DT(0),",",DT(1),",",DT(2),",",DT(3),",",DT(4),",",DT(5),",",DT(6),",",DT(7))
 
    rtc.SetAlarm(1,rtc.Sec2DT(rtc.DT2Sec(DT)+10))
    rtc.SetAlarm(2,rtc.Sec2DT(rtc.DT2Sec(DT)+5))
    rtc.SetAlarm(3,rtc.Sec2DT(rtc.DT2Sec(DT)+15))
    rtc.SetAlarm(9,rtc.Sec2DT(rtc.DT2Sec(DT)+rtc.SECONDS_1MINUTE))
 
    Log("RTC is running")
End Sub
 
private Sub rtc_Alarm(index As Byte)
    If index=0 Then
         'tick every second
         rtc.ReadClock(DT)
         Log("RTC: ",rtc.ShortDOW(DT),", ",rtc.ShortDate(DT),", ",rtc.Time24(DT))
         Return
    End If
    Log("AlarmEvent:  ",index)
End Sub

The above example has been revised.

Notes: My testing over several days showed a loss of 1 to 2 sec per day. The RTC could be set (SetClock) every week or so. As the Arduino Millis() function can only count continuously for about 40days the RTC should be set at least once a month.

Now updated to v1.02
see posting below for version details
 

Attachments

  • rSoftRtc1-02.zip
    4.8 KB · Views: 653
Last edited:

DarkMann

Member
Licensed User
Longtime User
I've been trying to integrate this into a simple project, as it seems like the ideal solution to keep things ticking over when communication to the server app for my arduino is down or otherwise unavailable.

I've run into a serious problem with the output though. Firstly, I had to edit all the ulong declarations in the .h and .cpp files into more acceptable unsigned long declarations to even get things to compile. This is about the limit of my knowledge of C++.

Now things compile, but when i read back a date or time, they are incorrect. For example, using this code to set date and time:
B4X:
rtc.Initialize("rtcAlarm",False)
DT(0)=21  'century
DT(1)=17  'year
DT(2)=9  'month
DT(3)=3  'day
DT(4)=17  'hour
DT(5)=20  'minute
DT(6)=0
DT(7)=0
rtc.SetClock(DT,0,0)

returns a short date of 02/Sep/17 and a time of 23:76:00

I guess it needs more than the ulong to unsigned long changes that I made, or that they actually broke something in the time parsing.

Any help appreciated.

David
 

Starchild

Active Member
Licensed User
Longtime User
I originally write the library for the ESP8266 as this hardware was WiFi enabled.
I have limited hardware to test this "rSoftRtc" library.
I have only tested it on ST32-Nucleo-F103RB and also the NodeMCU ESP board.
I don't have any standard Arduino boards.

I haven't had any problems with compiling the library as offered, ie with the ulong types.

What is the Target hardware board you are compiling for?
Have you set up the correct board Type/Variant in B4R>Tools>Board-Selector
Is your B4R version current?
What version is your Arduino environment? (I'm using v1.6.9)
 
Last edited:

DarkMann

Member
Licensed User
Longtime User
I'm using an Arduino Mega 2560, correctly configured in B4R.
B4R is Version 2.20.
Arduino Environment is Version 1.8.1. B4R's download page actually specifies version 1.8+ as a requirement.

I'm guessing that the Arduino environment version is the problem. Current Arduino documentation seems to specify the use of the long name "unsigned long" rather than "ulong" - see https://playground.arduino.cc/Code/DatatypePractices.

As I say, it sort of works - it is ticking the seconds over, just getting the dates and times wrong and not understanding 60 minutes in an hour. I just don't understand enough about the conversions between bytes and longs to see where the logic is failing in your code - I've always tried to avoid C - hence the B4R.

From my point of view, I may just have to add an RTC module into the hardware and work with that, but your library seemed like a neat solution to the problem without tacking another device into the circuitry we already have, as I'm already using sensors, relays, switches, LCD and Ethernet. The ethernet would allow me to set the clock regularly and that would work accurately enough.
 

Starchild

Active Member
Licensed User
Longtime User
I updated my Arduino environment to v1.8.1
I tried running the test program for rSoftRTC library.
It compiled and ran fine without any errors relating to "ulong" types.
It displayed the correct time/date for the DT SetClock values.
Tested it on a STM32-F103RB board.

So, I dug a little deeper.
See my next post.
 
Last edited:

Starchild

Active Member
Licensed User
Longtime User
Updated "rSoftRTC" library to version 1.01
Available for download from the top post.

The "ulong" type was defined by the Targeted Device library (but not in Arduino Mega 2560).
B4R rCore.h file has it's own define
... typedef uint32_t ULong;
My library code is now updated to use the B4R type "ULong".

I hope this fixes any "ulong" type mismatch problems in some Arduino environments.
 
Last edited:

DarkMann

Member
Licensed User
Longtime User
Thanks for that, it does indeed now compile without issue.

Here's the changes I made to your above example code:-

B4X:
Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    rtc.Initialize("rtc_Alarm",True)
    Log("RTC Initialised")
    'date .. 8th September 2017   (GMT)
    DT(0) = 21   '21st century (ie 2000)
    DT(1) = 17   'year
    DT(2) = 9     'month
    DT(3) = 8   'day
    'time .. 5:31pm  (zero sec)   (GMT)
    DT(4) = 17   'hour (0-23)
    DT(5) = 31   'minute
    DT(6) = 0     'seconds
    DT(7) = 0    'not required.
    rtc.SetClock(DT,0,0)   'offset for GMT=UTC=0
    Log("RTC is set")
    Log(rtc.ReadClock(DT))
    Log("RTC: ",DT(0),",",DT(1),",",DT(2),",",DT(3),",",DT(4),",",DT(5),",",DT(6),",",DT(7))
    rtc.SetAlarm(1,rtc.Sec2DT(rtc.DT2Sec(DT)+10))
    rtc.SetAlarm(2,rtc.Sec2DT(rtc.DT2Sec(DT)+5))
    rtc.SetAlarm(3,rtc.Sec2DT(rtc.DT2Sec(DT)+15))
    rtc.SetAlarm(9,rtc.Sec2DT(rtc.DT2Sec(DT)+rtc.SECONDS_1MINUTE))
    Log("RTC is running")
End Sub

Just changing to today's date and time and setting the offset to GMT/UTC (i'm not worried about BST at the moment, just getting a meaningful time.

This is the start of the log output:-

B4X:
AppStart
RTC Initialised
RTC is set
1
RTC: 21,17,9,7,23,87,0,4
RTC is running
RTC: Thu, 07/Sep/17, 23:87:01
RTC: Thu, 07/Sep/17, 23:87:02
RTC: Thu, 07/Sep/17, 23:87:03
RTC: Thu, 07/Sep/17, 23:87:04

When I forgot to change the offset - left it at +8, it looked like this:-

B4X:
AppStart
RTC Initialised
RTC is set
1
RTC: 21,17,9,8,7,18,44,5
RTC is running
RTC: Fri, 08/Sep/17, 07:18:45
RTC: Fri, 08/Sep/17, 07:18:46
RTC: Fri, 08/Sep/17, 07:18:47
RTC: Fri, 08/Sep/17, 07:18:48
RTC: Fri, 08/Sep/17, 07:18:49

These are both wrong for 08/Sep/17 at 17:31 GMT.

Clearly, it compiles and ticks every second as expected, the alarms in the test program fire as well, right on schedule after the correct number of seconds.

Thanks for your time so far,

David
 

Starchild

Active Member
Licensed User
Longtime User
I ran the test program with your changes listed above.
For me, I get the correct time/date log outputs.
For both 0 and +8 offsets.
At this point I don't know why you are getting a different result.

I know the underlying device libraries do vary for the different target modules.
Maybe, like the "ulong" typedef was missing from the Arduino Mega 2560 target library, some other definitions may not be the same as for the targets I am using.

I made one other variable declaration change based on the erroneous value displayed in the Minutes field of DT of your log output.

I have again updated the rSoftRtc ZIP in the top post, but I have not incremented the version number, still shown as v1.01

Download it again. If this still gives you erroneous results I think we will need someone else with the same hardware as yourself (Arduino Mega 2560) to investigate further.
 
Last edited:

DarkMann

Member
Licensed User
Longtime User
Well, I've also a couple of Arduino Unos that I've tried. I get exactly the same results as with the Mega2560.

This prompted me to do some further testing.

If I set the hour to any number above 9, then the time - and even the date - come out wrong immediately. Setting the hour to 8 or less seems to work just fine. I've not had time to let it run on to see when - or indeed if - it goes wrong.

If I set the time to 9:00 it works, but this only holds true up to a time of 9:06.

If I set the time to 9:07 it's wrong. Interestingly, if I set it to 9:06 and leave it ticking for a minute, then 9:07 comes past just as expected. Something strange going on in the time setting code?

I'll plug it into the laptop and leave it running all day to see what happens

EDIT: It ran fine for about 10 Hours, just from a prior to 9:00 starting time.
 
Last edited:

Starchild

Active Member
Licensed User
Longtime User
I still cannot get your error to present itself.

Some Further Thoughts.
A different compiler is used for various Target Devices. I am assuming this is the case between
my boards and yours.
You get a total error for hours/minutes etc. but only on larger counts.
The library uses some large constants to spilt years/months/days/hours/minutes apart.
After reading Arduino refs, it seems that untyped constants are treated as (int). This could be the problem. (int) can only hold maximum values of 32768. (I use 86400 in several places).
I think my targeted compiler is treating untyped constants as (long), so I don't get a problem, but you do.

Based on this "theory" ..
I have again edited the rSoftRtc library and forced the large untyped constants to unsigned-long allowing them to be a larger value.
It still works fine for me on my boards.

I have updated the download link in the top post. I haven't changed the version number.
it is still referenced as rSoftRtc1-01.zip

Please give this a go.
 

DarkMann

Member
Licensed User
Longtime User
Working fine with this latest version :D

Thanks for your time and patience. I owe you a beer!

David
 

Starchild

Active Member
Licensed User
Longtime User
Updated "rSoftRTC" library to version 1.02
Available for download from the top post.

I have now incremented the version number to identify the
changes made resolving "untyped constants" compilation problems.
This is detailed in full in the above posts.

Cheers!
 
Last edited:

derez

Expert
Licensed User
Longtime User
I try to run the application on STM32 and it works , but I can't get it to run without updating the time every time I load it.
When I comment the setclock command I get zeroes...

Edit: I understand now that it is using the internal RTC, so of course it doesn't hold the time set before...
 
Last edited:

Starchild

Active Member
Licensed User
Longtime User
As you say, it does not use any hardware internal RTC. It simulates an RTC in software. As such when it is powered down it no longer retain any previous date time information. Therefore it should access a date time source st startup reset. This would require a network connection to a server or internet time source to calibrate itself.
Even without this calibration, it can still be used as a relative timed event generator.
 

derez

Expert
Licensed User
Longtime User
As you say, it does not use any hardware internal RTC. It simulates an RTC in software. As such when it is powered down it no longer retain any previous date time information. Therefore it should access a date time source st startup reset. This would require a network connection to a server or internet time source to calibrate itself.
Even without this calibration, it can still be used as a relative timed event generator.

Thank you.
To connect an external RTC I tried to use RTC_DS1307 with its B4R library, connected to pins PA5,PA6,PA7 but it does not work.
Do you know how to do it ?
 

Starchild

Active Member
Licensed User
Longtime User
Thank you.
To connect an external RTC I tried to use RTC_DS1307 with its B4R library, connected to pins PA5,PA6,PA7 but it does not work.
Do you know how to do it ?
I don't know anything about that library or part.
You should start a new thread regarding this matter so others can help.
 

BaGRoS

Active Member
Licensed User
Longtime User
I have
B4X:
Private Sub SetTimeNIST
   
    lcd.SetCursor(0,0)
    lcd.Write(TimeNIST.GetDate)
    Log("Date: ", TimeNIST.GetDate)
    Log("Time (UTC): ", NumberFormat(TimeNIST.GetHours, 2, 0), ":", NumberFormat(TimeNIST.GetMinutes, 2, 0), _
        ":", NumberFormat(TimeNIST.GetSeconds, 2, 0), "  Summer:", TimeNIST.CheckSummerTime)
    lcd.SetCursor(0,1)
   
    Dim c As String = JoinStrings(Array As String(NumberFormat(TimeNIST.GetHours, 2, 0), ":", NumberFormat(TimeNIST.GetMinutes, 2, 0), _
        ":", NumberFormat(TimeNIST.GetSeconds, 2, 0), " S:", TimeNIST.CheckSummerTime))
    lcd.Write(c)
   
    'date .. 10th March 2017   (GMT)
    DT(0) = 21   '21st century (ie 2000)
    DT(1) = TimeNIST.GetYear   'year
    DT(2) = TimeNIST.GetMonth    'month
    DT(3) = TimeNIST.GetDay   'day

    'time .. 1:26pm  (zero sec)   (GMT)
    DT(4) = TimeNIST.GetHours   'hour (0-23)
    DT(5) = TimeNIST.GetMinutes   'minute
    DT(6) = TimeNIST.GetSeconds     'seconds

    DT(7) = 0    'not required.

    If TimeNIST.CheckSummerTime Then
        rtc.SetClock(DT,1,0)   'offset for London. DT +01:00 - summer
    Else
        rtc.SetClock(DT,0,0)   'offset for London. DT +00:00 - winter
    End If
    Log("RTC is set")

    Log(rtc.ReadClock(DT))
    Log("RTC: ",DT(0),",",DT(1),",",DT(2),",",DT(3),",",DT(4),",",DT(5),",",DT(6),",",DT(7))
   
End Sub

Private Sub rtc_Alarm(index As Byte)
    If index=0 Then
        'tick every second
        rtc.ReadClock(DT)
        Log("RTC: ",rtc.ShortDOW(DT),", ",rtc.ShortDate(DT),", ",rtc.Time24(DT))
        Return
    End If
    Log("AlarmEvent:  ",index)
      
End Sub

Public Sub TimeIsAvailable
    'Pobrany czas z internetu
    lcd.Clear
    'Clock.Enabled = True
    SetTimeNIST
    rtc.Initialize("rtc_Alarm", True)
    Log("RTC Initialised")
  
    'rtc.SetAlarm(1,rtc.Sec2DT(rtc.DT2Sec(DT)+10))
    'rtc.SetAlarm(2,rtc.Sec2DT(rtc.DT2Sec(DT)+5))
    'rtc.SetAlarm(3,rtc.Sec2DT(rtc.DT2Sec(DT)+15))
    'rtc.SetAlarm(9,rtc.Sec2DT(rtc.DT2Sec(DT)+rtc.SECONDS_1MINUTE))
  
End Sub

but sub rtc_Alarm not running in every second
 

Starchild

Active Member
Licensed User
Longtime User
Is your sub "TimeIsAvailable" called at program start to initialise the rtc class.
Have you confirmed this (write message to log)?

Does the example code in the top post function correctly on your hardware?
What hardware board (mcu board) are you targeting?

To test, that the rtc class is running, does sub "rtc_alarm" run for a scheduled alarm event (use rtc function SetAlarm), say 5 seconds after start?
 

Johan Hormaza

Well-Known Member
Licensed User
Longtime User
What type of variable can I place here, in which I customize it with the serial port?
Thank you.
B4X:
DT(0)= century-----> Variable
DT(1)= year-----> Variable
DT(2)= month-----> Variable
DT(3)= day-----> Variable
DT(4)= hour-----> Variable
DT(5)= minute-----> Variable
DT(6)=0
DT(7)=0
 

Starchild

Active Member
Licensed User
Longtime User
What type of variable can I place here, in which I customize it with the serial port?
Thank you.
B4X:
DT(0)= century-----> Variable
DT(1)= year-----> Variable
DT(2)= month-----> Variable
DT(3)= day-----> Variable
DT(4)= hour-----> Variable
DT(5)= minute-----> Variable
DT(6)=0
DT(7)=0

All of the date/time fields are bytes.
in the example DT is declared as
Dim DT(8) as Byte
NB: .. A byte variable is limited to positive values only (ranging 0 to 255)
 
Top