案例详细讲解了基于DoitCar的STA模式操作过程。程序使NodeMCU的工作在STA模式,连接到无线路由器。通过建立TCP客户端,连接到远程服务器,实现微信、网页以及手机APK的远程控制。
本案例程序源码见资料包,包括:init.lua、sta.lua和DoitCarControlSTA.lua三个文件。
NodeMCU在启动时预留了init.lua作为应用程序入口,如果没有该文件则忽略,如果存在则开始执行该文件。利用这个特性可以在init.lua中写入需要执行的代码,以便上电自动运行。本案例使用的init文件如下所示。
资料包中文件名为“init.lua”。
23 print("\n")
24 print("ESP8266 Started")
25
26 local exefile="sta"
27 local luaFile = {exefile..".lua","DoitCarControlSTA.lua"}
28 for i, f in ipairs(luaFile) do
29 if file.open(f) then
30 file.close()
31 print("Compile File:"..f)
32 node.compile(f)
33 print("Remove File:"..f)
34 file.remove(f)
35 end
36 end
37
38 if file.open(exefile..".lc") then
39 dofile(exefile..".lc")
40 else
41 print(exefile..".lc not exist")
42 end
43 exefile=nil;luaFile = nil
44 collectgarbage()
程序第1、2行打印字符信息。
第3行定义编译后需要执行的lc文件名,注意不包含后缀“.lc”或者“.lua”。
第5行定义需要编译的lua文件名。
第6行是使用for循环完成多个文件的操作。
第7行判断文件是否存在,如果存在则执行编译。如果不存在则忽略。
第8行是关闭已经打开的文件。
第9~12行完成编译,自动生成“DoitCarControl.lc”文件。
第16~20行判断文件是否存在,如果存在则执行刚刚编译完成的lc文件。
第21~22行是回收内存。
sta.lua”文件源码如下:
1 print("Ready to Set up wifi mode")
2 wifi.setmode(wifi.STATION)
3
4 wifi.sta.config("MERCURY_1013","123456789")--ssid and password
5 wifi.sta.connect()
6 local cnt = 0
7 tmr.alarm(3, 1000, 1, function()
8 if (wifi.sta.getip() == nil) and (cnt < 20) then
9 print("Trying Connect to Router, Waiting...")
10 cnt = cnt + 1
11 else
12 tmr.stop(3)
13 if (cnt < 20) then print("Config done, IP is "..wifi.sta.getip())
14 else print("Wifi setup time more than 20s, Please verify wifi.sta.config() function. Then re-download the file.")
15 end
16 cnt = nil;
17 collectgarbage();
18 dofile("DoitCarControlSTA.lc");
19 end
20 end)
其中第4行是需要设置自己的无线路由器的SSID及密码,以便NodeMCU连接到该路由器。
第18行是在完成连接后执行DoitCarControlSTA.lc程序。
在DoitCarControlSTA.lua文件中完成GPIO端口初始化,TCP客户端的创建、周期性尝试连接,设置定时器对速度进行调节。在连接成功并收到数据后,对数据进行解析,实现DoitCar小车电机的控制。
源码如下。
122 --GPIO Define
123 function initGPIO()
124 --1,2EN D1 GPIO5
125 --3,4EN D2 GPIO4
126 --1A ~2A D3 GPIO0
127 --3A ~4A D4 GPIO2
128
129 gpio.mode(0,gpio.OUTPUT);--LED Light on
130 gpio.write(0,gpio.LOW);
131
132 gpio.mode(1,gpio.OUTPUT);gpio.write(1,gpio.LOW);
133 gpio.mode(2,gpio.OUTPUT);gpio.write(2,gpio.LOW);
134
135 gpio.mode(3,gpio.OUTPUT);gpio.write(3,gpio.HIGH);
136 gpio.mode(4,gpio.OUTPUT);gpio.write(4,gpio.HIGH);
137
138 pwm.setup(1,1000,1023);--PWM 1KHz, Duty 1023
139 pwm.start(1);pwm.setduty(1,0);
140 pwm.setup(2,1000,1023);
141 pwm.start(2);pwm.setduty(2,0);
142 end
143
144 --Control Program
145 print("Start DoitRobo Control");
146 initGPIO();
147
148 spdTargetA=1023;--target Speed
149 spdCurrentA=0;--current speed
150 spdTargetB=1023;--target Speed
151 spdCurrentB=0;--current speed
152 stopFlag=true;
153
154 tmr.alarm(1, 200, 1, function()
155 if stopFlag==false then
156 spdCurrentA=spdTargetA;
157 spdCurrentB=spdTargetB;
158 pwm.setduty(1,spdCurrentA);
159 pwm.setduty(2,spdCurrentB);
160 else
161 pwm.setduty(1,0);
162 pwm.setduty(2,0);
163 end
164 end)
165
166 local flagClientTcpConnected=false;
167 print("Start TCP Client");
168 tmr.alarm(3, 5000, 1, function()
169 if flagClientTcpConnected==false then
170 print("Try connect Server");
171 local conn=net.createConnection(net.TCP, false)
172 conn:connect(6005,"182.92.178.210");
173 conn:on("connection",function(c)
174 print("TCPClient:conneted to server");
175 flagClientTcpConnected = true;
176 end)
177 conn:on("disconnection",function(c)
178 flagClientTcpConnected = false;
179 conn=nil;
180 collectgarbage();
181 end)
182 conn:on("receive", function(conn, m)
183 print("TCPClient:"..m);
184 if string.sub(m,1,1)=="b" then
185 conn:send("cmd=subscribe&topic=".."car".."\r\n");
186 elseif string.sub(m,1,1)=="0" then --stop
187 pwm.setduty(1,0)
188 pwm.setduty(2,0)
189 stopFlag = true;
190 conn:send("ok\r\n");
191 elseif string.sub(m,1,1)=="1" then --forward
192 gpio.write(3,gpio.HIGH)
193 gpio.write(4,gpio.HIGH)
194 stopFlag = false;
195 conn:send("ok\r\n");
196 elseif string.sub(m,1,1)=="2" then --backward
197 gpio.write(3,gpio.LOW)
198 gpio.write(4,gpio.LOW)
199 stopFlag = false;
200 conn:send("ok\r\n");
201 elseif string.sub(m,1,1)=="3" then --left
202 gpio.write(3,gpio.LOW)
203 gpio.write(4,gpio.HIGH)
204 stopFlag = false;
205 conn:send("ok\r\n");
206 elseif string.sub(m,1,1)=="4" then --right
207 gpio.write(3,gpio.HIGH);
208 gpio.write(4,gpio.LOW);
209 stopFlag = false;
210 conn:send("ok\r\n");
211 elseif string.sub(m,1,1)=="6" then --A spdUp
212 spdTargetA = spdTargetA+50;if(spdTargetA>1023) then spdTargetA=1023;end
213 conn:send("ok\r\n");
214 elseif string.sub(m,1,1)=="7" then --A spdDown
215 spdTargetA = spdTargetA-50;if(spdTargetA《0) then spdTargetA=0;end
216 conn:send("ok\r\n");
217 elseif string.sub(m,1,1)=="8" then --B spdUp
218 spdTargetB = spdTargetB+50;if(spdTargetB>1023) then spdTargetB=1023;end
219 conn:send(spdTargetA.." "..spdTargetB.."\r\n");
220 elseif string.sub(m,1,1)=="9" then --B spdDown
221 spdTargetB = spdTargetB-50;if(spdTargetB<0) then spdTargetB=0;end
222 conn:send(spdTargetA.." "..spdTargetB.."\r\n");
223 else print("Invalid Command:"..m);end;
224 collectgarbage();
225 end)
226 end
227 end)
程序第1~21行定义initGPIO()函数,初始化GPIO口。
第25行执行initGPIO()函数。
第27~30行定义了四个变量,用于记录左轮、右轮的当前速度和目标设置速度。
第31行定义一个标志,用于记录停止状态。
第33~43行启动了周期性定时器1,每隔200毫秒对当前速度和目标速度进行计算,实现速度控制。其基本思路是:apk设置目标速度,程序在定时器中,将当前速度直接作为PWM占空比设置值进行输出。
第45行启用flagClientTcpConnected变量,记录TCP客户端的连接状态。
第47行启动周期性定时器3,每隔5秒钟检查一次TCP连接情况,并进行相应的处理。通过flagClientTcpConnected判断是否需要向服务器发起连接请求。本案例的服务器地址为:“182.92.178.210”,端口为“6005”。
第52~60行是分别注册了TCP客户端的“connection”和“disconnection”事件。
第61~104行是数据接收函数的实现代码。在这里面判断接收数据,并根据不同数据执行响应的动作。
其中,第64行是向服务器发送设备名称。当NodeMCU连接到远程服务器时,服务器会返回字符“b”,此时需要向服务器报告设备名称。该设备名称用于手机apk、网页或者微信控制。本教程设置名称为“tank”。
第103行,使用了collectgarbage()函数显示的回收内存。
程序运行Log如下。
30 NodeMCU 0.9.6 build 20150406 powered by Lua 5.1.4
31
32
33 ESP8266 Started
34 Compile File:sta.lua
35 Remove File:sta.lua
36 Compile File:DoitCarControlSTA.lua
37 Remove File:DoitCarControlSTA.lua
38 Ready to Set up wifi mode
39 > Trying Connect to Router, Waiting...
40 Trying Connect to Router, Waiting...
41 Config done, IP is 192.168.1.111
42 Start DoitRobo Control
43 Start TCP Client
44 Try connect Server
45 TCPClient:conneted to server
46 TCPClient:b
47
48 TCPClient:cmd=subscribe&res=1
49
50 Invalid Command:cmd=subscribe&res=1
51
52 TCPClient:1
53
54 TCPClient:2
55
56 TCPClient:3
57
58 TCPClient:4