Arduino UNO
Table of Contents

Áttekintés

Szükséges előképzettség

Az Arduino fejlesztés elsajátításához szükségünk van némi elektronikai alapismeretekre, legalább azon a szinten, hogy mi micsoda. Valamint programozási alapismeretek is szükségesek a jobb, gyorsabb megértés érdekben, ideális esetben C++ nyelven.

Szükséges eszközök

A lenti projektek elkészítéséhez a következőket kell megvenni:

  • Arduino kezdőkészlet (keresőkulcs: Arduino Starter Kit). Egy jól felszerelt készlet ára kb. 20$. Fontos, hogy tartalmazza az alábbiakat:
    • Arduino UNO lapka (lehet igazából másfajta is, viszont bizonyos példák más esetekben másképp működhetnek).
    • USB kábel.
    • Kábelek (keresőkulcs: Arduino jumper): valójában szükség van apa-apa, apa-anya és anya-anya kombinációra is. (A tapasztalatom szerint érdemes plusz kábeleket is venni, mivel az alapkészlet hiányos lehet. A Geekcreit-nek van olyan csomagja, mely tartalmaz mindhárom típusból 40 darabot; azt javaslom pluszban megvásárlásra, melynek ára kb. 4$.)
    • Próbapanel (breadboard).
    • Alap elektronikai eszközök: LED-ek, ellenállások.
    • Változtatható ellenállás (potentiometer).
    • Nyomógombok.
    • Kijelzők: 7 szegmenses, 4 karakteres, 8x8-as mátrix.
    • LCD kijelző. (Előfordulhat, hogy egy olcsóbb készletben ez nincs benne, ez esetben külön meg kell rendelni. Kereső kifejezés: LED 1602 with I2C. Fontos, hogy legyen rajta I2C adapter.)
    • Távirányító + infravörös érzékelő.
    • 74HC595 számú IC.
    • Berregő (buzzer).
    • Értékelők: fotoellenállás (photoresistor), hőmérséklet érzékelő, láng érzékelő, … (Az érzékelőkről később lesz részletesen szó. Még a legegyszerűbb kezdőcsomagban is van pár alapérzékelő, mellyel ki lehet próbálni, működik-e. Programozás szempontjából az érzékelők többsége hasonlóan működik.)
    • Motorok: szervó motor, léptető motor.
  • Érzékelő csomag: a leggazdaságosabb csomag keresőkifejezése ez: 37 in 1 sensor kit for Arduino. Ahogy a neve is sugallja, ez 37 eszközt, nagyrészt érzékelőt tartalmaz, és ár/érték arányban igen gazdaságos, mindössze 10$ körüli az ára. (Egy eszköz átlagára postaköltséggel kevesebb mint 100 Ft; összehasonlításul a hasonló LEGO érzékelők ára több tízezer forint darabja.) Bizonyos hasznos érzékelők még ebben sincsenek benne, azokat külön tudjuk megvásárolni; az adott érzékelőknél jelezve vannak, hogy ez része-e az érzékelő csomagnak vagy sem. Az érzékelő csomag 37 eleméből valójában 10 kimenet, így az érzékelők száma 27. A tartalma az alábbi (amiből az is látható, hogy nagyjából fele jelent valódi értéket, de szerintem még így is bőven megéri).
    • Többszínű LED-ek: van 3 darab RGB (ebből egy 7 színű villogó) és 2 darab kétszínű LED. (Egy érdekes, a többi felesleges.)
    • Lézer: vezérlése a LED-éhez hasonló. Érdekes, de oktatási szempontból nincs neki jelentősége.
    • Berregők: van egy passzív és egy aktív berregő. Az aktív berregő inkább csak idegesítő, a passzív berregő viszont többnyire része az alapkészletnek, és ennek különösebb hozzáadott értéke nincs.
    • Relé: előfordulhat, hogy az alapkészlet nem tartalmaz, ugyanakkor ez egy fontos elem.
    • Nyomógomb: az alapkészletek mindig része, így itt felesleges.
    • Joystick: ha az alapkészletnek nem része, akkor hasznos.
    • Elhajlás érzékelő: kb. úgy működik mint a potenciométer, és mivel az tipikusan része az alapkészletnek, itt ennek túl nagy jelentősége nincs (egyébként van belőle 3).
    • Fotoellenállás: hasznos, de az alapcsomagnak ugyancsak tipikusan része.
    • Hőmérséklet érzékelők: 4 ilyen van, melyek közül egy is elég lenne, mégpedig a DHT11 jelű hőmérséklet- és páratartalom érzékelő.
    • Mágnes érzékelők: 5 darab van a csomagban, melyből 3 Hall és 2 reed érzékelő; más felosztásban két analóg (melynél számít a polaritás is) és három digitális (vagy van mágnes vagy nincs). Mondjuk egy digitális reed szenzor és egy analóg Hall szenzor elég lenne, a többi felesleges.
    • Hang érzékelők: van kettő belőle; igazából egy is elég lenne.
    • Kopogás érzékelő: érdekes, kicsit nehézkesen működik, de egynek elmegy.
    • Rázkódás érzékelő: szintén.
    • Érintés érzékelő: ez is.
    • Akadály érzékelő: ez is.
    • Fény akadály érzékelő: ez is.
    • Szívritmus érzékelő: érdekes, bár elsőre többet gondolnánk bele, mint amennyit ténylegesen tud. Ugyanakkor egy kiválóan felépített anyag áll rendelkezésre, nagyon hazsnos tudnivalókkal.
    • Nyomkövető érzékelő: érdekes, bár egynek önmagában nincs túl nagy jelentősége.
    • Lángérzékelő: érdekes, bár az alapkészleteknek is része lehet.
    • IR kapcsolat: ez két érzékelőként szerepel a felsorolásban, melyek közül az egyik az adó (tehát nem is érzékelő), a másik a vevő. Egyébként nagyon hazsnos páros.

Amiket érdemes megvenni:

  • Külső áramforrás: nem kötelező tartozék, de ajánlott, hogy azzal is ki tudjuk próbálni. Az alapkészlet is tipikusan szokott tartalmazni olyat, melyre vagy egy 9V-os elemet tudunk kapcsolni, vagy 4 darab 1,5 Voltos ceruzaelemet, viszont ha van rá módunk, keressünk a háztartásban (jó eséllyel van), vagy vásároljunk megfelelő áram átalakítót. Kereső kifejezés: power supply. Ügyeljünk arra, hogy Magyarországon használható dugasz legyen rajta, és a leadott egyenfeszültség 7-12 Volt között legyen. Vehetünk változtatható ellenállású átalakítót is. Az ára 5$ körül kezdődik.
  • Bővítőlap (keresőkifejezés: shield for Arduino): ennek segítségével kényelmesebben elférünk, kapunk sok extra 5V és GND kimenetet. Az ára 2,5-3$ körüli.
  • Bővített áramellátó lap (MB102): nem kötelező, de számos esetben hasznos lehet. Az Arduino-n maximálva van az, hogy mennyi áramot tud leadni összesen, és előfordulhat, hogy ha túl sok eszközt szeretnénk rácsatlakoztatni, akkor a nagyobb fogyasztóknak külön áramellátást biztosítunk. Az is előfordult már, hogy egyszerűen szükség volt pár tűre, mely földet, 3,3 Voltot és 5 Voltot biztosított, és ez jól jött. Az ára 1$ körüli.
  • TM1638 bővített 7 szegmenses kijelző nyomógombok és LED-ek: jelentősen megkönnyíti a bevitelt és kimenetelt a hagyományos eszközökhöz képest.
  • ESP8266-01, lehetőleg programozóval. Önmagában a wifi-n keresztül történő internet kapcsolódáshoz szükséges, a programozó viszont új világot nyit számunkra.

Ez idáig olyan 40$.

Ami jó, ha van, de nem szükséges:

  • Forrasztópáka: alapból nem kell forrasztani, viszont jó ha van, mert a gyakorlatban már több esetben előfordult.
  • Extra tűkészlet (pin header strip): bizonyos eszközök úgy érkeznek, hogy nem tudjuk rákötni a kábelt, mert csak a lyukak vannak kifúrva, emiatt hasznos, ha van egy ilyen tűkészlet. Ára bő 1$ egy olyan készletnek, amely hosszú ideig megteszi.
  • Elektromos multiméter (electronic multimeter): legalább feszültséget, áramerősséget, ellenállást tudunk mérni a segítségével. Hibakereséshez jól jöhet.
  • Oszcilloszkóp: ez már nagyon "nice to have" téma, de ha valaki el szeretne mélyedni hosszabb távon is a témában, annak hasznos lehet. Nemrégiben megjelent a piacon egy egycsatornás digitális egyszerű oszcilloszkóp, melynek ára összeszerelve kb. 15$; azt szerintem érdemes megvenni. (És ha már oszcilloszkóp: jelgenerátor, hogy teljes legyen a kép, de ennek már nincs közvetlen köze az Arduino fejlesztéshez.)
  • USB-soros: ezt az adaptert az USB-be kell dugni, és lehetővé teszi, hogy a 6 USB lábat (GND, 3V3, 5V, TX, RX, RST) elérjük vele. A CH340 típusú USB to TTL UART átalakító egy dollár alatti összegért beszerezhető.

Arduino-s helló világ: villogó LED

Az Arduino fejlesztéshez szükségünk van fejlesztő környezetre. Ezt alapvetően kétféle módon tudjuk használni:

Ebben a leírásban az offline szerkesztőt fogjuk használni.

A fejlesztői felületen készítsük el ezt a kódot:

int led = 13;
 
void setup() {
  pinMode(led, OUTPUT);
}
 
void loop() {
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
  delay(1000);
}

Mentsük el. Az Arduino programok kiterjesztése .ino, és a fájlnévnek meg kell egyeznie a könyvtárnévvel. Válasszunk egy alkalmas könyvtárat, a forrás neve pedig legyen mondjuk blink.ino, ami automatikusan a blink könyvtárba kerül.

A kód magyarázata:

  • Minden Arduino kódnak tartalmaznia kell a paraméter és visszatérési érték nélküli setup() és loop() függvényt. Az előbbi egyszer fut le induláskor, az utóbbi folyamatosan.
  • Az Arduino UNO lapkán található többek között számos láb (angolul pin), ami lehet kimeneti és bemeneti egyaránt. Az Arduino kód legvégső soron nem csinál mást, csak ezeket olvassa, ill. írja. A 13-as láb mellett található egy kis LED; az valójában össze van belül kötve a 13-as lábbal.
  • Az Arduino kód valójában (valamelyest egyszerűsített) C++. Implicit módon tartalmazza az Arduino.h beolvasását, így bizonyos függvények, konstansok alapból elérhetőek. Létrehozhatunk benne globális és lokális változókat, függvényeket. Itt egy globális változó a led, ami azt mondja meg, hogy mely lábat szeretnénk használni.
  • A pinMode() függvénnyel azt adjuk meg, hogy az adott lábat kimenőként vagy bemenőként szeretnénk-e használni. Mivel információt írunk ki rá, és nem olvasunk be, ezért most kimenőként használjuk.
  • A digitalWrite() függvény digitálisan kiír adott értéket adott lábra. A digitális érték HIGH és LOW lehet, ami 5 Voltot vagy 0 Voltot ír az adott portra.
  • A delay() függvény segítségével várunk; a paraméterként megadott időt ezredmásodpercben kell értelmezni.

A kód tehát a következő: beállítjuk a 13-as lábat kimenetként, majd végtelen ciklusban magasra állítjuk, várunk egy másodpercet, alacsonyra állítjuk, várunk egy másodpercet. Tehát lassan villogni fog a beépített LED:

Kapcsoljuk USB kábellel az Arduino UNO-t a számítógéphez. Ellenőrizzük az alábbiakat az Arduino fejlesztő felületen:

  • Tools -> Board: itt az Arduino/Genuino Uno kell, hogy kiválasztva legyen; ha nem ez van kiválasztva, akkor válasszuk ki.
  • Tools -> Port: itt az a port kell, hogy kiválasztva legyen, mely mellett zárójelben ott van az, hogy Arduino/Genuino Uno.

Ebben az egyszerű esetben nincs szükség áramkör kialakítására. Most fordítsuk le és töltsük fel a programot. Ezt a következőképpen tudjuk megtenni: Sketch -> Upload; Ctrl + U billentyűkombináció; a menü alatti ikonok közül a jobbra mutató nyíl (balról a második). Ha mindent jól csináltunk, a LED villogni fog.

Ahhoz, hogy lássuk: a számítógép ekkor már csak az áramot adja, a logika magán az Arduino eszközön fut, érdemes kipróbálni a következőt: húzzuk ki az USB kábelt, és csatlakoztassunk rá a jobb oldalán levő dugaszra 7-12 Volt közötti egyenáramot (keresőkulcs: power supply).

Az Arduino UNO

