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

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

  1. ИгорьК

    ИгорьК Победитель модулей

    Что такое MQTT.

    Активный участник этой темы снял потрясное видео о протоколе MQTT.
    Этот протокол, большей частью, и используется в Интернете Вещей.

    В общем, для реального пипла, получающего образование по фильмам и форумам, рекомендую:


    Не забывайте там лайкать, и писать в комментах, что я прислал :)

    А для тупняков, которые вообще не схватывают и русский не знают, даю ссылку, где на картинках все нарисовано.
    mqtt5.jpg
     
    Последнее редактирование: 23 авг 2017
  2. ИгорьК

    ИгорьК Победитель модулей

    Счетчик расхода воды. Пошагово.

    Сырье здесь.

    Вдохновившись видео из предыдущего поста и, благодаря ему, поняв все о мире IoT приступим к неспешному изготовлению счетчика расхода воды.
    Железо повторим из этого проекта.
    [​IMG]

    Или еще проще:

    water08.jpg


    1. Как зайти в сеть.
    Первоисточник здесь.
    Модуль ESP-8266 может работать с сетью в четырех режимах:
    1. как точка доступа
    2. как клиент
    3. как то и другое вместе
    4. никак не работать
    Для нас, древних и замшелых, интересен клиент.
    Чтобы зайти в сеть, следует, ясный пень,
    1. установить соответствующий режим
    2. загнать в память модуля реквизиты сети
    3. дать команду "фас"
    Все это можно выполнять программно каждый раз при включении модуля, но есть тонкость: если залить во флэш-память необходимые реквизиты - повторно это можно не делать.

    Удобнее всего в ESPlorer создать сниппет и заводить в сетку каждый вновь прибывший модуль однократно, после чего он сам будет восстанавливать соединение при включении и нам остается только ожидать приятных сюрпризов самостоятельного появления wifi соединения.

    Делаем сниппет:
    Код (Lua):
    wifi.setmode(wifi.STATION) -- установка режима
    wifi.sta.clearconfig() -- очистка от барахла
    local scfg={} -- таблица установок ржима
    scfg.auto = true -- входить и поддерживать сеть автоматически
    scfg.save = true -- запомнить эти установки во флэше
    scfg.ssid = 'AP_Home' -- название сетки
    scfg.pwd = 'superpassword'-- пароль сетки
    wifi.sta.config(scfg) -- конфигурируем сеть
    wifi.sta.connect() -- старт соединения
    -- через 15 секунд посмотрим что получилось:
    tmr.alarm(6,15000, tmr.ALARM_SINGLE, function() print('\n', wifi.sta.getip()) end)
    water01.jpg

    Итак, на модуль подали питание и ему требуется некоторое время, чтобы войти в сеть.
    Вход в сеть - событие. Раньше чем оно не наступит делать мы ничего не будем.

    Первый файл, который мы загрузим в ESP-8266 будет модуль, который и будет ждать соединения с сеткой getwifi.lua.
    Код (Lua):

    -- При загрузке любого модуля ему передается
    -- единственный параметр - его имя.
    -- Имя модуля ловится так:
    local modn = ...
    print("Got name", modn)
    -- Локальная таблица
    local M = {}
    -- Эта таблица состоит из одной функции
    -- которой передается callback - то
    -- что надо будет сделать после входа в сетку
    M.getwifi = function(call)
        -- есть ли сетка?
        local ip = (wifi.sta.getip())
        -- функция выполнит callback и
        -- выгрузит модуль  из памяти
        local killall = function()
           -- выполнение callback
           if call then call() end
           -- самоуничтожение модуля
           package.loaded[modn] = nil
        end

        -- если мы не в сетке
        if not ip then
            -- создаем СОБЫТИЕ - постоянный таймер на 5 секунд
            tmr.create():alarm(5000, 1, function(t)
                -- узнаем есть ли сетка
                ip = (wifi.sta.getip())
                print("ip now", ip)
                -- если сеть
                if ip then
                    -- останавливаем таймер
                    tmr.stop(t)
                    -- убиваем таймер
                    tmr.unregister(t)
                    -- убиваем переменную, что содержит таймер
                    t = nil
                    -- выполняем что надо
                    killall()
                end
            end)
            -- событие обработки соединения создано -
            -- начинаем само соединение
            wifi.sta.connect()
        -- ну а если в мы в сетке
        else
            -- выполняем что надо
            killall()
        end
    end
    return M

    Вызовем модуль вот такой процедурой:
    Код (Lua):
    do
    -- загрузка модуля
    wf = require("getwifi")
    -- готовим реакцию на событие: что будет, когда появится сеть? А вот что:
    -- callback функция, которая выполняется после входа в сеть
    local call = function()
        print("Yes! It Works!")
        -- убиваем переменную, что содержала модуль
        wf = nil
        print("Kill Module, wf = ", wf)
    end
    -- вызываем функцию из модуля и передаем ей callback
    wf.getwifi(call)
    end
    И увидим:
    1. загрузка модуля
    2. вставляем код вызова
    3. выделяем код, отправляем на исполнение клавишей Block
    4. видим результат

    water02.jpg
     
    Последнее редактирование: 25 авг 2017
    alp69 и SergeiL нравится это.
  3. ИгорьК

    ИгорьК Победитель модулей

    2. Как работать с герконом (он же кнопка)
    Еще один модуль - debm.lua

    Модуль работает с дребезгом и через 2,5 секунды после размыкания геркона на счетчике выполняет callback функцию, которую принимает при старте.

    Код (Lua):
    -- Генерим новые таблицы
    local function setnew()
        return {}
    end
    -- Таблица для экспорта
    local M = {}
    -- Одна функция в таблице
    -- принимает номер ноги и callback для исполнения
    M.set = function(pin, short)
    -- Устанавливаем ногу на прием
    gpio.mode(pin, gpio.INPUT, gpio.PULLUP)
    -- Добываем пустую таблицу для этой ноги
    local o = setnew ()
    -- Нога еще будет нужна для опроса
    o.buttonPin = pin
    -- Счетчик результато опроса ноги
    o.cicle = 0
    -- Словили нажатие или нет
    o.gotpress = false
    -- Склад callback(ов)
    o.doshort = short
    -- Состояние ноги для отлова
    o.catch = 0
    -- Функция работы с ногой
    o.startpin = function(self)
        -- Установка события - прерывание на обвал ноги в ноль
        -- Очищаем прерывание
        gpio.trig(self.buttonPin)
        -- Устанавливаем заново
        gpio.trig(self.buttonPin, "down",function (level)
            -- Первый ноль - отключаем дальнейшую реакцию на прерывания
            if self.gotpress == false then
                self.gotpress = true
                -- Функция, которая выполнится в результате срабатывания геркона
                local function exitnow(buf)
                    -- Таймер опроса остановлен, снят с регистрации и уничтожен
                    tmr.stop(buf)
                    tmr.unregister( buf)
                    buf = nil
                    -- Выполняем callback
                    self.doshort()
                    -- Устанавливаем в исходное таблицу
                    self.cicle, self.gotpress = 0, false
                end
                -- На первое прерывание устанавливаем таймер на 50 мс
                tmr.create():alarm(50, 1, function(buf)
                    -- Который читает ногу и считает результаты считывания
                    -- в зависимости от состояния o.catch, сначала ловит ноль,
                    -- а потом единицу - то есть геркон размкнулся
                    if gpio.read(self.buttonPin) == o.catch then
                        self.cicle = self.cicle + 1
                    end
                    -- Если 5 х 50 = 250 мс
                    if self.cicle >=5 then
                        -- если геркон еще разомкнут
                        if o.catch == 0 then
                            -- устанавливаем счетчик в минус 15 чтобы
                            -- сработка произошла через 20х50 = 2500 (2,5 секунды)
                            -- после азмыкания геркона
                            self.cicle = -15
                            -- меняем результат считывания на единицу
                            o.catch = 1
                        -- если геркон замкнулся
                        else
                            -- счетчик в ноль
                            self.cicle = 0
                            -- данные для отлова в ноль
                            o.catch = 0
                            -- выполняем callback
                            exitnow(buf)
                        end
                    end
                end)
            end
        end)
    end
    -- Поехали:
    return o:startpin()
    end
    return M

    Вызываем так:
    Код (Lua):
    do
    -- Склад данных о воде
    water = {
        cool = 0,
        hot = 0
    }
    -- вызов модуля
    deb = require("debm")
    -- callback для третей ноги
    function doshort3()
        water.cool = water.cool + 0.01
        print("Cool got "..water.cool.." m3.")
    end
    -- callback для четвертой ноги
    function doshort4()
        water.hot = water.hot + 0.01
        print("Hot got "..water.hot.." m3.")
    end
    -- определение callback(ов) и ног
    deb.set(3, doshort3)
    deb.set(4, doshort4)
    end
    Видим это:
    water03.jpg

    1. загружаем модуль
    2. вставляем код
    3. выделяем код и жмeм Block
    4. нажимаем на кнопку/геркон и наблюдаем

    Бонус. Вот здесь я для вас библу накатал, чтобы работать с кнопками по-полной.
     
    Последнее редактирование: 6 сен 2017
    alp69 и SergeiL нравится это.
  4. ИгорьК

    ИгорьК Победитель модулей

    3. Соединение с брокером MQTT.
    Соединение будем осуществлять файлом workmqtt.lua

    Код (Lua):
    -- Вспомогательные элементы, потом удалим
    if not water then water = {hot = 0, cool = 0} end
    if not mod then mod = {} end

    -- Глобальные переменные
    mtop = "" -- Топик от брокера
    mload = "" -- Данные в топике
    myClient = "water02" -- Имя клиента
    local pass = "superpass" -- Пароль

    -- Создаем MQTT объект
    m = mqtt.Client(myClient, 60, myClient, pass)
    -- При потере связи брокер выдаст сообщение
    m:lwt(myClient, "OFF", 0, 0)

    -- Событие - потеря связи
    m:on("offline", function(con)
        -- Закрыть соединение
        m:close()
        -- Сбросить флаг наличия связи с брокером
        mod.broker = false
    end)

    -- Событие - приход сообщения
    m:on("message", function(conn, topic, data)
        -- Выделяем чистый топик, без информации  об устройстве
        topic = string.gsub(topic, myClient.."/","")
        -- Делаем соообщение глобальным
        mtop = topic
        mload = data
        print(mtop, mload)
        -- Вызываем обработчик глобального сообщения
        -- dofile("analize_broker.lua")
    end)

    -- Выполняем соединение с брокером
    m:connect("iot.eclipse.org", 1883, 0, 0,
        function(con)
            print("Connected to Broker")
            -- Подписываемся на два топика - установки горячей и холодной воды
            m:subscribe({[myClient.."/cool"]=0,[myClient.."/hot"]=0}, function(conn)
                print("Subscribed.")
            end)
            -- Публикуем сообщение на брокере что мы в сети
            m:publish(myClient, "ON", 0,0)
            -- Устанавливаем флаг связи с брокером
            mod.broker = true
    end)
    Однако вызвать его "в лоб" нельзя: у нас есть wifi соединение или нет? Значит вызов должен быть осуществлен только в callback модуля getwifi.lua, что рассматривали ранее.

    Итак, как это вызывается:
    Код (Lua):
    do
    wf = require("getwifi")
    local call = function()
        print("Yes! It Works!")
        wf = nil
        print("Kill Module, wf = ", wf)
        -- Новая строчка:
        dofile("workmqtt.lua")
    end
    wf.getwifi(call)
    end
    И как выглядит:
    water008.jpg
    1. загружаем два файла
    2. набираем код выполнения
    3. выделяем код и нажимаем кнопку Block
    4. наблюдаем результат
    Кроме того, пора разобраться с таким явлением как MqttSpy. Это задание для самостоятельной работы.

    Запустите приложение, установите связь со своим брокером и подпишитесь на топик "water02/#"

    В результате надо получить вот такую картинку:

    water009.jpg
     
    Последнее редактирование: 23 авг 2017
    alp69, Securbond и SergeiL нравится это.
  5. ИгорьК

    ИгорьК Победитель модулей

    4. Логика программ и блок MQTT чуть глубже.

    Пора сказать несколько слов об организации программ.
    ESP-8266 интересное явление. Оперативки чуть, а флэша - сколько напаяешь.
    ВердуиноЯзык этому не учит, а вот Lua позволяет в ходе основного тела программы выполнять код прямо из флэша.

    Для этого действа используется (в основном) два приема.
    • dofile("ИМЯФАЙЛА.lua")
    • require("ИМЯФАЙЛА.lua")
    dofile - прямо выполняет код из файла, после чего все что там было объявлено локальным - удаляется из памяти автоматом.

    require - загружает код в память и делает доступными функции, которые в нем содержатся. require имеет возможность выгружать из памяти модули в "ручном режиме" и вычищать память.

    При внешнем различии, глубинной разницы между этими способами немного.
    Различие в том, что если вызываемому коду надо передать данные, в dofile это делается через глобальные переменные, а в результате действий require становятся доступными функции, которым можно передавать аргументы обычным порядком.

    В этом проекте мы видим оба способа вызовов программ из памяти модуля.

    ==========================================================

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

    Итак, для общения вещей из интернета нужны:
    1. два общающихся и
    2. брокер между ними.
    Любое конечное устройство может выступать и передатчиком информации и приемником и "оба рядом".
    Протокол легкий, и зарегламентирован по самые помидоры. А именно,
    • информация передается в виде пары: ТОПИК and ДАННЫЕ.
    • и то и другое представляет собой строки.
    За раз модуль передает одну пару.

    Есть три режима доставки пары в работе брокера (качество обслуживания): "0" - пнул и забыл, "2" - два раза перепроверил, и "1" - что-то между нулем и двойкой.

    Есть режим сохранения последнего сообщения на брокере. Смысл. Когда на брокер приходит сообщение, он передает его всем подписчикам, КОТОРЫЕ В ЭТОТ МОМЕНТ висят на связи с ним, после чего сообщение херится полностью.

    Если включен режим сохранения (включение передается в теле источника сообщения) - брокер помнит последнее сообщение источника и втыкает его каждому вновь появившемуся подписчику. Причем если подписчик "ушел", а потом - "вернулся", сообщение втыкается ему вновь.

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

    Чтобы "срубить" режим сохранения сообщения, источник должен отправить на брокер пару ТОПИК(как обычно) и ДАННЫЕ - пустое поле.

    В заключение, надо знать - есть "последняя воля погибшего" - lwt. При установлении связи с брокером клиент передает ему специальное сообщение, которое будет опубликовано всем подписчикам, если клиент "почит в бозе".

    Оно может и не передаваться, но делать его - хорошая практика. Вот смотрите, при установке связи с брокером мы можем передать в некоторый топик единожды, например, сообщение "ON". Подписчики, прочитав его, узнают что "пиво есть!".

    Если же одновременно будет создано сообщение в этот же топик как "последня воля" с данными "OFF" - те же клиенты узнают о потере связи. Это очень и очень удобно. В нашем мини проекте все так и реализовано.

    Теперь кусочки кода workmqtt.lua в картинках.

    water04.jpg

    water05.jpg
     
    Последнее редактирование: 23 авг 2017
    Securbond, SergeiL и alp69 нравится это.
  6. ИгорьК

    ИгорьК Победитель модулей

    5. Обработка управляющей информации.
    В файле workmqtt.lua есть закомментированная строка - dofile("analize_broker.lua")
    analize_broker.lua
    - реакция на данные, пришедшие от брокера нашему устройству.

    !!! не забудьте расскоментировать эту строку и перезагрузить файл workmqtt.lua в модуль.

    А что устройство может получить от брокера? Установочную информацию о текущем положении дел на расходомерах воды. Наше же устройство просто считает размыкания геркона и не знает, сколько воды израсходовано до наших злодейств.

    В общем, workmqtt.lua обеспечивает реакцию на событие - прием пары Топик/Данные от брокера, путем формирования двух глобальных переменных mtop и mload а затем вызывает обработчик.

    Вот его код.
    Код (Lua):
    -- служебное:
    if not water then water = {} end
    myClient = "water02"
    -- копируем обе глобальные переменные в локальные
    -- чем освобождаем глобальные для других возможных событий
    -- в нашем случае не обязательно, но это хорошая практика
    local top = mtop

    -- поскольку MQTT гоняет текстовую информацию, преобразуем
    -- ее в цифровую. Причем, если она "непреобразуемая" - заменяем нулем
    local data = tonumber(mload) or 0

    -- Если пришли нормальные цифры
    if data ~= 0 then
        -- вставляем их в таблицу текущих значений холодной и горячей воды
        if top == "cool" then
            water.cool =  data
            print("Set water.cool to "..water.cool)
        end
        if top == "hot" then
            water.hot = data
            print("Set water.hot to "..water.hot)
        end

        --[[
        Публикуем на брокер пустые данные в топики
        установки значений воды. Зачем? Вспоминаем, что
        так снимается флаг "retain" - сохранения данных для
        вновь появившихся подписчиков.
        --]]

        m:publish(myClient.."/"..top, "", 0, 1)
    end

    Теперь скрипт запуска будет выглядеть так:
    Код (Lua):
    do
    mod = {}
    water = {
        cool = 0,
        hot = 0,
    }

    deb = require("debm")
    function doshort3()
        water.cool = water.cool + 0.01
        print("Cool got "..water.cool.." m3.")
    end

    function doshort4()
        water.hot = water.hot + 0.01
        print("Hot got "..water.hot.." m3.")
    end
    deb.set(3, doshort3)
    deb.set(4, doshort4)

    function gotmqttnow()
        wf = require("getwifi")
        local call = function()
            dofile("workmqtt.lua")
            wf = nil
            print("wf = ", wf)
        end
        wf.getwifi(call)
    end
    gotmqttnow()
    end
    Ничего нового в нем нет, а вот работа с ESPLorer и MqttSpy доставит нам удовольствие.

    После запуска скрипта и соединения с брокером вручную установим начальные значения счетчиков воды.

    Понаблюдаем за реакцией устройства, понажимаем кнопочки...
    water06.jpg

    water07.jpg

    Мы видим, что устройство начинает оживать - можно установить начальные значения воды, а срабатывание геркона/кнопки их увеличивает, что и видно в консоли ESPLorer(a).

    Осталось наладить периодическую отправку текущих значений воды обратно брокеру и дело сделано...
     
    Последнее редактирование: 24 авг 2017
    alp69 нравится это.
  7. ИгорьК

    ИгорьК Победитель модулей

    6. Публикуем данные.
    Нынче изготовим два файла - публикации и сохранения данных.
    Для публикации - pubmqtt.lua
    Код (Lua):

    -- Если все соединено
    if mod.broker == true then
      -- Вспомогательное
      if not water then water = {} end
      if not sent then sent = {} end
      -- Сохраним данные, что будем отправлять
      dofile("savedata.lua")
      -- Локальная таблица - сюда скопируем данные для отправки
      local topub = {}
      -- Копируем данные
      for k,v in pairs(water) do
          -- для отправки
          table.insert(topub, {k,v})
          -- в таблицу отправленных данных
          sent[k] = v
      end
      -- Публикация
      local function punow()
          -- Если в таблице что-то есть
          if #topub ~= 0 then
              -- Изымаем из нее оди н элемент -  таблицу из двух элементов
              local tp = table.remove(topub)
              -- Формируем топик из первого элемента
              local top = "water02".."/"..tp[1].."/state"
              -- Данные - ворой элемент
              local dat = tp[2]
              -- Публикуем
              m:publish(top, dat, 0,0,
                  -- и в callack рекурсивно вызываем себя
                  function(cl)
                      punow()
              end)
          end
      end
      -- Первый запуск публикации
      punow()
    end
     
    Еще один файл - сохраняет последние публикуемые данные в память модуля.

    Зачем это нужно? Если произойдет сбой питания или перезагрузка модуля по какой-то причине, при старте мы восстановим последние данные.
    Файл savedata.lua:

    Код (Lua):
    do
    -- Открываем файл на запись,
    -- этот временный файл потом будет переименован
    -- в файл с названием "data.lua"

    lst = file.open("list.lua", "w")
    -- Если открыли
    if lst then
        -- Локальная переменная
        local line
        -- Читаем пару значений из таблицы сосотяния воды
        for k,v in pairs(water) do
            --Формируем строку типа "water.cool = 22.11"
            line = "water."..k.."="..v.."\n"
            -- Пишем строку в файл
            lst:write(line)
        end
    end
    -- Закрываем файл, удаляем ссылку на него
    lst:close(); lst = nil
    -- Удаляем прежний файл с данными
    file.remove("data.lua")
    -- Переименовываем файл
    file.rename("list.lua", "data.lua")
    end
     
    Последнее редактирование: 25 авг 2017
  8. ИгорьК

    ИгорьК Победитель модулей

    7. Объединяем все в одну программу.
    В одну программу объединит все файл main.lua.
    Что же он делает в целом?
    • Создает глобальные таблицы water и sent
    • Вызывает файл загрузки water сохраненными данными
    • Назначает функции на герконы
    • Запускает функцию соединения с wifi и брокером
    • Запускает таймер отправки данных о воде на брокер
    Практически все элементы этого скрипта мы рассмотрели ранее - там где говорилось о способах вызова того или иного скрипта, поэтому ничего сложного в этом вы не увидите.

    Вот он:
    Код (Lua):
    -- main.lua
    -- Глобальная таблица для поддержания информации
    -- о состоянии соединения.
    mod = {}
    -- Таблица данных счетчиков воды
    water = {
        cool = 0,
        hot = 0,
    }
    -- Таблица состояния последних переданнных данных
    sent = {
        cool = 0,
        hot = 0,
    }
    -- Загрузка таблицы счетчика сохраненными данными
    -- о последней отправке при включении питания
    dofile("data.lua")

    -- Вызов модуля обратки дребезга
    deb = require("debm")

    -- Две функции увеличения данных в таблице воды
    function doshort3()
        water.cool = water.cool + 0.01
        print("Cool got "..water.cool.." m3.")
    end

    function doshort4()
        water.hot = water.hot + 0.01
        print("Hot got "..water.hot.." m3.")
    end

    -- Установка обработчиков герконов
    deb.set(3, doshort3)
    deb.set(4, doshort4)

    -- Зпуск всех соединений
    function gotmqttnow()
        -- Загрузка модуля ожидания wifi
        wf = require("getwifi")
        -- Сallback на появление сети
        local call = function()
            -- Создание объекта MQTT, подписки, обработки установочных
            -- данных о воде
            dofile("workmqtt.lua")
            -- уничтожение переменной, содержавшей ссылку на модуль wifi,
            -- выгрузка этого модуля происходит в нем самом после появления
            -- сети
            wf = nil
            print("wf = ", wf)
        end
        wf.getwifi(call)
    end
    -- первый запуск после включения
    gotmqttnow()

    -- Таймер периодической отправки данных на брокер
    tmr.create():alarm(30000, 1,  function()
        -- Только если информация изменилась
        if (water.cool ~= sent.cool) or (water.hot ~= sent.hot) then
            dofile("pubwater.lua")
        else
            print("No changes!")
        end
    end)
     
    Последнее редактирование: 25 авг 2017
  9. ИгорьК

    ИгорьК Победитель модулей

    8. Заключение.
    В этом небольшом проекте я изложил основные подходы к написанию законченных решений на языке Lua для ESP-8266.

    Обобщим и подведем итог.
    Вот как выглядит работающее устройство:
    water09.jpg

    В проекте показаны основные моменты, которыми отличается программирование Lua от Си.

    Мы определяем необходимые нам события и готовим реакцию на них.

    Код в проектах исполняется асинхронно и нам нет причин думать о том, как "развести" между собой то или иное событие.

    События могут взаимодействовать между собой. Лучший способ связи - работа с таблицами. Одно событие пишет свой лог в таблицу, другое - читает его и что-то делает.

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

    Программы на Lua можно делать из кусочков-скриптов, что позволяет задействовать огромный ресурс памяти, при ограниченной оперативке. Замечу, что такого нет даже в Espruino/Iskra JS.

    Вызов кусочка-скрипта осуществляется через dofile("script.lua"). При необходимости для этого скрипта могут быть подготовлены данные в виде глобальных переменных, таблиц.
    Этот же скрипт может и оставлять за собой глобальные данные.

    Даже в этом случае есть возможность каким-либо очередным скриптом обработать эти данные и просто уничтожить, присвоив им значение nil. Такой подход очищает глобальную память.

    Вызывать постоянно функцию collectgarbage(), как пишут на форумах, не обязательно - система делает это самостоятельно.

    Если требуется постоянное "зависание" в памяти некоторых функций, можно делать модули.
    Применение модулей позволяет гибко передавать данные в их функции, в том числе и задействовать callback(и).

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

    Таким образом, программирование на Lua отличается от Си не другими управляющими конструкциями (они везде приблизительно одинаковые), а логикой, к которой сразу непросто привыкнуть.

    Lua имеет мощные инструменты работы с таблицами и парсинга строк без всяких библиотек, что в совокупности с асинхронным кодом и событийным подходом делает его привлекательным для разминки мозга и изготовления устройств домашней автоматизации.

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

    Удачи всем любителям Ардуино :)

    water10.jpg
     
    Последнее редактирование: 26 авг 2017
    Securbond и 9xA59kK нравится это.
  10. Securbond

    Securbond Гик

    Здравствуйте Игорь. Я так понимаю, что в конечном варианте вы переименовали модуль - pubmqtt.lua в pubwater.lua . ?
     
  11. ИгорьК

    ИгорьК Победитель модулей

    Видимо. Все файлы на bitbucket - посмотрите.