...

четверг, 9 апреля 2015 г.

[Из песочницы] Многофункциональный датчик температуры/влажности на ESP8266 или еще один шаг к «интернету вещей»

Дисклеймер: данная статья может содержать ошибки, поскольку я не так давно работаю с модулем ESP8266 и еще не до конца понимаю многие архитектурных аспекты данного устройства.

Сегодня практически в любом доме есть Wi-Fi роутер и было бы недальновидно не воспользоваться этим устройством для домашней автоматизации, тем более что сегодня на рынке есть все доступное оборудование для реализации любых идей. Ниже будет представлен вариант создания небольшого электронного устройства, являющегося платформой для построения различных датчиков/исполнительных механизмов на основе Wi-Fi модуля — ESP8266.


image



Данный модуль хорошо описан здесь, здесь, а на этом сайте вы найдете вообще все, что знает человечество о модуле ESP8266.


Итак, что должно «уметь» устройство:



  • Получать данные с датчика влажности/температуры DHT22;

  • Управлять твердотельным реле (например SSR-25 DA);

  • Подключаться к Wi-Fi роутеру с заданным логином и паролем;

  • Передавать и получать данные через MQTT брокер;

  • Подключаться по USB для отладки и прошивки.


Схема устройства:


image


Модифкаций модуля ESP8266 очень много (варианты здесь), но, в принципе, отличаются они только размерами, типом антенны и количеством доступных портов ввода-вывода. Я использовал модуль ESP8266 ESP-01:


image


У него всего два порта (не считая USART) — GPIO0, GPIO2, но для моих целей достаточно, один порт — для датчика и второй — для управления нагрузкой.


Интерфейс USB реализуется USB-USART преобразователем CH340G.


image


Здесь описанно его подключение к 3.3 и 5В логике. Микросхема очень дешевая и удобная в использовании. Из обвязки только кварцевый генератор на 12MHz и пара конденсаторов. В итоге у вас с одной стороны USART, а с другой — USB. На PC устройство отображается как виртуальный последовательный порт.


Для управления нагрузкой я использовал пару транзисторных ключей. Почему так — спросит прожженный электронщик внимательный читатель. Все дело в том, что напряжение питания может быть разным, а мне хотелось, чтобы реле управлялось напряжением=напряжению питания. При использовании pnp транзистора, ток эмиттера (при напряжении питания > напряжения модуля ESP8266) пойдет в модуль, что совсем не хорошо. Использовать только npn транзистор я не мог, так как тогда порт GPIO0 все время будет подтянут к минусу, а в этом случае модуль будет входить в режим программирования каждый раз, когда мы рестартим модуль. Таким образом, подключив pnp + npn транзисторы, я управляю минусом реле.


Датчик DHT22:


image


Не требует никакой дополнительной обвязки и подключается прямо к модулю ESP. Ему нужен только один порт для обмена данными(интерфейс подобный 1-wire).


Так же на схеме:



  • Перемычка JP2 — для прошивки модуля. Перемычка подтягивает GPIO0 к минусу. В рабочем режиме — разомкнута, во время прошивки — замкнута);

  • Кнопка S1 — reset модуля;

  • Разъем SV1 — подключение реле;

  • Разъем J1 — гнездо питания;

  • Разъем JP1 — micro USB мама.




Питается устройство 5 — 12В.

Теперь поговорим о программном обеспечении.


Есть такой проект NodeMCU. На мой взгляд, очень крутая штука. Небольшая ОС, которая может выполнять ваши lua-скрипты прямо на ESP8266. NodeMCU умеет работать с кучей протоколов из коробки, может поднять web сервер, создать TCP соединение…


В начале прошиваем в наш модуль NodeMCU. Инструкция по прошивке.


После того, как модуль прошит, можно загружать наши скрипты. Способов множество, но лично мне нравится утилита ESPlorer — очень удобная софтина не только для загрузки скриптов, но и для разработки, дебага скриптов.


Теперь более подробно. Нам нужно залить три скрипта:


dht22.lua - собственно модуль считывающий данные с датчика DHT22
— ***************************************************************************

— DHT22 module for ESP8266 with nodeMCU

— — Written by Javier Yanez

— but based on a script of Pigs Fly from ESP8266.com forum

— — MIT license, http://ift.tt/UfT0PS

— ***************************************************************************

local moduleName =…

local M = {}

_G[moduleName] = M


local humidity

local temperature


function M.read(pin)

local checksum

local checksumTest

humidity = 0

temperature = 0

checksum = 0


— Use Markus Gritsch trick to speed up read/write on GPIO

local gpio_read = gpio.read


local bitStream = {}

for j = 1, 40, 1 do

bitStream[j] = 0

end

local bitlength = 0

— Step 1: send out start signal to DHT22

gpio.mode(pin, gpio.OUTPUT)

gpio.write(pin, gpio.HIGH)

tmr.delay(100)

gpio.write(pin, gpio.LOW)

tmr.delay(20000)

gpio.write(pin, gpio.HIGH)

gpio.mode(pin, gpio.INPUT)


— Step 2: DHT22 send response signal

— bus will always let up eventually, don't bother with timeout

while (gpio_read(pin) == 0 ) do end

local c=0

while (gpio_read(pin) == 1 and c < 500) do c = c + 1 end

— bus will always let up eventually, don't bother with timeout

while (gpio_read(pin) == 0 ) do end

c=0

while (gpio_read(pin) == 1 and c < 500) do c = c + 1 end


— Step 3: DHT22 send data

for j = 1, 40, 1 do

while (gpio_read(pin) == 1 and bitlength < 10 ) do

bitlength = bitlength + 1

end

bitStream[j] = bitlength

bitlength = 0

— bus will always let up eventually, don't bother with timeout

while (gpio_read(pin) == 0) do end

end


--DHT data acquired, process.

for i = 1, 16, 1 do

if (bitStream[i] > 3) then

humidity = humidity + 2 ^ (16 — i)

end

end

for i = 1, 16, 1 do

if (bitStream[i + 16] > 3) then

temperature = temperature + 2 ^ (16 — i)

end

end

for i = 1, 8, 1 do

if (bitStream[i + 32] > 3) then

checksum = checksum + 2 ^ (8 — i)

end

end


checksumTest = (bit.band(humidity, 0xFF) + bit.rshift(humidity, 8) + bit.band(temperature, 0xFF) + bit.rshift(temperature, 8))

checksumTest = bit.band(checksumTest, 0xFF)


if temperature > 0x8000 then

— convert to negative format

temperature = -(temperature — 0x8000)

end


— conditions compatible con float point and integer

if (checksumTest — checksum >= 1) or (checksum — checksumTest >= 1) then

humidity = nil

end

end


function M.getTemperature()

return temperature

end


function M.getHumidity()

return humidity

end


return M






main.lua - основной скрипт, выполняет подключение к Wi-Fi сети, получает данные, отправляет их по mqtt и управляет нагрузкой
function subscribe()

m:subscribe("/myhome/"..id.."/light",0,function(conn)print(«Subscribe success»)end)

m:on(«message»,function(conn,topic,data)

print(topic… ": "..data )

if data==«ON»then gpio.write(3, gpio.LOW)end

if data==«OFF»then gpio.write(3, gpio.HIGH)end

end)

end

function dht22_get_data()

dht22=require(«dht22»)

dht22.read(4)

local t=dht22.getTemperature()

local h=dht22.getHumidity()

if t~=nil then

t=((t-(t % 10))/10).."."..string.format("%.i",(t % 10))

else t=nil

end

if h~=nil then

h=((h-(h % 10))/10).."."..string.format("%.i",(h % 10))

else h=nil

end

dht22=nil

package.loaded[«dht22»]=nil

collectgarbage()

return t, h

end

function post_data()

t, h = dht22_get_data()

if t ~= nil then

m:publish("/myhome/"..id.."/temperature",t,0,0, function()

print(«Temperature »..t)

if h ~= nil then

m:publish("/myhome/"..id.."/humidity",h,0,0, function()print(«Humidity »..h)end)

end

end)

end

end


function init_network()

collectgarbage()

print(id)

if wifi.sta.status() ~= 5 then

print(«Reconnecting WIFI»)

wifi.setmode(wifi.STATION)

wifi.sta.config(«Login»,«password»)

wifi.sta.connect()

tmr.alarm(0,5000,0,function()init_network()end)

else

print(«IP: »..wifi.sta.getip())

print(«Connecting to MQTT server»)

tmr.alarm(0,7000,0,function()init_network()end)

if m~=nil then

m:close()

end

m = mqtt.Client(id, 120)

m:connect(«192.168.0.x»,1883,0,function(conn)

tmr.stop(0)

print(«Connected»)

subscribe()

tmr.alarm(0, 60000, 1, function() post_data() end)

m:on(«offline»,function(con)

print(«offline.Reconnecting»)

init_network()

end)

end)

end

end


gpio.mode(3, gpio.OUTPUT)

id=«esp_»..wifi.sta.getmac()

init_network()






init.lua - стартовый скрипт. Его первым запускает NodeMCU на старте.

print(«ESP8266_home_board_v_x.x»)

dofile('main.lc')





Здесь есть нюанс. К сожалению, внешней флэш памяти модуля не достаточно для загрузки NodeMCU и моих скриптов, поэтому я использую следующее «костыльное» решение: загружаю один скрипт, выполняю команду node.compile(«dht22.lua») — данная команда компилирует скрипт в «dht22.lc», в результате он занимает меньше места и во флэш памяти и в оперативной памяти, та как потом NodeMCU будет загружать его в память во время выполнения основного скрипта. Потом удаляем нескомпилированный скрипт командой file.remove («dht22.lua»). Проделываем те же манипуляции с main.lua. Последним загружаем init.lua скрипт, его уже не компилируем. Рестартим модуль.

На старте NodeMCU выполнит «init.lua» скрипт, который в свою очередь запустит «main.lua». «main.lua» скрипт будет коннектится к сети, отправлять данные в COM порт и в сеть на заданный mqtt брокер.


Более подробно по скриптам отвечу в комментариях.


Ну, вроде бы все. Если тема интересна, в следующей статье расскажу про mqtt брокер и подключение всего этого дела к Openhab.


Спасибо за внимание.


This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий