Taking Control of CircuitPython: Disabling and Restoring USB Mass Storage

Thanks to ChatGPT for help with this…

Ah, CircuitPython. A delightful platform that turns "playing with microcontrollers" into an accessible and joyful experience. But every so often, you hit a problem that makes you scratch your head and say, "Wait, what?" Today, we’re diving into one such scenario: disabling USB mass storage, and (more importantly) restoring it without bricking yourself into a corner. Let's dig in!

Why Disable USB Mass Storage?

Good question! CircuitPython presents itself to your computer as a USB drive (the iconic CIRCUITPY), which makes transferring code files a breeze. However, there are times when you need the microcontroller itself to have exclusive write access to the filesystem—perhaps to log data or update files without interference from your computer. For this, we can disable USB mass storage in the boot.py file.

But what happens if you decide, "No, wait, I do want my CIRCUITPY drive back?" Worry not! With the power of the REPL (and a pinch of Pythonic magic), you can re-enable mass storage mode. Let’s see how.

Step 1: Disabling USB Mass Storage

First, create a boot.py file with the following content:

import storage
import usb_cdc

# Disable USB mass storage
storage.disable_usb_drive()

# Keep USB REPL enabled to allow deploying files via REPL tools
usb_cdc.enable(console=True, data=True)

# Remount the filesystem so CircuitPython code can write to it
storage.remount("/", readonly=False)

When this code runs on boot, the CIRCUITPY drive will no longer appear on your computer. But don’t panic! Your device is still alive and kicking, and you can access it through the REPL (Read-Eval-Print Loop).

Important: The boot.py file runs before code.py, and it’s where you set up USB behavior. Always keep this distinction in mind.

Step 2: Accessing the REPL

Even with USB mass storage disabled, the REPL remains accessible via the USB serial interface. Here’s how to connect:

  1. Open a Serial Terminal: Use a tool like Mu Editor, Thonny, or even mpremote.

    • On Linux/macOS: Use screen (e.g., screen /dev/ttyACM0 115200).

    • On Windows: Use a terminal program like PuTTY.

  2. Press Enter: Once connected, hit Enter to activate the REPL.

  3. Start Typing Python Code: You’re now in the CircuitPython shell, ready to command your device!

Step 3: Restoring USB Mass Storage

From the REPL, you can modify or delete the offending boot.py file to restore USB mass storage functionality.

Here’s how to rename it (so you can keep it as a backup):

import os

# Rename boot.py to boot_backup.py
os.rename("boot.py", "boot_backup.py")

Alternatively, if you’re feeling bold, you can delete it entirely:

import os

# Delete boot.py
os.remove("boot.py")

Once you’ve made the change, reboot your device:

import microcontroller

# Reboot the board
microcontroller.reset()

And just like that, the CIRCUITPY drive will reappear! Congratulations, you’ve regained control.

Pro Tips

  1. Test Before You Commit: Before adding storage.disable_usb_drive() to your boot.py, ensure you’re comfortable using the REPL and os commands.

  2. Keep Backups Handy: Always back up critical files before experimenting. If you’re locked out, you’ll need to reflash CircuitPython to recover.

  3. Use Safe Mode: Double-press the reset button to enter safe mode, which bypasses boot.py and lets you recover from mistakes.

Wrapping Up

Disabling USB mass storage can seem scary at first, but with a bit of planning (and the magic of the REPL), it’s a totally reversible process. Now you can take full control of your CircuitPython projects without fear of getting stuck.

As always, happy hacking!

Stop your Circuit Python devices showing up as a disk drive

Click the image to watch the video….

Got a message from Chris over on YouTube. He’s built my PICO Chord Keyboard design (it worked - phew) and he was wondering if there was a way to stop it appearing as a storage device each time it is plugged in. This is a very useful feature of Circuit Python - it’s how you get the program code onto the device - but it can be irritating, as well as giving folks access to your device that you might not want. I sent a reply and then I thought I’d share it on the blog:

