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

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

  1. ip-ua

    ip-ua Нерд

    Нужно в правилах по крону ( с любым интервалом) проверять, как давно получены даные с датчика и если интервал больше заданного принимать решение.
    Или вот так с использованием таймера: https://github.com/openhab/openhab/...-does-not-change-for-a-certain-period-of-time
     
  2. dimm71

    dimm71 Нерд

    По мотивам "Как узнать время" + синхронизация по SNTP (время постоянно тикает в модуле даже без доступа в Интернет. При появлении Интернет - синхронизируется)

    Итак, необходимы модули rtcfifo,rtcmem,rtctime,sntp. Прошивка выложенная ИгорьК в последнем его сообщении подходит.

    Шаблон
    Код (C++):

    rtctime.set(0, 0) -- изначально устанавливаем дату и время 1970/01/01 00:00:00
    GMT = 3600*3 -- 3 hour

    function SNTP()
    sntp.sync('0.ru.pool.ntp.org',
      function(sec,usec,server)
    --    print('sync', sec, usec, server)
    --    print (rtctime.get ())
        end,
      function()
       print('failed SNTP!')
      end)
    end

    function get_TIME()
         tm = rtctime.epoch2cal(rtctime.get()+GMT)
         print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]))
    end

    function run_SNTP()
         tmr.alarm(2, 3600000, 1, SNTP ) --  синхронизируем раз в час (может оно и не нужно так часто)
         print("Synchro  SNTP")
    end

    run_SNTP()
    tmr.alarm(5, 10000, 0, SNTP ()  -- синхронизируем 1 раз через 10 сек. после запуска
    end)

     
    выполняем в ESPlorer
    Код (C++):
    =get_TIME()
    MCU_time.png
     
    Последнее редактирование: 18 дек 2016
    Securbond и ИгорьК нравится это.
  3. ИгорьК

    ИгорьК Гуру

    Вынесу в заголовок.
     
  4. dimm71

    dimm71 Нерд

    Что то я намудрил в шаблоне. Время показывает 24 часа, 25, 26 :) Где правильно исправить TZ пока не разобрался
     
  5. ИгорьК

    ИгорьК Гуру

    Покопайтесь в моем модуле - увидите.
     
  6. zloy-glide

    zloy-glide Нерд

    То же самое что и мой пример. Я к тому что цветовое выделение работает только в сайтмэпе. И на правила никак не распространяется.
     
  7. SergeiL

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

    Попробуйте так:
    tm = rtctime.epoch2cal(rtctime.get()+3600*3)
    print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]))
    В теории так правильней, но я не проверял...
    Среды под nodemcu не осталось, почти год как перешел на Arduino IDE для ESP8266
     
  8. ИгорьК

    ИгорьК Гуру

    Работает. Вот, ведь, как все просто...
     
  9. dimm71

    dimm71 Нерд

    Спасибо. Чуть по другому сделал. То же самое, но в профиль
    GMT = 10800 -- 3 hour
    function get_TIME()
    tm = rtctime.epoch2cal(rtctime.get()+GMT)
    print(string.format("%04d/%02d/%02d %02d:%02d:%02d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"]))
    end
     
  10. Smerlin

    Smerlin Нерд

    Небольшое дополнение к яндекс погоде (тут вроде еще не было..)
    Код (Java):
    Number    YandexTempNight    "t° Ночью [%.1f °C]"     <temperature>    { http="<[http://export.yandex.ru/bar/reginfo.xml?region=ххххх.xml:120000:REGEX(.*?<night_short><temperature.*?>(.*?)</temperature></night_short>.*)]" }
    Number    YandexTempTom    "t° Завтра [%.1f °C]"     <temperature>    { http="<[http://export.yandex.ru/bar/reginfo.xml?region=ххххх.xml:120000:REGEX(.*?<tomorrow><temperature.*?>(.*?)</temperature></tomorrow>.*)]" }
    вместо "ххххх" вставить код своего города.

    Температура так скачет, вот и решил доделать, чтоб на завтра видно было....
     
    ИгорьК нравится это.
  11. dimm71

    dimm71 Нерд

    Со временем на модуле вроде разобрались. А как время с модуля передать в OH в удобочинаемом виде. В RTC передать не проблема - но там время в секундах. Как заставить OH перевести в удобочитаемый вид? В Интернет примеров rules не нашел
     
  12. ИгорьК

    ИгорьК Гуру

    http://forum.amperka.ru/threads/ard...ый-дом-азы-управления.5043/page-61#post-95639

    Последний код - там я ваше же время и отправляю.
     
  13. dimm71

    dimm71 Нерд

  14. alp69

    alp69 Форумчанин

    Если не секрет - для чего?
     
  15. dimm71

    dimm71 Нерд

    Пока мне интересно что к чему. А вообще хочу сделать что то вроде розетки с таймером.
    Время и дату уже передал а ОН
     
  16. faster_light

    faster_light Нерд

    Специально зарегистрировался, чтобы сказать автору топика человеческое спасибо!
    А именно, за исправления с учетом изменений raspbian, openhab и т.п. Сейчас даже в англоязычных форумах ногу сломишь.

    Из-за всей этой суматохи переезда openhab на openhab2, джесси, а так же RPi3 (где некоторые вещи вроде uart ставят палки в колеса).

    Однако всё же спустя выходные без опыта в linux удалось помигать светодиодом через gpio/uart=>arduino и т.п.

    Но, в виду отсутствия сетевых девайсов вроде esp и arduino shield не очень понятен принцип и способ реализации mosquitto. Это чисто сетевой интерфейс, или можно использовать и с другими протоколами МК?
    Если у ИгорьК будет время, напишите о Mosquitto поподробней

    А я дальше читать тему пошел, интересно
     
  17. ИгорьК

    ИгорьК Гуру

    Лучше чем здесь мне не написать.
     
  18. faster_light

    faster_light Нерд

    Спасибо, ознакомлюсь.
     
  19. ИгорьК

    ИгорьК Гуру

    46. Шаблон. Модуль для отправки данных на thingspeak.com.
    На всякий случай. Загружаем, отправляем, выгружаем.
    Единственное условие (которое мне очень не нравится), название полей таблицы с данными должно быть "field1", "field2", а не человеческое. То есть так как они называются на сайте:
    thingspeak01.jpg

    Наверно, можно что-то придумать, но пока не сообразил. Любая подстановка утяжелит код.
    Все принты из модуля после проверки работоспособности лучше удалить. Модуль обозвать tsmod.lua
    Применять, например, так:
    Код (Lua):
    do
    -- для информации - удалось отправить или нет
    sentTS = false
    -- API Key - ваш ключ, а не мой
    key = "14125136FDGHHJ%"
    -- Таблица со всеми данными для отправки
    data = {
        -- temperature
        field1 = 0,
        -- heap
        field2 = 0
    }
    -- callback функция, которая отработает после отправки
    function callTS()
        -- выгрузка модуля
        sendTS = nil
        package.loaded["sendTS"]=nil
        print("Unloaded sendTS Module")
        sentTS = true
        collectgarbage()
    end
    -- загрузка модуля
    sendTS = require('tsmod')
    -- просто меняем данные в таблице
    data.field1 = tmr.now() %100
    data.field2 = node.heap()
    -- отправка на thingspeak.com
    sendTS.sendTS(key, data, callTS)
    end

    А также сделал модуль для отправки по протоколу MQTT. Отправлять почти также:
    Код (Lua):
    do
    -- для информации - удалось отправить или нет
    sentTS = false
    -- API Key
    key = "14125136FDGHHJ%" -- ваш а не мой
    ID = "333222" -- и это тоже ваш

    data = {
        -- temperature
        field1 = 95,
        -- heap
        field2 = 100000
    }
    -- callback функция, которая отработает после отправки
    function callTS()
        -- выгрузка модуля
        MQTTsendTS = nil
        package.loaded["tsmqttmod"]=nil
        print("tsmqttmod Module Unloaded")
        sentTS = true
        collectgarbage()
    end
    -- загрузка модуля
    function sts()
        MQTTsendTS = require('tsmqttmod')
        -- просто меняем данные в таблице
        data.field1 = tmr.now() %100
        data.field2 = node.heap()
        MQTTsendTS.pub(key, ID, data, callTS)
    end
    sts()
    tmr.alarm(0, 120000, 1, function() sts() end)
    end
     

    UPD 22.12.2016. Важно. Но исключительно ИМХО.
    Казалось бы, а зачем это все надо? У нас, опенхабоводов, есть свой 'thingspeak', куда мы отправляем данные. В общем, сам не знал зачем это нужно. А теперь стало понятно - чтобы найти и прочитать вот этот абзац:
    leak2.jpg
    Так в чем проблема? Если вы повторили эти примеры (23.12.2016. модули уже исправлены!), то возможно заметили (а скорее - не заметили), что они съедают память нипадецки. Проблема описана вот здесь.

    Суть в том, что некоторые библиотечные функции, причем не lua(nodemcu) а SDK, "don"t correctly dereference callback references" (некорректно уничтожают ссылки на функции обратного вызова), что, в результате, upvalues(неглобальные переменные для локальных функций) не могут быть корректно уничтожены сборщиком мусора. И, замечу, это характерно не только для Lua, но и для Arduino реализации.

    Эту проблему не так просто заметить, поскольку суть программ такова, что как только wtchdog словит зависание - программа перезапускается.

    К чему все это. Запустил и оставил на ночь пример передачи на ThingSpeak по MQTT. Все работало. Утром встал, посмотрел в окно ESPlorer - модуль не отвечает. Ну, мало ли что... Хотел перегрузить, но посмотрел на сайт - увидел, что модуль продолжает отправлять данные, а результат его работы выгдядит примерно так:
    leak3.jpg

    Затем, на моих глазах, модуль перезагружается, что видно в окне ESPlorer, и все начинается сначала. Причем, после перезагрузки, модуль начинает отвечать по UART.
    Как к этому относиться? Конечно, это баг. Баг незаметный, но, похоже, присутствующий во многих скриптах.
    Он незаметен, поскольку модуль работает "до последнего", затем перегружается и все идет по кругу.
    Однако, для некоторых случаев, баг может проявляться существенно. В частности, когда на UART что-то висит и обеспечивается связь. Замечал для некоторых своих устройств, (не описанных здесь, в частности снифер 433 МГц) что они на определенное время перестают работать, но через некоторое время связь с ними восстанавливается.

    В общем, что рекомендует нам отрывок, приведенный выше. Две вещи:
    • больше использовать глобальные переменные, вместо upvalues, и
    • вручную уничтожать использованные локальные (хотя Lua этого не требует).
    Кстати, кроме побочной цели - отправлять данные на thingspeak эти модули имели (для меня) более важную - научиться работать с подгружаемыми/выгружаемыми модулями через callback функии. Научился :) ...

    Что же видится в арсенале учета (борьбы) с этого явления.

    1. Тестировать каждый код на утечку памяти. Понимать, когда ее становится мало и устройство перестает нормально работать. Не ждать watchdog, а делать работу за него:
    Код (Lua):
    if node.heap() < minheap then node.restart() end
    minheap определять для каждого конкретного случая.

    2. Меньше пользоваться собственными callback, хотя это так вкусно. Обходился же я как-то раньше без них (и как раньше без них обходился). Если они есть - см. п.1. Вот такой loop :)

    3. В функциях, в конце, перед выходом, удалять локальные переменные путем присвоения им значения nil . UPD 23.12.2916. Да! Это работает!

    4. Программно учитывать необходимость перегрузки модуля, когда он работает по UART с устройствами. ИМХО,
    • требовать со стороны другого устройства подтверждения получения информации модулем;
    • не реагировать на UART - выбросы при перезагрузке модуля. Кстати, лучший способ - работать с UART на той скорости, на которой он не загружается.
    5. Использовать возможность модуля хранить важные переменные в специальном участке памяти, когда они сохраняются и при перезагрузке. В частности, использовать возможности, предоставленне модулем rtcmem.

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

    В заключение - о хорошем. :) Вот этот код отправляет данные на thingspeak и память не только ест, но и отрыгивает. Поиграйтесь, интересно:
    Код (Lua):
    -- API Key & ID - меняйте на свои!
    key = "111111111111111"
    ID = "22222"

    dat = {
        -- temperature
        field1 = 95,
        -- heap
        field2 = 0,
    }
    dat.field2 = node.heap()
    clientid = "ClientIvan"
    broker = "mqtt.thingspeak.com"
    topic = "channels/"..ID.."/publish/"..key
    data = ""
    m = mqtt.Client(clientid, 180, "user", "password")

    function pub()
        local dt = ""
        for k,v in pairs(dat) do
            dt = dt..k.."="..v.."&"
        end

        dt = dt.."status=MQTTPUBLISH"
        -- local m = mqtt.Client(clientid, 120, "user", "password") -- сделал глобальный объект

        m:on("connect", function(con) print ("connected") end)

        m:connect(broker, 1883, 0, function(conn)
            print("connected")
            m:publish(topic,dt,0,0,
                function(conn)
                dt = nil -- вот оно! Ручное удаление локальной переменной.
                m:close() -- thingspeak закрывает соединение  после приема информации,
                -- нет смысла держать соединение постоянно
                print("sent")
                collectgarbage()
            end)
        end)
    end

    function sts()
        dat.field1 = tmr.now() %100
        dat.field2 = node.heap()
        pub()
    end

    print(node.heap())
    sts()
    tmr.alarm(0, 20000, 1, function()
        local heapNow = node.heap()
        print(dat.field2.." - "..heapNow.." = "..(dat.field2 - heapNow) )
        dat.field2 = heapNow
        sts()
    end)
    И результат его работы:
    leak4.jpg

    UPD 23.12.2016.

    Исправил, а скорее - уточнил, с учетом сказанного ранее модули и перезалил их. Теперь MQTT работает так:
    leak5.jpg

    А http модуль работает вообще удивительно:
    leak6.jpg

    То есть - утечка памяти прекращена. Что же для этого было необходимо в данном случае? Присвоить значение nil всем локальным (неглобальным, upvalues) переменным перед вызовом callback функции, которая выгружает модули.
    Все просто.

    Полагаю, этот опус будет кому-то полезен.
     

    Вложения:

    Последнее редактирование: 30 дек 2016
    alp69 нравится это.
  20. ИгорьК

    ИгорьК Гуру

    Продолжим развлечения.
    Цель - загружать и выгружать модули без слива памяти. Задача - в одном скрипте отправить данные на ThingSpeak и на OpenHab.

    Решаем путем загрузки и выгрузки трех модулей: датчика температуры, отправки на ThingSpeak и отправки на OpenHab.
    Код (Lua):
    key = "ВАШ_КЛЮЧ"
    ID = "ВАШ_ID"
    -- to ThingSpeak
    data = {
        -- temperature
        field1 = 95,
        -- heap
        field2 = 100000
    }
    -- temperature from ds18b20
    a = {}
    -- выгрузка модуля ThingSpeak
    function callTS()
        MQTTsendTS = nil
        package.loaded["tsmqttmod"]=nil
        collectgarbage()
        sMe()
    end

    -- загрузка модуля ThingSpeak
    function sts()
        MQTTsendTS = require('tsmqttmod')
        data.field1 = a[1]
        data.field2 = node.heap()
        MQTTsendTS.pub(key, ID, data, callTS)
    end
    -- выгрузка модуля OpenHab
    function callMe()
        MQTTsendMe = nil
        package.loaded["mymqttmod"]=nil
        collectgarbage()
    end
    -- загрузка модуля OpenHab
    function sMe()
        MQTTsendMe = require('mymqttmod')
        MQTTsendMe.pub("testq/temper", a[1], callMe)
    end

    function gettemp()
        ds=require"ds18b20mod"
        ds.setup(4) -- нога датчика
        -- через 750 мс читаем данные
        function get()
            tmr.alarm(0, 750, 0, function()
            a = ds.readResult()
            print(a[1])
            ds = nil
            -- выгружаем модуль ds18b20
            package.loaded["ds18b20mod"]=nil
            collectgarbage()
            print(node.heap())
            sts()
            end)
        end
        -- библиотечная функция читает адреса датчиков
        -- и вызывает библиотечную же функцию отправки команды на
        -- вычисление температуры, а также отправляет callback функцию
        -- на запуск функции get после опроса датчиков
        ds.addrs(get)
    end

    gettemp()
    tmr.alarm(1,30000,1,function() gettemp() end)
    Переделал модуль датчика DS18b20. Суть переделки
    • заточить под выгрузку в callback функции,
    • обеспечить формирование таблицы значений, если на шине больше одного датчика. Всегда возвращается таблица со значениями, даже если датчик один.
    • обеспечить возможность раздельного вызова задач а) команды датчику на измерение и б) чтения температуры (датчику требуется 750 мс между этими командами) через callback.

    Это работает, память не течет, но это не готовый проект, а шаблон.
    Результат:
    8882.jpg
    8881.jpg

    А так прочитать все датчики DS18b20:
    Код (Lua):
    do
        a = {} -- склад температур
        function gettemp()
            ds=require"ds18b20mod" -- грузим модуль
            ds.setup(4) -- устанавливаем ногу где датчики
            function get() -- здесь чтение значений и выгрузка модуля
                tmr.alarm(0, 750, 0, function() -- ждем 750 мс для чтения датчиком
                    a = ds.readResult() -- чтение
                    for k in pairs(a) do -- печать таблицы значений
                        print(k..": "..a[k])
                    end
                    ds = nil -- выгрузка модуля
                    package.loaded["ds18b20mod"]=nil
                    collectgarbage()
                end)
            end
            -- ds.addrs(get) делает следующее
            -- читает все адреса
            -- возвращает количество  датчиков
            -- вызывает функцию выдачи задания датчику на преобразование температуры
            -- передает ей callback, в данном случае - get
            print("No:",ds.addrs(get))
        end
        gettemp() -- поехали
    end
     

    Вложения:

    • ds18b20mod.zip
      Размер файла:
      658 байт
      Просмотров:
      249
    • mymqttmod.zip
      Размер файла:
      362 байт
      Просмотров:
      216
    • tsmqttmod.zip
      Размер файла:
      469 байт
      Просмотров:
      250
    Последнее редактирование: 25 дек 2016
    alp69 нравится это.