A lapka leírása itt található: https://www.arduino.cc/en/reference/board. Az eszköz egy ATMega328P típusú mikrovezérlő, melynek részletes specifikációja itt található: http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf. A legfontosabbak:

  • Memória (a memóriáról bővebben: https://www.arduino.cc/en/tutorial/memory)
    • Flash memória: 32 kB; ebben tárolódik a lefordított kód
    • SRAM (static random access memory): 2 kB, ez a tulajdonképpeni használható memória; pl. a változók értékei ide kerülnek.
    • EEPROM: 1 kB; az itt tárolt adat újraindítás után is megmarad.
  • Lábak
    • Digitális: 14 digitális láb található, egymás mellett, 0-tól 13-ig jelölve. Mindegyik használható digitális írásra és olvasásra egyaránt, viszont néhány lábat speciális célra is használhatunk:
      • TX-RX soros adatkapcsolat: a 0 (RX) és az 1 (TX) lábak használhatóak erre a célra. Ha egy TX-RX kommunikációra képes eszközt rákapcsolunk ezekre a lábakra úgy, hogy a TX-et a TX-szel, az RX-et az RX-szel kapcsoljuk össze, akkor a számítógépről közvetlenül, Serial monitoron keresztül tudunk azzal az eszközzel kommunikálni. Egyébként ha így szeretnénk megvalósítani a kommunikációt az Arduino és az a másik eszközt között, akkor a TX-et az RX-szel és fordítva kell összekötni, és ez esetben gondoskodni kell külső áramforrásról, a számítógép USB-je nem megfelelő.
      • Digitális PWM: ezzel analóg jelet tudunk szimulálni. Pl. 70%-ot úgy, hogy az idő 30%-ában van LOW és 70%-ában HIGH szinten, gyorsan váltakozva. Az analóg író-olvasó függvényeket használhatjuk erre a célra. Az erre alkalmas lábak ~-mal vannak jelölve: 3, 5, 6, 9, 10, 11. Érdemes kipróbálni: küldjük egy fix analóg értéket egy adott lábra, majd oszcilloszkóppal ellenőrizhetjük.
      • Külső megszakításra alkalmas lábak: 2, 3. Részletek: https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/.
      • SPI: Serial Peripheral Interface, azaz soros külső interfész. Érintett PIN-ek: tetszőleges digitális (tipikusan 10), valamint 11, 12, 13.
      • A 13-as PIN-re rá van kötve egy alaplapi LED.
    • Analóg: a másik felén található, A0-A5-tel jelölve. Innen csak beolvasni tudunk adatot. Speciális lábak:
      • Az A4-A5 lábak alkalmasak I2C (TWI) kommunikációra. Az A4 az SDA, az A5 pedig az SCL. Ennek a lényege: az egyiken az órajelet küldjük (SCL), a másikon az adatot (SDA), így több fogadó lábra is tudunk adatot küldeni. Erre példa az LCD kijelző, melyet alapból, I2C nélkül 14 kábellel tudunk összedrótozni, míg I2C-vel elég 4 kábel: feszültség, föld, SDA, SCL.

Az alábbi ábra (melynek forrása egy internetes kérdésben volt található, de a copyright információ szerint az arduino.cc oldalra vezethető vissza) a kivezetéseket illusztrálja:

Arduino_UNO_pinout.jpg

Nézzünk körül!

E leírás során az Arduino UNO R3-as modellt használtam, azon belül is az után gyártott kék színűt, nem pedig az eredeti zöldet (elvileg nincs különbség a kettő között, a gyakorlatban viszont már tapasztaltam). Valószínűleg ez a legelterjedtebb változat, de mielőtt belemennénk a részletekbe, lássuk, milyen hasonló eszközök vannak!

Sok, az Arduino UNO-val szinte 100%-ban megegyező eszköz létezik: Genuino, Funduino, Leonardo stb. Ezt egyrészt amiatt fontos tudni, hogy ha olyan nevű eszközünk van, és Arduino-zni szeretnénk, akkor nem kell feltétlenül Arduino-t vennünk, másrészt sok esetben a leírásokban ill. hardverelemekben adott típusra hivatkoznak. Ezek egymással teljesen kompatibilisak.

Kicsit felfelé nézve (de még az Arduino-s mikrokontrollereknél maradva) találjuk az Arduino Mega és Arduino Due lapokat. Kívülről is látványos különbség az Arduino UNO-val szemben a lábak száma, melyből 54 van. A memóriájuk is jóval nagyobb, pl. a program memória 512 kB (ellentétben az Arduino UNO 32 kB-jával).

"Oldalra-felfelé" tekintve találjuk az ESP8266 kódú WiFi-s világot, mely számos formában létezik. Az ESP 01 kódú alapváltozatnak nincsenek is önálló lábai, csak az, amivel össze lehet kötni más eszközökkel, programozni lehet, ill. innan kapja a tápellátást. Ujjhegynyi mérete ellenére a benne levő memória típustól függően 512 vagy 1024 kB; tehát izmosabb mint a Mega és a Due. Alapból olyan firmware van rajta, amellyel AT parancsokat végrehajtva tudunk rákapcsolódni a wifi-re, de ez felülírható (ami egyébként nem egyszerű). A WeMos tkp. az Arduino és az ESP8266 összegyúrása: mérete és elrendezése megegyezik az Arduino UNO-éval, az IC viszont nem az Arduino-nál megszokott ATmega328, hanem at ESP8266. Ide tartozik még a NodeMCU, ami szintén 32 lábas és szintén ESP8266-ot tartalmaz, viszont kinézetében szakított az Arduino UNO-val. Ezek az eszközök jók, érdekesek, és ezek a valódi IoT (Internet of Things) alap építőkövei, mivel adatokat tudnak küldeni-fogadni. Ami miatt viszont még nem szorították ki az Arduino-t, az szerintem a kiforratlanságuk. A feltöltés borzasztó lassú, ami ha egyszer történik, még rendben van, de ha sokszor egymás után (ami a gyakoribb), akkor kifejezetten zavaró. A programot ráadásul több esetben nem is lehet csak úgy feltölteni, hogy rádugom és már töltöm is, azt programozó módba kell tenni. (És naná, hogy nem működik a leírásnak megfelelően!) Ráadásul számos érzékelővel nem kompatibilis, vagy legalábbis még nem jelent meg az ESP8266 kompatibilis könyvtár.

Nagyon felfelé tekintve találjuk a különböző, PI elnevezésű mikro PC-k világát: Raspberry PI, Orange PI, Banana PI (kíváncsi vagyok, megjelenik-e valaha a túlárazott, de design-os Apple PI…). Ezek valóban már mikroszámítógépek, pl. a memóriájuk még a legizmosabb Arduino-s eszközökhöz képest nagyságrendileg ezerszer akkorák; itt még GB-os nagyságrendről beszélünk. Az interfészek is ennek megfelelően vannak kialakítva: USB-k, HDMI, jack dugó stb. került a lapkára. Viszont az Arduino-s világban megszokott tűk még itt is szerepelnek.

Életkorban lefelé találjuk a gyerekeknek megalkotott micro:bit eszközt, mely általános iskolában nagyon jó alapozó lehet a középiskolai Arduino-s foglalkozások számára.

Az Arduino fejlesztői környezet

A számítógépről futó fejlesztői környezet legfontosabb részei az alábbiak:

  • A szerkesztő, mely szintaktikus ellenőrzést ugyan nem végez, de a kulcsszavakat beszínezi.
  • A kódot fordítani a Sketch -> Verify/Compile menüponttal, a Ctrl+R billentyűkombinációval vagy a menü alatt első ikonnal lehetséges.
  • Fordítani és feltölteni az Arduino-ra: Sketch -> Upload; Ctrl+U; második ikon.
  • A File -> Examples alatt számos példaprogram található. Pl. a már bemutatott villogó itt is elérhető: File -> Examples -> 01.Basics -> Blink.
  • A lapka típusát a Tools -> Board alatt, míg a portot a Tools -> Port alatt tudjuk beállítani. Lapkafüggő, hogy ezek alatt még mi minden jelenik meg.
  • A soros adatkapcsolatot a Tools -> Serial Monitor (gyors hívó: Ctrl+Shift+M) segítségével tudjuk vezérelni: itt írja ki a képernyőre a bejövő adatokat, és itt tudunk adatokat küldeni. Pl. a már említett TX-RX mechanizmussal itt tudjuk elérni az Arduino UNO 0 és 1 lábakra kötött eszközöket. Fontos, hogy az átviteli sebesség megfelelő értékre legyen beállítva. A kódban ez így jelenik meg: Serial.begin(9600);, a seral monitoron pedig jobb oldalon lent egy lenyíló listából lehet kiválasztani. A leggyakoribb értékek: 9600 (lassú, de megbízható) és a 115200 (gyors, de bizonyos esetekben instabil lehet).
  • A soros adatkapcsolat egy másik lehetősége a Tools -> Serial Plotter, mely diagramot rajzol a kapott értékekből. Pl. ha egy analóg érzékelő értékét tizedmásodpercenként kiküldjük a soros portra, akkor látványos függvény lehet az eredmény.
  • A külső könyvtárakat háromféleképpen tudjuk beállítani.
    • Tools -> Manage Libraries… -> (vagy Sketch -> Include Library -> Manage Libraries…) itt rákeresünk a megfelelő könyvtárra (pl. DHT sensor library), és telepítjük.
    • Letöltjük az internetről (a fenti példában https://github.com/adafruit/DHT-sensor-library -> Clone or Download -> Download ZIP), majd az Arduino IDE-ből Sketch -> Include Library -> Add .ZIP Library… -> kiválasztjuk a letöltött zip fájlt és telepítjük.
    • Az internetről letöltött zip fájlt kézzel kicsomagoljuk a megfelelő helyre. Ezt a File -> Preferences megnyitásával tudjuk megnézni, és az alapértelmezett hely a következő: C:\Users\[user]\Documents\Arduino.

Az Arduino C++ programozási nyelv

Az Arduino-t egy C++ alapú programozási nyelv segítségével tudjuk programozni. A hardver viszont oly mértékben korlátozza a lehetőségeket, hogy a valóságban a C++-nak csak egy nagyon szűk szelete érhető el. Még a legösszetettebb Arduino kód is ránézésre egyszerűsített C-re hasonlít.

A referencia itt található: https://www.arduino.cc/reference/en/. Lássuk a lényegesebb részeket!

Alapok

A fájl kiterjesztése .ino, és a fájl nevének (kiterjesztés nélkül) meg kell egyeznie azzal a könyvtárral, amelyben van. A két kötelező függvény a void setup() és a void loop(). Az előbbi egyszer fut le, a rendszer indulásakor, az utóbbi pedig folyamatosan, egymás után, végtelen ciklusban. (Érdemes egyébként ezt figyelembe venni akkor is, amikor Arduino-t programozunk, más eszközzel, pl. az mBlock-kal: mindig pontosan egy setup és egy loop van.)

Az abszolút minimum kód kb. így néz ki:

void setup() {}
void loop() {}

Ha a forrásfájl neve minimal.ino, akkor annak a minimal/ könyvtárban kell lennie.

Változók

Vannak globális és lokális változók. A leggyakoribb alaptípusok és a hozzá tartozó műveletek megtalálhatóak. Beépített String típus is van, a leggyakoribb megszokott string műveletek elérhetőek: összefűtés a + művelettel (pl. "Result:" + result).

Vezérlő struktúrák

Az alap vezérlő struktúrák meg vannak, pl. a feltételkezelés, ciklus, a megszokott C/C++ szintaxissal, pl.:

if (a < b) {
  foo();
} else {
  bar();
}
for (int i=0; i<10; i++) {
  something(i);
}
while (a < b) {
  a++;
}

Ki- és bemeneti függvények

Talán a leglényegesebb része a nyelvnek az író/olvasó műveletek (pinMode(), digitalRead(), digitalWrite(), analogRead(), analogWrite()), melynek segítségével kommunikálni tudunk a külvilággal. Az adatkommunikációt a Serial osztályon keresztül érjük el, pl. Serial.println("hello"), int incomingValue = Serial.read().

Saját függvények

A C-ben megszokott struktúrájú függvényeket írhatunk: lehetnek paraméterek, visszatérési típus, a visszatérési értéket pedig a return utasítás után adhatjuk meg.

Építőkockák

Serial monitor

A Serial monitor alkalmas arra, hogy a számítógép kommunikáljon az Arduino UNO lappal. Szinte ez az egyetlen módja a hibakeresésnek is. A Serial osztály függvényei segítségével tudjuk elérni a műveleteket (https://www.arduino.cc/reference/en/language/functions/communication/serial/), az Arduino IDE-ben pedig a Serial Monitort a már leírt módon tudjuk elindítani. Töltsük fel az Arduino-ra a következő kódot:

int ledpin = 13;
 
void setup() {
  Serial.begin(9600);
  pinMode(ledpin, OUTPUT);
  Serial.println("Issue command on or off.");
}
 
void loop() {
  if (Serial.available()) {
    String command = Serial.readString();
    command.trim();
    if (command == "on") {
      digitalWrite(ledpin, HIGH);
      Serial.println("LED turned on.");
    } else if (command == "off") {
      digitalWrite(ledpin, LOW);
      Serial.println("LED turned off.");
    } else {
      Serial.println("Unknown command: " + command + ". Possible commands: on, off.");
    }
  } else {
    delay(100);
  }
}

A Serial Monitor felső sorába írt parancsok (on, off) segítségével tudjuk vezérleni a lapka 13-as lábhoz kapcsolt LED-et. Némi magyarázat:

  • A Serial.println() függvény segítségével küld adatot az Arduino a számítógépnek.
  • A Serial.read() függvény egyetlen bájtot olvas be. A Serial.readString() a teljes szöveget beolvassa.
  • A String osztály segítségével alapvető string műveleteket tudunk végrehajtani. A trim() helyben törli az elejéről a nem látható karaktereket, erre jelen esetben amiatt van szükség, mert alapból az új sor karaktert is tartalmazza a beolvasott string.
  • A delay() a megadott századmásodpercig vár.
  • A program példa a feltételkezelésre is.

Digitális érzékelő

A digitális érzékelők kétféle értéket tudnak érzékelni. Így ha csak azt szeretnénk látni, hogy az adott esemény bekövetkezett-e, a legegyszerűbb a lapkán található 13-as LED-et be- ill kikapcsolni. Ha feltételezzük, hogy a 2-es lábon jelenik meg a logikai alacsony vagy magas érték, akkor a vonatkozó kód ez:

const int sensorPin = 2;
const int ledPin = 13;
 
void setup() {
  pinMode(ledPin, OUTPUT); 
  pinMode(sensorPin, INPUT);
}
 
void loop() {
  digitalWrite(ledPin, digitalRead(sensorPin));
}

Analóg érzékelő

Az analóg érzékelők végső soron egy 0 és 1023 közötti egész számot adnak. Ha feltesszük, hogy egy ilyen érzékelő az A0-ás lábra van kötve, akkor a kód a következő:

const int sensorPin = A0;
 
void setup() {
  pinMode(sensorPin, INPUT);
  Serial.begin(9600);
}
 
void loop() {
  int sensorValue = analogRead(sensorPin);
  Serial.println(sensorValue);
  delay(100);
}

I2C kapcsolat

Az I2C (I2C, IIC, ami az Inter-Integrated Circuit rövidítése) egy olyan protokoll, melynek segítségével több eszközt tudunk ugyanazon a lábon megcímezni. Két részből áll: az adat (SDA, mely az Arduino UNO-n az A4 lábat jelenti) és az óra (SCL, Arduino UNO A5). Ezen kívül szükség van még egy tápfeszültségre (VCC) és egy földre (GND), így eszközönként 4 vezetékkel megússzuk (ellentétben például az I2C nélküli LCD kijelzővel, melyhez 14 kábel kell, ld. lejjebb), de az igazán nagy hozzáadott értéke nem es ez, hanem az, hogy több eszközt tudunk rádugni ugyanazokra a lábakra egyszerre.

Az I2C a Philips cég specifikációja. Az Azmel (melynek chipje az Arduino-ban van) ezt Two Wire Interface (TWI) néven fejlesztette tovább. A lényege ugyanaz: egy adatkábel és egy órajel segítségével lehessen több mindent megcímezni. Az I2C ill. TWI Arduino specifikációja itt található: https://www.arduino.cc/en/reference/wire.

Az I2cScanner nevű program (https://playground.arduino.cc/Main/I2cScanner) segítségével fel tudjuk deríteni, hogy épp milyen eszközök vannak rákapcsolva, és milyen címen lehet azokat elérni. Pl. ha ráteszünk egy I2C-vel ellátott 2x16 soros karakteres LCD kijelzőt, valamint egy DS3231-es kódjelű órát, akkor azt látjuk, hogy az LCD címe a 0x27 (ez szerepel a kódban is), az órához meg két cím is tartozik: 0x57 és 0x68.

Az eszközök I2C címeit ezen az oldalon találjuk: https://learn.adafruit.com/i2c-addresses/the-list.

SPI

Az SPI a Serial Peripheral Interface (soros külső interfész) rövidítése. leírása itt található: https://www.arduino.cc/en/reference/SPI. A felépítése master-slave (mester-szolga), azaz az egyik eszköz (tipikusan a mikrokontroller) irányítja a kommunikációt. A szükséges lában egyúttal megmagyarázzák a működését is:

  • MOSI (Master In Slave Out): ezen a csatornán küld üzenetet a szolga a mesternek. Az Arduino UNO-n ez a 11-es láb.
  • MISO (Master Out Slave In): ezen a csatornánk a mester küld üzenetet a szolgának. Az Arduino UNO-n ez a 12-es láb.
  • SCL (Serial Clock): a mester ezen keresztül generálja az órajeleket az adattovábbítás szinkronizálása érdekében. Az Arduino UNO-n ez a 13-as láb.
  • SS (Slave Select): ezzel tudja a mester engedélyezni vagy letiltani a szolgát, ezáltal lehetővé tenni azt, hogy több eszköz csatlakozzon az SPI-n keresztül. Egyetlen eszköz csatlakozása esetén tipikusan a 10-es lábat szokás használni, mert az a többi mellett van, de tetszőleges láb használható erre a célra.

Ha tehát mondjuk 3 eszközt szeretnénk ezen az interfészen keresztül csatlakoztatni, akkor a MOSI-t, MISO-t és az SCL-t ugyanarra csatlakoztatjuk, az SS pedig lehet mondjuk a 10, a 9 és a 8, és kódból vezéreljük az Arduino-n, hogy az aktuális kommunikáció kinek szól.

Példa az SPI-re a mikro SD kártya olvasó.

Külső megszakítások

Megszakítás során a program szekvenciális futása megszakad, lefut egy művelet, majd folytatódik az eredeti program. Alapvetően megkülönböztetünk külső és belső megszakítást. A külső megszakítás valamilyen külső esemény hatására következik be, míg a belső tipikusan időzítő.

Az Arduino UNO-n a 2-es és a 3-as láb alkalmas külső megszakításra. A vonatkozó függvény ez: attachInterrupt(interrupt, function, mode) (vonatkozó lekapcsolás: detachInterrupt(interrupt)). A paraméterek:

  • interrupt: a megszakítás száma. Lehetséges értékek: 0 (2-es láb esetén) és 1 (3-as láb esetén). Jó programozási gyakorlat használni az int digitalPinToInterrupt(pin) függvényt, mellyel a fizikai lába tudjuk megadni, és ezzel a portolhatóságot segítjük elő. Pl. digitalPinToInterrupt(2) azt mondja meg, hogy a 2-es lábra kötött megszakítást szeretnénk hazsnálni.
  • function: az a paraméter és visszatérési érték nélküli függvény, mely lefut, amikor a megszakítás bekövetkezik.
  • mode: azt mutatja meg, hogy mikor következzen be a megszakítás. lehetséges értékek:
    • LOW: mindig bekövetkezik, amikor LOW értéket kap a láb.
    • CHANGING: változásra fut le, tehát LOW -> HIGH és HIGH -> LOW esetben is.
    • RISING: LOW -> HIGH változásra fut le.
    • FALLING: HIGH -> LOW változásra fut le.

A megszakításokat ki lehet kapcsolni a noInterrupts() függvénnyel, visszakapcsolni pedig az interrupts() függvénnyel.

Vonatkozó oldalak:

A következő példaprogramban a 2-es lábra kötött megszakítás fut le változásra, és változás esetén a 13-as lábra kötött LED változtatja állapotát.

int led = 13;
volatile int state = LOW;
 
void setup() {
  pinMode(led, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(2), blink, CHANGE);
}
 
void loop() {}
 
void blink() {
  state = !state;
  digitalWrite(led, state);
}

Látható, hogy a loop() függvény teljesen üres, mégis, állapotváltozás következik be a futás során.

Figyeljük meg a volatile kulcsszót a state változó előtt: ez azt jelenti, hogy bármikor megváltozhat, nemcsak a szekvenciális futás során. Ennek jelentősége a következő: a fordítóprogram a háttérben rengeteget optimalizál(hat), pl. egy int típusú i változó esetén ha beállítjuk az értékét mondjuk 2-re, majd három utasítással később van egy olyan, hogy if (i<5), de közben nem szerepel az i, akkor a fordító alapból igaznak veszi a feltételt, és tkp. bele se teszi. A volatile megakadályozza az ilyen optimalizálást: ez esetben nem feltételezi, hogy közben nem változhat meg az értéke, mert pl. egy megszakítás hatására pont megváltozhat.

Bekötés:

  • Nyomógomb A -> Arduino 5V
  • Nyomógomb B -> Arduino 2
  • Nyomógomb B -> 10 kΩ ellenállás -> Arduino GND

Teszt: nyomjuk meg és engedjük fel a nyomógombot többször egymás után, közben figyeljük a 13-as LED-et.

Időzítő

Az időzítő a belső megszakítások tipikus példája. Ennek segítségével adott időközönként le tudunk futtatni egy programot. A delay() függvény erre nem teljesen tökéletes, mert hozzáadódik a program futásának az ideje, így ha pontos időzítést szeretnénk, akkor érdemes a megszakítást használni.

A megszakításokat legegyszerűbben egy külső, de beépített könyvtár segítségével, a Timer1-gyel tudjuk vezérelni, melynek specifikációja itt található: https://playground.arduino.cc/code/timer1. Példaprogram fél másodperces megszakítással, mely a 13-as LED-et villogtatja:

#include "TimerOne.h"
 
void setup() {
  pinMode(13, OUTPUT);
  Timer1.initialize(500000);
  Timer1.attachInterrupt(callback);
}
 
void callback() {
  digitalWrite(13, 1 - digitalRead(13));
}
 
void loop() {}

Saját könyvtár készítése

Saját könyvtárakat is lehet készíteni, bár a gyakorlatban célszerű inkább már létezőt keresni, mint nekiállni megírni. Szükség esetén lássuk a klasszikus példát: Morze jelek (rövid és hosszú) küldése. A könyvtár C++-ban készül, és a következőket tartalmazza.

Fejléc fájl: praktikusan Morse.h, és a tartalma legyez alábbi:

#ifndef Morse_h
#define Morse_h
 
#include "Arduino.h"
 
class Morse {
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};
 
#endif

Megvalósítás, jelen esetben Morse.cpp:

#include "Arduino.h"
#include "Morse.h"
 
Morse::Morse(int pin) {
  pinMode(pin, OUTPUT);
  _pin = pin;
}
 
void Morse::dot() {
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}
 
void Morse::dash() {
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

Ahhoz, hogy el tudjuk érni az Arduino beépített függvényeit, be kell tölteni az Arduino.h-t (ez az Arduino ino fájlok esetén automatikus).

Kulcsszó fájl: ezt felhasználva segíti a könyvtárat használó fejlesztőt az Arduino IDE, megfelelő színezéssel. Fájlnév: keywords.txt, melynek tartalma (tabulátorokkal):

Morse    KEYWORD1
dash    KEYWORD2
dot    KEYWORD2

Példa program: ha a könyvtár- ill. fájlnév ez: examples\SOS\SOS.ino, akkor a File -> Examples -> Morse alatt fog megjelenni, SOS névvel.

#include <Morse.h>
 
Morse morse(13);
 
void setup() {}
 
void loop() {
  morse.dot(); morse.dot(); morse.dot();
  morse.dash(); morse.dash(); morse.dash();
  morse.dot(); morse.dot(); morse.dot();
  delay(3000);
}

Mindezt tegyük be egy Morse nevű könyvtárba, és ezt csomagoljuk be, Morse.zip néven. tehát a Morse.zip tartalmazni fog egy Morse könyvtárat, melyen belül található a többi. Ezt a szokásos módon telepíthetjük: Sketch -> Include Library -> Add .ZIP Library…, vagy közvetlenül kicsomagolva a c:\Users\[user]\Documents\Arduino\libraries\ könyvtárba.

Kimenetek

Fényes alkalmazások

Egyszerű LED-es alkalmazás

Az ember annál egyszerűbb, valamit is csináló elektronikai alkalmazást szinte el sem tud képzelni, mint a LED villogtatót. Erről már volt szó beépített LED-del, most valósítsuk meg külsővel! Az egyszerűség érdekében a kód maradjon az, amit a bevezetőben már láttunk, a 13-as láb melletti LED villogtatásánál, viszont most emellett használjunk egy tényleges külső LED-et is.

A gyakorlatban meglepő módon nem is annyira egyszerű! A problémát az okozza, hogy nem tudjuk csak úgy bedugni a LED hosszabb szárát a 13-as lyukba, a rövidebbet pedig a mellette levő GND-be, ugyanis ez esetben egyszerűen kiég. Szükség van előtét ellenállásra. Az ellenállás ne legyen túl nagy, mert akkor nagyon halvány lesz az eredmény, de túl kicsi se, nehogy kiégjen; kb. pár száz Ω-os legyen. (Én 220 Ω-os ellenállást használtam, és azzal szépen működött.) Mindehhez szükség van egy beszúrkálós próbapanelre, ill. - ha csak nem szeretnénk nagyon megerőszakolni az alkatrészeket - két apa kábelre is. (Mindezek egyébként tipikusan részei az alapkészletnek, amúgy a próbapanel kivételével filléres tételek. Emiatt ha van annyi LED-ünk, hogy belefér egy-egy tönkretétele, egy életben egyszer érdemes kipróbálni előtét ellenállás nélkül: tapasztaljuk meg mi magunk is, hogy tényleg tönkremegy.)

Kapcsolás:

  • Arduino 13 -> ellenállás (pár száz Ω) -> LED (fontos a polaritás: ebben a sorrendben előbb jön a hosszabb szára, ami a + fele, anódnak hívjuk) -> Arduino GND.

A próbapanelen oldal végig hosszában össze vannak kötve a lyukak, belül pedig ötösével. Elvileg bárhogy kapcsolhatjuk, viszont az a konvenciót, hogy az oldalán levők a feszültség és a 0.

Teszt: ha mindent jól csináltunk, a bekapcsolt LED lassan villog.

A LED fényerejének változtatása PWM segítségével

A fenti példa látványos, ám nagyon bináris volt: van világított, vagy nem. A digitális lábakon is tudunk analóg jelent szimulálni PWM technikával: nagyon gyorsan váltakoztatva az idő bizonyos százalékában HIGH, bizonyos százalékában pedig LOW állapotban van. Vegyük például a következő kódot:

void setup() {
  pinMode(12, OUTPUT);
}
 
void loop() {
  digitalWrite(12, HIGH);
  delay(1);
  digitalWrite(12, LOW);
  delay(4);
}

Kössünk egy LED-et a 12-es lábra, előtét ellenállás nélkül; a LED világítani fog, de nem megy tönkre, mert 1 Voltként fogja érzékelni a 20%-ban 5 Volt, 80%-ban nulla osztást. Ezzel viszont az a probléma, hogy gyakorlatilag a teljes processzoridőd arra használjuk, hogy gyorsan módosítsa a kimenő jelent 0 és 5V között, ez tehát nem egy működőképes opció. Viszont léteznek olyan lában, melyek hardveresen képesek a PWM-re: egy 0 és 255 közötti számot kell analóg módon kiírni, és ennek megfelelő lesz az eredmény. Az ilyen lábak mellett ~ jel található, és ezek a következők: 3, 5, 6, 9, 10, 11. Először győződjünk meg erről! Töltsük fel az alábbi kódot:

void setup()  {
  pinMode(9, OUTPUT); 
  analogWrite(9, 100);
}
 
void loop() {}

Valamelyik GND-ből és a 9-es lábból vezessünk ki egy-egy kábelt. Először mérjük meg multiméterrel az eső feszültséget: valamivel 2V alatt kell, hogy legyen. Majd oszcilloszkóppal nézzük meg részletesebben! Azt kell látnunk kb. 1 ms-os "nagyításban", hogy a jel sűrűn változik 0V és 5V között.

pwmjel.jpg

Látható, hogy egy teljes periódus 2ms, másodpercenként tehát ötszázszor van 5V->0V és 0V->5V átmenet.

Most térjünk vissza az eredeti feladatra! A kapcsolással kezdjük, mivel nagyon hasonlít a korábbihoz:

  • Arduino 9 -> előtét ellenállás (pár száz Ω) -> LED -> Arduino GND.

A kód a következő:

const int ledPin = 9;
 
void setup()  {
  pinMode(ledPin, OUTPUT); 
}
 
void loop() {
  for (int fadeValue = 0; fadeValue <= 255; fadeValue +=5) { 
    analogWrite(ledPin, fadeValue);    
    delay(25);       
  } 
  for (int fadeValue = 255; fadeValue >= 0; fadeValue -=5) { 
    analogWrite(ledPin, fadeValue);
    delay(25);
  } 
}

A digitális lábakra 0 és 255 közötti "analóg" értékeket tudunk kiírni. A példában fokozatosan erősítjük majd csökkentjük a jel értékét, ezáltal a LED-en erősödő és elhalványuló fényerőt kapunk eredményül.

Valójában elég kényelmetlen előtét ellenállást alkalmazni a LED-eknél, még egy ilyen egyszerű áramkör esetén is szükségünk volt próbapanelre. Így lássunk egy másik technikát, melyben megtapasztalhatjuk a PWM erejét! Szedjünk le minden alkatrészt az Arduino-ról, és töltsük fel rá a következő programot:

const int ledPin = 11;
 
void setup()  {
  pinMode(ledPin, OUTPUT); 
}
 
void loop() {
  for (int fadeValue = 0; fadeValue <= 50; fadeValue +=5) { 
    analogWrite(ledPin, fadeValue);    
    delay(25);
  } 
  for (int fadeValue = 50; fadeValue >= 0; fadeValue -=5) { 
    analogWrite(ledPin, fadeValue);
    delay(25);
  } 
}

Vegyük le a feszültségről, és helyezzük rá a LED-et, mindenféle előtét ellenállás nélkül, közvetlenül a megfelelő lyukakba: az anódot (pozitív fele, hosszabb szár) a 11-esbe, a katódot (negatív fele, rövidebb szár) pedig a GND-be. Amiatt ezekbe, mert a 13-as láb melletti GND és a 11-es láb elég közel vannak egymáshoz ahhoz, hogy ezt ott helyben is meg tudjuk tenni. (A 12-es és 13-as láb nem megfelelő erre a célra, mivel nincs mellette ~.) Majd kössük rá a feszültségre ismét. A LED nem ég ki, hanem szépen villog, noha nem kapott előtét ellenállást! Mi történt valójában? Folyamatosan kap 5 Voltot és 0 Voltot is, viszont olyan rövid ideig, hogy szinte "egybefolyik" számára: még az 50-es érték is valamivel kevesebb mint 1 Voltnak felel meg (a 255 lenne az 5 Volt). Ez a technika nagyon fontos lesz a későbbiekben, amikor a LED-re épülő összetettebb megjelenítőket (pl. a 7 szegmenses számkijelzőt) fogjuk használni. Most viszont ne felejtsük el kihúzni, mert egy véletlen tartós 5 Volt a 11-es lábon végzetes lehet a LED számára.

Többszínű LED-ek

A 37-in-1 szenzor gyűjteménybe bekerült néhány többszínű LED is (ezek tehát nem szenzorok), most ezek bemutatása következik.

RGB LED-ek

Ezek a legáltalánosabb LED-ek, bármilyen színt ki lehet "keverni" a piros, zöld és kék színekből. Kétféle tokozással kerül forgalomba: az egyik egy henger alakó, mely félgöbben végződik, 3_CIr felirattal, a másik pedig egy vízszintes lapka, 3_Clor felirattal.

Bekötés: a lapka maga tartalmaz ellenállást (ez a többire is igaz), így nem szükséges előtét ellenállást alkalmaznunk. Valamint a következő trükkel a kábeleket is megspóroljuk: ahova a GND kerülne, arra egyszerűen logikai LOW-t (0V, GND) teszünk, így működni fog. (Nem tudom, mennyire szabályos, de gyors, és működik.) tehát a bekötés:
- RGB LED - -> Arduino 8 (vagy Arduino GND, ha nagyon szabályosak szeretnénk lenni)
- RGB LED R -> Arduino 9
- RGB LED G -> Arduino 10
- RGB LED B -> Arduino 11

A kód a következő:

int gndPin = 8; // PIN 8 works as GND
int redPin = 9;
int greenPin = 10;
int bluePin = 11;
 
void setup() {
  pinMode(gndPin, OUTPUT);
  digitalWrite(gndPin, LOW);
  pinMode(redPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  pinMode(greenPin, OUTPUT);
}
 
int red[] = {255, 255, 255, 255, 0, 0, 0, 0};
int green[] = {255, 255, 0, 0, 255, 255, 0, 0};
int blue[] = {255, 0, 255, 0, 255, 0, 255, 0};
 
void loop() {
  for (int i=0; i<8; i++) {
    analogWrite(redPin, red[i]);
    analogWrite(greenPin, green[i]);
    analogWrite(bluePin, blue[i]);
    delay(1000);
  }
}

A két RGB LED nem teljesen egyforma, fel van cserélve az R és a G; a fenti kód a "kilógó" változatra vonatkozik. Ha mindent jól csináltunk, akkor a főbb színeket mutatja a LED sorban.

Kétszínű LED-ek

Két kétszínű (piros és zöld) LED is helyet kapott a 37 szenzor csomagban: az egyik kisebb, a másik nagyobb. Jellemzőjük, hogy magának a LED-nek is 3 lába van.

Bekötés: itt is el tudjuk kerülni a kábelek használatát, az alábbi bekötéssel:

  • RG LED S (ez a zöld) -> Arduino 12
  • RG LED középső (ez a piros) -> Arduino 13
  • RG LED - (ez a GND) -> Arduino GND

Ennek a bekötésnek a hátránya, hogy a lapkán levő LED is villog. A kód:

int greenPin = 12;
int redPin = 13;
 
void setup() {
  pinMode(greenPin, OUTPUT);
  pinMode(redPin, OUTPUT);
}
 
void loop() {
  analogWrite(redPin, 255);
  analogWrite(greenPin, 0);
  delay(1000);
 
  analogWrite(redPin, 0);
  analogWrite(greenPin, 255);
  delay(1000);
}

Animálós LED

A gyűjteményben található egy olyan LED, melyre csak áramot kell kötni, és mindenféle színben pompázik (látszólag) véletlenszerűen. Két lába van, a hosszanti oldalán elnyúlik, és a lapkán (valószínűleg tévedésből) az olvasható, hogy DHT11.

Használatát kód nélkül is demonstrálni tudjuk, ha az S-sel jelölt láb 5 Voltra kerül, a mínusszal jelölt pedig a GND-re; ezt meg tudjuk tenni az analóg lábak melletti 5V-GND-GND segítségével, közvetlenül.

Ha kóddal szeretnénk vezérleni, akkor a következőképpen tudjuk megtenni. Bekötés:

  • LED S -> Arduino 12
  • LED - > Arduino GND

(Itt is beletehetjük közvetlenül; a középső, nem használt láb a 13-as lyukba kerül.)

int ledPin = 12;
 
void setup() {
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
}
 
void loop() {}

Lézer

A 37 szenzor gyűjtemény része. Réz henger van a lapkán, onnan lehet felismerni. Használata: az animálós LED-nél leírtak szerint.

Megjelenítők

Hétszegmenses kijelző

A 7 szegmenses kijelző egy igazán kiváló tanuló eszköz arra, hogy miért ne használjuk, és ezt megismerve egyrészt jobban megérjük, másrészt jobban becsüljük a fejlettebb technikákat. Általában része az alapcsomagnak.

A működése a következő: valójában 8 különálló LED-ből áll az eszköz, ebből 7 szegmens a vonalkák, a nyolcadik pedig a pont. 10 lába van, ebből kettőt a GND-re kell kötni, a másik 8 pedig a 8 szegmens anódja egyenként. A bekötéshez pár száz Ohm-os előtét ellenállást kell alkalmazni; én 220 Ohm-osat használtam. Érdemes a kijelzőt beleszúrni egy próbapanelba úgy, hogy a felső és az alsó 5 tű külön oldalra kerüljön. A bekötés:
- Kijelző felső 1 -> ellenállás -> Arduino 9
- Kijelző felső 2 -> ellenállás -> Arduino 8
- Kijelző felső 3 -> Arduino GND
- Kijelző felső 4 -> ellenállás -> Arduino 7
- Kijelző felső 5 -> ellenállás -> Arduino 6
- Kijelző alsó 1 -> ellenállás -> Arduino 11
- Kijelző alsó 2 -> ellenállás -> Arduino 10
- Kijelző alsó 3 -> Arduino GND
- Kijelző alsó 4 -> ellenállás -> Arduino 5
- Kijelző alsó 5 -> ellenállás -> Arduino 4

A kódban egyesével be kell állítani a megfelelő logikai értéket:

int states[10][7] = {
  {HIGH, HIGH, HIGH, HIGH, LOW,  HIGH, HIGH}, // 0
  {HIGH, HIGH, LOW,  LOW,  LOW,  LOW,  LOW }, // 1
  {LOW,  HIGH, HIGH, LOW,  HIGH, HIGH, HIGH}, // 2
  {HIGH, HIGH, HIGH, LOW,  HIGH, HIGH, LOW }, // 3
  {HIGH, HIGH, LOW,  HIGH, HIGH, LOW,  LOW }, // 4
  {HIGH, LOW , HIGH, HIGH, HIGH, HIGH, LOW }, // 5
  {HIGH, LOW,  HIGH, HIGH, HIGH, HIGH, HIGH}, // 6
  {HIGH, HIGH, HIGH, LOW,  LOW,  LOW,  LOW }, // 7
  {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH}, // 8
  {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, LOW }  // 9
};
 
void displayNumber(int number) {
  for (int i = 5; i <= 11; i++) {
    digitalWrite(i, states[number][i-5]);
  }
}
 
void setup() {
  for (int i = 4; i <= 11; i++) {
    pinMode(i, OUTPUT);
  }
  digitalWrite (4, LOW);
}
 
void loop() {
  for (int i = 0; i < 10; i++) {
    displayNumber(i);
    delay(1000);
  }
}

Végeredményben nagyon jól látható, hogy ezt miért nem érdemes használni:

  • A bekötéshez 10 kábelre és 8 előtét ellenállásra van szükség. Az összekötés sok időt elvesz.
  • A 14 digitális lábból elvesz 8-at. Mivel az Arduino-n összesen 20 olyan láb van, melyre tudunk értéket kiírni, ezzel a technikával legfeljebb 2 számjegyet tudunk egyszerre kiírni. (Talén emiatt van az, hogy az én kezdőkészletembe 2 darabot adtak?)
  • Lényegében csak a 10 számjegy kijelzésére alkalmas (bár a pontot is figyelembe véve elvileg 256 különböző mintát tudunk kirajzolni). Tehát alkalmatlan valódi adatok megjelenítésre.
  • A kód meglehetősen hosszú. Ahhoz, hogy kiírjunk egy 5-öst, a mai világban már elvárnánk azt, hogy elég legyen a kódba azt írni, hogy print(5), és a rendszer tegye a dolgát, nem?

Négyszámjegyes kijelző

A 4 számjegyes kijelzőt már nem is lehet a fenti, egy számjegyesnél bemutatott technikával megoldani, már csak a megfelelő számú láb hiánya miatt sem. Ugyanis mind a négy számjegy 8 szegmensből áll, ami már önmagában 32, de kell minimum egy katód. Az Arduino-nak összesen 32 lába van, de ebből max. 20 olyan, mely ilyen célra végső soron használható. Látni fogjuk, hogy ez a kijelző típus oktatási célra kiválóan alkalmas, ugyanis nagyon jó, máshol is hasznosítható (hasznosított) ötletek vannak benne, de a gyakorlatban önmagában nemigazán alkalmas a használatra.

A kimenetek számának hiányát a következő ötlettel oldották meg: mind a 8 szegmensnek szegmensenként közös az anód (tehát pl. a tizedespont mind a 4 számjegynél ugyanaz), szegmensenként pedig közös a katód (tehát pl. a baloldali számjegy mind a 8 szegmense ugyanarra a katódra van kötve). Ezzel a módszerrel tetszőleges szegmenst be tudunk kapcsolni. Viszont hogyan oldjuk meg azt a problémát, hogy különböző helyi értékeknél különböző értékek jelenjenek meg? Itt használhatjuk ki a szemünk tehetetlenségét: ha valami nagyon gyorsan (pl. másodpercenként ötvenszer) vibrál, azt folyamatosnak látjuk. A megoldás tehát a következő: bekapcsoljuk a bal oldali helyi értéket, a többit lekapcsoljuk, megjelenítjük egy pillanatra a bal oldali számjegyet, majd ki is kapcsoljuk. Utána a másodikkal, a harmadikkal, végül a negyedikkel. Majd ismét kezdjük az elejéről.

Le feledjünk, hogy LED-ekkel van dolgunk, mely 5 Volt hatására kiég. Többféleképpen kezelhetjük a problémát:

  • Az egy számjegyes kijelzőnél bemutatott módon, mindegyik szegmenshez előtét ellenállást alkalmazunk.
  • Jobban belegondolva: itt elegendő a szegmens kiválasztó katódokhoz előtét ellenállást tenni, ezáltal nem kell 8, elég 4 is.
  • Van egy elegáns, de veszélyes megoldás: előtét ellenállás nélkül, a PWM-nél bemutatott módon. Ha kellően nagy frekvenciával kapcsolgatjuk ki-be a LED-et, akkor elegendő azt biztosítani, hogy az idő max. 20%-ában legyen bekapcsolva, amit 1 Voltként fog értelmezni. A fent vázolt algoritmust annyival kell kiegészíteni, hogy miután kiírtuk mind a 4 számjegyet, kapcsoljuk ki az összeset, és hagyjuk úgy egy nagyon rövid ideig (ha a felvillanás 0,5 ms, akkor 10 ms várakozás két kiírás között jó választás lehet).

Most a harmadik esetet mutatom be. Ennek a hátránya, hogy itt tényleg nem szabad hibázni, mert különben ha csak néhány tizedmásodpercig 5 Volt esés fut a LED-en (az egyik anódra tartósan HIGH, az egyik katódra pedig tartósan LOW kerül), akkor tönkremegy. Emiatt a kódot előbb fel kell tölteni, mint bekötnénk az eszközt, nehogy egy korábbi program az eszköz szempontjából végzetes szemetet hagyjon maga mögött.

int digits[] = {2, 5, 6, 13};
int segments[] = {3, 7, 11, 9, 8, 4, 12, 10};
 
void setup() {
  for (int i = 0; i < 8; i++) {
    pinMode(segments[i], OUTPUT);
  }
  for (int i = 0; i < 4; i++) {
    pinMode(digits[i], OUTPUT);
    digitalWrite(digits[i], HIGH);
  }
}
 
void loop() {
  displayNumber(millis() / 100);
}
 
void displayNumber(int numberToDisplay) {
  long beginTime = millis();
  for (int i = 3; i >= 0; i--) {
    digitalWrite(digits[i], LOW);
    displayDigit(numberToDisplay % 10);
    delayMicroseconds(500);
    displayDigit(10);
    digitalWrite(digits[i], HIGH);
 
    numberToDisplay /= 10;
  }
  while((millis() - beginTime) < 10); 
}
 
int states[11][7] = {
  {1,1,1,1,1,1,0}, // 0
  {0,1,1,0,0,0,0}, // 1
  {1,1,0,1,1,0,1}, // 2
  {1,1,1,1,0,0,1}, // 3
  {0,1,1,0,0,1,1}, // 4
  {1,0,1,1,0,1,1}, // 5
  {1,0,1,1,1,1,1}, // 6
  {1,1,1,0,0,0,0}, // 7
  {1,1,1,1,1,1,1}, // 8
  {1,1,1,1,0,1,1}, // 9
  {0,0,0,0,0,0,0}  // off
};
 
void displayDigit(int digitToDisplay) {
  for (int i = 0; i < 7; i++) {
    digitalWrite(segments[i], states[digitToDisplay][i]);
  }
}

Miután feltöltöttük a kódot, kössük be a 4 számjegyes kijelzőt az alábbi módon (célszerűen húzzuk ki a kábelt, majd csak a bekötést követően dugjuk ismét be). Ehhez szúrjuk a kijelzőt a próbapanel közelébe úgy, hogy a felső fele az egyik, az alsó fele pedig a másik részben helyezkedik el, majd a következő bekötést hajtsuk végre:

  • 4 szegmenses kijelző felső 1 (első számjegy) -> Arduino 2
  • 4 szegmenses kijelző felső 2 (A szegmens) -> Arduino 3
  • 4 szegmenses kijelző felső 3 (F szegmens) -> Arduino 4
  • 4 szegmenses kijelző felső 4 (második számjegy) -> Arduino 5
  • 4 szegmenses kijelző felső 5 (harmadik számjegy) -> Arduino 6
  • 4 szegmenses kijelző felső 6 (B szegmens) -> Arduino 7
  • 4 szegmenses kijelző alsó 1 (E szegmens) -> Arduino 8
  • 4 szegmenses kijelző alsó 2 (D szegmens) -> Arduino 9
  • 4 szegmenses kijelző alsó 3 (tizedespont) -> Arduino 10
  • 4 szegmenses kijelző alsó 4 (C szegmens) -> Arduino 11
  • 4 szegmenses kijelző alsó 5 (G szegmens) -> Arduino 12
  • 4 szegmenses kijelző alsó 6 (negyedik számjegy) -> Arduino 13

A szegmensek az alábbiak:

 AAA
F   B
F   B
F   B
 GGG
E   C
E   C
E   C
 DDD  DP

Ennek a bekötésnek az az előnye, hogy a szegmensen egymás mellett levő lábak az Arduino-n is egymás mellé kerülnek. Ugyanakkor látható, hogy két láb kivételével ez elfoglalja az összes digitális kimenetet (a maradék kettő a 0 és az 1, ami a soros kommunikációra van fenntartva), a szükséges kábelek számát (12) is soknak tartom, ráadásul a kábelek is elég jól eltakarják a kijelzőt. Tehát ez ebben a formában nemigazán alkalmas arra, hogy komolyabb projektekre használjuk. Ellen ellenére elterjedt és népszerű, ehhez viszont további trükkökre van szükség…

Kitérő: a TM1638-as eszköz

Most, hogy láttuk a többszámjegyes kijelzés lehetőségeit és korlátait, talán jobban értékeljük a TM1638-as eszközt, aminek segítségével 8 számjegyet tudunk kiíratni, és ezen kívül van rajta 8 nyomógomb, valamint 8 piros LED (egyes típusokon 8 kétszínű: piros-zöld LED). Mindezt az eddigi technológiákkal - akárhogyan optimalizálunk is - művészet lenne rákötni. Viszont itt az ún. shift out technológiát alkalmazzák, melyre még később visszatérünk. A kód viszont rendkívüli mértékben leegyszerűsíthető, ahogyan a bekötés is.

Bekötés:

  • TM1638 VCC -> Arduino 3.3V
  • TM1638 GND -> Arduino GND
  • TM1638 STB -> Arduino 7
  • TM1638 CLK -> Arduino 9
  • TM1638 DIO -> Arduino 8

Látható tehát, hogy mindössze 5 lábbal megoldható a teljes bekötés, melyből mindössze 3 az adatláb (az eredeti koncepcióval erre 64 adatlábra, a 4 számjegyesnél megmutatott módszerrel pedig 16 adatlábra lenne szükség).

A natív kód önmagában eléggé bonyolult, viszont ha feltelepítjük ezt a könyvtárat: https://github.com/rjbatista/tm1638-library, akkor az is igen egyszerűvé válik.

#include <TM1638.h>
 
TM1638 module(8, 9, 7);
 
void setup() {}
 
void loop() {
  module.setDisplayToDecNumber(millis() / 100, 0);
}

8x8-as mátrix kijelző

A 8x8-as mátrix kijelző önmagában szintén inkább csak érdekes mist hasznos. Ebben 64 darab LED van mátrix-szerűen betokozva, így a klasszikus módon (külön anód, közös katód) esélytelen bekötni. Valójában összesen 16 lába van, mégpedig mindegyik sornak egy-egy anódja, és mindegyik oszlopnak egy-egy katódja. Alapból katódokra magas, míg az anódokra alacsony értéket teszünk, és mivel a LED-ben csak a másik irányba folyik ára, kikapcsolt állapotban lesz mindegyik. Ha valamelyik LED-et világítani szeretnénk, akkor a megfelelő anódra kell a magas és az anódra az alacsony értéket írni. De ügyelnünk kell arra, hogy utána ki is kapcsoljuk, és így vibráltassuk, és legalább ötször olyan hosszú ideig legyen kikapcsolt állapotban, mint bekapcsolva, különben kiég a LED. A valóságban végigpásztázunk az összes képponton, és felvillantjuk majd rögtön le is kapcsoljuk azt, amelyiket azt szeretnénk, hogy világítson, és a nagyon gyors vibrálást látja a szemünk folyamatos világításnak. Amint tehát látható, elég nehézkes, mivel 16 lábat kell bekötni, ráadásul a főprogramunkat is elveszi. A valóságban viszont még rosszabb a helyzet…

Először a kódot adom meg, mivel ha egy korábbi program szemete az egyik, anódként használt lábra állandó magas, egy katódként használt lábra pedig állandó alacsony értéket "felejtett", akkor az tönkretenné az egyik LED-et.

int cols[8] = {6, 11, 10, 3, A1, 4, 8, 9};
int rows[8] = {2, 7, A3, 5, 13, A2, 12, A0}; 
int leds[8][8] = {
  {1,1,1,1,1,1,1,1},
  {1,0,0,0,0,0,0,0},
  {1,0,1,1,1,1,1,1},
  {1,0,1,0,0,0,0,1},
  {1,0,1,0,0,1,0,1},
  {1,0,1,1,1,1,0,1},
  {1,0,0,0,0,0,0,1},
  {1,1,1,1,1,1,1,1}
};
 
void setup() {
  for (int i = 2; i <= 13; i++) {
    pinMode(i, OUTPUT);
  }
  pinMode(A0, OUTPUT);
  pinMode(A1, OUTPUT);
  pinMode(A2, OUTPUT);
  pinMode(A3, OUTPUT);
  for (int i = 0; i < 8; i++) {
    digitalWrite(cols[i], HIGH);
    digitalWrite(rows[i], LOW);
  }
}
 
void loop() {
  for (int row = 0; row < 8; row++) {
    for (int col = 0; col < 8; col++) {
      if (leds[row][col] == 1) {
        digitalWrite(rows[row], HIGH);
        digitalWrite(cols[col], LOW);
      }
      digitalWrite(rows[row], LOW);
      digitalWrite(cols[col], HIGH);
    }
  }
}

Látható, hogy a kód folyamatosan "szaladgál" körbe-körbe, és ha kell, felvillantja a LED-et. A kód kirajzolja a megadott spirált.

Most lássuk a bekötést! Ez szintén nem egyszerű, több okból kifolyólag sem. Az egyik: sem sorban szerepelnek a sorok és oszlopok, hanem össze-vissza. A következő a lábak jelentése (S: sor, O: oszlop):

  • Felső sor: O8, O7, S2, O1, S4, O6, O4, S1
  • Alsó sor: S5, S7, O2, O3, S8, O5, S6, S3

Igyekeztem úgy kialakítani a bekötést, hogy ami egymás mellett van a mátrix LED-en, az lehetőleg egymás mellé kerüljön az Arduino-n is. A bekötés:

  • Mátrix felső sor 1 -> Arduino 9
  • Mátrix felső sor 2 -> Arduino 8
  • Mátrix felső sor 3 -> Arduino 7
  • Mátrix felső sor 4 -> Arduino 6
  • Mátrix felső sor 5 -> Arduino 5
  • Mátrix felső sor 6 -> Arduino 4
  • Mátrix felső sor 7 -> Arduino 3
  • Mátrix felső sor 8 -> Arduino 2
  • Mátrix alsó sor 1 -> Arduino 13
  • Mátrix alsó sor 2 -> Arduino 12
  • Mátrix alsó sor 3 -> Arduino 11
  • Mátrix alsó sor 4 -> Arduino 10
  • Mátrix alsó sor 5 -> Arduino A0
  • Mátrix alsó sor 6 -> Arduino A1
  • Mátrix alsó sor 7 -> Arduino A2
  • Mátrix alsó sor 8 -> Arduino A3

Arról, hogy a kód megfelelően kezelje ezt a bekötést, a rows és cols tömbök gondoskodnak.

Teszt: most már bedughatjuk ismét az Arduino-t, és ha mindent jól csináltunk, akkor egy spirált kell látnunk. A valóságban viszont az a valószínűbb, hogy néhány pixel világít ugyan, de ezek nem alakítanak ki egy jó látható ábrát. Ennek az az oka, hogy elég könnyen alakul ki kontakthiba: érdemes finoman összeszorítani két ujjunkkal a bekötéseket.

Végeredményben tehát felhasználtunk 16 lábat, 16 kábelt és a loop() függvényt ahhoz, hogy egy statikus dolgot kirajzoljunk, ami kontakthiba miatt csak nehézkesen sikerül. A gyakorlatban tehát ez ebben a formában használhatatlan, persze erre is van megoldás…

32x8-as mátrix kijelző

Már a 8x8-as bekötése és programozása is nehézkes volt, és az ott megtapasztaltak szerint lehetetlenség e fölé menni, pedig lehetséges! A MAX7219 jelű áramkör segítségével SPI technikával egymás után kötött 8x8-as LED mátrixokat is meg tudunk címezni. A példában 4 ilyen kijelző van egymás után kötve. (Egyébként nem volt egyszerű működő kódot találni, végül ez segített: https://leetsacademy.blogspot.com/2017/02/32x8-led-dot-matrix-with-max7219-using.html.)

Bekötés:

  • 32x8 VCC -> Arduino 5V
  • 32x8 GND -> Arduino GND
  • 32x8 DIN -> Arduino 11 (MOSI)
  • 32x8 CS -> Arduino 10
  • 32x8 CLK -> Arduino 13 (SCK)

Kód: először az alábbi könyvtárakat telepítsük fel:

A Max72xxPanel valójában a GFX könyvtárra épül, és örökli annak bizonyos függvényeit. Miután feltelepítettük a fenti könyvtárakat, a következő kódot töltsük fel az Arduino-ra:

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
 
int pinCS = 10;
int numberOfHorizontalDisplays = 1;
int numberOfVerticalDisplays = 4;
 
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);
 
void setup() {
  matrix.setIntensity(2);
  matrix.setRotation(1);
  printText("Szia!");
}
 
void printText(String text) {
  matrix.fillScreen(LOW);
  int x = 0;
  for (int i = 0; i < text.length(); i++) {
    matrix.drawChar(x, 0, text[i], HIGH, LOW, 1);
    x += 6;
  }
  matrix.write();
}
 
void loop() {}

Ha mindent jól csináltunk, akkor kiírja, hogy "Szia!". Némi magyarázat:

  • A setIntensity függvénnyel a képpontok fényességét adhatjuk meg, mely 0..15 közötti értékeket vehet fel.
  • setRotation: ezzel tudjuk a megfelelő irányba forgatni. Az 1-es paraméter azt jelenti, hogy a baloldali lesz az Arduino-hoz közel, és úgy fogja vízszintesen írni. (Az alapértelmezett nem megfelelő.)
  • fillScreen(LOW): törli a képernyőt
  • drawChar: szöveget közvetlenül nem tudunk kiírni, csak karaktereket egyesével, ezzel a függvénnyel. Ezt a GFX-től örökli.
  • write: a kiíró, kirajzoló függvények csak egy puffert módosítanak, és ez küldi ki ténylegesen a mátrixra az ábrát.

Futószöveget is ki tudunk írni, ld. pl. a beépített Ticker nevű példát. Viszont az valószínűleg egy más felépítésű eszközre készülhetett, és némi változtatást végre kell hajtani, amit sajnos helyben nem tudunk megtenni, máshova le kell menteni. Szükséges változtatások:

  • A sorok és oszlopok számét fel kell cserélni (numberOfHorizontalDisplays és numberOfVerticalDisplays az elején).
  • A setup() függvénybe be kell írni a következő sort: matrix.setRotation(1);.
  • Szerintem az intenzitást érdemes lejjebb venni.

Kétsoros LCD kijelző

A termék kódja LCD1602. Kétféle színben lehet vásárolni: kék és zöld, ill. kétféle formában: van-e rajta I2C adapter vagy nincs. Kereső kifejezések vásárláshoz (egyik opció kell a három közül):

  • LCD 1602 with I2C (javaslom ezt; nem kell forrasztani)
  • LCD 1602 + potentiometer (lehetséges, hogy forrasztani is kell)
  • LCD 1602 + I2C adapter for LCD 1602 (biztosan kell forrasztani)

I2C adapter nélkül

Ehhez szükség van feszültségosztóra (angolul potentiometer). Kapcsolás:

  • LCD VSS -> Arduino GND
  • LCD VDD -> Arduino 5V
  • LCD V0 -> Feszültségosztó közép
  • LCD RS -> Arduino D12
  • LCD RW -> Arduino GND
  • LCD E -> Arduino D11
  • LCD D4 -> Arduino D5
  • LCD D5 -> Arduino D4
  • LCD D6 -> Arduino D3
  • LCD D7 -> Arduino D2
  • LCD A -> Arduino 5V
  • LCD K -> Arduino GND
  • Feszültségosztó 5V (felülnézetből jobb oldali) -> Arduino 5V
  • Feszültségosztó GND (felülnézetből bal oldali) -> Arduino GND

A beépített LiquidCrystal könyvtár megfelelő. Ezt a kódot töltsük fel:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
 
void setup() {
  lcd.begin(16, 2);
  lcd.print("hello, world!");
}
 
void loop() {
  lcd.setCursor(0, 1);
  lcd.print(millis()/1000);
}

A feszültségosztóval állítsuk be a fényerősséget, hogy olvasható legyen a szöveg. Ha mindent jól csináltunk, akkor az első sorban a hello, world! szöveg látható, míg alatta az indulás óta eltelt másodpercek számát.

I2C adapterrel

Látható, hogy az alap LCD kijelző bekötése igen összetett: 14 kábel kell hozzá, kell külön feszültségosztó, és szerelőlap (breadboard) vagy bővítőlap (shield) nélkül nem is megoldható. Hosszú ideig tart összeszerelni, könnyű elrontani, és szinte alig marad szabad láb. E probléma kiküszöbölésére találták ki az I2C adaptert, amit külön is megvásárolhatunk, de érdemes eleve olyan LCD kijelzőt vásárolni, amely ezt már tartalmazza.

A bekötés jóval egyszerűbb, a táp és föld mellett mindössze két kábelre van szükség, az SDA-ra és az SCL-re:

  • LCD GND -> Arduino GND
  • LCD VCC -> Arduino 5V
  • LCD SDA -> Arduino A4 (Arduino SDA)
  • LCD SCL -> Arduino A5 (Arduino SCL)

Hátránya, hogy az Arduino Uno esetén csak az A4-A5 párosra lehet kötni, így azt másra nem lehet használni.

A kód, ami a fenti műveletet hajtja végre:

#include <LiquidCrystal_I2C.h>
 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
 
void setup() {
  lcd.begin(16, 2);
  lcd.print("hello, world!");
}
 
void loop() {
  lcd.setCursor(0, 1);
  lcd.print(millis()/1000);
}

Ehhez viszont szükség van egy külső könyvtárra: Newliquidcrystal_1.3.5.zip, mely innen letölthető: https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/. Windows alatt csomagoljuk ki ide: c:\Users\[user]\Documents\Arduino\libraries\.

A fényerősséget az I2C adapteren található kis kék tokban levő csavarral tudjuk beállítani.

Utasítások

Az LCD utasításkészlet referenciája itt található: https://www.arduino.cc/en/Reference/LiquidCrystal. A legfontosabbak:

  • lcd.begin(16, 2)
  • lcd.setCursor(oszlop, sor) (az első sor ill. oszlop sorszáma 0)
  • lcd.clear()
  • lcd.print("…")

Az mBlock-hoz létezik az I2C interfésszel ellátott 1602-es LCD-hez kiegészítő, így blokk programozással is programozható.

Grafikus LCD kijelző

Ez a kijelző többnyire nem része a kezdőcsomagoknak. Pár dollárért megvásárolható; a keresőkulcs ez: SSD1306 Oled I2C LCD display. A most bemutatott változat 128x64 képpont méretű. A leírásnak ez a szakasza ez alapján készült: https://startingelectronics.org/tutorials/arduino/modules/OLED-128x64-I2C-display/, de nem pontosan követi azt.

Szükséges beállítások a fejlesztői környezeten.

Telepíteni kell a következő két Adafruit könyvtárat:

  • SSD1306
  • GFX

Mindkettő telepíthető a Library Manager (Tools -> Manage Libraries…) segítségével. Telepíts után viszont módosítani kell ezt: c:\Users\[user]\Documents\Arduino\libraries\Adafruit_SSD1306\Adafruit_SSD1306.h. Eredeti tartalom:

#define SSD1306_128_64 ///< DEPRECTAED: old way to specify 128x64 screen
//#define SSD1306_128_32   ///< DEPRECATED: old way to specify 128x32 screen

Módosított tartalom:

//#define SSD1306_128_64 ///< DEPRECTAED: old way to specify 128x64 screen
#define SSD1306_128_32   ///< DEPRECATED: old way to specify 128x32 screen

Bekötés

A bekötés a szokásos I2C bekötés:

  • Oled GND -> Arduino GND
  • Oled VDD -> Arduino 5V
  • Oled SCK -> Arduino A5 (Arduino SCL)
  • Oled SDA -> Arduino A4 (Arduino SDA)

Kód

A következő példaprogram demó formájában bemutatja a kijelzési lehetőségeket: File -> Examples -> Adafruit SSD1306 -> ssd1306_128x64_i2c. Viszont ezen is kell egyetlen karaktert módosítani ahhoz, hogy jól működön. Eredeti sor:

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { // Address 0x3D for 128x64

Módosított sor:

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64

A helló világot kiíró és egy vonalat rajzoló kód (a kírás angolul, mert egyelőre nem jól kezeli a magyar karaktereket):

#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
 
#define OLED_ADDR 0x3C
 
Adafruit_SSD1306 display(-1);
 
void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
  display.clearDisplay();
  display.display();
 
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(27, 30);
  display.print("Hello, world!");
  display.drawLine(5, 58, 123, 58, WHITE);
  display.display();
}
 
void loop() {}

A működése: megadjuk a kirajzolandó dolgokat, majd a display() függvény rajzolja ki azokat. Műveletek:

  • print("text"): szöveget ír ki. Vonatkozó függvények: setTextSize(textSize), setTextColor(color), setCursor(x, y).
  • write('c'): egyetlen karaktert ír ki
  • drawLine(x1, y1, x2, y2): vonalat rajzol
  • drawRect(x1, y1, x2, y2, color): téglalapot rajzol
  • drawRoundRect(x1, y1, x2, y2, r, color): lekerekített téglalapot rajzol
  • fillRect(x1, y1, x2, y2, color): kitöltött téglalapot rajzol
  • fillRoundRect(x1, y1, x2, y2, r, color): kitöltött lekerekített téglalapot rajzol
  • drawTriangle(x1, y1, x2, y2, x3, y3, color): háromszöget rajzol
  • fillTriangle(x1, y1, x2, y2, x3, y3, color): kitöltött háromszöget rajzol
  • drawCircle(x, y, r): körvonalat rajzol
  • fillCircle(x, y, r, color): kitöltött kört rajzol
  • drawBitmap(x, y, bmp, width, height): képet rajzol ki
  • startscrollright(start, stop), startscrollleft(start, stop), startscrolldiagonalright(start, stop), startscrolldiagonalleft(start, stop), stopscroll(): képernyő tartalmának mozgatása.
  • boolean getPixel(x, y): adott képpont értékét adja vissza.

Jelölések:

  • color: szín; lehetséges értékek: BLACK, WHITE, INVERSE
  • x: x-koordináta; lehetséges értékek: 0-127
  • y: y-koordináta; lehetséges értékek: 0-63
  • r: sugár
  • width: szélesség
  • height: magasság
  • bmp: a kirajzolandó kép; típusa unsigned char []; ld. a példát.

Hangot kiadó alkalmazások

Ide soroljuk az aktív és passzív berregőt. A passzív berregő tipikusan része a csomagoknak, de a 37 szenzor gyűjteményben is helyet kapott. Aktív berregőt találunk a 37 szenzoros csomagban. Az aktív berregőnek elég áramot adni, és sípol, még a passzív berregőt ha áramra kötjük, semmi sem csinál, azt le kell programozni. Ránézésre ugyanúgy néznek ki; a 37 szenzoros csomagban az aktívra rá van ragasztva egy lapka, valamint a + jel közelebb van a lábakhoz.

Aktív berregő

Ez az egyszerűbb: csak adjunk neki az S-re (ill. ha külön van: a pozitív, azaz hosszabb lábára) 5 Voltot, a másikat pedig kössük a GND-ra. Elég hangosan sípol.

Passzív berregő

Nem nyilvánvaló, hogy hogyan szólaltassuk meg, különösen hogyan játsszunk le dallamot, mert ha egyszerűen csak HIGH értéket kötünk rá, akkor nem történik semmi. A megoldáshoz elő kell vennünk a matematikai és fizikai tudásunkat, így ez egy gyakorlati fizikaórának is kiválóan alkalmas!

A hang valójában hullám, az emberi fül számára hallható tartomány 20 Hz-től 20.000 Hz-ig terjed. Néhány hang frekvenciája az alábbi táblázatban található (keressünk rá a neten arra a kifejezésre, hogy frequency of notes):

Hangjegy Frekvencia [Hz] Félperiódus (μs)
c 261 1915
d 294 1700
e 329 1519
f 349 1432
g 392 1275
a 440 1136
h 493 1014
C 523 956

Az, hogy pl. az 'a' hang 440 Hz, fizikailag azt jelenti, hogy másodpercenként 440 teljes periódusú sinus hullám megy végbe. Egy periódus hossza 1/440 s ≈ 0.0022727 s = 2272,7 μs. A félperiódus ennek a fele, kerekítve 1136 μs. A képlet tehát: félperiódus=1/(2*frekvencia). Minden hangjegy kerekített félperiódusát szintén tartalmazza a táblázat.

ideális esetben olyan sinus hullámokat kellene generálni, melynek félperiódusa a táblázatban megadott érték. Az Arduino esetén erre nincs mód, itt csak közelíteni tudjuk, mégpedig úgy, hogy egy félperiódusnyi ideig ad le az adott láb HIGH értéket (5 Voltot), majd ugyanennyi ideig LOW értéket (0 Voltot). Ez nem sinus, hanem négyzetes jel, ráadásul az y-tengely is el van tolva 2,5 Voltnyit, így a hangás nem lesz tökéletes, de elképzelésünk ennek alapján is lesz arról, hogy a profibb készülékek hogyan generálják a hangokat.

A kódban lementjük egy tömbbe a kiszámolt értékeket. Meg kell határozni a ritmusát; a 0,3 másodperc (300 ms) egy nyolcadhangra megfelelő választás lehet. Majd lejátszunk egy dalocskát: a Boci-boci tarkát. Itt először a 'c' hangnak kell szólnia 300 ms (300.000 μs) ideig; ebbe kb. 78 olyan periódust jelent, melyben 1915 μs ideig kap HIGH és ugyanennyi ideig LOW értéket. Utána jön az 'e' hang, ugyancsak 300 ms ideig, kb. 98 periódussal. A dallam ötödik hangja ('g') egy negyedhangnyi ideig kell, hogy szóljon, ami 600 ms. Két hang között rövid szünetet kell tartanunk. A programot ezeket figyelembe véve alkotjuk meg.

int speakerPin = 12;
char notes[] = "ceceggceceggChagfagfedcc "; // a space represents a rest
int beats[] = {1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 4};
int tempo = 300; // ms = 1 beat
 
void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speakerPin, LOW);
    delayMicroseconds(tone);
  }
}
 
char names[] = {'c' , 'd' , 'e' , 'f' , 'g' , 'a' , 'h' , 'C'} ;
int tones[] = {1915, 1700, 1519, 1432, 1275, 1136, 1014, 956};
 
void playNote(char note, int beat) {
  // play the tone corresponding to the note name
  for (int i = 0; i < 8; i++) {
    if (names[i] == note) {
      playTone(tones[i], tempo * beat);
    }
  }
}
 
void setup() {
  pinMode(speakerPin, OUTPUT);
}
 
void loop() {
  int length = sizeof(notes) / sizeof(notes[0]);
  for (int i = 0; i < length; i++) {
    if (notes[i] == ' ') {
      delay(beats[i] * tempo); // rest
    } else {
      playNote(notes[i], beats[i]);
    }
    delay(tempo / 2); // pause between notes
  }
}

A bekötés igazából egyszerűbb, mint látszik, ugyanis csak két lábat kell bekötni:

  • Berregő - -> Arduino GND
  • Berregő S -> Arduino 12

(A lábakat szándékosan választottam úgy, hogy a háromlábú berregőt be lehessen szúrni a GND-13-12 hármasba, így nem szükséges külön kábelt használni. Ez egy olyan hack, melyet ebben a speciális esetben ki tudunk használni. Nyilván nem működik a dolog, ha szükségünk lenne a 13-as láb kivezetésére.)

Ha mindent jól csináltunk, a berregő lejátssza a Boci-boci tarkát. A fent leírtak miatt a hangok nem teljesen tiszták, a tempó sem tökéletes (ugyanannyi szünetet hagy ki a nyolcad és a negyedhangok között is), viszont mindezen hiányosságok ellenére ezzel a projekttel megismerkedtünk a hanggenerálás rejtelmeivel.

Érdemes oszcilloszkóppal megvizsgálni a kimenő jelent, hogy tényleg az, aminek gondoljuk: nagyon szépen kirajzolódik, hogy igen.

Az Arduino tone() függvényét is használhatjuk közvetlenül, melyről itt találunk egy leírást: https://www.arduino.cc/en/Tutorial/toneMelody.

Meghajtás

Ide azok az eszközök kerülnek, melyek megfelelő jelre fizikailag is megmozdulnak.

Szervó motor

A szervó motor segítségével olyan projekteket lehet készíteni, ahol finom motorikai mozgásokra van szükség. Például a rajzoló óra három ilyen motort tartalmaz (kettő a pozíció beállításhoz, egy az emeléshez), a robotkar négyet. A kódjele SG90, és többnyire része az alapkészletnek. Általában 180°-nyit lehet vele elmozdulni.

Bekötés: a fenti kódjelű szervó motor esetén a kábelek össze vannak ragasztva. Nem érdemes őket széttépni, hanem ha használunk bővítőlapot, akkor simán rá tudjuk szúrni. (Anélkül egyébként is szükségünk van segéd apa-apa kábelekre, így ez esetben sem szükséges szétbontanunk.) Helyezzük a sárga kábelt valamelyik valamelyik lábra (pl. 9), így a piros a V-re (feszültség), a barna pedig a G-re (föld) kerül.

Kód:

#include <Servo.h>
 
Servo servo;
 
void setup() {
  servo.attach(9);
}
 
void loop() {
  for (int i = 0; i <= 180; i++) {
    servo.write(i);
    delay(10);
  }
  for (int i = 180; i >= 0; i--) {
    servo.write(i);
    delay(10);
  }
}

Látható a kódban, hogy egy külső, egyébként alapból feltelepülő könyvtárat használunk, a Servo-t, melynek dokumentációs itt található: https://www.arduino.cc/en/reference/servo. A leglényegesebb utasítása a write(), amelynek segítségével 0 és 180 fok közötti értékre tudjuk állítani a szervó motort. A fenti példában egyik-másik irányba fordul el a motor feje. Ennél precízebb módon tudjuk vezérelni a kissé megtévesztő nevű writeMicroseconds() függvény segítségével, amelynél 1000-2000 közötti értékeket tudunk megadni (1000 = 0°, 1500 = 90°, 2000 = 180°). Viszont ez már gyártófüggő; a specifikáció is megemlíti, hogy egyes gyártók a még tágabb, 700-2300 intervallumot használják.

A gyakorlatban 3D nyomtatóval nyomtatott vagy lézerrel kivágott eszközöket lehet rákapcsolni.

Léptető motor

A léptető motor neve valószínűleg megtévesztő: ideális működés során ugyanis nem lép, hanem körbe-körbe forog. Ami miatt léptetőnek hívják az az, hogy a köröket kis lépésekben teszi meg, melynek egyrészt itt nem részletezett technikai okai vannak, másrészt olyan értelemben gyakorlati, hogy az Arduino kód megáll forgás során. Tehát érdemes kis lépésekben forgatni a motort, és közben lehetőséget biztosítani más kódrészletek lefutására is.

A léptető motor specifikációja itt található: https://www.arduino.cc/en/tutorial/stepperSpeedControl. A következő oldal segített valamelyest megérteni a működését: https://circuitdigest.com/microcontroller-projects/arduino-stepper-motor-control-tutorial. A motor kódja ez: 28BYJ-48. Ha külön vásároljuk, fontos, hogy úgy vegyük, hogy legyen hozzá ULN2003 kódjelű meghajtó modul.

Kapcsolás: a motor maga a meghajtóra van kötve, és a meghajtót kötjük az Arduino-ra.

  • ULN2003 IN1 -> Arduino 8
  • ULN2003 IN2 -> Arduino 9
  • ULN2003 IN3 -> Arduino 10
  • ULN2003 IN4 -> Arduino 11
  • ULN2003 - (ez külön van, egy jumper mellett) -> Arduino GND
  • ULN2003 + -> Arduino 5V

Kód:

#include <Stepper.h>
 
#define STEPS 128
 
Stepper stepper(STEPS, 8, 10, 9, 11);
 
void setup() {
    stepper.setSpeed(200);
}
 
void loop() {
    stepper.step(4);
}

A szükséges könyvtár automatikusan feltelepül az Arduino IDE-vel.

(Megjegyzés: egyelőre inkább érdekesnek találtam, mint hasznosnak, logikusan gondolkodva viszont ilyet, vagy ehhez hasonlót kell használni robotok mozgatásához. Ahhoz viszont számomra gyanúsan lassan forgott, akárhogy is próbálkoztam, és a programozása is nehézkes.)

Robot motor

Relé

Az Arduino alapvetően kis, pár voltos feszültségekkel dolgozik. A relé (relay module) segítéségével tehetjük alkalmassá arra, hogy nagyobb feszültségű, akár a fali konnektorba dugott eszközöket vezéreljünk vele. Az eszköz egyébként hasznossága ellenére rendkívül egyszerű, olcsó, és tipikusan többcsatornás reléket tudunk vásárolni. Az alapkészleteknek is néha része, és tartalmazza a 37 szenzorból álló gyűjtemény.

Bekötés. Az Arduino-ra kötés a 37 szenzor gyűjtemény kialakítása alapján úgy, hogy a felirat vízszintesen látszódik), jobb oldalon:

  • Relé felső -> Arduino GND
  • Relé középső -> Arduino 5V
  • Relé alsó -> Arduino 3

Kód:

int relayPin = 3; 
 
void setup() { 
  pinMode(relayPin, OUTPUT);
}
 
void loop() { 
  digitalWrite(relayPin, HIGH);
  delay(2000); 
  digitalWrite(relayPin, LOW);
  delay(2000); 
}

Teszt: ha a fenti kódot feltöltjük, akkor kattogás hallatszik; ez jelzi a be- ill. kikapcsolást. A relé másik felé csavarhúzóval ráerősíthető kimenetek vannak, melyek jelentése a következő:

  • Felső: normál kimenet
  • Középső: bemenet
  • Alsó: inverz kimenet

Normál működéshez a 2 vagy 3 kábel fázisát kell átvágni, majd az egyik végét a középsőre, a másikat a felsőre kötni. Ha nem szeretnénk a fali feszültséggel kísérletezni, alakítsuk ki a következő áramkört, független áramforrással, pl. egy MB102 típusú áramellőtó lappal.

  • 5 Volt -> LED -> 220 Ω előtét ellenállás -> Relé középső (csavarhúzóval)
  • 0 Volt -> relé felső (csavarhúzóval)

Azt fogjuk tapasztalni, hogy a LED villogni fog. Viszont ha van elég bátorságunk, akkor kialakíthatunk egy olyan áramkört is, melyben egy normál izzó villog hasonlóan.

Bemenetek

Egyszerű bemenetek

Nyomógomb

A nyomógomb felfogható bináris szenzorként, így az ott ismertetett kód használható. A bekötése viszont meglepően nem triviális, és nem ússzuk meg a próbapanelt. A nyomógomb belseje úgy van kialakítva, hogy a közeli 2-2 láb össze van kötve. Egyébként teljesen szimmetrikus, mindegy, hogy milyen irányban kötjük be. Az egyik fele legyen A, a másik bele B; ez esetben a kapcsolás:

  • Nyomógomb A -> Arduino 5V
  • Nyomógomb A -> 10k ellenállás -> Arduino GND
  • Nyomógomb B -> Arduino 2

A viszonylag nagyobb ellenállásra annak érdekében van szükség, hogy eltörpüljön a feszültségesés a nyomógombon, tehát alatta is lényegében 5V körüli értékként, azaz logikai magasként érzékelje. A bekötésnél nekem problémáim voltak azzal, ha a szembe levő lábat választottam; egyfajta "inverz kontakthibát" tapasztaltam: néha akkor is áramot jelzett, amikor nem volt lenyomva. Néha elég volt csak éppen hozzáérni. Lehet, hogy a próbapanel volt hibás, mindenesetre átlós lábakat használva rendesen működött.

Teszt: nyomjuk meg a nyomógombot: a 13-as LED bekapcsolódik; engedjük el: a 13-as LED elalszik.

Potenciométer

A potenciométer (gyakori rövidítéssel egyszerűen csak pot, de előfordult, hogy egyszerűen csak analóg olvasóként hivatkoznak rá) lényegében egy változtatható ellenállás, amely csavarással a feszültséget osztja. Mivel 5 Volt feszültséget kap az Arduino-tól, a kimeneten egy 0 és 5 közötti érték jelenik meg. Ezt célszerű egy analóg lábra kötni, ahol egy 0 és 1023 közötti értéket kapunk.

A potenciométer tipikusan része a kezdőkészleteknek, de a 37 szenzor csomag is tartalmazza.

A legegyszerűbb kapcsolásban kiírjuk a a beolvasott értéket a soros monitorra.

  • Pot bal -> Arduino GND
  • Pot közép -> Arduino A0
  • Pot jobb -> -> Arduino 5V

A kódban tizedmásodpercenként kiírjuk az éppen beolvasott értéket:

void setup() {
  Serial.begin(9600);
}
 
void loop() {
  Serial.println(analogRead(A0));
  delay(100);
}

A soros monitor mellett érdemes a soros rajzolót (Serial Plotter) is megnéznünk, hiszen igen látványos eredményt tudunk létrehozni.

Joystick

A joystick egy két dimenziós beviteli eszköz, gyakorlatilag két potenciométer, mely része a 37 szenzor készletnek. Bekötés:

  • Joystick GND -> Arduino GND
  • Joystick +5V -> Arduino 5V
  • Joystick VRx -> Arduino A0
  • Joystick VRy -> Arduino A1
  • Joystick SW -> Arduino 2

Kód:

int joystickX = A0;
int joystickY = A1;
int joystickButton = 2;
 
void setup() {
  pinMode(joystickX, INPUT);
  pinMode(joystickY, INPUT);
  pinMode(joystickButton, INPUT);
  Serial.begin(9600);
}
 
void loop () {
  Serial.print(analogRead(joystickX));
  Serial.print(", ");
  Serial.print(analogRead (joystickY));
  Serial.print(", ");
  Serial.println(digitalRead(joystickButton));
  delay(100);
}

Teszt: indítsuk el a Serial Monitort, és mozgassuk a joystick-ot. Mindkét dimenzióban nyugalmi helyzetben 511 körüli értéket kapunk, és a 0-1023 intervallumban olvasunk be értékeket. (Megjegyzés: az én példányomnál nem működött a nyomógomb; a fenti kóddal elvileg működnie kell.)

Érzékelők

Ide azokat sorolom, amelyek a külső környezet valamilyen tulajdonságát számszerűsítik, és az eredmény közvetlenül, vagy nagyon minimális átalakítással felhasználható. Ilyen eszköz pl. az egyszerű hőérzékelő.

LM35 hőmérséklet érzékelő

Ez talán a legegyszerűbb és legolcsóbb hőmérséklet érzékelő, mely tipikusan része a kezdőkészleteknek. Kapcsolás:

  • LM35 bal -> Arduino 5V
  • LM35 közép -> Arduino A0
  • LM35 jobb -> Arduino GND

A kódban egy trükköt alkalmazunk a pontosabb értékért. Tudnunk kell, hogy 10 mV változás jelent 1°C változást, 0-ról indulva. 1 Volt tehát 100 °C-ot jelentene, melynek mérésére az LM35 már nem is képes, így egy nagyon szűk tartományban adna csak olyan értékeket, melyek pontosabbak is lehetnének. Az analogReference(INTERNAL) beállítással a 0-5 Volt intervallum lecsökken 0-1,1 Voltra, így jobban lehet finomhangolni a mért értéket. A soros monitorra kerül az eredmény.

void setup() {
  Serial.begin(9600);
  pinMode(A0, INPUT);
  analogReference(INTERNAL);
}
 
void loop() {
  int sensorValue = analogRead(A0);
  Serial.print("Sensor value: ");
  Serial.println(sensorValue);
  float voltage = sensorValue * 1.1 / 1024; // 1.1V because INTERNAL analog reverence; it would be 5V by default
  Serial.print("Voltage: ");
  Serial.println(voltage);
  float temperatureC = voltage * 100; // temperature (°C) = 0°C (no offset) + 1°C/10mV
  Serial.print(temperatureC);
  Serial.println(" °C");
  delay(1000);
}

Teszt: próbáljuk meg változtatni a hőmérsékletet, és figyeljük a soros monitort. Figyeljük meg, hogy szoba hőmérsékleten 0 mért feszültség 0,20-0,25 Volt közötti, mégis, a szenzor érték 200 körüli; a pontosság ezáltal fél fok helyett tizedfok körüli.

Dallas 18B20 hőmérséklet érzékelő

Ennek a létjogosultságát leginkább az adja, hogy míg az LM35 érzékelőt csak analóg portra köthetjük, a DS18B20 érzékelőt akár digitális portra is, ráadásul a OneWire technológia segítségével akár 64-et is ráköthetünk, ugyanarra a lábra. (Viszont annak ellenére, hogy ez a OneWire technológia igen ígéretes, furcsamód csak ennél az eszköznél találkoztam vele, emiatt nincs felsorolva az általános technikák között.) Ez a hőmérséklet érzékelő egyébként része a 37 szenzor készletnek. Kinézete: 3 lába van, félbe vágott hengerre hasonlít (kb. mint egy tranzisztor), és DALLAS 18B20 felirat van rajta.

A megvalósítást ezen az oldalon találjuk: https://create.arduino.cc/projecthub/TheGadgetBoy/ds18b20-digital-temperature-sensor-and-arduino-9cc806.

Bekötés, ha a lábak jobb oldalon vannak:

  • DS18B20 felső -> Arduino 2
  • DS18B20 középső -> Arduino 5V
  • DS18B20 alsó -> Arduino GND

Ha önmagában használjuk, akkor a az adat és a feszültség közé egy 4,7 kΩ ellenállást is kell tenni, de ha a 37 szenzor csomagból vettük ki, akkor az már ezt tartalmazza.

A kód lefordításához szükségünk van két könyvtárra:

#include <OneWire.h>
#include <DallasTemperature.h>
 
#define ONE_WIRE_BUS 2 
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
 
void setup() {
  Serial.begin(9600);
  sensors.begin();
}
 
void loop() { 
  sensors.requestTemperatures(); 
  Serial.print("Celsius temperature: ");
  Serial.print(sensors.getTempCByIndex(0)); 
  Serial.print(" - Fahrenheit temperature: ");
  Serial.println(sensors.getTempFByIndex(0));
  delay(1000);
}

Figyeljük meg a függvény nevét: getTempCByIndex(0): több IC-t tudunk ugyanarra a lábra dugni, és ez az elsőt jelenti.

Teszt: változtassuk a hőmérsékletet, miközben figyeljük a soros monitort.

DHT11 és DHT22 hőmérséklet és páratartalom érzékelő

A DHT11 kék, a DHT22 pedig fehér; ez utóbbi drágább, viszont nagyobb tartományban és nagyobb pontossággal mér. A DHT11 része a 37 szenzor csomagnak. Beállítása ugyanaz mindkét esetben.

Bekötés

DHT11:

  • DHT11 S -> Arduino 2
  • DHT11 középső -> Arduino 5V
  • DHT11 - -> Arduino GND

DHT22:

  • DHT11 + -> Arduino 5V
  • DHT11 out -> Arduino 2
  • DHT11 - -> Arduino GND

Látható tehát, hogy egyetlen általános digitális lábbal megoldható a beolvasás.

Kód: szükség van megfelelő library-re. Számos DHT könyvtár létezik. A példában az Adafruit DHT Sensor Library-jét használjuk, mely mindkét típusra alkalmas. Ehhez fel kell telepítenünk az alábbiakat:

  • Adafruit Unified Sensor by Adafruit
  • DHT sensor library by Adafruit
#include <DHT.h>
 
#define DHTPIN 2
#define DHTTYPE DHT11
 
DHT dht(DHTPIN, DHTTYPE);
 
void setup() {
  Serial.begin(9600);
  dht.begin();
}
 
void loop() {
  Serial.print("Humidity: ");
  Serial.print(dht.readHumidity());
  Serial.print("%  Temperature: ");
  Serial.print(dht.readTemperature());
  Serial.println("°C ");
 
  delay(1000);
}

DHT22 esetén írjuk át a kódban a DHT11-et DHT22-re; mást nem kell változtatni rajta.

Teszt: indítsuk el a Serial Monitort, és közben próbáljuk megváltoztatni a hőmérsékletet és a páratartalmat (pl. egy ráleheléssel).

Megjegyzés: a 37 szenzor csomagban található még két hőmérséklet érzékelő, az egyik kis fekete lapkára szerelve, a másik nagyobb piros lapkára. Igazából egyiket sem sikerült működésre bírnom. Viszont a DHT-ban ugyanolyan hőmérséklet érzékelő található.

BMP180 légnyomás- és hőmérséklet érzékelő

Ez az 1$ alatti alkatrész általában nem része a kezdőszetteknek, sem a 37 szenzor csomagnak, így ha használni szeretnénk, azt külön meg kell vásárolni. Vagy a típusra keressünk rá, vagy a barometric pressure sensor keresőkulcsra. Amit rendeltem, külön érkezett az érzékelő és a tű, azt rá kellett forrasztani. Az eszköz I2C-vel van ellátva, ennek megfelelő a bekötés ill. a kód is.

Bekötés:

  • BMP180 VIN -> Arduino 3.3V (fontos, hogy ne az 5V-ra kössük!)
  • BMP180 GND -> Arduino GND
  • BMP180 SCL -> Ardunio A5
  • BMP180 SDA -> Arduino A4

Kód: a használatához telepítenünk kell egy BMP180 könyvtárat. Ha rákeresünk erre, többet is találunk; én az Adafruit BMP085 Library-t telepítettem; ugyanaz kell ugyanis a BMP085 típusnak is, mint a BMP180-nak. Egyetlen példaprogram van csak, melynek neve BMP085test. Ez ne riasszon el bennünket, működik BMP180-nal is. A könyvtár tartalmaz magasság "mérést" is, bár az inkább számíts int mérés, és nem jól működik. Az eszköz valójában két dolgot mér: a légnyomást és a hőmérsékletet. A példaprogramból készített egyszerűsített kód, mely a légnyomást írja ki úgy, hogy azt a serial plotterrel is meg tudjuk jeleníteni, a következő:

#include <Wire.h>
#include <Adafruit_BMP085.h>

Adafruit_BMP085 bmp;

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

void loop() {
    //Serial.println(bmp.readTemperature());
    Serial.println(bmp.readPressure());
    delay(100);
}

Megjegyzésbe téve találjuk a hőmérséklet beolvasásának lehetőségét is, ami °C-ban adja vissza.

Teszt: a légnyomást sajnos nem tudjuk változtatni, legalábbis házi körülmények között nem. 101700 Pa körüli értéket mért, ami kb. 200 Pa-lal több mint a háztartásban megtalálható másik érzékelő által mért érték. A mért értéknek van pár Pa kilengése, így e két tényezőből valószínűsíthető, hogy működik. A hőmérséklet tesztelése jóval egyszerűbb, elég csak rálehelni, és máris felugrik a mért érték.

Vízérzékelő

A vízérzékelő általában nem része a kezdőcsomagnak, pedig igen hasznos, az analóg érzékelők "állatorvosi lova" is lehetne, egyszerű és látványos használata matt. Ha meg szeretnénk vásárolni, akkor a keresőkulcs ez: Arduino water sensor. Általában hosszúkás, téglalap alakú, és hosszú párhuzamos vonalak láthatóak rajta. (Az enyém piros, Funduino felirattal.)

Kapcsolás:

  • Vízérzékelő S -> Arduino A0
  • Vízérzékelő + -> Arduino 5V
  • Vízérzékelő - -> Arduino GND

Kód: az analóg érzékelőknél leírtak szerint.

Teszt: nyissuk meg a Serial Monitort, vagy ez esetben inkább a Serial Plottert. Ne felejtsük el beállítani a baud-ot (9600). Majd nedvesítsük be az érzékelőt, várjuk meg, hogy megszáradjon, nedvesítsük be ismét, töröljük le. Közben figyeljük meg, hogy a kirajzolt diagram látványosan követi azt, hogy mikor mennyire volt nedves. A száradás folyamata meglepően lineáris.

Figyeljük meg azt is, hogy ahogy az Arduino fogadja az adatot, úgy villog rajta az RX LED.

Talajnedvesség érzékelő

Angolul soil moisture sensor. Általában nem része a kezdőkészletnek, és a 37 szenzor készletnek sem, a 45 részesnek viszont igen. Ez is olcsó, száz forintos nagyságrendért megvásárolható. Klasszikus érzékelő típus, melynek van analóg és digitális kimenete is. Tipikusan két részből áll: egy villa alakú mérőből (amit a földbe kell szúrni) és egy erősítőből. A már bemutatott analóg tesztet használjuk, az ott bemutatott programot töltsük föl. Az érzékelőt és az erősítőt 2 kábellel kell összekötni, az erősítőt az Arduino-ra pedig értelemszerűen. Ne az 5V-ra, hanem a 3.3V-ra kössük rá. Érdemes a Serial Plotter kimenetét figyelni, száraz földdel kezdeni a tesztet, és közben öntsünk rá vizet. (Nálam a nagyobb nedvességtartalom alacsonyabb értékként jelent meg, de lehet, hogy amiatt, mert fordítva kötöttem be.)

Infravörös érzékelő távirányítóval

Szokásos rövidítése: IR. Általában része az alapkészleteknek, és a 37 szenzoros készletben is benne van. Kössük az érzékelőt a 2-es lábra, a VCC-t az 5V-ra, a mínuszt pedig a GND-re. (A natív esetben balról jobbra: OUT, GND, VCC.) Telepítsük az IRRemote csomagot, majd töltsük fel az alábbi kódot:

#include <IRremote.h>
 
IRrecv irrecv(2);
decode_results results;
 
void setup() {
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
}
 
void loop() {
  if (irrecv.decode(&results)) {
    switch (results.value) {
      case 0XFFFFFFFF: break;
      case 0XFF6897: Serial.println(0); break;
      case 0XFF30CF: Serial.println(1); break;
      case 0XFF18E7: Serial.println(2); break;
      case 0XFF7A85: Serial.println(3); break;
      case 0XFF10EF: Serial.println(4); break;
      case 0XFF38C7: Serial.println(5); break;
      case 0XFF5AA5: Serial.println(6); break;
      case 0XFF42BD: Serial.println(7); break;
      case 0XFF4AB5: Serial.println(8); break;
      case 0XFF52AD: Serial.println(9); break;
      default: Serial.println(results.value, HEX);
    }
    irrecv.resume();
  }
}

Figyeljük a soros monitort, miközben nyomkodjuk a számokat a távirányítón. (Megjegyzés: nálam nem működött a natív érzékelő, a 37 szenzor csomagban található érzékelő viszont működött.) Fontos a távirányító típusa, mely az Arduino készletekben általában NEC.

A témával kapcsolatban hasznos leírást találunk itt: http://www.circuitbasics.com/arduino-ir-remote-receiver-tutorial/.

Akadály érzékelő

Az akadály érzékelő (angolul: Obstacle Avoidance Sensor) a robotoknál lehet hasznos. Része a 37 szenzor csomagnak. Van rajta egy infravörös LED (átlátszó) és egy érzékelő (fekete); az érzékelő a visszaverődő infravörös fényt érzékeli. Érzékelési tartománya 2-40 cm, melyet csavarhúzóval tudunk beállítani.

Bekötés:

  • Akadály érzékelő 1 (GND) -> Arduino GND
  • Akadály érzékelő 2 -> Arduino 5V
  • Akadály érzékelő 3 -> Arduino 2
  • Akadály érzékelő 4 -> szabadon marad.

Kód: a digitális érzékelőnél bemutatott kód használható, de vegyük figyelembe, hogy a LOW érték jelenti az akadályt, a HIGH érték pedig azt, hogy nincs akadály.

Teszt: helyezzünk akadályt az érzékelő elé, és figyeljük a 13-as LED-et. (Megjegyzés: nálam bizonytalanul működött.)

Ultrahangos távolságérzékelő

Az infravörös távolságérzékelő bináris eredményt adott, mivel a fénysebesség túl nagy ahhoz, hogy pár centis távolságok esetén ki tudjuk mérni, az ultrahangos érzékelő (angolul: ultrasonic sensor) segítségével viszont már ki tudjuk számolni a távolságot, mivel a mérésnél mikroszekundumokról beszélünk, és számolhatunk hangsebességgel. Az érzékelő általában nem része az alapkészletnek, de érdemes megvásárolni, kb. 1$-os árért. A működési elve a következő: az egyik "szeme" (a bal oldali, VCC-hez közelebb eső része) ultrahangot bocsájt ki (ezt hívjuk triggernek), a másik pedig fogadja (ezt hívjuk echo-nak). Meg fogunk ismerkedni egy újabb utasítással: pulseIn() (https://www.arduino.cc/reference/en/language/functions/advanced-io/pulsein/), mely addig vár, míg a paraméterben átadott lábra a paraméterben átadott jel (HIGH vagy LOW) meg nem érkezik (így az elején megfelelően be kell állítani, ezt majd látni fogjuk a kódban), és eredményül az eltelt mikroszekundumok számát adja. A hangsebességből már számolható a távolság: s = v * t / 2 (az osztás kettővel amiatt kell, mert a hang kétszer teszi meg a távolságot: oda és vissza), itt v = 340 m/s, t pedig az eltelt idő mikroszekundumokban. Ha a távolságot cm-ben szeretnénk megkapni, akkor s = 34000 [cm / s] * 100000 * t [s] / 2 = t * 0.034 / 2.

Bekötés:

  • UH VCC -> Arduino 5V
  • UH Trig -> Arduino 9
  • UH Echo -> Arduino 10
  • UH GND -> Arduino GND

Kód:

const int trigPin = 9;
const int echoPin = 10;
 
void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Serial.begin(9600);
}
 
void loop() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
 
  long duration = pulseIn(echoPin, HIGH);
  int distance = duration * 0.034 / 2;
 
  Serial.print("Distance: ");
  Serial.println(distance);
 
  delay(1000);
}

Teszt: indítsuk el a Serial Monitort, és tartsunk akadályt az UH érzékelő elé, különböző távolságokban.

Nyomkövető

Angolul tracking sensor. Része a 37 szenzor gyűjteménynek. Kinézete: hosszúkás, az érzékelője pedig kék és fekete. A teszthez használjuk a megfelelő bekötést (VCC, OUT, GND) és töltsük fel a digitális érzékelőnél bemutatott programot. Ha fehér valamit teszünk az érzékelő elé, akkor kigyullad az érzékelőn a LED és elalszik a 13-as LED. Ha feketét, akkor fordítva.

Felhasználási terület: robotok esetén vonalkövetés.

Mozgásérzékelő

Angolul motion detector, gyakori rövidítéssel PIR, ami a pyroelectric sensor (vagy passive infrared) rövidítése. A rövidítés a működési elvével kapcsolatos, ugyanis valójában a hőmérséklet megváltozására érzékeny az eszköz. Az eszköz általában nem része a kezdőkészleteknek és a 37 szenzor gyűjteménynek sem, 1$ körüli összegért megvásárolható külön.

Bekötés felülnézetből:

  • PIR bal -> Arduino 5V
  • PIR közép -> Arduino 2
  • PIR jobb -> Arduino GND

Kód: a digitális érzékelő kódja használható.

Teszt: közelítsünk az érzékelőhöz ill. távolodjunk tőle, és közben figyeljük a lapra épített LED-et. (Megjegyzés: számomra meglepően érzéketlennek tűnik, összehasonlítva pl. az mBot-hoz vásárolható mozgásérzékelővel.)

Gázérzékelők

Az MQ jelű gázérzékelők (gas sensor) különböző gázfajtákat érzékelnek:

  • MQ-2: füst
  • MQ-3: alkohol
  • MQ-4: metán
  • MQ-5: metán
  • MQ-6: LPG
  • MQ-7: szénmonoxid
  • MQ-8: hidrogén
  • MQ-9: éghető gáz
  • MQ-135: levegő minőség

Általában nem része a kezdőcsomagnak, és a 37 szenzor készletnek sem, viszont darabja kb. 1 dollárért megvásárolható.

Bekötés és kód: az analóg érzékelőknél leírtak szerint. (A D0-t nem kell bekötni.) Teszt: helyezzünk a közelébe olyan gázt, amire érzékeny. Pl. én MQ-2-t vásároltam, és egy gyufát meggyújtva majd rögtön elfújva füst keletkezik, aminek hatására látványosan emelkedni kezdett a mért érték. Az eszköz egyébként nemcsak a füstre, hanem egyéb összetevőkre is érzékeny, és pl. egy ráleheléskor is magasabb értéket mutat. (Megjegyzés: az elején várni kellett pár percet, hogy elérjen egy nyugalmi minimumértéket.)

Fotoellenállás

Angolul photo resistor, de hívják még fotocellának (photocell) is. Egyszerű és látványos eszköz: segítségével azt tudjuk megállapítani, hogy mennyire van világos. Az alapkészletek többnyire tartalmazzák, és a 37 szenzor készletnek is része.

Bekötés: natív módon a következő kis áramkört kell kialakítani:

  • Arduino 5V -> 10 kΩ ellenállás -> itt két részre bontjuk
  • (1) -> fotoellenállás -> Arduino GND
  • (2) -> Arduino A0

A 37 szenzoros gyűjteményben ez az áramkör már meg van. Ott 3 lábat kell bekötni, az alábbi módon (sajnos elrontották a jelölést):

  • Fotoellenállás - -> Arduino A0
  • Fotoellenállás középső -> Arduino GND
  • Fotoellenállás S -> Arduino 5V

Kód: lehet ugyanaz, mint az analóg érzékelőknél bemutatott.

Teszt: indítsuk el a Serial Plottert, és világítsunk rá az érzékelőre, majd takarjuk el.

Érintés érzékelő

Angolul touch sensor. Kinézete: általában koncentrikus köröket tartalmaz, a 37 szenzor csomagban viszont piros, és a végén egy fémdrótba keretezett két lábú fekete érzékelő van. Kipróbáláshoz használjuk a digitális érzékelő bekötést; az A0-t nem kell bekötni. Érintsük ujjunkat az érzékelőhöz, miközben a 13-as LED-et figyeljük.

Ütközés érzékelő

Angolul collision sensor vagy crash sensor, néha (helytelenül) touch sensor. Általában nem része az alapkészletnek, valamint a 37 darabos szenzor gyűjteménynek semi, viszont filléres összegért megvásárolható. Gyakorlatilag úgy működik mint egy kapcsoló, viszont a kialakítása olyan, hogy leginkább egy robot ütközését érzékelje. A digitális érzékelőnél leírtakat alkalmazzuk, értelemszerű bekötéssel.

Elhajlás érzékelő

Angolul tilt sensor. Ránézésre hasonlít a polaritásos kondenzátorra. Az enyém fekete és az van ráírva az oldalára, hogy AETHOX. A nyomógombnál leírtak szerint kell bekötni, valamint a digitális értékelőknél ismertetett kód használható itt is. Teszt: tartsuk függőlegesen a (próbapanelre beleszúrt) érzékelőt: a 13-as LED nem világít; fordítsuk el úgy, hogy fekvő helyzetbe kerüljön: a 13-as LED világít.

A 37 szenzor készletben van egy ún. mágikus lámpa (angolul magic light cup) pár mely gyakorlatilag tartalmaz egy-egy elhajlás érzékelőt és egy-egy LED-et. Ha a LED-et PWM lábra kötjük és a párjának az elhajlásával vezéreljük, akkor olyan hatást lehet vele kelteni, mintha a fényt átöntenénk az egyikből a másikba. (Ez mondjuk inkább érdekes mint hasznos.)

Gyorsulásérzékelő és giroszkóp

Az MPU 6050 jelű eszköz 3 dimenziós gyorsulásérzékelésre, 3 dimenziós elfordulásérzékelésre és még hőmérséklet érzékelésre is alkalmas. A gyorsulásérzékelés angolul accelerometer, az elfordulásérzékelő angolul gyroscope. Ez a szinte filléres eszköz általában nem része az alapkészleteknek, ill. a 37 érzékelőből álló szenzorgyűjteménynek sem. Beüzemelni sajnos nem egyszerű, de a probléma talán betudható annak, hogy olyan eszközt sikerült rendelnem, amelyet forrasztani kellett, és lehet, hogy némi kontakt hiba van benne.

Kapcsolás:

  • MPU 6050 VCC → Arduino 3.3V
  • MPU 6050 GND → Arduino GND
  • MPU 6050 SCL → Arduino A5
  • MPU 6050 SDA → Arduino A4
  • MPU 6050 XDA → nem kell bekötni
  • MPU 6050 XCL → nem kell bekötni
  • MPU 6050 ADO → nem kell bekötni
  • MPU 6050 INT → Arduino 2

A nyers adatokat kiolvasó és soros monitorra kiíró kód az alábbi:

#include<Wire.h>
 
const int MPU6050_addr=0x68;
int16_t AccX, AccY, AccZ, Temp, GyroX, GyroY, GyroZ;
 
void setup() {
  Wire.begin();
  Wire.beginTransmission(MPU6050_addr);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
  Serial.begin(9600);
}
 
void loop(){
  Wire.beginTransmission(MPU6050_addr);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU6050_addr, 14, true);
  AccX = Wire.read()<<8 | Wire.read();
  AccY = Wire.read()<<8 | Wire.read();
  AccZ = Wire.read()<<8 | Wire.read();
  Temp = Wire.read()<<8 | Wire.read();
  GyroX = Wire.read()<<8 | Wire.read();
  GyroY = Wire.read()<<8 | Wire.read();
  GyroZ = Wire.read()<<8 | Wire.read();
  Serial.print("AccX = "); Serial.print(AccX);
  Serial.print(" || AccY = "); Serial.print(AccY);
  Serial.print(" || AccZ = "); Serial.print(AccZ);
  Serial.print(" || Temp = "); Serial.print(Temp);
  Serial.print(" || GyroX = "); Serial.print(GyroX);
  Serial.print(" || GyroY = "); Serial.print(GyroY);
  Serial.print(" || GyroZ = "); Serial.println(GyroZ);
  delay(1000);
}

Ha értelmezhetetlen adatokat ad (pl. csupa 0-t), akkor a következőket tehetjük:

  • Várunk; kb. 10 másodperc, amíg magához tér.
  • Az i2c_scanner nevű programmal (https://playground.arduino.cc/Main/I2cScanner/) érdemes lehet ellenőrizni, hogy a cím valóban 0x68-e (lehet 0x69 is).
  • Ellenőrizzük a kábelezést. Pl. egy minimális kontakt hiba is problémát okozhat.

Ha stabilan érkeznek a mért adatok, akkor próbáljuk meg elforgatni, mozgatni, és közben figyeljük a soros monitoron az adatokat. A nyers adatok értelmezése kissé bonyolult, ehhez telepítsük valamelyik MPU6050 Arduino könyvtárat, és a példaprogramokat használjuk fel a további lépések megtételéhez.

Néhány hasznos oldal a témában, amelyek a kapcsolás és a kód mellett a működési elvet is vázolják:

Rázkódás érzékelő

Angolul vibration sensor. Fekete, henger alakú, belül rézszínű. A jelölésnek megfelelően kell bekötni, a digitális szenzor kódját használni, és teszteléshez rázogatni kell, vagy inkább ütögetni magát a hengert, miközben figyeljük a lapkán a LED-et.

Hangérzékelő

Angolul sound sensor. A 37 szenzor készletben kettő is található. Mindkettő piros, és van rajta egy szürke oldalú henger, végén fekete hártyával. Az A0 az analóg, a D0 pedig a digitális kimenet, így elvileg analóg és digitális módon is használhatjuk. Látványosabb a digitális: az ott bemutatott kacsolással és kóddal indítsuk el, miközben mindenféle hangokat adunk ki.

A hangérzékelőnek valójában egy speciális fajtása a kopogás érzékelő (knock sensor). Ez is része a 37 szenzor készletnek. Rajta található egy fehéres, átlátszó tokba szerelt rugó. Analóg és digitális lábon is kipróbálhatjuk. Teszt: magán az érzékelőn kopogjunk, egyébként eléggé érzéketlen a kopogásra.

Mágneses mező érzékelők

Angolul magnetic field sensors. A 37 szenzor készletben öt is helyet kapott: három fekete és kettő piros. Technológiáját tekintve három Hall érzékelő és kettő reed kapcsoló. A Hall érzékelő egy háromlábú tranzisztorra hasonlít, míg a reed vékony üvegszálat tartalmaz. A két fekete Hall érzékelő közül a két fekete közül az egyik analóg, a másik digitális. Nehéz megkülönböztetni őket; a digitálisnál a mínusz láb fölött is van kettő valami. Az hasonlóan kinéző piros lényegében megegyezik a fekete analóg párjával, az viszont ott egyrészt az érzékenységet be lehet állítani, másrészt van neki digitális kimenete is. A reed kapcsolóknál mágnes hatására szakadásból rövidzár lesz, így bináris eredményt ad.

Teszt:

  • A két analóg Hall mágneses érzékelő: az analóg érzékelőknél bemutatott módon kössük rá és töltsük fel a kódot. Közelítsünk mágnest az érzékelőhöz, miközben figyeljük a soros monitort. Mágnes nélkül 500 körüli értéket kell kapnunk, és függően a mágnes polaritásától egy 500-nál lényegesen magasabb vagy lényegesen alacsonyabb értéket kapunk.
  • A digitális Hall érzékelő és a két reed relé: a digitális érzékelők kódját töltsük fel megfelelő bekötéssel, és a mágnes közelítésével a 13-as LED-et figyeljük. Itt mindegy a mágnes polaritása.

Pulzus érzékelő

A 37 részes szenzor készletben helyet kapott egy igen érdekes érzékelő: a pulzusmérő. Ez az analóg érzékelő valójában egy IR LED-ből és ez IR érzékelőből áll, és azt méri, hogy mennyi infravörös fényt árnyékol valami. Egy nagyon jó bemutató leírást láthatunk itt: https://www.hackster.io/Johan_Ha/from-ky-039-to-heart-rate-0abfca. A környezeti zajok miatt igen nehéz hasznos információt kinyerni belőle.

Fény akadály érzékelő

A 37 szenzor készlet része. A fekete színű érzékelőjében egy kis rész van, és azt "figyeli", hogy kerül-e oda valami. A digitális teszteknél látott módon kössük be és az ottani kódot használjuk, a kipróbáláshoz pedig helyezzük egy vékony tárgyat (pl. papírlapot) az érzékelőbe.

Színérzékelő

A színérzékelő (angolul color sensor) általában nem része a kezdőszettnek, és a 37 szenzor készletnek sem, kb. 2 dollárért rendelhető. Alább a TCS230 és TCS3200 típust mutatom be. Hasznos oldalak:

A színérzékelő nem pont úgy működik, ahogyan azt elképzeljük (tehát odatartjuk valahova, és megmondja, milyen színű), de talán pont emiatt érdekes, különösen oktatásra, kísérletezésre.

Először röviden a működési elvéről: az érzékelő 64, 8x8-as elrendezésű fotodiódát tartalmaz. A fotodióda egy olyan eszköz, ami árammá alakítja a fényt. A 64 érzékelőből 16 piros szűrőt (hullámhossz: 700~635 nm), 16 zöld szűrőt (560~520), 16 kék szűrőt (490-450) tartalmaz, míg 16 nem tartalmaz szűrőt. Az S2 és S3 bemenetekkel (ld. később) tudjuk vezérleni azt, hogy melyik érzékelő adatát szeretnénk beolvasni. Lehetőségek (S2-S3):

  • LOW-LOW: piros
  • LOW-HIGH: kék
  • HIGH-LOW: szűrő nélküli
  • HIGH-HIGH: zöld

Az S0 és S1 bemenetekkel (ugyancsak ld. lejjebb) a frekvencia skálázást tudjuk beállítani. Lehetséges értékek (S0-S1):

  • LOW-LOW: kikapcsolt
  • LOW-HIGH: 2%
  • HIGH-LOW: 20%
  • HIGH-HIGH: 100%

A leggyakrabban használt érték a 20%, mi is azt fogjuk használni.

Az eszközön 8 láb található, melynek kialakítása az alábbi (állítsuk úgy azt eszközt, hogy fent bal oldalt lássuk a GND feliratot):

  • Fent, balról jobbra:
    • GND - föld
    • OE - kimeneti frekvencia engedélyezése
    • S1 - frekvencia skálázás
    • S0 - frekvencia skálázás
  • Lent, balról jobbra
    • VCC - táp
    • OUT - kimenet
    • S2 - szűrés
    • S3 - szűrés

Az eszköz beüzemelése két lépésből áll: először megnézzük, hogy milyen színre milyen értékeket kapunk, majd abból meghatározunk egy algoritmust, ami megpróbálja kitalálni, hogy a látott szín milyen.

Kapcsolás (mindkét esetben):

  • Színérzékelő GND - Arduino GND
  • Színérzékelő OE - nincs bekötve
  • Színérzékelő S1 - Arduino 5
  • Színérzékelő S0 - Arduino 4
  • Színérzékelő VCC - Arduino 5V
  • Színérzékelő OUT - Arduino 8
  • Színérzékelő S2 - Arduino 6
  • Színérzékelő S3 - Arduino 7

A kalibrációhoz az alábbi kódot használjuk:

#define S0 4
#define S1 5
#define S2 6
#define S3 7
#define sensorPin 8
 
void setup() {
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(sensorPin, INPUT);
 
  // Setting frequency scaling to 20%
  digitalWrite(S0,HIGH);
  digitalWrite(S1,LOW);
 
  Serial.begin(9600);
}
 
int redFrequency = 0;
int greenFrequency = 0;
int blueFrequency = 0;
int clearFrequency = 0;
 
void loop() {
  for (int i = 0; i < 10; i++) {
    // red
    digitalWrite(S2, LOW);
    digitalWrite(S3, LOW);  
    redFrequency = pulseIn(sensorPin, LOW);
    delay(100);
 
    // green
    digitalWrite(S2, HIGH);
    digitalWrite(S3, HIGH);
    greenFrequency = pulseIn(sensorPin, LOW);
    delay(100);
 
    // blue
    digitalWrite(S2, LOW);
    digitalWrite(S3, HIGH);
    blueFrequency = pulseIn(sensorPin, LOW);
    delay(100);
 
    // clear
    digitalWrite(S2, HIGH);
    digitalWrite(S3, LOW);
    clearFrequency = pulseIn(sensorPin, LOW);
    delay(100);
 
    Serial.println("R = " + String(redFrequency) + " G = " + String(greenFrequency) + " B = " + String(blueFrequency) + " C = " + String(clearFrequency));
  }
  Serial.println("*****");
  delay(1000);
}

Teszt: a kód úgy van elkészítve, hogy tízszer egymás után megméri és kiírja a mért értékeket a soros monitorra, majd egy csillagokból álló sort ír ki és tart egy másodperc szünetet. Indítsuk tehát el a soros monitort, tartsuk oda különböző színekhez, majd a végén másoljuk az eredményt a vágólapra, majd egy szövegszerkesztőbe, további elemzés céljából. Próbáljunk szabályokat alkotni az egyes színekre.

Én egy Rubik-kocka 6 oldalát mértem le közvetlen közelről, éjszaka, mesterséges fény mellett. Az alábbi kód illusztrálja az eredményt, melyet a fenti kapcsolással használjunk:

#define S0 4
#define S1 5
#define S2 6
#define S3 7
#define sensorPin 8
 
void setup() {
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(sensorPin, INPUT);
 
  // Setting frequency scaling to 20%
  digitalWrite(S0,HIGH);
  digitalWrite(S1,LOW);
 
  Serial.begin(9600);
}
 
int redFrequency = 0;
int greenFrequency = 0;
int blueFrequency = 0;
 
void loop() {
  // red
  digitalWrite(S2, LOW);
  digitalWrite(S3, LOW);  
  redFrequency = pulseIn(sensorPin, LOW);
  delay(100);
 
  // green
  digitalWrite(S2, HIGH);
  digitalWrite(S3, HIGH);
  greenFrequency = pulseIn(sensorPin, LOW);
  delay(100);
 
  // blue
  digitalWrite(S2, LOW);
  digitalWrite(S3, HIGH);
  blueFrequency = pulseIn(sensorPin, LOW);
  delay(100);
 
  String colorGuess = "";
  if (redFrequency > 110) {
    if (greenFrequency > 110) {
      colorGuess = "blue";
    } else {
      colorGuess = "green";
    }
  } else {
    if (greenFrequency > 130) {
      colorGuess = "red";
    } else if (greenFrequency > 75) {
      colorGuess = "orange";
    } else if (blueFrequency > 50) {
      colorGuess = "yellow";
    } else {
      colorGuess = "white";
    }
  }
  Serial.println(colorGuess);
  delay(1000);
}

Jó eséllyel át kell írni a számokat, de lehet, hogy a meghatározás logikáját is. Eredményül a soros monitoron megjelenik a megállapított szín angol neve.

Ebben a projektben tehát egy minimális mesterséges intelligenciát is programoztunk!

Összetett eszközök

Több kimenet egy lábon: 74HC595

Amint láttuk a megjelenítő eszközöknél, ha egyesével próbáljuk megcímezni pl. a LED-eket, igen hamar kifogyunk a lábakból. Pl. egy 8x8-as LAD mátrix 16 lábat vesz el a 20-ból, ráadásul ott már trükköt kellett alkalmazni, hogy ne legyen szükség 64 lábra.

Egy ötlet lábak spórolására: ugyanazon a lábon írjuk ki sorban egyesével a logikai értékeket, és a fogadó is ennek alapján értelmezze, majd "ossza szét" belül. Az ötlet jó, viszont problémát okoz az időzítés, melynél valójában két problémát kell megoldani:

  • Nem garantálható, hogy a két eszköz belső időzítője teljesen tökéletesen szinkronban van, pl. függ a küldő processzorának sebességétől. Akármennyire is tökéletes oszcillátorokat használunk, könnyen "szétcsúszik" az adat. Emiatt szükség van egy órajelre is: az adó nemcsak az adatot küldi, hanem egy másik szálon az ütemezést is. Így a fogadó tudja, hogy eddig tartott az előző adat, most jön a következő.
  • Azt is tudatni kell a fogadóval, hogy mikortól figyelje az adatokat. Ezt egy újabb szálon tudjuk átadni. Ezzel azt is meg tudjuk mondani, hogy meddig tart, és a fogadó miután megkapta a vége jelet, állítja csak be az állapotát, tehát nem sorban, ahogy jön a jel, hanem egyszerre. (Emlékezzünk csak: a mátrix kijelzőnél a képpontokat sorban villantottuk fel.)

Adja magát tehát a megoldás. Szükség van 3 lábra: egy adatra (data), egy órajelre (clock) és egy zárra (latch vagy strobe). Pont ezt valósítja meg a 74HC595 kódú IC, melynek segítségével 8 kimenetet tudunk megcímezni 3 bemenet segítségével. Ráadásul a fentiekből az is következik, hogy akármelyik 3-at használhatjuk a 20-ból (ellentétben pl. az I2C kommunikációval, melyre kizárólag csak az A4 és A5 alkalmas).

A példa alapján elég nehézkesnek tűnik, és elsőre nem feltétlenül tűnik nagy csodának, a következőkben, a TM1638-as eszköz bemutatásánál viszont látni fogjuk ennek a hatalmas erejét. Egyébként itt érhető el egy részletes leírás: https://www.arduino.cc/en/tutorial/ShiftOut. Először lássuk a bekötést. Az IC-n az egyik szélső lábhoz közel (tehát nem középen!) található egy kis kör alakú bemélyedés: az az első láb, és az óramutató járásával ellentétes irányban haladva növekszik 16-ig. Ahol nincs semmi, azt nem kell bekötni.

  • 74HC595 1 (Q1) -> 220Ω -> LED -> Arduino GND
  • 74HC595 2 (Q2) -> 220Ω -> LED -> Arduino GND
  • 74HC595 3 (Q3) -> 220Ω -> LED -> Arduino GND
  • 74HC595 4 (Q4) -> 220Ω -> LED -> Arduino GND
  • 74HC595 5 (Q5) -> 220Ω -> LED -> Arduino GND
  • 74HC595 6 (Q6) -> 220Ω -> LED -> Arduino GND
  • 74HC595 7 (Q7) -> 220Ω -> LED -> Arduino GND
  • 74HC595 8 (GND) -> Arduino GND
  • 74HC595 9 (Q7')
  • 74HC595 10 (MR) -> Arduino 5V
  • 74HC595 11 (SH_CP; órajel) -> Arduino 4
  • 74HC595 12 (ST_CP; zár) -> Arduino 5
  • 74HC595 13 (OE; output engedélyezés) -> Arduino GND
  • 74HC595 14 (DS; adat) -> Arduino 2
  • 74HC595 15 (Q0) -> 220Ω -> LED -> Arduino GND
  • 74HC595 16 (VCC) -> Arduino 5V

A kód a következő:

int latchPin = 5;
int clockPin = 4;
int dataPin = 2;
 
void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}
 
void loop() {
  for (int numberToDisplay = 0; numberToDisplay < 256; numberToDisplay++) {
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, numberToDisplay);  
    digitalWrite(latchPin, HIGH);
    delay(500);
  }
}

