Badger2040w RTC experiments

Hi All,
I’m working on a Badger2040w project which will depend on the RTC.

It’s been unclear to me which of the various ways to call the time are actually calling to the clock which stays active on battery power when unplugged from the USB cable.

To call the time, I’ve typically used machine.RTC().datetime() and utime.localtime(), but neither of these seems to persist after the USB connection breaks or after the current script goes on to halt.

A bit of google work identified that I may need to access the pcf85063a rtc directly on the RPI2040W chip.
To explore this I made two separate scripts

cl1.py connects to wifi and uses ntptime to synchronise the clocks on the badger2040w.
It then calls machine.RTC().datetime(), utime.localtime() and the time on the pcf85063a rtc PCF85063A(i2c).datetime() sequentially. It displays the time on each clock on the screen of the badger.

import machine
import badger2040
import time
import utime
import ntptime
from pcf85063a import PCF85063A
import badger_os

badger = badger2040.Badger2040()

badger.set_pen(15)
badger.clear()
badger.set_pen(1)

badger.connect()

if badger.isconnected():
    # Synchronize with the NTP server to get the current time
    ntptime.settime()

# Get the time after synchronizing with the NTP server
ut = str(machine.RTC().datetime())


badger.set_pen(15)
badger.clear()
badger.set_pen(1)
badger.text(f"ut: {ut}", 10, 0, 1)
badger.update()
time.sleep(0.05)

print(utime.localtime())
# Set the time on the Pico's onboard RTC
def set_pico_time():
    rtc = machine.RTC()
    now = utime.localtime()
    rtc.datetime((now[0], now[1], now[2], now[6], now[3], now[4], now[5], 0))

# Set the time on the external PCF85063A RTC
def set_pcf85063a_time():
    now = utime.localtime()
    i2c = machine.I2C(0, scl=machine.Pin(5), sda=machine.Pin(4))
    rtc_pcf85063a = PCF85063A(i2c)
    rtc_pcf85063a.datetime((now[0], now[1], now[2], now[3], now[4], now[5], now[6]))

# Set the time on the Pico's onboard RTC
set_pico_time()

# Set the time on the external PCF85063A RTC
set_pcf85063a_time()

# Get the time after setting the RTCs
ut2 = str(machine.RTC().datetime())


badger.text(f"Pico_RTC: {ut}", 80, 0, 1)
badger.text(f"PCF_RTC: {ut2}", 200, 0, 1)
badger.update()
time.sleep(0.05)

print("Pico RTC:", ut)
print("PCF85063A RTC:", ut2)

badger_os.launch("launcher")

Starting with the badger2040W disconnected, I attach a battery and run the cl1.py script.

In this image you can see that all three clock interfaces are null, having been reset when the battery was disconnected.

I then killed the cl1.py script and ran cl2.py

Here’s the code for cl2.py

import machine
import badger2040
import utime
from pcf85063a import PCF85063A

# Create Badger2040 instance
display = badger2040.Badger2040()

# Create Pico's RTC instance
rtc = machine.RTC()

# Create PCF85063A RTC instance
i2c = machine.I2C(0, scl=machine.Pin(5), sda=machine.Pin(4))
rtc_pcf85063a = PCF85063A(i2c)

# Clear screen
display.set_pen(15)
display.clear()
display.set_pen(1)

# Display system's time
display.text(f"system_time: {utime.localtime()}", 10, 0, 1)
display.update()
utime.sleep(0.02)

# Display Pico's RTC
display.set_pen(15)
display.clear()
display.set_pen(1)
display.text(f"pico_RTC: {rtc.datetime()}", 10, 0, 1)
display.update()
utime.sleep(0.02)

# Display PCF85063A's RTC
display.set_pen(15)
display.clear()
display.set_pen(1)
display.text(f"PCF_RTC: {rtc_pcf85063a.datetime()}", 10, 0, 1)
display.update()
utime.sleep(0.02)

The big difference here is that cl2.py doesn’t connect to ntptime. It just asks for the time from each of the three clock systems.

Here’s what it returns

As you can see, the only method of the three which actually keeps the time inbetween two different scripts being run on the badger2040W is the pcf85063a method.

My take home from this is that you should use the pcf85063a to establish any rtc link to the badger.

The minimal code needed to pull time from the RTC is

from pcf85063a import PCF85063A
# Create PCF85063A RTC instance
i2c = machine.I2C(0, scl=machine.Pin(5), sda=machine.Pin(4))
rtc_pcf85063a = PCF85063A(i2c)
print(rtc_pcf85063a.datetime())

I’ve tested that this method works both for a li-on 3.7 V battery plugged in to the batt socket on the back, and also for a badger running on a USB cable connected to a mobile phone charger pack. The RTC keeps running on both, even though I had to push the button on the charger pack to start pushing buttons. It seems that the buttons on the badger can’t trigger the activation of the generic mobile charger, but the RTC can keep running.

Code & badger_os icons available here GitHub - chrissyhroberts/badger2040w_code

Nicely done. Some of this stuff can be really hard to sort out, hardware / options wise.

Looking at the schematic, the PCF85063A is a dedicated RTC chip added to the board by Pimoroni. Thats why you need to use i2c to access it. IMHO, that would be the one to use. It is crystal controlled.
There is also an onboard RTC on the Pico.

I’ve added RV3028 RTC breakouts to several of my Pi and Pico builds. It has a battery backup, so no need to sync the time every time I use them.

badger_w.sch (shopify.com)

1 Like

The Badger2040 class has two additional methods which come in handy:

badger2040.pico_rtc_to_pcf()
badger2040.pcf_to_pico_rtc()

BTW: the rtc from the rp2040 needs an external crystal (which the Pico provides). The Badger2040 (without W) does not use a Pico and you can see the crystal in the schematic.

1 Like