Close

Arduino a Processing

Graf hodnot odeslaných ze slideru Arduino Esplora

V minulém díle jsme si představili prostředí Processing. I dnes se jím budeme zabývat, ale ukážeme si, jak může Arduino s tímto prostředím komunikovat. Na příkladech si ukážeme funkce pro zpracování dat ze sériové komunikace.

 

Úvod

Jak už asi mnohé napadlo, komunikace mezi Processingem a Arduinem bude probíhat po sériové lince. Nabízí se nám ale dva možné způsoby komunikace a ovládání Arduina. Pro tento účel totiž existuje speciální firmware Firmata. Ten se nahraje do Arduina a poté se stará o veškeré jeho ovládání. Následně stačí přidat do Processing knihovnu arduino a začít programovat. Tato cesta má tedy výhodu v tom, že odpadá nutnost pracovat ve dvou prostředích najednou, avšak za cenu omezení funkcí, které umí. Druhou cestou je naprogramovat si Arduino podle svých představ, odesílat z něj data a ty poté zpracovávat v Processing. Tento způsob je sice pomalejší, ale neomezuje nás v použití celého arzenálu Arduina. My si obě cesty představíme.

 

Firmata

Nastavení

V první řadě musíme nastavit Arduino tak, aby mohlo s Processing komunikovat. Tato část je velmi jednoduchá – Firmata totiž IDE obsahuje už od stažení. V Examples rozbalíme nabídku Firmata a otevřeme program StandardFirmata a nahrajeme ho do Arduina. Tím jsme vyřešili veškeré nastavování na straně Arduina. Jednoduché, že?

Poté přichází na řadu knihovna pro Processing. Tu stáhneme zde ve formátu zip. Stažený archiv rozbalíme, a složku arduino umístíme do Dokumentů > Processing > libraries. Pokud právě prostředí běží, změny se projeví až po jeho restartu. Jako ukázkový příklad si v Processing otevřeme Examples > Contributed Libraries > Arduino (Firmata) > arduino_input, popřípadě arduino_input_mega (pokud máme Arduino Mega). Tento příklad zkoumá všechny digitální i analogové piny. Pokud je na nějakém hodnota HIGH, vybarví se jemu odpovídající čtvereček. U analogových pinů zkoumá jejich hodnotu a vykresluje kružnice podle její velikosti.

 

Propojení

V Processing si vytvoříme nový program. Hned na začátku musíme určit, jaké doplňkové knihovny budeme potřebovat. V našem případě se jedná o knihovnu, která umí pracovat se sériovou linkou (ta je v Processing od začátku) a knihovnu pro práci s Arduinem (tu, kterou jsme před chvílí přidali). V dalším kroku vytvoříme prázdnou proměnnou pro naše Arduino, se kterým budeme dále pracovat. Všimněme si, že slovo Arduino při vytváření objektu zde funguje stejně jako u datových typů. Základní nastavení si ukažme na příkladu.

//vložení knihoven
import processing.serial.*;
import cc.arduino.*;

//vytvoření proměnné mojeArduino
Arduino mojeArduino;

void setup() {

}

void draw() {

}

Tímto jsme vytvořili základní objekt pro práci s Arduinem. V dalším kroku si vypíšeme všechna zařízení dostupná přes sériovou linku. Seznam těchto zařízení získáme funkcí Arduino.list(), která jej vrací jako pole. Většinou však je v poli jediná hodnota a to právě naše připojené Arduino. Poté přiřadíme k proměnné mojeArduino nový objekt. To provedeme pomocí new Arduino(this, Arduino.list()[0], 57600), kdy první parametr je vždy this, druhým parametrem je sériový port s naším Arduinem (v tomto případě port s indexem 0) a třetím parametrem je rychlost sériové komunikace – zde 57600 baud.

void setup() {
	println(Arduino.list()); //vypíše dostupná zařízení
	//vytvoření nového objektu
	mojeArduino = new Arduino(this, Arduino.list()[0], 57600);
}

Nyní už máme Arduino i Processing připravené ke spolupráci a můžeme se pustit do programování. Sami zjistíte, že dostupné funkce jsou téměř totožné s těmi, které už známe.

 

 

Programování

Knihovna arduino s sebou přináší několik konstant. Patří mezi ně: Arduino.HIGH, Arduino.LOW, Arduino.OUTPUT a Arduino.INPUT. Ty odpovídají konstantám HIGH, LOW a dalším v jazyce Wiring. Dále zde nalezneme nám známé funkce, jako pinMode(), digitalRead(), digitalWrite(), analogRead() a analogWrite(). Ty se však musí psát s názvem vybraného Arduina a tečkou, u nás tedy například mojeArduino.digitalRead(…). U analogových funkcí se navíc nepoužívá A při určování čísla pinu, ale pouze číslo. Jejich použití si ukažme na dvou příkladech.

První z nich pracuje s digitálními funkcemi. Při stisknutí tlačítka na pinu 2 se rozsvítí LED na 13 a zobrazí se bílý kruh na monitoru.

import processing.serial.*;
import cc.arduino.*;

//vytvoření objektu mojeArduino
Arduino mojeArduino;
int led = 13;
int tlacitko = 2;

void setup() {
    size(500,500);
  
    println(Arduino.list()); //vypíše dostupná zařízení
    //vytvoření nového objektu
    mojeArduino = new Arduino(this, Arduino.list()[0], 57600);
    
    mojeArduino.pinMode(tlacitko, Arduino.INPUT);
    mojeArduino.pinMode(led, Arduino.OUTPUT);
}

void draw() {
    background(0);
    if(mojeArduino.digitalRead(tlacitko) == Arduino.HIGH){
        fill(255);
        stroke(255); 
        ellipse(250,250, 450, 450); 
        mojeArduino.digitalWrite(led, Arduino.HIGH);
    }
    else{
        mojeArduino.digitalWrite(led, Arduino.LOW);
    }
}
Processing funkce ellipse()

Funkce ellipse()

V druhém programu si ukážeme použití analogových funkcí. Budeme číst hodnotu na A0 a podle toho rozsvěcovat LED diodu a také zvětšovat a zmenšovat kruh na monitoru.

import processing.serial.*;
import cc.arduino.*;

Arduino mojeArduino;
int led = 13;
int pot = 0;

void setup() {
    size(500,500);
  
    println(Arduino.list());
    mojeArduino = new Arduino(this, Arduino.list()[0], 57600);
}

void draw() {
    background(0);
    int hodnota = mojeArduino.analogRead(pot);
    ellipse(250,250,hodnota/3, hodnota/3);
    mojeArduino.analogWrite(led, hodnota/4);
    
}
Processing funkce ellipse()

Funkce ellipse()

Tímto jsme vyčerpali možnosti tohoto typu komunikace a dostáváme se ke zdlouhavějšímu, ale šikovnějšímu způsobu.

Poznámka: Schválně jsem nezmínil funkci servoWrite(). Servomotory jsme se totiž ještě nezabývali, takže se jí nebudeme zdržovat.

 

 

Vlastní způsob komunikace

Jak už jsem řekl v úvodu, druhý způsob je sice časově náročnější, zato nijak neomezuje funkce Arduina. Abychom mohli začít programovat, musíme se seznámit s funkcemi v Processing pro práci se sériovou linkou. Pro účely ukázek budu používat Arduino Esplora. Po malé úpravě ale budou použitelné pro jakékoliv Arduino.

 

Sériová komunikace

Stejně jako u příkladů s Firmata, i zde pracujeme s knihovnou serial, kterou vložíme pomocí import processing.serial.*;. Vytvoření proměnné pro port a přiřazení objektu je stejné jako u objektu Arduino. Také zde nalezneme funkci pro zobrazení dostupných zařízení, kterou je Serial.list(). Nový objekt vytvoříme new Serial(this, Serial.list()[0], 57600).

import processing.serial.*;

Serial port;

void setup() {
    size(500,500);
      
    println(Serial.list());  
    port = new Serial(this, Serial.list()[0], 57600);
}

void draw() {
  
}

Dále zde nalezneme funkci available(). Jedná se o velmi užitečnou funkci, která vrací počet čekajících bytů v zásobníku pro sériovou komunikaci. Používá se například, když chceme zjistit, jestli vůbec přišla nějaká data. Také se často používá funkce clear(), která zahodí všechna data v zásobníku. Tu použijeme, když se chceme pojistit, abychom nezačali číst nějakou delší informaci uprostřed. Pro přijímání dat použijeme readStringUntil(), která načte ze zásobníku řetězec od jeho začátku až po první výskyt ASCII znaku, který si zvolíme pomocí parametru funkce. Nejčastěji se používá znak zalomení řádku, který má ASCII hodnotu 10. Z Arduino Esplora si nechme posílat data ze slideru. Ty poté vypíšeme v konzoli a budeme vykreslovat kružnici s daným poloměrem. Musíme však pamatovat na to, že přijatá data jsou datového typu String a musíme je tedy před použitím převést na číslo. Do Esplory nahrajeme kód:

#include <Esplora.h>

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

void loop(){
    Serial.println(Esplora.readSlider());
    delay(100);
}

Kód pro Processing může vypadat třeba takto:

import processing.serial.*;

Serial port;
String prijem;
int hodnota;
int eol = 10; //ASCII znak pro zalomení řádku

void setup() {
    size(512,512);
      
    println(Serial.list());  
    port = new Serial(this, Serial.list()[0], 57600);
    port.clear();
}

void draw() {
    fill(color(100,50,200));
    while(port.available() > 0){
        prijem = port.readStringUntil(eol);
        if(prijem != null){
            background(255);
            //funkce trim() ořízne přebytečné znaky (mezery...)
            hodnota = int(trim(prijem));
            println(hodnota);   
            ellipse(256,256,hodnota/2,hodnota/2);
        }
    }       
}
Processing funkce ellipse()

Funkce ellipse()

Tímto jsme si ukázali, jak číst data z Arduina a dostáváme se k odesílání informací do Arduina. K tomu slouží funkce write(). Její parametr může být datového typu byte, char, int, string, nebo pole bytů. Pokud si již nevzpomínáte, jak se se sériovou linkou pracuje u Arduina, můžete si to připomenout v pátém článku. V první ukázce Processing odešle Arduinu Esplora hodnotu červené barvy, podle které se poté rozsvítí LED na desce.

//Arduino program

#include <Esplora.h>

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

void loop(){
    if(Serial.available() > 0){
        Esplora.writeRGB(Serial.read(),0,0);
    }
}
//Processing program
import processing.serial.*;

Serial port;

void setup(){
    size(512,512);
      
    println(Serial.list());  
    port = new Serial(this, Serial.list()[0], 57600);
    
    port.write(10);
}

void draw(){
   
}

Když potřebujeme odeslat více číselných informací najednou, je vhodné použít Wiring funkci Serial.parseInt(), která vyhledá nejbližší vhodné číslo typu integer a vrátí jeho hodnotu. Poté stačí z Processing odeslat řetězec s čísly oddělenými například dvojtečkou a funkce se postará o zbytek. Na ukázce vidíte program, který podle přijatých hodnot bude nastavovat barvu RGB LED u Arduino Esplora.

//Arduino program
#include <Esplora.h>

int r=0, g=0, b=0;

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

void loop(){
    if(Serial.available() > 0){
        r = Serial.parseInt();
        g = Serial.parseInt();
        b = Serial.parseInt();
        
        Esplora.writeRGB(r,g,b);
    }    
}
//Processing program
import processing.serial.*;

Serial port;

void setup() {
    size(512,512);
      
    println(Serial.list());  
    port = new Serial(this, Serial.list()[0], 57600);
    
    port.write("50:20:90");
}

void draw() {
   
}
Sériový monitor Arduino IDE

Sériový monitor

Na závěr ještě zmíníme funkci stop(), která komunikaci se sériovou linkou ukončí.

Poznámka: Nepředstavili jsme si všechny funkce, ale jen ty nejužitečnější. Přehled všech funkcí naleznete v dokumentaci.

 

 

Příklad 1.

V prvním příkladu si ukážeme, jak se dá navigovat po plátně pomocí joysticku u Esplory. Budeme potřebovat:

  1. Arduino Esplora, nebo Arduino s připojeným joystickem či potenciometry
  2. Processing

Z Arduina budeme odesílat dvě hodnoty oddělené mezerou – každou pro jednu osu. Poté budeme na obrazovce vykreslovat malou tečku (kvůli viditelnosti není vhodné použít pouze jeden bod).

//Arduino program
#include <Esplora.h>

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

void loop(){
    Serial.print(Esplora.readJoystickX()+512);
    Serial.print(' ');
    Serial.println(Esplora.readJoystickY()+512);
}
//Processing program
import processing.serial.*;

Serial port;
String x;
String y;
int intx, inty;
int eol = 10; //ASCII znak pro zalomení řádku
int sp = 32; //ASCII znak pro mezeru

void setup() {
    size(512,512);
      
    println(Serial.list());  
    port = new Serial(this, Serial.list()[0], 57600);
    port.clear();
}

void draw() {
   while(port.available() > 0){
       fill(255);
       stroke(255);
       x = port.readStringUntil(sp);
       y = port.readStringUntil(eol);
       if(x != null){
           if(y != null){
               background(0);
               x = trim(x);
               y = trim(y);
               intx = 1024-int(x);
               inty = int(y);
               ellipse(intx/2,inty/2,5,5);
           }
       }
   }
}
Pohyb bodu pomocí joysticku Arduino Esplora

Pohyb bodu pomocí joysticku

Příklad 2

V druhém příkladu si ukážeme, jak se dá v Processing vykreslovat graf z potenciometru.

//Arduino program 
#include <Esplora.h>

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

void loop(){
    Serial.println(Esplora.readSlider());
    delay(10);
}
//Processing program
import processing.serial.*;

Serial port;
String h;
int inth, i = 0;
int eol = 10; //ASCII znak pro zalomení řádku

void setup() {
    size(512,512);
      
    println(Serial.list());  
    port = new Serial(this, Serial.list()[0], 57600);
    port.clear();
    
    background(255);
    fill(255);
    stroke(color(0,0,255));
}

void draw() {
    while(port.available() > 0){
       if(i == 512){
           background(255);
           i = 0;
       }
       h = port.readStringUntil(eol);
       if(h != null){
          inth = int(trim(h));
          line(i,512,i,512-inth/2);
          i++;  
       }
   }
}
Graf hodnot odeslaných ze slideru Arduino Esplora

Graf hodnot odeslaných ze slideru

 

 

V případě jakýchkoliv dotazů či nejasností se na mě neváhejte obrátit v komentářích.

 

Zbyšek Voda

2 Comments on “Arduino a Processing

Kryštof
23.2.2016 at 17:38

Mám dotaz…
a to ze mám dejme tomu arduino které odesílá informace po serialu ve formátu:
Data1,Data2,Data3, … a chci udělat aby se Data1 zobrazovala v Graf1 a Data2 v Graf2 … atd.
Nemám tušení jak v processingu rozpoznat co je Data1 nebo Data2 které jdou z arduina do PC.
Ano máte tam návod na dvě hodnoty za sebou které mají nějaký znak na konci ale já mám 3 a více hodnot oddělené čárkou …

Zbyšek Voda
23.2.2016 at 17:45

Dobrý den. Napadá mě více cest. Například ta, že před každou hodnotou pošlete do jakého grafu se má zaznamenat. Tok dat by mohl například vypadat takto: „D1,10,D2,450,D3,200,D2,346,…“ potom v Processing budete mít vždy dvojici – cíl + hodnota, se kterou můžete dále pracovat, vykreslovat do grafů…

Napsat komentář