基于STM32与FreeRTOS的智能空调遥控系统设计与实现
2026/5/26 1:11:12 网站建设 项目流程

1. 项目概述:一个“民主化”的空调遥控器

在开放式的办公空间里,空调温度设定常常引发“战争”。有人觉得冷,有人觉得热,遥控器还经常不知所踪。这个项目就是为了解决这个痛点而生的:一个基于STM32微控制器的“民主化”空调遥控系统。它的核心思想很简单——让在场的每个人通过手机App投票决定温度,系统根据投票结果(比如计算平均值或中位数)自动调整空调设定,实现“少数服从多数”的温控民主。

我选择使用ST的STM32WB5MM-DK开发板作为核心,因为它集成了BLE(蓝牙低功耗)功能,非常适合连接多部手机。目标空调是Atlantic Fujitsu的某一型号,这意味着我需要先破解它的红外遥控协议。整个系统设计为双模式运行:手动模式下,它就是一个功能完备的实体遥控器;BLE模式下,则开启民主投票功能,最多支持8部手机同时连接,并根据在场人员的投票动态调整温度。

这个项目的价值不仅在于解决一个具体的办公环境问题,更在于它提供了一个可复用的框架。除了针对特定空调的红外编码部分,其多设备连接、投票逻辑、用户界面等模块,完全可以移植到其他需要群体决策的物联网控制场景中。

2. 核心设计思路与架构解析

2.1 为什么选择这样的技术栈?

选择STM32WB5MM-DK是经过深思熟虑的。首先,STM32WB系列是ST推出的双核无线MCU,一个Cortex-M4内核处理应用,一个Cortex-M0+内核专用于处理蓝牙协议栈。这种硬件隔离的设计,能确保无线连接的稳定性和实时性,不会因为应用层的复杂逻辑而断连,对于需要同时连接多部手机的场景至关重要。

其次,这块开发板自带OLED屏幕、按键、飞行时间(ToF)距离传感器,几乎为我们这个项目量身定做。OLED用于显示状态和菜单,按键用于手动操作和导航,ToF传感器则被创新性地用作“非接触式旋钮”,通过手势距离来精细调节数值,这比单纯的加减按键体验好得多。

最后,使用FreeRTOS实时操作系统是管理多任务的必然选择。系统中有多个需要并行运行的任务:处理用户界面(HMI)、监听按键、读取ToF传感器、测量环境温度、运行BLE服务、生成红外信号等。FreeRTOS的任务调度、消息队列和信号量机制,能让这些任务井然有序地协作,代码结构也更清晰、易于维护。

2.2 系统工作模式详解

系统设计了两种核心工作模式,以覆盖不同场景的需求:

  1. 手动模式:此模式下,BLE投票功能被禁用。用户直接通过设备上的物理按键和ToF传感器来设置温度、模式、风速等所有参数,就像使用一个普通的智能遥控器。设置完成后,设备会立即通过红外发射管将指令发送给空调。这个模式适用于临时单独使用空间,或者网络出现问题时作为备用方案。

  2. BLE民主模式:这是项目的核心模式。设备启动BLE广播,附近的同事可以用手机App(待开发)搜索并连接到名为“Demo_clim”的设备。连接后,App上会显示当前环境温度和可设定的温度范围,用户可以进行“投票”。设备端会持续扫描周围的蓝牙信号强度(RSSI),以此粗略判断用户是否在附近(例如,信号强度低于某个阈值则认为用户已离开)。温度设定点(Setpoint)的计算逻辑是关键,目前考虑两种算法:

    • 平均值:计算所有“在场”用户投票温度的平均值。优点是计算简单,能反映整体倾向;缺点是容易被极端值(比如个别人特别怕冷或怕热)带偏。
    • 中位数:将所有“在场”用户的投票温度排序,取中间值。优点是对异常值不敏感,更能代表大多数人的“舒适区间”。 系统会根据环境温度自动判断空调应处于制冷还是制热模式,然后应用计算出的设定温度。

2.3 软件架构与任务划分

基于FreeRTOS,我将系统功能分解为以下几个独立的任务,每个任务负责一个明确的职能:

  • 看门狗任务:这是一个系统健康守护者。它定期检查其他所有关键任务是否在正常运行(通过接收任务发送的“心跳”信号)。如果某个任务卡死无法喂狗,看门狗任务会触发系统复位,防止设备“死机”。
  • HMI任务:人机交互的核心。它管理OLED屏幕的显示、处理两个按键的输入事件、接收来自ToF任务的距离数据。它实现了基于“屏幕”和“控件”的图形界面,可以在不同设置页面间切换,并处理数值的编辑过程。
  • 按键任务:独立扫描两个物理按键,消抖后,将按键事件(如按下、释放、长按)通过消息队列发送给HMI任务,实现响应式交互。
  • ToF任务:负责初始化并周期性地读取VL53L0X等ToF传感器的距离值。在非编辑模式下,这个数据可能用于判断用户是否靠近;在编辑模式下,精确的距离值被映射为进度条,用于无接触调节数值。
  • 温度传感器任务:周期性地读取环境温度传感器(如ST的HTS221)的数据,并进行简单的软件滤波(例如移动平均滤波)以消除抖动,然后将稳定的温度值发送给主控任务。
  • 红外任务:这是与具体空调型号强相关的部分。它接收来自主控任务的指令(温度、模式等),按照逆向工程得到的Atlantic空调协议,组装数据帧、计算校验和,并调制生成对应的38kHz载波红外信号,通过GPIO驱动红外发射管发出。
  • BLE任务:负责初始化和管理蓝牙协议栈,广播设备信息,创建GATT服务(如环境温度读取服务),并处理来自手机App的连接、数据读写请求。目前该任务仅实现了温度上报,完整的投票指令接收功能是下一步开发重点。
  • 主控任务:系统的“大脑”。它接收来自HMI任务的用户设定值或来自BLE任务的投票计算结果,结合温度任务提供的环境温度,进行逻辑判断(例如,决定制冷/制热模式),然后将最终的执行指令分发给红外任务。同时,它也将系统状态反馈给HMI任务进行显示。

这种模块化、任务化的设计,使得代码耦合度低,调试方便,未来增加新功能(如定时开关、场景模式)也只需新增或修改特定任务即可。

3. 关键实现细节与实操要点

3.1 红外协议逆向工程全记录

这是项目中最具挑战性也最有乐趣的部分。要让STM32板子控制空调,首先得学会说它的“语言”——即红外遥控协议。

3.1.1 信号捕获:选择合适的“窃听”工具

有两种主流方法捕获红外信号:

  1. 使用一体化红外接收头模块:这种模块三根线(VCC, GND, OUT),价格低廉,使用方便。它内部已经完成了对38kHz载波的解调,输出的是纯净的数字信号(高低电平),可以直接接入单片机的IO口或逻辑分析仪。缺点是你看不到原始的载波波形,无法直接得知载波频率。
  2. 直接探测红外LED引脚:拆开原装遥控器,用逻辑分析仪的探头直接夹在红外发射LED的两个引脚上。这样能看到包含载波的原始信号。你需要自己从调制信号中解析出数据位。如果条件允许,可以同时接上接收头模块和LED,对比着看,理解会更深刻。

注意:如果使用两个独立的设备(如逻辑分析仪和遥控器主板),务必用导线将它们的“地”连接在一起,确保共地,否则信号可能会乱跳或无法捕获。

我手头有一台DSLogic逻辑分析仪,通道数多,适合这种多线测量。但对于这个任务,一个8通道、24MHz采样率的廉价逻辑分析仪完全够用,配合开源的PulseView(Sigrok)软件即可。

3.1.2 捕获设置与技巧

在逻辑分析仪软件中,关键要平衡采样率缓冲区大小。红外载波通常在38kHz左右,根据奈奎斯特定理,采样率至少需要76kHz。为了留有余量并看清细节,我设置为1MHz或更高。缓冲区要能容纳一次完整的按键发射序列。Atlantic遥控器在按键后约有1-2秒的延迟才发射,所以我设置了10秒的捕获时间,确保能抓到完整信号。

3.1.3 协议解码:从波形到字节

捕获到一堆高低电平的脉冲后,手动解码是噩梦。幸运的是,PulseView和DSView这类软件支持用Python编写解码器。我观察波形,发现它很像索尼的SIRC协议,但时序不同。于是我在SIRC解码器的基础上修改了时序参数,成功将脉冲流解码成了看得懂的字节数据。

Atlantic协议的一帧数据以一个长的起始信号开始(载波3.8ms, 停顿1.5ms)。每个数据位由一段固定长度的载波(400us)和一段不同长度的停顿来表示:逻辑“0”停顿400us,逻辑“1”停顿1ms。这就是典型的“脉冲距离编码”。

3.1.4 数据映射与校验和破解

解码出字节后,下一步是找出每个字节对应的功能。方法是控制变量法:固定其他所有设置,只改变一个参数(比如温度),然后对比多次捕获的数据帧。

例如,我分别捕获了设定温度为21°C、22°C、23°C时的信号。对比解码后的数据,发现某一字节的高4位(bit7-bit4)依次是5, 6, 7。这显然与温度相关。进一步测试发现,当设定为16°C时,这个值是0。于是规律就出来了:发送值 = 设定温度 - 16。这样,21°C就对应发送值5。

更棘手的是校验和。数据帧的末尾通常有1-2个字节用于校验,确保传输无误。我尝试使用reveng这类CRC逆向工具,但没能匹配出标准算法。于是转而进行人工分析:观察当数据部分微小变化时,校验字节的变化规律。我发现Atlantic协议使用了两个独立的4位校验和,分别保护数据帧的高4位部分和低4位部分。它们的初始值都是0b1111(15),算法是递减式:校验和 = 15 - (所有被保护数据位的和)。如果结果小于0,则循环回15继续减。通过大量数据验证后,这个算法被确认。

3.2 用户界面设计与交互逻辑

HMI(人机界面)是用户与设备直接交互的窗口,设计需要直观且高效。

3.2.1 屏幕组件化设计

我没有使用复杂的GUI库,而是自己实现了一个轻量级的控件系统。屏幕由几个基本部分组成:

  • 标题栏:顶部区域,显示当前屏幕的名称(如“温度设置”)。
  • 状态栏:底部区域,固定显示关键信息:当前控制模式(MANUAL/BLE)、空调运行状态(OFF/COOL/HEAT)、以及当前温度设定点。这让用户在任何界面下都能一眼看到核心状态。
  • 内容区:中间大部分区域,根据不同的设置页面动态加载不同的控件,如数值显示框、进度条、选项列表等。

3.2.2 创新的交互方式:按键+手势

设备只有两个按键(B1, B2)和一个ToF距离传感器,却要完成复杂的设置。

  • B1(左键):在浏览模式下,用于在多个设置屏幕间循环切换;在编辑模式下,则用于取消当前编辑并跳转到下一个屏幕。
  • B2(右键):在浏览模式下,按下即进入当前焦点控件的编辑状态(如果该控件允许编辑),被编辑的控件会有边框高亮;在编辑模式下,按下则确认当前修改的值。
  • ToF传感器:这是交互的亮点。进入编辑模式后(例如编辑温度),屏幕下方会出现一个水平进度条。用户无需触摸设备,只需将手在传感器上方前后移动,改变手与传感器的距离,进度条就会随之变化,从而连续、无级地调整数值。这比反复按按键“加一减一”要快捷和精准得多。

3.3 蓝牙多设备连接管理

STM32WB的蓝牙协议栈已经处理了底层的连接管理。我们的任务是在应用层实现多设备连接和简单的在场判断。

  1. 连接管理:协议栈支持多个并发连接(通常8个)。我们需要维护一个连接句柄列表。当手机App发起连接时,BLE任务会收到回调,将新设备的句柄加入列表;断开时则移除。
  2. 在场判断:这是一个简化设计。我们通过读取每个已连接设备的接收信号强度指示来粗略估计距离。在App端,可以设计为:当用户打开App并进入投票界面时,才认为该用户“在场”。或者,在设备端设置一个RSSI阈值,只有当信号强度高于此阈值(意味着设备在较近距离内),才将该用户的投票计入有效票。这种方法不精确,但对于办公室场景(设备固定,人员活动范围有限)是一个低成本、可用的方案。
  3. 数据同步:设备端需要定义一个GATT特征(Characteristic)用于接收投票数据。手机App以“写”的方式将用户选择的温度值发送过来。同时,设备可以定义另一个特征,以“通知”的方式,主动向所有已连接的手机推送当前的环境温度、投票结果或系统模式,保持多端信息同步。

4. 开发环境搭建与代码结构导读

4.1 开发工具链准备

项目基于STM32CubeIDE V1.13.2构建。这是一款ST官方推出的免费集成开发环境,基于Eclipse,集成了CubeMX配置工具、编译器、调试器,对STM32开发非常友好。

  1. 安装STM32CubeIDE:从ST官网下载并安装。
  2. 导入项目:在IDE中选择“File -> Import -> Existing Projects into Workspace”,然后定位到项目根目录下的Sources/MCU/BLE_Freertos文件夹。这个文件夹结构是CubeIDE的标准项目结构。
  3. 解决可能的编译问题:项目说明中特别提到,由于手动修改了spi.ci2c.c以移除自动生成的初始化函数(MX_SPI1_InitMX_I2C3_Init),如果重新用CubeMX生成代码,需要注意不要覆盖这些修改,或者确保HAL库文件被正确链接。

4.2 项目代码目录结构解析

清晰的目录结构是大型项目可维护性的基础。本项目结构如下:

Elektor_Demo_Clim/ ├── Documentation/ # 项目文档 │ ├── Datasheets/ # STM32等芯片的数据手册 │ ├── Internal_Documentation/ # 内部编码规范与模板 │ ├── Reverse_atlantic_protocol/# 红外协议逆向工程的所有捕获数据和解析笔记 │ └── Technical_Documentation/ # 软件架构设计文档(含PlantUML图) ├── Sources/ │ ├── MCU/ │ │ └── BLE_Freertos/ # 主工程目录(STM32CubeIDE项目) │ │ ├── Core/ │ │ │ ├── Inc/ # 头文件 │ │ │ ├── Src/ # 主循环、硬件初始化等 │ │ │ └── task/ # **核心**:所有FreeRTOS任务源码 │ │ │ ├── tsk_hmi.c # HMI任务 │ │ │ ├── tsk_ir_atl.c # Atlantic红外协议任务 │ │ │ ├── tsk_ble.c # BLE任务 │ │ │ └── ... # 其他任务 │ │ ├── 3rdparty/ # 第三方库:单色OLED图形库和控件库 │ │ └── STM32_WPAN/ # STM32无线协议栈中间件 │ └── tools/ # 实用工具,如为DSView写的红外解码器脚本 └── Bin/ └── MCU/ # 存放编译好的.hex固件文件

重点关注的目录

  • Core/task/:这里是所有业务逻辑所在。每个.c文件对应一个FreeRTOS任务,职责单一,通过消息队列和事件标志组进行通信。
  • 3rdparty/:里面的图形库是针对小内存、单色屏优化的,比通用的LVGL或emWin更轻量,适合本项目。
  • Documentation/Reverse_atlantic_protocol/:如果你要适配其他品牌空调,这里的逆向工程方法论和原始数据将是宝贵参考。

4.3 系统初始化与任务启动流程

理解启动流程对调试至关重要。在main.cmain()函数中,顺序大致如下:

  1. HAL初始化HAL_Init()初始化硬件抽象层。
  2. 系统时钟配置SystemClock_Config(),确保CPU和外围总线运行在正确频率。
  3. 外设初始化:通过CubeMX生成的代码初始化GPIO、I2C(连接OLED和温度传感器)、SPI(可能连接ToF)、定时器(用于红外载波生成)等。
  4. FreeRTOS初始化:创建任务所需的内存管理、队列等。
  5. 创建所有应用任务:按优先级从低到高(或按依赖关系)创建看门狗、HMI、按键、温度、红外、BLE等任务。优先级需要仔细设计,例如处理用户输入的HMI任务和红外发送任务需要较高响应优先级。
  6. 启动调度器osKernelStart(),之后FreeRTOS接管CPU,开始在多任务间调度。

5. 调试心得与常见问题排查

在实际开发中,我遇到了不少坑,这里分享一些典型的排查思路和解决方法。

5.1 红外发射无反应或空调不识别

这是最可能遇到的问题。

  • 检查硬件连接:首先确认红外发射管的正负极没有接反,限流电阻值合适(通常100-330欧姆)。用手机摄像头对准发射管,按下按键时能看到闪烁的紫光,说明硬件通路基本正常。
  • 验证载波频率:空调遥控常用38kHz,但可能是36kHz, 40kHz等。用逻辑分析仪或示波器测量驱动红外管的PWM输出频率。STM32的定时器PWM输出精度很高,重点检查定时器分频和自动重载值的计算是否正确。
  • 核对协议细节
    • 起始码:长度是否精确?我遇到的Atlantic协议起始码是3.8ms高+1.5ms低,误差太大空调可能不认。
    • 逻辑电平定义:“0”和“1”的高低电平时间是否与解码时的一致?常见错误是弄反了。
    • 重复码:长按按键时,原装遥控器是连续发送完整帧还是只发送一个简短的重复码?需要模拟这个行为。
    • 校验和:这是最大的坑。务必用多个不同的数据样本验证你的校验和算法。一个字节算错,整个帧都会被空调丢弃。可以先将捕获到的原装遥控器数据帧(含校验和)硬编码到程序里发送,如果空调能响应,再切换到动态计算校验和。

5.2 FreeRTOS任务卡死或系统重启

  • 堆栈溢出:这是多任务系统最常见的崩溃原因。在STM32CubeIDE中,可以在FreeRTOSConfig.h中开启configCHECK_FOR_STACK_OVERFLOW选项。当检测到溢出时,会触发钩子函数,方便定位是哪个任务出了问题。适当增加该任务的堆栈大小。
  • 看门狗复位:如果开启了硬件看门狗(IWDG),确保每个需要监控的任务都能定期“喂狗”。在我的设计中,由独立的看门狗任务统一管理。检查是否所有任务都正确地向看门狗任务发送了存活信号。
  • 资源竞争:如果多个任务(如HMI和BLE)同时访问同一个硬件外设(如I2C),必须使用互斥锁(Mutex)。我修改了HAL I2C驱动,在其内部加入了RTOS互斥锁,确保线程安全。
  • 优先级反转:如果高优先级任务等待一个低优先级任务占有的资源(如信号量),而该低优先级任务又被中优先级任务抢占,就会导致高优先级任务无限期等待。合理设置优先级,或使用优先级继承互斥锁。

5.3 BLE连接不稳定或手机搜不到设备

  • 广播参数:检查广播间隔(Advertising Interval)。间隔太短(如20ms)耗电,间隔太长(如几秒)则手机扫描时可能错过。100ms到1s是常用范围。确保广播数据包中包含了完整的设备名称和需要的服务UUID。
  • 连接参数:连接建立后,主设备(手机)和从设备(STM32)会协商连接参数,包括连接间隔、从设备延迟、监督超时。STM32WB作为从设备,可以提出连接参数更新请求。如果连接间隔设得太长,数据传输会不实时;太短则增加功耗。需要根据应用需求在功耗和响应速度间权衡。
  • 内存不足:每个BLE连接都会消耗RAM。如果计划连接8个设备,需要在FreeRTOSConfig.h中为BLE任务分配足够的堆栈和堆内存。同时,检查协议栈配置中定义的最大连接数是否足够。
  • 天线与干扰:确保开发板上的蓝牙天线区域没有被金属遮挡。2.4GHz频段容易受到Wi-Fi、微波炉等干扰,尽量远离强干扰源。

