Adatcsere soros porton két Arduino között

Kétség kívül hardver és szoftver szempontból is a sorosport a legegyszerűbb adatkapcsolati lehetőség. Ha csak egy irányba kell adatokat továbbítanunk egyik Ardiono-ból a másikba, akkor közösíteni kell földet, tehát kell egy vezeték a GND pontok összekötésére, és kell egy vezeték a az adatokat küldő Arduino TX kivezetésétől az adatokat fogadó Arduino RX kivezetéséhez. Ez összesen két vezeték. Fordított irányt csak akkor kell bekötni, ha oda-vissza kommunikációra lenne szükség. Így néz ki a megvalósítás:

Ezen az ábrán mindkét irány be lett kötve, de ha a bal oldali Arduino a master (küldi az adatokat), a jobb oldali pedig a slave (fogadja az adatokat), akkor csak a kék vezetékre van szükség. Nomeg a feketére, ami a GND pontokat köti össze, de ez alap!

A soros portnak van egy másik nagy-nagy előnye. Meglehetősen rugalmasan lehet beállítani a továbbítás sebességét, ami hosszú vezetékek esetén nagyon hasznos dolog. Sajnos nem volt lehetőségen a gyakorlatban kipróbálni nagyon-nagyon hosszú vezetékkel, de 10-20m távolságig még a 115200 baud is működött. Elméletben akár többszáz méter is lehet az áthidalható távolság, természetesen egyre kisebb sebességgel. A nagyon hosszú vezeték már rengeteg zajt szednek össze, ezért még kis sebesség esetén is sérülhet az adat. Ekkor érdemes bekapcsolni a paritás bitet is az átvitelben, ami minimális hibadetektálást fog végezni. A példa programokban a soros port inicializálása így néz ki: Serial.begin(9600), ahol 9600 a port sebessége baudban. Ez a sebesség Arduino Ide soros monitorán  300 baud-tól egészen 2.000.000 baud ig beállítható. A felső határ valószínűleg elméleti és hardver függő. Az ATmega328 adatlapján számomra nem teljesen világos módon volt megfogalmazva a lehetséges baudrate érték, mivel ez órajelfüggő. A fellelt táblázatban a boudrate érték százalékos eltérését adták meg az elméleti értéktől. 16Mhz órajelnél 1.000.000 baud volt a táblázatban a maximális sebesség. A lényeg a táblázatban az volt, hogy a minimumként feltüntetett 2400 baud hibája (eltérése az ideálistól) minimális, míg a 115200 hibája maximális, ‑3.5%. A 9600 baud is minimális sebesség hibával működik az ATmega328 chip-ben. Két Arduino összekapcsolásakor ezzel nem kell foglalkozni, mert mindkettő órajele 16Mhz, és senkit nem érdekel, hogy mennyi is a boudrate konkrét értéke, ha mindkét eszköz azonos értékkel tér el az elméletitől. Azonban két különböző eszköznél ennek lehet jelentősége. Visszatérve a paritás bit bekapcsolására! Szinte soha nem látni a példákban, hogy a Serial.begin()-nek lenne más paramétere is, mint a sebesség. Pedig van! Itt lehet utánanézni a másik paraméternek: https://www.arduino.cc/reference/en/language/functions/communication/serial/begin/ Nem foglalkoztam vele, mert nem volt szükségem még rá, de a második paraméterrel bekapcsolható a paritás ellenőrzés. Ha az adat sérül, akkor a paritás bitből ezt meg lehet állapítani, és ha sikerül észrevenni a hardvernek, akkor eldobja az adatot! Ez részemről csak feltételezés, mert a Serial.read() függvénynél nem találtam olyan visszatérő értéket, ami jelezné a hibás adatot, tehát csakis eldobhatja. Ha tudjuk hogy hibás, akkor minek tenné elérhetővé! A paritás bit sajnos nem tuti megoldás a hibadetektálásra, mert csak azt veszi észre, ha egy bit változott meg az átvitel során. Két bit változását már nem veszi észre.

A mintapéldák „Hello word” szöveget visznek át, és nem konkrét adatokat. Ezért némi időt kellett szánnom rá, hogyan is kell adatokat átvinni. A küldő oldalon a Serial.write() függvényt kell használni, aminek paramétere egy byte típusú adat vagy változó. A fogadó oldalon pedig a Serial.read() függvényt kell elhelyezni a programban. Ez egy int értéket ad vissza, aminek értéke -1, ha nincs adat. Van egy kényelmes eszköz a vezérlőben, ez a soros puffer memória, ami 64 byte-os. A fogadó áramkör a beérkezett adatokat behelyezi a soros puffer memóriába. Ha kiolvassuk, akkor mindig a legrégebben érkezett adatot adja vissza a Serial.read()-nek, és csökken a pufferben lévő adatok száma. Tehát a fogadott adat tölti, a Serial.read() üríti ezt a „zsákot”. Hasznos függvény még a Serial.available() ami a pufferben található fogadott adatok számát adja vissza. Ha ennek értéke 0-nál nagyobb, akkor van mit kiolvasni a Serial.read() függvénnyel. Más tudnivaló ezzel nincs is. Nem bonyolult programot írni! A soros port teljesen hardveres, tehát nem kell foglalkoznunk vele. Tudtommal nem generál megszakítást, nem vesz el időt a programunktól, teljesen önálló. Mindössze néha rá kell nézni, és kiolvasni a pufferből, ha van benne valami.

Azonban nem ennyire ideális a helyzet a soros portal. Nagy baj vele, hogy csak egy van belőle az Arduino Nano és Uno-n. Pl. a sokkal drágább Arduino Mega már 4db-ot tartalmaz! Uno és Nano esetén viszont csak egy! Sajnos a programfeltöltés a soros porton történik. Ha van egy külső eszköz a soros porton pl. egy másik Arduino, akkor azt a programozás idejére le kell választani. Ez kényelmetlen dolog. De van rosszabb is! Aki gyakran fejleszt, az tudja, hogy a programot nem lehet általában hibátlanul megírni. A fejlesztés egy elhúzódó folyamat, amiben igen sokszor töltünk programot és kipróbáljuk, hogy az mit csinál. A program kipróbálás egyik legkényelmesebb és legfontosabb eszköze a soros port, amin keresztül a PC-nek beküldhet a program adatokat, változókat. Így lehet könnyen megkeresni a programhibákat. Ha a soros portot elhasználjuk, akkor ez a lehetőség megszűnik. Azért nem teljesen, mert vannak lehetőségek. Ha pl. a programunk csak fogad adatokat, akkor nem kell a TX vezetéket bekötni, és az továbbra is küldhet adatokat a PC-nek. Számomra ez nem volt teljesen egyértelmű, ezért ki is próbáltam, működik. Érdekes lehet itt még az Arduino hardver megoldása, ahogyan az USB soros illesztő csatlakozik a TX és RX kivezetésekhez. Íme a kapcsolási rajz megfelelő részlete:

