DIY Arc Reactor CPU Performance Monitor, Powered By An Arduino

In this project, I’m going to be showing you how to turn one of these popular DIY arc reactor kits into a useful Arc Reactor CPU performance monitor for your computer. It plugs into one of your computers USB ports and displays your CPU performance on the OLED display and the arc reactor pulses according to your CPU usage, increasing the pulse frequency with an increased CPU load.

I started out by assembling an Arc Reactor kit, which has a built-in USB powered LED light and that got me thinking of a way to turn it into something useful for my desk. I initially thought about using an Arduino to make the LED pulse as some sort of readout or indication. This lead me to my computers CPU performance and I then decided to add the OLED display for a more accurate readout as well.

The arc reactor LED and OLED display are powered by an Arduino Uno in the base, which receives updates on the computers CPU usage every two and a half seconds and adjusts the pulse duration accordingly.

On the computer’s side, a python script reads the CPU performance data from a Hardware Monitoring application and posts this data to the Arduino through a serial communication port.

The device doesn’t need any external power, it is powered by the USB port as well.

Here’s a video of the build and the Arc Reactor in use, read on for the full step by step instructions as well as the code and print file downloads.

What You Need To Build Your Performance Monitor

You’ll also need to 3D print the components for the stand. If you don’t have a 3D printer and you enjoy DIY projects and building things, you should really consider getting one. They’ve become a lot more affordable and really expand your workshop capabilities.

How To Build Your Own Arc Reactor CPU Performance Monitor

I’ve split the build process up into a couple of steps to make it easier to follow. We’ll start by assembling the Arc Reactor (with is done according to the enclosed instructions), then solder the electronics, then assemble the base and finally upload the sketch and run the Python script.

Assemble The Arc Reactor

Theses Arc Reactor kits have been around for a couple of years and most of them are reasonably good quality, so you shouldn’t have any trouble following the enclosed instructions and assembling your reactor. There isn’t anything you need to modify or change during the build, you can even use one which is preassembled if you’ve got one you’ve already built.

Arc Reactor Kit Components

Arc Reactor Kit Assembly

Depending on your kit, it should take around half an hour to an hour to assemble. Some kits do require you to wind your own coils or trim off a lot of excess plastic which can be time-consuming.

Assembled DIY Arc Reactor Kit

Solder The Electronic Components

Before we assemble the base components, we need to make up the electrical circuit. The circuit for this project is quite simple and consists of an I2C connection between the Arduino Uno and the OLED display and then an NPN transistor connected to pin 5 of the Arduino to drive the Arc Reactor. The LED array on the Arc Reactor draws about 500 milliamps, which is too much to drive directly off of the Arduino. I added a 510Ω current limiting resistor onto the base leg of the transistor as well. I soldered this resistor directly onto the transistor and covered it with heat shrink tubing.

Arc Reactor CPU Performance Monitor Schematic

I connected the components using some coloured ribbon cable and header pins to plug into the Arduino and covered the exposed wiring on the transistor and plug with some heat shrink tubing.

Wiring Hardness

Important Note – There seem to be two variants of these OLED displays available online and they look very similar. One version has the GND pin first and the other the VCC pin first. They do not have any reverse polarity protection, so if you connect them the wrong way around they instantly “pop” and have to be thrown out.  Make sure that you check this before soldering the pin connector.

Print & Assemble The Arc Reactor Stand

I designed four components which stack together with some hex head screws to make up the stand. There is a base compartment to house the Arduino. A cover to go over the base and connect to the display housing and then a post on top of the display housing to support the arc reactor.

3D Print Design

Download The 3D Print Files – CPU Monitor Stand Files

I 3D printed the components using black PLA with a 15% infill. You could print the base out of a different colour as well, it would probably look great in silver or grey too. I usually try to hide the screws underneath the housing, but in this design I wanted the screws to be visible to add a bit of detail to the base.

3D Printing The Housing

3D Printed Arduino Uno Housing

I’ve used a single M5 x 8mm hex head screw to secure the reactor post to the display housing. Make sure that the top surface of the post is angled to point the Arc Reactor slightly upwards, ie the highest point is in the front.

M5 Screw To Hold Vertical Post

Then glued the OLED display into the housing using a glue gun.

Gluing OLED Display Into Place

I then glued the Arduino Uno into the base, with the USB port lined up with the cutout.

Gluing Arduino Uno Into Place

I then plugged the connector into the OLED display and attached the Arduino cover plate using four M3 x 8mm screws.

Installing The Electrical Assembly

Securing The M3 Screws

The cover then goes onto the base with four M4 x 8mm screws. I used M4 screws so that the heads are a bit larger and more visible on the base. You can switch these out for small M3 screws if you’d like, you’ll just need to modify the holes in the Arduino case to be slightly smaller.

Securing The M4 Screws

I attached the arc reactor to the base using the glue gun again. There isn’t really a nice flat surface on the bottom of the arc reactor, so you need to build up the glue around the supports until it’s held securely in place.

Gluing The Arc Reactor Into Place On The Stand

Lastly, glue the arc reactor plug into the back of the stand. This is just a dummy plug but it looks better if it’s actually plugged into something. If you’d like to neaten up the wiring even further, you could remove the existing plug on the LED and solder this plug’s leads onto the LED instead and then buy a real socket to plug the arc reactor into.

Glue The Back Plug In

Remember to also connect the power lead to the LED.

Plug The LED Socket In

And that’s the Arc Reactor CPU Performance Monitor’s hardware complete, now we just have to program it.

Programming The Arc Reactor CPU Performance Monitor

There are three things you need to set up to get your CPU data to be sent to your Arduino, a source of the data on your computer, a script to read the source on your computer and send the data through a serial communication port to your Arduino and the code on the Arduino to read the data, recompile it and then update the display and reactor LED.

We’ll start off with the Arduino’s code.

Programming The Arduino Uno

The Arduino’s code is fairly simple thanks to the OLED displays libraries. Most of the code is actually dedicated to turning the data received through the serial communication port back into usable data.

Let’s have a look at the code:

//Michael Klements
//The DIY Life
//Arc Reactor CPU Monitor
//25 May 2020

#include <SPI.h>                //Include the libraries for the display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128        //OLED display width, in pixels
#define SCREEN_HEIGHT 32        //OLED display height, in pixels

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);   //Create the display object

int pinReactor = 5;             //Pin to reactor
int brightness = 25;            //How bright the reactor LED is at minimum. Its distracting if it turns off entirely.
int fadeAmount = 1;             //How many increments to fade the reactor LED by in each loop cycle

char receivedChars[5];          //Variables to track the incoming Serial data
boolean newData = false;

int cpuLoad = 0;                //Variable for the CPU load

void setup() 
{ 
  Serial.begin(9600);                                     //Begin Serial communication with the PC
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))          // Address 0x3C for 128x32 display
  {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);                                              //Don't proceed if it can't communicate with the display
  }
  display.clearDisplay();                                 //Clear the display
  display.setTextSize(1);                                 //Set the display text size
  display.setTextColor(SSD1306_WHITE);                    //Draw white text
  updateDisplay();                                        //Update the displayed CPU load, 0% initially
}

void loop() 
{ 
  analogWrite(pinReactor, brightness);                    //Set the reactor LED brightness
  brightness = brightness + fadeAmount;                   //Increment the brightness by the fade amount
  if (brightness <= 25 || brightness >= 255)              //Reverse the direction of the fade increment once fully bright or dim
  {
    fadeAmount = -fadeAmount;
  }
  if (Serial.available())                                 //If an updated CPU load is received
  {
    delay(10);
    static byte ndx = 0;                                  //Index to keep track of received char position
    char endMarker = '%';                                 //Symbol indicating that the full CPU load value has been recieved
    char rc;                                              //Temporary variable to store the received char
    cpuLoad = 0;                                          //Reset CPU load variable to 0
    while (Serial.available() > 0 && newData == false)    //While data is being recieved
    {
      rc = Serial.read();                                 //Save data to rc
      if (rc != endMarker)                                //Check that the end of the data symbol hasn't been received
      {
            receivedChars[ndx] = rc;                      //Save char received to receivedChars array
            int temp = (int)rc - 48;                      //Convert ASCII char to int
            if(temp >= 0)                                 //Check that the converted char was not a space (ASCII 32)
            {
              if(ndx == 0)                                //Update CPU load based on 100s, 10s or units position
                cpuLoad = cpuLoad + temp*100;
              else if(ndx == 1)
                cpuLoad = cpuLoad + temp*10;
              else
                cpuLoad = cpuLoad + temp;
            }
            ndx++;                                        //Increment the index
        }
        else 
        {
            receivedChars[ndx] = '\0';                    //Terminate the string
            ndx = 0;                                      //Reset the index
            newData = true;                               //Terminate the loop
        }
    }
    if (newData == true)                                  //Reset new data
    {
        newData = false;
    }
    updateDisplay();                                      //Update the displayed CPU load
  }
  int delayDuration = map(cpuLoad, 0, 100, 10000, 500);   //Maps CPU Load to a delay duration, higher load is a shorter fade time
  delayMicroseconds(delayDuration);                       //Wait the calculated delay duration
}

void updateDisplay ()                                     //Update the displayed text
{
  display.clearDisplay();
  display.setCursor(40,5);
  display.println(F("CPU Load"));
  display.setCursor(55,15);
  display.print(cpuLoad);
  display.print(F("%"));
  display.display();
}

Download the Sketch – ArcReactorCPUMonitor

We start off by importing the libraries necessary to control the OLED display, then set up the display parameters and create a display object.

We then define the Arc reactor LED control pin and set parameters for the initial brightness and the fade amount or difference in light intensity for each cycle. I created the initial brightness variable as a non-zero value so that the LED doesn’t turn off entirely between cycles, I found this “flashing” to be too distracting on my desk.

We then create a character array to receive the serial communication data and create a variable to store the CPU load.

In the setup function, we start the Serial communication, then establish a connection with the display and update it to display the initial CPU load, which will be zero. If the Arduino is not able to communicate with the display, the code will hang here and won’t continue into the loop. This is good for troubleshooting and makes sure that your Arduino is able to communicate with the OLED display

In the loop function, we start by updating the arc reactors brightness and then increment the brightness variable by the fade amount for the next loop cycle. We then have a check to see if the brightness has reached a maximum or minimum and if so, reverse the fade direction.

We then check if any Serial communication data is available. If there is data available then it is read into the character array until the % sign is received, indicating that the data is complete.

Data is transferred to the Arduino letter by letter in ASCII format, not as an integer. So each digit is received individually and the Arduino needs to know when the number is complete and then convert it into an integer. The rest of the code in this section does just that, converting each digit received into either hundreds, tens or units and then adding them together to form a complete integer which the Arduino can then quantify.

We then update the display to reflect the new CPU load and then map the CPU load to a delay duration between cycles, which corresponds to a faster or slower arc reactor pulse. The higher the CPU load, the faster the arc reactor will pulse.

Lastly, we have a function to update the display which just clears the previous display and displays the new CPU load.

While you’ve got your Arduino IDE open, also make a note of which com port is assigned to your Arduino Uno, as you’ll need to update this in the python script.

Getting Your CPU Load Data

In order to send the CPU load to your Arduino, we first need to be able to access it. One of the easiest ways to do this is using a freeware application called Open Hardware Monitor.

Open Hardware Monitor Dashboard

The Open Hardware Monitor app runs in the background on your PC and minimises to the system tray. It collects information on a number of different hardware items on your computer and makes it available to be read using a Python script.

You’ll need to make a note of your processor name displayed in the monitor as you’ll need to update this in the python script in order to access your CPU load.

You’ll also need to set the Open Hardware Monitor to run on startup so that you don’t need to open it every time you restart your PC. To do this, go to options and make sure that “Start Minimized”, “Minimize To Tray” and “Run on Windows Startup” are all checked. Then minimise the app.

Posting The Data To Your Arduino Using A Python Script

Lastly, we have a python script which transmits the CPU load through the serial com port to your Arduino. The script reads the data posted by the Hardware monitor app, finds the CPU load and then posts the value to the Arduino.

I didn’t write this code. I’ve modified the code from Leonidas Tsekouras’s Arduino PC Monitor example in which he sends a range of data to be displayed on an external LCD display.

Here is the code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import json
import os
import time
from urllib.error import URLError, HTTPError
from urllib.request import Request, urlopen

import serial
import serial.tools.list_ports

serial_debug = False

def space_pad(number, length):
    """
    Return a number as a string, padded with spaces to make it the given length
    :param number: the number to pad with spaces (can be int or float)
    :param length: the specified length
    :returns: the number padded with spaces as a string
    """

    number_length = len(str(number))
    spaces_to_add = length - number_length
    return (' ' * spaces_to_add) + str(number)

def get_json_contents(json_url):
    """
    Return the contents of a (remote) JSON file
    :param json_url: the url (as a string) of the remote JSON file
    :returns: the data of the JSON file
    """

    data = None

    req = Request(json_url)
    try:
        response = urlopen(req).read()
    except HTTPError as e:
        print('HTTPError ' + str(e.code))
    except URLError as e:
        print('URLError ' + str(e.reason))
    else:
        try:
            data = json.loads(response.decode('utf-8'))
        except ValueError:
            print('Invalid JSON contents')

    return data


def find_in_data(ohw_data, name):
    """
    Search in the OpenHardwareMonitor data for a specific node, recursively
    :param ohw_data:    OpenHardwareMonitor data object
    :param name:        Name of node to search for
    :returns:           The found node, or -1 if no node was found
    """
    if ohw_data == -1:
        raise Exception('Couldn\'t find value ' + name + '!')
    if ohw_data['Text'] == name:
        # The node we are looking for is this one
        return ohw_data
    elif len(ohw_data['Children']) > 0:
        # Look at the node's children
        for child in ohw_data['Children']:
            if child['Text'] == name:
                # This child is the one we're looking for
                return child
            else:
                # Look at this children's children
                result = find_in_data(child, name)
                if result != -1:
                    # Node with specified name was found
                    return result
    # When this point is reached, nothing was found in any children
    return -1

def get_hardware_info():
    """
    Get hardware info from OpenHardwareMonitor's web server and format it
    """
    global serial_debug

    # Init arrays
    my_info = {}

    # Get data
    if serial_debug:
        # Read data from response.json file (for debugging)
        data_json = get_local_json_contents('response.json')
    else:
        # Get actual OHW data (Modify to your Open Hardware Monitor's IP Address)
        ohw_json_url = 'http://192.168.20.10:8085/data.json'
        data_json = get_json_contents(ohw_json_url)

    # Get info for CPU (Modify to your CPU name shown in Open Hardware Monitor)
    cpu_data = find_in_data(data_json, 'Intel Core i5-7200U')
    cpu_load = find_in_data(cpu_data, 'CPU Total')

    # Get CPU total load, and remove ".0 %" from the end
    cpu_load_value = cpu_load['Value'][:-4]

    my_info['cpu_load'] = cpu_load_value

    return my_info


def main():
    global serial_debug

    # Connect to the specified serial port (Modify to your Arduino's COM port)
    serial_port = 'COM3'
    if serial_port == 'TEST':
        serial_debug = True
    else:
        ser = serial.Serial(serial_port)

    while True:
        # Get current info
        my_info = get_hardware_info()

        # Prepare CPU string
        cpu = space_pad(int(my_info['cpu_load']), 3) + '%'

        # Send the strings via serial to the Arduino
        if serial_debug:
            print(arduino_str)
        else:
            ser.write(cpu.encode())

        # Wait until refreshing Arduino again
        time.sleep(2.5)


if __name__ == '__main__':
    main()

Download The Code – ArduinoPCMonitor

You’ll need to update the IP address that your Open Hardware Monitor is posting the data too. This can be found in the Open Hardware Monitor app by going to Options -> Remote Web Server -> Port.

You’ll also need to change your processor name and check that the Arduino’s com port is correct.

Both the python script and then hardware monitor can be set to automatically startup with your computer and run in the background, so you don’t have to keep running them every time you turn your computer off. The python script can be automatically started using the windows “Start a Program” utility.

Using The Arc Reactor CPU Monitor

Upload the code your Arduino and make sure that the Open Hardware Monitor app is running on your computer and then run the Python script to start posting the data to your Arduino. You should then start seeing updates to your CPU performance on the OLED display and the reactor pulsing accordingly.

Arc Reactor CPU Performance Monitor

On start-up, with no data being transmitted, the OLED display should still display CPU Load 0% and the reactor should be pulsing slowly.

CPU Load Displayed

If you open up a benchmarking application to increase the CPU load to 100%, you will see it pulse really quickly.

You can make your own adjustments to the pulse durations and brightness levels in the code. I found it too distracting to have the pulse turn off completely each cycle, so I limited the minimum brightness and also set the low CPU usage pulse duration to be quite long. You can edit these by making changes to the mapped delay, to the brightness variable and to the fadeAmount variable.

Let me know if you’re going to be building your own arc reactor CPU performance monitor or what you’ve done with an arc reactor you’ve built.

Share This Guide

Arc Reactor PC Monitor Pinterest

Michael Klements
Michael Klements
Hi, my name is Michael and I started this blog in 2016 to share my DIY journey with you. I love tinkering with electronics, making, fixing, and building - I'm always looking for new projects and exciting DIY ideas. If you do too, grab a cup of coffee and settle in, I'm happy to have you here.

1 COMMENT

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest posts

LincStation N1 All-SSD NAS by LincPlus, Unboxing & Review

Today we're going to be taking a look at the LincStation N1, a 6-bay all-SSD NAS by LincPlus. It is marketed as being great...

I Tried 3 New Hats For The Raspberry Pi 5

Today we've got three new hats for the Raspberry Pi 5 that we will be trying out. I've used variants of each of these...

Beelink EX Docking Station For The GTi 14 Ultra

A couple of weeks ago, I tested the Beelink GTi 14 Ultra, a powerful mini PC with a full-size PCIe port underneath it. At...

Related posts