Dallas DS18B20 egyvezetékes hőmérő

A Dallas által fejlesztett hőmérő hatalmas előnye, hogy az információ digitálisan áramlik a vezetékeken, ezért a vezeték hossza nem befolyásolja a mért értéket. Ennek természetesen csak akkor van jelentősége, ha a vezeték hosszú. Márpedig a DS18B20 hőmérő chip a gyári adatlap szerint akár 400m távolságból is működhet. Egy vezetékes (onewire) hőmérőnek nevezik, mivel egyetlen adatvezetéken zajlik a kommunikáció, ugyanazon a vezetéken, ahol a tápfeszültséget is kapja a chip. Valójában ez két vezeték, de azért nem nagy hazugság az elnevezés!
Másik nagy előny, hogy ugyanarra a vezetékre sok-sok chip fűzhető fel, én 8db-al próbálkoztam eddig, és működött! Az általam kipróbált legnagyobb vezeték távolság csillagpont topológiával (egy chip, egy vezeték) 25+20+10+5m.

8 chip-et egyszerre csak 20cm vezetékkel próbáltam. Természetesen jól működött. A kísérlet célja az egyes chip-ek hőmérséklet érték szórásainak vizsgálata volt. Úgy 0,5 fok celsius értéken belül voltak az értékek. Ha nem kell preciziós mérés, akkor ez tökéletesen megfelelő.

Technikai adatok:
tápfeszültség: 3,0 – 5,5V
mérési tartomány: -55 – +125 fok celsius
pontosság +/- 0,5 fok celsius
mérési idő: 750ms


Az alábbi ábrákon látható, hogy kell bekötni a chip-et egy arduino-ra:

Több eszközt is használhatunk egyszerre, ezeket párhuzamosan lehet kötni. Azt, hogy egyszerre hányat lehet egyszerre felfűzni, nem sikerült kiderítenem. Jómagam 8 chip-et vásároltam meg, így azt biztosan tudom állítani, hogy 8 chip működik egyszerre normál módban egy vezetéken. Az ábrán látható bekötés három vezetéket használ. Mivel nem kellett nagy távolságra kábeleznem (max 25m), nem volt lényeges a vezeték ára, így 4 eres riasztó kábelt használtam. Működik azonban UTP kábellel is. A vezeték hosszal voltak problémáim, első próbálkozásra 25m kábelre már két chip-et nem tudtam rátenni. Később kiderült, hogy felhúzó ellenállás értékét választottam meg rosszul. Kezdetben 10Kohm akadt a kezembe, azzal működött egy kb. 2m-es vezetékkel. A 25m-es vezetékkel nem ment a 10Kohm-on, de amikor kicseréltem 4,7K-ra, már működött. Jóval később fejtettem meg, hogy miért nem ment egyszerre két chip-el. A felhúzó ellenállást 1,5K-ig csökkentettem, és biztonsággal működött két chip egyszerre. Hosszabb vezetékem nem volt, így a 25m-el van tapasztalatom.

Lehetséges a chipeket úgynevezett parazita módban is használni, amikor is ténylegesen csak 2 vezetékes a rendszer, és az adatvezeték végzi az energia ellátást is. A kommunikációk ideje alatt az adatvezeték természetesen 0 és tápfesz között ugrál, de ezt a chip-ben elhelyezett belső kapacitás elméletileg áthidalja, és amíg a vezetéken 0 a feszültség ellátja energiával. Nem próbáltam ki, mert az én körülményeim között mindegy volt, hogy 3 vagy csak 2 eret kell bekábelezek.

Nézzük meg hogyan is lehet lekérdezni egy chipet:
Mint azt a DHT11-nél is láttuk az 1-wire kommunikáció lényege, hogy valahogyan jelezzük, hogy infót várunk vagy adunk. Itt sincs ez másként. A DATA vezetéket a mikrovezérlő 0-ra húzza le 480 – 960 mikrosec időtartamban (master reset). Ezt követően a chip-ek mindegyike egy úgynevezett „presente” jelenlét jelet ad ki az alábbi ábra szerint:

