Node.js 树莓派(Raspberry Pi) RGB LED 与 WebSocket

使用脉宽调制

在前面的章节中,我们学习了如何使用 WebSocket,以及如何使用 GPIO 来打开和关闭 LED。

在这一章中,我们将使用 RGB LED 和 PWM(脉冲宽度调制)根据用户通过 WebSocket 的输入显示不同的颜色。

RGB LED 是具有 3 种不同颜色的 LED。 它有一个红色、绿色和蓝色 LED (RGB LED)。

使用 PWM,我们可以设置 3 个 LED 的单独强度。 这将允许我们混合它们,设置颜色。


我们需要什么?

在本章中,我们将创建一个示例,通过 WebSocket 控制带有网页的 RGB LED。

为此,您需要:

单击上面列表中的链接了解不同组件的说明。

注释:您需要的电阻器可能与我们使用的电阻器不同,具体取决于您使用的 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" 模块,我们可以使用它来与树莓派(Raspberry Pi) 的 GPIO 交互。

注释:由于 "pigpio" 模块使用 pigpio C 库,它需要 root/sudo 权限才能访问硬件外围设备(如 GPIO)。


构建电路

现在是时候在我们的电路板上构建电路了。

如果您不熟悉电子产品,我们建议您关闭树莓派(Raspberry Pi) 的电源。 并使用防静电垫或接地带,以免损坏它。

使用以下命令正确关闭树莓派(Raspberry Pi) :

pi@w3demopi:~ $ sudo shutdown -h now

在树莓派(Raspberry Pi) 上的 LED 停止闪烁后,从树莓派(Raspberry Pi) 上拔下电源插头(或转动它所连接的电源板)。

在没有正常关机的情况下拔掉插头可能会导致存储卡损坏。

在构建此电路时,重要的是要知道您是否有共阳极或共阴极 RGB LED:

您可以咨询您的提供商,或自行测试:

将电缆连接到 GND 和 3.3V 引脚。 将 GND 连接到 RGB LED 的最长腿,将 3.3 V 连接到任何其他腿。 如果它亮起,则您的 RGB LED 有一个共阴极。 如果不是,它有一个共阳极。

树莓派(Raspberry Pi) 3 带实验电路板,RGB LED共阴极

看上面的电路图。

  1. 在电路板上,将 RGB LED 连接到右侧接地总线列,并确保每条腿连接到不同的行。最长的腿是共阴极腿。在此示例中,我们将 LED 连接到第 1-4 行,公共阴极脚连接到第 2 行第 I 列。RED 脚连接到第 1 行列 J , GREEN 腿连接到第 3 行 J 列, BLUE 腿连接到第 4 行列 J
  2. 在树莓派(Raspberry Pi) 上,将第一根跳线的母腿连接到 Ground。您可以使用任何 GND 引脚。在此示例中,我们使用了物理引脚 9(GND,第 5 行,左列)
  3. 在电路板上,将第一根跳线的公腿连接到右侧接地总线列的同一行,即您将公共阴极连接到的那一行。在此示例中,我们将其连接到第 2 行 F 列
  4. 在树莓派(Raspberry Pi) 上,将第二根跨接电缆的母腿连接到 GPIO 引脚。我们将把它用于 RED 腿,在这个例子中,我们使用物理引脚 7 (GPIO 4,行4,左栏)
  5. 在电路板上,将第二根跳线的公腿连接到左侧接地总线,与 LED 的 RED 腿连接的同一行。在此示例中,我们将其连接到第 1 行 A 列
  6. 在电路板上,在 LED 的 RED 支脚所在行的左右接地总线列之间连接一个电阻。在此示例中,我们将其附加到第 1 行、E 列和 F 列
  7. 在树莓派(Raspberry Pi) 上,将第三根跨接电缆的母腿连接到 GPIO 引脚。我们将它用于 GREEN 腿,在这个例子中我们使用物理引脚 11 (GPIO 17,行6,左栏)
  8. 在电路板上,将第三根跳线的公腿连接到左侧接地总线,与 LED 的 GREEN 腿连接的同一行。在此示例中,我们将其连接到第 3 行 A 列
  9. 在电路板上,在 LED 的 GREEN 脚行的左右接地总线列之间连接一个电阻。在此示例中,我们将其附加到第 3 行、E 列和 F 列
  10. 在树莓派(Raspberry Pi) 上,将第四根跨接电缆的母腿连接到 GPIO 引脚。我们将把它用于 BLUE 腿,在这个例子中我们使用物理引脚 13 (GPIO 27,行7,左栏)
  11. 在电路板上,将第四根跳线的公腿连接到左侧接地总线,与 LED 的 BLUE 腿连接的同一行。在此示例中,我们将其连接到第 4 行 A 列
  12. 在电路板上,在 LED 的 BLUE 腿所在行的左右接地总线列之间连接一个电阻器。在此示例中,我们将其附加到第 4 行、E 列和 F 列

您的电路现在应该已经完成​​,您的连接应该与上图非常相似。

现在是时候启动树莓派(Raspberry Pi),并编写 Node.js 脚本与之交互了。

树莓派(Raspberry Pi) 3 带实验电路板,RGB LED共阳极

看上面的电路图。

  1. 在电路板上,将 RGB LED 连接到右侧接地总线列,并确保每条腿连接到不同的行。最长的腿是共阳极腿。在此示例中,我们将 LED 连接到第 1-4 行,公共阴极脚连接到第 2 行第 I 列。RED 脚连接到第 1 行列 J ,GREEN 腿连接到第 3 行 J 列,而 BLUE 腿连接到第 4 行列J
  2. 在树莓派(Raspberry Pi) 上,将第一根跨接电缆的母腿连接到 GPIO 引脚。我们将把它用于 RED 腿,在这个例子中,我们使用物理引脚 7 (GPIO 4,行4,左栏)
  3. 在电路板上,将第一根跳线的公腿连接到左侧接地总线,与 LED 的 RED 腿连接的同一行。在此示例中,我们将其连接到第 1 行 A 列
  4. 在电路板上,在 LED 的 RED 支脚所在行的左右接地总线列之间连接一个电阻。在此示例中,我们将其附加到第 1 行、E 列和 F 列
  5. 在树莓派(Raspberry Pi) 上,将第二根跨接电缆的母腿连接到 GPIO 引脚。我们将它用于 GREEN 腿,在这个例子中我们使用物理引脚 11 (GPIO 17,行6,左栏)
  6. 在电路板上,将第二根跳线的公腿连接到左侧接地总线,与 LED 的 GREEN 腿连接的同一行。在此示例中,我们将其连接到第 3 行 A 列
  7. 在电路板上,在 LED 的 GREEN 脚行的左右接地总线列之间连接一个电阻。在此示例中,我们将其附加到第 3 行、E 列和 F 列
  8. 在树莓派(Raspberry Pi) 上,将第三根跨接电缆的母腿连接到 GPIO 引脚。我们将把它用于 BLUE 腿,在这个例子中我们使用物理引脚 13 (GPIO 27,行7,左栏)
  9. 在电路板上,将第三根跳线的公腿连接到左侧接地总线,与 LED 的 BLUE 腿连接的同一行。在此示例中,我们将其连接到第 4 行 A 列
  10. 在电路板上,在 LED 的 BLUE 腿所在行的左右接地总线列之间连接一个电阻器。在此示例中,我们将其附加到第 4 行、E 列和 F 列
  11. 在树莓派(Raspberry Pi) 上,将第四根跳线的母腿连接到 3.3V。在此示例中,我们使用了物理引脚 1(3.3V,第 1 行,左列)
  12. 在电路板上,将第四根跨接导线的公腿连接到右侧接地总线列的同一行,即您将公共阳极连接到该行。在此示例中,我们将其连接到第 2 行 F 列

您的电路现在应该已经完成​​,您的连接应该与上图非常相似。

现在是时候启动树莓派(Raspberry Pi),并编写 Node.js 脚本与之交互了。



树莓派(Raspberry Pi) 和 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" 确认名称。


树莓派(Raspberry Pi) 和 Node.js WebSocket UI

现在是时候添加允许用户通过 WebSocket 输入的 HTML。

为此我们想要:

  • 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://www.w3ccoo.com/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 Color</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>Or pick a color: <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://www.w3ccoo.com/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 ..

Run the code:

pi@w3demopi:~ $ sudo node rgbws.js

注释: 由于 "pigpio" 模块使用 pigpio C 库,因此它需要 root/sudo 权限才能访问硬件外围设备(如 GPIO)。

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

现在 RGB LED 应该会根据用户输入改变颜色。

Ctrl+c 结束程序。