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

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

  1. virtual

    virtual Нуб

    Делаю вывод, что для любого более-менее серьёзного проекта, этот http сервер бесполезен.
     
  2. ИгорьК

    ИгорьК Гуру

    Выводы можно делать любые, но каждое железо имеет свои ограничения.
    Если обновление прошивки по воздуху есть суть явления - берите Arduino Yun или Малину или еще чтото.
    ESP-8266 есть дешевое китайское железо и слова "сeрьезный проект" в отношении него звучат слишком громко.
    Кроме того, многое зависит от мастерства программирования. За удобство Lua расплачиваемся памятью. Мало - переходите на Си. На сях тоже не влезает? Работайте с машинным кодом.
    В общем, это, как всегда, вопрос не железа, а того, кто им управляет.
     
    Последнее редактирование: 14 мар 2018
  3. ИгорьК

    ИгорьК Гуру

    Немного о coroutine.

    Вопрос встречается не часто, но может быть полезен.
    Напишем и запустим на исполнение код:

    Код (Lua):
    do
    print('\n')
    co = coroutine.create(function (a, b)
        local u = a/b -- Аргументы будут текстовыми!
        local z = nil * nol / nil -- Бред полный!
        print('z = '..z) -- И здесь бред - соединять текст и nil низзя! Ашипка!
    end)
    coroutine.resume(co, 'Raz', 'Dva') -- вызываем функцию
    print('is good')
    end
    Полный бред, не так ли?
    А если запустить скрипт на исполнение?
    upload_2018-3-14_18-47-7.png
    Фигасе! Никаких отказов в исполнении. Бред внутри функции тихо слился и опечаталсь фраза "хорошооооо".

    ...
     
    Последнее редактирование: 10 июл 2018
  4. b707

    b707 Гуру

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

    Давно сюда не заглядывал...
    Смотрю, вы лезете все глубже и глубже. С таким упорством, взялись бы вы за Си -С++ ....может тогда он перестал бы вам казаться чужим и неудобным. А применений у него куда больше, чем у Луа :)
     
  5. ИгорьК

    ИгорьК Гуру

    Напишу, конечно. Просто времени нет пока.

    Это верно. Но кто тогда о Луа писать будет.
     
    alp69 и b707 нравится это.
  6. igor777ivan

    igor777ivan Нерд

    Подскажите, пожалуйста, как можно организовать в ESP8266 что-то наподобие индикации LAN порта на роутере. Например, использовать встроенный синий светодиод ESP8266 или прицепить внешний на свободный GPIO и если он мигает – идет обмен данными, если просто горит - связь установлена, не горит - связи нет. А то иногда в условиях помех (когда вокруг работает много WIFI устройств) ESP притормаживает и ломаешь голову - есть связь или временно нет ее, а так можно было бы в динамичном режиме отслеживать связь ESP c роутером.
     
  7. ИгорьК

    ИгорьК Гуру

    Есть функция wifi.sta.status() , которая возвращает состояние модуля. Вызывайте ее по таймеру и жгите дополнительный светодиод в зависимости от ответа.
    Тот что на плате можно применить тоже, но здесь придется думать над кодом - он связан с ногой UART
     
  8. igor777ivan

    igor777ivan Нерд

    Игорь, спасибо за ответ! По поводу UART я в курсе, c wifi.sta.status() , буду пробовать. Еще раз Спасибо!
     
  9. ИгорьК

    ИгорьК Гуру

  10. glaviznin

    glaviznin Нерд

    Где бы ещё пример почитать как закидывать на этот народный монитор.
    Планирую 10 метеостанций сделать, с возможностью просматра через интернет.
    не ткнете примером?
     
  11. ИгорьК

    ИгорьК Гуру

    Мне как-то даже не удобно предлагать воспользоваться поиском по сайту, у меня здесь три темы про народный мониторинг. И еще есть не мои.
     
    Последнее редактирование: 21 мар 2018
    Arduino_man нравится это.
  12. glaviznin

    glaviznin Нерд

    Почему же неудобно. Очень даже удобно.:)
    Я может как то не так ищу, но прям явных статей не нашел.
    Кстати взгляд со стороны, в некоторых постах Вы отвечаете ссылаясь на какие то темы, но ссылки не выкладываете. Как потом их искать вообще не понятно.
    По поводу цикла статей. Респект, все понятно.
    Но как руководитель отдела автоматизации, могу дать направление мыслей для статей.
    Как правило если что то описывается как извлекать, то предварительно пишется как туда эти данные вносятся. Другими словами прежде чем получить отчет из БД, её еще с начало надо наполнить.
    Вот я и подумал, было неплохо сделать статью 15.0, о том как записывать данные на народный монитор.
     
  13. glaviznin

    glaviznin Нерд

    Почему же неудобно. Очень даже удобно. :)
    Я может как то не так ищу, но прям явных статей не нашел.
    Кстати взгляд со стороны, в некоторых постах Вы отвечаете ссылаясь на какие то темы, но ссылки не выкладываете. Как потом их искать вообще не понятно.
    По поводу цикла статей. Респект, все понятно.
    Но как руководитель отдела автоматизации, могу дать направление мыслей
     
  14. ИгорьК

    ИгорьК Гуру

    http://forum.amperka.ru/threads/esp8266-отправка-температуры-на-Народный-мониторинг-Бюджетный-вариант.4568/

    Эта тема не об изготовлении устройств по хотелкам юзеров а об азах программировения на Lua.

    О нродном мониторинге у меня здесь 3 темы точно и еще модуль отправки в теме об OpenHab. Поищите.
     
    glaviznin нравится это.
  15. glaviznin

    glaviznin Нерд

    Художнику конечно видней.:) но раз всё в одном месте, почему бы и об этом не написать, хотя бы в виде ссылок на те же посты.
     
  16. alp69

    alp69 Форумчанин

    А потом еще о ссылках на ссылки?:D
    Я думаю, что Игорь сам решит, о чем и где ему писать.
     
  17. ИгорьК

    ИгорьК Гуру

    Анонимные функции и callback. Таймеры.
    Lua (как и JavaScript) имеет в своем арсенале анонимные функции.
    В целом - это ни о чем! (А не что-то волшебное, как кажется вновь прибывшим).

    Давайте разбираться. Callback -это функция, которая передается другой функции для вызова.
    Делается это, большей частью, тогда, когда функция, принимающая callback, асинхронная. Она отработает свое и вызовет callback в завершение - обычно передавая ей результаты своей деятельности.

    Вот пример, что был ранее (хотя синяя функция не асинхронная, это всего лишь пример):

    [​IMG]


    Перепишем чуть-чуть:

    Код (Lua):
    do
    function getrnd(call)
        local sum = 0
        for i = 1, 5000 do
            sum = sum + math.random()
        end
        call(sum)
    end
    getrnd(function(data) print(string.format("%.02f", data)) end)
    end
    upload_2018-4-16_17-23-23.png

    Фигасе! А сколько разговоров...

    В общем (в основном), в тех случаях, когда надо сделать что-то один раз делать вызов из одного места , можно не грузить модуль созданием имени функции, а пихнуть ее куда надо прямо записав целиком, без названия, анонимно, так сказать.
    Вы будете сталкиваться с этим часто... и в конце концов привыкните :)
    Сначала это кажется диким, а потом действительно: если можно обойтись без лишнего имени - зачем его создавать?

    ==============
    Если нет настроения - пропустите этот блок и читайте про таймер .
    Или еще пример. Идиотизма. Чтобы было тупо и непонятно.

    Код (Lua):
    do
    function getrnd(maxToSum)
        local sum = 0
        for i = 1, maxToSum do
            sum = sum + math.random()
        end
        return (function() return string.format("%.02f", sum) end)()
    end
    print(getrnd(55))
    end
    upload_2018-4-16_16-58-16.png

    Этот код делает все то же самое, но с закосом под крутизну (а знаете кто так любит делать? не, не скажу - опять камнями будете кидаться) - в аргументе передается максимальное число вызовов math.random.

    А теперь "раскрутим" то что закручено выше:

    Код (Lua):
    do
    function callbk(data)
        print(string.format("%.02f", data))
    end

    function getrnd(maxToSum, call)
        local sum = 0
        for i = 1, maxToSum do
            sum = sum + math.random()
        end
        call(sum)
    end
    getrnd(55, callbk)
    end
    Опять все несложно.
    ==============


    И, наконец, самый простой, часто встречающийся и очень важный пример - таймер.

    Первое что надо учитывать, код, вызываемый таймером, становится асинхронным.
    Это значит что если после функции запуска таймера есть следующий код - он будет выполняться дальше, а когда наступит время срабатывания таймера - он исполнит код "параллельно" с тем, что исполняется в этот момент.


    Таймер создается вызовом:
    Код (Lua):
    mytimer = tmr.create()
    Дальше надо определить, чем будет заниматься таймер:
    Код (Lua):
    mytimer:register(5000, tmr.ALARM_SINGLE, function(t) print("Я таймер!") end)
    Определив таймер, его в подходящий момент надо запустить:
    Код (Lua):
    tmr.start(mytimer)
    Также, таймер можно остановить
    Код (C++):
    tmr.stop(mytimer)
    Можно поменять интервал:
    Код (C++):
    mytimer:interval(3000)
    Вглядитесь, "function(t) print("Я таймер!") end" - анонимная функция.
    А можно изменить все это построение на callback:

    Код (Lua):
    mytimer = tmr.create()
    function call(t)
        print("Я таймер!")
    end
    mytimer:register(5000, tmr.ALARM_SINGLE, call)
    tmr.start(mytimer)
     
    ИМХО, все логично. Создали таймер, определили что он делает и запустили на выполнение.

    А теперь начнем таймер упаковывать.
    В первую очередь, применим функцию "alarm". Это просто register + start. Функция одновременно регистрирует что должен делать таймер и запускает его.

    Код (Lua):
    mytimer = tmr.create()
    function call(t)
        print("Я таймер!")
    end
    mytimer:alarm(5000, tmr.ALARM_SINGLE, call)
     
    Следующим шагом в упаковке будет (уже сформировавшаяся привычка) не давать названий тем явлениям, что не будут управляться. Например, если всегда-всегда, вне зависимости ни от чего некоторый таймер будет проверять что-то, то можно его не называть, а сделать так:

    Код (Lua):

    function call(t)
        print("Я таймер!")
    end
    tmr.create():alarm(5000, tmr.ALARM_AUTO, call)
     
    А если заменить callback на анонимную функцию, то будет одной строкой:

    Код (Lua):

    tmr.create():alarm(5000, tmr.ALARM_AUTO, function(t) print("Я таймер!") end)
     
    upload_2018-4-18_14-23-11.png

    Вроде все? Нет!
    Не заметили там в аргументе исполняемой функции буковку t? Что за хрень?

    upload_2018-4-18_14-27-7.png

    Предлагаю запустить код и поразмышлять над его результатами:
    Код (Lua):
    mytimer = tmr.create()
    mytimer:register(1000, tmr.ALARM_SINGLE, function(t, d)
        print(t)
        print(d)
    end)
    mytimer:start()
    print(mytimer)
    upload_2018-4-18_14-30-32.png

    Взгляд ваш, наполненный мудростью, должен заметить, что результаты напечатания на экране двух функций print - одинаковы а одна дала nil. (Не забудьте порассуждать, также, почему стрелки отражают вывод именно в таком порядке - это не ошибка).
    О чем это говорит? О том, что callback/анонимная функция вызывается таймером с получением от него единственного аргумента - ссылки на сам таймер.

    Зачем это нужно и как это можно применить? Ну, например, так. Ждем мы наступление некоторого события, что-то проверяя таймером. Событие наступило, вызвана функция по его наступлению и ждать нам больше не чего. Таймер больше не нужен.

    Если идти нормальной, человеческой дорогой, то следует вызвать такую последовательность. Предположим, работал таймер mytimer.

    Код (C++):
    tmr.stop(mytimer) -- останавливаем
    tmr.unregister(mytimer) -- удаляем регистрацию того, чем он занимался
    mytimer = nil -- удаляем сам таймер
     
    А если мы это плющили и не называли таймер никак? Все просто - внутри функции всегда присутствует ссылка на этот таймер. Предположим, мы ожидали после старта в файле init.lua поднятия wifi чтобы продолжить исполнение программы:

    Код (Lua):
    count = 1
    getConnect = function(t)
        if wifi.sta.status() == 5 then
            tmr.stop(t) -- останавливаем
            tmr.unregister(t) -- удаляем регистрацию того, чем он занимался
            t = nil -- удаляем сам таймер
            print('WiFi нашелся!')
            -- dofile('main.lua') -- выполняем следующий файл
        else
            print("Ждем WiFi "..(count*5).." секунд")
            count = count + 1
            if count > 20 then node.restart() end -- рестарт модуля, нет wifi
        end
    end
    tmr.create():alarm(5000, 1, getConnect)
    Бонус. Выполните, упоминавшийся здесь скрипт, с аргументом 100000,
    Код (Lua):
    do
    function getrnd(maxToSum)
        local sum = 0
        for i = 1, maxToSum do
            sum = sum + math.random()
        end
        return (function() return string.format("%.02f", sum) end)()
    end
    print(getrnd(100000))
    end
    Вам теперь понятно, почему устройства ESP-8266 с NodeMCU достаточно стабильны в работе?

    Вот, полагаю, рассказаны основные вещи, касаемые таймера. Остается пожелать вам что-нибудь хорошее и закончить этот вопрос. Что я и делаю: всего доброго :)
     
    Последнее редактирование: 9 янв 2021
    SmileOfFortune и alp69 нравится это.
  18. GrOV

    GrOV Нерд

    Начинающий..С удовольствием читаю тему.
    Прошу Вас , если найдёте время, привести пример построения с использованием tmr. "медленного" ШИМ(PWM) на LUA NodeMCU
    c периодом , скажем 5 -10 сек..
    Аппаратный шим имеет минимальный период 1 Гц.(если не ошибаюсь)
    А хотелось бы период единицы-десятки секунд.
    С уважением..GrOV.
     
  19. ИгорьК

    ИгорьК Гуру

    Попробуйте разобраться с этим скриптом и прокомментировать его для всех:

    Код (Lua):

    do
    next = 1
    period = 5000
    high = 1000
    now = high

    function pwm()
      next = next == 0 and 1 or 0
      print(next, period - now)
      now = now ~= high and high or (period - high)
      return tmr.create():alarm(now, 0, pwm)
    end
    pwm()
    end

    -- set new fill:
    -- high = 1500
     
    upload_2018-4-20_12-50-14.png
     
    Последнее редактирование: 20 апр 2018
  20. GrOV

    GrOV Нерд

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