Domoticz cz. 2: Termometr WiFi – stacja pogodowa, czyli ESP8266 dla początkujących w praktyce

Ostatnio uruchamialiśmy Domoticz’a – zanim przejdziemy do Z-Wave – przyjrzyjmy się fantastycznemu małemu gadżetowi – ESP8266

Co to jest ESP8266?

ESP8266 to SoC czyli System on a Chip, co w tym przypadku oznacza bardzo ciekawe rozwiązanie – CPU/RAM/FLASH z GPIO i WiFi – a więc mały bezprzewodowy mini komputer. Można o nim myśleć jako o ultra małym, bardzo energooszczędnym Pi w wersji AAA (z dość ograniczoną możliwością uruchamiania własnych programów), z własnym WLAN i minimalnie dwoma GPIO do sterowania. Tak możemy na nim uruchomić serwer WWW!

  • CPU – 80MHz (znacznie szybszy niż Arduino, znacznie wolniejszy niż Pi)
  • RAM – 64kB dla instytucji i 96kB
  • ROM – 64kB (uruchomienie)
  • FLASH: 4Mb (512kB)
  • WLAN: radio 2,4GHz – 802.11 b/g/n z obsługą WPA2
  • GPIO, I2C, ADC, SPI, PWM
  • Zasilanie 3,3V (przypadkiem mamy tyle na PIN 1 w Raspbery Pi)
  • Interpreter oraz kompilator LUA (sic!)
  • CENA: $3. Tak. Trzy dolary amerykańskie

Producentem jest Espressif, a inne firmy tworzą gotowe płytki ze złączami i antenami:

  • ESP-01 (wersja 1 i wersja 2) – część wspólna to zasilanie +3,3V, GND, TX,RX oraz GPIO 0 i GPIO 2 (mają także pull-up). Antena WiFi to wydrukowana ścieżka. Najpopularniejszy i najlepszy na początek – PINy takie jak w Pi, łatwy do podłączenia
  • ESP-03 – tym razem mamy 8x GPIO
  • ESP-12 – SoC ma ekranowanie, lepszą antenę oraz 10x GPIO

esp-07-web

Dlaczego?

Podsumujmy – mamy możliwość sterowania dwoma GPIO, łącząc się z urządzeniem po WiFi, które zużywa bardzo mało prądu. Możemy też napisać program, który cyklicznie dokonuje aktualizacji danych – ponieważ możemy nie tylko uruchomić serwer http (WWW) czy telnetu, ale także w pętli czytać z GPIO i przesyłać np: w stylu nodejs albo URL json.html dla domoticza. Ta druga opcja jest zdecydowanie bardziej stabilna i niewymagająca od ESP zbyt dużo na początek.
Zatem – mamy świetny zdalny termometr do stacji pogodowej – pod warunkiem, że są gotowe przykłady, które możemy zaadaptować.

Hardware

  • Raspberry Pi (dowolny model – A, B, B+, 2 lub 3, nawet Pi Zero)
    pi-b_plus2
  • ESP8266 (dowolna wersja, całkiem przyjazna wersja z PIN’ami to ESP-01 – tak też go szukamy)
    esp-01-pinout
  • Termometr ds18b20 – szukamy według nazwy – są wersje wodoodporne
    20151016_200115
  • Rezystor 4,7k – dla zapewnienia prawidłowego odczytu z temometru. Jeśli odczyt nie działa, możesz zastosować mniejszy – 2,2k:
    4k7
  • OPCJA: Moduł step-down zmiejszający napięcie stałe z 5V na 3,3V (szukamy przez step-down 3.3V)
    pi-esp-12
  • OPCJA: Jedna lub dwie baterie Li-Ion 18650 – mogą być to baterie z starych baterii dla laptopów albo power banków – ale polecam bardzo dużą ostrożność w odzyskiwaniu z obudowy – 18650 są niebiezpieczne przy zwarciu, a bardzo niebiezpieczne przy perforacji! Może to być właściwie dowolna bateria Li-Ion o ile potrafimy ją bezpiecznie zamontować. Załadowana w pełni ma napięcie 4,2V i pojemność około 2300mAh
  • OPCJA: moduł zasilania i ładowania 5V do baterii Li-Ion. Często jest to moduł z wejściem USB (mini albo micro), który służy do odpowiedniego ładowania baterii 18650
    zasilacz-5v

Podłączenie i aktualizacja firmware

Układ posiada UART – złącza TX,RX – które świetnie nadają się do domyślnej komunikacji przez… Raspberry Pi. Jest to najlepsza moim zdaniem konsola dla ESP8266. Ta jednak domyślnie jest okupowana przez konsolę systemową – a więc jeśli podłączymy ją do ESP to zalejemy go danymi. Należy w pliku /boot/cmdline.txt usunąć część: console=/dev/ttyAMA0,115000. Po ponownym uruchomieniu możemy ją wreszcie wykorzystać.
Będziemy potrzebować:

pi-esp-conn

ESP8266 najczęściej trafia do nas z firmware reagującym na komendy AT, jeśli kiedyś rozmawialiście z modemem w latach 90-tych albo modemem GSM – wiecie o czym mówię. My jednak użyjemy firmware od NodeMCU – obsługujący LUA – bardzo prosty do nauki język programowania (a więc bardzo potężny!). W chwili pisania dostępny był: nodemcu_integer_0.9.6-dev_20150704.bin, który pobieramy wraz z narzędziem – esptool.py. Teraz przystępujemy do prawidłowego połączenia ESP8266 do Raspberry Pi:

  • pobieramy 3,3V z pinu PI – +3,3V i podłączamy do pinu VCC oraz do PINu CH_PD na ESP (zobacz na zdjęciu – należy połączyć kable razem)
  • GND z Pi do GND na ESP
  • PIN 13 Pi do TX na ESP a PIN 14 na Pi do RX ESP
  • Dodatkowo musimy jeszcze koniecznie zewrzeć GPIO 0 do GND na ESP-01, ponieważ tylko wtedy pozwoli na zmianę firmware. Aktualizacja firmware sprowadza się do wydania komendy:

Upewnijmy się najpierw – czy w systemie mamy bibliotekę do obsługi portu szeregowego przez pythona:

sudo apt-get install python-serial

Teraz możemy aktualizować firmware:

python esptool.py -p /dev/ttyAMA0 write_flash 0x000000 nodemcu_integer_0.9.6-dev_20150704.bin

Jeśli zapomniałeś o podłączeniu GPIO0 z ESP-01 do GND Raspberry Pi to zobaczysz:

pi@raspberrypi ~/esp8266 $ python esptool.py -p /dev/ttyAMA0 write_flash 0x000000 nodemcu_integer_0.9.6-dev_20150704.bin
Connecting...
Traceback (most recent call last):
  File "esptool.py", line 353, in 
    esp.connect()
  File "esptool.py", line 138, in connect
    raise Exception('Failed to connect')
Exception: Failed to connect

A jeśli nie to, po podłączeniu GPIO0 do masy (GND) i resecie (odłącz zasilanie ESP8266 na chwilę) aktualizacja przebiegnie poprawnie:

python esptool.py -p /dev/ttyAMA0 write_flash 0x000000 nodemcu_integer_0.9.6-dev_20150704.bin
Connecting...
Erasing flash...
Writing at 0x00005800... (5 %)
Writing at 0x00031800... (45 %)
Writing at 0x00057c00... (80 %)
Writing at 0x0006dc00... (100 %)

Leaving...

Po aktualizacji należałoby się oswoić z nową zabawką. W tym celu – dostaniemy się do niej przez konsolę. Instalujemy picocom i wydajemy:

picocom -b 9600 --omap crcrlf /dev/ttyAMA0

Jak widać powitał nas prompt „>”. Jeśli znacie LUA to można przystąpić do testów komend, albo po prostu użyć: przykładów z oficjalnej strony:

print(wifi.sta.getip())
--nil
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","password")
print(wifi.sta.getip())
--192.168.1.12

Jak można zobaczyć, po zamianie SSID oraz password na właściwe dla Waszej sieci domowej – urządzenie połączy się z Access Point’em i pobierze adres z DHCP. Dobra nasza, to połowa sukcesu. Teraz czas na połączenie termometru Dallas DS18b20. Jest to dość ciekawe i tanie rozwiązanie pozwalające na pomiar temperatury. NodeMCU posiada doskonały przykład obsługi tego konkretnego chipu, z którego skorzystamy. Pobieramy więc następujący plik z: biblioteki modułów: ds18b20.lua.

Ponieważ jednak standardowo po jego wykonaniu (o tym za chwilę) dostajemy precyzję tylko do jednego stopnia Celsjusza – lokalizujemy linię:

t = t / 10000

i zamieniamy ją na

t = t / 100

Otrzymamy teraz precyzję do dwóch miejsc po przecinku. Gotowy plik – dla pewności – zamieszczam jego wersję po zmianach:

--------------------------------------------------------------------------------
-- DS18B20 one wire module for NODEMCU
-- NODEMCU TEAM
-- LICENCE: http://opensource.org/licenses/MIT
-- Vowstar <[email protected]>
-- 2015/02/14 sza2 <[email protected]> Fix for negative values
--------------------------------------------------------------------------------

-- Set module name as parameter of require
local modname = ...
local M = {}
_G[modname] = M
--------------------------------------------------------------------------------
-- Local used variables
--------------------------------------------------------------------------------
-- DS18B20 dq pin
local pin = nil
-- DS18B20 default pin
local defaultPin = 9
--------------------------------------------------------------------------------
-- Local used modules
--------------------------------------------------------------------------------
-- Table module
local table = table
-- String module
local string = string
-- One wire module
local ow = ow
-- Timer module
local tmr = tmr
-- Limited to local environment
setfenv(1,M)
--------------------------------------------------------------------------------
-- Implementation
--------------------------------------------------------------------------------
C = 0
F = 1
K = 2
function setup(dq)
  pin = dq
  if(pin == nil) then
    pin = defaultPin
  end
  ow.setup(pin)
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
      -- print("Device is a DS18S20 family device.")
      ow.reset(pin)
      ow.select(pin, addr)
      ow.write(pin, 0x44, 1)
      -- tmr.delay(1000000)
      present = ow.reset(pin)
      ow.select(pin, addr)
      ow.write(pin,0xBE,1)
      -- print("P="..present)
      data = nil
      data = string.char(ow.read(pin))
      for i = 1, 8 do
        data = data .. string.char(ow.read(pin))
      end
      -- print(data:byte(1,9))
      crc = ow.crc8(string.sub(data,1,8))
      -- print("CRC="..crc)
      if (crc == data:byte(9)) then
        t = (data:byte(1) + data:byte(2) * 256)
        if (t > 32767) then
          t = t - 65536
        end
        if(unit == nil or unit == C) then
          t = t * 625
        elseif(unit == F) then
          t = t * 1125 + 320000
        elseif(unit == K) then
          t = t * 625 + 2731500
        else
          return nil
        end
        t = t / 100
        -- print("Temperature="..t1.."."..t2.." Centigrade")
        -- result = t1.."."..t2
        return t
      end
      tmr.wdclr()
    else
    -- print("Device family is not recognized.")
    end
  else
  -- print("CRC is not valid!")
  end
  return result
end

function read(addr, unit)
  t = readNumber(addr, unit)
  if (t == nil) then
    return nil
  else
    return t
  end
end

-- Return module table
return

zachowujemy na później (za chwilę o wgrywaniu na ESP) – przechodzimy do podstawowej własności ESP8266 w wersji z LUa od NodeMCU – uruchamianiu programów po starcie SoC’a

Autostart programów na ESP8266

ESP8266 po starcie wykonuje automatycznie program w init.lua, który jest umieszczony na jego flash. Bardzo ważne jest przetestowanie programu przed zapisaniem go jako init.lua, tak aby ESP nie wpadł w pętlę. Wszyscy znamy sytuacje, gdzie jeden błąd powoduje, że pętla wykonuje się w nieskończoność. W związku z tym – nasz plik/program init.lua powinien zawierać minimalny zestaw komend – faktycznie powinien tylko pobrać adres IP przez WiFi i wykonać główny program czytający i wysysający temperaturę do domoticza. Tak więc niezłym pomysłem będzie próba połączenia z DWOMA różnymi sieciami WiFi – najpierw z pierwszą – a jeśli się nie powiedzie to z drugą. Dzięki temu mamy możliwość ustawienia sobie 'roboczej’ sieci WiFi na potrzeby debugowania lub zmiany oprogramowania w ESP. Druga ważna funkcja to pauzy. Init.lua przed pobraniem adresu powinno dawać wystarczająco dużo czasu na zmianę pliku init.lua – skasowanie go, tak aby zastąpić go nowym – jeśli nie mamy możliwości zestawienia połączenia z siecią WiFi. Zatem – tworzymy init.lua w edytorze (nano, joe – na Pi):

--init.lua
 wifi.setmode (wifi.STATION)
 wifi.sta.config("MOJE SSID 1","tajne_haslo_do_ssid_1")
 wifi.sta.connect()
 tmr.alarm(1, 6000, 1, function()
  if wifi.sta.getip()== nil then
  print("Brak polaczenia i IP, probuje dalej...")
  wifi.sta.config("DRUGIE ZAPASOWE SSID","jeszcze_bardziej_tajne")
  wifi.sta.connect()
 else
  tmr.stop(1)
 print("Tryb pracy ESP8266: " .. wifi.getmode())
 print("Adres MAC ESP8266: " .. wifi.ap.getmac())
 print("Sukces, adres IP: "..wifi.sta.getip())
 dofile ("domoticz.lua")
 end
 end)

Tak przygotowany program na początku ustawia w tryb kliencki ESP (bo ESP może być też np: Access Pointem), ustawia parametry sieci WiFi i nakazuje się połączyć. Następnie po 6000ms spradzany jest warunek czy wifi.sta.getip() zawiera cokolwiek innego niż „nil” i jeśli nie zawiera – to konfigurowana jest zapasowa sieć WiFi i co 6000ms próba jest ponawiana do skutku. Jest to też czas na skasowanie programu jeśli coś poszło nie tak. Na końcu mamy komendę dofile, której zadaniem jest uruchomienie głównego programu – domoticz.lua:

Główny program odczytujący temperaturę oraz wysyłający do Domoticz’a:

--domoticz.lua
require('ds18b20')
gpio0 =3
gpio2 =4
ds18b20.setup(gpio2)
t=ds18b20.read()
print("Surowy odczyt z ds28b20:" .. t .. " C\n")
if(t==nil) then
t=0
end
tmr.alarm(0,240000, 1, function()
 t=ds18b20.read()
 tpre=t/100
 tsub=string.sub(t,-2)
 temp=tpre .. "." .. tsub
 print("Temperatura przekazywana:" .. temp .. " C\n")
 conn=net.createConnection(net.TCP, 0)
 conn:on("receive", function(conn, payload) print(payload) end )
 conn:connect(8080,"192.168.1.15")
 conn:send("GET /json.htm?type=command¶m=udevice&idx=11&nvalue=0&svalue=" .. temp .. " HTTP/1.1\r\nHost: termometr.domek\r\n".."Connection: keep-alive\r\nAccept: */*\r\n\r\n")
end)

Główny program ma za zadanie wykorzystać wcześniej załączoną bibliotekę ze strony NodeMCU (z drobną modyfikacją). Następnie ustawia PINy na ESP odpowiednio, tak aby czytać temperaturę z GPIO 2. Możemy szybko wyświetlić surowy odczyt, który będzie zawierał 4 cyfry. To jest właśnie sedno modyfikacji – nie przetestowany do końca hack, który pobiera 4 cyfry, a następnie manipulując zamianą na ciąg znaków – bierze ostanie dwie, dwie pierwsze i wstawia między nie kropkę. Tak spreparowaną zmienną wysyła używając interfejsu JSON domoticza, który występuje tutaj pod adresem 192.168.1.15 na porcie 8080 – w którym należy poprawnie podać IDX sensora temperatury jaki stworzyliśmy (Urządzenia). I to wszystko, chociaż nadal czekam na ujemną temperaturę aby móc wrócić do pomysłu odbierania precyzyjnych danych! Pozostaje więc umieszczenie trzech plików na ESP8266.

Wgrywanie programów na ESP8266

Dzięki kolejnemu programowi w Pythonie, możemy zamiast przepisywać linia po linii kod, który trzeba umieścić w pliku na ESP8266, otworzyć/zamknąć – możemy użyć luatool.py:

python luatool.py -p /dev/ttyAMA0 --src init.lua --dest init.lua

python luatool.py -p /dev/ttyAMA0 --src domoticz.lua --dest domoticz.lua
python luatool.py -p /dev/ttyAMA0 --src ds18b20.lua --dest ds18b20.lua

Jeśli wcześniej poprawnie udało się połączyć z ESP przez picocom, luatool.py powinien podać po kolei wydawane komendy, a następnie zapisać plik. Jeszcze raz przypominam, że init.lua musi być bezpieczny – jeśli ESP wpadnie w pętlę, zacznie się resetować – będzie ciężko wymazać filesystem.

Podłączenie do panelu fotowolataicznego (solarnego)

Ponieważ czujnik temperatury z WiFi jest z natury urządzeniem bardzo „mobilnym” – możemy zadbać o zasilenie go z baterii, które będą ładowane przez panel fotowoltaniczny. W tym celu należy wybrać panel, baterie Li-Ion oraz najlepszą metodę ładowania i zasilania.
Po kilku próbach najlepsze okazały się następujące komponenty:

  • trzy baterie Li-Ion typu 18650
  • kontroler ładowania z powerbanku DIY, który ma opcję jednoczesnego zasilania układu z portu USB i ładowania baterii z drugiego portu (micro) USB
  • Panelu 5V o mocy max. 400mA

UWAGA: Pamiętaj, aby nie używać baterii 18650 poza specyfikacją – np: w niskich lub wysokich temperaturach! Baterie 18650 mogą być niebezpieczne, jeśli używane są niezgodnie z przeznaczeniem i specyfikacją.
Panel montujemy na podstawce – tutaj po prostu kawałek pleksi i umieszczamy frontem do przechodzącego przez nieboskłon słońca pod kątem 45 stopni:

PC310007-768x479

Panel należy zabezpieczyć przed wilgocią – klejem:

PC310008-768x331

A następnie podłączyć do gotowego układu (przez przejściówkę z załączonej wtyczki na USB:

easy-dht22-powered-768x431

Tutaj jak widać mamy już wersję w obudowie ESP-12E oraz płytce NodeMCU (o czym w kolejnym wpisie o ESP Easy)

Previous Post Next Post


Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (c) 2014-2024 Łukasz C. Jokiel, [CC BY-NC-SA 4.0 DEED](https://creativecommons.org/licenses/by-nc-sa/4.0/)