With FP4 (and probably other models) it is not possible to limit charging of the battery to a certain percentage. Limiting the charge to certain percentage increases the lifespan of your battery, other manufactures allow you to set such a charge limit, the sustainable Fairphone company apparently has decided such a feature is not necessary and when you want to do it with an app, you need to root your phone, which comes with other problems, which should be considered before taking action. (E.g. banking apps tend to not work on rooted phones)
Here I share how I implemented a “smart charger” with a RaspberryPi, a common cheap singleboard computer. You can set a charging limit for your phone without rooting it. However, it does require you to enable ADB USB debugging, which could cause security issues and it is not intended use of this feature. So it would be better, easier and more efficient for Fairphone to implement it in their OS.
DISCLAIMER: This is not an officially supported system and I do not take any responsibility for you breaking your device or for this to work properly, safe and secure. Replicate at your own risk.
Drawbacks:
- The RaspberryPi continually draws idle current wasting energy
- Slow charging (500mA / 2.5W)
- Enabling ADB USB debugging on the phone could cause security issues
Description
So in very short how does it work?
The RPi is configured to connect to your phone, reads the battery percentage, and when your set threshold is reached (e.g. 80%) it turns off the USB port for a while. When the port is turned on again, it looks again for the percentage and decides on either to charge or turn off the port again.
More detailed description:
It queries your phone through ADB to get the phone battery percentage. This is done in a systemd service, which is triggered automatically as soon as an USB device is connected over an udev rule. This then checks for an android device with adb, and terminates if not found. If found, it checks the percentage regularly and deactivates the port when your threshold (80% set by standard) is reached.
Between 23:00 and 05:00 it will deactivate the USB port until 05:00, otherwise it will deactivate if for an hour until activating it again and repeat polling of the battery level.
Install
What you need is:
- RaspberryPi (I used Model 2B) + SD card for it
- The script
charge_limit.shcan be adapted also to your laptop etc.! - You would have to change the code to read the USB port ID and only disable the one your phone is connected to. I might add an example later.
- The script
- A USB charger for the RPi (>2A / >10W)
- 1 USB cable to supply power to the RPi
- 1 USB cable to connect your phone to the RPi
Instructions:
The instructions are for advanced users. If there is enough demand I may simplify it for beginners. Otherwise you will have to google and follow instructions there, if a step is unclear. I might have forgotten some steps, let me now if it does not work.
- Install RPi OS on the SD card, if not preinstalled (choose username rpi if possible)
- Optional: Edit the /boot/config.txt on the SD to your likings (e.g. turn off LEDs, enable SSH)
- Boot the RPi with the SD card and continue either through SSH or with mouse, keyboard and display directly on the RPi
- Install packages
sudo apt install adb uhubctl - Put
charge_limit.shin the RPi home directory, and make it executablechmod +x charge_limit.sh - Optional: change the charge limit in the file, standard is set to 80%
- Put
91-charge-limit.rulesunder/etc/udev/rules.d/91-charge-limit.rules - Reload with:
udevadm control --reload-rules && udevadm trigger - Put
charge_limit.serviceunder/etc/systemd/system/charge_limit.service - If you use a different RPi model to model 2, you have to change some code in
charge_limit.sh, see the note below the files in this post further down. - Reload systemd:
sudo systemctl daemon-reload - Enable ADB USB Debugging in the developer settings of your PHONE
- Connect your phone the first time with the RPi and accept the footprint permanently (the prompt that comes when you connect)
- It now should work, as long as ADB USB debugging is activated. If you deactivate it, your phone will just charge to 100% and there will be no smart features. If you activate ADB on your phone and you plug it into the RPi again, it should work again automatically.
- If it does not work, reboot the RaspberryPi and try connecting your phone again
- If it does work, disconnect your RaspberryPi from your home network to increase security and only connect if you want to do some changes that require internet connection
How to increase charging current beyond 500mA:
For RPi 2 and probably also the other models, BC 1.2 is not implemented. This is a protocol that tells the USB Hardware (i.e. your phone) that it can draw more current, up to 1.5A (7.5W). Alternatively there is USB PD, but also this is not implemented by RaspberryPi afaik.
So you would need to do two things:
- Buy or develop additional hardware to put between RPi and your phone to implement BC 1.2
- Set flags in the config.txt of RPi to allow maximum current, required on some models
The maximum RPi USB current varies between 0.5 and 1.6A, depending on model.
Here are the contens of the files, as FP does not allow me to attach it directly:
config.txt
# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details
# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1
# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16
# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720
# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1
# uncomment to force a specific HDMI mode (this will force VGA)
hdmi_group=1
#hdmi_mode=1
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
hdmi_drive=2
# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4
# uncomment for composite PAL
#sdtv_mode=2
#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on
# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18
# Increase USB Power Output to max 1.2A from 0.6A
max_usb_current=1
# LEDs off
dtparam=act_led_trigger=none
dtparam=pwr_led_trigger=none
#DVD screen auto turn on cec
#hdmi_ignore_cec_init=1
# Additional overlays and parameters are documented /boot/overlays/README
# Enable audio (loads snd_bcm2835)
dtparam=audio=on
# Automatically load overlays for detected cameras
camera_auto_detect=1
# Automatically load overlays for detected DSI displays
display_auto_detect=1
# Enable DRM VC4 V3D driver
dtoverlay=vc4-kms-v3d
max_framebuffers=2
# Disable compensation for displays with overscan
disable_overscan=1
[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1
[all]
[pi4]
# Run as fast as firmware / board allows
arm_boost=1
[all]
charge_limit.sh
#!/bin/bash
# Script is triggered on device USB connection
# It determines device charge level with adb
# and if threshold is reached, it turns off the USB
# until 05:00 in the morning or for 1h, depending on
# time of day.
# That way charging is limited to threshold percentage
threshold=80
# Wait for system to detect adb device
sleep 1
# Regulary check for level and if device is still there
while :
do
# Get battery charge
level=$(adb shell dumpsys battery >&1 2>/dev/null | grep level | tr -d -c 0-9)
# If level is empty, no android adb enabled device
if [ -z "$level" ]; then
echo "No adb device"
exit 0
fi
if (( level >= threshold )); then
break;
fi
sleep 1
done
# Level has reached threshold
# Turn off charging, and probe again later
uhubctl -l 1-1 -p 2 -a off
# If 23:00 - 05:00 => Turn USB off until 05:00
# Else turn off for 1 hour (@ 15%/h max discharge rate measured)
# Get current hour (24-hour format)
current_hour=$(date +%H)
if [ "$current_hour" -ge 23 ] || [ "$current_hour" -lt 5 ]; then
# Calculate seconds until 05:00
now=$(date +%s)
tomorrow_5am=$(date -d '05:00' +%s)
# If current time is after midnight but before 05:00, '05:00' is today
# If current time is after 23:00, '05:00' is tomorrow
if [ "$current_hour" -ge 23 ]; then
tomorrow_5am=$(date -d 'tomorrow 05:00' +%s)
fi
wait_seconds=$(( tomorrow_5am - now ))
echo "It's night time. Waiting until 05:00 ($wait_seconds seconds)..."
sleep "$wait_seconds"
else
echo "It's daytime. Waiting for 1 hour..."
sleep 3600
fi
# Turn on USB
uhubctl -l 1-1 -p 2 -a on
# Script is automatically recalled if device is still connected on repower
91-charge-limit.rules
ACTION=="add", SUBSYSTEM=="usb", TAG+="systemd", ENV{SYSTEMD_WANTS}="charge_limit.service"
charge_limit.service
[Unit]
Description=Run USB Android charge limit script
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/home/rpi/charge_limit.sh
User=root
[Install]
WantedBy=multi-user.target
ATTENTION: replace rpiwith your chosen username for the RPi, if you chose it differently from the instructions.
If you use a different RPi Model than Model 2:
You have to change the USB Port ID in the charge_limit.sh file at every spot uhubctl is used. It probably would make sense to adapt the code, to automatically read the USB port of the android device and turn off this group. On Model 2 all USBs are connected together in one group, so they can only turned off all together. In later models, USB ports are split in individual controllable groups. In theory you could also exploit this to allow the RPi to individually check and control charging of each of multiple android devices connected at the same time, if they are connected to individual groups.
