Badger W - RTC?

The documentation for the BadgerW says:

Real-time Clock

Badger 2040 W includes a PCF85063a RTC which continues to run from battery when the Badger is off. It can be used to wake the Badger on a schedule.

There doesn’t seem to be any other documentation and parsing the badger2040w files has not shed any more light on to the issue. I found more information on using RTC with the Inkyframe but nothing for the BadgerW.

Can anyone point me in the right direction?

1 Like

I got into a discussion about this in another thread here.

The biggest problem I’m trying to solve for is how do I keep the time up to date when the badger2040w halt function. I found that if I use that function, it shuts off the pin that is drawing power (I looked at the code). That said, there’s no way that the badge is entirely shutting down because the buttons would stop working.

In the above mentioned thread another user pointed me to the pcf85063a library. When I investigated that, I found the very same thing you are finding, no documentation. When I used the dir() function to inspect the module and PCF85063A class, I see there is a datetime function. Whenever I try to use it like I would with machine.RTC, it fails:

>>> pcf85063a.PCF85063A.datetime(time.localtime())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: argument has wrong type

>>> pcf85063a.PCF85063A.datetime()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: function missing 1 required positional arguments

It’s also entirely unclear how to instantiate pcf85063a.PCF85063A.

Does that lead you down any paths?

You are calling the datetime-function like a class-function, not as an object function. You should first create an object and then call the datetime() method, something like:

badger_rtc = pcf85063a.PCF85063A()   # instantiation: badger_rtc is an instance of the class
badger_rtc.datetime(time.localtime())

Once you have the object, you can use all of the methods provided by the class.

Another hint: take a look at pcb-pico-en-control/main.py at main · bablokb/pcb-pico-en-control · GitHub

This is a very simplistic sample program that uses a different, but very similar rtc. And it is in CircuitPython and not MicroPython. So this is nothing for copy&paste. But it should give you the “big picture”, i.e. it demonstrates how you can setup your badger-application and use the timer or the alarm to wake up again some time later.

I tried this code using the pimoroni-badger2040w-v1.19.16-micropython-with-examples.uf2
firmware.

When I tried to run the code below, I get an error about a bad I2C object. This now goes well beyond my experience with microcontrollers unfortunately.

>>> import pcf85063a
>>> rtc = pcf85063a.PCF85063A()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Bad I2C object

Any thoughts on what I might need to do to define that I2C object?

Sorry, my code was incomplete. When you create (instantiate) the rtc-object, the python-code has to know which bus it is on and which i2c-adress to use. It is probably something along the line of:

from pimoroni_i2c import PimoroniI2C

i2c = PimoroniI2C(sda=...,scl=...)
rtc = pcf85063a.PCF85063A(i2c)

You must fill in the pin-numbers for sda/scl for the Badger2040w (should be documented somewhere, I think it was sda=4,scl=5).

Also looking for an answer. PCF85063A RTC alarm and timer work fine when looping and waiting for rtc.read_alarm_flag() or rtc.read_timer_flag() but I can’t wake the device from sleep using these.

For reference, here’s a (non-)working example. It works fine when powered by USB, but not battery.

import machine
import time
from pimoroni_i2c import PimoroniI2C
from pcf85063a import PCF85063A

led = machine.Pin(22, machine.Pin.OUT)
enable = machine.Pin(10, machine.Pin.OUT)

i2c = PimoroniI2C(sda=4, scl=5)
rtc = PCF85063A(i2c)


def blink():
    for _ in range(3):
        led.off()
        time.sleep(0.5)
        led.on()
        time.sleep(0.5)


def alarm_in_seconds(date, seconds):
    if seconds < 0 or seconds > 86400:
        raise ValueError("seconds must be between 0 and 86400")
    year, month, day, hour, minute, second, dow = date
    second += seconds
    minute += second // 60
    second %= 60
    hour += minute // 60
    minute %= 60
    day += hour // 24
    hour %= 24
    return second, minute, hour


def main():
    enable.on()
    blink()
    rtc.clear_alarm_flag()
    now = rtc.datetime()
    print("now", now)
    alarm = alarm_in_seconds(now, 5)
    print("alarm", alarm)

    rtc.set_alarm(*alarm)
    enable.off()
    while not rtc.read_alarm_flag():
        pass
    rtc.clear_alarm_flag()
    blink()


main()

Based on the schematics I would have guessed an RTC alarm/timer to have the same effect as a button press. And button presses do wake the board.

I contacted Pimoroni by mail, hoping for some official documentation as the feature is advertised on the product page.

I posted an issue on the GitHub. One of the developers said he was porting the rtc code from the Inly Frame to the BadgerW. He all ready has it in the development builds. I would imagine it will be in the next release.

You must also enable the interrupt of the RTC. The alarm-flag is just an internal register, it does not trigger the interrupt and thus does not wakeup the device.

Doesn’t set_alarm enable the interrupt? How do you enable it?

Turns out you also need rtc.enable_alarm_interrupt(True) / rtc.enable_timer_interrupt(True)

Thanks! I’m able to wake from sleep now 👍

This is the code that I came up with. The general idea is that the badger checks for an active RTC to gather the right time. If that’s not possible, it then invokes ntptime to set the time.

import badger2040w
from pimoroni_i2c import PimoroniI2C
import pcf85063a
import machine
import ntptime
from custom_logging import log    # this is my custom logging function

def set_time():
    display = badger2040w.Badger2040W()
    rtc = machine.RTC()
    i2c = PimoroniI2C(sda=4,scl=5)
    badger_rtc = pcf85063a.PCF85063A(i2c)

    if badger_rtc.datetime()[0] < 2023 and rtc.datetime()[0] >= 2023:
        badger_rtc.datetime(time.localtime())
    elif badger_rtc.datetime()[0] >= 2023 and rtc.datetime()[0] < 2023:
        year, month, day, hour, minute, second, dow = badger_rtc.datetime()
        rtc.datetime((year, month, day, dow, hour, minute, second, 0))
    elif badger_rtc.datetime()[0] < 2023 and rtc.datetime()[0] < 2023:
        try:
            if not display.isconnected():
                display.connect(show_status=False, client_timeout=10)
            if display.isconnected():
                ntptime.settime()
            else:
                log("badger_os.set_time - wifi connection failed - unknown reason")
        except (RuntimeError, OSError) as e:
            log("badger_os.set_time - could not get ntptime: " + str(e))
        else:
            badger_rtc.datetime(time.localtime())

A couple of notes:

  • This assumes you’re using the standard Badger2040W firmware as noted with the import of badger2040w

  • I created a custom logging utility for my badger to track errors in setting time as shown with the use of the log() function

Although I am using CircuitPython, I use a logic quite similar to yours. But there are two things to note: sometime the PCF85063A gives dates way in 2100+. So your first elif will set a wrong time in that case. And sometimes the month is zero. So in the end testing for a valid date is a bit more involved.

There is an “oscillator-stop” register bit in the PCF85063A which actually should tell you if you can trust the datetime, but I found that this flag does not seem to be reliable.

What I do: whenever I update the time from the internet, I set a “badger_rtc”-valid flag. The PCF85063A has a spare byte for that, which is really nice.

If the rtc looses power this byte will be reset and that tells me that it is time to query for the correct time again. At every startup I will check that flag and then update the internal rtc directly (if it is ok) or after the badger_rtc was updated.

The precision of the badger RTC will be around ± two seconds a day, so in addition I do a forced update once a day.

I have several RP2040 setups where I added an RV3028. My Tufty gets put in my pocket now and then, and its nice to have a reliable display of Date and Time.