si08guy
Posts: 1
Joined: Mon Jan 25, 2021 9:42 pm

RTC Library

Mon Jan 25, 2021 9:52 pm

There doesn't seem to be an RTC library in the pico port of micropython. I know there is one in C++, but is there a plan to add one for micropython?

Cheers!

User avatar
scruss
Posts: 5727
Joined: Sat Jun 09, 2012 12:25 pm
Location: Toronto, ON

Re: RTC Library

Tue Jan 26, 2021 5:14 pm

Haven't got my board yet (still a day or two away) but it looks like you're right: there's no mention of the MicroPython standard machine.RTC() interface in the docs.

machine.RTC() is very basic — forget about timezone and daylight-savings correction. It's also hilariously inaccurate on some platforms.

It doesn't look like there's any backup battery facility on the Pico board, so any RTC will lose the time if it's disconnected.
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.
Pronouns: he/him

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Wed Jan 27, 2021 1:08 pm

You could easily create a ‘backup battery’ on a Pico by connecting one to VSYS [requires a voltage greater than 1.8v and less than 5.5v] via a schottky diode [see section 4.4 & 4.5 of the Raspberry Pi Pico Datasheet https://datasheets.raspberrypi.org/pic ... asheet.pdf] . . . as long as the battery voltage is less than that coming in from the USB cable, power will be drawn from the USB supply and not the battery . . . when you unplug the Pico from its USB supply, the Pico will keep on running, using power from the battery.

The documentation uses the following examples: a single Lithium-Ion cell* (cell voltage ~3.0V to 4.2V) will work well, as will 3xAA series cells (~3.0V to ~4.8V) and any other fixed supply above ~2.3V . . . Note: be careful with Lithium-Ion cells as they do not react well to being drained below their minimum voltage [you have been warned !].

The presence or absence of power from the USB can be detected by monitoring GPIO24 (which monitors the existence of VBUS) or ADC channel 3 (which will provide VSYS voltage - if it is higher than the maximum of the chosen battery, then USB is connected) and you could programmatically switch into a ‘lower power execution mode’ when on battery. The example code below uses GPIO 24 to detect whether the Pico is being powered via the micro USB or a battery connected to VSYS (if a battery the LED off time is extended from 0.5 seconds to 1.5 seconds).

Code: Select all

import machine
import utime

led_onboard = machine.Pin(25, machine.Pin.OUT)
USBpower = machine.Pin(24, machine.Pin.IN) 

while True:
   led_onboard.value(1)
   utime.sleep(0.5)
   led_onboard.value(0)
   utime.sleep(0.5)
   if USBpower() != 1:
      utime.sleep(1)

Power consumption when running this code is approximately 0.1W (19mA at 5V, so 4 x AA batteries (@ 2,000mAh each) would keep the Pico running for well over 4 days

Not perfect, but would be do-able on a standard Pico board and for a use case such as data logging, 4 days running on battery would seem to be perfectly adequate . . .

. . . we just need RTC implementing as a MicroPython module to make this possible.

UPDATE: my post (viewtopic.php?f=146&t=300275&p=1807232#p1807232), lower down this thread, shows to way to implement a RTC on the Pico using a few lines of MicroPython code.
Last edited by DWiskow on Thu Jan 28, 2021 11:20 pm, edited 1 time in total.

User avatar
scruss
Posts: 5727
Joined: Sat Jun 09, 2012 12:25 pm
Location: Toronto, ON

Re: RTC Library

Wed Jan 27, 2021 8:33 pm

DWiskow wrote:
Wed Jan 27, 2021 1:08 pm
You could easily …
That's quite a bit more than "easily". Other microcontrollers have a Vbat pin that takes a coin cell for backup. That would be my definition of easily.

MicroPython does have an RTC library, it's just not included in this port.
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.
Pronouns: he/him

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Wed Jan 27, 2021 10:11 pm

scruss wrote:
Wed Jan 27, 2021 8:33 pm
That's quite a bit more than "easily" . . .
Just working with the circuitry that the Pico board provides (and all for $4 or £3.60 in real money) . . . :D

MrLunk
Posts: 12
Joined: Wed Feb 08, 2017 4:46 pm
Location: Netherlands

Re: RTC Library

Thu Jan 28, 2021 12:27 pm

Pfff, glad it's not me...
I have been trying sooo many things to get the RTC going...
Finally found this post to see it's not implemented yet...

First thing I went for was trying the RTC...
Been filing for 2 days...
And my brain frying on it...

PL.

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Thu Jan 28, 2021 2:48 pm

scruss wrote:
Wed Jan 27, 2021 8:33 pm
DWiskow wrote:
Wed Jan 27, 2021 1:08 pm
You could easily …
That's quite a bit more than "easily" . . .

If you want to keep your Pico running when you unplug it from your computer, use one of these https://www.tindie.com/products/8086net ... al-diodes/.

It will allow you to select/start a MicroPython program using Thonny on your computer and then disconnect from your computer. Remember to connect a USB Power Pack to the alternate micro USB connector, before disconnecting. This will then automatically take over powering the Pico whilst it is operating 'stand alone'.

Plug it back into your computer, before disconnecting the USB Power Pack to allow your Pico to continue running seamlessly through the transition.

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Thu Jan 28, 2021 11:04 pm

There is a RTC function in MicroPython on the Pico, but unfortunately the clock is initialised to 1609459201 (Thursday 1st January 2021 00:00:01) each time the Pico is powered on (with no immediate or easy way to set it to the correct value) . . .

. . . however, by initially obtaining the current date & time from the user, it is possible to create a 'corrected RTC' for use in data logging applications and for any other purpose. Once the current date & time have been input, an offset (or delta) from the Pico/MicroPython RTC can be stored, and then automatically applied when a corrected RTC value is needed elsewhere in the program.

The code below prompts for the date & time to be entered using the Thonny REPL/shell when the program is first run, and then defines a function timeNow() to be used whenever the correct date & time is required later in the program.

Code: Select all

import utime
print()
print("                           YYYY MM DD HH MM SS")
dateTime = (input ("Enter current date & time: "))+' 0 0'
synchronisedTime = utime.mktime(list(map(int, tuple(dateTime.split(' ')))))
timeDelta = synchronisedTime - int(utime.time())

def timeNow():
   return utime.localtime(utime.time() + timeDelta)

while True:
   dateTime = timeNow()
   print("{:02d}-{:02d}-{:04d} {:02d}:{:02d}:{:02d}".format(dateTime[2],dateTime[1],dateTime[0],dateTime[3],dateTime[4],dateTime[5]))
   utime.sleep(1)
In the above example, the MicroPython program merely prints out the current date & time every second.

By combining this approach with the use of a battery power source (described in detail in this post viewtopic.php?f=146&t=300676), it is reasonably easy to construct a data logger with a corrected RTC in MicroPython using the Raspberry Pi Pico.

Alternately, it is possible to automatically synchronise the 'corrected RTC' with that of computer connected to the Pico USB, using a short Python script running on the computer to send the correct date & time to the Pico and thus remove the need for user input.
Last edited by DWiskow on Fri Jan 29, 2021 11:41 am, edited 1 time in total.

MrLunk
Posts: 12
Joined: Wed Feb 08, 2017 4:46 pm
Location: Netherlands

Re: RTC Library

Fri Jan 29, 2021 11:16 am

DWiskow wrote:
Thu Jan 28, 2021 11:04 pm
There is a RTC function in MicroPython on the Pico, but unfortunately the clock it initialised to 1609459201 .... etc...


Thanks for that little script there, clarified a lot for me.
PL.

User avatar
scruss
Posts: 5727
Joined: Sat Jun 09, 2012 12:25 pm
Location: Toronto, ON

Re: RTC Library

Sat Jan 30, 2021 4:46 pm

The MicroPython rshell utility automatically syncs the RTC on any connection/transfer event. I'm not quite sure if it's set up to know about the Raspberry Pi Pico's clock, though.
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.
Pronouns: he/him

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Sat Jan 30, 2021 8:20 pm

scruss wrote:
Sat Jan 30, 2021 4:46 pm
The MicroPython rshell utility automatically syncs the RTC ...

Whilst rshell does work with the Pico, it doesn't appear to sync/update the Picos RTC :x

Also, when running rshell, on an Apple MacBook it is necessary to explicitly specify the USB/serial port or it doesn't find the Pico (see example command line output below)

Code: Select all

dorianwiskow@MacBookPro ~ % rshell --editor nano 
Welcome to rshell. Use Control-D (or the exit command) to exit rshell.

No MicroPython boards connected - use the connect command to add one

Code: Select all

dorianwiskow@MacBookPro ~ % rshell -p /dev/cu.usbmodem0000000000001 --editor nano
Using buffer-size of 32
Connecting to /dev/cu.usbmodem0000000000001 (buffer-size 32)...
Trying to connect to REPL  connected
Testing if ubinascii.unhexlify exists ... Y
Retrieving root directories ... /blink.py/ /clock.py/ /i2cScan.py/ /main.py/ /oled.py/ /sh1106.py/ /temperature.py/
Setting time ... Jan 30, 2021 20:08:32
Evaluating board_name ... pyboard
Retrieving time epoch ... Jan 01, 1970
Welcome to rshell. Use Control-D (or the exit command) to exit rshell.

Once connected everything runs as expected, EXCEPT the RTC is not synchronised with the host computer (as demonstrated below in the REPL) :x

Code: Select all

/Users/dorianwiskow> repl
Entering REPL. Use Control-X to exit.

>>> import time
>>> time.time()
1609460000
>>> time.localtime(time.time())
(2021, 1, 1, 0, 13, 38, 4, 1)

User avatar
scruss
Posts: 5727
Joined: Sat Jun 09, 2012 12:25 pm
Location: Toronto, ON

Re: RTC Library

Sat Jan 30, 2021 8:41 pm

DWiskow wrote:
Sat Jan 30, 2021 8:20 pm
Whilst rshell does work with the Pico, it doesn't appear to sync/update the Picos RTC :x
Hence my comment.

I think there's a Pico-specific branch somewhere in that repo. That might just be to account for specific flash storage decisions, though. There's much more on this in the MicroPython.org forum
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.
Pronouns: he/him

JumpZero
Posts: 1419
Joined: Thu Mar 28, 2013 7:35 pm
Location: Arcachon, France

Re: RTC Library

Sun Jan 31, 2021 2:20 pm

Hello,
As far as I tested:
the latest rshell github pico branch corrects the not-working cp command as mentionned by rshell author here
But even if rshell gives a message saying it has set the time when connected to the pico.. it hasn't
time.localtime() returns 1st Jan 2021..

Actually there is no MicroPython RTC class for the pico.
Starting page 164 of C/C++ SDK there is the rtc library including a rtc_set_datetime function.
It seems it hasn't been ported (yet?) to MicroPython.

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Sun Jan 31, 2021 3:27 pm

JumpZero wrote:
Sun Jan 31, 2021 2:20 pm
Actually there is no MicroPython RTC class for the pico ...

But in MicroPython, there is utime and a time function within that, which allows read access to the RD2040 RTC. This can easy be turned into a host computer synchronised RTC using the example here https://www.raspberrypi.org/forums/vie ... 6&t=301502 with the simple connection of AA batteries to VSYS & GND.

User avatar
scruss
Posts: 5727
Joined: Sat Jun 09, 2012 12:25 pm
Location: Toronto, ON

Re: RTC Library

Sun Jan 31, 2021 6:57 pm

I don't think that it's reading the system RTC, though. What you're doing (while a very nifty workaround) is correcting the chip's system clock, and powering the whole system.

A MicroPython board with a non-volatile RTC might look something like this:
Image
See the little cylindrical 32768 Hz crystal to the right of the main crystal and the coin cell battery backup below and to the right? That runs a completely separate clock on the chip that draws a tiny amount of power. It's completely decoupled from the main system clock, and can drift off doing its own thing unless you calibrate it.

Fancier MicroPython boards than the one pictured can fire off events based on the RTC, and even go into deep sleep until one happens.

(While this is a Raspberry Pi-only board, there is a decent MicroPython port to that board: mcauser/BLACK_F407VE: MicroPython board definition for the MCUDev Black STM32F407VET6 board. They're about 2-3× the price of a Pico.)
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.
Pronouns: he/him

JumpZero
Posts: 1419
Joined: Thu Mar 28, 2013 7:35 pm
Location: Arcachon, France

Re: RTC Library

Mon Feb 01, 2021 3:45 pm

+1 with @scruss I think utime is the system clock and not the RTC

I guess this is a typo:
scruss wrote:
Sun Jan 31, 2021 6:57 pm
(While this is a Raspberry Pi-only board...

Some boards (e.g. ESP32, ESP8266) does have a RTC (or so called RTC because there is no backup battery)
And this RTC can trigger events such as wake up from deep sleep. Very convenient to lower power consumption!
But the pico does have a nice RTC as well: page 164 of C/C++ SDK we can read
4.1.19.1. Function List
• void rtc_init (void)
• bool rtc_set_datetime (datetime_t *t)
• bool rtc_get_datetime (datetime_t *t)
• bool rtc_running (void)
• void rtc_set_alarm (datetime_t *t, rtc_callback_t user_callback)
• void rtc_disable_alarm (void)
So the point is, I think, it's not yet included in MicroPython port.
Wait & see..

danjperron
Posts: 4683
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RTC Library

Mon Feb 01, 2021 5:28 pm

OK I modified DWiskow's function to set the clock.
Not time offset is needed anymore!

I'm using the direct access to the RTC to set the clock right!
works great!

Code: Select all

import machine
import utime
print()
print("                           YYYY MM DD HH MM SS")
dateTime = (input ("Enter current date & time: "))+' 0 0'
givenTime = utime.mktime(list(map(int, tuple(dateTime.split(' ')))))

ctime=utime.localtime(givenTime)

# ok insert data into the rtc

setup_0 = (ctime[0] << 12) | (ctime[1] << 8) | ctime[2]

def weekDay(year, month, day):
    offset = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
    afterFeb = 1
    if month > 2: afterFeb = 0
    aux = year - 1700 - afterFeb
    # dayOfWeek for 1700/1/1 = 5, Friday
    dayOfWeek  = 5
    # partial sum of days betweem current date and 1700/1/1
    dayOfWeek += (aux + afterFeb) * 365                  
    # leap year correction    
    dayOfWeek += aux // 4 - aux // 100 + (aux + 100) // 400     
    # sum monthly and day offsets
    dayOfWeek += offset[month - 1] + (day - 1)               
    dayOfWeek %= 7
    return int(dayOfWeek)


setup_1 =  (ctime[3] << 16) | (ctime[4] << 8) | ctime[5]
setup_1 =  setup_1 |  (weekDay(ctime[0],ctime[1],ctime[2]) << 24)
# Be aware that RTC start at sunday=0
# and utime start dayofweek monday=0

# set rtc
machine.mem32[0x4005c004]= setup_0
machine.mem32[0x4005c008]= setup_1
machine.mem8[0x4005c00c] = machine.mem8[0x4005c00c] | 0x10


>>> %Run -c $EDITOR_CONTENT

YYYY MM DD HH MM SS
Enter current date & time: 2021 2 1 12 24 00
>>> utime.localtime()
(2021, 2, 1, 12, 24, 1, 0, 32)
>>>

zmarties
Posts: 66
Joined: Fri Jan 22, 2021 6:53 pm

Re: RTC Library

Mon Feb 01, 2021 9:28 pm

That's great, but you don't need to go to such lengths to calculate the RTC day of the week - it's simply (ctime[6] + 1) % 7

danjperron
Posts: 4683
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RTC Library

Mon Feb 01, 2021 9:43 pm

@zmarties very well seen!

Oh gosh !!!!
I was in my own bubble. and didn't thing about the already calculated ctime.

danjperron
Posts: 4683
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RTC Library

Tue Feb 02, 2021 12:13 am

This is the revisited version with the usage of bitset mask to load the RTC register.

Code: Select all

print()
print("                           AAAA MM JJ HH MM SS")
dateTime = (input ("Enter date and time: "))+' 0 0'
givenTime = utime.mktime(list(map(int, tuple(dateTime.split(' ')))))

ctime=utime.localtime(givenTime)

# insert data  to RTC register

setup_0 = (ctime[0] << 12) | (ctime[1] << 8) | ctime[2]
setup_1 =  (ctime[3] << 16) | (ctime[4] << 8) | ctime[5]
setup_1 =  setup_1 |  (((ctime[6] + 1) % 7) << 24)

# register RTC address
rtc_base_mem = 0x4005c000
atomic_bitmask_set = 0x2000

machine.mem32[rtc_base_mem + 4] = setup_0
machine.mem32[rtc_base_mem + 8] = setup_1
machine.mem8[rtc_base_mem + atomic_bitmask_set + 0xc] = 0x10

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Tue Feb 02, 2021 2:43 am

Elevating this one step higher (and building on the posts above) . . .

UPDATE: the code will now automatically execute main.py, if present on the Pico board, on completion of synchronisation

Code: Select all

#!/usr/bin/env python3
#
# Vendor:Product ID for Raspberry Pi Pico is 2E8A:0005
#
# see section 4.8 RTC of https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf and in particular section 4.8.6 
# for the RTC_BASE address (0x4005C000) and details of the RD2040 setup registers used to program the RT (also read
# 2.1.2. on Atomic Register Access)
#
from serial.tools import list_ports
import serial, time

picoPorts = list(list_ports.grep("2E8A:0005"))
utcTime = str( int(time.time()) )
pythonInject = [
    'import machine',
    'import utime',
    'rtc_base_mem = 0x4005c000',
    'atomic_bitmask_set = 0x2000',
    'led_onboard = machine.Pin(25, machine.Pin.OUT)',
    '(year,month,day,hour,minute,second,wday,yday)=utime.localtime('+utcTime+')',
    'machine.mem32[rtc_base_mem + 4] = (year << 12) | (month  << 8) | day',
    'machine.mem32[rtc_base_mem + 8] = ((hour << 16) | (minute << 8) | second) | (((wday + 1) % 7) << 24)',
    'machine.mem32[rtc_base_mem + atomic_bitmask_set + 0xc] = 0x10',
    'for i in range(5):',
    '    led_onboard.toggle()',
    '    utime.sleep(0.03)',
    'led_onboard.value(0)'
    ]

if not picoPorts:
    print("No Raspberry Pi Pico found")
else:
    picoSerialPort = picoPorts[0].device
    with serial.Serial(picoSerialPort) as s:
        
        s.write(b'\x03')   # interrupt the currently running code
        s.write(b'\x03')   # (do it twice to be certain)
        
        s.write(b'\x01')   # switch to raw REPL mode & inject code
        for code in pythonInject:
            s.write(bytes(code+'\r\n', 'ascii'))
            time.sleep(0.01)
        time.sleep(0.25)
        s.write(b'\x04')   # exit raw REPL and run injected code
        time.sleep(0.25)   # give it time to run (observe the LED pulse)

        s.write(b'\x02')   # switch to normal REPL mode
        time.sleep(0.5)    # give it time to complete
        s.write(b'\x04')   # execute a 'soft reset' and trigger 'main.py'

    print( 'Raspberry Pi Pico found at '+str(picoSerialPort)+'\r' )
    print( 'Host computer epoch synchronised over USB serial: '+utcTime+'\r' )

The above code is designed to be executed on a host computer (Windows, Mac or Linux) and will automagically find a connected Pico and then synchronise its date and time (this code requires that python3 and pyserial are installed on the host computer).

No MicroPython code needs be added or running on the Pico for this to work, but Thonny does need to not be running on the host computer (so as not to block the USB serial port) whilst the code does it's magic

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 (kudos to rshell for some of the ideas/techniques used https://github.com/dhylands/rshell)

Oh, and the injected code generates a short pulse of the Pico on-board LED to let the user know the RTC has been synchronised :D

More information can be found on pyserial here https://pyserial.readthedocs.io/en/latest/pyserial.html. To install pyserial enter the following on the host computer command line.

Code: Select all

python -m pip install pyserial

NOTE: If code is running on the Pico, the synchronisation process will interrupt it. The code running on the Pico board can be restarted using Thonny and the updated RTC will remain, provided the Pico has not been disconnected from power (effectively reseting it). If the code running was automatically started (in main.py), it will be automatically restarted again once the data and time have been synchronised. If the Pico is reset, or disconnected from power, it will need to re-sync with the host computer again to have an accurate RTC (by default, at startup, the MicroPython interpreter running on the Pico initialises the Pico system clock to 1609459201 - Thursday, 1st January 2021 00:00:01).

This post viewtopic.php?f=146&t=300676 provides information regarding how to achieve a ‘RTC battery backup’ by attaching a battery to VSYS and GND, but it does have the disadvantage of powering the whole Pico (~ 19mA) rather than just the RTC.
Last edited by DWiskow on Wed Feb 03, 2021 9:50 am, edited 24 times in total.

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Tue Feb 02, 2021 2:53 am

plus, a noddy MicroPython script to run on the Pico to illustrate the valid RTC values :D

Code: Select all

import utime
days = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']

for _ in range(5):
    (year,month,day,hour,minute,second,wday,yday)=utime.localtime(utime.time())    
    print("%d-%02d-%02d %02d:%02d:%02d %s, day %d of the year" %
         (year,month,day,hour,minute,second,str(days[wday]),yday))
    utime.sleep(1)

DWiskow
Posts: 57
Joined: Sun Apr 09, 2017 1:56 pm

Re: RTC Library

Tue Feb 02, 2021 11:24 am

danjperron wrote:
Tue Feb 02, 2021 12:13 am
This is the revisited version . . .
This all made sense to me [and was consistent with the RD2040 documentation I read] . . . the only thing I couldn’t identify was the use of the atomic_bitmask_set, can you explain please and/or point me at this in the documentation?

JumpZero
Posts: 1419
Joined: Thu Mar 28, 2013 7:35 pm
Location: Arcachon, France

Re: RTC Library

Tue Feb 02, 2021 1:16 pm

@danjperron & @DWiskow well done guys! Excellent
I noticed it's better to have Thonny not running simultaneously with the sync script on the host, because it works sometimes and sometimes not. Anyway two process sharing the same port has never been a good thing (OS is supposed to put locks, isn't it?)

danjperron
Posts: 4683
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: RTC Library

Tue Feb 02, 2021 1:42 pm

the only thing I couldn’t identify was the use of the atomic_bitmask_set, can you explain please and/or point me at this in the documentation?
There is 4 methods to write into the register. They are explained on page 18 of the RP2040 datasheet.

I'm using the bit set, this is an offset of 0x2000 from the base address! This way I don't need to read the register first!

The advantage of bit manipulation is that you don't care about other bits in the register and it won't disturb other threads or interrupts running at the same times. No need to have a lock method!

B.T.W. Since the register are 32bits we should use mem32.
machine.mem32[rtc_base_mem + atomic_bitmask_set + 0xc] = 0x10

Information about the RTC, it's on page 566 of the RP2040 datasheet.

Return to “MicroPython”