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.

Leave a Reply

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