Using OnOff Shim with Recalbox


#1

Thanks for a great price competitive product. Confirmed it’s working well with Retropie but prefer to use it with Recalbox which is more popular. Recalbox has built-in support for several power switches but not specifically for OnOff Shim. Can I modify powerswitch.sh script to use with OnOff Shim? Tried modifying atxraspi but have an issue with it prematurely shutting down the Pi with just these two lines:

echo “4” > /sys/class/gpio/export
echo “out” > /sys/class/gpio/gpio4/direction

powerswitch.sh
#!/bin/bash

atx_raspi_start()
{
    # This is GPIO 7 (pin 26 on the pinout diagram).
    # This is an input from ATXRaspi to the Pi.
    # When button is held for ~3 seconds, this pin will become HIGH signalling to this script to poweroff the Pi.
    SHUTDOWN=$1
    REBOOTPULSEMINIMUM=200        #reboot pulse signal should be at least this long
    REBOOTPULSEMAXIMUM=600        #reboot pulse signal should be at most this long
    echo "$SHUTDOWN" > /sys/class/gpio/export
    echo "in" > /sys/class/gpio/gpio$SHUTDOWN/direction

    # Added reboot feature (with ATXRaspi R2.6 (or ATXRaspi 2.5 with blue dot on chip)
    # Hold ATXRaspi button for at least 500ms but no more than 2000ms and a reboot HIGH pulse of 500ms length will be issued

    # This is GPIO 8 (pin 24 on the pinout diagram).
    # This is an output from Pi to ATXRaspi and signals that the Pi has booted.
    # This pin is asserted HIGH as soon as this script runs (by writing "1" to /sys/class/gpio/gpio8/value)
    BOOT=$2
    echo "$BOOT" > /sys/class/gpio/export
    echo "out" > /sys/class/gpio/gpio$BOOT/direction
    echo "1" > /sys/class/gpio/gpio$BOOT/value

    echo "ATXRaspi shutdown script started: asserted pins ($SHUTDOWN=input,LOW; $BOOT=output,HIGH). Waiting for GPIO$SHUTDOWN to become HIGH..."

    #This loop continuously checks if the shutdown button was pressed on ATXRaspi (GPIO7 to become HIGH), and issues a shutdown when that happens.
    #It sleeps as long as that has not happened.
    while true; do
        shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value)
        if [ $shutdownSignal = 0 ]; then
            /bin/sleep 0.2
        else
            pulseStart=$(date +%s%N | cut -b1-13) # mark the time when Shutoff signal went HIGH (milliseconds since epoch)
            while [ $shutdownSignal = 1 ]; do
                /bin/sleep 0.02
                if [ $(($(date +%s%N | cut -b1-13)-$pulseStart)) -gt $REBOOTPULSEMAXIMUM ]; then
                    echo "ATXRaspi triggered a shutdown signal, halting Rpi ... "
                    touch "/tmp/poweroff.please"
                    poweroff
                    exit
                fi
                shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value)
            done
            #pulse went LOW, check if it was long enough, and trigger reboot
            if [ $(($(date +%s%N | cut -b1-13)-$pulseStart)) -gt $REBOOTPULSEMINIMUM ]; then 
                echo "ATXRaspi triggered a reboot signal, recycling Rpi ... "
                reboot
                exit
            fi
        fi
    done    
}

atx_raspi_stop()
{
    # Cleanup GPIO init
    for i in $*; do
        echo "$i" > /sys/class/gpio/unexport
    done
}

mausberry_start()
{
    # Init GPIO :
    # $1 is the GPIO pin connected to the lead on switch labeled OUT
    # $2 is the GPIO pin connected to the lead on switch labeled IN
    echo "$1" > /sys/class/gpio/export
    echo "in" > /sys/class/gpio/gpio$1/direction

    echo "$2" > /sys/class/gpio/export
    echo "out" > /sys/class/gpio/gpio$2/direction
    echo "1" > /sys/class/gpio/gpio$2/value

    # Wait for switch off signal 
    power=0
    while [ "$power" = "0" ]; do
        sleep 1
        power=$(cat /sys/class/gpio/gpio$1/value)
    done
    
    # Switch off
    if [ "$?" = "0" ]; then
        touch "/tmp/poweroff.please"
        poweroff
    fi
}

mausberry_stop()
{
    # Cleanup GPIO init
    for i in $*; do
        echo "$i" > /sys/class/gpio/unexport
    done
}

msldigital_start()
{
    # Init GPIO : 
    # $1 is the GPIO pin receiving the shut-down signal
    echo "$1" > /sys/class/gpio/export
    echo "in" > /sys/class/gpio/gpio$1/direction

    # Wait for switch off signal 
    power=0
    while [ "$power" = "0" ]; do
        sleep 1
        power=$(cat /sys/class/gpio/gpio$1/value)
    done

    # Switch off
    if [ "$?" = "0" ]; then
        touch "/tmp/poweroff.please"
        poweroff
    fi
}

msldigital_stop()
{
    if [ -f "/tmp/shutdown.please" -o -f "/tmp/poweroff.please" ]; then
        if [ -f "/tmp/shutdown.please" -a "$CONFVALUE" = "REMOTEPIBOARD_2005" ]; then
            # Init GPIO
            GPIOpin=15
            echo "$GPIOpin" > /sys/class/gpio/export

            # Execute shutdown sequence on pin
            echo "out" > /sys/class/gpio/gpio$GPIOpin/direction
            echo "1" > /sys/class/gpio/gpio$GPIOpin/value
            usleep 125000
            echo "0" > /sys/class/gpio/gpio$GPIOpin/value
            usleep 200000
            echo "1" > /sys/class/gpio/gpio$GPIOpin/value
            usleep 400000
            echo "0" > /sys/class/gpio/gpio$GPIOpin/value
            sleep 1

            # Uninit GPIO
            echo "$GPIOpin" > /sys/class/gpio/unexport
        fi
        echo "out" > /sys/class/gpio/gpio$1/direction
        echo "1" > /sys/class/gpio/gpio$1/value
        sleep 3
    fi
    # Cleanup GPIO init
    for i in $*; do
        echo "$i" > /sys/class/gpio/unexport
    done
}

wittyPi_start()
{
    # LED on GPIO-17 (wiringPi pin 0)
    led_pin=$1

    # halt by GPIO-4 (wiringPi pin 7)
    halt_pin=$2

    # make sure the halt pin is input with internal pull up
    gpio mode $halt_pin up
    gpio mode $halt_pin in

    # delay until GPIO pin state gets stable
    counter=0
    while [ $counter -lt 10 ]; do  # increase this value if it needs more time
        if [ $(gpio read $halt_pin) == '1' ] ; then
            counter=$(($counter+1))
        else
            counter=0
        fi
        sleep 1
    done

    # wait for GPIO-4 (wiringPi pin 7) falling, or alarm B
    while true; do
        gpio wfi $halt_pin falling
        sleep 0.05  # ignore short pull down (increase this value to ignore longer pull down)
        if [ $(gpio read $halt_pin) == '0' ] ; then
            break
        fi
    done

    # Switch off
    touch "/tmp/poweroff.please"
    shutdown -h now
}

wittyPi_stop()
{
    # LED on GPIO-17 (wiringPi pin 0)
    led_pin=$1

    # halt by GPIO-4 (wiringPi pin 7)
    halt_pin=$2

    # light the white LED
    if [ -f "/tmp/shutdown.please" -o -f "/tmp/poweroff.please" ]; then
        gpio mode $led_pin out
        gpio write $led_pin 1
    fi

    # restore GPIO-4
    gpio mode $halt_pin in
    gpio mode $halt_pin up
}

pin356_start()
{
	mode=$1
	python /recalbox/scripts/rpi-pin356-power.py -m "$mode" &
    pid=$!
    echo "$pid" > /tmp/rpi-pin356-power.pid
    wait "$pid"
}
pin356_stop()
{
    if [[ -f /tmp/rpi-pin356-power.pid ]]; then
        kill `cat /tmp/rpi-pin356-power.pid`
    fi
}

