【问题标题】:Uploading a Lua script to NodeMCU using Wifi使用 Wifi 将 Lua 脚本上传到 NodeMCU
【发布时间】:2016-03-19 21:25:42
【问题描述】:

是否可以使用 Wifi 接口而不是串口将 Lua 脚本上传到 NodeMCU?

我找到的教程和示例都使用串行接口(即电缆)来编程 NodeMCU,但我想在不连接任何东西的情况下更改程序(使用智能手机或浏览器)

【问题讨论】:

    标签: lua wifi nodemcu


    【解决方案1】:

    我通过 wifi 上传所有模块。我首先以通常的方式(通过 USB)上传bootstrap.lua 程序。然后可以使用该程序上传真实(更大)的有效载荷。这是引导程序:

    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)
    

    第一行使用网关服务器作为上传程序的网络服务器。 第二行设置要上传的程序的端口(@98​​7654324@)、路径(/upload)和名称(u.lc)。 然后它获取文件并最终运行它(最后一行)。

    在运行此之前,您必须激活无线连接,并且您的网络服务器当然应该处于活动状态,您的有效负载位于 /upload/u.lc

    当然,您可以更改硬连线值,甚至使它们动态化。

    标题 ##This 应该是您想要的内容的简单起点。

    顺便说一句,压缩格式可以使初始上传速度更快,我使用--dofile 选项上传luatool.py

    稍后更新您的程序 (u.lc) 是对 dofile("bootstrap.lua") 的简单重复。

    我的u.lc 是一个第 2 阶段的引导程序,可以上传一长串文件(主要是 .lc)。对于这个简短的答案,可能涉及太多了。

    最后,我要提一下,这是基于https://github.com/Manawyrm/ESP8266-HTTP/的松散的

    HTH

    【讨论】:

      【解决方案2】:

      是的,这是可能的。这是一种自制的选择,但在一定程度上有效。当然,唯一的限制是尺寸,但除此之外它工作得很好。看看:

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

      如果你不能用另一种语言编写代码,你需要有一种方法来编写 PHP 程序(我用 C# 编写),你可以下载并重用这个用户编写的代码并使用你自己的 PHP 服务器一切顺利。

      如果您有任何问题,请提出。

      【讨论】:

      • 这是一个很好的教程,正是我想要的。谢谢!
      【解决方案3】:

      存在上述解决方案的变体 (http://www.instructables.com/id/ESP8266-WiFi-File-Management/),但使用桌面 .NET 应用程序而不是 PHP Web 服务器https://github.com/Orudnev/.Net-WiFi-File-Manager-for-ESP8266。如果你不喜欢安装 web 服务器可能会更方便,启动 FileManager.exe 应用程序就足够了。

      【讨论】:

        【解决方案4】:

        我有另一个解决方案,它不受大小限制。此外,它不需要任何其他 Web 服务器,您可以直接从您的工作站发送文件。下面的文件提供了对芯片的上传和下载。

        编辑:我将下面的源示例开发成一个文件管理器,能够上传、重命名、备份、删除,当然还有服务文件,在 ESP8266 和 ESP32 上运行(抽象了 WiFi 连接差异。)The project can be found here。 (取决于 NodeMCU。)

        不幸的是,它没有使用网络浏览器使用的标准上传方案,最后提供了一个上传到它的 javascript 文件。可以在 SendTo 文件夹中创建 js 的快捷方式,从而将其添加到每个文件的上下文菜单中的 Send To 选项列表中,但它只能处理单个文件选择。 (需要一个 shell 扩展来处理多个选定的文件。)

        确实支持常规浏览器下载。

        请注意,此方案严重依赖于特定的 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,在第 75 行附近调用 require 引用(上传是因为芯片将文件发送到请求主机。)它有助于使用常规浏览器下载任何大小的文件。如果没有传递文件名,则默认为“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();
        

        【讨论】:

        • “设置为 AP”是什么意思?
        • AP 作为接入点,而不是站点。 AP 模式类似于 WiFi 路由器,它接受传入的连接,为它们分配 DHCP 地址等。
        • 其实在AP或者Station模式下都是一样的,可以是一个Station连接到同一个WiFi路由器,唯一的区别是你如何确定它的IP。如果它是 AP,您已经知道它的 IP 地址。如果一个站执行广播 ping(最后一个四边形 255),然后检查 ARP 缓存是否有新到达(命令提示符下的 arp -a。)
        【解决方案5】:

        如果您使用 Esplorer 作为 IDE 的另一种方法记录在 here

        它使用ESP8266上的telnet服务器,并将Esplorer串口重定向到telnet服务器的地址;这是一个 Windows 示例,但我已经设法在 linux 下使用“socat”使其工作。

        我发现的唯一问题是,如果您需要多个 tcp 服务器,而 Nodemcu 不允许这样做,在这种情况下,可能是另一个 ESP8266 作为 tcp/串行控制台中继。

        【讨论】:

          猜你喜欢
          • 2019-05-06
          • 2020-01-10
          • 2013-07-24
          • 2012-05-27
          • 2019-11-26
          • 1970-01-01
          • 2020-07-06
          • 1970-01-01
          • 2016-05-11
          相关资源
          最近更新 更多