A probléma csak az, hogy több chip is lehet a vonalon, ezek semmit nem tudnak egymásról, csak szolgai módon lehúzzák a vezetéket 0-ra. Viszont a mikrovezérlő legalább tudja, hogy van ott valaki. Persze ha tudjuk, hogy csak egy eszköz van a vonalon, akkor egyszerűbb a helyzet, de most tegyük fel, hogy többen vannak. Ekkor feltétlenül meg kell tudni, hogy kik vannak a vonalon. Természetesen minden egyes chip-nek van egy gyárilag beégetett egyedi címe, így kizárt hogy két tök azonos chip legyen a vonalon. Már csak meg kell állapítani, hogy mik is ezek címek. Nyilván ha már valahogyan kiderítettük a címet, akkor fogunk tudni beszélgetni az adott chip-el, de most még nem tudjuk.

Így hát a master reset után a mikrovezérlő elkezdi a felderítést. Ehhez a vonal 0-1 megszabott időzítések szerinti „rángatásával” kezd kommunikálni. Sok parancsot adhat, ezek kódját később egy táblázatban megmutatom. Most számunkra a „search ROM” nevű utasítás a fontos, aminek a kódja F0h. Ennek algoritmusát nem fontos pontosan megérteni. A lényeg az, hogy amikor ezt kiadja a mikrovezérlő, minden chip kiteszi a DATA vezetékre a saját címének 0. bitjét, aztán annak invertáltját. A mikrovezérlő ezt követően már tudja, hogy van-e a vezetéken olyan chip aminek 0. bitje 0 vagy 1. Ezt követően a mikrovezérlő közli a chip-ekkel, hogy azzal akar társalogni a továbbiakban, amelyiknek a 0. bitje adott értékű, mondjuk éppen a nullával kezdődő címekkel foglalkozik. Ekkor már csak azok a chip-ek figyelnek, melyek címe nullával kezdődik, és jöhet a cím következő bitje. Ezt a három lépést fogja a mikrovezérlő ismételgetni mindaddig, amíg egyetlen eszközre nem szűkíti a kört. Ha pedig egy eszközt már felderített, kezdi újra a serch ROM parancsot, de már csak a maradék eszközökre „koncentrál” és felderít egy újabb chip-et. A pontos algoritmust nem sikerült megértenem, de talán nem is lényeges, hiszen van kész program könyvtár, ami ezt a bonyolult algoritmust megvalósítja. Az máris látható, hogy ez a felderítési folyamat, nem is olyan gyors. Ha több chip is van a vonalon, akár másodpercekig is eltarthat. Erre érdemes odafigyelni, és ha nem akarunk sok időt vesztegetni a chipek címeinek kiderítésére, akkor a kiderített címeket érdemes tárolni egy „setup” folyamatban és a továbbiakban már csak használni a programban a kiderített adatokat. Akár az Arduino EEPROM-jába be is írhatjuk, hogy kikapcsolást követő újbóli bekapcsoláskor se kelljen ezzel foglalkozni.

