I was recently using various system monitors on my Ubuntu 9.10, the most popular being "System Monitor" (the default gnome application for this purpose), and I realised that neither of these utilities presented any information about the CPU core temperature.
Perhaps its not such a big requirement, but I'd really like to know how hot my CPU core gets under different load conditions. For instance, when I run a massive search on my filesystem, I find my laptop getting a little warmer (a little too warm sometimes!!). The CPU core will always be at a higher temperature than the body/ambient environment since it's always at work. The operating range of most CPUs is between 55 to 85 degree centigrade. So if the ambient temperature (temperature of the body) seems to have risen by a small margin, there is a good chance that the increase is considerable inside your core.
So I decide to compliment the existing utilities by making an application of my own - TempMon. I decided to use PyQt4 for my application as its easy to use and fits perfectly with Python, my language of choice. Before I began, I penned down the goals for this application. Here they are:
- The application must display the core temperature of the CPU at all times
- The application must also display the average CPU load (a.k.a CPU Usage). This is usually represented as a percentage by many system monitors. Since I have a dual core 64 bit AMD at the heart of my motherboard, I will compute the "AVERAGE" cpu load. Gnome's system monitor provides individual load percentages for each core
- The application must allow me to trigger an alert/alarm if the core temperature exceeds a particular threshold value that can be user defined
- The application must be dockable to the taskbar or system tray
- Finally, all the data assimilated (temperature and usage info) over a certain period of time must be displayed using a plotter or a graph
Firstly, its important to understand your data source for this application. Where does the application get the temperature and CPU usage data from? On Linux, this information is available on the local filesystem on the following files:
- '/proc/acpi/thermal_zone/TZ01/temperature' contains a string that contains the immediate value of the temperature. In fact, this file has only one line that says "temperature: 64 C". Makes my life a lot easier :)
- '/proc/stat' contains the information about CPU usage. There is a lot of information available on this file and not all of it is relevant to our need. The first line is all we need. It looks something like 'cpu 936808 309 246585 14286660 39745 3447 13778 0 0'. Only the values in bold digits are important to us.
- So how do we calculate the CPU usage? Its rather simple. In the above example, consider the four numbers (in bold). Let them be u1, s1, t1 and i1. At another time (usually on a successive read of this file), let the values for the same numbers be u2, s2, t2 and i2. Now we will calculate two quantities (since we need a ratio for a percentage): usage(U) and total usage(T). The ration of these two values multiplied by 100 will be the percentage of CPU usage
- U = (u2-u1)+(s2-s1)+(t2-t1); T = U + (i2 - i1); CPU Usage = (U/T) x 100 %
I quickly opened up Qt Designer, and a few clicks later, the GUI was ready. Small and compact but very informative. Here's what the UI looked like:
Two labels that displayed the temperature and usage percentage, one spin box to set the threshold value for temperature, a checkbox and a push button. Thats pretty much it. Once I was done, I saved the file as "fontend.ui" and subsequently converted the GUI into a python source file using pyuic4:
shell$pyuic4 frontend.ui > frontend.py
A file "frontend.py" is generated and contains a class for this dialog. That class has a method called
setupUi that draws the form shown above. Now, I'm ready to start coding the core of the app. I create another file called "tempmonitor.py". You can download the entire source code from
here (Look for a file labelled "Temperature Monitor").
In the init routine of the main class of "tempmonitor.py", I created a system tray icon object:
self.trayIcon = QtGui.QSystemTrayIcon(QtGui.QIcon("trayicon.png"),self)
Following which I connected this tray icon to a slot where in I toggled the form's visibility:
self.connect(self.trayIcon,SIGNAL("activated(QSystemTrayIcon::ActivationReason)"),self.toggleVisibility) This creates a system tray icon, which allows me to show/hide the form by simply clicking it. The self.toggleVisibility routine simply changes the visibility status of the form to the opposite of its current state.
Next up, its time to write the actual tasks of polling the temperature and usage values from the respective files. That is done using self.getTemp and self.getCpuUsage methods. Both these methods read the respective files (mentioned above), and process the data within to create two strings, one for temperature and another for CPU usage. Once all the hard work is done, each of these methods, emits a custom signal viz. "tempUpdate" and "cpuUpdate" respectively.
These signals are very important. Since these routines do the "heavy work", its only natural to run them as a thread in the background (in order to prevent the GUI event loop from being interrupted). Now if they're going to run as concurrent children threads, directly providing them with access to the widgets on the form is risky business. Signals help us solve this problem. The background threads emit signals which the parent thread (GUI event loop) comes to know about. Each of these signals is tied to a slot (a method basically, that allows us the parent thread to "do" something when it discovers the signal). The following statements help redirect these signals to their respective slots.
self.connect(self,SIGNAL("tempUpdate"),self.tempUpdate) self.connect(self,SIGNAL("cpuUpdate"),self.cpuUpdate) Now these slots are tied up to the
self.tempUpdate and
self.cpuUdate slots respectively. These slots are simple methods of the class, which can now be used to update the text in the labels for temperature and CPU Usage.
So the heavy work is done. Its time to actually spawn a thread that will run do all the heavy work. This thread must run until the parent thread dies (application is closed). I put up all the methods which I want to run, into a single routine called
self.idleTime, which is now run in the background using the following statement:
thread.start_new_thread(self.idleTime,()) The empty parentheses indicate that no arguments are being passed to this routine. This routine periodically runs all the other methods which will read the temperature and usage data from the files, process this information and emit signals to indicate that their work is done. After every iteration of these methods, I put the thread to sleep for a couple of seconds (this allows for context switching, and since the data we read isn't changing drastically every moment, this resolution is pretty good).
We're almost done, except for the last criterion that demands a graphical display of the assimilated data. If you notice, in the source for
self.tempUpdate and
self.cpuUpdate (slots for the signals we spoke about earlier), the values that are read are stored as tuples of (temperature, cpu percentage) in a list called data. This list is going to serve as our source of information when we plot the temperature and usage values graphically using
Google Charts.
The Google Charts API is really easy to use and the best part is that it works easily using simple GET requests. So all you really have to do is take all the data you want to plot and formulate a URL. That URL can be opened in a web browser and your chart is ready. As a hack I used the QWebView widget to dynamically generate the URL for the chart every few seconds, thereby giving the appearance of a real time graph/plot (just like system monitor does). Anyway, that is not part of this tutorial, so let's stay on point.
The
self.getChartLink method generates the URL based on the values present in the list ''data". This routine is also added to the idle task routine. So now, the worker thread that runs in the background will read the temperature, the CPU usage and generate the URL for a graph based on the values stored in data. Since Google Charts API has a limit to how many points can be plotted in a single chart, I've restricted the plot to the last 200 values that were sampled. Currently I'm sampling the values at an interval of 2 seconds (sleep time for the worker thread), thereby giving myself a plot of the temperature and CPU usage over the last 400 seconds or roughly 6 minutes. Here is what the chart looks like:
The green trace represents the CPU temperature (in centigrade) and the blue trace represents the CPU usage (in percentage).
Download the source from my
projects page (look for Temperature Monitor). In order to run this project you will need to have PyQt4 installed along with Python (>=v2.5.0).
And that is how its done!