Build a Better Raspberry Pi Power Button

Introduction: Build a Better Raspberry Pi Power Button

It's very easy to make a Raspberry power-off or shutdown button. There are lots of such projects on the web and a few here on Instructables, but none of them (that I can see) tell you when your Pi has actually finished shutting down and hence it's safe to pull the power. In fact they don't even acknowledge that the button-press has been seen.

There are projects, too, to restart a Pi that has been shut down, but neither do these give you any visual feedback.

But who needs such a button? If you're a bit of a nerd like me, or even just an aspiring nerd, you can always log in to your Pi locally or over the network and type sudo shutdown -h now. But if you're building a project for non-technical users, that just won't do. True, you can nearly always get away with just pulling the power cord, but note, I said nearly always! Everybody's luck runs out sooner or later. I had an SD card die on me only last week, though I'll never know whether or not it was really due to an abrupt power loss.

In my case I needed to add a power button to a Pi we use as a midi sequencer for recording and playing back hymns and songs in church, for when we don't have a live pianist available. I can always type the shutdown command but I need to de-skill it for when I'm not there.

My intention here is not to give you a finished product, complete with beautifully 3D printed case, like so many other Instructables. Everybody will have a different use for it or want to incorporate in their own project. Rather, I'll set you up with the technology that you can add to your project, whether it be a media centre, an IoT device, or anything else.

