4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ArduinoとC#でパソコンのCPU使用率を7SegLEDに表示してみた

Last updated at Posted at 2021-08-08

はじめに

今回は夏休みの自由研究と称して
7SegLEDにCPU使用率を表示してみたいと思います

FPS(主にApex)にはまっている自分がぱっと見でCPU使用率を把握して
ゲームでカクついているときに何が悪いのかをあたりを付けるという意味で作成しました

目次

  1. 実現したいこと
  2. 準備するもの
  3. 回路の設計
  4. C#側プログラミング
  5. Arduino側プログラミング
  6. 完成
  7. 参考にしたサイト

実現したいこと

7segLEDに現在のCPU使用率を1秒おきに転送し、表示すること
完成したものはこちらです

完成図.gif
CPU-Zで負荷をかけるとそれに応じてタスクマネージャと一緒に7SegLEDの表示が変わっていることがわかると思います

準備するもの

  1. Arduino Uno
  2. ブレッドボードおよびジャンパ線(回路試作のため)
  3. 7SegLEDと4.7kΩ炭素被膜抵抗
  4. ArduinoにつなげるUSBケーブル

ブレッドボードは回路の試作のためなので
いつかユニバーサル基盤に実装したいと思います

回路の設計

まずは表示を担う7SegLEDの回路を設計します
今回使う7SegLEDは秋月電子通商で買えるこちらにしました
I-03954.jpg
ダイナミック接続4桁赤色7セグメントLED表示器です

こいつのデータシートを見るとアノードコモンでダイナミック接続との記載があるので
アノード側の表示させたい桁に5Vをかけ
カソード側で表示する数字に対応する部分を出力する方式で行きます

回路図は
Arudino7SegLED.png
デジタル10番ピンからからデジタル13番ピンまでがアノードで5Vをかけること
デジタル2番ピンからデジタル9番ピンまでは表示したい部分をLOWにすることで表示、
HIGHにすることで非表示という制御になりますので4.7kΩでプルダウンしてあります

例えば
デジタル10番ピンがHIGHの時にデジタル3番ピン,4番ピンにLOW
残りの2,5,6,7,8,9番ピンにHIGHがかかっていると7SegLEDの一桁目に「1」が表示されます

回路図をもとにブレッドボードに配線を行っていきます
ArduinoHAisen.jpeg
ぐちゃぐちゃですがこんな感じ

C#側プログラム

C#(PC)側はCPUの使用率を取得し、シリアル経由でArduinoの文字列として送信する役割を持ちます

Arduinoとの通信はSerialPortで行います

シリアルポート探索
private void AnalyzeComPort()
{
    string[] ports = SerialPort.GetPortNames();
    foreach (string port in ports)
    {
        comboBox1.Items.Add(port);
    }
}

シリアルポートの利用可能なものを列挙して
選択式のコンボボックスに格納しています

シリアル通信開始/終了
private void button1_Click(object sender, EventArgs e)
{
    if (button1.Text == "Connect")
    {
        serialPort1.PortName = comboBox1.SelectedItem.ToString();   // 選択されたCOMをポート名に設定
        serialPort1.Open();	// ポートを開く
        button1.Text = "Disconnect";
    }
    else
    {
        serialPort1.Close();
        button1.Text = "Connect";
    }
    
}

ボタンが押されるとコンボボックスで選択されたCOMポート名を指定して
serialPort1.Open()で通信開始
serialPort1.Close()で切断しています

CPU使用率取得
public void getCPUUsege()
{
    var cpuUsage = new PerformanceCounter("Processor", "% Processor Time", "_Total");
    Thread.Sleep(1000);
    var firstCall = cpuUsage.NextValue();
    while(true)
    {
        Thread.Sleep(1000);
        CPUperStr = cpuUsage.NextValue().ToString();
        label3.Text = "Now:" + CPUperStr;
        if (button1.Text == "Disconnect")
        {
            serialPort1.Write(CPUperStr + "\0");
        }
    }
}

C#でCPU使用率をとるならPerformanceCounterでしょ!という安直な考えです…
ひたすらwhileで回してserialPort1.Write();で一秒おきに送信しています

SerialPort1.Write(CPUperStr + "\0");の最後になぜナル文字(\0)が入っているかは
Arudino側のプログラムでわかります

Arduino側プログラム

Arduino側はPCからCPU使用率の数字を文字列(100.0とか58.5とか)として送信されたものを
しかるべきLEDに表示する役割を持ちます

こちらの記事 arduinoで並列処理(ノンプリエンプティブ)を参考に
7segLEDの表示タスクと
USBのシリアル通信でCPU使用率の数字を受け取るタスクに分けて
並列処理として実装してます

CPUDisplay
# include <TimedAction.h>
String str = ""; //受信した文字列を格納する
double num = 0;
void LEDtask();
void Serialtask();

TimedAction ledAction = TimedAction(5,LEDtask); //LED表示タスクを5msのインターバルで実行する
TimedAction serialAction = TimedAction(30,Serialtask); //シリアル通信タスクを30msのインターバルで実行する

LED表示タスクは各桁を5msごとにダイナミック点灯させて制御しています
0-9までの表示は各関数に定義してどの桁でその表示を行うかを引数に渡すことでダイナミック点灯を実現しています