pin56_start()
{
    mode=$1
    python /recalbox/scripts/rpi-pin56-power.py -m "$mode" &
    pid=$!
    echo "$pid" > /tmp/rpi-pin56-power.pid
    wait "$pid"
}
pin56_stop()
{
    if [[ -f /tmp/rpi-pin56-power.pid ]]; then
        kill `cat /tmp/rpi-pin56-power.pid`
    fi
}

# First parameter must be start or stop
if [[ "$1" != "start" && $1 != "stop" ]]; then
    exit 1
fi

CONFFILE="/recalbox/share/system/recalbox.conf"
CONFPARAM="system.power.switch" 
CONFVALUE=
if [ -e $CONFFILE ]; then
    CONFVALUE=$(sed -rn "s/^$CONFPARAM=(\w*)\s*.*$/\1/p" $CONFFILE | tail -n 1)
fi

echo "$CONFVALUE"

case "$CONFVALUE" in
    "ATX_RASPI_R2_6")
        atx_raspi_$1 7 8
    ;;
    "MAUSBERRY")
        mausberry_$1 23 24
    ;;
    "REMOTEPIBOARD_2003")
        msldigital_$1 22
    ;;
    "REMOTEPIBOARD_2005")
        msldigital_$1 14
    ;;
    "WITTYPI")
        wittyPi_$1 0 7
    ;;
    "PIN56ONOFF")
        pin56_$1 onoff
    ;;
    "PIN56PUSH")
        echo "will start pin56_$1"
        pin56_$1 push
    ;;
    "PIN356ONOFFRESET")
        echo "will start pin356_$1"
        pin356_$1 onoff
    ;;
	"PIN356PUSHRESET")
        echo "will start pin356_$1"
        pin356_$1 push
    ;;
esac

#2

BCM4 on the OnOff SHIM is the pin that immediately cuts power to the Pi when asserted, so your premature shutdown is to be expected.

The button is connected to BCM 17.

You should be able to implement poweroff functionality with this line in /boot/config.txt

dtoverlay=gpio-poweroff,gpiopin=4,active_low=1,input=1

If you’re editing directly on your Recalbox Pi you need to hit F4 to quit, then ctrl+alt+f2 to switch to a terminal, then log in with root / recalboxroot and run sudo mount -o,remount,rw /boot to make /boot/config.txt writeable.

Then sudo nano /boot/config.txt to edit, and add the line:

dtoverlay=gpio-poweroff,gpiopin=4,active_low=1,input=1

Then ctrl+x then y and Enter to save.

This will cause BCM4 to be asserted at shutdown, fully powering off your Pi (if it’s powered through OnOff shim) but it wont make the button on OnOff SHIM initiate a shutdown because- for some reason- the gpio-shutdown overlay is missing from Recalbox. At least the version I’m testing on.

Trying to shoehorn gpio-shutdown into recalbox leads to no success, either, it works, loads and the button press is recognised but recalbox just ignores the shutdown button.

If you want to try yourself, it’s a little advanced:

First you would have to grab https://github.com/raspberrypi/firmware/blob/master/boot/overlays/gpio-shutdown.dtbo

wget -O /boot/overlays/gpio-shutdown.dtbo https://github.com/raspberrypi/firmware/blob/master/boot/overlays/gpio-shutdown.dtbo

Then add the line:

dtoverlay=gpio-shutdown,gpio_pin=17,active_low=1

Into /boot/config.txt in the same way as above.

This should add a keyboard with just a shutdown button. I verified this by running cat /dev/input/event0 andf observing the output when I press the OnOff SHIM button.


#3

Thanks!

We now appear to have a fully working solution tested on Recalbox 18.04.20 by combining your suggestion to add the following line to /boot/config.txt:

dtoverlay=gpio-poweroff,gpiopin=4,active_low=1,input=1

And, modifying Recalbox recalbox.conf, powerswitch.sh and onoffshim.py (from rpi-pin56-power.py) scripts.

