Node.js: Node.js và Raspberry Pi - Máy chủ web và WebSocket
WebSocket là gì?
Websocket là giao thức hỗ trợ giao tiếp hai chiều giữa client và server để tạo một kết nối trao đổi dữ liệu. Giao thức này không sử dụng HTTP mà thực hiện nó qua TCP. Mặc dù được thiết kế để chuyên sử dụng cho các ứng dụng web, lập trình viên vẫn có thể đưa chúng vào bất kì loại ứng dụng nào.
WebSocket có thể chạy cùng với một máy chủ HTTP thông thường. Bạn cũng có thể nhấp vào nút trong trình duyệt web và bật GPIO trên Raspberry Pi để bật đèn trong nhà bạn. Tất cả trong thời gian thực và với giao tiếp diễn ra theo cả hai cách! Trong phần này, chúng ta sẽ thiết lập một máy chủ web với WebSocket. Sau đó, tạo giao diện người dùng trình duyệt để tương tác với các phần trước đó về bật và tắt đèn LED bằng một nút bấm.
Vậy chúng ta cần những gì?
Đối với hướng dẫn này, bạn cần có Raspberry Pi. Trong các ví dụ sau đây thì ta sẽ sử dụng Raspberry Pi 3, nhưng hướng dẫn này sẽ hoạt động với hầu hết các phiên bản.
Để làm được điều này, bạn cần:
- Raspberry Pi với Raspian, internet, SSH, đã cài đặt Node.js
- Mô-đun bật tắt cho Node.js
- Mô-đun socket.io cho Node.js
- Breadboard (x1)
- Điện trở 68 Ohm (x1)
- Điện trở 1k Ohm (x1)
- Đèn LED xuyên lỗ (x1)
- Nút ấn (x1)
- Dây cắm có hai đầu ( đầu đực - đầu cái ) (x4)
- Dây cắm có hai đầu ( đầu đực - đầu đực ) (x1)
Nhấp vào liên kết trong danh sách trên để biết mô tả về các thành phần khác nhau.
Lưu ý: Điện trở bạn cần có thể khác với điện trở trong hướng dẫn, tùy thuộc vào loại đèn LED bạn sử dụng. Hầu hết các đèn LED nhỏ chỉ cần một điện trở nhỏ, khoảng 200-500 ohms. Nói chung, giá trị chính xác mà bạn sử dụng không quan trọng, nhưng giá trị của điện trở càng nhỏ thì đèn LED càng sáng.
Khác với các phần trước chúng ta đã làm, điều mới duy nhất ta cần là thiết lập máy chủ web và cài đặt mô-đun socket.io.
Máy chủ web cho Raspberry Pi và Node.js
Theo các phần trước trong hướng dẫn Node.js này, hãy thiết lập một máy chủ web có thể phục vụ các tệp HTML.
Trong thư mục "nodetest" , hãy tạo một thư mục mới mà chúng ta có thể sử dụng cho các tệp html tĩnh:
Bây giờ hãy thiết lập một máy chủ web. Tạo tệp Node.js mở tệp được yêu cầu và trả lại nội dung cho máy khách. Nếu có gì sai, hãy báo lỗi 404.
webserver.js:
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();
});
}
Chuyển đến thư mục "public":
Và tạo một tệp HTML, index.html:
index.html:
<html>
<body>
<h1>Control LED light</h1>
<input id="light" type="checkbox">LED
</body>
</html>
Tập tin này sẽ không có bất kỳ chức năng nào. Hiện tại nó chỉ là một trình để giữ chỗ. Giờ hãy xem thử máy chủ web có hoạt động không:
Mở trang web trong trình duyệt bằng http://[RaspberryPi_IP]:8080/:
Máy chủ web bây giờ sẽ hoạt động và chúng ta có thể chuyển sang phần WebSocket.
Cài đặt socket.io cho Node.js
Với máy chủ web được thiết lập, hãy cập nhật phần hệ thống Raspberry Pi của bạn lên phiên bản mới nhất.
Cập nhật danh sách gói hệ thống của bạn:
Nâng cấp tất cả các gói đã cài đặt của bạn lên phiên bản mới nhất:
Thực hiện việc này thường xuyên sẽ giúp cài đặt Raspberry Pi của bạn luôn được cập nhật.
Để tải xuống và cài đặt phiên bản mới nhất của socket.io, hãy sử dụng lệnh sau:
Thêm WebSocket vào máy chủ Web
Bây giờ chúng ta có thể sử dụng WebSocket trong ứng dụng của mình. Hãy cập nhật tệp index.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>
Và tệp webserver.js:
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
}
});
});
Hãy kiểm tra máy chủ:
Mở trang web trong trình duyệt bằng http://[RaspberryPi_IP]:8080/:
Bây giờ, máy chủ sẽ xuất tất cả các thay đổi đối với hộp kiểm sang bảng điều khiển trên Raspberry Pi. Máy khách đang gửi các thay đổi đến máy chủ và máy chủ đang phản hồi và hãy thêm đèn LED điều khiển bằng nút nhấn từ phần trước.
Thêm phần cứng và gửi phản hồi cho Máy khách
Hãy cập nhật lại tệp web server.js của bạn. Chúng ta sẽ sử dụng rất nhiều mã từ chương LED điều khiển bằng nút nhấn.
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
});
Hãy kiểm tra máy chủ:
Mở trang web trong trình duyệt bằng http://[RaspberryPi_IP]:8080/:
Bây giờ, máy chủ sẽ xuất tất cả các thay đổi đối với hộp kiểm sang bảng điều khiển trên Raspberry Pi. Máy khách đang gửi các thay đổi đến máy chủ và máy chủ đang phản hồi.
Kết thúc chương trình bằng Ctrl+c.