Arduino, ESP8266 Lua, Raspberry Pi 2 && OpenHab. Умный дом: азы управления.

Тема в разделе "Глядите, что я сделал", создана пользователем ИгорьК, 12 май 2015.

  1. ИгорьК

    ИгорьК Оракул Модератор

    Вот как выглядит скрипт pzem.lua - скрипт добычи информации с модуля:
    Код (Lua):
    -- Скрипт постоянно вызывается из main.lua
    do
    -- Это склад запростов модулю
    local asknow = {
        voltage = "\176\192\168\001\001\000\026",
        current = "\177\192\168\001\001\000\027",
        activeP = "\178\192\168\001\001\000\028",
        energy  = "\179\192\168\001\001\000\029"
    }
    -- Это склад ответов, которые используются в main.lua
    answer = {}

    -- Создаем корутину, которая из склада достает по одному
    -- запросы и отправляет в UART
    local now = function()
        local ask
        local getansw
        local check
        local dig = {}
        ask = coroutine.create(function()
            -- можно использовать альтернативный UART
            -- uart.alt(1)
            -- Скорость работы с моудем 9600
            uart.setup(0, 9600, 8, uart.PARITY_NONE, uart.STOPBITS_1, 0)
            -- Перебираем склад запросов
            for k, v in pairs(asknow) do
                -- Запускаем обработчик ответа
                getansw(k)
                -- Отправляем по одному запросу в UART
                uart.write(0,v)
                -- Оттормаживаемся
                coroutine.yield()
            end
            -- Как все порешали - возвращаем UART в исходное положение
            -- uart.alt(0)
            uart.setup(0, 115200, 8, uart.PARITY_NONE, uart.STOPBITS_1, 1)
            return
        end)
        -- Обработка ответов модуля принимает ключ таблицы запросов
        -- чтобы делать а таблице answer пары ключ - значение из модуля
        getansw = function(k)
            -- Переменная для ловли начала пакета модуля
            local start = false
            -- Запускаем таймер, который в случае неответа модуля
            -- вернет управление в корутину
            local breaker = tmr.create()
            breaker:alarm(2000, 0, function()
                -- Очищаем UART
                uart.on("data")
                -- В таблицу ответов будет записана ошибка "err"
                -- Можете поменять на что-то другое
                answer[k] = "err"
                coroutine.resume(ask)
            end)
            -- Считаем семь приходящих байтов
            local i = 1
            -- Ловим их по одному
            uart.on("data", 1,
              function(data)
                local s = string.byte(data)
                -- Начинаем если ответ идет с правильной цифры
                if start == true or (s >=0xA0 and s <0xA4) then
                start = true
                dig[i] = s
                i = i + 1
                -- Как словили 7 штук
                if i == 8 then
                    -- выключаем таймер-тормоз
                    breaker:stop()
                    -- отключаем UART
                    uart.on("data")
                    -- Даем сигнал в переработку, отправляя в качестве аргумента
                    -- ключ от запроса
                    check(k) end
                end
            end,0)
        end
        -- Переплавляем 7 байт в реальный ответ
        check = function(k)
            -- Проверяем контрольную сумму
            local sum = 0
            for i = 1, 6 do
                sum = sum + dig[i]
                i = i + 1
            end
            sum = bit.band(sum, 0xFF)
            -- Сумма не бьется - ошибка в таблицу ответов
            if sum ~= dig[7] then answer[k] = "err"
            -- Заполняем таблицу в зависимости от первого байта ответа
            elseif dig[1] == 0xA0 or dig[1] == 0xA1 then
                answer[k] = ""..dig[3].."."..dig[4]
            elseif dig[1] == 0xA2 then
                local  s = dig[2] * 256 + dig[3]
                answer[k] = ""..s
            elseif dig[1] == 0xA3 then
                local s = dig[2] * 65536 + dig[3]* 256 + dig[4]
                answer[k] = ""..s
            end
            -- Все сработало - запускаем корутину дальше
            coroutine.resume(ask)
        end

    -- Первый запуск корутины
    coroutine.resume(ask)
    end
    -- Запуск самого файла
    now()
    end
     
    В скрипте "main.iua" (главный скрипт) находится следующее:
    Код (Lua):
    do
    -- Это склад готовой продукции от PZEM-004t
    -- а также любых других измерений
    -- Сюда поступает информация
    answer = {
        voltage = "0",
        current = "0",
        activeP = "0",
        energy  = "0",
        adc = "0",
        heap = "0"
    }
    Broker = "ВАШ_САЙТ"
    port = 1883
    myClient = "PZEM004_01"
    pass = "pass"

    -- Это для передачи по ссылке модулю
    -- восстановления связи с брокером MQTT
    mod = {}
    -- Публикация запрещена
    mod.publish = false

    -- Создаем объект связи с брокером
    m = mqtt.Client(myClient, 30, myClient, pass)
    m:lwt(myClient, 0, 0, 0)

    -- Функция соединяет с брокером и реконнектит связь при потере
    function connecting()
        -- Об этом модуле писал ранее
        connect = require('mqttm')
        connect.connecting(m, Broker, port, mod, function ()connect = nil end)
    end

    -- При отсутствии коннекта
    m:on("offline", function(con)
          -- Публикация запрещена
          mod.publish = false
          -- Коннект закрывается официально
          m:close()
          -- И начинается реконнект
          connecting()
    end)

    -- Корутина для последовательной публикации топиков
    -- из таблицы
    publ = function()
        -- Прочитаем аналоговый датчик и запишем в таблицу
        answer.adc = ""..adc.read(0)
        -- Запишем в таблицу и размер кучи
        answer.heap = ""..node.heap()
        -- Заране объявляем функцию
        local sendMQ
        -- Отправляем на передачу пару "ключ/значение"
        -- из таблицы answer
        local getd = coroutine.create(function()
            for k, v in pairs(answer) do
                sendMQ(k, v)
                coroutine.yield()
            end
                collectgarbage()
        end)
        -- Принимаем и публикуем пару
        sendMQ = function(k, v)
            m:publish(myClient.."/"..k,v,0,0,
                function(con)
                    coroutine.resume(getd)
            end)
        end
        coroutine.resume(getd)
    end
    -- Устанавливаем связь с брокером
    connecting()

    -- Таймер через 20 секунд вызывает "pzem.lua"
    -- который заполняет таблицу answer
    -- и через 10 секунд после вызова отправляет
    -- информацию на брокер
    local next = tmr.create()
    next:alarm(20000, 1, function()
        dofile("pzem.lua")
        tmr.create():alarm(10000, 0, function()
            if mod.publish == true then
                publ()
            end
        end)

    end)
    end
    Модуль mqttm.lua - в архиве, а init.lua, уверен, напишете сами :)

    Таким образом, в модуль загружаются 4 скрипта.
    init.lua - ждет ... секунд и вызывает main.lua
    main.lua :
    1. вызывает mqttm.lua для установление связи с брокером и реконнекта.
    2. вызывает pzem.lua каждые 20 секунд для заполнения таблицы "answer" данными из PZEM-004t
    3. добавляет к таблице "answer" данные с аналогового входа и о heap и публикует на брокере.

    Все скрипты в приложении одним файлом.
    А также иконки в подарок.

    Что касается файлов OpenHab - их еще нет. Устройство есть, рабочее, но к опенхабу пока не подключено :)

    UPD 07.04.2017. Две недели проработало отлично, без перегрузок, а потом начались ошибки. Буду разбираться, думаю, дело в PZEM. Ошибки только при передаче набранной мощности.
    Snap_2017.04.07_17h44m07s_001.jpg

    UPD 14.06.2017. А ларчик просто открывался - коде была ошибка в вычислении контрольной суммы.

    Да, и в том коде, что в архиве в файле pzem.lua поменяйте пару строк, иначе удивлению не будет предела :) :
    Код (Lua):
    -- было
    -- answer[k] = "err"
    answer[k] = ""..math.random(1,100)

    -- надо
    answer[k] = "err"
    -- answer[k] = ""..math.random(1,100)
     
     

    Вложения:

    Последнее редактирование: 14 июн 2017
    omelchuk890, SergeiL и alp69 нравится это.
  2. alp69

    alp69 Форумчанин

    Как воткнуть гистограммы в ОН?
     
  3. ИгорьК

    ИгорьК Оракул Модератор

    53. Графики!
    Устанавливаем InfluxDB и Grafana вот так.
    "Installation [Grafana] on a Raspberry Pi is also possible, installation instructions are given here."
    У меня это добро установлено на другой малине, на которой стоит многострадальный OH2 со своим брокером в режиме моста с основным.

    Добавка к вышеуказанной инструкции:
    1. Если малина не выполняет apt-get update:
    Код (Bash):
    sudo apt-get install apt-transport-https ca-certificates
    sudo apt-get update
    2. Персистенс для influxdb нужен версии 1.9.
    По умолчанию, через apt-get устанавливается 1.8 - он не работает с последней версией influxdb. Скачивать и устанавливать персистенс надо вручную bp http://www.openhab.org/downloads.html

    Получаем:
    001.jpg
    002.jpg
    003.jpg

    Смотрим в работе. Если нажать на название того или иного графика - выпадает меню. Жмем "View" и график раскрывается: https://snapshot.raintank.io/dashboard/snapshot/mGJKxV13mQAU4tUWEJfzMh1GYJDoS0X0
     
    Последнее редактирование: 5 апр 2018
    Smerlin, glory24, SergeiL и ещё 1-му нравится это.
  4. ИгорьК

    ИгорьК Оракул Модератор

    54. Коннектимся к запасным сеткам, когда основная упала.
    В некоторых случаях у нас имеется две сети.
    В частности, у меня работает LTE Мегафон безлимит, и, про запас, - сильно лимитированный но дешевый тариф от МТС как вспомогательный.
    Мегафон иногда падает до суток, хотя в последне время все реже и реже.
    МТС - молодцом, такого не замечал.

    МТС появился, когда Мегафон не работал три недели и пришлось хоть как-то выправлять ситуацию. С тех пор и остался.

    Есть критичные устройства, которые требуют максимальной устойчивости связи, а именно - датчик температуры теплоносителя в ситеме отопления зимой. Вот он и работает с этим скриптом. В этом году он просигнализировал, когда котел остановился в сильные морозы, чем спас от разморозки. Правда, в какой сетке он находился при этом я не знаю :)

    Коннектимся таким скриптом.
    Код (Lua):
    function connect()
        local ap_db={
            {'AP_1' , "password1"},
            {'AP_2' , "password2"},
            {'AP_3' , "password3"},
    }
        local i = 1
        function get()
            wifi.sta.config(ap_db[i][1],ap_db[i][2])
            wifi.sta.autoconnect(1)
            print("\nConnectig: "..ap_db[i][1])

            tmr.create():alarm(10000, tmr.ALARM_SINGLE,
            function()
                local ip = wifi.sta.getip()
                if ip == nil or ip == "0.0.0.0" then
                    print("\nFail: ".. ap_db[i][1])
                    i = i + 1
                    if i > #ap_db then i = 1 end
                    get()
                else
                    print("\nConnected: "..ap_db[i][1])
                    --[[
                    if i ~= 1 then
                        i = 1
                        print("Reconnect to Main AP Starts at 1800 sec." )
                        tmr.create():alarm(1800000, tmr.ALARM_SINGLE,
                        function()
                            get()
                        end)
                    end
                    --]]

                end
            end)
        end
        get()
    end
    connect()
    Если в первой строке указать основную точку доступа и расскомментировать кусок кода, то через полчаса система будет перепроверять появление основной сетки.

    А этот скрипт - коннектится к лучшей из своих точек по силе сигнала.
    Можно вызывать как исполняемый файл в случае потери wifi.
    Код (Lua):
    do
    local myssid = {
        {"MyAP_01", "secretpassword01"},
        {"MyAP_02", "secretpassword02"},
        {"MyAP_03", "secretpassword03"},
    }

    local myrssi = {}
    local get
    local i = 1
    local printt

    local connect = function()
        print("#myrssi = "..#myrssi)
        if #myrssi ~= 0 then
        local max = 1
        local key
        for k, v in pairs(myrssi) do
            if v > max then max = v; key = k end
        end
        print("connect to ssid # "..key.." ".. myssid[key][1])
        wifi.sta.config(myssid[key][1],myssid[key][2])
        wifi.sta.autoconnect(1)
        else
        print "Oooops! No AP to connect!"
    end

    end

    printt = function(t)
        if t then
            table.foreach(t, function(...)
            local sd, sa = ...
            local _, rssi = string.match(sa, "([^,]+),([^,]+)")
            print(sd, rssi)
            myrssi[i] = - rssi
            end)
        end

        i = i + 1
        if i <= #myssid  then
            get()
        else
            connect()
        end
    end

    get = function()
        local st = tmr.create()
        st:alarm(5000, tmr.ALARM_SINGLE, function()
            printt()
        end)

        local scan_cfg={}
        scan_cfg.ssid = myssid[i][1]
        print("Ask #"..i.." = "..myssid[i][1])

        tmr.create():alarm(1500, tmr.ALARM_SINGLE, function()
            wifi.sta.getap(scan_cfg, 0,
            function(t)
            tmr.stop(st)
            tmr.unregister(st)
            printt(t)
        end)

        end)
    end

    get()
    end

    Далее смотрим
    ПРИМЕР.
     
    Последнее редактирование: 24 мар 2017
    SergeiL нравится это.
  5. SergeiL

    SergeiL Оракул Модератор

    Я таким образом WiFi точки дома перебираю. Ну не совсем таким :) На Си.

    Дома три точки с отличающимися ssid, подключенные к одной и той-же LAN.
    Имена типа "WiFi_1", "WiFi_2", "WiFi_3"

    При старте выбираю первую по списку, пытаюсь подключиться, если нет - вторая.

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

    Хочу еще дописать сортировку по RSSI, чтобы список строился по убыванию сигнала.
     
    glory24 нравится это.
  6. ИгорьК

    ИгорьК Оракул Модератор

    Это можно... Но не мой случай - точки "стоят" разных денег :)
     
  7. SergeiL

    SergeiL Оракул Модератор

    Понятно, но блин.. монолит этот, стены по 30 сантиметров. :(.
    Пытался использовать одну в центре, но все равно оставались зоны, где вроде ловил сигнал, но скорость никакая. Так появилась вторая, а третья досталась от Ростелекома в виде PON роутера.
    Стало совсем хорошо!

    Про сортировку задумался, так как не прикольно править очередность проверки точек, перед заливкой прошивки в устройство другой комнаты.:)

    Кстати, хорошая идея! :) Сбросить имя SSID на OpenHAB через mqtt. Добавлю у себя.
     
  8. ИгорьК

    ИгорьК Оракул Модератор

    Выявление лучшей ssid:
    Код (Lua):
    do
    allssid = {}
    function listap(t) -- (SSID : Authmode, RSSI, BSSID, Channel)
         for ssid,v in pairs(t) do
            -- local authmode, rssi, bssid, channel = string.match(v, "([^,]+),([^,]+),([^,]+),([^,]+)")
            local _, rssi = string.match(v, "([^,]+),([^,]+)")
            local n = - tonumber(rssi)
            allssid[n]=ssid
        end
        table.foreach(allssid, print)
    --
        local max = 1
        for k, v in pairs(allssid) do
            if tonumber(k) > max then max = k end
        end
    --]]
        print ("Best ssid is "..allssid[max].." with "..max.." rssi." )
    end
    wifi.sta.getap(listap)
    end
    Хотя, это выбор из всех, а не только из "своих" АР. Надо думать глубже... Чет не хочется нынче :)
     
    Последнее редактирование: 21 мар 2017
    SergeiL нравится это.
  9. ИгорьК

    ИгорьК Оракул Модератор

    Добавил в тему 54 скрипт, который выбирает лучшую точку из своих.
     
  10. alp69

    alp69 Форумчанин

    Не совсем то, что искал. Нужна классическая гистограмма для того, чтобы нагляднее отображать суммарный расход чего бы то ни было за определенный промежуток времени. Графиками удобно динамику процесса отображать, но не промежуточные итоги с накоплением.
     
  11. ИгорьК

    ИгорьК Оракул Модератор

    Будет.
     
  12. ИгорьК

    ИгорьК Оракул Модератор

    Вы это имеете в виду?
    3333333.jpg
     
  13. alp69

    alp69 Форумчанин

    Скорее это
    [​IMG]
    Но чтобы столбик не в конце месяца появлялся, а рос в течение месяца. В новом месяце - новый столбик. Так нагляднее (субъективно).
     
  14. ИгорьК

    ИгорьК Оракул Модератор

    54.1. Рабочий пример.
    Датчик температур и ADC, который ловит лучшую сеть из своих при старте или потере wifi. Четыре файла. Первый - на запуск.

    Код (Lua):
    do
    answer = {}

    Broker = "ВАШ_БРОКЕР"
    port = 1883
    myClient = "supertemp"
    pass = "ВАШ_ПАРОЛЬ"

    mod = {}
    mod.publish = false

    m = mqtt.Client(myClient, 30, myClient, pass)
    m:lwt(myClient, 0, 0, 0)

    function connecting()
        connect = require('mqttm')
        connect.connecting(m, Broker, port, mod, function ()connect = nil end)
    end

    m:on("offline", function(con)
          mod.publish = false
          m:close()
          connecting()
    end)

    publ = function()
        answer.adc = ""..adc.read(0)
        answer.heap = ""..node.heap()
        local sendMQ
        local getd = coroutine.create(function()
            for k, v in pairs(answer) do
                sendMQ(k, v)
                coroutine.yield()
            end
                collectgarbage()
        end)

        sendMQ = function(k, v)
            m:publish(myClient.."/"..k,v,0,0,
                function(con)
                    print("Send", k, "=", v)
                    coroutine.resume(getd)
            end)
        end
        coroutine.resume(getd)
    end

    local askt = function()
        answer = {}
        function myWork() print("Got DS18b20: "..#answer); table.foreach(answer, print)end
        function unload() ds = nil end
        ds = require('ds18b20m')
        -- pin = 4
        -- del = 750
        -- ds.getTemp(pin, del, answer, myWork, unload)
        ds.getTemp(4, 750, answer, myWork, unload)
    end


    local next = tmr.create()
    next:alarm(20000, 1, function()
        tmr.create():alarm(10000, 0, function()
            if mod.publish == true then
                publ()
            end
        end)
        askt()
    end)
    end

    f = loadfile("connect001003.lua")
    f()
    start(connecting)

    Код (Lua):
    function start(call)
    local myssid = {
        {"MYAP_1", "mysuperpasswird_1"},
        {"MYAP_2", "mysuperpasswird_2"},
        {"MYAP_3", "mysuperpasswird_3"},
    }

    local myrssi = {}
    local get
    local i = 1
    local printt

    local connect = function()
        print("#myrssi = "..#myrssi)
        if #myrssi ~= 0 then
            local max = 1
            local key
            for k, v in pairs(myrssi) do
                if v > max then max = v; key = k end
            end
            print("connect to ssid # "..key.." ".. myssid[key][1])
            collectgarbage()
            wifi.sta.config(myssid[key][1],myssid[key][2])
            wifi.sta.autoconnect(1)
            tmr.create():alarm(25000, tmr.ALARM_SINGLE, function()
                print('\n', wifi.sta.getip())
                start = nil
                if call then call() end
            end)
        else
            print "Oooops! No AP to connect!"
    end

    end

    printt = function(t)
        if t then
            table.foreach(t, function(...)
            local sd, sa = ...
            local _, rssi = string.match(sa, "([^,]+),([^,]+)")
            print(sd, rssi)
            myrssi[i] = - rssi
            end)
        end

        i = i + 1
        if i <= #myssid  then
            get()
        else
            connect()
        end
    end

    get = function()
        local st = tmr.create()
        st:alarm(5000, tmr.ALARM_SINGLE, function()
            printt()
        end)

        local scan_cfg={}
        scan_cfg.ssid = myssid[i][1]
        print("Ask #"..i.." = "..myssid[i][1])

        tmr.create():alarm(1500, tmr.ALARM_SINGLE, function()
            wifi.sta.getap(scan_cfg, 0,
            function(t)
            tmr.stop(st)
            tmr.unregister(st)
            printt(t)
        end)

        end)
    end
    get()
    end
     

    Код (Lua):
    local M={}
    M.adrtbl = {}
    M.pin = false

    function M.addrs()
        ow.setup(M.pin)
        ow.reset_search(M.pin)
        local adr
        repeat
            adr = ow.search(M.pin)
            if(adr ~= nil) then
                table.insert(M.adrtbl, adr)
            end
        until (adr == nil)
        ow.reset_search(M.pin)
        adr = nil
        M.askT()
    end

    function M.askT()
        ow.setup(M.pin)
        for _, v in pairs(M.adrtbl) do
            ow.reset(M.pin)
            ow.select(M.pin, v)
            ow.write(M.pin, 0x44, 1)
        end
        v = nil
    end

    function M.readResult(temptabl)
        local data
        local crc
        local t
        for _, v in pairs(M.adrtbl) do
            ow.reset(M.pin)
            ow.select(M.pin, v)
            ow.write(M.pin,0xBE,1)
            data = string.char(ow.read(M.pin))
            for i = 1, 8 do
                data = data .. string.char(ow.read(M.pin))
            end
            crc = ow.crc8(string.sub(data,1,8))
            if (crc == data:byte(9)) then
                t = (data:byte(1) + data:byte(2) * 256)
                if (t > 32767) then t = t - 65536 end
                t = t * 625 /10000
                table.insert(temptabl, t)
            end
        end
        data = nil
        crc = nil
        t = nil
    end

    function M.getTemp(pin, del, ttable, call, unload)
        M.pin = M.pin or pin or 4
        if #M.adrtbl == 0 then
            M.addrs()
        else
            M.askT()
        end
        local getDS = tmr.create()
        getDS:register(del, tmr.ALARM_SINGLE, function (t)
            t:unregister()
            getDS = nil
            M.readResult(ttable)
            -- print("Got DS18b20: ",#ttable)
            if unload then
                M.getTemp = nil
                M.adrtbl = nil
                M.pin = nil
                M.setup = nil
                M.askT = nil
                M.readResult = nil
                unload()
                package.loaded["ds18b20m"]=nil
                call()
                return
            end
            if call then call() end
        end)
        getDS:start()
    end
    return M

    Код (Lua):
    M={}
    function M.connecting(m, Broker, port, mod, unload)
        local getConnect
        getConnect = function()
           -- print("Start Connect")
           if wifi.sta.status() == 5 then
                -- print("Got WiFi!")
                m:connect(Broker, port, 0, 0,
                    function(con)
                        tmr.stop(1)
                        -- print("Connected")
                        if mod then mod.publish = true end
                        if unload then
                            getConnect = nil
                            package.loaded["mqttconnect"]=nil
                            unload()
                        end
                end)
            else
                tmr.stop(1)
                tmr.unregister(1)
                f = loadfile("connect001003.lua")
                f()
                start(connecting)
            end
        end
        tmr.alarm(1, 5000, 1, function()
            getConnect()
        end)
        getConnect()
    end
    return M
     

    Snap_2017.03.24_17h27m46s_005.jpg

    Snap_2017.03.24_17h45m49s_006.jpg
     
    Последнее редактирование: 24 мар 2017
  15. ИгорьК

    ИгорьК Оракул Модератор

  16. ИгорьК

    ИгорьК Оракул Модератор

    Растолкал типовые задачки по самостоятельным файлам.
    Каковы они?
    Первая задачка - создать объект для соединения с MQTT брокером. Вызов этого файла оставляет в памяти объект - MQTT клиент. Вот так:
    Код (Lua):
    function setmqtt(myClient)
        mod = {}
        mod.publish = false
        local begin = function()
            local pass = "password"
            m = mqtt.Client(myClient, 30, myClient, pass)
            m:lwt(myClient, 0, 0, 0)
            connecting = function ()
                connect = require('getmqtt')
                connect.connecting(m, "ВАШ_БРОКЕР", 1883, mod, function() connect = nil end)
                begin, setmqtt = nil, nil
            end
            m:on("offline", function(con)
                mod.publish = false
                m:close()
                print("Offline Now!")
                connecting()
            end)
            connecting()
        end
        begin()
    end

    Вторая - установить связь с брокером.
    Код (Lua):
    M={}
    function M.connecting(m, Broker, port, mod, unload)
        local getConnect
        getConnect = function()
           if wifi.sta.status() == 5 then
                m:connect(Broker, port, 0, 0,
                    function(con)
                        tmr.stop(1)
                        print("Connected to "..Broker.." at "..port)
                        if mod then mod.publish = true end
                        if unload then
                            getConnect = nil
                            package.loaded["getmqtt"]=nil
                            unload()
                        end
                end)
            else
                tmr.stop(1)
                tmr.unregister(1)
                -- get wifi
                f = loadfile("connectmainwifi.lua")
                f()
                f = nil
                startwifi(connecting)
            end
        end
        tmr.alarm(1, 5000, 1, function()
            getConnect()
        end)
        getConnect()
    end
    return M
     

    Для установки связи с брокером может понадобиться wifi. Файл ниже будет вызван, если сети нет. Если есть - не будет вызван. В него забивается информация о возможных точках доступа для соединения. Эти точки перебираются последовательно.
    Главной считается первая точка в таблице. Если эта АР будет не доступна - соединение произойдет с любой другой из доступных, однако каждые 30 минут доступность главной будет перепроверяться.
    Код (Lua):
    -- Переподключает к главной AP - должна быть в списке первой
    function startwifi(call)
        local ap_db={
            {'AP_01'    , "secretpassword"},
            {'AP_02'    , "secretpassword"},
            {'AP_03'    , "secretpassword"}
        }
        local i = 1
        function get()
            wifi.sta.config(ap_db[i][1],ap_db[i][2])
            wifi.sta.autoconnect(1)
            print("\nConnectig: "..ap_db[i][1])
       
            tmr.create():alarm(17000, tmr.ALARM_SINGLE,
            function()
                local ip = wifi.sta.getip()
                if ip == nil or ip == "0.0.0.0" then
                    print("\nFail: ".. ap_db[i][1])
                    i = i + 1
                    if i > #ap_db then i = 1 end
                    get()
                else
                    print("\nConnected: "..ap_db[i][1])
                    --
                    if i ~= 1 then
                        i = 1
                        print("Reconnect to Main AP Starts at 1800 sec." )
                        tmr.create():alarm(1800000, tmr.ALARM_SINGLE,
                        function()
                            wifi.setmode(wifi.NULLMODE)
                            -- get()
                        end)
                    end
                    --]]
                    if call then
                        startwifi, ap_db, i, get, ip = nil, nil, nil, nil, nil
                        call()
                    end
                end
            end)
        end
        get()
    end

    Перечисленные выше три файла обеспечивают поддержку работы.

    Следующий - модуль публикации на брокер. Его функция публикации принимает таблицу с данными, добавляет к ней размер кучи и результат ADC и последовательно все публикует:
    Код (Lua):
    M={}
    M.publ = function(answer, call)
        answer.adc = ""..adc.read(0)
        answer.heap = ""..node.heap()
        local sendMQ
        local getd = coroutine.create(function()
            for k, v in pairs(answer) do
                sendMQ(k, v)
                coroutine.yield()
            end
                collectgarbage()
                if call then
                    M.publ,sendMQ, getd, M  = nil, nil, nil, nil
                    package.loaded["pubmqtt"]=nil
                    call()                
                end
        end)
        sendMQ = function(k, v)
            m:publish(myClient.."/"..k,v,0,0,
                function(con)
                    print("Send", k, "=", v)
                    coroutine.resume(getd)
            end)
        end
        coroutine.resume(getd)
    end
    return M

    И, наконец, что все объединяет? Главный файл main.lua.
    Логика его работы очень простая.
    Нам нужно некоторое действие? У нас есть необходимый скрипт!
    Готовим callback функцию для его выгрузки заранее, вызываем скрипт и после отработки - расправляемся с ним через cakkback.
    Код (Lua):
    do
    answer = {} -- склад информации
    myClient = "supertemp"
    mod = {}
    mod.publish = false
    -- публикуем таблицу, если разрешено
    publ = function()
        if mod.publish then
            local pu = require('pubmqtt')
            local unload = function() pu = nil end
            pu.publ(answer, unload)
        end
    end
    -- опрос датчиков
    local askt = function()
        answer = {}
        -- callback отправляет данные на публикацию
        -- local myWork = function() table.foreach(answer, print); publ() end
        local myWork = function() publ() end
        local unload = function () ds = nil end
        ds = require('ds18b20am')
        --   4 - на ноге сидит связка датчиков, 750 - задержка для преобразования температуры
        ds.getTemp(4, 750, answer, myWork, unload)
    end

    -- это каждые 20 секунд запускает измерение температуры
    local next = tmr.create()
    next:alarm(20000, 1, function()
        askt()
    end)

    -- создаем брокера
    f = loadfile("setmqtt.lua")
    f()
    -- функцию уничтожаем, а брокер - остается
    f = nil
    -- настраиваем брокера
    setmqtt(myClient)
    end
    Файл решает простую задачу - опрашивает датчики температуры и отправляет на брокер.

    Еще изменил модуль ds18b20. Теперь он возвращает не массив данных, а таблицу, где ключами - адреса датчиков. Зачем это нужно? После пяти лет работы датчики начинают выходить из строя.

    Замена датчиков ведет к изменению порядка данных в массиве. Это есть плохо - у кого несколько датчиков - тот знает. Модуль ds18b20.lua в общем архиве.

    Выглядит все так:
    Snap_2017.03.29_17h13m52s_002.jpg
     

    Вложения:

    • allinone.zip
      Размер файла:
      3,5 КБ
      Просмотров:
      169
    Последнее редактирование: 29 мар 2017
    SergeiL и alp69 нравится это.
  17. tammat

    tammat Нуб

    Добрый день!
    Не могу запустить один выключатель на группу

    8.3. Сделаем один выключатель на группу.
    xx.items
    Код (C++):
    Group I_Home
    Group:Switch:OR(ON, OFF) At_home  "Я: [(%d)]" (I_Home)
    Switch    t1    "Кухня"    <light>    (Lighting,At_home)
    Switch    t2    "t2"    <light>    (Lighting,At_home)
     
    xx.sitemap
    Код (C++):

       Switch item=At_Home mappings=[ON="Дома!", OFF="Ушел!"]    
         Group item=Lighting label="Дом" icon="firstfloor"
     
    В чем проблема?
     

    Вложения:

    • 1.JPG
      1.JPG
      Размер файла:
      24,2 КБ
      Просмотров:
      433
    • 2.JPG
      2.JPG
      Размер файла:
      45,1 КБ
      Просмотров:
      454
  18. ИгорьК

    ИгорьК Оракул Модератор

    В невнимательности. Или у меня так? Посмотрите названия группы и группу у выключателей.
     
    Последнее редактирование: 3 апр 2017
  19. tammat

    tammat Нуб

    У вас должно быть

    Switch item=Zadnitsa Moscow_group mappings=[ON="Дома!", OFF="Ушел!"]
     
    ИгорьК нравится это.
  20. ИгорьК

    ИгорьК Оракул Модератор

    ESP-8266 only.
    Подержка работы котла.
    Он, паразит, останавливается, когда бывают падения газа в трубопроводе. А они бывают. Котел Vaillant (e-bus) - внутрь лезть не готов, поэтому все топорно: питание котла через замкнутое реле. Для перезагрузки на реле подается питание, оное размыкается.

    Комплексное устройство (смесь бульдога с носорогом).
    Показывает текущее потребление котлом электричества (итемы от PZEM-004T).
    Измеряет три температуры и силу ветра.
    Управляет (1) реле питания котла на размыкание и (2) реле обогрева трубы от скважины - на замыкание.

    Дистанционно можно отключить питание зависшего котла. Включается автоматом через установленное время (здесь 15 секунд).

    Отправляет все на MQTT брокер и Народный Мониторинг.

    Можно сделать автоматом:
    - одна перезагрузка котла после падения температуры системе ниже установленной и
    - включение обогрева трубы по температуре наружнего воздуха.

    Кино. Там кое-что о глюках OpenHAB 2.

     
    Последнее редактирование: 6 апр 2017