Successfully tested the following functions:

  • OnOffShim button shuts down and cuts power
  • OnOffShim button powers on
  • Recalbox menu shutdown option shuts down and cuts power
  • Recalbox menu restart works properly
  • Recalbox command line ‘shutdown -h now’ shuts down and cuts power
  • Recalbox command line ‘shutdown -h -r now’ reboots properly

Mount Recalbox read/writable with the following commands to be able to make the following changes:
mount -o remount, rw /boot
mount -o remount, rw /

Backup original /boot/config.txt then add the following line:
dtoverlay=gpio-poweroff,gpiopin=4,active_low=1,input=1

Backup original file then add the following line to /recalbox/share/system/recalbox.conf to ‘A - System Options’ section:
system.power.switch=ONOFFSHIM # https://shop.pimoroni.com/products/onoff-shim

Backup and replace /recalbox/scripts/powerswitch.sh with modified version and make sure it’s executable with command ‘chmod +x /recalbox/scripts/powerswitch.sh’:

#!/bin/bash

# http://lowpowerlab.com/atxraspi/#installation
atx_raspi_start()
{
    # This is GPIO 7 (pin 26 on the pinout diagram).
    # This is an input from ATXRaspi to the Pi.
    # When button is held for ~3 seconds, this pin will become HIGH signalling to this script to poweroff the Pi.
    SHUTDOWN=$1
    REBOOTPULSEMINIMUM=200        #reboot pulse signal should be at least this long
    REBOOTPULSEMAXIMUM=600        #reboot pulse signal should be at most this long
    echo "$SHUTDOWN" > /sys/class/gpio/export
    echo "in" > /sys/class/gpio/gpio$SHUTDOWN/direction

    # Added reboot feature (with ATXRaspi R2.6 (or ATXRaspi 2.5 with blue dot on chip)
    # Hold ATXRaspi button for at least 500ms but no more than 2000ms and a reboot HIGH pulse of 500ms length will be issued

    # This is GPIO 8 (pin 24 on the pinout diagram).
    # This is an output from Pi to ATXRaspi and signals that the Pi has booted.
    # This pin is asserted HIGH as soon as this script runs (by writing "1" to /sys/class/gpio/gpio8/value)
    BOOT=$2
    echo "$BOOT" > /sys/class/gpio/export
    echo "out" > /sys/class/gpio/gpio$BOOT/direction
    echo "1" > /sys/class/gpio/gpio$BOOT/value

    echo "ATXRaspi shutdown script started: asserted pins ($SHUTDOWN=input,LOW; $BOOT=output,HIGH). Waiting for GPIO$SHUTDOWN to become HIGH..."

    #This loop continuously checks if the shutdown button was pressed on ATXRaspi (GPIO7 to become HIGH), and issues a shutdown when that happens.
    #It sleeps as long as that has not happened.
    while true; do
        shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value)
        if [ $shutdownSignal = 0 ]; then
            /bin/sleep 0.2
        else
            pulseStart=$(date +%s%N | cut -b1-13) # mark the time when Shutoff signal went HIGH (milliseconds since epoch)
            while [ $shutdownSignal = 1 ]; do
                /bin/sleep 0.02
                if [ $(($(date +%s%N | cut -b1-13)-$pulseStart)) -gt $REBOOTPULSEMAXIMUM ]; then
                    echo "ATXRaspi triggered a shutdown signal, halting Rpi ... "
                    touch "/tmp/poweroff.please"
                    poweroff
                    exit
                fi
                shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value)
            done
            #pulse went LOW, check if it was long enough, and trigger reboot
            if [ $(($(date +%s%N | cut -b1-13)-$pulseStart)) -gt $REBOOTPULSEMINIMUM ]; then 
                echo "ATXRaspi triggered a reboot signal, recycling Rpi ... "
                reboot
                exit
            fi
        fi
    done    
}

atx_raspi_stop()
{
    # Cleanup GPIO init
    for i in $*; do
        echo "$i" > /sys/class/gpio/unexport
    done
}

# http://mausberry-circuits.myshopify.com/pages/setup
mausberry_start()
{
    # Init GPIO :
    # $1 is the GPIO pin connected to the lead on switch labeled OUT
    # $2 is the GPIO pin connected to the lead on switch labeled IN
    echo "$1" > /sys/class/gpio/export
    echo "in" > /sys/class/gpio/gpio$1/direction

    echo "$2" > /sys/class/gpio/export
    echo "out" > /sys/class/gpio/gpio$2/direction
    echo "1" > /sys/class/gpio/gpio$2/value

    # Wait for switch off signal 
    power=0
    while [ "$power" = "0" ]; do
        sleep 1
        power=$(cat /sys/class/gpio/gpio$1/value)
    done
    
    # Switch off
    if [ "$?" = "0" ]; then
        touch "/tmp/poweroff.please"
        poweroff
    fi
}

mausberry_stop()
{
    # Cleanup GPIO init
    for i in $*; do
        echo "$i" > /sys/class/gpio/unexport
    done
}

# http://www.msldigital.com/pages/support-for-remotepi-board-2013
# http://www.msldigital.com/pages/support-for-remotepi-board-plus-2015
msldigital_start()
{
    # Init GPIO : 
    # $1 is the GPIO pin receiving the shut-down signal
    echo "$1" > /sys/class/gpio/export
    echo "in" > /sys/class/gpio/gpio$1/direction

    # Wait for switch off signal 
    power=0
    while [ "$power" = "0" ]; do
        sleep 1
        power=$(cat /sys/class/gpio/gpio$1/value)
    done

    # Switch off
    if [ "$?" = "0" ]; then
        touch "/tmp/poweroff.please"
        poweroff
    fi
}

msldigital_stop()
{
    if [ -f "/tmp/shutdown.please" -o -f "/tmp/poweroff.please" ]; then
        if [ -f "/tmp/shutdown.please" -a "$CONFVALUE" = "REMOTEPIBOARD_2005" ]; then
            # Init GPIO
            GPIOpin=15
            echo "$GPIOpin" > /sys/class/gpio/export

            # Execute shutdown sequence on pin
            echo "out" > /sys/class/gpio/gpio$GPIOpin/direction
            echo "1" > /sys/class/gpio/gpio$GPIOpin/value
            usleep 125000
            echo "0" > /sys/class/gpio/gpio$GPIOpin/value
            usleep 200000
            echo "1" > /sys/class/gpio/gpio$GPIOpin/value
            usleep 400000
            echo "0" > /sys/class/gpio/gpio$GPIOpin/value
            sleep 1

            # Uninit GPIO
            echo "$GPIOpin" > /sys/class/gpio/unexport
        fi
        echo "out" > /sys/class/gpio/gpio$1/direction
        echo "1" > /sys/class/gpio/gpio$1/value
        sleep 3
    fi
    # Cleanup GPIO init
    for i in $*; do
        echo "$i" > /sys/class/gpio/unexport
    done
}

# http://www.uugear.com/witty-pi-realtime-clock-power-management-for-raspberry-pi/
# https://github.com/uugear/Witty-Pi/blob/master/wittyPi/daemon.sh
wittyPi_start()
{
    # LED on GPIO-17 (wiringPi pin 0)
    led_pin=$1

    # halt by GPIO-4 (wiringPi pin 7)
    halt_pin=$2

    # make sure the halt pin is input with internal pull up
    gpio mode $halt_pin up
    gpio mode $halt_pin in

    # delay until GPIO pin state gets stable
    counter=0
    while [ $counter -lt 10 ]; do  # increase this value if it needs more time
        if [ $(gpio read $halt_pin) == '1' ] ; then
            counter=$(($counter+1))
        else
            counter=0
        fi
        sleep 1
    done

    # wait for GPIO-4 (wiringPi pin 7) falling, or alarm B
    while true; do
        gpio wfi $halt_pin falling
        sleep 0.05  # ignore short pull down (increase this value to ignore longer pull down)
        if [ $(gpio read $halt_pin) == '0' ] ; then
            break
        fi
    done

    # Switch off
    touch "/tmp/poweroff.please"
    shutdown -h now
}

