Sorry for the late reply, Iām not going to be around the RPi much due to the ending of the summer holidays.
āThankfullyā today I managed to reproduce the problem for once.
I had to press the power on button three times to turn the RPi on for some reason, but the LEDs and fan were off before (behaving normally). It could just be me not pressing hard enough properly.
No matter, I will provide what you wanted. I will repost my argononed.py again because I believe I changed it while playing around.
#!/usr/bin/python3
#
# This script set fan speed and monitor power button events.
#
# Fan Speed is set by sending 0 to 100 to the MCU (Micro Controller Unit)
# The values will be interpreted as the percentage of fan speed, 100% being maximum
#
# Power button events are sent as a pulse signal to BCM Pin 4 (BOARD P7)
# A pulse width of 20-30ms indicates reboot request (double-tap)
# A pulse width of 40-50ms indicates shutdown request (hold and release after 3 secs)
#
# Additional comments are found in each function below
#
# Standard Deployment/Triggers:
# * Raspbian, OSMC: Runs as service via /lib/systemd/system/argononed.service
# * lakka, libreelec: Runs as service via /storage/.config/system.d/argononed.service
# * recalbox: Runs as service via /etc/init.d/
#
import sys
import os
import time
from threading import Thread
from queue import Queue
sys.path.append("/etc/argon/")
from argonsysinfo import *
from argonregister import *
from argonpowerbutton import *
# Initialize I2C Bus
bus = argonregister_initializebusobj()
OLED_ENABLED=False
if os.path.exists("/etc/argon/argoneonoled.py"):
import datetime
from argoneonoled import *
OLED_ENABLED=True
OLED_CONFIGFILE = "/etc/argoneonoled.conf"
UNIT_CONFIGFILE = "/etc/argonunits.conf"
# This function converts the corresponding fanspeed for the given temperature
# The configuration data is a list of strings in the form "<temperature>=<speed>"
def get_fanspeed(tempval, configlist):
for curconfig in configlist:
curpair = curconfig.split("=")
tempcfg = float(curpair[0])
fancfg = int(float(curpair[1]))
if tempval >= tempcfg:
if fancfg < 1:
return 0
elif fancfg < 25:
return 25
return fancfg
return 0
# This function retrieves the fanspeed configuration list from a file, arranged by temperature
# It ignores lines beginning with "#" and checks if the line is a valid temperature-speed pair
# The temperature values are formatted to uniform length, so the lines can be sorted properly
def load_config(fname):
newconfig = []
try:
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
tmpline = curline.strip()
if not tmpline:
continue
if tmpline[0] == "#":
continue
tmppair = tmpline.split("=")
if len(tmppair) != 2:
continue
tempval = 0
fanval = 0
try:
tempval = float(tmppair[0])
if tempval < 0 or tempval > 100:
continue
except:
continue
try:
fanval = int(float(tmppair[1]))
if fanval < 0 or fanval > 100:
continue
except:
continue
newconfig.append( "{:5.1f}={}".format(tempval,fanval))
if len(newconfig) > 0:
newconfig.sort(reverse=True)
except:
return []
return newconfig
# Load OLED Config file
def load_oledconfig(fname):
output={}
screenduration=-1
screenlist=[]
try:
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
tmpline = curline.strip()
if not tmpline:
continue
if tmpline[0] == "#":
continue
tmppair = tmpline.split("=")
if len(tmppair) != 2:
continue
if tmppair[0] == "switchduration":
output['screenduration']=int(tmppair[1])
elif tmppair[0] == "screensaver":
output['screensaver']=int(tmppair[1])
elif tmppair[0] == "screenlist":
output['screenlist']=tmppair[1].replace("\"", "").split(" ")
elif tmppair[0] == "enabled":
output['enabled']=tmppair[1].replace("\"", "")
except:
return {}
return output
# Load Unit Config file
def load_unitconfig(fname):
output={"temperature": "C"}
try:
with open(fname, "r") as fp:
for curline in fp:
if not curline:
continue
tmpline = curline.strip()
if not tmpline:
continue
if tmpline[0] == "#":
continue
tmppair = tmpline.split("=")
if len(tmppair) != 2:
continue
if tmppair[0] == "temperature":
output['temperature']=tmppair[1].replace("\"", "")
except:
return {}
return output
def load_fancpuconfig():
fanconfig = ["65=100", "60=90", "43=80"]
tmpconfig = load_config("/etc/argononed.conf")
if len(tmpconfig) > 0:
fanconfig = tmpconfig
return fanconfig
def load_fanhddconfig():
fanhddconfig = ["50=100", "45=55", "40=30"]
fanhddconfigfile = "/etc/argononed-hdd.conf"
if os.path.isfile(fanhddconfigfile):
tmpconfig = load_config(fanhddconfigfile)
if len(tmpconfig) > 0:
fanhddconfig = tmpconfig
else:
fanhddconfig = []
return fanhddconfig
# This function is the thread that monitors temperature and sets the fan speed
# The value is fed to get_fanspeed to get the new fan speed
# To prevent unnecessary fluctuations, lowering fan speed is delayed by 30 seconds
#
# Location of config file varies based on OS
#
def temp_check():
INITIALSPEEDVAL = 0 # ensures fan speed gets set during initialization (e.g. change settings)
argonregsupport = argonregister_checksupport(bus)
fanconfig = load_fancpuconfig()
fanhddconfig = load_fanhddconfig()
prevspeed=INITIALSPEEDVAL
while True:
# Speed based on CPU Temp
val = argonsysinfo_getcputemp()
newspeed = get_fanspeed(val, fanconfig)
# Speed based on HDD Temp
val = argonsysinfo_getmaxhddtemp()
tmpspeed = get_fanspeed(val, fanhddconfig)
# Use faster fan speed
if tmpspeed > newspeed:
newspeed = tmpspeed
if prevspeed == newspeed:
time.sleep(30)
continue
elif newspeed < prevspeed:
# Pause 30s before speed reduction to prevent fluctuations
time.sleep(30)
prevspeed = newspeed
try:
# if newspeed > 0:
# Spin up to prevent issues on older units
# argonregister_setfanspeed(bus, 100, argonregsupport)
# Set fan speed has sleep
argonregister_setfanspeed(bus, newspeed, argonregsupport)
time.sleep(30)
except IOError:
time.sleep(60)
#
# This function is the thread that updates OLED
#
def display_loop(readq):
weekdaynamelist = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
monthlist = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
oledscreenwidth = oled_getmaxX()
fontwdSml = 6 # Maps to 6x8
fontwdReg = 8 # Maps to 8x16
stdleftoffset = 54
temperature="C"
tmpconfig=load_unitconfig(UNIT_CONFIGFILE)
if "temperature" in tmpconfig:
temperature = tmpconfig["temperature"]
screensavermode = False
screensaversec = 120
screensaverctr = 0
screenenabled = ["clock", "ip"]
prevscreen = ""
curscreen = ""
screenid = 0
screenjogtime = 0
screenjogflag = 0 # start with screenid 0
cpuusagelist = []
curlist = []
tmpconfig=load_oledconfig(OLED_CONFIGFILE)
if "screensaver" in tmpconfig:
screensaversec = tmpconfig["screensaver"]
if "screenduration" in tmpconfig:
screenjogtime = tmpconfig["screenduration"]
if "screenlist" in tmpconfig:
screenenabled = tmpconfig["screenlist"]
if "enabled" in tmpconfig:
if tmpconfig["enabled"] == "N":
screenenabled = []
while len(screenenabled) > 0:
if len(curlist) == 0 and screenjogflag == 1:
# Reset Screen Saver
screensavermode = False
screensaverctr = 0
# Update screen info
screenid = screenid + screenjogflag
if screenid >= len(screenenabled):
screenid = 0
prevscreen = curscreen
curscreen = screenenabled[screenid]
if screenjogtime == 0:
# Resets jogflag (if switched manually)
screenjogflag = 0
else:
screenjogflag = 1
needsUpdate = False
if curscreen == "cpu":
# CPU Usage
if len(curlist) == 0:
try:
if len(cpuusagelist) == 0:
cpuusagelist = argonsysinfo_listcpuusage()
curlist = cpuusagelist
except:
curlist = []
if len(curlist) > 0:
oled_loadbg("bgcpu")
# Display List
yoffset = 0
tmpmax = 4
while tmpmax > 0 and len(curlist) > 0:
curline = ""
tmpitem = curlist.pop(0)
curline = tmpitem["title"]+": "+str(tmpitem["value"])+"%"
oled_writetext(curline, stdleftoffset, yoffset, fontwdSml)
oled_drawfilledrectangle(stdleftoffset, yoffset+12, int((oledscreenwidth-stdleftoffset-4)*tmpitem["value"]/100), 2)
tmpmax = tmpmax - 1
yoffset = yoffset + 16
needsUpdate = True
else:
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "storage":
# Storage Info
if len(curlist) == 0:
try:
tmpobj = argonsysinfo_listhddusage()
for curdev in tmpobj:
curlist.append({"title": curdev, "value": argonsysinfo_kbstr(tmpobj[curdev]['total']), "usage": int(100*tmpobj[curdev]['used']/tmpobj[curdev]['total']) })
#curlist = argonsysinfo_liststoragetotal()
except:
curlist = []
if len(curlist) > 0:
oled_loadbg("bgstorage")
yoffset = 16
tmpmax = 3
while tmpmax > 0 and len(curlist) > 0:
tmpitem = curlist.pop(0)
# Right column first, safer to overwrite white space
oled_writetextaligned(tmpitem["value"], 77, yoffset, oledscreenwidth-77, 2, fontwdSml)
oled_writetextaligned(str(tmpitem["usage"])+"%", 50, yoffset, 74-50, 2, fontwdSml)
tmpname = tmpitem["title"]
if len(tmpname) > 8:
tmpname = tmpname[0:8]
oled_writetext(tmpname, 0, yoffset, fontwdSml)
tmpmax = tmpmax - 1
yoffset = yoffset + 16
needsUpdate = True
else:
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "raid":
# Raid Info
if len(curlist) == 0:
try:
tmpobj = argonsysinfo_listraid()
curlist = tmpobj['raidlist']
except:
curlist = []
if len(curlist) > 0:
oled_loadbg("bgraid")
tmpitem = curlist.pop(0)
oled_writetextaligned(tmpitem["title"], 0, 0, stdleftoffset, 1, fontwdSml)
oled_writetextaligned(tmpitem["value"], 0, 8, stdleftoffset, 1, fontwdSml)
oled_writetextaligned(argonsysinfo_kbstr(tmpitem["info"]["size"]), 0, 56, stdleftoffset, 1, fontwdSml)
if len(tmpitem['info']['state']) > 0:
oled_writetext( tmpitem['info']['state'], stdleftoffset, 8, fontwdSml )
if len(tmpitem['info']['rebuildstat']) > 0:
oled_writetext("Rebuild:" + tmpitem['info']['rebuildstat'], stdleftoffset, 16, fontwdSml)
# TODO: May need to use different method for each raid type (i.e. check raidlist['raidlist'][raidctr]['value'])
#oled_writetext("Used:"+str(int(100*tmpitem["info"]["used"]/tmpitem["info"]["size"]))+"%", stdleftoffset, 24, fontwdSml)
oled_writetext("Active:"+str(int(tmpitem["info"]["active"]))+"/"+str(int(tmpitem["info"]["devices"])), stdleftoffset, 32, fontwdSml)
oled_writetext("Working:"+str(int(tmpitem["info"]["working"]))+"/"+str(int(tmpitem["info"]["devices"])), stdleftoffset, 40, fontwdSml)
oled_writetext("Failed:"+str(int(tmpitem["info"]["failed"]))+"/"+str(int(tmpitem["info"]["devices"])), stdleftoffset, 48, fontwdSml)
needsUpdate = True
else:
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "ram":
# RAM
try:
oled_loadbg("bgram")
tmpraminfo = argonsysinfo_getram()
oled_writetextaligned(tmpraminfo[0], stdleftoffset, 8, oledscreenwidth-stdleftoffset, 1, fontwdReg)
oled_writetextaligned("of", stdleftoffset, 24, oledscreenwidth-stdleftoffset, 1, fontwdReg)
oled_writetextaligned(tmpraminfo[1], stdleftoffset, 40, oledscreenwidth-stdleftoffset, 1, fontwdReg)
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "temp":
# Temp
try:
oled_loadbg("bgtemp")
hddtempctr = 0
maxcval = 0
mincval = 200
# Get min/max of hdd temp
hddtempobj = argonsysinfo_gethddtemp()
for curdev in hddtempobj:
if hddtempobj[curdev] < mincval:
mincval = hddtempobj[curdev]
if hddtempobj[curdev] > maxcval:
maxcval = hddtempobj[curdev]
hddtempctr = hddtempctr + 1
cpucval = argonsysinfo_getcputemp()
if hddtempctr > 0:
alltempobj = {"cpu": cpucval,"hdd min": mincval, "hdd max": maxcval}
# Update max C val to CPU Temp if necessary
if maxcval < cpucval:
maxcval = cpucval
displayrowht = 8
displayrow = 8
for curdev in alltempobj:
if temperature == "C":
# Celsius
tmpstr = str(alltempobj[curdev])
if len(tmpstr) > 4:
tmpstr = tmpstr[0:4]
else:
# Fahrenheit
tmpstr = str(32+9*(alltempobj[curdev])/5)
if len(tmpstr) > 5:
tmpstr = tmpstr[0:5]
if len(curdev) <= 3:
oled_writetext(curdev.upper()+": "+ tmpstr+ chr(167) +temperature, stdleftoffset, displayrow, fontwdSml)
else:
oled_writetext(curdev.upper()+":", stdleftoffset, displayrow, fontwdSml)
oled_writetext(" "+ tmpstr+ chr(167) +temperature, stdleftoffset, displayrow+displayrowht, fontwdSml)
displayrow = displayrow + displayrowht*2
else:
maxcval = cpucval
if temperature == "C":
# Celsius
tmpstr = str(cpucval)
if len(tmpstr) > 4:
tmpstr = tmpstr[0:4]
else:
# Fahrenheit
tmpstr = str(32+9*(cpucval)/5)
if len(tmpstr) > 5:
tmpstr = tmpstr[0:5]
oled_writetextaligned(tmpstr+ chr(167) +temperature, stdleftoffset, 24, oledscreenwidth-stdleftoffset, 1, fontwdReg)
# Temperature Bar: 40C is min, 80C is max
maxht = 21
barht = int(maxht*(maxcval-40)/40)
if barht > maxht:
barht = maxht
elif barht < 1:
barht = 1
oled_drawfilledrectangle(24, 20+(maxht-barht), 3, barht, 2)
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
elif curscreen == "ip":
# IP Address
try:
oled_loadbg("bgip")
oled_writetextaligned(argonsysinfo_getip(), 0, 8, oledscreenwidth, 1, fontwdReg)
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
else:
try:
oled_loadbg("bgtime")
# Date and Time HH:MM
curtime = datetime.datetime.now()
# Month/Day
outstr = str(curtime.day).strip()
if len(outstr) < 2:
outstr = " "+outstr
outstr = monthlist[curtime.month-1]+outstr
oled_writetextaligned(outstr, stdleftoffset, 8, oledscreenwidth-stdleftoffset, 1, fontwdReg)
# Day of Week
oled_writetextaligned(weekdaynamelist[curtime.weekday()], stdleftoffset, 24, oledscreenwidth-stdleftoffset, 1, fontwdReg)
# Time
outstr = str(curtime.minute).strip()
if len(outstr) < 2:
outstr = "0"+outstr
outstr = str(curtime.hour)+":"+outstr
if len(outstr) < 5:
outstr = "0"+outstr
oled_writetextaligned(outstr, stdleftoffset, 40, oledscreenwidth-stdleftoffset, 1, fontwdReg)
needsUpdate = True
except:
needsUpdate = False
# Next page due to error/no data
screenjogflag = 1
if needsUpdate == True:
if screensavermode == False:
# Update screen if not screen saver mode
oled_power(True)
oled_flushimage(prevscreen != curscreen)
oled_reset()
timeoutcounter = 0
while timeoutcounter<screenjogtime or screenjogtime == 0:
qdata = ""
if readq.empty() == False:
qdata = readq.get()
if qdata == "OLEDSWITCH":
# Trigger screen switch
screenjogflag = 1
# Reset Screen Saver
screensavermode = False
screensaverctr = 0
break
elif qdata == "OLEDSTOP":
# End OLED Thread
display_defaultimg()
return
else:
screensaverctr = screensaverctr + 1
if screensaversec <= screensaverctr and screensavermode == False:
screensavermode = True
oled_fill(0)
oled_reset()
oled_power(False)
if timeoutcounter == 0:
# Use 1 sec sleep get CPU usage
cpuusagelist = argonsysinfo_listcpuusage(1)
else:
time.sleep(1)
timeoutcounter = timeoutcounter + 1
if timeoutcounter >= 60 and screensavermode == False:
# Refresh data every minute, unless screensaver got triggered
screenjogflag = 0
break
display_defaultimg()
def display_defaultimg():
# Load default image
#oled_power(True)
#oled_loadbg("bgdefault")
#oled_flushimage()
oled_fill(0)
oled_reset()
if len(sys.argv) > 1:
cmd = sys.argv[1].upper()
if cmd == "SHUTDOWN":
# Signal poweroff
argonregister_signalpoweroff(bus)
elif cmd == "FANOFF":
# Turn off fan
argonregister_setfanspeed(bus,0)
if OLED_ENABLED == True:
display_defaultimg()
elif cmd == "SERVICE":
# Starts the power button and temperature monitor threads
try:
ipcq = Queue()
t1 = Thread(target = argonpowerbutton_monitor, args =(ipcq, ))
t2 = Thread(target = temp_check)
if OLED_ENABLED == True:
t3 = Thread(target = display_loop, args =(ipcq, ))
t1.start()
t2.start()
if OLED_ENABLED == True:
t3.start()
ipcq.join()
except Exception:
sys.exit(1)
Here is the sudo i2cdetect -y 1
log:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
sudo dmesg -T | grep -i "i2c"
log:
[Tue Aug 27 17:00:12 2024] i2c_dev: i2c /dev entries driver
[Tue Aug 27 17:00:14 2024] brcmstb-i2c fef04500.i2c: @97500hz registered in polling mode
[Tue Aug 27 17:00:14 2024] brcmstb-i2c fef09500.i2c: @97500hz registered in polling mode
And the sudo journalctl -u argononed
log:
13:23:34 CEST, ends at Tue 2024-08-27 17:06:44 CEST. --
Aug 27 17:00:20 osmc-RPI4b systemd[1]: Starting Argon One Fan and Button Daemon Service...
Aug 27 17:00:20 osmc-RPI4b systemd[1]: argononed.service: Can't open PID file /run/argononed.pid (yet?) after start: Operation not permitted
Aug 27 17:00:20 osmc-RPI4b systemd[1]: Started Argon One Fan and Button Daemon Service.
RPiās CPU didnāt go over 31Ā°C. I kept it on just now, but I expect the fan will not stop spinning on turn off and LEDs will stay on as usual.
If I will have to turn it off before you reply I will try starting the argononed service manually and reply to/edit this post.