入坑树莓派-09-远程控制LED

入坑树莓派-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灯的亮灭了:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注