pigpioがCPUを浪費している

こんにちは。
いよいよ夏本番といった暑さですね。
夏といえばラヂエーター！
ピンク色の車屋さんが気になる季節ですが、ラズパイユーザーとしては木苺が腐らないようにしっかりと冷やしてあげたいところ。
私は`pigpio`の`hardware_PWM`を用いてCPUファンの回転数制御をしています。

常時稼働させているので極力静かに、そしてエアバンドを聞いているので高周波ノイズを少なく制御するように心がけております。
こちらの制御については追々記事を書くつもりですが、本日は制御に使用している**`pigpio`のデーモン（`pigpiod`）がCPUを常時5~7%使い続けている問題**に関してです。

#pigpioの構成
`pigpio`はpythonとC言語でGPIOを操作するライブラリの決定版的存在のようです。
[Raspberry PiのGPIO制御の決定版 pigpio を試す](https://karaage.hatenadiary.jp/entry/2017/02/10/073000)

実際使いやすく、初心者の私でも簡単に実装することができました。
`pigpio`は`pigpiod`というデーモンがバックグラウンドで起動してGPIOへの指示を処理してくれます。
ソケットで待ち受けてうんぬんかんぬんと難しいことをしてくれているようですが、そんなことは意識せず関数を呼び出すだけでGPIOを操作できます。
また、指示を与えるだけでなく常にGPIOの入力電圧などをモニターしていて、その変化をトリガーとすることもできるようです。

#pigpiodの憂鬱
さて、こちらの`pigpiod`ですがCPU使用率が常時5~7%と高止まりになっており、Google先生にお尋ねしたところ事例によっては15%食うこともあるようです。

原因について簡単に書かせていただきますと、`pigpiod`は前述の「GPIOの入力をトリガーにする機能」のため常にGPIOの状態をサンプリングしており、そちらの処理がCPUを消費しているということのようです。
（ソフトウェアPWMもこちらのサンプリングに基づいて行われていると思われます。）
サンプリングはデフォルトで5μsという結構早いレートで行われており、CPUを食うのも納得なのですが私が使用しているのは`hardware_PWM`関数のみ、感度ビンビンのサンプリングなんて必要ありません。

#エンドレス∞
~~見出しに意味なんてありません。~~
CPUを冷やすための制御がCPUを浪費するなんて悪循環は止めてしまいたい。
喫茶店で言うべきセリフを探すため、ドキュメントを漁りました。
[pigpio Daemon](http://abyz.me.uk/rpi/pigpio/pigpiod.html)

簡単なことですが`hardware_PWM`関数ではサンプリングは必要がないのでサンプリングを止めてしまえばよいのです。
夏休みは宿題をする、当たり前のことですね。

#解決策
`pigpiod`は`systemd`によってデーモンとして起動しています。
デフォルトではきっと以下のようになっているはず。

```bash:/lib/systemd/system/pigpiod.service
[Unit]
Description=Daemon required to control GPIO pins via pigpio
[Service]
ExecStart=/usr/bin/pigpiod -l
ExecStop=/bin/systemctl kill pigpiod
Type=forking
[Install]
WantedBy=multi-user.target
```

~~起動時に実行される`ExecStart`の部分を編集して`pigpiod`のサンプリング機能を無効にするオプション`-m`を付加します。~~

# 2024/10/31追記
編集方法を改めました。

:::note info
ユニットファイルを直接編集するとしていましたが、直接編集してはいけません！
アップデートなどの際にリセットされてしまい困ることになります。
幸い、便利な上書き方法があるのでそちらで編集しましょう。
:::

```bash
sudo systemctl edit pigpiod.service
```

このコマンドで、オーバーライドファイルの作成と、編集が一気にできます。
そして`daemon-reload`までやってくれる優れものです。
立ち上がった編集画面に下記を入力してください。

```bash
[Service]
ExecStart=
ExecStart=/usr/bin/pigpiod -l -m
```

引っ掛かりポイントとしては、`ExecStart=`を入れていったんリセットしないと、重複と言われてエラーになることです。
編集が完了したら`sudo systemctl restart pigpiod`で再起動して作業は完了です。

`systemctl status pigpiod`から起動コマンドに`-m`がついていることを確認しましょう。

#### 更新前の編集方法
[2024/10/31追記](https://qiita.com/drafts/b577b03fe52d273b328f/edit#20241031%E8%BF%BD%E8%A8%98)を参照してください。

```bash:/lib/systemd/system/pigpiod.service
[Unit]
Description=Daemon required to control GPIO pins via pigpio
[Service]
ExecStart=/usr/bin/pigpiod -l -m #Disable alerts
ExecStop=/bin/systemctl kill pigpiod
Type=forking
[Install]
WantedBy=multi-user.target
```

~~以下を実行してサービスを再読み込み→`pigpiod`を再起動~~

1. ~~`sudo systemctl daemon-reload`~~
2. ~~`sudo systemctl restart pigpiod`~~

***

こちらの操作で`pigpiod`のCPU使用率はほぼゼロになりました。
もちろん`hardware_PWM`は問題なく使えてます。

ちなみに`ExecStart`にデフォルトでついていた`-l`オプションは`pigpiod`のリモート待ち受けを無効化するオプションです。
デフォルトでは8888ポートでGPIOへの操作を待ち受けるようになっているそうですが、なんかセキュリティ的にも気持ち悪いですしローカルだけの使用なのでつけたままにしておきます。

#Samplingを無効化したくない人
ソフトウェアPWMを使用している人や入力トリガーを使用している人向けの代替案です。
以下のように記述することでサンプリングの回数を抑えることができますので、当方の環境では7%から5%ほどに変化し、やや抑えめとなりました。
`ExecStart=/usr/bin/pigpiod -l -s 10`
こちらの設定では10μsとなっています。
どうやらこの値が大きすぎると高い周波数におけるソフトウェアPWMの精度に影響するようですので設定する際はお気を付けください。
設定可能な値は`1, 2, 4, 5, 8, or 10 microseconds`とのことです。

#まとめ
大変便利な`pigpio`ですが若干重いのが玉に瑕でした。
簡単に改善できたのですが、日本語で情報がなかったのでまとめさせていただきました。
皆様のお役に立てば幸いです。