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

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

  1. ИгорьК

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

    Еще пример.
    Код (Lua):
    do
    local size = 20 -- размер буфера
    aver = require("buffm")
    aver.bufsize(size) -- установить размер буфера, не обязательно. 10 по умолчанию

    av = require("kalmanm")
    av.set()

    local temp = {}; -- Таблица для температуры
    local pin = 4; -- Здесь датчик
    local del = 750 -- задержка между запросом температуры и чтением
    ds = require('ds18b20m')  -- модуль (мой, в приложении) чтения датчика

    function mywork() -- Это сделать после чтения
        print("Start heap = "..node.heap())
        local data = temp[1] or 85 -- Один датчик, читаем первый элемент таблицы
        print("Got = "..data)
        local avr = aver.add(data) -- отправить данные в кольцевой буфер, обратно получить среднее значение
        local kdata = av.update(data) -- фильтруем данные Калманом
        kdata = string.format("%.2f", kdata)
        avr =  string.format("%.2f", avr)
        print("Filtered = "..avr, "  Kalman = "..kdata)
    end

    tmr.alarm(0, 3000, 1, function()
        temp = {}
        ds.getTemp(pin, del, temp, mywork) -- вызываем чтение датчика и скармливаем calback
    end)
    end
    Вот оно:
    dsss01.jpg

    Кольцевой фильтр.
    Фильтр Калмана.
    ds18b20 - в приложении.
     

    Вложения:

    • ds18b20m.zip
      Размер файла:
      818 байт
      Просмотров:
      99
    Последнее редактирование: 28 фев 2017
    alp69 нравится это.
  2. ИгорьК

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

    И в комплекте с MQTT:
    Код (Lua):
    do
    Broker = "ВАШ_САЙТ"
    port = 1883
    myClient = "tester"
    name = myClient
    pass = "pass"
    publish = false

    m = mqtt.Client(myClient, 180, name, pass)
    m:lwt("/lwt", myClient, 0, 0)

    function connecting()
        print('(Re)Connecting')
        local getConnect
        local myrecon = tmr.create()
        tmr.register(myrecon, 1500, tmr.ALARM_AUTO, function()
              getConnect()
        end)
        getConnect = function()  
           print("get")
           if wifi.sta.status() == 5 and wifi.sta.getip() ~= nil then
                print("Got WiFi!")
                tmr.stop(myrecon)
                tmr.unregister(myrecon)
                m:connect(Broker, port, 0, 1,
                    function(conn)
                        print("Connected")
                        publish = true
                         --m:subscribe(myClient.."/#",0, function(conn)
                         --   print("Subscribed.")
                         -- end)
                    end)  
            end
        end
        getConnect()
        tmr.start(myrecon)
    end

    m:on("offline", function(con)
          publish = false
          connecting()
          print("Lost connection!!!")
    end)

    function pub(top, dat)
      if top and dat then
             m:publish(myClient.."/"..top,dat,0,0, function(conn)
                print("Publised "..dat.." at topic "..top)
             end)
      end
    end

    --------------------  End MQTT actions ------------------
    av = require("kalmanm")
    av.set()

    ds = require('ds18b20m')

    local temp = {};
    local pin = 4;
    local del = 750

    function mywork()
        local data = temp[1] or 85
        print("Got = "..data)
        local kdata = av.update(data) -- фильтруем данные
        kdata = string.format("%.2f", kdata)
        pub("temp001", kdata)
    end

    tmr.alarm(0, 20000, 1, function()
        if publish then
            pub("heap", node.heap())
            temp = {}
            ds.getTemp(pin, del, temp, mywork)
        end
    end)

    connecting()
    end
     
    Последнее редактирование: 2 мар 2017
    alp69 нравится это.
  3. ИгорьК

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

    Теперь пример со многими данными. Кольцевой буфер фильтрует. Модуль несколько изменен.
    Код (Lua):
    local M={}

    function M.new(size)
        local set = {
            av = 0,
            buf = {},
            bufs = size or 10,
            ind = 1,
            fil = false
        }
        return set
    end

    function M.newbuf(name, size)
        M[name] = M.new(size)
    end


    function M.add(name, dat)
        if not M[name].fil then
            for i = 1, M[name].bufs do
                M[name].buf[i] = dat
            end
            M[name].fil = true
        end
        M[name].buf[M[name].ind] = dat
        M[name].ind = M[name].ind +1
        if M[name].ind > M[name].bufs then M[name].ind = 1 end
     
        local sum = 0
        for ii = 1, M[name].bufs do
            sum = sum + M[name].buf[ii]
        end
        M[name].av = sum/M[name].bufs
        return M[name].av
    end
    return M
    Применяем так:
    Код (Lua):

    do
    size = 5 -- размер буфера, для каждого можно установить свой
    name1 = "d1" -- имена трех "датчиков"
    name2 = "d2"
    name3 = "d3"
    aver = require("buffmanym") -- загрузка модуля
    aver.newbuf(name1, size) -- установка буфера для кадого датчика (обязательно) и размер буфера(не обязательно). Буфер по умолчанию = 10
    aver.newbuf(name2, size)
    aver.newbuf(name3, size)
    tmr.alarm(0, 3000, 1, function()
      local data1 = math.random(10) -- "Датчики" в трех диапаонах
      local data2 = (math.random(10) * 2) + 10
      local data3 = (math.random(10) * 5) + 50
      print("Got now =  ", data1, data2, data3)
      local av1 = aver.add(name1,data1) -- Фильтруем для каждого "Датчика"
      local av2 = aver.add(name2,data2)
      local av3 = aver.add(name3,data3)
      print("Filtered now = ", av1, av2, av3)
    end)
    end
    Видим это:
    sample.jpg
     
  4. ИгорьК

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

    И чтобы закрыть тему фильтров - многопоточный Калман (kalmanMultm.lua):
    Код (Lua):
    local M = {}

    function M.new(varian,varProce)
        local set ={
            Pc = 0.0,
            G = 0.0,
            P = 1.0,
            Xp = 0.0,
            Zp = 0.0,
            Xe = 0.0,
            variance = varian or 1.12184278324081/100000,
            varProcess = varProce or 1/100000000
        }
        return set
    end

    function M.newkalm(name, variance, varProcess)
        M[name] = M.new(variance, varProcess)
    end

    M.update = function(name,vol)
        M[name].Pc = M[name].P + M[name].varProcess
        M[name].G = M[name].Pc/(M[name].Pc + M[name].variance)
        M[name].P = (1-M[name].G)*M[name].Pc
        M[name].Xp = M[name].Xe
        M[name].Zp = M[name].Xp
        M[name].Xe = M[name].G*(vol-M[name].Zp) + M[name].Xp
        return M[name].Xe
    end

    return M

    Применение:
    Код (Lua):
    do
    av = require("kalmanMultm") -- Грузим модуль
    name1 = "d1" -- Названия трех датчиков
    name2 = "d2"
    name3 = "d3"

    av.newkalm(name1) -- Создаем три фильтра
    av.newkalm(name2)
    av.newkalm(name3)
    -- av.newkalm(name3, variance, varProcess) -- Можно и так устанавливать, иначе значения по умолчанию

    tmr.alarm(0, 3000, 1, function()
        data1 = math.random(10) -- Три источника цифр
        data2 = (math.random(50)) + 10
        data3 = (math.random(100)) + 100
        print(string.format("Got     : %.2f   %.2f   %.2f", data1, data2, data3))
     
        local d1 = av.update(name1, data1) -- Три фильтрации
        local d2 = av.update(name2, data2)
        local d3 = av.update(name3, data3)

        print(string.format("Filtered: %.2f   %.2f   %.2f", d1, d2, d3))
    end)
    end
    Результат:
    kalman.jpg
     
    Последнее редактирование: 1 мар 2017
    alp69 нравится это.
  5. ИгорьК

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

    И рабочий пример - три датчика температуры DS18b20, что-то там на аналогоом входе и кучу памяти (всего пять предметов) отправляем на брокер MQTT:
    Код (Lua):
    Broker = "ВАШ_БРОКЕР"
    port = 1883
    myClient = "tester"
    name = myClient
    pass = "pass"
    publish = false

    m = mqtt.Client(myClient, 180, name, pass)
    m:lwt("/lwt", myClient, 0, 0)

    function connecting()
        print('(Re)Connecting')
        local getConnect
        local myrecon = tmr.create()
        tmr.register(myrecon, 1500, tmr.ALARM_AUTO, function()
              getConnect()
        end)
        getConnect = function()
           print("get")
           if wifi.sta.status() == 5 and wifi.sta.getip() ~= nil then
                print("Got WiFi!")
                tmr.stop(myrecon)
                tmr.unregister(myrecon)
                m:connect(Broker, port, 0, 0,
                    function(conn)
                        print("Connected")
                        publish = true
                         --m:subscribe(myClient.."/#",0, function(conn)
                         --   print("Subscribed.")
                         -- end)
                    end)
            end
        end
        getConnect()
        tmr.start(myrecon)
    end

    m:on("offline", function(con)
          publish = false
          connecting()
          print("Lost connection!!!")
    end)

    function puball()
        send = coroutine.create(function()
            for i = 1, 5 do
                local top = names[i]
                local dat = datat[i]
                pub(top, dat)
                coroutine.yield()
            end
        end)

        pub = function(top, dat)
          if top and dat then
                 m:publish(myClient.."/"..top,dat,0,0, function(conn)
                    print("Publised "..dat.." at topic "..top)
                    coroutine.resume(send)        
                 end)
          end
        end
        coroutine.resume(send)
    end
    --------------------  End MQTT actions ------------------
    av = require("kalmanmanym")

    names = {
        "t1",
        "t2",
        "t3",
        "wind",
        "heap"
    }

    for _, n in pairs(names) do
        av.newkalm(n)
    end

    ds = require('ds18b20m')

    temp = {}
    datat = {}

    local pin = 4;
    local del = 750

    function mywork()
        for i = 1, 3 do
            datat[i] = av.update(names[i], temp[i])
        end

        local w = adc.read(0) / 10.0
        datat[4] = av.update(names[4], w)

        for i = 1, 4 do
            datat[i] = string.format("%.2f", datat[i])
        end
        datat[5] = node.heap()
        print("===========next===========")
        puball()
    end

    tmr.alarm(0, 8000, 1, function()
        if publish then
            temp = {}
            datat = {}
            ds.getTemp(pin, del, temp, mywork)
        end
    end)
    connecting()
    Результат:
    next.jpg

    Скрипт пригодится всем тем, кто мониторит температуру на даче:
    • улица
    • система
    • внутри.
     
    Последнее редактирование: 2 мар 2017
    alp69 нравится это.
  6. ИгорьК

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

    Завалилась Малинка 3. Кроме ОН2 и Mosquitto ничего на ней не стояло. Может дело и в SD - карте, все бывает. Но так, к слову...
     
  7. alp69

    alp69 Гик

    Охлаждение было?
     
  8. ИгорьК

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

    Нет.
    Пардон. Жив.
    Пару раз перегружал - без признаков. А теперь, надо же...
    tmp_3808-Screenshot_20170303-20434710347543.png
    tmp_3808-Screenshot_20170303-204605-344202829.png
     
    Последнее редактирование: 3 мар 2017
  9. alp69

    alp69 Гик

    Я так понял, что весомых причин перейти на ОН2 так и не находится? Ну кроме изучения в целях подготовки к ОН3 :cool:
     
    ИгорьК нравится это.
  10. ИгорьК

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

    Никаких. Единственная - у них там правильно слайдер работает. А скрипты- на том же странном языке.
     
    alp69 нравится это.
  11. ИгорьК

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

    Запаковал реконнект MQTT в модуль. Модуль загружается при старте и потере связи и выгружается когда все налаживается. Все ради экономии памяти.
    Пользуюсь так:
    Код (Lua):
    do
    Broker = "МОЙ_БРОКЕР"
    port = 1883
    myClient = "test001"
    pass = "pass"

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

    m = mqtt.Client(myClient, 30, myClient, pass) -- типовой клиент
    m:lwt(myClient, 0, 0, 0) -- и его кончина

    function connecting() -- Этим коннектимся и реконнектимся
        connect = require('mqttconnect') -- Грузим пакет
        -- И применяем, а затем выгружаем.
        connect.connecting(m, Broker, port, mod, function()
            connect = nil
        end) -- и так каждый раз
    end

    m:on("offline", function(con) -- потеряли связь?
          mod.publish = false -- тормозим публикацию
          m:close() -- сбрасываем соединение
          print("Offline Now!")
          connecting() -- и ищем снова
    end)
    connecting()  -- Начали труды праведные
    end
    Так как выше - теоретически правильно. Но поскольку у каждого свой брокер и он постоянный, можно сделать неправильно, но еще уменьшить код - попробовать спрятать данные о брокере в модуль. Попробую как-нибудь.
     

    Вложения:

    • mqttconnect.zip
      Размер файла:
      418 байт
      Просмотров:
      158
    alp69 нравится это.
  12. alp69

    alp69 Гик

    Ааааа! На шаг впереди идете :D Давно в планах всю рутину в модули позапихивать. Отчаянная нехватка времени :)
     
    ИгорьК нравится это.
  13. ИгорьК

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

    Тем более, что модули в Lua рисовать и налаживать, это не то что в сях с библиотеками т-ра...тата...
    И, кстати, занятие это очень развивает понимание... JavaScript :)
     
  14. alp69

    alp69 Гик

    Не логичнее m:close() тоже в модуль переместить? Перед m:connect?
    И еще один вопрос - зачистка collectgarbage() после выгрузки модуля, я так понял, не обязательна?

    Да, и еще:
    Так?
    Код (Lua):
    function connecting() -- Этим коннектимся и реконнектимся
        connect = require('mqttconnect')
    .......
     
    Или так?
    Код (Lua):
    function connecting() -- Этим коннектимся и реконнектимся
        local connect = require('mqttconnect')
    .........
     
     
  15. ИгорьК

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

    +1
    Это надо проверять. Но особой разницы нет, поскольку модуль выгружается. Надо тестировать на слив памяти.
     
  16. ИгорьК

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

  17. alp69

    alp69 Гик

  18. ИгорьК

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

    Tmr.alarm прикрутили к независимым таймерам.
    А что за наработки по 31 биту? Его надо учитывать при замере времени для устранения дребезга кнопки и это уже учтено везде где она задействована.
    Больше то он нигде и не встречается.
     
  19. alp69

    alp69 Гик

    Не отслеживал, по-моему на прошлой или позапрошлой неделе оно уже висело. Могу и ошибаться. Но вчера днем я это точно видел.
     
  20. ИгорьК

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

    52. PZEM-004t.
    Начнем. По сути, это электрический счетчик. Описание здесь. Для секты Ардуино библиотека здесь. Но это не наш случай: ESP-8266 + Lua!
    010.jpg

    И сразу сюрприз. Вот что пишут на одном из форумов:
    011.jpg

    Чтобы добиться от модуля информации - ему надо задать вопросы.
    Модуль знает о четырех параметрах: нужно и четыре вопроса (верхняя строка скриншота - 4 пакета запросов, нижняя - 4 пакета в ответ на них).

    0083.jpg

    На картинке - время ответа модуля. На вопросы о напряжении и токе он размышляет дольше всего. Таким образом, общее время ответа составляет 1. 54 сек. Из них ответ на вопрос о напряжении (последний пакет) - 0,9 сек.
    Вау!

    Мы это все победим и ничего там подвисать не будет! Главное вопросы задавать после прихода ответа и его обработки. Это обеспечит минимально возможное время запрос/ответ (что и видно на рисунке - очередной запрос после предыдущего ответа, сколько бы по времени этот ответ не ожидался. Интервал ответ -> новый запрос составляет 15 - 17 мс).
    :) Так что:
    0093.jpg

    Сначала, все таки, немного напильника. Модуль этот пятивольтовый и с тремя вольтами ESP-8266 не дружит. Для инициации любви требуется внесение изменения в сопротивляемость резистора R17 (номинал на плате 1 ком) - находится рядом с UART. Варианта два - либо заменить на 500 ом, либо припаять параллельно ему тот же килоомный резистор.
    Кстати, почитайте и этот материал - здесь лучше всего показано как дейстововать. Картинка из ссылки:
    [​IMG]

    В материале, также, есть код на lua. Он проще и понятнее.
    Я так не умею, и пошел другим путем. Путь усердно закомментировал в следующем топике.
     
    Последнее редактирование: 16 мар 2017
    glory24, SergeiL и alp69 нравится это.