본문 바로가기
아두이노

아두이노로 PC 실시간 대시보드 만들기 (1편)

by 구루가 되고픈 2021. 5. 22.

PC를 사용하다보면 이 고가의 장비를 사 놓고 어느정도의 자원을 활용하는지 궁금할때가 있습니다.

그래서 아래 사진과 같은 대시보드를 직접 제작하였습니다.

 

시스템 모니터 작동모습

 

 

시스템을 모니터를 하는 방법은 주로 작업관리자를 통한 것입니다.

 

pc의 시스템 모니터

 

저는 언제부턴가 이러한 시스템 사용률을 모니터 할 수 있는 대시보드를 만들어보고 싶었습니다.

처음 구상했던것은 AIDA64를 활용하는 방법이었는데, 일단 추가 소형모니터를 준비해야하고 프로그램 구매가격이 년간 구독하는 형식이어서 비용이 지속적으로 발생하는 문제가 있어서 이번에 아두이노로 직접 구형하여 만들게 되었습니다.

 

 

 

먼저 이것을 구현하려면 PC에서 실시간 데이터를 아두이노로 넘겨주어야 합니다.

이게 가장 어려운 점인데요.

일반적으로 시스템 사용률을 모니터하는 프로그램은 여러가지가 있는데, 데이터를 외부로 출력하는 기능을 가진 프로그램은 없다는 것이 문제였습니다.

 

그래서 직접 만들어서 구현하게 되었습니다.

 

PC쪽 프로그램은 파이썬을 통해 제작하였습니다.

전 파이썬은 배워본적이 없는데, 6시간짜리 무료 동영상강의를 보니 아주 기초적인 것은 따라할수 있을것 같아 도전해 보게 되었습니다.

 

파이썬에는 시스템 사용률을 모니터할 수 있는 여러가지 모듈이 있는데 PSUTIL은 CPU, 메모리 등의 정보를, GPUINFO는 NVIDIA의 그래픽 카드인 경우 GPU정보를 모니터할 수 있습니다.

 

필요한 모듈 import

프로그램 코드는 간단합니다.

여러가지 시스템 정보를 하나의 문자열로 만들어서 시리얼 포트로 출력시키는게 전부입니다.

 

Serial 객체 선언
하나의 문장으로 만들어서 시리얼 포트로 출력

 

시스템 정보는 각 값을 슬래시(/)를 구분자로 넣었습니다.

35/23/34/56/0/0/98

이런식으로 데이터값이 슬래시로 구분된 하나의 문장을 만들어서 이 문장을 시리얼포트로 출력하는 것이 PC프로그램의 기능 전부입니다.

 

전체 코드는 약 50줄 정도입니다.

 

파이썬 코드
파이썬 코드

 

이 코드를 실행파일로 만들어서 윈도우 시작시 자동으로 실행시키도록 하면 PC쪽 준비는 끝납니다.

 

파이썬 프로그램 실행모습

 

아두이노에서는 시리얼로 들어오는 문자열을 받아서 구분자인 /를 가지고 값을 추출해서 활용하게 됩니다.

출력은 1.3인치 OLED액정을 사용하였고, 그래픽 라이브러리는 u8glib를 사용하였습니다.

 

PC쪽에서 넘어오는 값은 14가지인데요,

cpu사용률, 쓰레드0, 쓰레드1, 쓰레드2, 쓰레드3, 쓰레드4, 쓰레드5, 쓰레드6, 쓰레드7, 메모리사용률, C드라이브사용률, 네트워크업로드, 네트워크다운로드, GPU사용률

 

저는 이중에 3가지만 액정에 표시하였습니다.

가지고 있는 액정이 마침 3개 뿐이었기 때문인데요, 아두이노는 레오나르도 계열의 micro pro를 사용하였습니다.

 

브레드보드에 회로를 구성해서 프로그램 프로그램과 테스트작업을 합니다.

 

브레드보드에 회로를 구성하고 테스트

 

프로그래밍에는 어쩔수 없이 차트를 그리는 코드가 제일 시간이 많이 걸립니다.

아두이노 한개에 액정 한개씩 연결을 하였고, PC쪽 신호는 CPU쪽 아두이노에서 HW시리얼로 받은 다음 softwareSerial로 나머지 2개 아두이노로 전달하게 됩니다.

 

각가 아두이노는 받은 문자열(값이 슬래시로 구분되어 나열되어 있는)을 각각 처리하여 값을 추출하고 차트를 그리는 알고리즘입니다.

 

그래픽라이브러이와 소프트웨어시리얼을 include
PC쪽과 동일하게 9600bps로 선언
넘어온 문자열을 받아서 슬래시를 구분자로 각각 추출
추출된 숫자는 문자열이기 때문에 차트를 그리기 위해 각각 정수형, 실수형으로 변환

 

이상이 아두이노의 핵심코드부입니다.

한가지 32행의 코드는 문자열을 받자마자 나머지 2개의 아두이노로 software시리얼로 보내는 코드입니다.

 

 

나머지 2개의 아두이노에서는 소프트웨어 시리얼로 받아서 위 코드와 동일하게 처리하여 값을 추출하게 됩니다.

값이 추출되었으면 액정에 표시하면 되는데요.

예쁘게 하려고 여러가지 시도해 보다가 최종 아래처럼 디자인 되었습니다.

 

테스트 작동모습, micro pro 3개, oled 3개 사용

 

위에 핵심코드를 설명드렸으나 참고하시라고 PC에 연결되는 아두이노의 전체 코드를 참고 하십시오.

 

