B4R Tutorial Getting local date & time (easy and free)

Discussion in 'B4R Tutorials' started by Kevin, Jun 2, 2018.

  1. Kevin

    Kevin Well-Known Member Licensed User

    I saw a post or two about how to get the local date & time with no easy answers. I need this myself for my first Arduino project so I had a look around and found a free API that does just this!

    Although they also have a premium API, their free one is generous, allowing 1 request per second. I intend to only check it once or twice a day, myself. I should add that I found this through a 2-year-old post online where one person said it isn't reliable. I've only tested it a few times but so far, so good.

    Info and how to sign up: https://timezonedb.com/

    Sample URL: http://api.timezonedb.com/v2/get-time-zone?key=YOURAPIKEY&format=xml&by=zone&zone=America/Chicago
    (Link will not work as it requires a working API key which I am not sharing. :p

    Sample response:

    Hope this helps someone!

    Now a question for Erel or anyone else who would know:

    I've read about the need to be efficient in B4R, so how would be the best way to process this response into something usable for updating our internal "clock" on the board?
     
    RWK and jgmdavies like this.
  2. jgmdavies

    jgmdavies Member Licensed User

    Thanks Kevin - looks useful.
    What sort of Arduino clock are you using - is it from http://playground.arduino.cc/Code/Time or similar, or are you using an add-on hardware RTC board/shield?
    Jim
     
  3. jgmdavies

    jgmdavies Member Licensed User

    OliverA and Kevin like this.
  4. derez

    derez Expert Licensed User

    See this example https://www.b4x.com/android/forum/threads/esp8266-requesting-time-from-a-server.68636/#post-435534, it does not need a special API.
    I use time from Italy :
    Code:
    Public Sub Connect(u As  Byte)
        
    Log("Connecting to Italian server...")
        
    If socket.ConnectHost("time.ien.it",  13Then
            astream.Initialize(
    socket.Stream, "astream_NewData""astream_Error")
            
    Log("Connected")
        
    Else
            
    Log("Failed to connect to Italian server")
            CallSubPlus(
    "Connect"100000)
        
    End If
    End Sub
    I calculate the offset by the dates.
     
    OliverA and Kevin like this.
  5. Kevin

    Kevin Well-Known Member Licensed User

    @derez

    As I understood it, people were having issues converting the UTC to local while also accounting for Daylight Savings Time which starts to complicate things. Perhaps I'm wrong though - it wouldn't be the first time! :D
     
  6. Kevin

    Kevin Well-Known Member Licensed User

    I was looking at https://www.b4x.com/android/forum/threads/resp8266rtc-a-real-time-clock.72783/ but I don't know if there are better options or not. I'm very new to this stuff.

    I was planning on using this on an 8266. Either an 8266-01 or 12E. I am monitoring the weather (via forecast) and controlling a pool heater based on that information.

    Edit: Now I am not sure that is the thread I was looking at before. It may have been an older post before better solutions came about. So confused! :confused:
     
    Last edited: Jun 2, 2018
  7. Kevin

    Kevin Well-Known Member Licensed User

    In case anyone is interested in this option, here is some working code. My reason for wanting to use this API as opposed to using the NIST time example or retrieving it from a script on my server is because I felt the better option was to use something that a) would not require me to determine through code if DST is in effect or not and b) would be a bit more universal as opposed to relying on a specialized script on a server that must reside in the correct timezone, else you need to adjust for it, not that it is a bad option. Possibly worth noting is that while I use this option first, I also use the server script option (described in the library noted below) as a backup if for some reason this API fails, though I won't get into that in the sample code.

    The code uses the JSON reponse (and the "formatted" field) option as mentioned by jgmdavies above.

    I use this in conjunction with the rESP8266rtc library, which suited my needs due to having built-in alarm functions for performing certain tasks at desired times.

    This code as-is requires the library listed above as well as the rHttpUtils2 module. Replace YOURAPIKEY with the API key you are given when you sign up for the free API access.

    Code:
    Private Sub SyncClock 'Call this to begin the process of setting the clock after the clock has been initialized
        HttpJob.Initialize ("GetTime")
        
    HttpJob.Download ("http://api.timezonedb.com/v2/get-time-zone?key=YOURAPIKEY&format=json&fields=formatted&by=zone&zone=America/Chicago")
        
    'If successful, the Job Done sub will send it to the parser sub.  If it fails, it will try SyncClock2 from the script on my website server.
    End Sub

    Sub JobDone (Job As JobResult)
        
    Log("*******************************")
        
    Log("JobName: ", Job.JobName)
        
    If Job.Success Then
            
    Log("Response: ", bc.SubString2(Job.Response, 0Min(200, Job.Response.Length))) 'truncate to 200 characters
            If Job.JobName = "GetTime" Then
                
    Log ("Job GetTime detected.  Sending to parser.")
                ParseTime (Job.Response)
            
    End If
        
    Else 'Job Failed
                Log("Job failed: ", Job.JobName)
                
    Log("ErrorMessage: ", Job.ErrorMessage)
                
    Log("Status: ", Job.Status)
                
    Log(Job.Response)
                
    If Job.JobName = "GetTime" Then
                    SyncClock2 
    'This is where I use the library's sync from server option
                End If
        
    End If
    End Sub

    Private Sub ParseTime (Resp As Object)
        
    'Sample reponse:  {"status":"OK","message":"","formatted":"2018-06-22 20:13:20"}

        
    Dim iStart, iEnd As Int, RespParse() As Byte
        iStart = bc.IndexOf(Resp, 
    "formatted")
        iEnd = (bc.IndexOf2(Resp, 
    "}", iStart)) - 1
        RespParse = bc.SubString2 (Resp, iStart + 
    12, iEnd)
        
    Log ("Parsed string = ",RespParse)

        
    Dim cn, yy, mo, dd, hh, mm, ss As Byte
        
    Dim i As Byte = 0
        
    For Each s() As Byte In bc.Split(RespParse, " ")
            
    Select i
                
    Case 0
                    
    Dim n As Byte = 0
                    
    For Each s2() As Byte In bc.Split(s, "-")
                        
    Select n
                            
    Case 0
                                
    Dim temp() As Byte
                                bc.ArrayCopy (s2, temp)
                                
    Dim tmp As Int = bc.StringFromBytes(bc.SubString2(temp, 0,2))
                                cn = 
    NumberFormat(tmp + 1,2,0)
                                
    Dim tmp2 As Int = bc.StringFromBytes(bc.SubString2(temp, 2,4))
                                yy = tmp2
                            
    Case 1
                                mo = bc.StringFromBytes(s2)
                            
    Case 2
                                dd = bc.StringFromBytes(s2)
                        
    End Select
                        n = n + 
    1
                    
    Next
                
    Case 1
                    
    Dim n As Byte = 0
                    
    For Each s3() As Byte In bc.Split(s, ":")
                        
    Select n
                            
    Case 0
                                hh = bc.StringFromBytes(s3)
                            
    Case 1
                                mm = bc.StringFromBytes(s3)
                            
    Case 2
                                ss = bc.StringFromBytes(s3)
                        
    End Select
                        n = n + 
    1
                    
    Next
            
    End Select
            i = i + 
    1
        
    Next
        
    Log ("Century, Year, Month, Day: ", cn, ",", yy, ",", mo,",", dd)
        
    Log ("hh = ",hh)
        
    Log ("mm = ",mm)
        
    Log ("ss = ",ss)

        
    Dim Stamp() As Byte = Array As Byte(cn, yy, mo, dd, hh, mm, ss, 0)
        rtc.SetClock(Stamp,
    0,0'No offset needed when syncing with this service as it is already adjusted
        rtc.ReadClock(DT)
        
    Log("RTC: ",rtc.ShortDOW(DT),", ",rtc.ShortDate(DT),", ",rtc.Time24(DT))
        rtc.SetAlarm(
    1,rtc.Sec2DT(rtc.DT2Sec(DT)+rtc.SECONDS_1HOUR)) 'Alarm 1 is set to sync the clock every hour
    End Sub

    private Sub rtc_Alarm(index As Byte)
        
    If index=0 Then 'This is a default alarm that ticks every second if enabled in Initialize
             'tick every second
             rtc.ReadClock(DT)
             
    Log("RTC: ",rtc.ShortDOW(DT),", ",rtc.ShortDate(DT),", ",rtc.Time24(DT))
             
    Return
        
    End If

        
    Select Case index
            
    Case 1 'Alarm #1 used to sync clock every hour - Afterwards, it will set this alarm again for an hour later
                Log("AlarmEvent (", index, "): SyncClock called")
                SyncClock
        
    End Select
    End Sub
     
    Last edited: Jun 23, 2018
    derez, jgmdavies and OliverA like this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice