327days since
2013 Indy 500

383days since
Game of Thrones

Arduino‎ > ‎

CPU Memory Meter With Digits

Probably the final form of my CPU & Memory Meter

The Fritzing Diagram - The wiring to the 7 Segment displays is a bit confusing.  Pins 0 - 16 on the shift registers should be to A,B,C,D,E,F,G,DP,A1,B1,C1,D1,E1,F1,G1,DP1.  It can be corrected in code if you get it wrong :)


A short video of it working


The Arduino Code

/*
  PC CPU Meter by Kraegar 1/8/13
  Modified 1/16/2013 to add memory, run from shift registers
  Modified 1/17/2013 to add a 7 digit display showing CPU
  7 digit display code taken from the Arduino Cookbookf
  Requires 2 x 10 LED sets, can use 10 segment LEDs, and something running on the host to send data
  from 0 - 10.  Data needs to come in the form of 'CPU,MEM\n' Where CPU & MEM are values from 0 - 10
  Should probably add in some validation on the data we're getting
  The shift register code, including the great subroutines, are from DrLuke @ http://bildr.org/2011/02/74hc595/
 */
const int ledCount = 10;    // the number of LEDs in the bar graph - has to be 10!

//The pins for the shift registers
int SER_Pin = 3;   //pin 14 on the 75HC595, pin3 on the Arduino
int RCLK_Pin = 4;  //pin 12 on the 75HC595, pin4 on the Arduino
int SRCLK_Pin = 5; //pin 11 on the 75HC595, pin5 on the Arduino


int LED7_SER = 8;
int LED7_RCLK = 9;
int LED7_SRCLK = 10;
#define LED7_595s 2
#define numOfLED7Pins LED7_595s * 8
boolean LED_regs[numOfLED7Pins];

//How many of the shift registers - change this
#define number_of_74hc595s 3

//do not touch - used to set our total array size, 8 pins per register
#define numOfRegisterPins number_of_74hc595s * 8
boolean registers[numOfRegisterPins];


int ledLevel;  //used for CPU level
int ledLevel2;  //used for memory level
const int NUMBER_OF_FIELDS = 4; // how many comma separated fields we expect
int fieldIndex = 0; // the current field being received
int values[NUMBER_OF_FIELDS]; // array holding values for all the fields


const int numeralO[11] = {
//ABCDEFG /dp
B11111100, // 0
B01100000, // 1
B11011010, // 2
B11110010, // 3
B01100110, // 4
B10110110, // 5
B00111110, // 6
B11100000, // 7
B11111111, // 8
B11100110, // 9
B00000010, // 10 = -
};

const int numeralT[11] = {
//ABCDEFG /dp
B00000000, // 0
B01100000, // 1
B11011010, // 2
B11110010, // 3
B01100110, // 4
B10110110, // 5
B00111110, // 6
B11100000, // 7
B11111111, // 8
B11100110, // 9
B00000010, // 10 = -
};

const int segmentOPins[8] = { 7,6,5,4,3,2,1,0 };
const int segmentTPins[8] = { 15,14,13,12,11,10,9,8 };

void setup() {
  Serial.begin(9600);  //Begin serial communcation
  // Set the shift register pins to output
  pinMode(SER_Pin, OUTPUT);
  pinMode(RCLK_Pin, OUTPUT);
  pinMode(SRCLK_Pin, OUTPUT);
  pinMode(LED7_SER, OUTPUT);
  pinMode(LED7_RCLK, OUTPUT);
  pinMode(LED7_SRCLK, OUTPUT);
  //reset all register pins
  clearRegisters();
  writeRegisters();
  clearLED7Regs();
  writeLED7Regs();
}

