Умный дом на ESP8266+MajorDoMo. Часть 4. Подключение преобразователя температуры DS18B20.

Наконец дошли до меня преобразователи температуры DS18B20.

Разбивать статью на части не буду, т.к. описание структуры ESP8266 и создание классов MajorDoMo есть в предыдущих частях.

Преобразователь DS18B20 подключен по схеме с внешним питанием к пину 1 (GPIO 5) модуля ESP8266.

Структура моей программы не изменилась — состоит из 4 файлов. Сама библиотека 1-Wire встроена в прошивку NodeMCU (помните, в конструкторе прошивки ставили галочку?).

Код файла init.lua (автозагрузка модуля):

dofile("config.lua") -- подгружаем файл config.lua
dofile("main.lua") -- подгружаем основной файл программы main.lua

Код файла config.lua (файл общих настроек):

-- Для дальнейшего удобства лучше хранить данные в таблице, а не в переменной.
-- При изменении какого-либо значения по-воздуху POST-запросом
-- удобнее будет перезаписывать данные.
-- Для редактирования в ESPlorer тип значений может быть любой

config = {}
config.LanSSID = "ssid" -- название wifi-сети
config.LanPWD = "password" -- пароль wifi-сети
config.LanServer = "192.168.1.15" -- IP-адрес сервера с установленной системой MajorDoMo

Код файла main.lua (основной файл выполнения программы):

--Конфигурируем чип как клиента, присоединяемся к существующей сети WiFi
--Присоединяемся к сети WiFi
--При неудачном соединении создаем свою точку доступа ssidESP8266
--При удачном - поднимаем веб-сервер
--Опрашиваем датчик DS18B20 каждые 5000 мс

-- создаем переменные
 connect=0 --статус присоединения к существующей сети WiFi


-- поднимаем точку доступа
 function setSOFTAP()
 -- создаем таблицу с данными создаваемой точки доступа
 local cfg={}
 cfg.ssid="ssidESP8266"
 cfg.pwd="password"

wifi.setmode(wifi.SOFTAP) -- конфигурируем чип как точку доступа
wifi.ap.config(cfg) -- поднимаем точку доступа с нашими параметрами
print(wifi.ap.getip()) -- пачатаем IP созданной точки доступа
end

-- Проверка на соединение с точкой доступа (проверка получения IP)
function getConnect()
 if wifi.sta.getip() ~= nil then -- если IP-адрес получен (не равен NIL)
 connect = 1 -- ставим статус получения адреса
 print("IP-адрес получен: "..wifi.sta.getip())
 startWork() -- запускаем функцию начала цикла работы
 else
 connect = 0
 print("IP-адрес не получен, поднимаем точку доступа")
 setSOFTAP() -- если присоединиться не удалось, поднимаем свою точку доступа

 end
end

-- Конфигурируем чип как клиента, присоединяемся к существующей сети WiFi
-- Заводим таймер на 5 сек. Если по прошествии этого времени не получили IP?
-- то поднимаем свою точку доступа, свой веб-сервер
wifi.setmode(wifi.STATION) -- конфигурируем чип как клиент
wifi.sta.autoconnect(1) -- включаем автоприсоединение к сети
wifi.sta.config(config.LanSSID, config.LanPWD, true) -- присоединяемся к сети, сохраняем настройки конфигурации во FLASH
tmr.alarm(0, 5000, tmr.ALARM_SINGLE, getConnect) --заводим таймер на 5 секунд



function startWork()
 -- поднимаем сервер на 80 порту с таймаутом 30 сек
 srv = net.createServer(net.TCP, 30)
 -- формируем ответ из нескольких строк клиенту и отправляем
 function receiver(sck, data)
 local response = {}
 response[#response + 1] = "<html><body style=\"font-family: Verdana, Arial, Helvetica, sans-serif;\"><center>"
 response[#response + 1] = "<h1>Ap SmartHome</h1> <br/>На данный момент T="..DS18B20_READ().." °C"
 response[#response + 1] = "<br/>ID="..DS18B20_READ_ID()
 response[#response + 1] = "</center></body></html>"
 -- отправляем и удаляем первый элемент из таблицы response, пока не отправим все
 local function send(localSocket)
 if #response > 0 then --пока длина таблицы response не равна нулю
 localSocket:send(table.remove(response, 1))
 else
 localSocket:close() -- если все отправили - закрываем сокет
 response = nil -- удаляем переменную "response"
 end
 end

 -- triggers the send() function again once the first chunk of data was sent
 sck:on("sent", send) --если отправили посылку - отправляем еще раз
 send(sck)
 end

 -- Мониторим 80 порт. Если есть запрос от клиента - выполняем функцию "receiver"
 if srv then
 srv:listen(80, function(conn)
 conn:on("receive", receiver)
 end)
 end

 local function DS18B20()
 dofile("DS18B20.lua")
 print(DS18B20_READ())
 -- Отправка данных о температуре
 srv = net.createConnection(net.TCP, 0) -- создаем новое TCP-соединение
 srv:connect(81,"192.168.1.15") -- коннектимся к серверу с установленным MajorDoMo
 srv:on("connection", function(sck, c) -- если соединение установлено, отправляем данные на сервер
 sck:send("GET /objects/?object=temperatureKitchen&op=set&p=temperature&v="..DS18B20_READ().." HTTP/1.1\r\nHost: 192.168.1.15\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n")
 end)
 tmr.wdclr()
 end

Код файла DS18B20.lua (библиотека преобразователя DS18B20. Основная функция — чтение температуры):

ds18B20_data = {} --Глобальная таблица данных датчика DS18B20
ds18B20_data.temperature=0 --температура
ds18B20_data.id=0 --ID датчика DS18B20

function ds18B20_start()
 local pin_DS18B20=1

 ow.setup(pin_DS18B20)
 local count = 0
 local addr
 repeat
 count = count + 1
 addr = ow.reset_search(pin_DS18B20) -- очищаем состояние поиска
 addr = ow.search(pin_DS18B20) -- выполняем поиск устройства
 -- ID найденного устройства в HEX
 ds18B20_data.id = string.format("0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X", addr:byte(1,9))
 tmr.wdclr()
 until (addr ~= nil) or (count > 10)
 if addr == nil then
 return 0
 end
 local crc = ow.crc8(string.sub(addr,1,7))
 if crc == addr:byte(8) then
 count=count+1
 ow.reset(pin_DS18B20)
 ow.select(pin_DS18B20, addr)
 ow.write(pin_DS18B20, 0x44, 0)
 tmr.delay(1000000)
 local present = ow.reset(pin_DS18B20)
 ow.select(pin_DS18B20, addr)
 ow.write(pin_DS18B20,0xBE, 0)
 local data = nil
 data = string.char(ow.read(pin_DS18B20))
 for i = 1, 8 do
 data = data .. string.char(ow.read(pin_DS18B20))
 end
 crc = ow.crc8(string.sub(data,1,8))
 if crc == data:byte(9) then
 local t = (data:byte(1) + data:byte(2) * 256) * 625
 local t1 = t / 10000
 local t2 = t % 10000
 ds18B20_data.temperature=t1.."."..t2

 tmr.wdclr()
 else
 print("CRC is not valid!")
 end
 collectgarbage()
end

--Возвращает температуру
function DS18B20_READ()
 return ds18B20_data.temperature
end

--Возвращает ID преобразователя DS18B20
function DS18B20_READ_ID()
 return ds18B20_data.id
end

ds18B20_start() -- запускаем 1 цикл опроса датчика

Опрос датчика температуры происходит каждые 5000 мс. По окончании измерения создаем соединение к серверу с установленной системой МДМ, и отправляем GET-запросом данные о температуре, где они и сохраняются. Также показания температуры выводятся в UART.

Теперь можно ввести в браузере адрес нашего чипа и увидеть показания датчика.

Теперь займемся интеграцией температуры в МДМ. В прошлых частях мы создавали основной класс для всех устройств ESP8266, и его подкласс для преобразователей движения. Теперь создадим новый подкласс для преобразователей температуры. Назовем его TemperatureSensor. Зададим время хранения данных равным 365 дней. Для такого значения рекомендую отправлять значения 1 раз в 5-10 минут (в примере выше 5 секунд). Температура в комнате ведь вещь инерционная, зачем нам каждые 5 секунд ее опрашивать и записывать? 1 раз в 10 минут вполне достаточно, а можно и реже.

В этом классе (TemperatureSensor) создаем новое свойство temperature.

Теперь создадим объект этого класса с названием temperatureKitchen — соответственно показания температуры на кухне.

Теперь почти все готово. Строка из «main.lua»:

sck:send("GET /objects/?object=temperatureKitchen&op=set&p=temperature&v="..DS18B20_READ().." HTTP/1.1\r\nHost: 192.168.1.15\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n")

будет устанавливать значение свойства temperature объекта temperatureKitchen равным нашей температуре, и зайдя в свойства этого объекта ее можно будет там лицезреть.

Но веселее видеть ее на главном экране. Создадим новый элемент меню управления (Панель управления -> Объекты -> Меню управления).

Значение между знаками % является значением переменной (не знаю как точно назвать). В данном случае это свойство temperature объекта temperatureKitchen, где и хранится наша температура. Сохраняемся. Вот так будет выглядеть наше меню управления:

Ну и соответственно зайдя на главную страницу сервер МДМ увидим показания температуры.

Но все показания температуры сохраняются! Было бы неплохо вывести график температуры за 1 час (день, неделю, месяц, год). Зайдем в пункт Charts (Панель управления -> Объекты -> Меню управления -> Charts). Если такой отсутствует — его нужно подключить (Система -> Маркет дополнений -> Информация -> Charts). Нажимаем кнопку добавить.

После создания графика добавим в него данные. Зайдем в редактирование созданного графика, и выбираем вверху пункт «Данные». В качестве типа графика выбираем «Area» (можете попробовать, как отображаются другие графики). В качестве связанного объекта выбираем наш созданный класс temperatureKitchen. После этого в пункте «Связанное свойство» появится список свойств объекта, у нас одно одно — temperature. И сохраняем.

Вот и все. Внизу у нас появится график, построенный из сохраненных значений температуры.

Комментарии 1

  • Пытался по статье повторить подключение DS18B20. После заливки lua скриптов сыпятся ошибки при запуске в Esplorer. Новичкам не рекомендую руководствоваться статьей. Модули прошивки изменились или lua версия , сейчас код из статьи не работает.
    PANIC: unprotected error in call to Lua API (main.lua:54: attempt to call global ‘DS18B20_READ’ (a nil value))

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.