久しぶりの投稿になりますが、今回はサーバーのCPU負荷をフルカラーLEDで可視化をすることにチャレンジします。

動機ですが、LEDテープが余っておりサーバーのフロントパネルに仕込んだところ、いい感じの見た目になり、「CPU負荷に応じて色が変わったら面白いのでは」と思ったからです。

青色に光らせたらいい感じに
(クリックで拡大)
アニメ「ソードアートオンライン」に
登場するサーバーっぽい(元ネタはIBM Z13)

ハードウェアの製作

まずハードウェアを製作します。と言っても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()

Pythonなので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);
}

Arduino側ではCPU負荷に応じて青色から赤色のグラデーションで負荷率を表現します。また夜間信号を受け取った際は輝度をゼロに設定します。

if(data!=val){
    if(data>val){val++;}
    if(data<val){val--;} 
    }

この部分では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 -> ../../ttyUSB0

これによりシリアルデバイスが”/dev/ttyUSB0″に割り当てられたことがわかります。これをもとにPythonコードの6行目を適宜書き換えます。

Windowsの場合はデバイスマネージャーで確認できます。

あとはPythonコードを実行し、負荷を掛ければLEDの色が変化するはずです。自分の環境ではCronで自動的に走るようにしてます。

完成前にCineBenchでWindowsに負荷を掛けたときの動画も載せておきます。

現在はマイクラサーバーの負荷表示に使っていますが、夜間はきちんと消灯し問題なく動作しています。

様々なデバイスで動作すると思いますので、面白そうだと思った方はぜひ作ってみてください。

ここまで読んでいただきありがとうございました。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です


次の投稿

ジャンクスピーカーを改造してハイレゾ対応にした話

日 2月 19 , 2023
今回行った改造 今回はかなり久しぶりの投稿になりま […]