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

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

  1. DIYMan

    DIYMan Гуру

    Сделать две HTML-формы - религия не позволяет?
     
  2. Crazy_Volt

    Crazy_Volt Нуб

    Фантазируйте дальше
    Позволяет, но если есть метод все реализовать на одной странице, это будет лучше
     
  3. DIYMan

    DIYMan Гуру

    Открою СТРАШНУЮ тайну - тегов <FORM> на одной HTML-странице может быть много.

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

    Более того - даже если только одна форма (тег FORM) юзается - то можно динамически менять ей атрибут target.
     
  4. Crazy_Volt

    Crazy_Volt Нуб

    Согласен. Но если один <form>-это вся страница целиком, куда вставить второй <form>?
     
  5. DIYMan

    DIYMan Гуру

    Вы совсем не знаете основ HTML, видимо:
    HTML:
    <html>
        <head>
            <title>Stupid page</title>
        </head>
     
        <body>
            <form target="test.lua" method="get">
                <input type="submit" value="DO"/>
            </form>

            <form target="other.lua" method="get">
                <input type="submit" value="DO 2"/>
            </form>
         
        </body>

    </html>
    Где вы видите ограничение по кол-ву форм на одной странице? Почитайте основы HTML, плз.
     
  6. ИгорьК

    ИгорьК Давно здесь Команда форума

  7. Crazy_Volt

    Crazy_Volt Нуб

    А вот этой инфы я не находил... спасибо...пошел искать )
     
  8. Crazy_Volt

    Crazy_Volt Нуб

    это понятно, Я про тот случай, когда <form> идет от начала до конца <body>. Так у меня идет сохранение выставленных параметров.
     
  9. ИгорьК

    ИгорьК Давно здесь Команда форума

  10. ИгорьК

    ИгорьК Давно здесь Команда форума

    19. Датчик DS2438: полезная штука. Как работать с любым 1Wire устройством.

    Слышали о таком? Это контроллер батарейного питания и, одновременно, датчик температуры а также 40 байт EEPROM. Если питать что-то от батареек - может сильно помочь. Я собираюсь делать устройство, работающее от автомобильного аккумулятора, и, среди прочего, применить эту микросхему.
    Давайте напишем для нее модуль.

    Микросхема DS2438 умеет измерять напряжение собственного питания и имеет дополнительную ногу - вход тоже для измерения напряжения.

    upload_2018-2-28_11-2-58.png

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

    Это так, но у ESP-8266 только один аналоговый вход, и он может быть нужен для других измерений. И здесь все равно придется что-то придумывать. Например, можно воспользоваться инструкцией от товарища Пушной звер:


    Вещица хорошая. Но DS2438 имеет некоторые фичи:
    - протокол позволяет экономить целую ногу, коих у ESP-8266 немного,
    - расстояние от МК, в силу протокола, может очень большим, в результате чего некоторые товарищи делают на ней высокопроизводительные датчики температура/влажность с участием семейства hihххх от Ханейвел (я бы точно так сделал для проветривателя подвала, если бы знал в тот момент об этом решении):
    [​IMG]
    - (высока вероятность) эта микросхема существенно экономичнее чем указанная выше.

    Читаем даташит.
    Понимаю, что поколение программистов, подготовленных школой Амперка на платформе Iskra JS, в принципе не знает о таком явлении, и, надеюсь, открою для кого-то новые вещи.

    Итак, чтение даташита и иной запрещенной документации позволит нам узнать, что общение по шине 1-Wire происходит только по инициативе МК, путем выдачи в линию последовательностей команд, среди которых может вклиниваться и чтение данных от датчика.
    Не будем вдаваться в детали.
    Во-первых, нам нужен адрес устройства на шине.
    Получим его (практически :) ) копипастой из существующего у NodeMCU примера:
    Код (Lua):
    do
        addr2438 = "" -- адрес DS2438
        addrTb = {} -- общая таблица адресов
        pin = 4  -- нога One Wire

        -- Ищем устройства на линии
        local search = function()
            -- так положено
            ow.setup(pin)
            ow.reset_search(pin)
            local adr -- текущий адрес найденного устройства
            -- Крутим шарманку
            repeat
                adr = ow.search(pin)
                if(adr ~= nil) then
                    table.insert(addrTb, adr) -- вставляем очередной адрес в таблицу
                    -- А если это именно DS2438 - выписываем адрес в самостоятельную переменную
                    if adr:byte(1) == 0x26 then addr2438 = adr end
                end
            until (adr == nil)
            -- Так положено после поиска
            ow.reset_search(pin)
            -- Итого найдено железок:
            print('Got Devices:', #addrTb)

            -- Напечатаем адрес каждой железки.
            -- Выбираем последовательно значения таблицы адресов
            for _, v in pairs(addrTb) do
                local as = ""
                for ii = 1, #v do
                    -- Превращаем каждую "букву" в ее байт-код и форматируем в шестнадцатеричном исчислении
                    local hx = string.format("%02X", (string.byte(v, ii)))
                    -- и  соединяем в единое целое
                    as = as ..hx
                end
                print(as)
            end
        end
        search()
    end
    Вот что обнаружено на шине:
    upload_2018-2-27_16-3-51.png

    Тот что начинается на 0х26 и есть DS2438, а также обнаружили парочку DS18b20.

    Теперь о порядке злодействий с датчиком.
    Первой важной информацией для нас будет таблица 13:
    upload_2018-2-27_16-41-49.png
    В ней рассказана последовательность действий с целью добычи информации о температуре и напряжении. В левой колонке что должен делать ESP-8266 - передавать или принимать.
    Причем, для парочки TX/Reset - RX/Presence у Lua есть всего одна функция:
    Код (Lua):
    ow.reset()
    Она и возвращает информацию о наличии или отсутствии железок (хотя бы одной) на линии.
    Дальше в таблице есть такая команда:
    TX/CCh, что значит "Пропустить ROM", то есть "всем кто на линии - выполнять дальнейшие инструкции".
    Мы не будем ее применять - на линии может присутствовать много разных устройств.
    Вместо нее применяем обращение к конкретной железке:
    Код (Lua):
    ow.select(pin, address) -- нога и адрес устройства
    Вроде, пора уже написать одну красивую функцию по добыче данных! А вот и нет!
    То что в таблице указано как последовательность команд на деле работать не будет ни разу, потому что (читали даташит, да?) между отправкой команд на измерение температуры и измерение напряжения ОБЯЗАНО пройти время, а именно - на температуру требуется не менее 10 мс, на напряжение - 4.

    Ясный перец - втыкай delay(10) и будет сачтье, но это не наш метод! В асинхроном программировании любые тормоза процессора - табу.

    Поэтому придется разрабатывать отдельные функции, а потом как-то ими управлять.
    В целом, кроме функции поиска нам нужны будут еще команды:
    1. на измерение температуры
    2. на измерение напряжения
    3. на перенос данных из ПЗУ (куда пишутся результаты) в (типа) ОЗУ
    4. на чтение дпнных из ОЗУ
    А кроме того, лишними не будут еще команды:
    1. на перенос данных из ОЗУ в ПЗУ - там можно запоминать источник измерения напряжения: питание DS2438 или специальная нога
    2. на установление этого самого источника чтения
    Ну, ничего... Не страшно все. Кстати, посмотрите библиотеку для Ардуино. Сначала я пытался переложить ее на ноты Lua, а потом забил и написал свою. Над(у)еюсь, она будет проще и понятнее, а также без ошибок и косяков этой библиотеки. К ним мы, возможно, вернемся.

    Итак, создадим таблицу, куда выпишем все заявленные выше функции, воспользовавшись информацией:
    upload_2018-2-27_17-34-14.png

    Код (Lua):
    local M = {}
    M.addr = ""
    M.addrTb = {}
    M.pin = 4
    M.dat = ""
    -----

    -- Это уже было
    M.search = function()
        ow.setup(M.pin)
        ow.reset_search(M.pin)
        local adr
        repeat
            print("Search")
            adr = ow.search(M.pin)
            if(adr ~= nil) then
                table.insert(M.addrTb, adr)
                M.addr = adr
            end
        until (adr == nil)
        ow.reset_search(M.pin)
        print('Adr No:', #M.addrTb)
        for _, v in pairs(M.addrTb) do
            local as = ""
            for ii = 1, #v do
                local hx = string.format("%02X", (string.byte(v, ii)))
                as = as ..hx
            end
        print(as)
        end
    end

    -- Все что ниже - из даташита
    -- Читать температуру
    M.starttemp = function()
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0x44, 1)
        ow.reset(M.pin)
    end

    -- Читать напряжение
    M.startVolt = function()
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0xb4, 1)
        ow.reset(M.pin)
    end

    -- С какой ноги читать вольты
    M.setVAD = function(inp)
        local dt = inp == 0 and 0 or 0x08
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0x4e, 1)
        ow.write(M.pin, 0x00, 1)
        ow.write(M.pin, dt, 1)
        ow.reset(M.pin)
    end

    -- Добыть измерения из "ПЗУ"
    M.recallMem = function()
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0xb8, 1)
        ow.write(M.pin, 0x00, 1)
        ow.reset(M.pin)
    end

    -- Прочитать данные в переменную
    M.readPage = function()
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin,0xBE,1)
        ow.write(M.pin,0x00,1)
        M.dat = ow.read_bytes(M.pin, 9)
        print(M.dat:byte(1,9))
        ow.reset(M.pin)
    end

    return M
     
    Последнее редактирование: 28 фев 2018
    alp69 и SergeiL нравится это.
  11. ИгорьК

    ИгорьК Давно здесь Команда форума

    Получился (недо)модуль _ds2438.lua , который уже можно применять в ручном режиме, так:
    Код (Lua):
    ds = require('_ds2438')
    ds.search() -- ищем адреса
    ds.setVAD(1) -- устанавливаем чтение напряжения питания
    -- ds.setVAD(0) -- устанавливаем чтение напряжения с ноги
    ds.starttemp() -- измеряем температуру
    ds.startVolt() -- измеряем напряжение
    ds.recallMem() -- получаем данные из ПЗУ
    ds.readPage() -- читаем 9 байт
    Каждую команду следует отдавать руками - помним, что необходимо небольшое время на обработку предыдущей.
    Кстати, привыкайте к такой работе - где это в Ардуино вы работаете в режиме реального времени.

    Вот что увидим:
    upload_2018-2-27_17-46-9.png

    Информация о температуре и напряжении находится у нас в 2-5 прочитанных байтах:
    upload_2018-2-27_17-50-19.png

    А прочитали мы из модуля 9 байт. Зачем?
    А вот какая фигня: если прочитать 8 байтов со страницы 0х00, то девятым придет контрольная сумма первых восьми. И будет она верной если успешно проведено преобразование & данные приняты правильно.

    Поэтому, сделаем еще одну функцию - проверку CRC. Она есть в документации NodeMCU и, следовательно, будет выглядеть так:
    Код (Lua):
    M.checkCRC = function()
        -- Хватаем M.dat, где лежат принятые данные и
        -- считаем crc8 для 1 - 8 байтов
        local crc = ow.crc8(string.sub(M.dat,1,8))
        -- сравниваем с 9 байтом и возвращаем тру или нет
        if crc == M.dat:byte(9) then
            return true
        else
            print('Bad CRC')
            return false
        end
    end
    А теперь займемся преобразованием данных в температуру и напряжение. Начнем с температуры, потому что (ах, тайные желания) скопипастить алгоритм с аруиновской библиотеки не удастся: там нет ничего об отрицательных температурах.

    Смотрим сюда:
    upload_2018-2-27_18-4-19.png
    Видим, что температура кодируется двумя байтами, кои называются MSB и LSB - мост(больший, старший) и лист(меньший, младший) значимые байты.

    Читаем дальше :
    upload_2018-2-27_18-7-21.png

    В вольном переводе это значит, что LSb имеет разрешение 0.03125 (фигасе!!!) градуса, а MSB - тот чистый градусник.

    Кроме того, три правых бита LSb - не применяются, и чтобы правильно сформировать его значение, его нужно сдвинуть вправо на 3 разряда:
    Код (Lua):
    LSb = bit.rshift(LSb, 3)
    -- или:
    LSb = LSb/2/2/2
    -- а кто профессор в математике
    LSb = LSb/8

    -- а потом умножить:
    LSb = LSb * 0.03125
    За отрицательную температуру отвечает 7 бит MSb. Пока его нет - это просто градусы.
    Когда он есть - остальные биты инвертируются. В общем, с ним надо поступать так:
    Код (Lua):
    local raw = (MSb > 127) and (MSb - 256) or MSb
    Получили тренарный оператор: если MSb > 127 (то есть установлен 7 бит) - берем MSb - 256 иначе не трогаем его.

    Чтобы проверить наши измышления, напишем функцию, которая примет данные из таблицы выше, и рассчитает температуру один в один:

    Код (Lua):
    do
        temp = {
            -- Это значения MSb и LSb из таблички
            {0x7d, 0x00},
            {0x19, 0x10},
            {0x00, 0x80},
            {0x00, 0x00},
            {0xff, 0x80},
            {0xe6, 0xf0},
            {0xc9, 0x00}
        }
        -- Функция преобразования
        temp2438 = function(mb, lb)
            local raw = (mb > 127) and (mb - 256) or mb
            print (raw + (bit.rshift(lb, 3)) * 0.03125)
            print (raw + ((lb/8) * 0.03125))
        end
        -- кормим функцию таблицей
        for _, v in pairs(temp) do
            temp2438(v[1], v[2])
        end
    end
    Неплохо получилось:
    upload_2018-2-27_18-22-3.png

    C вольтами вообще просто:

    upload_2018-2-28_10-53-5.png

    Код (Lua):
    do
        volt = {
            {0x00, 0x05},
            {0x01, 0x0e},
            {0x01, 0x68},
            {0x01, 0xf4},
            {0x02, 0xd0},
            {0x03, 0xe7},
            {0x03, 0xe8}
        }
        volt2438 = function(mb, lb)
            local v = ((mb * 256) + lb) / 100
            print(v)
        end
        for _, v in pairs(volt) do
            volt2438(v[1], v[2])
        end
    end
    upload_2018-2-27_18-33-41.png

    В заключение, прилагаю итоговый файл библиотеки _ds2438.lua в стиле ардуино, но без некоторой автоматики и гораздо проще. В той библиотеке, что взята для примера есть функция update() которая читает информацию с применением delay(), а потом парой функций можно выудить из нее данные.
    С нашей тоже можно поступать аналогичным образом, но завтра мы будем все автоматизировать и упаковывать, а пока вот что получилось (очень хочу верить, что вам ясно здесь назначение каждой функции):
    Код (Lua):
    local M = {}
    M.addr = ""
    M.addrTb = {}
    M.pin = 4
    M.dat = ""
    -----
    M.search = function() -- ищем датчики
        ow.setup(M.pin)
        ow.reset_search(M.pin)
        local adr
        repeat
            print("Search")
            adr = ow.search(M.pin)
            if(adr ~= nil) then
                table.insert(M.addrTb, adr)
                M.addr = adr
            end
        until (adr == nil)
        ow.reset_search(M.pin)
        print('Adr No:', #M.addrTb)
        for _, v in pairs(M.addrTb) do
            local as = ""
            for ii = 1, #v do
                local hx = string.format("%02X", (string.byte(v, ii)))
                as = as ..hx
            end
        print(as)
        end
    end

    M.starttemp = function() -- запуск измерения температуры
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0x44, 1)
        ow.reset(M.pin)
    end

    M.startVolt = function() -- запуск измерения напряжения
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0xb4, 1)
        ow.reset(M.pin)
    end

    M.setVAD = function(inp) -- 0 или 1 -- откуда читать напряжение, нога или питание
        local dt = inp == 0 and 0 or 0x08
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0x4e, 1)
        ow.write(M.pin, 0x00, 1)
        ow.write(M.pin, dt, 1)
        ow.reset(M.pin)
    end

    M.recallMem = function() -- вызвать данные из ПЗУ
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0xb8, 1)
        ow.write(M.pin, 0x00, 1)
        ow.reset(M.pin)
    end

    M.readPage = function() -- читать данные в переменную
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin,0xBE,1)
        ow.write(M.pin,0x00,1)
        local data = ""
        for i = 1, 9 do
            data = data .. string.char(ow.read(M.pin))
        end
        print(data:byte(1,9))
    end

    M.checkCRC = function() -- проверка цилического кода (целостности данных)
       local crc = ow.crc8(string.sub(M.dat,1,8))
       if crc == M.dat:byte(9) then
         return true
       else
         print('Bad CRC')
         return false
       end
    end

    M.temp = function() -- получить температуру
        if checkCRC() then
            local mb = M.dat:byte(3)
            local raw = (mb > 127) and (-256 + mb) or mb
            return (raw + (bit.rshift(M.dat:byte(2), 3)) * 0.03125)
        end
    end

    M.volt = function() -- получить напряжение
        if checkCRC() then
            return ((M.dat:byte(5) * 256) + M.dat:byte(4)) / 100
        end
    end

    return M
    Продолжение.
     
    Последнее редактирование: 28 фев 2018
    alp69 нравится это.
  12. ИгорьК

    ИгорьК Давно здесь Команда форума

    Продолжим...
     
  13. ИгорьК

    ИгорьК Давно здесь Команда форума

    Пока неоплаченная реклама: для тех же целей существует расширитель портов от Амперки о девяти плюсуемых ногах.
    [​IMG]
    У него всего четыре недостатка:
    1. имеет стремление, но не смог дотянуть ценой до Бугатти - настоящим пацанам такой покупать стыдно;
    2. имеет библиотеки для всего, что имеет много ног, а для esp-8266 - не имеет (я про Lua);
    3. имеет загадочный, футуристический форм-фактор;
    4. имеет отсутствие описания API.
    В общем, для чего он задумывался маркетологами - не ясно, но инженер (Василий?) сделал его с душой.

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

    ИМХО, возникла хорошая тенденция - умные модули. Царем здесь, видимо, является дисплей Nextion.
    К дисплею (херовому) привинтили микроконтроллер (дешманский) и упростили взаимодействие юзверя с ним. Все что делает Nextion можно сделать и обычным дисплеем, изрядно попотев в написании функций и отъев прилично памяти.
    Вот и называемый здесь модуль относится к устройствам, упрощающим жизнь, в том числе путем облегчения перемещения кошелька в пространстве.
     
    Последнее редактирование: 28 фев 2018
  14. ИгорьК

    ИгорьК Давно здесь Команда форума

    19.2 Датчик DS2438: полезная штука. Как работать с любым 1Wire устройством.

    Завершим написание упаковку модуля. Сначала о "внешних" подходах.

    Микросхема предназначена для выдачи пары значений, и нас не интересует, ничто кроме этого. Значит, нет смысла городить какие-то отдельные команды: пнул, а на выходе получил результат.
    Исключение - возможна команда на выбор источника измерения, но в готовом устройстве мы вряд ли будем ей постоянно пользоваться. Один раз установили режим по умолчанию и все.

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

    И последнее, я привык писать скрипты так: делаю глобальную таблицу текущих результатов всяких значений. Она передается различным модулям/функциям для внесения в нее записей. То есть, я не извлекаю из функций через return то или иное значение.
    Знаю, что таблицы поначалу сильно пугают начинающих от Ардуино, поскольку ассоциируются с масивами и указателями, но в Lua таблицы - вещь настолко приятная, что следует понять из глубже.

    В общем, написание модуля начнем не с него, а с ... :) внешнего скрипта работы с ним. Вот:
    Код (Lua):
    do
        -- Грузим еще не написанный модуль
        ds = require('_ds2438nnn')
        -- Создаем таблицу с данными
        dattb = {}
        -- callback функция
        call = function()
            -- просто печатаем показания
            table.foreach(dattb, print)
            --[[
            Здесь можно вставлять свой код для работы
            с результатами, вызывать другие функции,
            а таблица dattb снабдит их необходимой информацией
            --]]

        end
        -- единственная системная функция получения данных
        ds.update(dattb, call)
    end

    -- Это можно делать в ручном режиме
    -- найти датчик
    ds.search()

    -- установить источник измерения напряжения
    -- входная нога
    ds.writeDS(ds.ncom.setVAD)
    -- напряжение питания
    ds.writeDS(ds.ncom.setVVD)
    -- сохранить источник как вызываемый по умолчанию
    ds.writeDS(ds.ncom.copySP)
    Займемся внутренностями.
    Функция поиска адресов необходима в любых случаях. Для ситуации с одним датчиком ds2438 можно сильно упростить ее, но шина 1wire так и манит затолкать не нее лишний термометр. Таким образом, оставим как было - сбор всех адресов в таблицу и, дополнительно, вынос адреса этого датчика в самостоятельную переменную.

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

    Функция поиска адресов, все разжевано раньше:
    Код (Lua):
    M.search = function()
        ow.setup(M.pin)
        ow.reset_search(M.pin)
        local adr
        repeat
            adr = ow.search(M.pin)
            if(adr ~= nil) then
                table.insert(M.addrTb, adr)
                if adr:byte(1) == 0x26 then M.addr = adr end
            end
        until (adr == nil)
        ow.reset_search(M.pin)
        print('Got Devices:', #M.addrTb)
    end
    Теперь съедим простую задачу - объединим в одну функции проверки CRC, расчета напряжения и температуры. Работают они последовательно, решение очевидно, а код объяснен ранее.
    Дополнительно вспомним, что в Lua из функции можно возвращать любое количество значений, поэтому:

    Код (Lua):
    M.count = function()
        local crc = ow.crc8(string.sub(M.dat,1,8))
        if crc == M.dat:byte(9) then
            local mb = M.dat:byte(3)
            local raw = (mb > 127) and (-256 + mb) or mb
            return (raw + (bit.rshift(M.dat:byte(2), 3)) * 0.03125), ((M.dat:byte(5) * 256) + M.dat:byte(4)) / 100
        else
            print('Bad CRC')
            return 85, -1 -- если все фигово, то вернем отрицательное напряжение
            -- и температуру 85 (что для датчиков температуры, обычно, есть ошибка )
            -- но можете сами решить что возвращать
        end
    end
    И, наконец, десерт! Надо запрессовать четыре функции типовой работы с датчиком. Посмотрите как они похожи:

    Код (Lua):
    M.starttemp = function()
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0x44, 1)
        ow.reset(M.pin)
    end

    M.startVolt = function()
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0xb4, 1)
        ow.reset(M.pin)
    end

    M.recallMem = function()
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin, 0xb8, 1)
        ow.write(M.pin, 0x00, 1)
        ow.reset(M.pin)
    end

    M.readPage = function()
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        ow.write(M.pin,0xBE,1)
        ow.write(M.pin,0x00,1)
        local data = ""
        for i = 1, 9 do
            data = data .. string.char(ow.read(M.pin))
        end
        print(data:byte(1,9))
        crc = ow.crc8(string.sub(data,1,8))
        print("CRC="..crc)
        M.dat = data
    end
    Вначале все кидают датчику несколько команд, заключение тоже одинаковое, и лишь последняя вклинивает ближе к концу чтение данных.
    Вывод - делаем функцию, которая:
    1. примет любое число данных для отправки;
    2. примет тру/фальс на чтение
    И дело в шляпе! А что есть "любое число данных для отправки"? Конечно - таблица!
    Пишем функцию:

    Код (Lua):
    M.writeDS = function(tb, read) -- принимаем таблицу с перечнем данных и да/нет на чтение
        if M.addr == "" then M.search() end -- если еще нет адреса датчика - получаем
        ow.reset(M.pin) -- у всх так
        ow.select(M.pin, M.addr)
        for _, v in pairs(tb) do -- перебираем таблицу
            ow.write(M.pin, v, 1) -- пинаем байты в линию
        end
        -- а это вообще красота - в Lua есть функция чтения произвольного количества байт
        -- применяем ее
        if read then M.dat = ow.read_bytes(M.pin, 9) end
        ow.reset(M.pin) -- Все!!!!!!!!
    end
     
    Последнее редактирование: 28 фев 2018
  15. ИгорьК

    ИгорьК Давно здесь Команда форума

    Мы запрессовали 4 функции в одну! Теперь займемся ее обработчиком.
    Обработчик, как предлагалось раньше, примет таблицу для заполнения и callback функцию, которая будет выполнена после добычи всех данных.
    Обработчик будет
    1. рекурсивно вызвать 4 раза предыдущую функцию
    2. заполнить таблицу
    3. вызвать callback
    Поехали:

    Код (Lua):
    M.update = function(dattbl, call) -- принял таблицу и ссылку на функцию
        local nextcom = 1 -- считаем вызовы
        -- Важный нюанс!!! Если мы хотим рекурсии - объявлять
        -- рекурсивную функцию надо до определения ее тела
        -- Просто надо знать!
        local step
        step = function()
            -- Таймер с интервалом 20 миллисекунд
            tmr.create():alarm(20, 0, function()
            -- Вызываем нашу запрессованную функцию с передачей таблицы
            -- (о таблице - позже), а также передаем true/false путем сравнения
            -- текущего шага с необходимым для вызова чтения, то есть четвертым
                M.writeDS(M.ncom[nextcom],(nextcom == 4) )
                if nextcom == 4 then -- после четвертого вызова получаем данные
                    local t, v = M.count()
                    -- втыкаем их в таблицу
                    dattbl.volt = v
                    dattbl.temp2438 = t
                    -- вызываем callback с передачей ей таблицы
                    if call then call(dattbl) end
                else
                    -- иначе повторяем вызов
                    nextcom = nextcom +1
                    step()
                end
            end)
        end
        step()
    end
    Поясню. Я сделал один таймер, который вызывает функцию 4 раза и каждый раз через 20 мс. Для работы требуется лишь две паузы - 10 и 4 мс. Можно ли это реализовать? Можно! Но это усложнит код. Существенно. Тем не менее, все зависит от нужд проекта. В домашнем хозяйстве этими паузами можно пренебречь. А если у вас будут датчики DS18b20 - отдайте им команды на преобразование температуры и во врем ожидания (там минимум 50 мс, максимум - 750) спокойно обработайте DS2438 - времени вагон.

    Если очень надо - время работы функции можно сократить, например, таким образом:
    Код (Lua):

    M.update = function(dattbl, call)
       M.writeDS(M.ncom[1], false )
       local interval = 10
       local nextcom = 2
       local step
       step = function()
         tmr.create():alarm(interval, 0, function()
           interval = 4
           M.writeDS(M.ncom[nextcom],(nextcom == 4) )
           if nextcom == 4 then
             local t, v = M.count()
             dattbl.volt = v
             dattbl.temp2438 = t
             if call then call(dattbl) end
           else
             nextcom = nextcom +1
             step()
           end
         end)
       end
       step()
    end
     
    И последнее - таблица команд. Вот она:
    Код (Lua):
    M.ncom = {
        {0x44}, -- starttemp
        {0xb4}, --startVolt
        {0xb8, 0x00}, -- recallMem
        {0xBE, 0x00}, -- readPage
        setVAD = {0x4e, 0x00, 0x00}, -- Измерить напряжение на ноге
        setVVD = {0x4e, 0x00, 0x08}, -- Измерить напряжение питания датчика
        copySP = {0x48, 0x00}, -- Сделать любую из вышеуказанных команд по умолчанию
    }
    Да, такая странная. Lua позволяет комбинировать нумерованные и именованные данные (да, товарищи любители ардуино, держите крышу - сносит).
    Первые четыре строки мы и применяем в обработчике, вызывая их по порядковым номерам.
    Последние три именованные строки позволяют вручную или из скрипта установить где измерять напряжение и как это делать потом по умолчанию.

    Наконец, пора опубликовать полный скрипт модуля:
    Код (Lua):
    local M = {}
    M.addr = ""
    M.addrTb = {}
    M.pin = 4
    M.dat = ""

    M.ncom = {
        {0x44}, -- starttemp
        {0xb4}, --startVolt
        {0xb8, 0x00}, -- recallMem
        {0xBE, 0x00}, -- readPage
        setVAD = {0x4e, 0x00, 0x00},
        setVVD = {0x4e, 0x00, 0x08},
        copySP = {0x48, 0x00},
    }

    M.search = function()
        ow.setup(M.pin)
        ow.reset_search(M.pin)
        local adr
        repeat
            adr = ow.search(M.pin)
            if(adr ~= nil) then
                table.insert(M.addrTb, adr)
                if adr:byte(1) == 0x26 then M.addr = adr end
            end
        until (adr == nil)
        ow.reset_search(M.pin)
        print('Got Devices:', #M.addrTb)
    end

    M.count = function()
        local crc = ow.crc8(string.sub(M.dat,1,8))
        if crc == M.dat:byte(9) then
            local mb = M.dat:byte(3)
            local raw = (mb > 127) and (-256 + mb) or mb
            return (raw + (bit.rshift(M.dat:byte(2), 3)) * 0.03125), ((M.dat:byte(5) * 256) + M.dat:byte(4)) / 100
        else
            print('Bad CRC')
            return 85, -1
        end
    end

    M.writeDS = function(tb, read)
        if M.addr == "" then M.search() end
        ow.reset(M.pin)
        ow.select(M.pin, M.addr)
        for _, v in pairs(tb) do
            ow.write(M.pin, v, 1)
        end
        if read then M.dat = ow.read_bytes(M.pin, 9) end
        ow.reset(M.pin)
    end

    M.update = function(dattbl, call)
        local nextcom = 1
        local step
        step = function()
            tmr.create():alarm(20, 0, function()
                M.writeDS(M.ncom[nextcom],(nextcom == 4) )
                if nextcom == 4 then
                    local t, v = M.count()
                    dattbl.volt2438 = v
                    dattbl.temp2438 = t
                    if call then call(dattbl) end
                else
                    nextcom = nextcom +1
                    step()
                end
            end)
        end
        step()
    end

    return M
    И посмотреть как это работает:

    upload_2018-2-28_12-52-35.png
     
    Последнее редактирование: 1 мар 2018
  16. ИгорьК

    ИгорьК Давно здесь Команда форума

    Несколько слов в заключение.
    Давайте взглянем еще раз на ардуиновскую библиотеку DS2438 и вспомним ее косяки: не с целью "все они козлы"!
    С простой целью - что мы делаем, когда бездумно копипастим чужие библиотеки, и считаем это нормой.
    Эта библиотека
    - не работает с отрицательными температурами
    - имеет много лишних телодвижений, например для установления источника напряжения выполняется чтение первого байта нулевой страницы, потом к нему подмешивается 0/1 в третьем бите: upload_2018-2-28_13-20-9.png
    - не имеет (я не увидел?) возможности установок по умолчанию
    - втыкает тормоза в программу:
    upload_2018-2-28_13-22-39.png
    Пару таких библиотек, и работа асинхронных функций в ардуино, типа pubsub() или что-то серверное будет нарушаться. (Единственно выручает, что преобразования на модулях, а следовательно - тормоз, вряд ли вызываются часто.)

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

    Работа с шиной 1Wire настолько проста, что поняв несколько азов, вы можете работать с любыми устройствами. Модуль, который мы создали, полагаю, может быть применен к любой железке - вдумчиво меняйте лишь содержание командной таблицы, да разбирайтесь с порядком интерпретации данных от устройств.
    Удачи вам, Lua в помощь.
     
    Последнее редактирование: 28 фев 2018
    alp69 и Securbond нравится это.
  17. Securbond

    Securbond Гуру

    Может дождёмся когда Ув. ИгорьК, напишет библиотеку NeoMatrix для ws2812b на LUA с блеклжеком и ш... русскими шрифтами и эффектами.:rolleyes::rolleyes::rolleyes:
     
  18. ИгорьК

    ИгорьК Давно здесь Команда форума

    Имхо, овчинка не цепляет :)
     
  19. virtual

    virtual Нуб

    Приветствую, форумчане! Пробовал кто по http грузить, считывать, исполнять и удалять на модуле файлы?
    Я столкнулся с тем, что можно считать список файлов около 10-12, а если их будет больше, то "
    PANIC: unprotected error in call to Lua API (out of memory)"
     
  20. ИгорьК

    ИгорьК Давно здесь Команда форума

    Дык, она же пишет "unprotected error in call to Lua API (out of memory)" , что переводится "чего пристал, я контроллер а не компьютер."