Загрузка сценария Lua в NodeMCU с использованием Wi-Fi

Можно ли загрузить сценарий Lua в NodeMCU, используя интерфейс Wi-Fi вместо последовательного?

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


person user2518618    schedule 14.12.2015    source источник


Ответы (5)


Да, это возможно. Это своего рода домашний вариант, но он работает до определенной степени. Единственное ограничение, конечно, в размере, но в остальном он работает довольно хорошо. Взгляни на:

http://www.instructables.com/id/ESP8266-WiFi-File-Management/

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

Если у вас есть вопросы, пожалуйста, задавайте.

person ProgrammerV5    schedule 14.12.2015
comment
Это хороший учебник, именно то, что я искал. Спасибо! - person user2518618; 15.12.2015

Я загружаю все модули через Wi-Fi. Сначала я загружаю программу bootstrap.lua обычным способом (через USB). Затем эту программу можно использовать для загрузки реальной (более крупной) полезной нагрузки. Вот загрузочная программа:

ip, mask, host = wifi.sta.getip()
port, path, pgm = 80, "/upload", "u.lc"
file.remove(pgm) ; file.open(pgm, "w+") payloadFound = false
local conn = net.createConnection(net.TCP, 0)
conn:on("connection", function(conn)
        conn:send("GET "..path.."/"..pgm.." HTTP/1.0\r\n".."Host: "..host.."\r\nConnection: close\r\nAccept: */*\r\n\r\n") end)
conn:on("receive", function(conn, payload)
        if (payloadFound) then file.write(payload) file.flush()
        else payloadOffset = string.find(payload, "\r\n\r\n")
                if (payloadOffset) then
                        file.write(string.sub(payload, payloadOffset + 4)) file.flush() payloadFound = true
                end end end)
conn:on("disconnection", function(conn) file.close() dofile(pgm) end) conn:connect(port,host)

Первая строка использует сервер шлюза в качестве веб-сервера, с которого загружаются программы. Во второй строке задаются порт (80), путь (/upload) и имя (u.lc) программы для загрузки. Затем он ПОЛУЧАЕТ файл и, наконец, запускает его (последняя строка).

У вас должно быть активно беспроводное соединение, прежде чем запускать это, и ваш веб-сервер, конечно же, должен быть активен с вашей полезной нагрузкой в ​​/upload/u.lc.

Естественно, вы можете изменить жестко заданные значения или даже сделать их динамическими.

Заголовок ##Это должно быть простой отправной точкой для того, что вы хотите.

Кстати, сжатый формат предназначен для быстрой начальной загрузки, я загружаю с помощью luatool.py, используя опцию --dofile.

Обновление вашей программы (u.lc) позже — это простое повторение dofile("bootstrap.lua").

Мой u.lc — это загрузчик этапа 2, который загружает длинный список файлов (в основном .lc). Вероятно, слишком вовлечен для этого короткого ответа.

Наконец, я должен упомянуть, что это свободно основано на https://github.com/Manawyrm/ESP8266-HTTP/

ХТН

person Eyal    schedule 20.12.2015

Существует вариант решения, описанный выше (http://www.instructables.com/id/ESP8266-WiFi-File-Management/), но вместо веб-сервера PHP используется настольное приложение .NET https://github.com/Orudnev/.Net-WiFi-File-Manager-for-ESP8266. Это может быть удобнее, если вы не хотите устанавливать веб-сервер, достаточно запустить приложение FileManager.exe.

person Oleg Rudnev    schedule 05.12.2016

У меня есть другое решение, свободное от ограничений по размеру. Также он не требует каких-либо других веб-серверов, вы можете отправить файл прямо с вашей рабочей станции. Файл ниже обеспечивает как загрузку, так и загрузку в/из чипа.

Редактировать: я разработал исходные примеры ниже в файловый менеджер, способный загружать, переименовывать, создавать резервные копии, удалять и, конечно же, обслуживать файлы, работающий на ESP8266. Проект можно найти здесь. (Зависит от NodeMCU.)

К сожалению, он не использует стандартную схему загрузки, используемую веб-браузерами, файл javascript, который загружается в него, предоставляется в конце. Можно создать ярлык для js в папке «Отправить», таким образом добавив его в список параметров «Отправить» в контекстном меню для каждого файла, но он будет способен обрабатывать выборки только одного файла. (Для обработки нескольких выбранных файлов потребуется расширение оболочки.)

Он поддерживает обычную загрузку через браузер.

Обратите внимание, что эта схема критически зависит от определенного соглашения XMLHTTPRequest, а именно, что тело POST отправляется во втором/последующем кадре после запроса. Если бы это было не так, код должен был бы найти первый \r\n\r\n в полезной нагрузке исходного запроса и добавить данные, которые следовали за этим, в файл.

headerBlock = "\r\nContent-type: text/html\r\nConnection: close\r\nAccess-Control-Allow-Origin: *\r\nCache-Control: no-cache\r\n\r\n"
local currentFileName = ""
local isPostData = false
print("filexfer")
local srv=net.createServer(net.TCP, 60) 
srv:listen(80,
    function(conn) 
        local function writefile(name, mode, data)
            if (file.open("temp_" .. name, mode) == nil) then
                return -1
            end
            file.write(data)
            file.close()
        end
        conn:on("disconnection", 
            function(conn) 
                isPostData = false
            end
        )
        conn:on("sent", 
            function(conn) 
                currentFileName = ""
                isPostData = false
                conn:close()
            end
        )
        conn:on("receive",
            function(conn, payload)
                tmr.wdclr();
                local s, e, m, buf, k, v
                local tbl = {}
                local i = 1
                local retval = ""
            
                if isPostData then
                    writefile(currentFileName, "a+", payload)
                else
                    s, e = string.find(payload, "HTTP", 1, true)
                    if e ~= nil then
                        buf = string.sub(payload, 1, s - 2)
                        for m in string.gmatch(buf, "/?([%w+%p+][^/+]*)") do
                            tbl[i] = m
                            i = i + 1
                        end
                        m = nil
                        if #tbl > 2 then
                            local cmd = tbl[2]
                            if (tbl[3] ~= nil) and (tbl[3] ~= "/") then
                                currentFileName = tbl[3]
                            --else return an error
                            end

                            if (cmd == "put") then
                                writefile(currentFileName, "w+", "")
                            end

                            if (cmd == "append") then
                                isPostData = true
                            end

                            if (cmd == "persist") then
                                file.rename("temp_" .. currentFileName, currentFileName)
                            end

                            buf = ""
                            if retval == nil then
                                retval = "[nil]"
                            end
                            buf = "HTTP/1.1 200 OK" .. headerBlock .. retval
                        else
                            local filename = "index.html"
                            if tbl[2] ~= nil and tbl[2] ~= "/" then
                                filename = tbl[2]
                            end
                            require("fileupload")(conn, filename)
                            buf = ""
                        end
                        conn:send(buf)
                    end
                end
            end
        ) 
    end
)

Это fileupload.lua, на который ссылается вызов require в строке 75 (загрузить, потому что чип отправляет файл запрашивающему хосту). Он облегчает загрузку файлов любого размера с помощью обычного браузера. Если имя файла не передается, по умолчанию используется index.html.

local module =...
    return function(conn, fname)
        local buf
        tmr.wdclr()
        if file.list()[fname] ~= nil then
            file.open(fname, "r")
            buf = "HTTP/1.1 200 OK" .. headerBlock
        else
            file.open("error404.html", "r")
            buf = "HTTP/1.1 404 FILE NOT FOUND" .. headerBlock
        end

        conn:on ("sent",
            function(sck)
                function sendfile(sck)
                    buf = file.read(255)
                    if buf ~= nil then 
                        sck:send(buf)
                    else
                        sck:close()
                        if module ~= nil then
                            package.loaded[module] = nil
                        end
                        module = nil
                        return
                    end
                end
                sck:on("sent", sendfile)
                sck:on("disconnection",
                    function(sck)
                        print("[disconnection fileupload.sendfile]", node.heap())
                    end
                )
                sendfile(sck)
            end
        )
        conn:on ("receive",
            function(sck, pl)
                sck:close()
            end
        )
        if buf == nil then
            buf = ""    
        end
        conn:send(buf)
    end

А это клиентский javascript-файл, используемый для загрузки в чип. Передайте полный или относительный путь к загружаемому файлу в качестве первого/единственного аргумента. (Если аргументы не переданы, будет выдана ошибка.)

var filepath = WScript.Arguments(0);
var fso = new ActiveXObject("Scripting.FileSystemObject");
var str = fso.OpenTextFile(filepath, 1);
var file = fso.GetFile(filepath);
var filename = file.Name;
var buf = "";

var xhr = new ActiveXObject("MSXML2.XMLHTTP.6.0");
xhr.open("GET", "http://192.168.4.1/put/" + filename, false);
xhr.send();

while (str.AtEndOfStream == false)
{
    buf = str.read(255);
    xhr.open("POST", "http://192.168.4.1/append/" + filename, false);
    xhr.send(buf);
} 

str.close();
xhr.open("GET", "http://192.168.4.1/persist/" + filename, false);
xhr.send();
person Mark McGinty    schedule 20.05.2016
comment
Что вы подразумеваете под установкой в ​​качестве точки доступа? - person dmigo; 09.03.2018
comment
AP как в точке доступа, в отличие от станции. Режим AP аналогичен WiFi-роутеру, он принимает входящие соединения, присваивает им DHCP-адрес и т. д. - person Mark McGinty; 10.03.2018
comment
На самом деле это работает одинаково в режиме AP или Station, это может быть станция, подключенная к тому же маршрутизатору WiFi, единственная разница заключается в том, как вы определяете ее IP. Если это точка доступа, вы уже знаете ее IP-адрес. Если станция выполняет широковещательный пинг (последний четверной 255), а затем проверяет кэш ARP на наличие новых поступлений (arp -a из командной строки). - person Mark McGinty; 08.04.2021

Другой подход, если вы используете Esplorer в качестве IDE, задокументирован здесь

Он использует telnet-сервер на ESP8266 и перенаправляет последовательный порт Esplorer на адрес telnet-сервера; Это пример для Windows, но мне удалось заставить его работать с помощью «socat» под Linux.

Единственная проблема, которую я обнаружил, заключается в том, что если вам нужно более одного tcp-сервера, который Nodemcu не разрешает, и в этом случае ответом может быть другой ESP8266, действующий как ретранслятор tcp/serial console.

person Farti Slartbast    schedule 19.04.2016