Fyzikální kabinet FyzKAB
Články Modul Arduino (něco navíc) Skrytý teploměr v Arduino

Skrytý teploměr v Arduino

Věděli jste, že mnoho čipů Arduino a ATtiny má vestavěné teplotní čidlo (teploměr)? Výstupní hodnota z tohoto vestavěného teplotního čidla sice není kalibrována, ale lze provést několik měření a výstupní hodnotu na teplotu aspoň přibližně převést. Jeden z článků, kde byla tato informace uvedena a popsána možnost využití vnitřního čidla, byl například: TINKERIT – SecretThermometer.wiki. (Oproti původnímu článku je zde přidána podpora i pro Arduino Leonardo.) Protože v českém jazyce jsme zatím na žádný článek o zabudovaném teplotním čidle v modulu Arduino „nevygooglili“, dovolíme si zde tuto informaci prezentovat.

Arduino jako teplomer

Na samém začátku je třeba zdůraznit, že pochopitelně vnitřní čidlo čipu modulu Arduino je primárně určeno ke sledování teploty na čipu, takže naměřené hodnoty se mimo jiného odvíjejí od jeho výpočetního zatížení. Chce-li použít modul Arduino pro přesné měření teploty, je třeba k němu připojit nějaké sofistikované teplotní čidlo (např. DS18B20).

Následující program ilustruje možnost načtení teplotního čidla několika AVR čipů, která lze v různých typech modulů Ardiuno najít. Nebo je lze použít i samostatně v některém ze svých zapojení s čipy ATmega32U4, ATtiny24/44/84, ATtiny25/45/85.

Čtení vnitřního snímače teploty:

Načtení vnitřního teplotního čidla dosáhneme pomocí funkce readTemp() – viz následující ukázka výpisu probramu. V principu jde o načtení zabudovaného teplotního čidla oproti vnitřní napěťové referenci 1,1 V. První část dle zvoleného čipu nastavuje správné parametry pro načtení teplotního čidla a vnitřní napěťové reference. Po tomto nastavení je načtena dvojice hodnot (proměnné low a high) pro vytvoření celkové hodnoty odpovídající napětí na teplotním čidle. Výsledná výstupní hodnota odpovídající teplotě (v zatím nezkalibrovaných jednotkách) je funkcí readTemp() navrácena pomocí proměnné result.

long readTemp() {
   // Cteni teplotniho senzoru proti 1,1V referenci
   #if defined(__AVR_ATmega32U4__)
      ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);
      ADCSRB = _BV(MUX5);          // the MUX5 bit is in the ADCSRB register
   #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
      ADMUX = _BV(REFS1) | _BV(MUX5) | _BV(MUX1);
   #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
      ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);
   #else
      ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
   #endif

   delay(2);                          // Pockame, nez se nastaveni ADMUX ustali
   ADCSRA |= _BV(ADSC);               // Start prevodu
   while (bit_is_set(ADCSRA,ADSC));   // mereni
      uint8_t low = ADCL;             // musi nejprve precist ADCL - pak uzamkne ADCH
      uint8_t high = ADCH;            // odemkne oba
      long result = (high << 8) | low;   // spojení dvou
   return result;
}

Kalibrace načtených dat z teplotního čidla

Jakmile budeme mít získaná „syrová“ data, která odpovídají hodnotě z teplotního čidla, můžete je zkalibrovat. To učiníme měřením při dvou známých teplotách a následným přiřazením „syrových“ hodnot z funkce readTemp() těmto teplotám.

Následující funkce ukazuje jednoduché řešení převodu „syrových“ hodnot na teplotu:

float normalizeTemperature(long rawData) {
   // nahradte tyto konstanty svymi 2 datovymi body
   // toto jsou vzorove hodnoty (ve stupních C)
   float temp1 = 0;
   long data1 = 274;
   float temp2 = 21.6;
   long data2 = 346;

   // calculate the scale factor
   float scaleFactor = (temp2 - temp1) / (data2 - data1);

   // now calculate the temperature
   float temp = scaleFactor * (rawData - data1) + temp1;

   return temp;
}

Pro správnou funkci výše uvedené funkce normalizeTemperature() potřebujeme získat čtyři potřebné konstanty: temp1, data1, temp2 a data2, které určují převodní vztah mezi vstupními daty (např. z funkce readTemp()) a výstupní hodnotou teploty. To provedeme poměrně jednoduše. Spustíme program s funkcí readTemp() a vypisujeme hodnoty z teplotního čidla. Můžeme například pustit modul Arduino při pokojové teplotě. Za proměnnou temp1 dosadíme teplotu, při které jsme nechali modul Arduino měřit – tedy tu pokojovou teplotu. Za hodnotu proměnné data1 dosadíme výstupní hodnotu funkce readTemp(). Dvojici hodnot pro proměnné data2 a temp2 získáme obdobně, jen měříme při jiné teplotě. Je vcelku jedno zda své Arduino společně s kontrolním teploměrem dáte do mírně vyhřáté trouby nebo do lednice. Důležité je opět získat dvojici hodnot – teplota a hodnota z funkce readTemp(). Čím vzdálenější teploty proměříme, tím by naše kalibrace měla být kvalitnější. V této funkci předpokládáme, že teplotní čidlo má lineární průběh. Pokud bychom chtěli měření přesnější, bylo by třeba vytvořit převodní funkci na základě více hodnot a kupříkladu kvadratického vztahu – ale to by bylo s ohledem na charakter zabudovaného teplotní čidla naprosto zbytečné.

Ke kalibraci je třeba jen doplnit následující varování:
Je třeba si uvědomit, že modul Arduino je elektronické zařízení s určitými fyzikálními limity. Takže pokusy o kalibraci např. ponořením modulu do hrnce vařící vody nebo vložením Arduina do rozpálené trouby (více jak 120 °C), budou asi to poslední, co s modulem vyzkoušíte. 😜

Celkový program

Dále uvedený program využívá obou výše uvedených funkcí. Na sériové monitoru je vypsána jak hodnota přímo z teplotního čidla, tak přepočítaná hodnota teploty. Pochopitelně je třeba nejdříve program spustit při dvou známých teplotách a poznamenat si hodnoty z teplotního čidla. Pak tyto hodnoty společně se známými teplotami zapsat do kalibračních konstant data1, temp1 a data2, temp2 ve funkci normalizeTemperature(). Až poté začne vypisovaná hodnota teploty odpovídat reálné teplotě.

long readTemp() {
   // Cteni teplotniho senzoru proti 1,1V referenci
   #if defined(__AVR_ATmega32U4__)
      ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);
      ADCSRB = _BV(MUX5);          // the MUX5 bit is in the ADCSRB register
   #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
      ADMUX = _BV(REFS1) | _BV(MUX5) | _BV(MUX1);
   #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
      ADMUX = _BV(REFS1) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);
   #else
      ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
   #endif

   delay(2);                          // Pockame, nez se nastaveni ADMUX ustali
   ADCSRA |= _BV(ADSC);               // Start prevodu
   while (bit_is_set(ADCSRA,ADSC));   // mereni
      uint8_t low = ADCL;             // musi nejprve precist ADCL - pak uzamkne ADCH
      uint8_t high = ADCH;            // odemkne oba
      long result = (high << 8) | low;   // spojení dvou
   return result;
}

float normalizeTemperature(long rawData) {
   // nahradte tyto konstanty svymi 2 datovymi body
   // toto jsou vzorove hodnoty (ve stupních C)
   float temp1 = 0;
   long data1 = 274;
   float temp2 = 21.6;
   long data2 = 346;

   // calculate the scale factor
   float scaleFactor = (temp2 - temp1) / (data2 - data1);

   // now calculate the temperature
   float temp = scaleFactor * (rawData - data1) + temp1;

   return temp;
}

void setup() {
   // put your setup code here, to run once:
   Serial.begin(9600);
}

void loop() {
   // put your main code here, to run repeatedly:
   Serial.print("Hodnota z teplotniho cidla je ");
   Serial.println( readTemp() );
   Serial.print("Odpovidajici teplota je ");
   Serial.print( normalizeTemperature(readTemp()) );
   Serial.println(" *C");
   delay(1000);
}

Následující obrázek ukazuje výstup programu zobrazený na seriovém monitoru prostředí Arduino IDE.

vystup na Serial Monitor
UPOZORNĚNÍ:
Nesouhlasíme s vyřazením Newtonových zákonů, Ohmova zákona a zákona zachování energie z učiva fyziky základních škol v České republice!