Raspberry Pi Pico: how to access the filesystem

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Eureka!

Post by pythoncoder » Tue Jan 26, 2021 5:26 pm

Yikes, that's novel. Is it documented anywhere? It does indeed work.

Thank you for your help.
Peter Hinch
Index to my micropython libraries.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Spoke too soon...

Post by pythoncoder » Tue Jan 26, 2021 5:37 pm

Now reverted to standard _boot.py. I can read from /pyboard but not write to it:

Code: Select all

/home/adminpete/bin> cp pyplayerc.py /pyboard/
timed out or error in transfer to remote: b''
Trying to edit a file from within rshell produced a hang.

Code: Select all

/mnt/qnap2/data/Projects/MicroPython/rshell> ls /pyboard/
rats.py
/mnt/qnap2/data/Projects/MicroPython/rshell> edit /pyboard/rats.py
Retrieving /pyboard/rats.py ...
Updating /pyboard/rats.py ...
The editor read the file but it hung when I tried to save the changed contents.

[EDIT]
It works properly when mounted at /flash.
Peter Hinch
Index to my micropython libraries.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Raspberry Pi Pico: how to access the filesystem

Post by dhylands » Tue Jan 26, 2021 5:59 pm

Yeah, rshell creates a virtual directory for each board using the board name (which defaults to pyboard).

It always puts the root directory of the pyboard under this virtual directory.

As a convenience, for the first board connected, rshell will also create virtual directories for the directories under '/' on the pyboard (which works out ok for say the pyboard which only has l/flash and /sd) and gets a bit more confusing when the filesystem is mounted at /.

The rshell boards command will show all of the virtual directories created for a given board.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Raspberry Pi Pico: how to access the filesystem

Post by pythoncoder » Tue Jan 26, 2021 6:03 pm

Any thoughts as to why I couldn't write to /pyboard?
Peter Hinch
Index to my micropython libraries.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Raspberry Pi Pico: how to access the filesystem

Post by dhylands » Tue Jan 26, 2021 7:13 pm

You should be able to write to /pyboard and it should write to / on the pyboard.

I have a couple RPi Picos coming in the mail, so I'll play around with them and see if there is something weird going on.

If you run rshell with the -d option and try copying a file, it will print out the body of the function recv_file_from_host and then print the invocation (about 10 lines up from the prompt) and you should see something like this:

Code: Select all

output = recv_file_from_host(None, '/flash/heartbeat.py', 149)
I did this on a pyboard, on the Pico I would expect to see a filename like '/heartbeat.py'.

The python code then tries to open that filename using:

Code: Select all

        with open(dst_filename, dst_mode) as dst_file:[code] receives data over the serial port and writes it to the file.
 
 dst_mode will be 'wb'. Can you try manually writing to the file using a similar with statement?

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Raspberry Pi Pico: how to access the filesystem

Post by pythoncoder » Wed Jan 27, 2021 8:20 am

Ignore all the stuff about mountpoints. Even with _boot.py set to mount at /flash the thing does not work properly. I've now tested this on two Linux PC's with the same outcome: reading is OK but writes time out. Regardless of mountpoint.

tl;dr I think there is a communications problem. You are better placed than me to figure out whether the fault is in the board firmware or in rshell.

Using the -d option, with mountpoint '/', here is the outcome:

Code: Select all

/home/adminpete/bin> cp pyplayerc.py /pyboard
Executing "cp pyplayerc.py /pyboard"
----- About to send 458 bytes of code to the pyboard -----
def get_mode(filename):
    """Returns the mode of a file, which can be used to determine if a file
       exists, if a file is a file or a directory.
    """
    import os
    try:
        # Since this function runs remotely, it can't depend on other functions,
        # so we can't call stat_mode.
        return os.stat(filename)[0]
    except OSError:
        return 0
output = get_mode('/')
if output is None:
    print("None")
else:
    print(output)

-----
-----Response-----
b'16384\r\n'
-----
----- About to send 2263 bytes of code to the pyboard -----
def recv_file_from_host(src_file, dst_filename, filesize, dst_mode='wb'):
    """Function which runs on the pyboard. Matches up with send_file_to_remote."""
    import sys
    import ubinascii
    import os
    if False:
        try:
            import pyb
            usb = pyb.USB_VCP()
        except:
            try:
                import machine
                usb = machine.USB_VCP()
            except:
                usb = None
        if usb and usb.isconnected():
            # We don't want 0x03 bytes in the data to be interpreted as a Control-C
            # This gets reset each time the REPL runs a line, so we don't need to
            # worry about resetting it ourselves
            usb.setinterrupt(-1)
    try:
        with open(dst_filename, dst_mode) as dst_file:
            bytes_remaining = filesize
            if not False:
                bytes_remaining *= 2  # hexlify makes each byte into 2
            buf_size = 32
            write_buf = bytearray(buf_size)
            read_buf = bytearray(buf_size)
            while bytes_remaining > 0:
                # Send back an ack as a form of flow control
                sys.stdout.write('\x06')
                read_size = min(bytes_remaining, buf_size)
                buf_remaining = read_size
                buf_index = 0
                while buf_remaining > 0:
                    if False:
                        bytes_read = sys.stdin.buffer.readinto(read_buf, read_size)
                    else:
                        bytes_read = sys.stdin.readinto(read_buf, read_size)
                    if bytes_read > 0:
                        write_buf[buf_index:bytes_read] = read_buf[0:bytes_read]
                        buf_index += bytes_read
                        buf_remaining -= bytes_read
                if False:
                    dst_file.write(write_buf[0:read_size])
                else:
                    dst_file.write(ubinascii.unhexlify(write_buf[0:read_size]))
                if hasattr(os, 'sync'):
                    os.sync()
                bytes_remaining -= read_size
        return True
    except:
        return False
