電源が低電圧状態かどうかを把握する方法と、その電圧状態の変化を検知する方法、また状態変化をトリガーにして何らかのコマンドを実行する手段について。
- 検証および動作確認に使用したデバイス・OS
- Raspberry Pi 5 / Ubuntu 24.04 LTS
- Raspberry Pi 3 Model B+ / Ubuntu Server 22.04 LTS
- Raspberry Pi Zero 2 W / Ubuntu Server 24.04 LTS
電源の電圧状態を把握する
電源が低電圧状態(undervoltage)かどうかを把握する方法について。
sysfs
sysfs(/sys
)のファイルを参照することで、電源の電圧状態を把握できる。 ファイル/sys/class/hwmon/hwmon<n>/in0_lcrit_alarm
の内容が0
なら正常、1
なら低電圧状態を表す。
$cat /sys/class/hwmon/hwmon1/in0_lcrit_alarm 0 $cat /sys/class/hwmon/hwmon1/in0_lcrit_alarm 1
このファイルは、デバイスの種類によってhwmon0
あるいはhwmon1
となるかが異なる模様。 (詳細未調査)
sensorsコマンド(lm_sensors)
sensorsコマンドを使用することで電圧状態を把握することもできる。 低電圧状態の場合、ALARMと表示される。
$sensors rpi_volt-isa-0000 rpi_volt-isa-0000 Adapter: ISA adapter in0: N/A $sensors rpi_volt-isa-0000 rpi_volt-isa-0000 Adapter: ISA adapter in0: N/A ALARM (LCRIT)
sensorsコマンドを引数なしで実行すれば、表示できるすべてのセンサーの情報が表示される。
syslog
/var/log/syslog
には電圧状態の変化が記録される。 低電圧状態になった時点ではUndervoltage detected!、低電圧状態から平常状態に復帰した時点ではVoltage normalisedというメッセージが出力される。
Jul 2 20:51:51 rpi kernel: [35941.910398] hwmon hwmon1: Undervoltage detected!
︙
︙
Jul 2 20:51:53 rpi kernel: [35943.926437] hwmon hwmon1: Voltage normalised
︙
︙
normalise
はイギリス英語で一般的なスペル、normalize
はアメリカ英語で一般的なスペルとのこと。
電圧状態の変化を検知する
電源の低電圧状態(undervoltage)への遷移および復帰の状態変化を検知する方法について。
udevイベント (検知できない)
実際にはこの方法では検知できないが、udevイベントを捕捉して検知を試みる手段について。 今後の参考として記録しておく。
まず、sysfsのファイルパスをもとに、電圧センサのDEVPATH
を取得する。
$udevadm info --path=/sys/class/hwmon/hwmon1 --query=path
/devices/platform/soc/soc:firmware/raspberrypi-hwmon/hwmon/hwmon1
次に、電圧センサの状態変化イベントを捕捉するためのudevルールファイルを/etc/udev/rules.d/
に作成する。
# hwmonサブシステムの、DEVPATHに該当するデバイスについて、属性in0_lcrit_alarmが0になった(change)ら、RUNで指定したコマンドを実行する
ACTION=="change", SUBSYSTEM=="hwmon", DEVPATH=="/devices/platform/soc/soc:firmware/raspberrypi-hwmon/hwmon/hwmon1", ATTR{in0_lcrit_alarm}=="0", RUN+="/usr/bin/logger -t power-voltage -p user.info 'normalised'"
# hwmonサブシステムの、DEVPATHに該当するデバイスについて、属性in0_lcrit_alarmが1になった(change)ら、RUNで指定したコマンドを実行する
ACTION=="change", SUBSYSTEM=="hwmon", DEVPATH=="/devices/platform/soc/soc:firmware/raspberrypi-hwmon/hwmon/hwmon1", ATTR{in0_lcrit_alarm}=="1", RUN+="/usr/bin/logger -t power-voltage -p user.info 'undervoltage'"
# hwmonサブシステムの、DEVPATHに該当するデバイスについて、変化があった(change)ら、RUNで指定したコマンドを実行する
ACTION=="change", SUBSYSTEM=="hwmon", DEVPATH=="/devices/platform/soc/soc:firmware/raspberrypi-hwmon/hwmon/hwmon1", RUN+="/usr/bin/logger -t power-voltage -p user.info 'voltage alerm changed'"
作成したudevルールファイルをアクティブにするために、ルールをリロードする。
$sudo udevadm control --reload-rules
電圧センサの状態変化に応じてhwmonサブシステムからchange
イベントが起これば上記のルールがトリガーされるはずだが、実際には動作しない。
udevadm monitor
コマンドでイベントをモニタリングしても、電圧センサの状態変化に起因するイベントが発生しないので、したがって上記ルールもトリガーされることがない。 vcgencmd get_throttled
で取得できるスロットル状態についても同様で、udevのイベントとしては捕捉できない。
sysfsファイルの変化を監視する
sysfsファイルの変化を監視することによって、電圧状態の変化を検知し、それをトリガーにしてコマンドを実行する方法について。
ここではファイルの内容変化を検知する方法として、inotifywait
コマンドを使用する。 inotifywait
コマンドを使用するには、inotify-tools
をインストールする必要がある。
$sudo apt install inotify-tools
inotifywait
コマンドに引数--event modify
を指定すれば、ファイル内容に変化があるまで待機することができる。
$inotifywait --event modify /sys/class/hwmon/hwmon1/in0_lcrit_alarm
Setting up watches.
Watches established.
ファイル内容に変化があると、次のように出力してコマンドは終了する。
/sys/class/hwmon/hwmon1/in0_lcrit_alarm MODIFY
例として、電圧状態の変化が起こるたびにメッセージを表示するスクリプトを書くと次のようになる。
#!/bin/bash
SYSFS_UNDERVOLTAGE=/sys/class/hwmon/hwmon1/in0_lcrit_alarm
while true
do
# ファイルSYSFS_UNDERVOLTAGEの内容に"1"が含まれるかどうか
grep -q 1 ${SYSFS_UNDERVOLTAGE}
# "1"が含まれていれば終了コード0、そうでなければそれ以外の終了コードとなる
undervoltage=$?
if [ $undervoltage -eq 0 ]
then
# 低電圧状態に遷移した
echo "`date --iso-8601=seconds`: undervoltage"
else
# 平常状態に遷移した
echo "`date --iso-8601=seconds`: normalised"
fi
# ファイルの内容が変化するまで待機する
inotifywait -qq --event modify ${SYSFS_UNDERVOLTAGE}
done
2024-07-02T22:06:20+09:00: normalised 2024-07-02T22:06:37+09:00: undervoltage 2024-07-02T22:06:41+09:00: normalised 2024-07-02T22:08:21+09:00: undervoltage 2024-07-02T22:08:23+09:00: normalised
以下は、このスクリプトをシステム起動時からバックグラウンドで動作させるために、systemdユニットとして動作させる方法について。
上記スクリプトを起動するsystemdユニットファイルを/etc/systemd/system/
に作成する。
[Unit]
Description=Undervoltage alert
[Service]
ExecStart=/path/to/alert-undervoltage.sh
User=pi
Type=simple
Restart=always
RestartSec=2s
[Install]
WantedBy=multi-user.target
このユニットを有効にして、起動する。 以降は、再起動した後も自動的に起動されるようになる。
$sudo systemctl daemon-reload $sudo systemctl enable undervoltage-alert $sudo service undervoltage-alert start $service undervoltage-alert status