Arduino 1-Wire kommunikációs protokoll

Az Arduino 1-Wire egy kétirányú kommunikációs protokoll, amely lehetővé teszi az adatok átvitelét az Arduino és más 1-Wire kompatibilis eszközök között. Az 1-Wire protokoll egy egyszerű, de hatékony módja az adatok átvitelének.

A vezérlőeszköz egyetlen adatvonalon keresztül kommunikál egy vagy több egyvezetékes periféria eszközzel, amely a periféria áramellátására is használható, ezt parazita áramellátásnak is nevezik.

Az 1-Wire protokollt Dallas Semiconductor (ma már Maxim néven ismert) fejlesztette ki, és azóta széles körben használják az iparban és az otthoni automatizálásban. Az 1-Wire protokollt használó eszközök közé tartoznak pl. hőmérséklet-érzékelők, EEPROM-ok, időzítők és egyéb szenzorok.

Egy népszerű eszköz például a DS18B20 digitális hőmérséklet-érzékelő

TO92 tokozású, 55°C – +125°C közötti méréstartomány, ez 12bit felbontásban jelenik meg az adatkimeneti lábán.

Támogatja a 3,3V-ov és az 5V-os üzemi feszültséget.

Egy népszerű eszköz például a DS18B20 digitális hőmérséklet-érzékelő

hirdetés

Az Arduino 1-Wire kommunikációhoz szükségünk van egy 1-Wire buszra, amely lehetővé teszi pl. egy Arduino és 1-Wire kompatibilis eszközök közötti kommunikációt. Az 1Wire busz általában egy 4,7 kohmos ellenállással van felhúzva a tápfeszültségre. Hosszabb vezeték és alacsonyabb tápfeszültség esetén a felhúzó ellenállás értékét csökkenthetjük.

Parazita tápfeszültség üzemmódban csak két vezeték szükséges, egy adatvezeték és egy gnd. A vezérlőnél egy 4,7k-os felhúzó ellenállással fel kell húzni az adatvonalat. Amikor az adatvezeték magas állapotban van, az eszköz ebből a feszültségből feltölti a belső kondenzátorát.

Például amikor egy DS18S20 periféria hőmérséklet-átalakítást végez, az áram akár a 1,5 mA-t is elérheti. Ilyenkor a buszvezérlőnek magasan kell tartania a 1-Wire buszt, hogy elegengő tápellátást biztosítson a művelet befejezéséig. A vezérlő ez idő alatt nem használhatja az 1-Wire buszt.

arduino ds18b20 1-wire busz parazita tápellátással

Normál tápellátás esetén három vezetékre van szükségünk, az adatvezetékre, a tápfeszültségre és a gnd-re. A 4,7k-os felhúzó ellenállást itt is el kell helyenünk az adatvonal és a tápvezeték közé. Ebben az esetben a busz szabad, így a mikrokontroller folyamatosan kommunikálhat a periféria eszközökkel.

arduino ds18b20 1-wire busz normál tápellátással

Minden 1-Wire eszköznek van egy egyedi 64 bites ROM címe, amelyet az Arduino-nak ismernie kell ahhoz, hogy kommunikálni tudjon az eszközzel. Az első 8 bit a családkód, majd a 48 bites sorozatszám és végül a 8 bites CRC.

A családkód azonosítja, hogy milyen eszköz típusát. Pl. a DS18B20 családkódja 0x28. A CRC (Cyclic Redundancy Check) egy ellenőrző összeg, amelyet a kommunikációs protokollokban használnak a hibák észlelésére. A CRC segítségével az Arduino képes észlelni a kommunikációs hibákat.

DS18B20 digitális Hőmérséklet érzékelő

DS18B20 digitális Hőmérséklet érzékelő

Vízálló, Saválló kapszulában, 3méter hosszú kábellel. 55°C – +125°C közötti méréstartomány, 12bit felbontás. 3,3V-ov és az 5V-os rendszerekhez.

hirdetés

Az Arduino 1-Wire kommunikációhoz szükségünk van a OneWire könyvtárra. Ha még nincs telepítve, akkor telepítsük azt az Arduino könyvtárkezelőjén keresztül. A OneWire könyvtárat be kell illeszteni az Arduino vázlat elején.

#include <OneWire.h>

Arduino Mega 2560 mikrokontroller kártya

Arduino Mega 2560
Arduino Mega 2560 – hirdetés

OneWire objektum

Létrehozza a OneWire objektum egy példányát, paraméterként az 1 Wire busz által használt pint kell megadni. Az 1 Wire buszra több perifériát is csatlakoztathatunk. Ha mégis több 1 Wire buszt szeretnénk használni az Arduino eszközünkel, akkor minden pinhez szükségünk van egy OneWire példányra.

OneWire myWire(pin);

A tartalomhoz