wittyPi_stop()
{
    # LED on GPIO-17 (wiringPi pin 0)
    led_pin=$1

    # halt by GPIO-4 (wiringPi pin 7)
    halt_pin=$2

    # light the white LED
    if [ -f "/tmp/shutdown.please" -o -f "/tmp/poweroff.please" ]; then
        gpio mode $led_pin out
        gpio write $led_pin 1
    fi

    # restore GPIO-4
    gpio mode $halt_pin in
    gpio mode $halt_pin up
}

pin356_start()
{
	mode=$1
	python /recalbox/scripts/rpi-pin356-power.py -m "$mode" &
    pid=$!
    echo "$pid" > /tmp/rpi-pin356-power.pid
    wait "$pid"
}
pin356_stop()
{
    if [[ -f /tmp/rpi-pin356-power.pid ]]; then
        kill `cat /tmp/rpi-pin356-power.pid`
    fi
}

pin56_start()
{
    mode=$1
    python /recalbox/scripts/rpi-pin56-power.py -m "$mode" &
    pid=$!
    echo "$pid" > /tmp/rpi-pin56-power.pid
    wait "$pid"
}
pin56_stop()
{
    if [[ -f /tmp/rpi-pin56-power.pid ]]; then
        kill `cat /tmp/rpi-pin56-power.pid`
    fi
}

# https://shop.pimoroni.com/products/onoff-shim
onoffshim_start()
{
    mode=$1
    python /recalbox/scripts/onoffshim.py -m "$mode" &
    pid=$!
    echo "$pid" > /tmp/onoffshim.pid
    wait "$pid"
}
onoffshim_stop()
{
    if [[ -f /tmp/onoffshim.pid ]]; then
        kill `cat /tmp/onoffshim.pid`
    fi
}

# First parameter must be start or stop
if [[ "$1" != "start" && $1 != "stop" ]]; then
    exit 1
fi

CONFFILE="/recalbox/share/system/recalbox.conf"
CONFPARAM="system.power.switch" 
CONFVALUE=
if [ -e $CONFFILE ]; then
    CONFVALUE=$(sed -rn "s/^$CONFPARAM=(\w*)\s*.*$/\1/p" $CONFFILE | tail -n 1)
fi

echo "$CONFVALUE"

case "$CONFVALUE" in
    "ATX_RASPI_R2_6")
        atx_raspi_$1 7 8
    ;;
    "MAUSBERRY")
        mausberry_$1 23 24
    ;;
    "REMOTEPIBOARD_2003")
        msldigital_$1 22
    ;;
    "REMOTEPIBOARD_2005")
        msldigital_$1 14
    ;;
    "WITTYPI")
        wittyPi_$1 0 7
    ;;
    "PIN56ONOFF")
        pin56_$1 onoff
    ;;
    "PIN56PUSH")
        echo "will start pin56_$1"
        pin56_$1 push
    ;;
    "PIN356ONOFFRESET")
        echo "will start pin356_$1"
        pin356_$1 onoff
    ;;
    "PIN356PUSHRESET")
        echo "will start pin356_$1"
        pin356_$1 push
    ;;
    "ONOFFSHIM")
        echo "will start onoffshim_$1"
        onoffshim_$1 push
    ;;	
esac

Add the following file /recalbox/scripts/onoffshim.py:

import RPi.GPIO as GPIO
import os
import argparse

parser = argparse.ArgumentParser(description='power manager')
parser.add_argument("-m", help="mode onoff or push", type=str, required=True)
args = parser.parse_args()

mode = args.m

GPIO.setmode(GPIO.BCM)
# GPIO on pin 11 is the GPIO 17 in BCM mode
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def shutdown():
  os.system("shutdown -h now")

try:
  if mode == "push" :
    GPIO.wait_for_edge(17, GPIO.FALLING)
    shutdown()
  else:
    print("Unrecognized mode")
except KeyboardInterrupt:
    print ""

finally:
    print "cleaning up gpio"
    GPIO.cleanup()

Reboot to take effect then confirm by testing.