電源が低電圧状態かどうかを把握する方法と、その電圧状態の変化を検知する方法、また状態変化をトリガーにして何らかのコマンドを実行する手段について。

電源の電圧状態を把握する

電源が低電圧状態(undervoltage)かどうかを把握する方法について。

sysfs

sysfs(/sys)のファイルを参照することで、電源の電圧状態を把握できる。 ファイル/sys/class/hwmon/hwmon<n>/in0_lcrit_alarmの内容が0なら正常、1なら低電圧状態を表す。

sysfsを参照して電源電圧の状態を把握する
$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コマンドを使用して電源電圧の状態を把握する
$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というメッセージが出力される。

syslogに記録される電圧状態の変化
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コマンドを使用して電圧センサのDEVPATHを取得する
$udevadm info --path=/sys/class/hwmon/hwmon1 --query=path
/devices/platform/soc/soc:firmware/raspberrypi-hwmon/hwmon/hwmon1

次に、電圧センサの状態変化イベントを捕捉するためのudevルールファイルを/etc/udev/rules.d/に作成する。

/etc/udev/rules.d/90-power-voltage-alerm.rules
# 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ルールファイルをアクティブにするために、ルールをリロードする。

udevルールをリロードする
$sudo udevadm control --reload-rules

電圧センサの状態変化に応じてhwmonサブシステムからchangeイベントが起これば上記のルールがトリガーされるはずだが、実際には動作しない。

udevadm monitorコマンドでイベントをモニタリングしても、電圧センサの状態変化に起因するイベントが発生しないので、したがって上記ルールもトリガーされることがない。 vcgencmd get_throttledで取得できるスロットル状態についても同様で、udevのイベントとしては捕捉できない。

sysfsファイルの変化を監視する

sysfsファイルの変化を監視することによって、電圧状態の変化を検知し、それをトリガーにしてコマンドを実行する方法について。

ここではファイルの内容変化を検知する方法として、inotifywaitコマンドを使用する。 inotifywaitコマンドを使用するには、inotify-toolsをインストールする必要がある。

inotify-toolsをインストールする
$sudo apt install inotify-tools

inotifywaitコマンドに引数--event modifyを指定すれば、ファイル内容に変化があるまで待機することができる。

inotifywaitコマンドを使ってsysfsファイルの変化を監視する
$inotifywait --event modify /sys/class/hwmon/hwmon1/in0_lcrit_alarm
Setting up watches.
Watches established.

ファイル内容に変化があると、次のように出力してコマンドは終了する。

inotifywaitコマンドを使って監視しているファイルに変化があった場合
/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/に作成する。

/etc/systemd/system/undervoltage-alert.service
[Unit]
Description=Undervoltage alert

[Service]
ExecStart=/path/to/alert-undervoltage.sh
User=pi
Type=simple
Restart=always
RestartSec=2s

[Install]
WantedBy=multi-user.target

このユニットを有効にして、起動する。 以降は、再起動した後も自動的に起動されるようになる。

作成したsystemdユニットをリロード・有効にして、起動する
$sudo systemctl daemon-reload
$sudo systemctl enable undervoltage-alert
$sudo service undervoltage-alert start
$service undervoltage-alert status