Tegyük fel, hogy már tudjuk egy adott chip címét, és azzal az egy szerencsés kiválasztottal akarunk beszélgetni. A folyamat teljesen azonos, kiadunk egy master resetet, megjön a presente jelzés, és már adhatjuk is ki parancsunkat. Ezek a parancsok a következők lehetnek:
Read ROM (ROM olvasás) [33h] : ez az utasítás lehetővé teszi, hogy a mikrovezérlő kiolvassa a DS1820-as 8 bites család-kódját és a  48 bit hosszú egyedi címét és ezen adatokra vonatkozó CRC kódot. Mindezek együtt 64 bit hosszú adatsorozatot eredményeznek a vonalon a parancs kiadását követően. A CRC kód igen fontos, mert a vezetékezés hosszától függően sok zaj lehet a vezetéken, és ez hibás olvasásokat eredményez, ami fel kell fedezni és szükség esetén ismételni kell. Ez az utasítás csak akkor ad „értelmes” eredményt, ha egyetlen chi csatlakozik a buszra!  
Match ROM (ROM kijelölés) [55h] : az utasítás követő 64 bites adatsorozat kijelöli az aktív eszközt, ha több eszköz csatlakozik a buszra. Az az eszköz lesz aktív, amelyiknek a gyárilag beégetett címe megegyezik a kiküldött adatsorozattal. A többi eszköz a következő master reset-ig  inaktív marad.
Skip ROM (ROM „átugrás”) [CCh] : ez az utasítás időt takarít meg, ha egy eszköz csatlakozik a buszra, mert ezt követően rögtön jöhet egy vezérlő utasítás, amiről hamarosan szó lesz.
Search ROM (ROM keresés) [F0h] : ezt az utasítást már ismerjük, nem írnék többet róla
Alarm Search (riasztás keresés) [ECh] : erre az utasításra az eszköz akkor válaszol, ha az utolsó mérésnél a riasztási feltétel igaz. A riasztási feltétel akkor igaz, ha a mért hőmérséklet egy előre beállított értéknél nagyobb (TH regiszter) illetve egy másik értéknél kisebb (TL regiszter). Termosztátok esetén sok kommunikációt lehet ezzel megtakarítani.

A ROM parancsok után a chip-nek vezérlőutasítást kell küldeni. Ezek a vezérlő utasítások a következők lehetnek:
Write Scratchpad („regiszterek írása”) [4Eh] : az utasítást követő két byte az chip TH és TL regiszterének tartalmát írja felül. Ezzel beállíthatók a riasztási hőmérséklet értékek. Sajnos ezek 8 bites regiszterek, ezért csak egész hőmérsékleti értékek adhatók meg riasztási értekként.
Read Scratchpad („regiszterek olvasása”) [BEh] : a regiszterek tartalmának beolvasása. A 18B20-nak 8db regisztere van, ezeket tudjuk kiolvasni. Az alábbi táblázatban láthatók a regiszterek. Túl sok magyarázat azt hiszem nem kell hozzájuk.

A Temperature LSB/MSB regiszterekben van az utolsó mért hőmérséklet, A TH/TL regiszterekben a riszatási értékek. A config regiszterben pedig a hőmérséklet mérés felbontása, ami a hőmérséklet mérés pontosságát is meghatározza. Az alábbi táblázatokban ezt látjuk:

A gyári alapbeállítás a 12 bites felbontás. Minél kisebb a felbontás, annál gyorsabb a mérési idő. A fenti táblázatban látható, hogy a maximális 12 bites felbontás esetén már csaknem 1 másodperc kell a méréshez. Ez idő alatt a chip áramot is fogyaszt, ezért parazita táplálási módban nem szerencsés egyszerre több chipben is elindítani a mérést. Ha biztosak vagyunk abban, hogy a gyári alapbeállítás van érvényben, akkor a programban nem igazán érdemes figyelni ezeknek a biteknek az állapotát. Én a letöltött példa programban  (lásd később) készen kaptam ennek a regiszternek a figyelését, ezért benne hagytam okulásul. A gyári alapbeállítás megtartásával a a mért hőmérsékletet meghatározó (konverziós) program rész jelentősen lerövidíthető, ami a program áttekinthetőségét javítja. A felbontás csökkentésének más haszna szerintem nincs, hacsak nem irányítunk repülőgépet a mikrovezérlőnkkel, és kell a számítási kapacitás valami nagyon fontos más dologra J
Valójában a fenti adatregiszterek kiolvasása 9 byte-os eredményt ad, mivel a chip a 8 adatregiszter tartalmat kiegészíti egy CRC byte-al. Ez nagyon fontos, mert a kiolvasott értékek a vezetéken a zajok és zavarok miatt sérülhetnek.
Copy Scratchpad („regiszterek másolása”) [48h] : a TH és TL regiszterek bemásolása az E2RAM regiszterekbe, így ez az érték a tápfeszültség kikapcsolása után is megmarad.
Convert T („hőmérséklet mérés indítása”) [44h] : ez a parancs indítja el a hőmérséklet mérést. Ha ezalatt olvassuk a buszt 0-t kapunk, ha kész a konverzió, akkor a buszról 1 olvasható. A konverzió ideje függ attól, hogy milyen felbontásban várjuk a hőmérséklet adatot, de maximum 750 ms.
Recall E2 („E2RAM előhívása”) [B8h]: ez az utasítás bemásolja a scratchpad TH és TL regiszterébe az E2RAM-ban eltárolt riasztási hőmérséklet értéket. Ez a művelet a tápfeszültség bekapcsolásakor automatikusan lezajlik, így nemigazán kell foglalkozni ezzel.
Read Power Suply (tápfesz kiolvasás) [B4h] :  ha a chip a az utasítást követően 1-t helyez a buszra akkor külső tápfeszültségről működik, ha 0-t akkor az adatvonalról parazita módban.
Ebből számunkra a legfontosabb (alap esetben mással nem is kell foglalkoznunk) a Read Scratchpad [BEh] parancs, amivel a chip adat regisztereit tudjuk kiolvasni. Ha nem akarjuk használni a riasztás funkciót illetve elegendő a gyárilag alapértelmezetten 12 bites felbontás, akkor semmi másra nincs szükségünk. Ha ezt a parancsot kiadjuk, akkor 9 byte-ot kapunk eredményül.

