Arduino 1-Wire-Kommunikationsprotokoll

Arduino 1-Wire ist ein bidirektionales Kommunikationsprotokoll, das die Übertragung von Daten zwischen Arduino und anderen 1-Wire-kompatiblen Geräten ermöglicht. Das 1-Wire-Protokoll ist eine einfache, aber effiziente Möglichkeit zur Datenübertragung.

Das Steuergerät kommuniziert mit einem oder mehreren Single-Wire-Peripheriegeräten über eine einzige Datenleitung, die auch zur Stromversorgung des Peripheriegeräts verwendet werden kann, was auch als parasitäre Stromversorgung bezeichnet wird.

Das 1-Wire-Protokoll wurde von Dallas Semiconductor (heute Maxim) entwickelt und wird seitdem häufig in der Industrie und der Heimautomation eingesetzt. Zu den Geräten, die das 1-Wire-Protokoll nutzen, gehören z.B. Temperatursensoren, EEPROMs, Timer und andere Sensoren.

Ein beliebtes Gerät ist beispielsweise der digitale Temperatursensor DS18B20

TO92-Gehäuse, Messbereich zwischen 55°C und +125°C, dies wird in 12-Bit-Auflösung am Datenausgangspin angezeigt.

Es unterstützt eine Betriebsspannung von 3,3 V und 5 V.

Ein beliebtes Gerät ist beispielsweise der digitale Temperatursensor DS18B20

Werbung

Für die Arduino 1-Wire-Kommunikation benötigen wir einen 1-Wire-Bus, der z.B. Kommunikation zwischen einem Arduino und 1-Wire-kompatiblen Geräten. Der 1-Wire-Bus wird üblicherweise mit einem 4,7-Ohm-Widerstand an die Versorgungsspannung angeschlossen. Bei einem längeren Kabel und einer geringeren Versorgungsspannung kann der Wert des Pull-up-Widerstands reduziert werden.

Im parasitären Strommodus sind nur zwei Drähte erforderlich, ein Datenkabel und ein Massekabel. Am Controller muss die Datenleitung mit einem 4,7k Pull-up-Widerstand hochgezogen werden. Wenn die Datenleitung hoch ist, lädt das Gerät seinen internen Kondensator aus dieser Spannung auf.

Wenn beispielsweise ein DS18S20-Peripheriegerät eine Temperaturumwandlung durchführt, kann der Strom bis zu 1,5 mA erreichen. In diesem Fall muss der Buscontroller den 1-Wire-Bus auf Hochpegel halten, um ausreichend Strom bereitzustellen, bis der Vorgang abgeschlossen ist. Während dieser Zeit kann der Controller den 1-Wire-Bus nicht nutzen.

Arduino DS18B20 1-Draht-Bus mit parasitärer Stromversorgung

Für eine normale Stromversorgung benötigen wir drei Drähte, den Daten, die positive Leitung und den GND. Auch hier müssen wir den 4,7k-Pull-Up-Widerstand zwischen der Datenleitung und der Stromleitung platzieren. In diesem Fall ist der Bus frei, sodass der Mikrocontroller kontinuierlich mit den Peripheriegeräten kommunizieren kann.

Arduino DS18B20 1-Draht-Bus mit normaler Stromversorgung

Jedes 1-Wire-Gerät verfügt über eine einzigartig 64-Bit-ROM-Adresse, die der Arduino kennen muss, um mit dem Gerät kommunizieren zu können. Die ersten 8 Bits sind der Familiencode, dann die 48-Bit-Seriennummer und schließlich der 8-Bit-CRC.

Der Familiencode identifiziert den Gerätetyp. zB. der Familiencode des DS18B20 ist 0x28. CRC (Cyclic Redundancy Check) ist eine Prüfsumme, die in Kommunikationsprotokollen zur Fehlererkennung verwendet wird. CRC wird von Arduino verwendet, um Kommunikationsfehler zu erkennen.

DS18B20 digitaler Temperatursensor

DS18B20 digitaler Temperatursensor

Wasserdicht, in einer Edelstahlkapsel, mit 3 Meter langem Kabel. Messbereich zwischen 55°C und +125°C, 12-Bit-Auflösung. Für 3,3V- und 5V-Systeme.

werbung

Für die Arduino 1-Wire-Kommunikation benötigen wir die OneWire-Bibliothek. Wenn es noch nicht installiert ist, installieren wir es über den Arduino-Bibliotheksmanager. Die OneWire-Bibliothek muss am Anfang der Arduino-Skizze eingefügt werden.

#include <OneWire.h>

Der Arduino Mega 2560 Mikrocontroller-Board

Arduino Mega 2560
Werbung – Arduino Mega 2560

OneWire-Objekt

Erstellt eine Instanz des OneWire-Objekts. Der vom 1-Wire-Bus verwendete Pin muss als Parameter angegeben werden. An den 1 Wire Bus können mehrere Peripheriegeräte angeschlossen werden. Wenn wir mit unserem Arduino-Gerät noch mehr 1-Wire-Busse verwenden möchten, benötigen wir für jeden Pin eine OneWire-Instanz.

OneWire myWire(pin);

Zurück zum Inhaltsverzeichnis.

Bei Verwendung der Funktion search() sucht der Arduino nach allen Geräten im 1-Wire-Netzwerk. Das 1-Wire-Protokoll verwendet eindeutige Kennungen zur Identifizierung von Geräten. Die Funktion search() gibt die IDs aller Geräte zurück, die auf dem 1-Draht-Bus gefunden wurden. Der Parameter „addrArray“ ist ein 8-Byte-Array. Wenn es ein Gerät findet, schreibt es seine Adresse in das Array und gibt true zurück. Gibt false zurück, wenn keine weiteren Geräte gefunden werden.

myWire.search(addrArray);

Zurück zum Inhaltsverzeichnis.

Mit der Funktion „reset_search()“ wird der Suchvorgang zurückgesetzt, um alle Geräte am 1-Wire-Bus zu finden. Mit der Funktion reset_search() kann das Arduino erneut nach Peripheriegeräten auf dem 1-Wire-Bus suchen.

if ( !myWire.search(addrArray)) 
{
  Serial.println("Keine Adressen mehr.");
  myWire.reset_search();
  delay(250);
  return;
}

Zurück zum Inhaltsverzeichnis.

reset()

Die Funktion reset() setzt den 1-Wire-Bus zurück und sendet eine Anwesenheitsabfrage an die Peripheriegeräte. Wenn die Geräte vorhanden sind, gibt die Funktion „true“ zurück, andernfalls „false“. Es wird normalerweise vor Beginn der Kommunikation verwendet.

boolean present = myWire.reset();

Zurück zum Inhaltsverzeichnis.

select()

Diese Funktion wählt das Gerät mit der angegebenen ROM-Adresse auf dem 1-Wire-Bus aus. Der als Parameter angegebene Wert ist ein konstantes Array, das die ROM-Adresse des auszuwählenden Geräts enthält.

Nach dem Zurücksetzen verwenden wir die Funktion „select()“, um auszuwählen, mit welchem ​​Peripheriegerät wir kommunizieren möchten. Anschließend erfolgt die gesamte Kommunikation mit dem ausgewählten Peripheriegerät, bis wir den 1-Wire-Bus mit der Funktion „reset()“ erneut zurücksetzen.

myWire.select(const uint8_t rom[8])

Zurück zum Inhaltsverzeichnis.

skip()

Die Funktion „skip()“ überspringt die Auswahl von Peripheriegeräten. Wenn wir nur ein Peripheriegerät am 1-Draht-Bus haben, können wir die Suche vermeiden und sofort auf ein Gerät zugreifen.

myWire.skip();

Zurück zum Inhaltsverzeichnis.

write()

Mit der Funktion write() werden Daten auf das Peripheriegerät geschrieben. Der als Parameter angegebene Wert ist vom Typ Byte.

write(byte mydata);

Die folgende Funktion wird verwendet, um die Peripherie des 1-Wire-Busses mit parasitärer Stromversorgung zu beschreiben. Es schreibt ein Byte an das Peripheriegerät und lässt den 1-Wire-Bus bis zum Ende der Kommunikation mit Strom versorgt.

myWire.write(byte mydata, 1);

Zurück zum Inhaltsverzeichnis.

read()

Die Funktion read() liest die Daten vom 1-Wire-Bus. Gibt sie in einer Variablen vom Typ Byte zurück.

byte myData = myWire.read();

Zurück zum Inhaltsverzeichnis.

crc8()

Diese Funktion berechnet die CRC8-Prüfsumme für die als Parameter angegebenen Daten. Die als Parameter angegebenen Daten müssen sich in einem Array vom Typ uint8_t befinden, und der Parameter len gibt die Größe des Arrays an.

uint8_t crc = myWire.crc8(uint8_t dataArray, uint8_t len);

Zurück zum Inhaltsverzeichnis.

1-Wire-Beispielcodes

Im folgenden Beispiel lesen wir einen DS18B20-Temperatursensor mit Arduino am 1-Wire-Bus aus.

#include <OneWire.h>

// Arduino Pin 10 ist die Datenleitung des 1-Wire-Busses
const char oneWirePin = 10;

// Wir erstellen eine OneWire-Objekt-instanz
OneWire oneWire(oneWirePin);

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

void loop()
{
  // Wir erstellen ein Array für die aus Scratchpad gelesenen Daten
  byte data[12];

  // Array zum Lesen des ROM
  byte addr[8];

  // Den 1-Wire-Bus zurücksetzen,
  // Dies ist vor Beginn der Kommunikation erforderlich
  oneWire.reset();
  

  // Sensor-ID lesen
  // Mit diesem Befehl kann der Busmaster den 8-Bit-Familiencode,
  // die eindeutige 48-Bit-Seriennummer und den 8-Bit-CRC des DS18B20 lesen. 
  // Dieser Befehl kann nur verwendet werden, 
  // wenn sich nur ein DS18B20 am Bus befindet.
  oneWire.write(0x33);
  for (int i = 0; i < 8; i++)
  {
    addr[i] = oneWire.read();
  }

  // CRC-Prüfung
  if (OneWire::crc8(addr, 7) != addr[7])
  {
    Serial.println("Sensor-ID-CRC-Fehler.");
    return;
  }

  // Den 1-Wire-Bus zurücksetzen
  oneWire.reset();

  // Der Befehl Convert T [0x44] startet die Temperaturkonvertierung.
  oneWire.write(0x44);
  
  // Das Hinzufügen eines zweiten Parameters zeigt an,
  // dass das Sensorgerät mit parasitärer Energie betrieben wird.
  // oneWire.write(0x44, 1);

  // Warten auf den Abschluss der Messung.
  // Während der Messung ist der ds18b20 beschäftigt und nimmt keine Befehle entgegen
  // Laut Datenblatt dauert die Messung bei einer Auflösung von 12 Bit maximal 750ms.
  delay(1000);

  
  oneWire.reset();

  // Scratchpad-Auslesung, 
  // die gemessene Temperatur ist in diesem Speicherbereich zu finden
  oneWire.write(0xBE);
  
  for (int i = 0; i < 9; i++)
  {
    data[i] = oneWire.read();
  }

  // CRC-Prüfung
  if (OneWire::crc8(data, 8) != data[8])
  {
    Serial.println("Data-CRC-Fehler.");
    return;
  }

  // Berechnung der Temperatur
  int16_t raw = (data[1] << 8) | data[0];
  byte cfg = (data[4] & 0x60);
  if (cfg == 0x00) raw = raw & ~7;
  else if (cfg == 0x20) raw = raw & ~3;
  else if (cfg == 0x40) raw = raw & ~1;
  
  // in eine lesbare Form umwandeln
  float celsius = (float)raw / 16.0;
  float fahrenheit = celsius * 1.8 + 32.0;


  // Dann drucken wir die Temperatur auf dem seriellen Monitor aus
  Serial.print("Temperatur: ");
  Serial.print(celsius);
  Serial.println(" °C");
  Serial.print(fahrenheit);
  Serial.println(" °F");
  Serial.println();
  
  delay(1000);
}

