Add ROCK 5C service and monitoring setup

This commit is contained in:
wimby
2026-09-26 02:24:18 +02:00
parent 301c8309ab
commit e3bd6b6268
10 changed files with 237 additions and 1 deletions

8
.env.example Normal file
View File

@@ -0,0 +1,8 @@
# ROCK 5C + Radxa Penta SATA HAT Top Board
SDA=I2C8_SDA_M2
SCL=I2C8_SCL_M2
OLED_RESET=GPIO1_A5
PWMCHIP=1
BUTTON_CHIP=4
BUTTON_LINE=11
HARDWARE_PWM=1

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.env
.venv/
__pycache__/
*.py[cod]

View File

@@ -0,0 +1,77 @@
# Radxa Penta SATA HAT controller on ROCK 5C
Local ROCK 5C setup for the Radxa Penta SATA HAT Top Board.
It runs the existing Python OLED/fan controller as a systemd service and can
export fan/temperature state through node_exporter's textfile collector.
## ROCK 5C hardware setup
The current host uses these device-tree overlays:
- `rk3588-i2c8-m2`
- `rk3588-pwm14-m1`
The OLED is an SSD1306 on `/dev/i2c-8` at address `0x3c`. The HAT fan
uses hardware PWM exposed as `/sys/class/pwm/pwmchip1/pwm0`.
Copy the board environment before starting the service:
```bash
cp .env.example .env
```
The service intentionally runs as root because the controller needs I2C,
GPIO and PWM access.
## Dependencies
Ubuntu packages:
```bash
sudo apt install python3-virtualenv python3-libgpiod gpiod
```
Create the virtualenv with system packages available and install Python
dependencies:
```bash
virtualenv --system-site-packages .venv
.venv/bin/pip install -r requirements.txt
```
## systemd
The unit currently assumes this repository is located at
`/home/ubuntu/radxa-hat`.
```bash
sudo cp radxa-hat.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now radxa-hat.service
systemctl status radxa-hat.service
```
## Prometheus / node_exporter
`export-prometheus.sh` writes
`/var/lib/node_exporter/textfile_collector/radxa_hat.prom`.
The exported values include:
- `radxa_hat_service_up`
- `radxa_hat_oled_i2c_present`
- `radxa_hat_cpu_temperature_celsius`
- `radxa_hat_pwm_duty_ratio` — raw kernel PWM duty
- `radxa_hat_fan_command_ratio` — effective fan command, corrected for the
inverted HAT PWM mapping
Install the timer with:
```bash
sudo cp radxa-hat-metrics.service radxa-hat-metrics.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now radxa-hat-metrics.timer
```
The timer refreshes metrics every 15 seconds.

68
export-prometheus.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/bin/bash
set -eu
OUTDIR=/var/lib/node_exporter/textfile_collector
OUT="$OUTDIR/radxa_hat.prom"
TMP="$OUT.tmp.$$"
PWM=/sys/class/pwm/pwmchip1/pwm0
mkdir -p "$OUTDIR"
service_up=0
systemctl is-active --quiet radxa-hat.service && service_up=1
i2c_present=0
[ -e /dev/i2c-8 ] && i2c_present=1
temp_c="NaN"
if [ -r /sys/class/thermal/thermal_zone0/temp ]; then
raw=$(cat /sys/class/thermal/thermal_zone0/temp)
temp_c=$(awk -v v="$raw" 'BEGIN { printf "%.3f", v/1000 }')
fi
period=0
duty=0
enabled=0
polarity="unknown"
[ -r "$PWM/period" ] && period=$(cat "$PWM/period")
[ -r "$PWM/duty_cycle" ] && duty=$(cat "$PWM/duty_cycle")
[ -r "$PWM/enable" ] && enabled=$(cat "$PWM/enable")
[ -r "$PWM/polarity" ] && polarity=$(cat "$PWM/polarity")
duty_ratio="NaN"
fan_command_ratio="NaN"
if [ "$period" -gt 0 ] 2>/dev/null; then
duty_ratio=$(awk -v d="$duty" -v p="$period" 'BEGIN { printf "%.6f", d/p }')
fan_command_ratio=$(awk -v d="$duty" -v p="$period" 'BEGIN { printf "%.6f", 1-(d/p) }')
fi
cat >"$TMP" <<EOF
# HELP radxa_hat_service_up Whether radxa-hat.service is active.
# TYPE radxa_hat_service_up gauge
radxa_hat_service_up $service_up
# HELP radxa_hat_oled_i2c_present Whether the HAT I2C bus device exists.
# TYPE radxa_hat_oled_i2c_present gauge
radxa_hat_oled_i2c_present $i2c_present
# HELP radxa_hat_cpu_temperature_celsius CPU temperature used by the fan controller.
# TYPE radxa_hat_cpu_temperature_celsius gauge
radxa_hat_cpu_temperature_celsius $temp_c
# HELP radxa_hat_pwm_enabled Kernel PWM channel enable state.
# TYPE radxa_hat_pwm_enabled gauge
radxa_hat_pwm_enabled $enabled
# HELP radxa_hat_pwm_period_nanoseconds Raw PWM period from sysfs.
# TYPE radxa_hat_pwm_period_nanoseconds gauge
radxa_hat_pwm_period_nanoseconds $period
# HELP radxa_hat_pwm_duty_cycle_nanoseconds Raw PWM duty cycle from sysfs.
# TYPE radxa_hat_pwm_duty_cycle_nanoseconds gauge
radxa_hat_pwm_duty_cycle_nanoseconds $duty
# HELP radxa_hat_pwm_duty_ratio Raw sysfs duty divided by period.
# TYPE radxa_hat_pwm_duty_ratio gauge
radxa_hat_pwm_duty_ratio $duty_ratio
# HELP radxa_hat_fan_command_ratio Effective fan command ratio for the inverted HAT PWM, 0=minimum/off and 1=full.
# TYPE radxa_hat_fan_command_ratio gauge
radxa_hat_fan_command_ratio $fan_command_ratio
# HELP radxa_hat_pwm_polarity_info PWM polarity information.
# TYPE radxa_hat_pwm_polarity_info gauge
radxa_hat_pwm_polarity_info{polarity="$polarity"} 1
EOF
chmod 0644 "$TMP"
mv -f "$TMP" "$OUT"

View File

@@ -53,7 +53,8 @@ def read_conf():
try:
cfg = ConfigParser()
cfg.read('/etc/rockpi-penta.conf')
#cfg.read('/etc/rockpi-penta.conf')
cfg.read('./rockpi-penta.conf')
# fan
conf['fan']['lv0'] = cfg.getfloat('fan', 'lv0')
conf['fan']['lv1'] = cfg.getfloat('fan', 'lv1')

View File

@@ -0,0 +1,8 @@
[Unit]
Description=Export Radxa HAT metrics for node_exporter
After=radxa-hat.service
[Service]
Type=oneshot
User=root
ExecStart=/home/ubuntu/radxa-hat/export-prometheus.sh

11
radxa-hat-metrics.timer Normal file
View File

@@ -0,0 +1,11 @@
[Unit]
Description=Refresh Radxa HAT Prometheus metrics
[Timer]
OnBootSec=20s
OnUnitActiveSec=15s
AccuracySec=1s
Persistent=true
[Install]
WantedBy=timers.target

21
radxa-hat.service Normal file
View File

@@ -0,0 +1,21 @@
[Unit]
Description=Radxa Penta SATA HAT OLED/Fan Controller
After=local-fs.target systemd-udev-settle.service
Wants=systemd-udev-settle.service
[Service]
Type=simple
User=root
WorkingDirectory=/home/ubuntu/radxa-hat
EnvironmentFile=/home/ubuntu/radxa-hat/.env
Environment=PYTHONUNBUFFERED=1
ExecStartPre=/usr/bin/test -e /dev/i2c-8
ExecStartPre=/usr/bin/test -d /sys/class/pwm/pwmchip1
ExecStart=/home/ubuntu/radxa-hat/.venv/bin/python -u /home/ubuntu/radxa-hat/main.py
Restart=always
RestartSec=3
KillSignal=SIGINT
TimeoutStopSec=10
[Install]
WantedBy=multi-user.target

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
adafruit-circuitpython-ssd1306
pillow

36
rockpi-penta.conf Normal file
View File

@@ -0,0 +1,36 @@
[fan]
# When the temperature is above lv0 (35'C), the fan at 25% power,
# and lv1 at 50% power, lv2 at 75% power, lv3 at 100% power.
# When the temperature is below lv0, the fan is turned off.
# You can change these values if necessary.
lv0 = 35
lv1 = 40
lv2 = 45
lv3 = 50
[key]
# You can customize the function of the key, currently available functions are
# slider: oled display next page
# switch: fan turn on/off switch
# reboot, poweroff
# If you have any good suggestions for key functions,
# please add an issue on https://github.com/radxa/rockpi-penta/issues
click = slider
twice = switch
press = none
[time]
# twice: maximum time between double clicking (seconds)
# press: long press time (seconds)
twice = 0.7
press = 1.8
[slider]
# Whether the oled auto display next page and the time interval (seconds)
auto = true
time = 10
[oled]
# Whether rotate the text of oled 180 degrees, whether use Fahrenheit
rotate = false
f-temp = false