Raspberry Pi Pico dynamic loading font from sd card with micropython

Hi, I have a Raspberry Pi Pico, just built lv_micropython firmware successfully a few days ago.
Followed the steps in github:

But when I loaded a font at run-time, the display didn’t show anything.
I referred to this example:
It seems that no data can be read from the SD card.
The files are stored in the /font directory of the sd card, which uses the FAT32 format.

Traceback (most recent call last):
  File "<stdin>", line 52, in <module>
  File "fs_driver.py", line 23, in fs_open_cb
RuntimeError: fs_open_callback(/font/font-PHT-cn-20.bin) exception: [Errno 2] ENOENT

I don’t know if I need to modify the filesystem configuration before building.

/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/
#define LV_USE_FS_FATFS 1
    #define LV_FS_FATFS_LETTER 'S'     /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
    #define LV_FS_FATFS_CACHE_SIZE 0    /*>0 to cache this number of bytes in lv_fs_read()*/

But after I changed it like this, the build report was missing the ff.h file.
The SD card needs to be connected to the pin to read and write, but I don’t see the information of the configuration pin from the example.
What should I do if I want to run through this example?
My code:

import sys
from st77xx import *
from xpt2046 import *

    script_path = __file__[:__file__.rfind('/')] if __file__.find('/') >= 0 else '.'
except NameError:
    script_path = ''



import lvgl as lv

import fs_driver
fs_drv = lv.fs_drv_t()
fs_driver.fs_register(fs_drv, 'S')

scr = lv.scr_act()

myfont_cn = lv.font_load("S:%s/font/font-PHT-cn-20.bin" % script_path)

label1 = lv.label(scr)
label1.set_style_text_font(myfont_cn, 0)  # set the font
label1.align(lv.ALIGN.CENTER, 0, 25)

I’m a newer of micropython, any response is welcome.

1 Like

If you are using MicroPython you do not need to use the internal handling for the file system in LVGL. It is already built into MicroPython. I suggest you get access to the SD card using that and pass the data from the font file to the dynamic font loading.

I do this kind of thing with images.

import os
import lvgl as lv

img = lv.img(lv.scr_act())

size = os.stat('some_image.png')[6]

with open('some_image.png', 'rb') as f:
    img.set_data(lv.img_dsc_t({'data_size': size , 'data': f.read()}))

IDK if MicroPython would pitch a fit if I did this instead.

import os
import lvgl as lv

img = lv.img(lv.scr_act())

with open('some_image.png', 'rb') as f:
    img.set_data(lv.img_dsc_t({'data_size': os.stat('some_image.png')[6], 'data': f.read()}))

I have not tried it. there is already a file handle that has been opened so I am not sure if that would cause an issue getting the size. Loading the data this way reduces the amount of memory used.

I am sure that something similar can also be done in LVGL for the font.

Your code is also not going to work. There is nothing set to initialize the SD card SPI

The SD card uses SPI and that has to be set up in order to access the card. I don’t see anywhere in your code where that has been done.

I would need to see a photo of the display you are using in order to know how you need to connect it.

The other thing is your path to the file locaation is not going to work either.

If you look at these 2 code blocks what you are doing is returning the first section of the path of the script.So if you have the script located in “/some/path/script,py” you are cutting off the filename ending up with a path that looks like this “/some/path/”

    script_path = __file__[:__file__.rfind('/')] if __file__.find('/') >= 0 else '.'
except NameError:
    script_path = ''
myfont_cn = lv.font_load("S:%s/font/font-PHT-cn-20.bin" % script_path)

Then in this last code block above that path gets put back in and you end up with this.

I am pretty sure that path is not a correct path.

I believe something along these lines might work

import machine
from micropython import const
import os

_SD_MISO = const(10)
_SD_SCK = const(11)
_SD_MOSI = const(12)
_SD_CS = const(13)

sd = machine.SDCard(slot=1, width=1,sck=_SD_SCK, miso=_SD_MISO, mosi=_SD_MOSI, cs=_SD_CS)
os.mount(sd , '/sd')

fs_drv = lv.fs_drv_t()
fs_driver.fs_register(fs_drv, '')

font = lv.font_load('/sd/font/font-PHT-cn-20.bin')

Thanks kdschlosser

Yes, I also think the problem lies in the SD card reading, but I haven’t seen the code for the SD card in the document examples, so I have been unsure how to do it.


After I added your code, it reported this error, as it seems that the class machine.SDCard is not present in my lv_micropython firmware.

AttributeError: 'module' object has no attribute 'SDCard'

I found the microcython file for the SD card in Github and added it.

from sdcard import SDCard
import os
sd = SDCard(spi, machine.Pin(22, machine.Pin.OUT), 30_000_000)
os.mount(sd, '/sd')

Removed this code

    script_path = __file__[:__file__.rfind('/')] if __file__.find('/') >= 0 else '.'
except NameError:
    script_path = ''

Updated this part of the code

import fs_driver
fs_drv = lv.fs_drv_t()
fs_driver.fs_register(fs_drv, '')

myfont_cn = lv.font_load('/sd/font/font-PHT-cn-20.bin')

But there was a new error

Traceback (most recent call last):
  File "<stdin>", line 33, in <module>
  File "fs_driver.py", line 80, in fs_register
TypeError: ord() expected a character, but string of length 0 found

It seems that the fs_register method cannot use an empty string parameter.

My display screen is this:


You must provide a letter, doesn’t matter which.
Call fs_driver.fs_register(fs_drv, 'S') for example, and then call font_load('S:/sd/font/font-PHT-cn-20.bin')

then what exactly is the purpose of the drive letter?

LVGL can support multiple FS drivers simultaneously, the letter let’s the user select a driver.
When you register an FS driver with specific letter, you can then tell LVGL to use that driver by specifying the same letter in the path.

So the letter is not actually used as a way to identify the volume being used?

Yes, it works :smiley:
Thanks amirgon kdschlosser

No worries M8 glad to help.