ESP8266 i DHT22 - pomiar temperatury i wilgotności przez WiFi

Pierwsze zabawy z ESP8266 już za mną, pora zrobić coś pożytecznego. Na pierwszy ogień pójdzie współpraca z czujnikiem wilgotności i temperatury - DHT22, a dokładniej - jego obudowaną wersją - AM2301. ESP będzie pracował pod kontrolą firmware NodeMcu , co oznacza, że program będziemy tworzyć w języku Lua.

ESP8266 i DHT22

Zacznijmy od sprzętu. Co będzie potrzebne? ESP-01, który jest najprostszą wersją ESP8266 i posiada wyprowadzone 2 wyjścia GPIO. Potrzebny będzie też sam czujnik temperatury - AM2301, który jest zgodny z popularnym sensowem DHT22. Całość trzeba jeszcze zasilić napięciem 3,3V, więc przyda się stabilizator napięcia. Ja użyłem popularnego LD33V. Stabilizator wymaga użycia dwóch kondensatorów. Całość wraz z goldpinami wlutowałem na płytkę uniwersalną dodając jeszcze na wszelki wypadek piny połączone z napięciem przez rezystory (1100Ω i 4700Ω), jakby kiedyś trzeba było podłączyć coś jeszcze (choćby przez one-wire). Do całości podłączony jest zasilacz 7,5V, ale skoro mamy stabilizator, to może napięcie być oczywiście inne.

Zasilanie (3,3V) i masa zostały podłączone zarówno do ESP, jak i czujnika DHT (czerwone i czarne przewody). Pomarańczowy przewód połączył zasilanie z CH_PD (ESP-01). A żółte przewody łączą przwód danych czujnika z pinem GPIO2 w ESP. Sprzętowo, jak widać, jest to bardzo prosty układ.

AM2301

Ciekawy jest sam czujnik wilgotności. AM2301, znany jako DHT21, zgodny z DHT22 oferuje dobry zakres pomiarów (0%-100% wilgotność i -40 do 80 stopni C), jak i niewielki błąd (poniżej 3 punktów procentowych wilgotności i ćwierć stopnia Ceslsjusza w najbardziej interesującym zakresie temperatur). Komunikacja odbywa się przez jeden przewód (plus oczywiście zasilanie). Czujnik nie wymaga kalibracji. Zasilany może być napięciem 3,3 - 5,2V, a przewód łączący z układem nadrzędnym może mieć do 20m (zapewne przy 3,3V - mniej). W roli higrometru pokojowego sprawdzi się więc idealnie. Sam czujnik sprawdziłem już przy okazji innegro projektu i spisuje się świetnie.

Sprzętowo wszystko jasne, więc możemy przejść do programowania. Jako, że to pierwszy projekt w NodeMcu, więc na początek kilka uwag praktycznych, które nasunęły mi się przy tworzeniu pierwszego programu. Sam kod jest bardzo prosty, natomiast jego pisanie, a przede wszystkim testowanie nastęczyło drobnych problemów. Nic wielkiego, ale jednak.

Przede wszystkim - pamięć. Komenda print(node.heap()) wyświetla rozmiar wolnej pamięci (sterty, możemy uznać, że jest to tożsame) i o ten rozmiar będziemy dbać. Dlaczego? Bo mamy jej bardzo ograniczoną ilość, szczególnie, że posługujemy się językiem skryptowym, interpretowanym, nieco rozrzutnym w tym względzie. Ja uznałem, że warto zwrócić na to uwagę, do tego stopnia, że przy każdej komunikacji z serwerem będę przesyłał informację o rozmiarze wolnej pamięci. Być może jestem nieco przewrażliwiony, natomiast można się spodziewać, że wycieki pamięci przy naszym programie, używanych modułach, a także w interpreterze języka mogą być (acz nie muszą) bolesne.

Być może jestem przewrażliwiony, natomiast projekt zrealizowałem na firmware z 8 stycznia, zamiast na najnowszym, bo w tym drugim był problem z dołączeniem biblioteki do DHT22 - być może ze względu na dostępną mniejszą wielkość wolnej pamięci. Dodatkowo postanowiłem restartować układ (node.restart()), gdy wielkość stosu spadnie poniżej jakiejś wartości. Trochę dmuchanie na zimne, bo układ pewnie zrobiłby to sam, natomiast dla działania tego projektu nie ma to żadnego znaczenia - nic z nim nie będzie się łączyć, nie jest istotne zachowanie stanów, chodzi jedynie o cykliczne wysyłanie pomiarów do serwera.

ESP8266 + AM2301 (DHT21)

Problemy z firmware nauczyły mnie jednej podstawowej rzeczy - przy tworzeniu rozwiązania nie warto umieszczać głównego od razu w init.lua. Jeżeli błąd spowoduje, że układ będzie się co chwilę restartował, to nie uda się wgrać poprawionego pliku i trzeba będzie zacząć od wgrania nowego firmware. Lepiej więc program umieścić np. w test.lua i uruchamiać go wydając komendę require(’test’) lub dofile(’test.lua’).

Przejdźmy do samego programu. Jego celem jest cykliczne odczytywanie pomiarów z czujnika i wysyłanie tych pomiarów do serwera www (przez zapytanie GET). Cykliczne wywołanie pomiarów będzie się odbywało przez jeden z zegarów, uruchomiony funkcją tmr.alarm. Po każdym pomiarze (także nieudanym) będzie uruchamiana komunikacja z serwerem (także przez zegar, dla zachowania jak największej asychroniczności i dla oddania wątku na obsługę systemu, np. wifi). Co jakiś czas będzie też uruchamiana funkcja sprawdzająca czy nie warto zrestartować układu (w przypadku niewielkiej dostępnej pamięci lub po określonej liczbie błędów komunikacji). Jak pisałem, to trochę dmuchanie na zimne, bo próby pokazują, że zaniki połączenia są dobrze obsługiwane, ale urządzenie ma pracować w trybie ciągłym, więc im więcej przypadków obsłużymy, tym lepiej. Poza rozmiarem pamięci przy każdym połączeniu raportowany będzie też numer komunikacji - dla wykrycia ewentualnych restartów.

dht22 = require("dht22")
gpio0 = 3
gpio2 = 4
local timeout=60000
local tmrtimeout=6
local lasttemp = -100
local lasthum = -100
local numreq=0;
local connwait=false;
local numerr=0;
local httpsend=function(txt)
 if connwait == true then
 numerr=numerr+1
 else
 connwait=true
 tmr.alarm(tmrtimeout, timeout, 0, function() conn:close() connwait=false numerr=numerr+1 end)
 conn=net.createConnection(net.TCP, 0) 
 conn:on("receive", function(conn, pl) print(pl) end)
 conn:on("sent", function(conn) numreq=numreq+1 end)
 conn:on("connection", function(conn) conn:send("GET /url/pomiar.php?i="..node.chipid().."&h="..node.heap().."&rq="..numreq..txt.." HTTP/1.0\nHost: www.luksoft.org\n\n") end)
 conn:on("disconnection", function(conn) conn:close() tmr.stop(tmrtimeout) connwait=false end)
 conn:connect(80,"www.mojserwer.com")
 end
end
local checknode=function()
 if numerr>20 then node.restart() end 
 if node.heap()<1000 then node.restart() end
end
local checkdht=function()
 dht22.read(gpio2)
 t = dht22.getTemperature()
 h = dht22.getHumidity()
if t>-400 then 
 lasttemp=t
 lasthum=h
 tmr.alarm(2, 50, 0, function() httpsend("&t="..lasttemp.."&h="..lasthum) end)
 else
 tmr.alarm(2, 50, 0, function() httpsend("&msg=dhtproblem") end)
 end
end
tmr.alarm(0, 10000, 1, checkdht)
tmr.alarm(1, 90000, 1, checknode)

Program wykorzystuje moduł do obsługi DHT22 , który odpowiada za całą obsługę czujnika. Dla lepszej czytelności, trzy kluczowe funkcje zostały wyróżnione kolorami, natomiast do analizy kodu najlepiej skopiować go do jakiegoś edytora, żeby mieć pewność, że końce wierszy nie zostały ucięte. Przy powyższych parametrach, zapytania wykonywane są co 10 sekund. Docelowo planuję minutę, natomiast na razie testuję także stabilność układu, stąd częste zapytania.

Co po drugiej stronie? Skrypt php, który odbiera pomiary (dzieli przez 10, dla uzyskania właściwych wartości) i przez rddtool zapisuje w pliku. Następnie generowane są wykresy zmian temperatury i wiglotności. Poniważ ten fragment opisywałem już przy innej okazji, teraz nie będę do tego wracał.

Czy ESP8266 z oprogramowniem NodeMCU działa stabilnie? Zobaczymy. To pierwsze urządzenie ma między innymi na celu zdiagnozowanie stabilności układu i oprogramowania. ESP8266 posiadam od niedawna i do tej pory nie miałem problemu, jednak póki co najdłużej działający układ wykonuje zapytania dopiero czwartą dobę, trudno więc cokolwiek wyrokować. Ja w każdym razie wiążę z tym układem spore nadzieje.