I built this analog CPU load meter for my dev workstation:

All I did was drill a few holes into the CPU and probe the power supply lines...

Okay, I lied. This is actually a fun project that would make a great intro to embedded electronics, or a quick afternoon hack for someone with a bit of experience.

The parts The main components are: Current meter : I got this at MIT Swapfest. The scale printed on the face is misleading: the meter itself measures only about 600 microamps in each direction. (It's designed for use with a circuit like this one). We can determine the actual current scale by connecting (in series) the analog meter, a variable resistor, and a digital multimeter, and driving them from a 5 volt power supply. This lets us adjust and reliably measure the current flowing through the analog meter.

Arduino : This little board has a 16 MHz processor, with direct control of a few dozen input/output pins, and a USB serial interface to a computer. In our project, it will take commands over USB and produce the appropriate amount of current to drive the meter. We're not even using most of the capabilities of the Arduino, but it's hard to beat as a platform for rapid development.

Resistor: The Arduino board is powered over USB; its output pins produce 5 volts for a logic 'high'. We want this 5 volt potential to push 600 microamps of current through the meter, according to the earlier measurement. Using Ohm's law we can calculate that we'll need a resistance of about 8.3 kilohms. Or you can just measure the variable resistor from earlier. We'll also use some wire, solder, and tape.

Building it The resistor goes in series with the meter. I just soldered it directly to the back: Some tape over these components prevents them from shorting against any of the various junk on my desk. Those wires run to the Arduino, hidden behind my monitor, which is connected to the computer by USB: That's it for hardware!

Code for the Arduino The Arduino IDE will compile code written in a C-like language and upload it to the Arduino board over USB. Here's our program: #define DIRECTION 2 #define MAGNITUDE 3 void setup () { Serial . begin ( 57600 ); pinMode ( DIRECTION , OUTPUT ); pinMode ( MAGNITUDE , OUTPUT ); } void loop () { int x = Serial . read (); if ( x == - 1 ) return ; if ( x < 128 ) { digitalWrite ( DIRECTION , LOW ); analogWrite ( MAGNITUDE , 2 *( 127 - x )); } else { digitalWrite ( DIRECTION , HIGH ); analogWrite ( MAGNITUDE , 255 - 2 *( x - 128 )); } } When it turns on, the Arduino will execute setup() once, and then call loop() over and over, forever. On each iteration, we try to read a byte from the serial port. A value of -1 indicates that no byte is available, so we return and try again a moment later. Otherwise, we translate a byte value between 0 to 255 into a meter deflection between −600 and 600 microamps. Pins 0 and 1 are used for serial communication, so I connected the meter to pins 2 and 3, and named them DIRECTION and MAGNITUDE respectively. When we call analogWrite on the MAGNITUDE pin with a value between 0 and 255, we get a proportional voltage between 0 and 5 volts. Actually, the Arduino fakes this by alternating between 0 and 5 volts very rapidly, but our meter is a slow mechanical object and won't know the difference. Suppose the MAGNITUDE pin is at some intermediate voltage between 0 and 5 volts. If the DIRECTION pin is low (0 V), conventional current will flow from MAGNITUDE to DIRECTION through the meter. If we set DIRECTION high (5 V), current will flow from DIRECTION to MAGNITUDE . So we can send current through the meter in either direction, and we can control the amount of current by controlling the effective voltage at MAGNITUDE . This is all we need to make the meter display whatever reading we want.

Code for the Linux host On Linux we can get CPU load information from the proc special filesystem: keegan@lyle$ head -n 1 /proc/stat cpu 965348 22839 479136 88577774 104454 5259 24633 0 0 These numbers tell us how much time the system's CPUs have spent in each of several states: user: running normal user processes nice: running user processes of low priority system: running kernel code, often on behalf of user processes idle: doing nothing because all processes are sleeping iowait: doing nothing because all runnable processes are waiting on I/O devices irq: handling asynchronous events from hardware softirq: performing tasks deferred by irq handlers steal: not running, because we're in a virtual machine and some other VM is using the physical CPU guest: acting as the host for a running virtual machine The numbers in /proc/stat are cumulative totals since boot, measured in arbitrary time units. We can read the file twice and subtract, in order to get a measure of where CPU time was spent recently. Then we'll use the fraction of time spent in states other than idle as a measure of CPU load, and send this to the Arduino. We'll do all this with a small Python script. The pySerial library lets us talk to the Arduino over USB serial. We'll configure it for 57,600 bits per second, the same rate specified in the Arduino's setup() function. Here's the code: #!/usr/bin/env python import serial import time port = serial . Serial ( '/dev/ttyUSB0' , 57600 ) old = None while True : with open ( '/proc/stat' ) as stat : new = map ( float , stat . readline () . strip () . split () [ 1 :] ) if old is not None : diff = [n - o for n, o in zip ( new , old ) ] idle = diff [ 3 ] / sum ( diff ) port . write ( chr ( int ( 255 * ( 1 - idle )))) old = new time . sleep ( 0.25 )