A működése a következő:

  • A zár 0-ra állítása indítja az adatokat, 1-re állítása pedig jelzi a fogadónak az adatok végét. A fogadó tehát nem az adott értéket, hanem az átmenetet figyeli.
  • A shiftOut() utasítás segítségével küldjük ki az adatot. Megadjuk az adatlábat, az óralábat, a kiírandó 8 bites számot, ill. jelezzük a bitek sorrendjét is.

Teszt: a fenti bekötés és példaprogram azt eredményezi, hogy a LED-ek egyesével "elszámolnak" binárisan 0-tól 255-ig.

A példa igen bonyolult, és elsőre jogosan gondolhatjuk azt, hogy igen nehézkes. A bekötéshez én 17 kábelt használtam, az eredeti előtét ellenállásokat ki kellett cserélnem jobbakra, néhány LED-et is, az eredetileg talált bekötés rossz volt, ráadásul a lábkiosztás sem számít szerintem logikusnak (én legalább a 8 outputot egymás mellé tettem volna). Az eredeti példában hibás volt a bekötés, azzal is elment egy csomó időm. Ráadásul így sem teljes: a fenti linken található leírás áramkörében szerepel egy kondenzátor is. Mindenesetre nálam anélkül is működik. Viszont első ránézésre nem tűnik olyan nagy nyereségnek: 5 adatlábbal többel és 7 kábellel kevesebbel ugyanezt meg lehet oldani.

