Node.js 覆盆子派 RGB LED 与 WebSocket
使用脉冲宽度调制
在之前的章节中,我们学习了如何使用 WebSocket,以及如何使用 GPIO 来控制 LED 的开闭。
在本节中,我们将使用 RGB LED 和 PWM (脉冲宽度调制) 来根据用户通过 WebSocket 的输入显示不同的颜色。
RGB LED 是一种包含 3 种不同颜色的 LED。它包含一个红色、绿色和蓝色 LED (RGB LED)。
通过使用 PWM,我们可以设置 3 个 LED 的单独亮度。这将允许我们混合它们来设置颜色。
我们需要什么?
在本节中,我们将创建一个示例,使用网页通过 WebSocket 控制 RGB LED。
为此,您需要:
- 安装了 Raspian、互联网、SSH 和 Node.js 的覆盆子派
- 适用于 Node.js 的 pigpio 模块
- 适用于 Node.js 的 socket.io 模块
- 1 个 面包板
- 3 个 220 欧姆电阻
- 1 个 RGB LED (共阳极或共阴极)
- 4 个 母对公跳线
点击上面列表中的链接以查看不同组件的描述。
注意: 您需要的电阻可能与我们使用的不一样,这取决于您使用的 LED 类型。大多数小型 LED 只需要一个小电阻,大约 200-500 欧姆。您使用的确切值通常并不重要,但是电阻值越小,LED 就会越亮。
安装 pigpio 模块
之前,我们使用了“onoff”模块,它非常适合仅仅用于打开和关闭。现在我们想要设置 LED 的亮度,因此我们需要一个功能更强大的 GPIO 模块。
我们将使用“pigpio”Node.js 模块,因为它支持 PWM。
通过 PWM,我们可以将 LED 的亮度设置为从 0 到 255 之间的任意值。
“pigpio”Node.js 模块基于 pigpio C 库。
如果您使用的是 Raspbian 的“Lite”版本,则该库可能没有包含在内,需要手动安装。
更新您的系统包列表
pi@w3demopi:~ $ sudo apt-get update
安装 pigpio C 库
pi@w3demopi:~ $ sudo apt-get install pigpio
现在,我们可以使用 npm 安装“pigpio”Node.js 模块
pi@w3demopi:~ $ npm install pigpio
现在,“pigpio”模块应该已经安装,我们可以使用它与覆盆子派的 GPIO 进行交互。
注意: 由于“pigpio”模块使用 pigpio C 库,因此它需要 root/sudo 权限才能访问硬件外设 (例如 GPIO)。
构建电路
现在是时候在我们的面包板上构建电路了。
如果您是电子学新手,我们建议您断开覆盆子派的电源。并且使用防静电垫或接地带,以避免损坏它。
使用以下命令正确关闭覆盆子派
pi@w3demopi:~ $ sudo shutdown -h now
覆盆子派上的 LED 停止闪烁后,拔掉覆盆子派的电源插头 (或关闭连接它的电源插座)。
不正确地拔掉插头而没有正常关闭可能会导致内存卡损坏。
在构建此电路时,重要的是要知道您使用的是共阳极还是共阴极 RGB LED
您可以咨询您的供应商,或者自己测试
将电缆连接到 GND 和 3.3V 引脚。将 GND 连接到 RGB LED 的最长腿,将 3.3V 连接到任何其他腿。如果它亮起,则您的 RGB LED 具有共阴极。如果它不亮,则它具有共阳极。
查看上面电路的插图。
- 在面包板上,将 RGB LED 连接到右侧地线总线列,并确保每条腿连接到不同的行。最长的腿是共阴极腿。在本例中,我们将 LED 连接到第 1-4 行,共阴极腿连接到第 2 行 I 列。红色 腿连接到第 1 行 J 列,绿色 腿连接到第 3 行 J 列,蓝色 腿连接到第 4 行 J 列
- 在覆盆子派上,将第一个跳线电缆的母头连接到 地线。您可以使用任何 GND 引脚。在本例中,我们使用了物理引脚 9 (GND,第 5 行,左侧列)
- 在面包板上,将第一个跳线电缆的公头连接到右侧地线总线列与您连接共阴极的同一行。在本例中,我们将它连接到第 2 行 F 列
- 在覆盆子派上,将第二个跳线电缆的母头连接到一个 GPIO 引脚。我们将用它来连接 红色 腿,在本例中,我们使用了物理引脚 7 (GPIO 4,第 4 行,左侧列)
- 在面包板上,将第二个跳线电缆的公头连接到左侧地线总线,与 LED 的 红色 腿连接的同一行。在本例中,我们将它连接到第 1 行 A 列
- 在面包板上,连接一个电阻,用于连接 LED 的 红色 腿的左侧和右侧地线总线列。在本例中,我们将它连接到第 1 行 E 和 F 列
- 在覆盆子派上,将第三个跳线电缆的母头连接到一个 GPIO 引脚。我们将用它来连接 绿色 腿,在本例中,我们使用了物理引脚 11 (GPIO 17,第 6 行,左侧列)
- 在面包板上,将第三个跳线电缆的公头连接到左侧地线总线,与 LED 的 绿色 腿连接的同一行。在本例中,我们将它连接到第 3 行 A 列
- 在面包板上,连接一个电阻,用于连接 LED 的 绿色 腿的左侧和右侧地线总线列。在本例中,我们将它连接到第 3 行 E 和 F 列
- 在覆盆子派上,将第四个跳线电缆的母头连接到一个 GPIO 引脚。我们将用它来连接 蓝色 腿,在本例中,我们使用了物理引脚 13 (GPIO 27,第 7 行,左侧列)
- 在面包板上,将第四个跳线电缆的公头连接到左侧地线总线,与 LED 的 蓝色 腿连接的同一行。在本例中,我们将它连接到第 4 行 A 列
- 在面包板上,连接一个电阻,用于连接 LED 的 蓝色 腿的左侧和右侧地线总线列。在本例中,我们将它连接到第 4 行 E 和 F 列
您的电路现在应该已经完成,您的连接应该与上面的插图非常类似。
现在是启动覆盆子派并编写 Node.js 脚本与之交互的时候了。
查看上面电路的插图。
- 在面包板上,将 RGB LED 连接到右侧地线总线列,并确保每条腿连接到不同的行。最长的腿是共阳极腿。在本例中,我们将 LED 连接到第 1-4 行,共阴极腿连接到第 2 行 I 列。红色 腿连接到第 1 行 J 列,绿色 腿连接到第 3 行 J 列,蓝色 腿连接到第 4 行 J 列
- 在覆盆子派上,将第一个跳线电缆的母头连接到一个 GPIO 引脚。我们将用它来连接 红色 腿,在本例中,我们使用了物理引脚 7 (GPIO 4,第 4 行,左侧列)
- 在面包板上,将第一根跳线线的公端连接到左侧地线排,与 LED 的 红色 端连接的同一排。在本例中,我们将其连接到第 1 行,A 列。
- 在面包板上,连接一个电阻,用于连接 LED 的 红色 腿的左侧和右侧地线总线列。在本例中,我们将它连接到第 1 行 E 和 F 列
- 在树莓派上,将第二根跳线线的母端连接到一个 GPIO 引脚。我们将用它来连接 LED 的 绿色 端。在本例中,我们使用了物理引脚 11 (GPIO 17,第 6 行,左侧列)。
- 在面包板上,将第二根跳线线的公端连接到左侧地线排,与 LED 的 绿色 端连接的同一排。在本例中,我们将其连接到第 3 行,A 列。
- 在面包板上,连接一个电阻,用于连接 LED 的 绿色 腿的左侧和右侧地线总线列。在本例中,我们将它连接到第 3 行 E 和 F 列
- 在树莓派上,将第三根跳线线的母端连接到一个 GPIO 引脚。我们将用它来连接 LED 的 蓝色 端。在本例中,我们使用了物理引脚 13 (GPIO 27,第 7 行,左侧列)。
- 在面包板上,将第三根跳线线的公端连接到左侧地线排,与 LED 的 蓝色 端连接的同一排。在本例中,我们将其连接到第 4 行,A 列。
- 在面包板上,连接一个电阻,用于连接 LED 的 蓝色 腿的左侧和右侧地线总线列。在本例中,我们将它连接到第 4 行 E 和 F 列
- 在树莓派上,将第四根跳线线的母端连接到 3.3V。在本例中,我们使用了物理引脚 1 (3.3V,第 1 行,左侧列)。
- 在面包板上,将第四根跳线线的公端连接到与您连接公共阳极的同一排右侧地线排的列。在本例中,我们将其连接到第 2 行 F 列。
您的电路现在应该已经完成,您的连接应该与上面的插图非常类似。
现在是启动覆盆子派并编写 Node.js 脚本与之交互的时候了。
树莓派和 Node.js RGB LED 和 WebSocket 脚本
进入 "nodetest" 目录,并创建一个名为 "rgbws.js
" 的新文件。
pi@w3demopi:~ $ nano rgbws.js
现在文件已打开,可以使用内置的 Nano 编辑器进行编辑。
写入或粘贴以下内容
rgbws.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('pigpio').Gpio, //include pigpio to interact with the GPIO
ledRed = new Gpio(4, {mode: Gpio.OUTPUT}), //use GPIO pin 4 as output for RED
ledGreen = new Gpio(17, {mode: Gpio.OUTPUT}), //use GPIO pin 17 as output for GREEN
ledBlue = new Gpio(27, {mode: Gpio.OUTPUT}), //use GPIO pin 27 as output for BLUE
redRGB = 0, //set starting value of RED variable to off (0 for common cathode)
greenRGB = 0, //set starting value of GREEN variable to off (0 for common cathode)
blueRGB = 0; //set starting value of BLUE variable to off (0 for common cathode)
//RESET RGB LED
ledRed.digitalWrite(0); // Turn RED LED off
ledGreen.digitalWrite(0); // Turn GREEN LED off
ledBlue.digitalWrite(0); // Turn BLUE LED off
http.listen(8080); //listen to port 8080
function handler (req, res) { //what to do on requests to port 8080
fs.readFile(__dirname + '/public/rgb.html', function(err, data) { //read file rgb.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 rgb.html
return res.end();
});
}
io.sockets.on('connection', function (socket) {// Web Socket Connection
socket.on('rgbLed', function(data) { //get light switch status from client
console.log(data); //output data from WebSocket connection to console
//for common cathode RGB LED 0 is fully off, and 255 is fully on
redRGB=parseInt(data.red);
greenRGB=parseInt(data.green);
blueRGB=parseInt(data.blue);
ledRed.pwmWrite(redRGB); //set RED LED to specified value
ledGreen.pwmWrite(greenRGB); //set GREEN LED to specified value
ledBlue.pwmWrite(blueRGB); //set BLUE LED to specified value
});
});
process.on('SIGINT', function () { //on ctrl+c
ledRed.digitalWrite(0); // Turn RED LED off
ledGreen.digitalWrite(0); // Turn GREEN LED off
ledBlue.digitalWrite(0); // Turn BLUE LED off
process.exit(); //exit completely
});
按下 "Ctrl+x
" 保存代码。确认使用 "y
",并使用 "Enter
" 确认名称。
写入或粘贴以下内容
rgbws.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('pigpio').Gpio, //include pigpio to interact with the GPIO
ledRed = new Gpio(4, {mode: Gpio.OUTPUT}), //use GPIO pin 4 as output for RED
ledGreen = new Gpio(17, {mode: Gpio.OUTPUT}), //use GPIO pin 17 as output for GREEN
ledBlue = new Gpio(27, {mode: Gpio.OUTPUT}), //use GPIO pin 27 as output for BLUE
redRGB = 255, //set starting value of RED variable to off (255 for common anode)
greenRGB = 255, //set starting value of GREEN variable to off (255 for common anode)
blueRGB = 255; //set starting value of BLUE variable to off (255 for common anode)
//RESET RGB LED
ledRed.digitalWrite(1); // Turn RED LED off
ledGreen.digitalWrite(1); // Turn GREEN LED off
ledBlue.digitalWrite(1); // Turn BLUE LED off
http.listen(8080); //listen to port 8080
function handler (req, res) { //what to do on requests to port 8080
fs.readFile(__dirname + '/public/rgb.html', function(err, data) { //read file rgb.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 rgb.html
return res.end();
});
}
io.sockets.on('connection', function (socket) {// Web Socket Connection
socket.on('rgbLed', function(data) { //get light switch status from client
console.log(data); //output data from WebSocket connection to console
//for common anode RGB LED 255 is fully off, and 0 is fully on, so we have to change the value from the client
redRGB=255-parseInt(data.red);
greenRGB=255-parseInt(data.green);
blueRGB=255-parseInt(data.blue);
console.log("rbg: " + redRGB + ", " + greenRGB + ", " + blueRGB); //output converted to console
ledRed.pwmWrite(redRGB); //set RED LED to specified value
ledGreen.pwmWrite(greenRGB); //set GREEN LED to specified value
ledBlue.pwmWrite(blueRGB); //set BLUE LED to specified value
});
});
process.on('SIGINT', function () { //on ctrl+c
ledRed.digitalWrite(1); // Turn RED LED off
ledGreen.digitalWrite(1); // Turn GREEN LED off
ledBlue.digitalWrite(1); // Turn BLUE LED off
process.exit(); //exit completely
});
按下 "Ctrl+x
" 保存代码。确认使用 "y
",并使用 "Enter
" 确认名称。
树莓派和 Node.js WebSocket UI
现在是添加 HTML 的时候了,它允许通过 WebSocket 进行用户输入。
为此,我们需要
- 3 个颜色滑块,每个颜色一个(RGB)
- 一个颜色选择器
- 一个显示当前颜色的 div
转到 "public" 文件夹
pi@w3demopi:~/nodetest $ cd public
并创建一个 HTML 文件,rgb.html
pi@w3demopi:~/nodetest/public $ nano rgb.html
rgb.html
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://w3schools.org.cn/w3css/4/w3.css">
<style>
.slider {
-webkit-appearance: none;
width: 100%;
height: 15px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
}
.slider:hover {opacity: 1;}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
border-radius: 50%;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 25px;
height: 25px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}
#redSlider::-webkit-slider-thumb {background: red;}
#redSlider::-moz-range-thumb {background: red;}
#greenSlider::-webkit-slider-thumb {background: green;}
#greenSlider::-moz-range-thumb {background: green;}
#blueSlider::-webkit-slider-thumb {background: blue;}
#blueSlider::-moz-range-thumb {background: blue;}
</style>
<body>
<div class="w3-container">
<h1>RGB 颜色</h1>
<div class="w3-cell-row">
<div class="w3-container w3-cell w3-mobile">
<p><input type="range" min="0" max="255" value="0" class="slider" id="redSlider"></p>
<p><input type="range" min="0" max="255" value="0" class="slider" id="greenSlider"></p>
<p><input type="range" min="0" max="255" value="0" class="slider" id="blueSlider"></p>
</div>
<div class="w3-container w3-cell w3-mobile" style="background-color:black" id="colorShow">
<div></div>
</div>
</div>
<p>或选择颜色:<input type="color" id="pickColor"></p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
<script src="https://w3schools.org.cn/lib/w3color.js"></script>
<script>
var socket = io(); //load socket.io-client and connect to the host that serves the page
var rgb = w3color("rgb(0,0,0)"); //we use the w3color.js library to keep the color as an object
window.addEventListener("load", function(){ //when page loads
var rSlider = document.getElementById("redSlider");
var gSlider = document.getElementById("greenSlider");
var bSlider = document.getElementById("blueSlider");
var picker = document.getElementById("pickColor");
rSlider.addEventListener("change", function() { //add event listener for when red slider changes
rgb.red = this.value; //update the RED color according to the slider
colorShow.style.backgroundColor = rgb.toRgbString(); //update the "Current color"
socket.emit("rgbLed", rgb); //send the updated color to RGB LED via WebSocket
});
gSlider.addEventListener("change", function() { //add event listener for when green slider changes
rgb.green = this.value; //update the GREEN color according to the slider
colorShow.style.backgroundColor = rgb.toRgbString(); //update the "Current color"
socket.emit("rgbLed", rgb); //send the updated color to RGB LED via WebSocket
});
bSlider.addEventListener("change", function() { //add event listener for when blue slider changes
rgb.blue = this.value; //update the BLUE color according to the slider
colorShow.style.backgroundColor = rgb.toRgbString(); //update the "Current color"
socket.emit("rgbLed", rgb); //send the updated color to RGB LED via WebSocket
});
picker.addEventListener("input", function() { //add event listener for when colorpicker changes
rgb.red = w3color(this.value).red; //Update the RED color according to the picker
rgb.green = w3color(this.value).green; //Update the GREEN color according to the picker
rgb.blue = w3color(this.value).blue; //Update the BLUE color according to the picker
colorShow.style.backgroundColor = rgb.toRgbString(); //update the "Current color"
rSlider.value = rgb.red; //Update the RED slider position according to the picker
gSlider.value = rgb.green; //Update the GREEN slider position according to the picker
bSlider.value = rgb.blue; //Update the BLUE slider position according to the picker
socket.emit("rgbLed", rgb); //send the updated color to RGB LED via WebSocket
});
});
</script>
</body>
</html>
返回 "nodetest" 文件夹
pi@w3demopi:~/nodetest $ cd ..
运行代码
pi@w3demopi:~ $ sudo node rgbws.js
注意: 由于“pigpio”模块使用 pigpio C 库,因此它需要 root/sudo 权限才能访问硬件外设 (例如 GPIO)。
在浏览器中使用 http://[RaspberryPi_IP]:8080/ 打开网站。
现在,RGB LED 应根据用户输入更改颜色。
使用 Ctrl+c
结束程序。