(In the video I'm demonstrating it with a Pi Zero v1.2 and a monitor I made from a repurposed laptop screen and a controller from the Far East.)

Step 1: The Design

This is what my power button will do for you:

  • When the Pi is running, a LED is lit continuously. If it's shut down manually the LED goes off only when it's safe to unplug the power.
  • Whilst running, if you press the button for at least a second a shutdown is initiated and the LED flashes off for a quarter of a second every second until it's safe to unplug the power.
  • From a shutdown state (if the power hasn't been removed), pressing the button starts it booting and flashes the LED on for a quarter of a second every second until it's booted up. (It may take a little longer until all services such as ssh and vnc are running.)

The components are very cheap. All you need is:

  • ATTiny85 (Arduino-compatible chip)
  • 3 resistors: 2 x 330Ω and 1 x 10kΩ
  • 1 LED - I suggest green or blue, but it's your choice
  • breadboard and jumper wires, or stripboard, or however you want to build it.

Step 2: How It Works

As with all Pi power buttons, this one pulls a GPIO pin to a low state to signal a shutdown request to a helper program running on the Pi. I used GPIO4 (pin 7) but you can use any other pin.

The only way to tell that a Pi has completed shutdown is by watching TxD pin 8, which then goes low. This depends on the serial console being enabled, which it is by default. In fact TxD will regularly go up and down while it's being used as a serial console, but it will never go low for more than around 30mS at a time, even at the slowest common baud rate. It can still be used for a serial console as we just passively watch it.

To reboot, we need to briefly pull SCL1 (pin 5) low. This pin is used by any I2C devices (including my midi interface), but after initiating the boot we leave it alone.

Most of the complexity is in the Arduino sketch which we load into the ATTiny85. This implements a "state machine" - a very useful and powerful way of coding any problem that can be represented by a number of "states". A washing machine works in just the same way. The states represent the stages in the wash cycle, and each one defines what the machine should be doing at that point (motors or pumps to be run, valves to be opened or shut) and what sensor inputs (temperature, water level, timers) determine when to move to the next state and which next state to choose.

The hand sketch is my first draft of a state diagram, showing all the state transitions. This is just to show you how you can initially plan your states and state transitions - it may not be completely accurate as that was before I started debugging.

In our case, we have 6 states which I've called OFF, BOOT REQUEST, BOOTING, RUNNING, SHUTDOWN REQUEST, and SHUTTING DOWN. (After SHUTTING DOWN it moves back to OFF.) These are identified by comments in the sketch, and for each, further comments say what it should be doing and what events will move it to another state.

The helper program running on the Pi is just a little more complicated than for most shutdown buttons. It responds to a long low pulse on the GPIO pin by initiating a shutdown, but it also responds to a short pulse by itself briefly pulling the GPIO pin low. This is how the ATTiny85 can tell that it's running and so can move from the BOOTING to the RUNNING state.

Step 3: Building a Demo Prototype

For demonstration purposes you can prototype it on a solderless breadboard as shown but I've also given you the schematic so you can work out your own layout using stripboard or a custom PCB, perhaps part of a wider project.

Step 4: Programming the ATTiny85

The Arduino sketch and the helper program are attached to this step. In your Arduino sketches folder, create a folder called PiPwr and copy the file PiPwr.ino into it. Launching the Arduino IDE you will now find it in your sketchbook.

There are several ways of programming an ATTiny85. If yours has a bootloader installed you can use an ATTiny85 development board costing only a few pounds. This connects to your PC via a USB port. I used a Hidiot which is essentially the same but with a prototyping area.

In the Arduino IDE under File - Preferences, add

http://digistump.com/package_digistump_index.json

to the Additional boards manager URLs.

Under Tools - Board you should now see a number of Digispark options. Select Digispark (Default - 16.5MHz).

If your ATTiny85 doesn't have a bootloader (or you don't know) then you can get an AVR ISP programmer for a few pounds. Or you can use an Arduino Uno or cheaper Pro Mini or Nano as a programmer. Google for "arduino as isp attiny85" (without the quotes) for instructions.

If you want to modify the sketch you will find it fully commented and hopefully easy to follow. For debugging it's much easier to use an Arduino Pro Mini or Nano. Uncomment the serial.begin() in Setup and the print statements in loop() in order to see the steps it goes through using the serial monitor. There are alternate pin definitions in the source, commented out, for a Uno, Pro Mini or Nano.

On your Raspberry Pi, copy the file shutdown_helper.py to folder /etc/local/bin and set it as executable with the command

sudo chmod +x /usr/local/bin/shutdown_helper.py

Now edit the file /etc/rc.local with your favourite editor. (You will need to do so as root.) Before the last line (exit 0) insert the line

nohup /usr/local/bin/shutdown_helper.py &

Reboot, and the helper program will start automatically.

Be the First to Share

    Recommendations

    • Make it Glow Contest

      Make it Glow Contest
    • First Time Author Contest

      First Time Author Contest
    • PCB Challenge

      PCB Challenge

    17 Discussions

    0
    LSoerensen
    LSoerensen

    4 weeks ago

    Very nice instructable :-)
    I have (almost) no activity on TX (pin 8) from the RPI during boot. The RPI boots when I pull pin 5 low, but the only thing that happens on the TX is that TX goes high after 9ms and then low again after additional 75ms. It then stays low.
    What am I missing? I am using a RPI4 with Raspbian. Please ask if you need more details.

    2020-12-16 20.39.17.jpg
    0
    p_leriche
    p_leriche

    Reply 11 days ago

    Have you enabled Serial in raspi-config under Interfacing? It won't detect power-off from the TX pin otherwise.

    0
    LSoerensen
    LSoerensen

    Reply 8 days ago

    Hi Leriche,

    Thank you very much for your reply!
    I tried to flash the SD-card with a clean Raspbian version (through the Raspberry Pi Imager). I then ran "sudo raspi-config" in the terminal and enabled the Serial, and restarted the RPI. However, I still do not get anything on the TX (the still boots fine when a short the pin 5 and 6.
    Do you have any other suggestions?

    (Ps. I will try to get another RPI for testing. I will also try to write a small program for the ATTiny to see if it is just my digital analyzer that is broken (it seems to work fine for any other use).)

    0
    p_leriche
    p_leriche

    Reply 7 days ago

    There's not much activity expected on TxD. It should be low when the Pi is shut down (even with power still applied) and should be largely high when running, though will oscillate if it generates serial console messages. Double check - correction - triple check you're looking at the right pin and that the Serial interface is enabled. We all make unbelievably dumb mistakes. It might be worth getting a cheap Pi Zero to see if it's any different, or an FTDI serial to USB adapter and seeing if you can detect the console messages. (Make sure it's a 3.3v one or the jumper is in the 3.3v position. Connect TxD to RxD and vice versa, and Gnd to Gnd. On a PC you can use PuTTy or Arduino Monitor to watch the serial port.)

    0
    LSoerensen
    LSoerensen

    Reply 2 days ago

    Hi Leriche,
    Thank you so much for your reply! That is very much appreciated.
    I found the error, which (of cause) my mistake. I miss understood/read what activity to expect on the TX. I (wrongly) thought that TX was never high or low more than 30 ms, but as you write it is never low more than 30 ms.
    By the way, on my setup it takes ~5.5 sec from the SCL pin is pulsed low to the TX goes high. Is that normal? If I understand your state diagram correctly, then the program will go back to OFF (because of the 50 ms timeout) and then first enter BOOTING, when TX goes high.
    ---
    Side note: I am trying to extend your (very nice) setup with a button that contains three leds to show more information, but for that, I need to move to the ATTiny84. I found the button here https://www.aliexpress.com/item/1955136917.html and it is possible to design the specification. I chose: momentary switch, red/green/blue LED, 5V, black body, common anode, and power symbol (it also includes the led resistors). The quality is nice.

    0
    p_leriche
    p_leriche

    Reply 1 day ago

    Glad you sorted it! Nice button, but I felt flashing 25% duty cycly for booting and 75% for shutting down was more intuitiv than fdifferent colours.

    Does yours now work? Looking back at the code it does seem that it expects TxD to go high within 50mS of signalling a boot request through SCL and that certainly worked for me. But I guess it could be 5 seconds before the kernel enables the TxD/RxD functions on pins 8 and 10 if the firmware hasn't already. In that case, you'd need to change the 50 on line 111 to a minimum of 6000.

    0
    serimi89
    serimi89

    6 weeks ago

    Thanks so much for this great tut!
    I have tried to rebuild it and encounter some issues, similar to aditya-ranjan above. For your information, I am using GPIO 10 / BCM 15 (Is it an issue that it is the Rx pin?).
    Otherwise the behaviour is as follows:
    1. when RPI is on, LED is off
    2. when button is pressed and RPI is off, RPI boots up, LED blinks until RPI booted
    3. when button is pressed and RPI is on, nothing happens.
    I have checked and the helper python file is running (using ps -fA | grep python). I have changed the line in /etc/rc.local to 'python /usr/local/bin/shutdown_helper.py &' and I have changed the Pin definition to '15' in the python file.

    When running the /etc/rc.local file the output shows 'Starting' but doesn't recognise any button press.

    Is there any advice you can give? Is there any way to check if the issue is on the RPI or on the Attiny?
    Thanks a lot!

    0
    LSoerensen
    LSoerensen

    Reply 27 days ago

    Hi, :-)
    I would love to look into your questions. However, I have a problem with my RPI (no activity on the TX pin) as described above. Maybe you could help me, so I can help you? :-)
    Nevertheless, I use my Logic Analyzer a lot for analyzing logic signals:
    Analyzer (like this): https://www.aliexpress.com/item/4000146595503.html
    Hook (like this): https://www.aliexpress.com/item/1037153617.html
    Software (I do not know if the new version is free): https://www.saleae.com/downloads/

    0
    aditya-ranjan
    aditya-ranjan

    Question 12 months ago

    Hi, I followed the procedure in your article and got a functional push button. However, it only works in case of switching on the pi. If I press the button to perform shutdown, the led simply goes off for the duration of button press and that is it. The pi does not shutdown.

    The button does work incase of trying to boot the pi.

    I have done ps -fA | grep python to check if shutdown_helper.py is running or not. It does seem to be running correctly.

    If I manually start the shutdown_helper.py like a typical python script on terminal then only "Starting" message shows. Nothing else is displayed or pi does not shutdown on button press.

    0
    boschj
    boschj

    Answer 11 months ago

    Hi. In my case I had to do some modification to work.
    changed
    nohup /usr/local/bin/shutdown_helper.py &
    to
    nohup python /usr/local/bin/shutdown_helper.py &
    since you seem to have the python script running probably that is not needed.
    In some versions of Raspbian, I had to include 'sudo' before the shutdown instruction in the python script, that solved the problem. Hope this helps.

    0
    petrockblog
    petrockblog

    1 year ago

    Thanks for the informative article!
    You might also be interested in the PowerBlock (https://powerblock.petrockblock.com), which is a ready-to-go power button solution for safe shutdowns and four-stage power status indication via LED.

    0
    mdamini
    mdamini

    1 year ago

    Great Instructable! You said you are demonstrating on a Raspberry Pi Zero, would this work on a Raspberry Pi 3 B+? I would love to add a safe power off button to my project, but I'm not great with electronics.

    0
    p_leriche
    p_leriche

    Reply 1 year ago

    Hi, yes it should work jusy as well with any old Pi.

    0
    SulhanF
    SulhanF

    Question 1 year ago on Step 4

    after upload PiPwr.ino to ATTiny85, it's got error like this picture below.
    Bootloader works just fine and Blink program also working except PiPwr.ino.
    any idea how to resolve this?

    PrgTB.JPG
    1
    p_leriche
    p_leriche

    Answer 1 year ago

    It says your sketch is using 2264 bytes of program storage but the maximum is 2048. I've just recompiled it and mine is using 2198 bytes - I've probably got a slightly different version of the IDE or libraries.

    It looks like you're using an ATTiny25. You need an ATTiny45 which has 4096 bytes or an ATTiny85 with 8192 bytes. The difference in cost isn't worth bothering about.

    Or if you're sure you've got the 85 then maybe you've set the board wrong. In Boards Manager, make sure you've got Digispark (and install it if you haven't). You should be setting the board as Dgispark (Default - 16.5MHz).

    0
    SulhanF
    SulhanF

    Reply 1 year ago

    Thanks for your answer
    i'm sure i use the ATTiny85 one cause the name printed like that on the chip.
    is there anything wrong with the injecting bootloader procedure of mine?

    btw i use Arduino IDE 1.8.9 and attiny board library 1.0.2 by David A.Mellis

    error2.JPGerror1.png
    1
    p_leriche
    p_leriche

    Reply 1 year ago

    I don't know anything about that board library - no doubt you could get it to work (maybe you've used it successfully before on other projects), but I suggest you download the Digispark library by including http://digistump.com/package_digistump_index.json in your additional boads manager URLs in Preferences. Then choose the Digispart (Default 16.5MHz) as the board. That should work. (I'll update the Instructable.)