FanSpeedControl PowerBoard X735 von Geekworm / Phyton

Begonnen von sash.sc, 31 Dezember 2021, 11:31:30

Vorheriges Thema - Nächstes Thema

MadMax-FHEM

#15
Hm...

Wie hast du cron eingerichtet?

Als User pi?
(oder global/system, dann verm. root)

Evtl. darf der User fhem nichts mit GPIO machen, evtl. müsste man den User fhem in eine bestimmte Gruppe stecken, da bin ich aber leider überfragt... :-\

EDIT: du kannst ja mal schauen wo der User pi überall drin steckt ;)

groups pi


Geht denn:

"/opt/fhem/RPM/read_fan_speed_once.py"

in FhemWeb?
(würde aber erwarten, dass nicht, weil so wird es ja per at/DOIF aufgerufen)

Wenn es für dich so passt, dann lass es...
...bzw. ist die Variante es vom System aus aufrufen zu lassen eh "besser"... 8)

Wenn es nun (für dich) funktioniert, setzt du dann noch ein [gelöst] davor, danke.

Wenn nicht: was fehlt?

Gut das "Ansteuern" ist noch gewünscht...
Da wird allerdings verm. das GPIO-Problem wieder auftauchen...

Aber auch hier würde ich das durch das System machen lassen, da ja abhängig von der Temperatur geregelt wird.
Wie würdest du das mit fhem machen wollen?
Temperatur mittels System-Aufrufen abfragen und dann per Python-Script den Lüfter-Speed setzen?
Etwas "umständlich", oder? ;)

Gruß, Joachim
FHEM PI3B+ Bullseye: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)

sash.sc

#16
Danke für Deine Hilfe.

Der AUfruf mit den Anführungszeichen funktioniert nicht im FHEMWEB, aber egal.
Habe es in einen cronjobvom system (sudo crontab) eingetragen.


@reboot python /home/pi/x735-v2.5/pwm_fan_control.py
@reboot ntpdate -s 0.de.pool.ntp.org
0 */12 * * * ntpdate -s 0.de.pool.ntp.org
* * * * * /opt/fhem/RPM/read_fan_speed_once.py



beim booten wird auch das im ersten post genannte python script für die lüfterkontrolle aufgerufen.
Die Installationsanweisung gab der Hersteller.

Ich denke dass es reicht im Minutentakt den Lüfter abzufragen.
Ich habe auch mal mit den Werten für die Temp.abfrage gespielt bzw. dann das Leistunglevel angepasst um zu schauen, ob die Drehzahl sich ändert. Das funktioniert auch ohne Probleme.

Es wäre "nice to Have" den PWM/Tacho Pin abzufragen und das ganze über FHEM zu steuern. Aber auch nur weil man es evtl. könnte.
Aber so macht es auch seinen Job.

Mit FEHM würde ich das ganze über ein DOIF realisieren. Temp.grenzen gestaffelt und dann entsprechend die Drehzahl setzen.

Dank Deiner Unterstützung funktiniert es ganz gut mit der Abfrage.

Danke nochmal
Gruß
Sascha 






P.S.: Für die, die es interessiert. Beim booten wird ein python Script aufgerufen, welches dei CÜU Temp. abfragt.
Das Script fragt regelmäßig die Temp. und passt das PWM (Taktung des Lüfters).
Abgefragt wird die Drehzal mit einem 2. Pythonscript. Das Orginal wurde von Joachim angepasst, dass keine Dauerschleife läuft.
Wenn der Rückgabewert der Drehzal größer 0 ist, dann wird der Wert über telnet in das Device mit dem Reading geschrieben.
Und die Abfrage ist beendet.
Das 2. Script wird über ein crontab vom system im Minutentakt aufgerufen und wieder das Reading geschrieben im entsprechenden Device.
Die Drehzahl frage ich ab, um zu schauen wie der Lüfter sich verhält, als Ergänzung zur RPI Systemvisualisierung im FEHM.

Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

sash.sc

Hallo MadMax-FHEM

Ich bräuchte nochmal deine Hilfe.
Ich habe mein System von buster auf bullseye geupdatet. Dabei wurde python2 entfernt.
Jetzt versuche ich das Script, welches die Drehzahl in fhem schreibt, umzuschreiben.

Ein Teil klappt schonmal. Es hakt aber hier.


pi@raspberrypi:~/x735-v2.5 $ python3 read_fan_speed_once.py
0 RPM
2292 RPM
Traceback (most recent call last):
  File "/home/pi/x735-v2.5/read_fan_speed_once.py", line 37, in <module>
    tn.write(b"setreading testdummy state %s\n"%rpm)
TypeError: %b requires a bytes-like object, or an object that implements __bytes__, not 'float'



hier das teilweise angepasste script



#!/usr/bin/python
# this python code is base python 2, not python 3
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
import time
import telnetlib

tn = telnetlib.Telnet('localhost',7072)

TACH = 16
PULSE = 2
WAIT_TIME = 1

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(TACH, GPIO.IN, pull_up_down=GPIO.PUD_UP)

t = time.time()
rpm = 0

def fell(n):
    global t
    global rpm

    dt = time.time() - t
    if dt < 0.005: return

    freq = 1 / dt
    rpm = (freq / PULSE) * 60
    t = time.time()

GPIO.add_event_detect(TACH, GPIO.FALLING, fell)

while True:
    print("%.f RPM" % rpm)
    if rpm > 0:
      tn.write(b"setreading testdummy state %s\n"%rpm)
      break
    rpm = 0
    time.sleep(1)

GPIO.cleanup()


Problem ist bei dem teil unten mit dem schreiben in den testdummy

Gruß
Sascha
Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

MadMax-FHEM

Ich weiß nicht, ob ich da viel helfen kann, leider.

Ich hab auch nur nach Python Telnet gegoogelt und bin dann bei dem Code gelandet.

Also "telnetlib"...

Evtl. dort mal schauen was man machen kann.
Oder in Python schauen, wie man den Datentyp "wandelt"...

Ich kann mal schauen aber ich bin jetzt auch kein Python Programmierer...

Gruß, Joachim

FHEM PI3B+ Bullseye: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)

MadMax-FHEM

#19
Ich weiß es ist schon ne Weile her...
...evtl. auch gar nicht mehr interessant aber ich dachte mir: was soll's ;)

Ich habe mir kürzlich ein POE-Hat für meinen PI besorgt, eines wo man alle GPIO noch nutzen kann :)
(weil ich muss ja mein EnOcean-Funkmodul da irgendwie drauf kriegen)

Was ich bislang an POE-Adaptern hatte war Mist...
...hoffe der hier ist besser...

So, nun aber:

der Hat steckt großflächig über dem PI und hat (daher) einen Lüfter ;)

Erste Idee war den Lüfter rauszubauen, dann ist zumindest mal nicht komplett "verdeckt"...
...dann dachte ich mir, ach doch mal schauen wegen Lüftersteuerung :)

Zunächst einfach nach Befehlen gesucht und nur mittels an/aus, das war dann wieder bash-Script.
Da Daten von fhem zu holen und hin zu schreiben konnte ich ja schon.
-> schnell fertig

Aber nicht so schön...


Dann weiter gesucht und hierauf gestoßen: https://www.raspberry-buy.de/Tutorial_Kuehlung_Luefterregelung_am_Raspberry_Pi.html
Das Script hat mich dann an den Thread hier erinnert und da dachte ich mir, versuch's doch mal...

So, nun aber wirklich ;)

Ich habe das Script angepasst und kann Parameter (Temperaturwerte/Lüftergeschwindigkeit) aus fhem auslesen und auch Temperatur und Lüftergeschwindigkeit an fhem zurück geben.

Z.B. folgender dummy (bei mir ist es ein AptToDate-Device / ist aber egal)

defmod fhem_Server dummy
attr fhem_Server userattr my_fanSettingsLow my_fanSettingsMed my_fanSettingsHigh
attr fhem_Server alias FHEM Server
attr fhem_Server icon system_fhem
attr fhem_Server my_fanSettingsHigh 55,100
attr fhem_Server my_fanSettingsLow 40,50
attr fhem_Server my_fanSettingsMed 45,75


Über die Attribute kann man (on the fly) einstellen wann welche Lüftergeschwindigkeit laufen soll:
my_fanSettingsHigh 55,100
my_fanSettingsLow 40,50
my_fanSettingsMed 45,75

Vorne die Temperatur und dahinter, durch Komma getrennt, die Lüftergeschwindigkeit.

Hier das Script:

#! /usr/bin/python3
# -*- coding: utf-8 -*-

import RPi.GPIO as GPIO
import time
import os
import telnetlib

# telnet connection to  fhem
tn = telnetlib.Telnet('localhost',7072)

# Zählweise der Pins auf GPIO-Nummern festlegen
GPIO.setmode(GPIO.BCM);

# Warnmeldungen ausschalten
GPIO.setwarnings(False);

# Konstanten definieren
FAN_OUT_PIN = 13                        # Lüfter-Ausgang (FAN_OUT_PIN) ist GPIO33 (PIN33)
FAN_STARTUP_TIME=1                      # fan speedup time used in startFan()

# variables for fhem -> adopt to your needs!
FHEM_SERVER="fhem_Server"
ATTR_FAN_SETTINGS_LOW="my_fanSettingsLow"
ATTR_FAN_SETTINGS_MED="my_fanSettingsMed"
ATTR_FAN_SETTINGS_HIGH="my_fanSettingsHigh"
READING_FAN_SPEED="fanSpeed"
READING_FAN_SPEED_PERCENT="fanSpeedPercent"
READING_CPU_TEMP="cpu_temp"

TASK_SLEEP_TIME=10

DEBUG_OUTPUT=False # True: output to console | False: no output (service mode)

# GPIO Eingänge definieren
GPIO.setup(FAN_OUT_PIN, GPIO.OUT)       # definiert unseren Lüfter-Pin als Ausgang
GPIO.output(FAN_OUT_PIN, GPIO.LOW)      # setzt unseren Ausgang auf Low (0 => 0V)

# PWM-Channel initialisieren
pwm1 = GPIO.PWM(FAN_OUT_PIN, 10)       # Frequenz ab 10 Hz funktioniert ganz gut
pwm1.start(100.0)

# Funktionen definieren
def debugOut(text="", context="general"): # debug output (if set to True)
    if DEBUG_OUTPUT:
      output = "DEBUGOUT: " + str(context) + " | " + str(text)
      print(output)

def getCpuTemp(): # ermittelt die CPU-Temperatur und gibt sie als String zurück
    raw = os.popen("vcgencmd measure_temp").readline()
    cpuTemp = (raw.replace("temp=","").replace("'C\n",""))
    debugOut(cpuTemp, "getCpuTemp")
    return(cpuTemp)

def sendCpuTemp(cpuTemp): # send CPU temp to fhem
    debugOut(cpuTemp, "sendCpuTemp")
    tn.write(bytes("setreading " + FHEM_SERVER + " " + READING_CPU_TEMP + " " + str(cpuTemp) + "\n", encoding='utf-8'))
    return

def setFanDuty(sfdc = 0.0): # Setzt den Dutycycle für den Lüfter
    pwm1.ChangeDutyCycle(sfdc)
    debugOut(sfdc, "setFanDuty")
    return

def startFan(): # Setzt den Lüfter für 1s Anlaufzeit auf 100%
    fanOutput = 100.0
    setFanDuty(fanOutput)
    debugOut("Anlaufzeit: " + str(FAN_STARTUP_TIME), "startFan")
    time.sleep(FAN_STARTUP_TIME)
    return

def readTempLow(): # read low temp from fhem
    tn.write(bytes("{AttrVal('" + FHEM_SERVER + "','" + ATTR_FAN_SETTINGS_LOW + "','42,55')}\n",encoding='utf8'))
    raw = ((tn.read_some()).decode('utf-8').split(","))[0]
    debugOut(raw, "readTempLow")
    return(raw)

def readTempMed(): # read medium temp from fhem
    tn.write(bytes("{AttrVal('" + FHEM_SERVER + "','" + ATTR_FAN_SETTINGS_MED + "','45,80')}\n",encoding='utf8'))
    raw = ((tn.read_some()).decode('utf-8').split(","))[0]
    debugOut(raw, "readTempMed")
    return(raw)

def readTempHigh(): # read high temp from fhem
    tn.write(bytes("{AttrVal('" + FHEM_SERVER + "','" + ATTR_FAN_SETTINGS_HIGH + "','55,100')}\n",encoding='utf8'))
    raw = ((tn.read_some()).decode('utf-8').split(","))[0]
    debugOut(raw, "readTempHigh")
    return(raw)

def readSpeedLow(): # read fan speed for temp low from fhem
    tn.write(bytes("{AttrVal('" + FHEM_SERVER + "','" + ATTR_FAN_SETTINGS_LOW + "','42,55')}\n",encoding='utf8'))
    raw = (tn.read_some()).decode('utf-8').split(",")
    raw = raw[1].replace('\n', '')
    debugOut(raw, "readSpeedLow")
    return(raw)

def readSpeedMed(): # read fan speed for temp medium from fhem
    tn.write(bytes("{AttrVal('" + FHEM_SERVER + "','" + ATTR_FAN_SETTINGS_MED + "','45,80')}\n",encoding='utf8'))
    raw = (tn.read_some()).decode('utf-8').split(",")
    raw = raw[1].replace('\n', '')
    debugOut(raw, "readSpeedMed")
    return(raw)

def readSpeedHigh(): # read fan speed for temp high from fhem
    tn.write(bytes("{AttrVal('" + FHEM_SERVER + "','" + ATTR_FAN_SETTINGS_HIGH + "','55,100')}\n",encoding='utf8'))
    raw = (tn.read_some()).decode('utf-8').split(",")
    raw = raw[1].replace('\n', '')
    debugOut(raw, "readSpeedHigh")
    return(raw)

def sendFanSpeed(speed): # send fan speed to fhem
    debugOut(str(speed), "sendFanSpeed")
    tn.write(bytes("setreading " + FHEM_SERVER + " " + READING_FAN_SPEED + " " + str(speed) + "\n", encoding='utf-8'))
    tn.write(bytes("setreading " + FHEM_SERVER + " " + READING_FAN_SPEED_PERCENT + " " + str(speed) + "%\n", encoding='utf-8'))

# Hauptprogramm starten
cpuTemp = getCpuTemp()
debugOut("CPU-Temperatur: " + str(cpuTemp) + " °C")
fanOutput = 0.0

while True:
    cpuTemp = float(getCpuTemp())

# read settings from fhem
    fanTempLow = float(readTempLow())
    fanTempMed = float(readTempMed())
    fanTempHigh = float(readTempHigh())
    fanSpeedLow = float(readSpeedLow())
    fanSpeedMed = float(readSpeedMed())
    fanSpeedHigh = float(readSpeedHigh())

# calculate fan speed according to settings   
    if cpuTemp < fanTempLow:
        fanOutput = 0.0
    elif cpuTemp > fanTempHigh:
        fanOutput = fanSpeedHigh
    elif cpuTemp > fanTempMed:
        if fanOutput < fanSpeedMed:
            startFan()
        fanOutput = fanSpeedMed
    elif cpuTemp > fanTempLow - 0.1:
        if fanOutput < fanSpeedLow:
            startFan()
        fanOutput = fanSpeedLow

    setFanDuty(fanOutput) # set calculated fan speed

# send data to fhem
    sendFanSpeed(fanOutput)
    sendCpuTemp(cpuTemp)
   
    debugOut("CPU-Temperatur: " + str(cpuTemp) + " °C / Lüfterausgang: " + str(fanOutput) + "%" + " tl:" + str(fanTempLow) + " tm:" + str(fanTempMed) + " th:" + str(fanTempHigh) + " | sl:" + str(fanSpeedLow) + " sm:" + str(fanSpeedMed) + " sh:" + str(fanSpeedHigh))
   
    time.sleep(TASK_SLEEP_TIME) # wait until next loop
       
pwm1.stop()


Kann man sicher noch verbessern aber für mich taugt das erst mal...

Wenn man im Script dann noch debugOutput auf False setzt und folgendes Unit-File unter /etc/systemd/system (z.B. fan-control.service) platziert und den Dienst entsprechend aktiviert und startet, dann läuft das Script als daemon :)

[Unit]
Description=fan control

After=fhem.service
Requires=fhem.service

[Service]
ExecStart=/opt/fhem/own-scripts/001-maintenance/fan-control.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

EDIT: Unit File bearbeitet. Bin aber immer noch nicht sicher, ob das so tut... Weil ja die Abhängigkeit zu fhem. Evtl. besser im Python abfangen? Evtl. später... Mal sehen...

Pfad zum Script nat. anpassen ;)

Läuft aktuell unter Bullseye.
Ich musste (interessanterweise) nichts weiter installieren oder tun...

Gruß, Joachim
FHEM PI3B+ Bullseye: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)

sash.sc

Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

sash.sc

#21
Nach langer Zeit habe ich es ausprobiert, und es funktioniert.
Musste erstmal überlegen wie das ganze als daemon  einzubinden ist. Aber jetzt geht´s.

Kann man das ganze auch von der Drehzahl abhängig machen, ohne feste Werte ?
Wenn ja, wie

Danke nochmal
Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

MadMax-FHEM

#22
Zitat
Kann man das ganze auch von der Drehzahl abhängig machen, ohne feste Werte ?
Wenn ja, wie

Verstehe nicht was du meinst...

Du kannst für niedrige, mittlere und hohe Temperatur angeben
1. was bedeutet niedrig, mittel bzw. hoch (also welche Temp)
2. mit welcher Drehzahl der Lüfter dann jeweils laufen soll...

Einstellbar per Attribut.

Die aktuelle Temp und Drehzahl steht dann im Dummy (wo auch die Attribute hängen)...

EDIT:
Zitat
Über die Attribute kann man (on the fly) einstellen wann welche Lüftergeschwindigkeit laufen soll:
my_fanSettingsHigh 55,100
my_fanSettingsLow 40,50
my_fanSettingsMed 45,75

Vorne die Temperatur und dahinter, durch Komma getrennt, die Lüftergeschwindigkeit.

my_fanSettingsHigh Temperatur ab wann high ist,Drehzahl für hohe Temp

Zitat
attr fhem_Server userattr my_fanSettingsLow my_fanSettingsMed my_fanSettingsHigh
attr fhem_Server my_fanSettingsHigh 55,100
attr fhem_Server my_fanSettingsLow 40,50
attr fhem_Server my_fanSettingsMed 45,75


Das mit daemon steht doch auch beschrieben?
Zumindest für Systeme mit systemd...
EDIT: gut, ich bin bzgl. der Abhängigkeit zu fhem immer noch unschlüssig... Aber ich glaube so läuft das bei mir aktuell? (muss ich mal schauen)

Gruß, Joachim
FHEM PI3B+ Bullseye: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)

sash.sc

Du hast ja 3 Stufen für die Drehzahl fest gelegt.
Ich meine das ganze stufenlos, wie ein frequenzumrichter, Regeln zu lassen.

Notfalls könnte ich noch mehrere Abstufungen einbauen, um es ein bisschen feiner zu gestalten.

Gruß und danke
Sascha
Raspi 4B+ Bullseye ;LaCrosse; HomeMatic; MapleCUL; ZigBee; Signalduino ESP32 ; Shellys; MQTT2; Grafana mit Influxdb

MadMax-FHEM

Ah, ok.

Naja, Basis (von dir und dann auch mir) war ja das Python-Script.
Das ist im Prinzip eben geblieben (3 Stufen), ich habe es nur per fhem parametrisierbar gemacht und auch eine Rückmeldung an fhem eingebaut.

Wenn du einen brauchbaren Algorithmus hast für stufenlos, kann man das sicher einbauen...

Allerdings muss ich anmerken (nachdem es bereits einige zeit läuft):

der Lüfter läuft quasi konstant mit 55 Umdrehungen (also mittlere Stufe)
(Temp.: so um die 40+Grad)


An einem anderen PI (ein PI4) läuft ein "großer Lüfter" (ist so ein "stackable Gehäuse" / also für mehrere PI "übereinander", habe aber nur einen drin stecken) angeschlossen an 3,3V, also "dauer-an".
Die Temp des PI liegt so beo 40Grad.

Daher war/ist das eine nette Spielerei, allerdings überlege ich, ob ich nicht einfach den Lüfter auch an 3,3V durchlaufen lasse...


Bzw. bei "stufenlos" kommt noch dazu, dass die "Bastellösung" hier ja von einem Lüfter ausgeht, der eigentlich nicht gesteuert werden kann (daher ja die "Transistorendstufe"), drum ja auch der "Anlaufschubbs", wenn die Geschwindigkeit geändert wird -> startFan()

Evtl. wäre es dann besser einen steuerbaren Lüfter (mit 3 Anschlüssen: +/- pwm) zu nehmen...

Gruß, Joachim
FHEM PI3B+ Bullseye: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)