Powering a Raspberry Pi from batteries reliably requires more than just wiring a USB power bank to the USB-C port. The Pi 4 and Pi 5 are notoriously sensitive to voltage sag; a momentary drop below 4.63V under CPU load triggers a brownout, throttling the SoC and potentially corrupting the SD card. To build a robust off-grid or mobile node, you need a high-efficiency buck-boost converter fed by a multi-cell lithium pack, paired with an I2C fuel gauge to trigger a safe OS shutdown before the Battery Management System (BMS) hard-cuts the power.
Hardware Spec Sheet & Parts List
This build targets the Raspberry Pi 4 Model B (4GB RAM). While the Pi 5 is the current flagship, its 27W (5V/5A) USB-C PD requirement makes custom battery builds significantly more complex and expensive. The Pi 4's 5V/3A requirement is perfectly matched to standard 3S lithium-ion packs and widely available buck converters.
| Component | Exact Variant / Model | Purpose & Notes |
|---|---|---|
| Single Board Computer | Raspberry Pi 4 Model B (4GB) | Target board for code and power calculations. |
| Current/Voltage Sensor | Adafruit INA219 Breakout (ID: 904) | High-side I2C DC current and bus voltage monitor. |
| Battery Cells | 3x Molicel P28A 18650 (or Samsung 30Q) | High-drain Li-ion cells; avoids voltage sag under load. |
| Battery Holder & BMS | 3S 18650 Holder w/ Daly 20A BMS | Provides over-discharge/over-current protection. |
| Step-Down Regulator | XL4015 5A CC/CV Buck Converter | Steps 11.1V nominal down to 5.1V for the Pi. |
| Wiring | 18 AWG Silicone Wire + Dupont | 18 AWG for power rails; Dupont for I2C logic. |
Pin Mapping & Power Wiring
The INA219 monitors the raw battery pack voltage and current draw. Wire the INA219 Vin to the positive terminal of your 3S battery pack, and GND to the pack's negative terminal. The buck converter takes the pack voltage and steps it down to the Pi's GPIO 5V rail.
| Raspberry Pi 4 GPIO | Physical Pin | INA219 Breakout Pin | Wire Color (Suggested) |
|---|---|---|---|
| 3.3V Power | Pin 1 | VCC (Logic) | Red |
| GPIO 2 (SDA1) | Pin 3 | SDA | Blue |
| GPIO 3 (SCL1) | Pin 5 | SCL | Yellow |
| Ground | Pin 6 | GND | Black |
Note: Ensure the INA219 Vin and GND screw terminals are connected directly to the 3S battery pack output, bypassing the buck converter, so it measures the true cell voltage.
Python Battery Monitor with Safe Shutdown
This script uses the Adafruit CircuitPython INA219 library to poll the battery pack. If the voltage drops to the safe shutdown threshold (9.5V, or ~3.16V per cell), it issues a system halt. If the I2C bus throws an error, it catches the exact exception to prevent the monitoring daemon from crashing.
Target Environment: Raspberry Pi 4 Model B (4GB) running Raspberry Pi OS Bookworm (64-bit). Requires pip3 install adafruit-circuitpython-ina219 and I2C enabled via raspi-config.
import time
import os
import board
from adafruit_ina219 import ADCResolution, BusVoltageRange, INA219
# Initialize I2C bus (Bus 1 on Pi 4 GPIO pins 3 and 5)
i2c_bus = board.I2C()
ina219 = INA219(i2c_bus)
# Configure for 16V max bus (3S Li-ion peaks at 12.6V)
ina219.bus_voltage_range = BusVoltageRange.RANGE_16V
ina219.adc_resolution = ADCResolution.ADCRES_12BIT_32S
# 3S Li-ion thresholds (Nominal 11.1V, Full 12.6V, Empty 9.0V)
CRITICAL_VOLTAGE = 9.0 # BMS will hard-cut shortly after this
SHUTDOWN_VOLTAGE = 9.5 # Trigger safe OS shutdown here
def safe_shutdown():
print('Critical battery voltage reached. Initiating safe shutdown...')
os.system('sudo shutdown -h now')
print('Starting battery telemetry monitor...')
while True:
try:
bus_voltage = ina219.bus_voltage
current_ma = ina219.current
power_w = ina219.power / 1000.0
print(f'Pack: {bus_voltage:6.3f} V | Draw: {current_ma:7.2f} mA | Power: {power_w:5.3f} W')
if bus_voltage <= SHUTDOWN_VOLTAGE and bus_voltage > CRITICAL_VOLTAGE:
safe_shutdown()
elif bus_voltage <= CRITICAL_VOLTAGE:
print('WARNING: Voltage below critical threshold! Imminent hard-fail.')
except OSError as e:
# Catches exact I2C communication failures
if e.errno == 121:
print('OSError: [Errno 121] Remote I/O error - I2C bus disconnected or sensor locked up.')
else:
print(f'I2C Read Error: {e}')
except Exception as e:
print(f'Unexpected daemon error: {e}')
time.sleep(5.0)
Debugging Power Failures & I2C Errors
When a battery-powered Pi fails, it rarely just 'turns off.' It usually throws specific kernel warnings or I2C faults. Here is how to diagnose the two most common failure modes.
1. The 'Under-voltage detected!' Kernel Warning
If you run dmesg | grep -i voltage and see Under-voltage detected!, your Pi's PMIC is seeing less than 4.63V. This is the primary enemy when powering a Raspberry Pi from batteries.
Ranked Causes:
- Open-Circuit Tuning: You set the buck converter to 5.0V with no load. When the Pi CPU spikes, the converter sags to 4.5V. Fix: Set voltage while running
stress --cpu 4. - Wire Gauge Too Thin: Using 22 AWG or thinner wire between the buck converter and the Pi GPIO causes a massive voltage drop over distance. Fix: Use 18 AWG or thicker for power rails.
- Buck Converter Ripple: Cheap XL4015 clones have high output ripple. The Pi's PMIC reads the troughs of the ripple wave as undervoltage. Fix: Add a 1000µF electrolytic capacitor across the 5V/GND GPIO pins.
2. The 'OSError: [Errno 121] Remote I/O error'
This is the exact string Python throws when the smbus2 or adafruit_ina219 library fails to get an ACK from the sensor on the I2C bus.
Ranked Causes:
- Loose Dupont Connections: Vibration or thermal expansion breaks the SDA/SCL contact. Fix: Solder header pins or use JST connectors.
- Missing Pull-up Resistors: The Pi has internal 1.8kΩ pull-ups, but long wire runs add capacitance, ruining the I2C rise time. Fix: Add external 4.7kΩ pull-ups to 3.3V on the SDA/SCL lines.
- Sensor Brownout: If the INA219 is powered from the Pi's 3.3V rail, and the Pi experiences a micro-brownout, the 3.3V regulator drops out, resetting the INA219 and locking the I2C bus. Fix: Add a 10µF decoupling capacitor directly across the INA219 VCC/GND pins.
- Measure voltage directly at the Pi's GPIO 5V (Pin 2) and GND (Pin 6) with a multimeter while the Pi is under heavy load.
- Run
i2cdetect -y 1in the terminal to verify the INA219 is acknowledging on address0x40. - Check your BMS wiring to ensure the discharge trace isn't bottlenecking current (verify the BMS is rated for at least 15A continuous).
Extending or Simplifying the Build
Not every project needs a custom-wired buck converter and raw I2C polling. Depending on your deployment timeline, you can adjust the complexity of this build.
How to Simplify:
If you need to deploy this week and don't want to tune buck converters, swap the custom power stage for an integrated UPS HAT. Boards like the Geekworm X1202 or PiSugar 3 Plus plug directly into the GPIO header. They handle 5V boosting, battery charging, and I2C fuel gauging on a single PCB. You lose the ability to use high-capacity custom 3S packs, but you gain plug-and-play reliability.
How to Extend:
To turn this into a production IoT node, extend the Python script by adding the paho-mqtt library. Publish the bus_voltage and current_ma variables to an MQTT broker (like Mosquitto or Home Assistant) every 60 seconds. Additionally, enable the BCM2711 hardware watchdog timer in /boot/firmware/config.txt (dtparam=watchdog=on) so the Pi automatically reboots if the Python monitoring script hangs or the kernel panics from a momentary power glitch.
Frequently Asked Questions
How long can I power a Raspberry Pi from batteries using a 3S 18650 pack?
A 3S pack using three 3000mAh (3Ah) 18650 cells provides roughly 33.3 Watt-hours (Wh) of energy (11.1V nominal × 3Ah). A Pi 4 idling with WiFi on draws about 3.5W. Factoring in 85% efficiency from the XL4015 buck converter, you can expect roughly 8 hours of runtime at idle. If you add a USB camera and run continuous CPU loads, draw spikes to 7W+, cutting runtime to under 4 hours.
Is it safe powering a Raspberry Pi from batteries while simultaneously charging the pack?
Yes, but only if your BMS supports 'charge-and-discharge' simultaneously (most standard Daly or generic 3S BMS boards do). However, the INA219 current readings will be inaccurate during this state, as it will measure the net current (charge current minus Pi load) rather than the Pi's actual consumption. For accurate telemetry, use a dual-sensor setup or a dedicated UPS HAT that isolates the charge and load paths.
Why do I get undervoltage warnings when powering a Raspberry Pi from batteries via USB power banks?
Standard USB power banks are designed for charging phones, not powering single-board computers. They often lack the fast transient response needed when the Pi's CPU switches from idle to 100% load in milliseconds. This causes a microsecond voltage sag that the Pi's PMIC registers as an undervoltage event. Furthermore, many power banks auto-shutoff if the current draw drops below 50mA, which can happen if your Pi enters a deep sleep state, killing your remote node.
Can I use LiFePO4 cells instead of Li-ion for powering a Raspberry Pi from batteries?
Yes, and it is highly recommended for solar or long-life deployments. A 4S LiFePO4 pack (nominal 12.8V, full 14.6V) paired with a buck converter offers a much flatter discharge curve and thousands of extra cycles. The trade-off is physical size and weight; LiFePO4 cells have lower energy density than NMC Li-ion 18650s, so your battery enclosure will need to be roughly 30% larger for the same runtime.






