Dnes se podíváme, jak vytvořit uživatelsky definované funkce a jakými způsoby může Arduino měřit teplotu.
1.9.2013 18:00 | Zbyšek Voda | přečteno 27684×
Při vymýšlení projektů typu "co mám dělat teď?" nás jistě někdy napadla zapojení, ve kterých je potřebné zjistit teplotu okolí. Mohly by to být: meteostanice, termostaty, teploměry a podobné. Elektronických součástek, které mohou měřit teplotu, je ale celá řada a proto nelze napsat článek, který by je obsáhl všechny. Pro ukázkové účely tedy vybereme zástupce každé z hlavních skupin. Prvním z nich jsou ty, které s čipem komunikují digitálně, druhé analogově s lineárním průběhem a třetí analogově s jiným průběhem. Poslední skupinou teploměrů jsou ty, které mají některé z čipů ATmega zabudované přímo v sobě. Také si ukážeme, jak se dají v jazyce Wiring vytvořit funkce.
S funkcemi jsme se v průběhu seriálu již setkali, byly jimi například funkce analogRead(), serial.println() a další. Tyto funkce však obsahuje jazyk Wiring již v samotném základu, čili existují bez přičinění uživatele. V některých případech by se však hodila možnost vytvořit si vlastní funkce. Patří mezi ně například situace, kdy se některá část kódu má opakovat stále dokola, ale nevyhovuje nám použití cyklu. Pomocí funkce můžeme tento kód "zabalit" do jakéhosi softwarového balíčku a tím umožnit jeho vícenásobné používání.
Hned na začátek je nutno říct, že i funkce musí mít datový typ. Může to být typ int, double a další. Také se setkáme s doteď nezmíněným typem void. Ten se používá, pokud funkce nevrací žádnou hodnotu, jen provádí určité příkazy. Deklarace (nastavení) funkce musí být provedeno mimo tělo funkcí void loop() a void setup(). To, že je funkce deklarovaná, ještě neznamená, že bude probíhat automaticky. Pro vykonání příkazů funkce ji musíme v požadovaném místě kódu "zavolat".
Našim cílem bude vytvoření jednoduché funkce, která změří hodnotu na A0, počká sekundu a sečte ji s novou hodnotou A0. Poté vše vypíše pomocí Serial.println(). Naše funkce nebude vracet žádnou hodnotu, bude tedy mít datový typ void.
Začneme "obalem" funkce.
void mereni(){ //prázdné závorky jsou nutné, aby program věděl, že jde o funkci
...
}
Nyní přidáme funkci její funkčnost.
void mereni(){
int x = analogRead(A0);
delay(1000);
int y = analogRead(A0);
Serial.print("Merim: ");
Serial.println(x+y);
delay(1000);
}
Nyní příjde na řadu volání (provedení) funkce.
void setup(){
Serial.begin(9600);
}
void loop() {
mereni(); //volání funkce mereni()
}
Nyní si ukážeme, jak deklarovat funkci s parametry, která bude vracet určitou hodnotu. Parametr je vstupní informace, kterou předáváme funkci v závorkách. Názvy parametrů si můžeme zvolit a pomocí nich s daty v těle funkce pracovat stejně, jako s proměnnými. Pokud funkce dostává více parametrů, oddělují se čárkou. Nejjednodušším příkladem je součet dvou čísel typu int a vrácení jejich hodnoty (nevypíše, pouze vrátí hodnotu).
int soucet(int a, int b){ //říkáme funkci, že budeme pracovat se dvěma čísly (a, b) typu int
return a+b; //slovo return je zde důležité, tím funkci říkáme, jakou hodnotu má vrátit
}
Následuje zavolání této funkce s parametry 20 a 50.
void setup(){
Serial.begin(9600);
Serial.println(soucet(20,50));
}
void loop() {
}
Teď už se můžeme podívat na způsoby měření teploty pomocí Arduina.
Hned na začátek je nutno upozornit, že přesnost těchto senzorů je značně nejistá. Jelikož jsou součástí pouzdra čipu, jsou snadno ovlivnitelné teplem, které při práci čipy produkují (výchylka 10 °C je zcela běžná). Někdy jsou však plně dostačující. K měření okolní teploty bez větších výchylek jsou vhodné jenom v případě, že bylo Arduino minimálně 10 minut nečinné. Hodí se ale k sledování teploty při náročných operacích (například když čipem protéká větší proud), kdy můžeme předejít poškození čipu včasným odstavením desky. Největší nevýhodou je, že ho neobsahují všechny čipy, které na Arduinu najdeme.
Podpora teploměrů v čipech:
ATmega8 | Ne |
ATmega8L | Ne |
ATmega8A | Ne |
ATmega16 | Ne |
ATmega168A | Ano |
ATmega168P | Ano |
ATmega328 | Ano |
ATmega328P | Ano |
ATmega1280(Arduino Mega) | Ne |
ATmega2560(Arduino Mega 2560) | Ne |
ATmega32U4(Arduino Leonardo) | Ano |
long readTemp() {
long result;
// Read temperature sensor against 1.1V reference
ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = (result - 125) * 1075;
return result;
}
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println( readTemp(), DEC );
delay(1000);
}
Výstupem jsou tisíciny °C. Výsledek pak tedy stačí vydělit 1000 a získáme naměřenou teplotu v °C.
Druhou skupinou teploměrů jsou ty, které nám zasílají naměřené hodnoty digitálně. Bývají často nejpřesnější a na jednu sběrnici je jich možné připojit i více (záleží na typu). Pro ukázkové účely jsem vybral senzor DALLAS 18B20 od firmy Maxim Integrated. Ten komunikuje pomocí 1-Wire sběrnice, nepotřebuje externí napájení, rozsah hodnot má -55 až +125°C a má nastavitelné rozlišení 9-12 bitů. Každý senzor má vlastní unikátní 64-bitové číslo, které slouží k jeho identifikaci (podobně jako IP adresa u PC). Díky tomu je možné připojit i více těchto teploměrů na jeden datový vodič. Největší výhodou je, že pro Arduino existuje knihovna určená k práci s tímto teploměrem. Na obrázku vidíte pinout teploměru a schéma zapojení více senzorů na jednu sběrnici.
Pokud máme vše zapojeno podle schématu, nezbývá nám, než zprovoznit teploměr i na straně softwaru. Pro komunikaci po 1-Wire sběrnici budeme potřebovat knihovnu OneWire. Její dokumentaci i odkaz ke stažení nalezneme na oficiálních stránkách Arduina. Tato knihovna obsahuje i příklad (v Examples) pro připojení tohoto teploměru. Po stažení stačí rozbalit obsah knihovny OneWire do složky libraries a zapnout Arduino IDE (popř. restartovat).
Poté v Examples rozklikneme OneWire a vybereme soubor DS18x20 Temperature. Ten nahrajeme do Arduina a otevřeme Serial monitor. Vypisovaný text by měl obsahovat nejenom naměřené hodnoty, ale i informace o teploměru (adresa atd.), což je pro běžné účely moc informací. Po prohlédnutí kódu a promazání výpisu zbytečných informací se ale dá výpis lehce přizpůsobit jen pro zobrazování teploty. Příklad výpisu vidíte níže:
ROM = 28 C9 9F BA 3 0 0 A1
Chip = DS18B20
Data = 1 94 1 4B 46 7F FF C 10 26 CRC=26
Temperature = 25.25 Celsius, 77.45 Fahrenheit
No more addresses.
Po upravení může výpis vypadat například takto:
Temperature = 27.62 Celsius, 81.72 Fahrenheit
Další skupinou jsou teplocitlivé součástky, které na změnu teploty reagují změnou odporu. U této skupiny má změna lineární charakter. Například vždy při zvýšení teploty o 1 °C se změní odpor součástky o 100 ohmů, a to v celém měřitelném rozsahu. Pokud bychom přenesli závislost odporu na teplotě do grafu, výsledný obrazec by byla přímka (linea -> lineární průběh).
Pro prezentační účely jsem vybral teploměr LM35. Ten umí měřit teploty buďto v rozsahu +2 - +150 °C, nebo -55 - +150 °C (podle zapojení). V menším rozsahu výrobce uvádí přesnost 1/4 °C a ve větším přesnost 3/4 °C. Převod výstupního napětí je 10 mV/°C. Jeho popis nalezneme i na české wikipedii společně s doporučeným schématem pro zapojení. Pro demonstraci si vybereme lehčí zapojení s rozsahem hodnot nad 0 °C a jako zdroj použijeme 5V z Arduina. Výstup teploměru připojíme na A2. Pro získání výsledné hodnoty budeme potřebovat trochu matematiky: Arduino umí rozlišit 1024 analogových hodnot v rozsahu 0 až 5V. Nám tedy stačí převést naměřené hodnoty z rozsahu 0-1023 do rozsahu 0-500, abychom dostali skutečné napětí měřené v desítkách mV. To totiž odpovídá převodu 10 mV/°C. Tímto převodem získáme skutečnou teplotu ve °C.
Kód vypisující naměřenou teplotu vypadá takto:
void setup(void) {
Serial.begin(9600);
}
void loop(void) {
float temp = analogRead(A2);
temp = map(temp, 0, 1023, 0, 500);
Serial.print("Namereno: ");
Serial.println(temp);
delay(1000);
}
Když tyto součástky nazývám teploměry, trochu jim lichotím. Jde o termistory, což jsou součástky, které se změnou teploty mění svůj odpor. Dělí se na pozitivní a negativní. U pozitivních odpor s teplotou vzrůstá. Nárůst je zde skokový, proto najdou více než při měření teploty využití spíše v automatizaci, kde je potřeba například sepnout obvod při dané teplotě. U negativních termistorů je grafem křivka.
Podívejme se ale na celý problém prakticky. Pro ukázku jsem vybral tento 10 kOhm termistor, jehož datasheet nalezneme zde. Ten zapojíme do děliče napětí společně s 10 kOhm rezistorem. Vývod připojíme na pin A0.
Napětí vycházející z děliče vypočteme následovně: Analogový vstup rozlišuje 1024 hodnot (0-1023). Vydělíme-li 5V/1024 získáme velikost jednoho "dílku" stupnice ve voltech. Naměřená hodnota na A0 je tedy počet těchto dílků. Když vynásobíme velikost jednoho dílku naměřenou hodnotou, získáme skutečné napětí na A0.
Pro výpočet odporu termistoru použijeme dělič napětí. Přesněji řečeno vzorec z anglické wikipedie.
Kde Vin je 5V (výstup Arduina), Vout je naměřená hodnota napětí na A0. R1 je odpor termistoru, R2 je odpor rezistoru (u nás 10 kOhm).
Funkce pro výpočet odporu termistoru by mohla vypadat takto:
float mereni(){
int aread = analogRead(A0);
float V2 = (V1/1023) * aread; //U2 obsahuje hodnotu napětí na A0
float R1 = ((R2*V1)/V2)-R2;
return R1;
}
#include <math.h> //zde se středník na konci řádku nepíše
float mereni(){
int aread = analogRead(A0);
float V2 = (V1/1024) * aread; //U2 obsahuje hodnotu napětí na A0
float R1 = ((R2*V1)/V2)-R2;
float T = 1/((1/T0)+((1/B)*log(R1/R0))); // výstupem je teplota v Kelvinech
float TC = T - 273.15;
return TC;
}
#include <math.h>
int R2 = 10000; //velikost odporu připojeného k děliči
float V1 = 5.0; //napětí z Arduina
float T0 = 298.15; //teplota v Kelvinech
float B = 4300; //tabulková hodnota
float R0 = 10000; //hodnota odporu termistoru při 25°C
float mereni(){
int aread = analogRead(A0);
float V2 = (V1/1024) * aread; //U2 obsahuje hodnotu napětí na A0
float R1 = ((R2*V1)/V2)-R2;
float T = 1/((1/T0)+((1/B)*log(R1/R0))); // výstupem je teplota v Kelvinech
float TC = T - 273.15; //převode teploty z K na °C
return TC;
}
void setup(){
Serial.begin(9600);
}
void loop() {
Serial.println(mereni());
delay(1000);
}
Pokud porovnáte hodnoty všech použitých měřících technik, mnohdy zjistíte, že se od sebe podstatně liší. Mělo by však platit, že při použití s Arduinem jsou nejpřesnější kalibrované digitální teploměry a poté kalibrované analogové teploměry (zde vznikají odchylky kvůli převodníku Arduina, který nemusí mít dostatečně přesnou stupnici). U termistorů a vnitřních teploměrů je přesnost různá a může se lišit typ od typu.