tl;dr
- アナログ電圧計は、PWMでお手軽にアナログメーターを作ることができるよ。その記事を見つけて、非常に惹かれたよ。
- ESP32-C3をWebサーバとして、送られてきた値をPWMに流すファームウェアを作ったよ。
- GoでCPU使用率をHTTPで送るプログラムを作って、使ってみたよ。ヘッドレスLinuxPCとして使っているOrangePi5+のCPU使用率を表示できる様にしたよ。
- LEDとかで光らせても良かったけど、自分には不要と感じたので、CPUとメモリ使用率を表示して完成としたよ。
アナログ電圧計をPCのCPUモニタとして使う記事
Hackadayでアナログ電圧計をCPUモニタとして使う記事を見つけました(ina_aniさんの紹介で知りました)。
アナログメーターを使ったCPUメーター / “Keep Tabs On PC Use With Custom Analog Voltmeter” (1 user) https://t.co/6WVcVGbeih
— ina_ani@4歳児のパパ (@ina_ani) May 4, 2024
これを見て、非常に簡単で面白いアイディアだと思いました。実際に針が動くのは面白く、加えてアナログ電圧計であればPWMがあれば簡単に制御できます。
アナログ電圧計を表示器にするの、お手軽で動きに面白みがあって、良いなー。Macから操作してるOrangePi5+のCPU表示させたい。https://t.co/vtOlXEfw8u
— 74th (@74th) May 4, 2024
自分はMacを使いながら、ほとんどの開発はVS CodeのリモートSSH機能を使って、OrangePi5+上のUbuntuで開発しています。その時、CPUやメモリ使用率はターミナル上で表示させることはできますが、結構煩雑です。ですので、これを表示できるようにしたいという需要がありました。
アーキテクチャ
以下のような仕組みにするように考えました。
- PCとマイコンの間はWiFiを使い、HTTP通信で送るようにする
- ESP32-C3を使い、WebServerを立てる。受け取った値を、PWMとして出力して、表示する
- PC上で、CPU使用率をHTTPで送るプログラムを動かす
PCとマイコン間は、USBで接続しても良いのですが、設置の簡単さからWiFiを使うことにしました。ESP32であれば、簡単にArduinoでWebServerを立てることができます。REST APIとして作ることにしました。以下のように送ることになります。なお、自宅内ネットワークのため、認証等はありません。
POST http://192.168.1.105/cpu {"value": 50}
私はIoT機器の電子工作にはESP32-C3を良く使っています。ESP32-C3-WROOM-02は秋月で310円と、非常に安価に購入できます。今回もESP32-C3を使うようにしました。
今回は簡単な回路であり、加えて複数個作る予定もないため、PCBを制作せず、ユニバーサル基板で実装することにしました。回路図は以下に置いてあります。
KiCanvas 回路図
アナログ電圧計を用意する
アナログ電圧計には、表示上限の電圧があり、マイコンのIOの3.3Vで操作する都合、3Vの電圧計を使うことにしました。1個400円ほどで購入できました。
アナログ電圧計 長方形アナログボルトパネルメーターゲージ 10 スタイルボルト T - AliExpress
ESP32-C3で、出力するPWMを受け付けるWebServerを作る
ESP32-C3でWebServerを作るのは、Arduinoを使えば簡単です。加えてJSONを処理できるArduinoJsonを使えば、JSONの読み取りも比較的簡単にできます。
// src/main.cpp #include <Arduino.h> #include <WiFi.h> #include <WebServer.h> #include <ArduinoJson.h> #include <ssid.h> const char *ssid = WIFI_SSID; const char *pass = WIFI_PASSWORD; WebServer server(80); void handleCPU() { const char *path = "cpu"; if (server.method() != HTTP_POST) { server.send(405, "application/json", "{\"message\": \"Method Not Allowed.\"}"); return; } Serial.printf("POST /%s\r\n", path); String requestBody = server.arg("plain"); Serial.printf("request body: %s\r\n", requestBody.c_str()); DynamicJsonDocument doc(1024); DeserializationError error = deserializeJson(doc, requestBody); if (error) { Serial.println("failed to deserialize"); Serial.printf("POST /%s 400\r\n", path); server.send(400, "application/json", "{\"message\": \"Bad Request.\"}"); return; } int32_t value = doc["value"].as<int32_t>(); if (value < 0 || 100 < value) { Serial.printf("POST /%s 400\r\n", path); server.send(400, "application/json", "{\"message\": \"Bad Request. Unexpected value.\"}"); return; } Serial.printf("input value: %d\n", value); // ここにPWM制御を記述する Serial.printf("POST /%s 200\r\n", path); server.send(200, "text/json", "{\"message\": \"Success.\"}"); } void setup() { Serial.begin(9600); // WiFiの接続 WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); } Serial.printf("IP:%s \r\n", WiFi.localIP()); // Webサーバの開始 server.on("/", handleRoot); server.on("/cpu", HTTP_POST, handleCPU); server.onNotFound(handleNotFound); server.begin(); } void loop() { server.handleClient(); }
なお、このssid.hはWiFiのSSIDを記述したファイルです。いつもこのファイルを作っておき、.gitignoreに加えています。ArduinoIDEではなく、PlatformIOを使うと、Arduino開発であっても、普通のC++の開発として利用できます。
// src/ssid.h #define WIFI_SSID "__WIFI_SSID__" #define WIFI_PASSWORD "__WIFI_PASSWORD__"
PWMも、以下のような形でできます。
// src/main.cpp #define METER_PIN_1 GPIO_NUM_0 #define METER_PIN_2 GPIO_NUM_1 #define METER_PWM_CH_1 0 #define METER_PWM_CH_2 1 void setup() { // 略 // PWMの設定 pinMode(METER_PIN_1, OUTPUT); ledcSetup(METER_PWM_CH_1, 1000, 8); ledcAttachPin(METER_PIN_1, METER_PWM_CH_1); ledcWrite(METER_PWM_CH_1, 0); pinMode(METER_PIN_2, OUTPUT); ledcSetup(METER_PWM_CH_2, 1000, 8); ledcAttachPin(METER_PIN_2, METER_PWM_CH_2); ledcWrite(METER_PWM_CH_2, 0); } void handleCPU() { // 略 // 3.3Vを上限とすると、若干低くなったため、3.1Vとして計算 float_t analogValue = 256 * 3 / 3.1 * value / 100; uint16_t pwmValue = (uint16_t)analogValue; Serial.printf("pwm value: %d\n", pwmValue); ledcWrite(pwm_ch, analogValue); // 略 }
ソースコードは以下に置いてあります。
CPU使用率を送るプログラム
CPU使用率を送るプログラムはGoを使って記述しました。
マルチプラットフォームに対応したCPU・メモリ使用率を取得する便利なライブラリがあり、こちらを利用しました。
ソースコードは以下に置いてあります。
これをUbuntuのsystemd上で自動起動するようにしました。
[Unit] Description=CPU Usage Sender Service After=network.target [Service] Type=simple User=nnyn Group=nnyn ExecStart=/usr/local/bin/send-cpu-usage -h http://192.168.1.105 -i 1000ms ExecStop=/bin/kill ${MAINPID} Restart=always [Install] WantedBy=multi-user.target
sudo cp ./send-cpu-usage /usr/local/bin/ sudo cp systemd/send-cpu-usage.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable send-cpu-usage.service sudo systemctl start send-cpu-usage.service sudo systemctl status send-cpu-usage.service
とりあえずブレッドボードで運用
まずはブレッドボードで実装して運用することにしました。使っていると、欲しい機能が見えてくると思ったためです。元の記事にあるように光らせたらカッコ良いなど、やりたくなるのではと思っていました。
しかし、2週間運用してみて、常に視界に入るものなので、光る必要はないなという結論に至りました。3Dプリンタでケースを作ることも考えたのですが、モニタの上にちょこんとアナログメーターがあるだけで良いと思い、ケースも作らないことにしました。ユニバーサル基板用の簡易ケースだけ作るつもりです。
運用してみた感想は、VS Codeにつなぎに行くとき、結構CPU使うなーとか、Python実装している時保存する度にmypy、black、pylintが動くのかキュキュっとCPUが触れるので、わりと実装時もCPU使うなーとか、そのくらいでした。
ユニバーサル基板に移す
ユニバーサル基板で実装し、稼働させました。ツイート中の動画で、ESP32のファームウェアをビルドをしてCPUが触れるのを確認できます。
アナログ電圧計を使ったCPU使用率表示の作成、ブレッドボードで2週間ほど運用して、針さえ動けばLEDとか要らないという結論になった。メモリの表示を追加してユニバーサル基板に移して完成とした!
— 74th (@74th) June 15, 2024
これで机の下でサーバになっているOrangePi5+の様子がよくわかる。 https://t.co/jBATt3m5cU pic.twitter.com/dqxLjSSevx
まとめ
アナログでカッコ良いCPU・メモリ使用率モニタを、ESP32-C3を使って作ることができました。 開発サーバとして使っているヘッドレスPCであっても、CPU使用率が常に見えるので良い感じです。
アナログ電圧計はPWMで簡単に扱うことができるので、今後もおしゃれにしたいときに組み込んでいきたいと思います。