ublo
bogdan's (micro)blog

bogdan » arduino: termometru

09:21 am on Nov 7, 2018 | #more | tags:

în urma rugăminții profesorului Stamatin, de câțiva ani țin un curs la Facultatea de Fizică din Măgurele. dacă inițial numele «modelare și simulare» ascundea ecuații matematice și metode numerice, recent am înlocuit diferențialele cu Arduino, în ovațiile celor câtorva studenți care frecventează cursul. dotările limitate m-au făcut să devin creativ cu materialele de curs, iar rezultatul mi s-a părut suficient de interesant pentru a-l reproduce aici. așa că:

temperatura este una dintre mărimile fizice pe care o conștientizezi imediat: e prea frig afară, e prea fierbinte supa, oare ai febră? pe lângă conștientizarea acestei mărimi, este foarte simplu să achiziționezi un termometru cu care să poți cuantifica aceste senzații. dar unde e farmecul dacă nu e făcut de tine și nu poți înregistra periodic datele pentru a urmări procesele în timp real? în cele ce urmează îți voi prezenta o variantă de termometru pe care o poți realiza cu Arduino.

termistor

un termistor este un dispozitiv electronic în care temperatura modifică modul în care conduce curentul electric: dacă temperatura crește, rezistența pe care acesta o opune trecerii curentului electric se micșorează și invers. un Arduino nu poate măsura rezistența direct. dispozitivele cele mai potrivite din interiorul acestuia pentru o astfel de operație sunt convertoarele analog-digitale, care transformă o tensiune într-un număr. mai exact, pentru fiecare 0,004882V aplicați la intrare, Arduino va înregistra câte o unitate, în așa fel încât pentru 0V va înregistra 0, iar pentru 5V numărul 1023, variația fiind liniară (ex. 1,5V → 307, 3,3V → 676). acest lucru nu te ajută prea mult direct, mărimea care variază cu temperatura fiind rezistența dispozitivului. ajutorul vine din partea legii lui Ohm:

$$U = R_{termistor} I$$

unde:
U este tensiunea electrică la bornele termistorului, în volți (V),
Rtermistor este rezistența acestuia, în ohmi (Ω), iar
I este curentul care trece prin circuit, în amperi (A).

Arduino pune prin conectorii săi mai multe tensiuni pe care le poți utiliza ca referință: 3,3V (3V3), 5V sau Vin. cum Arduino poate măsura tensiuni între 0 și 5V, 3,3V e o tensiune prea mică față de capătul intervalului, pierzând astfel din precizie, Vin poate fi cu mult peste cei 5V și are și dezavantajul că nu este stabilă, rămânând astfel opțiunea 5V.

notă
3,3V poate fi folosit destul de ușor, conectând un fir între terminalul AREF și 3,3V, stabilind în acest fel capătul intervalului la valoarea 3,3V. acest lucru poate fi făcut cu orice sursă de tensiune, cuprinsă între 0 și 5V. trebuie să ții cont însă, că valoarea unei unități se modifică corespunzător. în cazul 3,3V, o unitate va însemna 0,003222V (ex. 1,5V → 465, 3,3V → 1023).

alegând U = 5V, în legea lui Ohm rămân totuși două necunoscute, rezistența termistorului și curentul prin circuit. pentru curent însă putem să aplicăm încă o dată legea lui Ohm, introducând în circuit un rezistor cu valoare cunoscută, în serie cu termistorul. tensiunea la bornele acestui rezistor de referință este direct proporțională cu curentul prin circuit, tensiune pe care o poți măsura direct folosind Arduino.

Simple Thermometer Animation

fie Rreferință valoarea în ohmi a rezistorului de referință și cu Ureferință valoarea măsurată cu ajutorul Arduino:

$$\begin{cases}5V = (R_{termistor} + R_{referinta}) I \\ U_{referinta} = R_{referinta} I\end{cases} \Rightarrow \\ R_{termistor} = R_{referinta} (\frac{5V}{U_{referinta}} – 1)$$

dependența de temperatură a rezistenței termistorului este dată de ecuația Steinhart-Hart, puțin prea complexă pentru nevoi obișnuite. drept urmare îți recomand să folosești ecuația beta, o formă simplificată a ecuației anterioare, dar utilizată de producători în caracterizarea termistorilor comercializați:

$$\beta = \frac{ln(R_{termistor}) – ln(R_{25})}{\frac{1}{T} – \frac{1}{T_{25}}}$$

ecuația anterioară descrie dependența de temperatura absolută T, în Kelvin, a rezistenței termistorului Rtermistor, în ohmi, folosind trei constante: R25, în ohmi, care este rezistența termistorului la 25°C – de altfel și valoarea sub care termistorul se comercializează, T25, în Kelvin, fiind temperatura absolută care corespunde 25°C, adică 298,15K și β care este o constantă proprie fiecărui termistor și care se măsoară tot în Kelvin.

