Skip to content

Instantly share code, notes, and snippets.

@g3ida
Created March 26, 2026 22:21
Show Gist options
  • Select an option

  • Save g3ida/0e8c2aa34fc38702a33260f4c7fd17b3 to your computer and use it in GitHub Desktop.

Select an option

Save g3ida/0e8c2aa34fc38702a33260f4c7fd17b3 to your computer and use it in GitHub Desktop.
battery controller for Pixel3a on PostmarktOS

Create the script file and paste the code from battery-guard.sh

nano /usr/local/bin/battery-guard.sh

Create the service file and paste the code from battery-limit.service

sudo nano /etc/systemd/system/battery-limit.service

Reload the daemon (tells systemd a new file exists):

sudo systemctl daemon-reload

Enable it (makes it start automatically on boot):

sudo systemctl enable battery-limit.service

Start it now:

sudo systemctl start battery-limit.service

Check the status:

sudo systemctl status battery-limit.service

check the logs in systemd

sudo journalctl -u battery-limit.service -f
#!/bin/sh
# Pixel 3a battery Balancer
# --- Hardware Paths ---
CHARGER="/sys/class/power_supply/pm660-charger/current_max"
BAT_FLOW="/sys/class/power_supply/qcom-battery/current_now"
PM660_FLOW="/sys/class/power_supply/pm660-charger/current_now"
BAT_CAP="/sys/class/power_supply/qcom-battery/capacity"
PWR_CONN="/sys/class/power_supply/pm660-charger/online"
# --- Logic Configuration ---
UPPER_CAP=65
LOWER_CAP=45
SAFE_DRAIN_CUR=100000 # 100mA
SAFE_MAX_CUR=1000000 # 1A
STEP=25000 # Hardware resolution (25mA)
HALF_STEP=$((STEP / 2)) # For rounding to nearest step
DEAD_ZONE=$HALF_STEP # 12.5mA - this is becasue we only have 25mA resolution, so we want to avoid constant toggling
GAIN_NUM=1 # Damping: apply GAIN_NUM/GAIN_DEN of correction per cycle
GAIN_DEN=2 # 1/2 = 50% gain — converge gradually
ERROR_ACCUM=0 # Accumulate fractional error lost to quantization
DEBUG_LOG=1
SAMPLES=10
SAMPLE_RATE="0.2"
REST_PERIOD=5
IDLE_PERIOD=20
echo "Balancer pixel3a Started. capacity: $(cat $BAT_CAP)% Target: ${LOWER_CAP}%-${UPPER_CAP}%"
while true; do
# Check for active power connection
IS_PLUGGED=$(cat $PWR_CONN 2>/dev/null || echo 0)
if [ "$IS_PLUGGED" -eq 0 ]; then
[ "$DEBUG_LOG" -eq 1 ] && echo "$(date +%T) [IDLE] No power detected. Sleeping ${IDLE_PERIOD}s."
sleep "$IDLE_PERIOD"
continue
fi
# Battery current sampling
ITER=0; SUM_FLOW=0
while [ $ITER -lt $SAMPLES ]; do
SUM_FLOW=$((SUM_FLOW + $(cat $BAT_FLOW)))
ITER=$((ITER + 1))
sleep $SAMPLE_RATE
done
AVG_FLOW=$((SUM_FLOW / SAMPLES))
CUR_LIMIT=$(cat $CHARGER)
# Control Logic
CAP=$(cat $BAT_CAP)
if [ "$CAP" -ge "$UPPER_CAP" ] && [ "$CUR_LIMIT" -gt "$SAFE_DRAIN_CUR" ]; then
echo $SAFE_DRAIN_CUR > $CHARGER
echo "$(date +%T) [EVENT] Capacity $CAP%. Dropping to floor $((SAFE_DRAIN_CUR / 1000))mA."
continue
fi
if [ "$CAP" -le "$LOWER_CAP" ] && [ "$CUR_LIMIT" -lt "$SAFE_MAX_CUR" ]; then
echo $SAFE_MAX_CUR > $CHARGER
echo "$(date +%T) [EVENT] Capacity $CAP%. Recharging at $((SAFE_MAX_CUR / 1000))mA."
continue
fi
# If Capacity is in the "Happy Zone" (45-70), we balance.
if [ "$CAP" -gt "$LOWER_CAP" ] && [ "$CAP" -lt "$UPPER_CAP" ]; then
ERROR=$(( -1 * AVG_FLOW ))
# If error magnitude is greater than dead zone, adjust. Otherwise, we're close enough
if [ ${ERROR#-} -gt "$DEAD_ZONE" ]; then
# Combine with accumulated fractional error from previous rounding losses
TOTAL_ERROR=$(( ERROR + ERROR_ACCUM ))
# Apply damped correction
CORRECTION=$(( (TOTAL_ERROR * GAIN_NUM) / GAIN_DEN ))
RAW_TARGET=$(( CUR_LIMIT + CORRECTION ))
NEW_LIMIT=$(( ((RAW_TARGET + HALF_STEP) / STEP) * STEP ))
# Clamp to safe hardware boundaries
[ $NEW_LIMIT -gt "$SAFE_MAX_CUR" ] && NEW_LIMIT=$SAFE_MAX_CUR
[ $NEW_LIMIT -lt "$SAFE_DRAIN_CUR" ] && NEW_LIMIT=$SAFE_DRAIN_CUR
if [ "$NEW_LIMIT" -ne "$CUR_LIMIT" ]; then
echo $NEW_LIMIT > $CHARGER
[ "$DEBUG_LOG" -eq 1 ] && echo "$(date +%T) [PI_UPDATE] Flow: $((AVG_FLOW / 1000))mA. Correction: $((CORRECTION / 1000))mA. Setting Limit: $((NEW_LIMIT / 1000))mA (Calc: $((RAW_TARGET / 1000))mA)"
fi
# Calculate how much error was actually corrected and save the remainder
CORRECTION_APPLIED=$(( NEW_LIMIT - CUR_LIMIT ))
ERROR_CORRECTED=$(( (CORRECTION_APPLIED * GAIN_DEN) / GAIN_NUM ))
ERROR_ACCUM=$(( TOTAL_ERROR - ERROR_CORRECTED ))
else
[ "$DEBUG_LOG" -eq 1 ] && echo "$(date +%T) [STABLE] Flow: $((AVG_FLOW / 1000))mA is within Deadzone."
fi
fi
sleep "$REST_PERIOD"
done
[Unit]
Description=Pixel 3a Battery Charge Limiter
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/local/bin/battery-guard.sh
Restart=always
RestartSec=10
User=root
[Install]
WantedBy=multi-user.target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment