入坑树莓派-09-远程控制LED
1.前言
在之前的文章中(这一篇),我们手写了一个websocket服务器,websocket全双工、完全自定协议的优势让人无法抗拒,今天我们尝试用websocket做一个简单的应用–远程控制LED灯,方便我们了解其原理。
在这个例子中,我们将学会:
- 如何用python实现一个websocket服务器
- 如何用python和javascript实现json字符串的打包和解析
- 如何利用javascript实现页面局部动态刷新
2.架构
整体架构以及协议栈如下:
3.原理及代码
首先是页面部分,我们设想中的页面分为三个部分:
- 第一个部分用来展示一些实时的信息,叫做web_scr(web_screen)
- 第二个部分放置一些控制led灯的按钮,叫做web_ctl(web_control)
- 第三个部分防止指令执行的日志,叫做web_log
核心代码如下:
<div id="web_scr" class="web_scr"></div>
<div id="web_ctl" class="web_ctl">
<button id="btn_led_red_on">打开红灯</button>
<button id="btn_led_red_off">关闭红灯</button>
<button id="btn_led_yellow_on">打开黄灯</button>
<button id="btn_led_yellow_off">关闭黄灯</button>
<br>
<button id="btn_led_green_on">打开绿灯</button>
<button id="btn_led_green_off">关闭绿灯</button>
<button id="btn_led_blue_on">打开蓝灯</button>
<button id="btn_led_blue_off">关闭蓝灯</button>
<br>
<button id="btn_led_all_on">打开全部</button>
<button id="btn_led_all_off">关闭全部</button>
</div>
<div id="web_log" class="web_log">这个地方用来显示日志</div>
<style type="text/css">
.web_scr{font-size:20px; height: 200px; width: 600px; background-color: rgb(29, 241, 241);}
.web_ctl{font-size:20px; height: 100px; width: 600px; background-color: rgb(195, 183, 183);}
.web_log{font-size:20px; height: 200px; width: 600px; background-color: rgb(138, 233, 233);}
</style>
整体控制逻辑代码如下:
<script>
if (!window.WebSocket){
window.alert("您的浏览器不支持 WebSocket!");
};
var ws_addr = 'ws://10.42.0.1:8080';
var web_scr = document.getElementById('web_scr');
var web_btn_r_on = document.getElementById('btn_led_red_on')
var web_btn_y_on = document.getElementById('btn_led_yellow_on')
var web_btn_g_on = document.getElementById('btn_led_green_on')
var web_btn_b_on = document.getElementById('btn_led_blue_on')
var web_btn_r_off = document.getElementById('btn_led_red_off')
var web_btn_y_off = document.getElementById('btn_led_yellow_off')
var web_btn_g_off = document.getElementById('btn_led_green_off')
var web_btn_b_off = document.getElementById('btn_led_blue_off')
var web_btn_a_on = document.getElementById('btn_led_all_on')
var web_btn_a_off = document.getElementById('btn_led_all_off')
var web_led = {
"led_r": "off",
"led_y": "off",
"led_g": "off",
"led_b": "off",
};
var ws = new WebSocket(ws_addr);
// websocket回调函数
ws.onopen = function () {
web_log.innerText = '连接成功';
};
ws.onmessage = function (e) {
var obj = JSON.parse(e.data);
var str = "<table border=\"1\">";
str += "<tr><td>红灯状态</td><td>" + obj.led_r + "</td></tr>";
str += "<tr><td>黄灯状态</td><td>" + obj.led_y + "</td></tr>";
str += "<tr><td>绿灯状态</td><td>" + obj.led_g + "</td></tr>";
str += "<tr><td>蓝灯状态</td><td>" + obj.led_b + "</td></tr>";
str += "</table>";
web_scr.innerHTML = str;
};
ws.onclose = function (e) {
web_log.innerText = '连接断开' + e.reason;
};
ws.onerror = function (e) {
web_log.innerText = '连接失败';
};
// 按钮点击函数
web_btn_r_on.onclick = function(){
web_led["led_r"] = "on";
ws.send(JSON.stringify(web_led));
}
web_btn_y_on.onclick = function(){
web_led["led_y"] = "on";
ws.send(JSON.stringify(web_led));
}
web_btn_g_on.onclick = function(){
web_led["led_g"] = "on";
ws.send(JSON.stringify(web_led));
}
web_btn_b_on.onclick = function(){
web_led["led_b"] = "on";
ws.send(JSON.stringify(web_led));
}
web_btn_r_off.onclick = function(){
web_led["led_r"] = "off";
ws.send(JSON.stringify(web_led));
}
web_btn_y_off.onclick = function(){
web_led["led_y"] = "off";
ws.send(JSON.stringify(web_led));
}
web_btn_g_off.onclick = function(){
web_led["led_g"] = "off";
ws.send(JSON.stringify(web_led));
}
web_btn_b_off.onclick = function(){
web_led["led_b"] = "off";
ws.send(JSON.stringify(web_led));
}
web_btn_a_on.onclick = function(){
web_led["led_r"] = "on";
web_led["led_y"] = "on";
web_led["led_g"] = "on";
web_led["led_b"] = "on";
ws.send(JSON.stringify(web_led));
}
web_btn_a_off.onclick = function(){
web_led["led_r"] = "off";
web_led["led_y"] = "off";
web_led["led_g"] = "off";
web_led["led_b"] = "off";
ws.send(JSON.stringify(web_led));
}
</script>
代码非常地显而易见,我们不再解释了(JSON.stringify是将javascript的对象转换为json字符串的函数,JSON.parse则是做相反操作的函数)。
服务端也很简单,直接有现成的库,直接看代码吧:
import json
import asyncio
import websockets
import RPi.GPIO as GPIO
import time
def on_recv(message):
data = json.loads(message)
if data['led_r'] == 'on':
GPIO.output(LED_R_PIN, GPIO.HIGH)
if data['led_y'] == 'on':
GPIO.output(LED_Y_PIN, GPIO.HIGH)
if data['led_g'] == 'on':
GPIO.output(LED_G_PIN, GPIO.HIGH)
if data['led_b'] == 'on':
GPIO.output(LED_B_PIN, GPIO.HIGH)
if data['led_r'] == 'off':
GPIO.output(LED_R_PIN, GPIO.LOW)
if data['led_y'] == 'off':
GPIO.output(LED_Y_PIN, GPIO.LOW)
if data['led_g'] == 'off':
GPIO.output(LED_G_PIN, GPIO.LOW)
if data['led_b'] == 'off':
GPIO.output(LED_B_PIN, GPIO.LOW)
return json.dumps(data)
async def loop_func(websocket, path):
async for message in websocket:
rspstr = on_recv(message)
await websocket.send(rspstr)
async def main():
async with websockets.serve(loop_func, "0.0.0.0", 8080):
await asyncio.Future()
LED_R_PIN = 17
LED_Y_PIN = 27
LED_G_PIN = 22
LED_B_PIN = 5
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(LED_R_PIN, GPIO.OUT)
GPIO.setup(LED_Y_PIN, GPIO.OUT)
GPIO.setup(LED_G_PIN, GPIO.OUT)
GPIO.setup(LED_B_PIN, GPIO.OUT)
asyncio.run(main())
在python中,json字符串转python对象的函数是json.loads,做反向操作的函数是json.dumps。
运行起来之后,就能在网页上控制led灯的亮灭了: