A Power Button For Raspberry Pi, Courtesy Of Device Tree Overlays

As a standard feature of the Linux kernel, device tree overlays (DTOs) allow for easy enabling and configuration of features and drivers, such as those contained within the standard firmware of a Raspberry Pi system. Using these DTOs it’s trivial to set up features like as a soft power-off button, triggering an external power supply and enable drivers for everything from an external real-time clock (RTC) to various displays, sensors and audio devices, all without modifying the operating system or using custom scripts.

It’s also possible to add your own DTOs to create a custom overlay that combines multiple DTO commands into a single one, or create a custom device tree binary (DTB) for the target hardware. Essentially this DTB is loaded by the Linux kernel on boot to let it know which devices are connected and their configuration settings, very similar to what the BIOS component with x86-based architectures handles automatically.

Ultimately, the DTB concept and the use of overlays allow for easy configuration of such optional devices and GPIO pin settings, especially when made configurable through a simple text file as on the Raspberry Pi SBC platform.

The Linux Device Tree

List of files including board-specific Device Tree Binaries (DTBs) for a Raspberry Pi SBC.
List of files including board-specific Device Tree Binaries (DTBs) for a Raspberry Pi SBC.

As noted earlier, the device tree is compiled into a binary that is handed to the kernel on boot. Contained in this device tree is the list of connected devices, their driver and other relevant settings. On platforms that do not use a BIOS-like system to auto-detect devices, like graphics cards and network cards, and for devices which do not have an auto-detect and auto-configuration option like I2C and SPI devices, the device tree has to be constructed in this manner.

The use of such an external device tree saves the trouble of recompiling the kernel with every hardware change, with a central DTB file for the system device configuration, like those provided for Raspberry Pi SBCs. Recompiling this DTB for any newly added or changed device would be as much hassle as recompiling the entire kernel. This is where overlays come into play.

After the DTB is loaded by the kernel, DTOs are applied, per the Linux kernel documentation on DTOs. The overlay itself is specified as a device tree source, compiled using the device tree compiler into a device tree binary overlay (DTBO) file. For the Raspberry Pi these DTBOs can be found just like the DTBs on the Raspberry Pi GitHub account.

 

Over on the Bootlin blog an excellent article explains how to write your own overlays and apply them using the BeagleBone Black platform. This shows how to load DTBOs from the U-Boot bootloader, which is a somewhat more involved process than that offered by other platforms.

With Armbian for example, DTOs can be set and changed from the armbianEnv.txt file, assuming one uses an image for a platform that supports this feature. Assuming it is a supported feature on the used Armbian image, all it takes is editing the /boot/armbianEnv.txt to add the requisite overlay names and their parameters. This approach is highly reminiscent of the approach chosen on the Raspberry Pi SBC platform, with a similar text file loaded from /boot/config.txt.

Soft Power Button

Momentary NO switch installed on a Raspberry Pi 2B.

As a simple but rather useful example of what DTOs on Raspberry Pi SBCs can accomplish, let’s take a look at how to implement a soft power on and off button using nothing but a default Raspberry Pi OS image, a momentary (normally open) switch and some wiring to connect this switch to two pins on the GPIO header, as in the image on the right.

To make this work, there are two components at play here. The first concerns the soft power on. On Raspberry Pi boards, when the CPU is in halt state (powered, but CPU not running), GPIO3 is kept in high state due to an external pull-up resistor. Whenever GPIO3 is pulled low in this state, the CPU is resumed.

This is accomplished by putting our NO switch on GPIO3 and a ground (GND) pin. When the system enters the halt state and we push the button, GPIO3 is pulled low and the system resumes.

For soft power-off, we need to use the first overlay. Since we’ll be using GPIO3 also for powering off the system, we will add the following device tree overlay (dtoverlay) to /boot/config.txt:

dtoverlay=gpio-shutdown,gpio_pin=3,active_low=1,debounce=1500

The gpio-shutdown DTO is described in the overlay README:

Name: gpio-shutdown
Info: Initiates a shutdown when GPIO pin changes. The given GPIO pin
is configured as an input key that generates KEY_POWER events.
This event is handled by systemd-logind by initiating a
shutdown.

If the system is booted when we trigger this event, it will thus act as if we pushed the ‘power’ button on a desktop system, turning the system off and halting the CPU.

Beyond the GPIO pin number, we can also configure the state of the pin when it should trigger the event (here when pulled low), and since we are using a mechanical switch we’d like to have a built-in debounce delay before the event is triggered.

Of course, since we can configure the GPIO pin here, we don’t necessarily have to use GPIO3 here, which may be desirable since GPIO3 is also the (non-remappable) I2C1 SCL pin, and losing access to the primary I2C bus could be a problem. Instead another GPIO pin (like 17) could be used, with the complication that using a single momentary switch as in the previous example would no longer be possible without jumping through some extra hoops.

Another interesting power-related overlay is the gpio-poweroff one:

Name: gpio-poweroff
Info: Drives a GPIO high or low on poweroff (including halt). Using this
overlay interferes with the normal power-down sequence, preventing the
kernel from resetting the SoC (a necessary step in a normal power-off
or reboot). This also disables the ability to trigger a boot by driving
GPIO3 low.

When using an external power supply module, this signal is used to turn off power to the SBC, and likely shutdown the power supply itself until woken up again by some other signal. This could conceivably be useful in industrial and remote locations where some level of automation and/or power savings would be desirable.

Real Time Clock

PCF8523-based Real Time Clock (RTC) module.
PCF8523-based Real Time Clock (RTC) module.

A sorely missed feature on Raspberry Pi SBCs is a real-time clock (RTC), as this means that without internet access, the system time will be practically meaningless. Fortunately it’s easy enough to install any of a number of RTC modules, such as the ubiquitous  PCF8253, DS1307, DS3231 and higher-end options.

Most of these RTC modules communicate using the I2C bus, which means wiring up 3.3V, GND and the I2C SCL/SDA lines. Note that we’d already have a potential conflict here if using the soft power-off feature, since by default it is assumed that we’re using the primary (i2c_arm) I2C bus. After this we have to enable the appropriate overlay for the module we’re using.

 

According to the overlay documentation:

Name: i2c-rtc Info: Adds support for a number of I2C Real Time Clock devices Load: dtoverlay=i2c-rtc,<param>=<val>

With as fallback the software I2C option if we cannot use the primary I2C bus:

Name: i2c-rtc-gpio
Info: Adds support for a number of I2C Real Time Clock devices
using the software i2c controller
Load: dtoverlay=i2c-rtc-gpio,<param>=<val>

With all of this in place, we only need to deal with the fake hardware clock (fake-hwclock) that is used by default, as else we’ll have to manually set the (fake) hwclock time from the RTC. For example on Debian-based OSes like Raspberry Pi OS:

sudo apt-get -y remove fake-hwclock 
sudo update-rc.d -f fake-hwclock 
sudo remove systemctl disable fake-hwclock

Just The Beginning

Some of the Device Tree Binary Overlay (DTBO) files that are part of a Raspberry Pi OS image.
Some of the Device Tree Binary Overlay (DTBO) files that are part of a Raspberry Pi OS image.

Some may have noticed at this point that this procedure with overlays looks familiar if they have ever installed something like an I2S sound board on a Raspberry Pi system. The reason for this is that DTBOs for these devices are already present and can thus be loaded on boot without further modifications needed.

While a lot of this functionality can be duplicated by various shell- and python-scripts within the OS itself, it’s generally more straightforward to do it in the form of a device tree overlay, if only because everything that is required is already part of the default operating system image. This means that it is also guaranteed to keep working, even across Linux kernel and package upgrades.

A simple glance at the DTBO files that comes with a Raspberry Pi OS image or similar gives a good idea of just how many overlays there are. Everything from DACs, rotary encoders, various (LCD, OLED) screens, official Raspberry Pi devices like the PoE HAT, soundcards and countless GPIO-, SPI- and I2C-related features and devices.

With this in mind, it seems like a good idea to have a read through the overlays README to get some idea of what is supported, and to reference it before embarking on a new project on a Raspberry Pi SBC. Similarly, it can’t hurt to check the available overlays on other (ARM) platforms. For all you know, the feature you would like to have is a simple overlay toggle away.

 

11 thoughts on “A Power Button For Raspberry Pi, Courtesy Of Device Tree Overlays

  1. DTB is loaded by the Linux kernel on boot

    No, it is loaded by a bootloader in parallel with the kernel.

    On platforms that do not use a BIOS-like system to auto-detect devices

    BIOS was never meant for device detection (OK, there was PnP BIOS but it is no longer relevant today). It has been ACPI that by BIOS and UEFI systems have used to enumerate platform devices. Devices attached via PCI are enumerated using the bus enumeration features.

  2. Very interesting article, on a tough (to many, including me) subject but welle explianed in a concise (that’s important) and clear format (that’s also important). Keep them coming.

    Now I have a clearer idea on to how port PICOPi (bare metal PICO-8 on a bare-metal Rpi, no OS needed) on other SBC : https://github.com/keints/picopi

  3. A visual “run” indicator without the side effects of gpio-poweroff can be made by enabling the serial console and connecting LED + resistor to TxD and ground.
    btw: “act-led” is another useful overlay, it re-routes the activity led to a specified gpio pin.

  4. DTO’s are a good extension to the kernel I remember the old days of kernel machine configs for all those devices with tons of hacky patches to do little things different. The arm mailing lists are full of that kind of things. Nowerdays if a SOC is mainline you can simply base of that and add a DTO for your stuff.

    What I don’t like about the Raspberies is that they don’t use uboot (except the rpi4 uboot). If you know a lot about how things work on other embedded devices the learning curve is quite steep to add all that knowledge to create your own Linux distribution to Raspberries.

  5. Nice article! Reminds me that I really ought to start documenting my own projects somewhere (at least so that I could come back to them later…). I made a custom power supply supervisor for Rpi Zero- based nature camera (the project is still unfinished, though). But the main trick I’m most happy about, was a virtual I2C mux chip emulation inside a stm32. I was able to have virtual RTC chip, virtual EEPROM chip and virtual GPIO expander chip inside the stm32 firmware, all accessible for the Rpi via I2C.

    In doing so, I was able to leverage existing Linux kernel drivers for RTC, memory and GPIO. And one of the things I pulled off, was a virtual power switch, which the Linux kernel would trigger on “shutdown -h now”. The stm32 would then switch the p-fet feeding the Rpi 5v supply off. This was done to conserve power as much as possible.

    But, perhaps one day I’ll finish that project and wrote some proper notes.

    1. I finally got around to trying this as I have a visually impaired friend who needs a means to gracefully power off a Pi without using putty or other terminal access. I was unable to get this method to work using GPIO3. The button would resume a halted system just fine, but would not shut it down. I moved to GPIO17 and the shutdown works fine. I suspect some tug-of-war was taking place on GPIO3 as to power on or shutdown would prevail?

Leave a Reply to some guy Cancel reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.