Raspberry Pi Cluster Node – 18 Raspberry Pi Temperature Monitoring
This post builds on my previous posts in the Raspberry Pi Cluster series by starting to log temperature with the RaspberryPiVcgencmd Python module.
Installing RaspberryPiVcgencmd
RaspberryPiVcgencmd is a small python module aimed to control vcgencmd
and allow programmatic access to it. This can be installed with the following command.
python3 -m pip install RaspberryPiVcgencmd
Once installed it can be included in a file using the following import.
from RaspberryPiVcgencmd import Vcgencmd
Once this is installed on all the Raspberry Pi nodes that you wish to record the temperature we are ready to continue.
Added NodeConfig static class to hold general data
To ease accessing global configuration information I have moved the node config to a new class. This static NodeConfig
class will be used to load and access the node data from various places.
class NodeConfig:
''' Static class used to hold generic information about the node's Config'''
node_type = None
@staticmethod
def load(config_data):
NodeConfig.node_type = config_data.get("node_config", "node_type")
Here the data is loaded from the config parser object and stored in the static variables. For now we are only loading the node_type data which will tell the RaspberryPi cluster code what type of node it is.
This can then be used to perform different operations based on the node type.
Now in each basic script there are simple config loading lines.
from RpiCluster.NodeConfig import NodeConfig
config = configparser.ConfigParser()
config.read(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'rpicluster.cfg'))
NodeConfig.load(config)
Added cpu_temperature to vitals payload
Since we want to start transmitting the CPU temperature to the primary node we need to add that to the vitals Payload. By default this will be null so it isn’t required to be supplied.
class VitalsPayload:
def __init__(self, cpu_percentage, cpu_frequency, ram_free, swap_free, cpu_temperature=None):
self.cpu_percentage = cpu_percentage
self.cpu_frequency = cpu_frequency
self.ram_free = ram_free
self.swap_free = swap_free
self.cpu_temperature = cpu_temperature
In addition we need to tweak how the flat payload are created to optionally include the cpu_temperature.
def get_flat_payload(self):
""" This will be called to get the data in a flat format to be used to send as a payload """
base_object = {
'cpu_percentage': self.cpu_percentage,
'cpu_frequency': self.cpu_frequency,
'ram_free': self.ram_free,
'swap_free': self.swap_free,
}
# If there are more "optional" parts, these will be added if they exist
if self.cpu_temperature:
base_object['cpu_temperature'] = self.cpu_temperature
return base_object
Here if cpu_temperature is set to a non-false value it will be included in the object. By default this will not be set as basic nodes will not send this data.
Finally the static load_payload method is also changed to handle the fact cpu_temperature might be set.
@staticmethod
def load_payload(payload):
cpu_temperature = None
if payload['cpu_temperature']:
cpu_temperature = payload['cpu_temperature']
return VitalsPayload(payload['cpu_percentage'], payload['cpu_frequency'], payload['ram_free'], payload['swap_free'], cpu_temperature=cpu_temperature)
If it is set it will include this data in the newly created VitalsPayload object.
Reading CPU Temperature on Raspberry Pi’s
Now we have the Vcgencmd library to read the CPU temperature we can use it in the NodeVitals script.
def get_current_node_vitals():
"""When called various statistics about the node in its current state are returned
These are things that are expected to change minute to minute.
Currently this includes cpu percentage, cpu frequency, ram available and swap available.
"""
# None unless we have a node that supports exporting this
cpu_temperature = None
if NodeConfig.node_type == "raspberrypi":
#Only import this if we are a RaspberryPi node, other nodes might not have this
from RaspberryPiVcgencmd import Vcgencmd
vc = Vcgencmd()
cpu_temperature = vc.get_cpu_temp()
return VitalsPayload(
# TODO: Store fans, and battery details if available?
psutil.cpu_percent(1),
psutil.cpu_freq().current,
psutil.virtual_memory().free,
psutil.swap_memory().free,
cpu_temperature=cpu_temperature
)
Here the NodeConfig class is used to check what type of node it is. If it is a raspberrypi
node then it will import the Vcgencmd module and read the temperature.
Having the import enclosed in the if statement means that it will only be imported for Raspberry Pi’s which will have the module installed.
In the future further types of node will be supported and able to have their CPU temperature reported.
Storing temperature data to influx
The final step of keeping track of node temperatures is to log this to InfluxDB.
Here the start of log_vitals is changed a little to also log temperature if provided.
def log_vitals(self, vitals):
# TODO: Write these in one write_points API call rather than lots of smaller ones
cpu_data = {
"frequency": vitals.cpu_frequency,
"percentage": vitals.cpu_percentage,
}
if vitals.cpu_temperature:
cpu_data['temperature'] = vitals.cpu_temperature
self._write_datapoint("cpu", cpu_data)
Here it is written into the CPU field alongside frequency and percentage.
Summary of logging the CPU temperature
Now with some refactoring is it easy to keep track of what type of node the secondary is. This is shared across the codebase and is used to log the temperature of a raspberrypi node.
This is then transmitted to the primary and logged in InfluxDB. This will help keep an eye on the temperature to see if a node is overloaded.
The full code is available on Github, any comments or questions can be raised there as issues or posted below.