Comments 8
В прошлых похожих темах уже вставал вопрос, что делать с возможным вздутием батареи когда телефон постоянно подключен к ЗУ, как то прикидывали для себя этот вопрос?
Скажу честно, именно касательно вздутия особо не думал, но понимал, что деградация батареи и перегрев - довольно большие проблемы для данного решения. Я уже купил под него отдельный 10-ваттный адаптер, чтобы питание шло не с очень большим током и начал изучать вопрос ограничения заряда, чтобы батарея держалась в зоне 40-80 процентов. Но пока до конца это не реализовал.
Вообще, честно говоря, я готов пойти даже на радикальные меры вплоть до перепайки платы питания, чтобы полностью удалить батарею из устройства (например, прямо сейчас нашёл, как это сделали с аналогичным устройством вот тут -https://blog.kedio.co/post/how-to-run-a-oneplus-6t-without-battery/ с примерно той же мотивацией). Это, конечно, чуть-чуть ухудшит "надёжность сервера", что в нём по сути есть встроенный бесперебойник.
Но на текущий момент план - настроить потребление-разряд, чтобы держать в безопасном диапазоне, не перегревать, смотреть, что будет дальше. Думаю, что буду "ревизировать" устройство на состояние батареи раз в какое-то время (месяц) и, если проблема всё же возникнет, то уже ставить вопрос о "хирургическом вмешательстве". Либо с целью замены, либо с целью удаления батареи.
Но опять же, глобально этот вопрос пока не продумывал и возможно первые поверхностные мысли неидеальны.
В общем, я чуть-чуть изучил вопрос, управление на стороне устройства зарядкой выглядит немного костыльным. Потому что, если начинаешь управлять зарядом программно, то драйвер зарядки "тупеет" и перестаёт показывать реальное состояние кабеля - подключен/отключен. Он стандартно всегда считает, что зарядка подключена (и на входе просто 0,01 А). Я накидал в итоге такой скрипт управления, который позволяет и состояние заряда проверять и температуру контролировать и при этом всём раз в 15 минут проверять идёт зарядка или нет.
battery-limiter.service
[Unit]
Description=Battery charge limiter (40%%-80%%)
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/local/bin/battery-limiter.sh
# Re-enable charging if service stops so phone doesn't die
ExecStopPost=/bin/sh -c 'echo 500000 > /sys/class/power_supply/pmi8998-charger/current_max'
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.targetbattery-limiter.sh
#!/bin/bash
# battery-limiter.sh v9 -- keeps OnePlus 6 battery between 40% and 80%
# Deployed as systemd service: battery-limiter.service
#
# CONSTRAINT: any write to pmi8998-charger/current_max permanently
# corrupts the driver's readback (online/status/voltage_now freeze).
# All sensing uses bq27411-0 fuel gauge (independent i2c chip).
#
# Three states:
# charging -- current_max=500mA, battery charging toward 80%
# paused -- current_max=0, battery discharging toward 40%
# every 15 min: probe cable by restoring driver for 10s
# idle -- driver has full control, cable is disconnected
# poll bq27411/status every minute to detect charger
#
# Temp safety: pause at 45C, resume below 40C
set -uo pipefail
CHARGE_MAX=80
CHARGE_MIN=40
CHARGE_CURRENT=500000 # 500mA
TEMP_MAX=450 # 45.0C
TEMP_RESUME=400 # 40.0C
CHECK_INTERVAL=60 # 1 minute
PROBE_CYCLES=15 # 15 cycles = 15 min between cable probes
PROBE_SETTLE=10 # seconds to let driver settle during probe
HEARTBEAT_CYCLES=5 # 5 cycles = 5 min
SYS_CMAX="/sys/class/power_supply/pmi8998-charger/current_max"
SYS_CAP="/sys/class/power_supply/bq27411-0/capacity"
SYS_TEMP="/sys/class/power_supply/bq27411-0/temp"
SYS_CUR="/sys/class/power_supply/bq27411-0/current_now"
SYS_BQST="/sys/class/power_supply/bq27411-0/status"
STATE="idle"
TEMP_LOCKED=0
PROBE_CTR=0
HEARTBEAT_CTR=0
log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $1"; }
fmt_temp() { echo "$((${1}/10)).$((${1}%10))C"; }
write_cmax() {
if ! echo "$1" > "$SYS_CMAX" 2>/tmp/cmax_err; then
log "WARN: write current_max=$1 failed: $(cat /tmp/cmax_err)"
fi
}
enter_charging() {
write_cmax "$CHARGE_CURRENT"
STATE="charging"
PROBE_CTR=0
log "CHARGING (${CHARGE_CURRENT}uA, cap=${1}%)"
}
enter_paused() {
write_cmax 0
STATE="paused"
PROBE_CTR=0
log "PAUSED (cap=${1}%)"
}
enter_idle() {
# Give control back to driver -- write a normal value so it
# can re-negotiate, then hands off.
write_cmax "$CHARGE_CURRENT"
STATE="idle"
PROBE_CTR=0
log "IDLE -- driver has control (cable absent)"
}
# --- Validate sysfs ---
for node in "$SYS_CMAX" "$SYS_CAP" "$SYS_TEMP" "$SYS_CUR" "$SYS_BQST"; do
[ -e "$node" ] || { log "ERROR: missing $node"; exit 1; }
done
log "Starting: range ${CHARGE_MIN}%-${CHARGE_MAX}%, temp $(fmt_temp $TEMP_MAX), probe every ${PROBE_CYCLES}min"
# --- Initial state: start idle, let first cycle decide ---
CAP=$(cat "$SYS_CAP" 2>/dev/null) || CAP=50
BQST=$(cat "$SYS_BQST" 2>/dev/null) || BQST="Unknown"
if [ "$BQST" = "Charging" ]; then
# Charger is connected right now
if [ "$CAP" -ge "$CHARGE_MAX" ]; then
enter_paused "$CAP"
else
enter_charging "$CAP"
fi
else
log "IDLE -- no charge detected at start (bq=${BQST}, cap=${CAP}%)"
fi
# --- Main loop ---
while true; do
sleep "$CHECK_INTERVAL"
CAP=$(cat "$SYS_CAP" 2>/dev/null) || continue
TEMP=$(cat "$SYS_TEMP" 2>/dev/null) || continue
CUR=$(cat "$SYS_CUR" 2>/dev/null) || continue
# --- Heartbeat ---
HEARTBEAT_CTR=$((HEARTBEAT_CTR + 1))
if [ "$HEARTBEAT_CTR" -ge "$HEARTBEAT_CYCLES" ]; then
HEARTBEAT_CTR=0
BQST=$(cat "$SYS_BQST" 2>/dev/null) || BQST="?"
log "STATUS state=${STATE} cap=${CAP}% temp=$(fmt_temp $TEMP) cur=${CUR}uA bq=${BQST}"
fi
# --- Temperature safety (always wins) ---
if [ "$TEMP" -ge "$TEMP_MAX" ] && [ "$STATE" = "charging" ]; then
log "OVERHEAT $(fmt_temp $TEMP) -- pausing"
TEMP_LOCKED=1
enter_paused "$CAP"
continue
fi
if [ "$TEMP_LOCKED" = "1" ] && [ "$STATE" != "idle" ]; then
if [ "$TEMP" -lt "$TEMP_RESUME" ]; then
log "COOLED $(fmt_temp $TEMP) -- temp lock off"
TEMP_LOCKED=0
else
# Stay paused/enforce while hot
if [ "$STATE" = "paused" ] && [ "$CUR" -gt 0 ]; then
write_cmax 0
log "ENFORCE 0 (temp lock, cur=${CUR}uA)"
fi
continue
fi
fi
# =====================
# STATE: idle
# Driver has full control. We just watch bq27411/status.
# When charging detected -> take over.
# =====================
if [ "$STATE" = "idle" ]; then
BQST=$(cat "$SYS_BQST" 2>/dev/null) || continue
if [ "$BQST" = "Charging" ]; then
log "CHARGER DETECTED (bq=${BQST}, cap=${CAP}%)"
if [ "$CAP" -ge "$CHARGE_MAX" ]; then
enter_paused "$CAP"
else
enter_charging "$CAP"
fi
fi
continue
fi
# =====================
# STATE: charging
# current_max=500mA, charging toward 80%
# =====================
if [ "$STATE" = "charging" ]; then
if [ "$CAP" -ge "$CHARGE_MAX" ]; then
enter_paused "$CAP"
fi
# If driver reset current_max on re-plug and current is
# flowing but higher than expected, that's fine -- we're
# charging anyway. No enforce needed in this state.
continue
fi
# =====================
# STATE: paused
# current_max=0, discharging toward 40%
# Enforce if driver resets current_max (detect via positive current).
# Probe cable every PROBE_CYCLES.
# =====================
if [ "$STATE" = "paused" ]; then
# Resume charging at 40%
if [ "$CAP" -le "$CHARGE_MIN" ]; then
enter_charging "$CAP"
continue
fi
# Enforce: if battery is gaining charge, driver must have
# reset current_max (USB re-negotiation on cable insert)
if [ "$CUR" -gt 0 ]; then
write_cmax 0
log "ENFORCE 0 (driver reset, cur=${CUR}uA)"
PROBE_CTR=0 # just wrote -- reset probe timer
continue
fi
# Periodic cable probe
PROBE_CTR=$((PROBE_CTR + 1))
if [ "$PROBE_CTR" -ge "$PROBE_CYCLES" ]; then
PROBE_CTR=0
log "PROBE: restoring driver for ${PROBE_SETTLE}s ..."
write_cmax "$CHARGE_CURRENT"
sleep "$PROBE_SETTLE"
CUR2=$(cat "$SYS_CUR" 2>/dev/null) || CUR2=-1
if [ "$CUR2" -gt 0 ]; then
# Cable still connected -- re-pause
write_cmax 0
log "PROBE: cable connected (cur=${CUR2}uA) -- staying paused"
else
# Cable gone -- hand off to driver
enter_idle
fi
fi
continue
fi
doneМне кажется, лучшее и самое правильное решение для сохранения жизни аккумулятора - это такая же схема, но с умной розеткой. Путь такой:
Телефон только контролирует своё состояние заряда. Если нужно зарядится (ниже 40%) - отправляется сигнал умной розетке, она врубается. Если нужно перейти в разрядку (выше 80% или перегрев) - тоже сигнал розетке, она вырубается. При этом розетка должна управляться по локальной сети, чтобы, если интернет-соединение потеряется, телефон не мог уйти в перегрев.
Ну и да, всё же рано или поздно менять батарею или удалять её в любом случае придётся. Когда момент придёт - я задумаюсь, что именно из этого хочу сделать. Но пока пусть живёт в таком режиме.
Вообще конечно не рекомендовал бы держать устройство с аккумулятором постоянно включенным в сеть. Вздутие это фигня, аккум выкинуть можно. Есть риск взрыва аккумулятора из-за деградации, а вот это опасно
Если я правильно понимаю физику процесса, взрыв всё же происходит, когда аккумулятор уже вздулся и на крайней стадии деградации. Он тем менее вероятно произойдёт, чем лучше состояние батареи, ниже ток, ниже температура, ниже заряд и легче режим работы для аккумулятора. Потому как по факту взрыв - это крайняя стадия распирания внутренними газами или тотальный перегрев.
Да, исключить такой сценарий полностью при постоянном подключении, наверное, нельзя, но, во-первых, в описанной схеме подключение именно что непостоянное (да, кабель воткнут, но батарея питается только, когда идёт заряд). Во-вторых, вероятность взрыва аккумулятора при удержании заряда в цикле 40/80 на низком питании с защитой по температуре, пожалуй, сопоставимо, если не ниже, чем вероятность взрыва аккумулятора обычного смартфона при зарядке, тем более с использованием всяких fastCharge. Да, такие случаи происходят, но это, скорее, исключение, чем правило.
Мне кажется, что основное требование теперь - ревизия "сервера" раз в месяц на предмет отсутствия видимого вздутия. Если оно всё же будет обнаружено - в этот момент, да, риски кратно возрастают и стоит на это как-то реагировать. Как я и сказал выше, первостепенно рассматриваю, если такое произойдёт, просто замену батареи или, если будет желание поэкспериментировать, впаивание схемы-обманки для постоянного питания.
Нашёл один баг. Надо доработать кусок
if [ "$STATE" = "idle" ];
# =====================
# STATE: idle
# Driver has full control. We just watch bq27411/status.
# When charging detected -> take over.
# Safety: if cap drops to CHARGE_MIN, force charge ourselves
# (driver may be stuck after our previous writes).
# =====================
if [ "$STATE" = "idle" ]; then
# Safety net: don't let battery die while waiting for driver
if [ "$CAP" -le "$CHARGE_MIN" ]; then
log "SAFETY: cap=${CAP}% <= ${CHARGE_MIN}% in idle -- forcing charge"
enter_charging "$CAP"
continue
fi
BQST=$(cat "$SYS_BQST" 2>/dev/null) || continue
if [ "$BQST" = "Charging" ]; then
log "CHARGER DETECTED (bq=${BQST}, cap=${CAP}%)"
if [ "$CAP" -ge "$CHARGE_MAX" ]; then
enter_paused "$CAP"
else
enter_charging "$CAP"
fi
fi
continue
fiЯ прошивал postmarketOS на Samsung Galaxy Tab 10.1, и докер там поднимал. Но узнав какие риски при постоянно подключенном к питанию аккумуляторе убрал планшет в ящик. На днях вот реализовал другой вариант: взял Vontar X3 - ТВ приставка на amlogic s905x3 (antutu порядка 75К)- прошил на Armbian. 4gb ram, 32gb nand flash. Usb2+usb3+microSD. Поднял Docker + Portainer. Цена вопроса - меньше 3тыр с алика. На usb порт можно подцепить внешний жесткий диск и использовать как samba, dlna и прочие свистульки
Тоже интересный вариант! Но всё же, кажется, что ddr3 память и представленный процессор сильно слабее oneplus 6 (нашёл цифру на antutu - 276510), который мне встал немногим дороже. Но, честно говоря, основной идеей была в целом проверка того, насколько просто поставить сервак на телефон и что из этого получится. Я даже немного расстроился, что вышло так просто, потому что рассчитывал поковыряться.
Так что можно сказать, что адекватные простые хорошие решения меня интересовали мало =)
На один плюс больше. Сервер Mobian на OnePlus 6. Оказалось слишком просто