Monitorando temperatura, CPU e cooler do Raspberry Pi 5 com Python

O Raspberry Pi 5 possui um sistema de gerenciamento térmico bastante interessante. Além de permitir a leitura da temperatura do processador, o Linux disponibiliza informações sobre o controlador do cooler, PWM, rotação do ventilador, estado térmico e os chamados thermal trip points.

Neste artigo vamos criar um monitor em Python capaz de acompanhar essas informações diretamente através do sistema de arquivos virtual do Linux.

O programa não utiliza bibliotecas externas. Ele acessa diretamente os arquivos disponibilizados pelo kernel em:

/sys/class/thermal
/sys/class/hwmon
/proc/stat

Isso torna o monitor leve e adequado para ser executado continuamente em um Raspberry Pi 5.


1. O que o programa monitora

O monitor apresenta várias informações importantes:

  • temperatura atual da CPU;
  • temperatura mínima registrada;
  • temperatura máxima registrada;
  • utilização da CPU;
  • controlador do cooler;
  • valor PWM aplicado ao cooler;
  • rotação do cooler em RPM;
  • modo automático ou manual do PWM;
  • estado atual do sistema de refrigeração;
  • estado máximo do sistema de refrigeração;
  • tipo do dispositivo de cooling;
  • todos os thermal trip points disponíveis.

A tela do programa fica aproximadamente assim:

============================================================
Raspberry Pi 5 - MONITOR
============================================================
Hora       : 17/09/2026 08:45:32

CPU
------------------------------------------------------------
Temperatura :   47.8 °C
Temp Min    :   46.9 °C
Temp Max    :   52.3 °C
CPU Load    :    8.7 %

COOLER
------------------------------------------------------------
HWMon       : /sys/class/hwmon/hwmon2
PWM         :     85 / 255
RPM         :   1820
Modo        : automático
PWM Enable  : 1
Thermal     : 1 / 4

THERMAL TRIP POINTS
------------------------------------------------------------
Trip 0    :    50.0 °C  active
Trip 1    :    60.0 °C  active
Trip 2    :    70.0 °C  active
Trip 3    :    80.0 °C  critical

Cooling device: pwm-fan
============================================================


Figura acima mostra o programa em python rodando, Acesso via RealVNC.

Os valores acima são apenas ilustrativos. Os valores reais dependem da configuração do Raspberry Pi, do kernel e da temperatura no momento da leitura.


2. Como o Linux disponibiliza essas informações

Uma das características interessantes do Linux é que muitos dispositivos podem ser monitorados através do filesystem virtual.

No Raspberry Pi 5, a temperatura da CPU pode ser encontrada em:

/sys/class/thermal/thermal_zone0/temp

O valor normalmente é fornecido em milésimos de grau Celsius.

Por exemplo:

47800

corresponde a:

47,8 °C

No programa fazemos essa conversão:

return int(value) / 1000.0

3. Medindo a temperatura

A função responsável pela temperatura é:

def get_temperature():
    value = read_file(os.path.join(THERMAL_ZONE, "temp"))

    if value is None:
        return None

    try:
        return int(value) / 1000.0
    except ValueError:
        return None

A vantagem de utilizar /sys/class/thermal é que não precisamos instalar uma biblioteca específica para acessar o sensor.


4. Medindo a utilização da CPU

A utilização do processador é obtida através de:

/proc/stat

O Linux mantém nessa estrutura informações acumuladas sobre o tempo utilizado pela CPU.

O programa lê os campos:

user
nice
system
idle
iowait
irq
softirq
steal

Depois calcula a diferença entre duas leituras consecutivas.

A lógica é:

tempo total
     │
     ├── tempo ocupado
     │
     └── tempo ocioso

A utilização é calculada por:

usage = 100.0 * (total_delta - idle_delta) / total_delta

Essa abordagem é interessante porque não depende de ferramentas externas como top.


5. Localizando o controlador do cooler

Uma particularidade importante é que não devemos assumir que o cooler estará sempre em:

/sys/class/hwmon/hwmon3

O número do diretório hwmon pode mudar.

Por isso o programa procura automaticamente o dispositivo cujo nome seja:

pwmfan

A função utilizada é:

def find_cooling_fan_hwmon():
    base = "/sys/class/hwmon"

    if not os.path.exists(base):
        return None

    for name in sorted(os.listdir(base)):
        if not name.startswith("hwmon"):
            continue

        path = os.path.join(base, name)

        device_name = read_file(
            os.path.join(path, "name")
        )

        if device_name == "pwmfan":
            return path

    return None

Essa abordagem torna o programa mais robusto.


6. PWM do cooler

Depois de localizar o pwmfan, podemos consultar:

pwm1

O programa faz:

pwm = read_file(
    os.path.join(hwmon, "pwm1")
)

O valor normalmente está na faixa:

0 ... 255

Portanto, por exemplo:

PWM = 128

representa aproximadamente metade da escala disponível.

É importante observar que PWM não é necessariamente igual a uma porcentagem exata de rotação do ventilador. O comportamento mecânico do fan e o controlador utilizado precisam ser considerados.


7. RPM do cooler

Quando o driver disponibiliza a leitura da rotação, ela pode ser encontrada em:

fan1_input

O programa lê:

rpm = read_file(
    os.path.join(hwmon, "fan1_input")
)

Assim podemos obter algo como:

RPM : 1850

Essa informação é particularmente interessante para diagnosticar o cooler.

Por exemplo, podemos observar situações como:

Temperatura sobe
       ↓
PWM aumenta
       ↓
RPM aumenta
       ↓
Temperatura estabiliza

Esse comportamento fornece uma indicação muito mais completa do funcionamento do sistema de refrigeração do que observar somente a temperatura.


8. Modo automático do cooler

Outro parâmetro importante é:

pwm1_enable

O programa interpreta esse valor através da função:

def get_pwm_mode(pwm_enable):
    if pwm_enable == 1:
        return "automático"
    elif pwm_enable == 0:
        return "manual"
    else:
        return "desconhecido"

Assim podemos verificar se o controlador está trabalhando em modo automático ou manual.

Para um sistema que queremos deixar responsável pelo próprio gerenciamento térmico, o modo automático é particularmente importante.


9. Thermal cooling state

Além do PWM, o kernel Linux mantém um estado para o dispositivo de refrigeração:

/sys/class/thermal/cooling_device0/cur_state

O programa lê esse valor com:

def get_thermal_state():
    value = read_file(
        os.path.join(COOLING_DEVICE, "cur_state")
    )

    if value is None:
        return None

    try:
        return int(value)
    except ValueError:
        return None

Também é possível consultar o valor máximo:

/sys/class/thermal/cooling_device0/max_state

Isso permite apresentar:

Thermal : 1 / 4

ou seja, o estado atual em relação ao número máximo de estados disponibilizados pelo dispositivo.


10. Thermal trip points

Uma das partes mais interessantes do programa é a leitura dos thermal trip points.

O kernel pode disponibilizar arquivos como:

trip_point_0_temp
trip_point_0_type

trip_point_1_temp
trip_point_1_type

trip_point_2_temp
trip_point_2_type

e assim por diante.

O programa percorre automaticamente esses arquivos:

def get_trip_points():

    trip_points = []

    index = 0

    while True:

        temp_path = os.path.join(
            THERMAL_ZONE,
            f"trip_point_{index}_temp"
        )

        type_path = os.path.join(
            THERMAL_ZONE,
            f"trip_point_{index}_type"
        )

        temp = read_file(temp_path)
        trip_type = read_file(type_path)

        if temp is None and trip_type is None:
            break

        try:
            temperature = int(temp) / 1000.0
        except (ValueError, TypeError):
            temperature = None

        trip_points.append({
            "index": index,
            "temperature": temperature,
            "type": trip_type or "unknown",
        })

        index += 1

    return trip_points

Isso evita assumir antecipadamente quantos níveis existem.


11. Por que os trip points são importantes?

Os trip points fazem parte do mecanismo térmico do kernel.

Eles representam temperaturas associadas a diferentes ações térmicas.

Por exemplo, um sistema pode ter pontos associados a estados como:

active
passive
hot
critical

O significado exato e os valores dependem da configuração do sistema e do driver.

Portanto, não devemos simplesmente assumir que determinado valor, como 70 °C, significa obrigatoriamente que o cooler deverá estar em determinada rotação.

O importante é observar o comportamento real do conjunto:

Temperatura
     │
     ▼
Thermal zone
     │
     ▼
Thermal framework
     │
     ▼
Cooling device
     │
     ▼
PWM
     │
     ▼
Fan
     │
     ▼
RPM

12. Atualização a cada dois segundos

O programa utiliza:

INTERVAL = 2

Portanto, a cada dois segundos ele:

  1. lê a temperatura;
  2. atualiza mínimo e máximo;
  3. calcula a utilização da CPU;
  4. procura os dados do cooler;
  5. lê o estado térmico;
  6. lê os trip points;
  7. atualiza a tela.

No final do ciclo:

time.sleep(INTERVAL)

A tela é limpa antes de cada atualização:

os.system("clear")

Dessa maneira temos um monitor semelhante a uma pequena aplicação de diagnóstico, mas executado diretamente no terminal.


13. Executando o programa

Salve o arquivo, por exemplo:

rpi-monitor.py

Depois dê permissão de execução:

chmod +x rpi-monitor.py

Execute:

./rpi-monitor.py

ou:

python3 rpi-monitor.py

Para encerrar:

Ctrl + C

O programa trata a interrupção através de:

except KeyboardInterrupt:
    print()
    print("Monitor encerrado.")

14. Uma vantagem importante: nenhuma biblioteca externa

O programa utiliza somente módulos que fazem parte da instalação padrão do Python:

import os
import time
from datetime import datetime

Não é necessário instalar:

pip
numpy
psutil
matplotlib

nem qualquer outra biblioteca.

Isso é uma vantagem para um Raspberry Pi que esteja sendo utilizado como servidor, pois o monitor possui um consumo muito pequeno de recursos.


15. O que podemos diagnosticar com esse monitor?

Esse pequeno programa pode ser utilizado para investigar diversos problemas.

Temperatura elevada

Podemos observar:

CPU Temperature
CPU Load
PWM
RPM

Se a CPU estiver com carga elevada, uma temperatura maior pode ser perfeitamente esperada.


Cooler não acionando

Uma situação interessante seria:

Temperatura : 70 °C
PWM         : 0
RPM         : 0

Nesse caso existe uma indicação de que precisamos investigar o controlador térmico, o driver ou a conexão do cooler.


Cooler acionando

Outro cenário:

Temperatura : 65 °C
PWM         : 120
RPM         : 2100

Nesse caso conseguimos observar que o sistema está efetivamente comandando o ventilador.


Cooler aumentando a rotação

O cenário mais interessante para um teste é observar uma sequência como:

50 °C → PWM 60  → 1200 RPM
55 °C → PWM 90  → 1700 RPM
60 °C → PWM 130 → 2200 RPM
65 °C → PWM 170 → 2800 RPM

Os valores acima são apenas um exemplo ilustrativo.

O importante é observar a relação entre temperatura, PWM e RPM no equipamento real.


16. Teste de funcionamento do cooler

Podemos utilizar o monitor juntamente com uma carga artificial da CPU.

Por exemplo, podemos instalar:

sudo apt install stress-ng

e executar:

stress-ng --cpu 4 --timeout 120s

Enquanto isso, em outro terminal:

python3 rpi-monitor.py

Assim podemos acompanhar a evolução da temperatura.

O objetivo não é simplesmente alcançar uma temperatura elevada, mas observar o comportamento do sistema:

Carga CPU
   ↓
Temperatura aumenta
   ↓
Controle térmico reage
   ↓
PWM aumenta
   ↓
RPM aumenta
   ↓
Temperatura estabiliza

Esse teste permite verificar de forma prática a interação entre o processador, o kernel e o sistema de refrigeração.


17. Conclusão

O Raspberry Pi 5 oferece ao Linux uma quantidade bastante interessante de informações sobre seu funcionamento interno.

Com poucas linhas de Python é possível acessar diretamente:

/sys/class/thermal
/sys/class/hwmon
/proc/stat

Permite  construir uma ferramenta de diagnóstico sem depender de grandes frameworks ou bibliotecas externas.

O ponto mais interessante desse projeto é que não estamos apenas olhando a temperatura da CPU. Estamos acompanhando todo o caminho do gerenciamento térmico:

           

Esse tipo de monitor é especialmente útil quando o Raspberry Pi 5 está sendo utilizado como servidor, pois permite acompanhar o comportamento térmico sem a necessidade de uma interface gráfica pesada.

No meu caso, essa ferramenta também serve como uma etapa de diagnóstico do Raspberry Pi 5, permitindo verificar não somente a temperatura do processador, mas se o sistema de refrigeração está realmente respondendo ao aumento de temperatura.

O próximo passo natural é transformar esse monitor em uma pequena aplicação gráfica, com gráfico de temperatura, carga da CPU, PWM e RPM ao longo do tempo, permitindo visualizar o comportamento térmico do Raspberry Pi 5 de forma muito mais clara.

 

 

Código Fonte Completo

Código-fonte explicado por etapas

