Tartalom:
- A DS18B20 szenzor műszaki adatai
- Összekötése az Arduino-val, három vezetékes külső táplálású működési mód, és kétvezetékes „parazita” működési mód
- A chip regiszterei, és alapvető parancsai az adok lekérdezéséhez
- Több chip párhuzamos használata egy vezetékre felfűzve, egyedi cím lekérdezésének működése
- Programkönyvtár letöltése és használata
- Példa program egyetlen chip használata esetén
- Példa program több chip egy vezetéken történő párhuzamos használata esetén
————————————————————————–
A hőmérsékelt mérésére szolgáló szenzorok általában ellenállás mérésre vezethetők vissza, és a közvetlen jel feszültségszint. Ha ezt a feszültséget nagy távolságra kell elvezetni, a kábel hosszúsága, és hőmérséklete valamint az elektronikus zavarok befolyásolják a mérés pontosságát. Nem meglepő hát, hogy jellemzően intelligens szenzorokkal találkozunk, melyek egy picika tokozásban mérnek és rögtön digitálisan jelet is továbbítanak. A digitális jeltovábbítás nagy előnye, hogy nem befolyásolja a vezeték hossza a mérést. A digitális jeltovábbításhoz, kommunikációhoz viszont általában 3-4 vezeték is szükséges. Ennek természetesen csak akkor van jelentősége, ha a vezeték hosszú. Nem véletlen tehát, hogy készültek olyan szenzorok, amik minimalizálják a szükséges vezeték ereinek számát. Ilyen szenzor a DS18B20 hőmérő chip, ami 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, míg 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ó, ahhoz, hogy csak megadott idő után foglalkozzon 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és 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(); //érdekes, de itt már mindenképpen 1 volt a ds.reset() válasza, ha nem volt eszköz a vonalon, 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ának 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 értek 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ömbbe 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, míg 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ó, ahhoz, hogy csak megadott idő után foglalkozzon 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 felderítettek közül byte ds_tmp_olvas=0; //segédváltozó, ami megmondja melyik chip-et olvassuk a felderí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és 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ömbbe 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); //elindítunk 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ömbbe ds_cim[i]=ds_cim_t[ds_tmp_olvas][i]; } ds.reset(); //érdekes, 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ának 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 észre venni //ha valamilyen hardver hiba van else { homerseklet=(float) (((ds_data[1]<<8)|ds_data[0])/16.0); //mért értek 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 } }
Próbapanelon pár centiméteres vezetékekkel 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.
Sokat kínlódtam egy 25 m-es vezeték esetén. Kezdetben tévedésből 10Kohm ellenállást szereltem felhúzónak. Nem is működött 25m-es vezetékkel, csak maximum 15 m-ig. Akkoriban még egy vezetékre több chip-et is szereltem. Aztán kicseréltem 4,7K-ra a felhúzó ellenállást, mert erre az értékre emlékeztem minimum értékként. Így már működött a 25m-es kábelen a chip, de csak akkor, ha egyedül volt a vonalon. Később észrevettem, hogy akár 1Kohm-ig le lehet menni a felhúzó ellenállás értékével. Így már mind a négy chip működött egyszerre, bár ekkor már inkább csak egyet szereltem egy vezetékre, mert egyszerűbb lett a program, kivezetés meg volt bőven. Aztán beindultak a 25m-es vezetékre szerelt chip-nél a CRC hibák. Közel fél év telt el, mire rávettem magam, hogy felhúzzam a kábelt az aknából. Addig szoftver hibára gyanakodtam, és napokat töltöttem a szoftver átírásával. Végül kiderült, hogy nem tökéletesen öntöttem ki a a chip-et és a kábelvéget, és az aknában a nedvesség elkorrodálta a vezetéket. Itt egy fotó a kiöntött régi szenzorról:
Csúnyán néz ki, pedig a kiöntött vezetékvéget felfelé rögzítettem, hogy lecsöpögjön róla a pára okozta víz. A korróziótól a chip hol működött, hol nem. Előfordult CRC hiba, volt, hogy nem is látta a program a chip-et. Az adatvezeték korrodált el annyira, hogy 10cm levágása után is csak egy barna cérnaszálnak látszott a rézvezeték a szigetelés alatt. Szerencsére volt másik szabad ér a vezetéken, így újra a chip cseréje után minden tökéletesen működik. Az a korrózió két év alatt keletkezett! Addig ment minden rendben 4,7K illetve 1K felhúzó ellenállással!