Arduino soros kommunikáció

Minden Arduino kártyán (tipustól függően) elérhető egy vagy több soros port (UART vagy USART). A soros port az Arduino tábla és az erre alkalmas eszközök közötti kommunikációt látja el.

Vegyük figyelembe, hogy az Arduino táblán a soros kommunikáció az TX/RX lábakon TTL logikai szinteket használ, ez a kártyától függően 5 V vagy 3,3 V lehet.  Ha a csatlakoztatni kívánt eszköz azonos feszültséggel működik, közvetlenül csatlakoztathatjuk ezeket egymáshoz. Az alábbi képen az Arduino UNO egy HC-05 Bluetooth modulhoz csatlakozik. Mivel mindkét áramköri lap 5V feszültségen működik, közvetlenül összeköthetjük őket.

Arduino soros port kapcsolat

Amennyiben az eszközök feszültsége nem egyforma, abban az esetben szintillesztő áramkört kell alkalmazni. Ha ezt elmulasztjuk károsodhat az Arduino tábla vagy a csatlakoztatott eszköz. A következő ábra az Arduino UNO és egy ESP8266-01 modul soros kapcsolatát mutatja be. A két áramköri lap különböző feszültségen működik, ezt a problémát megoldja a szintillesztő áramkör.

Arduino soros port kapcsolat szintillesztő áramkörrel

Az Arduino Uno, Nano és Mega táblák rendelkeznek USB-Soros átalakítóval, ezek a 0 és 1 érintkezőket használják a számítógéppel való kommunikációhoz.

Az Arduino Mini kártyák fedélzetén nincs USB-Soros átjáró, ezért a számítógéppel való kommunikációhoz szükségünk van egy ilyen eszközre. Kapcsoljuk össze az Arduino tábla TX lábát az USB-Soros átalakító RX lábával, majd az Arduino RX az USB-Soros átalakító TX tűhöz, végül a GND lábakat is kössük össze.

Arduino mini pro - FTDI232 USB-Soros átjáróval

Az Arduino Mega összesen 4 soros porttal rendelkezik. Ebből az első soros port össze van kapcsolva a fedélzeti USB-Soros átalakítóval. Ahhoz, hogy az extra soros portokat a számítógépel való kommunikációhoz használjuk, további USB-soros adapterre lesz szükség.

Az alábbi táblázatban látható, hogy egyes Arduino táblákon melyik lábakon található a soros port.

Arduino TáblákSerialSerial1Serial2Serial3
Uno, Nano, Mini0(RX), 1(TX)
Mega0(RX), 1(TX)19(RX), 18(TX)17(RX), 16(TX)15(RX), 14(TX)
Leonardo, Micro, Yún0(RX), 1(TX)
Uno WiFi Rev.2Csatlakoztatva az USB-hez0(RX), 1(TX)Csatlakoztatva a NINA-hoz
MKR táblák13(RX), 14(TX)
ZeroCsatlakoztatva a Programozási Porthoz0(RX), 1(TX)
Due0(RX), 1(TX)19(RX), 18(TX)17(RX), 16(TX)15(RX), 14(TX)
1010(RX), 1(TX)
Arduino táblák soros port lábkiosztása

Az Uno, Nano, Mini és Mega esetén a 0 és 1 lábakat használjuk a számítógéppel való kommunikációhoz. Ezeket a pineket használhatjuk digitális ki és bemeneteknek, amennyiben nincs szükség a soros portra. Ha valamit csatlakoztatunk ezekhez a érintkezőkhöz, és használni szeretnénk a soros portot az megzavarhatja a kommunikációt, valamint okozhat sikertelen programfeltöltéseket az Arduino kártyára.

Ezekre szükség lehet a gyakorláshoz:

Soros kommunikáció, a szoftveres rész

Lássuk hogy bírjuk működésre a soros kommunikációt az Arduino kártyán. A soros kommunikáció használatához nincs szükség extra könyvtárra, az Arduino IDE tartalmazza a szükséges függvényeket.

Az első legfontosabb függvény a begin(); A Serial.begin(baud); beállítja az adatsebességet a soros adatátvitelhez. A baud értéket bit per másodpercben kell megadni. Ezt a setup() függvényben kell megadni.

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

void loop()
{
  
}

Az Arduino IDE Serial Monitorával való kommunikációhoz válasszunk a képernyő jobb alsó sarkában található legördülő menüben található ​​adatátviteli sebességek egyikét.

​​Arduino Serial Monitor adatátviteli sebesség beállítása
​​Arduino Serial Monitor adatátviteli sebesség beállítása

Serial.begin(baud, config); Az opcionális második argumentum konfigurálja az adat, paritás és stopbiteket. Az alapértelmezett 8 adatbit, nincs paritás, és egy stop bit.

void setup()
{
  Serial.begin(9600, SERIAL_8N1);   // alapértelmezett
}

void loop()
{
  
}

Válasszunk az alábbi az érvényes értékek közül:

Nincs paritás:

SERIAL_5N1
SERIAL_6N1
SERIAL_7N1
SERIAL_8N1
SERIAL_5N2
SERIAL_6N2
SERIAL_7N2
SERIAL_8N2

Páros paritás:

SERIAL_5E1
SERIAL_6E1
SERIAL_7E1
SERIAL_8E1
SERIAL_5E2
SERIAL_6E2
SERIAL_7E2
SERIAL_8E2

Páratlan paritás:

SERIAL_5O1
SERIAL_6O1
SERIAL_7O1
SERIAL_8O1
SERIAL_5O2
SERIAL_6O2
SERIAL_7O2
SERIAL_8O2

Vissza a tartalomjegyzékhez.

A Serial.end() függvény letiltja a soros kommunikációt, felszabadítva az RX és TX lábakat, így ezeket ki- és bemenetként használhatjuk. Ha újra szeretnénk használni a soros kommunikációt, meg kell hívni a Serial.begin() függvényt.

Próbáljuk ki:

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

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

void loop() 
{
  Serial.println("1. String");
  Serial.end();

  pinMode(0, OUTPUT);
  digitalWrite(0, HIGH);
  delay(500);
  digitalWrite(0, LOW);

  Serial.begin(9600);
  Serial.println("2. String");
  delay(1000);
}

Vissza a tartalomjegyzékhez.

A Serial.flush() megvárja a kimenő soros adatok továbbításának befejezését. A legtöbb esetben a soros átviteli puffer jó dolog, ez megakadályozza, hogy az Arduino program lekötve várja a soros átvitelt. De előfordulhat, hogy az Arduino programban meg kell várni a soros adatátvitel végét. Ebben segít a Serial.flush() függvény.

Serial.println(F("Hello Arduino"));
Serial.flush();

Vissza a tartalomjegyzékhez.

Az if(Serial) igaz értéket ad vissza, ha a megadott soros port használatra kész. Ennek a natív USB-vel rendelkező kártyák használatánál van jelentősége. A nem USB CDC-portok esetében ez mindig igaz lesz.

void setup()
{
  Serial.begin(9600);
  while(!Serial)
  {
    // Megvárja amíg a soros port csatlakozik. 
    // Natív USB-vel rendelkező kártyák esetén szükséges!
  }
}

void loop()
{
  
}

Vissza a tartalomjegyzékhez.

A Serial.print() az adatokat ASCII szövegként nyomtatja ki a soros portra.

A számok minden számjegyhez ASCII karaktert használnak.

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

void loop()
{
  Serial.print(29);  // 29
  delay(1000);
}

Az opcionálisan hozzáadható második paraméter határozza meg a formátumot, a következő kódrészletben például különböző számrendszerben nyomtathatjuk ki az értéket.

Serial.print(29, BIN);  // 11101

Serial.print(29, OCT);  // 35

Serial.print(29, DEC);  // 29

Serial.print(29, HEX);  // 1D

A lebegőpontos számok alapértelmezés szerint két tizedesjegy pontossággal kerülnek kinyomtatásra.

Serial.print(1.23456);  // 1,23

Az opcionális második paraméter határozza meg a lebegőpontos számok esetén a használandó tizedesjegyek számát.

Serial.print(1.23456, 0);  // 1

Serial.print(1.23456, 1);  // 1,2

Serial.print(1.23456, 4);  // 1,2345

A bájtok egyetlen karakterként kerülnek elküldésre.

A karakterek és a karakterláncok úgy kerülnek elküldésre, ahogy vannak.

Serial.print('M');                 // M
Serial.print("myhomethings.eu")    // myhomethings.eu

Vissza a tartalomjegyzékhez.

A Serial.println() hasonlóan működik, mint a Serial.print(), azzal a különbséggel, hogy a Serial.println() egy kocsivissza karakterrel (ASCII 13 vagy ‘\r’) és egy újsor karakterrel (ASCII 10 vagy ‘\n’) van kiegészítve.

Vissza a tartalomjegyzékhez.

A Serial.write() Bináris adatokat ír a soros portra. Az adatokat bájtként vagy bájtok sorozataként küldi el. Számkarakterek elküldéséhez használjuk a Serial.print() vagy Serial.println() függvényt.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

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

void loop() 
{
  Serial.write(65);     // ASCII 65 értéke -> A
  Serial.write(66);     // B
  Serial.write(67);     // C
  Serial.write('\n');     // újsor karakter


  int bytesLength = Serial.write("myhomethings.eu\n");  
  // Kiírja a "myhomethings.eu" Stringet és visszatér a String hosszával
  
  Serial.println(bytesLength);
  // Számkarakterek küldéséhez használjuk a print() vagy println() függvényt.
  
  char buf[] = {'m','y','h','o','m','e','t','h','i','n','g','s','.','e','u','\n'};
  int len = 16;

  Serial.write(buf, len);   // Elküld a buf tömbből len számú byte-ot

  Serial.write('\n');
  delay(1000);
}

Vissza a tartalomjegyzékhez.

A Serial.availableForWrite() segítségével beszerezhető a soros pufferbe írható bájtok száma az írási művelet blokkolása nélkül. Lássunk egy példát a használatára.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

String myString = "Arduino soros puffer - myhomethings.eu";

void setup() 
{
  Serial.begin(9600);
  
  // lekérdezzük a puffer méretét, majd kiírjuk a soros portra.
  Serial.print("A TX buffer mérete: ");
  Serial.println(Serial.availableForWrite());
}

void loop() 
{
  // ha a puffer mérete nagyobb mint a String...
  if(Serial.availableForWrite() > myString.length()) 
  {

    // kiírjuk a Stringet a soros portra
    Serial.println(myString);
  }
  delay(1000);
}

Vissza a tartalomjegyzékhez.

A Serial.available() visszaadja a soros portról olvasható bájtok számát, amelyek már a soros vételi pufferben tárolódnak. Az alábbi példában megvizsgáljuk, hogy a vételi puffer tartalmaz-e adatokat. Ha a vételi puffer nem üres, a Serial.read() függvény segítségével addíg olvasunk, amíg ki nem ürül.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

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

void loop()
{
  if(Serial.available() > 0)
  {
    char myChar = Serial.read();
    Serial.print(myChar);
  }
}

Vissza a tartalomjegyzékhez.

A Serial.read() beolvassa az adatokat a soros portról. A visszatérési értéke a bejövő soros adatok első bájtja, vagy -1, ha nincs további adat a soros vételi pufferben.

Vissza a tartalomjegyzékhez.

Arduino Mega 2560 mikrokontroller kártya

Arduino Mega 2560
Arduino Mega 2560 – hirdetés

A Serial.readBytes() beolvassa a karaktereket a soros portról egy pufferbe. A függvény akkor fejeződik be, ha a meghatározott hossz kiolvasásra került, vagy ha túllépi az alapértelmezett 1 másodperces időkorlátot vagy a Serial.setTimeout() függvényben megadott értéket. A Serial.readBytes() a pufferben lévő karakterek számát adja vissza, vagy 0-át ha nem található érvényes adat.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

char myBuff[64];
int len = sizeof(myBuff)-1;

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

