Tuesday, 11 April 2023

Using a Real Time Clock (RTC) with a Raspberry Pi Pico

 A Real Time Clock (RTC) is a crucial component for any microcontroller-based system that needs to keep track of time. Ostensibly this would then allow the system to maintain time even when the system is powered off or reset.

I've written this short explanation as part of the much larger book 'Raspberry Pi Pico Tips and Tricks'. You can download it for free from here.

Just what is a RTC?

An RTC is a small, clock circuit that is designed to keep track of the current date and time. It typically includes a clock crystal oscillator, a battery, and a small amount of non-volatile memory for storing the time and date information.

Typically RTC’s will be separate modules that can interface with a microcontroller using a variety of communication protocols such as I2C, SPI, or UART to read and write the date and time information. The RTC provides accurate timekeeping for the microcontroller and can be used to timestamp events, trigger time-based events, and schedule tasks.

An RTC is especially useful for systems that require time-sensitive actions such as data logging, scheduling, and timing critical operations. It can also be used to implement features such as alarms, timers, and watchdogs (used to facilitate automatic correction of temporary hardware faults).

Overall, an RTC is an essential component for any microcontroller-based system that needs to keep accurate track of time, even when power is lost or the system is reset.

The RTC on a Raspberry Pi Pico

The RP2040 chip in the Raspberry Pi Pico incorporates a Real Time Clock internally. This derives an accurate time from a reference oscillator (internal by default, although an external reference is possible) and a fixed start time (which in the Pico appears to be initialised to start on the 1st of January 2021.

This is great, but there is a bit of a caveat. When the Pico is first started, without an external reference point it will default to the time being the 1st of January 2021. The worst case scenarios for this type of set up is when there is a power interruption and the time gets reset. It can be overcome in many external modules by utilising a battery backup that preserves the timing circuit, but this is not built into the Pico.

Therefore, it is useful to find a method to synchronise the Pico with an external time source to ensure that the time is accurate. This can be most easily accomplished by using a WiFi connection on the Pico W to use the Network Time Protocol to find the correct time.

From there we can use Greenwich Mean Time (GMT) (now referred to as Coordinated Universal Time or Universal Time Coordinated (UTC)) to know what time it is.

Hang on a minute I hear you say. I want to know what the time is in the country where I am running my Pico! I hear you and I acknowledge your concerns. Sadly this turns out to be way more difficult that it seems at face value. It appears that keeping track of local times is a complex job more suited to super computers and rooms full of frustrated programmers with sleeping disorders. In short, we need to learn to embrace UTC and where required to convert to our respective local times we do it as required (i.e programmatically in a script or spreadsheet. This is the only way we preserve our sanity.

The Code

The following code connects to our local WiFi network (using the secrets file to set all the appropriate network particulars (see the WiFi section for details)). From there it connects to a NTP server, pulls a time value and sets the Pico’s RTC. Then it goes about logging a timestamp every 30 seconds and flashing the on-board LED every time it writes a value to memory.

import network
import socket
import time
import struct
import machine

from secrets import secrets

NTP_DELTA = 2208988800
host = "pool.ntp.org"

rtc=machine.RTC()

def set_time():
    # Get the external time reference
    NTP_QUERY = bytearray(48)
    NTP_QUERY[0] = 0x1B
    addr = socket.getaddrinfo(host, 123)[0][-1]
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.settimeout(1)
        res = s.sendto(NTP_QUERY, addr)
        msg = s.recv(48)
    finally:
        s.close()

    #Set our internal time
    val = struct.unpack("!I", msg[40:44])[0]
    tm = val - NTP_DELTA    
    t = time.gmtime(tm)
    rtc.datetime((t[0],t[1],t[2],t[6]+1,t[3],t[4],t[5],0))

wlan = network.WLAN(network.STA_IF)

# Set up Wifi connection details
ssid = secrets['ssid']
password = secrets['pw']
rp2.country('NZ') # change to <your> country code
ip = secrets['ip']
netmask = secrets['netmask']
gateway = secrets['gateway']
dns = secrets['dns']

# Connect to Wifi
wlan.active(True) # activate the interface
if not wlan.isconnected(): # check if connected to an AP
    print('Connecting to network...')
    wlan.connect(ssid, password) # connect to an AP
    wlan.ifconfig((ip,netmask,gateway,dns))
    while not wlan.isconnected(): # wait till we are connected
        print('.', end='')
        time.sleep(0.1)
    print()
    print('Connected:', wlan.isconnected())
else:
    print("Already connected!")

led_onboard = machine.Pin('LED', machine.Pin.OUT)
led_onboard.value(0)

file = open("timestamps.txt", "a")

#Set our RTC
set_time()

# Log some time
while True:
    timestamp=rtc.datetime()
    timestring="%04d-%02d-%02d %02d:%02d:%02d"%(timestamp[0:3] +
                                                timestamp[4:7])
    print(timestring)
    file.write(timestring + "\n")
    file.flush()
    led_onboard.value(1)
    time.sleep(0.01)
    led_onboard.value(0)
    time.sleep(30)

For general use we would replace the data logger portion of the code to allow it to carry out whatever task we desired that included accurate time!

What gives? My Pico appears to have accurate time already!

For those who (like me) were a bit confused when not only did their Raspberry Pi Pico return accurate time while it was connected to my computer, but it returned it in accurate local time. There is a cool reason for this.

A change in the code for Thonny was introduced which allowed a small piece of code to be executed on a host computer (Windows, Mac or Linux) that will automagically find a connected Pico and then synchronise its date and time.

No MicroPython code needs be added or running on the Pico for this to work. It works by injecting a small MicroPython script, which it modifies on the fly with the host computers UTC, into the Pico over the USB serial link using the RAW REPL functionality of MicroPython! how about that.

Don't forget, if you're looking for the book 'Raspberry Pi Pico Tips and Tricks'. You can download it for free (or donate if you wish) from here.

2 comments:

  1. any way to get correct time while not connected to computer?

    ReplyDelete
    Replies
    1. Certainly possible, but possibly complicated. You could have a GPS unit connected and derive time from that (certainly something out of my experience, but do-able). Conversly, connecting to a network only very infrequently (but I suspect that's what you're not wanting to do)

      Delete