Valamint nem tűnik átütőnek az sem, hogy 3 bemenet segítségével nyerünk 8 kimenetet, mert az nettóban csak 5 nyereség. A problémát nem is a 8 kimenet okozza, hanem az, ha ennek többszörösére van szükség. Erre a megoldás a következő: A zár segítségével egymás után több adatot is kiküldünk. Felfoghatjuk úgy is, hogy parancsokat küldünk, majd paramétereket, és a fogadó csak akkor fogja végrehajtani, miután mindent megkapott. Ehhez persze szüksége van a fogadónak is némi memóriára, így tovább bonyolódik a rendszer…

Ugyanakkor a hatalmas ereje nem abban rejlik, hogy mi magunk elkezdjük élesben használni a 74HC595 IC-t, hanem olyan, kész eszközöket vásárolunk, melyben benne vannak a fentiek, előlünk el vannak rejtve. A következőben látni fogunk egy ilyen eszközt: a TM1638-as kódút!

TM1638

Ez az eszköz tartalmaz 8 darab 7 szegmenses kijelzőt (az összesen 64 LED) + 8 darab nyomógombot + 8 darab különálló LED-et. Mindennek megcímzéséhez alapból 80 lábra lenne szükség, viszont a fent vázolt technikával 3 lábbal (+ GND és VCC) meg tudjuk oldani. Ráadásul az alacsony szintű shiftout() utasításokkal sem kell feltétlenül bajlódnunk, mert létezik olyan könyvtár, ami elrejti előlünk az ilyen részleteket, és elég annyit írnunk, hogy "írd ki azt, hogy 123". De erről később!

A következő leírás alapján értettem meg ennek az eszköznek a működését: https://blog.3d-logic.com/2015/01/10/using-a-tm1638-based-board-with-arduino/. A működésének a logikája a következő: 8 bites parancsokkal lehet vezérelni. Ezek a következők:

  • 1000abbb (0x8?): ezzel lehet aktiválni. Az 'a' bitet 1-re állítva aktiváljuk a rendszert, a 'bbb'-vel pedig a fényerőt állíthatjuk. A 0x8F utasítással aktiváljuk, maximális fényerővel.
  • 01000100 (0x44): adat kiírás. Ennek két paramétere van (1-1- bájt): cím és adat. Ezekről később.
  • 01000000 (0x40): ezzel folyamatosan lehet kiírni adatokat. Paraméterek: kezdő cím, és utána a legfeljebb 16 adat. Ez az utasítás tehát akár 18 bájt hosszú is lehet; elméletileg 144 lábra lenne ehhez szükség!
  • 01000010 (0x42): nyomógombok állapotának beolvasása. Paramétere nincs, viszont az adat lábat INPUT-ra kell állítani, és a shiftin() függvényt kell használni. A beolvasás viszont nem triviális: először az első és ötödik, majd a második és hatodik, utána a harmadik és hetedik, végül a negyedik és nyolcadik nyomógomb értékét olvashatjuk be, mégpedig a jobboldali, valamint jobbról az ötödik biten, tehát 4 darab beolvasásra, majd bit mozgatásra és bitenkénti logikai vagy művelet végrehajtására van szükség.