void loop()
{
  if(Serial.available() > 0) 
  {
    size_t read = Serial.readBytes(myBuff, len);
    myBuff[read] = '
/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/
char myBuff[64];
int len = sizeof(myBuff)-1;
void setup()
{
Serial.begin(9600);
}
void loop()
{
if(Serial.available() > 0) 
{
size_t read = Serial.readBytes(myBuff, len);
myBuff[read] = '\0';
Serial.print(myBuff);
}
}
'; Serial.print(myBuff); } }

Vissza a tartalomjegyzékhez.

A Serial.readBytesUntil() beolvassa a karaktereket a soros pufferből egy tömbbe. A függvény akkor fejeződik be ha a meghatározott hossz beolvasásra került, ha időtúllépés van, vagy ha a lezáró karaktert talál.

char myBuff[64];
int len = sizeof(myBuff)-1;

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

void loop()
{
  if(Serial.available() > 0) 
  {
    size_t read = Serial.readBytesUntil('\n', myBuff, len);
    myBuff[read] = '
char myBuff[64];
int len = sizeof(myBuff)-1;
void setup()
{
Serial.begin(9600);
}
void loop()
{
if(Serial.available() > 0) 
{
size_t read = Serial.readBytesUntil('\n', myBuff, len);
myBuff[read] = '\0';
Serial.println(myBuff);
}
}
'; Serial.println(myBuff); } }

Vissza a tartalomjegyzékhez.

A Serial.readString() beolvassa a karaktereket a soros pufferből egy Stringbe. Időtúllépés esetén a függvény leáll, szükség esetén ezt az időt a Serial.setTimeout() függvénnyel beállíthatjuk. A Serial.readString() függvény nem áll le, ha az adatok sorvégi karaktereket tartalmaznak. 

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

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

void loop()
{
  Serial.println("Kérem a szöveget:");
  if(Serial.available() > 0) 
  {
    String myString = Serial.readString();
    myString.trim();
    if(myString != "")
    {
      Serial.print("A beírt szöveg: ");
      Serial.println(myString);
      myString ="";
      Serial.println("Kérem a szöveget:");
    }
  }
}

Vissza a tartalomjegyzékhez.

A Serial.readStringUntil() függvény beolvassa a karaktereket a soros pufferből a lezáró karakterig egy Stringbe. A lezáró karakter nem kerül be a Stringbe. A függvény leáll időtúllépés esetén. Az idő kitolható setTimeout() függvény használatával.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

char terminator = 't'; // a lezáró karakter

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

void loop()
{
  Serial.println("Kérem a szöveget:");
  if(Serial.available() > 0) 
  {
    String myString = Serial.readStringUntil(terminator);
    myString.trim();
    if(myString != "")
    {
      Serial.print("A beírt szöveg: ");
      Serial.println(myString);
      myString ="";
      Serial.println("Kérem a szöveget:");
    }
  }
}

Vissza a tartalomjegyzékhez.

A Serial.find() addig olvassa az adatokat a soros pufferből, amíg meg nem találja a target paramétert vagy a végére nem ér vagy időtúllépés van. Az idő beállítható a setTimeout() függvénnyel. A függvény true értékkel tér vissza, ha a cél megtalálható, false értékkel, ha nem találja.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

char target[] ="hello";

void setup()
{
  Serial.begin(9600);
  Serial.println("Kérem a szöveget:");
}

void loop()
{

  if(Serial.available() > 0) 
  {
    if (Serial.find(target))
    {
      Serial.println("Talált!");
    }
    else
    { 
      Serial.println("Sajnos nem talált!"); 
    }
    Serial.println("Kérem a szöveget:");
  }
}

Opcionálisan megadható egy második length paraméter amely meghatározza, hogy a target-ből hány karaktert keressen.

char target[] ="hello";
size_t length = 3;

Serial.find(target, length)

Vissza a tartalomjegyzékhez.

A Serial.findUntil() addig olvassa az adatokat a soros pufferből, amíg meg nem találja a ‘target‘ paramétert vagy a ‘terminal‘ lezáró karakterláncot vagy időtúllépés van. Az idő beállítható a setTimeout() függvénnyel. A függvény true értékkel tér vissza, ha a cél megtalálható, false értékkel, ha nem találja.

char target[] ="hello";
char terminal[] = "abc";

Serial.findUntil(target, terminal);

Vissza a tartalomjegyzékhez.

A Serial.parseInt() következő érvényes egész számot keresi a bejövő sorozatban. A függvény leáll időtúllépés esetén. Ezen segíthetünk a setTimeout() függvénnyel.

Amennyiben a Serial.parseInt() nem numerikus értékeket talál az érvényes egész szám elött, akkor eldobja őket, és visszaadja az egész számot, a többit pedig elhagyja. Ha a Serial.parseInt() nem talál numerikus értéket, akkor nullát ad vissza.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

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

void loop()
{
  if(Serial.available() > 0) 
  {
    long myInt = Serial.parseInt();
    Serial.println(myInt);
  }
}

A Serial.parseInt() függvényt opcionális paraméterekkel is meghívhatjuk.

Serial.parseInt()
Serial.parseInt(lookahead)
Serial.parseInt(lookahead, ignore)

Az első opcionális paraméter a lookahead, előretekintési módnak nevezik. Három előre meghatározott értéke lehet.

SKIP_ALL: a számjegyeken és a mínuszjelen kívül minden karakter figyelmen kívül marad, amikor egész számot keres az adatfolyamban. Ez az alapértelmezett mód.

SKIP_NONE: Semmi sem kerül kihagyásra. Ha a soros vételi puffer első értéke nem számjegy, akkor 0-t ad vissza.

SKIP_WHITESPACE: Csak a tabulátorok, szóközök, soremelések és kocsivissza karakterek kerülnek kihagyásra.

A második opcionális paraméter az ignore: a jelzett karakter kihagyásra kerül a keresésben. Ez hasznos lehet, ha vannak olyan ismert karakterek, amelyeket nem szeretnénk határolóként használni a számokban. Használhatjuk többek között tizedesvessző vagy perjel kihagyására.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

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

void loop()
{
  if(Serial.available() > 0) 
  {
    unsigned long myInt = Serial.parseInt(SKIP_NONE, ',');
    Serial.println(myInt);
  }
}

Vissza a tartalomjegyzékhez.

A Serial.parseFloat() visszaadja az első érvényes lebegőpontos számot a soros pufferből. Az első nem lebegőpontos szám karakter befejezi a Serial.parseFloat() függvény futását.  A függvény leáll időtúllépés esetén is. Ezen segíthetünk a setTimeout() függvénnyel.

A Serial.parseFloat() függvényt opcionális paraméterekkel is meghívhatjuk.

Serial.parseFloat()
Serial.parseFloat(lookahead)
Serial.parseFloat(lookahead, ignore)

Az első opcionális paraméter a lookahead, előretekintési módnak nevezik. Három előre meghatározott értéke lehet.

SKIP_ALL: a mínuszjelen, a tizedesvesszőn és a számjegyeken kívül minden karakter figyelmen kívül marad, amikor lebegőpontos számot keres az adatfolyamban. Ez az alapértelmezett mód.

SKIP_NONE: Semmi sem kerül kihagyásra, és nem érintik meg a folyamot, hacsak az első várakozó karakter nem érvényes.

SKIP_WHITESPACE: Csak a tabulátorok, szóközök, soremelések és kocsivissza karakterek kerülnek kihagyásra.

A második opcionális paraméter az ignore: a jelzett karakter kihagyásra kerül a keresésben. Ez hasznos lehet, ha vannak olyan ismert karakterek, amelyeket nem szeretnénk határolóként használni a számokban.

Vissza a tartalomjegyzékhez.

A Serial.setTimeout() beállítja a maximális időt ezredmásodperben, amíg a soros adatokra várni kell. Az alapértelmezett érték 1000ms.

long time = 800; // 800ms

void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(time);
}
void loop()
{
  
}

Vissza a tartalomjegyzékhez.

A Serial.peek() bejövő soros adatok következő bájt (karakter) decimális értékét adja vissza anélkül, hogy eltávolítaná azokat a belső soros pufferből. Vagyis a Serial.peek() egymást követő hívásai ugyanazt a karaktert adják vissza, mint a következő Serial.read() hívás. Tehát a Serial.peek() lehetővé teszi, hogy a következő karaktert olvasás nélkül elemezze, hogy releváns-e számára.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

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

void loop()
{
  if(Serial.available() > 0) 
  {
    if(isDigit(Serial.peek()))
    {
      Serial.println("Ez egy szám!");
      char myChar = Serial.read();
      Serial.println(myChar);
    }
    else
    {
      Serial.println("Ez nem szám!");
      Serial.read();                   // ez kiüríti a bejövő puffert
    }
  }
}

Vissza a tartalomjegyzékhez

A serialEvent() funkció automatikusan meghívásra kerül a loop(), végén amennyiben soros adatok találhatók a soros pufferben. Az alábbi példa kiválogatja a szám karaktereket a soros pufferből.

/*****************************************/
/**       https://myhomethings.eu       **/
/*****************************************/

char myChar ="";
bool flag = false;

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

void loop() 
{
  if(flag) 
  {
    Serial.println(myChar);
    myChar = "";
    flag = false;
  }
}

void serialEvent() 
{
  if(Serial.available() > 0) 
  {
    myChar = Serial.read();
    if(isDigit(myChar))
    {
      flag = true;
    }
  }
}

Vissza a tartalomjegyzékhez