A Dallas 18B20 chip egyébként -55 és 125 között mér. Felbontása 12bit!

Itt találtam hozzá könyvtárat: http://www.tweaking4all.com/hardware/arduino/arduino-ds18b20-temperature-sensor/

Az első mintaprogramot később írtam meg, mint a később ismertetésre kerülőt. Sokat küzdöttem azzal, hogy egy bonyolult program sok memóriát foglalt el, és ezért időnként lefagyott, mert ha nagyon kevés a rendelkezésre álló változó memória, ez gyakran előfordul. Így elkezdtem kísérletezgetni, hogyan is lehetne egyszerűsíteni, minimalizálni a programot. A sok közül az egyik ilyen terület a Dallas hőmérő kezelése volt. Azt találtam ki, hogy nem fogom minden egyes bekapcsoláskor (setup()-ban) lekérdezni a chip egyedi címét. Egy programmal kiolvasom, felírom magamnak, és “beleégetem” a programba. A chip csere így körülményesebb, de sebaj. Azt is kihasználhattam a programban, hogy négy Dallas chip-et használtam, de mindet más Arduino kimenetre kötöttem, így egy vezetéken csak egy chip volt. Ekkor vettem észre, hogy utóbbi esetben a chip-et nem is kell megcímezni. Így alakult ki az első mintaprogram. Nincs címzés, hiszen erre nincs is szükség egy hőmérő chip esetén. Egy kicsike hibakezelést is sikerült megvalósítani. Megnéztem a onewire könyvtár függvényeit, és kiderült, hogy a függvények legtöbbjének nincs is visszaadott értéke. Meglepő módon a reset függvény az egyetlen (a read()-en és search()-on kívül) ami visszaad valamit. 0-át ha semmi nincs a vonalon, és 1-et ha van ott Dallas hőmérő. Ezt használtam fel arra, hogy észre vegyem, ha a hőmérő chip adatvezetékét széthúzom. Így a program menet közben felismeri, ha kihúzom, bedugom a chip-et. Észre veszi még a crc hibát (ha sérül az adat a hosszú vezetékeken. Sajnos a hőmérő chip kihúzásakor keletkezik egy fals mérési eredmény, de csak egyetlenegy, aztán jelzi, hogy eltűnt a chip, nincs kivel kommunikálni.
Ennek a programnak van még egy fontos tulajdonsága. Nem használ delay() függvényt, így nem lassítja egy nagyobb program részeként a többi funkció végrehajtását. A program 2 másodpercenként foglalkozik a hőmérővel. Felváltva vagy mérést indít el, vagy kiolvassa az előző mérési eredményt. Ha pl. csak óránként akarnánk mérni, akkor kicsit át kellene alakítani, mert a mérést fél órával a kiolvasás előtt csinálná, ami nem mindig szerencsés.

A forrás:

#include <OneWire.h>          //Dallas DS18b20 hőmérő kezeléshez one-wire busz
OneWire  ds(A0);              // a A0. kivezetéshez kell kapcsolni a chip data kivezetését 
byte ds_data[9];              //kiolvasott adtok tárolására
float homerseklet;            //ebbe a változóba kerül a kiolvasott hőmérséklet
bool meres=true;              //azért, hogy ne kelljen delay()-t használni, az egyik loop() ciklusban utasítjuk a dallas chip-et
                              //hogy mérjen, mig a következőben kiolvassuk a mért értéket. Ez jelzi, amikor csak mérni kell
long ido_tmp=millis();        //segéd változó, ahooz, hogy csak megadott idő után foglakozzon a loop() a dallas hőmérővel 

void setup()
{
  Serial.begin(9600);
  Serial.println("Indul:");
}

void loop() {
  if(millis()>ido_tmp+2000) { //két másodperc után foglalkozunk a hőmérséklet méréss indítással, vagy az eredmény kiolvasással
    if (meres) {              //ha meres=1 akkor arra utasítjuk majd a chip-et, hogy végezzen egy mérést, ha 0 akkor kiolvasunk (else ág)
      if (ds.reset()==1) {    //itt 1-el jelez, ha a reset jelre válaszol egy DS18b20, tehát van a vonalon chip. Ha nincs a vonalon DS18b20, akkor ez 0.
         ds.skip();           //ezzel azt mondjuk a chip-nek, hogy egyedül van a vonalon, nem adunk meg eszköz címet, mindenképpen csinálja amira utasítást kap
         ds.write(0x44,1);    //elinditunk egy mérést a chip-ben
         meres=false;         //ezzel jelezzük, hogy elindítottunk egy mérést, 750msec múlva ki lehet olvasni a mért hőmérsékletet
       }
       else {Serial.println("Nincs DS18b20 a vonalon!");}  //meres változót nem állítjuk false-ra, így várunk amíg lesz chip a vonalon
    }
    else {                    //mivel volt eszköz a vonalon és az előző ciklusban mért is egyet, ki fogjuk olvasni az eredményt
      ds.reset();             //éredekes, de itt már mindenképpen 1 volt a ds.reset() válasza, ha nem volt eszköz a onalon, akkor is,
                              //így itt már nem is vizsgálom. Viszont mivel itt nem vizsgálom, egy fals mérés lehetséges. 
                              //Ennek eredménye 0.00fok, amiről nem lehet megállapítani, hogy nem valós mért érték.
      ds.skip();              //ezzel azt mondjuk a chip-nek, hogy egyedül van a vonalon, nem adunk meg eszköz címet, mindenképpen csinálja amira utasítást kap
      ds.write(0xBE);         // Chip memóriájánbak olvasása következik
      for ( byte i=0;i<9;i++) {ds_data[i]=ds.read();}    // 9 bytot olvasunk ki
      //ds_data[3]=0;         //ez egy mesterséges crc hiba előállítás, hogy meglássuk működik-e a crc vizsgálat. Helyes működéshez ki kell kommentezni ezt a sort
      if ( OneWire::crc8(ds_data,8)!=ds_data[8]) {Serial.println("CRC hiba a kiolvasott adatokban!");}   
      else {
        homerseklet=(float) (((ds_data[1]<<8)|ds_data[0])/16.0);          //mert ertek visszaadása 
        Serial.print("Hőmérséklet:");Serial.println(homerseklet); 
        meres=true;                                                       //ezzel jelezzük, hogy következő ciklusban mérést kell indítani
      }
    }
    ido_tmp=millis();        //pillanatnyi idő tárolása 
  }
}
 

A következő program egy vonalra kötött több érzékelővel zajló kommunikációt valósít meg. Nem én írtam, sajnos már nem emlékszem honnan sikerült letölteni. Abban különbözik az előzőtől, hogy kideríti a chip címét, és a hőmérséklet mérés indításakor és az adatok kiolvasásakor használja is ezt a címet. Ebben is van hibakezelés, de csak a cím felderítéskor ellenőriz, igaz, minden mérés előtt lekérdezi a címet, ami felesleges, ha csak a setup() ban olvassuk ki és tároljuk valahol. Azonban ekkor elveszítjük annak lehetőségét, hogy biztosan tudjuk, hogy ott a chip és működik. Ugyanis csak a reset() függvény ad vissza jelenlét adatot (a search()-on kívül), de az csak azt mondja meg, hogy legalább egy chip van a vonalon.

#include <OneWire.h>
OneWire  ds(A0);            // az A0 kivezetéshez kell kapcsolni a DS18B20 data kivezetést

void setup(void) 
{
  Serial.begin(9600);
  Serial.println("Indul...");
}

void loop() 
{
  byte i;
  byte adat[9];
  byte ds_cim[8];
  float homerseklet;

  if ( !ds.search(ds_cim)) {                           //keresünk egy érzékelőt, ha megtaláljuk, akkor az ds_cim tömbbe kerül az elérési címe
    Serial.println();Serial.println();
    Serial.println("Nincs több érzekelő a vezetéken!");
    Serial.println("Újraindul a keresés!");
    ds.reset_search();                                 //alaphelyzetbe állítjuk a chip keresést
    delay(250);
    return;                                            //újraindul a loop()
  }

   if (OneWire::crc8(ds_cim,7)!=ds_cim[7]) {           //ellenőrizzük az érzékelő címének érvényességét
      Serial.println(" CRC hiba a kiderített címben!");
      return;
  }

  //kiírjuk az érzékelő címét
  Serial.println();
  Serial.print("DS cím:");
  for( i = 0; i < 8; i++) {
    Serial.print(" ");
    Serial.print(ds_cim[i], HEX); 
  }
  //elősször mérünk egyet a címmel kiválasztott chip-en
  ds.reset();                                       // alaphelyzetbe állítjuk a kommunikációt
  ds.select(ds_cim);                                //kiválasztjuk a megadott címmel rendelkező érzékelőt
  ds.write(0x44, 1);                                // Elindítjuk a konverziót
  delay(1000);                                      // legalább 750ms-ot kell várni a mérésre
  //ki fogjuk olvasni a mérési adatokat
  ds.reset();                                       //alaphelyzetbe állítjuk a kommunikációt
  ds.select(ds_cim);                                //kiválasztjuk a megadott címmel rendelkező érzékelőt
  ds.write(0xBE);                                   // elindítjuk a mért értékek kiolvasását (9 byte-ban, amiből az utolsó a CRC)

  Serial.println();
  Serial.print(" Adatok:");
  for ( i = 0; i < 9; i++) {                        //9 byte-ot fogunk kiolvasni 
    adat[i] = ds.read();                            //egy byte kiolvasása
    Serial.print(adat[i], HEX);
    Serial.print(" ");
  }
  //ellenőrizzuk a crc-t és ha jó, kiszámítjuk a hőmérsékletet és kiírjuk
  if ( OneWire::crc8(adat,8)!=adat[8]) 
    {Serial.println("CRC hiba a kiolvasott adatokban!");} 
  else {
    // Aktuális hőmérséklet kiszámítása következik a kiolvasott adatokból, a felbontást is figyeli,
    // bár azt nem állítottuk be, így az a gyári alapértelmezett 12 bit.
    // Az első két byte kell az adatregiszterből a hőmérséklethez
    int16_t raw = (adat[1] << 8) | adat[0];         //összemásoljuk a két byte-ot egy int-be
    byte cfg=(adat[4] & 0x60);                      //a konfigurációs regiszter két felső bitje fogja megmondani a felbontást
    if (cfg==0x00) raw=raw & ~7;                    // 9 bit felbontás (93.75 ms mérési idő kell)
    else if (cfg==0x20) raw=raw & ~3;               // 10 bit felbontás (187.5 ms mérési idő kell)
    else if (cfg==0x40) raw=raw & ~1;               // 11 bit felbontás (75 ms mérési idő kell)
    // default felbontás 12 bit (750 ms mérési idő kell)
    homerseklet=(float)raw / 16.0;
    Serial.println();
    Serial.print(" Homerseklet:");
    Serial.print(homerseklet);
    Serial.print(" C fok");
  }
  delay(2000);
}

Ennek a programnak nagy hibája, hogy delay()-t használ a mérés utáni várakozásra. Ha pl. egy másodpercet is kijelző óra időpontja mögé szeretnénk kiírni a hőmérsékletet, akkor azt tapasztalnánk, hogy a másodperc nem frissül másodpercenként. Miért? Mert egy másodperc a delay() alatt elmegy a semmire. Ilyen esetben jobb megoldás az előző program.
Viszont jó is van ebben a programban: bemutatja, hogyan lehet a felbontást kezelni. Előny még az is, hogy atom biztosan jelzi, ha egy chip eltűnik a vonalról, működés közben nyugodtan lehet ki be dugogatni. Már ha ez fontos valakinek. Pl. egy adatgyűjtőben hasznos lehet, ha pont annyi chip-et mér és jelez ki a program, amennyit rádugtam, és mérések közben is lehet bővíteni a szenzorok számát stb.

Gyorsan megírtam egy olyan változatát a fenti programak, ami nem használ delay() függvényt. Kicsit bonyolult a loop(), mert felváltva mérek vagy olvasok, és váltogatni kell a felderített chip-ek közül, amelyikkel éppen foglalkozok. Ha növelem a chip-ek számát, akkor egyre ritkábban mér egy chi-et.

#include <OneWire.h>          //Dallas DS18b20 hőmérő kezeléshez one-wire busz
OneWire  ds(A0);              // a A0. kivezetéshez kell kapcsolni a chip-ek data kivezetéseit 
byte ds_cim[8];               //ebbe a tömbbe fogja a dallas search() függvénye beolvasni a megtalált chip címét
byte ds_cim_t[5][8];          //ebben a tömbben tároljuk a chip-ek címeit. Azért másolom egy külön tömbbe a címeket
                              //hogy ne kelljen a loop-ban minden mérés előtt felderíteni a chip címet. Így csak
                              //a setup részben csinálok cím felderítést egyszer. Max öt chip lehetséges, de lehet több is.
byte ds_data[9];              //kiolvasott adtok ebbe a tömbba töltődnek kiolvasáskor
float homerseklet;            //ebbe a változóba kerül a kiolvasott hőmérséklet
bool meres=true;              //azért, hogy ne kelljen delay()-t használni, az egyik loop() ciklusban utasítjuk a dallas chip-et
                              //hogy mérjen, mig a következőben kiolvassuk a mért értéket. Ez jelzi, amikor csak mérni kell
long ido_tmp=millis();        //segéd változó, ahooz, hogy csak megadott idő után foglakozzon a loop() a dallas hőmérővel 
byte ds_darab=0;              //segédváltozó, ebben tároljuk a megtalált Dallas chip-ek darabszámát (tömbindexét)
byte ds_tmp_mer=0;            //segédváltozó, ami megmondja melyik chip-et mérjük a federítettek közül
byte ds_tmp_olvas=0;          //segédváltozó, ami megmondja melyik chip-et olvassuk a federítettek közül
void setup()
{
  Serial.begin(9600);
  Serial.println("Indul:");

  while(ds.search(ds_cim))                             //keresünk egy chip-et a vonalon
  {
     Serial.print("Találtunk egy chip-et! Címe:");
     for (byte i=0;i<8;i++) {                          //elmásoljuk a kiderített címet egy másik tömbbe későbbi felhasználásra
       ds_cim_t[ds_darab][i]=ds_cim[i];
       Serial.print(ds_cim[i], HEX);                   //cím byte-ok kiírása 
       Serial.print(",");                              
     }
     if (OneWire::crc8(ds_cim,7)==ds_cim[7])          //ellenőrizzük az érzékelő címének érvényességét
       {Serial.print(" CRC OK!");}                    //cím rendben
     else {Serial.print(" CRC hiba!");}               //crc hiba van a címben
     ds_darab++;
     Serial.println("");                              //új sor a következő chip adataihoz
  }  
}

void loop() {
  if(millis()>ido_tmp+2000) {                         //két másodperc után foglalkozunk a hőmérséklet méréss indítással, vagy az eredmény kiolvasással
    if (meres) {                                      //ha meres=1 akkor arra utasítjuk majd a chip-et, hogy végezzen egy mérést, ha 0 akkor kiolvasunk (else ág)
      for (byte i=0;i<8;i++) {                          //visszamásoljuk a cim tömbből az egyik chip címét a ds_cim tömbe
        ds_cim[i]=ds_cim_t[ds_tmp_mer][i];
      } 
      if (ds.reset()==1) {                            //itt 1-el jelez, ha a reset jelre válaszol legalább egy DS18b20, tehát van a vonalon chip. Ha nincs a vonalon DS18b20, akkor ez 0.
        ds.select(ds_cim);                            //ezzel kiválasztjuk a cím alapján a chip-et
        ds.write(0x44,1);                             //elinditunk egy mérést a chip-ben
        ds_tmp_mer++;                                 //következő ciklusban a következő chip-en indítunk mérést
        if (ds_tmp_mer>ds_darab-1) {ds_tmp_mer=0;}
        meres=false;                                  //ezzel jelezzük, hogy elindítottunk egy mérést, 750msec múlva ki lehet olvasni a mért hőmérsékletet
      }
      else {                                          //nincs egyetlen chip sem a vonalon, meres változó marad false, így nem fogjuk kiolvasni a chip-et akövetkező cklusban
        Serial.println("Nincs egy DS18b20 sem a vonalon!");}  
    }
    else {                                            //mivel volt eszköz a vonalon és az előző ciklusban mért is egyet, ki fogjuk olvasni az eredményt
      for (byte i=0;i<8;i++) {                        //visszamásoljuk a cim tömbből az egyik chip címét a ds_cim tömbe
        ds_cim[i]=ds_cim_t[ds_tmp_olvas][i];
      } 
      ds.reset();                                     //éredekes, de itt már mindenképpen 1 volt a ds.reset() válasza, ha nem volt eszköz a onalon, akkor is,
                                                      //így itt már nem is vizsgálom.  
      ds.select(ds_cim);                              //ezzel kiválasztjuk a cím alapján a chip-et
      ds.write(0xBE);                                 // Chip memóriájánbak olvasása következik
      for ( byte i=0;i<9;i++) {ds_data[i]=ds.read();} // 9 bytot olvasunk ki
      if ( OneWire::crc8(ds_data,8)!=ds_data[8]) 
        {Serial.println("CRC hiba a kiolvasott adatokban!");}  //crc hibát jelez, ha kivesszük a chip-et, itt lehet észrevenni 
                                                               //ha valamilyen hardver hiba van
      else {
        homerseklet=(float) (((ds_data[1]<<8)|ds_data[0])/16.0);  //mért ertek visszaadása 
        Serial.print(ds_tmp_olvas);
        Serial.print(". chip hőmérséklet:");
        Serial.println(homerseklet); 
        meres=true;                                   //ezzel jelezzük, hogy következő ciklusban mérést kell indítani
      }
      ds_tmp_olvas++;                                 //következő ciklusban a következő chip-en indítunk mérést
      if (ds_tmp_olvas>ds_darab-1) {ds_tmp_olvas=0;}
    }
    ido_tmp=millis();        //pillanatnyi idő tárolása 
  }
}
 

Hibás CRC üzenetet soha nem kaptam sem ezzel a programmal sem az előzővel. De gondolom ez vezeték hossz kérdése, és a 15m kábel semmiség a katalógusban olvasott maximális 400m-hez képest. (későbbi tapasztalatom, hogy 25m kábelen már néha előfordul a CRC hiba, 1.5K felhúzó ellenállást használtam, ennek további csökkentése nem hozott eredményt)