Node.js 和 Raspberry Pi - 带WebSocket的Web服务器
什么是WebSocket?
WebSocket 支持实时双向通信。
WebSocket 可以与常规HTTP服务器一起运行。你可以点击网页浏览器中的按钮,然后启用Raspberry Pi上的GPIO,从而打开你家中的灯。所有这些都是实时的,并且通信是双向的!
在本章中,我们将设置一个带WebSocket的Web服务器。然后创建一个浏览器UI来与我们之前关于用按钮控制LED开关的示例进行交互。
我需要什么?
本教程需要一台Raspberry Pi。在我们的示例中,我们使用的是Raspberry Pi 3,但本教程应该适用于大多数版本。
For this you need
- A Raspberry Pi with Raspian, internet, SSH, with Node.js installed
- Node.js 的 onoff 模块
- Node.js 的 socket.io 模块
- 1 x Breadboard
- 1 x 68 欧姆电阻
- 1 x 1k 欧姆电阻
- 1 x 直插 LED
- 1 x 按钮
- 4 x Female to male jumper wires
- 1 x 公对公跳线
Click the links in the list above for descriptions of the different components.
Note: The resistor you need can be different from what we use depending on the type of LED you use. Most small LEDs only need a small resistor, around 200-500 ohms. It is generally not critical what exact value you use, but the smaller the value of the resistor, the brighter the LED will shine.
与我们之前的示例相比,我们唯一需要新增的是设置一个Web服务器,并安装socket.io模块。
Raspberry Pi 和 Node.js 的 Web服务器
遵循本Node.js教程之前的章节,让我们设置一个可以提供HTML文件的Web服务器。
在我们的“nodetest”目录中,创建一个新目录,用于存放静态HTML文件
pi@w3demopi:~/nodetest $ mkdir public
现在让我们设置一个Web服务器。创建一个Node.js文件,该文件会打开请求的文件并将内容返回给客户端。如果出现任何错误,则抛出404错误。
pi@w3demopi:~/nodetest $ nano webserver.js
webserver.js
var http = require('http').createServer(handler); //需要http服务器,并用handler()函数创建服务器
var fs = require('fs'); //需要文件系统模块
http.listen(8080); //监听8080端口
function handler (req, res) { //创建服务器
fs.readFile(__dirname + '/public/index.html', function(err, data) { //读取public文件夹中的index.html文件
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'}); //错误时显示404
return res.end("404 Not Found");
}
res.writeHead(200, {'Content-Type': 'text/html'}); //写入HTML
res.write(data); //写入index.html中的数据
return res.end();
});
}
进入“public”文件夹
pi@w3demopi:~/nodetest $ cd public
然后创建一个HTML文件,index.html
pi@w3demopi:~/nodetest/public $ nano index.html
index.html
<!DOCTYPE html>
<html>
<body>
<h1>控制LED灯</h1>
<input id="light" type="checkbox">LED
</body>
</html>
这个文件现在还没有任何功能。目前它只是一个占位符。让我们看看Web服务器是否正常工作
pi@w3demopi:~/nodetest/public $ cd ..
pi@w3demopi:~/nodetest $ node webserver.js
使用 http://[RaspberryPi_IP]:8080/ 在浏览器中打开网站
现在Web服务器应该已经启动并运行,我们可以继续进行WebSocket部分了。
安装Node.js的socket.io
设置好Web服务器后,将你的Raspberry Pi系统包更新到最新版本。
Update your system package list
pi@w3demopi:~ $ sudo apt-get update
将所有已安装的软件包升级到最新版本
pi@w3demopi:~ $ sudo apt-get dist-upgrade
定期执行此操作可以保持你的Raspberry Pi安装的最新。
要下载并安装最新版本的socket.io,请使用以下命令
pi@w3demopi:~ $ npm install socket.io --save
为Web服务器添加WebSocket
现在我们可以在应用程序中使用WebSocket了。让我们更新index.html文件
index.html
<!DOCTYPE html>
<html>
<body>
<h1>控制LED灯</h1>
<p><input type="checkbox" id="light"></p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script> <!-- 包含socket.io客户端脚本 -->
<script>
var socket = io(); //加载socket.io-client并连接到提供页面的主机
window.addEventListener("load", function(){ //页面加载时
var lightbox = document.getElementById("light");
lightbox.addEventListener("change", function() { //添加事件监听器,用于复选框变化时
socket.emit("light", Number(this.checked)); //将按钮状态发送到服务器(作为1或0)
});
});
socket.on('light', function (data) { //从客户端获取按钮状态
document.getElementById("light").checked = data; //根据Raspberry Pi上的按钮更改复选框
socket.emit("light", data); //将按钮状态发送回服务器
});
</script>
</body>
</html>
还有我们的webserver.js文件
webserver.js
var http = require('http').createServer(handler); //需要http服务器,并用handler()函数创建服务器
var fs = require('fs'); //需要文件系统模块
var io = require('socket.io')(http) //需要socket.io模块并传递http对象(服务器)
http.listen(8080); //监听8080端口
function handler (req, res) { //创建服务器
fs.readFile(__dirname + '/public/index.html', function(err, data) { //读取public文件夹中的index.html文件
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'}); //错误时显示404
return res.end("404 Not Found");
}
res.writeHead(200, {'Content-Type': 'text/html'}); //写入HTML
res.write(data); //写入index.html中的数据
return res.end();
});
}
io.sockets.on('connection', function (socket) {// WebSocket连接
var lightvalue = 0; //静态变量,用于存储当前状态
socket.on('light', function(data) { //从客户端获取灯的开关状态
lightvalue = data;
if (lightvalue) {
console.log(lightvalue); //打开或关闭LED,目前我们只在控制台中显示它
}
});
});
让我们测试一下服务器
pi@w3demopi:~ $ node webserver.js
使用 http://[RaspberryPi_IP]:8080/ 在浏览器中打开网站
现在服务器应该将所有复选框的变化输出到Raspberry Pi上的控制台中。
客户端正在发送变化到服务器,服务器正在响应。
让我们添加由按钮控制的LED,这是我们之前章节的内容。
添加硬件,并向客户端发送响应
让我们再次更新webserver.js文件。我们将使用“按钮控制LED”章节中的许多代码。
webserver.js
var http = require('http').createServer(handler); //需要http服务器,并用handler()函数创建服务器
var fs = require('fs'); //需要文件系统模块
var io = require('socket.io')(http) //需要socket.io模块并传递http对象(服务器)
var Gpio = require('onoff').Gpio; //包含onoff以与GPIO交互
var LED = new Gpio(4, 'out'); //使用GPIO引脚4作为输出
var pushButton = new Gpio(17, 'in', 'both'); //使用GPIO引脚17作为输入,并处理'both'(按钮按下和释放)事件
http.listen(8080); //监听8080端口
function handler (req, res) { //创建服务器
fs.readFile(__dirname + '/public/index.html', function(err, data) { //读取public文件夹中的index.html文件
if (err) {
res.writeHead(404, {'Content-Type': 'text/html'}); //错误时显示404
return res.end("404 Not Found");
}
res.writeHead(200, {'Content-Type': 'text/html'}); //写入HTML
res.write(data); //写入index.html中的数据
return res.end();
});
}
io.sockets.on('connection', function (socket) {// WebSocket连接
var lightvalue = 0; //静态变量,用于存储当前状态
pushButton.watch(function (err, value) { //观察pushButton上的硬件中断
if (err) { //如果发生错误
console.error('There was an error', err); //将错误消息输出到控制台
return;
}
lightvalue = value;
socket.emit('light', lightvalue); //将按钮状态发送给客户端
});
socket.on('light', function(data) { //从客户端获取灯的开关状态
lightvalue = data;
if (lightvalue != LED.readSync()) { //仅当状态发生变化时才更改LED
LED.writeSync(lightvalue); //打开或关闭LED
}
});
});
process.on('SIGINT', function () { //当按下Ctrl+C时
LED.writeSync(0); //关闭LED
LED.unexport(); //取消导出LED GPIO以释放资源
pushButton.unexport(); //取消导出Button GPIO以释放资源
process.exit(); //完全退出
});
让我们测试一下服务器
pi@w3demopi:~ $ node webserver.js
使用 http://[RaspberryPi_IP]:8080/ 在浏览器中打开网站
现在服务器应该将所有复选框的变化输出到Raspberry Pi上的控制台中。
客户端正在发送变化到服务器,服务器正在响应。
End the program with Ctrl+c
.