A search() függvény használata során az Arduino az 1 Wire hálózaton található összes eszközt keresi. Az eszközök azonosításához az 1 Wire protokoll egyedi azonosítókat használ. A search() függvény az összes eszköz azonosítóját visszaadja, amelyek az 1 Wire buszon talál. Az „addrArray” paraméter egy 8 bájtos tömb. Ha talál egy eszközt, beírja a címét a tömbbe, és true értéket ad vissza. Ha nem talál több eszközt, false értéket ad vissza.

myWire.search(addrArray);

A tartalomhoz

A reset_search() függvényt a keresési folyamat visszaállítására használjuk, hogy az összes eszközt megtaláljuk a 1-wire buszon. A reset_search() függvény lehetővé teszi az arduino számára, hogy újra keresse a periféria eszközöket a 1 Wire buszon.

if ( !myWire.search(addrArray)) 
{
  Serial.println("No more addresses.");
  myWire.reset_search();
  delay(250);
  return;
}

A tartalomhoz

reset()

A reset() függvény alaphelyzetbe állítja az 1 wire buszt, és küld egy jelenlét lekérdezést a periféria eszközöknek. Ha az eszközök jelen vannak, akkor a függvény igaz értéket ad vissza, ellenkező esetben hamisat. Általában a kommunikáció megkezdése elött használjuk.

boolean present = myWire.reset();

A tartalomhoz

select()

Ez a függvény kiválasztja az adott ROM címmel rendelkező eszközt az 1 wire buszon. A paraméterként megadott érték egy konstans tömb, amely a kiválasztandó eszköz ROM címét tartalmazza.

Az alaphelyzetbe állítás után a select() függvény segítségével kiválasztjuk, hogy melyik periféria eszközel szeretnénk kommunikálni, ezután minden kommunikáció a kiválasztott perifériával történik, amíg a reset() függvénnyel ismét alaphelyzetbe nem állítjuk az 1 Wire buszt.

myWire.select(const uint8_t rom[8])

A tartalomhoz

skip()

A skip() függvény átugorja a periféria eszközök kiválasztását. Ha csak egy periféria eszközünk van az 1 Wire buszon, elkerülhetjük a keresést, és azonnal hozzáférhetünkba eszközhöz.

myWire.skip();

A tartalomhoz

write()

A write() függvény segítségével adatokat írunk a periféria eszközre. A paraméterként megadott érték byte típusú.

write(byte mydata);

Az alábbi függvényt a parazita áramellátással rendelkező 1 Wire busz perifériáinak írásához használjuk. Egy bájtot ír a perifériára, és áram alatt hagyja az 1 Wire buszt a kommunikáció végéig.

myWire.write(byte mydata, 1);

A tartalomhoz

read()

A read() függvény olvassuk az 1 wire buszról az adatokat. Visszaadja azokat egy byte típusú változóban.

byte myData = myWire.read();

A tartalomhoz

crc8()

Ez a függvény kiszámítja a CRC8 ellenőrző összeget a paraméterként megadott adatokra. A paraméterként megadott adatok egy uint8_t típusú tömbben kell legyenek, és a len paraméter a tömb méretét adja meg.

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

A tartalomhoz

1 Wire példakódok

A következő példában egy DS18B20 hőmérséklet érzékelőt olvasunk Arduino-val az 1 Wire buszon.

#include <OneWire.h>

// az Arduino 10-es láb az 1 Wire busz adatvonala
const char oneWirePin = 10;

// Létrehozunk egy OneWire objektum példányt
OneWire oneWire(oneWirePin);

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

void loop()
{
  // Létrehozunk egy tömböt a Scratchpad-ból olvasott adatok számára
  byte data[12];

  // És kell egy tömb a ROM kiolvasásához is
  byte addr[8];

  // Az 1 Wire busz alaphelyzetbe állítása,
  // a kommunikáció megkezdése elött ez szükséges
  oneWire.reset();
  

  // Érzékelő azonosító olvasása
  // Ez a parancs lehetővé teszi a buszmester számára, 
  // hogy beolvassa a DS18B20 8 bites családkódját,
  // egyedi 48 bites sorozatszámát és 8 bites CRC-jét. 
  // Ez a parancs csak akkor használható, ha egyetlen DS18B20 van a buszon.
  oneWire.write(0x33);
  for (int i = 0; i < 8; i++)
  {
    addr[i] = oneWire.read();
  }

  // CRC ellenőrzése
  if (OneWire::crc8(addr, 7) != addr[7])
  {
    Serial.println("Érzékelő azonosító CRC hiba.");
    return;
  }

  // Az 1 Wire busz alaphelyzetbe állítása
  oneWire.reset();

  // Convert T [0x44] parancs elindítja a hőmérséklet-átalakítást.
  oneWire.write(0x44);
  
  // Ha hozzáadunk egy második paramétert, az jelzi, 
  // hogy az érzékelő eszköz parazita áramellátással van táplálva.
  // oneWire.write(0x44, 1);

  // Várakozás a mérés befejezésére.
  // A mérés időtartama alatt a ds18b20 foglalt, nem fogad parancsokat
  // Az adatlap szerint 12 bit felbontás mellett a mérés maximum 750ms ideig tart.
  delay(1000);

  
  oneWire.reset();

  // Scratchpad olvasása, ezen a memoriaterületen talál található a mért hómérséklet
  oneWire.write(0xBE);
  
  for (int i = 0; i < 9; i++)
  {
    data[i] = oneWire.read();
  }

  // CRC ellenőrzése
  if (OneWire::crc8(data, 8) != data[8])
  {
    Serial.println("Adat CRC hiba.");
    return;
  }

  // Hőmérséklet kiszámítása
  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;
  
  // konvertáljuk olvasható formába
  float celsius = (float)raw / 16.0;
  float fahrenheit = celsius * 1.8 + 32.0;


  // Majd kiírjuk a hőmérsékletet a soros monitorra
  Serial.print("Hőmérséklet: ");
  Serial.print(celsius);
  Serial.println(" °C");
  Serial.print(fahrenheit);
  Serial.println(" °F");
  Serial.println();
  
  delay(1000);
}

