项目描述
Funpack第三季第五期活动,选用板卡 BeagleBone Black,实现任务2 -- 在系统中建立一个网页,并且与LED联动,使用网线连接到设备上时,可以从网页中控制 LED 的开关与闪烁。
1. 项目介绍
该项目采用 BeagleBone Black 板卡,运行 Linux 系统,采用 Python 编程语言,基于 Flask 框架建立网页,网页中展示4个LED的状态和开关,点击按钮可以控制对应 LED 的开关或者闪烁频率。
2. BeagleBone Black 板卡介绍
BeagleBone Black 是一款面向开发人员和业余爱好者的低成本、高扩展、社区支持的开发平台,板卡处理器选用的是 TI 公司 AM3358 芯片, 处理器集成了高达 1GHz 的 ARM Cortex™ A8 内核,具有强大的处理能力并提供了丰富的外设接口。BeagleBone® Black面向开源社区用户、开源硬件爱好者和任何对Arm Cortex-A8低成本处理器感兴趣的人而设计,配备精简,旨在为不需要搭建完整开发平台并想体验Arm处理器功能的用户提供入门的捷径。它配备了最低限度的外设,使用户能够体验处理器的强大功能,还提供了许多接口,用户还可以开发自己的电路板或添加自己的电路。
3. 设计思路
任务2拆分成以下3个要点:
- 系统中建立网页;
- 控制LED开关与闪烁;
- 前两项结合,通过网页控制LED;
3.1 系统中建立网页
考虑到我对于网页编程知之甚少,再加上编程语言的限制,只能从C语言和Python语言中找寻可以实现网页编程的框架。
以下是一些C语言的HTTP框架:
- BOA:Boa 是一个轻量级的 HTTP 服务器,非常适合嵌入式系统。它支持 CGI、SSL,并且资源占用非常小,适合资源受限的环境;
- THTTPD:THTTPD 是 ACME 公司设计的一款精巧的 HTTP 服务器,支持 HTTP/1.1、CGI、SSL等,具有独特的流量控制功能,适合需要安全且功能丰富的 HTTP 服务器的场景;
- Mini_httpd:Mini_httpd 是一个专门为嵌入式系统设计的轻量级 HTTP 服务器,支持静态文件服务,适合资源受限的环境;
- Lighttpd:Lighttpd 虽然是一个轻量级的 Web 服务器,但通常需要较少的资源,适合在资源受限的嵌入式系统中运行;
以下是一些Python语言的HTTP框架:
- Flask:Flask 是一个轻量级的 Web 框架,适合小型到中型的应用程序。它提供的基本工具和功能,是的开发Web应用变得简单;
- Django:Django 是一个高级的 Web 框架,适合大型和复杂的应用程序;
- FastAPI:FastAPI 是一个现代的、快速(高性能)的Web框架,基于 Starlette 和 Pydantic。它支持异步编程,并且自动生成 API 文档;
- Tornado:Tornado 是一个异步Web框架和网络库,适合需要处理大量并发连接的应用程序;
- Pyramid:Pyramid 是一个灵活的 Web 框架,适合各种规模的应用程序。它提供了最小的核心功能,并允许通过插件扩展;
思量之后最终选择了Python语言的Flask框架,一方面是Python程序方便编写和调试,另一方面 Flask 简单易于上手,教程多。
基于 Flask 的一个最简单的网页编程示例:
#!/usr/bin/python
# 导入库,必须的
from flask import Flask
# 实例化 Flask 对象
app = Flask(__name__)
# 注册一个处理函数,这个函数处理某个请求。Flask官方把这个函数叫做视图函数 view function
# 使用 app.route() 装饰器来为这个函数绑定对应的 URL
@app.route("/")
def hello():
return "Welcome to Flask!"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080, debug=True)
浏览器输入网址 http://192.168.1.102:8080/
打开网页如下所示:
Flask 入门
Flask 是典型的微框架,作为 Web 框架来说,它进保留了核心功能:请求响应处理和模版渲染。
视图函数
上文编写了一个简单的主页,主页的URL一般就是根地址,即 /
。当用户访问根地址的时候,我们需要返回一行欢迎文字。
在这个代码中我们从 flask
包导入 Flask
类,通过实例化这个类,创建一个程序对象 app
。接着,
我们要注册一个处理函数,这个函数就是处理某个请求的处理函数,Flask 官方把它叫做视图函数 (view function
),我们可以理解为 请求处理函数。所谓的注册,就是给这个函数带上一个装饰器帽子。我们使用 app.route()
装饰器来为这个函数绑定对应的URL,当用户在浏览器访问这个URL的时候,就会触发这个函数,获取返回值,并把返回值显示到浏览器窗口。
@app.route('/')
def hello():
return "Welcome to Flask!"
这段代码注册了一个视图函数 hello()
它绑定的 URL 是 /
,即用户在浏览器访问根地址时触发这个函数调用,返回 "Welcome to Flask!" 给用户。
【注】网页控制LED的原理与此类似,绑定一个视图函数到某个URL,当用户访问这个URL时,触发这个函数调用,执行开灯、关灯操作,并返回灯的实际状态给用户,刷新网页。
在前面,我们之所以把传入 app.route
装饰器的参数称为 URL 规则,是因为我们也可以在 URL 里定义变量部分。比如下面这个视图函数会处理所有类似 /user/<name>
的请求:
@app.route('/user/<name>')
def user_page(name):
return f'User page: {escape(name)}'
不论你访问 http://localhost:8080/user/apple
还是 http://localhost:8080/user/banana
都会触发这个函数。
此外,一个视图函数也可以绑定多个URL,这通过附加多个装饰器实现,例如:
@app.route('/')
@app.route('/index')
@app.route('/home')
def hello():
return 'Welcome to Flask'
这样无论访问 http://localhost:8080/
、 http://localhost:8080/index
还是 http://localhost:8080/home
都可以看到相同的页面。
模版
一般来说 Web 程序的页面是动态的,因为要根据不同用户、不同的情况进行调整,例如双十一和双十二需要展示不同的信息,因此页面需要动态生成。
在 Flask 框架中把包含变量和运算逻辑的 HTML 或其他格式的文本叫做模版(存放位置是:在模块同级目录的 templates 文件夹),这行这些变量替换和逻辑计算的过程称为渲染。
模版基本语法
模版中的 Jinja2 的语法和 Python 大致相同,常用的定界符如下:
{{ ... }}
用来标记变量;{% ... %}
用来标记语句,比如 if 语句和 for 语句等;{# ... #}
用来写注释;
渲染主页模版
使用 render_template()
函数可以把模版渲染出来。注意,必须传入模板文件名。还需要传入模版内部使用的变量通过关键字参数传入这个函数。
一个示例如下:
from flask import Flask, render_template # 注意必须导入 render_template
@app.route('/')
def index():
# user 为形参,name 是实参。模板文件 index.html 中使用 user 变量,实际传入变量为 name
return render_template('index.html', user=name)
关于 Flask 还有其他一些深入的、高级的用法,这里就不展开了。
3.2 控制LED开关与闪烁
我的思路是:用两种控制方法分别控制两组LED。
- 一组LED的开关通过GPIO控制,改变管脚电平实现LED的点亮与熄灭;
- 一组LED闪烁通过PWM方式调节周期和占空比;
方法一:GPIO 控制 LED
Linux 环境下可以通过 shell 命令操作 GPIO,也可以编程实现,都是通过读写 GPIO 系统文件实现的,这里以 shell 命令为例。
首先要明确两点:
- GPIO 必须先导出(export)然后才能读写(写1或者写0);
- 通过GPIO编号来访问,不是板卡上标注的 header pin,而是 header pin 对应的内部 GPIO 编号即 gpio1 或者 gpio2 等等;
以下 shell 示例以 GPIO50 为例,配置 GPIO 为输出模式,写1 写0 来点亮LED。
导出 gpio
# 注意:文件 /sys/class/gpio/export 仅有写权限,不可读
echo "50" > /sys/class/gpio/export
设置 gpio 为输出方向
# out 表示输出;in 表示输入
echo "out" > /sys/class/gpio/gpio50/direction
设置输出电平
写1 、写0来点亮 LED
echo "1" > /sys/class/gpio/gpio50/value
echo "0" > /sys/class/gpio/gpio50/value
释放 gpio
使用结束,释放 gpio 控制权,即 unexport
echo "50" > /sys/class/gpio/unexport
方法二:PWM 控制 LED
这里 P9_14 为例,对应 PWM 1 的通道 A,对应的系统文件为 /dev/bone/pwm/pwm1/a
。
管脚配置为 PWM 模式
在 BeagleBone Black 板卡上,一般管脚默认配置为 GPIO 模式,如果需要把某些管脚配置为 PWM 模式,需要使用命令 config-pin
进入配置。例如配置 P9_14 为 PWM 模式,输入以下即可:
config-pin P9_14 pwm
查询 P_14 是哪种模式:
config-pin -q P9_14
配置 P_14 为 GPIO 模式:
config-pin P9_14 gpio
使能 PWM
如下 shell 命令,参数为1表示使能 PWM,参数为0表示禁止 PWM
echo "1" > /dev/bone/pwm/1/a/enable
echo "0" > /dev/bone/pwm/1/a/enable
设置 PWM 周期
echo "100000000" > /dev/bone/pwm/1/a/period
设置 PWM 占空比
echo "50000000" > /dev/bone/pwm/1/a/duty_cycle
使用 PWM 的注意事项
- Linux 下 PWM 的周期和占空比是以纳秒为单位;
- 修改PWM的周期和占空比注意数值范围,某些板卡支持极小的周期和占空比,某些板卡不支持,修改参数会报错,调整数值到合理的范围即可;
- 占空比数值不能大于周期数值,所以代码中修改这两个数值时一定要注意先把占空比设置为0,设置为周期再设置占空比就不会报错;
3.3 通过网页控制LED
实现思路如下:
- 基于Flask的网页展示两组LED状态,一组是GPIO控制的,一组是PWM控制的;
- 网页中LED有对应的按钮可以控制其亮灭,即点击网页按钮,发送请求到网页服务器,网页服务器根据和参数执行代码控制对应的LED,例如LED亮灭或者修改PWM参数使LED闪烁;
- 网页执行完对应的代码,更新各个LED参数,并更新网页内容,组织网页内容重新发送给浏览器;
交互图如下:
4. 硬件介绍
板卡介绍
此次项目采用 BeagleBone Black 板卡 + 一块 NXP MAPS Dock 扩展板。
- BeagleBone Black 作为主控,给扩展板供电,并连接4个LED相关的管脚;
- NXP MAPS Dock 扩展板,此板卡没有主控,仅板载了一些常用外设,例如按键、LED、LCD显示屏、TouchPad、RS485、耳机、麦克风接口,这里仅用到4个LED;
在这个项目中可以把 NXP MAPS Dock 板卡看做一个已经连接器件的大号面包板。
4个LED
NXP MAPS Dock 板上的4个LED原理图如下,4个LED对应的管脚接低电平点亮、高电平熄灭。
4个LED对应 NXP MAPS Dock 板上的 CN 接口。
连接关系
两个板卡关于4个LED的连接关系如下表所示:
Dock 板 CN2 管脚 | BBB 管脚 |
---|---|
LED1 --> P22 | P9.11 --> GPIO_30 |
LED2 --> P21 | P9.13 --> GPIO_31 |
LED3 --> P20 | P9.15 --> GPIO_48 |
LED4 --> P19 | P9.14 --> PWM_1_4 |
BBB 的 GPIO 和 PWM 说明
BBB 板卡上的绝大部分管脚默认配置为GPIO,如下图所示:
其中 P9.11, P9.13 和 P9.15 分别对应 GPIO_30, GPIO_31 和 GPIO_48。这个对应关系非常重要,在通过系统文件控制GPIO时,必须对应 GPIO 编号。
其中 P9.14 可配置为 PWM 输出管脚,对应的是 PWM1 通道A。这个对应关系非常重要,在通过系统文件控制 PWM 是,必须对应 PMW 编号和通道。
软件流程图及主要代码说明
此项目采用了基于 Flask 的 Web 框架,在主程序中注册了4个视图函数,当浏览器请求某个URL时触发对应的视图函数,视图函数执行操作(LED开关或闪烁)并更新网页内容传递给浏览器。
软件主流程
- 第一步:实例化
app = Flask(__name__)
这个是必须的,后续注册视图函数、启动 Web 服务器都需要用到app
对象; - 第二步:注册4个视图函数,如上图所示,
/
对应浏览器刷新返回主页,/toggle/<led_name>
对应 led1/2/3 按钮单击时切换LED的状态,如点亮状态切换为关闭状态,关闭状态切换为点亮状态,/pwm/led4_fast
对应修改 PWM 参数,让 led4 快速闪烁,而/pwm_led4_slow
对应修改 PWM 参数,让 led4 慢速闪烁; - 第三步:在
main()
函数中调用 GPIO 和 PWM 的初始化参数,即让 led1/2/3 熄灭,让 led4 慢速闪烁; - 第四步:调用
app.run()
启动 Web 服务;
主要代码
入口函数
if __name__ == "__main__":
# 初始化 led1/2/3,通过GPIO方式控制,全部熄灭
gpio_led_init()
# Flask 启动时,默认开启 PWM 并设置为慢速闪烁
pwm_led_control(
"set", duty_cycle=800 * 1000 * 1000, period=1000 * 1000 * 1000
) # 默认慢速闪烁
app.run(host="0.0.0.0", port=8080, debug=False)
- 调用
gpio_led_init()
函数,初始化 led1/2/3,通过GPIO方式控制,全部熄灭; - 调用
pwm_led_control()
函数,初始化 led4,通过PWM方式控制,设置为慢速闪烁; - 调用
app.run()
函数,启动 Web 服务,端口号设置为 8080;
函数 gpio_led_init()
和 pwm_led_control()
详细的流程见下方。
/
视图对应的视图函数
@app.route("/")
def index():
global led4_mode
led_states = {
"led1": gpio_led_get_state(1),
"led2": gpio_led_get_state(2),
"led3": gpio_led_get_state(3),
"led4": (
LedStatus.ON
if os.path.exists(f"{PWM_PATH}/enable")
and open(f"{PWM_PATH}/enable").read().strip() == "1"
else LedStatus.OFF
),
}
return render_template("index.html", led_states=led_states, led4_mode=led4_mode, LedStatus=LedStatus)
当浏览器第一次访问这个网络又或者浏览器强制刷新,都会向Web服务器发起这个请求,Flask 框架进而调用这个视图函数 index()
。
- 获取全局变量
led4_mode
表示 led4 快闪还是慢闪; - 调用函数获取 led1/2/3/4 开关状态,其中 led1/2/3 都是调用函数
gpio_led_get_state()
通过 GPIO 方式获取点亮、熄灭状态,注意 NXP MAPS Dock 板卡上的4颗 LED 需要低电平点亮;其中 led4 的状态获取是通过读取 PWM 系统文件,如果PWM使能了,则认为点亮了; - 最后调用
render_template()
时传递 led1/2/3/4 的状态给模板,模版中的代码根据变量更新网页内容;
模版文件 index.html
说明见下文。
/toggle/<led_name>
对应的视图函数
@app.route("/toggle/<led_name>")
def gpio_led_toggle(led_name):
logging.info(f"--------------------------led_name = {led_name}")
# 提取 LED ID,确保在 1-3 之间
led_id = int(led_name[-1]) # 假设传入的 led_name 是 led1/led2/led3
if led_id in [1, 2, 3]:
oldState = gpio_led_get_state(led_id)
if oldState == LedStatus.ON: # 执行熄灭动作
led_states[led_name] = LedStatus.OFF
gpio_led_control(led_id, LedStatus.OFF.value)
else: # 执行点亮动作
led_states[led_name] = LedStatus.ON
gpio_led_control(led_id, LedStatus.ON.value)
return redirect(url_for("index")) # 重定向回首页
此 URL 绑定的视图函数为 gpio_led_toggle(led_name)
,其中参数来自 URL /toggle/<led_name>
的最后一部分。
此函数的主要逻辑如下:
- 取出
led_name
中的编号,即1
/2
/3
,赋值给led_id
; - 调用
gpio_led_get_state()
获取对应的 LED 的状态,如果之前的状态是点亮状态,则更新 LED 状态变量为熄灭,并调用函数gpio_led_control()
熄灭LED;如果之前的状态是熄灭,则更新 LED 状态变量为点亮,并调用函数gpio_led_control()
点亮 LED; - 最后调用
redirect(url_for("index"))
重定向到首页,即调用上面的/
对应的视图函数,返回主页给浏览器;
/pwm_led4_fast
对应的视图函数
# 控制 LED4 快速闪烁
@app.route("/pwm_led4_fast")
def pwm_led4_fast():
global led4_mode
logging.info(f"old led4 mode = {led4_mode}")
pwm_led_control(
"set", duty_cycle=50 * 1000 * 1000, period=100 * 1000 * 1000
) # 50ms 周期,50% 占空比
led4_mode = "fast" # 更新全局变量为快速模式
return redirect(url_for("index"))
此 URL 绑定的视图函数为 pwm_led4_fast()
,没有参数。当点击网页中 LED4 Fast 按钮触发此URL请求继而调用此函数。
函数逻辑如下:
- 调用
pwm_led_control()
函数修改 led4 对应的 PWM 参数,调整 duty_cycle 和 period,实现 led4 快速闪烁; - 设置全局变量
led4_mode = "fast"
; - 最后调用
redirect(url_for("index"))
重定向到首页,即调用上面的/
对应的视图函数,返回主页给浏览器;
其中的 pwm_led_control()
见下面的讲解。
/pwm_led4_slow
对应的视图函数
# 控制 LED4 慢速闪烁
@app.route("/pwm_led4_slow")
def pwm_led4_slow():
global led4_mode
logging.info(f"old led4 mode = {led4_mode}")
pwm_led_control(
"set", duty_cycle=800 * 1000 * 1000, period=1000 * 1000 * 1000
) # 1000ms 周期,80% 占空比
led4_mode = "slow" # 更新全局变量为慢速模式
return redirect(url_for("index"))
此 URL 绑定的视图函数为 pwm_led4_slow()
,没有参数。当点击网页中的 Slow 按钮触发此URL请求继而调用此函数。
函数逻辑如下:
- 调用
pwm_led_control()
函数修改 led4 对应的 PWM 参数,调整 duty_cycle 和 period,实现 led4 慢速闪烁; - 这是全局变量
led4_mode = "slow"
; - 最后调用
redirect(url_for("index"))
重定向到首页,即调用上面的/
对应的视图函数,返回主页给浏览器;
gpio_led_init()
函数
def gpio_led_init():
"""
初始化 LED 相关的3个GPIO管脚,最后关闭所有 LED
# 导出管脚
# 设置管脚方向为输出
# 管脚写1表示熄灭 LED
"""
for i in [1, 2, 3]: # 遍历 1/2/3
logging.info(f"i = {i}")
gpio_led_export(i)
gpio_led_set_dir_out(i)
gpio_led_control(i, LedStatus.OFF.value)
此函数对 led1/2/3 分别进行初始化,分别调用以下函数:
gpio_led_export()
即导出 GPIO 管脚,对应的 shell 命令为echo X > /sys/class/gpio/export
;gpio_led_set_dir_out()
即设置 GPIO 为输出方向,对应的 shell 命令为echo "out" > /sys/class/gpio/gpioX/direction
;gpio_led_control()
即设置 GPIO 管脚电平,例如输出低电平对应的 shell 命令为echo "0" > /sys/class/gpio/gpioX/value
pwm_led_control()
函数
def pwm_led_control(action, duty_cycle=None, period=None):
"""
控制 PWM 的开关或设置 PWM 的占空比和周期
:param action: 'on' 打开 PWM, 'off' 关闭 PWM, 'set' 设置 PWM 参数
:param duty_cycle: PWM 占空比 (单位:纳秒)
:param period: PWM 周期 (单位:纳秒)
"""
try:
logging.info(f"PWM Control Action: {action}, Period: {period}, Duty Cycle: {duty_cycle}")
if action == "on":
with open(f"{PWM_PATH}/enable", "w") as f:
f.write("1")
elif action == "off":
with open(f"{PWM_PATH}/enable", "w") as f:
f.write("0")
elif action == "set":
# 这一段逻辑非常关键,在修改 PWM 参数前先关闭,修改参数之后再使能 PWM
with open(f"{PWM_PATH}/enable", "w") as f:
f.write("0")
if (duty_cycle is not None) and (period is not None):
with open(f"{PWM_PATH}/duty_cycle", "w") as f:
f.write(str(0)) # 先把 duty_cycle 写成 0
if period is not None:
with open(f"{PWM_PATH}/period", "w") as f:
logging.info(f"Setting {f.name} = {period}")
f.write(str(period))
if duty_cycle is not None:
with open(f"{PWM_PATH}/duty_cycle", "w") as f:
logging.info(f"Setting {f.name} = {duty_cycle}")
f.write(str(duty_cycle))
with open(f"{PWM_PATH}/enable", "w") as f:
f.write("1")
except IOError as e:
logging.error(f"Error controlling PWM: {e}")
此函数设置 PWM 周期和占空比,实现 LED 的快闪和慢闪。
- 入口参数
action
取值 on 表示使能PWM,取值 off 表示禁止 PWM,取值 set 表示设置 PWM 的周期和占空比参数。
注意
- PWM的周期和占空比是以纳秒为单位,且不同的芯片支持的周期和占空比数值范围不一样,如果设置的参数超过了允许的范围则设置参数失败并产生报错信息;
- 修改占空比和周期,注意设置参数的顺序。如果占空比比周期大也会报错,所以上面的代码先把占空比设置为0,然后修改周期和占空比,这样更安全;
模版文件 index.html
上文提过,把包含变量和运算逻辑的 HTML 或者其他格式的文本叫做模版,Flask 框架把模版放在主程序同级目录的 templates 文件夹中,这里的模版文件名子为 index.html。网页的文字内容都在这个模版文件里,其中还包含一些变量。网页如果只包含文字会非常丑陋,因此我还添加了 CSS 定义。
- 网页标题放在
<head>
部分; - 网页引用的样式文件也在
head
部分进行声明,这里引用的样式文件路径为static/style.css
; - 网页正文在
<body>
部分,led1/2/3 用按钮表示,如果LED点亮了则引用led-button.on
样式,如果LED熄灭了,则引用led-button.off
样式;led4 单独在一块区域显示,添加两个按钮pwm_led4_fast
和pwm_led4_slow
,分别引用不同的样式; - 网页
footer
部分显示作者与版权;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Funpack3_5 - BBB - LED WebControl</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
</head>
<body>
<div class="container">
<h1>Funpack3_5 - BBB - LED Control Panel</h1>
<div class="button-container">
<form action="{{ url_for('gpio_led_toggle', led_name='led1') }}" method="get">
<button type="submit" class="led-button {% if led_states['led1'] == LedStatus.ON %}on{% else %}off{% endif %}">
LED1
</button>
</form>
<form action="{{ url_for('gpio_led_toggle', led_name='led2') }}" method="get">
<button type="submit" class="led-button {% if led_states['led2'] == LedStatus.ON %}on{% else %}off{% endif %}">
LED2
</button>
</form>
<form action="{{ url_for('gpio_led_toggle', led_name='led3') }}" method="get">
<button type="submit" class="led-button {% if led_states['led3'] == LedStatus.ON %}on{% else %}off{% endif %}">
LED3
</button>
</form>
</div>
<!-- LED4 控制区域 -->
<div class="led4-container">
<span id="lbl_led4">LED4 PWM</span>
<form action="{{ url_for('pwm_led4_fast') }}" method="get">
<button type="submit" id="pwm_led4_fast" class="led-button {% if led4_mode == 'fast' %}led4-on{% else %}off{% endif %}">Fast</button>
</form>
<form action="{{ url_for('pwm_led4_slow') }}" method="get">
<button type="submit" id="pwm_led4_slow" class="led-button {% if led4_mode == 'slow' %}led4-on{% else %}off{% endif %}">Slow</button>
</form>
</div>
</div>
<!-- 添加版权信息 -->
<footer>
<p>Author: Batman9527 | Copyright © MIT 20241015</p>
</footer>
</body>
</html>
样式文件 style.css
在 Flask 中 style.css 是一个用于存放 CSS 样式定义的外部文件。CSS 是一种用于描述 HTML 或者 XML 文档样式的语言。通过在 Flask 应用中使用 style.css 文件,可以将样式与内容分离,使得网页设计更加模块化和易于维护。
/* 公共按钮样式 */
.led-button {
padding: 15px 30px;
border-radius: 8px;
border: none;
cursor: pointer;
font-size: 16px;
margin: 5px;
height: 60px; /* 保证按钮高度一致 */
transition: background-color 0.3s, transform 0.2s;
}
.led-button:hover {
background-color: #f5f5f5;
}
.led-button:active {
transform: scale(0.98); /* 点击时按钮稍微缩小 */
}
.on {
background-color: red;
}
.off {
background-color: gray;
}
.led4-on {
background-color: yellow; /* LED4 快速和慢速闪烁的按钮颜色 */
}
/* Container 样式,居中标题 */
.container {
text-align: center; /* 标题居中对齐 */
margin-top: 20px;
}
/* 按钮容器样式 */
.button-container {
display: flex;
gap: 10px;
justify-content: center;
}
/* LED4 控制区域,保证和上面的 LED1/2/3 一致,并使 lbl_led4 和按钮水平对齐 */
.led4-container {
display: flex;
justify-content: center;
align-items: center; /* 确保 lbl_led4 和按钮在垂直方向上对齐 */
gap: 10px; /* 保证 UI 元素之间的间距一致 */
margin-top: 20px;
height: 60px;
}
/* 确保 LED4 控件的按钮高度与 LED1/2/3 一致 */
#pwm_led4_fast, #pwm_led4_slow {
height: 60px;
}
/* 确保 lbl_led4 标签的高度与按钮保持一致 */
#lbl_led4 {
font-size: 20px;
font-weight: bold;
height: 60px; /* 与按钮高度一致 */
display: flex;
align-items: center; /* 垂直居中对齐文本 */
}
/* 页脚样式,居中对齐 */
footer {
text-align: center;
margin-top: 20px;
padding: 10px;
}
功能展示及说明
1. shell 命令行启动Web服务
BBB 板卡通过USB供电,电脑就可以通过 192.168.7.2:8080
访问这个网页;如果 BBB 还插入了网线,例如它的IP地址为 192.168.1.102
,就可以通过 192.168.1.102:8080
访问这个网页。
2. 浏览器界面
浏览器第一次打开这个网址,界面如下,只有 led4 的慢闪按钮是黄色的,且板卡上只有黄色的 led4 在闪烁,其他3个 LED 都是熄灭状态。
任意点击 led1/2/3 按钮就可以切换它的状态,点亮是按钮是红色,熄灭是按钮是灰色,鼠标悬浮在按钮上按钮变白色。
点击 led4 的 Fast 或者 Slow 可以切换 led4 闪烁频率。
3. 视频演示
参见网址:
电子森林 Funpack第三季第五期活动,BeagleBone Black 网页点灯
心得体会
此次活动收获颇丰:
- 学习了BeagleBone Black板卡的使用,Linux 系统的命令行操作;
- 体会到了 Linux 中 一切皆文件 的思想,操作 GPIO 和 PWM 都是通过读写文件的方式进行;
- 学习了Flask框架,了解其中视图函数、模版、样式的概念,并写一个简单的网页;
这个项目还是有改进空间的,例如单击按钮后服务器只返回对应LED状态的更改而不需要刷新整个页面,这样可以节省传输文件的大小,减少出错的可能。