如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?

简介

我们撰写了一系列关于连接到“物联网”(Internet of Things,IoT)以及与“物联网”配合工作的文章,这些文章难度较大,本文是其中的第二部分。第一部分文章在这里。这个项目相对不是很复杂,但是仍然需要使用安装Linux系统的终端。在尝试这个项目前,我们建议你最好已经拥有使用LinuxLua脚本语言的经验。这里是关于Lua脚本语言的文档,而这里是关于NodeMCU API的文档。请记住,文档是你的好朋友!

上一次,我们讨论了如何使用采用了开源工具源文件来定制和编译NodeMCU固件。假设我们已经安装好一个可以正常工作的NodeMCU固件,我们现在要探索该设备的真正功能,开始与“物联网”中的“网”以及“物”展开交互。首先,我们刷新NodeMCU固件,这个项目将通过Screen连接到设备,同时使用内置的Lua解释器以建立文件系统并连接到网络上。接下来,我们使用Luatool自动化脚本执行进程,同时使用普通的TCP服务器将所有内容整合到一起,且这个TCP服务器可以与脉宽调制LED、电位器以及开关交互。

所需设备:

· 运行Linux系统的电脑 (Ubuntu 15.04 Vivid Vervet)

· NodeMCU Devkit v0.9,且包含如下用户模组:Node, File, GPIO, WiFi, Net, PWM, Timer, ADC, UART 

· 可使用的WiFi

· 1个LED灯

· 1个100-300欧姆的电阻器

· 1个5K 电阻器

· 1个10K电位器

· 1个正常使用的瞬时开关


软件:

· Git​

· Python 2.7

· Pyserial

· Linux Screen

· Luatool 


要在Ubuntu 15.04上安装这些软件,请在终端上键入以下内容:

sudo apt-get install git screen python2.7 python-serial
mkdir -p ~/.opt && cd ~/.opt # just a user-owned directory for software. Replace with what suits your needs
git clone https://github.com/4refr0nt/luatool.git

第一次连接到NodeMCU

1) 连接到NodeMCU

所有与NodeMCU之间的交互都是通过波特率为9600的串口实现的。我建议使用Screen终端,你也可以使用Minicom或者其它任何能够与串口交互的程序。插入NodeMCU Devkit,然后输入:

screen /dev/ttyUSB0 9600


如果NodeMCU Devkit没有挂载到/dev/ttyUSB0请使用dmesg | grep tty找到它。

这将显示一个普通的黑色终端屏幕,且没有文字。一旦连接成功,按下带有USER标志的按钮,重新启动设备。或者在终端中键入以下内容,并按下回车键:

git clone https://github.com/4refr0nt/luatool.git
node.restart()

如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第1张图片


按下这里的小按钮

这个时候你应该有点儿糊涂了(不同波特率的通信),然后,欢迎信息和“>”符号出现了。现在,你已经进入了NodeMCULua解释器。由于这是一次全新的安装,而且我们也尚未向设备发送任何脚本,因此Init.lua这个错误的出现在我们的意料之中。一旦启动,NodeMCU 就会运行Init.lua(类似于Arduino中的setup()函数)。


如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第2张图片


解释器已经位于电脑中了?!就是这么简单!

 

2) 格式化节点文件系统

如果你的NodeMCU是新安装好的,在开始编写和保存Lua脚本文件之前,我们需要格式化NodeMCU的文件系统。在解释器中键入:


File.format() 


一旦完成,你可以通过使用调用file.fsinfo()查看文件系统上的信息:

remaining, used, total = file.fsinfo()
print("\nFile system info:\nTotal: "..total.." Bytes\nUsed: "..used.." Bytes\nRemaining: "..remaining.." Bytes\n")


这只会显示出 NodeMCU 文件系统的总大小、剩余空间以及已使用空间,而不会显示 ESP8266 的内存信息。ESP8266拥有64KiB的指令内存,96KiB的数据内存以及大约4MiB的外部闪存。