ecuația anterioară poate fi aranjată într-o formă mai prietenoasă pentru calculul direct al temperaturii, cu mențiunea că temperatura obținută se măsoară în Kelvin, astfel:

$$T = \frac{1}{\frac{1}{\beta}ln(\frac{R_{termistor}}{R_{25}}) + \frac{1}{T_{25}}}$$

următorul pas în rezolvarea problemei este identificarea constantelor lipsă. pentru a determina facil aceste constante, îți recomand să achiziționezi termistorul dintr-o sursă care pune la dispoziție și datele de catalog. la curs am ales un termistor cu valoarea R25 de 100KΩ. raționamentul e simplu: 100KΩ deoarece puterea disipată prin trecerea curentului electric prin termistor nu-l va încălzi suficient pentru a afecta măsurătorile, iar în foaia de catalog vei găsi direct valoarea lui β. pentru termistorul pe care l-am ales, β=4600K.

Termistor Beta Characteristic

notă
pentru rezistorul de referință am ales o valoare egală cu R25. raționamentul e simplu, atunci când rezistenețele sunt egale, tensiunea în punctul median este jumătate din tensiunea de alimentare, plasând astfel temperatura de 25°C în centrul scalei. din cauza neliniarității funcției de conversie, îmi convine să am această valoare situată central deoarece este o valoare de interes pentru activitatea umană și măsurătorile din vecinătatea ei vor avea o precizie mai mare. în general, e bine să aleg rezistorul de referință în așa fel încât temperatura medie a intervalului de interes să corespundă jumătății tensiunii de alimentare.

programare

în acest moment ai toate informațiile necesare pentru a putea scrie un mic program Arduino care să citească temperatura cu ajutorul unui termistor conectat în configurația descrisă mai sus. pentru început, orice program Arduino conține o funcție setup și o funcție loop.

conținutul funcției setup este rulat de Arduino atunci când acesta este inițializat, fie după reset, fie atunci când este alimentat, o singură dată. în această funcție se trec de obicei instrucțiuni pentru a inițializa perifericele Arduino. în cazul de față, convertorul analog-digital care poate măsura tensiuni este inițializat automat.

funcția loop este cea care ne interesează aproape întotdeauna. proprietatea care o face interesantă este că atunci când lista de instrucțiuni pe care le conține a fost epuizată, loop se va reseta și o va lua de la început. Arduino va face acest lucru până când este apăsat butonul reset, se încarcă un program nou sau alimentarea plăcuței este întreruptă.

în fragmentul de cod de mai jos apare și funcția analogRead cu parametrul A0. această funcție primește ca parametru eticheta unuia dintre pinii analogici ai Arduino, de la A0 la A5 și întoarce un număr întreg cuprins între 0 și 1023, număr care este proporțional cu tensiunea aplicată intrării A0. având în vedere că numărul întors este întreg, pentru a forța zecimale în calculele realizate de Arduino, am ales să reprezint toate numerele în format zecimal (”cu virgulă”).

cuvântul cheie float din fața etichetei U_referinta anunță Arduino să rezerve memorie în care să stocheze un număr zecimal care poate fi apelat întotdeauna folosind eticheta U_referinta. același lucru se aplică și pentru R_termistor, T și t. vei observa că eticheta acestor fragmente de memorie ține cont de tipul literelor (mici sau mari), astfel încât T și t vor face referire la fragmente separate, deci valori diferite.

am mai utilizat încă două funcții, log, care corespunde logaritmului natural, luând ca parametru un număr zecimal și întorcând tot un număr zecimal și delay, care suspendă execuția listei de instrucțiuni pentru numărul de milisecunde furnizate drept parametru. dacă nu folosești această funcție, operațiile vor fi efectuate extrem de rapid, furnizând o cantitate mare de date, oarecum irelevantă, mai ales datorită vitezei relativ mici de variație a rezistenței termistorului cu temperatura.

void setup() {
}

void loop() {
  // măsor tensiunea în volți înmulțind valoarea citită prin
  // conectorul A0 cu factorul 0.004882V (= 5V / 1024)
  float U_referinta = 0.004882 * analogRead (A0);
  // din U_referință și valoarea lui R_referință = 100k ohmi
  // calculez R_termistor
  float R_termistor = 100000.0 * (5.0 / U_referinta - 1.0);
  // folosim ecuația temperaturii, în care beta = 4600K și
  // R_25 = 100k ohmi, T_25 = 298.15K și obținem temperatura în
  // Kelvin
  float T = 1.0 /
  (1.0 / 4600.0 * log (R_termistor / 100000.0) + 1.0 / 298.15);
  // pentru a afla temperatura în grade Celsius, scădem din
  // temperatura în Kelvin 273.15
  float t = T - 273.15;
  // fac o pauză de 1 secundă, după care repet instrucțiunile
  delay (1000);
}

micul program de mai sus poate fi compilat și încărcat pe plăcuța ta Arduino folosind Arduino IDE. conectezi Arduino prin cablul USB la calculator și aștepți ca sistemul de operare să instaleze automat uneltele pentru conectare. apoi deschizi aplicația Arduino și vei alege utilizând meniul Tools → Boards → modelul de Arduino pe care îl ai și folosind meniul Tools → Port → portul la care ai conectat plăcuța. te prinzi ușor despre ce port e vorba. dacă nu, le poți încerca fără probleme pe toate, până reușești.

în fereastra care s-a deschis, copiezi codul de mai sus, sau mai bine, îți recomand să-l scrii de mână. poți sări peste liniile care încep cu // și sunt colorate cu albastru. ele reprezintă comentarii și nu influențează programul. după ce ai terminat, vei apăsa butonul Verify, așteptând ca în bara de stare a ferestrei să apară cuvintele Done compiling.

Arduino - Verify - Upload

în situația în care au apărut erori, verifică în ordine următoarele lucruri:

  • ai pus ; (punct și virgulă) după fiecare instrucțiune? din experiența cu studenții, e de departe cea mai frecvent întâlnită eroare;
  • fiecare paranteză deschisă, fie că este paranteză rotundă sau acoladă, trebuie să se și închidă la un moment dat, fix ca la matematică, în ordinea inversă deschiderii lor. adică {…(…)…} este corect, în timp ce sunt greșite: {…(…}…), {…(…)…, {…)…}
  • felul literelor contează mult, așa că verifică dacă ai scris cu litere mari și mici ca în exemplul de mai sus; etichetele, cum sunt U_referinta, R_termistor, T și t le poți modifica după plac, cu singura mențiune să le modifici pe peste tot;
  • cu excepția liniilor cu comentarii, în rest nu ai voie să folosești diacritice; știu, nu e așa comun, dar am întâlnit și eroarea asta;

Arduino Done Compiling

ultimul pas este cel de încărcare al programului pe plăcuța Arduino, apăsând butonul Upload. în caz de erori, cele mai întâlnite sunt:

  • nu ai selectat corect portul la care este conectat Arduino;
  • nu ai selectat corect tipul de plăcuță Arduino; pentru a te asigura, tipul plăcuței este scris pe ea, nu ai cum să greșești;
  • portul USB la care ai conectat plăcuța nu funcționează corect și va trebui să alegi altul;
  • cablul USB nu este de calitate și va trebui să-l schimbi sau să folosești unul mai scurt;

note

  • în jargonul programatorilor, loop și setup poartă denumirea de proceduri, pentru că nu returnează nimic. cuvântul cheie void care alcătuiește definiția unei funcții specifică fix acest lucru: că respectiva bucată de cod întoarce ”vid” (nimic). prefer să folosesc denumirea de funcție, care provine din matematică, pentru a simplifica asimilarea noțiunilor.
  • în teorie funcția log are nevoie de o bibliotecă de funcții să funcționeze. din fericire, Arduino știe să o adauge implicit, așa că micul program va funcționa fără probleme așa cum este scris.
  • în ceea ce privește tipul de variabile pe care lucrează implicit log, care se numește double, pentru majoritatea plăcuțelor Arduino, acesta este doar un sinonim pentru float. chiar dacă nu ar fi așa, Arduino se ocupă de conversia implicită între cele două formate.
  • spațiile nu sunt relevante, însă ele asigură lizibilitate programului scris, așa că dacă ți-e lene, le poți omite.

lcd

până acum, Arduino știe ce temperatură măsoară, însă o ține pentru el. pentru a o putea afișa, are nevoie de un dispozitiv extern, cum ar fi un ecran LCD cu 2 rânduri a câte 16 caractere, foarte des întâlnit (și ieftin). în plus, mai ai nevoie de un mic potențiometru semiregrabil necesar în ajustarea contrastului, cu valoarea de 10KΩ, o rezistență de 220 de Ω, câteva fire de conexiune mai lungi, colorate și o plăcuță pentru experimente (breadboard). tot ce ai de făcut în continuare pe partea fizică este să replici construcția din desenul următor, după ce ai deconectat Arduino de la portul USB:

Arduino Thermometer with LCD

sfatul meu este să începi cu firele roșii și negre (alimentarea), plasând apoi rezistența de 100KΩ, cea de 220 de Ω, termistorul și potențiometrul semiregrabil pe plăcuța pentru experimente, urmate de firele mov, galben, portocaliu și roz, iar într-un final de cele verzi. verifici de două ori așezarea firelor și conexiunile, ții degetele încrucișate și conectezi din nou Arduino la portul USB. nu-ți face griji, portul USB este protejat în caz de probleme, așa că nu ai ce strica la calculator. un semn bun, că totul merge, îl reprezintă luminarea ecranului LCD. ajustând potențiometrul semiregrabil, ar trebui să vezi la unul dintre capete 2 rânduri cu câte 16 pătrățele negre alcătuite din pixeli, pe ecran. dacă nu funcționează, verifică din nou conexiunile.

vei modifica acum micul program de mai sus pentru a include ecranul LCD folosind biblioteca inclusă în aplicația Arduino, LiquidCrystal.h. aceasta definește un tip de obiect LiquidCrystal care poate fi folosit pentru a controla ecranul LCD. pentru început, vei ințializa obiectul dându-i o denumire și specificând conexiunile către Arduino. pentru a inițializa ecranul, vei adăuga instrucțiunea de inițializare a acestuia în funcția setup: lcd.begin. inițializarea se face specificând dimensiunile ecranului. Arduino știe prin intermediul LiquidCrystal.h să comunice cu ecrane de mai multe dimensiuni.

// am nevoie de o bibliotecă care știe să comunice cu ecranul LCD
#include <LiquidCrystal.h>
// inițializez un obiect de tip LiquidCrystal pe care îl denumesc
// lcd, căruia îi spun la ce conectori Arduino am conectat firele
// 12 = galben, 11 = portocaliu, 5, 4, 3, 2 = cele 4 fire verzi
LiquidCrystal lcd (12, 11, 5, 4, 3, 2);

void setup() {
  // inițializez ecranul și îi spun că are 2 rânduri cu 16 caractere
  lcd.begin (2, 16);
}

după ce ai calculat temperatura, vei curăța ecranul pentru a te asigura că nu e nimic pe el folosind lcd.clear. vei muta cursorul pe coloana 0, linia 0 (colțul stânga-sus) folosind lcd.setCursor, cu parametri coloană (de la 0 la 15) urmat de linie (de la 0 la 1), după care vei tipări diferite lucruri, în ordine, folosind lcd.print. lcd.print poate primi ca parametri șiruri de caractere delimitate de ghilimele, numere zecimale sau întregi, dar și caractere speciale, prin codul lor, cum este simbolul °, dar pe care trebuie să-l diferențiezi de un număr întreg obișnuit, folosind sintaxa (char). această operație se numește conversia tipului (cast în engleză) și îi spune Arduino să trateze numărul 223 ca un caracter (char).

notă
lcd este o etichetă pe care am ales-o eu în vederea accesării obiectului LiquidCrystal. dacă ai altă preferință, cum ar fi ecran, o poți folosi fără probleme. trebuie să ții cont că funcțiile apelate se vor modifica corespunzător: lcd.begin devine ecran.begin, lcd.clear devine ecran.clear, lcd.setCursor devine ecran.setCursor și lcd.print devine ecran.print. acest tip de funcții, care depind de obiectul folosit, în jargonul programatorilor se numesc metode și se folosesc de starea internă a obiectului de care sunt anexate pentru a-și îndeplini rolul. fără inițializarea unui astfel de obiect, ele nu pot fi folosite, așa că să nu uiți bucata anterioară funcției setup în care alegem o etichetă pentru un obiect de tip LiquidCrystal.

void loop() {
  // ...
  // bucata de până aici rămâne neschimbată
  float t = T - 273.15;
  // dacă e ceva scris pe ecranul LCD, îl șterg
  lcd.clear ();
  // îi spun ecranului LCD că vreau să scrie începând cu colțul stânga-sus
  // primul 0 îl reprezintă coloana (0-15), al doilea rândul (0-1)
  lcd.setCursor (0,0);
  // pornind de la cursor, afiseaza T = 
  lcd.print ("T = ");
  // apoi temperatura în grade Celsius
  lcd.print (t);
  // urmată de simbolul grad
  lcd.print ((char) 223);
  // și apoi de litera C
  lcd.print ("C");
  // fac o pauză de 1 secundă, după care repet instrucțiunile
  delay (1000);
}

după încărcarea programului pe plăcuța Arduino, singurul lucru pe care îl mai ai de făcut este să ajustezi contrastul în așa fel încât literele și cifrele să fie lizibile.

aceast sait folosește cookie-uri pentru a îmbunătăți experiența ta, ca vizitator. în același scop, acest sait utilizează modulul Facebook pentru integrarea cu rețeaua lor socială. poți accesa aici politica mea de confidențialitate.