三、ESP8266之 TCP服务器(基于LUA开发)
2017-10-07 22:41
405 查看
8266做服务器,PC | 手机做客户端
先看代码吧。。(边写边调试…)
init.lua
--小灯 gpio2 -> IO4 --继电器 gpio4 -> IO2 gpio.mode(4, gpio.OUTPUT) --配置GPIO2为输出模式 gpio.mode(2, gpio.OUTPUT) --配置GPIO4为输出模式 gpio.write(4, 1) --初始化GPIO2,此时灯灭 tmr.alarm(0, 1000, 1, function() --定时器0控制1s一闪 gpio.write(4, 1-gpio.read(4)) end) --别忘了函数最后的end tmr.alarm(1, 1000, 0, function() dofile("wifi.lua") end)
wifi.lua
一点一点来哈,确保已经走下的每一步,才能走的更远,,
wifi.setmode(wifi.STATIONAP) --设置wifi的工作模式为STA+AP cfg = {} --设置8266wifi的名字及密码 cfg.ssid = "Hello8266" cfg.pwd = "11223344" wifi.ap.config(cfg) apcfg = {} --设置8266要连接的wifi名字及密码 apcfg.ssid = "6103" apcfg.pwd = "6a1a0a3a" wifi.sta.config(apcfg) wifi.sta.autoconnect(1) --设置自动重连 printip = 0 --ip只打印一次的标志位 wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T) --注册wifi事件监听函数,可以更新事件状态 printip = 0 end) wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T) if printip == 0 then print("IP: "..T.IP) end printip = 0 end)
来一个全的wifi.lua(带注解)
wifi.setmode(wifi.STATIONAP) --设置wifi的工作模式为STA+AP cfg = {} --设置8266wifi的名字及密码 cfg.ssid = "Hello8266" cfg.pwd = "11223344" wifi.ap.config(cfg) apcfg = {} --设置8266要连接的wifi名字及密码 apcfg.ssid = "6103" apcfg.pwd = "6a1a0a3a" wifi.sta.config(apcfg) wifi.sta.autoconnect(1) --设置自动重连 TcpSocketTable = {} --用来存储socket TcpConnectCnt = 0 --连接个数计数 --由于8266只能建立5个客户端,这里做的是0连、1连、2连、3连、4连0断、0连1、...... --没错只要5个可以连,但是上面却实际是4个在作用,为什么? --据说连5个后就不能进入事件监听函数了???待测试。。。 TcpServer = net.createServer(TCP, 28800) --建立TCP连接 TcpServer:listen(8080, function(socket) --服务器处于监听 TcpSocketTable[TcpConnectCnt] = socket --存储当前对应的socket print(TcpConnectCnt.."-Connected") --打印到串口相应的socket连接 --处理4连0断的情况 if TcpConnectCnt == 4 then --0123四个连接了,那么4连接就得断开1了! if TcpSocketTable[0] ~= nil then TcpSocketTable[0]:close() print("0-Disconnected") --两点需要注意,close后面有括号,它是函数。还有这一句必不可少,必须软件实现关闭socket TcpSocketTable[0] = nil --数组中相应的值清了 end --处理0连1断、1连2断......的情况 else if TcpSocketTable[TcpConnectCnt+1] ~= nil then TcpSocketTable[TcpConnectCnt+1]:close() local Cnt = TcpConnectCnt + 1 print(Cnt.."-Disconnect") TcpSocketTable[TcpConnectCnt+1] = nil end end TcpConnectCnt = TcpConnectCnt + 1 --socket目前连接计数 if TcpConnectCnt == 5 then --超过4清0 TcpConnectCnt = 0 end socket:on("receive", function(socket, data) --注册事件监听函数,当接收到数据的时候 uart.write(0, data) --打印到串口。 不要忘记写对应的串口号0(目前只能用串口0) end) socket:on("disconnection", function(sck, c) --当断开连接的时间发生的时候 for i=0, 4 do if TcpSocketTable[i] == sck then TcpSocketTable[i] = nil --这个务必写上(一会专门上图解释) print(i.. "-Disconnected") --打印相应的断开连接,更直观 end end end) en 10749 d) uart.on("data", 0, function(data) --串口准备数据要发送给客户端 for i=0, 4 do if TcpSocketTable[i] ~= nil then --发送给socket不为nil的 TcpSocketTable[i]:send(data) --关于print、uart.write(0,data)、socket:send(data) end end end) printip = 0 --ip只打印一次的标志位 wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, function(T) --注册wifi事件监听函数,可以更新事件状态 printip = 0 end) wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, function(T) if printip == 0 then print("IP: "..T.IP) end printip = 0 end)
关于
socket:on("disconnection", function(sck, c) for i=0, 4 do if TcpSocketTable[i] == sck then TcpSocketTable[i] = nil print(i.. "-Disconnected") end end end)
解决几个问题:
1、必加TcpSocketTable[i] = nil 的“大”理由 :)
先看少写的结果
再来分析产生错误的原因:
其实哈,可以这样想。
我图中的测试方法是这样的,0连0断,1连1断,……就这样一个接着一个,然后到了第三个该第四个就崩了,其实很容易想到,肯定是4到0出了问题。我们前面是有这样一段程序的:就是当socket计数到4的时候,判断对应的0socket是不是空的,不是空的就把它关了。然而,由于我们没有及时置空,就造成了已经关掉的socket,但是在数组里面没有置空,所以还会进入if的判断,继续执行关闭socket的程序。然后已经关闭的socket继续关闭当然报错了!
2、必须分清的两个东西,串口和网口
以及三个函数“print、uart.write(0,data)、socket:send(data) ”
其实后面还要进行TCP client的程序,所以很有必要区别串口和网口,以及print、uart.write(0,data)、socket:send(data),自己倒是花费挺长时间纠结这个的,如果您有更好的理解,欢迎留言。
串口,当做8266服务器的时候,当然是对应的“打开串口”的界面
是相应的接收和发送是串口的收发,而其他的界面,作为client的收发当然就是对应网口的收发了!当作为8266作为client的时候(其实内部可当做有5个client的)对应的也是打开串口的界面软件接收和发送就是串口的收发咯,然后PC模拟的服务器,就是对应网口的收发咯。很好理解吧,目前这样想和实验室很符合的。 :)
print以及uart.write(0,data)都是直接打印数据到串口的,对了,uart.write别忘了写0这个串口号,并且也只能写0这个串口号。
关于socket:send(data)就是直接发送数据到相应的网口咯~
3、关于程序的一点问题分享
上面的那个中间主要程序其实刚开始自己并不是这样写的,而是这样:
TcpServer = net.createServer(TCP, 28800) TcpServer:listen(8080, function(socket) TcpSocketTable[TcpConnectCnt] = socket print(TcpConnectCnt.."-Connected") TcpConnectCnt = TcpConnectCnt + 1 if TcpConnectCnt == 4 then if TcpSocketTable[0] ~= nil then TcpSocketTable[0]:close() print("0-Disconnected") TcpSocketTable[0] = nil else if TcpSocketTable[TcpConnectCnt+1] ~= nil then TcpSocketTable[TcpConnectCnt+1]:close() print(TcpConnectCnt+1.."-Disconnected") TcpSocketTable[TcpConnectCnt+1] = nil end end if TcpConnectCnt == 5 then TcpConnectCnt = 0 end socket:on("receive", function(socket, data) uart.write(0, data) end) socket:on("disconnection", function(sck, c) for i=0, 4 do if TcpSocketTable[i] == sck then TcpSocketTable[i] = nil print(i.. "-Disconnected") end end end) end)
特别注意 TcpConnectCnt = TcpConnectCnt + 1的位置,在if判断的前面。。。
导致的问题呢?
就是串口关闭的顺序不再是
,,,,,,4打开0关闭, 0打开1关闭 ……
其实仔细想想问题也很容易想通,if判断的是当前socket,而是+1后的socket
emmmm,,,,,,很显然,,,3打开0就关了。
easy8266连接的效果图
接着呢,我们加入指令,让继电器工作
协议这样定哈,,,
++MD610 -> 定义为继电器置高
++MD600 -> 定义为继电器置低
uart.on("data",0,function(data) for i=0,5 do if TcpSocketTable[i] ~= nil then TcpSocketTable[i]:send(data) end end end, 0)
并在
socket:on("receive",function(socket,data) uart.write(0,data) control(data) end)
中进行调用
emmmm, ,,,,, 加上回显也许回更好
那就用到我们前面讨论过的 socket:send(data)
function control(data) if data == "++MD610" then NowSocket:send("High") NowSocket = nil gpio.write(2, 1) end if data == "++MD600" then NowSocket:send("Low") NowSocket = nil gpio.write(2, 0) end end
需要在定义一个全局变量,并在receive中赋值。。
emmmm指令嫌麻烦,那么就到了按钮登场。。
按钮需要加入CRC16校验,因为上位机是这么写的。CRC16校验具体学习可参考这篇博文
function control(data) local RecLen = string.len(data) local crc = ow.crc16(string.sub(data, 1, RecLen-2)) local recrc = data:byte(RecLen) local recrc = recrc * 256 local recrc = recrc + data:byte(RecLen-1) if crc==recrc then if string.sub(data, 1, 7) == "++MD610" then --特别注意此处的写法!!! if NowSocket ~= nil then NowSocket:send("High") NowSocket = nil gpio.write(2, 1) end end if string.sub(data, 1, 7) == "++MD600" then if NowSocket ~= nil then NowSocket:send("Low") NowSocket = nil gpio.write(2, 0) end end else if NowSocket ~= nil then --写着写着感受到NowSocket的妙用 print("CRC16 Failed") NowSocket = nil end end end
当然利用手机的上位机也是可以的~~~~此处不贴图了。。
相关文章推荐
- 四、ESP8266之 TCP客户端 (基于LUA开发)
- 二、ESP8266之GPIO 定时器 以及串口(基于LUA开发)
- 六、ESP8266之 file操作 (基于LUA开发)
- 四,ESP8266 TCP服务器(基于Lua脚本语言)
- esp8266利用nodemcu平台开发用lua语言开发
- 3.4 ESP8266-劢领 SDK开发教程-TCP例程指南(第二节)
- 网络游戏服务器开发::用模板偏特化封装C++调用lua的代码
- 基于TCP开发的Hessian组件Nession
- SharpStreaming项目开发纪实:构建基于RTSP协议的服务器及客户端应用(三)——客户端的业务代码实现
- SharpStreaming项目开发纪实:构建基于RTSP协议的服务器及客户端应用(二)——服务器的业务代码实现
- Java Socket编程Demo-基于TCP的单服务器多客户机群发/私聊系统
- 基于select模型tcp服务器的掉线处理
- 详谈高性能TCP服务器的开发
- Delphi XE2 编写的----基于消息的单线程TCP服务器
- 基于 libevent 开发的 C++ 11 高性能网络服务器 evpp(360的作品)
- Linux TCP 服务器编程(六):基于线程的并发服务器
- 初探基于TCP的服务器/客户端结构的聊天系统(三)之表情聊天的实现
- DEARTCP1.2-通用TCP服务器组件,用于创建物联网开发
- 基于C++的纯面向对象的通用高性能大并发TCP-SERVER/CLIENT开发框架实践系列之基础篇
- 基于LwIP socket的TCP服务器