Hlavní navigace

Arduino: měříme a zobrazujeme fyzikální veličiny

2. 9. 2010
Doba čtení: 11 minut

Sdílet

Už jsme si v seriálu představili Arduino jako platformu pro vytváření internetových zařízení a ukázali jsme si různé variace tohoto vývojového kitu. V dnešním dílu uděláme krok stranou. Arduino totiž často slouží nejen jako řídicí modul, ale i jako měřicí zařízení – stačí připojit patřičná čidla…

Analogový vstup

Arduino nabízí šest analogových vstupů, na které můžeme připojit například různá analogová čidla – termistory, fotorezistory a jiná zařízení, která podle různých fyzikálních veličin mění buď výši generovaného napětí, nebo svůj odpor. Analogové vstupy totiž slouží jako jednoduchý voltmetr. Interně jsou řešené jako desetibitové A/D převodníky. Co to znamená? To znamená, že vstupní rozsah 0 až Aref voltů je rozdělena na stupnici, která má 1024 dílků (to je těch 10 bitů). Když připojíme na analogový vstup napětí v daném rozsahu, je jeho velikost změřena a výsledkem je číslo 0–1023, které udává velikost naměřeného napětí. (Tedy – teoreticky. V praxi mají měřiče různé nelinearity a nepřesnosti, ale ty můžeme pro naše účely zanedbat).

Co je ta výše zmíněná tajemná hodnota Aref? To je Analogové REFerenční napětí. Pokud jste si dobře prohlíželi desku Arduina, nalezli jste stejně popsaný vstup. Pokud na něj nic nezapojíte, chová se, jako by byl roven napájecímu napětí, tedy 5 V. V takovém případě je vstupní rozsah analogového vstupu 0 až 5 voltů a každý „dílek“ naší desetibitové měřicí stupnice odpovídá přibližně pěti milivoltům (0.0048828…)

Poznámka: Pokud vaše XXduino je nakonfigurované tak, že používá interně 3.3V, bude Aref právě 3.3V.

Pokud byste připojili na Aref napětí jiné (NEDĚLEJTE TO, dokud nebudete číst dál), třeba pokud přivedete 3.3 V ze zabudovaného stabilizátoru, zmenší se vstupní rozsah napětí (0 – 3.3), ale měřicí škála bude o něco jemnější – změna naměřeného údaje o 1 bude představovat změnu o 3 milivolty (0.0032226).

To, jestli bude referenční napětí rovno napájecímu nebo tomu na Aref nebo ještě jinému určuje funkce analogReference(), která má jeden parametr. Ten je buď DEFAULT, tedy Aref je napojeno na napájecí napětí, nebo INTERNAL (pak je Aref připojeno na interní generátor referenčního napětí, které je u nových Arduin s procesory 168 a 328 rovno 1.1V), nebo konečně EXTERNAL (a pak je Aref bráno z pinu Aref). Pokud chcete připojovat nějaké vlastní referenční napětí na vstup Aref, musíte bezpodmínečně nejprve přepnout referenci na EXTERNAL!!! Jinak hrozí riziko zničení procesoru.

Praktická ukázka

Dosti teorie, pojďme k praxi. Následujícím jednoduchým programem budeme číst údaje z analogového vstupu 1 a budeme je posílat na sériové rozhraní:

 void setup() {
   //kdybychom chtěli experimentovat
   //s jiným Aref, nezapomeneme si
   //povolit následující:

   //analogReference(EXTERNAL);

   Serial.begin(38400);
 }

 void loop() {
   // Přečteme vstup 0
   int hodnota = analogRead(0);
   // a výsledek pošleme na sériový port jako číslo
   Serial.println(hodnota);
   // počkáme, aby se převodník mohl ustálit, a pak celý postup zopakujeme
   delay(10);
 }

Program nahrajeme do Arduina, spustíme sériovou konzoli (nezapomeňte změnit rychlost z 9600 na 38400) a sledujeme ubíhající řadu čísel. Když si zkusíte propojit Analog Input 0 se zemí (Gnd), změní se hodnota na 0, když jej spojíte s napájecím napětím, bude to 1023 (údaje se mohou drobně lišit vlivem nepřesností při převodu). Voila, máme jednoduchý voltmetr!

Processing

Věřím, že mnohým z vás by ubíhající čísla v terminálu připadala jako dostatečně použitelné uživatelské rozhraní („Každý si přece může jasně přečíst hodnotu a prostým přepočtem zjistit napětí, a kdo ne, tak je lama“), ale my přesto zvolíme uživatelsky příjemnější způsob, a to nástroj (či jazyk?) Processing.

Důkladní čtenáři tohoto seriálu si jistě vzpomenou, že o Processingu už řeč byla; to když jsme si říkali o inspiraci, z níž Arduino vyšlo. Když se podíváte na stránky Processing.org a stáhnete si běhové prostředí Processing, zjistíte, že je vám velmi povědomé – IDE je až na detaily naprosto shodné s Arduinem, syntaxe a styl práce je rovněž téměř shodné, rozdíl je jen v určení: Zatímco Arduino IDE slouží k překládání programů pro kity Arduino, Processing překládá programy, které běží na desktopu.

Cílem autorů Processingu bylo totiž nabídnout jednoduché programovací prostředí pro vytváření vizualizačních programů k hardwarovým kitům. Takže v referenci k jazyku nalezneme nejen běžné funkce jako v každém minimálním C/C++, ale i funkce pro práci se sériovým portem (pro komunikaci s vývojovým kitem), pro práci s myší a klávesnicí, pro ukládání či načítání dat, a především pro práci s grafikou.

Samosebou by šlo napsat pro podobné účely jednorázový program v C/C++/Pythonu/Doplňtesi­vášoblíbenýjazyk, ale Processing přináší významné zjednodušení: Odstíní vás od problémů s grafikou a periferiemi, soustředíte se jen na to, jak se mají zpracovávat data, a neřešíte, jestli použít SDL nebo Allegro nebo volání systému nebo… co všechno. Navíc je napsaný v Javě a funguje jak na Linuxu, tak i na Macu a Windows.

Processing by zasloužil samostatný článek, a nevylučuju, že se k němu na stránkách Rootu ještě někdy vrátíme a představíme si jej podrobněji. Kromě práce se sériovým portem totiž umožňuje některé zajímavosti, jako snadnou komunikaci po síti, použití OpenGL, nahrávání videa a další pěkné věci. Ale ty my nebudeme tentokrát používat, omezíme se jen na základní grafické elementy.

Processing používá, podobně jako Arduino, dvě základní funkce setup() a draw(), které obsahují rutiny potřebné pro start programu, a pak akce, které se budou vykonávat v nekonečné smyčce. V našem příkladu smyčku samotnou nevyužijeme, místo ní budeme zpracovávat došlá data, zachycená ze sériového portu – Processing k tomu účelu nabízí hook funkci. Pokud jste si spustili předchozí příklad, tak se nám budou na portu objevovat čísla 0–1023, oddělená znakem „nový řádek“.

import processing.serial.*;

//Odsud čteme data
Serial myPort;

void setup () {
  size(200, 100);
  background(100);

  println(Serial.list());
  // Na mém PC obsadilo Arduino třetí sériový port,
  // takže používám Serial.list()[2].
  // Vy si upravte dle potřeby.
  myPort = new Serial(this, Serial.list()[2], 38400);
  // Počkáme si na znak nového řádku:
  myPort.bufferUntil('\n');

  lastx = width/2;
  lasty = height;

  arc(width/2, height, width, width, PI, 2*PI);

  for (int v = 1; v < 5; v++) {
    stupnice( v * ( PI / 5 ) );
  }

}

//Pomocné proměnné pro malování ručičky měřidla
int lastx, lasty;

void hand(float ang) {
  int x = floor(cos(ang+PI) * (width/2.2) + width/2);
  int y = height - floor(sin(ang) * (height/1.1));

  stroke(255);
  line(width/2, height, lastx,lasty);

  stroke(0);
  line(width/2, height, x,y);

  lastx = x;
  lasty = y;
}

void stupnice(float ang) {
  int x1 = floor(cos(ang+PI) * (width/2.2) + width/2);
  int y1 = height - floor(sin(ang) * (height/1.05));
  int x2 = floor(cos(ang+PI) * (width/2) + width/2);
  int y2 = height - floor(sin(ang) * (height));
  stroke(10);
  line(x1, y1, x2, y2);
}

void draw() {
   ; //prázdné
}

void serialEvent(Serial myPort) {
  String inString = myPort.readStringUntil('\n');
    if (inString != null) {
    // odsekneme znak konce řádku
    inString = trim(inString);

    // převedeme na číslo
    float inByte = float(inString);

    //mapujeme získané číslo v rozsahu 0-1023 na rozsah 0-PI
    inByte = map(inByte, 0, 1023, 0, PI);

    //namalujeme ručičku měřidla
    hand (inByte);
    }
}

Ručkové měřidlo v Processing. Analog 0 je připojen na 3.3V

Příklad je natolik jednoduchý, že byste se v něm se základní znalostí C-like jazyků neměli ztratit. Upravte si především číslo sériového portu, na němž běží vaše Arduino – u mne to je třetí sériový port, u vás to bude pravděpodobně jiné. Program samotný je přímočarý – v setup() si nejprve nastavíme příjem ze sériového portu, namalujeme vyplněný oblouk a na něj přiděláme, pro snazší orientaci, několik dílků stupnice. Funkce draw() zůstane prázdná, namísto toho obsluhujeme serialEvent, tedy příchozí znaky. Načítáme je až dokud nenarazíme na znak nové řádky. Pak přijatý řetězec zkonvertujeme na číslo, to namapujeme na rozsah 0 až PI (tedy 0 až 180 úhlových stupňů) a namalujeme ručičku měřidla. Funkce pro malování ručičky maže po sobě předchozí namalovanou ručičku. Výsledek tak může vypadat třeba tak, jak je vidět na obrázku.

Můžete si zkusit připojovat na analogový vstup 0 nejrůznější čidla, baterie, můžete si vyzkoušet, zda LED, pokud na ni posvítíte, nefunguje náhodou i jako fotodioda (nezapomeňte si předtím zmenšit maximální napětí, nejlépe přepnutím Aref na interní zdroj), můžete si zkusit zda na diodě zahřáté například plamenem zapalovače nebude vznikat napětí, můžete si zkusit udělat jednoduchý termočlánek z drátů z různých materiálů (např. měď a ocel) a zkusit, jestli z něj nevydolujete nějaké napětí, a můžete si ostatně vyzkoušet i to, zda vydechováním tabákového dýmu do vody nevznikne zlato.

Pokusy s teplem a vlhkostí

Pokud jste si už prošlapali všechny předchozí slepé cestičky, tak se můžeme vrátit k Arduinu a snímání fyzikálních veličin. Některé snímače neposílají údaje v podobě analogových veličin, ale rovnou jako číslo. Nejčastěji pak po jednoduché sériové sběrnici, jako je 1-Wire, I2C či SPI. Pro následující zapojení jsem vybral čidlo SHT15, které vyrábí společnost Sensirion, a které měří teplotu a relativní vlhkost. S okolím toto čidlo komunikuje po sběrnici I2C, což je poměrně známý standard sériové sběrnice, kde je jeden vodič vyhrazen pro hodinové pulsy a druhý pro poloduplexní výměnu dat. Toto čidlo je sice určeno pro povrchovou montáž, ale naštěstí Sparkfun dodává tzv. Breakout kit, což je destička s tímto čidlem a s vývody ve standardní rozteči 2.54. Navíc obsahuje i pull-up odpory, potřebné pro správnou funkci sběrnice I2C. Do ČR je dováží např. HW Kitchen.

Destička s čidlem má čtyři piny – napájení VCC, zem GND, SCK pro hodiny a DATA. Já jsem jej zapojil pomocí nepájivého kontaktního pole a několika drátů. Napájecí napětí jsem vzal z Arduina a sběrnici jsem připojil tak, že pin 10 jsem spojil s DATA a pin 11 se SCK. Arduino má knihovny pro komunikaci po sběrnici I2C, ale co víc – s trochou googlení se mi podařilo najít knihovnu SHT1× od autorů z webu Practical Arduino. To mi umožnilo přeskočit celou dlouhou pasáž pokusů a omylů a napsat rovnou jednoduchý program:

#include <SHT1x.h>

// Zadáme čísla pinů použitých pro SCK a DATA
#define DATA  10
#define SCK 11
SHT1x cidlo(DATA, SCK);

void setup()
{
   Serial.begin(38400);
   Serial.println("Go");
}

void loop()
{
  float teplota;
  float vlhkost;

  // Načtení hodnot
  teplota = cidlo.readTemperatureC();
  vlhkost = cidlo.readHumidity();

  // Pošleme je na sériový port
  Serial.print(teplota, DEC);
  Serial.print(" ");
  Serial.println(vlhkost, DEC);
}

Přeložíme, spustíme, a pokud je teplota –42, znamená to, že jsme udělali něco špatně a čidlo neměří. Pokud ale máte štěstí, uvidíte v sériovém terminálu dva sloupce čísel. První číslo je teplota, druhé relativní vlhkost. Teplota je ve stupních Celsia, vlhkost v procentech (0 = absolutní sucho, 100 = vodní pára).


Zbastleno jest! Stačilo Arduino, čtyři dráty z rozpleteného UTP kabelu a nepájivé kontaktní pole.

Po chvíli se hodnoty ustálí (tedy pokud nemáte na pracovišti zrovna vylitou vodu anebo horké kafe poblíž čidla). Po ustálení si můžete zkusit třeba… hmmm… dýchnout na čidlo. Uvidíte, jak se vlhkost i teplota o něco zvýší. To proto, že lidský dech má zhruba teplotu lidského těla a zpravidla i vyšší vlhkost.


Detail připojení čidla SHT15

Grafy sem!

Dva sloupce čísel možná někomu připadají jako dostatečné UI, ale … nepsal jsem to už? Každopádně když máme Processing a kostru pro čtení dat ze sériového portu, tak proč ji nepoužít? Tentokrát ale nebudeme kreslit ručkové měřidlo, ale graf. Rovnou dva.

import processing.serial.*;

Serial myPort;

// počáteční bod grafu
int xPos = 2;

//rozsah zobrazovaných hodnot
int tMin = 0;
int tMax = 30;

//pomůcka pro kreslení grafů - poslední hodnoty Y
int ylast=0;
int hlast=0;

//vykreslení mřížky
void grid() {
  background(0);

  for(int x=20; x<30;x++) {
    float inByte = map(x, tMin, tMax, 0, height);
    stroke(24,24,24);
    line(0, height-inByte, width, height - inByte);
  }
}

void setup () {
  size(400, 300);

  println(Serial.list());
  myPort = new Serial(this, Serial.list()[2], 38400);
  myPort.bufferUntil('\n');

  grid();
}

void draw () {
  //zase nic
}


void serialEvent (Serial myPort) {
  // první řetězec končí mezerou, druhý znakem "nová řádka"
  String inString = myPort.readStringUntil(' ');
  String inString2 = myPort.readStringUntil('\n');

  if (inString != null) {

    //zpracování teploty - modrý graf
    inString = trim(inString);

    float inByte = float(inString);
    inByte = map(inByte, tMin, tMax, 0, height);

    stroke(127,34,255);
    line(xPos-2, ylast, xPos, height - inByte);

    ylast = int(height - inByte);

    //zpracování vlhkosti - červený graf
    inString2 = trim(inString2);

    inByte = float(inString2);
    inByte = map(inByte, 0, 100, 0, height);

    stroke(255,34,127);
    line(xPos-2, hlast, xPos, height - inByte);

    hlast = int(height - inByte);


    // na konci okna začneme zase od začátku
    if (xPos >= width) {
      xPos = 2;
      grid();
    }
    else {
      // posuneme pisátko
      xPos+=2;
    }
  }
}

Přeložíme, spustíme – a kreslíme! Platí opět totéž jako u předchozího Processingu, tedy že je potřeba nastavit správně číslo sériového portu (pomůže ladicí výpis, který je v programu zabudován). Mřížka zobrazuje vodorovné čáry po 1° C od 20 do 30 stupňů pro snazší orientaci. Modrá čára znázorňuje teplotu, červená (dobrá, tak tedy růžová) relativní vlhkost. Lze tak sledovat vývoj těchto dvou veličin v čase, a lze i experimentovat. Takhle třeba vypadá dýchnutí na čidlo:


Dýchnul jsem si rovnou dvakrát

A takhle kápnutí malé kapky vody na horní část čidla:


Voda se pomalu odpařuje, teplota klesla, vlhkost stoupla

Doufám, že po tomhle článku nebudou pánové z HW Kitchen litovat, že nám čidlo k testování zapůjčili, protože po pokusu s dechem a vodou jsem zkusil kápnout na kryt trochu isopropylalkoholu (když říkám kapku, tak tím myslím množství „co se na špičku jehly vejde“) Graf vypadal takto:


Že by v tom lihu byla voda? :)

Pro zajímavost ještě kápnutí malé kapky zkapalněného propan-butanu (během sekundy se odpaří a prudce zchladí celé čidlo). Graf se mi moc líbil, tak si ho ještě ukážeme – vypadal takto:

root_podpora


Týrání čidla kapalným propan-butanem (teplota prudce klesla k nule)

Doufám, že si vlastním experimentováním svoje čidlo nezničíte – budeme ho potřebovat i příště, to si s ním uděláme takovou malou pokusnou meteostanici…

Na závěr – Příklady k dnešnímu dílu ke stažení

Děkujeme provozovateli obchodu Czechduino za laskavé zapůjčení Arduina pro účely testování a psaní tohoto článku. SHT15 Breakout board k otestování laskavě zapůjčil obchod HW Kitchen. Děkujeme.

Byl pro vás článek přínosný?

Autor článku

Martin Malý je autorem serveru Bloguje, mikroblogu Teidu či služby pro zkracování odkazů Jdem.cz. Vedl také magazín Zdroják.