You can stop the device appearing as a usb storage by editing the boot.py file (or adding one if it is not there) on the device. Put the following in there:

import usb_cdc
import board

# Disable USB mass storage
storage.disable_usb_drive()

This should stop the device appearing as a file-store. But remember that if you do this it will be tricky to update the code in the device. You'd have to wipe the EEPROM to get your PICO back to a state when you can change the files.

More details here: https://docs.circuitpython.org/en/latest/shared-bindings/storage/index.html

Breath detecting with an environmental sensor

Turns you can do it. I’ve been wondering how I can detect people blowing into a trombone. As you do. A microphone is one possibility, but that involves analogue to digital conversion and sound processing and stuff. And owning a suitable microphone. I do however have a bunch of BMP20 environmental sensors. These contain an air pressure detector. They are supposed to be used for weather data and determining your height above sea level. Would it work for breath?

The answer is yes. If you put a sensor in a closed box (see above) and then blow into the box you can make a detectable difference to the pressure inside. All you have to do is sample the air pressure at the start and then look for a change of around 5 or so during gameplay.

It worked really well for a while. Then the BMP280 stopped working. I had a look in the box and discovered why. It was rather disgusting. Breathing into a box produces not just air, but a lot of water vapour too. The inside of the box and the sensor itself was covered in what you could politely call “dew” but was actually something slightly different. Trombones have a “spit valve” on one end to release all the stuff that accumulates. I’m happy to have proved the principle. I guess I could engineer some baffles or a waterproof membrane over the sensor to keep it dry, but the thought has occurred that in these virus laden times, passing around some thing that you take in turns to breath into might not be a great idea.

So I’m building a version of the controller that uses buttons rather than breathing.

Linking a Raspberry Pi 4 and a PICO over a serial connection

I want the Pure Data patches in my Chocolate Synthbox to be able to display lights that flash in time with the music. The lights in question are a bunch of neopixels connected to a Raspberry Pi PICO which is handing all the inputs and outputs for the device. I’ve done this to keep the design simpler and to remove any chance of issues with the sound code on the Pi interfering with the pixel animations.

However, to make it work I have to connect the Pi and the PICO together. Both devices have plenty of serial ports, so the best way is just to use one of those.

On the Raspberry Pi 4 (note that this only works for the 4) there are four serial ports which are surfaced on the “hat” connector. You have to enable them and they surface as devices you can use.

You enable them by editing the /boot/config.txt file:

sudo nano /boot/config.txt

Then, if you want to enable serial port 2 (which I do) add the following line at the end of the file:

dtoverlay=uart2

Save the file and then restart the Pi. You can now ask it which pins it uses with the command

dtoverlay -h uart2

The important part of this information is the “uart 2 on GPIOS 0-3. This means that the pins will be assigned as follows:

Pin 27 GPIO0 UART2 TX
Pin 28 GPIO1 UART2 RX

The other two pins (GPIO2 and GPIO2) can be used for hardware handshaking, but I’ve not enabled that. The device will appear as /dev/ttyAMA1, I’m going to use it in Pure Data (but that’s a different blog post. For this one, let’s discover how to connect the port to a PICO. I’ve decided to use uart1 in the PICO. This can be used on pins GP8 (TX) and GP9 (RX). So the wiring I need is:

Raspberry PI         PICO
Pin 38 GND           Pin 13 GND
Pin 27 GP0 UART TX   Pin 12 GP9 UART1 RX
Pin 28 GP1 UART RX   Pin 11 GP8 UART1 TX

Note that the TX and RX are crossed over. The PIC is running Circuit Python, this is how to connect a program to this port:

serial_port = busio.UART(board.GP8, board.GP9, baudrate=19200,receiver_buffer_size=500)

Now if the Circuit Python program in the PICO sends data to this port it can be picked up in the PI.