Accessing boards’ sensors: how to display Internal Temperature using Python
In this post we are going to have a look at the lm-sensors package, which allows detecting and reading the values provided by the hardware sensors available in the PICO-APL3 board. We will also take this opportunity to introduce the Python programming language for our scripting code.
Set up the environment
So, we will start by installing the required software packages:
• Python: > sudo apt install python3 • lm-sensors: > sudo apt install lm-sensors > sudo sensors-detect # answer ‘yes’ to all questions > sudo /etc/init.d/kmod start
You can check that the lm-sensors package is correctly installed by running the sensors command: it should generate an output similar to the following:
> sensors aaeonec-isa-0000 Adapter: ISA adapter in0: +0.85 V in1: +2.97 V in2: +1.35 V in3: +2.97 V in4: +2.97 V in5: +2.97 V fan1: 0 RPM temp1: +37.0°C coretemp-isa-0000 Adapter: ISA adapter Package id 0: +41.0°C (high = +105.0°C, crit = +105.0°C) Core 0: +40.0°C (high = +105.0°C, crit = +105.0°C) Core 2: +41.0°C (high = +105.0°C, crit = +105.0°C)
The sensors command returns the measured values for several voltages and temperatures (there is also a fan speed with the value zero, which seems reasonable since the board used for test does not have a fan). We are going to walk through the process of periodically obtaining the sensor information and extracting the values that we are interested in, using a script to automate these tasks. For this example we will focus on the temperature values.
The Python code required to perform this job is very compact, so we will present it first and then we will explain in detail how it works.
The Python script: temp_display.py
#!/usr/bin/python3 import subprocess import re import sys import time def get_sensor_data(): sensors_out = subprocess.run(['sensors'], stdout=subprocess.PIPE) return sensors_out.stdout.decode('utf-8') def parse_temps(sensor_data): temps = re.findall('\d+(?:\.\d+)?°C ', sensor_data, re.MULTILINE) return [x[:-3] for x in temps] def update_output(temps): sys.stdout.write("Board: %s - Package: %s - Core 0: %s - Core 2: %s\r" % (temps, temps, temps, temps)) sys.stdout.flush() def shutdown(): print() print("Goodbye!") print() # Main program print() print("System temperatures (°C) reported by the 'sensors' command (Ctrl-C to stop):") try: while True: data = get_sensor_data() temp_values = parse_temps(data) update_output(temp_values) time.sleep(1) except KeyboardInterrupt: shutdown()
The script displays a title line, including instructions on how to stop it, and then starts an infinite loop (while True). Inside this loop the script performs the following tasks:
- Retrieve sensor data (function get_sensor_data).
- Extract the temperature values from the sensor data (function parse_temps).
- Display updated values (function update_values)
- Wait for on second before restarting the loop.
This loop keeps executing until the user presses the key combination Control – C. When this happens a farewell message is displayed (function shutdown) on the screen and the script finishes.
This function obtains the sensor data by executing the ‘sensors’ command (the same that was mentioned at the beginning of this post). We use the Python subprocess module for this purpose: its run() function executes the ‘sensors’ command and returns the result, which we can access through the stdout property. The get_sensor_data() function returns a long string with the same information that is dumped on the screen when the command is executed manually.
We need to search the text string generated by the get_sensor_data() function, to isolate and extract the temperature values. For this purpose we use regular expressions, a very powerful tool that is implemented in Python by the re module. What we do with the findall() function is look inside the sensor data string for sequences of characters that fulfil the following requirements:
- Start with one or more decimal digits (0, 1, … 9),
- Optionally followed by a decimal point and one more decimal digits,
- Followed by the characters “°C “ (a blank space follows the ‘C’).
As you can see in the ‘sensor’ command output above, these conditions are only met by the four temperature values ‘temp1’, ‘Package id 0’, ‘Core 0’ and ‘Core 2’. And these conditions are coded in a special format string that is passed to the findall() function as its first parameter.
The parse_temps() function analyses the input and returns a collection of strings with the detected temperature values, stripped of the trailing “°C“.
If we wanted to extract other values, for example the voltages, we could use the exact same approach, we would only have to modify the format string appropriately.
The last step in the loop is showing the values on the screen. This would be usually done with the print() function, but with print() we would generate a new line every time we update the sensor values, and the old values would scroll up in the screen.
For that reason we decided to use the sys.stdout.write() function, which allows us to update the temperature values always on the same line and without scrolling.
The rest of the code in the script is very straightforward: the final step in every loop iteration is a one second delay generated by the sleep() function, and the shutdown() function is called in case the user presses Ctrl-C. After that, the script ends.
Testing the Script
The ‘sensors’ command does not require root permissions, therefore the temp_display.py script can be executed by a normal user:
apl3@APL3:~$ python3 temp_display.py System temperatures (°C) reported by the 'sensors' command (Ctrl-C to stop): Board: 35.0 - Package: 39.0 - Core 0: 38.0 - Core 2: 39.0
The temperature values will change over time, the update time should be around one second.
A good check that the sensors are working correctly can be performed by increasing the processor load. We can use a tool called stress that is designed with this goal. So, first we need to install this tool:
> sudo apt install stress
The stress tool can be invoked with a timeout option. We can use this option to observe the temperature difference when the processor load changes. The following command loads the processor for the first 30 seconds, while the temp_display script keeps running until interrupted by the user:
> stress -c 16 -t 30s -q & python3 temp_display.py
The temperatures displayed at the beginning are higher than usual:
System temperatures (°C) reported by the 'sensors' command (Ctrl-C to stop): Board: 36.0 - Package: 52.0 - Core 0: 52.0 - Core 2: 51.0
But they return quickly to their normal values after the 30 second timeout.