如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第3张图片


剩余3.5MB已经足够我们使用了。

注意:如果在键入指令时,你看到了类似于stdin:1: unexpected symbol near ‘char(27)’ 或者“stdin: bad header in precompiled chunk 的内容的话,请不必担心:这只是Screen出现的小问题。Hannes Lehmann 在他自己的网站上写到了这个问题,他说:“如果你遇到了下面的错误,比如‘stdin:1: unexpected symbol near ‘char(27) 或者 stdin: bad header in precompiled chunk’,而且你的终端也无法实现后退,而且也无法正常使用箭头键(或者是复制&粘贴,或者是你已经修改了要输入的内容),不要担心,重新输入指令即可。”

我对Screen进行了专门设置,因此Screen似乎可以使用后退键进行更正,但是当我尝试使用箭头键的时候,Screen遇到了麻烦。

 

Hello World, Hello WiFi!

3) 连接到无线网

由于ESP8266的主要卖点是它的WiFi协议栈,现在我们使用Lua 解释器连接到局域网并获得一个IP地址。

设备上可交互的Lua终端非常适合简单的代码。要连接到你的无线局域网并显示IP信息,请向终端中键入:

wifi.setmode(wifi.STATION)
wifi.sta.config("wifi_name","wifi_pass") -- Replace these two args with your own network
ip, nm, gw=wifi.sta.getip()
print("\nIP Info:\nIP Address: "..ip.." \nNetmask: "..nm.." \nGateway Addr: "..gw.."\n")


如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第4张图片

连接成功!

 

4) 用 Luatool 实现自动化

测试简单的代码感觉非常棒,但是如果你想编写一些更复杂的东西而且在启动时就自动运行的话,那该怎么做呢?Github用户 4refr0nt 编写了一个名为 Luatool的程序,该程序可以从你的电脑上把Lua脚本上传到NodeMCU Devkit,而且可以在设备的文件系统上保存脚本。导航至你一开始从Github中复制的Luatool文件夹:


cd ~/.opt/luatool/luatool


除了luatool.py: init.luamain.lua文件夹中应该还包括另外两份文件。使用你比较喜欢的编辑器,把不同的文件修改成下面这个样子:

 

                    -- init.lua --


-- Global Variables (Modify for your network)
ssid = "my_ssid"
pass = "my_pass"

-- Configure Wireless Internet
print('\nAll About Circuits init.lua\n')
wifi.setmode(wifi.STATION)
print('set mode=STATION (mode='..wifi.getmode()..')\n')
print('MAC Address: ',wifi.sta.getmac())
print('Chip ID: ',node.chipid())
print('Heap Size: ',node.heap(),'\n')
-- wifi config start
wifi.sta.config(ssid,pass)
-- wifi config end

-- Run the main file
dofile("main.lua")

                  -- main.lua --


-- Connect 
print('\nAll About Circuits main.lua\n')
tmr.alarm(0, 1000, 1, function()
   if wifi.sta.getip() == nil then
      print("Connecting to AP...\n")
   else
      ip, nm, gw=wifi.sta.getip()
      print("IP Info: \nIP Address: ",ip)
      print("Netmask: ",nm)
      print("Gateway Addr: ",gw,'\n')
      tmr.stop(0)
   end
end)

 -- Start a simple http server
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
  conn:on("receive",function(conn,payload)
    print(payload)
    conn:send("

Hello, NodeMCU!!!

"
)
end)
conn:on("sent",function(conn) conn:close() end) end)

5) 关闭当前的Screen会话(否则 Luatool 将无法与 NodeMCU 通信),然后将两个文件都上传至NodeMCU;

python luatool.py --port /dev/ttyUSB0 --src init.lua --dest init.lua --verbose
python luatool.py --port /dev/ttyUSB0 --src main.lua --dest main.lua --verbose

 

