Наконец дошли до меня преобразователи температуры 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. И сохраняем.
Вот и все. Внизу у нас появится график, построенный из сохраненных значений температуры.
Пытался по статье повторить подключение DS18B20. После заливки lua скриптов сыпятся ошибки при запуске в Esplorer. Новичкам не рекомендую руководствоваться статьей. Модули прошивки изменились или lua версия , сейчас код из статьи не работает.
PANIC: unprotected error in call to Lua API (main.lua:54: attempt to call global ‘DS18B20_READ’ (a nil value))