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

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

  1. ИгорьК

    ИгорьК Гуру

    В NodeMCU Lua распространены асинхронные функции. Они, будучи вызванными, начинают свою работу "в подполье", и, одновременно, передают управление следующему блоку кода, то есть, условно, работают параллельно программе.

    Это, с одной стороны, удобно: вызвал и забыл, с другой - существуют проблемы, если асинхронную функцию нужно (1.) вызывать несколько раз подряд или (2.) требуется получить от нее данные.

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

    Асинхронная функция выполняет что задумано, и после всего вызывает callback. Во многих случаях callback-функциям передается в качестве аргумента результат работы асинхронной функции.

    1. Если вам нужно вызвать несколько раз подряд асинхронную функцию, то NodeMCU, обычно не сопротивляется, а ставит в очередь все вызовы (нагружая при этом память). Это ваш случай.
    2. Если вам нужно получить данные от асинхронной функции, то есть два пути - передать ей callback-функцию для их обработки, либо сделать так, чтобы асинхронная функция выставляла свои данные в глобальную переменную. Затем вы периодически проверяете эту переменную. Но это плохой вариант, хотя иногда бывает.

    В нашем случае обращаемся к документации.

    upload_2024-1-30_9-39-58.png


    function(sent) callback function for sending string - то что нам нужно. Это та функция, что будет вызвана после отправки куска данных.

    Заметьте, function(sent) заключена в квадратные скобки - то есть является необязательным аргументом, во многих примерах она отсутствует. Мы ее увидели лишь понимая общие принципы.

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

    В принципе, вариантов здесь несколько. Callback может работать не рекурсивно, а вызывать некую другую функцию, которая организует получение данных из файла и вновь вызывает sck:send(), пока данные есть либо закрывает соединение, если данные кончились.
    Код (Lua):
    function getline()
        if file.open("pict.lua", "r") then
            local ln = file.readline()
            if ln then
                return ln
            else
                file.close()
                return nil
            end
        else
            return nil
        end
    end

    function sender(sck)
        local function send(sck)
            -- получаем кусок данных
            local data = getline()
            -- если есть данные - отправляем, иначе закрываем соединение
            if data then
                -- первый аргумент - данные, второй - рекурсивный вызов callback
                sck:send(data, send)
            else
              sck:close()
            end
        end
    end
    Это не готовое решение, а направление мысли. Я всегда проверяю в железе то что пишу, но сейчас нет такой возможности. Кончились ESP (ржунимагу), но вот так. (Да и реально чтобы проверить все до конца надо повторять ваш проект, что сейчас не возможно)

    Пожалуйста! Поймите логику происходящего! Тем где асинхронные функции циклы любого рода - табу (за редчайшим исключением)!

    ---------------------------

    Теперь возможная проблема. function(sent) - не совсем ясно из документации что передает в этот callback асинхронная функция. Скорее - это ссылка на сокет, который необходим для sck:send(...) Если это так, то все заработает. Если нет - придется делать нечто дополнительно. Например:

    Код (Lua):
    sck:send(data, function(snd) send(sck) end)
    Ну... или поищите готовые решения, не может их не быть. Раз уж взялись за это... :)
     
    Последнее редактирование: 31 янв 2024
  2. obuhanoe

    obuhanoe Гик

    Чтение page.lua содержащий сервер, repeat until - это как раз исключение?
     
  3. ИгорьК

    ИгорьК Гуру

    Ну вы же уже пробовали :) Не получается. Значит не ваш случай. :) Пишу, пишу...
    Таки не хотите въехать в callback? А придется. :)

    Чтение строки из файла - это не асинхронная задача, а вот отправка - асинхронная. Значит callback, только он.

    Можно еще сопрограммы задействовать, но это еще сложнее. Так что без вариантов - отправка через callback. Я же практически код вам дал.
     
    obuhanoe нравится это.
  4. obuhanoe

    obuhanoe Гик

    Приступил к изучению из данной темы.
    Спасибо.
     
    ИгорьК нравится это.
  5. ИгорьК

    ИгорьК Гуру

    upload_2024-2-1_7-58-28.png


    upload_2024-2-1_8-8-22.png
     
    Последнее редактирование: 1 фев 2024
  6. ИгорьК

    ИгорьК Гуру

    Еще вариант

    upload_2024-2-1_8-32-47.png
     
    Последнее редактирование: 1 фев 2024
    naz нравится это.
  7. obuhanoe

    obuhanoe Гик

    Игорь, разбираясь с callback с толкнулся (кстати не в первый раз) что вызов без reapeat/until:
    Код (Text):
    file.readline()
    читает так:
    1. если файле есть перенос строк (разделил на ~1000 байт каждую строку в файле) читает строго только первую строку
    2. если в файле все ~3500 байт в одну строку и каждые ~1000 байт я вставляю \n readline читает строго 1024 байт как в нотации.

    Реализация чтения сделана по Вашему примеру с callback.

    Почему может игнорироваться принудительный перенос строк или EOF '\n' ?

    Из-за того что не доходит до конца файла, происходит зацикливание.
    Может я не правильно расставляю EOF в файле?
    Пример файла ниже.

    Код (Lua):

    function dispatch()
       if file.open("lamp.lua", "r") then
         local line = file.readline()
         if line then
           print(line)
           senddata(sck, data)
         else
           file.close()
           sck.close()
         end
       end
    end

    function senddata(sck, data)
       sck:send(data, function() dispatch()  end)
    end

    dispatch()
     
    Код (Text):
    <img src=" data:image/png;base64,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">
     
    Последнее редактирование: 1 фев 2024
  8. ИгорьК

    ИгорьК Гуру

    Без кода, файла и всей картины здесь сложно сказать.

    В опровержение или доказательство своего тезиса сами напишите простой код с file.readline(), печатью прочитанного и вызов всего этого по таймеру. Посмотрите как все это работает.
     
  9. obuhanoe

    obuhanoe Гик

    Дополнил предыдущее сообщение.
     
  10. ИгорьК

    ИгорьК Гуру

    Код (C++):

    counter = 0
    function dispatch()
       if file.open("lamp.lua", "r") then
         local line = file.readline()
         if line then
           print(line)
           senddata(sck, data)
         else
           file.close()
           sck.close()
         end
       end
    end

    function senddata(sck, data)
       sck:send(data, function() counter = counter +1; print('Counter at callback:'..counter);  dispatch()  end)
    end

    dispatch()
     
    Посмотрите, срабатывает ли callback
     
  11. ИгорьК

    ИгорьК Гуру

    Если проблема в части чтения строки из файла - решайте ее через вызов dispatch() по таймеру. Надо добиться чтобы файл устойчиво открывался, читался частями и закрывался.
     
  12. obuhanoe

    obuhanoe Гик

    После измененения Counter at callback:54, пока не перегрузил так как выводит одна и та же строка, окончание на
    ...36LiVn27F
     
  13. ИгорьК

    ИгорьК Гуру

    Это же псевдокод...

    А сколько строк вы сделали?
     
  14. obuhanoe

    obuhanoe Гик

    тут ошибочка вышла, просто в Вашем коде не увидел объявление переменной
    пример файла был выше:
    1 строка на ~3200 байт со вставленным EOF \n каждые 1000 байт

    Если файл с переносом строк - то проблема остается.
    Не видит \n или перенос строк
     
  15. ИгорьК

    ИгорьК Гуру

    Ну вывод какой - проблема в формировании этого файла.

    Делайте простой код
    1. вызов диспетчера по таймеру.
    2. Вместо файла изображения сделайте что-то типа "abc\nqwe\nghdas\n"
    3. Проверьте, работает ли это с простым файлом.

    4. Верните файл изображения, побейте его на 10 кусков, посмотрите где "стопор".
    5. Часть где стопорится побейте еще на части.
    6. Найдите последовательность, которую не "переваривает" код. Поменяйте ее - в файле изображения один байт мало что значит.
     
  16. obuhanoe

    obuhanoe Гик

    Возник один вопрос: а как внутри dispatch file.readline знает что нужно взять следующую порцию данных, следующую строку.
    с repeat/until вроде ясно, файл целиком считывается и бегает по нему.
    А в моем случае получается каждый вызов dispatch приводит к тому что он читает первую строку, так как не знает что ранее первая строка была уже прочитана.
     
  17. ИгорьК

    ИгорьК Гуру

    Таки попробуйте сделать чтение не локальным, а глобальным : уберите "local". Получится?
    Не забудьте заранее объявить line глобально.
     
  18. ИгорьК

    ИгорьК Гуру

    Чет, в таком случае, меня наталкивает на мысль, что придется идти к корутинам... Но посмотрим.
     
  19. obuhanoe

    obuhanoe Гик

    без изменений к сожалению.
     
  20. obuhanoe

    obuhanoe Гик

    именно так и сделал.
    line = nil
    function dispatch()