Serwisy partnerskie:
Close icon
Serwisy partnerskie

Kurs Arduino odcinek 2 - termometry: 'diodowy', pokojowy oraz 'scalony' analogowy

Przed miesiącem, w pierwszym odcinku kursu Arduino, nauczyliśmy się korzystać z programu Arduino IDE, by tworzyć, modyfikować, weryfikować i wgrywać do płytki programy – szkice. Omówiliśmy podstawowe polecenia wejścia/ wyjścia: digitalRead(), digitalWrite(), analogRead() i analogWrite(). Zaczęliśmy też korzystać z łącza szeregowego (serial) do komunikacji z konsolą na ekranie komputera. Początki okazały się zaskakująco proste. W drugim odcinku kontynuujemy takie zachęcające, łatwe ćwiczenia.
Article Image
1. Kurs Arduino: wprowadzenie 2. Kurs Arduino odcinek 1 - co to jest i jak zacząć? (sprzęt, schematy, programowanie) 3. Kurs Arduino odcinek 2 - termometry: 'diodowy', pokojowy oraz 'scalony' analogowy 4. Kurs Arduino odcinek 3 - moduł wyświetlacza LCD (HD44780) 5. Kurs Arduino odcinek 4 - czujnik BME280 (pomiar ciśnienia, wilgotności i temperatury) 6. Kurs Arduino odcinek 5 - biblioteki do trzyfunkcyjnego czujnika BME280 7. Kurs Arduino odcinek 6 - wykorzystanie łącza I2C (TWI) 8. Kurs Arduino odcinek 7 - wykorzystanie łącza SPI (karty pamięci, MAX31865, MAX31855) 9. Kurs Arduino odcinek 8 - wykorzystanie czujnika dwutlenku węgla (MH-Z19) 10. Kurs Arduino odcinek 9 - wykorzystanie modułów do odmierzania czasu 11. Kurs Arduino odcinek 10 - wykorzystanie czujników temperatury (DS18B20) 12. Kurs Arduino odcinek 11 - budowa loggera (rejestratora danych) 13. Kurs Arduino odcinek 12 - rejestracja i prezentacja danych oraz obsługa karty SD 14. Kurs Arduino odcinek 13 - rejestracja danych (wykorzystanie modułu OpenLog) 15. Kurs Arduino odcinek 14 - czym zastąpić płytkę Arduino Uno? 16. Kurs Arduino odcinek 15 - budowa rejestratora (chiński klon Arduino Pro Mini) 17. Kurs Arduino odcinek 16 - problemy z kwadratowym modułem Strong 18. Kurs Arduino odcinek 17 - czujniki do pomiaru wilgotności gleby (instalacja, konfiguracja) 19. Kurs Arduino odcinek 18 - protokół MODBUS i łącze RS-485 20. Kurs Arduino odcinek 19 - wyświetlacze od podstaw (matrycowe, 7-segmentowe, MAX7219) 21. Kurs Arduino odcinek 20a - wyświetlacze matrycowe i biblioteki MD_MAX 22. Kurs Arduino odcinek 20b - przyciski i biblioteki do obsługi wyświetlaczy 23. Kurs Arduino odcinek 20c - biblioteki do obsługi wyświetlaczy graficznych 24. Kurs Arduino odcinek 21 - moduł wyświetlacza OLED 25. Kurs Arduino odcinek 22 - moduł wyświetlacza 1,3 cala 26. Kurs Arduino odcinek 23 - przygotowanie bitmap do wyświetlania 27. Kurs Arduino odcinek 24 - omówienie fontu podstawowego (Adafruit GFX) 28. Kurs Arduino odcinek 25 - font podstawowy biblioteki Adafruit GFX 29. Kurs Arduino odcinek 26 - wykorzystanie bitmapowych fontów GFX 30. Kurs Arduino odcinek 27 - polskie znaki w glcdfont.c 31. Kurs Arduino odcinek 28 - zmiana ulepszonych fontów GFX

