Here’s a very useful technique if you’re working on a CircuitPython program that you need to store data on the host microcontroller’s Flash—and to continue to be able to mount and access the device from your computer. I’ve used it with a Raspberry Pi RP2040-based board, but it should work with other CircuitPython devices too.
A typical mounted CircuitPython device: files are accessible, but the app can’t access the Flash
CircuitPython has a great advantage that is also a great disadvantage. It is designed to make the host microcontroller’s Flash accessible to a connected computer as a USB flash drive. This is very handy because it allows you to copy your code across without any extra tools. The downside, of course, is that code can’t write data to disk at the same time. If it could, the two ‘views’ of the Flash—the microcontroller’s and the computer’s—could get out of sync, which is bad.
Credit to CircuitPython’s developers: they understood this might be an issue for some programmers, so provided a solution. You can choose whether you want the computer have access to the device (‘disk mode’) or to allow app-to-Flash writes (‘Flash mode’). Setting up for either mode is easy, but it requires finessing if you’re to support both modes as and when you need them: say when you’re running code that needs access to the Flash, but may latter need to be updated.
CircuitPython’s API to control access to the Flash is this:
| |
If you pass True as the second argument, CircuitPython activates disk mode; pass False instead to switch to Flash mode.
This code should be used in your boot.py file so it can switch on Flash mode before your application code, in code.py, starts to run.
In Flash mode, the CircuitPython device will still mount as a drive if connected to your computer, but as a read-only volume. However, your app code can write data—application preferences, for example—to storage.
In Flash mode, the CIRCUITPY disk is locked—note the icon—but the app can write to Flash
To flip between the two modes at will, you need some sort of switch. You could connect a button to GPIO or use an on-board button if one is available. My project doesn’t have the latter and I’m keen to avoid the former to reduce clutter. But I do have an I²C segment LED attached via a Stemma QT cable, so I chose to use that as a a proxy switch: if the LED is attached, assume the application is in use so run in Flash mode. Otherwise, the code is to be updated, so run in disk mode.
Again, all this takes place in boot.py so the system is fully prepared when code.py runs. Here’s the detection code:
| |
Checking for an I²C device this way is quite handy, and I also use it in the application code so I’m not running unnecessary tasks when the setup is in disk mode.