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:
`++:. `-/+/
.` `/
接下来是安装我所需的工具,python
与nginx
,使用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实现并提供接口,实现后端
首先对于非sudo
或root
用户执行,需要对其进行提权
# 提权函数,如果当前用户不是 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): # 假设有4个LED,编号为0到3
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]: # 只接受0或1
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上写前后端,有成熟的方案,真爽