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)

Mennyire volt hasznos amit olvastál?

Kattints egy csillagra az értékeléshez!

Sajnálom, hogy amit olvastál nem volt hasznos számodra!

Szeretném ha elégedett lennél!

Írd le kérlek, hogy mi nem tetszett!?