output = recv_file_from_host(None, '/pyplayerc.py', 2621)
if output is None:
    print("None")
else:
    print(output)

-----
timed out or error in transfer to remote: b''
/home/adminpete/bin> 
At this point communications seemed to be cattled because the following hung:

Code: Select all

/home/adminpete/bin> ls /pyboard
Executing "ls /pyboard"
----- About to send 626 bytes of code to the pyboard -----
def stat(filename):
    """Returns os.stat for a given file, adjusting the timestamps as appropriate."""
    import os
    try:
        # on the host, lstat won't try to follow symlinks
        rstat = os.lstat(filename)
    except:
        rstat = os.stat(filename)
    return rstat[:7] + tuple(tim + 0 for tim in rstat[7:])


def get_stat(filename):
    """Returns the stat array for a given file. Returns all 0's if the file
       doesn't exist.
    """
    try:
        return stat(filename)
    except OSError:
        return (0,) * 10
output = get_stat('/')
if output is None:
    print("None")
else:
    print(output)

-----
After a power cycle I found that the filename had been copied but attempting to cat the file or list it showed that the contents had not been copied.

Code: Select all

/home/adminpete> ls -l /flash
     0 Jan  1 1970  pyplayerc.py
    23 Jan  1 00:01 rats.py
Peter Hinch
Index to my micropython libraries.

JumpZero
Posts: 54
Joined: Mon Oct 30, 2017 5:54 am
Location: Arcachon - France

Re: Raspberry Pi Pico: how to access the filesystem

Post by JumpZero » Wed Jan 27, 2021 8:49 am

aivarannamaa wrote:
Tue Jan 26, 2021 4:51 pm
Default builds for ESP8266, ESP32 and CircuitPython also mount at '/'. I was thinking of PyBoard and some ESP builds (eg M5Stack) with their /flash to be exceptions :)
ESP8266 and ESP32 with rshell are always mounted as /pyboard (With default last stable generic build from here micropython.org)
I never seen mounted anywhere else with many different boards ESP*

User avatar
aivarannamaa
Posts: 171
Joined: Fri Sep 22, 2017 3:19 pm
Location: Estonia
Contact:

Re: Raspberry Pi Pico: how to access the filesystem

Post by aivarannamaa » Wed Jan 27, 2021 9:34 am

JumpZero wrote:
Wed Jan 27, 2021 8:49 am
aivarannamaa wrote:
Tue Jan 26, 2021 4:51 pm
Default builds for ESP8266, ESP32 and CircuitPython also mount at '/'. I was thinking of PyBoard and some ESP builds (eg M5Stack) with their /flash to be exceptions :)
ESP8266 and ESP32 with rshell are always mounted as /pyboard (With default last stable generic build from here micropython.org)
I never seen mounted anywhere else with many different boards ESP*
It looks like I misunderstood the discussion. I meant how the flash is mounted inside the MicroPython VM (eg you have /main.py on some devices vs /flash/main.py on others), but now I see the others were talking about how rshell presents it. Thank you for pointing this out!
Aivar Annamaa
https://thonny.org

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Raspberry Pi Pico: how to access the filesystem

Post by Roberthh » Wed Jan 27, 2021 9:59 am

If on the target a file board.py exists, which define a symbol called name with the name of that board, pyboard mounts the board under that name. e.g a board,py file with the content:

name = "esp32"

causes the board mounter under the directory /esp32. Side effect: If you use:

name = ""

the board will be mounted under /, but no files can be copied to that place. Using e.g. esp32 works.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: Raspberry Pi Pico: how to access the filesystem

Post by Roberthh » Wed Jan 27, 2021 10:21 am

Just an update on the unability to write back to the pico board with rshell.
- I created a file board.py on the pico and called the board "rp2"
I can use cp or rm to create or delete file on the board (path /rp2/....)
I cannot edit files on the board using vi or nano. The file will be loaded, but cannot be written back.
I CAN edit files & write them back with the linux version of my pye editor. One difference to nano or vi: pye does not attempt to restore file permissions. It just renames the old file to a tempname, creates a new file under the old name, and then deletes the temp named file.

Post Reply