Node.js 和 Raspberry Pi - 带 WebSocket 的网络服务器


什么是 WebSocket?

WebSocket 支持通过网络进行实时双向通信。

WebSocket 可以与普通的 HTTP 服务器一起运行。您可以单击 Web 浏览器中的按钮,然后启用 Raspberry Pi 上的 GPIO,从而打开您家中的灯。一切都是实时的,并且是双向沟通的!

在本章中,我们将使用 WebSocket 设置一个 Web 服务器。然后创建一个浏览器 UI 来与我们之前的示例进行交互使用按钮打开和关闭 LED


我需要什么?

对于本教程,您需要一个 Raspberry Pi。在我们的示例中,我们使用 Raspberry Pi 3,但本教程应该适用于大多数版本。

为此,您需要:

单击上面列表中的链接以获取不同组件的说明。

笔记:您需要的电阻器可能与我们使用的不同,具体取决于您使用的 LED 类型。大多数小型 LED 仅需要一个小电阻,大约 200-500 欧姆。通常使用的具体值并不重要,但电阻值越小,LED 就会越亮。

与我们之前的示例相比,我们唯一需要的新东西是设置一个 Web 服务器,并安装 socket.io 模块。


适用于 Raspberry Pi 和 Node.js 的网络服务器

按照本 Node.js 教程前面的章节,让我们设置一个可以提供 HTML 文件的 Web 服务器。

在我们的 "nodetest" 目录中创建一个可用于静态 html 文件的新目录:

pi@w3demopi:~/nodetest $ mkdir public

现在让我们设置一个网络服务器。创建一个 Node.js 文件,该文件打开请求的文件并将内容返回给客户端。如果出现任何问题,请抛出 404 错误。

pi@w3demopi:~/nodetest $ nano webserver.js

网络服务器.js:

var http = require('http').createServer(handler); //require http server, and create server with function handler()
var fs = require('fs'); //require filesystem module

http.listen(8080); //listen to port 8080

function handler (req, res) { //create server
  fs.readFile(__dirname + '/public/index.html', function(err, data) { //read file index.html in public folder
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
      return res.end("404 Not Found");
    }
    res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
    res.write(data); //write data from index.html
    return res.end();
  });
}

转到文件夹"public":

pi@w3demopi:~/nodetest $ cd public

并创建一个 HTML 文件,index.html:

pi@w3demopi:~/nodetest/public $ nano index.html

索引.html:

<!DOCTYPE html>
<html>
<body>

<h1>Control LED light</h1>
<input id="light" type="checkbox">LED

</body>
</html>

该文件尚不具备任何功能。目前它只是一个占位符。让我们看看网络服务器是否正常工作:

pi@w3demopi:~/nodetest/public $ cd ..
pi@w3demopi:~/nodetest $ node webserver.js

使用 http://[RaspberryPi_IP]:8080/ 在浏览器中打开网站:

Web 服务器现在应该已启动并正在运行,我们可以继续讨论 WebSocket 部分。


为 Node.js 安装 socket.io

设置网络服务器后,将 Raspberry Pi 系统软件包更新到最新版本。

更新您的系统软件包列表:

pi@w3demopi:~ $ sudo apt-get update

将所有已安装的软件包升级到最新版本:

pi@w3demopi:~ $ sudo apt-get dist-upgrade

定期执行此操作将使您的 Raspberry Pi 安装保持最新状态。

要下载并安装最新版本的 socket.io,请使用以下命令:

pi@w3demopi:~ $ npm install socket.io --save


将 WebSocket 添加到我们的 Web 服务器

现在我们可以在我们的应用程序中使用 WebSocket。让我们更新我们的index.html 文件:

索引.html:

<!DOCTYPE html>
<html>
<body>

<h1>Control LED light</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> <!-- include socket.io client side script -->
<script>
var socket = io(); //load socket.io-client and connect to the host that serves the page
window.addEventListener("load", function(){ //when page loads
  var lightbox = document.getElementById("light");
  lightbox.addEventListener("change", function() { //add event listener for when checkbox changes
    socket.emit("light", Number(this.checked)); //send button status to server (as 1 or 0)
  });
});
socket.on('light', function (data) { //get button status from client
  document.getElementById("light").checked = data; //change checkbox according to push button on Raspberry Pi
  socket.emit("light", data); //send push button status to back to server
});
</script>

</body>
</html>

还有我们的 webserver.js 文件:

网络服务器.js:

var http = require('http').createServer(handler); //require http server, and create server with function handler()
var fs = require('fs'); //require filesystem module
var io = require('socket.io')(http) //require socket.io module and pass the http object (server)

http.listen(8080); //listen to port 8080

function handler (req, res) { //create server
  fs.readFile(__dirname + '/public/index.html', function(err, data) { //read file index.html in public folder
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
      return res.end("404 Not Found");
    }
    res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
    res.write(data); //write data from index.html
    return res.end();
  });
}

io.sockets.on('connection', function (socket) {// WebSocket Connection
  var lightvalue = 0; //static variable for current status
  socket.on('light', function(data) { //get light switch status from client
    lightvalue = data;
    if (lightvalue) {
      console.log(lightvalue); //turn LED on or off, for now we will just show it in console.log
    }
  });
});

让我们测试一下服务器:

pi@w3demopi:~ $ node webserver.js

使用 http://[RaspberryPi_IP]:8080/ 在浏览器中打开网站:

现在服务器应该将复选框的所有更改输出到 Raspberry Pi 上的控制台。

客户端正在将更改发送到服务器,并且服务器正在响应。

让我们添加按钮控制 LED来自上一章。


添加硬件并向客户端发送响应

让我们再次更新我们的 webserver.js 文件。我们将使用按钮控制 LED 章节中的大量代码。

网络服务器.js:

var http = require('http').createServer(handler); //require http server, and create server with function handler()
var fs = require('fs'); //require filesystem module
var io = require('socket.io')(http) //require socket.io module and pass the http object (server)
var Gpio = require('onoff').Gpio; //include onoff to interact with the GPIO
var LED = new Gpio(4, 'out'); //use GPIO pin 4 as output
var pushButton = new Gpio(17, 'in', 'both'); //use GPIO pin 17 as input, and 'both' button presses, and releases should be handled

http.listen(8080); //listen to port 8080

function handler (req, res) { //create server
  fs.readFile(__dirname + '/public/index.html', function(err, data) { //read file index.html in public folder
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
      return res.end("404 Not Found");
    }
    res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
    res.write(data); //write data from index.html
    return res.end();
  });
}

io.sockets.on('connection', function (socket) {// WebSocket Connection
  var lightvalue = 0; //static variable for current status
  pushButton.watch(function (err, value) { //Watch for hardware interrupts on pushButton
    if (err) { //if an error
      console.error('There was an error', err); //output error message to console
      return;
    }
    lightvalue = value;
    socket.emit('light', lightvalue); //send button status to client
  });
  socket.on('light', function(data) { //get light switch status from client
    lightvalue = data;
    if (lightvalue != LED.readSync()) { //only change LED if status has changed
      LED.writeSync(lightvalue); //turn LED on or off
    }
  });
});

process.on('SIGINT', function () { //on ctrl+c
  LED.writeSync(0); // Turn LED off
  LED.unexport(); // Unexport LED GPIO to free resources
  pushButton.unexport(); // Unexport Button GPIO to free resources
  process.exit(); //exit completely
});

让我们测试一下服务器:

pi@w3demopi:~ $ node webserver.js

使用 http://[RaspberryPi_IP]:8080/ 在浏览器中打开网站:

现在服务器应该将复选框的所有更改输出到 Raspberry Pi 上的控制台。

客户端正在将更改发送到服务器,并且服务器正在响应。

结束程序Ctrl+c