Címzés: formája 1100aaaa, tehát 0xC0-tól indul, és 16 címet lehet vele megcímezni. A páros számúak (tehát a 0xC0, 0xC2, 0xC4 stb.) a 7 szegmenses kijelzőket jelenti, balról jobbra, míg a páratlanok (tehát 0xC1, 0xC3 stb.) az egyes LED-eket.

Adatok: a 7 szegmenses kijelzőnél a már bemutatott formában tudunk elvileg bármilyen karaktert kirajzoltatni. A LED esetén a mi esetünkben bináris a dolog; az érték 0 vagy 1 lehet. Viszont létezik olyan hasonló eszköz is, ahol a LED nem egy, hanem kétszínű (piros-zöld): ez esetben a jobboldali bit a piros, jobbról a második bit pedig a zöld LED állításáért felelős.

Bekötés mindegyik esetben (amint azt már korábban láttuk):

  • TM1638 VCC -> Arduino 3.3V
  • TM1638 GND -> Arduino GND
  • TM1638 STB -> Arduino 7
  • TM1638 CLK -> Arduino 9
  • TM1638 DIO -> Arduino 8

Először lássuk a 8 számjegyes kijelző kódját!

const int latch = 7;
const int data = 8;
const int clock = 9;
 
void sendCommand(int value) {
  digitalWrite(latch, LOW);
  shiftOut(data, clock, LSBFIRST, value);
  digitalWrite(latch, HIGH);
}
 
void reset() {
  sendCommand(0x40);
  digitalWrite(latch, LOW);
  shiftOut(data, clock, LSBFIRST, 0xc0);
  for (int i = 0; i < 16; i++) {
    shiftOut(data, clock, LSBFIRST, 0x00);
  }
  digitalWrite(latch, HIGH);
}
 
void setup() {
  pinMode(latch, OUTPUT);
  pinMode(clock, OUTPUT);
  pinMode(data, OUTPUT);
  sendCommand(0x8f);
  reset();
}
 
void loop() {
  displayNumber(millis() / 100);
}
 
uint8_t digits[] = {
  B00111111, // 0
  B00000110, // 1
  B01011011, // 2
  B01001111, // 3
  B01100110, // 4
  B01101101, // 5
  B01111101, // 6
  B00000111, // 7
  B01111111, // 8
  B01101111  // 9
};
 
void displayNumber(int numberToDisplay) {
  int queue[8];
  for (int i = 0; i < 8; i++) {
    queue[i] = numberToDisplay % 10;
    numberToDisplay /= 10;
  }
  sendCommand(0x40);
  digitalWrite(latch, LOW);
  shiftOut(data, clock, LSBFIRST, 0xc0);
  for (int position = 7; position >= 0; position--) {
    shiftOut(data, clock, LSBFIRST, digits[queue[position]]);
    shiftOut(data, clock, LSBFIRST, 0x00);
  }
  digitalWrite(latch, HIGH);
}

A fent leírt módon adjuk ki az utasításokat, és végeredményben azt mutatja, hogy hány tizedmásodperc telt el az indulástól számítva.

Most lássuk, hogyan tudjuk használni a nyomógombokat és a LED-eket! A fent leírtak alapján a kód az alábbi:

const int strobe = 7;
const int data = 8;
const int clock = 9;
 
void sendCommand(uint8_t value) {
  digitalWrite(strobe, LOW);
  shiftOut(data, clock, LSBFIRST, value);
  digitalWrite(strobe, HIGH);
}
 
void reset() {
  sendCommand(0x40); // set auto increment mode
  digitalWrite(strobe, LOW);
  shiftOut(data, clock, LSBFIRST, 0xc0);   // set starting address to 0
  for(uint8_t i = 0; i < 16; i++) {
    shiftOut(data, clock, LSBFIRST, 0x00);
  }
  digitalWrite(strobe, HIGH);
}
 
void setup() {
  pinMode(strobe, OUTPUT);
  pinMode(clock, OUTPUT);
  pinMode(data, OUTPUT);
 
  sendCommand(0x8f);  // activate
  reset();
}
 
void loop() {
  buttons();
}
 
void buttons() {
  uint8_t buttons = readButtons();
  for(uint8_t position = 0; position < 8; position++) {
    uint8_t mask = 0x1 << position;
    setLed(buttons & mask ? 1 : 0, position);
  }
}
 
uint8_t readButtons(void) {
  uint8_t buttons = 0;
  digitalWrite(strobe, LOW);
  shiftOut(data, clock, LSBFIRST, 0x42);
  pinMode(data, INPUT);
  for (uint8_t i = 0; i < 4; i++) {
    uint8_t v = shiftIn(data, clock, LSBFIRST) << i;
    buttons |= v;
  }
  pinMode(data, OUTPUT);
  digitalWrite(strobe, HIGH);
  return buttons;
}
 
void setLed(uint8_t value, uint8_t position) {
  pinMode(data, OUTPUT);
  sendCommand(0x44);
  digitalWrite(strobe, LOW);
  shiftOut(data, clock, LSBFIRST, 0xC1 + (position << 1));
  shiftOut(data, clock, LSBFIRST, value);
  digitalWrite(strobe, HIGH);
}

Most ha nyomkodjuk a nyomógombokat, akkor a neki megfelelő LED villan fel.

Ez ebben a formában még mindig nem átütő, bár legalább a kapcsolás egyszerű lett. Viszont most, hogy megismertük, lássuk, milyen lesz a kód, ha használjuk a már említett könyvtárat (https://github.com/rjbatista/tm1638-library). A fenti számláló megvalósítása ennek segítségével:

#include <TM1638.h>
 
TM1638 module(8, 9, 7);
 
void setup() {}
 
void loop() {
  module.setDisplayToDecNumber(millis() / 100, 0);
}

A nyomógombos példáé:

#include <TM1638.h>
 
TM1638 module(8, 9, 7);
 
void setup() {}
 
void loop() {
  module.setLEDs(module.getButtons());
}

Látható, hogy egyetlen include és egyetlen sornyi inicializálás után (ahol az adat, az óra és a zár lábakat kell megadni) mindkettő mindössze egy-egy sorrá zsugorodott. Ez már elfogadhatóan egyszerű úgy a bekötést mind a programozást illetően, és igen komoly lehetőségeket kapunk. A referencia itt található: https://github.com/rjbatista/tm1638-library/wiki/Reference.

Óra

A pontos idő megjegyzéséhez Arduino-n belül nem lehetséges, mert nincs állandó tápellátás. Szerencsére vásárolható viszonylag olcsón olyan eszköz, mely gombelemmel működik, és folyamatosan működik. Ennek jele DS3231. Elképzelhető, hogy magát a 2032-es gombelemet külön kell megvásárolnunk hozzá.

Bekötés: I2C protokollon keresztül csatlakozik az Arduino-ra, így a bekötés az alábbi:

  • DS3231 SCL -> Arduino A5
  • DS3231 SDA -> Arduino A4
  • DS3231 VCC -> Arduino 5V
  • DS3231 GND -> Arduino GND

Kód: először telepítsük fel az alábbi könyvtárakat (mindkettő Michael Mangolis gondozásában) a Library Manager segítségével (vagy GitHub-ról):

Először be kell állítani a pontos időt. Ehhez nyissuk meg a DS1307RTC -> SetTime példaprogramot és töltsük fel. Ez a program az aktuális rendszeridőt tölti fel.

A tapasztalat szerint a valóságban sajnos ez sem jegyzi meg az időt örökre, időnként végre kell hajtani a fenti folyamatot.

A DS1307RTC -> GetTime példaprogram másodpercenként egyszer kiolvassa a pontos időt és megfelelően megformázva a Serial monitorra írja. Ennek egy egyszerűsített változata, mely nem tartalmaz hibakelezést, szép formázást és dátumot sem, az alábbi:

#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
 
void setup() {
  Serial.begin(9600);
}
 
tmElements_t tm;
 
void loop() {
  RTC.read(tm);
  Serial.print(tm.Hour);
  Serial.print(":");
  Serial.print(tm.Minute);
  Serial.print(":");
  Serial.println(tm.Second);
  delay(1000);
}

Mikro SD kártya

A mikro SD kártya olvasó tipikusan nem része a kezdőkészleteknek, de száz Ft-os nagyságrendért vásárolhatunk. Az mikro SD kártyára írhatunk és olvashatunk is, így az kimeneti és bemeneti eszköz egyszerre. A kommunikáció az ún. SPI-n (Serial Peripheral Interface, soros külső interfész) keresztül történik.

Bekötés:

  • Micro SD 3v3 -> Arduino 3.3V
  • Micro SD CS -> Arduino 10
  • Micro SD MOSI -> Arduino 11
  • Micro SD CLK -> Arduino 13
  • Micro SD MISO -> Arduino 12
  • Micro SD GND -> Arduino GND

Kód: a File -> Examples -> SD -> ReadWrite alapján készült:

#include <SPI.h>
#include <SD.h>
 
void setup() {
  Serial.begin(9600);
 
  SD.begin(10);
  File outFile = SD.open("test.txt", FILE_WRITE);
  outFile.println("hello world");
  outFile.close();
 
  File inFile = SD.open("test.txt");
  while (inFile.available()) {
    Serial.write(inFile.read());
  }
  inFile.close();
}
 
void loop() {}

Teszt: helyezzünk be egy mikro SD kártyát, töltsük fel a programot, és figyeljük a Serial Monitort. Utána számítógépen ellenőrizzük a létrehozott fájlt. (Megjegyzés: nehézkesen sikerült csak működésre bírni; két darab 32 GB-os kártyára nem sikerült írni, végül egy 1 GB-osra sikerült, de arra sem egyből.)

GSM

TODO: kidolgozni

https://lastminuteengineers.com/sim800l-gsm-module-arduino-tutorial/

Adatkapcsolatok

Ebben a fejezetben néhány olyan adatkapcsolatról lesz szó, melynek segítségével két Arduino egymással tud kommunikálni. Ezekhez tehát két Arduino eszközre van szükség.

Soros adatkapcsolat kábellel

Ebben az esetben a két Arduino-t kábellel közvetlenül összekötjük, és úgy történik az adatátvitel. A soros kapcsolat két lába a TX (transmitter, küldő) és az RX (receiver, fogadó). Az egyik Arduino TX-ét a másik RX-ére kell kötni és fordítva. Valamint közös GND-re kell kötni őket. Létezik beépített soros adatkapcsolat, ahol a TX az 1-es láb, az RX a 0-s, ezen keresztül is ki fogjuk próbálni, de mi magunk is használhatunk saját lábat.

A beépített soros adatkapcsolat használata

Bekötés:

  • Arduino A 0 <-> Arduino B 1
  • Arduino A 1 <-> Arduino B 0
  • Arduino A GND <-> Arduino B GND

Kód:

A küldőé:

void setup() {
  Serial.begin(9600);
}
 
void loop() {
  Serial.write("a", 1);
  delay(500);
  Serial.write("b", 1);
  delay(500);
}

A fogadóé:

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}
 
void loop() {
  char command[1];
  Serial.readBytes(command, 1);
  if (command[0] == 'a') {
    digitalWrite(LED_BUILTIN, HIGH);
  } else if (command[0] == 'b') {
    digitalWrite(LED_BUILTIN, LOW);
  }
}

Itt valójában feltöltéskor ki kell kötni a TX és RX adatkapcsolatot, különben nem sikerül a feltöltés, ugyanis a számítógép COM portjával is ezek keresztül csatlakozik. A kódban a küldőt és a fogadót is 9600 bps-re állítjuk. A küldő fél másodpercenként küld egy egykarakteres parancsot, a fogadó pedig folyamatosan figyeli, és ennek alapján kapcsolgatja ki-be a panelen található LED-et.

Saját soros adatkapcsolat kialakítása

Kényelmetlen lehet a beépített soros adatkapcsolat használata, mivel egyrészt ha be van kötve, akkor nem működik a feltöltés, másrészt elveszi a lehetőséget a számítógéppel való kommunikációra (pl. hibakeresésnél), harmadrészt ha csak ez a lehetőség lenne, akkor egyetlen másik Arduino-val tudnánk csak soros adatkapcsolatot létrehozni. A SoftwareSerial.h beépített könyvtár segítségével más lábakat is ki tudunk alakítani soros kommunikációs célra. A lenti példában a 10-es és 11-es lábakat alapítjuk ki.

Bekötés:

  • Arduino A 10 <-> Arduino B 11
  • Arduino A 11 <-> Arduino B 10
  • Arduino A GND <-> Arduino B GND

Kód:

A küldőé:

#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(10, 11); // RX, TX
 
void setup() {
  mySerial.begin(9600);
}
 
void loop() {
  mySerial.write("a", 1);
  delay(500);
  mySerial.write("b", 1);
  delay(500);
}

A fogadóé:

#include <SoftwareSerial.h>
 
SoftwareSerial mySerial(10, 11); // RX, TX
 
void setup() {
  mySerial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}
 
void loop() {
  char command[1];
  mySerial.readBytes(command, 1);
  if (command[0] == 'a') {
    digitalWrite(LED_BUILTIN, HIGH);
  } else if (command[0] == 'b') {
    digitalWrite(LED_BUILTIN, LOW);
  }
}

Itt már nyugodtan be lehet kötve feltöltéskor is, sőt, a számítógépes összeköttetéshez az USB-t is használhatnánk, mint pl. az ezen az oldalon található példa is teszi: https://www.arduino.cc/en/Tutorial/SoftwareSerialExample.

Rádió

A 433 MHz-es RF rádiós adó és vevő meglepően olcsón megvásárolható, bár általában nem része sem az alapkészletnek, sem a 37 részes szenzor csomagnak sem. A küldő (transmitter) négyzet alakú, és van rajta egy kör alakú, R433S feliratú egység, a fogadó (receiver) pedig hosszúkás téglalap alakú, rajta LM358 feliratú chip-pel.

Bekötés:

  • Küldő
    • GND -> Arduino K GND
    • középső -> Arduino K 12
    • VCC -> Arduino K VCC
  • Fogadó
    • GND -> Arduino F GND
    • 2 -> nem kell bekötni
    • 3 -> Arduino F 11
    • VCC -> Arduino F VCC

Kód: a kód fordításához először fel kell telepíteni a VirtualWire könyvtárt, ami nem is olyan egyszerű, mint amennyire fontos:

A fogadó kódja az alábbi:

#include <VirtualWire.h>
 
byte message[VW_MAX_MESSAGE_LEN];
byte messageLength = VW_MAX_MESSAGE_LEN;
 
void setup() {
  Serial.begin(9600);
  vw_set_rx_pin(11); // default
  vw_setup(2000); // bits per sec
  vw_rx_start();
}
 
void loop() {
  messageLength = VW_MAX_MESSAGE_LEN;
  if (vw_get_message(message, &messageLength)) { // non-blocking
    for (int i = 0; i < messageLength; i++) {
      Serial.write(message[i]);
    }
    Serial.println();
  }
}

Ezt az Arduino F-re kell feltölteni. A vw_get_message() nem blokkoló függvény, az azonnal visszatér, akár talált valamit, akár nem. Itt is az alapértelmezett, jelen esetben 11-es lábra kötöttük az eszközt, tehát az azt beállító sorra valójában nincs szükség, de ahhoz, hogy tudjuk, hogyan kell átállítani, benne maradt a példaprogramban.

A küldő kódja az alábbi, melyet a fenti Arduino K-ra kell rátölteni:

#include <VirtualWire.h>
 
void setup() {
  vw_set_tx_pin(12); // default
  vw_setup(2000);  // bits per sec
}
 
int counter = 0;
 
void loop() {
  String msgToSend = "Counter: ";
  msgToSend += counter++;
  sendMessage(msgToSend.c_str());
  delay(1000);
}
 
void sendMessage(char *message) {
  vw_send((uint8_t *)message, strlen(message));
  vw_wait_tx();
}

Az alapértelmezett port a 12-es, de a fenti példa segítségével ezt át tudjuk állítani. A program egy számláló értékét növeli másodpercenként egyszer és küldi rádión keresztül a fogadónak. A küldőnél és a fogadónál is ugyanazt az átviteli sebességet kell beállítani, ami jelen esetben 2000 bit másodpercenként.

Teszt: töltsük fel előbb a fogadót, és indítsuk el a Serial Monitor-t a megfelelő porton (pl. COM3), majd a küldőt, a neki megfelelő porton (pl. COM4). Figyeljük a Serial Monitort. Játsszunk el velük, pl. helyezzünk akadályt a küldő és a fogadó közé, csomagoljuk be teljesen a küldőt (akár pl. a marunkba), és közben figyeljük a fogadót a soros monitoron.

Megjegyzés: ugyanez a kód működik akkor is, ha a küldőt és a fogadót közvetlenül összekötjük kábellel, tehát: Arduino K 12 -> Arduino F 11.

Infravörös kapcsolat

A fogadót (pl. a 37 részes szenzor csomagból) az infravörös érzékelő távirányítóval alfejezetben leírtak szerint indítsuk el, de most távirányítóra csak annyiban van szükség, hogy ellenőrizzük, a fogadó tényleg jól működik.

A küldőt (mely szintén része a 37 részes szenzor csomagnak) ráköthetjük egy másik USB portra, így más COM-ot kap. Bekötés: az S a 3-as lábra kerüljön (kérdés: miért?), a mínusz a GND-re a középső pedig az 5V-re. A következő kód 0-tól 9-ig sorban, másodpercenkét egy számjegyet küld a fogadónak:

#include <IRremote.h>
 
IRsend irsend;
 
void setup() {}
 
long codes[] = {0XFF6897, 0XFF30CF, 0XFF18E7, 0XFF7A85, 0XFF10EF, 0XFF38C7, 0XFF5AA5, 0XFF42BD, 0XFF4AB5, 0XFF52AD};
 
void loop() {
  for (int i = 0; i < 10; i++) {
    irsend.sendNEC(codes[i], 32);
    delay(1000);
  }
}

Itt tehát kihasználtuk azt, hogy NEC protokollt fogad a fogadó (ez a kódoláshoz fontos), a kódokat pedig az igazi távirányítóból kell lesni.

Wifi

Wifire az ESP8266 oldalon bemutatott chip segítségével tudunk csatlakozni. Mivel ez képes szerver és kliens módban is működni, technikailag lehetséges az, hogy két Arduino UNO wifi-n keresztül kommunikáljon egymással. Erre a hivatkozott oldalon látunk példát.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License