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.
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.
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.
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ő
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>
Az OneWire könyvtár függvényei:
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);
search()
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);
reset_search()
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;
}
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();
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])
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();
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);
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();
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);
Atmega328 Nano V3 fejlesztőkártya
hirdetés
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);
}
Funduino UNO XXL Tanulókészlet
hirdetés
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);
}