1-Wire Timing-Problem bei langer Busstrecke (DS18B20)?

Hallo, ich habe ein interessantes Problem:

ich habe mir für den Uno ein kleines Programm zusammengestellt, dass 6 DS18B20 ausliest und dann die Messwerte als Modbus-RTU-Slave zur Verfügung stellt.

Das funktioniert einwandfrei, wenn ich die Sensoren unmittelbar am Uno anschließe (Pullup 4,7k). Differenz der Sensoren 0,1K (alle 6 als Bündel zusammen)

Wenn ich die Sensorik an eine lange Busleitung (ca. 70m) als Linie anschließe (Pullup 1,56k) werden die Sensoren ebenfalls noch ausgelesen, allerdings weichen die Messwerte dann bis zu 2,8K voneinander ab. Der Messort und das Programm sind immer noch identisch.

Spiele ich bei gleichem Setup, das Beispielprogramm DS18x20 aus der OneWire-Bibliothek ein, ermittelt dieses die richtigen Messwerte! Es werden keine CRC-Fehler erkannt. Die Hardware scheint also zu funktionieren!

Allerdings liefert mein eigenes Programm andere Werte, also kann es doch nur noch ein Timing-Problem innerhalb des Controllers/Programm sein, oder?

// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ModbusRtu.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 3
#define TXEN  2
//
uint16_t au16data[6];

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

// Aktivieren des ModBus-Slave
Modbus slave(1, 0, TXEN); // this is slave @1 and RS-485

// Feste Zuordnung der Sensoren über die Seriennummer
DeviceAddress Sensor0 = {0x28, 0xAA, 0xBA, 0x34, 0x13, 0x13, 0x01, 0x70};
DeviceAddress Sensor1 = {0x28, 0xAA, 0x7C, 0x35, 0x13, 0x13, 0x01, 0x41};
DeviceAddress Sensor2 = {0x28, 0xAA, 0x7A, 0x35, 0x13, 0x13, 0x01, 0xDD};
DeviceAddress Sensor3 = {0x28, 0xAA, 0x15, 0x25, 0x13, 0x13, 0x01, 0x9B};
DeviceAddress Sensor4 = {0x28, 0xAA, 0xA7, 0x32, 0x13, 0x13, 0x01, 0xE9};
DeviceAddress Sensor5 = {0x28, 0xAA, 0xD7, 0x2C, 0x13, 0x13, 0x01, 0xA9};

/*
   The setup function. We only start the sensors here
*/
void setup(void)

{
  slave.begin( 9600 ); // baud-rate at 19200

  // Start up the library
  sensors.begin();
}

/*
   Main function, get and show the temperature
*/
void loop(void)
{
  sensors.requestTemperatures(); // Send the command to get temperatures
  float modbuswert_float_0 = sensors.getTempC(Sensor0);
  au16data[0] = modbuswert_float_0 * 1000;
  float modbuswert_float_1 = sensors.getTempC(Sensor1);
  au16data[1] = modbuswert_float_1 * 1000;
  float modbuswert_float_2 = sensors.getTempC(Sensor2);
  au16data[2] = modbuswert_float_2 * 1000;
  float modbuswert_float_3 = sensors.getTempC(Sensor3);
  au16data[3] = modbuswert_float_3 * 1000;
  float modbuswert_float_4 = sensors.getTempC(Sensor4);
  au16data[4] = modbuswert_float_4 * 1000;
  float modbuswert_float_5 = sensors.getTempC(Sensor5);
  au16data[5] = modbuswert_float_5 * 1000;
  slave.poll( au16data, 6 );
}

Grüße Alex

Bau mal testweise ein delay von 1sek ein. Die Sensoren brauchen ca 0,7 sek. um die Daten richtig zur Verfügung zu stellen.
Das wird im originalen Beispiel auch berücksichtigt.
Im Endausbau das delay aber durch eine Funktion mit millis() ersetzen.

Xander8:
....Allerdings liefert mein eigenes Programm andere Werte, also kann es doch nur noch ein Timing-Problem innerhalb des Controllers/Programm sein, oder?

Zum Timing sagt das Datenblatt:
"The 1-Wire bus must be switched to the strong pullup within 10μs (max)
after a Convert T [44h] or Copy Scratchpad [48h] command is issued, and
the bus must be held high by the pullup for the duration of the
conversion (tCONV) or data transfer (tWR = 10ms)."

70m (und vielleicht kein speziell kapazitätsarmes Kabel verwendet) und 6 Sensoren sind schon
eine Herausforderung. Den Arduino zu den Fühlern setzen und die 70m mit dem Modbus nutzen könnte
eine Lösung sein.

Ich habe ein ähnliches Problem mit einem DS2482 gelöst.
DS2482 und DS18B20 passen immer zu 100% zusammen.

Als Kabel eignet sich auch hervorragend ein Antennenkabel.
GND = Schirm, D = Mittelleiter, VCC = extra Draht
und direkt am Sensor eine Kondenstorkaskade mit 100µ + 100n + 100p zwischen VCC und GND.
Den Pullup habe ich auch aufgeteilt dh. 10K am µC und 10K am Sensor macht zusammen 5K oder auch weniger.

Gruß Aron

Hi

I2C über 70m? :o
Nicht schlecht, hatte hier nach wenigen Metern keine Kommunikationspartner mehr finden können - allerdings über Telefon-Kabel (Cat 3, 4x2x0,6);

MfG

Edit siehe #9f

Hallo,

die Spannungsversorgung ist wichtig. Nicht das Xander alles parasitär betreibt. Ich habe ähnlich wie ar182 an den Sensoren direkt je einen 100nF dran. 5V werden auf ca. 10m Netzwerkkabel mitgeführt. Der Pullup sitzt bei mir nahe des Arduinos am Anfang der Leitung, also nicht irgenwo im Kabel. Alle Sensoren sind trennbar. Ich könnte jederzeit verlängern oder kürzen durch hinzufügen oder entfernen von Sensoren. Variationen wird es viele geben. Die Spannungsversorgung ist allerdings äußerst wichtig und das Timing sowieso. Der DS18B20 benötigt je nach Auflösung eine Conversion Pause zwischen 100 und 750ms zwischen Request und Daten abholen.

postmaster-ino:
Hi

I2C über 70m? :o
Nicht schlecht, hatte hier nach wenigen Metern keine Kommunikationspartner mehr finden können - allerdings über Telefon-Kabel (Cat 3, 4x2x0,6);

MfG

Wieso I2C ?

Hallo,

das passiert wenn mal wieder jemand verschiedene Aussagen vermischt. :confused:
Xander hat 70m Leitungslänge aber kein I2C.
ar182 hat die I2C-OneWire Bridge DS2482 aber keine 70m.
Alle Klarheiten beseitigt? :slight_smile:

Hi

Ich bezog mich auf

ar182:
Ich habe ein ähnliches Problem mit einem DS2482 gelöst.

In Annahme, daß Er ebenfalls eine längere Entfernung zum Sensor zu überbrücken hatte und Das als erfolgreiche Lösung anzubieten hat.
Kurz gegurgelt und den DS2482 als 'I2C->1wire' identifiziert, also 1wire 'lokal' und I2C bis dahin.

MfG

Edit
Ebenfalls siehe #9f

postmaster-ino:
Hi

Ich bezog mich aufIn Annahme, daß Er ebenfalls eine längere Entfernung zum Sensor zu überbrücken hatte und Das als erfolgreiche Lösung anzubieten hat.
Kurz gegurgelt und den DS2482 als 'I2C->1wire' identifiziert, also 1wire 'lokal' und I2C bis dahin.

MfG

Dazu passt dann aber die Kabelbeschreibung nicht. Die ist für 1Wire.

:o stimmt - mit drei Drähten kommt man bei I2C nicht sonderlich weit ...

postmaster-ino:
Kurz gegurgelt und den DS2482 als 'I2C->1wire' identifiziert, also 1wire 'lokal' und I2C bis dahin.

NEIN!
DS2482 am µC und die lange Leitung ist 1Wire!

Alles klar?

I2C ist noch schwieriger auf langen Leitungen.

postmaster-ino:
:o stimmt - mit drei Drähten kommt man bei I2C nicht sonderlich weit ...

Ok...alles klar.
Jetzt warten wir, wie der TO sein Problem löst.

Nur so nebenbei, mit richtigem Kabel und richtiger Hardware kann man I2C auch mit ähnlicher KabelLänge nutzen.

Die Spannungsversorgung ist separat ausgeführt, kein parasitärer Betrieb.

Ein Delay von 1-2s verbessert die Situation nicht.

Alle Beispielprogramme die die DallasTemperature.h nutzen zeigen das gleiche Problem:

20.56
20.37
20.50
20.50
20.44
20.44
18.19
19.81
23.75
20.44
20.31
23.12
18.50
19.81
23.56
20.44
20.31
22.56
17.94

Im Vergleich dazu (keine Hardwareänderung):

ROM = 28 AA 7C 35 13 13 1 41
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
ROM = 28 AA BA 34 13 13 1 70
Chip = DS18B20
Data = 1 48 1 4B 46 7F FF 7F 10 75 CRC=75
Temperature = 20.50 Celsius, 68.90 Fahrenheit
ROM = 28 AA 7A 35 13 13 1 DD
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
ROM = 28 AA 15 25 13 13 1 9B
Chip = DS18B20
Data = 1 48 1 4B 46 7F FF C 10 96 CRC=96
Temperature = 20.50 Celsius, 68.90 Fahrenheit
ROM = 28 AA A7 32 13 13 1 E9
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
ROM = 28 AA D7 2C 13 13 1 A9
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
No more addresses.

ROM = 28 AA 7C 35 13 13 1 41
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
ROM = 28 AA BA 34 13 13 1 70
Chip = DS18B20
Data = 1 48 1 4B 46 7F FF 7F 10 75 CRC=75
Temperature = 20.50 Celsius, 68.90 Fahrenheit
ROM = 28 AA 7A 35 13 13 1 DD
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
ROM = 28 AA 15 25 13 13 1 9B
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
ROM = 28 AA A7 32 13 13 1 E9
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
ROM = 28 AA D7 2C 13 13 1 A9
Chip = DS18B20
Data = 1 47 1 4B 46 7F FF C 10 6C CRC=6C
Temperature = 20.44 Celsius, 68.79 Fahrenheit
No more addresses.

Dann poste doch mal den verwendete Beispielsketch, bei dem es ok aussieht.

Bitteschön, der funktionierende Code :slight_smile:

#include <OneWire.h>

// OneWire DS18S20, DS18B20, DS1822 Temperature Example
//
// http://www.pjrc.com/teensy/td_libs_OneWire.html
//
// The DallasTemperature library can do all this work for you!
// https://github.com/milesburton/Arduino-Temperature-Control-Library

OneWire  ds(10);  // on pin 10 (a 4.7K resistor is necessary)

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
  byte i;
  byte present = 0;
  byte type_s;
  byte data[12];
  byte addr[8];
  float celsius, fahrenheit;
  
  if ( !ds.search(addr)) {
    Serial.println("No more addresses.");
    Serial.println();
    ds.reset_search();
    delay(250);
    return;
  }
  
  Serial.print("ROM =");
  for( i = 0; i < 8; i++) {
    Serial.write(' ');
    Serial.print(addr[i], HEX);
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println("CRC is not valid!");
      return;
  }
  Serial.println();
 
  // the first ROM byte indicates which chip
  switch (addr[0]) {
    case 0x10:
      Serial.println("  Chip = DS18S20");  // or old DS1820
      type_s = 1;
      break;
    case 0x28:
      Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      Serial.println("Device is not a DS18x20 family device.");
      return;
  } 

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);        // start conversion, with parasite power on at the end
  
  delay(1000);     // maybe 750ms is enough, maybe not
  // we might do a ds.depower() here, but the reset will take care of it.
  
  present = ds.reset();
  ds.select(addr);    
  ds.write(0xBE);         // Read Scratchpad

  Serial.print("  Data = ");
  Serial.print(present, HEX);
  Serial.print(" ");
  for ( i = 0; i < 9; i++) {           // we need 9 bytes
    data[i] = ds.read();
    Serial.print(data[i], HEX);
    Serial.print(" ");
  }
  Serial.print(" CRC=");
  Serial.print(OneWire::crc8(data, 8), HEX);
  Serial.println();

  // Convert the data to actual temperature
  // because the result is a 16 bit signed integer, it should
  // be stored to an "int16_t" type, which is always 16 bits
  // even when compiled on a 32 bit processor.
  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3; // 9 bit resolution default
    if (data[7] == 0x10) {
      // "count remain" gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    // at lower res, the low bits are undefined, so let's zero them
    if (cfg == 0x00) raw = raw & ~7;  // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw & ~3; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw & ~1; // 11 bit res, 375 ms
    //// default is 12 bit resolution, 750 ms conversion time
  }
  celsius = (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
  Serial.print("  Temperature = ");
  Serial.print(celsius);
  Serial.print(" Celsius, ");
  Serial.print(fahrenheit);
  Serial.println(" Fahrenheit");
}

Xander8:
Bitteschön, der funktionierende Code :slight_smile:

