空调这一类产品看起来“简单”——制冷、制热、除湿、送风,似乎逻辑并不复杂,但真正接触过项目开发后你会发现,空调是整个家电行业里通信链路最长、协议种类最杂、联调成本最高的品类之一。从内机主板上的温湿度传感器,到外机压缩机变频驱动板,再到线控器、集控系统、Wi-Fi模块直至手机 App 和云平台,一条数据要经过芯片引脚、板级总线、机组内部网络、家庭路由器、云端服务器等多个环节。每一段链路都有自己专属的通信协议,搞不清楚边界,调试时就会四处碰壁。
这篇文章想围绕“空调开发”这条主线,把板级、系统级和云端通信协议做一次系统梳理。不是单纯罗列协议名称,而是从实际开发视角出发,讲清楚每条链路上用什么协议、为什么选它、数据怎么流动、联调时经常遇到哪些坑。无论你是刚转入嵌入式通信领域的学生,还是在做空调控制器、暖通集控、智能家居接入的工程师,这篇内容都可以作为一份查阅型的通信地图来用。
1. 先把空调通信场景拆开,再看协议
1.1 一条温度数据从传感器到 App 要经过几跳
理解空调通信最好的方式,是跟着一条真实的数据流走一遍。假设用户通过手机 App 远程把空调设定温度从 26℃ 改到 22℃,这条指令看起来只是“按下按钮”,实际在设备端到底发生了什么,可以先看一个整体链路图:
手机 App ↓ ① HTTPS/MQTT 云平台(设备管理、指令下发) ↓ ② MQTT over TLS 家庭 Wi-Fi 路由器 ↓ ③ TCP/IP 空调 Wi-Fi 模块(乐鑫/Realtek/海思方案) ↓ ④ UART(通常是自定义帧协议) 空调内机主控 MCU ↓ ⑤ CAN / RS485(室内外机通信) 室外机变频驱动板 / 压缩机 ↓ ⑥ I2C/SPI(驱动板内部传感器读取) 温度传感器 / 电流采样芯片这里每一层之间都有一套独立协议,而且协议之间经常需要相互转换。内机主控 MCU 收到 Wi-Fi 模块通过串口发来的“设定温度 22℃”帧后,要解析出指令码和参数,再把参数转换成自己的控制逻辑能识别的数据结构,随后通过 CAN 总线发给室外机,室外机变频板再从 CAN 报文里提取目标频率,结合 I2C 读取的管温、排气温度、电流值做压缩机闭环控制。
换句话说,“设 22 度”这个动作从云端到压缩机,最少要跨 4 种以上通信协议。做嵌入式软件的同学如果只盯着单片机外设驱动,往往不会意识到自己写的 I2C 读温度函数,其实只是这条长链路里最底层的一小环。
1.2 空调通信协议的层次划分
为了便于梳理,我个人习惯把空调通信分成三个层级:
- 板级通信:指同一块 PCB 内部,或主控板与非常近的距离(几厘米到几十厘米)之间的通信。典型如 MCU 与温湿度传感器、EEROM、触摸面板、显示屏驱动之间的 I2C、SPI、UART。特点是距离短、速率要求低、实时性要求取决于传感器类型。
- 机组级通信:指空调室内机、室外机、线控器、电表、集中控制器之间的通信。典型如 CAN 总线、RS485 总线,或者针对多联机系统的专用总线。特点是距离远(几米到上百米)、节点多、抗干扰要求高、数据实时性强。
- 云端级通信:指设备通过 Wi-Fi、4G、NB-IoT 等联网模块接入云端平台的过程。典型链路是 Wi-Fi 模块通过 UART 与主控交互,再通过 TCP/UDP 栈发起 MQTT、HTTP、COAP 等应用层协议与云平台通信。特点是数据链路动态变化,需要处理断网重连、掉线补发、设备配网、证书校验等一系列连接管理问题。
很多刚入行的朋友容易把“CAN”和“MQTT”放在一起比较,这是没意义的——它们根本不在一个层级,一个是现场总线,一个是应用层消息协议。做空调通信架构设计时,第一件事是先把协议划分到正确的层级里,再谈选型。
2. 板级通信协议:I2C、SPI、UART 的空调场景
2.1 空调开发中的板级通信角色
空调主板上的板级通信是嵌入式开发者最先接触的部分。传感器、存储芯片、显示驱动、Wi-Fi 模块等,都要和主控 MCU 进行数据交换。常见的板级协议有 I2C、SPI、UART、GPIO 模拟时序等。它们各有优劣,选择取决于传输速率、引脚占用、从机数量、抗干扰能力等。
在空调开发中,板级通信常见场景包括:
- 温度传感器(如室温、管温、排气温度)通过 I2C 或单总线协议读取数据。很多空调使用 NTC 热敏电阻,通过 MCU ADC 采样即可,不涉及数字通信协议。但新款变频空调逐渐使用数字温度传感器(如 I2C 接口的 SHT30),通信时序要求更严格。
- 存储芯片(如 EEPROM、Flash)通常使用 I2C 或 SPI 接口,保存运行参数、故障记录、用户设定等。
- 显示面板(LCD、段码屏、LED 点阵)可能通过 SPI 或 I2C 驱动,数据传输量大时用 SPI,简单显示用 I2C。
- Wi-Fi 模块和主控 MCU 通信,绝大多数使用 UART,部分模组也支持 SPI 和 SDIO,但 UART 是最通用的方式,因为几乎所有 MCU 都有 UART 外设,而且 AT 指令交互简单。
如果从调试角度来说,I2C 最怕总线卡死,SPI 要留意极性和相位配置错误,UART 则经常出现波特率不匹配或帧格式不一致的问题。下面逐个展开。
2.2 I2C 通信协议在空调板级的使用与踩坑
I2C(Inter-Integrated Circuit)由飞利浦公司发明,是一种两线制同步串行总线,由 SCL(时钟)和 SDA(数据)两根线组成。它的应用场景是连接低速外设,属于板级通信中比较典型的协议。在空调主板上,I2C 总线常用于 EEPROM 存储芯片(如 AT24C02)、温湿度传感器、部分 IO 扩展芯片等。
I2C 的基本特征:
- 主从架构:MCU 作为主机,各芯片分配从机地址。
- 传输速率标准模式 100kbit/s,快速模式 400kbit/s,高速模式 3.4Mbit/s。
- 支持多主机,不过空调场景中几乎都是单主机。
- 两根线必须接上拉电阻(通常 4.7kΩ 或 2.2kΩ),具体阻值取决于总线电容与通信速率。
空调开发中 I2C 的坑点通常不在协议时序上,而在于总线异常处理与数据校验。例如,当外接传感器线束较长时,I2C 信号很容易因线间电容过大导致波形畸变,造成通信失败。下面给出一个模拟 I2C 读取 EEPROM 的示例片段,完整的 I2C 时序模拟可移植到不支持硬件 I2C 的 MCU 上。
#include "i2c_soft.h" #define SCL_H gpio_set_level(SCL_GPIO, 1) #define SCL_L gpio_set_level(SCL_GPIO, 0) #define SDA_H gpio_set_level(SDA_GPIO, 1) #define SDA_L gpio_set_level(SDA_GPIO, 0) #define SDA_READ gpio_get_level(SDA_GPIO) static void i2c_start(void) { SDA_H; SCL_H; delay_us(5); SDA_L; delay_us(5); SCL_L; } static void i2c_stop(void) { SDA_L; SCL_H; delay_us(5); SDA_H; delay_us(5); } static uint8_t i2c_read_byte(void) { uint8_t data = 0; for (int i = 0; i < 8; i++) { SCL_H; delay_us(2); data = (data << 1) | SDA_READ; SCL_L; delay_us(2); } return data; }实际空调产品中,I2C 设备地址错位、总线死锁是最常见的问题。有些传感器芯片在总线繁忙时无法正确响应 ACK,如果软件没做超时处理,整个读取流程会卡死在 while 循环里。更可怕的是 I2C 总线一直为低,后续所有从机都无法通信。此时需要在初始化时检测 SDA 状态,如果发现拉低则主动产生 9 个时钟脉冲恢复总线,或者给从机单独复位。
一个很实际的经验:I2C 通信务必开启超时退出机制,而不是死等 ACK。因为这个协议本身就没有内置错误重传,一旦某个设备故障,主控必须具备“读失败就跳过本次采样,等待下一次轮询”的能力,否则一条传感器的故障就会拖垮整个控制逻辑。
2.3 SPI 通信协议在空调显示与存储中的应用
SPI(Serial Peripheral Interface)是另一种板级同步串行协议。相比于 I2C 只有两根信号线,SPI 通常使用四根线:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。每个从机需要一根独立的片选线,所以从机多时占用的 GPIO 也就多。
SPI 的特点:
- 传输速率高,可达几十 Mbps。
- 全双工,主机发送和接收可以同时进行。
- 没有 ACK 应答机制,主机无法直接确认从机是否收到数据。
- 需要片选信号来选中目标设备,同一时刻只能与一个从机通信。
空调主板上,SPI 常用于大容量 Flash 存储、LCD 显示屏驱动,以及部分高速 ADC 芯片。如果主控是 Cortex-M 系列,硬件 SPI 外设会附带 DMA 功能,可以做到大量显示数据不需要 CPU 逐字节搬运,显著降低主控负载。
一个比较典型的场景是空调线控器上的彩屏显示,屏幕刷新数据量可能达到几十 KB,如果软件模拟 SPI 的 GPIO 翻转,主频会被大量耗时操作占用,屏幕刷新不流畅。推荐的方案是开启硬件 SPI 的 DMA 发送,主控只需要把要显示的帧缓冲区地址交给 DMA 控制器,传输完成后再触发中断。参考代码:
// 伪代码:SPI DMA 发送显示缓冲区 void lcd_dma_send(uint8_t *buf, uint32_t len) { while (DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET); DMA_ClearFlag(DMA1_FLAG_TC2); SPI_DMACmd(SPI1, SPI_DMAReq_TX, ENABLE); DMA_Cmd(DMA1_Channel2, ENABLE); }注意 SPI 有四种模式(CPOL/CPHA 组合),不同从机要求不同。很多初学者在这上面踩坑,现象是“读出来的数据全是 0xFF”或者“屏幕有噪声条纹”。排查办法是用逻辑分析仪抓 SCLK、MOSI、CS 波形,对照从机数据手册中的时序要求逐项检查。另一种容易出问题的场景是 Flash 擦写时关闭了中断导致指令超时,或者 SPI 时钟速率超过 Flash 芯片最高规格造成数据写入不稳定。在量产产品里,SPI Flash 写入异常可能导致空调故障记录无法保存,这个坑相当隐蔽。
2.4 UART(串口)是空调板级与模块通信的中枢
UART(Universal Asynchronous Receiver/Transmitter)是异步串行通信,没有时钟线,通信双方需要约定波特率、数据位、停止位、校验位。空调开发中 UART 使用频率极高,最典型的就是:
- 主控 MCU 与 Wi-Fi 模块之间的 AT 指令通信。
- 主控 MCU 与蓝牙模块之间的数据交互。
- 主控 MCU 与某些传感器模块(PM2.5、CO2、甲醛传感器)之间的串口读取。
- 调试串口打印日志。
UART 在空调板级通信中的核心作用,可以理解为“主板和外部智能模块之间的翻译官”。尤其 Wi-Fi 模块,无论是 ESP8266、ESP32 还是其他国产 Wi-Fi 模组,初期方案几乎都支持 UART AT 指令方式接入。主控不需要理解 TCP/IP、TLS、JSON 这些复杂概念,只需把 AT 指令按照约定格式通过串口发出,模块就会把数据包转发到云端。
下面是一段常用的连接 Wi-Fi 并建立 MQTT 的 AT 指令示例(以乐鑫 ESP 系列为例,指令细节因固件版本而异):
AT+CWMODE=1 AT+CWJAP="my_wifi_ssid","my_wifi_password" AT+MQTTUSERCFG=0,1,"client_id","username","password",0,0,"" AT+MQTTCONN=0,"mqtt.cloud.example.com",8883,1 AT+MQTTSUB=0,"device/001/publish",0 AT+MQTTPUB=0,"device/001/subscribe","{\"cmd\":\"set_temp\",\"value\":22}",0,0简单解释一下这段指令:第一条设置 Wi-Fi 模块为 Station 模式;第二条让模块连接家庭路由器;第三条配置 MQTT 用户属性,包括客户端 ID、用户名、密码以及 TLS 服务端证书校验开关;第四条连接 MQTT Broker;第五、六条分别是订阅和发布主题。这种模式下主控 MCU 只需要在串口中断里做字符串解析即可,压力非常小。
UART 调试中最容易忽略的问题是硬件流控和电平匹配。MCU 的 UART 通常是 3.3V TTL 电平,Wi-Fi 模块是 3.3V,但有些 4G 模块需要 1.8V 电平,接反了轻则无响应,重则烧毁模块 GPIO。另外串口的地线必须共地,否则通信会出现随机乱码。量产测试环节还容易出现波特率误差累积问题,建议波特率不要选太高的非标值,9600 和 115200 是行业最稳妥的选择。
3. 机组级通信:CAN 与 RS485/Modbus 造就空调系统的神经网
3.1 为什么空调内部机组通信不用 I2C 或 UART?
空调室内机和室外机之间、多台室内机与室外机之间,不能直接用 I2C 或 SPI 来通信。原因有三:
- 距离太长。I2C 设计初衷是板级通信,超过 1 米后信号完整性问题会很突出。
- 干扰太大。空调压缩机启动、风机调速、继电器吸合都会产生强烈电磁干扰。普通 UART 单端信号在这种环境里误码率很高。
- 节点多,实时性要求高。一拖多多联机系统中,一台室外机可能需要同时和十几台室内机通信,每个室内机都有不同的运行状态和控制指令,必须使用多主或主从轮询效率够高、误码率低的总线方案。
因此空调行业常用的机组级通信协议集中在 CAN 和 RS485 上。
3.2 CAN 总线协议在空调多联机中的应用
CAN(Controller Area Network)总线最初是博世公司为汽车电子设计的,后来大量应用于工业控制和楼宇暖通。CAN 是差分信号传输,使用 CAN_H 和 CAN_L 两根线,抗干扰能力很强。它支持多主通信,任何一个节点都可以主动往总线上发数据,不需要主机轮询,因此响应速度快,非常适合压缩机、电子膨胀阀等需要实时控制的场景。
空调多联机系统中,CAN 通信的主要特点:
- 波特率通常配置为 5kbps 到 500kbps。空调系统常用 50kbps、125kbps 或者 250kbps,需要根据总线上节点数量和距离来定。
- 报文采用 ID 优先级仲裁,ID 越小优先级越高。一般把压缩机的控制指令设为高优先级,温度采集等状态上报设为低优先级。
- 支持错误检测和错误恢复机制,瞬时干扰导致的错误帧可以自动重发。
- 硬件成本不高,很多 MCU 内置 CAN 控制器,只需要外接一个 CAN 收发器(如 TJA1050、SN65HVD230)。
下面是一个标准 CAN 数据帧的位段结构:
| 位段 | 功能 |
|---|---|
| SOF | 帧起始,表示一帧报文开始 |
| 仲裁段 | 包含报文 ID 和 RTR 位,决定优先级 |
| 控制段 | 包含 IDE、DLC,声明数据长度 |
| 数据段 | 要发送的实际数据,最多 8 字节 |
| CRC 段 | 校验数据,检测传输错误 |
| ACK 段 | 接收节点应答,确认已收到报文 |
| EOF | 帧结束 |
实际空调开发中,CAN 协议的上层往往还会自定义“应用层协议”。比如规定 0x01 代表室内机运行状态上报,0x02 代表室外机控制指令。报文里第 1 个字节放目标地址,第 2 个字节放源地址,第 3 个字节放指令码,后面字节放参数。不同的空调厂家协议格式差异很大,但核心设计原则都是:ID 尽量体现报文优先级,数据段尽量紧凑且重要信息前置。
CAN 开发调试一个很常见的现象:用 USB-CAN 分析仪连到总线上,发现不停地收到错误帧,或者某个节点总是离线。这通常不是 MCU 代码问题,而是波特率不匹配或终端电阻缺失。CAN 总线两端必须接 120Ω 终端电阻,否则信号在末端反射,波形边沿会产生台阶,误码率飙升。空调内机和外机距离远,如果每个 PCB 都默认不焊终端电阻,联调时就要在总线物理两端额外并联 120Ω 电阻。
3.3 RS485 与 Modbus 协议在空调集控中的兼容性
RS485 也是一种差分总线协议,和 CAN 类似使用双绞线传输,抗干扰能力强,最远通信距离可达 1200 米,支持最多 32 个节点(部分驱动芯片可扩展更多)。但与 CAN 不同,RS485 是半双工、主从模式,总线上的通信完全由主机(集中控制器)发起,从机不能主动上报数据。
空调集控系统中,RS485 最常见的上层协议是 Modbus RTU。Modbus 协议里,每个设备分配一个从站地址(1-247),主机通过“功能码”告诉从机要做什么操作:读保持寄存器、写单个寄存器、写多个寄存器等。空调的开关机、模式设定、设定温度、风扇转速、故障状态都可以映射为不同寄存器地址。
下面是一个 Modbus RTU 读取室内机运行状态的标准帧示例:
请求帧(主机 -> 从机): 地址 功能码 起始寄存器高 起始寄存器低 寄存器数高 寄存器数低 CRC低 CRC高 0x01 0x03 0x00 0x01 0x00 0x06 0x95 0xCB 响应帧(从机 -> 主机): 地址 功能码 字节数 数据区... CRC低 CRC高 0x01 0x03 0x0C 寄存器数据... ...用一句话拆解请求帧:读取地址为 0x01 的从机,起始寄存器为 0x0001,连续读 6 个寄存器。寄存器地址 0x0001 如果定义为当前运行模式,0x0002 定义为设定温度,0x0003 定义为室内环境温度,那么主机拿到 12 个字节的寄存器值后,就可以解析出这台空调的完整运行状态。
RS485 在空调开发中的坑,主要来自收发切换方向控制和时间间隙控制。RS485 是半双工通信,发送数据前要把 DE/RE 引脚置为发送状态,发完后必须马上切回接收状态,否则会漏掉从机的应答。问题在于 MCU 串口发送寄存器清空和引脚电平翻转之间存在时序空隙,如果代码处理不严谨,最后一个字节容易发送不完全。另一个问题是帧间隔,Modbus RTU 规定两帧之间需要至少 3.5 个字符时间的静默间隔,部分协议栈把这部分实现简化,导致连续快速轮询时从机无法正确切帧。
对于空调外机变频驱动板来说,RS485 的低成本优势依然明显,很多中小功率的单元机仍然选择 RS485 做内外机通信,而不是成本略高的 CAN。如果你在空调企业面试时被问到“为什么有些空调外机用 CAN,有些用 RS485”,可以考虑从成本、实时性、节点数、协议复杂度几个维度回答:多联机对实时性和多主通信要求高,用 CAN;普通单元机成本压力大,控制逻辑简单,用 RS485 足够了。
3.4 总线选择对比:CAN 还是 RS485
把 CAN 与 RS485 放在表格里比较会更直观:
| 对比项 | CAN | RS485 |
|---|---|---|
| 物理层 | 差分两线,CAN_H/CAN_L | 差分两线,A/B |
| 通信模式 | 多主,任意节点可主动发送 | 半双工,主从模式 |
| 最高速率 | 1Mbps(短距离) | 10Mbps(极短距离) |
| 典型速率 | 125kbps、250kbps | 9600bps、115200bps |
| 错误处理 | 有硬件错误检测与重发 | 无硬件级错误重发,依赖上层协议 |
| 终端电阻 | 两端各 120Ω | 两端各 120Ω |
| 成本 | 稍高,需要 CAN 收发器 | 较低,硬件简单 |
| 应用场景 | 多联机、变频驱动、汽车 | 集控系统、电表、楼宇自控 |
选择时还要考虑你 MCU 内部是否集成了 CAN 控制器。比如 STM32F103 就带基本扩展 CAN(bxCAN),但很多低成本 MCU 没有 CAN 外设,此时要软件模拟 CAN 或直接选 RS485。部分厂家的方案是 MCU 内部用 SPI 接口外接一个独立的 CAN 控制器(如 MCP2515),再搭配 CAN 收发器,硬件设计就复杂一些了。
4. 从板级走向云端:Wi-Fi 模块与主控 MCU 之间的通信协作
4.1 为什么 AI 空调联网部分依然保留 MCU 架构
市面上很多空调的“智能”体现在 App 远程控制、语音控制、能耗统计、故障主动上报。但即使云平台功能再丰富,空调本体的控制逻辑仍然运行在可靠的 MCU 上。Wi-Fi 模块如果直接接到压缩机控制电路里,一旦模块死机、固件升级失败或者被网络攻击,后果可能非常严重。因此几乎主流空调方案都会采用“MCU + 通信模块”分离的设计:
- MCU 负责空调核心控制逻辑,包括温度采集、压缩机启停、风机调速、电子膨胀阀控制、故障保护。
- Wi-Fi 模块负责网络接入、TCP/TLS 连接、MQTT 协议解析、OTA 固件下载。
- MCU 与 Wi-Fi 模块之间通过 UART 连接,使用私有串口协议或 AT 指令交互。
这种架构的好处是逻辑隔离、职责单一、便于过认证(无线相关的认证主要针对模块),也方便支撑未来的协议升级。MCU 程序可以长时间不做大改,只升级 Wi-Fi 模块固件来适配新的云平台或新的加密方式。
4.2 MCU 与 Wi-Fi 模块的 UART 通信设计
设计 MCU 与 Wi-Fi 模块之间的串口通信,要解决几个关键问题:帧格式、数据校验、分包粘包、应答超时、异常恢复。
一个常见的设计方案如下:
帧头 : 0xAA 0x55 帧长 : 1 字节,表示后续内容长度 命令码 : 1 字节,如 0x01 查询状态,0x02 下发控制 设备地址 : 1 字节 数据区 : N 字节 校验和 : 1 字节(数据累加和或 CRC8) 帧尾 : 0x0D 0x0A主控 MCU 向 Wi-Fi 模块下发“上报当前温度”命令时,帧内容可能是这样:
AA 55 03 01 01 64 69 0D 0A拆开解读:
AA 55:帧头,表示一帧的开始,防止粘包时字节错位。03:数据区长度,即后面01 01 64三个字节。01:命令码,0x01 代表查询当前状态。01:设备地址,表示内机 1 号机。64:参数,这里可以是附加参数。69:前面数据段的校验和。0D 0A:帧尾。
MCU 的串口接收建议使用环形缓冲区 + 状态机解析的方式。串口中断只负责把字节放入缓冲区,主循环或专用解析任务负责从缓冲区里寻找帧头、提取长度、组装完整帧、校验、分发。不要在串口中断里做耗时的 JSON 解析或字符串格式化,这会导致中断响应超时,丢失后续字节。
4.3 配网方式演变与开发注意点
空调 Wi-Fi 模块要上云,必须先让模块知道家庭 Wi-Fi 的 SSID 和密码。早期空调配网主要在 App 里输入 Wi-Fi 密码,由 App 通过 AirKiss、SmartConfig 等方式把 Wi-Fi 配置信息以广播包形式发给模块。这种方式配网成功率不稳定。
后来逐步演进为热点配网(SoftAP)方式:模块切换到 AP 模式,手机连接模块发出来的热点,然后通过 HTTP 页面或 UDP 协议把 Wi-Fi 信息传给模块,模块再切换回 STA 模式连接路由器。这种方式更稳定,但用户操作步骤多。
再到现在很多智能家居品牌采用“近距离蓝牙配网”方式:模块上集成蓝牙,手机 App 通过蓝牙把 Wi-Fi 信息传给模块,然后用 Wi-Fi 连接,配网成功率大幅提升。空调品类因为设备功率大、安装位置固定,开始大量采用“Wi-Fi + 蓝牙”双模方案。
从开发者角度看,配网相关的关键代码主要在模块侧或 App 侧,MCU 需要关心的核心点是:收到模块“配网完成并成功连接路由器”的事件后,要立即上报设备上线状态给云平台,同时更新本地状态指示,例如让空调面板上的 Wi-Fi 指示灯从快闪变为常亮。
5. 从设备到云端的通信协议:MQTT 与 HTTP/COAP
5.1 MQTT 协议为什么是智能家电上云的主流
设备联网后,主控数据和云平台之间的通信协议非常关键。目前空调行业使用占比最高的应用层协议是 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)。它基于发布/订阅模型,特别适合网络不稳定、设备数量庞大、带宽有限的物联网场景。
MQTT 的核心概念包括 Broker(消息代理服务器)、Topic(主题)、Client 端(发布者和订阅者)、QoS(服务质量)。空调设备和云平台之间的常见 Topic 设计可能是:
空调设备发布上行状态: /product/{productKey}/{deviceName}/thing/event/property/post 云平台下发控制指令: /product/{productKey}/{deviceName}/thing/service/property/set 设备上报事件: /product/{productKey}/{deviceName}/thing/event/{eventId}/post设备端订阅“属性设置”主题后,平台一旦下发设置目标温度指令,设备端收到 JSON 消息并解析后,把控制参数转成 MCU 协议帧再通过 UART 发送给空调主控。
在嵌入式资源受限环境下,MQTT 客户端库选择也很重要。若使用 ESP32,官方有 esp-mqtt 组件,内部基于 TCP/TLS,支持订阅、发布、遗嘱消息、Keep Alive。下面是一个基于 ESP-IDF 的 MQTT 连接配置参考:
#include "mqtt_client.h" esp_mqtt_client_config_t mqtt_cfg = { .broker.address.uri = "mqtts://mqtt.cloud.example.com:8883", .broker.verification.certificate = server_cert_pem_start, .credentials.username = "thermostat_device_001", .credentials.authentication.password = "device_secret_123", .session.keepalive = 60, }; esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); esp_mqtt_client_register_event(client, ESP_MQTT_EVENT_ANY_EVENT, mqtt_event_handler, NULL); esp_mqtt_client_start(client);这里特别要提醒安全边界:设备上云必须启用 TLS 加密,并且要校验服务器证书。不要把设备密钥硬编码在客户端代码里,建议在产线上为每台设备烧录唯一的设备证书/密钥,云端再结合设备证书做双向认证。空调被非法控制除了造成财产损失,还可能导致制热环境下超温运行引发安全隐患,所以“设备身份认证”不是可有可无的事,而是底线要求。
5.2 HTTP 与 COAP 协议在空调云通信中的定位
HTTP 协议也常用于设备与云端通信,但它本质是客户端主动请求服务器模式,设备需要不断轮询或依靠长连接,大量不必要的数据交互会导致流量浪费。HTTP 适合设备升级包下载、设备首次激活获取 Token 这类低频、大数据量的场景。
COAP(Constrained Application Protocol)则专为资源受限设备设计,基于 UDP,报文采用二进制格式,比 HTTP 更轻量。不过 COAP 在空调设备中应用并不像 MQTT 那样普遍,因为空调不像传感器节点一样严重受限于电力或带宽,Wi-Fi 模组性能足够跑 MQTT over TLS。COAP 更常见于 NB-IoT、LoRa 等低功耗广域网场景的智能表计和水浸传感器等。
做空调云平台接入时,我的建议是:设备状态上行和控制下行主链路优先考虑 MQTT;OTA 固件升级和日志文件上传用 HTTPS;设备首次配网获取 Token 或证书申请用 HTTPS;不要用 HTTP 明文传输任何控制指令。
5.3 设备端数据格式:JSON 在 MCU 侧的处理策略
云平台和设备之间传输的控制指令,通常使用 JSON 格式,因为便于平台端解析和业务逻辑扩展。但空调整机的主控 MCU 资源往往很紧张,处理 JSON 可能让 ROM 和 RAM 都压力山大。这种矛盾的解决方案是:
- Wi-Fi 模块负责 JSON 的编码与解析:模块收到云平台下发的 JSON 消息后,先解析提取字段,再打包成紧凑的二进制帧发给 MCU。
- MCU 只管私有二进制协议:MCU 不解析 JSON,只需按帧格式收发数据。比如收到“设定温度 22℃”的二进制帧,提取温度字段即可执行后续逻辑。
反过来,MCU 上报状态时,也是先发给模块一个紧凑的二进制状态帧,模块负责包装成 JSON 并通过 MQTT 发布。这个架构的关键是 MCU 和模块之间需要定义一套足够清晰、可扩展的串口映射表:
0x10 查询设备基本信息 0x11 上报设备运行状态(温度、模式、风速) 0x12 设置运行模式(制冷/制热/除湿/送风) 0x13 设置目标温度 0x14 设置风速 0x15 查询故障状态如果 MCU 资源实在紧张到无法处理复杂状态机,也可以考虑在 MCU 上使用轻量 JSON 库(如 cJSON),但只建议用于调试阶段,不建议在生产固件里大量解析 JSON。因为 JSON 解析天然存在内存碎片、深层嵌套解析不完备等问题,会让系统稳定性打折扣。
6. 空调通信联调实战:一条“设 22 度”指令的完整链路
6.1 设备端软件模块划分
前面聊了这么多协议,下面用一个具体场景把整个联调过程串起来。假设我们要实现:“用户从手机 App 把空调设定温度从 26℃ 改为 22℃。” 涉及的模块包括:
- 手机 App
- 云平台 MQTT Broker
- 家庭 Wi-Fi 路由器
- 空调 Wi-Fi 模块
- 空调内机主控 MCU
- 室内外机通信总线(假设是 CAN bus)
- 室外机变频驱动板
6.2 云平台下发的报文格式示例
手机 App 点击“22℃”按钮后,云平台将控制指令包装成一条 MQTT 消息。Topic 是设备订阅的属性设置主题,Payload 如下:
{ "method": "thing.service.property.set", "id": "123456", "params": { "target_temp": 22, "unit": "celsius" }, "version": "1.0" }Wi-Fi 模块收到后,先解析 JSON,提取params.target_temp = 22,再根据模块协议映射表,把它转成一条发给内机主控 MCU 的串口二进制帧:
AA 55 05 22 01 02 00 16 40 0D 0A字段拆解:
AA 55:帧头。05:数据区长度(后面 5 个字节)。22:设备地址,表示内机 1 号。01:命令码,0x01 表示控制命令。02:控制类型,0x02 表示设置目标温度。00 16:大端表示目标温度 0x0016,即十进制 22。40:前面数据的校验和(这里按累加和计算)。0D 0A:帧尾。
6.3 MCU 收到指令后的处理流程
内机主控 MCU 的 UART 接收状态机解析到一帧完整数据后,先从校验和确认数据没有被破坏,再根据命令码分发到温度控制模块。这个判断过程可以用一个简化函数表示:
int temp_control_handle_frame(const uint8_t *frame, uint16_t len) { if (!check_frame_crc(frame, len)) { return -1; } uint8_t dev_addr = frame[2]; uint8_t cmd = frame[3]; uint8_t ctrl_type = frame[4]; if (cmd != 0x01) { return -2; } if (ctrl_type == 0x02) { // 设置目标温度 int16_t target_temp = ((int16_t)frame[5] << 8) | frame[6]; target_temp = (target_temp > 3000) ? 3000 : (target_temp < 1600) ? 1600 : target_temp; system_set_target_temp(target_temp / 100.0f); return 0; } return -3; }注意这里我使用了target_temp / 100.0f的换算逻辑,因为部分帧协议为了避免小数会把温度乘以 100 再传输,比如 22.00℃ 就是 2200。实际工程中这个精度可以根据需求调整,用 10 倍还是 100 倍,需要和云端定好数据定义,避免出现“设置 22℃,空调却跑到了 2200℃”的笑话。
内机 MCU 更新目标温度后,需要通过 CAN 总线把新的目标温度同步给室外机。假设 CAN 报文的 ID 为 0x0A(高优先级控制帧),数据格式如下:
CAN ID : 0x0A DLC : 0x08 数据字节0 : 源地址 0x01(室内机) 数据字节1 : 目标地址 0x10(室外机) 数据字节2 : 控制码 0x03(温度设定) 数据字节3 : 温度整数部分 22 数据字节4 : 温度小数部分 0 数据字节5 : 备用 0x00 数据字节6 : 备用 0x00 数据字节7 : 校验和或计数器发送代码参考:
void can_send_target_temp(uint8_t src, uint8_t dst, float temp) { CAN_TxHeaderTypeDef tx_header; uint8_t tx_data[8]; tx_header.StdId = 0x0A; tx_header.IDE = CAN_ID_STD; tx_header.RTR = CAN_RTR_DATA; tx_header.DLC = 8; tx_data[0] = src; tx_data[1] = dst; tx_data[2] = 0x03; tx_data[3] = (uint8_t)((int)temp); tx_data[4] = (uint8_t)((temp - (int)temp) * 10); tx_data[5] = 0x00; tx_data[6] = 0x00; tx_data[7] = 0x00; if (HAL_CAN_AddTxMessage(&hcan1, &tx_header, tx_data, &tx_mailbox) != HAL_OK) { // 错误处理,例如总线繁忙时重试 } }室外机收到 CAN 报文后,解析出目标温度,再结合室内环境温度、蒸发器管温、室外环境温度等参数进行压缩机目标频率计算。这部分涉及变频空调的核心控制逻辑“PID + 模糊算法”,虽然不属于通信协议范围,但通信的实时性和可靠性直接决定了控制算法能不能拿到准确输入。如果 CAN 报文延迟 100ms,也许还能容忍;如果报文丢失导致室外机频繁启停,那通信问题就被转化成了整机性能投诉。
6.4 室外的数据上行:状态上报链路
空调执行温度设定后,需要把最新状态上报到云端,让 App 界面更新。完整的反馈路径为:
外机控制结果 -> (CAN)-> 内机 MCU -> (UART)-> Wi-Fi 模块 -> (MQTT over TLS)-> 云平台 -> App
内机 MCU 把最新的运行状态打包成二进制帧给 Wi-Fi 模块,模块负责 JSON 编码和 MQTT 发布:
{ "method": "thing.event.property.post", "id": "654321", "params": { "target_temp": 22, "current_temp": 26.5, "mode": "cool", "fan_speed": "auto", "power": "on" }, "version": "1.0" }云平台解析这条状态后,会比对设备影子(Device Shadow)中保存的期望值与设备实际报告值是否一致。如果设备已成功执行 22℃,那么影子状态被更新,App 收到推送后展示最新温度。这里有一个常见的开发陷阱:设备端上报要在控制指令执行完成之后触发,不要一收到指令就盲目上报“已执行”,一定要等到传感器或控制步进真正完成后再上报,否则 App 上看到的设置值和实际运行状态不一致,用户就会反复提交指令,造成云端和设备的“指令风暴”。
7. 空调通信协议的调试与测试工具箱
7.1 串口级调试:逻辑分析仪与串口助手
MCU 与 Wi-Fi 模块的 UART 报文调试,建议使用逻辑分析仪抓取真实波形,不要只依赖串口助手打印。逻辑分析仪能直观看到帧头、数据帧间隔、字节之间是否有异常延迟。采样率建议设置为波特率的 8 倍以上,例如波特率 115200 时用 1MHz 采样率,可以清晰看到每个 bit 的电平变化。如果用串口助手透传调试,请关闭“发送新行”里的自动加\r\n选项,否则会莫名多出两个字节,导致对端解析错位。
7.2 CAN 总线调试:CAN 分析仪抓包法
CAN 总线最有效的调试手段是使用 USB-CAN 分析仪,把 PC 接到 CAN 总线中,像抓网络包一样抓取所有 CAN 报文。调试要点:
- 配置与设备一致的波特率,把比特率误差控制在 0.5% 以内。
- 在总线空闲状态下观察是否有错误帧或 Bus Off。
- 如果收不到报文,先用示波器查看 CAN_H 与 CAN_L 之间的差分波形,正常显性电平差约 2V。
- 使用分析仪的“自发自收”功能验证收发器芯片和 MCU CAN 控制器是否正常。
7.3 MQTT 云端调试:本地 Broker 与抓包
云端联调难度大,容易受到公网波动、证书过期、DNS 解析等因素干扰。推荐的调试方法是先在本地搭建一个 MQTT Broker(如 EMQX、Mosquitto),让设备和本地 Broker 通信,验证设备端协议逻辑。本地调通后再切换到云端环境,逐步排查网络链路。
本地调试时可以在 PC 上开启 Wireshark 抓包,过滤端口 8883 或 1883 查看 MQTT 报文,但需要注意 TLS 加密后内容不可直接读取。如果只是验证连接是否建立,看 TCP 握手与 TLS 握手包就够了。若需要进一步观察业务报文,可以在设备端暂用 1883 明文端口做本地测试,测试完成后必须切回加密端口,避免把安全隐患带入生产环境。
7.4 常见问题排查清单
把空调通信开发中的高频问题整理成一张排查表:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| MCU 读不到 I2C 温湿度数据 | 总线死锁、地址错误、上拉电阻缺失 | 用万用表测量 SDA/SCL 电平,检查从机地址和上拉电阻 |
| SPI Flash 读写数据不稳定 | CPOL/CPHA 配置错误、时钟过快 | 对照数据手册时序图,降低 SPI 时钟测试 |
| UART 收到数据乱码 | 波特率偏差、地线不共地、干扰 | 使用逻辑分析仪解码,检查共地,降低波特率 |
| Wi-Fi 模块搜索不到家庭网络 | 路由器 5G 频段不兼容、配网流程出错 | 优先用 2.4G Wi-Fi 测试,确认模块进入配网模式 |
| MQTT 连接频繁掉线 | Keep Alive 过短、网络不稳定 | 根据实际网络环境调整 Keep Alive 为 30-120 秒 |
| CAN 总线错误帧较多 | 波特率不匹配、缺少终端电阻 | 用 CAN 分析仪查错,确认总线两端 120Ω 电阻 |
| 设备已执行但 App 状态不更新 | 状态上行上报时机不对 | 确保控制执行完成后再上报状态 |
| 设备证书校验失败 | 设备时间错误、根证书过期 | 检查设备 RTC,更新根证书,关闭失效证书缓存 |
8. 工程最佳实践:把通信可靠性做在产品代码里
空调产品属于“长期在线、低容错”的智能设备,通信链路中的任何一个环节出了故障,轻则控制失灵,重则造成设备损坏。因此从第一天写代码时就要把通信可靠性设计进去。
8.1 帧协议设计:留好版本号与扩展位
无论是板级 UART 私有协议还是 CAN 应用层协议,都应该在协议开头预留一个版本号字段。空调产品生命周期长达 5-10 年,后期增加新风控制、净化模块、语音交互时,如果一开始没有预留协议扩展空间,升级过程就会非常痛苦。我见过一些项目在协议帧里用掉了所有字节,后期为加一个字段不得不整帧重定义,导致老设备固件全部不兼容,只能通过云端 OTA 强刷,升级战线拉得非常长。
8.2 通信超时与重连机制
所有通信链路都要有超时概念。MCU 与 Wi-Fi 模块之间如果超过 5 秒没有收到心跳应答,就应该主动重启模块或做一次 UART 复位。模块与云端断线后,需要按指数退避策略重连(1 秒、2 秒、4 秒……最大 5 分钟),避免在路由器或云端故障恢复时,大量设备同时请求导致服务端压力过大。
设备本地还需要维护一个“平台连接状态标志”。当断网时间过长时,空调应该自动进入“本地控制模式”,用户按面板或遥控器仍然可以正常控制空调,只是无法远程操作。等网络恢复后,设备要把离线期间的状态变化补报给云端,并拉取云端下发的影子消息,确保远端状态与本地真实状态一致。
8.3 数据安全与合法授权
这是空调云通信必须划清楚的红线部分。设备接入云端时,需要保证整个链路使用 TLS 加密,服务端证书校验和客户端证书双向认证不能省。设备身份信息要在产线烧录时写入安全区,一机一密,防止同一套密钥在所有设备间复用被攻破后出现“批量仿冒”。开发者不能为了方便调试而把生产环境密钥、测试账号提交到 Git 仓库。使用云平台时必须遵守服务商的使用协议和当地法律,只接入合法运营的云平台。整机产品若涉及远程控制空调,OTA 升级指令也要在云端做操作者和设备自身的双重校验,防止越权指令下发。
8.4 日志和可观测性
空调设备本身资源有限,无法像服务器一样记录庞大数据日志。但我们需要在关键节点留下轻量痕迹:Wi-Fi 模块启动记录、路由器连接成功点、MQTT 连接成功点、首次上报状态时间点、每次收到控制指令的时间。这些事件用环形日志存储在 Flash 中,用户在售后反馈“空调有时无法远程控制”时,工程师远程拉取设备日志就能快速判断问题是出在 Wi-Fi 模块、路由器连接、MQTT 连接还是 MCU 交互层。
日志设计要遵循“正常不刷屏、异常打关键”原则。例如每 5 分钟定时上报状态是正常行为,不需要每次打印完整 JSON;但连接失败、重连退避、CRC 校验错误、连续控制失败等异常路径必须记录。
8.5 开发阶段建议预留的测试接口
硬件设计阶段建议预留以下调试接口:
- 主控 MCU 引出独立的 UART 调试串口,不要和 Wi-Fi 模块串口共用。
- 通信模块底板预留 CAN 收发器接口和终端电阻可选位。
- 电源设计上,Wi-Fi 模块在发射瞬间会有较大电流波动,要给模块独立 LDO 或 DC-DC,并做好地与主控地之间的单点连接。
- 预留状态指示灯,至少能区分“模块上电”“Wi-Fi 已连接”“云端已连接”三个阶段。
量产阶段,产线测试要覆盖:Wi-Fi 模块 SSID 扫描、路由器连接、MQTT 连云、设备信息上报、远程控制响应时间。如果这些测试出现在用户家里才第一次跑通,售后成本会成倍上升。
9. 总结与下一步学习路线
空调嵌入式通信开发是一个典型的“层层协议 + 软硬结合”的领域。从本文的梳理可以看出,真正的难点不在于单个协议有多复杂——I2C、UART、CAN、MQTT 每一种单独拿出来都是成熟技术。难点在于把这些协议放到一条完整的数据链路上,让它们在不同层级之间无缝转换,并且在整个生命周期内保持稳定。开发中要形成分层意识:板级解决芯片间的数据搬运,机组级解决强电磁干扰环境下的可靠通信,云端解决设备远程管理与状态同步。
对于刚接触这个方向的同学,建议按这样的顺序逐步深入:
- 首先用开发板跑通基础总线协议,I2C 读温湿度传感器、SPI 驱动 LCD、UART 与 PC 通信。
- 然后学习在 MCU 上写一个带状态机的串口帧接收解析器,通过模拟 Wi-Fi 模块下发指令来测试自己的解析逻辑。
- 再进一步接触 CAN 总线,配合 USB-CAN 分析仪实现两组 MCU 的 CAN 收发通信。
- 最后把 ESP32 或 Wi-Fi 模块接到云端,跑通 MQTT 发布订阅流程,把传感器数据或控制状态移植到自己的设备协议里。
如果本文对你有帮助,可以收藏备用,后续开发需要时翻到对应章节排查。如果你已经做过空调或其他家电的通信开发,欢迎在评论区分享你遇到过的奇葩通信坑,比如某个“神秘”的校验算法、某种只能在特定批次硬件上复现的总线超时,互相借鉴经验会让后面的开发顺利不少。