Arduino a sériová komunikace

Pokud chceme použít Arduino pro komunikaci s dalšími zařízeními, jako je počítač, další Arduino, nebo úplně jiný mikrokontroler, můžeme k tomu využít sériový UART. Minimálně jeden má každá verze Arduina (např. Uno má jeden, Mega má 4) a je možné jej využít k odesílaní a přijímání dat z/do libovolného zdroje.

V tomto článku se podíváme na to, jak se UART používá a jaké další možnosti komunikace máme.

Co je to UART

Nejdříve jen velmi stručně o tom, co je to UART a k čemu může být dobrý.

Zkratka UART by se dala přeložit jako univerzální asynchronní přijímač/vysílač. Je to kousek hardwaru, který pomocí dvou pinů (většinou označovaných jako RX a TX) odesílá a přijímá data. Jelikož se jedná o asynchronní způsob komunikace, obsahuje přijímač i vysílač vlastní generátor hodinového signálu, kterým se UART řídí. A jelikož je UART univerzální, je také možné rychlost těchto hodin řídit (není stanovena), stejně jako velikost jednoho bajtu, počet stop bitů, paritní bit, atd.

Často můžeme také slyšet zkratku USART. Jedná se v podstatě o to samé, pouze s tím rozdílem, že je USART obsahuje i synchronní režim.

Pro zájemce o podrobnější výklad odkazuji například na anglickou wikipedii a článek UART.

Knihovna Serial

Tato knihovna, která je standardní součástí vývojového prostředí Arduina, používá hardawarový UART, který je na digitálních pinech 0 (RX) a 1 (TX). Tyto piny jsou zároveň připojeny k čipu ATMega8U2 a tím pádem slouží i jako sériová linka spojená s počítačem.

Na webu Arduino.cc naleznete dokumentaci k této knihovně, stejně jako několik jednoduchých příkladů použití.

Velmi jednoduše tak můžeme odeslat z počítače do Arduina libovolná data (toho jsem například využil v nedávném článku o analogovém výstupu). Můžeme použít buď Serial Monitor, který je součástí vývojového prostředí,

"Serial Monitor" v Arduino programovacím prostředí.
"Serial Monitor" v Arduino programovacím prostředí. Stisknutím tlačítka "Send" se odešle znak "k" do Arduina.

nebo programově pomocí jazyka Python (případně jakéhokoliv jiného, záleží na vás a vašem výběru).

>>> import serial
>>> s = serial.Serial(port='/dev/tty.usbmodem641', baudrate=9600)
>>> s.write(chr(107))
1
>>>

Na straně Arduina pak stačí jednoduchý kód, který bude tato data přijímat a dělat s nimi něco smysluplného.

byte value;

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

void loop() {
  if(Serial.available())
    value = Serial.read();

  // V proměnné "value" je přijatá hodnota.
  // Co s ní uděláte je na vás.
}

Opačný směr, kdy chceme z Arduina data odesílat je také možný, dokonce ještě jednodušší.

byte value = 0;

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

void loop() {
  if(value > 255)
    value = 0;
  else
    value++;

  Serial.write(value);
  // V případě, že chcete odesílat řetězce lze použít
  // Serial.print("ASCII text.");
}

Zatím jsme ale vždy komunikovali Arduino-počítač. Pokud chcete komunikovat i s jiným zařízením, je nutné odpojit USB kabel. Arduino Uno má totiž jen jeden UART a ten zároveň slouží jako sériová linka s osobním počítačem přes kabel USB. V případě Arduina Mega je možné použít jiný sériový port, aniž byste museli USB odpojovat.

Tento příklad (funkční pouze na Arduino Mega) je možné nalézt mezi příklady ve vývojovém prostředí. Jmenuje se MultiSerialMega.

void setup() {
  // Inicializace dvou UART rozhraní.
  Serial.begin(9600);
  Serial1.begin(9600);
}

void loop() {
  // Budeme číst z jednoho a zapisovat
  // do druhého rozhraní.
  if (Serial1.available()) {
    int inByte = Serial1.read();
    Serial.print(inByte, BYTE);
  }
}

Knihovna SoftwareSerial

Občas se může stát (hlavně na Uno), že potřebujeme více UART rozhraní, než nám hardware Arduina nabízí. V takovém případě je možné sáhnout po knihovně, která toto sériové rozhraní simuluje softwarově — SoftwareSerial.

Komunikovat lze na kterékoliv dvojici digitálních pinů. Je ovšem mít na paměti, že toto softwarové rozhraní má několik omezení — pracuje pouze do rychlosti 9600 baudů, funkce available() neexistuje, funkce read() je blokující, takže čeká celou dobu, dokud nedorazí nějaké data. To také znamená, že data, která přijdou na UART a Arduino to nebude čekat, se ztratí.

Příklad sem nebudu přepisovat, jelikož je dostupný na Arduino.cc — SoftwareSerialExample.

Knihovna EasyTransfer

Nedávno jsem narazil na velice zajímavou knihovnu, která ještě více ulehčuje odesílaní dat pomocí rozhraní UART. Jejím autorem je Bill Porter a její zdrojový kód je ke stažení na jeho stránkách.

O co jde? Pokud bychom chtěli například komunikovat mezi dvěma Arduino deskami (například chceme aby jedno Arduino provádělo měření a druhé aby výsledky zpracovávalo), je nutné navrhnou si nějaký jednoduchý komunikační protokol, napsat funkce pro odesílání, přijímání, zpracování přijatých dat, ošetření chyb, apod. Knihovna EasyTransfer se nám s tímto úkolem snaží pomoct.

Kód knihovny je relativně malý a je pod licencí Creative Commons. Můžete jej stáhnout ze stránek autora a začít rovnou používat (po stáhnutí rozbalte archiv do adresáře s ostatními knihovnami). Pokud nechcete knihovnu použít přímo, můžete se jí alespoň inspirovat a napsat vlastní (v tom případě bych se rád podíval na váš výtvor, pokud je to možné ;)).

Její použití je velmi snadné. Vytvoříte si strukturu, která bude obsahovat všechna data, co chcete odeslat. Tato struktura bude jak na přijímacím tak i odesílacím Arduinu (je velmi důležité, aby byla na obou stranách struktura stejná). Knihovna následně data odešle, pro kontrolu spočítá i checksum a na druhé straně data zase složí do struktury.

Zde je jednoduchý příklad a použitá struktura:

struct SEND_DATA_STRUCTURE {
  int blinks;
  int pause;
};

Tuto strukturu pak na straně vysílače naplňte a pomocí knihovních funkcí odešlete přes UART.

EasyTransfer ET;
SEND_DATA_STRUCTURE mydata;

void setup() {
  Serial.begin(9600);
  ET.begin(details(mydata), &Serial);

  randomSeed(analogRead(0));
}

void loop(){
  mydata.blinks = random(5);
  mydata.pause = random(5);

  ET.sendData(); // a teď data odešleme
  delay(10000);
}

Na straně přijímače je kód podobný.

EasyTransfer ET;
RECEIVE_DATA_STRUCTURE mydata;

void setup(){
  Serial.begin(9600);
  ET.begin(details(mydata), &Serial);

  pinMode(13, OUTPUT);
} 

void loop(){
  if(ET.receiveData()){
    // V tomto příkladu pouze blikáme diodou,
    // takže jdeme na to!
    for(int i = mydata.blinks; i>0; i--){
      digitalWrite(13, HIGH);
      delay(mydata.pause * 100);
      digitalWrite(13, LOW);
      delay(mydata.pause * 100);
    }
  }

  delay(250);
} 

Na tomto příkladě je vidět, že vysílač vyplní strukturu mydata informacemi o počtu a délce blikání a přijímač pak podle těchto dat bliká diodou na pinu číslo 13. Použití dvou Arduin a sériové komunikace pro takový jednoduchý úkol je zbytečné, jde ale o ukázku knihovny.

Na knihovně je hezké i to, že lze použít se softwarovým UART, k čemuž využívá již dříve popisovanou SoftwareSerial. Další příklady použití lze nalézt mezi zdrojovými kódy.

Ještě na závěr upozorním na jedno omezení: maximální délka struktury, kterou lze pomocí EasyTransfer odeslat je 255 bajtů. Tento zádrhel najdete v souboru EasyTransfer.h:

uint8_t rx_array[255]; //RX packet parsing buffer

Pokud budete potřebovat odesílat větší struktury, není problém si to v kódu upravit.

Závěr

Tímto bych uzavřel kapitolu o sériové komunikaci pomocí Arduina. Jak bylo vidět na příkladech, nejedná se o příliš složité téma, jelikož dostupné knihovny celou úlohu výrazně zjednodušují (ostatně použití UART není složité ani na samotném ATMega328) a vývojář se tak může soustředit na důležitější věci.

Pokud máte k tématu ještě nějaké otázky, qa.uart.cz je místo kam se můžete obrátit.

Články v sérii<< Vývojové prostředí a programování ArduinaArduino a přerušení >>