Arduino X. - měření teploty

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 27679×

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.


Vytváření uživatelských funkcí

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".

Příklad 1.

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()
}

Příklad 2.

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.


Interní teploměry čipů

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:
ATmega8Ne
ATmega8LNe
ATmega8ANe
ATmega16Ne
ATmega168AAno
ATmega168PAno
ATmega328Ano
ATmega328PAno
ATmega1280(Arduino Mega)Ne
ATmega2560(Arduino Mega 2560)Ne
ATmega32U4(Arduino Leonardo)Ano
Pro vyzkoušení tohoto teploměru jsem použil Arduino Leonardo a kód, který je k nalezení zde.

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.


Digitální teploměry

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.
Pinout teploměru DALLAS 18B20Zapojení více teploměrů DALLAS 18B20 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


Analogové teploměry s lineárním průběhem

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. Doporučené zapojení teploměru LM35.
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);
}

Analogové teploměry s nelineárním průběhem

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.
Schéma zapojení deliče napětí.
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;
}

Poznámka: Když pokud se při zahřívání naměřená hodnota R1 zvětšuje, pravděpodobně je chyba v zapojení. Stačí však vyměnit termistor za rezistor a problém by měl být vyřešen.

Když už máme vypočítaný odpor, můžeme se podívat na výpočet teploty. Tu z odporu vypočteme pomocí následujícího vztahu:

Po vyvození T z předchozího vztahu získáme rovnost pro výpočet teploty.
výpočet teploty z termistoru
Kde T je výsledná teplota, T0 je tabulková hodnota (většinou 25°C = teplota, při které je měřeno R0), B je také tabulková hodnota, která je k nalezení v datasheetu daného termistoru (ta se sice mírně mění s měnící se teplotou, ale pro účely základního měření nám bude postačovat jen jedna konstantní hodnota) a R1 je naměřený odpor. Ještě je třeba zmínit, že při počítání je nutno dosazovat teploty v kelvinech (K) a že log() je zápis pro výpočet přirozeného logaritmu. Funkce pro výpočet logaritmu není přímo v základu jazyka, ale naštěstí existuje v knihovně, která je do celého balíku zahrnuta. Pomocí příkazu #include vložíme na samý začátek kódu knihovnu math.h. Poté již budeme funkci log() moci použít.
Převodní vztah °C na K je následující: 0°C = +273,15K => 25°C = 298,15K

Vložení knihovny do skriptu:

#include <math.h> //zde se středník na konci řádku nepíše

Rozšíříme-li funkci mereni() o výpočet teploty, bude vypadat následovně:

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;
}

Pokud všechny části kódu seskládáme dohromady výsledek bude vypadat následovně:

#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);
}

Závěr

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.

Online verze článku: http://www.linuxsoft.cz/article.php?id_article=1990