STM32 从 0 到 1 做智能家居(十四):整机联调与验收 —— 把七个模块装成一台机器

本文聚焦STM32智能家居系统的整机联调与验收,涵盖上电流程、七层分层验证(A-G层)、日志判读速查、端到端时序分析及典型故障复盘。强调以串口日志为唯一真相源,遵循分层定位、单变量调试原则,解决模块间交互问题如拨码开关误置、MQTT初始化未完成即操作、自动模式覆盖手动命令等。内容面向嵌入式固件侧联调,不涉及网页前端实现。

文章目录

系列最后一篇。前十三章,每个模块都单独跑通过:传感器能读、PWM 能调、按键能响应、蓝牙能通、MQTT 能连、网页能开。按说拼起来就该能用。

但"每个零件都好"和"整机好"是两件事。我联调那一晚,五个问题全是零件之间的:

  1. 蓝牙测完忘了拨回 WiFi 侧,进 MQTT 全 FAIL,我还在查代码;
  2. MQTT 初始化要十几秒,我等不及连按了两下 KEY4,第二下被丢弃,日志里一句 [KEY4] busy 我根本没看;
  3. 网页点了风扇,转了两秒自己停了 —— 因为板子停在自动模式,自动逻辑每 2 秒把它覆盖掉;
  4. 看到一条 [PUB] FAILED 就判定"MQTT 坏了",其实下一条就 OK;
  5. 烧完程序没按复位,串口里还是旧程序的日志,对着日志查了半天新功能。

这一篇不再讲"某个模块怎么实现",只讲怎么按顺序把整机验一遍、怎么读日志、出问题时怎么在 30 秒内定位到哪一层,最后给一份能直接照着念的演示脚本。

一、先把"真相源"定下来

1.1 联调时你有三个窗口

整机联调时,同时能观察到现象的地方有三个:

窗口看什么可信度
串口助手(USART1,115200)板子自己的日志:模式、传感器、命令、ESP 回包★★★★★ 唯一"真相"
实物(风扇/加热/舵机/LED)到底动没动★★★★☆ 真实但滞后
H5 网页展示状态、连接状态★★☆☆☆ 只是"展示"

为什么 H5 只能给两星?因为它是本地乐观更新 + 单向订阅:

  • 按钮颜色是网页自己翻的,不代表设备执行了(第 13 章 6.4);
  • 网页只订阅上报主题,收不到设备的命令回执(第 13 章 6.7);
  • "MQTT 已连接"只代表浏览器连上了 broker,不代表板子在线(第 13 章 4.4)。

所以:排查永远以串口为准,H5 只用来确认"最后一跳"通不通。

1.2 一张分层真相表

联调时脑子里的第一张表应该是这个 —— 出问题先定位到层,再动手:

第 7 层  H5 显示 / 控制        ← 只有前 6 层全绿,这一层才有意义
第 6 层  broker 收发           ← 看 [PUB] OK、看 MQTTX
第 5 层  ESP-AT 会话           ← 看 +MQTTCONNECTED、setMqttPara
第 4 层  WiFi 连接             ← 看 connectWifi
第 3 层  USART2 物理链路       ← 看 [ESP] online
第 2 层  传感器 + 执行器       ← 看 [ENV]、看实物
第 1 层  供电 + 时钟 + 串口    ← 看有没有任何日志

下层没通,上层的一切现象都不可信。 这句话在第 1 章就写过,到第十四章它才真正发挥威力:因为只有整机联调时,上层才会出现"看起来能动其实没动"的假象。

1.3 联调工具清单

别小看这张表,缺一样就会多花一小时:

工具用途缺了会怎样
串口助手(SSCOM/XCOM)USART1 115200没有真相源,只能瞎猜
浏览器 + F12H5 与 Console不知道网页哪一步断了
MQTTX / EMQX 在线客户端独立于网页验证 broker分不清是网页问题还是设备问题
手机蓝牙调试 App蓝牙层只能靠猜
万用表量 3.3V / 5V / 通断供电问题查不出来
手机热点备用网络校园网封 8084 时抓瞎
一张纸打勾验收清单漏测,答辩现场才发现

1.4 一个反直觉的规矩

联调时只改一个变量。 这条听着像废话,但联调现场最容易犯的错就是"同时拨了开关、改了代码、换了热点",然后现象变了,你根本不知道是谁的功劳。

每次只动一处,动完立刻看串口。慢,但是唯一可靠的方式。

二、上电与模式:把状态走对

2.1 固定上电顺序(七步)

① 拨码开关打到蓝牙侧(先验蓝牙,链路最短)
② 插 ST-Link,烧录固件
③ 烧完手动按一下板子的 RESET           ← 别省这一步
④ 打开串口助手:USART1,115200,8N1
⑤ 看启动日志(应该只有一两行)
⑥ 蓝牙验证 → 拔蓝牙 / 拨到 WiFi 侧 → KEY2 退出 → KEY4 → KEY4 进 MQTT
⑦ 打开 H5,观察数据;点按钮,观察实物

第 ③ 步单独拎出来说:烧录成功 ≠ 新程序在跑。Keil 下载后有时不会自动复位,尤其勾了 “Reset and Run” 之外的情况。日志对不上新功能时,第一件事是手动复位一次,而不是去翻代码。

2.2 按键语义总表(一张表看完 8 种)

按键行为完全取决于当前模式,这是本项目最容易被自己绕晕的地方:

按键非手动模式(自动/远程)手动模式(MODE_MANUAL)
KEY1(PC2)串口周期打印开关,[KEY1] send_flag=1/0风扇 开/关,[MANUAL] FAN ON/OFF
KEY2(PC3)进手动模式退出手动 → 回自动模式,[MODE] Auto ...
KEY3(PA1)进自动模式窗帘 拉开/关闭,[MANUAL] CURTAIN OPEN/CLOSE
KEY4(PB10)进远程 / 蓝牙↔MQTT 切换加热 开/关,[MANUAL] HEATER ON/OFF

三个必须记住的反直觉点:

  1. 手动模式下 KEY2 是"回到自动",不是"退出手动到别处"。 因为手动模式的另一头就是自动(代码里写死 MODE_MANUAL ? MODE_AUTO : MODE_MANUAL)。
  2. 手动模式下 KEY4 是加热,不会切网络协议。 所以你想从"MQTT 远程"切回蓝牙,必须先 KEY2 离开手动,再在远程模式里按 KEY4。
  3. 进任何模式都会"全关"。 Switch_Mode() 无条件把三路 CCR 清 0、标志清零,进手动时还会把窗帘归位到"关闭"(这样标志位 0 和实物一致,之后按 KEY3 一定能看到动作)。

2.3 KEY4 的三段循环,以及为什么按快了没用

KEY4 在非手动模式下的状态机:

        当前不是远程
             │ 按 KEY4
             ▼
      [MODE] Remote (KEY4) | Bluetooth ON
      g_net_req = NET_REQ_BT
             │ 主循环执行
             ▼
      [NET] Bluetooth @9600
             │ 按 KEY4(g_net == NET_BT)
             ▼
      [KEY4] request MQTT
             │ 主循环执行(首次要十几秒)
             ▼
      [NET] Switch HW dial to WiFi(W_TXD/W_RXD)! MQTT init ~10s...
      ...
      [NET] MQTT ready (WiFi + broker + sub all OK)
             │ 按 KEY4(g_net != NET_BT)
             ▼
      [KEY4] request Bluetooth  →  [NET] Bluetooth @9600

关键在中间那个"十几秒":

else if(g_net_busy)
{
    printf("[KEY4] busy: MQTT init in progress, ignored\r\n");
}

g_net_busy 在 Mqtt_Server_Init() 期间是 1。这时按 KEY4 直接被丢弃,只打印一行 [KEY4] busy。

为什么必须丢?因为 Mqtt_Server_Init() 是一长串 发指令 → 等应答 的阻塞流程。半路插进一条"切回蓝牙"的请求,会在 AT 指令流中间切波特率,把 ESP 彻底搅乱 —— 后面所有指令都回 ERROR,你还会以为是 ESP 坏了。

所以:进 MQTT 后耐心等 [NET] MQTT ready 出来再动按键。 屏幕上没反应不代表死机,只代表在等 ESP 回话。

2.4 为什么开机不连 WiFi

这一点和第 12 章呼应,但联调时特别重要:

// 初始状态:手动模式,全关
Switch_Mode(MODE_MANUAL);

// ESP-12F 改为"按需初始化":开机不连WiFi/MQTT,避免拨在蓝牙侧时白等十几秒。
// 首次按 KEY4 切到 MQTT 模式时,才由主循环的 g_net_req 处理段调用 Mqtt_Server_Init()。

好处是开机 1 秒内就有日志、按键立刻可用;代价是首次进 MQTT 必须等十几秒。这是设计取舍,不是 bug。

2.5 上电日志应该长什么样(三段)

第一段:冷启动

[MODE] Manual (KEY2) | Keys: 1=FAN 3=CURTAIN 4=HEATER

如果一分钟过去了连这一行都没有 → 第 1 层就挂了(供电 / USART1 接线 / MicroLIB / 波特率)。

第二段:进入远程(默认蓝牙)

[MODE] Remote (KEY4) | Bluetooth ON
[NET] Bluetooth @9600

注意 [MODE] Remote ... | Bluetooth ON 里的 “Bluetooth ON” 是写死的文案,不代表此刻真的走蓝牙。真正的协议状态看紧接着的 [NET] Bluetooth @9600 或 [NET] WiFi/MQTT @115200。这是日志本身的一个小瑕疵。

第三段:首次进 MQTT(完整正常流程)

[KEY4] request MQTT
[NET] Switch HW dial to WiFi(W_TXD/W_RXD)! MQTT init ~10s...
[NET] MQTT init start...
[ESP] online @115200
setEchoOff : OK!!!
setWifiMode:OK!!!
connectWifi : OK!!!
[NET] link after wifi: connected=0 recv...
[NET] reset MQTT link (DISCONN + CLEAN)...
setMqttPara : OK!!!
MqttConn : OK!!!
MqttSub : OK!!!
[NET] test publish: {"t":27,"h":45,"a":2,"c":612,"v":123}
MqttPub(test) : OK!!!
[NET] MQTT ready (WiFi + broker + sub all OK)
[NET] WiFi/MQTT @115200
[PUB] OK
[PUB] OK
...

只要看到 [NET] MQTT ready 和后面连续的 [PUB] OK,固件端就完全正常了。 之后所有问题都在 broker / 网页那一侧,不用再碰代码。

2.6 一个容易漏掉的状态细节

Switch_Mode() 会把 g_net 置回 NET_BT,但不在这里切波特率(因为可能在中断里调用,而切波特率要等待)。

后果:如果你在 MQTT 模式下直接按 KEY2 进手动,USART2 仍然是 115200,而 g_net 已经写成 BT。

这时如果蓝牙模块还接着,你发的命令在 9600 的蓝牙看来就是乱码;反过来蓝牙回的也是乱码。而且手动模式下 KEY4 是加热,你没法把波特率切回去。

恢复办法只有一个:KEY2 回自动 → KEY4 进远程 → 再按 KEY4 切蓝牙([NET] Bluetooth @9600)。

这是"整机联调"里典型的跨模块副作用:逻辑正确,但物理通道没跟着回来。日志里看不出来,只有"蓝牙突然不听话"这个现象。

2.7 一次完整的正常流程(逐秒对照)

把上面所有片段串起来,就是一条完整的时间线。第一次联调时照着这个对照,哪一行没出现,就停在哪一行。

t=0.0s   上电/复位
         [MODE] Manual (KEY2) | Keys: 1=FAN 3=CURTAIN 4=HEATER

t=0.5s   按 KEY1(手动模式下 = 风扇)
         [ENV] Temp:27.5C  Humi:45.0%  AQI:2  TVOC:123 ppb  eCO2:612 ppm  Air:GOOD  Mode=0
         [MANUAL] FAN ON

t=1.5s   按 KEY1 再一下
         [MANUAL] FAN OFF

t=2.0s   按 KEY2(进自动)
         [MODE] Auto | Temp Control ON
         (之后每 2 秒一条 [ENV],状态变化时才多打一行 [HIGH]/[LOW]/[AIR])

t=60s    按 KEY2(回手动)→ KEY4(进远程,默认蓝牙)
         [MODE] Manual (KEY2) | Keys: 1=FAN 3=CURTAIN 4=HEATER
         [MODE] Remote (KEY4) | Bluetooth ON
         [NET] Bluetooth @9600

t=62s    手机发 FAN_ON(蓝牙通道)
         [CMD] FAN ON            ← 前提:11 章那个清空顺序 bug 已修

t=70s    拨码开关切到 WiFi 侧,按 KEY4
         [KEY4] request MQTT
         [NET] Switch HW dial to WiFi(W_TXD/W_RXD)! MQTT init ~10s...
         [NET] MQTT init start...
         [ESP] online @115200
         setEchoOff : OK!!!
         setWifiMode:OK!!!
         connectWifi : OK!!!