6) 使用Screen重新连接到NodeMCU Devkit,按下USER按钮以重新设置设备:


screen /dev/ttyUSB0 9600


然后你会看到如下内容:

如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第5张图片

访问这个IP地址(我的是192.168.168.10),好了,搞定!

如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第6张图片

检查一下吧!现在我们有了一个很小的服务器! 

 

现在让我们看看硬件设置

7) 搭建电路,上传服务器代码

如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第7张图片

这是电路图简图。因为我们主要处理的是软件问题,因此要相对简单。

 如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第8张图片 

Fritzing中硬件分布示意图

如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?_第9张图片

我个人的设置

 

现在,编辑之前获得的init.luamain.lua文件夹, 把它们修改成下面这个样子

                   -- init.lua --

-- Network Variables
ssid = "your_ssid"
pass = "your_pass"

-- Byline
print('\nAllAboutCircuits.com NodeMCU Example\n')

-- Configure Wireless Internet
wifi.setmode(wifi.STATION)
print('set mode=STATION (mode='..wifi.getmode()..')\n')
print('MAC Address: ',wifi.sta.getmac())
print('Chip ID: ',node.chipid())
print('Heap Size: ',node.heap(),'\n')

-- Configure WiFi
wifi.sta.config(ssid,pass)

dofile("main.lua")
                  
               -- main.lua --

----------------------------------
-- WiFi Connection Verification --
----------------------------------
tmr.alarm(0, 1000, 1, function()
   if wifi.sta.getip() == nil then
      print("Connecting to AP...\n")
   else
      ip, nm, gw=wifi.sta.getip()
      print("IP Info: \nIP Address: ",ip)
      print("Netmask: ",nm)
      print("Gateway Addr: ",gw,'\n')
      tmr.stop(0)
   end
end)


----------------------
-- Global Variables --
----------------------
led_pin = 1
sw_pin = 2
adc_id = 0 -- Not really necessary since there's only 1 ADC...
adc_value = 512

-- Amy from Gargantia on the Verdurous Planet
blink_open = "http://i.imgur.com/kzt3tO8.png"
blink_close = "http://i.imgur.com/KS1dPa7.png"
site_image = blink_open

----------------
-- GPIO Setup --
----------------
print("Setting Up GPIO...")
print("LED")
-- Inable PWM output
pwm.setup(led_pin, 2, 512) -- 2Hz, 50% duty default
pwm.start(led_pin)

print("Switch")
-- Enable input
gpio.mode(sw_pin, gpio.INPUT)

----------------
-- Web Server --
----------------
print("Starting Web Server...")
-- Create a server object with 30 second timeout
srv = net.createServer(net.TCP, 30)

-- server listen on 80, 
-- if data received, print data to console,
-- then serve up a sweet little website
srv:listen(80,function(conn)
	conn:on("receive", function(conn, payload)
		--print(payload) -- Print data from browser to serial terminal
	
		function esp_update()
            mcu_do=string.sub(payload,postparse[2]+1,#payload)
            
            if mcu_do == "Update+LED" then 
            	if gpio.read(sw_pin) == 1 then
            		site_image = blink_open
            		-- Adjust freq
            		pwm.setclock(led_pin, adc_value)
            		print("Set PWM Clock")	
        		elseif gpio.read(sw_pin) == 0 then
        			site_image = blink_close
        			-- Adjust duty cycle
        			pwm.setduty(led_pin, adc_value)
        			print("Set PWM Duty")	
            	end		
            end
            
            if mcu_do == "Read+ADC" then
            	adc_value = adc.read(adc_id)
            	-- Sanitize ADC reading for PWM
				if adc_value > 1023 then
					adc_value = 1023
				elseif adc_value < 0 then
					adc_value = 0
				end
				print("ADC: ", adc_value)
            end
        end

        --parse position POST value from header
        postparse={string.find(payload,"mcu_do=")}
        --If POST value exist, set LED power
        if postparse[2]~=nil then esp_update()end


		-- CREATE WEBSITE --
        
        -- HTML Header Stuff
        conn:send('HTTP/1.1 200 OK\n\n')
        conn:send('\n')
        conn:send('\n')
        conn:send('\n')
        conn:send('ESP8266 Blinker Thing\n')
        conn:send('

ESP8266 Blinker Thing!

\n'
) -- Images... just because conn:send(''" WIDTH="392" HEIGHT="196" BORDER="1">

\n'
) -- Labels conn:send('

ADC Value: '..adc_value..'


'
) conn:send('

PWM Frequency (Input High): '..adc_value..'Hz

'
) conn:send('

or

'
) conn:send('

PWM Duty Cycle (Input Low): '..(adc_value * 100 / 1024)..'%


'
) -- Buttons conn:send('
\n') conn:send('\n') conn:send('\n') conn:send('\n') conn:on("sent", function(conn) conn:close() end) end) end)

再一次关闭处于活动中的Screen会话,以便让Luatool工具将两个文件夹都上传到NodeMCU

python luatool.py --port /dev/ttyUSB0 --src init.lua --dest init.lua --verbose
python luatool.py --port /dev/ttyUSB0 --src main.lua --dest main.lua --verbose

8) 使用Screen重新连接到NodeMCU Devkit,按下USER按钮以重启设备: 

screen /dev/ttyUSB0 9600

这里正在发生什么?

当用户按下浏览器中的“Read ADC”时,浏览器将更新电位器的最新ADC读数;如果你打开了串行终端,该数值将会发送到串行终端。如果未按下按钮,输入引脚处于拉高状态,这就意味着最新的ADC读数将被用于设置LEDPWM频率。如果按下了按钮,并且输入引脚也被拉低LED的工作周期将会得到调整。根据设置的不同的LED参数,你将在浏览器中获得不同的图像。

现在,让我们深入挖掘代码,看一看这一切都是如何实现的。Init.lua包括了来自“Hello Word, Hello WiFi”部分的绝大多数代码。显示了关于芯片的信息,负责连接到无线网。Main.lua是所有有趣的事情发生的地方,它是对这些代码的改进。该脚本流程的目的是打印IP信息,初始化全局变量,配置I/O,以及创建一个监听80端口的TCP服务器。一旦按下按钮,浏览器将会调用一个HTTP POST方法。脚本中的string.find()方法搜索HTTP标头,并尝试找到任何提及一个名为“mcu_do”的按钮。如果发现了“mcu_do”按钮,将会调用esp_update()函数,根据分配到mcu_do的具体数值,它将会读取ADC读数或者更新LED的参数。就是这样,你将获得一些能够以有意义的方式与浏览器互动的硬件,反之亦然。 

 

结束语

我们可以利用ESP8266NodeMCU做很多事情,而本文提及的项目仅仅只是这些事情中的一个小部分。它可以充当MQTT代理,和运行TCP一样运行UDP,执行加密,通过I2CSPI与外围设备交互,以及其它许多功能。NodeMCU Devkit是功能强大的硬件设备,能够实现功能非常强大的IoT应用,但是,这并不意味着它是唯一一个或者甚至不是所有项目的最佳解决方案。请多加留意,因为在蓬勃发展的IoT领域,新的软硬件解决方案将不断涌现出来

 

版权声明:

本译文仅用于学习和交流目的。非商业转载请注明译者、出处,并保留文章在译言的完整链接。 

原文来源:http://www.allaboutcircuits.com/

原文标题:How to Make an Interactive TCP Server with NodeMCU on the ESP8266

原文地址:http://www.allaboutcircuits.com/projects/how-to-make-an-interactive-tcp-server-nodemcu-on-the-esp8266





你可能感兴趣的:(如何使用集成了ESP8266芯片的NodeMCU以配置一个可交互的TCP服务器?)