您的位置:首页 > 理论基础 > 计算机网络

三、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




当然利用手机的上位机也是可以的~~~~此处不贴图了。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: