zunächst vielen Dank für die Aufnahme hier !
Hier die Konfig:
Bei mir ist in meinem Serverschrank ein Raspi 3 B+ verbaut, welcher u.a. die Belüftung des Schrankes temperaturabhängig per Python steuert.
Er gibt dazu auch diverse Daten per LCD Dsplay aus
Folgender Aufbau:
- 4 Noctua 140 mm 4 Pin 12 V
- 2 LED
- Grün: Normalbetrieb
- Rot : Störung eines Lüfters
- 1 Temperatursensor DS18B20
- 1 LCD Diplay LCD1602
Ziel des Python Scripts:
- Erfasse die Temperatur des Sensors
- Erfasse alle 4 Lüfterdrehzahlen
- Schalte bei i.O.-Betrieb die grüne LED und gib aktuelle Temperatur /PWM-Signal / gemittelte DRZ aller 4 Lüfter per Display aus -> gib das entsprechende PWM-Signal an die Lüfter aus
- Steuerung (grob dargestellt) in 4 Stufen:
- < 30 °C Lüfter aus +LED Grün
- >= 30 °C PWM 25% + DRZausgabe+ LED Grün
- >= 34°C PWM 50% + DRZausgabe+ LED Grün
- >= 38 °C PWM 100% +DRZ Max + LED Grün
(Es geht hier sicher auch stufenlos, aber das übersteigt leider mein Wissen !!! )
- Schalte im Fehlerfall die rote LED und gib den ausgefallenen Lüfter per Display aus
Nun zum eigentlichen Problem:
Prio 1:
Das Programm läuft stundenlang und steigt dann mit folgendem Fehr aus:
Code: Alles auswählen
Traceback (most recent call last):
File "noctua_7.py", line 230, in <module>
if TemperaturAuswertung() > 30 and TemperaturAuswertung() <= 34:
File "noctua_7.py", line 58, in TemperaturAuswertung
while lines[0].strip()[-3:] != 'YES':
IndexError: list index out of range
Bekomme ständig den Fehler:
Code: Alles auswählen
'continue' not properly in loop
Prio 2:
Die Noctua-Lüfter geben ein Rechtecksignal aus, das in Hz und dann in die DRZ umgerechnet werden / gemittelt werden soll.
Das ist mir eher nur diletantisch gelungen...
Bitte helft mir mit der DRZ-Erfassung der Lüfter, ich kanns leider nicht besser.
Ich hänge noch Daten aus dem Datasheet der Lüfter an.
Prio 3:
Das Script ist eine Mischung aus Google , sowie Try & Error...bin leider noch ein blutiger Amateur und war froh, dass ichs überhaupt hinbekommen hab...
Bitte helft mir, das Programm zu entrümpeln, es gibt sicher genuge Codezeile, die überflüssig oder unsauber geschrieben sind...
Hier das Script:
Code: Alles auswählen
a=u"°"
import sys
import time
import datetime
from time import sleep
import termios
import tty
import glob
import RPi.GPIO as GPIO
import numpy as np
import lcddriver
lcd = lcddriver.lcd()
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
signal = 13 #Lüfter 1
signal_2 = 15 #Lüfter 2
signal_3 = 19 #Lüfter 3
signal_4 = 21 #Lüfter 4
led_g = 10 #LED Normalbetrieb
led_r = 12 #LED Störung Lüfter
#GPIO Grundsetup
GPIO.setup(7, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(led_r, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(led_g, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(11, GPIO.OUT, initial=GPIO.LOW)
#'D18B20 Initialisierung
# Nach Aktivierung des Pull-UP Widerstandes wird gewartet,
# bis die Kommunikation mit dem DS18B20 Sensor aufgebaut ist
#print 'Warte auf Initialisierung...'
base_dir = '/sys/bus/w1/devices/'
while True:
try:
device_folder = glob.glob(base_dir + '28*')[0]
break
except IndexError:
sleep(0.5)
continue
device_file = device_folder + '/w1_slave'
#print 'Initialisierung...beendet !'
# Funktion wird definiert, mit dem der aktuelle Messwert am Sensor ausgelesen werden kann
def TemperaturMessung():
f = open(device_file, 'r')
lines = f.readlines()
f.close()
return lines
# Zur Initialisierung, wird der Sensor einmal "blind" ausgelesen:
TemperaturMessung()
# Die Temperaturauswertung: Beim Raspberry Pi werden erkannte one-Wire Slaves im Ordner
# /sys/bus/w1/devices/ einem eigenen Unterordner zugeordnet. In diesem Ordner befindet sich die Datei w1-slave
# in diesem werden die Daten,welche per One-Wire Bus gesendet wurden gespeichert.
# In dieser Funktion werden diese Daten analysiert,die Temperatur herausgelesen und ausgegeben
def TemperaturAuswertung():
lines = TemperaturMessung()
while lines[0].strip()[-3:] != 'YES':
try:
time.sleep(0.2)
lines = TemperaturMessung()
except IndexError:
sleep(1)
continue
equals_pos = lines[1].find('t=')
if equals_pos != -1:
temp_string = lines[1][equals_pos+2:]
temp_c = float(temp_string) / 1000.0
return temp_c
#Definition PWM Signal der 4 Gehäuselüfter
luefter_pwm=GPIO.PWM(11,25000)
luefter_pwm.start(0)
#Definition der Eingänge für die Drehzaherfassung der 4 Gehäuselüfter
rpm = 0
rpm11 = 0
rpm1 = 0
rpm12 = 0
rpm3 = 0
rpm13 = 0
rpm4 = 0
rpm14 = 0
rpm_mid = 0
flankenzeit = 0
flankenzeit_2 = 0
flankenzeit_3 = 0
flankenzeit_4 = 0
t = 0
t_2 = 0
t_3 = 0
t_4 = 0
pwms = 0
r = 0
PIO.setup(signal, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(signal_2, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(signal_3, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(signal_4, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#Drehzaherfassung:
def puls(n):
global r
global t
global flankenzeit
global rpm
global rpm1
flankenzeit = np.mean(time.time() - t)
time.sleep(0.001)
if flankenzeit < 0.001: return
frequenz = np.mean(1 / flankenzeit)
rpm = np.average((frequenz / 2) * 60)
if rpm > 1800: return
if pwms > 95:
rpm1 = "%.f" % (rpm,)
else:
rpm1 = "%.f" % (np.average(rpm,))
time.sleep(0.01)
t = time.time()
GPIO.add_event_detect(signal, GPIO.FALLING, puls)
def puls_2(m):
global t_2
global flankenzeit_2
global rpm11
global rpm12
flankenzeit_2 = np.mean(time.time() - t_2)
time.sleep(0.001)
if flankenzeit_2 < 0.001: return
frequenz_2 = np.mean(1 / flankenzeit_2)
rpm11 = np.average((frequenz_2 / 2) * 60)
if rpm11 > 1800: return
if pwms > 95:
rpm12 = "%.f" % (rpm11,)
else:
rpm12 = "%.f" % (np.average(rpm11,))
time.sleep(0.01)
t_2 = time.time()
GPIO.add_event_detect(signal_2, GPIO.FALLING, puls_2)
def puls_3(m):
global t_3
global flankenzeit_3
global rpm3
global rpm13
flankenzeit_3 = np.mean(time.time() - t_3)
time.sleep(0.001)
if flankenzeit_3 < 0.001: return
frequenz_3 = np.mean(1 / flankenzeit_3)
rpm3 = np.average((frequenz_3 / 2) * 60)
if rpm3 > 1800: return
if pwms > 95:
rpm13 = "%.f" % (rpm3,)
else:
rpm13 = "%.f" % (np.average(rpm3,))
time.sleep(0.01)
t_3 = time.time()
GPIO.add_event_detect(signal_3, GPIO.FALLING, puls_3)
def puls_4(m):
global t_4
global flankenzeit_4
global rpm4
global rpm14
global rpm_mid
flankenzeit_4 = np.mean(time.time() - t_4)
time.sleep(0.001)
if flankenzeit_4 < 0.001: return
frequenz_4 = np.mean(1 / flankenzeit_4)
rpm4 = np.average((frequenz_4 / 2) * 60)
if rpm4 > 1800: return
if pwms > 95:
rpm14 = "%.f" % (rpm4,)
else:
rpm14 ="%.f" % (np.average(rpm4,))
time.sleep(0.01)
t_4 = time.time()
#rpm_mid = str (rpm_mid)
#rpm_mid = "%.f" %((rpm1 + rpm12 + rpm13 + rpm14) / 4)
GPIO.add_event_detect(signal_4, GPIO.FALLING, puls_4)
# HAUPTSCHLEIFE
L = ""
lcd.lcd_clear()
time.sleep(2)
GPIO.output(10, GPIO.HIGH)
#try:
while True:
# try:
#Drehzahl in lesbares Format übersetzen / mitteln (glätten)
r = "%.1f" % (TemperaturAuswertung(),) #Variable für Temperatur DS18B20
flankenzeit = np.mean(time.time() - t)
time.sleep(0.001)
frequenz = np.mean(1 / flankenzeit)
rpm = np.average((frequenz / 2) * 60)
flankenzeit_2 = np.mean(time.time() - t_2)
time.sleep(0.001)
frequenz_2 = np.mean(1 / flankenzeit_2)
t = time.time()
rpm11 = np.average((frequenz_2 / 2) * 60)
t_2 = time.time()
flankenzeit_3 = np.mean(time.time() - t_3)
time.sleep(0.001)
frequenz_3 = np.mean(1 / flankenzeit_3)
rpm3 = np.average((frequenz_3 / 2) * 60)
t_3 = time.time()
flankenzeit_4 = np.mean(time.time() - t_4)
time.sleep(0.001)
frequenz_4 = np.mean(1 / flankenzeit_4)
rpm4 = np.average((frequenz_4 / 2) * 60)
t_4 = time.time()
if pwms > 95:
rpm1 = "%.f" % (rpm,)
else:
rpm1 = "%.f" % (rpm,)
time.sleep(0.01)
if pwms > 95:
rpm12 = "%.f" % (rpm11,)
else:
rpm12 = "%.f" % (np.average(rpm11,))
time.sleep(0.01)
if pwms > 95:
rpm13 = "%.f" % (rpm3,)
else:
rpm13 = "%.f" % (np.average(rpm3,))
time.sleep(0.01)
if pwms > 95:
rpm14 = "%.f" % (rpm4,)
else:
rpm14 ="%.f" % (np.average(rpm4,))
time.sleep(0.01)
lcd.lcd_display_string("Luefter aus. ", 2)
#Lüfteransteuerung mit Temperaturschwellen und Displayausgabe
if TemperaturAuswertung() > 30 and TemperaturAuswertung() <= 34:
r = "%.1f" % (TemperaturAuswertung(),)
pwms = 25
lcd.cursor_pos = (0, 0)
lcd.lcd_display_string("Temp: " + r + " Grd.", 1)
lcd.cursor_pos = (1, 0)
lcd.lcd_display_string("PWM:" + str (pwms) + "-" + str (rpm1) + "U/MIN", 2)
elif TemperaturAuswertung() > 34 and TemperaturAuswertung() <= 38:
r = "%.1f" % (TemperaturAuswertung(),)
pwms = 50
lcd.cursor_pos = (0, 0)
lcd.lcd_display_string("Temp: " + r + " Grd.", 1)
lcd.cursor_pos = (1, 0)
lcd.lcd_display_string("PWM:" + str (pwms) + "-" + str (rpm1) + "U/MIN", 2)
elif TemperaturAuswertung() > 38:
r = "%.1f" % (TemperaturAuswertung(),)
pwms = 100
lcd.cursor_pos = (0, 0)
lcd.lcd_display_string("Temp: " + r + " Grd.", 1)
lcd.cursor_pos = (1, 0)
lcd.lcd_display_string("Luefter-DRZ Max. ", 2)
r = "%.1f" % (TemperaturAuswertung(),)
else:
pwms = 0
lcd.cursor_pos = (0, 0)
lcd.lcd_display_string("Temp: " + r + " Grd.", 1)
time.sleep(4)
lcd.cursor_pos = (1, 0)
lcd.lcd_display_string("Luefter aus. ", 2)
luefter_pwm.start(0)
Lueftertastverhaeltnis = pwms
luefter_pwm.ChangeDutyCycle(Lueftertastverhaeltnis)
time.sleep(2)
print r + " " +str(t)
#Drehzahlüberwachung der 4 Gehäuselüfter mit Displayausgabe und LED -Ansteuerung
if pwms >= 10 and rpm < 100 :
L = 1
GPIO.output(led_g, GPIO.LOW)
GPIO.output(led_r, GPIO.HIGH)
lcd.lcd_display_string("Luefter Nummer " + str(L), 1)
lcd.lcd_display_string("!!! Stoerung !!! ", 2)
elif pwms >= 10 and rpm11 < 100 :
L = 2
GPIO.output(led_g, GPIO.LOW)
GPIO.output(led_r, GPIO.HIGH)
lcd.lcd_display_string("Luefter Nummer " + str(L), 1)
lcd.lcd_display_string("!!! Stoerung !!! ", 2)
elif pwms >= 10 and rpm3 < 100 :
L = 3
GPIO.output(led_g, GPIO.LOW)
GPIO.output(led_r, GPIO.HIGH)
lcd.lcd_display_string("Luefter Nummer " + str(L), 1)
lcd.lcd_display_string("!!! Stoerung !!! ", 2)
elif pwms >= 10 and rpm4 < 100 :
L = 4
GPIO.output(led_g, GPIO.LOW)
GPIO.output(led_r, GPIO.HIGH)
lcd.lcd_display_string("Luefter Nummer " + str(L), 1)
lcd.lcd_display_string("!!! Stoerung !!! ", 2)
else:
GPIO.output(led_g, GPIO.HIGH)
GPIO.output(led_r, GPIO.LOW)
# except KeyboardInterrupt :
# print(" Luefteransteuerung manuell beendet! ")
# luefter_pwm.stop()
# lcd.lcd_clear()
# time.sleep(3)
# lcd.lcd_backlight("off")
# time.sleep(0.5)
# GPIO.output(led_g, GPIO.LOW)
# GPIO.output(led_r, GPIO.LOW)
# GPIO.cleanup
#sys.exit()
PWM Ansteurung der Lüfter:
Target frequency: 25kHz, acceptable range 21kHz to 28kHz Maximum voltage for logic low: VIL=0,8V
Absolute maximum current sourced: Imax=5mA (short circuit current) Absolute maximum voltage level: VMax=5,25V (open circuit voltage) Allowed duty-cycle range 0% to 100%
DRZ-Erfassung:
Note that while the tachometer output signal is in Hertz (= per second), fan speeds are usually specifed in RPM (= revolutions per minute).
This means that the signal must be multiplied by 60 in order to convert it to RPM.
However, as the fan puts out two impulses per revolution, the reading must also be divided by 2.
Therefore, the formula for obtaining correct RPM speed is:
fan speed [rpm] = frequency [Hz] × 60 ÷ 2
The calculation for the above example waveform is:
86 [Hz] ÷ 2 × 60 = 2580 [rpm]
Operation below 20% PWM duty-cycle is not ofcially supported in the Intel specifcation (undefned behaviour).
However, most Noctua PWM fans can be operated at below 20% and will stop at 0% duty-cycle.
Only the following models keep running at their specifed minimum speed when the input is below 20%: NF-A20 PWM, NF-S12B redux 1200 PWM and NF-B9 redux 1600 PWM
Vielen, vielen Dank schon Mal für Eure Zeit und Mühe Leute !!!!
Gruß Frank.