Keine Werte bei vielen DS18B20 an einem OneWire-Bus

Hallo zusammen,
nach langem Verweilen in diversen Beiträgen mit Lösungsansätzen zu meinem Problem konnte ich dieses bislang nicht lösen und benötige nun Expertenhilfe.

Allg. Informationen: Arduino Nano / Versorgungsspannung 5V / DS18B20 in Normal Mode (keine parasitäre Energie) / insgesamt 24 Sensoren am Bus / Schirmung der Sensorleitungen mit GND verbunden

Vorhaben: Ich möchte die Temperaturen von 8 Pufferspeichern mit je 3 DS18B20 Sensoren auslesen (oben, mitte, unten). Somit liegt die Gesamtanzahl an dem Bus bei 24 Sensoren. Die Topologie soll ein typischer Bus sein, der in meinem Fall durch ein J-Y(St)Y 4x2x0,6 als Hauptstrang gebildet wird. Über jedem Puffer ist eine Zweigstelle, in der die 3 Sensoren mit dem Hauptstrang verklemmt sind. Die DS18B20 sind an ein LiYCY 3x0,25mm² angelötet und haben jeweils eine länge von 1m, 2m und 3m analog zur Position oben, mitte, unten.

Problemstellung: Wenn alle Sensoren am Bus angeschlossen sind, erhalte ich bei jedem den Wert -127,00. Jedes Dreierpaket lässt allerdings alleine ansprechen und auslesen.

Was ich versucht habe:

  • kontrollieren der Verdrahtung (die drei weißen Adern der Adernpaare des J-Y(St)Y sind richtig zugeordnet)
  • Verändern des Pull-Up Widerstands zu ca. 3k Ohm (keine Veränderung bemerkbar)
  • verschiedene Anzahl von Sensoren am Bus in 3er Stufen (bis 8 Sensoren können bei geringer Leitungslänge ausgelesen werden, der 9. zeigt 85,00 an)
  • ab 12 Sensoren wird überall der Wert -127,00 angezeigt
  • verschiedene Programme, wie bspw. die Beispiele der DallasTemp Library oder zum Finden der angeschlossen Sensoren (es wurden nie mehr wie 9 Sensoren gefunden)

Aktuell hängt der Arduino Nano nah an dem 1. Puffer dran, allerdings soll er final in einem anderen Raum platziert werden. Die Leitungslänge bis zur 1. Zweigstelle beträgt dann ca. 30m.

Ich hoffe alle relevanten Informationen kurz und kompakt genannt zu haben und bin für jede Hilfe sehr dankbar(!), um das Problem in den Griff zu bekommen.

Bild Schaltplan:

Code:

#include <Wire.h>  
#include <LiquidCrystal_I2C.h> 
#include <OneWire.h>
#include <DallasTemperature.h>

#define ONE_WIRE_BUS 10
#define taster 7

OneWire oneWire(ONE_WIRE_BUS);        //OneWire Instanz um mit Busgeräten zu kommunizieren
DallasTemperature sensors(&oneWire);  // Verknüpfen mit Dallas Temperature
LiquidCrystal_I2C lcd(0x27, 20, 4);   // (Adresse, Zeichen, Zeilen)

byte menuCnt = 0;     // Menü-Auswahl Zähler
byte merker = 0;      // Zustandsmerker für Taster
byte menuMerker = 9;  // Zustandsmerker für Menüvergleich

// eigene Zeichendefinitionen für I2C-Display
byte grad[8] = {B00110, B01001, B01001, B00110, B00000, B00000, B00000, B00000};    
byte U[8] = {B01010, B00000, B10001, B10001, B10001, B10001, B01110, B00000};
byte KlammAuf[8] = {B00011, B00100, B00100, B00100, B00100, B00100, B00011, B00000};
byte KlammZu[8] = {B11000, B00100, B00100, B00100, B00100, B00100, B11000, B00000};

// Sensoradressen 
uint8_t P1o[8] = {0x28, 0xB9, 0xFD, 0xEB, 0x0C, 0x00, 0x00, 0xA3}, P1m[8] = {0x28, 0x34, 0x07, 0xEC, 0x0C, 0x00, 0x00, 0x9E}, P1u[8] = {0x28, 0x78, 0xC4, 0xEC, 0x0C, 0x00, 0x00, 0xFA},  // Puffer 1
        P2o[8] = {0x28, 0x88, 0x34, 0xEC, 0x0C, 0x00, 0x00, 0x2E}, P2m[8] = {0x28, 0x0F, 0x7A, 0xEB, 0x0C, 0x00, 0x00, 0x8C}, P2u[8] = {0x28, 0xE3, 0xBD, 0xEC, 0x0C, 0x00, 0x00, 0x2D},  // Puffer 2
        P3o[8] = {0x28, 0x55, 0xC9, 0xEC, 0x0C, 0x00, 0x00, 0xEA}, P3m[8] = {0x28, 0x15, 0xEC, 0xEC, 0x0C, 0x00, 0x00, 0xB5}, P3u[8] = {0x28, 0xF4, 0x10, 0xEC, 0x0C, 0x00, 0x00, 0x2C},  // Puffer 3
        P4o[8] = {0x28, 0xEC, 0xD3, 0xEC, 0x0C, 0x00, 0x00, 0xBA}, P4m[8] = {0x28, 0x67, 0xA0, 0xEC, 0x0C, 0x00, 0x00, 0x8B}, P4u[8] = {0x28, 0x4C, 0xDE, 0xEC, 0x0C, 0x00, 0x00, 0x0A},  // Puffer 4
        P5o[8] = {0x28, 0x2F, 0x39, 0xED, 0x0C, 0x00, 0x00, 0x94}, P5m[8] = {0x28, 0x00, 0x0C, 0xED, 0x0C, 0x00, 0x00, 0x50}, P5u[8] = {0x28, 0xC1, 0xA7, 0xEB, 0x0C, 0x00, 0x00, 0xB2},  // Puffer 5
        P6o[8] = {0x28, 0x06, 0x1F, 0xED, 0x0C, 0x00, 0x00, 0xD0}, P6m[8] = {0x28, 0x79, 0xB9, 0xEC, 0x0C, 0x00, 0x00, 0x4C}, P6u[8] = {0x28, 0xCA, 0x71, 0xED, 0x0C, 0x00, 0x00, 0x81},  // Puffer 6
        P7o[8] = {0x28, 0x81, 0xB5, 0xEC, 0x0C, 0x00, 0x00, 0xBE}, P7m[8] = {0x28, 0x4E, 0x5A, 0xEC, 0x0C, 0x00, 0x00, 0xB0}, P7u[8] = {0x28, 0x5B, 0xC5, 0xEC, 0x0C, 0x00, 0x00, 0xD8},  // Puffer 7
        P8o[8] = {0x28, 0x46, 0x6E, 0xEB, 0x0C, 0x00, 0x00, 0x0C}, P8m[8] = {0x28, 0xA9, 0xCB, 0xEB, 0x0C, 0x00, 0x00, 0xE0}, P8u[8] = {0x28, 0xF7, 0xB1, 0xEC, 0x0C, 0x00, 0x00, 0x8B};  // Puffer 8
        
// Sensordaten Array    [0] = oben, [1] = mitte, [2] = unten
float P1Temp[3], P2Temp[3], P3Temp[3], P4Temp[3], P5Temp[3], P6Temp[3], P7Temp[3], P8Temp[3];

void setup(){
  
pinMode(taster, INPUT);

lcd.init();               // Startet das LCD
lcd.backlight();          // Hintergrundbeleuchtung (0 = AUS / () = EIN)

lcd.createChar(0, grad);        // Erstelle eigenes Zeichen im LCD 
lcd.createChar(1, U);
lcd.createChar(2, KlammAuf);
lcd.createChar(3, KlammZu);

lcd.setCursor(7,1); lcd.print("Lade");
lcd.setCursor(3,2); lcd.print("Temperaturen");

sensors.begin();
sensors.setResolution(11);
sensors.requestTemperatures();  

P1Temp[0] = sensors.getTempC(P1o); P1Temp[1] = sensors.getTempC(P1m); P1Temp[2] = sensors.getTempC(P1u);
P2Temp[0] = sensors.getTempC(P2o); P2Temp[1] = sensors.getTempC(P2m); P2Temp[2] = sensors.getTempC(P2u);
P3Temp[0] = sensors.getTempC(P3o); P3Temp[1] = sensors.getTempC(P3m); P3Temp[2] = sensors.getTempC(P3u);
P4Temp[0] = sensors.getTempC(P4o); P4Temp[1] = sensors.getTempC(P4m); P4Temp[2] = sensors.getTempC(P4u);
P5Temp[0] = sensors.getTempC(P5o); P5Temp[1] = sensors.getTempC(P5m); P5Temp[2] = sensors.getTempC(P5u);
P6Temp[0] = sensors.getTempC(P6o); P6Temp[1] = sensors.getTempC(P6m); P6Temp[2] = sensors.getTempC(P6u);
P7Temp[0] = sensors.getTempC(P7o); P7Temp[1] = sensors.getTempC(P7m); P7Temp[2] = sensors.getTempC(P7u);
P8Temp[0] = sensors.getTempC(P8o); P8Temp[1] = sensors.getTempC(P8m); P8Temp[2] = sensors.getTempC(P8u);

}

void loop(){

  if (digitalRead(taster) == LOW && merker == 0) {
    merker = 1;
    if (menuCnt > 8) menuCnt = 0;                     // umspringen der Menüseiten von letzter zu erster
    else menuCnt++;
  }
 
  if (digitalRead(taster) == HIGH && merker == 1) {
    delay(100);
    merker = 0;
  }

  if (menuCnt != menuMerker) {
     menuMerker = menuCnt;
     switch (menuCnt) {
        case 0:           // Zeige Übersichtsmenü
          menu0();
          break;
        case 1:           // Menü Puffertemperaturen 1
          menuP1();
          break;
        case 2:           // ...
          menuP2();
          break;
        case 3:
          menuP3();
          break;
        case 4:
          menuP4();
          break;
        case 5:
          menuP5();
          break;
        case 6:
          menuP6();
          break;
        case 7:
          menuP7();
          break;
        case 8:
          menuP8();
          break;
     }
  } 
}

int avg(float _oben,float _mitte,float _unten) {  // Mittelwert der Temperaturen bilden für Anzeige auf Menü Übersichtsseite

  int var = ((int)(_oben+.5) + (int)(_mitte+.5) + (int)(_unten+.5))/3;
  return var;
}

void menu0(void) {

  lcd.clear();
  lcd.setCursor(2,0); lcd.write(1); lcd.print("bersicht "); lcd.write(2); lcd.print("/"); lcd.write(3); lcd.setCursor(16,0); lcd.write(0); lcd.print("C");   // Zeile 1
  lcd.setCursor(0,1);                   lcd.print("P1:"); lcd.print(avg(P1Temp[0],P1Temp[1],P1Temp[2]));                                                     // Zeile 2
                                        lcd.print("  P2:"); lcd.print(avg(P2Temp[0],P2Temp[1],P2Temp[2])); 
                                        lcd.print("  P3:"); lcd.print(avg(P3Temp[0],P3Temp[1],P3Temp[2]));
  lcd.setCursor(0,2);                   lcd.print("P4:"); lcd.print(avg(P4Temp[0],P4Temp[1],P4Temp[2]));                                                     // Zeile 3
                                        lcd.print("  P5:"); lcd.print(avg(P5Temp[0],P5Temp[1],P5Temp[2])); 
                                        lcd.print("  P6:"); lcd.print(avg(P6Temp[0],P6Temp[1],P6Temp[2]));
  lcd.setCursor(3,3);                   lcd.print("P7:"); lcd.print(avg(P7Temp[0],P7Temp[1],P7Temp[2]));                                                     // Zeile 4
                                        lcd.print("    P8:"); lcd.print(avg(P8Temp[0],P8Temp[1],P8Temp[2]));
}

void menuP1(void) {

  lcd.clear();
  lcd.setCursor(6,0); lcd.print("Puffer 1");                                                        // Zeile 1
  lcd.setCursor(0,1); lcd.print("oben:  "); lcd.print(P1Temp[0]); lcd.write(0); lcd.print("C");     // Zeile 2
  lcd.setCursor(0,2); lcd.print("mitte: "); lcd.print(P1Temp[1]); lcd.write(0); lcd.print("C");     // Zeile 3
  lcd.setCursor(0,3); lcd.print("unten: "); lcd.print(P1Temp[2]); lcd.write(0); lcd.print("C");     // Zeile 4
}

void menuP2(void) {

  lcd.clear();
  lcd.setCursor(6,0); lcd.print("Puffer 2");                                                        // Zeile 1
  lcd.setCursor(0,1); lcd.print("oben:  "); lcd.print(P2Temp[0]); lcd.write(0); lcd.print("C");     // Zeile 2
  lcd.setCursor(0,2); lcd.print("mitte: "); lcd.print(P2Temp[1]); lcd.write(0); lcd.print("C");     // Zeile 3
  lcd.setCursor(0,3); lcd.print("unten: "); lcd.print(P2Temp[2]); lcd.write(0); lcd.print("C");     // Zeile 4
}

void menuP3(void) {

  lcd.clear();
  lcd.setCursor(6,0); lcd.print("Puffer 3");                                                        // Zeile 1
  lcd.setCursor(0,1); lcd.print("oben:  "); lcd.print(P3Temp[0]); lcd.write(0); lcd.print("C");     // Zeile 2
  lcd.setCursor(0,2); lcd.print("mitte: "); lcd.print(P3Temp[1]); lcd.write(0); lcd.print("C");     // Zeile 3
  lcd.setCursor(0,3); lcd.print("unten: "); lcd.print(P3Temp[2]); lcd.write(0); lcd.print("C");     // Zeile 4
}

void menuP4(void) {

  lcd.clear();
  lcd.setCursor(6,0); lcd.print("Puffer 4");                                                        // Zeile 1
  lcd.setCursor(0,1); lcd.print("oben:  "); lcd.print(P4Temp[0]); lcd.write(0); lcd.print("C");     // Zeile 2
  lcd.setCursor(0,2); lcd.print("mitte: "); lcd.print(P4Temp[1]); lcd.write(0); lcd.print("C");     // Zeile 3
  lcd.setCursor(0,3); lcd.print("unten: "); lcd.print(P4Temp[2]); lcd.write(0); lcd.print("C");     // Zeile 4
}

void menuP5(void) {

  lcd.clear();
  lcd.setCursor(6,0); lcd.print("Puffer 5");                                                        // Zeile 1
  lcd.setCursor(0,1); lcd.print("oben:  "); lcd.print(P5Temp[0]); lcd.write(0); lcd.print("C");     // Zeile 2
  lcd.setCursor(0,2); lcd.print("mitte: "); lcd.print(P5Temp[1]); lcd.write(0); lcd.print("C");     // Zeile 3
  lcd.setCursor(0,3); lcd.print("unten: "); lcd.print(P5Temp[2]); lcd.write(0); lcd.print("C");     // Zeile 4
}

void menuP6(void) {

  lcd.clear();
  lcd.setCursor(6,0); lcd.print("Puffer 6");                                                        // Zeile 1
  lcd.setCursor(0,1); lcd.print("oben:  "); lcd.print(P6Temp[0]); lcd.write(0); lcd.print("C");     // Zeile 2
  lcd.setCursor(0,2); lcd.print("mitte: "); lcd.print(P6Temp[1]); lcd.write(0); lcd.print("C");     // Zeile 3
  lcd.setCursor(0,3); lcd.print("unten: "); lcd.print(P6Temp[2]); lcd.write(0); lcd.print("C");     // Zeile 4
}

void menuP7(void) {

  lcd.clear();
  lcd.setCursor(6,0); lcd.print("Puffer 7");                                                        // Zeile 1
  lcd.setCursor(0,1); lcd.print("oben:  "); lcd.print(P7Temp[0]); lcd.write(0); lcd.print("C");     // Zeile 2
  lcd.setCursor(0,2); lcd.print("mitte: "); lcd.print(P7Temp[1]); lcd.write(0); lcd.print("C");     // Zeile 3
  lcd.setCursor(0,3); lcd.print("unten: "); lcd.print(P7Temp[2]); lcd.write(0); lcd.print("C");     // Zeile 4
}

void menuP8(void) {

  lcd.clear();
  lcd.setCursor(6,0); lcd.print("Puffer 8");                                                        // Zeile 1
  lcd.setCursor(0,1); lcd.print("oben:  "); lcd.print(P8Temp[0]); lcd.write(0); lcd.print("C");     // Zeile 2
  lcd.setCursor(0,2); lcd.print("mitte: "); lcd.print(P8Temp[1]); lcd.write(0); lcd.print("C");     // Zeile 3
  lcd.setCursor(0,3); lcd.print("unten: "); lcd.print(P8Temp[2]); lcd.write(0); lcd.print("C");     // Zeile 4
}

Man kann - wenn ich mich nicht irre - auch Grüppchen von Sensoren an unterschiedliche Pins hängen. Schon mal probiert?

Dein Problem ist vermutlich, du lässt den Sensoren nicht genug Zeit, um die Temperaturen zu messen und bereit zu stellen.
Schau dir mal die Beispiele der Library an. Da gibt es eins, das die entsprechende Zeit für dich einstellt.

Das habe ich noch nicht probiert. Wenn ich es mit einer Buslinie nicht zum laufen bekomme, ist das die nächste Option. Doch laut dem Maxim Application Note sollte mit einem Pull-Up Widerstand eine (nicht komplexe) Bustopologie von 200m möglich sein.

Ja, das geht.
Aber in Deinem Aufbau kommst Du mit den Stichleitungen zu Problemen.
Du könntest gemäss https://pdfserv.maximintegrated.com/en/an/AN148.pdf mit einem Reihenwiderstand versuchen das auszugleichen.
Meine Lösung war, auf jedem Zweig mit einem 3.3K am letzten Sensor.

Ansonsten bleibt Dir nichts weiter, als die Hauptleitung an jedem Stich aufzutrennen und Stichleitungen zum Hauptstrang zurückzuführen.
Das geht auch ganz gut....

Das auteilen auf mehrere Busse geht auch. halte ich aber bei Deinen paar Messstellen nicht für zwingend notwendig...

Danke, dann werde ich dort noch genauer schauen.
Aber für mein Verständnis: requestTemperatures() gibt den Befehl, dass alle Sensoren gleichzeitig eine Temperaturwandlung starten sollen. Also misst jeder Sensor die Temperatur und speichert diese. Das ist doch die Zeit, z.B. 375ms für 11 Bit Auflösung, die ich warten muss, bis ein getTempC() erfolgen kann?

Verstehe ich das richtig, dass bei deiner Lösung der 3k3 als Pull-Up von DQ nach Vdd geht?
Wie viele Sensoren hast du denn bei deinem Aufbau?

Hier ein Bild von einer Zweigstelle, wo der Hauptstrang durchgeht und drei Sensoren, je mit einer einzelnen Stichleitung, angeschlossen sind. Also es geht immer eine Leitung vom Sensor bis zur Zweigstelle am Hauptstrang.

Ja,
Allerdings wartest Du nicht lange genug.
Du setzt im Setup die Auflösung, startest den request und liest die Sensoren aus - da sind die noch nicht soweit.

Baue den Abruf in eine eigene Funktion:

void rufeTemp()
{
  static unsigned long letzterStart = 0;
  if (millis() - letzterStart > 1000)
  {
    P1Temp[0] = sensors.getTempC(P1o); P1Temp[1] = sensors.getTempC(P1m); P1Temp[2] = sensors.getTempC(P1u);
    P2Temp[0] = sensors.getTempC(P2o); P2Temp[1] = sensors.getTempC(P2m); P2Temp[2] = sensors.getTempC(P2u);
    P3Temp[0] = sensors.getTempC(P3o); P3Temp[1] = sensors.getTempC(P3m); P3Temp[2] = sensors.getTempC(P3u);
    P4Temp[0] = sensors.getTempC(P4o); P4Temp[1] = sensors.getTempC(P4m); P4Temp[2] = sensors.getTempC(P4u);
    P5Temp[0] = sensors.getTempC(P5o); P5Temp[1] = sensors.getTempC(P5m); P5Temp[2] = sensors.getTempC(P5u);
    P6Temp[0] = sensors.getTempC(P6o); P6Temp[1] = sensors.getTempC(P6m); P6Temp[2] = sensors.getTempC(P6u);
    P7Temp[0] = sensors.getTempC(P7o); P7Temp[1] = sensors.getTempC(P7m); P7Temp[2] = sensors.getTempC(P7u);
    P8Temp[0] = sensors.getTempC(P8o); P8Temp[1] = sensors.getTempC(P8m); P8Temp[2] = sensors.getTempC(P8u);
    sensors.requestTemperatures();
    letzterStart = millis();
  }
}

Im Setup eine zusätzliche Zeile:

  sensors.setWaitForConversion(false);

und im loop() rufst Du nur die Funktion auf: rufeTemp() - fertig.
Dann bekommst Du alle Sekunde einen Wert - mehr brauchst Du nicht. :wink:

Ja.
Und zwar in jedem Stich am letzten Sensor.

An den PufferspeicherSensoren habe ich das als Stern gebaut. Also alle 4 Sensoren direkt an die Hauptleitung ran - einen 4K7 R dort direkt verbaut und gut war.

Ich hab auch ne längere Zeit gebraucht um das stabil zu bekommen - zumal teilweise mit Neonröhren verstrahlt ...

Du musst dir nur mal den Code in der Bibliothek anschauen. Das ist fast alles leicht verständlich. Dann siehst du dass requestTemperature() normalerweise schon selbst wartet. Wenn man das nicht-blockierend machen will muss man es vorher explizit angeben

Somit wäre ein zusätzliches delay() wohl nicht zwingend notwendig? Für 9 Sensoren funktioniert das soweit auch, nur dann bei höherer Anzahl bekomme ich von keinem mehr einen Wert ...

Ich habe noch eine - möglicherweise abwegige - Idee, anschließend an #2 von Klaus.

Lt. Datenblatt bei Reichelt haben die Sensoren einen Standby-Strom von bis zu einem Milliampere.
Nun werden sie zwar fremdgespeist, aber könnte es nicht sein, dass zu viele davon an langen Kabeln vielleicht doch den Portpin übermäßig belasten?

Dann könnte eine Aufteilung auf mehrere Portpins und entsprechend viele OneWire-Instanzen vielleicht doch helfen.

Da die 5V mitgeführt werden und keine parasite Versorgung erfolgt, ist das auszuschliessen.

Aber die Leitungskapazität könnte zu hoch werden und damit das Signal zu sehr verschliffen.

Gruß Tommy

Das ja, aber nicht durch die Kapazität.
Durch die Stichleitungen kommt es zu ähnlichen Effekten.