Mam już drukarkę 3d. Pora jej użyć w praktycznym zastosowaniu – zbudować kompletne urządzenie z porządną obudową. Skoro już ma być solidnie i elegancko, to będzie także płytka drukowana i żadnego kleju na gorąco. A wszystko przygotuję od podstaw, razem z projektami i oprogramowaniem dla ESP8266.
Na początek założenia – urządzenie ma być małe, ale nie musi być miniaturowe. Ma mieć możliwość podłączenia kilku termometrów do magistrali one-wire, ale ze względów praktycznych musi być możliwość podłączenia wszystkich przewodów do urządzenia, żeby uniknąć łączenia przewodów na zewnątrz. Oczywiście zaletą magistrali jest to, że wiele czujników można umieszczać na jednym kablu, natomiast w praktycznych zastosowaniach, np. przy monitoringu temperatur w kotłowni, dobrze jest jak wszystkie przewody wychodzą z jednego miejsca i nie są łączone dalej. Dla maksymalnej prostoty i uniknięcia instalacji kilku gniazd i wtyczek, termometry będą podłączane przez złącza goldpin wewnątrz obudowy.
Jednostką sterującą będzie ESP8266, a konkretnie jego mała odmiana ESP8266-01, która posiada wyprowadzenia w rastrze 2,54mm i dysponuje 2 wyjściami GPIO. Do urządzenia będzie można podłączać termometry Dallas DS18B20, które pracują na magistrali one-wire. Układ zostanie zaprogramowany w języku LUA dzięki NodeMCU. Zasilany będzie z zewnętrznego zasilacza, którego napięcie będzie stabilizowane do 3,3V wewnątrz urządzenia. Podczas projektowania warto wykorzystać doświadczenia z wcześniejszego projektu.
Elektronika
Trzeba zająć się projektem urządzenia od strony elektronicznej. Schemat jest bardzo prosty i zakłada użycie:
- gniazda zasilania dla wtyku okrągłego 5,5mm x 2,5mm
- stabilizatora LD33V (L1117)
- kondensatorów potrzebnych dla stabilizatora – 100nf i 10μF
- gniazda goldpin 4×2 dla wetknięcia ESP8266-1
- rezystora dla połączenia linii danych z zasilaniem (4700Ω według specyfikacji lub 1000Ω w moim przypadku)
- pięciu złącz goldpin 3×1 dla podłączenia termometrów
Schemat został przygotowany w programie Eagle, który umożliwia także przygotowanie projektu płytki drukowanej. Do schematu powstała płytka drukowana o wymiarach 3cm x 5cm.
Warto zauważyć, że pole masy jest nieciągłe, więc lutując trzeba połączyć dwa pola przewodem (są potrzebne otwory w płytce). Na płytce znajdują się otwory do mocowania, natomiast zakładam, że będzie ona trzymana przez obudowę lub przykręcona jednym wkrętem (przez otwór w obudowie stabilizatora).
Płytkę można trawić samemu, ale można też zamówić ją u jednego z usługodawców. Lutowanie kilku elementów nie powinno dla nikogo stanowić problemu.
Obudowa
Aby zaprojektować obudowę, nauczyłem się podstaw obsługi programu OpenSCAD. Wybrałem go, bo jest bezpłatny, a projektowanie przypomina programowanie w specjalnym języku opisującym obiekty trójwymiarowe. Dla mnie jest to bardzo przyjazne, precyzyjne i daje duże możliwości. W programie tym zapisuje się obiekty w formacie .scad, jednak do zewnętrznego użycia eksportuje się je do formatu .stl, który jest szeroko używany przez programy do druku 3d.
Mając plik STL wygenerowany, dobrze jest go przepuścić przez jedno z narzędzi sprawdzających i naprawiających błędy (np. przenikanie się modelu czy tzw. wodoszczelność). Można to zrobić online. W rezultacie otrzymujemy plik stl, który już bez obaw można zaimportować do programu do obsługi druku (np. Repetier Host) czy wprost do slicera (np. Slic3r). W rezultacie otrzymujemy plik z gcode, czyli poleceń zrozumiałych dla drukarki 3d. Możemy go wgrać na kartę SD dla drukarki lub uruchomić drukowanie przez USB.
Po uruchomieniu wydruku pozostaje czekać. A potem wydrukować jeszcze górną część. Wydruk z filamentu PET-G wychodzi zawsze jak trzeba i bardzo dobrze wygląda. Mi szczególnie podoba się efekt przezroczystości.
Płytka wchodzi do obudowy ciasno, dzięki czemu jest stabilna, nawet bez przykręcania.
Oprogramowanie
Skoro sprzęt jest już gotowy pozostaje napisać oprogramowanie. Sam sposób wgrania programu oraz język programowania, były już opisywane na blogu. Nieco natomiast zmienił się sam projekt NodeMCU i teraz nie ma już gotowych obrazów do pobrania, w miejscu, gdzie utrzymywany jest projekt. Docelowe binaria można albo wygenerować samemu, albo użyć narzędzia do przygotowywania obrazów online. Jest to bardzo dobre podejście, bo można wybrać tylko te moduły, które są potrzebne i nie przejmować się brakiem pamięci RAM na pisane programy. Dla potrzeb internetowego termometru wybrałem kompilację z modułami bit, enduser_setup, file, gpio, net, node, ow, tmr, uart, wifi. Dostałem dwa linki do ściągnięcia, z których wybrałem wersję float. Po wgraniu firmware można przygotować program. Zaczynamy od init.lua:
wifi.setmode(wifi.STATION) wifi.sta.config("siecwifi","tajnehaslo") node.compile('ds18b20.lua') node.compile('test.lua') dofile('test.lc')
Ten plik definiuje tryb pracy wifi i dane dostępowe do sieci. Następne dwie linijki są nowością (w stosunku do ostatnich moich opisów) i odpowiadają za kompilację plików ze źródłami programów. Język Lua jest interpretowany i nie trzeba tego robić, natomiast pozwala to zaoszczędzić sporo pamięci RAM przy wykonywaniu programu. Ostatnia linijka wywołuje program zapisany w pliku test.lc, skompilowanym z test.lua. Będzie on odpowiadał za pracę urządzenia – sprawdzanie temperatur, wysyłanie ich do serwera oraz ewentualne restarty przy nieudanych próbach połączenia lub malejącej dostępnej pamięci.
Plik ds18b20.lua jest okrojoną przeze mnie wersją biblioteki do obsługi termometrów, w czasach gdy zajętość pamięci była jeszcze dużym problemem:
local modname = ... local M = {} _G[modname] = M local pin = nil local table = table local string = string local ow = ow local tmr = tmr setfenv(1,M) function setup(dq) pin=dq ow.setup(dq) end function addrs() setup(pin) tbl = {} ow.reset_search(pin) repeat addr = ow.search(pin) if(addr ~= nil) then table.insert(tbl, addr) end tmr.wdclr() until (addr == nil) ow.reset_search(pin) return tbl end function readNumber(addr, unit) result = nil setup(pin) flag = false if(addr == nil) then ow.reset_search(pin) count = 0 repeat count = count + 1 addr = ow.search(pin) tmr.wdclr() until((addr ~= nil) or (count > 100)) ow.reset_search(pin) end if(addr == nil) then return result end crc = ow.crc8(string.sub(addr,1,7)) if (crc == addr:byte(8)) then if ((addr:byte(1) == 0x10) or (addr:byte(1) == 0x28)) then ow.reset(pin) ow.select(pin, addr) ow.write(pin, 0x44, 1) present = ow.reset(pin) ow.select(pin, addr) ow.write(pin,0xBE,1) data = nil data = string.char(ow.read(pin)) for i = 1, 8 do data = data .. string.char(ow.read(pin)) end crc = ow.crc8(string.sub(data,1,8)) if (crc == data:byte(9)) then if(unit == nil or unit == C) then t = (data:byte(1) + data:byte(2) * 256) * 625 else return nil end t = t / 10000 return t end tmr.wdclr() else end else end return result end function read(addr, unit) t = readNumber(addr, unit) if (t == nil) then return nil else return t end end return M
Pozostał jeszcze główny program – test.lua:
vds = require("ds18b20") 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 temps={} 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) print("receive\n") end) conn:on("sent", function(conn) print("sent\n") numreq=numreq+1 end) conn:on("connection", function(conn) print("connection\n") conn:send("GET /skrypty/esp.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) print("disconnection\n") conn:close() tmr.stop(tmrtimeout) connwait=false end) conn:connect(80,"www.mojserwer.org") end end local checknode=function() print("checknode() "..node.heap().."\n") if numerr>20 then node.restart() end if node.heap()<1000 then node.restart() end end local discoverds=function() print("discoverds() ") addrs = vds.addrs() for i,id in ipairs(addrs) do print("*") if temps[id]==nil then temps[id]=-100 end end print("\n") end local actnum=1 local readnexttemp=function() print("readnexttemp()\n") local acttemp if addrs~=nil then acttemp=vds.read(addrs[actnum],nil) if(acttemp==nil) then acttemp=-100 end if(acttemp>-40 and acttemp<85) then temps[addrs[actnum]]=acttemp end actnum=actnum+1 if actnum>table.getn(addrs) then actnum=1 end end end local checkds=function() print("checkds()\n") local u="0" if(addrs~=nil and table.getn(addrs)>0) then u=string.format("%i", table.getn(addrs)) for i,id in ipairs(addrs) do if temps[id]~=nil then u=string.format("%s,%02X%02X%02X%02X%02X%02X%02X%02X,%.2f", u, id:byte(1),id:byte(2),id:byte(3),id:byte(4),id:byte(5),id:byte(6),id:byte(7),id:byte(8),temps[id]) end end end tmr.alarm(5, 50, 0, function() httpsend(string.format("&ds=%s", u)) end) end vds.setup(gpio2) discoverds() tmr.alarm(0, 30000, 1, checkds) tmr.alarm(1, 90000, 1, checknode) tmr.alarm(2, 3600000, 1, discoverds) tmr.alarm(3, 5000, 1, readnexttemp)
Ostatnie 4 linie odpowiadają za ustawienie cyklicznego wywoływania funkcji:
- checkds – formatującej odpowiedź z tablicy temperatur i wywołującej połączenie z serwerem www
- checknode – sprawdzającej czy nie warto zrestartować układu
- discoverds – sprawdzającej czy przypadkiem nowe termometry nie zostały podłączone
- readnextemp – odczytującej temperaturę z kolejnego termometru DS18B20
Zapytanie GET do serwra wysyłane jest w formacie:
/esp.php?i=<identyfikator_esp>&h=<ilość_wolnej_pamięci>&rq=<numer_wywołania_od_restartu>&ds=<liczba_termometrów>,<numer_seryjny_termometru1>,<temperatura_z_termometru1>,<numer_seryjny_termometru2>,<temperatura_z_termometru2>,…
np.
/esp.php?i=10310057&h=27448&rq=152&ds=2,2814B86704000060,27.50,283240BF040000E1,27.63
To co zrobi serwer www z tą informacją to już inna sprawa. Może zapisać do bazy i wygenerować wykresy albo ostrzec kogoś, że za bardzo rośnie temperatura, albo włączyć ogrzewanie, albo… zależy do czego ma służyć to urządzenie. U mnie zwykle służą do monitoringu temperatur – rysowania wykresów i ewentualnie ostrzegania o nieprawidłowościach. Jest też elementem systemu nawadniania ogródka.
Przy domach z piecami węglowymi bardzo przydaje się do analizy pracy kotłów i zaworów czwórdrogowych. Widać wszystko jak na dłoni. Mi przykładowo pozwoliło od razu zauważyć awarię zaworu zwrotnego w obwodzie cyrkulacji ciepłej wody użytkowej (i ciągłą cyrkulację grawitacyjną). Bez tego pewnie zastanawiałbym się, czemu kocioł pali tak dużo węgla. Przykładowy wykres z pracy kotła na ekogroszek latem:
Zastosowań urządzenia może być wiele. Można je też udoskonalać. Przykładowo do obudowy można dołożyć uchwyty do powieszenia na ścianę, gdyby miało być zainstalowane np. w kotłowni na stałe. Do kotłowni zrezygnowałbym też z otworu wentylacyjnego w obudowie, żeby zmniejszyć przenikanie pyłu węglowego.
Prototyp tego urządzenia działa u mnie już ponad rok (a pewnie znacznie dłużej) bez żadnych problemów czy potrzeby ręcznego restartowania.
Zainteresowanym mogę udostępnić wszystkie pliki konieczne do samodzielnego wykonania termometru według powyższego projektu.
Warto zaznaczyć, że wszystkie użyte programy są multiplatformowe. Ja używałem ich pod Ubuntu, natomiast nic nie stoi na przeszkodzie, żeby projekty wykonać w środowisku Windows.
Brawo kolego.
Swietna sprawa, co jest centrala?
Mozesz udostepnic projekt z Eagla?
Po drugiej stronie mam dwie wersje – skrypt php lub nodejs. W obu przypadkach wartości są przechowywane w bazie rrd. Komplet lików można pobrać z http://przeklej.org/file/A93Zb4/termometr-wifi-blog.zip
Sama obudowa jest do pobrania po adresem http://www.thingiverse.com/thing:1853704
Witam.
Mógłbym prosić o kod programu zarówno w php jak i lua
Pozdrawiam. Tadzik
W Lua jest, natomiast w jednym z poprzednich wpisów była wersja nodejs zamiast php.
Do projektowania 3D dla początkujących fajny jest jeszcze DesignSpark Mechanical. Jest darmowy do zastosowań niekomercyjnych i projektuje się w nim bardzo prosto.
Dodam jeszcze, że podobny i także darmowy jest także Fusion 360. Jest w sumie o tyle lepszy, że jest to pełna wersja płatnego programu. DesignSpark Mechanical to taka trochę okrojona wersja SpaceClaim.
Ciekawy projekt bo obsługuje kilka czujników na jednym porcie. Ostatnio zainteresowałem się projektem Supla, ciągle rozwijany, ale brakuje mu właśnie kilku czujników na jednym module ESP8266. Może autor artykułu ogarnie temat dodania kilku czujników w Supli, ciekawie by to wyglądało ponieważ wszystko można odczytywać na aplikacji pod systemem Android.
Nie znam projektu Supla, ale może w wolnej chwili popatrzę na niego.
Witam,
@Techniczny, czy byłaby szansa na otrzymanie kompletu plików? z góry dzięki.
Tak, dostępne są pod https://www.thingiverse.com/thing:1853704
Dzięki Techniczny, a jeszcze jedno – dałbyś radę jeszcze raz wrzucić gdzieś kpl. skryptów?
Cześć. Mam jedno pytanko. Dlaczego zasilanie 5V stabilizowane do 3.3 zamiast poprstu podpiąć USB?
Ponieważ ESP8266 wymaga napięcia 3.3V. USB daje 5V.
A jakbyśmy chcieli podlączyć LCD to 3.3V wystarczy?
Wtedy przede wszystkim raczej nie wybieramy modułu ESP-01…
Tak myślałem ale arduino uno z ESP8266 zrywa mi połączenie- nie tylko ja mam ten problem a na nodzie chodzi super
Witam.
Robię bardzo podobny projekt oparty o ESP8266 12E.
Sprawdzałem go wysyłając dane na thingspeak.com i dane są tam zbierane czyli wysyłanie wydaje się działać prawidłowo.
Chcę tak jak w tym przykładzie informacje wysyłać na mojej stronce ale nie działa mi ani metoda GET ani POST (oczywiście w odpowiedniej składni).
Kod wyświetlania danych na stronie jest poprawny, bo go sprawdziłem.
Ale nie dochodzą żadne dane z ESP na moją stronkę.
W ESPlorerze dostję komunikat zwrotny:
HTTP/1.1 404 Not Found
Date: Tue, 28 Nov 2017 22:45:13 GMT
Server: Apache
Content-Length: 328
Content-Type: text/html; charset=iso-8859-1
I jeszcze kilka linijek.
Podaję różne ścieżki dostępu i nic.
Zmieniam porty na serwer i też nic.
Próby prowadzę już z od jakiegoś czasu.
Net przekopałem i nie udało mi się znaleźć pomocy.
Może ktoś ma pomysł co mogę jeszcze zrobić lub sprawdzić?
Jesteś na dobrej drodze. 404 to odpowiedź z serwera mówiąca, że taki dokument nie istnieje. Sprawdź w logach serwera do czego się odwołujesz, to będzie wiadomo co robisz źle.
Pamiętaj też, że dla wirtualnych hostów bazujących na nazwie, musisz podać nazwę hosta w nagłówkach zapytania http. Może to jest problem?
Dzięki za szybką odpowiedź.
Co do loga to nie bardzo wiem czego w nim szukać a jest on duży.
Host nie jest wirtualny.
Mam jeszcze pytanie związane z serwerem.
Stronę powiedzmy aaa.pl mam na serwerze powiedzmy 100.100.100.100
Czy aby mój program zaczął wysyłać info na stronkę aaa.pl/esp8266.php to muszę zrobić tak:
conn:send(„GET /esp8266.php?temp=”..cels..” HTTP/1.1\r\n”)
conn:send(„Host: aaa.pl”)
conn:connect(80,”100.100.100.100″)
czy
conn:send(„GET /esp8266.php?temp=”..cels..” HTTP/1.1\r\n”)
conn:send(„Host: aaa.pl”)
conn:connect(80,”aaa.pl”)
?
Problem rozwiązany.
Miałem szkolny błąd.
Może komuś się przyda.
Po adresie hosta powinno być: \r\n
Jak poniżej:
conn:send(„Host: http://www.stronka.pl\r\n”)
Witam
Czy mógłbym prosić o udostępnienie plików do wykonania termometru według powyższego projektu.