Annyi látszik az ábrából, hogy a CH340G USB illesztőegy-egy soros 1Kohm-os ellenálláson keresztül kapcsolódik az ATmega328 (vagy más vezérlő) TX és RX kivezetéséhez (az ábrán TXD és RXD a jelölése). Ha ezeket a kivezetéseket másra használjuk, akkor tudnunk kell, hogy ezen soros ellenállások miatt kb. 1Kohm ellenállást vagy annál is kevesebbet mutatnak. Ez általában semmit nem zavar, tehát nincs vele gond. Sajnos az RX bemenet eredő ellenállása kisebb is mint 1 Kohm, mert van ott egy beépített led, aminek a soros ellenállása is 1Kohm a +5V felé. A TX kimeneten is van led, így ha ezt kimenetnek használjuk, nem lehet kivenni a maximális 50mA áramot, mert a beépített led felé is elfolyik némi áram. Ezek a led-ek a soros kommunikációt jelzik. Tényleg hasznosak, felvillanásokkal jelzik, ha jön vagy meg adat. Programfeltöltéskor sűrű villogás látható, amikor töltődik a chip-be a programuk. Ez megnyugtató látvány! Egyébként jó tudni, hogy a D0 és D1 kivezetést ha nem soros portnak használjuk, hanem valamilyen más célra kimenetként, akkor rögtön van az Arduino panelen két jelzőfény, ami mutatja a kimenetek állapotát. Úgy tapasztaltam, hogy a rátöltött program használhatja ezeket a kivezetéseket másra mint a soros kommunikáció a PC felé, ezzel csak a debug funkciót veszítjük el, hiszen nem tudunk adatokat küldeni a PC-nek. A program rátöltés azonban nem sérül. A chip-be töltött bootloader program ugyanis a reset után felprogramozza az RX kivezetést bemenetnek, a TX-et kimenetnek, és vár egy kicsit, hátha programot akar valaki rátölteni a chip-re. Ha nem, akkor elkezd futni a program, ami megfelelően beállítja ezeket a kivezetéseket. Azért figyelni kell, mert ha ezekre a kivezetésekre olyan külső hardvert teszünk, ami direkt logikai szintet köt valamelyik kivezetésre, akkor nem fog menni a feltöltés.

A másik említésre méltó lehetőség a sorftveres soros port, ami elég sok kivezetésen működhet, de nem bármelyiken. Az adat fogadással van a gond, mert annaka kivezetésnek amin használni szeretnénk támogatnia kell a vezérlőben a megszakításokat. Ez nem feltétlenül igaz minden kivezetésre.

Ezzel a kódrész lettel lehet a szükséges könyvtárat betölteni, és konfigurálni a kivezetéseket:

#include <SoftwareSerial.h>
const byte rxPin = 2;
const byte txPin = 3;
SoftwareSerial mySerial (rxPin, txPin);

A rendelkezésre álló függvények ugyanazok mint a hardveres soros portnál, tehát a programot lényegében nem kell átírni, csak a Serial helyett „mySerial”-t kell írni. Pl. mySerial.begin(9600).

Van ezzel a megoldással sok baj. Legfontosabb, hogy időt vesz el a programtól. Ez nem biztos, hogy elhanyagolható tulajdonság. Egyszerű programnál biztos nem gond, de ha pl. van az Arduino-ra kötve OneWire hőmérő, esetleg I2C buszon is folyik kommunikáció, vagy megszakításokat is használunk, nem vagyok biztos benne, hogy valahol nem fog gondot okozni. A másik nagy baja, hogy amíg adat fogadás van, addig nem lehet adatokat küldeni. Nem tudom, hogy mennyire volt egyértelmű, de a hardveres soros port tetszőlegesen tud küldeni és fogadni adatokat egymástól függetlenül és párhuzamosan! Ez a megoldás nem! De lehetnek olyan esetek, amikor ez biztosan nem gond, és felhasználásával a hardveres soros port megmaradhat a programfeltöltésre és a debug-ra!

Vannak azonban további szoftver jellegű komplikációk is! A mintapéldák általában jelzéseket visznek át a soros porton. Pl. megnyomom a gombot, bekapcsol a led a másik oldalon stb. Diszkréten hallgatnak arról a problematikáról, hogy ha adatcsomagokat szeretnénk átvinni, akkor hogyan lehet megállapítani, ha az adatcsomagból egy-egy byte kiesett hibás átvitel miatt. Ez már lényegesen komplikáltabb megoldást követel. Tudni kell, hogy hol az adatok eleje, vége! Erre írtam egy nagyon egyszerű általánosan felhasználható programocskát. Segítségével szabadon lehet a soros kábelt széthúzni és visszadugni a csatlakozóba. Elméletileg a hosszú vezetékeken keletkező adatsérülések sem okoznak gondot, persze a küldő oldalnak tudnia kell ismételni, vagy sokszor kell küldeni az adatcsomagot, valamelyik majd csak megérkezik alapon! Itt találod a program megoldást!

Mennyire volt hasznos amit olvastál?

Kattints egy csillagra az értékeléshez!

Szövegesen is leírhatod véleményedet!