SLEEP support development for CM4

This is my effort to implement device sleep support for CM4. I call it quasi-sleep.

Obviously, CM4 does not support true sleep, nor does it have “efficiency” cores to fall back to as Android does, so we only have left with:

  1. Put all processes to sleep to let CPU clock down and prevent any heavy compute while in “sleep”, effectively drawing just idle current [DONE]
  2. Switch off screen backlight [DONE]
  3. Shut down graphics core - I have no idea yet how to do this
  4. Lock the keyboard - dangerous, and I have yet to find a way to do this programmatically. It may be possible to issue a lock so that it can be unlocked with fn-combo?

To implement this, I wrote a simple script called qsleep.sh

#!/bin/bash

# Check if pidstat is already running
if pgrep -x "pidstat" > /dev/null; then
    echo "pidstat is already running. Exiting."
    echo 2 > /sys/class/backlight/backlight@0/brightness
    killall lxde-pi-shutdown-helper
    killall qsleep.sh
    exit 1
fi

if pgrep -x "cpulimit" > /dev/null; then
    echo "resume"
    killall cpulimit
    echo 2 > /sys/class/backlight/backlight@0/brightness    
    exit 1
fi

if [ "`cat /sys/class/backlight/backlight@0/brightness`" = "0" ] ; then
    echo "resume"
    killall cpulimit
    echo 2 > /sys/class/backlight/backlight@0/brightness    
    exit 1
fi

echo 0 > /sys/class/backlight/backlight@0/brightness

# Run pidstat and capture output
output=$(pidstat 1 1)

# Extract the 'Average:' lines from the output
averages=$(echo "$output" | grep '^Average:')

# Parse the output to find PIDs where %CPU > 2
pids=$(echo "$averages" | awk 'BEGIN {FS="[[:space:]]+"} $8 > 2 {print $3}')

# Print the PIDs
echo "PIDs using more than 2% CPU: $pids"

# Apply cpulimit to each PID
for pid in $pids; do
    cpulimit -z -b -l 2 --pid=$pid
done

Depends on sysstat and cpulmit:

sudo apt install sysstat cpulimit

To “install” I just replace the default binary that reacts to the power button press. Assuming you saved the script to your $HOME directory as qsleep.sh,

sudo mv /usr/bin/lxde-pi-shutdown-helper /usr/bin/lxde-pi-shutdown-helper.bak
sudo ln -s $HOME/qsleep.sh /usr/bin/lxde-pi-shutdown-helper

This has the effect of “locking the smartphone” as you would see from your ordinary Android phone, and extends the battery run in my case to about 13 hours or so on a normal smartphone-y use pattern. It can extend even more if I get the cpulimit to throttle processes to 0, but I leave them at 2% CPU to still receive notifications and mail in background.

The script also does not re-check if any new heavy processes pop up, this may also help extending. I will keep developing it and see how long it could sustain on more aggressive settings.

9 Likes

Good work! I use my uConsole for work and normally shut it down between jobs. While battery was never a problem working that way, it wouldn’t last all day if I did leave it on. Today I just put the uConsole to “sleep” while between jobs and the battery lasted all day.
I did change it to return to brightness level 9 when coming out of “sleep” because i’m working outside or bright areas. Great addition to help battery life.

3 Likes

I updated the script to squeeze out more power savings:

#!/bin/bash
# sudo apt install cpulimit sysstat wlr-randr procps

CPU_GOVERNOR="performance"  # or "ondemand"

if pgrep -x "pidstat" > /dev/null || pgrep -x "cpulimit" > /dev/null || [ "$(cat /sys/class/backlight/backlight@0/brightness)" = "0" ]; then
    echo "Resuming or stopping processes as required."
    killall pidstat
    killall cpulimit
    wlr-randr --output DSI-1 --on
    pkill -f qsleep_bg
    cat /tmp/brightness > /sys/class/backlight/backlight@0/brightness
    echo $CPU_GOVERNOR | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
    killall qsleep.sh
    killall lxde-pi-shutdown-helper
    exit 1
fi

cat /sys/class/backlight/backlight@0/brightness > /tmp/brightness
echo 0 > /sys/class/backlight/backlight@0/brightness
echo "powersave" | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

# Shut down GPU after some idling
nohup bash -c '(exec -a qsleep_bg sleep 60); if [ "`cat /sys/class/backlight/backlight@0/brightness`" = "0" ] ; then wlr-randr --output DSI-1 --off; fi' > /dev/null 2>&1 &
disown

# Put all CPU-heavy processes to sleep, progressively re-checking every 60 seconds
nohup bash -c '
while true; do 
    for pid in $(pidstat 1 1 | grep "^Average:" | awk "BEGIN {FS=\"[[:space:]]+\"} \$8 > 5 {print \$3}"); do 
        cpulimit -z -b -l 1 --pid=$pid; 
    done; 
    exec -a qsleep_bg sleep 60; 
done' > /dev/null 2>&1 &
disown

Changes:

  • It shuts down GPU after 60 seconds of sleep. Takes more time to wake.
  • Puts CPU “power governor” to “powersave”
  • Every 60 seconds re-checks if any new hungry processes pop up
  • Restores previous level of brightness
  • Has more dependencies (see on top)
  • Processes are now given 1% CPU max

It was tested on “bookworm” distro from this forum and labwc window manager that is now recommended by Pi. You can switch to labwc from raspi-config.

4 Likes

Sweet, i’m using about 1% of battery a hour during “SLEEP” now.
Edit: first % was was a fluke, it’s using about 4% an hour in “SLEEP” mode.

I’m using this little command I put together to make a log file on the desktop of uptime, battery %, and load times.

while true
do
uptime -p | sed -e 's/up/Uptime:/g' > $HOME/Desktop/uptime.log
cat /sys/class/power_supply/axp20x-battery/capacity | sed -e 's/^/Battery: /' | sed -e 's/$/%/' >> $HOME/Desktop/uptime.log
uptime | cut -d ',' -f 3-6 | sed -e 's/^[ \t]*//' | sed -e 's/load average/Load Average/g' >> $HOME/Desktop/uptime.log
sleep 60
done &
2 Likes

I’m wondering if this sleep support would be helpful on a Devterm as well? Specifically, I’ve had times where I used it sort of a like a server, with it on for long periods of time (days, weeks, etc.) I’ve always kept batteries in it though… I wonder if this would help with the batteries last a bit longer, or if the existing battery management stuff prevents much damage to them over time, with whatever draining and recharge cycle occurs. Or perhaps it would just reduce the power usage/cost to keep charging it, not that it’s much of a concern, as I doubt it uses much power anyway. I’m guessing the benefit would be minimal in both cases, but I also don’t really know much about battery charge cycles.

I don’t know if this helps, but I found some underclocking params here: https://forums.raspberrypi.com/viewtopic.php?t=250167&start=25

I wish they could be changed on the fly. But since I use TWM and have a 20Ah pack I don’t really chase power savings any more. (also to be fair I’m waiting for another uConsole and a cable from aliexpress to make my portable one)

But the cpulimit and turning off gpu is interesting. Maybe one could run the script when the screen saver turns off the display automatically?

If you remove the “cpupower” part of it it will definitely help with server scenario. cpupower makes it almost dead slow. As for batteries, I am sure they will deteriorate if you leave them 100% charged, so you may try to change max charge voltage to 70% charged, with latest patches it seems to be configurable, although I haven’t tried

1 Like

I would never use a uConsole as a server, have plenty of Raspberry 2/4 for that. But I use it mounted in a fixed location without batteries.

my gnome shell and postmaket os get me autolock and screen turn off of the shelf what is your OS ?

Uhh, yeah, like just that is a good substitute. The script is specifically designed to induce a low-power mode, like the sleep mode in smartphones. Don’t mistake GNOME’s feature with what is being worked on here.

3 Likes

I use this script to lock & enter pseudo-sleep mode.

echo 0 | doas tee /sys/class/backlight/backlight@0/brightness
echo "powersave" | doas tee /sys/devices/system/cpu/cpufreq/policy0/scaling_governor
nmcli r all off
slock &&\
echo 1 | doas tee /sys/class/backlight/backlight@0/brightness &&\
nmcli r all on &&\
echo "performance" | doas tee /sys/devices/system/cpu/cpufreq/policy0/scaling_governor

Never say never… You might find that you need to do just that some day.

Anyone know how to turn off the keyboard backlight? I tried xset led off but doesn’t seem to work. I also had a look around in /sys/class /dev/ but didn’t see anything for the keyboard backlight. I thought I would add that to the qsleep script.

that’s handled by the micro-controller that runs the keyboard, so i don’t think you’ll be able to script that.

@grandrew Thanks for the great work, my uconsole now supports sleep.
For the logout button in the start menu, I want to keep its original behavior: opening the shutdown options dialog. Do you know which config file should I change?

We need 2 features:

  • turn on/off backlight
  • programmatic lock/unlock so that Fn-ESC works as expected

Apparently the keyboard firmware is open source so two-way HID is at least possible to implement.

I haven’t figured yet a more ‘polite’ way of installing the qsleep script. Ideally it should be set as power button event hook directly, not as a replacement for the system app that shows poweroff dialog. Currently there is no way I know of to keep the behavior separate with button and menu. You may type poweroff command in terminal to power off…

Yeah, I guess I didn’t read carefully and understand that doing this would break the logout menu. I think personally I would would rather have the qlseep tied to a desktop icon that can be executed by clicking it rather than replace the logout menu. I wonder if I could wrap it in a Python TK script with a menu to choose sleep or logout? I might play with it a bit.

I just added a shutdown button to the start menu. I made a shutdown script and added it as a menu item. You could do the same with restart and logout.

mind sharing the script for having the button in the start menu? :innocent: