Kindle お天気ダッシュボード改造日記 2
こんにちは、戌咬音かんぬきです。 今日は Kindle の改造日記について、Claude のインスタンスの記録や僕の記憶を掘り起こしながら書いていこうと思います。
具体的には以前書いた Kindle お天気ダッシュボード改造日記 1 の続きです。
改めて簡単に説明すると、e-ink の常時表示を活かして、Raspberry Pi でダッシュボード画像を生成して、Kindle が定期的に取得して画面に表示するというシンプルな構成です。
e-ink は電力を使わずに表示を維持できるので、天気ダッシュボードのような「たまに更新すればいい」用途には最適なはずだったんですよね。 ところが、バッテリーが1日持ちません;;
最初の疑い:スクリプトの設計
手元にあるスクリプトは2種類ありました。
dash.sh—rtcwakeコマンドでサスペンドするバージョンdash-no-rtcwake.sh— 通常のsleepコマンドで待機するバージョン
dash.sh は Kindle のフレームワーク(OS のバックグラウンドプロセス群)を停止していなかったんですよね。
一方、dash-no-rtcwake.sh は stop framework や CPU ガバナーの powersave 設定など、省電力対策がしっかり入っていました。
まずはこの差を埋めることにしました。(ここも Claude が大活躍してくれました。いつもありがとう)
rtcwake は動かなかった
修正版を投入する前に、そもそもこの Kindle で rtcwake が動くのか確認してみました。
rtcwake -d /dev/rtc0 -m mem -s 30
操作は受け付けなくなるんですけど、画面は暗くなりません。(e-ink だから画面が暗くならないのは正常なんですけどね)
30秒後に SSH が復帰しない……
rtcwake はこの端末では使えませんでした。
仕方ないので sleep ベースの dash-no-rtcwake.sh をベースに修正版を作り直しました。
sleep の限界
修正版を投入してログを取りました。
[Thu Apr 2 15:00:36 UTC] Suspending for 43185s using manual RTC wakealarm
[Fri Apr 3 03:00:21 UTC] Battery level: 16%, 235 mAh
12時間で 87% → 16%。71% の消費。 話になりません😭
sleep コマンドは CPU を止めないんですよね。30分ごとにループのログ出力のために CPU が完全に起動して、また眠る……
CPU ガバナーを powersave にしていても、起きている間は電力を消費してしまいます。
手動 RTC サスペンドの発見
rtcwake コマンドはダメでも、RTC ハードウェア自体は生きているかもしれないと思い、rtcwake が内部でやっていることを手動で実行してみました。
echo 0 > /sys/class/rtc/rtc0/wakealarm
echo "+60" > /sys/class/rtc/rtc0/wakealarm
echo mem > /sys/power/state
操作を受け付けなくなります。60秒後——SSH が復帰しました!
dmesg を確認すると、PM: Entering mem sleep のログが出ています。
CPU はちゃんとサスペンドしていました。完璧です。
これで解決!!!と思ったんですけど、
サスペンドしているのに電池が減る
手動 RTC サスペンドに切り替えたスクリプトでテストしました。
Before: 90% 1280 mAh
After: 87% 1241 mAh (1時間後)
1時間で 39 mAh。CPU は完全にサスペンドしているのに。
WiFi チップ(ath6kl_sdio)が怪しかったんですよね。
ドライバを rmmod でアンロードすれば完全に電源を切れる——と思ったんですが、SSH は WiFi 経由で接続しています。
rmmod ath6kl_sdio を実行した瞬間、ターミナルが沈黙しました。
(あ〜〜んやっちゃった……😭)
なので、Claude にお願いして、テストスクリプトを書いて SSH が切れても自動でテストが走るようにしました。
しかし、タッチスクリーンや USB ドライバまでアンロードすると消費は逆に悪化しました。
ドライバを外すとハードウェアが正常な省電力状態を維持できなくなるんですね、な〜るほど。
powerd の存在
ここで一つの事実に立ち返りました。
Kindle をアイドル状態で放置すると、1日の消費はたった 3.5%(約 2 mAh/時)でした。
自分たちの最良結果が 39 mAh/時。20倍の差があります。何が違うんだろう🤔
答えは powerd でした。Kindle の電源管理デーモンです。
WiFi の電源管理、タッチスクリーンの制御、PMIC の電圧レール最適化、そしてサスペンド。
全部 powerd がやっていたんですよね。
フレームワークを stop framework した時点で、この恩恵を全て捨てていたわけです。
手動サスペンド(echo mem > /sys/power/state)と powerd のサスペンドは全く別物でした。
powerd との共存を模索する
方針を180度転換しました。 フレームワークを止めない。powerd に省電力を全部任せて、スクリプトはダッシュボードの更新だけやる。
しかし、powerd のサスペンドから定期的に起きる方法が必要でした。
ここから長い試行錯誤が始まります。
cron → サスペンド中に停止
Kindle には crond が存在しました。5分ごとのテストジョブを仕掛けたんですが……
18:30:00 cron fired
18:35:00 cron fired
19:15:41 cron fired
スクリーンセーバーに入ってサスペンドすると、crond も一緒に凍結されてしまいます。
RTC アラーム + sleep ループ → 消費と精度のトレードオフ
RTC アラームで powerd のサスペンドから起きられるか試しました。
sleep ループで時刻をポーリングする方式です。
| sleep 間隔 | 消費 | 時間精度 |
|---|---|---|
| sleep 300 | ~0 mAh/時 | 6倍遅延(sleep が凍結される) |
| sleep 30 | ~13 mAh/時 | 2倍遅延 |
| sleep 1 | ~25 mAh/時 | 1.2倍遅延 |
sleep が短いほどポーリングで電力を食って、長いとサスペンド中にタイマーが止まってタイミングがズレる……
トレードオフですね。
lipc-wait-event → 消費は完璧、だが起きない
lipc-wait-event で powerd のイベントを待つ方式を試しました。
CPU 負荷ゼロでイベント待ちできます。
Before: 37% 527 mAh
After: 28% 508 mAh (18時間後)
約 1 mAh/時。 アイドル時と同等の消費。完璧でした。
しかし、RTC アラームを 1 時間に設定したのに 18 時間眠り続けました。
powerd がサスペンド時に RTC アラームを上書きしているんですね。
powerd の RTC メカニズムを探る
powerd の設定ファイルを探りました。(もちろんこれも Claude と一緒に)
/etc/kdb/system/daemon/powerd/SYS_RTC_WAKEUP
→ /sys/devices/platform/imx-i2c.0/i2c-0/0-003c/max77696-rtc.0/rtc_delta_alarm
powerd は標準の /sys/class/rtc/rtc0/wakealarm ではなく、PMIC 固有のインターフェースを使っていました。
色々試してみたんですが……どれも効きませんでした。 毎回、自分で電源ボタンを押して起こしていました。悲しい;;
結論:電源ボタンでいい
powerd のサスペンドは完璧な省電力を実現するんですが、外部から起こす手段がない。 手動サスペンドなら起こせるんですが、省電力が 20 倍劣る。
ここで発想を変えました。
天気ダッシュボードって、常に最新である必要ってまぁぶっちゃけ全然ないんですよね。
気になったときに確認できればそれでいいんです。
お天気アプリを日がな1日見ることはない。そうでしょう?
使っていない間は天上図書館司書の逢魔牙ワルトくんが顔を出してくれます。それでいいじゃない。
最終的なスクリプトはこうなりました。
フレームワークに一切手を出さない
lipc-wait-eventでoutOfScreenSaver(電源ボタンによるスリープ解除)を待つ起きたら WiFi ON → ダッシュボード取得 → 画面更新 → WiFi OFF
あとは powerd に全て任せる
main_loop() {
while true; do
lipc-wait-event com.lab126.powerd outOfScreenSaver
log "Screen woke up, updating dashboard"
log_battery_stats
refresh_dashboard
log "Update complete, waiting for next wake"
done
}
バッテリー消費は powerd のアイドル管理そのまま、約 1〜2 mAh/時。 1日2〜3回の更新で2〜3週間持つ計算です。
ステータスバーがダッシュボード画像と干渉する問題は、サーバー側の画像生成でマージンを十数ピクセルずらして対応しました。
学んだこと
powerd は神。 Kindle の省電力は powerd が PMIC を通じて電圧レールを最適化することで実現されているんですね。手動サスペンドはこの最適化をスキップするため、消費電力に 20〜40 倍の差が出ます。
フレームワークを止めるな。
stop frameworkはバックグラウンドプロセスを倒す(やさしい表現)んですが、同時に powerd の省電力管理も失われてしまいます。トータルでは大幅に損をします。sleep は CPU を止めない。
sleepコマンドはプロセスを待機させるだけで、CPU はアクティブなままなんですよね。特に短い間隔のループは致命的です。ドライバの rmmod は諸刃の剣。 ドライバを外すとハードウェアが正常な省電力ステートに入れなくなって、消費が逆に増えることがあります。
要件を見直せ。 「3時間ごとの自動更新」という要件を「必要なときに手動更新」に変えたことで、全ての技術的問題が解消しました。最も難しい技術的課題の解決策が、技術ではなく発想の転換だったというのは、よくある話かもしれませんね。
それでは、今回はこのへんで。