Der obige Code kann nur zum Abfragen eines einzelnen DS18B20 verwendet werden. Mal sehen, was passiert, wenn wir mehrere Sensoren an den 1-Wire-Bus anschließen möchten.

Um mehrere DS18B20-Sensoren verwenden zu können, müssen wir die eindeutige Adresse jedes Sensors kennen. Dies ist wichtig, da der Arduino bei Verwendung der 1-Wire-Kommunikation wissen muss, welchen Sensor er abfragt. DS18B20-Sensoren verfügen über eine eindeutige 64-Bit-Adresse, die den Sensoren werkseitig zugewiesen wird. Um eindeutige Adressen zu erhalten, verwenden wir im folgenden Beispiel die Funktion search() der OneWire-Bibliothek.

Im loop()-Zyklus verwenden wir Die Funktion search(addr) der OneWire-Bibliothek, um die Geräte auf dem 1-Wire-Bus zu finden. Die Adresse des gefundenen Geräts wird im Funktionsparameter search() im Array addr gespeichert.

Mit der Funktion search() suchen wir nach allen Geräten am Bus und drucken die Temperaturdaten auf dem seriellen Monitor aus, wie aus dem vorherigen Beispiel bekannt.

Sobald alle Geräte gefunden wurden, wird mit der Funktion „reset_search()“ der Vorgang neu gestartet.

#include <OneWire.h>

// Arduino Pin 10 ist die Datenleitung des 1-Wire-Busses
const char oneWirePin = 10;

//  Wir erstellen eine OneWire-Objekt-instanz
OneWire oneWire(oneWirePin);

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

void loop() 
{
  // Wir erstellen ein Array für die aus Scratchpad gelesenen Daten
  byte data[12];

  // Array zum Lesen des ROM
  byte addr[8];
  
  // Die Funktion search() sucht nach Geräten am 1-Wire-Bus
  if(!oneWire.search(addr))
  {
    Serial.print("Keine Adressen mehr.");
	Serial.println();
	Serial.println();
	
	// wenn die Funktion search() die Suche beendet hat 
	// Wir starten die Suche neu, indem reset_search() aufrufen.
    oneWire.reset_search();
    delay(1000);
    return;
  }
  
  Serial.print("ROM: ");
  for(int i = 0; i < 8; i++)
  {
    // Wir drucken die Adresse des aktuellen Geräts aus
    Serial.print(addr[i], HEX);
    Serial.print(" ");
  }

  // CRC-Prüfung
  if (OneWire::crc8(addr, 7) != addr[7])
  {
    Serial.println("Sensor-ID-CRC-Fehler.");
    return;
  }
  
  // Den 1-Wire-Bus zurücksetzen
  oneWire.reset();
  
  // Wir wählen das Gerät am 1-Wire-Bus aus
  oneWire.select(addr);
  
  // Der Befehl Convert T [0x44] startet die Temperaturkonvertierung
  oneWire.write(0x44);
  
  // Warten auf den Abschluss der Messung.
  delay(1000);
  
  oneWire.reset();
  oneWire.select(addr);

  // Scratchpad-Auslesung
  oneWire.write(0xBE);

  for(int i = 0; i < 9; i++)
  {
    data[i] = oneWire.read();
  }
  
  // CRC-Prüfung
  if (OneWire::crc8(data, 8) != data[8])
  {
    Serial.println("Data-CRC-Fehler.");
    return;
  }
  
  // Berechnung der Temperatur
  int16_t raw = (data[1] << 8) | data[0];
  byte cfg = (data[4] & 0x60);
  if (cfg == 0x00) raw = raw & ~7;
  else if (cfg == 0x20) raw = raw & ~3;
  else if (cfg == 0x40) raw = raw & ~1;
  
  // in eine lesbare Form umwandeln
  float celsius = (float)raw / 16.0;
  float fahrenheit = celsius * 1.8 + 32.0;


  // Dann drucken wir die Temperatur auf dem seriellen Monitor aus
  Serial.print("Temperatur: ");
  Serial.print(celsius);
  Serial.print(" °C, - ");
  Serial.print(fahrenheit);
  Serial.println(" °F");
  
  delay(1000);
}	

Zurück zum Inhaltsverzeichnis.