post

The Pi Monitor

I’ve been having some problems configuring the optimal fan speeds on my computer. Part of the issue is that I have no way of seeing temperature changes while I’m using full-screen applications. A little help from Wolfram, a Raspberry Pi and Adafruit’s LCDPiPlate got me the information I needed.

In some other articles (which aren’t on my re-made website yet) , I’ve shown how to interact with the 16×2 LCD character screenattached to Raspberry Pi using the Wolfram language. Those projects were fun and not terribly useful, showing what could be done without actually providing an application of sorts. Here’s a simple implementation of a monitor that reports the current temperatures of my CPU, Case and GPU as well as the case and CPU fan speeds. I also take advantage of the ability to create custom characters and create an updating plot of the CPU fan speed.

pmmonitor

To cut to the chase, all of the code is stored in my GitHub repository , so I’m not going to reproduce a lot of it here. Simply put, The program does the following:

  1. During initialization, it checks to see if a log file is found. I use SpeedFan on my PC, which is able to log the speeds and temperatures at 3 second intervals. To simplify things, I have created a share so the RPi can mount that directory and have access to the log.
  2. The monitoring can be started with pmStartMonitor[], which pulls the last 20 entries in the log, reports the latest temps and fan speeds, and displays the recent CPU temps in a bar chart-style image.
  3. The screen changes color if the CPU temp exceeds a certain threshhold, and when finished, checks to see if the code ended on its own or was interrupted.

The code for this project is fairly straight forward. I use system commands inside of Import to speed up the log-reading process:

data = Import["!tail -n 20 "<>log,"TSV"];

This command just reads the last 20 lines of my log file, the name and path of which are stored in the symbol, log.  I explicitly note the type of import (TSV) since my log actually has a CSV extension, which leads to incorrect importing. Also, I don’t do this: data = <<“!tail -n 20 “<> log”…because using Get results in Mathematica interpreting the input as an expression. Therefore, I end up getting the product of the numbers in each line.
Plotting of the CPU temps is done with this command:

pmDefinePlotChars@pmPlot@pmPlotMatrix[data[[1;;20,2]]];

pmmatrix

I played around with a few methods to make this plot, and thought it was worth describing a bit. The general idea here is to create an image that can be downsized to a 2-bit 20×16 image that will fit within a set of 8 LCD characters in a 4×2 configuration. Originally, I was doing this with generating a typical plot using ListPlot and then applying Rasterize, ImageResize and Binarize. The result was fine; however the process is very slow on the RPi and the screen update was unsatisfactory to me. Instead, I used the fact that I have 20 pieces of information that I want to plot in 20 “columns” of LCD pixels.

pmPlotMatrix[data_List]:=Module[{lo = 40,hi=60}, 
  (* Prevent rescaling problems *)
  lo = Min[data,lo]; hi = Max[data,hi];
  (* Creates a matrix-style barchart with dimensions *)
  (* amenable to lcdCharDef *) 
  1-Normal@SparseArray[ 
    Flatten@MapIndexed[ 
      Table[{i,First@#2}->1,{i,#1}]&, 
      (Round@Rescale[#,{lo,hi},{16,1}]&/@data)],{16,20}] 
];

This function takes the portion of my log that I want to plot as input, rescales the values from 1 to 16 and creates a matrix, looking very much like a bar chart of sorts. pmPlot then carves this matrix up into 8 chunks of data that correspond to custom character definitions that the LCD screen understands.

pmfinished

There are some other tweaks in the code to help move things along. For example, it takes less time to create a custom character (several ms) than it does to print a character to the screen (10s of ms), so I only print the plot once and in subsequent iterations just change the custom character definitions. There’s certainly more tweaking to be done, but the code produced a working monitor that helped me diagnose my problem, so I considered this a complete project.
There is one bug that is (uhh) bugging me. A close look at the pmStartMonitor code reveals that the scheduled task only runs for 60 iterations. Somewhere between 200 and 500 iterations, I get kicked out of wolfram when running this code, and the terminal starts to behave badly (linefeeds are not realized, text input is recognized but not printed on the screen). I’ll need to generate some dummy code that reproduces this error regularly before I can diagnose it further. The real reason for this project, though, was to minimize the fan noise while playing Minecraft, so please excuse me while I go keep my mansion safe from a creeper infestation.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.