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

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

  1. ИгорьК

    ИгорьК Гуру

    Если необходимо подключаться к нескольким разным точкам доступа в разных местах, то:
    Код (Lua):
    do
        local aptb = {
            { 'AP_01', 'passwd_01' },
            { 'AP_02', 'passwd_02' },
            { 'AP_03', 'passwd_03' }
        }

        local cfg = {}
        cfg.ssid = ""
        cfg.pwd = ""
        cfg.save = true
        cfg.auto = true

        local function listap(t)
            wifi.setmode(wifi.STATION)
            for bssid, v in pairs(t) do
                local ssid = string.match(v, "([^,]+)")
                print(ssid)
                for k, v in pairs(aptb) do
                    if v[1] == ssid then
                        cfg.ssid = ssid
                        cfg.pwd = v[2]
                    end
                end
            end
            print('\n\nSet SSID as ' .. cfg.ssid)
            wifi.sta.config(cfg)
            wifi.sta.connect()
        end
        wifi.sta.getap({}, 1, listap)
    end
    -- Ответ на вопрос в личку.
     
    Последнее редактирование: 19 мар 2024
    obuhanoe нравится это.
  2. ИгорьК

    ИгорьК Гуру

    Если необходимо подключаться к нескольким разным точкам доступа в разных местах, и к тому же там есть одинаковые SSID с разным с разным уровнем сигнала, то:
    Код (Lua):
    do
        print ('Clear config is', wifi.sta.clearconfig())
        local aptb = {
            { 'AP_01', 'passwd_01' },
            { 'AP_02', 'passwd_02' },
            { 'AP_03', 'passwd_03' }
        }

        wifi.setmode(wifi.STATION)
        local cfg = {}
        cfg.ssid = ""
        cfg.pwd = ""
        cfg.save = true
        cfg.auto = true

        local myssidtb = {}
        local power = -100

        local function listap(t)
            for bssid, v in pairs(t) do
                local ssid, rssi = string.match(v, "([^,]+),([^,]+)")
                rssi = tonumber(rssi)
                print(ssid, bssid, rssi)
           
                for k, v in pairs(aptb) do
                    if v[1] == ssid then
                        if rssi > power then
                            myssidtb = {v[1], bssid, k}
                            power = rssi
                        end
                    end
                end
            end
            cfg.ssid = myssidtb[1]
            local nbssid = ""
            for k = 1, #myssidtb[2] do
                nbssid = nbssid .. string.upper(string.sub(myssidtb[2], k, k) )
            end
            cfg.bssid = nbssid
            cfg.pwd = aptb[myssidtb[3]][2]
            print('\n\nSet SSID as ' .. cfg.ssid.. ' and BSSID is '.. cfg.bssid.. 'and passwd is '..cfg.pwd )
            wifi.sta.config(cfg)
            wifi.sta.connect()
        end
        wifi.sta.getap({}, 1, listap)
    end
     
    Последнее редактирование: 19 мар 2024
  3. ИгорьК

    ИгорьК Гуру

    56. ESP32. Читаем SDS011 PM2.5:

    Код (Lua):

    uart.setup(2, 9600, 8, uart.PARITY_NONE, uart.STOPBITS_1, {tx = 17, rx = 16})
    uart.start(2)
    local d25 = 0
    local d10 = 0

    local gotRAW = {}
    local startUART = false

    local function ptrANSW()
        local crc = 0
        for i = 3, 8 do crc = crc  + gotRAW[i] end
        crc = bit.band(crc, 0xFF)
        -- print(string.format('\t\tcrc = %02X',crc))
        if crc ~= gotRAW[9] then print('Bad CRC'); gotRAW = {}; startUART = false return end

        d25 = ((gotRAW[4]*256 ) + gotRAW[3])/10
        d10 = ((gotRAW[6]*256 ) + gotRAW[5])/10
        print('\n\t\t\tPM2.5 = '..d25..' μg/m3,  PM10 = '.. d10..' μg/m3')

        gotRAW = {}
        startUART = false
        -- dofile('analizeMTRF.lua')
    end

    uart.on(2,"data",1,
        function(data)
            local bt = string.byte(data, 1)
            -- print(string.format("bt = %02X", bt))
            if startUART == false and bt ~= 0xAA then return
            elseif startUART == false then startUART = true end
            gotRAW[#gotRAW+1] = bt
            if gotRAW[2] and gotRAW[2] ~= 0xC0 then gotRAW = {}; startUART = false end
            if #gotRAW == 10 then ptrANSW() end
    end, 0)
     
     
    Последнее редактирование: 21 апр 2024
    serg3295 нравится это.
  4. obuhanoe

    obuhanoe Гик

    У Вас реализация для ESP8266, ниже для ESP32
    Код (Lua):
    do
        local l_wifi = {
             { 'AP_01', 'passwd_01' },
             { 'AP_02', 'passwd_02' }
        }
         -- стартуем Wifi: при дисконекте (callback произойдет)
        wifi.start()

         local scfg = {}  
         scfg.auto = true
         scfg.save = true
         scfg.ssid = ""
         scfg.pwd = ""

         local function connect_wifi(err, arr)
            if err then
              print(err)
            else
              wifi.mode(wifi.STATION, true)
              for i, ap in ipairs(arr) do
                print(i, ap.ssid, ap.channel,ap.bssid,ap.rssi,ap.auth,ap.bandwidth)
                for k, v in pairs(l_wifi) do
                    if v[1] == ap.ssid then
                        scfg.ssid = ap.ssid
                        scfg.pwd = v[2]
                    end
                end
              end
              print('\n\nSet SSID as ' .. scfg.ssid)
              wifi.sta.config(scfg, true)
              wifi.sta.connect()
            end
        end
       
         wifi.sta.scan({}, connect_wifi)

        wifi.sta.on("got_ip", function(ev, info)
            print("IP now:", info.ip)
        end)
     
        wifi.sta.on("disconnected", function(ev, info)    
               print("WIFI DISCONNECTED!!!")
        end)

        wifi.sta.on("connected", function(ev, info)
               print("WIFI "..info.ssid)
        end)
    end
     
    ИгорьК нравится это.
  5. ИгорьК

    ИгорьК Гуру

    57. ESP32. Читаем SPS30 PM2.5, PM10, etc.

    ... и здесь помог AI, ибо о применении функции string.unpack(...) для преобразования четырех байт во float никогда не слышал . Гуглопоиски ничего не давали, а вот Aria от Оперы помогла.

    Работаем через UART.

    Код (Lua):
    TX = 17
    RX = 16

    uart.setup(2, 115200, 8, uart.PARITY_NONE, uart.STOPBITS_1, {tx = TX, rx = RX})
    uart.start(2)

    TX, RX = nil, nil

    if not wth then wth = {} end

    local gotRAW = {}
    local startUART = false
    local uartdata = {}
    local btf, makedata, checksum, byte_stuffing
    btf = function(e)
        if type(e) ~= "table" or #e ~= 4 then return end
        local dtu = (string.unpack("f", string.char(e[4], e[3], e[2], e[1])))
        print( 'Bytes To Float:', string.format("%0.1f", dtu))
        return string.format("%0.1f", dtu)
    end

    checksum = function(tb)
        --[[
            The checksum is built before byte-stuffing
            and checked after removing stuffed bytes from
            the frame.
        --]]

        -- for k,v in pairs(tb) do print(k, string.format("0x%02X", string.byte(v))) end
        local sum = 0
        for k = 1, #tb - 1 do
            sum = sum + tb[k]
        end

        print('Sum = '.. string.format("bt = 0x%02X", sum))
        print('Comtrol byte = ', string.format("0x%02X", tb[#tb]))
        sum = bit.bxor(0xFF, bit.band(0xFF, sum))

        print('Sum bxor(0xFF, LSB sum) = '..string.format("0x%02X",sum))
        if sum == tb[#tb] then
            print('Sum is OK!' )
            return true
        else
            print('Wrong Sum!')
            return false
        end
    end

    byte_stuffing = function (tb)
        local ntb = {}
        local insert = 0
        local patt = {}
        patt[0x5E] = 0x7E
        patt[0x5D] = 0x7D
        patt[0x31] = 0x11
        patt[0x33] = 0x13

        for k = 1, #tb do
            if tb[k] == 0x7D then
                insert = patt[tb[k+1]]
            end
            if tb[k] ~= 0x7D then
                if insert ~= 0 then
                    ntb[#ntb+1] = insert
                    insert = 0
                else
                    ntb[#ntb+1] = tb[k]
                end
            end
        end
        return ntb
    end

    makedata = function (tb)
        local dtb = {}
        local pointer = 1
        local pat = {}
        pat[1] = 'pm1';    pat[2] = 'pm2_5';  pat[3] = 'pm4'
        pat[4] = 'pm10';   pat[5] = 'npm0_5'; pat[6] = 'npm1'
        pat[7] = 'npm2_5'; pat[8] = 'npm4';   pat[9] = 'npm10'
        pat[10] = 'tpz'

        for k = 5, 44 do
            dtb[#dtb+1] = tb[k]
            if #dtb == 4 then
                local ddig = btf(dtb)
                wth[pat[pointer]] = ddig
                dtb = {}
                pointer = pointer + 1
            end
        end
        uartdata = {}
        print('Now Got PM Data From SPS30')
    end

    uart.on(2,"data",1,
        function(data)
            local ok = false
            local bt = string.byte(data, 1)
            -- print(string.format("bt = 0x%02X", bt))
            if startUART == false and bt ~= 0x7E then
                return
            elseif startUART == false then
                startUART = true
                gotRAW = {}
                return
            end
            if bt == 0x7E then
                uartdata = byte_stuffing(gotRAW)
                ok = checksum(uartdata)
                print("Got ", ok , " Data from SPS30" )
                startUART = false
                if #uartdata > 40 then
                    makedata(uartdata)
                else
                    ok = uartdata[3]
                    if ok == 0 then
                        print('Command is Ok! No error.')
                    else
                        local errr = string.format("bt = 0x%02X", ok)
                        print('Command Error:', string.format("0x%02X", ok))
                        if ok == 0x43 then print('Command not allowed in current state') end
                        if ok == 0x03 then print('No access right for command') end
                    end
                end
                return
            end
            gotRAW[#gotRAW+1] = bt
            print(string.format("0x%02X,", bt))
        end, 0)

    function sstart() -- Начало Измерерия
        uart.write(2, table.unpack({0x7E, 0x00, 0x00, 0x02, 0x01, 0x03, 0xF9, 0x7E})) -- start
    end
    function sstop() -- Остановка SPS30
        uart.write(2, table.unpack({0x7E, 0x00, 0x01, 0x00, 0xFE, 0x7E})) -- stop
    end
    function sread() -- Чтение Данных
        uart.write(2, table.unpack({0x7E, 0x00, 0x03, 0x00, 0xFC, 0x7E})) -- read
    end
    function sclean() -- Немедленная Очистка Датчика
        uart.write(2, table.unpack({0x7E, 0x00, 0x56, 0x00, 0xA9, 0x7E})) -- clean fan
    end
    function sraci() -- Чтение данных об интервале очистки
        uart.write(2, table.unpack({0x7E, 0x00, 0x80, 0x01, 0x00, 0x7D, 0x5E, 0x7E}))  -- RACI
    end

    Отличный датчик загрязненности воздуха. Правда есть там небольшой косячок. Маленький, но очень опасный! Цвет проводов не соответствует действительному положению дел! Можно пожечь датчик, если довериться красному и черному проводу: держите в уме документацию.
    upload_2024-5-15_12-8-11.png

    В работе:

    upload_2024-5-15_12-2-28.png
     
    Последнее редактирование: 15 май 2024
    parovoZZ нравится это.
  6. Sergku

    Sergku Нерд

    всем привет. понадобилось мне, тутреле скваженного насоса сделать. Нужны таймеры на большие промежутки времени (3 часа), полез в доки tmr
    upload_2024-5-20_16-42-23.png
    что не есть хорошо. Может кто знает, какие модули еще этим рулить могут (еспха будет работать автономно, без ртц шилдов и инета)? костыли неохота втыкать сюда
     
  7. ИгорьК

    ИгорьК Гуру

    Я знаю.
     
  8. Sergku

    Sergku Нерд

    так, и ? :)
     
  9. ИгорьК

    ИгорьК Гуру

    Там под "Я знаю" - ссылка.
     
  10. Sergku

    Sergku Нерд

    апасна, нынче переводить.. да, я и нашел, вроде
    upload_2024-5-20_22-21-15.png
     
  11. ИгорьК

    ИгорьК Гуру

    58. Читаем SCD41 - датчик со2, температуры и влажности.
    Датчик - просто песТня! Особливо для часов - маленький и выдает все что надо.

    ... только я раскрыл даташит, только нашел ардуино библиотеку для подсматривания и написания lua библиотеки, пришла мысль поискать на Гитхабе сочетание "SDC41 lua". И, вот оно - нашлось!

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

    Доработка проста - библа выдает окончательные данные, а также вставляет их в таблицу, если оная передана в функцию чтения.

    Данные температуры и влажности в string формате (но ничто не мешает применить tonumber(...), если надо сложить температуру с табуретками).

    Код (Lua):
    local scd4x = {}
    local addr = 0x62

    function scd4x.start()
        i2c.start(0)
        if not i2c.address(0, addr, i2c.TRANSMITTER) then
            return false
        end
        i2c.write(0, {0x21, 0xb1})
        i2c.stop(0)
        return true
    end

    function scd4x.stop()
        i2c.start(0)
        if not i2c.address(0, addr, i2c.TRANSMITTER) then
            return false
        end
        i2c.write(0, {0x3f, 0x86})
        i2c.stop(0)
        return true
    end

    function scd4x.read(tbl)
        i2c.start(0)
        if not i2c.address(0, addr, i2c.TRANSMITTER) then
            return nil
        end
        i2c.write(0, {0xec, 0x05})
        i2c.stop(0)
        i2c.start(0)
        if not i2c.address(0, addr, i2c.RECEIVER) then
            return nil
        end
        local data = i2c.read(0, 9)
        i2c.stop(0)
        if not scd4x.crc_valid(data, 9) then
            return nil
        end
        local co2 = string.byte(data, 1) * 256 + string.byte(data, 2)
        local temp_raw = 175 * (string.byte(data, 4) * 256 + string.byte(data, 5))
        local humi_raw = 100 * (string.byte(data, 7) * 256 + string.byte(data, 8))
        local temp = string.format ("%d.%d ",(temp_raw/65536 - 45), (temp_raw%65536)/6554)
        local humi = string.format ("%d.%d ", humi_raw/65536, (humi_raw%65536)/6554)

        if tbl then tbl.co2 = co2; tbl.humidity = humi; tbl.temperature = temp end
        return co2, temp, humi
    end

    function scd4x.crc_word(data, index)
        local crc = 0xff
        for i = index, index+1 do
            crc = bit.bxor(crc, string.byte(data, i))
            for j = 8, 1, -1 do
                if bit.isset(crc, 7) then
                    crc = bit.bxor(bit.lshift(crc, 1), 0x31)
                else
                    crc = bit.lshift(crc, 1)
                end
                crc = bit.band(crc, 0xff)
            end
        end
        return bit.band(crc, 0xff)
    end

    function scd4x.crc_valid(data, length)
        for i = 1, length, 3 do
            if scd4x.crc_word(data, i) ~= string.byte(data, i+2) then
                return false
            end
        end
        return true
    end

    return scd4x

    Применение:

    Код (Lua):
    wth = {}
    SDA = 2
    SCL = 1
    scd4x = require("scd4x")
    i2c.setup(0, SDA, SCL, i2c.SLOW)
    scd4x.start()

    asksensor = function()
        local co2, temp, humi = scd4x.read(wth)
        print('co2:', co2,'Temperature:', temp, 'Humidity:', humi)
    end

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

    Проект интересен еще и тем, что не просто публикует данные на mqtt брокер, но и обеспечивает mqtt обнаружение девайса в Home Assistant .

    upload_2024-7-17_8-41-49.png

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

    Пожалуйста, подарите звездочку тов. derf на гитихабе.
     
    Последнее редактирование: 20 июл 2024
  12. ИгорьК

    ИгорьК Гуру

    59. SCD30 Модуль здесь:

    Код (Lua):

    local scd30 = {}
    local addr = 0x61
    local pressure = 1013
    local prt = prt or print
    local writei2c, getbytes, crc_word, crc_valid

    writei2c = function(tb)
        i2c.start(0)
        if not i2c.address(0, addr, i2c.TRANSMITTER) then
            prt '\n\n!!!Lost I2C'
            return false
        end
        i2c.write(0, tb)
        i2c.stop(0)
        return true
    end

    getbytes = function(dig) -- Разлагает число на байты и прилагает crc
        local mbt, lbt, cr
        mbt = bit.rshift(dig, 8)
        lbt = bit.band(dig, 0xFF)
        cr = crc_word(string.char(mbt, lbt), 1)
        return mbt, lbt, cr
    end

    function scd30.start(press) -- Старт SCD30 с коррекцией давления
        local mbt, lbt, cr
        press = press or pressure
        if press < 700 or press > 1400 then press = pressure end
        mbt, lbt, cr = getbytes(press)
        prt('\n--------\nStart SCD30 at Pressure Correction ' .. press .. ' mBar\nCorrection Data:', mbt, lbt, cr, '\n-------')
        writei2c({ 0x00, 0x10, mbt, lbt, cr })
    end

    function scd30.stop()
        writei2c({ 0x01, 0x04 })
    end

    function scd30.setTempOffset(offset) -- Коррекция температуры, шаг 0.01
        local woff, md, ld, cr
        woff = math.floor(offset * 100)
        md, ld, cr = getbytes(woff)
        prt('\nSet SCD30 Temperature Correction ' .. offset .. '°С \nCorrection Data:', md, ld, cr, '\n\n')
        writei2c({ 0x54, 0x03, md, ld, cr })
    end

    function scd30.altComp(alt) -- Компенсация высоты
        local al, md, ld, cr
        al = alt or 107
        md, ld, cr = getbytes(al)
        prt('\nSet SCD30 Altitude Compensation ' .. alt .. ' m. \nCorrection Data:', md, ld, cr, '\n\n')
        writei2c({ 0x51, 0x02, md, ld, cr })
    end

    function scd30.reset() -- Soft Reset
        prt('\nReset SCD30 Now!\n')
        writei2c({ 0xD3, 0x04 })
    end

    function scd30.read(tbl, call) -- Чтение данных в таблицу
        local data, dt, nt, s
        dt = {} -- таблица итоговых данных
        nt = {} -- названия данных
        nt[1] = 'co2'
        nt[7] = 'temp_scd30'
        nt[13] = 'humy_scd30'

        writei2c({ 0x03, 0x00 })
        i2c.start(0)
        if not i2c.address(0, addr, i2c.RECEIVER) then
            return nil
        end
        data = i2c.read(0, 18)
        i2c.stop(0)

        if not (crc_valid(data, 18)) then
            prt('\n\n!!! CRC Fall'); return
        end

        for k = 1, #data, 6 do
            s = string.sub(data, k, k + 1)
            s = s .. string.sub(data, k + 3, k + 4)
            dt[nt[k]] = string.format("%0.1f", string.unpack(">f", s))
            if tbl then
                tbl[nt[k]] = dt[nt[k]]
            end
        end
        if call then call() end
        return dt[nt[1]], dt[nt[7]], dt[nt[13]]
    end

    function scd30.readfw()
        local data
        writei2c({ 0xD1, 0x00 })
        i2c.start(0)
        if not i2c.address(0, addr, i2c.RECEIVER) then
            return nil
        end
        data = i2c.read(0, 3)
        i2c.stop(0)

        if crc_valid(data, 3) then
            prt('\nFW: ' .. string.byte(data, 1, 1) .. '.' .. string.byte(data, 2, 2))
        else
            prt('Lost Firmware Now!')
        end
    end

    crc_word = function(data, index)
        local crc = 0xff
        for i = index, index + 1 do
            crc = bit.bxor(crc, string.byte(data, i))
            for j = 8, 1, -1 do
                if bit.isset(crc, 7) then
                    crc = bit.bxor(bit.lshift(crc, 1), 0x31)
                else
                    crc = bit.lshift(crc, 1)
                end
                crc = bit.band(crc, 0xff)
            end
        end
        return bit.band(crc, 0xff)
    end

    crc_valid = function(data, length)
        for i = 1, length, 3 do
            if crc_word(data, i) ~= string.byte(data, i + 2) then
                return false
            end
        end
        return true
    end
    return scd30
     

    Применять:

    Код (C++):
    do
        SDA = 22
        SCL = 21
        if not dat then dat = {} end
        if not wth then wth = {} end

        if not dat.sda then dat.sda = SDA end
        if not dat.scl then dat.scl = SCL end
        if not dat.id then dat.id = i2c.SW end -- software interface
        if not dat.device_addr then dat.device_addr = 0x62 end
        SDA, SCL = nil, nil

        asksensor = function()
            local co2, temp, humi = scd30.read(wth)
            print('co2:', co2,'Temperature:', temp, 'Humidity:', humi)
        end

        if dat.sda and dat.scl and not dat.i2c then
            scd30 = require("_scd30mod")
            print('i2c set at pins SDA: '..dat.sda..', SCL: '..dat.scl..', Speed:', i2c.setup(dat.id, dat.sda, dat.scl, i2c.SLOW))
            scd30.start()
            dat.i2c = true
        else
            print('\t\t\t\ti2c Bus Ready')
        end

        asksensor()
        -- tmr.create():alarm(15000, 1, asksensor)
    end
     
    Последнее редактирование: 21 ноя 2024
    Voik нравится это.