Pooltemperatursteuerung mit DS18b20 Sensoren

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Kaffebohne
User
Beiträge: 11
Registriert: Donnerstag 31. Januar 2019, 11:27

Montag 21. Juni 2021, 16:48

Hi @all

In einem anderen Beitrag "www.python-forum.de/viewtopic.php?f=31&t=52317" wurde ich gefragt ob das alles ist was ich mit dem Pi mache.
Natürlich ist das schalten von Pumpen über Taster nicht das einzige was der Pi machen soll.
Ursprünglich habe ich damit angefangen, dass ich mit Temperatursensoren (DS18b20) die Wassertemperatur im Pool und die Solartemperatur an den Solarkollektoren messen und darüber die 2 Pumpen steuern wollte.
Ich habe das ganze bei mir mit 2 Pumpen gelöst damit ich nicht immer die große Filterpumpe laufen lassen muss, sondern mit einer kleineren den Solarheizkreis pumpen lasse. Einfach zum stromsparen.
Und natürlich sollte die Solarpumpe auch nur dann laufen wenn die Solarkollektoren warm genug sind und das Wass noch zu kalt ist...

Da ich das Skript als blutiger Anfänger geschrieben habe, hatte ich mir jetzt gedacht ich zeige euch das mal in der Hoffnung das das gar nicht so schlecht geworden ist. Funktionieren tut es bereits seit längerer Zeit :)

Gut gemeinte Kritik ist natürlich immer willkommen.
Vieleicht gibt es noch Optimierungen dazu!?

Code: Alles auswählen

import time
from RPi import GPIO

GPIO.setmode(GPIO.BCM) # GPIO Nummern statt Board Nummern

#GPIO Pin`s
FILTERPUMPE = 6
SOLARPUMPE = 13

# GPIO Modus zuweisen
GPIO.setup(FILTERPUMPE, GPIO.OUT)
GPIO.setup(SOLARPUMPE, GPIO.OUT)

S01_daten_pfad = "/sys/bus/w1/devices/28-0120638705a7/w1_slave" #Wassersensor
S02_daten_pfad = "/sys/bus/w1/devices/28-02131e36dfaa/w1_slave" #Solarsensor

Text = """\
    ____              ______  _ 
   / __ \____  ____  / / __ \(_)
  / /_/ / __ \/ __ \/ / /_/ / / 
 / ____/ /_/ / /_/ / / ____/ /     ___Pumpensteuerung___
/_/    \____/\____/_/_/   /_/   
                                
                   __             ____                        
                  / /_  __  __   / __ )___  ____  ____  __  __
                 / __ \/ / / /  / __  / _ \/ __ \/ __ \/ / / /
                / /_/ / /_/ /  / /_/ /  __/ / / / / / / /_/ / 
               /_.___/\__, /  /_____/\___/_/ /_/_/ /_/\__, /  
                     /____/                          /____/
                     
"""
print (Text)

#Sensor_1 (DS18b20 / Wassertemperatur)
def Temperatur_S01():
      
    # 1-wire Slave Datei lesen
    file = open(S01_daten_pfad)
    filecontent = file.read()
    file.close()

    # Temperaturwerte auslesen und konvertieren
    stringvalue = filecontent.split("\n")[1].split(" ")[9]
    temperature = float(stringvalue[2:]) / 1000
    return(temperature)

#Sensor_2 (DS18b20 / Solartemperatur)
def Temperatur_S02():
      
    # 1-wire Slave Datei lesen
    file = open(S02_daten_pfad)
    filecontent = file.read()
    file.close()

    # Temperaturwerte auslesen und konvertieren
    stringvalue = filecontent.split("\n")[1].split(" ")[9]
    temperature = float(stringvalue[2:]) / 1000
    return(temperature)
  
try:
    while True:
            print(time.strftime("%d.%m.%Y %H:%M"))
        
            print("Wassertemperatur aktuell:","{:.0f}".format(Temperatur_S01()),"C°")
            print("Solartemperatur aktuell:",'{:.0f}'.format(Temperatur_S02()),"C°")
            time.sleep(0.1)
         
            if   ((Temperatur_S01()) < 29.5 and (Temperatur_S02())>= 40 and (Temperatur_S02()-1) > (Temperatur_S01())):
                 GPIO.output(SOLARPUMPE, GPIO.HIGH)
                 print (">>> Solarpumpe AN")
                 time.sleep(900)
            if   ((Temperatur_S01()) < 29 and (Temperatur_S02())< 40 or (Temperatur_S02()-2) < (Temperatur_S01())):
                 GPIO.output(SOLARPUMPE, GPIO.LOW)
                 print (">>> Solarpumpe AUS")
                 time.sleep(900)
            if   (Temperatur_S01()) >= 29 and (Temperatur_S01()) < 30 and (Temperatur_S02())> 40 :
                 GPIO.output(SOLARPUMPE, GPIO.HIGH)
                 time.sleep(1)
                 GPIO.output(FILTERPUMPE, GPIO.HIGH)
                 Text = """\
                 >>> Max Pooltemperatur fast erreicht!
                 >>> Solarpumpe AN
                 >>> Filterpumpe AN
                 ------> 20 Minuten"""
                 print (Text)
                 time.sleep(1200)
            if   (Temperatur_S01()) >= 30:
                 GPIO.output(SOLARPUMPE, GPIO.LOW)
                 time.sleep(1)
                 GPIO.output(FILTERPUMPE, GPIO.LOW)
                 Text = """\
                 >>> Pool zu warm
                 >>> Solarpumpe AUS
                 >>> Filterpumpe AUS"""
                 print (Text)
                 time.sleep(900)
                 
except KeyboardInterrupt:
    print ("GoodBye :D")
finally:  
    GPIO.cleanup()
Sirius3
User
Beiträge: 15142
Registriert: Sonntag 21. Oktober 2012, 17:20

Montag 21. Juni 2021, 21:31

Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 4, mal 5 oder 8.
Konstanten schriebt man komplett gross, Variablennamen und Funktionen dagegen komplett klein.

Funktionen sind dazu da, dass man Codewiederholungen vermeiden kann, Deine beiden Temperatur_S-Funktionen sind aber nahezu identisch.
`return` ist keine Funktion, die Klammern also falsch. Auch bei Deinen if's hast Du zu viele Klammern.
Es ist ziemlich verschwenderisch, die selbe Temperatur 5 mal hintereinander zu lesen.

Mit .format kann man lesbare Strings schreiben, da ist es Kontraproduktiv, deinen einen String in drei doch wieder in drei aufzuspalten. Die Einheit ist °C und nicht C°.

Wenn wasser_temperatur < 29.5 und solar_temperatur >= 40, dann gilt immer solar_temperatur - 1 > wasser_temperatur, die letzte Bedingung ist also überflüssig
Ansonsten habe ich versucht, die Logik, wann welche Pumpe an ist, zu verstehen, vergeblich.

Code: Alles auswählen

import time
from RPi import GPIO

#GPIO Pin`s
FILTERPUMPE = 6
SOLARPUMPE = 13

WASSER_SENSOR = "/sys/bus/w1/devices/28-0120638705a7/w1_slave"
SOLAR_SENSOR = "/sys/bus/w1/devices/28-02131e36dfaa/w1_slave"

def read_temperature(filename):
    with open(filename) as file:
        _, _, temperature = file.read().partition("t=")
    return float(temperature)
  
try:
    GPIO.setmode(GPIO.BCM) # GPIO Nummern statt Board Nummern
    GPIO.setup([FILTERPUMPE, SOLARPUMPE], GPIO.OUT)
    while True:
        wasser_temperatur = read_temperature(WASSER_SENSOR)
        solar_temperatur = read_temperature(SOLAR_SENSOR)
        print(time.strftime("%d.%m.%Y %H:%M"))
        print("Wassertemperatur aktuell: {:.0f}°C".format(wasser_temperatur))
        print("Solartemperatur aktuell: {:.0f}°C".format(solar_temperatur))

        if wasser_temperatur >= 30:
            # Wasser zu warm, alles aus
            GPIO.output(SOLARPUMPE, GPIO.LOW)
            time.sleep(1)
            GPIO.output(FILTERPUMPE, GPIO.LOW)
            print(
                ">>> Pool zu warm\n"
                ">>> Solarpumpe AUS\n"
                ">>> Filterpumpe AUS\n")
        elif solar_temperatur < 40:
            # solar zu kalt, solar aus
            GPIO.output(SOLARPUMPE, GPIO.LOW)
            print(">>> Solarpumpe AUS")
        elif 29 <= wasser_temperatur < 30:
            # wasser temperatur fast erreicht, filter an
            GPIO.output(SOLARPUMPE, GPIO.HIGH)
            time.sleep(1)
            GPIO.output(FILTERPUMPE, GPIO.HIGH)
            print(
                ">>> Max Pooltemperatur fast erreicht!\n"
                ">>> Solarpumpe AN\n"
                ">>> Filterpumpe AN\n"
                "------> 20 Minuten\n")
            time.sleep(300)
        else:
            # wasser zu kalt, solar warm genug
            GPIO.output(SOLARPUMPE, GPIO.HIGH)
            print(">>> Solarpumpe AN")
        time.sleep(900)                 
except KeyboardInterrupt:
    print("GoodBye :D")
finally:  
    GPIO.cleanup()
LukeNukem
User
Beiträge: 153
Registriert: Mittwoch 19. Mai 2021, 03:40

Dienstag 22. Juni 2021, 01:01

Kaffebohne hat geschrieben:
Montag 21. Juni 2021, 16:48
Ursprünglich habe ich damit angefangen, dass ich mit Temperatursensoren (DS18b20) die Wassertemperatur im Pool und die Solartemperatur an den Solarkollektoren messen und darüber die 2 Pumpen steuern wollte.
Naja, fangen wir... bei den Basics an, und... naja, OneWire ist wirklich nett, aber hinsichtlich der Stabilität über größere Entgernungen leider... etwas speziell.

Du hast einen DS18B20 im (oder am) Pool und einen am oder auf dem Dach. Da so ein Pool eher selten auf einem Dach ist, würde ich ganz grundsätzlich mal vermuten, daß eine gewisse Strecke zwischen den beiden Temperatursensoren liegt. Wie hast Du das verdrahtet, und wie ist die Spannungs- und Stromversorgung gestaltet? Speist Du Deine DS18B20 parasitär, also über den Bus? Sind Deine Kabel geschirmt (CAT5 oder mehr)? Wie groß sind Deine Pullups? Wie werden die Pumpen geschaltet -- Transistoren oder FETs? Wie werden die Schaltsignale zu den Pumpen übertragen, direkt über RasPi-GPIOs oder sowas wie DS24xx? Wie ist Deine Versorgung mit Spannung und Strom ausgelegt? Kann es zum Beispiel sein, daß die Pumpen gleichzeitig oder in sehr kurzen Abständen geschaltet werden und der Einschaltstrom der Spulen in Deinen Bus oder mehr so herunterzieht, daß da Deine Spannungs- oder Stromversorgung einbricht?
Kaffebohne
User
Beiträge: 11
Registriert: Donnerstag 31. Januar 2019, 11:27

Dienstag 22. Juni 2021, 15:49

Sirius3 hat geschrieben:
Montag 21. Juni 2021, 21:31
Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 4, mal 5 oder 8.
Konstanten schriebt man komplett gross, Variablennamen und Funktionen dagegen komplett klein.

Funktionen sind dazu da, dass man Codewiederholungen vermeiden kann, Deine beiden Temperatur_S-Funktionen sind aber nahezu identisch.
`return` ist keine Funktion, die Klammern also falsch. Auch bei Deinen if's hast Du zu viele Klammern.
Es ist ziemlich verschwenderisch, die selbe Temperatur 5 mal hintereinander zu lesen.

Mit .format kann man lesbare Strings schreiben, da ist es Kontraproduktiv, deinen einen String in drei doch wieder in drei aufzuspalten. Die Einheit ist °C und nicht C°.

Wenn wasser_temperatur < 29.5 und solar_temperatur >= 40, dann gilt immer solar_temperatur - 1 > wasser_temperatur, die letzte Bedingung ist also überflüssig
Ansonsten habe ich versucht, die Logik, wann welche Pumpe an ist, zu verstehen, vergeblich.
Wie ich schon geschrieben hatte, das war mein erstes Skript und ich war zu dem Zeitpunkt erstmal froh das es überhaupt funktioniert hat.
Da ich mich lieber erstmal selbst mit etwas beschäftige als sofort allen Leuten auf die Nerven zu gehen :lol:

Das es quatsch ist dieselbe Temperatur so oft abzufragen stimmt natürlich. Hätte ich gewusst wie ich es besser machen kann, hätte ich dies getan ;)
Bei der Einheit "°C" waren wohl nur die Finger zu schnell :mrgreen:

Die Logik soll eigentlich nur sein das
1. wenn die Wassertemperatur zu kalt ist und die Solartemperatur hoch genug ist, die Solarpumpe eingeschaltet wird.
2. wenn die Wassertemperatur zu kalt ist und die Solartemperatur auch zu niedrig ist, soll die Pumpe natürlich nicht laufen.
3. wenn die maximale Wassertemperatur fast erreicht ist und die Solartemperatur hoch genug ist sollen beide Pumpen laufen. Die Filterpumpe (die stärkere / höhere Umwälzung) soll dann das Wasser umwälzen, weil unten im Pool meistens noch kälteres Wasser ist.
4. wenn der Pool warm genug ist, sollen alle Pumpen ausgeschaltet werden.

Ansonsten werde ich mich morgen mal mit deinem geposteten Skript beschäftigen. Habe ich heute leider keine Zeit zu.

Danke für die Hilfe :D
Kaffebohne
User
Beiträge: 11
Registriert: Donnerstag 31. Januar 2019, 11:27

Dienstag 22. Juni 2021, 16:10

LukeNukem hat geschrieben:
Dienstag 22. Juni 2021, 01:01
Kaffebohne hat geschrieben:
Montag 21. Juni 2021, 16:48
Ursprünglich habe ich damit angefangen, dass ich mit Temperatursensoren (DS18b20) die Wassertemperatur im Pool und die Solartemperatur an den Solarkollektoren messen und darüber die 2 Pumpen steuern wollte.
Naja, fangen wir... bei den Basics an, und... naja, OneWire ist wirklich nett, aber hinsichtlich der Stabilität über größere Entgernungen leider... etwas speziell.

Du hast einen DS18B20 im (oder am) Pool und einen am oder auf dem Dach. Da so ein Pool eher selten auf einem Dach ist, würde ich ganz grundsätzlich mal vermuten, daß eine gewisse Strecke zwischen den beiden Temperatursensoren liegt. Wie hast Du das verdrahtet, und wie ist die Spannungs- und Stromversorgung gestaltet? Speist Du Deine DS18B20 parasitär, also über den Bus? Sind Deine Kabel geschirmt (CAT5 oder mehr)? Wie groß sind Deine Pullups? Wie werden die Pumpen geschaltet -- Transistoren oder FETs? Wie werden die Schaltsignale zu den Pumpen übertragen, direkt über RasPi-GPIOs oder sowas wie DS24xx? Wie ist Deine Versorgung mit Spannung und Strom ausgelegt? Kann es zum Beispiel sein, daß die Pumpen gleichzeitig oder in sehr kurzen Abständen geschaltet werden und der Einschaltstrom der Spulen in Deinen Bus oder mehr so herunterzieht, daß da Deine Spannungs- oder Stromversorgung einbricht?
Also natürlich steht der Pool nicht auf dem Dach :lol:
Der Pi ist im Keller in einer Verteilerbox samt den Relais für die Pumpen etc. Von dort sind es ca. 10 Meter zum Pool wo der erste Sensor sitzt. Von dort aus geht es weiter zum Gartenhaus auf dem die Kollektoren sind, ca. auch noch mal 10 Meter. Also vom Pi zum letzten Sensor ca. 20 Meter. (Dort sitzt ein dritter Sensor für die Lufttemperatur).
Die "DS18B20" habe ich so angeschlossen wie es überall beschrieben wird.
Masse an Masse
+3,3 Volt an 3,3 Volt
Datenleitung an Datenleitung mit Wiederstand an 3,3 Volt
Beim Wiederstand hatte ich etwas experimentiert, daher weiß ich grade nicht welchen Wiederstand ich jetzt verbaut habe :?

Als Kabel habe ich ein 7x1,5mm² verwendet. (Hatte ich noch über)
Die Sensoren laufen auch sehr stabil, gab bis jetzt keine Ausfälle.

Die Pumpen werden über Relais geschaltet die auch in der Verteilerbox stecken. Die werden natürlich über GPIO gesteuert.

Mit Spannungseinbrüchen habe ich eigentlich keine Probleme, bisher.

Ich hoffe ich konnte deine Fragen soweit erstmal beantworten.
LukeNukem
User
Beiträge: 153
Registriert: Mittwoch 19. Mai 2021, 03:40

Montag 28. Juni 2021, 23:55

Kaffebohne hat geschrieben:
Dienstag 22. Juni 2021, 16:10
Ich hoffe ich konnte deine Fragen soweit erstmal beantworten.
Ja, konntest Du. Allerdings denke ich nicht nur über Spannungseinbrüche nach, sondern auch... nunja, Onewire ist wirklich schick, kann aber auch ein ziemliches Biest sein. Das fängt schon mit dem Pullup-Widerstand an: wenn der zu klein ist, können die Geräte den Bus möglicherweise nicht auf die nötige Schaltspannung herunterziehen. Ein weiteres Thema ist das sehr diffizile Timing, denn genau darüber wird die eigentliche Information übertragen, und wir reden hier schon im "normalen" Modus über wenige Mikrosekunden. Angesichts der Tatsache, daß etliche Komponenten auf einem Onewire-Bus womöglich parasitäre Kapazitäten haben, wird das mit dem Pullup-Widerstand natürlich wieder wichtig: wenn der zu groß ist, fließt nicht genug Strom, um den Bus innerhalb der entsprechenden Zeit wieder über die Schaltschwelle High zu bringen. Keine Ahnung, was Deine Software dann macht, aber wenn sie halbwegs wohlerzogen ist, macht sie einen Reset auf dem Bus, und Du bekommst davon entweder nichts mit oder, wenn Du Glück hast, eine Fehlermeldung.

Bei Deiner Installation kommt zu diesen ganz grundsätzlichen Themen noch ein weiteres hinzu, nämlich das ungeschirmte Kabel. Ein ungeschirmtes Kabel ist im Prinzip nichts anderes als eine riesige Antenne, die jede (elektro)magnetische Strahlung einfängt und dadurch Störungen auf dem Bus verursachen kann. Je näher das Kabel an einer Störquelle ist und je stärker diese Störquelle ist, desto größer ist natürlich auch die Wahrscheinlichkeit, daß es zu Problemen auf dem Bus führt. Zum Beispiel die Elektromotoren Deiner Pumpen: egal, ob es Gleich- oder Wechselstrommotoren sind, beim Ein- und Ausschalten bzw. beim Umpolen fließen große Ströme, die entsprechend starke elektromagnische Felder erzeugen -- und die können über Deine ungeschirmte Kabelage natürlich Deinen Bus stören. Dies vor allem dann, wenn Deine Pumpenmotoren oder die Versorgung derselben nahe bei Deinem Bus liegen und / oder gar parallel dazu... Schon aus diesen Gründen halte ich es für problematisch, wie Du Deinen (doch relativ langen) Bus betreibst. Und wo wir gerade bei elektromagnetischen Einstreuungen sind: die GPIOs eines Raspberry Pi haben meines Wissens keinerlei Schutzbeschaltung gegen Über- und Unterspannungen, sondern sind direkt auf den SOC geführt. Wenn Du wirklich Pech haben solltest, was ich Dir nicht wünsche, können solche Einstreuungen die betreffenden Pins oder sogar das SOC Deines RasPi zerstören.

Zusätzlich sehe ich ein gewisses Problem darin, wie Du Deinen Bus betreibst -- nämlich als reine Softwarelösung. Linux ist allerdings ein Multitasking-Betriebssystem und hat einen Betriebssystemkernel, der die Zeitscheiben auf dem Prozessor zwischen den (mitunter vielen) laufenden Prozessen verteilt. Wenn ein Prozeß an der Reihe ist, wird das Executable geladen, die Registerinhalte der CPU wiederhergestellt, und der Instruktionszeiger auf den entsprechenden Wert gesetzt. Danach darf der Prozeß je nach Priorität etc. eine gewisse Zeit lang auf der CPU laufen, wird dann wieder angehalten, die Registerinhalte und der Instruktionszeiger wieder gesichert, und dann ist der nächste Prozeß dran. Das hat für Dich und Deinen Bus, der ein hochgenaues Timing braucht, gewisse Konsequenzen. Hinzu kommen die sogenannten Interrupts: ein Interrupt sagt im Prinzip "Hallo Du, hier ist gerade etwas, um das Du Dich sofort kümmern mußt! Und das ist so wichtig, daß Du bitte sofort unterbrichst (to interrupt, daher der Name), was Du gerade tust, und meinen Interrupthandler ausführst!" Wann Dein Prozeß ein Timeslice auf der CPU bekommt, ist zwar berechenbar (es wird ja auch vom Kernel-Prozeßscheduler berechnet), hängt aber stark davon ab, wie viele Prozesse gerade auf dem System laufen, welche Prioritäten sie haben, und wieviel Last das System gerade verarbeiten muß. Viele Interrupts können ARM-Systeme wie der RasPi traditionell eh nicht so gut ab, und Context Switches -- also: das Sichern des Zustandes eines laufenden Prozesses und die Wiederherstellung eines anderen -- können moderne Betriebssysteme alle nicht so besonders schnell. Und selbst wenn es heute, morgen und vielleicht auch noch übermorgen funktionieren sollte: irgendwann magst Du vielleicht etwas mehr und / oder aufwändigeres auf dem RasPi machen, vielleicht noch eine Kamera mit Bewegungserkennung, eine Datenbank mit der Generierung hübscher Diagramme, oder oder oder... Und wenn Du Pech hast, hast Du das, was ich hier geblubbert habe, dann vergessen, bastelst etwas Neues und auf einmal funktioniert die Sache nicht mehr. Und glaub' mir: Du wirst den Fehler nicht bei Deiner Onewire-Implementierung suchen, die ist ja seit n Jahren problemlos gelaufen...

Wie dem auch sei: diesem letztgenannten Verhalten könnte man unter Linux begrenzt entgegenwirken, auch wenn ich davon eher abrate und hier nur der Vollständigkeit halber ausführe. Einerseits gibt es für den Linux-Kernel die sogenannten Realtime-Patches (RT), die zusätzlich zu den normalen Prioritäten (siehe nice(1)) eine "besonders hohe" Realtime-Priorität hinzufügen, die dann noch höher priorisiert wird und entsprechend öfter Zeitscheiben auf der CPU bekommen. Meine persönlichen Erfahrungen damit sind allerdings eher durchwachsen. Etwas wirksamer ist es nach meinen Erfahrungen, mit dem Bootparameter isolcpus bestimmte Cores der CPU vom normalen Prozeß-Scheduling des Betriebssystemkerns auszunehmen, und dann mit taskset(1) die gewünschten Prozesse darauf zu legen.

Aber wenn Du es wirklich richtig machen möchtest... dann würde ich Dir eine passende Brücke empfehlen, die USB und Onewire kann, wie etwa den DS9490R, und auf geschirmte Kabel ab CAT5 aufwärts gehen. Das kostet ein paar Euro mehr, aber es funktioniert stabil und langfristig... und Linux hat einen passenden Treiber dafür. Nur so ein Tipp. ;-)
Kaffebohne
User
Beiträge: 11
Registriert: Donnerstag 31. Januar 2019, 11:27

Samstag 3. Juli 2021, 11:31

LukeNukem hat geschrieben:
Montag 28. Juni 2021, 23:55
Kaffebohne hat geschrieben:
Dienstag 22. Juni 2021, 16:10
Ich hoffe ich konnte deine Fragen soweit erstmal beantworten.
Ja, konntest Du. Allerdings denke ich nicht nur über Spannungseinbrüche nach, sondern auch... nunja, Onewire ist wirklich schick, kann aber auch ein ziemliches Biest sein. Das fängt schon mit dem Pullup-Widerstand an: wenn der zu klein ist, können die Geräte den Bus möglicherweise nicht auf die nötige Schaltspannung herunterziehen. Ein weiteres Thema ist das sehr diffizile Timing, denn genau darüber wird die eigentliche Information übertragen, und wir reden hier schon im "normalen" Modus über wenige Mikrosekunden. Angesichts der Tatsache, daß etliche Komponenten auf einem Onewire-Bus womöglich parasitäre Kapazitäten haben, wird das mit dem Pullup-Widerstand natürlich wieder wichtig: wenn der zu groß ist, fließt nicht genug Strom, um den Bus innerhalb der entsprechenden Zeit wieder über die Schaltschwelle High zu bringen. Keine Ahnung, was Deine Software dann macht, aber wenn sie halbwegs wohlerzogen ist, macht sie einen Reset auf dem Bus, und Du bekommst davon entweder nichts mit oder, wenn Du Glück hast, eine Fehlermeldung.

Bei Deiner Installation kommt zu diesen ganz grundsätzlichen Themen noch ein weiteres hinzu, nämlich das ungeschirmte Kabel. Ein ungeschirmtes Kabel ist im Prinzip nichts anderes als eine riesige Antenne, die jede (elektro)magnetische Strahlung einfängt und dadurch Störungen auf dem Bus verursachen kann. Je näher das Kabel an einer Störquelle ist und je stärker diese Störquelle ist, desto größer ist natürlich auch die Wahrscheinlichkeit, daß es zu Problemen auf dem Bus führt. Zum Beispiel die Elektromotoren Deiner Pumpen: egal, ob es Gleich- oder Wechselstrommotoren sind, beim Ein- und Ausschalten bzw. beim Umpolen fließen große Ströme, die entsprechend starke elektromagnische Felder erzeugen -- und die können über Deine ungeschirmte Kabelage natürlich Deinen Bus stören. Dies vor allem dann, wenn Deine Pumpenmotoren oder die Versorgung derselben nahe bei Deinem Bus liegen und / oder gar parallel dazu... Schon aus diesen Gründen halte ich es für problematisch, wie Du Deinen (doch relativ langen) Bus betreibst. Und wo wir gerade bei elektromagnetischen Einstreuungen sind: die GPIOs eines Raspberry Pi haben meines Wissens keinerlei Schutzbeschaltung gegen Über- und Unterspannungen, sondern sind direkt auf den SOC geführt. Wenn Du wirklich Pech haben solltest, was ich Dir nicht wünsche, können solche Einstreuungen die betreffenden Pins oder sogar das SOC Deines RasPi zerstören.

Zusätzlich sehe ich ein gewisses Problem darin, wie Du Deinen Bus betreibst -- nämlich als reine Softwarelösung. Linux ist allerdings ein Multitasking-Betriebssystem und hat einen Betriebssystemkernel, der die Zeitscheiben auf dem Prozessor zwischen den (mitunter vielen) laufenden Prozessen verteilt. Wenn ein Prozeß an der Reihe ist, wird das Executable geladen, die Registerinhalte der CPU wiederhergestellt, und der Instruktionszeiger auf den entsprechenden Wert gesetzt. Danach darf der Prozeß je nach Priorität etc. eine gewisse Zeit lang auf der CPU laufen, wird dann wieder angehalten, die Registerinhalte und der Instruktionszeiger wieder gesichert, und dann ist der nächste Prozeß dran. Das hat für Dich und Deinen Bus, der ein hochgenaues Timing braucht, gewisse Konsequenzen. Hinzu kommen die sogenannten Interrupts: ein Interrupt sagt im Prinzip "Hallo Du, hier ist gerade etwas, um das Du Dich sofort kümmern mußt! Und das ist so wichtig, daß Du bitte sofort unterbrichst (to interrupt, daher der Name), was Du gerade tust, und meinen Interrupthandler ausführst!" Wann Dein Prozeß ein Timeslice auf der CPU bekommt, ist zwar berechenbar (es wird ja auch vom Kernel-Prozeßscheduler berechnet), hängt aber stark davon ab, wie viele Prozesse gerade auf dem System laufen, welche Prioritäten sie haben, und wieviel Last das System gerade verarbeiten muß. Viele Interrupts können ARM-Systeme wie der RasPi traditionell eh nicht so gut ab, und Context Switches -- also: das Sichern des Zustandes eines laufenden Prozesses und die Wiederherstellung eines anderen -- können moderne Betriebssysteme alle nicht so besonders schnell. Und selbst wenn es heute, morgen und vielleicht auch noch übermorgen funktionieren sollte: irgendwann magst Du vielleicht etwas mehr und / oder aufwändigeres auf dem RasPi machen, vielleicht noch eine Kamera mit Bewegungserkennung, eine Datenbank mit der Generierung hübscher Diagramme, oder oder oder... Und wenn Du Pech hast, hast Du das, was ich hier geblubbert habe, dann vergessen, bastelst etwas Neues und auf einmal funktioniert die Sache nicht mehr. Und glaub' mir: Du wirst den Fehler nicht bei Deiner Onewire-Implementierung suchen, die ist ja seit n Jahren problemlos gelaufen...

Wie dem auch sei: diesem letztgenannten Verhalten könnte man unter Linux begrenzt entgegenwirken, auch wenn ich davon eher abrate und hier nur der Vollständigkeit halber ausführe. Einerseits gibt es für den Linux-Kernel die sogenannten Realtime-Patches (RT), die zusätzlich zu den normalen Prioritäten (siehe nice(1)) eine "besonders hohe" Realtime-Priorität hinzufügen, die dann noch höher priorisiert wird und entsprechend öfter Zeitscheiben auf der CPU bekommen. Meine persönlichen Erfahrungen damit sind allerdings eher durchwachsen. Etwas wirksamer ist es nach meinen Erfahrungen, mit dem Bootparameter isolcpus bestimmte Cores der CPU vom normalen Prozeß-Scheduling des Betriebssystemkerns auszunehmen, und dann mit taskset(1) die gewünschten Prozesse darauf zu legen.

Aber wenn Du es wirklich richtig machen möchtest... dann würde ich Dir eine passende Brücke empfehlen, die USB und Onewire kann, wie etwa den DS9490R, und auf geschirmte Kabel ab CAT5 aufwärts gehen. Das kostet ein paar Euro mehr, aber es funktioniert stabil und langfristig... und Linux hat einen passenden Treiber dafür. Nur so ein Tipp. ;-)
Danke für diese ausführliche Antwort...

Grundsätzlich verstehe ich auch was du mir hier sagen möchtest. Jedoch verstehe ich nicht warum der "DS9490R" besser sein soll als der 1-wire Pin vom Raspberry!? Werden da nicht auch immer die abfragen gestartet und somit die CPU belastet?

Das Kabel gegen ein Abgeschirmtes zu ersetzten ist kein Problem, dies kann ich durchaus mal wenn die Zeit über ist machen. Hab sogar noch 100 Meter Cat7 im keller liegen :D
__deets__
User
Beiträge: 10472
Registriert: Mittwoch 14. Oktober 2015, 14:29

Samstag 3. Juli 2021, 13:01

Bitte nicht den Beitrag davor im Volltext zitieren. Der steht da schon. Und gerade bei einer solchen Textwand ist die Verdopplung nervig.

Und so wirklich verstanden hast du es nicht. Was nicht so verwunderlich ist, das sind komplexe Themen. Aber es entspricht trotzdem der Realität. USB funktioniert zuverlässig, und damit auch dieser Baustein. Die 1-wire-Treiber nicht, weil das system das Timing nicht stabil halten kann.
LukeNukem
User
Beiträge: 153
Registriert: Mittwoch 19. Mai 2021, 03:40

Sonntag 4. Juli 2021, 04:12

Kaffebohne hat geschrieben:
Samstag 3. Juli 2021, 11:31
Danke für diese ausführliche Antwort...
Sehr gerne... dafür sind wir ja hier, oder? ;-)
Kaffebohne hat geschrieben:
Samstag 3. Juli 2021, 11:31
Grundsätzlich verstehe ich auch was du mir hier sagen möchtest. Jedoch verstehe ich nicht warum der "DS9490R" besser sein soll als der 1-wire Pin vom Raspberry!? Werden da nicht auch immer die abfragen gestartet und somit die CPU belastet?
Jaein. Die CPU-Kerne Deines RasPi werden, wie oben beschrieben, von einem Multiuser- und Multitasking-Betriebssystemkernel verwaltet. Deswegen kann es, abhängig von der Lastsituation des Systems und weiteren Gegebenheiten, passieren, daß der Prozeß, der Deinen OneWire-GPIO händelt, mal eine Mikrosekunde Zeit auf der CPU bekommt und in fünf Mikrosekunden wieder... oder in zwanzig. Das ist alles vollkommen... nein, nicht erratisch, aber jedenfalls unvorhersehbar und, wie gesagt: stark abhängig von der anderweitigen Systemlast.

Wie werden Daten über OneWire übertragen? Nun, der Bus selbst -- nur deswegen kann das mit der parasitären Speisung der Geräte funktionieren -- liegt im "Ruhezustand" immer auf HIGH. Um eine 0 zu übertragen, wird die Spannung von HIGH auf LOW gezogen und laut Spezifikation nach 60 Mikrosekunden "losgelassen", so daß der Pullup-Widerstand die Spannung wieder über die HIGH-Schaltschwelle zieht. Genau dasselbe passiert, wenn eine 1 übertragen werden soll -- dann aber nur für ein bis 15 Mikrosekunden. Wenn Dein OneWire-Kernelmodul in diesen maximal 15 Mikrosekunden wieder eine Timeslice auf der CPU bekommt, ist alles gut. Wenn Dein System aber gerade eine hohe Last hat, dann dauert es womöglich die oben erwähnten zwanzig Millisekunden, bis Dein Treiber wieder auf der CPU arbeiten und den Pin umschalten kann... das können gutmütigere OneWire-Geräte wie die DS18[BS]20 meistens noch verkraften, aber ich kenne ein paar, die etwas zickiger sind... und die springen Dir womöglich dann ins Gesicht.

Der DS9490R dagegen hat einen eigenen OneWire-Prozessor, auf dem nur genau ein Task läuft -- und USB ist dabei in Hardware ausgeführt und von diesem Task vollständig entkoppelt. Das heißt: der Entwickler auf diesem Prozessor hat, im Gegensatz zu einem Multitasking-Betriebssystem wie dem Linux auf Deinem RasPi, die vollständige und absolut präzise zeitliche Kontrolle darüber, wann der der DS9490R den Bus auf LOW zieht und wann er ihn wieder losläßt. Hier ist der Prozeß nicht von einem Prozeßscheduler eines Multitasking-Betriebssystems wie Linux abhängig, sondern kontrolliert die CPU komplett selbst; er ist sozusagen das Betriebssystem. Dallas bzw. Maxim haben dazu die AppNote 126 veröffentlicht: [1], und die sagt ganz klar: "The system must be capable of generating an accurate and repeatable 1µs delay for standard speed and 0.25µs delay for overdrive speed". Das kann ein Linux-Betriebssystem ohne aufwändigere Tricksereien nicht in jedem Fall, da beißt die Maus keinen Faden ab. (Und nein: auch Windows, *BSD, Solaris, AIX und weitere Multitasking-Betriebssysteme, die ich kenne, können das nicht aus dem Stehgreif.)

Der USB-Teil des DS9490R ist dagegen (vermute ich dringend) in Hardware ausgeführt -- was genau darin steckt, weiß ich nicht. Was ich weiß, ist: es gibt preiswerte Chips mit Hardware-USB wie den AT90USB168 oder den (auch auf einigen Arduinos verwendeten) AtMega32U4, und die haben dann eine exterme Schnittstelle über I2C oder RS232. Da ist es ziemlich einfach, die aufwändige USB-Kommunikation (USB ist ein ziemlich komplexes Monster, wohlgemerkt) auf einem Chip laufen zu lassen und nur noch die Kommandos und deren Rückgaben mit dem OneWire-Master (think DS2480B+, DS2482-100 oder DS2483) zu übermitteln. Genau dadurch, durch die Zwischenpuffer, werden die einzelnen Komponenten Linux-Prozeßscheduler, USB-Kommunikation und OneWire-Treiber zeitlich voneinander entkoppelt und können deswegen mit genau dem hochgenauen Timing arbeiten, das OneWire prinzipbedingt und inhärent immer erfordert.
Kaffebohne hat geschrieben:
Samstag 3. Juli 2021, 11:31
Das Kabel gegen ein Abgeschirmtes zu ersetzten ist kein Problem, dies kann ich durchaus mal wenn die Zeit über ist machen. Hab sogar noch 100 Meter Cat7 im keller liegen :D
Wenn Du es langfristig störungsarm haben willst, dann bitte, tu' Dir den Gefallen.


[1] https://www.maximintegrated.com/en/desi ... 1/126.html
Antworten