一个基于 MQTT 的嵌入式节点管理系统,支持三种游戏模式(征服/占领/爆破),集成实时监控、音效播报、游戏状态管理。
%%{init: {'theme': 'dark'}}%%
graph TD
subgraph Controller ["Controller (主控)"]
MQTT_B["MQTT Broker<br>(amqtt)"]
Node_M["Node Manager<br>(内存缓存)"]
Game_M["Game Manager<br>(状态机)"]
EventBus{{"EventBus (信号总线)"}}
UI["UI<br>(PySide6)"]
Audio["Audio<br>Player"]
MQTT_C["MQTT<br>Client"]
MQTT_B & Node_M & Game_M <--> EventBus
EventBus <--> UI & Audio & MQTT_C
end
subgraph ESP ["ESP8266 节点网络 (MQTT)"]
direction LR
STA01["STA01"]
STA02["STA02"]
DET01["DET01"]
More["..."]
STA01 --- STA02 --- DET01 --- More
end
UI --> ESP
MQTT_C --> ESP
- 动态发现:节点首次发送心跳时自动创建
- 实时状态:在线/离线、激活队伍、最后心跳时间
- 心跳看门狗:600 秒超时自动标记离线
- 统计面板:总节点数、在线数、离线数
- 开始条件:激活的 STA 节点数 == 预设队伍数
- 淘汰机制:STA 节点再次激活 → 对应队伍淘汰
- 胜利条件:剩余队伍数 == 1
- 开始条件:激活的 STA 节点数 == 预设队伍数
- 计分机制:统计各队伍激活的 DET 节点数
- 胜利条件:某队伍激活的 DET 节点数 > 在线 DET 总数 / 2
- 开始条件:两个 STA 节点激活(装弹方/拆弹方)
- 炸弹激活:装弹方激活指定 DET 节点 → 40 秒倒计时
- 胜利条件:
- T 队:倒计时读完
- CT 队:任何时刻激活炸弹节点拆除
- 串行播放队列:同一时刻只播放一条,避免混音
- 延迟加载:首次播放时才初始化 QSoundEffect,避免启动阻塞
- 队伍特定音效:激活/淘汰/胜利各有队伍专属音效
- 紧急手动模式:可中断队列、立即播放任意音效
- 深蓝管理风格:专业后端管理界面配色
- 多页面分层:节点监控 / 游戏控制 / 游戏状态 / 紧急手动 / Frpc管理
- 实时反馈:游戏状态横幅、队伍卡片、倒计时进度条
- 响应式布局:自适应窗口大小
- 进程管理:启动 / 停止 frpc 客户端
- 配置界面:服务器地址、端口、认证令牌、代理列表可视化编辑
- 实时日志:frpc 进程 stdout 输出
- 配置持久化:上次运行配置自动恢复
Python 3.12+
make dev # uv run controlleruv run controller/test/send_test_message.pyuv sync
uv run pre-commit installMQTT 消息格式:7 字节 ASCII
[节点ID(5)] [动作(1)] [参数(1)]
STA01 H 0 # STA01 心跳
DET01 A 2 # DET01 激活为队伍 B (1=A, 2=B, 3=C, 4=D)
- 动作类型:
H:心跳(参数必须为0)A:激活(参数为队伍编号 1-4 或 A-D)
Cluster/
├── controller/
│ ├── log/ # 日志输出目录
│ ├── src/
│ │ ├── main.py # 应用入口
│ │ ├── ui.py # PySide6 UI 界面
│ │ ├── node_manager.py # 节点状态管理
│ │ ├── game_manager.py # 游戏状态机
│ │ ├── mqtt_client.py # MQTT 客户端
│ │ ├── embedded_broker.py # 内嵌 MQTT Broker
│ │ ├── audio_player.py # 音效播放队列
│ │ ├── event_bus.py # 信号总线
│ │ ├── frpc_manager.py # Frpc 进程管理
│ │ ├── config.py # Controller配置
│ │ ├── resources/
│ │ │ ├── audio/ # 音效文件目录
│ │ └── frpc/ # Frpc 二进制 (amd64/arm64)
│ ├── test/
│ └── send_test_message.py # MQTT 测试脚本
├── doc/ # 项目文档
├── scripts/
│ ├── check_bare_imports # 检查同包导入是否遗漏 '.' 前缀
│ ├── config_init.py # config.toml 生成与校验工具
│ ├── install_deps.py # 安装依赖
│ └── setup_dev.py # 开发环境初始化
├── mcu/ # ESP8266 固件代码
├── README.md # 本文件
├── config.toml # 配置文件
├── README.md # 本文件
├── LICENSE # 开源协议
编辑 config.toml (支持.env和系统环境变量):
# MQTT 配置
MQTT_BROKER = "127.0.0.1"
MQTT_PORT = 1883
MQTT_TOPIC_SUB = "node/status"
# 内嵌 Broker 配置
EMBEDDED_BROKER = True
BROKER_BIND_HOST = "0.0.0.0"
BROKER_BIND_PORT = 1883
# 心跳与看门狗
HEARTBEAT_TIMEOUT = 600 # 秒
WATCHDOG_INTERVAL = 30 # 秒
# Frpc 内网穿透 (config.toml 仅作默认值,运行时配置持久化在 controller/frpc.toml)
FRPC_SERVER_ADDR = ""
FRPC_SERVER_PORT = 7000
FRPC_AUTH_TOKEN = ""日志文件位于 controller/log/controller.log.*,按天滚动保留 7 天。
- 在
game_manager.py的GameMode枚举中添加新模式 - 在
GameManager.on_sta_activated()和on_det_activated()中实现逻辑 - 在
ui.py的游戏控制页添加对应的 UI 控件
- 将音效文件放入
controller/resources/audio/ - 在
config.py的AUDIO_FILES中添加映射 - 在
audio_player.py中添加对应的play_*()方法
本项目采用 CC BY-NC-SA 4.0 协议(署名-非商业性使用-相同方式共享)。详见 LICENSE 文件。
欢迎提交 Issue 和 Pull Request。
- 项目维护者:RyanZ
- 邮箱:[ryanzzzz@foxmail.com]