Argon ONE V2 - Fan always on after reinstalling RPi OS

I dont know exactly if its working, i dont think it is. When i start the service or enable on start up it does nothing not even when set 20c 100% and not when i stress the device and it reach 55 celsius like set by default.
When i reboot, nothing happens, its not spinning at all anymore. When i delet all that stuff and redownload it and pull the cable out and put it in a again, the fans start spining again but cant be controlled.

For now i will disable start up and and shut down the device because that stops the fan (but dont make it work) and keep it like this until i remove the whole desaster probably.

People need it simple, download, start, (maybe configure a little bit and easy) and start spin my friend. Before the new OS it worked fine from the get go.

But thanks for trying to help and fix it and your effords.

If you have the solution i will check here sometimes.

Hi, Iā€™m a total noob when it comes to the command line and this forum, but I tried your instructions.
Iā€™m running OSMC on RPI 4B 8GB, installed Argon stuff through their manual, and tried the extended patch file you posted. The case is set to default pins (Pin 1-2), NOT always on.

It didnā€™t solve my main issue where the fan would go full speed on boot and never adjust its speed, always being loud. When this happens the Pi never turns the fan or the LEDs off on shutdown or reboot.

I tried your 7 steps to near down the root cause and the fan would only misbehave like this when sudo i2cdetect -y 1 would not detect anything.
I didnā€™t follow the steps fully because it would either work (and not need adjusting) or it wouldnā€™t detect the i2c and not work.

I was wondering if this GitLab project could help with i2c detection?
Argon One Daemon by DarkElvenAngel

This is my (hopefully patched since it said HUNK #1 FAILED) argononed.py if needed:

#!/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=55", "55=30"]
	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)

Yes, DarkElvenAngelā€™s project is an alternative solution. I had a look at the code some time ago and it looks clear and promising. But Iā€™ve never used it myself.

If you want to try it out, make sure you donā€™t mess anything up. The scripts provided by Argon40 need to be uninstalled first.

Note: Both solutions require a working I2C bus. You have reported that this is not always the case. You have checked this with the i2cdetect command after booting. This could be an indication of

  • the MCU has not finished his work after the last shutdown (needs investigation why)
  • the I2C is unstable with the current kernel versions, because of dynamic clocking or something like that

If you start with a powerless situation of the case, the I2C should be ok after the first boot. Does the argononed script then stop the fan and the shutdown work properly (red LED should be go off after a while) ?
This would make the different, if the fan control work only one time but the shutdown is unsuccessful.

Do you use the IR remote control to shutdown the case or the power menu of KODI ?

I use the power menu in KODI to shut down the system.

Iā€™ve been trying to turn the RPi on and off numerous times and only got the fan problem once.
It took about 7-8 shutdowns to appear but after that, there wasnā€™t any problem for 20 more tries. I had to turn it off and leave after that.
When it worked, the fan only turned on on start and then turned off as it was supposed to. The LEDs would also turn off and itā€™d be detected in i2cdetect each time.

Also, I have to note that I have no way to test if the fan even turns back on at 55Ā°C, Iā€™m trying cpuburn and it didnā€™t want to go past 52Ā°C within 40 minutes.

Iā€™m trying to recap, please correct me if Iā€™m wrong:

Currently you use the patched version of script in the state you already posted and it works in general, but sporadic it doesnā€™t work.

Only to be sure, if you can reproduce the failed state again, please can you check if the I2C address 0x1a is populated or empty? And also try to remember if after the last shutdown the LED was going off or not.
Additional the kernel log and journald log could contains some important informations:

sudo dmesg -T | grep -i "i2c"
sudo journalctl -u argononed

Explanation of the expected behaviour::

  • Fan on, after power-on is triggered by the MCU every time
  • Fan off: at the end or during the boot process the argononed service sends the command to stop the fan via I2C
  • if one of the 2 temperature souces (CPU, HDD (includes SSD, NVMe, USB drives)) is over the threshold, argononed sends via I2C the command to the MCU to set the Fan to On (PWM value > 0) until both temperatures below the thresholds again

To test if the fan control is working in general you can lower the first threshold from 55Ā° celsius to 47Ā° for example and start cpuburn again.

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.

This is a important observation!
I donā€™t think it was triggered by you. It looks more that the MCU was already in a unusual state. It equals like the situation I already know from the remote control usage in combination with LibreELEC.

Instead of the power button of the remote control you could press the power button at the case for >= 3 seconds, wait 10 seconds afterwards. That should do the same.

Please can you also check the output of that command:
vcgencmd get_throttled

That looks that the MCU of the Argon ONE is currently stuck. The only way to heal this, is to make the case powerless. Hard reset (button press >= 5 seconds) shouldnā€™t work in that situation.

vcgencmd get_throttled gives me:
throttled=0x0
I will keep the RPi on with the fault for the next 2-3 hours so reply with a request if you will need me to check anything.

Can you check, if the power button at the case has currently a working function?

Please check in that order:

  • double tab
  • pressing button >= 3 seconds
  • pressing button >= 5 seconds

I would assume, nothing works.

Nothing happens at all. Tried what you wanted plus pressing the button numerous times and holding it for more than a minute. Nothing.

Enough testing for now.

Like I already assumed, the MCU is stuck. No issue of the script currently. You need to make the case powerless to get it work again.
It seems that a bug in the firmware of the MCU has been triggered during the last shutdown sequence. Normally such things should be corrected at firmware level. To try to find a workaround for that will be time consuming and maybe not reach a 100% success level. :thinking:

One thing remains. After you have restored the MCU to work properly, please can you test if that version works too?

#!/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=55", "55=30"]
	tmpconfig = load_config("/etc/argononed.conf")
	if len(tmpconfig) > 0:
		fanconfig = tmpconfig
	return fanconfig


def load_fanhddconfig():
	fanhddconfig = ["50=100", "40=55", "30=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 = 200	# 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 and prevspeed != INITIALSPEEDVAL:
			# Pause 30s before speed reduction to prevent fluctuations
			time.sleep(30)
		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)
			prevspeed = newspeed
		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)

1 Like

I only tested starting up the RPi and it worked normally so far. I canā€™t test the fan turning on during high temps right now. I will post again if I manage to reproduce the problem.

1 Like

Yes, no button clicks did something in any case.