Funpack3-5 使用BeagleBone Black搭建前端和后端进行点灯
该项目使用了BeagleBone Black,实现了基于网页的点灯的设计,它的主要功能为:通过网页交互,控制板载LED灯。
标签
Flask
FunPack3-5
BBB
vue
yekai
更新2025-01-13
5

Funpack第三季第五期板卡一

本期Funpack给我们提供的板卡为BeagleBone® Black,是一块Linux板,我在本期完成的任务为:在系统中建立一个网页,并且与LED联动,使用网线连接到设备上时,可以从网页中控制LED的开关与闪烁。

刷写系统

我选择的是Arch Linux Arm系统,相应的刷写教程在其官网有提供,BeagleBone Black | Arch Linux ARM。需要注意的是由于Arch是滚动发行的策略,在根据教程刷写完毕后需要使用root用户进行pacman -Syu更新,否则可能会导致某些依赖问题,当然,推荐大陆用户进行换源。由于板卡性能原因,此操作可能需要等待一些时间。

[alarm@alarm ~]$ neofetch
                  -`                   alarm@alarm
                .o+`                   -----------
                `ooo/                   OS: Arch Linux ARM armv7l
               `+oooo:                 Host: TI AM335x BeagleBone Black
              `+oooooo:                 Kernel: 6.2.10-1-ARCH
              -+oooooo+:               Uptime: 28 mins
            `/:-:++oooo+:               Packages: 173 (pacman)
           `/++++/+++++++:             Shell: bash 5.2.37
          `/++++++++++++++:             Terminal: /dev/pts/0
         `/+++ooooooooooooo/`           CPU: Generic AM33XX (Flattened Device Tree) (1) @ 1.000GHz
        ./ooosssso++osssssso+`         Memory: 53MiB / 485MiB
      .oossssso-````/ossssss+`
      -osssssso.     :ssssssso.
    :osssssss/       osssso+++.
    /ossssssss/        +ssssooo/-
  `/ossssso+/:-       -:/+osssso+-
 `+sso+:-`                 `.-/+oso:
`++:.                           `-/+/
.`                                 `/

接下来是安装我所需的工具,pythonnginx,使用pyenv安装python 3.10.15。由于pyenv是编译安装,需要安装gcc,并可能需要打开较大的swap空间,并等待一些时间。

整体流程

我准备采用前后端分离的方案,前端调用后端提供的api,后端控制gpio

尝试点亮LED

对于板载的4个LED,已经被挂载在设备树中,以文件io的方式读写/sys/class/leds/beaglebone:green:usrX即可控制LED。由于一些权限问题,需要使用sudo或是root用户。

[root@alarm beaglebone:green:usr0]# pwd
/sys/class/leds/beaglebone:green:usr0
[root@alarm beaglebone:green:usr0]# echo 1 > brightness
[root@alarm beaglebone:green:usr0]# echo 0 > brightness

或是

[alarm@alarm beaglebone:green:usr0]$ sudo sh -c "echo 1 > brightness"
[alarm@alarm beaglebone:green:usr0]$ sudo sh -c "echo 0 > brightness"

使用Python实现并提供接口,实现后端

首先对于非sudoroot用户执行,需要对其进行提权

# 提权函数,如果当前用户不是 root,则重新以 sudo 权限运行当前脚本
def elevate_privileges():
   if os.geteuid() != 0:  # 检查当前是否为 root 用户
       print("Elevating privileges...")
       # 通过 sudo 重新运行当前脚本
       subprocess.call(['sudo', sys.executable] + sys.argv)
       sys.exit()  # 提权后退出当前进程,新的进程会以 sudo 权限运行

然后是LED的控制函数

# 控制LED亮灭的函数
def set_led_brightness(led, brightness):
   led_path = f"/sys/class/leds/beaglebone:green:usr{led}/brightness"
   try:
       with open(led_path, "w") as f:
           f.write(str(brightness))
   except PermissionError:
       print(f"Permission denied to write to {led_path}. Exiting...")
       sys.exit(1)

接着就是提供http.post接口进行控制,这边我使用Flask在5000端口上提供/api/ledX的接口

app = Flask(__name__)
CORS(app)
# API 端点,控制指定的 LED
@app.route('/api/led<int:led_id>', methods=['POST'])
def control_led(led_id):
   if led_id not in range(4):  # 假设有4LED,编号为03
       return jsonify({"error": "Invalid LED ID"}), 400

   data = request.json
   if 'state' not in data:
       return jsonify({"error": "Missing state field"}), 400

   state = data['state']
   if state not in [0, 1]:  # 只接受01
       return jsonify({"error": "Invalid state value, must be 0 or 1"}), 400

   set_led_brightness(led_id, state)
   return jsonify({"message": f"LED {led_id} set to {'on' if state == 1 else 'off'}"}), 200

实现前端

前端就简简单单使用Vite+Vue实现一下,提供4个选项用于控制LED,同时实现一下闪烁逻辑,使用对应API下发。

<template>
 <div id="app">
   <h1>BeagleBone LED 控制面板</h1>
   <div class="led-container">
     <div class="led-control" v-for="led in leds" :key="led.id">
       <h2>LED {{ led.id }}</h2>
       <select v-model="led.mode" @change="setLedMode(led.id)">
         <option value="off">关闭</option>
         <option value="on">打开</option>
         <option value="blink">闪烁</option>
       </select>
     </div>
   </div>
 </div>
</template>

<script lang="ts">
import { defineComponent, reactive } from 'vue';

interface Led {
 id: number;
 state: boolean;
 mode: 'off' | 'on' | 'blink';
}

export default defineComponent({
 setup() {
   const leds = reactive<Led[]>([
    { id: 0, state: false, mode: 'off' },
    { id: 1, state: false, mode: 'off' },
    { id: 2, state: false, mode: 'off' },
    { id: 3, state: false, mode: 'off' },
  ]);

   const setLedMode = async (ledId: number) => {
     const led = leds.find((l: { id: number; }) => l.id === ledId);
     if (!led) return;

     if (led.mode === 'on') {
       led.state = true;
       await sendLedState(led.id, 1);
    } else if (led.mode === 'off') {
       led.state = false;
       await sendLedState(led.id, 0);
    } else if (led.mode === 'blink') {
       blinkLed(led.id);
    }
  };

   const sendLedState = async (ledId: number, state: number) => {
     try {
       let url = "";
       if (window.location.hostname === "localhost") {
         console.log("debuging");
         url = "http://192.168.0.22:5000"
      } else {
         url = `http://${window.location.hostname}:5000`
      }
       const response = await fetch(`${url}/api/led${ledId}`, {
         method: 'POST',
         headers: {
           'Content-Type': 'application/json',
        },
         body: JSON.stringify({ state }),
      });

       if (!response.ok) {
         throw new Error(`Failed to toggle LED ${ledId}`);
      }
       console.log(`LED ${ledId} state set to ${state}`);
    } catch (error) {
       console.error(`Failed to toggle LED ${ledId}`, error);
       alert(`切换 LED ${ledId} 失败`);
    }
  };

   const blinkLed = (ledId: number) => {
     const led = leds.find((l: { id: number; }) => l.id === ledId);
     if (!led) return;

     led.state = !led.state;
     sendLedState(led.id, led.state ? 1 : 0);

     setTimeout(() => {
       if (led.mode === 'blink') {
         blinkLed(ledId);
      }
    }, 1000);
  };

   return {
     leds,
     setLedMode,
  };
},
});
</script>

<style>
#app {
 font-family: Avenir, Helvetica, Arial, sans-serif;
 text-align: center;
 margin-top: 40px;
}

.led-container {
 display: flex;
 justify-content: center;
 gap: 20px;
}

.led-control {
 border: 2px solid #333;
 border-radius: 10px;
 padding: 20px;
 width: 100px;
}

select {
 padding: 10px;
 font-size: 16px;
 cursor: pointer;
 border: 1px solid #ccc;
 border-radius: 5px;
}
</style>

在本机使用pnpm i安装依赖,pnpm run dev调试,pnpm build打包,打包后把dist中的内容放入bbb的/var/www,然后修改/etc/nginx/nginx.conf,指向我们的文件,使用sudo systemctl start nginx即可启动。

server {
      listen       80;
      server_name  192.168.0.22;

       #charset koi8-r;

       #access_log logs/host.access.log main;

       #location / {
       #root   /home/alarm/front;
       #   index index.html index.htm;
       #}

      root /var/www;
      index index.html;

      location / {
          try_files $uri $uri/ /index.html;
      }

       #error_page 404             /404.html;

       # redirect server error pages to the static page /50x.html
       #
      error_page   500 502 503 504 /50x.html;
      location = /50x.html {
          root   /usr/share/nginx/html;
      }
  }

然后就能访问bbb的网页控制LED了

功能展示

心得

在Linux上写前后端,有成熟的方案,真爽



附件下载
eetree-bbb-main.zip
团队介绍
一个人
团队成员
yekai
一个大三的电子小白
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号