成果贴
一、前言
由于笔者一直在单片机平台开发,虽然以前也玩过树莓派和ubuntu电脑,但是在linux平台开发依然算是第一次,因此给自己指定了一个比较简单的小目标,实际开发下来,在ai的加持下还算顺利,各位大神看个乐子就好。
二、项目介绍
1、项目计划
本项目旨在利用 BeagleBone Black Rev C 内置的 Linux 系统和网络功能,搭建一个轻量级 Web 服务器,用户只需在手机或电脑浏览器中访问 BBB 的 IP 地址,即可远程控制其 4板载用户 LED的亮灭状态。该系统无需额外硬件,仅使用 BBB 自身资源,相对简单。
2、实现情况
先在ubuntu电脑上实验和开发,使用ai辅助开发,待做出一个较为满意的状态后,再移植到开发板上。虽然开发过程中遇到很多不熟悉的内容,但是也算完美达到自己的预期了!
三、整体设计思路
架构
采用经典的前后端分离架构,app.py对应后端服务,index.html对应前端界面。
前端——动态生成4个LED控制按钮,每2s自动同步状态
后端——获取LED状态,切换LED状态,返回前端页面
debian——通过文件系统I/O来控制和查询LED状态
整体架构时序图
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Browser │ │ Flask │ │ Sysfs │ │ LED │
│ │ │ Server │ │ (文件) │ │ Hardware│
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
│ │ │ │
│ GET / │ │ │
│───────────────────>│ │ │
│ │ │ │
│ HTML Response │ │ │
│<───────────────────│ │ │
│ │ │ │
│ GET /api/led/status │ │
│───────────────────>│ │ │
│ │ read brightness │ │
│ │───────────────────>│ │
│ │ │ read LED state │
│ │ │<───────────────────│
│ │ [0,1,0,1] │ │
│ │<───────────────────│ │
│ JSON [0,1,0,1] │ │ │
│<───────────────────│ │ │
│ │ │ │
│ User clicks LED 1 │ │ │
│ │ │ │
│ POST /api/led/toggle/1 │ │
│───────────────────>│ │ │
│ │ write '1' │ │
│ │───────────────────>│ │
│ │ │ turn on LED │
│ │ │───────────────────>│
│ │ success │ │
│ │<───────────────────│ │
│ {"success":true} │ │ │
│<───────────────────│ │ │
│ │ │ │
│ (Every 2s) GET /api/led/status │ │
│───────────────────>│ │ │
│ │ read brightness │ │
│ │───────────────────>│ │
│ │<───────────────────│ │
│<───────────────────│ │ │
│ │ │ │
▼ ▼ ▼ ▼四、功能展示
网页界面:

对应实物图:

视频演示:哔哩哔哩——
实际代码:
# app.py
import os
from flask import Flask, render_template, jsonify
app = Flask(__name__)
# BBB 上的 4 个 LED 名称
LED_NAMES = [
"beaglebone:green:usr0",
"beaglebone:green:usr1",
"beaglebone:green:usr2",
"beaglebone:green:usr3"
]
LED_PATH = "/sys/class/leds/{}/brightness"
# 检测是否运行在 BBB 硬件上
def is_bbb_hardware():
return os.path.exists(LED_PATH.format(LED_NAMES[0]))
# 开发模式下模拟 LED 状态
SIMULATED_LEDS = [0, 0, 0, 0]
def get_led_status():
"""读取所有 LED 当前状态(0=关, 1=开)"""
if not is_bbb_hardware():
return SIMULATED_LEDS.copy()
status = []
for name in LED_NAMES:
try:
with open(LED_PATH.format(name), 'r') as f:
val = int(f.read().strip())
status.append(1 if val > 0 else 0)
except Exception as e:
print(f"Error reading {name}: {e}")
status.append(0)
return status
def set_led(index, state):
"""设置指定 LED 状态(0=关, 1=开)"""
if 0 <= index < len(LED_NAMES):
if not is_bbb_hardware():
SIMULATED_LEDS[index] = state
print(f"[DEV MODE] LED {index} -> {'ON' if state else 'OFF'}")
return True
name = LED_NAMES[index]
try:
with open(LED_PATH.format(name), 'w') as f:
f.write('1' if state else '0')
return True
except Exception as e:
print(f"Error writing to {name}: {e}")
return False
return False
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/led/status')
def led_status():
return jsonify(get_led_status())
@app.route('/api/led/toggle/<int:index>', methods=['POST'])
def toggle_led(index):
if 0 <= index < 4:
current = get_led_status()[index]
new_state = 1 - current
success = set_led(index, new_state)
if success:
return jsonify({"success": True, "state": new_state})
return jsonify({"success": False}), 400
if __name__ == '__main__':
if is_bbb_hardware():
print("BBB hardware detected - using real LEDs")
else:
print("Running in DEVELOPMENT mode - simulating LEDs")
app.run(host='0.0.0.0', port=5000, debug=False) <!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BBB LED 控制面板</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 40px;
background: #f0f0f0;
}
h1 {
color: #2c3e50;
}
.led-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
max-width: 400px;
margin: 30px auto;
}
.led-btn {
height: 80px;
font-size: 18px;
font-weight: bold;
border: none;
border-radius: 12px;
cursor: pointer;
transition: all 0.2s;
background: #ccc; /* 默认灰色 = 关 */
color: white;
}
.led-btn.on {
background: #2ecc71; /* 绿色 = 开 */
box-shadow: 0 0 15px #2ecc71;
}
.led-btn:active {
transform: scale(0.95);
}
.status {
margin-top: 20px;
font-size: 14px;
color: #7f8c8d;
}
</style>
</head>
<body>
<h1>BeagleBone Black LED 控制器</h1>
<div class="led-grid" id="ledGrid">
<!-- 按钮由 JS 动态生成 -->
</div>
<div class="status" id="status">正在加载状态...</div>
<script>
const ledCount = 4;
const grid = document.getElementById('ledGrid');
const statusEl = document.getElementById('status');
// 创建按钮
for (let i = 0; i < ledCount; i++) {
const btn = document.createElement('button');
btn.className = 'led-btn';
btn.textContent = `LED ${i}`;
btn.dataset.index = i;
btn.onclick = () => toggleLED(i);
grid.appendChild(btn);
}
// 获取当前状态并更新 UI
async function updateStatus() {
try {
const res = await fetch('/api/led/status');
const states = await res.json();
const buttons = document.querySelectorAll('.led-btn');
buttons.forEach((btn, i) => {
if (states[i] === 1) {
btn.classList.add('on');
} else {
btn.classList.remove('on');
}
});
statusEl.textContent = "状态已同步";
} catch (e) {
statusEl.textContent = "❌ 无法获取 LED 状态";
console.error(e);
}
}
// 切换 LED
async function toggleLED(index) {
statusEl.textContent = "切换中...";
try {
const res = await fetch(`/api/led/toggle/${index}`, {
method: 'POST'
});
const data = await res.json();
if (data.success) {
updateStatus();
} else {
statusEl.textContent = "❌ 操作失败";
}
} catch (e) {
statusEl.textContent = "❌ 网络错误";
console.error(e);
}
}
// 页面加载时获取状态
updateStatus();
// 每 2 秒自动同步一次(防止手动操作导致不一致)
setInterval(updateStatus, 2000);
</script>
</body>
</html>五、心得体会
由于笔者主要开发单片机,因此体验linux+web开发的感觉很不一样:
●要先安装一堆依赖,搞一堆环境;
●开发起来真快,很少的代码就能实现一个很炫的效果(相对单片机来说);
●涉及面广。
可能这就是封装和分层思想的一种体现吧,若论指令如何一条一条执行的,那这个项目是无比复杂的:网页、网络、python脚本、linux内核,硬件。每一个深入下去都深不见底,一个简单的点灯都能牵扯背负这么多。但是现代成熟的软件工程将之分解,使“点灯”不再复杂。
我要赚赏金