LEDTask
void LEDtask()
{
    void digitalWriteTo0(int);
    void digitalWriteTo1(int);
    void digitalWriteTo2(int);
    void digitalWriteTo3(int);
    void digitalWriteTo4(int);
    void digitalWriteTo5(int);
    void digitalWriteTo6(int);
    void digitalWriteTo7(int);
    void digitalWriteTo8(int);
    void digitalWriteTo9(int);
    
   num = str.toDouble();

   // 各桁に分解
   int numBef = num * 10;
   int num1 = numBef / 1000;
   int num2 = numBef % 1000 / 100;
   int num3 = numBef % 100 / 10;
   int num4 = numBef % 10 ;

  // 桁ごとに表示
   switch(num1){
     case 0:
       digitalWriteTo0(1);
     break;
     case 1:
       digitalWriteTo1(1);
     break;
     case 2:
       digitalWriteTo2(1);
     break;
     case 3:
     digitalWriteTo3(1);
     break;
     case 4:
     digitalWriteTo4(1);
     break;
    case 5:
      digitalWriteTo5(1);
    break;
    case 6:
      digitalWriteTo6(1);
    break;
    case 7:
      digitalWriteTo7(1);
    break;
    case 8:
      digitalWriteTo8(1);
    break;
    case 9:
      digitalWriteTo9(1);
    break;
   }
   delay(5);
   switch(num2){
    case 0:
      digitalWriteTo0(2);
    break;
    case 1:
      digitalWriteTo1(2);
    break;
    case 2:
      digitalWriteTo2(2);
    break;
    case 3:
      digitalWriteTo3(2);
    break;
    case 4:
      digitalWriteTo4(2);
    break;
    case 5:
      digitalWriteTo5(2);
    break;
    case 6:
      digitalWriteTo6(2);
    break;
    case 7:
      digitalWriteTo7(2);
    break;
    case 8:
      digitalWriteTo8(2);
    break;
    case 9:
      digitalWriteTo9(2);
    break;
   }
 /*
以下略
  */
}

シリアル通信タスクは少し試行錯誤しました

SerialTaskの第一弾
void Serialtask()
{
  if (Serial.available() > 0){
     str = Serial.readString();
  }
}

Serial.readString();でUSBシリアル通信で来た文字列を受信しているのですが
以下の様な動作になりました
試行錯誤1.gif
データが来てから表示されるまでに謎のラグがあります
このラグをどうにかしてなくしたい!!
とArduinoの言語リファレンスをよく読んでみると

Serial.readString()は、シリアルバッファから文字を読み、文字列に書き込む。この関数はタイムアウトすれば終了する

との記述を発見
この受信してからデータが表示されるまでのラグは
受信のタイムアウトをわざわざ待っていたということに気が付きました

その後リファレンスを見返すと

Serial.readStringUntil()は、シリアルバッファから文字を読み、文字列に書き込む。この関数は、終端文字を検出するかタイムアウトすれば終了する(Serial.setTimeout()参照)。

めちゃくちゃいい関数あんじゃん!!!

ということで以下のように変更しました

SerialTaskの改良版
void Serialtask()
{
  if (Serial.available() > 0){
     str = Serial.readStringUntil('\0');
  }
}

Serial.readString()をSerial.readStringUntil()に書き換え
終端文字としてナル文字を指定、C#側でナル文字を文字列の末尾につけて送信するようにしました

すると…
試行錯誤2.gif
ラグがなくなってすんなりと表示ができるようになりました

loop()
void loop() {
  // put your main code here, to run repeatedly:

  ledAction.check();
  serialAction.check();
}

冒頭で宣言したタスクをcheckすることで起動しています

digitalWriteTo1()
void digitalWriteTo1(int port){
  // アノードコモン側のどの桁に表示するかの選択
  switch (port){
    case 1:
      digitalWrite(9, HIGH);
      digitalWrite(10, HIGH);
      digitalWrite(11, LOW);
      digitalWrite(12, LOW);
      digitalWrite(13, LOW);
    break;
    case 2:
      digitalWrite(9, HIGH);
      digitalWrite(10, LOW);
      digitalWrite(11, HIGH);
      digitalWrite(12, LOW);
      digitalWrite(13, LOW);
    break;
    case 3:
      digitalWrite(9, LOW);
      digitalWrite(10, LOW);
      digitalWrite(11, LOW);
      digitalWrite(12, HIGH);
      digitalWrite(13, LOW);
    break;
    case 4:
      digitalWrite(9, HIGH);
      digitalWrite(10, LOW);
      digitalWrite(11, LOW);
      digitalWrite(12, LOW);
      digitalWrite(13, HIGH);
    break;
  }
  // 数字の表示
  digitalWrite(2, HIGH);
  digitalWrite(3, LOW);
  digitalWrite(4, LOW);
  digitalWrite(5, HIGH);
  digitalWrite(6, HIGH);
  digitalWrite(7, HIGH);
  digitalWrite(8, HIGH);
}

上記の様な感じで0-9までの表示を行っています

完成!!

完成しました
これを表示しながらゲームするなり
レンダリングするなりベンチマークとったりすると非常に面白いです

夏休みの自由研究(暇つぶし)としてやってみましたが
所要時間3時間程度で終わってしまったため
また何かネタを探してちょろちょろやっていきたいと思います

参考にしたサイト

とても参考になりました
ありがとうございました

4
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?