// 첫번째 아두이노
// PC로부터 정보 수신 후 다른 아두이노로 전달
// CPU점유률 표시

#include "U8glib.h"
#include <SoftwareSerial.h>
SoftwareSerial sendSerial(14,16);
U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_FAST);

String sr;
String scpu_total, sthread0, sthread1, sthread2, sthread3, sthread4, sthread5, sthread6, sthread7, smemory, sdrivec, sdrived, snet_up, snet_dn, sgpu, scd_free, sdd_free;
int cpu_total, thread0, thread1, thread2, thread3, thread4, thread5, thread6, thread7, memory, drivec, drived, gpu;
float net_up, net_dn;
int s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, srlength;
int cpu[20], cy[20];
int cx=0;
unsigned long pre_time = 0;
unsigned long cur_time = 0;

void setup(void) {
  Serial.begin(19200);
  sendSerial.begin(19200);
  
  u8g.firstPage(); 
  do {
  u8g.setFont(u8g_font_profont22r);
  u8g.setPrintPos(47,40);
  u8g.print("CPU");
  }
  while( u8g.nextPage() );
}

void loop(void)
{
  cur_time = millis();

  if(Serial.available())
  {   
    sr = Serial.readStringUntil('\n');
    sendSerial.println(sr);

    s1 = sr.indexOf("/");         // 첫번째 슬래시 위치
    s2 = sr.indexOf("/", s1+1);   // 두번째 슬래시
    s3 = sr.indexOf("/", s2+1);
    s4 = sr.indexOf("/", s3+1);
    s5 = sr.indexOf("/", s4+1);
    s6 = sr.indexOf("/", s5+1);
    s7 = sr.indexOf("/", s6+1);
    s8 = sr.indexOf("/", s7+1);
    s9 = sr.indexOf("/", s8+1);
    s10 = sr.indexOf("/", s9+1);
    s11 = sr.indexOf("/", s10+1);
    s12 = sr.indexOf("/", s11+1);
    s13 = sr.indexOf("/", s12+1);
    s14 = sr.indexOf("/", s13+1);
    s15 = sr.indexOf("/", s14+1);
    s16 = sr.indexOf("/", s15+1);
        
    srlength = sr.length();    // 문자열 길이
    
    scpu_total = sr.substring(0, s1);
    sthread0 = sr.substring(s1+1, s2);
    sthread1 = sr.substring(s2+1, s3);
    sthread2 = sr.substring(s3+1, s4);
    sthread3 = sr.substring(s4+1, s5);
    sthread4 = sr.substring(s5+1, s6);
    sthread5 = sr.substring(s6+1, s7);
    sthread6 = sr.substring(s7+1, s8);
    sthread7 = sr.substring(s8+1, s9);
    smemory = sr.substring(s9+1, s10);
    sdrivec = sr.substring(s10+1, s11);
    sdrived = sr.substring(s11+1, s12);
    snet_up = sr.substring(s12+1, s13);
    snet_dn = sr.substring(s13+1, s14);
    sgpu = sr.substring(s14+1, s15);
    scd_free = sr.substring(s15+1, s16);
    sdd_free = sr.substring(s16+1, srlength);

    cpu_total = scpu_total.toInt();
    thread0 = sthread0.toInt();
    thread1 = sthread1.toInt();
    thread2 = sthread2.toInt();
    thread3 = sthread3.toInt();
    thread4 = sthread4.toInt();
    thread5 = sthread5.toInt();
    thread6 = sthread6.toInt();
    thread7 = sthread7.toInt();
    memory = smemory.toInt();
    drivec = sdrivec.toInt();
    drived = sdrived.toInt();
    net_up = snet_up.toFloat();
    net_dn = snet_dn.toFloat();
    gpu = sgpu.toInt();

    for(int i=0; i<19; i++)
    {
      cpu[i] = cpu[i+1];
    }
    cpu[19] = cpu_total;

    for(int i=0; i<20; i++)
    {
      cy[i] = map(cpu[i], 0, 100, 64, 21);
    }

    u8g.firstPage(); 
    do {
      u8g.setFont(u8g_font_chikita);
      u8g.drawStr(120, 14, "%");
      u8g.drawStr(0, 18, "100");
      u8g.drawStr(0, 39, " 50");
      u8g.drawStr(0, 61, "  0");

      u8g.setPrintPos(0, 6);
      u8g.print(cur_time - pre_time);

      u8g.drawStr(0, 21, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .");
      u8g.drawStr(0, 42, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .");
      u8g.drawStr(0, 64, ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .");

      u8g.drawFrame(115, cy[19]-2, 12, 64-cy[19]+2); 
      
      for(int i=0; i<19; i++)
      {
        u8g.drawLine(cx+(i*6), cy[i], cx+((i+1)*6), cy[i+1]);
        u8g.drawLine(cx+(i*6), cy[i]-1, cx+((i+1)*6), cy[i+1]-1);
        u8g.drawLine(cx+(i*6), cy[i]-2, cx+((i+1)*6), cy[i+1]-2);
      }  
  
      u8g.setFont(u8g_font_profont22r);
      
      if (cpu_total >= 100)
        u8g.setPrintPos(80, 14);
      else if (cpu_total >= 10 && cpu_total<100)
        u8g.setPrintPos(92, 14);       
      else
        u8g.setPrintPos(104, 14);
        u8g.print(cpu_total);
    }
    while( u8g.nextPage() );
    
    pre_time = cur_time;
  }
}

 

 

아래 영상은 이 프로젝트를 소개한 영상으로 전체적인 내용을 이해할 수 있으니 시청해 보시기 바랍니다.