// Got the serial read code from the arduino cookbook. Recipe 4.5 Everyone should have a copy.
void loop() {

  if( Serial.available()){  
    char ch = Serial.read();
    if(ch >= '0' && ch <= '9'){ // is this an ascii digit between 0 and 9?
                                // yes, accumulate the value if the fieldIndex is within range
                                // additional fields are not stored
      if(fieldIndex < NUMBER_OF_FIELDS) {
        values[fieldIndex] = (values[fieldIndex] * 10) + (ch - '0')+1;  //Convert from ASCII to decimal, add one so we can differentiate from a "null" array
      }
  }else if (ch == ','){ // comma is our separator, so move on to the next field
    fieldIndex++; // increment field index
  }else{
    // any character not a digit or comma ends the acquisition of fields
    // in this example it's the newline character sent by the Serial Monitor
    
    if(values[0] > 0) {  //If the first value is our array is not 0, we have CPU data - consider 0 "null"
      ledLevel = values[0] - 1;  //Subtract one - this avoids our null array logic.
      for (int thisLed = 0; thisLed < ledCount; thisLed++) {  //loop over the LEDs, up to the level we got
        // if the array element's index is less than ledLevel,
        // turn the pin for this element on
        if (thisLed < ledLevel) {
          setRegisterPin(thisLed, HIGH);
        } else {
          // turn off all pins higher than the ledLevel:
          setRegisterPin(thisLed, LOW); 
        }
      }
    }
    if(values[1] > 0) { //If the first value is our array is not 0, we have CPU data - consider 0 "null"
      ledLevel2 = values[1] - 1; //Subtract one - this avoids our null array logic.
      for (int thisLed = 0; thisLed < ledCount; thisLed++) { //loop over the LEDs, up to the level we got
        // if the array element's index is less than ledLevel,
        // turn the pin for this element on
        if (thisLed < ledLevel2) {
          setRegisterPin(thisLed+10, HIGH);  //add 10, because the first 10 pins are for the CPU
        } else {
          // turn off all pins higher than the ledLevel:
          setRegisterPin(thisLed+10, LOW);  //add 10, because the first 10 pins are for the CPU
        }
      }
      if(values[2] > 0 && values[2] < 11) {
        showDigitT(values[2]-1);
      }else if(values[2] > 10){
        showDigitT(10);
      }
      if(values[3] > 0 && values[3] < 11) {
        showDigitO(values[3]-1);
      }else if(values[3] > 10){
        showDigitO(10);
      }
    }
    writeRegisters();
    writeLED7Regs(); 
    for(int i=0; i < min(NUMBER_OF_FIELDS, fieldIndex+1); i++){
      values[i] = 0; // set the values to zero, ready for the next message
      }
      fieldIndex = 0; // ready to start over
    }
  }
}

//set all register pins to LOW
void clearRegisters(){
  for(int i = numOfRegisterPins - 1; i >=  0; i--){
     registers[i] = LOW;
  }
}

//Set and display registers
//Only call AFTER all values are set how you would like (slow otherwise)
void writeRegisters(){

  digitalWrite(RCLK_Pin, LOW);
  for(int i = numOfRegisterPins - 1; i >=  0; i--){
    digitalWrite(SRCLK_Pin, LOW);
    int val = registers[i];
    digitalWrite(SER_Pin, val);
    digitalWrite(SRCLK_Pin, HIGH);
  }
  digitalWrite(RCLK_Pin, HIGH);
}

//set an individual pin HIGH or LOW
void setRegisterPin(int index, int value){
    registers[index] = value;
}


//set all register pins to LOW
void clearLED7Regs(){
  for(int i = numOfLED7Pins - 1; i >=  0; i--){
     LED_regs[i] = LOW;
  }
}

//Set and display registers
//Only call AFTER all values are set how you would like (slow otherwise)
void writeLED7Regs(){

  digitalWrite(LED7_RCLK, LOW);
  for(int i = numOfLED7Pins - 1; i >=  0; i--){
    digitalWrite(LED7_SRCLK, LOW);
    int val = LED_regs[i];
    digitalWrite(LED7_SER, val);
    digitalWrite(LED7_SRCLK, HIGH);
  }
  digitalWrite(LED7_RCLK, HIGH);
}

//set an individual pin HIGH or LOW
void setLED7Pin(int index, int value){
    LED_regs[index] = value;
}

void showDigitO( int number) {
  boolean isBitSet;

  for(int i = 1; i < 8; i++){
    setLED7Pin(i, LOW);
  }
  for(int segment =1; segment < 8; segment++){
    isBitSet = bitRead(numeralO[number], segment);
    setLED7Pin(segmentOPins[segment], isBitSet);
  }
  
}

void showDigitT( int number) {
  boolean isBitSet;
  for(int i = 1; i < 8; i++){
    setLED7Pin(i+7, LOW);
  }
  for(int segment =1; segment < 8; segment++){
    isBitSet = bitRead(numeralT[number], segment);
    setLED7Pin(segmentTPins[segment], isBitSet);
  }
  
}



The Perl (PC) Code

#!/usr/bin/perl -w
# This code is almost 100% from http://www.perlmonks.org/?node_id=951420
# written by gulden
# Modified by Kraegar on 1/8/13 to send CPU use out the  COM3 port to an arduino
# Lots of cleanup needed, basically finished it where it was working, and haven't 
# reviewed it at all yet
# Not fully commented yet, since I didn't write most of it!
use strict;
use warnings;

use Win32::OLE;

# Init WMI
my $wmi = Win32::OLE->GetObject("winmgmts://./root/cimv2")
    or die "Failed getobject\n";


# get WMI values
sub get_wmi{
my $wmi = shift;
my $list, my $v;
my @properties = qw(PercentProcessorTime TimeStamp_Sys100NS);
my $class = 'Win32_PerfRawData_PerfOS_Processor';
my $key = 'Name';
$list = $wmi->InstancesOf("$class")
or die "Failed getobject\n";
my $hash;
foreach $v (in $list) {        
$hash->{$v->{$key}}->{$_}  = $v->{$_} for @properties;
}
$hash;
}


my $cpu;
my $hash_prev = get_wmi($wmi);

while(1){
#sleep(1);
select(undef, undef, undef, 0.5); #mimics sleeping for 1/2 a second
my $hash = get_wmi($wmi);
$cpu = sprintf("%.2f", 
(
1 - (
$hash->{'_Total'}->{'PercentProcessorTime'} - $hash_prev->{'_Total'}->{'PercentProcessorTime'}
) / 
(
$hash->{'_Total'}->{'TimeStamp_Sys100NS'} - $hash_prev->{'_Total'}->{'TimeStamp_Sys100NS'}
)* 100 );
my $nicecpu = sprintf("%.0f", $cpu);
my $rcpu = int($nicecpu/10+0.5)*10;
$rcpu = $rcpu/10;
my $list, my $v;

$list = $wmi->InstancesOf("Win32_OperatingSystem")
  or die "Failed getobject\n";

my $end_time = time;


my ($total_mem, $free_mem, $used_mem, $mem_percent, $free_percent, $rmem_percent);

foreach $v (in $list) {

  $total_mem = $v->{TotalVisibleMemorySize};
  $free_mem = $v->{FreePhysicalMemory};
  $used_mem = $total_mem - $free_mem;
  $mem_percent = sprintf("%.0f", $used_mem / $total_mem * 100);
  $rmem_percent = int($mem_percent/10+0.5)*10;
  $rmem_percent = $rmem_percent/10;
  $free_percent  =  sprintf("%.2f", $free_mem / $total_mem * 100);

  my $total_swap_mem = $v->{SizeStoredInPagingFiles};
  my $free_swap_mem = $v->{FreeSpaceInPagingFiles};
  my $used_swap_mem = $total_swap_mem - $free_swap_mem;
  my $used_swap_mem_perc = ($total_swap_mem - $free_swap_mem) / $total_swap_mem * 100;

}
my $cpu_a;
my $cpu_b;
if($nicecpu < 10){
$cpu_a = 0;
$cpu_b = $nicecpu;
}elsif($nicecpu==100){
$cpu_a=10;
$cpu_b=10;
}else{
$cpu_a = substr($nicecpu, 0, 1);
$cpu_b = substr($nicecpu, 1, 1);
}
print "$rcpu,$rmem_percent,$cpu_a,$cpu_b\n";
open( PORT, "+>COM3" ) or die "Can't open COM3: $!"; #I open the COM port each time. Seemed more reliable
print PORT "$rcpu,$rmem_percent,$cpu_a,$cpu_b\n";
close(PORT);  #Make sure to close the port, if you're going to open it each time especially!

$hash_prev = $hash;
}
Comments