Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raspberry Pi Pico RTC is not synchronised by Thonny (code to fix provided) #1592

Closed
DWiskow opened this issue Feb 3, 2021 · 4 comments
Closed
Milestone

Comments

@DWiskow
Copy link

DWiskow commented Feb 3, 2021

When using a Raspberry Pi Pico with Thonny the Pico RTC is not synchronised with that of the host computer . . . this leads to a variety of issues, from file dates and times being incorrect to the utime library generating incorrect time.

All that is required to update the RD2040 RTC is the following MicroPython code, please can this be incorporated into Thonny.

rtc_base_mem = 0x4005c000
atomic_bitmask_set = 0x2000
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

A working example of the Python/MicroPython code which updates the Pico RD2040 RTC is provided below.

#!/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.

No MicroPython code needs be added or running on the Pico for this to work, but Thonny can not be running on the host computer (it blocks the USB serial port) whilst the above script 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.

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).

@DWiskow DWiskow changed the title Raspberry Pi Pico RTC is not synchronised by Thonny Raspberry Pi Pico RTC is not synchronised by Thonny (code to fix provided) Feb 3, 2021
@aivarannamaa
Copy link
Member

aivarannamaa commented Feb 3, 2021

Thank you for this thorough report and suggestions!

I could incorporate this change into Thonny, but I don't see why the same functionality can't be included in Pico's firmware via machine.RTC class, just like in other MicroPython ports (https://docs.micropython.org/en/latest/library/machine.RTC.html)

I'll wait for Damien's response at micropython/micropython#6831 and then I'll decide how to solve this situation.

@aivarannamaa aivarannamaa added this to the 3.3.4 milestone Feb 3, 2021
@aivarannamaa
Copy link
Member

The work-around will be in Thonny 3.3.4

@thijstriemstra
Copy link

thijstriemstra commented Feb 7, 2021

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).

So why is there an RTC on the Pico? Do I still need to use an external RTC like DS3231 with a Pico to make time survive power interrupts?

The Pico SDK docs don't have any more details about this either: https://raspberrypi.github.io/pico-sdk-doxygen/group__hardware__rtc.html#details

Someone in this thread mentions:

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.

Would also explain why waveshare created a pico rtc based on ds3231: https://www.waveshare.com/wiki/Pico-RTC-DS3231

So DS3231, or equivalent, seems necessary for a good clock but any suggestions are welcome.

@aivarannamaa
Copy link
Member

@thijstriemstra, I guess RTC is for keeping time while Pico has power. Regarding your other questions, I recommend asking around in either RPi forum or MicroPython forum (http://forum.micropython.org/)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants