PCDisplay

3 minute read

Github: https://github.com/lucasoshiro/pcdisplay

This is a side project where I placed a LCD display on my computer case, displaying the following information:

  • CPU usage
  • RAM usage
  • Download/upload rate
  • CPU temperature
  • Date and time
  • Hostname and operating system name
  • Now playing (song title and artist)

The hardware was build using an Arduino Nano and a 16x2 characters display. In the Github repository, there is the host code, written in Ruby, that provide the system information to the Arduino, and the guest code, used by the Arduino to control the display.

Data flow


+------+           +---------+      Request      +------------+
|Button| --------> | Arduino | ----------------> |     PC     |
+------+           | (guest) |                   |   (host)   |
                   |         | <---------------- |            |
                   +---------+       Data        +------------+
                       |
                       |
                       |
                       V
                  +---------+
                  | Display |
                  +---------+

The format of the requests is REQUEST <arguments>, with the arguments separated by spaces. An argument can also be between quotation marks, and can be escaped the same way as bash does. The format of the responses is REQUEST <data>, with the same name as the request.

PCDisplay Host

The host code runs on the computer and sends the data to the Arduino. It has the following files:

  • arduino_serial.rb: contains the Arduino class, that abstracts the library SerialPort;

  • serial_server.rb: library that allows the creation of a server capable of processing a request sent by the Arduino and sending the requested data. Its syntax is based on the famous Ruby framework Sinatra;

  • pc.rb: contains the singleton class PC, that centralizes several measurements of the computer, always quickly providing the requested data. Each measurement is executed in a different thread, updating the PC singleton;

  • main.rb: this file contains the definitions of the callbacks for each request, following this format:

request 'REQUEST_NAME' do
   data = get_data
   "REQUEST_NAME #{data}"
end

In other words, the data flow inside the host is the following:



+-------+    Request     +------------+      +--------+
|       | -------------> |SerialServer| ---> |        |
|Arduino|                +------------+      |callback|
|       | <--------------------------------- |        |
+-------+             Data                   +--------+
                                                  ^
                                                  |
                                            +------------+        +--------+
                                            |            | <----- | Player |
                                            |     PC     |        +--------+
                                            |            |
                                            +------------+ <----- +---------+
                                            ^ ^   ^   ^ ^         | Network |
                                            | |   |   | |         +---------+
                                      +-----+ | +---+ | +----+
                                      | CPU | | |RAM| | |Time|
                                      +-----+ | +---+ | +----+
                                              |       |
                                         +--------+  +-----------+
                                         |Hostname|  |Temperature|
                                         +--------+  +-----------+


PCDisplay Guest

PCDisplay Guest is executed on Arduino, and it works as a state machine:


+---------+         +------+          +-----+
| SYSINFO | ------> | TIME | -------> | CPU |
+---------+         +------+          +-----+
     ^                                    |
     |                                    V
+-------+     +-----+      +------+     +-----+
| MEDIA | <-- | NET | <--- | TEMP | <-- | RAM |
+-------+     +-----+      +------+     +-----+

Each state has a associated screen update function, that reads the desired information contained in the global struct INFO, and show it on the screen.

The main loop do the following actions:

  1. If a state change is scheduled, it changes the state. Otherwise, it stays on the same state;

  2. Sends a request from the list of requests;

  3. Waits for the result and updates the struct INFO with the incoming data from the computer. If it timeouts, it considers that the connection was lost, and it tries to reconnect;

  4. Shows the information on the screen, executing the associated function.

A state change is scheduled when the button is pressed. That event triggers an interruption that sets a flag that signalizes that in the next cycle the state must be changed.

Hostname/distro Date/time CPU RAM Temperature Network speed rate Now playing
Gallery

Update (10/2021)

After some time, the display stopped working and I decided to replaced by a new one, with a graphical 128x64px display. This new display is able to show all the information in a single screen, so, it doesnt’t need the state machine and the button anymore.

The rest of the code remains the same.

Graphical display, with network, CPU, RAM, and media info

Updated: