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 байт
      Просмотров:
      370
    Последнее редактирование: 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 байт
      Просмотров:
      408
    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 нравится это.