Em vez de apresentar o programa completo de uma única vez, vamos analisar suas principais partes. Dessa forma, fica mais fácil entender como o Python obtém as informações térmicas diretamente das interfaces disponibilizadas pelo kernel Linux.

1. Configuração inicial

O programa utiliza somente módulos da biblioteca padrão do Python e define os caminhos das interfaces térmica e de refrigeração do Raspberry Pi 5.

#!/usr/bin/env python3

import os
import time
from datetime import datetime


INTERVAL = 2

THERMAL_ZONE = "/sys/class/thermal/thermal_zone0"
COOLING_DEVICE = "/sys/class/thermal/cooling_device0"

O intervalo de atualização é de dois segundos. As informações térmicas são obtidas diretamente através de /sys/class/thermal e /sys/class/hwmon.

2. Função auxiliar para leitura do sistema

def read_file(path, default=None):
    try:
        with open(path, "r") as file:
            return file.read().strip()
    except Exception:
        return default

Essa função centraliza a leitura dos arquivos do sistema. Caso um arquivo não exista ou não possa ser lido, o programa retorna None em vez de interromper a execução.

3. Leitura da temperatura da CPU

A temperatura da CPU é disponibilizada pelo kernel através do arquivo /sys/class/thermal/thermal_zone0/temp. O valor normalmente é fornecido em milésimos de grau Celsius.

def get_temperature():
    value = read_file(
        os.path.join(THERMAL_ZONE, "temp")
    )

    if value is None:
        return None

    try:
        return int(value) / 1000.0
    except ValueError:
        return None

Por exemplo, um valor de 47800 corresponde a 47,8 °C.

4. Leitura da utilização da CPU

A utilização do processador é calculada a partir dos contadores disponibilizados pelo Linux em /proc/stat. O programa compara duas leituras consecutivas para determinar a porcentagem de tempo em que a CPU esteve ocupada.

def get_cpu_load():
    line = read_file("/proc/stat")

    if not line:
        return None

    values = line.split()

    if values[0] != "cpu":
        return None

    try:
        user = int(values[1])
        nice = int(values[2])
        system = int(values[3])
        idle = int(values[4])
        iowait = int(values[5])
        irq = int(values[6])
        softirq = int(values[7])
        steal = int(values[8])
    except (ValueError, IndexError):
        return None

    idle_time = idle + iowait

    total = (
        user
        + nice
        + system
        + idle
        + iowait
        + irq
        + softirq
        + steal
    )

    return total, idle_time
def cpu_load(previous):
    current = get_cpu_load()

    if current is None:
        return None, previous

    if previous is None:
        return 0.0, current

    total_delta = current[0] - previous[0]
    idle_delta = current[1] - previous[1]

    if total_delta <= 0:
        return 0.0, current

    usage = (
        100.0 *
        (total_delta - idle_delta) /
        total_delta
    )

    return usage, current

5. Localizando automaticamente o cooler

O programa não assume que o controlador do cooler estará sempre em um diretório específico como hwmon3. O número do dispositivo pode variar. Por isso, o código procura automaticamente o dispositivo cujo nome seja pwmfan.

def find_cooling_fan_hwmon():
    """
    Procura automaticamente o hwmon associado ao cooler.
    Não assume que o diretório será hwmon3.
    """

    base = "/sys/class/hwmon"

    if not os.path.exists(base):
        return None

    for name in sorted(os.listdir(base)):
        if not name.startswith("hwmon"):
            continue

        path = os.path.join(base, name)

        device_name = read_file(
            os.path.join(path, "name")
        )

        if device_name == "pwmfan":
            return path

    return None

Essa abordagem torna o monitor mais robusto, pois ele procura o controlador pelo nome informado pelo driver em vez de depender de um número fixo.

6. Leitura do PWM, RPM e estado do cooler

Depois de localizar o controlador, o programa procura as interfaces pwm1pwm1_enable e fan1_input.

def get_fan_data():
    """
    Lê PWM, RPM e modo do cooler.
    """

    hwmon = find_cooling_fan_hwmon()

    if hwmon is None:
        return None

    pwm = read_file(os.path.join(hwmon, "pwm1"))
    pwm_enable = read_file(
        os.path.join(hwmon, "pwm1_enable")
    )
    rpm = read_file(
        os.path.join(hwmon, "fan1_input")
    )

    try:
        pwm = int(pwm) if pwm is not None else None
    except ValueError:
        pwm = None

    try:
        pwm_enable = (
            int(pwm_enable)
            if pwm_enable is not None
            else None
        )
    except ValueError:
        pwm_enable = None

    try:
        rpm = int(rpm) if rpm is not None else None
    except ValueError:
        rpm = None

    return {
        "hwmon": hwmon,
        "pwm": pwm,
        "pwm_enable": pwm_enable,
        "rpm": rpm,
    }

O valor pwm1 representa o valor de PWM disponibilizado pelo driver. Em muitos sistemas ele utiliza uma escala de 0 a 255. Quando disponível, fan1_input fornece a rotação do ventilador em RPM.

O campo pwm1_enable deve ser interpretado de acordo com o driver utilizado. Por isso, o programa mantém o valor bruto em vez de assumir que um determinado valor significa automaticamente “manual” ou “automático”.

7. Estado térmico do cooling device

O framework térmico do Linux também disponibiliza o estado atual e o estado máximo do dispositivo de refrigeração.

def get_thermal_state():
    value = read_file(
        os.path.join(
            COOLING_DEVICE,
            "cur_state"
        )
    )

    if value is None:
        return None

    try:
        return int(value)
    except ValueError:
        return None


def get_thermal_max_state():
    value = read_file(
        os.path.join(
            COOLING_DEVICE,
            "max_state"
        )
    )

    if value is None:
        return None

    try:
        return int(value)
    except ValueError:
        return None

Esses valores permitem acompanhar a atuação do dispositivo dentro do framework térmico do kernel.

8. Thermal Trip Points

Os thermal trip points são pontos de temperatura disponibilizados pela thermal zone. O programa percorre automaticamente todos os pontos existentes.

def get_trip_points():
    """
    Lê todos os trip points disponíveis na thermal_zone0.
    """

    trip_points = []

    index = 0

    while True:

        temp_path = os.path.join(
            THERMAL_ZONE,
            f"trip_point_{index}_temp"
        )

        type_path = os.path.join(
            THERMAL_ZONE,
            f"trip_point_{index}_type"
        )

        temp = read_file(temp_path)
        trip_type = read_file(type_path)

        if temp is None and trip_type is None:
            break

        try:
            temperature = int(temp) / 1000.0
        except (ValueError, TypeError):
            temperature = None

        trip_points.append({
            "index": index,
            "temperature": temperature,
            "type": trip_type or "unknown",
        })

        index += 1

    return trip_points

Os arquivos seguem uma estrutura como trip_point_0_temptrip_point_0_typetrip_point_1_temptrip_point_1_type e assim por diante.

9. Identificação do cooling device

def get_cooling_device():
    device_type = read_file(
        os.path.join(
            COOLING_DEVICE,
            "type"
        )
    )

    return device_type

Essa função consulta o tipo do dispositivo de refrigeração associado ao cooling_device.

10. Loop principal do monitor

A cada ciclo, o programa coleta as informações e atualiza a tela.

while True:

    temperature = get_temperature()

    load, previous_cpu = cpu_load(previous_cpu)

    fan = get_fan_data()

    thermal_state = get_thermal_state()
    thermal_max_state = get_thermal_max_state()

    cooling_type = get_cooling_device()

    trip_points = get_trip_points()

    clear_screen()

    # Exibição dos dados...

    time.sleep(INTERVAL)

O intervalo definido no início do programa é de dois segundos. O monitor continua executando até que o usuário pressione Ctrl+C.

11. Encerramento do programa

try:

    while True:
        # monitoramento

        time.sleep(INTERVAL)

except KeyboardInterrupt:

    print()
    print("Monitor encerrado.")

O tratamento de KeyboardInterrupt permite encerrar o monitor de forma limpa utilizando Ctrl+C.

Código-fonte completo

Os blocos apresentados acima mostram as principais partes do monitor e explicam como o programa obtém as informações diretamente das interfaces do kernel Linux.

O código-fonte completo está disponível para download no arquivo rpi-monitor.py. O arquivo original deve ser utilizado para execução, preservando integralmente sua indentação e formatação Python.

⬇ Baixar rpi-monitor.py

O arquivo pode ser executado no Raspberry Pi 5 com:

python3 rpi-monitor.py

Próximo passo

Uma evolução natural deste projeto seria transformar o monitor de terminal em uma aplicação capaz de registrar os dados ao longo do tempo e apresentar graficamente a temperatura da CPU, a utilização do processador, o PWM do cooler, a rotação do ventilador e o estado térmico.

Dessa forma, seria possível visualizar graficamente a resposta do sistema de refrigeração à variação da carga do Raspberry Pi 5

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *