📡 IoT · 5天课程

课程知识导图 · 点击展开详情

技术栈:STM32F407 标准库 · DX-BT24 BLE · ESP8266 AT · 华为云 IoTDA · 微信小程序 Vibe Coding(WorkBuddy / QClaw · GLM-4)

系统数据流向
手机
微信小程序
Vibe Coding
WorkBuddy/QClaw
① BLE 配网
配置阶段
BLE 模块
DX-BT24
串口透传
UART1
主控
STM32F407
双状态机 FSM
标准库裸机
② AT 指令
UART2
WiFi 模块
ESP8266
AT 固件
TCP/MQTT
③ MQTT
运行阶段
云平台
华为云 IoTDA
MQTT Broker
规则引擎
D1
项目全貌 · Vibe Coding 破冰 · 小程序脚手架
建立终态心智模型,跑通第一个真机预览
▼ 展开
📚 核心知识点
小程序目录结构 app.js 全局状态 页面生命周期 WXML 数据绑定 WorkBuddy 使用 QClaw 编辑器 GLM-4 Prompt 技巧 微信开发者工具 第一个真机预览
⚠️ 易踩坑
AppID 未配置导致真机预览失败 GLM 生成代码省略关键回调 不验证 AI 输出直接运行
🎮 课堂互动:Prompt 竞速赛
目标:用 QClaw + GLM-4 生成"温湿度卡片" UI,最少改动次数跑通者分享思路。评判标准:① 能真机预览 ② 数据绑定正确 ③ 样式美观
45min
架构讲解
30min
环境配置
60min
Vibe Coding
45min
脚手架
D2
自定义数据帧设计 · DX-BT24 BLE 驱动
设计配网帧和数据帧,STM32 串口中断正确拆包
▼ 展开
📚 核心知识点
帧头/帧尾/分隔符设计 异或校验 STM32 USART 中断接收 strstr / strchr 字符串解析 DX-BT24 AT 配置 BLE 广播名设置 SSCOM 串口助手 手机蓝牙可搜到设备 STM32 正确拆出 SSID+密码
📋 上下行帧规范
↑ 配网帧(小程序→STM32)
$WIFI:SSID,PASSWORD#
↑ 数据帧(STM32→云端)
$DATA:T=25.3,H=61,R=1#
↓ 控制帧(云端→STM32)
$CMD:RELAY=1#
↑ ACK 帧(STM32→小程序)
$ACK:OK#
⚠️ 易踩坑
蓝牙 MTU 限制导致长帧分包 中断中使用 printf 导致卡死 DX-BT24 需先退出 AT 模式才能透传
🎮 课堂互动:帧设计擂台
全班分组各设计一套帧格式,白板展示,投票选出最优方案。评判维度:① 解析难度 ② 抗粘包能力 ③ 可扩展性
D3
蓝牙配网(Vibe Coding 主战场)
三步法 Vibe Coding,小程序发配网包,STM32 LED 闪烁确认
▼ 展开
📚 核心知识点
wx.openBluetoothAdapter startBluetoothDevicesDiscovery createBLEConnection getBLEDeviceCharacteristics writeBLECharacteristicValue ArrayBuffer / TextEncoder Promise 链式调用 QClaw 分步生成 GLM-4 上下文传递技巧 配网成功 LED 闪烁
🪜 三步 Vibe Coding 节奏
1
搜索+列表 — 扫描附近 BLE 设备,列表展示设备名和信号强度
2
连接+测试发送 — 连接 DX-BT24,发送 [0x41,0x42,0x43],串口助手验收
3
配网 UI+协议发送 — 输入 SSID/密码,按帧格式发送,等待 $ACK:OK#
⚠️ 易踩坑
writeBLECharacteristicValue 需 ArrayBuffer 非 string 蓝牙 API 必须真机测试(模拟器无效) GLM 易生成竞态条件代码(未等 connect 完成就 getServices) 特征值必须有 WRITE 属性
🎮 课堂互动:配网接力赛
A 同学的小程序配网 → B 同学的 STM32 接收。成功后交换角色。公开验证增强成就感,同时暴露跨设备兼容性问题。
D4
ESP8266 AT 双状态机 · MQTT 上云
非阻塞 FSM,配网完成自动切换工作模式,数据上华为云
▼ 展开
📚 核心知识点
SysTick 时间戳非阻塞 FSM 状态枚举设计 AT+CWJAP 联网 AT+CIPSTART MQTT AT+CIPSEND 发帧 华为云三元组 MQTT Topic 规范 MQTTX 客户端验证 云端实时收到传感器数据
🔄 双状态机结构
配网状态机 (BT_FSM)
IDLE → WAIT_WIFI_INFO → PARSING → CONFIG_ESP → DONE
工作状态机 (WORK_FSM)
IDLE → MQTT_CONNECT → PUBLISHING → SUBSCRIBING → RUNNING
⚠️ 易踩坑
delay_ms 等 AT 响应导致蓝牙丢包 AT 响应含 \r\n 需处理 华为云 MQTT 端口 1883 非 8883 MQTT ClientId 格式错误连不上
🎮 课堂互动:故障对比实验
先演示用 delay_ms(3000) 等 AT 响应时蓝牙数据丢失的现象(串口助手抓包),再切换到状态机版本对比,形成强烈记忆。
D5
全链路联调 · 防御性设计 · 项目演示
分层 Debug,加入重连机制,手机控制继电器
▼ 展开
📚 核心知识点
分层 Debug 方法论 BLE 超时重连 AT 指令重试机制 MQTT 断线重连 MQTT Subscribe 下行控制 继电器/LED 控制逻辑 MQTTX 模拟下发 手机点按钮,板子 LED 响应
🐛 分层排查路线
BLE 层
SSCOM 抓包
串口层
旁路 USB-TTL
AT层
串口助手手发
MQTT层
MQTTX 订阅
🎮 课堂互动:故障注入演练
老师随机:① 断 WiFi ② 拔串口 ③ 强制关 MQTT。学生现场用分层排查定位故障层级并修复。
60min
联调 Debug
45min
防御性设计
45min
Demo 路演

交互任务清单

总体完成度 0%

微信小程序 HMI 高保真原型

模拟配网流程 · 环境监控 · 远程控制 · 可交互操作

9:41 📶 🔋
IoT 控制台
智能环境监控
设备状态: ● 未配网
📡 蓝牙配网 未连接
搜索 BLE 设备
点击开始扫描附近设备
🌡️ 环境监控
温度
--
°C
湿度
--
%RH
MQTT 连接
离线
最近上报:--
🔌 远程控制
继电器 1(LED)
当前状态:关闭
继电器 2(风扇)
当前状态:关闭
▶ 串口日志
[SYS] STM32F407 启动中...
[SYS] 系统初始化完成
[BLE] 等待配网指令...
🎮 操作步骤
  1. 点击"搜索"扫描 BLE 设备
  2. 点击列表中的 DX-BT24 连接
  3. 输入 WiFi SSID 和密码
  4. 点击"发送配网指令"
  5. 观察配网成功后数据刷新
  6. 拨动开关控制继电器
📦 模拟帧记录
[待操作] 暂无帧数据
⚡ 快捷操作

自定义数据帧 · 可视化解析

① 配网帧 $WIFI:SSID,PASSWORD#
$
帧头
0x24
WIFI
命令类型
固定4字节
:
分隔符
SSID
WiFi名称
可变长度
,
分隔符
PWD
密码
可变长度
#
帧尾
0x23
示例:
$WIFI:MyHome_5G,password123#
🔍 交互解析器(输入帧字符串)
② 上行数据帧 $DATA:T=xx.x,H=xx,R=x#
$
帧头
DATA
命令类型
:
分隔
T=xx
温度
,
分隔
H=xx
湿度
,
分隔
R=x
继电器
#
帧尾
🔧 数据帧生成器
③ 下行控制帧 $CMD:RELAY=x# 及 ACK
云端 → STM32
$CMD:RELAY=1#
→ 打开继电器1
$CMD:RELAY=0#
→ 关闭继电器1
STM32 → 云端 (ACK)
$ACK:OK#
→ 执行成功
$ACK:ERR#
→ 执行失败
④ 双状态机 FSM 设计(非阻塞)
配网状态机 BT_FSM
BT_IDLE
→ 蓝牙连接
WAIT_WIFI_INFO
→ 等待帧数据
PARSING
→ strstr 解析帧
CONFIG_ESP
→ AT+CWJAP
BT_DONE
→ 切换工作模式
工作状态机 WORK_FSM
WORK_IDLE
→ WiFi 已联网
MQTT_CONNECT
→ AT+CIPSTART
SUBSCRIBING
→ 订阅控制 Topic
RUNNING
→ 周期上报 + 响应下行
C 语言状态机骨架
typedef enum { BT_IDLE, WAIT_WIFI_INFO, PARSING, CONFIG_ESP, BT_DONE } BT_State;
typedef enum { WORK_IDLE, MQTT_CONNECT, SUBSCRIBING, RUNNING } Work_State;

