Monday, November 13, 2023

Shrinking a Raspberry Pi Image

Flash memory prices crashed in 2023, so it is getting hard to find low-capacity microSD cards. However, when you install Raspberry Pi OS on a new microSD card, one of the first things it does is resize the root partition to use the entire card. This is fine since it gives you space to play around with. However, when it's time to back up your system or make an image for cloning, you might end up with a giant, mostly empty image file. This post describes how to shrink it back down to a more convenient size.

The Process

Take the microSD card from you Pi and connect it to some other Linux machine. Assume it shows up as /dev/sdb and has two partitions, where the first is /boot and the second is the root filesystem. First, you can use the df command to see how many blocks are used:

$ df

Or use df -h to get the size in more directly useful units. You're interested in the value for /dev/sdb2 in the "Used" column.

Then, you'll probably need to unmount it:

$ sudo umount /dev/sdb1 /dev/sdb2

Next, you will resize the filesystem. First, check the filesystem for errors:

$ sudo e2fsck -f /dev/sdb2

Then, resize it to something a little bigger than the used space reported by du. For example, to make it 3GB, use this command:

$ sudo resize2fs /dev/sdb2 3G

When this command completes, it will tell you how many 4k blocks the filesystem now occupies. Multiply this number by 8 to get the number of 512-byte disk blocks.

Next, run fdisk

$ sudo fdisk /dev/sdb

Print the current partition table (p), delete the rootfs partition (d) and add it back (n). When it asks you for the new first sector, use the value from the original partition. For the last sector, type "+" followed by the number of disk blocks you calculated above. For example, to match the 3G partition above, which is 786432 4k blocks long, you'd type +6291456.

If asked whether you want to clear the ext4 signature, you can say no since you don't want to modify the partition.

Print the new partition table (p), then write the new partition table and exit (w).

To create an image file, you can use dd:

$ sudo dd bs=512 if=/dev/sdb of=sdcard.img count=6823936

Where the value after count= is the "End" sector number of the last partition shown when you printed out the new partition table.

This will give you a much more compact image file that can go onto any microSD card you happen to have laying around, leaving the unused space blank.

If you later want to re-expand it to fill the entire microSD card, then when you boot your Raspberry Pi, you can run the command:

$ sudo raspi-config nonint do_expand_rootfs

Then reboot. 

Note that this requires a read/write filesystem. If you have a read-only filesystem, you'll have to mount read/write and run resize2fs yourself.

Wednesday, October 25, 2023

Raspberry Pi 4B Power Switch on Raspberry Pi OS 11 Lite

The Raspberry Pi 4B is missing a power switch. This is very frustrating when using it for embedded data-logging applications because unclean shutdowns can corrupt the flash memory. And corrupted writes to an exfat partition on a USB stick might corrupt the entire drive!

Thankfully, the gpio-shutdown dtoverlay in /boot/config.txt lets you configure a GPIO pin to be used as a shutdown switch. Unfortunately, in the Lite version of Raspberry Pi OS, the directions given in /boot/overlays/README are out of date! So here's what I did to enable a power switch on a GPIO.

The Process

First, get a button like this one: https://www.pishop.us/product/squid-button/. Connect it between pins 39 and 40 on the 40-pin header. If you hold the board so the GPIO header is on top, these will be the two pins in the last column on the right side.

Second, add the following line to /boot/config.txt:

dtoverlay=gpio-shutdown,gpio_pin=21

Third, add the following line to /etc/console-setup/remap.inc:

keycode 116 = KeyboardSignal

Fourth, create a link from /etc/systemd/system/kbrequest.target to /lib/systemd/system/poweroff.target:

$ sudo ln -s /lib/systemd/system/poweroff.target /etc/systemd/system/kbrequest.target

Finally, reboot. When the system has started back up, you can push the GPIO button to request an orderly shutdown.

What is this doing?

The gpio-shutdown module tells the kernel to pretend that your GPIO button is a power key on a keyboard. In the desktop version of Raspberry Pi OS, this is intercepted by systemd-logind and the system shuts down. But Raspberry Pi OS Lite doesn't use systemd-logind, so we have to use another method.

The remap.inc entry modifies the kernel keymap so that the power key sends a SIGWINCH (window changed signal). This is caught by systemd, which launches kbrequest.target whenever it gets a SIGWINCH. We linked kbrequest.target to the poweroff target, so the system shuts down.

Unfortunately, this is a blunt tool. Anything else that sends a SIGWINCH will also shut down the system. By default, alt+up arrow sends a SIGWINCH. And there may be others lurking that I haven't found yet. But it will work fine for an embedded system with no keyboard and only tested software running on it.

Alternatives

It's also possible to write a daemon that monitors the GPIO pins directly and can run "halt" when the button is pressed. This might be useful if you want to do some other work before shutting down.

Instead of linking kbrequest.target to poweroff.target, you can also write your own kbrequest.target to perform an arbitrary action.