1. 项目概述:一个能“记住”比赛的智能篮球架
几年前,我在车库和朋友们打一对一篮球时,常常为了记分争执不休。“刚才那个球算不算?”“现在比分多少了?”这类问题频繁打断酣畅淋漓的对决。作为一个嵌入式开发爱好者,我萌生了一个想法:为什么不做一个能自动计分、记录历史,甚至带点“仪式感”的智能篮球系统呢?这就是“智能篮球对决”项目的起源。
这个项目的核心,是打造一套用于一对一篮球比赛的完整智能计分系统。它不仅仅是在篮筐上装个传感器那么简单,而是一个融合了嵌入式硬件开发、传感器网络、后端数据处理和前端交互的微型物联网工程。系统以树莓派4作为“大脑”,通过红外传感器精准探测进球,用复古的7段数码管实时显示比分和时间,并将每一场比赛的详细数据——包括球员、得分、时间戳——都存入数据库。你甚至可以通过一个简单的网页,回顾你和朋友的所有对战历史,看看谁是真正的“车库球王”。
无论你是对Raspberry Pi和Python编程感兴趣的开发者,还是想将物联网技术应用于具体场景的创客,亦或是体育科技领域的探索者,这个项目都能为你提供一个从硬件选型、电路搭建、软件编程到系统集成的完整实践路径。它不仅解决了记分这个具体问题,更展示了如何用低成本、开源的方案,将一项传统的体育活动数字化、智能化。
2. 系统整体设计与核心思路拆解
2.1 核心需求与功能定义
在动手之前,我首先明确了系统必须完成的几项核心任务,这直接决定了后续的硬件选型和软件架构。
- 精准进球检测:这是系统的基石。检测必须快速、准确,且能区分有效进球与篮球擦碰篮网等干扰。误触发会严重影响游戏体验。
- 实时比分与时间显示:比赛过程中,球员需要一目了然地看到当前比分和剩余时间。显示设备需要在室外或光线复杂的车库环境下清晰可见。
- 比赛流程控制:需要能够设置比赛时间、开始/暂停/结束比赛、手动调整比分(应对争议球)、以及重置系统。
- 数据持久化与追溯:比赛数据不能随着断电而消失。系统需要记录每一场比赛的详细信息,并允许用户事后查询。
- 用户交互界面:提供一个简单直观的方式(非命令行)进行系统设置和查看历史数据。
基于这些需求,我放弃了使用简单单片机(如Arduino)的方案,因为它在数据存储、网络服务和复杂逻辑处理上较为吃力。树莓派4以其强大的通用计算能力、丰富的GPIO接口、内置Wi-Fi/蓝牙以及可运行完整Linux操作系统的特性,成为理想的主控单元。它既能直接驱动传感器和显示器,又能轻松搭建Web服务器和数据库。
2.2 硬件架构与选型逻辑
硬件是系统的骨架,每个部件的选型都经过了仔细考量。
- 主控单元:Raspberry Pi 4 Model B (2GB RAM)。选择4代是因为其性能足以流畅运行Python后端、轻量级数据库(如SQLite)和Apache Web服务器。2GB内存版本性价比高,完全满足本项目需求。相比3代,其更快的CPU和USB 3.0接口在需要处理多个传感器数据流或未来扩展时更有优势。
- 进球检测传感器:红外光电传感器(IR Sensor)。这是最关键的选择。我测试了压力传感器、声音传感器和摄像头图像识别等多种方案。
- 压力传感器:安装在篮筐或篮网,但篮球下落冲击力大,易损坏,且安装复杂。
- 声音传感器:通过识别“刷网声”判断,但环境噪音干扰极大,不可靠。
- 摄像头+OpenCV:方案最智能,能识别球体轨迹,但对树莓派算力要求高,在光线变化、快速运动中实现稳定检测的算法复杂度大。
- 红外光电传感器:最终选择。其原理是发射红外光,由接收管接收。当篮球穿过篮网下落时,会短暂遮挡红外光束,接收端电平变化,从而触发信号。优点是响应速度快(微秒级)、不受声音干扰、电路简单、成本低廉。关键技巧是必须将发射管和接收管精密地相对安装在篮筐下方,形成一道“光幕”,确保只有篮球穿过才能触发。
- 显示单元:双位7段数码管 & Adafruit 7段显示模块(带I2C背板)。
- 双位7段数码管:用于显示两位数的比分(如12:09)。直接由树莓派GPIO驱动,编程简单,显示亮度高,非常适合户外环境。
- Adafruit 7段显示模块(带HT16K33背板):用于显示比赛时间(如5:00)。选择带I2C背板的模块是一个重要的经验决策。直接驱动多位数码管需要占用大量GPIO引脚(每位需要8段+1小数点,共9个引脚,两位就需要18个),而树莓派的GPIO资源是宝贵的。使用I2C背板芯片(如HT16K33)后,只需要2根I2C总线引脚(SDA, SCL)就能控制多个数码管,极大地节省了GPIO资源,并且简化了布线。I2C通信也使得显示编程更加简洁。
- 输入控制单元:旋转编码器 & RFID读卡器。
- 旋转编码器:用于设置比赛时间。旋转调节数值,按下确认。相比按键(增加/减少),操作更直观、快捷,用户体验更好。
- RFID读卡器:这是“手动覆盖”功能的入口。当传感器误触发或出现争议球时,裁判(或玩家)可以用特定的RFID卡刷卡,触发一个手动计分或调整比分的模式。这增加了系统的容错性和灵活性,比单纯按一个物理按钮更酷,也更有仪式感。
- 供电与结构:系统采用5V/3A的USB-C电源为树莓派供电,树莓派的GPIO口可为传感器和显示器提供5V或3.3V电源。整个计分板外壳通过3D打印制作,将电子元件封装在内,起到保护和美观的作用。
注意:红外传感器在强日光直射下可能失效,因为阳光中含有大量红外线,会淹没传感器发射的信号。解决方案是:1. 选择调制型红外传感器(发射经过特定频率调制的红外光,接收端只解调该频率的信号);2. 为传感器加装遮光筒;3. 将系统安装在背光或室内环境。
2.3 软件架构分层设计
软件上,我采用了典型的分层架构,确保逻辑清晰,便于维护和扩展。
- 硬件驱动层(Python):直接与GPIO交互,封装了所有硬件操作。
ir_sensor.py:持续监听红外传感器引脚的电平变化,使用去抖动逻辑,将物理信号转化为“进球事件”。display.py:包含两个类,分别驱动原始的7段数码管比分显示和通过I2C控制的Adafruit时间显示模块。input.py:读取旋转编码器的旋转方向和按键状态,以及RFID读卡器的卡片ID。
- 核心逻辑层(Python):这是系统的大脑。
game_logic.py:维护比赛状态机(等待、进行中、暂停、结束),处理进球事件(为对应玩家加分),管理比赛倒计时,处理来自输入设备的控制命令(设置时间、开始、暂停、手动计分)。database.py:封装所有数据库操作,提供简洁的API供逻辑层调用,如record_goal(player_id),start_new_match(player1, player2)等。
- 数据持久层(SQLite):选择SQLite是因为它无需单独的数据库服务器,零配置,单个文件存储,非常适合树莓派上的嵌入式应用。数据库设计了多个表:
players:存储玩家ID和姓名。matches:存储比赛ID、开始时间、结束时间、获胜者。goals:存储每一个进球事件关联的比赛ID、球员ID、进球时间戳。这种设计便于后期分析每个球员的得分时间分布。
- 服务与接口层:
- Python后端服务:一个长期运行的Python主程序(如
main.py),它初始化所有硬件,启动事件循环,将硬件驱动层、核心逻辑层和数据持久层串联起来。 - Web前端(HTML/CSS/JS):通过Apache服务器托管一个简单的静态页面。该页面通过AJAX调用后端提供的RESTful API(可以用Python的Flask或FastAPI框架快速搭建),获取比赛历史数据并以图表或列表形式展示。
- Python后端服务:一个长期运行的Python主程序(如
这种架构使得硬件交互、业务逻辑、数据存储和用户界面相互解耦,任何一层的修改都不会轻易影响其他部分。
3. 核心细节解析与实操要点
3.1 红外传感器电路与防误触发算法
红外传感器的连接看似简单,但稳定性是关键。我使用的是常见的LM393比较器模块,它已经将红外接收管的信号进行了比较和整形,直接输出数字信号(高/低电平)。
接线方式:
- VCC -> 树莓派 5V
- GND -> 树莓派 GND
- OUT -> 树莓派 GPIO Pin (例如 GPIO17)
软件防误触发是重中之重。篮球穿过光幕的遮挡时间很短(约几十到几百毫秒),但传感器信号可能因抖动产生多个边沿。此外,球员的手或衣物偶然掠过也可能造成短暂遮挡。
我的处理代码如下,包含了硬件去抖动和软件逻辑去抖:
import RPi.GPIO as GPIO import time IR_PIN = 17 DEBOUNCE_TIME = 0.05 # 50毫秒去抖动时间 MIN_BLOCK_TIME = 0.1 # 最短有效遮挡时间(秒) MAX_BLOCK_TIME = 1.0 # 最长有效遮挡时间(秒) last_detection_time = 0 COOLDOWN = 0.5 # 两次有效检测之间的冷却时间 GPIO.setmode(GPIO.BCM) GPIO.setup(IR_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) # 启用内部上拉电阻 def goal_callback(channel): global last_detection_time current_time = time.time() # 冷却期检查,防止连续误触发 if current_time - last_detection_time < COOLDOWN: return # 检测到下降沿(开始遮挡) block_start = time.time() time.sleep(DEBOUNCE_TIME) # 等待一段时间避开抖动 if GPIO.input(IR_PIN) == GPIO.LOW: # 确认仍然是低电平 # 持续监测,直到信号恢复 while GPIO.input(IR_PIN) == GPIO.LOW: if time.time() - block_start > MAX_BLOCK_TIME: break # 遮挡时间过长,视为无效(可能是物体卡住) time.sleep(0.01) block_duration = time.time() - block_start # 判断是否为有效进球:遮挡时间在合理范围内 if MIN_BLOCK_TIME < block_duration < MAX_BLOCK_TIME: last_detection_time = current_time print(f“有效进球检测!遮挡时长:{block_duration:.3f}秒”) # 触发进球处理逻辑 process_goal() else: print(f“忽略干扰,遮挡时长:{block_duration:.3f}秒”) else: print(“抖动干扰,已忽略”) # 添加事件检测,下降沿触发 GPIO.add_event_detect(IR_PIN, GPIO.FALLING, callback=goal_callback, bouncetime=300) # bouncetime是硬件去抖参数 try: while True: time.sleep(10) except KeyboardInterrupt: GPIO.cleanup()实操心得:
pull_up_down=GPIO.PUD_UP至关重要。它确保传感器空闲时引脚处于确定的高电平状态,避免因悬空引入噪声误触发。bouncetime参数和软件中的DEBOUNCE_TIME构成了双重去抖屏障。MIN_BLOCK_TIME用于过滤掉飞虫等快速物体的干扰。MAX_BLOCK_TIME和COOLDOWN用于防止篮球卡在篮筐上或玩家持续遮挡传感器导致的连续错误计数。
3.2 多设备GPIO管理与电源规划
树莓派的GPIO引脚有限,需要精心规划。以下是我的引脚分配表:
| 设备 | 引脚类型 | 树莓派引脚 (BCM编号) | 功能说明 |
|---|---|---|---|
| 红外传感器输出 | 数字输入 | GPIO17 | 检测进球信号 |
| 旋转编码器CLK | 数字输入 | GPIO5 | 旋转方向判断A相 |
| 旋转编码器DT | 数字输入 | GPIO6 | 旋转方向判断B相 |
| 旋转编码器SW | 数字输入 | GPIO13 | 按键(按下接地) |
| RFID读卡器RST | 数字输出 | GPIO25 | 复位引脚 |
| RFID读卡器SDA | SPI接口 | GPIO10 (MOSI) | SPI数据通信 |
| Adafruit显示模块 | I2C接口 | GPIO2 (SDA), GPIO3 (SCL) | I2C通信,显示时间 |
| 7段数码管(比分)段选a-g | 数字输出 | GPIO18, GPIO23, GPIO24, GPIO25, GPIO12, GPIO16, GPIO20 | 控制7个段的亮灭 |
| 7段数码管(比分)位选1,2 | 数字输出 | GPIO21, GPIO26 | 选择显示十位或个位 |
电源规划注意事项:
- 切勿从GPIO引脚汲取大电流!树莓派单个GPIO引脚最大安全电流约为16mA,所有GPIO总电流有上限。驱动多个LED数码管时,电流消耗可能很大。
- 解决方案:对于7段数码管,务必使用ULN2003或晶体管阵列作为驱动电路,由树莓派GPIO提供控制信号,而数码管的电源(5V)直接从树莓派的5V引脚(非GPIO)或外部电源引入。Adafruit模块自带驱动,直接从I2C取电即可。
- 建议:使用一块小型面包板或PCB,将树莓派的5V和GND引出作为电源总线,所有模块的VCC和GND都接到总线上,而不是全部堆叠在树莓派的引脚上。
3.3 数据库设计与数据流
使用SQLite3,数据库文件为basketball.db。表结构设计如下:
-- 球员表 CREATE TABLE players ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 比赛表 CREATE TABLE matches ( id INTEGER PRIMARY KEY AUTOINCREMENT, player1_id INTEGER NOT NULL, player2_id INTEGER NOT NULL, start_time TIMESTAMP NOT NULL, end_time TIMESTAMP, winner_id INTEGER, -- 比赛结束时更新 FOREIGN KEY (player1_id) REFERENCES players (id), FOREIGN KEY (player2_id) REFERENCES players (id), FOREIGN KEY (winner_id) REFERENCES players (id) ); -- 进球事件表(核心事实表) CREATE TABLE goals ( id INTEGER PRIMARY KEY AUTOINCREMENT, match_id INTEGER NOT NULL, player_id INTEGER NOT NULL, -- 得分的球员 goal_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 进球发生的时间 FOREIGN KEY (match_id) REFERENCES matches (id), FOREIGN KEY (player_id) REFERENCES players (id) );数据流:
- 系统启动或新游戏开始时,
game_logic.py调用database.start_match(“PlayerA”, “PlayerB”)。 - 该函数首先在
players表中查找或创建两名玩家,然后在matches表中插入一条新比赛记录,状态为进行中。 - 当红外传感器触发有效进球事件,
process_goal()函数被调用,确定当前得分方后,调用database.record_goal(current_match_id, scoring_player_id),在goals表中插入一条记录。 - 比赛结束时,
game_logic.py调用database.end_match(match_id, winner_id),更新matches表的end_time和winner_id。
这种设计的好处是数据非常规范化,便于进行复杂的查询分析,例如:“查询玩家‘小明’在所有比赛中的平均得分”、“绘制某场比赛的得分时间折线图”等。
4. 实操过程与核心环节实现
4.1 树莓派系统配置与依赖安装
首先,为树莓派安装 Raspberry Pi OS Lite(无桌面版,更轻量)。使用 Raspberry Pi Imager 工具时,记得在设置中提前启用 SSH 和配置 Wi-Fi,方便无头启动。
系统关键配置(通过sudo raspi-config):
- Interface Options -> I2C -> Yes:启用I2C,用于驱动Adafruit显示模块。
- Interface Options -> SPI -> Yes:启用SPI,用于驱动RFID读卡器(RC522模块常用SPI接口)。
- Performance Options -> Overclock:可适当超频,确保程序响应流畅(非必须)。
基础软件安装:
# 更新系统 sudo apt update && sudo apt upgrade -y # 安装Python3及pip(通常已预装) sudo apt install python3-pip -y # 安装GPIO库、I2C工具和数据库驱动 sudo apt install python3-rpi.gpio i2c-tools sqlite3 -y pip3 install adafruit-circuitpython-ht16k33 # Adafruit显示模块驱动 pip3 install spidev # SPI支持 pip3 install mfrc522 # RFID读卡器常用库 # 安装Apache用于托管前端页面 sudo apt install apache2 -y # 将你的HTML/CSS/JS文件放到 /var/www/html/ 目录下4.2 核心Python程序结构与实现
主程序main.py负责统筹所有模块。这里展示一个简化的核心循环结构:
import time import threading from hardware.ir_sensor import IRSensor from hardware.display import ScoreDisplay, TimeDisplay from hardware.input import RotaryEncoder, RFIDReader from game_logic import GameLogic from database import Database class SmartBasketballSystem: def __init__(self): self.db = Database(‘basketball.db’) self.game = GameLogic(self.db) # 初始化硬件 self.score_display = ScoreDisplay() # 控制比分数码管 self.time_display = TimeDisplay() # 控制时间显示模块 self.ir_sensor = IRSensor(callback=self.on_goal_detected) self.encoder = RotaryEncoder(callback=self.on_encoder_event) self.rfid = RFIDReader(callback=self.on_rfid_detected) # 启动硬件监听线程 self.ir_sensor.start() self.encoder.start() self.rfid.start() def on_goal_detected(self): """红外传感器回调函数""" if self.game.state == “PLAYING”: scoring_player = self.game.current_offensive_player # 假设有进攻方逻辑 self.game.add_score(scoring_player) # 更新比分显示 score_a, score_b = self.game.get_score() self.score_display.show(score_a, score_b) def on_encoder_event(self, event_type, value): """旋转编码器回调:旋转或按下""" if event_type == “ROTATE”: if self.game.state == “SETUP”: self.game.adjust_setup_time(value) # value为+1或-1 self.time_display.show_setup_time(self.game.setup_time) elif event_type == “CLICK”: if self.game.state == “SETUP”: self.game.start_match() self.time_display.start_countdown(self.game.match_duration) def on_rfid_detected(self, card_id): """RFID卡回调,用于手动模式""" if card_id == “KNOWN_ADMIN_CARD_ID”: self.game.toggle_manual_mode() # 进入手动计分模式,可以通过编码器选择玩家并加分 def run(self): """主循环,更新时间显示等""" try: while True: if self.game.state == “PLAYING”: remaining_time = self.game.get_remaining_time() self.time_display.show_time(remaining_time) if remaining_time <= 0: self.game.end_match() # 显示获胜信息... time.sleep(0.1) # 主循环频率 except KeyboardInterrupt: self.cleanup() def cleanup(self): self.ir_sensor.stop() self.encoder.stop() self.rfid.stop() GPIO.cleanup() if __name__ == “__main__”: system = SmartBasketballSystem() system.run()关键点:
- 使用多线程或异步IO来处理并发的硬件事件(如传感器触发和编码器旋转),避免主循环被阻塞。
- 所有硬件操作都封装在各自的类中,通过回调函数与主逻辑通信,保持代码模块化。
GameLogic类维护着游戏状态机,是协调所有行为的核心。
4.3 3D打印外壳设计与装配
计分板的外壳我使用Fusion 360进行设计,主要考虑以下几点:
- 散热:树莓派运行时会产生热量,外壳需要设计通风孔。我在主板正上方和侧面设计了网格状孔洞。
- 接口访问:预留出电源接口、网线接口(如果需要)以及TF卡插槽的位置,方便维护。
- 显示屏开窗:为7段数码管和Adafruit模块的显示屏精确开窗,确保无遮挡。
- 传感器安装位:设计一个可调节的支架,用于将红外传感器发射和接收管固定在篮筐下方,并考虑走线槽。
- 模块化固定:内部设计支柱和卡槽,用于固定树莓派、面包板(或定制PCB)和电池(如果使用)。
将设计好的模型导出为STL文件,使用PLA材料进行3D打印。打印完成后,进行必要的打磨和组装。先将电子元件安装到外壳内,连接好线缆,最后合上盖子。这个过程需要耐心,确保线路整齐,避免短路。
5. 常见问题与排查技巧实录
在开发和调试过程中,我遇到了不少“坑”。这里记录下来,希望能帮你节省时间。
5.1 硬件连接与通信问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 7段数码管不亮或部分段不亮 | 1. 限流电阻缺失或过大。 2. GPIO引脚配置错误(输入/输出)。 3. 共阳/共阴接反。 | 1.确认数码管类型:用万用表二极管档测量。共阳极,所有段(a-g)的阳极连在一起;共阴极,所有段的阴极连在一起。我使用的是共阳极,因此公共端接5V,段选端通过GPIO输出低电平来点亮。 2.检查电路:每个段都必须串联一个220Ω-1kΩ的限流电阻,直接接到GPIO会烧毁引脚或LED。 3.编写测试程序:逐个点亮每一段,检查硬件连接。 |
| I2C设备(Adafruit显示模块)无法检测到 | 1. I2C未启用。 2. 接线错误(SDA, SCL接反)。 3. 设备地址错误。 | 1. 运行sudo i2cdetect -y 1查看I2C总线上的设备地址。如果看不到设备(通常HT16K33地址是0x70),检查接线和电源。2. 确认树莓派4的I2C1引脚是GPIO2 (SDA) 和 GPIO3 (SCL)。 3. 在代码中确认使用的地址与 i2cdetect显示的地址一致。 |
| 红外传感器一直触发或无触发 | 1. 环境光干扰(阳光、白炽灯)。 2. 传感器对准不准。 3. 上拉/下拉电阻未配置。 | 1.屏蔽环境光:用黑色热缩管或胶带包裹传感器头部,只留出前端开口。 2.精细对准:发射管和接收管必须严格对正。使用激光笔辅助对准是一个好办法。 3.配置内部上拉:在代码中设置 GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP),确保默认状态稳定。4.调整灵敏度:有些模块有电位器可以调节灵敏度,调到临界点附近。 |
| 旋转编码器读数跳变、不准 | 1. 去抖动处理不足。 2. 中断冲突或回调函数处理太慢。 | 1.硬件去抖:在CLK和DT引脚对地接104(0.1uF)电容。 2.软件去抖:在中断回调函数中增加 time.sleep(0.001)短暂延迟后再读取引脚状态,或者使用GPIO.add_event_detect的bouncetime参数。3.使用轮询替代中断:如果中断不稳定,可以改用主循环快速轮询编码器引脚状态,通过状态机判断旋转方向。 |
5.2 软件与逻辑问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
| 进球被重复计数 | 传感器去抖动逻辑不完善,或冷却时间设置太短。 | 1. 增加DEBOUNCE_TIME(例如到0.1秒)。2. 确保在 goal_callback函数开始处检查与上一次有效触发的时间差(COOLDOWN),我设置为0.5秒,因为连续两次进球的最短物理间隔不可能小于这个值。 |
| 比赛时间显示不准,走得忽快忽慢 | 使用time.sleep()在主循环中控制计时,但循环内其他操作耗时不稳定。 | 不要用sleep计时!改用基于时间戳的计时方式:python<br>start_time = time.time()<br>match_duration = 300 # 5分钟<br>while game_is_on:<br> elapsed = time.time() - start_time<br> remaining = match_duration - elapsed<br> display.show_time(remaining)<br> time.sleep(0.1) # 这个sleep只控制刷新频率,不影响计时精度<br> |
| 数据库文件被锁或写入失败 | 多线程同时写入数据库,或程序异常退出未关闭数据库连接。 | 1.使用连接池或单例模式:确保整个应用共享一个数据库连接,或使用SQLite的线程安全模式。 2.异常处理:在所有数据库操作外使用 try...except...finally,在finally块中确保关闭游标和连接(或使用with语句自动管理)。3.考虑使用WAL模式:在SQLite连接字符串中添加 ?mode=wal,可以提高并发性能。 |
| Web前端无法获取后端数据 | 跨域问题(CORS),或后端API服务未启动。 | 1.检查后端服务:确保Python后端程序正在运行,并监听正确的端口(如localhost:5000)。2.解决CORS:如果前端页面通过Apache服务(在80端口),后端API在另一个端口,浏览器会因同源策略阻止请求。在后端Flask/FastAPI应用中添加CORS支持: pip install flask-cors,然后初始化CORS(app)。3.使用相对路径或正确配置代理:让Apache作为反向代理,将 /api/路径的请求转发到后端服务。 |
5.3 系统集成与稳定性问题
- 问题:系统运行一段时间后树莓派死机或重启。
- 排查:可能是电源问题。树莓派4在高负载时峰值电流可能超过2.5A。劣质或功率不足的电源会导致电压下降,引发系统不稳定。
- 解决:使用官方电源或质量可靠的5V/3A以上USB-C电源。避免使用长而细的USB线,线损会导致电压不足。
- 问题:户外使用,屏幕在阳光下看不清。
- 解决:选择高亮度的7段数码管。对于Adafruit模块,可以在代码中将其亮度调到最高(通常是15)。考虑为计分板加一个小的遮阳罩。
- 问题:RFID卡偶尔无法识别。
- 排查:SPI速率可能过高,或者卡片与读卡器距离稍远。
- 解决:在初始化RFID读卡器时,尝试降低SPI总线速度。确保卡片与读卡器天线距离在几厘米以内。
这个项目从构思到实现,充满了调试和解决问题的过程。最大的收获不是做出了一个能用的计分器,而是完整地走通了一个嵌入式物联网产品的开发流程:从需求分析、方案选型、硬件设计、软件编程、调试排错到最终集成。每一个踩过的坑,都让最终的系统更加稳定可靠。当你和朋友在篮下挥汗如雨,而计分板忠实地记录着每一个进球,那份将技术融入生活的成就感,远胜于代码本身。