

久しぶりの投稿になりますが、今回はサーバーのCPU負荷をフルカラーLEDで可視化をすることにチャレンジします。
動機ですが、LEDテープが余っておりサーバーのフロントパネルに仕込んだところ、いい感じの見た目になり、「CPU負荷に応じて色が変わったら面白いのでは」と思ったからです。
ハードウェアの製作#
まずハードウェアを製作します。と言ってもLEDテープをフロントパネルの裏に取り付け、Arduino Pro Miniをはんだ付けするだけです。使用したLEDテープはAdafruitのNeoPixel系のものですが、正確な型番は不明です。マルツで購入しました。
回路図は以下のようにシンプルです。
ソフトウェアの製作#
今回のプロジェクトではプログラムは2つ必要です。まず、サーバー上でCPU負荷の情報を読み取りシリアル通信で送信するPythonプログラムと、その情報を受け取りLEDを制御するArduino用のプログラムです。
またPython側では日本標準時を読み取り、夜間にLEDを消灯する機能も搭載します。
まずはPythonのコードを以下に示します。
import serial
import time
import psutil
import datetime
COM="/dev/ttyUSB0"
bitRate=15200
ser = serial.Serial(COM, bitRate)
time.sleep(2)
while 1:
cpu = int(psutil.cpu_percent(interval=0.1))
data="B"+str(cpu)
ser.write(str.encode(data))
while datetime.time(1, 0, 0)<datetime.datetime.now().time() and datetime.datetime.now().time()<datetime.time(7, 0, 0):
ser.write(str.encode("A0"))
time.sleep(10)
time.sleep(2)
ser.close()pythonPythonなのでLinuxでもWindowsでもMacでも動作すると思いますが、6行目のシリアルデバイスのポートは各自の環境に合わせて変更する必要があります。(Windowsなら「COM6」みたいな感じです)
プログラムとしてはpsutil.cpu_percentでCPU負荷を取得し、昼間はB+{CPU負荷率}の形式で送信し、夜間(午前1時〜7時)はA0を送信します。Arduino側では一文字目がAかBかで点灯・消灯を判断し、以降の文字でCPU負荷を受け取ります。
昼間はCPU負荷に俊敏に反応できるように約2秒おきに送信し、夜間は消灯するだけなので10秒おきに送信しています。夜間は一回だけの送信でもよいのですが、確実に消灯する為と、1時以降にプログラムを実行し始めても消灯するように10秒間隔で消灯信号を送り続けています。
次にArduinoのコードも示します。
#include <Adafruit_NeoPixel.h>
#include <string.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
#define PIN 9
#define NUMPIXELS 30 //使用するLEDの数
int data=0;
int val=0;
String command;
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup() {
pixels.begin();
Serial.begin(15200);
}
void loop() {
if(Serial.available()){
delay(10);
command = Serial.readString();
}
data = command.substring(1).toInt();
if(command.charAt(0)=='A'){pixels.setBrightness(0);}
if(command.charAt(0)=='B'){pixels.setBrightness(255);}
if(data!=val){
if(data>val){val++;}
if(data<val){val--;}
}
if(val>=100){val=100;}
if(val<=0){val=0;}
int r=val*255/100;
int b=255-r;
pixels.clear();
pixels.fill(pixels.Color(r, 0, b), 0, NUMPIXELS);
pixels.show();
delay(10);
}cppArduino側ではCPU負荷に応じて青色から赤色のグラデーションで負荷率を表現します。また夜間信号を受け取った際は輝度をゼロに設定します。
if(data!=val){
if(data>val){val++;}
if(data<val){val--;}
}cppこの部分ではCPU負荷変化に敏感に反応し過ぎて色が飛び飛びにならないように、受け取った値を直接表示するのではなく、受け取った値を目標にして値を1ずつ変化させています。これにより色の変化が滑らかになります。
ちなみにPythonのCPU負荷取得のインターバルやsleep時間、Arduinoのdelay時間を変更するとLEDが激しく明滅したり、色が変わらなくなることがあります。たぶんArduinoのシリアル割り込みを使ってないのが原因だと思いますが、もう面倒なのでこのままにします。いいアイデアがある方はコメントで教えていただけると幸いです。
プログラムの実行#
ではプログラムを動かしてみます。
まずUSBシリアル変換とサーバーマシンをUSBケーブルで接続します。
私の場合はVMware ESXiで仮想化しているので管理画面からUSBデバイスをマイクラサーバーに割り当てます。
ホストの設定画面上部の「その他のデバイスの追加」で新規USBデバイスを追加しプルダウンからUSBシリアル変換デバイスを選択します。
これで仮想マシンに目に見えないUSBコネクタを差し込んだことになります。
サーバーとのSSHを開きPython3をインストールし、pipでserialライブラリ等必要なものをインストールします。
以下のコマンドを実行し割り当てられたシリアルポートを確認します。
root@server3:/$ ls -l /dev/serial/by-id/
total 0
lrwxrwxrwx 1 root root 13 Sep 19 20:41 usb-1a86_USB2.0-Serial-if00-port0 -> ../../ttyUSB0shこれによりシリアルデバイスが/dev/ttyUSB0に割り当てられたことがわかります。これをもとにPythonコードの6行目を適宜書き換えます。
Windowsの場合はデバイスマネージャーで確認できます。
あとはPythonコードを実行し、負荷を掛ければLEDの色が変化するはずです。自分の環境ではCronで自動的に走るようにしてます。
完成前にCineBenchでWindowsに負荷を掛けたときの動画も載せておきます。
現在はマイクラサーバーの負荷表示に使っていますが、夜間はきちんと消灯し問題なく動作しています。
様々なデバイスで動作すると思いますので、面白そうだと思った方はぜひ作ってみてください。
ここまで読んでいただきありがとうございました。