空调开发中,嵌入式通信协议贯穿从板级到云端的全部环节,越是接近量产,越能看出通信设计的好坏。一个空调控制器既要读取温湿度、压力、风速等传感器,又要驱动变频压缩机、显示面板、步进电机,还要接收红外遥控指令、接入 WiFi/BLE,最后把运行状态经 MQTT 等协议上报到云端平台。这里的每一步都对应一类通信协议:板级常用 I2C、SPI、UART,设备间多用 CAN 和 RS485,向云端走则普遍选择 MQTT。下面按这条链路把协议选型、连接方法、报文结构、验证手段和排错思路串起来,适合正在做空调控制器、智能家居设备或物联网网关的嵌入式工程师参考。读完以后,可以按层判断问题出在板级采集、设备间通信还是云端链路,并能独立搭建一段最小可运行的验证示例。
1. 空调通信链路的三层结构:板级、设备级、云端各管什么
1.1 三层通信的职责边界
空调控制器里的通信任务并不是一个协议能覆盖的。它至少可以被拆成三层,每层的物理环境、数据量和可靠性要求都不同。
板级通信发生在同一块 PCB 或同一块控制器内部,时间尺度是微秒到毫秒。主控 MCU 需要读取温度传感器、湿度和压力传感器,可能需要读写 EEPROM 保存用户设置和历史参数,也可能要驱动一块点阵液晶或段码屏。这一层的特点是距离很短、布线可控、时序要求严格,接口通常直接挂在 MCU 的外设引脚上,常用 I2C、SPI、单总线、UART,甚至纯 GPIO 模拟时序。
设备级通信发生在空调不同部件之间,已经不局限于同一块板卡。室内机主控要把状态发给显示线控器,要和室外机交换压缩机频率、故障码、环境温度;大一点的商用空调还会接楼宇自控系统。这一层距离可能从几十厘米延伸到几十米甚至上百米,需要更强的抗干扰能力和错误检测机制,所以 CAN、RS485、Modbus RTU、红外协议会在这一层出现。
云端通信是空调具备联网能力之后新增的一层。设备端通过 WiFi 模组、BLE、以太网或蜂窝网络接入物联网平台,使用 MQTT、HTTP 或私有 TCP 长连接把温度、模式、风速、故障状态上报到服务端,同时接收平台下发的开关机、目标温度、定时策略等指令。这个部分的网络环境最不稳定,存在弱网、断网、丢包、平台重启等情况,因此需要在设备端设计重连机制和报文确认机制。
| 层级 | 通信范围 | 典型协议 | 空调场景中的典型用途 |
|---|---|---|---|
| 板级 | PCB 内部,几厘米到十几厘米 | I2C、SPI、UART、单总线、GPIO | 读温湿度、读写 EEPROM、驱动显示、接调试串口 |
| 设备级 | 控制器与面板、内外机、楼宇设备之间 | CAN、RS485、Modbus、红外 | 内外机通信、线控器交互、楼宇 BA 接入 |
| 云端 | 设备到物联网平台或服务器 | MQTT、HTTP/HTTPS、私有 TCP | 状态上报、命令下发、OTA、远程诊断 |
1.2 各类通信协议怎么选,关键看四个指标
选型时不要凭习惯或别人用过就定,要先看四个指标:通信距离、传输速率、可靠性机制、开发成本。
板级 I2C 适合连接寄存器型传感器和存储芯片,线少,但从设备地址是全局分配的,速率一般到 400 kHz 或 1 MHz,距离拉长以后上升沿变缓,容易出问题。SPI 速率高,适合视频缓存、大容量 Flash、LCD 等批量数据场景,但至少需要 4 根线,而且 SPI 主机必须知道从机的时钟极性和相位配置。UART 是设备之间最通用的接口,速率以 9600 和 115200 最常见,它只定义物理层字节传输,一帧数据的边界、校验、重传都要自己实现。
从板级跨到设备级,CAN 和 RS485 通常比 UART 更可靠。CAN 使用差分信号,带硬件仲裁和错误帧重发,适合多节点实时控制;RS485 也是差分信号,半双工主从模式,成本低,是中长距离 Modbus 通信的物理层基础。无线通信则要单独评估信道占用、干扰、丢包和功耗。
| 协议 | 典型速率 | 典型距离 | 可靠性机制 | 常见使用场景 |
|---|---|---|---|---|
| I2C | 100 kHz / 400 kHz,部分器件支持 1 MHz | 板级,通常小于 30 cm | 从机 ACK,但缺少内置 CRC 或重传 | 温湿度传感器、EEPROM、IO 扩展 |
| SPI | 几 MHz 到几十 MHz | 板级为主,走线短 | 全双工,无帧级 ACK,需软件上层保证 | Flash、LCD、触摸屏、高速 AD 采集 |
| UART | 通常 9600 bps 或 115200 bps | 板内或短距离线缆 | 只有帧级起始停止位,校验可加可省 | 调试日志、WiFi/BLE 模组透传 |
| CAN | 常见 250 kbit/s 或 500 kbit/s,最高 1 Mbit/s | 随速率下降,通常几十米 | 硬件仲裁、错误帧检测、自动重发 | 多联机内外机通信、变频驱动控制 |
| RS485 | 常见 9600 bps、19200 bps、115200 bps | 低波特率可达几百米至上千米 | 物理层差分,应用层需再加 CRC 重传 | 商用空调接楼宇、多台设备轮询 |
| MQTT | 取决于网络,不取决于协议本身 | 局域网或公网 | QoS 0/1/2、遗嘱、会话保持 | 空调状态上云、平台命令下发 |
这一节的核心判断是:空调项目里没有“最好的协议”,只有“这一层最合适的协议”。板级追求低成本和总线简单,设备级追求稳定和抗干扰,云端追求弱网可用和持续连接。下面从底层开始逐层看实现方法。
2. 板级通信最常接触的是这几类:单总线、I2C、SPI 和 UART
2.1 温度与湿度采集:NTC、DHT11 和 I2C 传感器并存
空调系统采温点是分散的。室内环温、蒸发器管温、室外环温、压缩机排气温度都可能使用 NTC 热敏电阻,MCU 通过 ADC 测量分压电压后查表得到温度,这种方式并不属于数字通信,但它在空调主板上占比最高。随着智能化程度提高,很多产品会再加入一块数字温湿度传感器来获得室内湿度,常见的选择是 DHT11、AHT20、SHT30 这类小封装器件。
DHT11 使用的是单总线协议,只有一根数据线,主机需要严格按时序发起读取。常见流程是主机把总线拉低 18 ms,然后释放总线,等待从机响应,再从总线里读回 40 bit 数据。40 位数据里包含湿度整数、湿度小数、温度整数、温度小数和校验和。读取函数里最容易出错的是对高低电平宽度的判断,因为单总线要求微秒级延时。
/* 单总线读取 DHT11 一个字节,时序依赖较强 */ static uint8_t dht11_read_byte(void) { uint8_t value = 0; for (uint8_t i = 0; i < 8; i++) { /* 等待数据线拉低,再等待 30us 左右采样判断电平 */ while (dht11_in_read() == 0); delay_us(30); if (dht11_in_read() == 1) { value |= (0x80 >> i); } /* 等待高电平结束,进入下一位 */ while (dht11_in_read() == 1); } return value; }使用 DHT11 前要确认你用的延时函数是阻塞延时还是任务调度延时。在 RTOS 环境里直接调用vTaskDelay做微秒级时序往往不准确,需要关中断或使用硬件定时器,否则数据位很容易错位。如果产品希望减少 MCU 资源占用,更推荐换成 I2C 接口的 AHT20 或 SHT30,它们把时序管理放在芯片内部,主机只按寄存器命令读取就能得到结果。
/* I2C 读取 AHT20 温湿度,器件地址以实际数据手册为准 */ static int aht20_read_temp_humidity(float *temperature, float *humidity) { uint8_t cmd[] = {0xAC, 0x33, 0x00}; uint8_t data[6] = {0}; /* 触发测量 */ i2c_write_bytes(AHT20_ADDR, cmd, sizeof(cmd)); /* 等待测量完成,不同传感器等待时间不同 */ delay_ms(80); /* 读取 6 字节状态和数据 */ i2c_read_bytes(AHT20_ADDR, data, sizeof(data)); *humidity = ((uint32_t)(data[1] << 12) | (data[2] << 4) | (data[3] >> 4)) * 100.0f / (1 << 20); *temperature = (((uint32_t)(data[3] & 0x0F) << 16) | (data[4] << 8) | data[5]) * 200.0f / (1 << 20) - 50.0f; return 0; }这里要注意,I2C 器件的 7 位地址和 8 位地址在不少 SDK 中存在混用。如果代码里写错高低位,主机会收到 NACK,或者一直读到 0xFF。AC 命令、测量时间和唤醒操作都应以最终选型的芯片手册为准。
2.2 SPI 在空调主板里一般负责什么
SPI 在空调主板上的存在感没有 I2C 那么强,但只要出现,往往是为了传输批量数据。典型场景包括:使用大容量 SPI NOR Flash 保存故障记录和升级包,使用 SPI 接口的 LCD 显示中文菜单,或者用 SPI 扩展多路 IO 去驱动继电器和阀体。SPI 是全双工同步串行接口,至少使用时钟线 SCLK、主机输出从机输入 MOSI、主机输入从机输出 MISO、片选 CS 四根线。
每个从设备都需要一根独立片选线,主机把 CS 拉低后,从机才能和主机通信。SPI 没有 ACK bit,任何一端的时钟极性或相位配置不统一,从机都会返回毫无意义的数据。在驱动 SPI Flash 时,寄存器读、扇区擦除、页编程是三条基本操作。
/* 伪代码:向 SPI Flash 写入一页数据 */ uint8_t page_program_cmd[] = { 0x02, /* Page Program 命令,不同 Flash 可能不同 */ addr >> 16, /* 地址高字节 */ addr >> 8, /* 地址中间字节 */ addr & 0xFF, /* 地址低字节 */ data[0], data[1], data[2] }; spi_select(FLASH_CS); spi_transmit(page_program_cmd, sizeof(page_program_cmd)); spi_deselect(FLASH_CS);使用 SPI 最大的坑是 CPOL 和 CPHA 不匹配。主机用模式 0,从机用模式 1,数据在错误的时钟沿被采样,轻则数据错位,重则完全无响应。联调前先确认两边的数据手册,最好用逻辑分析仪抓 CLK、MOSI、MISO 三根线,确认空闲电平和采样沿是否符合预期。
2.3 UART 是主控的“日志口”和“帧读写口”
UART 是空调嵌入式开发必须熟练掌握的一类接口。硬件上它只有 TX、RX 两根线,全双工通信,工作频率一般用波特率表示。空调项目里至少要保留一个 UART 做调试日志输出,把协议栈状态、传感器值、错误码打印出来;如果产品是 WiFi 模组或 BLE 模组,主控还会用另一个 UART 和模组交换 AT 指令或透传数据。
UART 本身在物理层只保证字节时序,它不知道一帧命令从哪里开始、到哪里结束。所以工程上必须自定义帧协议。常见做法是规定帧头、数据长度、命令字、数据和校验和。
[帧头] [帧头] [数据长度] [命令字] [数据...] [校验和] AA 55 0x02 0x01 0x00 0x??下面用状态机方式解析串口数据,可以避免按中断一次处理完整帧,既支持随机时间到达的字节,也防止数组越界。
typedef enum { WAIT_FRAME_HEADER_1, WAIT_FRAME_HEADER_2, WAIT_FRAME_LENGTH, WAIT_FRAME_DATA, WAIT_FRAME_CHECK } uart_rx_state_t; void uart_rx_state_machine(uint8_t byte, uint8_t *payload, uint8_t *len) { static uart_rx_state_t state = WAIT_FRAME_HEADER_1; static uint8_t frame_len = 0; static uint8_t frame_index = 0; switch (state) { case WAIT_FRAME_HEADER_1: if (byte == 0xAA) state = WAIT_FRAME_HEADER_2; break; case WAIT_FRAME_HEADER_2: if (byte == 0x55) { state = WAIT_FRAME_LENGTH; } else { state = WAIT_FRAME_HEADER_1; } break; case WAIT_FRAME_LENGTH: frame_len = byte; frame_index = 0; state = WAIT_FRAME_DATA; break; case WAIT_FRAME_DATA: payload[frame_index++] = byte; if (frame_index >= frame_len) {