OK...und dieser funktioniert bei dir mit den 6 Sensoren (zusammen) und 70m Kabel dazwischen ?

Warum verwendest du dann nicht diesen ?

HotSystems:
OK...und dieser funktioniert bei dir mit den 6 Sensoren (zusammen) und 70m Kabel dazwischen ?

Ja, genau. Ohne Fehler oder Auffälligkeiten

HotSystems:
Warum verwendest du dann nicht diesen ?

Weil ich blutiger Anfänger bin, und es noch nicht geschafft habe, ohne die Dallas-Lib die Sensoren über die Seriennummer zu adressieren ...

Hallo,

die Logik verstehe ich nun wiederum nicht. Du musst doch die Dallas Lib in jeden Fall verwenden um die Sensoren ansprechen zu können. Entweder mit der automatischen Erkennung oder eigener Vorgabe per Zuordnung über die Seriennummern.

Hallo,

ein Bsp. was du leicht anpassen kannst. Musst nur die Anzahl der Sensoren erweitern und Adressen eintragen.

/*   
   Arduino Mega 2560
   Arduino IDE v1.8.6
*/

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

// Data wire is plugged into port 12 on the Arduino
#define ONE_WIRE_BUS 12

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

// change of your sensors adress or use "getTempCByIndex" and see below
DeviceAddress sensor1 = { 0x10, 0x40, 0xDD, 0xC3, 0x02, 0x08, 0x00, 0xB4 };
DeviceAddress sensor2 = { 0x10, 0x8A, 0x0B, 0xAC, 0x02, 0x08, 0x00, 0x2C };
DeviceAddress sensor3 = { 0x28, 0xFF, 0x60, 0xAC, 0x6B, 0x14, 0x03, 0xB3 };

int resolution = 9;                         // DS18S20 only 9bit, other 9...12
unsigned long lastTempRequest;      
const unsigned int ConversionDelay = 750;   // default 750ms, okay for all DS1820 types
float temperature = 0.0;
bool AllowDallasTempRequest = true;
float TempSensor1 = 0.0;                    // Zwischenspeicher zum auslesen
float TempSensor2 = 0.0;                    // Zwischenspeicher zum auslesen
float TempSensor3 = 0.0;                    // Zwischenspeicher zum auslesen

const byte _Flash_LED = 13;                 // LED 13 auf Arduino Board

void setup(void)
{
  Serial.begin(9600);
  
  digitalWrite(_Flash_LED, LOW); 
  pinMode(_Flash_LED, OUTPUT);
  
  sensors.begin();
  // set the resolution (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(sensor1, resolution);
  sensors.setResolution(sensor2, resolution);
  sensors.setResolution(sensor3, resolution);
  sensors.setWaitForConversion(false);        // makes it async
}

void loop(void)
{ 
   
  // Request a temperature conversion 
  if ( AllowDallasTempRequest == true )
    {           
     sensors.requestTemperatures();
     lastTempRequest = millis(); 
     AllowDallasTempRequest = false;
    }
  
  // readout the Dallas sensors  
  if ( millis() - lastTempRequest >= ConversionDelay )  // waited long enough?
    {
     TempSensor1 = sensors.getTempC(sensor1);  // 1. Dallas Sensor auslesen (Device Adresse)
     TempSensor2 = sensors.getTempC(sensor2);  // 2. Dallas Sensor auslesen (Device Adresse)  
     TempSensor3 = sensors.getTempC(sensor3);  // 3. Dallas Sensor auslesen (Device Adresse)  
     AllowDallasTempRequest = true;

     Serial.print(F("Temps:")); Serial.print('\t');
     Serial.print(TempSensor1); Serial.print('\t');
     Serial.print(TempSensor2); Serial.print('\t');
     Serial.print(TempSensor3);
     Serial.println();
    }
  
  // *** does other things *** //
  LED_Flashing();
  
}   // End of Loop


// ----------------------------------------------------------------------------------- //
void LED_Flashing()                           // Heartbeat
{
  static bool state_LED = LOW;
  static unsigned long millis_LED = 0;
                  
     if (state_LED == LOW && millis() > millis_LED )  {
       digitalWrite(_Flash_LED, HIGH);        // LED einschalten für
       millis_LED = millis() + 100;           
       state_LED = HIGH;
     }
     if (state_LED == HIGH && millis() > millis_LED )  {
       digitalWrite(_Flash_LED, LOW);         // LED ausschalten für
       millis_LED = millis() + 900;         
       state_LED = LOW;
     }   
}