NODEMCU物联网nodemcu

9、nodeMCU学习笔记--net模块

2016-11-26  本文已影响5034人  谢mingmin

esp8266  nodemcu  lua  wifi  net  web

闲言碎语

nodemcu的wifi模块,花了三篇文章被我水完了。内容还是比较浅显的。不过,nodemcu的开发者确实把wifi模块设计的很简单,也和容易使用。配置函数、station类函数、ap类函数、监听注册函数,总体来讲还是很清晰明了。要用熟这些wifi功能,其实还要配合其他模块一起来(比如这篇文章要说的net),循序渐进。

模块函数

net模块的函数也是比较多的。不过,整体结构也是很清晰的。赶紧来看看这些函数

序号 函数名 参数 返回值
1 net.createConnection() type, secure net.socket 子模块
2 net.createServer() type, timeout net.server 子模块
3 net.multicastJoin() if_ip, multicast_ip
4 net.multicastLeave() if_ip, multicast_ip
5 net.server:close()
6 net.server:listen() port,[ip],function(net.socket)
7 net.server:on()
8 net.server:send()
9 net.socket:close() nil
10 net.socket:connect() port, ip / domain nil
11 net.socket:dns() domain, function(net.socket, ip)
12 net.socket:getpeer() ip, port
13 net.socket:hold()
14 net.socket:on() event, function() nil
15 net.socket:send() string[, function(sent)]
16 net.socket:unhold()
17 net.dns.getdnsserver() dns_index(0 / 1) ip
18 net.dns.resolve() host, function(ip) nil
19 net.dns.setdnsserver() dns_ip_addr, dns_index nil
20 net.cert.verify() enable / pemdata true

参数里面有个type的,只有两种选择,要么net.TCP,要么net.UDP。有几个server相关的API,几个socket相关的API。这里不打算一个一个函数的讲了,直接来几个例子反而更容易理解API的含义。

实践一下

光说不练假把式,直接来实践一下。从API中可以知道,net模块可以创建server和client。实践前,确保nodemcu已经连入网络。

wifi的配置后会一直生效。如果你先前配置过,可以不用配置。当然,重新配置一下也可以。

client

这里先来看看如何创建一个client,以及如何进行通信。

cl = net.createConnection(net.TCP, 0)
cl:connect(9999, "192.168.199.101")
cl:on("receive", function(sck, c) print(c) end)
cl:on("disconnection", function(sck, c) print("disconnection!") end)

使用.createConnection创建一个net.TCP客户端,函数会返回一个socket子模块,后面要用的都是socket相关的函数,第二个参数,1表示加密,0表示不加密。net.socket:connect用来连接到服务端。参数2既可以是ip地址,也可以是域名。注意connect前面用的是冒号:,不是点。接着,找一个网络调试工具来创建一个server。这里我找了个名字叫网络调试的手机APP。net.socket:on函数用来绑定几个事件回调,函数原型是这样的 function(net.socket[, string]):

这个例子里面,nodemcu连接到app创建的server后,并没有产生回调事件,具体是什么原因,不清楚。不过,尝试连接到域名却可以产生回调事件。比如下面这个域名

cl:connect(80, "www.nodemcu.com")
客户端 服务端
点击APP左边的客户端列表,断开nodemcu,得到一个预期的断开回调。使用.createConnection创建多个客户端。比如,这样子:
cl = net.createConnection(net.TCP, 0)
cl2 = net.createConnection(net.TCP, 0)

使用net.socket:send可以向服务端发送数据。比方说在ESPlorer右边的输入框里面输入下面这句语句:

=cl:send("Hello NodeMCU")

这里需要说明的是,send函数发送的数据长度是有限度的,大概是1400多个字节。当要发送大于1400字节的内容的时候,比如说发送一个带css、js的网页,就需要分成多次发送。多次发送也不是简单的把上面的代码复制几遍就能解决的。而是要用到"sent"事件来回调。

cnt = 0
cl = net.createConnection(net.TCP, 0)
cl:connect(9999, "192.168.199.101")
cl:on("receive", function(sck, c) print(c) end)
cl:on("disconnection", function(sck, c) print("disconnection!") end)
cl:on("sent", function(c) 
    if cnt ~= 10 then
        cl:send(cnt)
        cnt = cnt + 1
    end 
end)

这个例子可以让客户端在发送完第一条消息后,再发10条消息给服务端。激活的方法还是在ESPlorer中输入一条send语句。

=cl:send("Hello NodeMCU")

当nodemcu发送完第一条语句后,会触发"sent"事件,进而发送10条消息给服务端。

收到消息了!

server

知道了如何创建并使用一个client后,我们来继续看如何创建一个server。先上个开胃菜。

ns = net.createServer(net.TCP, 15)
ns:listen(80, function(c)
    c:on("receive", function(c, d)
        print(d)
        c:send(d)
    end)
    c:on("connection", function(c, d) print(d) end)
    c:on("disconnection", function(c, d) print("disconnection") end)
end)

使用.createServer创建一个net.TCP的服务端,第二次参数用于设置不活动连接的超时时间,返回一个net.server模块。nodemcu只能创建一个server,不像client可以创建多个。需要注意一下。net.server只有4个函数,其中的send和on仅对udp有用。tcp要使用socket的send和on函数。
  接着用net.server:listen创建一个监听。回调传入的是一个socket。可以尽情的使用socket的函数了,比如用net.socket:on设置各种事件回调。这个例子里面的"connection"依然没效果╮(╯_╰)╭。使用APP连接到创建好的server,试着发送信息。

这图有点大啊 ╮(╯_╰)╭
  接着到主菜上场了。内容有点长。主要是实现上篇文章说的enduser setup。动筷子前记得把wifi模式设置成AP模式或者混合模式。
web = '<!doctype html><html><head><meta charset=\'utf-8\'><meta name=\'viewport\'content=\'width=380\'><title>Connect gadget to you WiFi</title><style media=\'screen\'type=\'text/css\'>*{margin:0;padding:0}html{height:100%;background:linear-gradient(rgba(196,102,0,0.2),rgba(155,89,182,0.2)),url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAA8AgMAAACm+SSwAAAADFBMVEVBR1FFS1VHTlg8Q0zU/YXIAAADVElEQVQ4yy1TTYvTUBQ9GTKiYNoodsCF4MK6U4TZChOhiguFWHyBFzqlLl4hoeNvEBeCrlrhBVKq1EUKLTP+hvi1GyguXqBdiZCBzGqg20K8L3hDQnK55+OeJNguHx6UujYl3dL5ALn4JOIUluAqeAWciyGaSdvngOWzNT+G0UyGUOxVOAdqkjXDCbBiUyjZ5QzYEbGadYAi6kHxth+kthXNVNCDofwhGv1D4QGGiM9iAjbCHgr2iUUpDJbs+VPQ4xAr2fX7KXbkOJMdok965Ksb+6lrjdkem8AshIuHm9Nyu19uTunYlOXDTQqi8VgeH0kBXH2xq/ouiMZPzuMukymutrBmulUTovC6HqNFW2ZOiqlpSXZOTvSUeUPxChjxol8BLbRy4gJuhV7OR4LRVBs3WQ9VVAU7SXgK2HeUrOj7bC8YsUgr3lEV/TXB7hK90EBnxaeg1Ov15bY80M736ekCGesGAaGvG0Ct4WRkVQVHIgIM9xJgvSFfPay8Q6GNv7VpR7xUnkvhnMQCJDYkYOtNLihV70tCU1Sk+BQrpoP+HLHUrJkuta40C6LP5GvBv+Hqo10ATxxFrTPvNdPr7XwgQud6RvQN/sXjBGzqbU27wcj9cgsyvSTrpyXV8gKpXeNJU3aFl7MOdldzV4+HfO19jBa5f2IjWwx1OLHIvFHkqbBj20ro1g7nDfY1DpScvDRUNARgjMMVO0zoMjKxJ6uWCPP+YRAWbGoaN8kXYHmLjB9FXLGOazfFVCvOgqzfnicNPrHtPKlex2ye824gMza0cTZ2sS2Xm7Qst/UfFw8O6vVtmUKxZy9xFgzMys5cJ5fxZw4y37Ufk1Dsfb8MqOjYxE3ZMWxiDcO0PYUaD2ys+8OW1pbB7/e3sfZeGVCL0Q2aMjjPdm2sxADuejZxHJAd8dO9DSUdA0V8/NggRRanDkBrANn8yHlEQOn/MmwoQfQF7xgmKDnv520bS/pgylP67vf3y2V5sCwfoCEMkZClgOfJAFX9eXefR2RpnmRs4CDVPceaRfoFzCkJVJX27vWZnoqyvmtXU3+dW1EIXIu8Qg5Qta4Zlv7drUCoWe8/8MXzaEwux7ESE9h6qnHj3mIO0/D9RvzfxPmjWiQ1vbeSk4rrHwhAre35EEVaAAAAAElFTkSuQmCC)}body{font-family:arial,verdana}div{position:absolute;margin:auto;top:0;right:0;bottom:0;left:0;width:320px;height:274px}form{width:320px;text-align:center;position:relative}form fieldset{background:white;border:0 none;border-radius:5px;box-shadow:0 0 15px 1px rgba(0,0,0,0.4);padding:20px 30px;box-sizing:border-box}form input{padding:15px;border:1px solid#ccc;border-radius:3px;margin-bottom:10px;width:100%;box-sizing:border-box;font-family:montserrat;color:#2C3E50;font-size:13px}form.action-button{width:100px;background:#27AE60;font-weight:bold;color:white;border:0 none;border-radius:3px;cursor:pointer;padding:10px 5px;margin:10px 5px}form.action-button:hover,#msform.action-button:focus{box-shadow:0 0 0 2px white,0 0 0 3px#27AE60}.fs-title{font-size:15px;text-transform:uppercase;color:#2C3E50;margin-bottom:10px}.fs-subtitle{font-weight:normal;font-size:13px;color:#666;margin-bottom:20px}</style></head><body><div><form><fieldset><h2 class=\'fs-title\'>WiFi Login</h2><h3 class=\'fs-subtitle\'>Connect gadget to your WiFi</h3><input type=\'text\'autocorrect=\'off\'autocapitalize=\'none\'name=\'wifi_ssid\'placeholder=\'WiFi Name\'/><input type=\'password\'name=\'wifi_password\'placeholder=\'Password\'/><input type=\'submit\'name=\'save\'class=\'submit action-button\'value=\'Save\'/></fieldset></form></div></body></html>'    

sendBuf = {}

for i = 1, #web, 1400 do
    local len = #web - i 
    if len > 1400 then
        sendBuf[#sendBuf + 1] = string.sub(web, i, i+1400-1)
    else
        sendBuf[#sendBuf + 1] = string.sub(web, i, i+len)
    end
end

web数组存储了一个web页面。当然了,这个web页面比较大,远远超过了1400字节。需要将它分成几块,以便后面分批发送。所以,把这个web页面分块存储到一个table中。

function sendWeb(c)
    if #sendBuf > 0 then
        s = table.remove(sendBuf, 1)
        c:send(s)
    else
        c:close()
    end
end

函数sendWeb用来把table里面的内容发送出去,一边发送,一边remove表里面的内容,所以用浏览器浏览只能打开页面一次 o(╯□╰)o。或许这个地方可以优化一下。

sv = net.createServer(net.TCP, 60)

sv:listen(80, function(c)
    c:on("receive", function(cn, req)
        local _, _, method, path, vars = string.find(req, "([A-Z]+) (.+)?(.+) HTTP")
        if method == nil then
            _, _, method, path = string.find(req, "([A-Z]+) (.+) HTTP")
        end

        local _GET = {}
        if vars ~= nil then
            for k, v in string.gmatch(vars, "(%w+_%w+)=(%w+)&*") do
                _GET[k] = v
                print(k .. ":" .. v)
            end        
            local sendbuf = "<h1>Config Succeed!</h1>"
            sendbuf = sendbuf.."<p>wifi_ssid: ".._GET["wifi_ssid"].."</P>"
            sendbuf = sendbuf.."<p>wifi_password  :".._GET["wifi_password"].."</P>"
            cn:send(sendbuf)
            cn:close()
        else
            cn:on("sent", sendWeb)
            sendWeb(cn)
        end
    end)
end)

最后这一部分,和开胃菜那个例子的效果差不多,只是这回发送的是一个页面。回调函数中,先解析浏览器get过来的内容,之后把类似于这种格式的字符串("wifi_ssid=hello&wifi_password=12345678")存储到一个table中。最后又把提取到的内容send出来,赶紧用浏览器访问nodemcu看看效果吧。只需要在浏览器的地址栏输入ip地址即可。
  利用net的server,还可以显示web控制led之类的效果,网上有相关的例子。或者可以配合nodemcu上面的AD完成更多东西来。不过前提是,要能写出漂亮的web页面o(╯□╰)o。

一点lua语法

local _, _, method, path, vars = string.find(req, "([A-Z]+) (.+)?(.+) HTTP")

这个地方的 _ 实际上是一个变量,叫虚变量,因为string的find方法会返回子串的起始和结束地址。不需要的话,可以用虚变量来存储。

上一篇下一篇

猜你喜欢

热点阅读