Battery gauge & fans
Prerequisites: the overlays and the config.txt block from the
manual setup page - they already include the
stc3117-gauge and i2c-fan lines used here.
Battery gauge (STC3117)
Section titled “Battery gauge (STC3117)”The battery is measured by an STC3117 fuel gauge (I2C address 0x70) on the compute module’s SDA0/SCL0 pair. It sits behind the Pi / ESP32-S3 I2C multiplexer, which hands it to the Pi at power-on (while the Pi owns the display).
Raspberry Pi OS does not build CONFIG_FUEL_GAUGE_STC3117, so TypixDeck OS ships the driver
as a DKMS module - it is rebuilt automatically whenever a kernel and its headers are
installed.
Install
Section titled “Install”sudo apt install -y dkms linux-headers-rpi-v8 linux-headers-rpi-2712# (the dwc2fix kernel from the USB audio page brings its own headers)[ -d ~/typixdeck-pi-gen ] || git clone --depth 1 -b typixdeck https://github.com/TypixNode/pi-gen.git ~/typixdeck-pi-genSRC=/usr/src/stc3117-fuel-gauge-6.18sudo mkdir -p $SRCsudo cp ~/typixdeck-pi-gen/typixdeck-stage/03-battery-dkms/files/{stc3117_fuel_gauge.c,Makefile,dkms.conf} $SRC/sudo dkms add -m stc3117-fuel-gauge -v 6.18sudo dkms install -m stc3117-fuel-gauge -v 6.18sudo rebootCheck:
dmesg | grep stc3117cat /sys/class/power_supply/stc3117-battery/{capacity,voltage_now,current_now,status}upower -d | grep -E 'percentage|state'UPower picks the battery up by itself, so the battery indicator of every desktop (labwc panel, Phosh, Plasma) works without further setup.
The overlay
Section titled “The overlay”stc3117-gauge describes the board:
| Property | Value | Why |
|---|---|---|
shunt-resistor-micro-ohms |
10000 | the 10 mΩ current-sense resistor |
charge-full-design-microamp-hours |
2400000 | 604070 Li-Po, ~2400 mAh |
voltage-min/max-design-microvolt |
3.0 V / 4.2 V | |
factory-internal-resistance-micro-ohms |
140000 | pack resistance as the gauge sees it (cell + protection board + wiring), measured with a CPU load step; the driver’s 200 mΩ default seeded the charge ~15 points low |
Bus: on CM4 the default target is i2c_csi_dsi (GPIO44/45, i2c-10); on CM5 pass
the i2c_csi_dsi0 parameter (RP1, GPIO38/39) - which is why the config.txt lines differ.
Do not point the CM4 at i2c_csi_dsi0: on the CM4 that is GPIO0/1, which belong to the DPI
screen (pin gpio0 already requested). Parameters capacity-uah, shunt-uohm and
rint-uohm override the values for a different battery.
Why not the mainline driver
Section titled “Why not the mainline driver”Do not load the mainline stc3117_fuel_gauge as-is on a TypixDeck. It writes the
16 × 16-bit OCV table as 16 bytes and corrupts it, reports tenths of a percent as the
percentage (4.6 % shows as 46 %), reads current unsigned with the wrong sign convention, and
resets a gauge the ESP32-S3 has already started. The TypixDeck copy fixes those and adds:
- Seeding once per battery, shared with the ESP32-S3. The gauge’s state is shared by both processors, so they agree through a marker in the gauge’s own battery-backed RAM: whichever gets the bus first seeds the charge from the measured voltage and sets the mark; the other only reads. Removing the battery clears the RAM and re-arms the seed. Without it a freshly installed system could show 3-6 % on a full battery.
- A 4.2 V discharge curve for the 604070 pack, written together with the matching state-of-charge breakpoints (mainline’s curve belongs to a 4.35 V cell and never reaches 100 % here).
- Re-seeding only on consistent evidence - voltage and current are converted in different cycles, so a single reading during a load change is not trusted.
charge_now/charge_fullfor panel plugins that compute the percentage themselves, and the battery stays present while the multiplexer hands the gauge to the ESP32-S3.
EMC2301 fan controller (CM4 and CM5)
Section titled “EMC2301 fan controller (CM4 and CM5)”The i2c-fan,emc2301 lines in the config.txt block bind Raspberry Pi OS’s own i2c-fan
overlay to the board’s EMC2301 on the same SDA0/SCL0 pair. Without it the controller stays at
its power-on 100 % duty and no fan sensor exists.
- The overlay enables the bus itself - do not add
dtparam=i2c_vc=onalongside it. - If you tune it, keep
minpwm≤ 51 on kernel 6.12 and newer: a higher value makes the driver refuse the first cooling state and the fan probe fails silently.midtemp,maxtempandmaxpwmare safe to change.
CM5 4-pin fan curve
Section titled “CM5 4-pin fan curve”The CM5 also drives a Pi 5-style 4-pin PWM fan (pwmfan). The stock curve is timid for a
closed case - 29 % from 50 °C - so TypixDeck OS keeps the trip temperatures and raises the
duty at every step:
| Trip | Stock PWM | TypixDeck PWM | Measured |
|---|---|---|---|
| 50 °C | 75 | 130 | 3.6k → 6.5k RPM |
| 60 °C | 125 | 180 | |
| 67.5 °C | 175 | 220 | |
| 75 °C | 250 | 255 | 10.0k RPM at full |
[cm5]dtparam=cooling_fan=ondtparam=fan_temp0_speed=130dtparam=fan_temp1_speed=180dtparam=fan_temp2_speed=220dtparam=fan_temp3_speed=255These names are defined by the CM5 device trees themselves (fan_temp0..3, _hyst,
_speed), so they are safe; a misspelled dtparam is the classic way to end up with a
board that boots without its fan. To check what is running:
od -An -t u1 /proc/device-tree/cooling_fan/cooling-levels # PWM per statefor d in /sys/class/hwmon/hwmon*; do [ -r $d/fan1_input ] && echo "$(cat $d/name) $(cat $d/fan1_input) rpm pwm=$(cat $d/pwm1)"; doneThe hwmon numbers change between boots - look fans up by name, never by hwmonN.