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

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

  1. serg3295

    serg3295 Гик

    Код (C++):
    wifi.start()

    time.initntp("ntp1.stratum2.ru")
    print(time.ntpenabled())

    tt=time.epoch2cal(time.get() )
    print(string.format("%04d-%02d-%02d %02d:%02d:%02d DST:%d", tt["year"], tt["mon"], tt["day"], tt["hour"], tt["min"], tt["sec"], tt["dst"]))

    -- TZ MSK

    time.settimezone('EST-3')
    tl = time.getlocal()
    print(string.format("%04d-%02d-%02d %02d:%02d:%02d DST:%d", tl["year"], tl["mon"], tl["day"], tl["hour"], tl["min"], tl["sec"], tl["dst"]))
     
    Попробовал вручную посылать эти команды в есплорере. Всё нормально работает: 2022-07-10_13-11.png
     
    ИгорьК и obuhanoe нравится это.
  2. obuhanoe

    obuhanoe Гик

    Settimezone влияет на time.getlocal(), но не time.get()?
     
  3. serg3295

    serg3295 Гик

    Ну, если верить примеру из документации на сайте, то "no locale adjustment"
    2022-07-10_13-26.png
    А вот остальному в этом примере верить нельзя! ;)
    Потому как в первой же строчке модуль time меняется на таблицу со временем, и больше у вас этого модуля нет.
     
    obuhanoe и ИгорьК нравится это.
  4. ИгорьК

    ИгорьК Гуру

    Все таки надо уточнить.
    Верить можно, но автор по невнимательности переназначает глобальной переменной time ссылку с объекта работы со временем на таблицу, которую возвращает этот объект.

    Чтобы исправить, нужно заменить самый первый оператор time на что-то другое, tm, например. На это же tm заменить все time во второй строке.
    Это для начинающих, иначе им можно потеряться.
     
    arkadyf и serg3295 нравится это.
  5. obuhanoe

    obuhanoe Гик

    Интересную вещь обнаружил - Ваш код хорошо и правильно иллюстрирует работу.
    Но например мне удобно работать в своем проекте с time in the Unix epoch. Так как вот, значение с TZ хранится time.getlocal() - а это календарь, то чтобы получить вермя Unix epoch, нужно воспользоваться методом time.cal2epoch то данные отображаются без учета TZ
    Снимок.JPG
     
  6. serg3295

    serg3295 Гик

    Может просто прибавлять сдвиг каждый раз?
    Код (C++):
    -- TZ MSK
    TZ = 10800
    tm = time.epoch2cal(time.get() + TZ)
    print(string.format("%04d-%02d-%02d %02d:%02d:%02d DST:%d", tm["year"], tm["mon"], tm["day"], tm["hour"], tm["min"], tm["sec"], tm["dst"]))
     
    Если TZ в проекте меняется, то сделайте его таблицей.
     
    obuhanoe нравится это.
  7. obuhanoe

    obuhanoe Гик

    Да видимо так придется сделать.
    Странно что так работает и в описании об этом ничего нет.
     
  8. ИгорьК

    ИгорьК Гуру

    55. Проверка действительности данных по времени. Метатаблицы.

    Исходная посылка - есть часы на большой матрице WS2812, которые, ко всему прочему, показывают температуру на улице, выбирая наименьшую из нескольких. Датчики расположены в разных местах, одновременно хотя бы один находится в тени.

    Бывает, что какой-то датчик отваливается, но в "мозге" часов его данные остаются. Температура растет, а часы продолжают работать со старой минималкой. Логично проверять время последнего обновления датчика и уничтожать данные, если время превышено. Так, в лоб, в часах и сделано.

    Здесь и напрашивается универсальное решение через метатаблицы. А именно, до указанного промежутка времени запрос к таблице возвращает данные, а после - nil.

    Код, разъясненный до степени понятности мне самому:

    Код (Lua):
    do
        -- запускаем модуль времени. (В часах, конечно, идет синхронизация с NTP)
        rtctime.set(0)

        -- Прием таблицы для данных и интервала их действительности в секундах
        maketimecheck = function (t, sec)
            -- нет таблицы - изготовим
            if not t or type(t) ~= 'table' then t = {} end
            -- по умолчанию данные действительны 10 минут
            if not sec or type(sec) ~= 'number' then sec = 600 end

            -- создаем локальную копию таблицы данных
            local _t = t
            -- подменяем исходную таблицу новой
            t = {}
            -- изготавливаем метатаблицу
            local mt = {
                -- занесение данных (таблица, ключ, данные):
                __newindex = function (t, k, v)
                    -- получаем время
                    local tm = (rtctime.get())
                    print('Got Time:', tm)
                    -- к ключу создаем таблицу, первый элемент - данные, второй - время
                    _t[k] = {v, tm}
                end,
                -- доступ к существующему/несуществующему элементу
                __index = function(t, k)
                    -- проверяем интервал текущее время - время занесения данных,
                    -- и возвращаем результат в зависимости от ситуации
                    if not _t[k] then return nil end
                    if (rtctime.get()) - _t[k][2] < sec then
                        print('Good Time')
                        return _t[k][1]
                    else
                        print('Bad Time')
                         _t[k] = nil
                        return nil
                    end
                end,
            }
            setmetatable(t, mt)
            return t
        end
        -- создаем контролируемую таблицу,
        -- время контроля - 10 секунд
        dat = maketimecheck(_ , 10 )

        -- заполняем и читаем данные:
        dat.a = 22
        print(dat.a)

        -- проверяем те же данные через 15 секунд:
        tmr.create():alarm(15000, 1, function(t)
            t:stop()
            t = nil
            print('\n\nNow dat.a:', dat.a)
        end)
    end
    Коротко:
    Код (Lua):
    do
        rtctime.set(0)
        maketimecheck = function (t, sec)
            if not t or type(t) ~= 'table' then t = {} end
            if not sec or type(sec) ~= 'number' then sec = 600 end
            local _t = t
            t = {}
            local mt = {
                __newindex = function (t, k, v)
                    local tm = (rtctime.get())
                    _t[k] = {v, tm}
                end,
                __index = function(t, k)
                    if not _t[k] then return nil end
                    if (rtctime.get()) - _t[k][2] < sec then
                        return _t[k][1]
                    else
                         _t[k] = nil
                        return nil
                    end
                end,
            }
            setmetatable(t, mt)
            return t
        end
        dat = maketimecheck(_ , 10 )
        dat.a = 22
        print(dat.a)
    end
     
    Последнее редактирование: 25 июл 2022
    serg3295 нравится это.
  9. dmitrij2023

    dmitrij2023 Нуб

    Скажите, пожалуйста, что такое "luac.cross" и где это можно скачать? В инструкциях, что я нашел, всё предлагается делать из docker.
     
  10. ИгорьК

    ИгорьК Гуру

    dmitrij2023 нравится это.
  11. dmitrij2023

    dmitrij2023 Нуб

  12. alp69

    alp69 Форумчанин

    @ИгорьК, добрый день!
    Можно взглянуть на суть переделки? Если я правильно рассуждаю, то m:close() нужно переместить внутрь функции error()? И вызывать не перед m:connect, а колбеком из функции error() при наступлении события для ее (errоr) исполнения? И сбросить "reason"?
    Не так,
    Код (Lua):
    function connect_broker()
      m:close() -- закрываем соединение
      m:connect(broker, port,false, subscribe, error()
    ....
    )
     
    а так?
    Код (Lua):
    function connect_broker()
      m:connect(broker, port,false, subscribe,error()
      m:close() -- закрываем соединение
      reason = nil
    ....
    )
     
    Причем с задержкой не менее keepalive, потому, что "...клиент не может повторно использоваться сразу после этого вызова, а только после того, как сработает «автономный» обратный вызов." (гуглоперевод)
     
    Последнее редактирование: 18 сен 2022
  13. ИгорьК

    ИгорьК Гуру

    Привет :) Рад тебе.

    Сейчас нет, ибо не удобно код втыкать с телефона. Завтра, скорее. Но суть в том, чтобы не допустить двойного вызова повторного соединения.
    Калбэк может даже ничего не закрывать, а может и закрыть. Его вызов:
    1. проверяет наличие таймера повторного соединения.
    2.1. Нет таймера - создает таймер и в его колбэке вызывает соединение с брокером.
    2.2. Есть таймер - перезапускает его или вообще ничего не делает.

    И все.
     
  14. alp69

    alp69 Форумчанин

    Взаимно )))
    Спасибо!
     
  15. ИгорьК

    ИгорьК Гуру

    Код (Lua):
    -- Глобальные дела:
    dat = {}
    dat.broker = "192.168.100.100"
    dat.clnt = 'ampermeter8'
    dat.port = 1883
    dat.pass = 'superpasswd'


    do
        local subscribe, merror, msg, askcon
        function subscribe(con)
            dat.broker = true
            con:subscribe(dat.clnt.."/com/#", 0)
            con:publish(dat.clnt..'/state', "On", 0, 1)
            print("Subscribed to "..dat.clnt.."/com/# Heap: "..node.heap())
        end
     
        askcon = function()
            -----------------------------------------------------
            -- Вот и весь "секрет". Оно не пропустит второй вызов:
            if not contmr then -- Делаем ГЛОБАЛЬНЫЙ таймер:
                contmr = tmr.create()
                contmr:alarm(5000, tmr.ALARM_AUTO,  function(t)
                    if not dat.broker then
                        if (wifi.sta.getip()) then
                            m:connect(dat.brk, dat.port, false, subscribe, merror)
                        end
                    else
                        t:stop()
                        t:unregister()
                        t, contmr = nil, nil
                        print('con Timer Killed!')
                    end
                end)
            end
            collectgarbage()
        end
        function merror(con, reason)
            dat.broker = false
            print('MQTT Broker Error! Reason:', reason)
            do local ct = 0; for k,v in pairs(debug.getregistry()) do ct = ct + 1 end; print('Length = '..ct, 'Heap = '..node.heap()) end
            askcon()
        end
        function msg(con, top, dt)
            if not killtop then killtop = {} end
            top = string.match(top, "/(%w+)$")
            print('Got', top, dt)
            if top and dt then
                table.insert(killtop, {top, dt})
                if not dat.analiz then
                    dofile("mqttanalize.lua")
                end
            end
        end
        m = mqtt.Client(dat.clnt, 60, dat.clnt, dat.pass, 0, 1024)
        m:lwt(dat.clnt..'/state', "Off", 0, 1)
        m:on("offline", merror)
        m:on("connfail", merror)
        m:on("message", msg)
        print('Connect to', dat.brk, 'Heap', node.heap())
        askcon()
    end
    В приложении рабочий файл для ESP-8266.
     

    Вложения:

    • mqttset.zip
      Размер файла:
      1.012 байт
      Просмотров:
      1
    Последнее редактирование: 19 сен 2022
  16. dmitrij2023

    dmitrij2023 Нуб

    Здравствуйте. Расширяю количество портов esp8266, прошивка NodeMCU lua, собирал на сайте. С PCF8574 всё понятно и заработало сразу. А с PCF8575 не могу понять "как 2 байта переслать". Отправляю один байт - ничего не происходит, хотя возможно я как-то не так проверяю состояние ног, там открытый коллектор на выходе, но показания мультиметра не меняются. Выложите, пожалуйста, код на lua, если кто-нибудь с ним работал.