Cykl Arduino w EdW przeznaczony jest głównie dla elektroników, dlatego w miarę możliwości ćwiczenia dotyczą zagadnień, które elektronik wykorzystuje lub może wykorzystać przy budowie różnych urządzeń. Dlatego zaczniemy od czegoś jak najbardziej elektronicznego i „obowiązkowego”, a mianowicie termometru.

Termometr 'diodowy' na Arduino

Zbuduj układ według rysunku 1 i fotografii 2. Czujnikiem temperatury jest jakakolwiek zwyczajna dioda, na przykład 1N4148 albo 1N4001...7. Jak wiadomo, napięcie przewodzenia złącza półprzewodnikowego maleje ze wzrostem temperatury ze współczynnikiem około 2mV/°C. Włączymy rezystor podciągający w procesorze (około 30kΩ), aby przez diodę popłynął niewielki prąd (około 0,15mA) i za pomocą wejścia analogowego A0 będziemy mierzyć napięcie przewodzenia diody.

Rys.1 Schemat termometru 'diodowego'
Fot.2 Termometr 'diodowy'

Najprostszy program (dostępny w Elportalu w pliku A0201.ino) pokazujący na konsoli ekranowej komputera odczyty z takiego czujnika mógłby wyglądać jak w szkicu 1:

void setup() {
Serial.begin(9600);
pinMode(A0, INPUT_PULLUP);
}
void loop() {
Serial.println(analogRead(A0));
delay(1000);
}

Termometr działa, ale jak pokazuje rysunek 3, wskazania zmieniają się od wartości 110, co odpowiada temperaturze pokojowej 21°C, jaka wtedy panowała w pokoju, do wartości 104, co odpowiada nagrzaniu diody palcami do temperatury około 30 stopni. Po pierwsze rozdzielczość jest słaba, po drugie wskazania są dziwne i „odwrotne”.

Rys.3 Wskazania termometru 'diodowego'

Co do rozdzielczości, to jeden bit przetwornika o 1024 poziomach i napięciu odniesienia 5V odpowiada napięciu 4,88mV. A to przy współczynniku cieplnym diody 2mV/C daje kiepską rozdzielczość około 2,5 stopnia Celsjusza na jeden bit przetwornika.

Możemy to łatwo poprawić, wykorzystując wewnętrzne źródło odniesienia procesora (1,1V). Włączymy je, dodając w funkcji setup() polecenie wykorzystania go:

analogReference(INTERNAL);

Jak pokazuje rysunek 4, jest znacznie lepiej, bo różnicy temperatur około 10 stopni (od 21°C do 31°C) odpowiada zmiana wartości z przetwornika ADC o 26 (od 517 do 491). Daje to przyzwoity współczynnik 0,38 stopnia na jeden bit przetwornika i pozwala mierzyć temperaturę z rozdzielczością mniej więcej 1/3 stopnia Celsjusza.

Rys.4 Zwiększenie rozdzielczości termometru 'diodowego'

Trzeba tylko jeszcze zmienić wynik przetwarzania na stopnie Celsjusza. Wykorzystamy do tego bardzo pożyteczną „arduinową” funkcję map(). Składnia jest następująca:

map (liczba, fromLow, fromHigh, toLow, toHigh);

Funkcja map() zamienia liczbę całkowitą (zwykle zawartą w zmiennej) na inną liczbę według liniowej zależności wyznaczonej przez dwa punkty określone czterema liczbami. Dwie pierwsze liczby (fromLow, fromHigh) pokazują zakres wartości wejściowych, a dwie następne (toLow, toHigh) – zakres wartości wyjściowych. Funkcja ta służy do skalowania, np. może posłużyć do zamiany liczb 0…1023 z przetwornika ADC na 8-bitowe liczby z zakresu 0…255:

map (zADC, 0, 1023, 0, 255);

Ale funkcja map() może też służyć do jednoczesnego „odwracania”. W naszym przypadku liczbie 517 z przetwornika ADC odpowiada „dolna” temperatura 21°C, a liczbie 491 – „górna” temperatura 31°C. Dlatego napiszemy:

map (zADC, 517, 491, 21, 31);

Odrobinę zmieniony program według rysunku 5 wyświetla temperaturę w pełnych stopniach Celsjusza.

Rys.5 Wyświetlanie temperatury w pełnych stopniach Celsjusza

Dobrze, tylko wcześniej stwierdziliśmy, że w ten sposób możemy mierzyć temperaturę z lepszą rozdzielczością, mniej więcej 1/3 stopnia Celsjusza. Można to zrobić na wiele sposobów. Problemem jest to, że funkcja map() działa na liczbach całkowitych, a nie zmiennoprzecinkowych (typu float) i w podanej postaci zwraca wartość temperatury w postaci liczb całkowitych. Aby nie było takiego zaokrąglenia, możemy pracować na liczbach całkowitych, ale 10 razy większych, a potem przejść na zmiennoprzecinkowe i podzielić przez 10. Program może wyglądać jak na szkicu 2:

int zADC;
float temp;
void setup() {
Serial.begin(9600);
pinMode(A0, INPUT_PULLUP);
analogReference(INTERNAL);
}
void loop() {
zADC = 10 * analogRead(A0);
temp = map(zADC, 5170, 4910, 210,
310);
Serial.print(temp / 10, 1);
Serial.print(„\xC2\xB0”);
Serial.println(‚C’);
delay(1000);
}

Mamy tu dwie zmienne globalne: zmienną zADC typu int dla liczb całkowitych, drugą o nazwie temp typu float dla wyniku zmiennoprzecinkowego. Liczbę całkowitą odczytaną z przetwornika ADC mnożymy przez 10 i przeprowadzamy jej mapowanie ze współczynnikami też powiększonymi dziesięciokrotnie. Funkcja map() zwraca liczbę całkowitą, ale 10 razy większą niż temperatura w stopniach Celsjusza, więc nie tracimy dokładności. I tę całkowitą liczbę wpisujemy do zmiennej typu float.

Należy zauważyć, że dokonujemy tu „po cichu” konwersji typów, czyli rzutowania. Jest to tzw. rzutowanie niejawne – niezalecane, bo gdyby zostało przeprowadzone nieświadomie, może stać się przyczyną kłopotów. W naszym prościutkim szkicu kłopotów nie będzie, ale lepiej jest stosować tak zwane rzutowanie jawne, gdzie w programie wyraźnie widać, że świadomie i celowo dokonujemy rzutowania, czyli konwersji typów. Arduino jest „przykrywką” na kompilator języka C i jego ulepszenia C++, więc rzutowanie można zrobić zarówno w dość prosty sposób według reguł języka

C temp = (float) map(zADC, 5170, 4910, 210, 310);

jak też wykorzystując jeden z czterech sposobów dostępnych w języku C++:

temp = static_cast (map(zADC, 5170, 4910, 210, 310));

Po dokonaniu rzutowania, w zmiennej temp, która jest typu float, nadal mamy liczbę całkowitą dziesięć razy za dużą. Ale gdy podzielimy ją przez 10, otrzymamy liczbę zmiennoprzecinkową z dużą liczbą miejsc po przecinku (w Polsce mówimy „miejsc po przecinku”, ale w komputerach część całkowitą od ułamkowej oddziela kropka). Standardowo polecenie

Serial.print(temp / 10);

wyświetli wynik dzielenia z dwoma miejscami po przecinku (kropce). Polecenie

Serial.print(temp / 10, 8);

wyświetli wynik z bezsensowną „dokładnością” ośmiu miejsc po przecinku (kropce). W naszym termometrze wystarczy jedno miejsce po przecinku, stąd mamy

Serial.print(temp / 10, 1);

Zauważ, że wcześniej pisaliśmy println, teraz piszemy tylko print. Polecenie println to skrót od print linedrukuj linię, czyli: napisz i przejdź do nowej linii. My po wypisaniu na ekranie temperatury nie przechodzimy do nowej linii, tylko dopisujemy w tej linii znak stopnia Celsjusza. Jest pewien kłopot z wypisaniem na konsoli komputera symbolu stopnia (°) – robimy to poleceniem

Serial.print(„\xC2\xB0”);

Serial.print możemy wykorzystać Serial.write – polecenie wysłania jednego znaku. Ten sam symbol stopnia możemy uzyskać na kilka pokrewnych sposobów:

Serial.write(„\xC2\xB0”);
Serial.write(0xC2); Serial.write(0xB0);
Serial.print(char(194)); Serial.print(char(176));
Serial.write(char(194)); Serial.write(char(176));

ale nie zadziała polecenie

Serial.print(0xC2); Serial.print(0xB0);

które wypisze na ekranie liczbę 194176. Te niełatwe zagadnienia będziemy omawiać szerzej, w kontekście wyświetlania napisów na ekranie konsoli komputera oraz za pomocą wyświetlacza znakowego LCD.

Na koniec wypisujemy na ekranie dużą literkę C poleceniem println, powodującym przejście do nowej linii.

Rys.6 Termometr z najprostszym czujnikiem diodowym

I oto, jak pokazuje rysunek 6, stworzyliśmy najprawdziwszy termometr z najprostszym czujnikiem diodowym. Oczywiście jego prymitywna kalibracja pozostawia wiele do życzenia. Gdybyśmy jednak z pomocą jakiegoś termometru wzorcowego w dwóch znacznie różniących się temperaturach zmierzyli dane z przetwornika ADC, to uzyskalibyśmy naprawdę przyzwoity termometr, uwzględniający zarówno parametry użytej diody, jak też rozrzuty napięcia odniesienia przetwornika ADC. Może on znaleźć praktyczne zastosowanie, zwłaszcza z czujnikiem w postaci tranzystora (baza zwarta z kolektorem), np. tranzystora PNP mocy, którego wkładka radiatorowa będzie na potencjale masy.

Dokładniejszy termometr możemy zrealizować z wykorzystaniem popularnych układów scalonych.

'Scalony' termometr analogowy na Arduino

Do budowy dokładnego termometru możemy wykorzystać jakąś popularną kostkę, która wytwarza napięcie proporcjonalne do temperatury. Jest wiele tego rodzaju analogowych układów scalonych, między innymi LM35, LM335, TMP35... TMP37, MCP9700 czy TCP1047.

Zacznijmy od LM335 – czujnika temperatury bezwzględnej, który daje napięcie wprost proporcjonalne do temperatury bezwzględnej (w kelwinach) ze współczynnikiem 10mV/°C. Jak wiadomo, temperaturze 0°C odpowiada 273,15K. Jeżeli więc przykładowo napięcie czujnika wyniesie 2,946V (2946mV), daje to temperaturę 294,6 kelwina, czyli 21,45°C, w przybliżeniu 21,5°C.

Duże napięcie z czujnika uniemożliwia wykorzystanie wewnętrznego źródła napięcia odniesienia 1,1V. Ale w tej roli można wykorzystać obecny na płytce Arduino Uno stabilizator napięcia 3,3V, co umożliwi pomiar napięć do 3300mV, czyli temperatur do około 330 kelwinów (+57°C). Przy napięciu odniesienia 3300mV jednemu bitowi 10-bitowego przetwornika ADC odpowiada napięcie 3,2mV, co daje rozdzielczość około 1/3 stopnia, podobnie jak termometru z diodą.

Teraz kalibracja będzie dużo łatwiejsza – wystarczy jednopunktowa.

Jeśli masz kostkę LM335, zbuduj układ według rysunku 7 i fotografii 8. Według katalogu czujniki te mogą pracować przy prądach od 0,45mA do 5mA. Dlatego tu nie wystarczy włączenie wewnętrznego podciągania i potrzebny jest dodatkowy rezystor 2,2kΩ.

Rys.7 'Scalony' termometr analogowy - schemat
Fot.8 'Scalony' termometr analogowy

Napięcie 3,3V przez zworę – mostek podajemy na wejście zewnętrznego napiecia odniesienia AREF i w programie ustawimy zewnętrzne napięcie AREF jako napięcie odniesienia dla przetwornika ADC. Szkice tej wersji termometru dostępne są w Elportalu w pliku A0202.ino.

Aby skalibrować termometr, wystarczy zmierzyć wskazanie z przetwornika ADC w jednej znanej temperaturze. U mnie w temperaturze 22,8°C (296 kelwinów) przetwornik dał wskazanie 929 – rysunek 9, co daje współczynnik korekcyjny 3,1385/°C. Jeżeli więc aktualne wskazanie z przetwornika ADC podzielimy przez 3,1385, to otrzymamy temperaturę w kelwinach. Następnie od wyniku trzeba odjąć stałą 273,15, co da temperaturę w stopniach Celsjusza. W tym przypadku nie ma tu potrzeby stosowania funkcji mapującej, więc obliczenia można przeprowadzić na liczbach zmiennoprzecinkowych (typu float), na przykład według rysunku 10.

Rys.9 Kalibrowanie 'scalonego'termometru analogowego
Rys.10 Obliczenia przeprowadzane na zmiennych typu float

Jeśli wystarczy tylko skala Celsjusza, można wykorzystać dość przejrzysty szkic 3 z jedną zmienną zmiennoprzecinkową term:

float term;
void setup() {
Serial.begin(9600);
analogReference(EXTERNAL);
}
void loop() {
term = analogRead(A0);
term = term / 3.1385;
term = term - 273.15;
Serial.print(term, 1);
Serial.print(„\xC2\xB0”);
Serial.println(‚C’);
delay(1000); }

Można zupełnie zrezygnować ze zmiennej typu float, ale na liczbie całkowitej z przetwornika ADC trzeba przeprowadzić operacje zmiennoprzecinkowe, więc nie można zapomnieć o rzutowaniu (float), na przykład jak na zwięzłym szkicu 4.

void setup() {
Serial.begin(9600); analogReference(EXTERNAL); }
void loop() {
Serial.print(((float)analogRead(A0) / 3.1385 - 273.15), 1);
Serial.print(„\xC2\xB0”);Serial.println(‚C’);delay(1000); }

Tutaj argumentem przekazywanym do polecenia drukowania Serial.print() jest wyrażenie

((float)analogRead(A0) / 3.1385 - 273.15)

którego zmienoprzecinkowy wynik zostanie wydrukowany z jednym miejscem po przecinku. To z zapasem wystarczy, bo i tak realna rozdzielczość termometru z kostką LM335 nie będzie lepsza, niż obliczona wcześniej jedna trzecia stopnia. Lepsze właściwości zapewni nowocześniejszy układ LM35.

Precyzyjny termometr pokojowy na Arduino

Jeśli masz kostkę LM35, zestaw układ według rysunku 11 i fotografii 12. Szkice tej wersji termometru dostępne są w Elportalu w pliku A0203.ino.

Układ LM35 daje na wyjściu napięcie wprost proporcjonalne do temperatury wyrażonej w stopniach Celsjusza ze współczynnikiem 10mV/°C, co przy wewnętrznym napięciu odniesienia 1,1V pozwoli osiągnąć rozdzielczość około 0,1 stopnia.

Rys.11 Precyzyjny termometr pokojowy - schemat
Fot.12 Precyzyjny termometr pokojowy

Pięknie, tylko nie można w ten najprostszy sposób mierzyć napięć ujemnych - właśnie dlatego będzie to tylko termometr pokojowy, a nie zaokienny.

Z uwagi na tolerancję czujnika oraz tolerancję napięcia odniesienia, też potrzebna jest kalibracja – całkowicie wystarczy jednopunktowa.

Znów przy użyciu dokładnego termometru wzorcowego najpierw sprawdzamy, jaki jest odczyt z ADC przy znanej temperaturze. U mnie w temperaturze 21,7°C odczyt z ADC wynosił 228 (rysunek 13), co daje współczynnik kalibracyjny 10,507.

Rys.13 Precyzyjny termometr pokojowy - kalibracja
Rys.14 Szkic do precyzyjnego termometru pokojowego

Po uwzględnieniu go możemy wykorzystać banalnie prosty szkic 5 pokazany też na rysunku 14 (zawarty także w pliku A0203.ino).

void setup() {
Serial.begin(9600); analogReference(INTERNAL); }
void loop() {
Serial.print(((float)analogRead(A0) / 10.507), 1);
Serial.print(„\xC2\xB0”); Serial.println(‚C’);
delay(1000); }

I oto zbudowałeś naprawdę czuły i dokładny termometr, który możesz wykorzystać na wiele sposobów.

Jestem przekonany, że w razie potrzeby bez kłopotów dostosujesz programy do jeszcze innych podobnych czujników.

Jeśli zbudujesz czułą wersję o realnej rozdzielczości 0,1 stopnia z kostką LM35, koniecznie zwróć uwagę na fluktuacje wskazań. Prawdopodobnie takie obserwacje znacząco wpłyną na Twoje oczekiwania i wyobrażenia dotyczące precyzyjnych pomiarów temperatury.

Zachęcam do eksperymentów!

Do pobrania
Download icon Materiały do: Kurs Arduino odcinek 2 - termometr 'diodowy' i 'scalony' termometr analogowy
1. Kurs Arduino: wprowadzenie 2. Kurs Arduino odcinek 1 - co to jest i jak zacząć? (sprzęt, schematy, programowanie) 3. Kurs Arduino odcinek 2 - termometry: 'diodowy', pokojowy oraz 'scalony' analogowy 4. Kurs Arduino odcinek 3 - moduł wyświetlacza LCD (HD44780) 5. Kurs Arduino odcinek 4 - czujnik BME280 (pomiar ciśnienia, wilgotności i temperatury) 6. Kurs Arduino odcinek 5 - biblioteki do trzyfunkcyjnego czujnika BME280 7. Kurs Arduino odcinek 6 - wykorzystanie łącza I2C (TWI) 8. Kurs Arduino odcinek 7 - wykorzystanie łącza SPI (karty pamięci, MAX31865, MAX31855) 9. Kurs Arduino odcinek 8 - wykorzystanie czujnika dwutlenku węgla (MH-Z19) 10. Kurs Arduino odcinek 9 - wykorzystanie modułów do odmierzania czasu 11. Kurs Arduino odcinek 10 - wykorzystanie czujników temperatury (DS18B20) 12. Kurs Arduino odcinek 11 - budowa loggera (rejestratora danych) 13. Kurs Arduino odcinek 12 - rejestracja i prezentacja danych oraz obsługa karty SD 14. Kurs Arduino odcinek 13 - rejestracja danych (wykorzystanie modułu OpenLog) 15. Kurs Arduino odcinek 14 - czym zastąpić płytkę Arduino Uno? 16. Kurs Arduino odcinek 15 - budowa rejestratora (chiński klon Arduino Pro Mini) 17. Kurs Arduino odcinek 16 - problemy z kwadratowym modułem Strong 18. Kurs Arduino odcinek 17 - czujniki do pomiaru wilgotności gleby (instalacja, konfiguracja) 19. Kurs Arduino odcinek 18 - protokół MODBUS i łącze RS-485 20. Kurs Arduino odcinek 19 - wyświetlacze od podstaw (matrycowe, 7-segmentowe, MAX7219) 21. Kurs Arduino odcinek 20a - wyświetlacze matrycowe i biblioteki MD_MAX 22. Kurs Arduino odcinek 20b - przyciski i biblioteki do obsługi wyświetlaczy 23. Kurs Arduino odcinek 20c - biblioteki do obsługi wyświetlaczy graficznych 24. Kurs Arduino odcinek 21 - moduł wyświetlacza OLED 25. Kurs Arduino odcinek 22 - moduł wyświetlacza 1,3 cala 26. Kurs Arduino odcinek 23 - przygotowanie bitmap do wyświetlania 27. Kurs Arduino odcinek 24 - omówienie fontu podstawowego (Adafruit GFX) 28. Kurs Arduino odcinek 25 - font podstawowy biblioteki Adafruit GFX 29. Kurs Arduino odcinek 26 - wykorzystanie bitmapowych fontów GFX 30. Kurs Arduino odcinek 27 - polskie znaki w glcdfont.c 31. Kurs Arduino odcinek 28 - zmiana ulepszonych fontów GFX
AUTOR
Źródło
Elektronika dla Wszystkich kwiecień 2018
Udostępnij
UK Logo