Object storage (COS) has anti-theft chain settings, browser access may be blocked, but command line access is normal. But to make it easier to access, we’ve moved it to github.
If you have any other questions, please let me know.
Your script suggests to install libmraa and rockpi4-dtbo. The newest edition of libmraa is libmraa2 and rockpi4-dtbo I couldn’t find in any repository.
Your complaint is a common refrain on here, unfortunately. Some queries like yours will get an actual response, with varying degrees of usefulness. Others will just languish forever, getting no response at all.
This isn’t to say that there are no people on here who can help; I believe there are many very knowledgeable folks, but they’re just not of the sort who are going to go out of their way to assist. Then there are the people I think of as the “Radxa apologists”, who will (unhelpfully) tell you that if you want to fix your problem, you need to be running a supported distribution…neglecting the fact that most or all of those are literally years out of date.
I’ve seen the Radxa folks themselves explain that it’s more profitable to continually release new hardware than it is to make sure that the associated software stays fresh, especially if makers of similar hardware are just going to “steal” the fruits of their labor anyway. I would argue that this is how the world of Open Source works, but I do understand how that would be frustrating. Unfortunately, the end result is that the people who bought these devices in quantity for some sort of project fare just fine, while the folks like us who bought one because it was what we thought was the best option end up disappointed.
All that said, there are libraries available for controlling a fan via PWM, if you wanted to try rolling your own solution. I’m facing a similar conundrum, since I’ve been able to get basic on/off functionality to work fine, while variable speeds via PWM have so far eluded me.
I’m sorry that you ended up in a similar place as quite a number of others on here. I know exactly how disappointing that can be.
I’m sharing this here since I believe others might be facing the same issue.
Please make any modifications yourself as needed.
#!/usr/bin/env python3
import os
import time
import glob
from configparser import ConfigParser
# Constants
GPIO_FAN = 154 # GPIO154 for fan ON/OFF control
PERIOD = 1000000 # 1ms (1kHz)
CONFIG_PATH = "/etc/rockpi-poe.conf"
PWM1_DEVICE = "ff420010.pwm" # Device corresponding to PWM1
def get_pwm_chip():
"""Automatically get the pwmchipX corresponding to PWM1 (ff420010.pwm)"""
pwm_chips = glob.glob("/sys/class/pwm/pwmchip*")
for chip in pwm_chips:
device_path = os.path.realpath(f"{chip}/device")
if PWM1_DEVICE in device_path:
return os.path.basename(chip) # Get pwmchipX
return None
# Dynamically get pwmchipX
PWM_CHIP = get_pwm_chip()
PWM_CHANNEL = "pwm0"
PWM_PATH = f"/sys/class/pwm/{PWM_CHIP}/{PWM_CHANNEL}/"
if PWM_CHIP is None:
raise RuntimeError(f"PWM1 ({PWM1_DEVICE}) pwmchipX not found!")
def export_gpio():
"""Export GPIO154 (only on first execution)"""
if not os.path.exists(f"/sys/class/gpio/gpio{GPIO_FAN}"):
with open("/sys/class/gpio/export", "w") as f:
f.write(str(GPIO_FAN))
time.sleep(1) # Wait for reflection
def set_gpio(value):
"""Control ON/OFF of GPIO154"""
with open(f"/sys/class/gpio/gpio{GPIO_FAN}/direction", "w") as f:
f.write("out")
with open(f"/sys/class/gpio/gpio{GPIO_FAN}/value", "w") as f:
f.write(str(value))
def enable_pwm():
"""Enable PWM"""
if not os.path.exists(PWM_PATH):
with open(f"/sys/class/pwm/{PWM_CHIP}/export", "w") as f:
f.write("0")
time.sleep(2) # Wait for reflection
# Wait until the pwm0 directory is created
timeout = 5
while not os.path.exists(PWM_PATH) and timeout > 0:
time.sleep(0.5)
timeout -= 1
if not os.path.exists(PWM_PATH):
raise RuntimeError(f"PWM0 directory was not created: {PWM_PATH}")
# Apply settings
write_pwm("period", PERIOD)
write_pwm("enable", 1)
write_pwm("polarity", "normal")
def write_pwm(filename, value):
"""Write PWM settings"""
with open(f"{PWM_PATH}{filename}", "w") as f:
f.write(str(value))
def read_sensor_temp():
"""Get temperature from PoE HAT sensor"""
try:
v2t = lambda x: 42 + (960 - x) * 0.05 # Temperature conversion formula
with open('/sys/bus/iio/devices/iio:device0/in_voltage0_raw') as f:
raw_value = f.read().strip()
if not raw_value.isdigit(): # If not a number, return an invalid value
return 0
return v2t(int(raw_value))
except Exception:
return 0 # Return 0 if unable to retrieve
def read_soc_temp(zone=0):
"""Get SoC (CPU/GPU) temperature"""
try:
with open(f"/sys/class/thermal/thermal_zone{zone}/temp") as f:
return int(f.read().strip()) / 1000.0 # Convert from milli-degrees Celsius to Celsius
except Exception:
return 0 # Return 0 if unable to retrieve
def read_temp():
"""Get the highest temperature among PoE HAT, CPU, and GPU"""
return max(read_sensor_temp(), read_soc_temp(0), read_soc_temp(1))
def read_conf():
"""Read temperature thresholds from the configuration file"""
conf = {"lv0": 40, "lv1": 45, "lv2": 50, "lv3": 55}
try:
cfg = ConfigParser()
cfg.read(CONFIG_PATH)
conf["lv0"] = cfg.getint("fan", "lv0", fallback=40)
conf["lv1"] = cfg.getint("fan", "lv1", fallback=45)
conf["lv2"] = cfg.getint("fan", "lv2", fallback=50)
conf["lv3"] = cfg.getint("fan", "lv3", fallback=55)
except Exception:
pass
return conf
def set_fan_speed():
"""Adjust fan speed according to temperature"""
conf = read_conf()
while True:
temp = read_temp()
# Default to minimum rotation (25%)
duty_cycle = 0.75 * PERIOD
level = "25%" # Initial value
if temp == 0:
print("Temperature sensor data unavailable!")
level = "25%" # Set to minimum rotation for safety
elif temp >= conf["lv3"]:
duty_cycle = PERIOD
level = "100%"
elif temp >= conf["lv2"]:
duty_cycle = 0.75 * PERIOD
level = "75%"
elif temp >= conf["lv1"]:
duty_cycle = 0.50 * PERIOD
level = "50%"
elif temp >= conf["lv0"]:
duty_cycle = 0.25 * PERIOD
level = "25%"
else:
duty_cycle = 0
level = "Stopped"
# Output temperature + fan speed level only
print(f"{temp}°C → Fan speed: {level}")
write_pwm("duty_cycle", int(duty_cycle))
time.sleep(10)
def start():
"""Start the fan"""
export_gpio()
set_gpio(1) # Turn GPIO154 ON
enable_pwm()
set_fan_speed()
def stop():
"""Stop the fan"""
set_gpio(0) # Turn GPIO154 OFF
if os.path.exists(PWM_PATH):
write_pwm("enable", 0)
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Usage: python3 rockpi-poe.py start|stop")
sys.exit(1)
if sys.argv[1] == "start":
start()
elif sys.argv[1] == "stop":
stop()
else:
print("Usage: python3 rockpi-poe.py start|stop")