ESP-8266/ESP32 NodeMCU Lua: азы программирования.

Тема в разделе "ESP8266, ESP32", создана пользователем ИгорьК, 25 июл 2017.

  1. В коде нигде не устанавливается точность. Разрешение первого датчика 0,5 а у второго 0,1. неважно, вместе они работают или порознь... и в каком гнезде...
     
  2. ИгорьК

    ИгорьК Гуру

    В библиотеке от NodeMCU разрешение устанавливается:
    Код (C++):
    ds18b20.setting({"28:FF:FF:FF:FF:FF:FF:FF"}, 9)
    В моей такой функции не предусмотрено.
     
  3. Я про это и говорю. Если использовать стандартную функцию установки точности, то она работает, но опять с одним только датчиком. На второй не распространяется установка.
    У Вас нет установки точности, но результат тот же... разрешение у датчиков разное. Складывается впечатление, что сам датчик имеет "встроенное разрешение" чего по сути не должно быть.
     
  4. ИгорьК

    ИгорьК Гуру

    Разрешение, установленное однажды, сохраняется. Если вы установили его библиотекой от NodeMcu - оно и осталось. Изменяйте его, подключая датчики по-отдельности.
     
    Последнее редактирование: 29 май 2018
    SmileOfFortune нравится это.
  5. Пробовал, не помогает, точнее точность не меняется ни у одного из двух. Как было у одного 9 а у другого 12, так и осталось...
     
  6. ИгорьК

    ИгорьК Гуру

    Не могу прокомментировать. Либо ошибки в библиотеке, либо вы ошибаетесь в адресах.
    Если это важно - поменяйте разрешение через ардуино, если она есть.
     
    SmileOfFortune нравится это.
  7. ИгорьК

    ИгорьК Гуру

    Еще вариант (безумный) - играть с буквенными частями адреса: проверять и строчные и прописные буквы.

    Где-то я встречался с таким явлением.

    Попробуйте дать команду на установку разрешения с заранее несуществующим адресом. Если библиотека не выдаст ошибку - возможно дело в формате его указания. Но каким он должен быть - только методом инженерного тыка.
     
  8. ИгорьК

    ИгорьК Гуру

    Глядя на код от NodeMcu, могу предположить что адрес еще может выглядеть не как шестнадцатеричные, а как десятичные числа разделенные двоеточием.
     
  9. fixedip

    fixedip Гик

    Добрый вечер.
    А можно рассказать по UART в Lua?
     
  10. ИгорьК

    ИгорьК Гуру

    Пока в планах нет, поскольку uart он везде одинаков. Посмотрите здесь, например: http://forum.amperka.ru/threads/arduino-esp8266-raspberry-pi-2-openhab-Умный-дом-азы-управления.5043/page-20#post-65454

    Что надо сказать - в прошивках версии 2.х.х UART1 работает только на передачу, а в версиях 1.х.х - нормально. Это надо учитывать.

    Вообще, смотрите тему про ОН - там есть несколько устройств Lua с UART: http://forum.amperka.ru/threads/arduino-esp8266-raspberry-pi-2-openhab-Умный-дом-азы-управления.5043/
     
  11. ИгорьК

    ИгорьК Гуру

    Скелет программы IoT ч.2.

    Пожалуйста, посмотрите сюда.

    Рассмотрим еще несколько файлов/действий, которые требуются любому устройству домашней автоматизации.
    Чем бы ни занималось устройство, оно:
    1. получает и обрабатывает команды от брокера;
    2. отправляет информацию о своем состоянии;
    3. сохраняет в память некоторые параметры, которые важны в случае перезагрузки или отключения питания.
    Займемся указанными вопросами.

    4. pubnow.lua

    Работа организована таким образом, что любая часть программы, которая хочет отправить информацию на брокер, должна поместить данные в глобальную таблицу "topub" и, если не поднят флаг "dat.publ", выполнить "dofile('pubnow.lua')".

    Флаг "dat.publ" поднимает и сбрасывает сам скрипт. Скрипт по одной изымает строки из таблицы для публикации, публикует данные на брокер и рекурсивно в callback вызывает публикацию новой строки, пока они все не будут изъяты из таблицы.

    Так как публикация дело долгое и асинхронное, не возбраняется в ее ходе подкинуть в таблицу еще значения - они будут также отправлены. А вот вызывать "dofile('pubnow.lua')" во время его работы нельзя, предотвращение тому - флаг "dat.publ" .

    Итак:
    Код (Lua):
    do
    dat.publ = true -- Флаг публикации
    --[[
    -- Это для преобразования таблиц с ключом в индексируемые
    -- потребуется позже
    for k,v in pairs(tohome) do
        table.insert(topub, {k, v})
    end
    --]]

    -- Всегда публикуем 'heap', за ним надо наблюдать
    table.insert(topub, {'heap', node.heap()})
    local function punow()
        -- Если в таблице для публикации что-то есть и она возможна
        if #topub ~= 0 and wifi.sta.getip() and dat.broker then
            -- Изымаем элемент из таблицы
            local tp = table.remove(topub)
            -- Элемент с индексом 2 (сообщение топика) может отсутствовать
            tp[2] = tp[2] or ""
            -- Если сообщения нет, то поднимаем флаг "Retained"
            -- чтобы удалить на брокере принятое от него сообщение
            tp[3] = tp[2] == "" and "1" or "0"
            -- Публикуем и вызываем себя рекурсивно:
            m:publish(myClient.."/"..tp[1], tp[2], 0, tp[3], punow)
        else
            tp = nil
            dat.publ = nil
            punow = nil
            print('Published!')
            return
        end
    end
    punow()
    end

    Надеюсь, логика работы понятна, но... Возможно к ней придется добавлять костыль среднего размера, тогда, когда брокер у вас халявный (а значит - перегруженный). Вы же любите халяву, не так ли?

    5. analize.lua
    В прошлой заметке этот файл просто печатал данные, сегодня мы озадачим его двумя вещами: пусть включает/выключает освещение и устанавливает целевую температуру для поддержания.

    Замечу, что файл "analize.lua" универсальным по содержанию быть не может, но само его наличие и приемы работы с ним - универсальны.
    Логика его работы приблизительно такая же как у "pubnow.lua" - изымаем из таблицы данные для анализа, пока они не закончатся.

    Код (Lua):
    dat.analiz = true -- Анализ данных с брокера начался
    if #killtop ~= 0 then -- В таблице для анализа есть данные
        -- Выдергиваем запись из таблицы
        local com = table.remove(killtop)
        local top = com[1]
        local dt = com[2]
        -- Разбираем пару топик-дата
        if top and dt then
            -- Здесь тема освещения
            if top == "light" then
                if dt == "ON" then
                    dat.light = "ON"
                    gpio.write(pinlight, gpio.HIGH)
                    print("Light is ON")
                end
                if dt == "OFF" then
                    dat.light = "OFF"
                    gpio.write(pinlight, gpio.LOW)
                    print("Light is OFF")
                end
            -- Тема установки целевой температуры
            elseif top == "target" then
                -- Формируем цифру, а если мусор - 75
                local target = tonumber(dt) or 75
                -- Дальше защищаемся от неправильной уставки
                if target == 75 then
                    print('Got Wrong Target!')
                else
                    if target < 15 then
                        dat.target = 15
                    elseif target > 27 then
                        dat.target = 27
                    else
                        dat.target = target
                    end
                end
                target = nil
                print('Set Target Temperature At '..dat.target)
            end
        end
        com, top, dt = nil, nil, nil
        -- Вызываем себя рекурсивно для проверки данных от брокера
        dofile('analize.lua')
    else
        -- Данные для анализа кончились - уничтожаем флаг
        dat.analiz = nil
    end
     
    Последнее редактирование: 13 сен 2018
  12. ИгорьК

    ИгорьК Гуру

    6. savedata.lua (плюс вспомогательный listtosave.lua)
    Этот файл может вызываться из тех мест программы, где изменились некоторые важные данные и это изменение надо защитить от перезагрузки или потери питания.
    Например, мы поливаем помидоры каждый день, а когда пойдет рост плодов - дадим команду на полив через день. Или поменяем целевую температуру нагревателя и т.д.

    Рабочие данные у нас всегда находятся в таблице "dat". Эта таблица устанавливается в скрипте "setglobals.lua", например так:
    Код (Lua):
    dat = {}
    dat.target = 22 -- Целевая температура
    dat.light = 'OFF' -- Освещение выключено
    dat.broker = false -- Связи с брокером нет
    Здесь же, после формирования таблицы параметров, можно поместить такой код:
    Код (Lua):
    -- Если файл 'setsaveddat.lua' есть - выполним его
    if file.exists('setsaveddat.lua') then
          dofile('setsaveddat.lua')
    end
    По ходу работы нашего устройства файл 'setsaveddat.lua' может появиться и мы займемся его появлением. Он, например, будет перезаписывать целевую температуру относительно установленной в "setglobals.lua".
    Перечень ключей таблицы "dat" для сохранения находится в файле "listtosave.lua":
    Код (Lua):
    return {
       -- {'light'},
        {'target'}
    }
    Сам файл, который делает сохранение, выглядит так:
    Код (Lua):
    do
        -- "lst" - таблица с перечнем ключей, которые будут
        -- сохранены из рабочей таблицы 'dat'
        local lst = dofile('listtosave.lua')
        -- создаем и открываем временный файл
        tmp = file.open("tmp.lua", "w")
        if tmp then
            local line
            -- Выбираем значения из перечня полей для сохранения
            for _, v in pairs(lst) do
                -- Формируем строку для сохранения, печатаем и сохраняем
                -- во временный файл
                line = "dat."..v[1].."="..dat[v[1]].."\n"
                print('write: '..line)
                tmp:write(line)
            end
        end
        tmp:close(); tmp = nil
        -- Удаляем старый файл и заменяем новым
        file.remove("setsaveddat.lua")
        file.rename("tmp.lua", "setsaveddat.lua")
        lst = nil
    end

    Итак, если, например, в файл "analize.lua" в часть, где идет управление целевой температурой добавить одну строку
    Код (Lua):
    ...
    print('Set Target Temperature At '..dat.target)
    -- добавим:
    dofile('savedata.lua')
    ...
    то после каждой команды от брокера новый "dat.target" будет сохраняться в файле "setsaveddat.lua", который будет создан без вашего участия и вызываться при старте.

    Таким образом, мы добавили еще несколько файлов в типовой проект любого устройства IoT. Прилагаю старые и новые к этому топику без комментариев, чтобы брать и пользоваться.

    Уже в таком виде, будучи загруженными в модуль, мы можем дистанционно управлять одной ногой (можете добавить еще) еспшки.
    В комплекте и файл main.lua, который каждые 2 минуты гонит на брокер информацию о текущих параметрах устройства.

    Кроме того, в файле "pubnow.lua" QoS при публикации установлено в 2 и закомментирован костыль, который нужен лишь в том случае, если связь с брокером чрезвычайно плохая и от него не всегда доходят сообщения.

    Вот как это работает, и здесь, кстати, видно переподключение:
    upload_2018-6-15_20-56-14.png

    upload_2018-6-15_20-57-8.png

    Все работает. Осталось наполнить файл "main.lua" чем-нибудь полезным.
    А пока этим можно управлять со смартфона через MQTT Dash:
    SmartSelect_20180619-015834_MQTT Dash.jpg
     

    Вложения:

    • iot02.zip
      Размер файла:
      3,1 КБ
      Просмотров:
      416
    Последнее редактирование: 19 июн 2018
  13. swc

    swc Гик

    Отличное пособие для начинающих! Lua - очень замечательный язык. А в совокупности с библиотекой NodeMCU - бомба.
    Добавлю еще одну полезную фичу. (Польза проявится в процессе освоения и работы с Lua).

    Код (Lua):

    -- файл: "init.lua". Кнопочка с GPIO на землю позволяет устранить головную боль
    -- вариантов опубликовано много, это мой.
    local GPIO = 2 -- у меня так, использую дисплей
    gpio.mode(GPIO, gpio.INPUT,gpio.pullup)
    if gpio.read(GPIO) == 1 then
         print("==== RUN ====")
         dofile "main.lua"
    else
        print("==== STOP ====")
    end
     
    Последнее редактирование: 16 июн 2018
    ИгорьК нравится это.
  14. ИгорьК

    ИгорьК Гуру

    Скелет программы IoT ч.3.1. Управляем освещением на улице.

    Как уже писал ранее, работа с публичным сервером iot.eclipse.org выявила необходимость костыля, который бы принудительно удерживал соединение с брокером не смотря ни на что. Вот он внутри файла pubnow.lua:
    Код (Lua):
    do
        dat.publ = true
        table.insert(topub, {'heap', node.heap()})
        local tp
        -- Количество топиков к публикации!
        local strtpu = #topub
        local function punow()
            if #topub ~= 0 and wifi.sta.getip() and dat.broker then
                -- print('pub: ', #topub)
                tp = table.remove(topub)
                tp[2] = tp[2] or ""
                tp[3] = tp[2] == "" and "1" or "0"
                m:publish(myClient.."/"..tp[1], tp[2], 2, tp[3], punow)
            else
                tp = nil
                dat.publ = false
                punow = nil
                -- Сбрасываем счетчик провало публикации
                dat.clpub = 0
                dat.dispatch = false
                print('Published!')
                return
            end
        end
        punow()
        -- Костыль
        ---[[
        -- Создаем таймер
        tmr.create():alarm(15000, 0, function(t)
            t = nil
            -- Если через 15 секунд (от начала) публикации не сброшен флаг
            if dat.publ == true then
                -- Извещаем о кличестве неотправленны топиков
                print('----------------------- Kill Publishing! lost: ', #topub)
                -- Если неотправлено много - считаем количество провалов
                if strtpu - #topub < 3 then
                    dat.clpub = dat.clpub + 1
                end
                -- Возвращаем флаги и таблицыв правильное состояние
                dat.dispatch = false
                topub = {}
                tp = nil
                dat.publ = false
                punow = nil
                -- Три провала подряд - пересоединение
                if dat.clpub > 3 then
                    print('Restart MQTT!')
                    dat.clpub = 0
                    m = nil
                    dofile 'setmqtt.lua'
                end
            end
        end)
        --]]
    end

    Костыль очень жесткий, уничтожает любые баловства. Полагаю, даже если вы работаете и со своим брокером, лишним он не будет - мало ли что.

    upload_2018-7-10_14-17-30.png


    Теперь о нашем устройстве.
    Не будем задействовать датчик освещенности - спросим у солнышка что к чему.
    По адресу https://sunrise-sunset.org/ совершенно даром можно узнать про закат и восход все. Вот про Москву: http://api.sunrise-sunset.org/json?lat=55.75583&lng=37.61778

    Узнавать будет файл getsun.lua и толкать информацию в таблицу dat в соответствующие поля.

    Дык, вот он, весь с комментариями:
    Код (Lua):
    do
    -- Часовой пояс Москвы
    local offset = 3

    -- Здесь находится время для Москвы
    -- 'api.sunrise-sunset.org/json?lat=55.75583&lng=37.61778'

    -- Дергаем сервер раз в 10 минут. Можно еще реже
    if dat.setHMcount < 10 then
        dat.setHMcount = dat.setHMcount + 1
        print('Just:', dat.setH, dat.setM, dat.riseH, dat.riseM)
    else
        -- Локальные рабочие пермеменные
        local setH, setM, riseH, riseM, makeDig

        -- Текст запроса к серверу на тему времени
        local request = "GET /json?lat=55.75583&lng=37.61778&formatted=0 HTTP/1.1\r\n"..
            "Host: api.sunrise-sunset.org\r\n\r\n"
        -- Создаем соединение
        conn=net.createConnection(net.TCP, 0)
        -- Соединились - запрос
        conn:on("connection", function(conn, payload) conn:send(request) end)
        -- Пришел ответ:
        conn:on("receive", function(conn, payload)
            -- Из тела ответа выгрызаем кусок со временем заката и рассвета
            local ttmm = string.sub(payload,string.find(payload,'sunrise')+20,string.find(payload,'sunset')+24)
            payload = nil
            --print(ttmm)
            -- Парсим время
            riseH, riseM, setH, setM = string.match(ttmm,'(%d+):(%d+).+sunset.+T(%d+):(%d+)')
            --print(setH, setM, riseH, riseM)

            -- Функция переработки стрингов времени в цифры с учетом часового пояса
            -- (переменная trans обработки часов)
            makeDig = function(dig, trans)
                dig = tonumber(dig)
                if trans then
                    dig = ((dig + offset) < 24) and (dig + offset) or (dig - 21)
                end
                return dig
            end
            -- Устанавливаем время заката и рассвета
            if setH and setM then
                dat.setH = makeDig(setH, true)
                dat.setM = makeDig(setM)
            end
            if riseH and riseM then
                dat.riseH = makeDig(riseH, true)
                dat.riseM = makeDig(riseM)
            end

            -- Сброс счетчика проверки солнышка
            dat.setHMcount =  0
            print('Set:', dat.setH, dat.setM, dat.riseH, dat.riseM)
            setH, setM, riseH, riseM, request, makeDig, ttmm, offset = nil, nil, nil, nil, nil, nil, nil, nil
            conn:close()
            end)
        -- Приступаем к проверке времени
        conn:connect(80, 'api.sunrise-sunset.org')
    end
    end
     
    Последнее редактирование: 10 июл 2018
    SergeiL и swc нравится это.
  15. ИгорьК

    ИгорьК Гуру

    Получили время - что же делать с ним?
    По моей практике, если включать освещение, то через 30 минут после захода солнца.
    Выключать будем в 23:30 и спать.
    А если вручную? Не беда! Если освещение включено через смартфон, то установим флаг dat.lightF и не будем выключать в 23:30. Выключим тоже вручную.
    Но... Много пива, то да се... Свет остался включенным. Перед рассветом проверим трезвость и память и все равно свет выключим. Вот так:

    Код (Lua):
    -- Функция - вкл/выкл
    local function setlight(st)
        dat.light = st
        local wr = st == 'ON' and 1 or 0
        gpio.write(pins.light, wr)
        print("Light set "..st)
    end

    -- Если устройство в режиме охраны и свет включен - выключаем
    if dat.arm == 'ON'  then
        if  dat.light == 'ON' then setlight('OFF')  end
    -- Иначе, если время получено правильно - действуем
    elseif dat.setH ~= 100 and dat.riseH ~= 100 then
        -- Получим текущее время
        local tm = rtctime.epoch2cal(rtctime.get()+3*60*60)
        --print(string.format("%02d:%02d:%02d", tm.hour, tm.min, tm.sec))
        -- Если время было получено с сервера
        if tm.year ~= 1970 then
            -- Минут с начала дня
            local tmnow = tm.hour * 60 + tm.min
            -- Минут с начала дня для включения
            local swon = dat.setH * 60 + dat.setM + 30
            -- Минут с начала дня для выключения по пьянке
            local swoff = dat.riseH * 60 + dat.riseM - 30
            --Минут с начала дня для ботаников - 23:30
            local swoffsleep = 23*60+30

            -- Включаем освещение
            if tmnow > swon and dat.light == 'OFF' then
                setlight('ON')
            end

            -- Пора спать
            if (not dat.lightF) and (tmnow > swoffsleep) and  dat.light == 'ON' then
                setlight('OFF')
            end

            -- Включил и забыл свет выключить - как не стыдно!
            if math.abs(tmnow - swoff) < 2 and dat.light == 'ON' then
                dat.lightF = false
                setlight('OFF')
            end

        end
    end

    tm, tmnow, swon, swoff, swoffsleep, setlight, wr  = nil, nil, nil, nil, nil, nil, nil

    Приложу рабочие файлики без комментариев, чтобы не скучно было.
    Друзья, вообще то, это уже готовое устройство! Файлы можно загнать в модуль и в ручном режиме понаблюдать за работой. Не забудьте перед этим объявить таблицу dat.
    В файлах в самых интересных местах есть закомментированная функция print - можно раскомментировать и посмотреть пристальнее.

    И, главное, у вас в прошивке должны быть необходимые модули работы с часами.
    В частности, у меня такая прошивка - все подряд, но у вас для этого проекта обязательно:

    upload_2018-7-3_15-55-10.png

    И поиграем:

    upload_2018-7-3_16-0-49.png

    Играть с файлом lightnow.lua можно, но он требует много dat и текущего времени - чуть позже.

    UPD. 05.07.2018. Files.zip.

    Еще не конец!
     

    Вложения:

    • Files.zip
      Размер файла:
      1,9 КБ
      Просмотров:
      463
    Последнее редактирование: 5 июл 2018
    SergeiL и swc нравится это.
  16. ИгорьК

    ИгорьК Гуру

    Скелет программы IoT ч.3.2. Плюс управляем отоплением по расписанию/вручную.
    Небольшая заметка, поскольку работа с недельным расписанием уже была рассмотрена ранее.
    За работу установки контрольной температуры отвечает файл scedset.lua, его работа описана ранее, единственно добавлена проверка условия не находится ли устройство в режиме ручной установки температуры.

    Поддержка работы - файлы transform.lua и sced.lua. Все разъяснено по ссылке выше.

    Включение/выключение нагревателя - temper.lua.
    Действуем просто:
    запрашиваем температуру с датчика/датчиков DS18b20;
    заполняем таблицу для отправки хозяину;
    если указано имя контролируемого датчика - управляем отопителем.

    Код (Lua):
    -- Локальная таблица для сбора значений с датчиков
    local temp = {}
    -- Callback функция для передачи в модуль DS18b20,
    -- ее вызовет модуль как только закончит опрос датчиков
    local myWork = function(t)
        -- Выгружаем модуль
        package.loaded["_ds18b20"]=nil
        ds = nil
        -- Вставляем значения из временной таблицы в таблицу для публикации
        for k, v in pairs(temp) do
            table.insert(topub, {k,v})
            -- print(k,v)
        end
        -- Если в файле setglobals верно указан датчик контроля отопления,
        -- то есть его значение присутствует в таблице.
        if temp[dat.askds] then
            print('Now '..temp[dat.askds])
            -- Включаем или выключаем нагреватель и оповещаем таблицу dat
            if (temp[dat.askds] + 0.5) > dat.target then  gpio.write(pins.heat, gpio.LOW); dat.heat = 'OFF' end
            if (temp[dat.askds] - 0.5) < dat.target then  gpio.write(pins.heat, gpio.HIGH); dat.heat = 'ON' end
        else
            print('Set correct DS18b20 to be checked!')
        end

        temp, myWork = nil, nil
    end
    -- Загрузка модуля
    ds = require('_ds18b20')
    -- Вызов чтения температуры
    ds.getTemp(temp, myWork)

    В приложении 4 файла для работы с температурой и модуль DS18b20.
     

    Вложения:

    Последнее редактирование: 9 июл 2018
    swc нравится это.
  17. swc

    swc Гик

    Игорь, насколько обязательна строка: package.loaded["_ds18b20"]=nil ?
    По идее, если модуль был загружен, он уничтожается, когда ссылка на него исчезает: ds =nil.
     
  18. ИгорьК

    ИгорьК Гуру

    1. Модуль - не таблица и не переменная.
    2. Модуль, после загрузки, становится записью в таблице package.loaded.
    3. ds - не ссылка на объект, а ссылка на запись в таблице.
    Таким образом, запись в таблице уничтожается не уничтожением ссылки, а именно как запись.

    Код (Lua):
    -- уничтожается запись в таблице
    package.loaded["_ds18b20"]=nil
    -- уничтожается переменная, указывающая "никуда"
    -- типа как объявленная "local ds"
    ds = nil
     
    Последнее редактирование: 4 июл 2018
    swc нравится это.
  19. swc

    swc Гик

    Понял.
     
  20. ИгорьК

    ИгорьК Гуру

    Скелет программы IoT ч.3.3. Плюс охрана и сирена.

    Файлик setarm.lua будет вызываться из analize.lua по команде со смартфона. Он включает/выключает режим охраны и прост как три рубля:
    Код (Lua):
    -- Параметр устанавливается через mqtt брокер
    if dat.arm == "ON" then
        -- Гасим освещение
        dat.light = 'OFF'
        gpio.write(pins.light, gpio.LOW)
        print("Light set OFF")
        -- ГЛОБАЛЬНАЯ функция включения сирены
        function siren()
            -- Отмена наблюдения за ногой ПИР
            gpio.trig(pins.pir)
            print('Pir Disarmed')
            -- Включаем сирену
            dofile('sirennow.lua')
        end
        -- Назначаем вышеуказанную функцию как callback
        -- на ногу ПИР
        gpio.trig(pins.pir, "up", siren)
        print('Pir Armed')
    -- Сняли охрану
    elseif dat.arm == "OFF" then
        -- Снимаем наблюдение за ногой
        gpio.trig(pins.pir)
        print('Pir Disarmed')
        -- Выключаем сирену
        gpio.write(pins.siren, gpio.LOW)
        print('Siren stop')
        -- Уничтожаем функцию
        siren = nil
        -- Если работал таймер выключения сирены - уничтожаем.
        if dat.tmrsiren then
            dat.tmrsiren:stop()
            dat.tmrsiren:unregister()
            dat.tmrsiren = nil
        end
    end

    Из этого файла при срабатывании датчика движения будет вызываться другой - sirennow.lua:
    Код (Lua):
    if dat.arm == "ON" then
        print('Siren start')
        -- Сразу готовим информацию о нарушении на публикацию
        table.insert(topub, {'siren', 'ON'})
        dat.siren = 'ON'
        -- Запускаем публткацию
        if not dat.publ then dofile('pubnow.lua') end
        -- В глобальной таблице создаем таймер выключения
        dat.tmrsiren = tmr.create()
        -- Начинаем орать сиреной
        gpio.write(pins.siren, gpio.HIGH)
        -- Запускаем таймер выключения сирены через 30 сек.
        dat.tmrsiren:alarm(30000, 0, function(t)
            t = nil
            dat.tmrsiren = nil
            gpio.write(pins.siren, gpio.LOW)
            print('Siren stop')
            -- Возвращаем наблюдение за ногой ПИР
            gpio.trig(pins.pir, "up", siren)
            print('Pir Armed')
            -- Оповещаем брокер что сирена заглохла
            table.insert(topub, {'siren', 'OFF'})
            dat.siren = 'OFF'
            if not dat.publ then dofile('pubnow.lua') end
        end)
    end

    В целом как это работает.
    Через брокер устанавливаем или снимаем охрану.

    При установке начинается наблюдение за ногой датчика. Если пир сработал - включается сирена на 30 секунд, и отключается наблюдение за ногой, чтобы не вызывать постоянно функцию включения.
    После окончания песни сирены датчик пир вновь включается.

    При снятии с охраны вой сирены (если был) и наблюдение за датчиком выключаются.

    Режим сирены с отключением датчика сделан простым, чтобы не ломать вам мозг. Ничто не мешает не отключать датчик, а просто добавлять таймеру выключения еще времени - сирена будет выть пока обнаруживается движение.

    ... И это еще не все. Осталось собрать все перечисленные ранее файлы в кучу, сделать общую программу и насладиться результатом. Вот и займемся этим в следующий раз.
     

    Вложения:

    • SirenFiles.zip
      Размер файла:
      780 байт
      Просмотров:
      367
    Последнее редактирование: 5 июл 2018
    swc нравится это.