5.4 ToF传感器读数不准或跳动大

  • 电源噪声:ToF传感器对电源纹波敏感。确保其供电引脚有足够的去耦电容(通常手册会推荐一个10uF和一个0.1uF的电容并联)。
  • I2C上拉电阻:STM32开发板上的I2C总线通常已接上拉电阻(如4.7kΩ)。如果自己布线,必须接上拉电阻,阻值根据总线速度和线长选择,通常在2.2kΩ到10kΩ之间。
  • 软件滤波:传感器原始数据难免有跳动。在任务中不要直接使用单次读数,而应采用软件滤波。最简单有效的是移动平均滤波:维护一个包含最近N次读数的小数组,每次取平均值输出。N的大小根据采样周期和响应速度要求调整。
  • 环境光干扰:强光(特别是阳光)可能会干扰ToF传感器的红外光测量。在传感器窗口上方加一个短的遮光罩(如一小段黑色热缩管)可以有效改善。

5.5 功耗优化考虑

虽然目前是插电演示,但若未来改为电池供电,功耗至关重要。

  • 动态频率调整:在系统空闲或仅运行后台任务(如看门狗)时,可以通过HAL_RCC_ClockConfig降低系统主频。
  • 外设时钟门控:不使用的串口、SPI、ADC等外设,及时关闭其时钟(__HAL_RCC_XXX_CLK_DISABLE())。
  • 低功耗模式:在BLE模式下,当没有手机连接且处于非投票时段,可以让STM32WB进入低功耗的停止模式(Stop Mode),此时BLE协议栈由协处理核维持,主核暂停,功耗可降至微安级。有连接事件或按键中断时再唤醒。
  • 屏幕与传感器管理:OLED屏幕在无操作一段时间后可以关闭。ToF传感器可以设置为间歇性测量,而不是持续测量。

6. 项目总结与未来演进方向

经过一段时间的开发,这个民主空调遥控器的核心骨架已经搭建完成:红外协议成功逆向并实现、多任务系统稳定运行、基础HMI和BLE框架就绪。它已经可以作为一个功能强大的万能红外遥控器来使用。

我个人在实际操作中的体会是,嵌入式物联网项目的难点往往不在于单一技术的深度,而在于多种技术的集成与稳定协作。FreeRTOS是多任务管理的利器,但必须对任务优先级、同步通信有清晰的设计。无线通信(BLE)的调试比有线复杂,需要更多的耐心和工具(如手机端BLE调试App、空中抓包器)。而逆向工程则像侦探破案,需要细致的观察、合理的假设和反复的验证。

最后再分享一个小技巧:在开发类似项目时,建立一个清晰的“调试输出”系统非常重要。我利用STM32的串口(UART),定义了几个不同等级的调试信息宏(如DEBUG_ERROR, DEBUG_INFO, DEBUG_RAW),可以方便地在不同开发阶段打开或关闭特定模块的日志,这对定位跨任务、跨模块的问题有奇效。

这个项目还有很大的演进空间,也是我下一步计划:

  1. 手机App开发:这是实现“民主”功能的关键一环。计划使用Flutter或React Native开发一个跨平台App,界面简洁,主要功能就是显示当前环境温度、进行温度投票、查看实时投票结果。
  2. 智能在场判定:目前的RSSI方案比较粗糙。可以结合手机App,当用户打开App并进入办公室地理围栏(Geofencing)时,自动标记为“在场”;或者利用BLE的定向功能(如果手机和硬件支持)进行更精准的接近判断。
  3. 数据持久化与场景化:将用户偏好、定时任务(如工作日9点自动开启空调)保存到STM32的Flash或外置EEPROM中,实现断电记忆。甚至可以创建“会议模式”、“午休模式”等场景,一键切换。
  4. 云端同步与远程管理:增加Wi-Fi模块(或利用STM32WB的某些型号),将数据同步到云端。管理员可以远程查看办公室温度状况、能耗统计,甚至在特殊情况下进行远程干预。
  5. 支持更多空调品牌:将红外协议编码部分设计成插件化结构。通过配置文件或学习功能,让这个设备能够控制市面上绝大多数品牌的空调,真正成为一个通用的“智能红外网关”。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询