A fenti kódot csak egyetlen DS18B20 lekérdezéséhez használhatjuk. Lássuk mi a helyzet, ha több érzékelőt szeretnénk az 1 Wire buszra kötni.

Ahhoz, hogy több DS18B20 érzékelőt használjunk, minden érzékelőnek ismernünk kell az egyedi címét. Ez azért fontos, mert a 1 Wire kommunikáció használatakor az Arduino-nak tudnia kell, melyik érzékelőt kérdezi le. A DS18B20 érzékelőknek egyedi 64 bites címe van, amelyet az érzékelők gyárilag kapnak. Az egyedi címek megszerzéséhez a OneWire könyvtár search() függvényét használjuk a következő példában.

A loop() ciklusban az OneWire könyvtár search(addr) függvényével megkeressük az eszközöket az 1 Wire buszon. A talált eszköz címe a search() függvény paraméterében, az addr tömbben tárolódik.

A search() függvénnyel megkeressük az összes eszközt a buszon és az előző példából ismert módon kinyomtatjuk a hőmérséklet adatokat a soros monitorra.

Ha az összes eszközt megtaláltuk, a reset_search() függvény segítségével újraindítjuk a folyamatot.

#include <OneWire.h>

// az Arduino 10-es láb az 1 Wire busz adatvonala
const char oneWirePin = 10;

// létrehozunk egy OneWire objektum példányt
OneWire oneWire(oneWirePin);

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

void loop() 
{
  // tömb a Scratchpad-ból olvasott adatok számára
  byte data[12];

  // tömb a ROM kiolvasásához
  byte addr[8];
  
  // a search() függvény megkeresi az eszközöket az 1 Wire buszon
  if(!oneWire.search(addr))
  {
    Serial.print("Nincs több eszköz.");
	Serial.println();
	Serial.println();
	
	// ha a search() függvény befejezte a keresést 
	// reset_search() hívásával újraindítjuk a keresést
    oneWire.reset_search();
    delay(1000);
    return;
  }
  
  Serial.print("ROM: ");
  for(int i = 0; i < 8; i++)
  {
    // kinyomtatjuk az aktuális eszköz címét
	Serial.print(addr[i], HEX);
    Serial.print(" ");
  }

  // CRC ellenőrzése
  if (OneWire::crc8(addr, 7) != addr[7])
  {
    Serial.println("Érzékelő azonosító CRC hiba.");
    return;
  }
  
  // Az 1 Wire busz alaphelyzetbe állítása
  oneWire.reset();
  
  // kiválasztjuk az eszközt az 1 Wire buszon
  oneWire.select(addr);
  
  // Convert T [0x44] parancs elindítja a hőmérséklet-átalakítást.
  oneWire.write(0x44);
  
  // Várakozás a mérés befejezésére.
  delay(1000);
  
  oneWire.reset();
  oneWire.select(addr);

  // Scratchpad olvasása
  oneWire.write(0xBE);

  for(int i = 0; i < 9; i++)
  {
    data[i] = oneWire.read();
  }
  
  // CRC ellenőrzése
  if (OneWire::crc8(data, 8) != data[8])
  {
    Serial.println("Adat CRC hiba.");
    return;
  }
  
  // Hőmérséklet kiszámítása
  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;
  
  // konvertáljuk olvasható formába
  float celsius = (float)raw / 16.0;
  float fahrenheit = celsius * 1.8 + 32.0;


  // Majd kiírjuk a hőmérsékletet a soros monitorra
  Serial.print("Hőmérséklet: ");
  Serial.print(celsius);
  Serial.print(" °C, - ");
  Serial.print(fahrenheit);
  Serial.println(" °F");
  
  delay(1000);
}	

A tartalomhoz