BT_State   bt_state   = BT_IDLE;
Work_State work_state = WORK_IDLE;

void Main_FSM_Tick(void) {          // 在 while(1) 中每 10ms 调用一次
    uint32_t now = SysTick_GetMs();

    /* ── 配网状态机 ── */
    switch (bt_state) {
        case WAIT_WIFI_INFO:
            if (UART1_HasNewFrame()) { bt_state = PARSING; }
            break;
        case PARSING:
            if (Parse_WifiFrame(rx_buf, ssid, pwd)) {
                bt_state = CONFIG_ESP;
            }
            break;
        case CONFIG_ESP:
            if (ESP_SendAT("AT+CWJAP", ssid, pwd, now)) {
                bt_state = BT_DONE;
                work_state = WORK_IDLE;  // 触发工作状态机
            }
            break;
        default: break;
    }

    /* ── 工作状态机 ── */
    if (bt_state == BT_DONE) {
        switch (work_state) {
            case WORK_IDLE:   work_state = MQTT_CONNECT; break;
            case MQTT_CONNECT:
                if (MQTT_Connect(now)) { work_state = SUBSCRIBING; }
                break;
            case RUNNING:
                if (now - last_pub > 5000) { MQTT_Publish_Data(); last_pub = now; }
                MQTT_Handle_Incoming();
                break;
            default: break;
        }
    }
}

Vibe Coding · WorkBuddy / QClaw + GLM-4 黄金 Prompt

⚡ GLM-4 使用要点:① 必须说"输出完整可运行代码,不省略" ② 异步链用 Promise.then 而非 async/await(GLM 易生成竞态) ③ 每次只做一个功能点 ④ 最后附加"列出需要人工验证的3个关键点"
📶 Step 1 · 蓝牙搜索 + 设备列表(WorkBuddy 输入)
你是微信小程序开发专家,使用原生 JS(不用 TypeScript)。
帮我实现一个蓝牙搜索页面,要求如下:

【功能要求】
1. 页面加载时(onLoad)调用 wx.openBluetoothAdapter 初始化蓝牙
2. 点击"搜索"按钮:调用 wx.startBluetoothDevicesDiscovery({allowDuplicatesKey: false})
   监听 wx.onBluetoothDeviceFound,将每个发现的设备追加进 data.devices 数组
   设备对象结构:{ deviceId, name, RSSI },name 为空时显示"未知设备"
3. 点击"停止"按钮:调用 wx.stopBluetoothDevicesDiscovery
4. 点击列表中某个设备:将 deviceId 和 name 存入 app.globalData,
   跳转到连接页面 /pages/connect/connect
5. 页面销毁(onUnload)时必须调用 wx.closeBluetoothAdapter

【代码规范】
- 所有微信异步 API 使用 Promise 封装(success/fail 都处理)
- 不使用 async/await
- wx.showToast 显示错误信息
- 设备列表用 wx-open-data 或普通 view 渲染即可

【输出格式】
输出完整可运行的 4 个文件(不省略任何代码):
index.js / index.wxml / index.wxss / index.json

最后列出"需要人工验证的3个关键点"
🔗 Step 2 · 连接设备 + 发现特征值 + 发送测试字节(QClaw 续写)
在上一步的基础上,继续完成连接页面 /pages/connect/connect,要求:

【上下文】
- app.globalData.deviceId 已存有目标设备 ID(DX-BT24 BLE 模块)
- app.globalData.deviceName 已存有设备名

【功能要求】
1. 页面加载时自动开始连接流程(严格按以下顺序,每步成功才执行下一步):
   ① wx.createBLEConnection({ deviceId })
   ② wx.getBLEDeviceServices({ deviceId })
      取第一个 serviceId 存入 globalData.serviceId
   ③ wx.getBLEDeviceCharacteristics({ deviceId, serviceId })
      找出 properties.write == true 的 characteristicId
      存入 globalData.characteristicId
   ④ 连接成功后显示"已连接"状态,并自动发送测试字节 [0x41, 0x42, 0x43]

2. 封装函数 sendBytes(uint8Array):
   - 参数为 Uint8Array
   - 内部将 Uint8Array 转为 ArrayBuffer
   - 调用 wx.writeBLECharacteristicValue
   - 返回 Promise

3. UI 状态显示:
   连接中(转圈)→ 已连接(绿点)→ 发送成功(✓)→ 失败(红叉+错误信息)

【代码规范】
- 全部使用 Promise.then 链(禁止 async/await)
- 每个步骤失败均 wx.showToast 提示
- 输出完整可运行代码,不省略

最后列出"需要人工验证的3个关键点"
📡 Step 3 · 配网 UI + 协议帧发送 + ACK 监听(QClaw 续写)
在前两步的基础上,在 connect 页面底部添加配网区域,要求:

【上下文】
- globalData.deviceId / serviceId / characteristicId 已就绪
- sendBytes(uint8Array) 函数已封装完毕

【功能要求】
1. 配网表单:
   - SSID 输入框(placeholder: "WiFi 名称")
   - 密码输入框(type="password",右侧有"显示/隐藏"切换按钮)
   - "发送配网指令"按钮

2. 点击按钮时:
   ① 验证 SSID 和密码不为空,否则 showToast 提示
   ② 拼接字符串:`$WIFI:${ssid},${password}#`
   ③ 用 TextEncoder().encode() 转为 Uint8Array
   ④ 调用 sendBytes 发送
   ⑤ 发送后按钮置灰,显示"等待 STM32 确认..."
   ⑥ 调用 wx.notifyBLECharacteristicValueChange 监听 notify
      若3秒内收到包含 0xAA 的数据,显示"✅ 配网成功!"
      超过3秒未收到,显示"⏱ 超时,请重试"并恢复按钮

3. 配网成功后,在 globalData 中设置 isProvisioned = true
   底部显示"前往控制台"按钮,跳转到 /pages/dashboard/dashboard

【代码规范】
- 全部 Promise.then 链
- ArrayBuffer 与 Uint8Array 转换必须正确
- 输出完整可运行代码,不省略

最后列出"需要人工验证的3个关键点"
☁️ Step 4 · Dashboard · MQTT 订阅 + 数据展示 + 控制(WorkBuddy)
帮我实现微信小程序的 dashboard 页面,通过华为云 IoTDA 的 MQTT 接收传感器数据
并发送控制指令,要求:

【华为云 MQTT 配置】
- Broker: ${YOUR_IOTDA_ENDPOINT}:1883
- ClientId / Username / Password: 来自华为云设备三元组
- 上行 Topic: $oc/devices/{deviceId}/sys/messages/up
- 下行 Topic: $oc/devices/{deviceId}/sys/messages/down

【功能要求】
1. 页面加载时用 wx.connectSocket 建立 MQTT over WebSocket 连接
   连接成功后订阅下行 Topic

2. 收到下行消息时:
   解析 payload 字符串,格式为 "$DATA:T=xx.x,H=xx,R=x#"
   更新页面:温度数值、湿度数值、继电器状态

3. 继电器开关(toggle 组件):
   用户拨动时,构造字符串 "$CMD:RELAY=x#" 通过 wx.sendSocketMessage 发送
   同时乐观更新 UI

4. 连接状态指示:顶部圆点绿/红色

【代码规范】
- 不使用第三方 MQTT 库,用原生 WebSocket
- 处理断线重连(3次重试,间隔5秒)
- 页面销毁时 wx.closeSocket
- 输出完整可运行代码

最后列出"需要人工验证的3个关键点"
🧠 GLM-4 使用技巧 · 课堂发放
❌ 错误用法(GLM 易出问题)
"帮我写一个蓝牙配网小程序"
→ GLM 会一次性生成全部代码,async/await 竞态问题多,且省略错误处理
✅ 正确用法(分步 + 约束)
① 每次只要一个功能 ② 明确"Promise.then 链,禁止 async/await" ③ 结尾加"输出完整可运行代码,不省略" ④ 加"最后列出3个需人工验证的点"
🔍 人工验证清单(BLE 代码必查)
□ writeBLECharacteristicValue 的数据类型是否为 ArrayBuffer?
□ getBLEDeviceCharacteristics 后是否检查了 properties.write === true?
□ onBluetoothDeviceFound 是否在 startDiscovery 之前注册监听?
□ createBLEConnection 的 success 回调后才调用 getServices?
□ onUnload 时是否调用了 closeBluetoothAdapter?