I made a tiny script to benchmark the battery life which I find that could be pretty useful for uConsole.
uConsole has a voltage floor of ~3.18v. This means that we could never reach the marked capacity from the batteries we bought. Most 3.7V batteries has a voltage floor of ~2.8v.
For example, for the famous Samsung 35E, if you check the graph above:
Let’s assume that you’re running a light load on the uConsole. You didn’t enable anything power consuming. This means that no heavy load, no NVME drives, no SDR, no LORA, no active web browsing… just… letting it sit there. Typically, this cost about 5-6w. If you’re using 18650, then dual 18650 would translate to ~0.8Amps. If you’re using LiPo pouch batteries, then it would translate to ~1.6 amps.
If you’re using SDR with light load (no denoise, no image decoding or other fancy features, just listen to radio), then that’s about 6.5-7.5watts. For dual 18650 that’s about ~1amps, and for lipo that’s about ~2amps.
If you’re using a full-fledge uConsole, you would hit the wall hard by forcing it run at 25w. Which is pretty stressful for both powering options. For 18650s that means a ~3.4 amps load, which is still reasonable though not optimal, and for Lipo that’s ~6.8 amps!
No LiPo battery on the market can do that currently with a single pouch. For higher loads you definitely would need a parallel system.
Using the results above, we can get:
Using 35E (3500mah each, 7000 in total):
Light load: you can get 11.25whr each cell, so 22.5whr in total.
Heavy load: you can get 10.25whr each, and 20.5 in total.
Using Lipo (9858102, 10000mah):
My test is that under light load depending on the cell you got you can get anywhere from 24whr to 31whr.
I haven’t tested heavy load situation yet as I don’t have a NVME drive installed.
Here is how to run the test script:
- Copy and paste the script into an empty file, like battery_tester.py, save it as your home folder.
- Run it with this command:
nohup python battery_tester.py - then press Ctrl+Z
- then type in command:
bg - then you should see it generating a text file logging the battery status.
- you can monitor the log by using command:
watch -n 3 “tail -n 3 ~/battery_output_log.txt” - after the uConsole crashed due to battery low, you can reboot the system, and type in this command to see the last voltage and whrs used:
tail -n 15 ~/battery_output_log.txt - You can also use the
head - n 5 ~/battery_output_log.txtcommand to see when the test started. So that you can get a runtime estimation.
The script:
#!/usr/bin/env python3
import time
import traceback
from datetime import datetime
CURRENT_PATH = "/sys/class/power_supply/axp20x-battery/current_now"
VOLTAGE_PATH = "/sys/class/power_supply/axp20x-battery/voltage_now"
LOG_FILE = "battery_output_log.txt"
INTERVAL = 0.1 # seconds
# Convert (uV * uA * seconds) → mWh
UV_UA_TO_MWH = 1e-9 / 3600.0
def read_int(path):
with open(path, "r") as f:
return int(f.read().strip())
def main():
total_mwh = 0.0
last_time = time.time()
with open(LOG_FILE, "a") as log:
log.write("\n=============================\n")
log.write(f"Discharge session started at {datetime.now()}\n")
try:
while True:
now = time.time()
dt = now - last_time
last_time = now
try:
current_uA = read_int(CURRENT_PATH)
voltage_uV = read_int(VOLTAGE_PATH)
except Exception:
log.write("\n--- DRIVER FAILURE ---\n")
log.write(f"Time: {datetime.now()}\n")
log.write(traceback.format_exc())
log.write(f"Output energy before crash: {total_mwh:.3f} mWh\n")
log.flush()
break
# Discharge = negative current
if current_uA < 0:
discharge_uA = -current_uA # make positive magnitude
total_mwh += voltage_uV * discharge_uA * dt * UV_UA_TO_MWH
# Convert only for display
voltage_V = voltage_uV / 1_000_000
current_A = current_uA / 1_000_000
log.write(
f"{datetime.now()}, "
f"V={voltage_V:.3f}V, "
f"I={current_A:.3f}A, "
f"Output={total_mwh:.3f} mWh\n"
)
log.flush()
time.sleep(INTERVAL)
except KeyboardInterrupt:
log.write("\n--- Stopped by user ---\n")
log.write(f"Time: {datetime.now()}\n")
log.write(f"Final discharge energy: {total_mwh:.3f} mWh\n")
log.flush()
if __name__ == "__main__":
main()