t=74s    [NET] reset MQTT link (DISCONN + CLEAN)...
         setMqttPara : OK!!!
         MqttConn : OK!!!
         MqttSub : OK!!!
         [NET] test publish: {"t":27,"h":45,"a":2,"c":612,"v":123}
         MqttPub(test) : OK!!!
         [NET] MQTT ready (WiFi + broker + sub all OK)
         [NET] WiFi/MQTT @115200

t=75s    [PUB] OK
t=77s    [PUB] OK
t=79s    [PUB] OK           ← 到这里固件端 100% 正常

t=80s    浏览器打开页面
         Console: [MQTT] 已连接 wss://broker.emqx.io:8084/mqtt
         Console: [MQTT←STM32] 2604/topic-HH {"t":27,"h":45,"a":2,"c":612,"v":123}
         页面:温度 27.0、湿度 45、圆环约 54%/45%、空气"空气一般"

t=90s    网页点"风扇"
         Console: [MQTT→STM32] cmd: FAN_ON
         串口:    [MQTT] cmd: FAN_ON
                  [CMD] FAN ON
         实物:    风扇转

这张表里有四个"卡点",联调时最容易停在这四个地方:

卡点位置判据
① 连启动日志都没有t=0第 1 层(供电/串口)
② [NET] Bluetooth @9600 没出现t=60KEY4 没按对 / 模式不对
③ 停在 [ESP] online 之前t=70拨码 / 供电
④ [MQTT] cmd: 有但 [CMD] 没有t=90命令字面量拼错

三、分层验收:A 到 G 七层

按顺序打勾。哪一层没通过,就先修那一层,别往上跳。

3.1 A 层:基础

检查项期望现象
上电 1 秒内有日志[MODE] Manual (KEY2) ...
任意键打印一次环境[ENV] Temp:.. Humi:.. AQI:.. TVOC:.. eCO2:.. Air:.. Mode:..
非手动下按 KEY1[KEY1] send_flag=1,之后每秒一条 [ENV]

[ENV] 的完整样子(注意 Air 是 GOOD/BAD,Mode 是 0/1/2):

[ENV] Temp:27.5C  Humi:45.0%  AQI:2  TVOC:123 ppb  eCO2:612 ppm  Air:GOOD  Mode=0

注意 Humi:45.0% 中间那个点:DHT11 的湿度是"整数.小数"两字节,打印格式是 %d.%d%%。最后那个 %% 在 printf 里输出一个 %。别误以为格式错了。

3.2 B 层:传感器

检查项判据不合格怎么办
温度接近室温±3℃ 内查 DHT11 上拉、线长、delay_us
湿度合理30%~80%同上
AQI 在 1~51~5查 ENS160 I2C 地址(0x53/0x52)、OPMODE
eCO2 在 400~2000合理范围ENS160 需要预热,刚上电偏高正常
TVOC 在 0~2000合理范围同上
对着传感器哈气eCO2/TVOC 明显上升没反应 → 数据没刷新,查 Read_ENS160

ENS160 有个"预热期":刚上电头几分钟读数会漂,AQI 可能一直给 1 或 5。这是传感器自身的算法在收敛,不是代码错。验收时至少让它跑 3~5 分钟再看趋势。

3.3 C 层:执行器(手动模式)

先 KEY2 进手动(日志 [MODE] Manual ...),然后:

操作期望日志期望现象
KEY1[MANUAL] FAN ON / FAN OFF风扇转 / 停
KEY3[MANUAL] CURTAIN OPEN / CLOSE舵机两个明显位置
KEY4[MANUAL] HEATER ON / OFF加热片发热(手别碰)

进手动时窗帘会先归位到"关闭",这是 Switch_Mode() 里那句 CURTAIN_CLOSE_PULSE 干的。所以你第一次按 KEY3 一定是"拉开",能看到明显动作 —— 这是故意设计的,方便验收。

如果风扇不转但日志有 [MANUAL] FAN ON:

  • 量 PA6 有没有 PWM(万用表测平均电压,约 2.6V 左右);
  • 查 HAL_TIM_PWM_Start 有没有在 main() 里调用;
  • 查 MOS 管驱动供电。

3.4 D 层:自动模式

按 KEY3 进自动([MODE] Auto | Temp Control ON),然后用人为手段制造四种条件:

条件怎么造期望日志期望动作
高温电吹风/手握传感器加热到 ≥30℃[HIGH] Temp=30C | FAN ON | CURTAIN OPEN风扇转 + 窗帘开
低温冰袋/空调吹到 ≤29℃[LOW] Temp=29C | HEATER ON | CURTAIN CLOSE加热 + 窗帘关
温度正常 + 空气差喷点酒精/香水靠近 ENS160[AIR] AQI=.. eCO2=..ppm TVOC=..ppb | FAN ON | CURTAIN OPEN风扇通风 + 窗帘开
温度正常 + 空气好开窗通风等读数降下来无状态日志([ENV] 照常)全关,窗帘保持拉开

两个"排错难想到"的点:

  1. 状态日志只在"状态变化"时打印一次。 代码里用 last_state 记住上次状态,if(cur_state != last_state) 才打印。所以 [HIGH] 只出现一次,不会每 2 秒刷一条。"只打印了一次"不是卡住了,是防刷屏。
  2. 正常态窗帘是"拉开"的(不是关闭)。因为通风/采光的默认姿态是开。验收时别把"正常态窗帘开着"判成 bug。

3.5 E 层:蓝牙

检查项期望
手机能连上 HC-06配对成功(默认 PIN 1234)
发 FAN_ON风扇转,手机收到回显 FAN_ON
发 ALL_OFF全关
发乱码 ABC串口 [CMD] unknown: ABC

⚠ 这一层有个前提,必须说清楚。

第 11 章我们发现 main.c 的蓝牙分支有一个清空顺序 bug:

Remote_Cmd_Exec((char*)bt_cmd_buf, 1);
bt_cmd_buf[0] = '\0';          // ← 每收到一个字节就清空

因为 HAL_UART_Receive_IT(&huart2, bt_buf, 1) 是一次收一个字节,所以缓冲区永远是"一个字符 + 结束符",strstr 永远匹配不上。日志会呈现成:

[CMD] unknown: F
[CMD] unknown/A
[CMD] unknown: N
...(一个字符一行)

用仓库原始代码做 E 层验收,这一项必然过不了。 这不是联调新发现的故障,是已知问题。按第 11 章 5.4 节的最小改法(让 Remote_Cmd_Exec 返回是否命中、只在命中时清空)改完,E 层即可通过。

MQTT 通道不受影响,因为那条路径用的是整包 esp_sub_data,不是逐字节拼。

3.6 F 层:MQTT(固件侧,先不碰网页)

检查项期望
日志出现 [NET] MQTT ready初始化全绿
每 2 秒一条 [PUB] OK上报正常
MQTTX 订阅 2604/topic-HH每 2 秒收到 {"t":..,...}
MQTTX 往 2604/cmd-HH 发 FAN_ON串口 [MQTT] cmd: FAN_ON + [CMD] FAN ON,风扇转

注意 F 层要在没有网页的情况下测。 用 MQTTX 而不是网页,才能把"网页问题"和"固件问题"切开。这一步全绿再去开网页。

3.7 G 层:H5

检查项期望
顶部状态MQTT 已连接,绿点
温度/湿度数值在刷新,圆环跟着动
空气质量AQI / eCO2 / TVOC 三个数都有
点"风扇"实物转,串口 [MQTT] cmd: FAN_ON
点"全部关闭"全停
F12 Console[MQTT←STM32] 和 [MQTT→STM32] 都有

3.8 一条铁律

A 不过 → 不上 B      B 不过 → 不上 C
C 不过 → 不上 D      D 不过 → 可以上 E(E 不依赖 D)
E 不过 → 可以上 F(但先解释清楚为什么不过)
F 不过 → 绝不上 G

F 不过就上 G,等于给自己制造一堆无法解释的现象。 网页显示的 --、按钮不灵、状态乱跳,全都可能是 F 层的下游表现。

四、日志判读速查表

这一节建议截图保存,联调时对着看。

4.1 模式与按键

你看到的含义下一步
[MODE] Manual (KEY2) ...进/回到手动,全关正常
[MODE] Auto | Temp Control ON进自动正常,2 秒后开始控
[MODE] Remote (KEY4) | Bluetooth ON进远程(默认蓝牙)看下一行 [NET]
[KEY4] request MQTT请求切 MQTT等十几秒
[KEY4] busy: MQTT init in progress, ignored初始化期间按键被丢正常,等 [NET] MQTT ready
[KEY1] send_flag=1开了每秒打印正常
[MANUAL] FAN ON手动开风扇正常

4.2 ESP 探测

你看到的含义下一步
[ESP] online @115200探测通过继续
[ESP] online @9600 (not 115200), keep 9600ESP 被改成过 9600能用,但慢;建议 AT+UART 改回 115200
[ESP] no valid answer, but bytes did arrive -> baud mismatch (or ESP still booting)线通了,波特率不对/没启动完等 2 秒重试;或确认 ESP 波特率
[ESP] zero bytes received -> USART2 RX not connected to ESP (dial on Bluetooth side? ESP unpowered?)一个字节都没收到查拨码、ESP 供电、TX/RX 交叉
[NET] ESP probe failed -> skip WiFi/broker init ...探测失败,后面全部跳过先解决探测,别往下看

zero bytes 是命中率最高的一条。 十个"MQTT 全 FAIL"里至少五个是它:拨码开关还在蓝牙侧。

4.3 WiFi

你看到的含义下一步
connectWifi : OK!!!连上了继续
connectWifi : FAIL,recv(len=0)超时无应答SSID/密码、2.4G、信号
connectWifi : CWJAP? -> ...失败后核实真实状态看返回的 AP 是不是你要的
connectWifi : actually connected (state confirmed)其实连上了正常,继续

校园网/公司网的 WiFi 常带网页认证,ESP 连得上但出不了外网,表现为后面 MqttConn : FAIL 或 +PING:TIMEOUT。用手机热点最省事。

4.4 MQTT 会话

你看到的含义下一步
[NET] reset MQTT link (DISCONN + CLEAN)...正在擦 LinkID 0正常
setMqttPara : OK!!!配置成功继续
setMqttPara : FAIL,recv...ERRORLinkID 0 被旧配置占着确认 AT+MQTTCLEAN=0 执行了;重启 ESP 再试
MqttConn : OK!!!连上 broker继续
MqttConn : FAIL(try 1),recv...连不上查外网、DNS、broker 地址
MqttConn : network check (AT+PING)...失败后自动 PING 诊断看 ping 结果
[NET] reuse existing broker session, skip USERCFG/CONNESP 自己已经连上了正常,代码走了复用分支
MqttSub : OK!!!订阅成功继续
MqttPub(test) : OK!!!试发成功即将 ready

4.5 发布

你看到的含义下一步
[PUB] OK 连续上报正常✓
[PUB] FAILED, payload was -> {"t":..} 偶发网络抖动偶发可接受,看下一条
同上,连续出现会话已失效重进 MQTT(KEY4 两次)
[PUB] FAILED 且 payload 明显不对JSON 拼错了看 Mqtt_Build_Json
[MQTT] cmd: FAN_ON + [CMD] FAN ON命令链路全通✓
[MQTT] cmd: ... 但 [CMD] unknown命令字面量错对照第 13 章 6.6 命令表

注意 [PUB] FAILED, payload was -> ... 打印的是JSON 载荷本身(不是 AT 指令),这是刻意保存的,方便对照内容。别把它当成"发给 ESP 的指令"。

4.6 异常与重启

你看到的含义下一步
[ESP] ready 反复出现ESP 在重启供电不足,换独立 3.3V ≥500mA
[NET] ESP rebooted -> MQTT session lost, stop publishing...检测到 ESP 重启,停止发布按 KEY4 两次重新初始化
[WARN] DHT11 read failed, keep last stateDHT11 读失败,保持上次状态查上拉、线长;偶发正常
[ENV] DHT11 not ready缓存里没有有效值同上
一行日志都没有程序没跑/串口没通复位、查 USART1、MicroLIB

4.7 30 秒定位决策树

现场出问题时,按这棵树往下走,最多四步就能定位到层:

                        ┌─────────────────────┐
                        │  现象是什么?        │
                        └──────────┬──────────┘
                                   │
        ┌──────────────────────────┼──────────────────────────┐
        │                          │                          │
   "串口没日志"              "有日志但没数据"              "网页不对"
        │                          │                          │
        ▼                          ▼                          ▼
  复位一次                  看 [ESP] 行                  看 Console
        │                    ┌────┴────┐                ┌────┴────┐
   还是没有?            zero bytes  online          无"已连接"  有"已连接"
        │                    │          │                │          │
        ▼                    ▼          ▼                ▼          ▼
  查供电/USART1        查拨码/供电   看 connectWifi    wss/8084/  看主题
  /MicroLIB/波特率                   ┌────┴────┐      /mqtt      大小写
                                  FAIL       OK                   │
                                   │         │                    ▼
                              SSID/密码   看 setMqttPara      有没有
                              /2.4G       ┌────┴────┐       [MQTT←STM32]
                                          FAIL       OK      有→id写错
                                           │         │       无→设备没发
                                      CLEAN未执行  看 [PUB]

再给一张"症状→第一动作"的极简版,贴在显示器边上:

症状第一动作(不要想,直接做)
串口没日志按复位
有日志,没 [ESP] 行看拨码开关
[ESP] zero bytes看拨码 + 量 ESP 3.3V
connectWifi : FAIL手机连同一热点,确认能上网
setMqttPara : FAIL重启 ESP,重走 CLEAN
没 [PUB] OKKEY4 两次重初始化
网页"已连接"没数据串口有没有 [PUB] OK
网页有数据,按钮不灵板子是不是远程模式(Mode=2)
按钮灵,设备不动串口有没有 [CMD]

核心思路只有一句:先用串口把故障切到"设备侧"或"网页侧",再在那一侧细分。 不要一上来就两边同时改。

五、端到端时序:对着表看

5.1 上报链路(设备 → 网页)

STM32                     ESP-12F              broker.emqx.io        浏览器 H5
  │                          │                       │                    │
  │ 每2s: Sensors_Update()   │                       │                    │
  │ Mqtt_Set_Env(t,h,a,c,v)  │                       │                    │
  │                          │                       │                    │
  │──AT+MQTTPUBRAW=0,..─────▶│                       │                    │
  │◀──────── '>' ────────────│                       │                    │
  │──{"t":27,"h":45,...}────▶│                       │                    │
  │                          │──MQTT PUBLISH(qos1)──▶│                    │
  │                          │                       │──推送(qos1)───────▶│
  │◀──────── OK ─────────────│                       │                    │
  │  printf("[PUB] OK")      │                       │      页面更新      │

5.2 命令链路(网页 → 设备)

浏览器 H5              broker.emqx.io          ESP-12F                STM32
  │                         │                     │                     │
  │──publish(FAN_ON)───────▶│                     │                     │
  │   qos1, 主题 2604/cmd   │──推送──────────────▶│                     │
  │                         │                     │──+MQTTSUBRECV:0,..─▶│
  │                         │                     │                     │
  │                         │                     │   printf("[MQTT]     │
  │                         │                     │    cmd: FAN_ON")     │
  │                         │                     │   Remote_Cmd_Exec()  │
  │                         │                     │   CCR=800 → 风扇转   │

两条链路是独立的。 上报由主循环的 2 秒定时器驱动,命令由 ESP 的 +MQTTSUBRECV 异步推来。所以你点按钮的响应时间不一定是 2 秒,可能是几十毫秒。

5.3 关键时间常数(心里有个数)

常量值在哪里
上电到 ESP 就绪HAL_Delay(3000) + 探测Mqtt_Server_Init 开头
首次 MQTT 初始化总时长约 10~15 秒多条 AT 各自超时叠加
传感器采样2 秒主循环 last_ctrl_tick
MQTT 上报2 秒MQTT_PUB_PERIOD
按键消抖40 毫秒EXTI 回调
ESP 单条指令超时1500~2000 毫秒Esp_Cmd_Expect
发布等 >1500 毫秒Mqtt_Pub_Raw_Blocking
发布等 OK2000 毫秒同上
一个字节 @11520086.8 微秒8N1
一个字节 @96001.04 毫秒8N1

最后两行是"为什么 DHT11 会丢字节"的根源:读一次 DHT11 关中断约 4ms,9600 下正好丢 3~4 个字节。第 6 章讲过,这里只是让你在联调时别把它当成新问题。

六、「看起来是 bug 其实不是」十二条

这一节是给答辩和自测用的。每条都写清"要不要改"。

#现象真正原因要不要改
1网页显示"已连接"但没数据只代表浏览器连上 broker;设备可能没进远程 MQTT不用改,先看串口
2点按钮,设备动 2 秒又停板子在自动模式,自动逻辑每 2 秒覆盖不用改,先进远程模式
3温度跳动 ±1~2℃DHT11 精度就是 ±2℃不用改
4网页按钮状态与实物不一致本地乐观更新,无回执不用改(想要回执得加设备状态上报)
5偶尔一条 [PUB] FAILED公网 broker 抖动不用改,看是否连续
6自动模式下 [ENV] 每 2 秒一条采集即打印不用改
7[HIGH] 只出现一次last_state 防重复打印不用改
8[MODE] Remote ... | Bluetooth ON 但走的是 MQTT文案写死不用改,看 [NET] 行
9进 MQTT 后十几秒没反应阻塞初始化不用改,等 ready
10初始化期间按 KEY4 无效g_net_busy 主动丢弃不用改
11正常态窗帘是"拉开"的默认姿态就是开不用改
12网页看不到自己发的命令只订阅上报主题不用改(设计如此)

再补三条容易被当成 bug 的:

#现象原因结论
13湿度显示 45.0% 看着像小数精度DHT11 湿度本来就是"整数.小数"两字节,小数字节通常为 0正常
14温度统计卡 27.0、圆环 27°两处写法不同(toFixed(1) vs 直接拼接)正常,别以为是精度问题
15手动模式下蓝牙突然全是乱码从 MQTT 直接按 KEY2 进手动,USART2 还停在 115200见 2.6 的恢复办法

七、六个真故障的复盘

这一节是我联调当晚实际踩的(或见过最多的),每个都写了"我当时以为是"和"实际是"。

案例 1:进 MQTT 后全 FAIL

[ESP] zero bytes received -> USART2 RX not connected to ESP
[NET] ESP probe failed -> skip WiFi/broker init
setEchoOff : FAIL...

我当时以为是:ESP 坏了 / 代码回归了。
实际是:拨码开关还在蓝牙侧。USART2 的 RX 根本没接到 ESP 的 TX。

教训:zero bytes 出现,先看开关,别看代码。这是一条 30 秒能排除的检查。

案例 2:[PUB] OK 连续,网页没数据

我当时以为是:网页的解析写错了。
实际是:H5 的 TOPIC_DATA 写成了 2604/topic-hh(小写),固件是 2604/topic-HH。

MQTT 主题区分大小写。而且这是最容易发生、最难发现的差异 —— 两个字符串看起来一模一样。

教训:比对主题时,把两边并排打印出来逐字符看,不要用眼睛扫。Console 里一条 [MQTT←STM32] 都没有,就说明根本没收到,问题在订阅侧。

案例 3:点按钮,风扇转两秒就停

我当时以为是:命令丢了 / 定时器有问题。
实际是:板子停在自动模式(Mode=1)。自动逻辑每 2 秒把风扇关掉,因为当时温度正常、空气也好。

远程控制必须在远程模式下用。这是本项目最反直觉的一条:手动/远程/自动是三个互斥的状态机,自动模式下所有手动命令都会被覆盖。

教训:点按钮前先看 [ENV] ... Mode=?。Mode=0 才是手动,Mode=2 才是远程。

案例 4:[ESP] ready 反复出现

[ESP] ready
[ESP] ready
[ESP] ready
...

我当时以为是:ESP 固件有问题。
实际是:供电不足。USB 转 TTL 的 3.3V 输出电流不够,ESP 一跑 WiFi 就掉电重启。

教训:ESP-12F 必须独立 3.3V ≥500mA。看到反复 ready,先换电源,别刷固件。

案例 5:第二次上电 setMqttPara : FAIL

setMqttPara : FAIL,recv(len=7)"\r\nERROR"

我当时以为是:broker 拒绝了我。
实际是:ESP-AT 把 MQTT 配置存进 flash,上电 WiFi 一恢复它就自己 +MQTTCONNECTED 了,LinkID 0 被占着,再配置就 ERROR。

教训:必须 AT+MQTTDISCONN=0 + AT+MQTTCLEAN=0。这是第 12 章最重要的一个坑,第二次上电必现。

案例 6:离开远程一段时间,回来后没有 [PUB] OK

我当时以为是:MQTT 自己好了又坏了。
实际是:g_mqtt_inited 是 1,所以重新进 MQTT 只重挂了接收、没有重新初始化;而期间 ESP 可能重启过,mqtt_ready 已经是 0。

代码里清理 g_mqtt_inited 的判断在这段里:

if(g_mode == MODE_REMOTE && g_net == NET_MQTT)
{
    Mqtt_Poll();
    ...
    if(g_mqtt_inited && !Mqtt_Is_Ready())
    {
        g_mqtt_inited = 0;
    }
}

只有在远程 + MQTT 状态下才会清。你离开远程的那段时间,它不会清。

恢复办法:回到 MQTT 后如果一直没有 [PUB] OK,再按两次 KEY4(先切蓝牙、再切回 MQTT)。这时 g_mqtt_inited 已经被清 0,会重新走完整初始化。

教训:日志里那句 press KEY4 twice to retry 不是客套话,是真的要按两次。

八、稳定性与恢复能力

功能通了只是及格。真正的验收还要看"折腾它会不会死"。

8.1 连续跑 30 分钟,看四件事

观察正常异常
[PUB] OK 连续性连续出现,偶尔 FAILED 后恢复连续 FAILED
温度/湿度平滑变化,不卡死长时间不变 → 采集卡了
风扇/舵机状态稳定自己乱动 → 状态机被反复覆盖
ESP 是否重启无 [ESP] ready反复 ready → 供电/看门狗

8.2 四种故障注入(建议全做一遍)

这一节是联调里最有价值的,因为它验证的是恢复能力,不是功能。

注入做法期望行为
① 断外网关掉热点/拔路由器[PUB] FAILED 开始出现;恢复网络后自动回到 [PUB] OK
② ESP 复位按 ESP 的 RST,或拔插 ESP 电源日志出现 [ESP] ready → [NET] ESP rebooted -> MQTT session lost
③ 拔 USB 再插整板断电重启回到 [MODE] Manual,需要重新按键进远程
④ 只断 STM32按板子 RESET同 ③,但 ESP 可能还连着(这时容易踩"复用会话"分支)

第 ② 种恢复要有心理准备:不会自动恢复。 检测到 ESP 重启后,代码只是停止发布 + 清 mqtt_ready,需要你按 KEY4 两次重新初始化。这是有意的保守设计 —— 自动重连会在后台偷偷跑十几秒 AT 指令,期间按键全废,体验更差。

8.3 恢复路径表

故障最简恢复操作
单条 [PUB] FAILED什么都不用做
连续 [PUB] FAILEDKEY4 → KEY4(回蓝牙再回 MQTT)
ESP 重启KEY4 → KEY4
网页掉线等 3 秒自动重连,或刷新
设备命令没反应确认在远程模式;重发一次
全部乱套断电 → 拨码回蓝牙 → 上电 → 重走 2.1

8.4 缓冲与内存:三个数字

缓冲大小满了会怎样
BUF[1024](USART1 接收)1024只挂 2 字节接收,基本不会满
esp_rxbuf[512](ESP 环形缓冲)实际容量 511主循环不取就覆盖丢失;留一空位防"满/空"歧义
bt_cmd_buf[16]16攒满还没匹配 → 判为乱码清空

bt_cmd_buf 只有 16 字节,最长命令 CURTAIN_OFF 是 11 个字符 + 结束符,够用。但它没有"按换行断句"的机制,所以蓝牙通道必须"一条命令一次发完、中间不带换行、发完就停"。手机 App 里如果设了"每条自动加 \r\n",就会变成 FAN_ON\r\n —— strstr 仍然能匹配上 FAN_ON,所以实际能用,但缓冲区里会残留 \r\n 影响下一条。这是蓝牙通道比较脆的地方。

九、演示脚本与答辩

9.1 五分钟演示动线

开场前 5 分钟准备(比演示本身更重要):

□ 拨码打到 WiFi 侧
□ 手机热点开着,SSID/密码与 mqtt.c 一致(7 / 66668888)
□ 板子通电,串口助手开着(115200)
□ 浏览器页面已打开并显示"MQTT 已连接"(提前进好远程)
□ MQTTX 备用窗口开着(万一网页挂了能顶上)
□ 一段 30 秒的"待机"时间:确认 [PUB] OK 已连续出现

演示动线(按这个顺序讲,逻辑最顺):

时间动作讲解点
0:00复位板子,串口滚出 [MODE] Manual“上电默认手动,安全第一,四路全关”
0:30按 KEY1,风扇转;再按,停“手动模式:KEY1 风扇、KEY3 窗帘、KEY4 加热”
1:00按 KEY3,展示 [ENV] 一行“按键顺便打印环境数据:温湿度 + AQI/eCO2/TVOC”
1:30按 KEY3 进自动,用手捂传感器“温度 ≥30℃ → 风扇 + 窗帘开;≤29℃ → 加热 + 窗帘关,1℃ 回差”
2:30喷一点酒精靠近 ENS160“空气差 → 通风,不用你管”
3:00KEY2 进手动,KEY2 回自动,KEY4 进远程“三模式互斥,切换时先全关”
3:30切到 MQTT(KEY4),等 ready“这十几秒是在跟 ESP 一问一答,不是死机”
4:00打开网页,指着实时数据“ESP 每 2 秒发一条 JSON 到 broker,网页订阅”
4:30网页点风扇 / 全部关闭“浏览器 → wss → broker → ESP → 串口 → 板子,五跳”

讲的时候别念代码,念"为什么"。 评委不关心 HAL_TIM_SET_COMPARE,关心你为什么用 50Hz/CCR 500~2000、为什么 1℃ 回差、为什么自动模式优先温度。

9.2 一定会被问的十个问题

问题回答要点
为什么温湿度用 DHT11 不用 SHT30?成本 + 单总线省引脚;精度 ±2℃/±5%RH,教学够用
ENS160 和普通 MQ 传感器区别?数字 I2C,直接给 AQI/eCO2/TVOC 三指标,免校准
为什么自动模式温度优先?温度影响体感更直接;温度正常才让空气质量接管
1℃ 回差是干什么的?防止 29.9/30.0 反复横跳,避免执行器抖动
为什么用 1kHz PWM 驱动风扇?高于人耳可听范围附近,且 MOS 开关损耗可接受;低了会听到啸叫
舵机为什么 50Hz、CCR 500~2000?SG90 标准:20ms 周期,0.5~2.5ms 脉宽对应角度
蓝牙和 WiFi 怎么共存?硬件拨码二选一 + 软件切波特率,USART2 分时复用
为什么网页连 8084 不连 1883?浏览器只有 WebSocket,1883 是裸 TCP
为什么网页显示"已连接"却没数据?那只代表连上 broker,设备可能不在远程模式
网页按钮和设备状态不同步怎么办?目前是本地乐观更新、无回执;要同步得让设备上报状态

9.3 现场保命清单

□ 提前把 mqtt.min.js 下载到本地,改 index.html 引用(别赌教室网)
□ 手机热点提前测试能上 broker.emqx.io
□ 串口助手保存一份"正常日志"截图,异常时对比
□ 浏览器提前 Ctrl+F5 硬刷新一次
□ 板子多带一根 USB 线(线坏是最隐蔽的故障)
□ 会背"拨码 / 远程模式 / 主题"这三件事 —— 90% 的现场故障是它们
□ 演示前别改代码。演示前改代码 = 自找麻烦

十、本章易错点总结

现象真正原因处理
上电无任何日志没复位 / USART1 接线 / 波特率 / MicroLIB手动复位,逐项查
烧完日志还是旧的下载后没复位,旧程序在跑按板子 RESET
一上来就测网页,出问题无法定位跳过分层验收按 A→G 顺序
[ESP] zero bytes received拨码还在蓝牙侧 / ESP 没电 / TX-RX 未交叉先查拨码
[ESP] no valid answer, but bytes did arrive波特率不对或 ESP 没启动完等 2 秒重试,或查 ESP 波特率
进 MQTT 后十几秒没反应就以为死机阻塞初始化,属预期等 [NET] MQTT ready
初始化期间连按 KEY4g_net_busy 主动丢弃等 ready 再按
setMqttPara : FAIL(第二次上电)旧配置占着 LinkID 0必须 AT+MQTTCLEAN=0
connectWifi : FAILSSID/密码错、5G 热点、信号弱2.4G + 手机热点
MqttConn : FAIL + +PING:TIMEOUT出不了外网(网页认证 WiFi)换热点
[PUB] OK 连续,网页没数据主题大小写/字符不一致并排逐字符比对
一条 [PUB] FAILED 就判失败公网抖动看是否连续
连续 [PUB] FAILED会话失效KEY4 两次重初始化
[ESP] ready 反复供电不足独立 3.3V ≥500mA
ESP 重启后不自动恢复保守设计,需手动重进KEY4 两次
点按钮设备动 2 秒又停板子在自动模式先进远程模式
手动模式下蓝牙乱码从 MQTT 直接 KEY2 进手动,波特率还是 115200KEY2→KEY4→KEY4 回 9600
手动模式下按 KEY4 想切网络手动模式 KEY4 = 加热先离开手动
手动 KEY2 以为"退出手动到远程"实际回到自动记住三模式互斥
蓝牙发命令全 unknown 单字符第 11 章那个清空顺序 bug 未修按 11 章 5.4 改
蓝牙发了带 \r\n 的命令缓冲没断句机制一条一次发完,别加换行
自动模式 [ENV] 刷屏每 2 秒采集即打印正常
[HIGH] 只出现一次以为卡住last_state 防重复打印正常
网页"已连接"但设备不动只代表连上 broker看串口 [PUB] OK
网页命令看不到回显只订阅上报主题正常
全关后风扇还转固件 ALL_OFF 漏路 / 在自动模式被覆盖查 main.c ALL_OFF 与 g_mode
现场连不上依赖外网 CDN + 公网 broker提前离线化 + 热点
演示前临时改代码引入未验证变更别改

十一、小结:这个工程到底教会了什么

到这一章,整个系列闭环了。回头看,十四篇文章其实只在讲五件事:

① 读传感器   DHT11 单总线时序 / ENS160 I2C 寄存器
② 控执行器   TIM3 1kHz PWM / TIM5 50Hz 舵机
③ 管状态     按键消抖 + 三模式互斥 + 1℃ 回差
④ 通串口     调试口 / 蓝牙 / WiFi 分时复用 + 环形缓冲
⑤ 连云端     ESP-AT 一问一答 + MQTT 两条主题 + H5

真正难的从来不是写代码,而是把每一层的现象分离清楚:

  • 串口日志是唯一可信的"真相";
  • H5 是"展示",不是"状态";
  • 设备动作是"最终事实",但滞后;
  • 任何一层没通,上层的一切现象都不可信。

这套方法不只适用于这个项目。以后你调任何一块板子、任何一条通信链路,都是同一套动作:分层、定真相源、一次只改一个变量。

三个模块拼不起来的原因,往往不是代码写错,而是:

  • 硬件开关没跟着软件走(拨码);
  • 状态机没跟着操作走(自动模式覆盖手动命令);
  • 时间没跟着流程走(初始化没等完就按键);
  • 观察没跟着事实走(信网页不信串口)。

把这四句话记住,比记住任何一行代码都有用。


附录 A:可打印的验收清单

──────────────────────────────────────────────
 智能家居整机验收清单          日期:____  ___
──────────────────────────────────────────────
A 基础
  [ ] 上电 1 秒内出现 [MODE] Manual (KEY2) ...
  [ ] 按任意键打印 [ENV] Temp:.. Humi:.. AQI:..
  [ ] 非手动下 KEY1 → [KEY1] send_flag=1

B 传感器
  [ ] 温度 ±3℃ 内接近室温
  [ ] 湿度 30%~80%
  [ ] AQI 1~5
  [ ] eCO2 400~2000 ppm
  [ ] TVOC 0~2000 ppb
  [ ] 哈气后 eCO2/TVOC 上升

C 执行器(手动)
  [ ] KEY1 风扇开/关
  [ ] KEY3 窗帘两个位置
  [ ] KEY4 加热开/关
  [ ] 进手动时窗帘自动归位到关闭

D 自动模式
  [ ] ≥30℃ → [HIGH],风扇+窗帘开
  [ ] ≤29℃ → [LOW],加热+窗帘关
  [ ] 温度正常+空气差 → [AIR],风扇+窗帘开
  [ ] 温度正常+空气好 → 全关,窗帘保持拉开

E 蓝牙
  [ ] 手机能连 HC-06
  [ ] FAN_ON → 风扇转 + 回显
  [ ] ALL_OFF → 全关
  [ ] (前提:第 11 章清空顺序 bug 已修)

F MQTT(固件侧,用 MQTTX 测)
  [ ] [NET] MQTT ready
  [ ] 每 2 秒 [PUB] OK
  [ ] MQTTX 订阅 topic-HH 收到 JSON
  [ ] MQTTX 往 cmd-HH 发 FAN_ON → [MQTT] cmd + 风扇转

G H5
  [ ] 顶部"MQTT 已连接"
  [ ] 温湿度在刷新,圆环跟着动
  [ ] 空气质量三个数都有
  [ ] 点风扇 → 实物转
  [ ] 点全部关闭 → 全停
  [ ] Console 有 [MQTT←STM32] 与 [MQTT→STM32]

H 稳定性
  [ ] 连续 30 分钟 [PUB] OK 基本连续
  [ ] 断网后恢复 → 自动回到 [PUB] OK
  [ ] ESP 复位 → 能通过 KEY4×2 恢复
  [ ] 整板断电重启 → 能重走流程
──────────────────────────────────────────────

附录 B:正常 / 异常日志对照卡

正常(首次进 MQTT 全流程)

[KEY4] request MQTT
[NET] Switch HW dial to WiFi(W_TXD/W_RXD)! MQTT init ~10s...
[NET] MQTT init start...
[ESP] online @115200
setEchoOff : OK!!!
setWifiMode:OK!!!
connectWifi : OK!!!
[NET] reset MQTT link (DISCONN + CLEAN)...
setMqttPara : OK!!!
MqttConn : OK!!!
MqttSub : OK!!!
[NET] test publish: {"t":27,"h":45,"a":2,"c":612,"v":123}
MqttPub(test) : OK!!!
[NET] MQTT ready (WiFi + broker + sub all OK)
[NET] WiFi/MQTT @115200
[PUB] OK

异常 A:拨码在蓝牙侧

[ESP] zero bytes received -> USART2 RX not connected to ESP (dial on Bluetooth side? ESP unpowered?)
[NET] ESP probe failed -> skip WiFi/broker init (cause printed above)
[NET] MQTT init failed (see FAIL line above). press KEY4 twice to retry

异常 B:旧配置占着 LinkID 0

setMqttPara : FAIL,recv(len=7)"\r\nERROR"

异常 C:ESP 反复重启(供电不足)

[ESP] ready
[ESP] ready
[ESP] ready
[NET] ESP rebooted -> MQTT session lost, stop publishing. press KEY4 twice to re-init

异常 D:发布失败

[PUB] FAILED, payload was -> {"t":27,"h":45,"a":2,"c":612,"v":123}

附录 C:全系列索引

章一句话
1项目总览:五个功能、三条链路、硬件与软件清单
2软件环境:Keil / CubeMX / 串口助手 / 本地 HTTP 服务
3硬件连线:引脚总表、拨码开关、上电自检
4CubeMX:时钟树 100MHz、外设配置、生成工程
5工程结构:App 目录、添加 .c/.h、USER CODE 纪律
6DHT11:单总线时序、微秒延时、校验
7ENS160:I2C3、寄存器、AQI/eCO2/TVOC
8PWM 执行器:TIM3 1kHz、TIM5 50Hz、占空比与脉宽
9按键与三模式:EXTI、消抖、状态机、1℃ 回差
10串口与 printf 重定向:USART1、fputc、接收中断
11蓝牙 HC-06:AT 指令、逐字节拼命令、回显
12USART2 分时复用:ESP-AT、WiFi、MQTT、PUBRAW、环形缓冲
13H5 前端:mqtt.js、wss 8084、JSON 解析、圆环与空气卡
14整机联调:分层验收、日志判读、故障复盘、演示脚本

附录 D:全工程参数速查

引脚

引脚功能外设
PA0SG90 舵机TIM5_CH1
PA1 / PC2 / PC3 / PB10KEY3 / KEY1 / KEY2 / KEY4EXTI1 / 2 / 3 / 15_10
PA2 / PA3USART2 TX/RX蓝牙 / WiFi 复用
PA6 / PA7 / PB0风扇 / 加热 / 窗帘电机TIM3_CH1/CH2/CH3
PA8 / PC9I2C3 SCL/SDAENS160
PA9 / PA10USART1 TX/RX调试 115200
PB1DHT11 DATAGPIO
PC6 / PC7USART6语音预留(AF8)
PC13调试 LEDGPIO(发送完成翻转)

关键参数

参数值
系统时钟100 MHz(HSE 25M,PLL M25/N400/P4)
APB1 / APB250 / 100 MHz
TIM3PSC=99, ARR=999 → 1 kHz
TIM5PSC=99, ARR=19999 → 50 Hz
风扇 / 加热 PWMCCR=800(约 80%)
窗帘CCR=500(开)/ 2000(关)
高温 / 低温≥30℃ / ≤29℃
空气差AQI≥3 或 eCO2>1000 或 TVOC>650
传感器采样 / MQTT 上报2 s / 2 s
按键消抖40 ms
WiFi7 / 66668888
brokerbroker.emqx.io:1883(设备)
H5wss://broker.emqx.io:8084/mqtt
ClientID(设备 / 网页)ESP12F_2604_HH / h5_ + 随机
主题2604/topic-HH / 2604/cmd-HH

命令

命令动作
FAN_ON / FAN_OFF风扇开 / 关
HER_ON / HER_OFF加热开 / 关
CURTAIN_ON / CURTAIN_OFF窗帘拉开 / 关闭
ALL_OFF全部关闭(含窗帘落下)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值