这次我们来看一个非常实用的物联网控制项目:STM32单片机通过蓝牙APP控制2路继电器。这个项目不是停留在概念,而是能让你快速搭建一套可用的远程开关控制系统。核心就是利用一块常见的STM32开发板,搭配一个蓝牙模块(如HC-05/06),再编写一个简单的手机APP,实现对两个继电器(可以控制灯、风扇、电机等设备)的无线控制。
对于嵌入式开发者、电子爱好者或物联网初学者来说,这个项目的价值在于门槛低、成本可控、功能完整。你不需要复杂的网络协议栈,也不需要云服务器,就能实现一个典型的“手机控制硬件”场景。本文将带你从零开始,理清硬件选型、电路连接、STM32程序编写、蓝牙通信协议设计以及手机APP开发(提供简易方案)的全过程。读完本文,你将能够独立完成一套可工作的蓝牙继电器控制系统,并理解其扩展为更多路数或集成到更复杂项目中的方法。
1. 核心能力速览
在深入细节之前,我们先通过一个表格快速了解这个项目的关键信息,帮助你判断是否适合自己。
| 能力项 | 说明 |
|---|---|
| 核心功能 | 通过手机蓝牙APP,无线控制STM32连接的2路继电器开关状态。 |
| 主控芯片 | STM32系列单片机(如STM32F103C8T6,即常见的“蓝/黑”板)。 |
| 蓝牙模块 | 经典蓝牙串口模块(如HC-05,HC-06),成本低廉,连接简单。 |
| 继电器模块 | 2路继电器模块,通常支持5V或3.3V驱动,可控制220V/10A以内的交流负载。 |
| 开发环境 | Keil MDK-ARM / STM32CubeIDE, 配合STM32标准库或HAL库。 |
| APP开发 | 可使用Android Studio(Java/Kotlin)或更简单的MIT App Inventor等图形化工具。 |
| 通信协议 | 自定义简单的串口字符指令,例如A开继电器1,a关继电器1。 |
| 供电要求 | 5V USB供电或7-12V直流电源输入即可,整体功耗低。 |
| 适合场景 | 智能家居原型(灯、窗帘)、实验室设备远程控制、物联网教学演示、小型自动化项目。 |
| 扩展潜力 | 可轻松增加继电器路数、添加传感器反馈、改为Wi-Fi或4G控制。 |
2. 适用场景与使用边界
这个项目虽然简单,但能解决不少实际问题。它最适合那些需要低成本、快速实现、无线控制开关量设备的场景。
典型适用场景包括:
- 智能家居原型验证:控制房间的灯光、插座、小功率电器,验证想法可行性。
- 教学与实验:作为单片机、嵌入式系统、物联网通信课程的绝佳实践案例,涵盖GPIO控制、串口通信、外设驱动等多个知识点。
- 小型自动化控制:控制实验室的加热装置、搅拌器、通风设备,或者模型沙盘中的灯光、水泵。
- 设备状态远程指示:虽然本项目主要控制,但稍加修改(如读取继电器状态或接入传感器),也能实现状态上报。
使用边界与安全警告:
- 电气安全第一:继电器模块用于控制强电(220V交流)时,必须确保接线规范、绝缘良好,操作时断电进行。非专业人士请勿直接操作强电,建议先使用低压直流负载(如LED、小电机)进行测试。
- 控制功率限制:注意继电器模块的触点容量(如10A 250VAC),严禁超负荷使用,以免引发火灾。
- 通信距离限制:经典蓝牙(如HC-05)的有效通信距离通常在10米以内(无遮挡),不适合远距离控制。
- 非商业级产品:本项目作为学习和原型开发,在稳定性、安全性、抗干扰能力上无法与成熟商业产品相比,请勿直接用于对可靠性要求极高的生产环境或人身安全相关的控制。
- 隐私与授权:自制的蓝牙APP仅用于控制自有设备。任何涉及控制他人设备或公共设备的行为,必须获得明确授权,遵守相关法律法规。
3. 环境准备与前置条件
动手之前,请确保你已准备好以下软硬件环境。清单中的大部分物品都能在电商平台轻松购得。
硬件清单:
- STM32最小系统板:推荐使用STM32F103C8T6核心板(约10-20元),它资源丰富,社区支持好。
- 蓝牙串口模块:HC-05(主从一体)或HC-06(从机模式)。HC-05更常用,建议购买已焊好插针的模块。
- 2路继电器模块:选择支持低电平触发的5V继电器模块,通常带有光耦隔离和指示灯。
- USB转TTL串口模块:用于STM32程序下载和调试,如CH340、CP2102模块。
- 杜邦线:若干(公对公、公对母),用于连接。
- 负载:用于测试,如LED灯和220Ω电阻(低压测试),或一个小台灯(强电测试,务必谨慎!)。
- 电源:5V/2A的USB电源或手机充电器,用于给整个系统供电。
软件清单:
- 开发IDE:
- Keil MDK-ARM:传统且强大,需安装STM32器件支持包。
- STM32CubeIDE:ST官方免费IDE,集成CubeMX图形化配置,对新手友好。本文示例将侧重HAL库,可在CubeIDE中完成。
- STM32CubeMX:如果使用Keil,也需要用它来生成初始化代码。它是ST的图形化配置工具。
- 串口调试助手:如SSCOM、XCOM、Putty等,用于测试蓝牙模块和STM32的串口通信。
- 手机APP开发工具(二选一):
- MIT App Inventor:在线图形化编程,无需代码基础,快速生成安卓APP。最适合初学者快速验证。
- Android Studio:开发功能更完善、界面更专业的原生APP。
4. 硬件连接与电路设计
正确的硬件连接是成功的第一步。下图展示了系统的核心连接关系:
[手机蓝牙APP] <--(蓝牙无线)--> [HC-05蓝牙模块] <--(串口TX/RX)--> [STM32单片机] <--(GPIO)--> [继电器模块] <--(强电端)--> [用电设备]具体接线步骤(以STM32F103C8T6和HC-05为例):
- 供电统一:将STM32核心板、继电器模块、HC-05模块的
VCC(5V)和GND分别连接到一起,确保共地。可以使用面包板或焊接。 - STM32与HC-05连接:
- HC-05的
TXD接 STM32的PA3(USART2_RX)。 - HC-05的
RXD接 STM32的PA2(USART2_TX)。 - 注意:蓝牙模块的TXD要接MCU的RXD,反之亦然。
- HC-05的
- STM32与继电器模块连接:
- 继电器模块的
IN1接 STM32的PB0(用于控制继电器1)。 - 继电器模块的
IN2接 STM32的PB1(用于控制继电器2)。 - 继电器模块的
VCC接5V,GND接GND。 - 注意:大多数继电器模块低电平触发,即STM32引脚输出低电平时继电器吸合,高电平时断开。程序需对应。
- 继电器模块的
- 继电器负载端连接(低压测试示例):
- 将继电器的
COM(公共端)接电源正极(如5V)。 - 将继电器的
NO(常开端)接一个LED的正极。 - LED的负极接一个220Ω限流电阻后,连接到电源负极(GND)。
- 当继电器吸合时,
COM与NO接通,电路闭合,LED点亮。
- 将继电器的
关键点说明:
- 电平匹配:HC-05模块是3.3V TTL电平,但通常兼容5V供电和通信。STM32F103的IO口也是3.3V,两者直接连接无问题。
- 启动模式:STM32需要设置BOOT0和BOOT1引脚为正常启动模式(通常BOOT0=0,BOOT1=x),才能运行用户程序。
- 下载接口:使用USB转TTL模块下载程序时,连接STM32的
PA9(TX)和PA10(RX),注意交叉连接,并确保在下载时正确操作复位和BOOT0引脚。
5. STM32程序设计详解
我们将使用STM32CubeMX生成基础工程,然后在STM32CubeIDE中编写业务逻辑。程序的核心是:初始化串口和GPIO,在串口中断中接收APP发来的指令,并控制对应的GPIO输出高低电平。
5.1 使用STM32CubeMX配置工程
- 新建项目:打开CubeMX,选择MCU型号
STM32F103C8Tx。 - 配置时钟:在
RCC中,将HSE设置为Crystal/Ceramic Resonator。 - 配置SYS:将
Debug设为Serial Wire(方便ST-Link调试)。 - 配置USART2:
- 模式选择
Asynchronous(异步)。 - 参数默认(波特率9600, 8位数据,无校验,1停止位)。可与蓝牙模块匹配,后期可改为115200等更高波特率。
- 开启中断:在
NVIC Settings中勾选USART2 global interrupt。
- 模式选择
- 配置GPIO:
PB0和PB1设置为GPIO_Output,初始输出电平设为高电平(因为继电器模块低电平触发,初始状态应为断开)。- 可以重命名为
RELAY1_CTRL和RELAY2_CTRL以便识别。
- 配置时钟树:在
Clock Configuration标签页,将系统时钟(HCLK)设置为最高72MHz。 - 生成代码:在
Project Manager中设置项目名称、路径、选择IDE为STM32CubeIDE,然后点击GENERATE CODE。
5.2 编写核心控制代码
在生成的工程中,我们主要修改main.c和stm32f1xx_it.c(中断服务函数)。
在main.c的/* USER CODE BEGIN PV */区域定义变量:
/* Private variables ---------------------------------------------------------*/ uint8_t uart_rx_buffer = 0; // 用于存储接收到的单个字节 uint8_t relay1_state = 0; // 继电器1状态,0=关,1=开 uint8_t relay2_state = 0; // 继电器2状态在main.c的/* USER CODE BEGIN 2 */区域,启动串口接收中断:
/* USER CODE BEGIN 2 */ // 启动串口2接收中断(以字节为单位) HAL_UART_Receive_IT(&huart2, &uart_rx_buffer, 1); /* USER CODE END 2 */在stm32f1xx_it.c文件中,找到串口2中断服务函数USART2_IRQHandler,并在其中调用HAL库的中断处理函数。我们主要修改其回调函数。
在main.c文件末尾的/* USER CODE BEGIN 4 */区域,重写串口接收完成回调函数HAL_UART_RxCpltCallback:
/* USER CODE BEGIN 4 */ // 串口接收中断回调函数 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART2) // 判断是串口2 { // 根据接收到的字符执行相应动作 switch(uart_rx_buffer) { case 'A': // 打开继电器1 HAL_GPIO_WritePin(RELAY1_CTRL_GPIO_Port, RELAY1_CTRL_Pin, GPIO_PIN_RESET); // 输出低电平 relay1_state = 1; break; case 'a': // 关闭继电器1 HAL_GPIO_WritePin(RELAY1_CTRL_GPIO_Port, RELAY1_CTRL_Pin, GPIO_PIN_SET); // 输出高电平 relay1_state = 0; break; case 'B': // 打开继电器2 HAL_GPIO_WritePin(RELAY2_CTRL_GPIO_Port, RELAY2_CTRL_Pin, GPIO_PIN_RESET); relay2_state = 1; break; case 'b': // 关闭继电器2 HAL_GPIO_WritePin(RELAY2_CTRL_GPIO_Port, RELAY2_CTRL_Pin, GPIO_PIN_SET); relay2_state = 0; break; case 'S': // 查询状态 { char status_msg[32]; sprintf(status_msg, "R1:%d,R2:%d\n", relay1_state, relay2_state); HAL_UART_Transmit(&huart2, (uint8_t*)status_msg, strlen(status_msg), 100); } break; default: // 收到未知指令,可忽略或返回错误 break; } // 重新开启接收中断,等待下一个字节 HAL_UART_Receive_IT(&huart2, &uart_rx_buffer, 1); } } /* USER CODE END 4 */代码逻辑解析:
HAL_UART_Receive_IT启动了一次性接收1字节的中断。- 当蓝牙模块通过串口发送一个字符到STM32时,触发中断,进入回调函数。
- 在回调函数中,根据收到的字符(
uart_rx_buffer)执行对应的开关动作。 - 控制继电器实质是控制GPIO引脚输出高低电平。
GPIO_PIN_RESET为低电平(继电器吸合),GPIO_PIN_SET为高电平(继电器断开)。 - 指令
S用于查询状态,STM32会将两个继电器的当前状态格式化成字符串(如R1:1,R2:0\n)发回给手机APP,实现简单的状态反馈。 - 每次处理完指令后,必须再次调用
HAL_UART_Receive_IT重新使能接收中断,否则只会接收一次。
5.3 编译与下载
- 在STM32CubeIDE中,点击锤子图标编译项目,确保0错误,0警告。
- 将USB转TTL模块的
TX、RX、GND分别连接到STM32的PA10(RX)、PA9(TX)、GND。 - 设置STM32为下载模式(BOOT0=1, BOOT1=0),上电复位。
- 在IDE中选择正确的下载器配置(如ST-Link或串口),点击下载按钮。
- 下载完成后,将STM32设置为运行模式(BOOT0=0),复位重启。
6. 蓝牙模块配置与测试
新的HC-05模块通常需要配置其角色和波特率。我们可以先用USB转TTL模块连接电脑进行配置。
配置步骤:
- 连接:将HC-05的
VCC(5V)、GND、TXD、RXD分别接USB转TTL模块的5V、GND、RXD、TXD。注意交叉。 - 进入AT模式:按住HC-05模块上的小按钮(或使
KEY/EN引脚接高电平),再给模块上电。此时模块指示灯慢闪(约2秒一次),表示进入AT命令模式。 - 串口助手设置:打开串口调试助手,选择正确的COM口,波特率设置为
38400(HC-05 AT模式默认波特率),数据位8,停止位1,无校验。 - 发送AT指令:
- 发送
AT,应返回OK。 - 发送
AT+NAME=MyRelay,设置蓝牙名称为MyRelay。 - 发送
AT+PSWD=1234,设置配对密码为1234。 - 发送
AT+UART=9600,0,0,设置通信波特率为9600(与STM32程序匹配)。 - 发送
AT+ROLE=0,设置模块为从机模式(HC-06可跳过,它只能是从机)。
- 发送
- 退出AT模式:断开电源,重新上电(不按按钮),指示灯快闪,进入可配对状态。
与STM32联调测试:
- 将配置好的HC-05按第4章方式连接到STM32。
- 给整个系统供电。
- 打开手机蓝牙设置,搜索并配对名为
MyRelay的设备,密码1234。 - 在手机上安装一个通用的“蓝牙串口”APP(如
Serial Bluetooth Terminal)。 - 在APP中连接
MyRelay。 - 在APP的发送框中输入指令
A、a、B、b,分别观察两个继电器是否相应吸合/断开,同时观察LED的亮灭。输入S,查看是否返回状态信息。
至此,硬件和STM32底层驱动部分已全部完成。如果测试成功,说明你的蓝牙通信链路和继电器控制基础功能已经正常。
7. 手机APP开发方案(MIT App Inventor)
对于大多数只想快速实现功能的开发者,MIT App Inventor是最佳选择。它是一个图形化编程的在线平台,无需编写复杂代码。
核心组件与逻辑:
- 界面设计:添加两个按钮用于开关继电器1,两个按钮用于开关继电器2,一个按钮用于查询状态,一个
标签(Label)用于显示状态,一个列表选择框(ListPicker)用于选择蓝牙设备,以及必要的蓝牙客户端(BluetoothClient)组件。 - 逻辑设计:
- 点击“连接设备”按钮,调用
ListPicker显示已配对的蓝牙设备列表,选择后通过BluetoothClient连接。 - 连接成功后,点击“开继电器1”按钮,通过
BluetoothClient发送字符A。 - 点击“关继电器1”按钮,发送字符
a。继电器2同理。 - 点击“查询状态”按钮,发送字符
S。 - 配置
BluetoothClient的当收到数据时事件,将接收到的字节解码为字符串,并更新到显示状态的标签中。
- 点击“连接设备”按钮,调用
关键积木块示例(文字描述):
- 连接设备:当
ListPicker选择后 -> 调用BluetoothClient.Connect连接选中的地址。 - 发送指令:当按钮被点击 -> 调用
BluetoothClient.SendText发送对应的单字符(如A)。 - 接收数据:当
BluetoothClient收到数据 -> 用BluetoothClient.ReceiveText获取文本 -> 设置标签的显示文本为接收到的内容。
导出与安装:在App Inventor中完成设计后,可以打包生成APK文件,通过二维码下载安装到安卓手机。这样,一个专属的控制APP就诞生了。
8. 功能测试与效果验证流程
完成所有部分后,需要进行系统化测试,确保功能稳定可靠。
测试1:基础指令控制测试
- 操作:手机APP依次发送
A,a,B,b。 - 预期:继电器1吸合->断开,继电器2吸合->断开。可听到清晰的“咔哒”声,指示灯状态变化,负载(LED)同步亮灭。
- 失败排查:
- 继电器无反应:检查STM32 GPIO引脚输出电平(用万用表测量),检查继电器模块供电和输入信号线连接。
- 手机APP显示发送成功但无效果:用串口调试助手监听STM32与HC-05之间的TX/RX线,看指令是否被正确传输。检查STM32程序中的指令字符判断逻辑。
测试2:状态查询与反馈测试
- 操作:手动改变继电器状态(或通过指令),然后发送
S。 - 预期:手机APP收到格式为
R1:1,R2:0的字符串,并正确显示。 - 失败排查:
- 无返回数据:检查STM32的串口发送代码,确认
HAL_UART_Transmit函数被调用且参数正确。 - 返回乱码:检查手机APP和STM32的串口波特率、数据格式是否一致。
- 无返回数据:检查STM32的串口发送代码,确认
测试3:连续操作与稳定性测试
- 操作:快速、随机地点击APP上的各个开关按钮,持续1-2分钟。
- 预期:每次操作都有正确响应,无继电器误动作、程序死机或蓝牙断开连接。
- 失败排查:
- 蓝牙断开:检查供电是否稳定,距离是否过远或有严重遮挡。
- 程序死机:检查STM32的中断处理函数是否过于冗长或存在逻辑错误,导致无法及时响应新指令。
测试4:负载带载能力测试(务必注意安全!)
- 操作:在继电器输出端接入额定范围内的实际负载(如小功率台灯)。
- 预期:能正常控制负载的通断,继电器触点无打火、过热现象。
- 失败排查:如控制大功率负载失败,检查电源容量是否足够,继电器规格是否匹配,强电接线是否牢固。
9. 资源占用与性能观察
对于STM32F103C8T6这款芯片,本项目的资源占用极低,留有大量空间用于功能扩展。
- Flash占用:上述核心程序(包含HAL库、串口中断、GPIO控制)编译后通常不超过20KB,远小于芯片64KB的Flash容量。
- RAM占用:全局变量很少,主要消耗在栈和中断,通常不超过2-3KB,远小于20KB的RAM。
- CPU负载:系统绝大部分时间处于空闲或低功耗状态,仅在收到蓝牙指令时触发短暂的中断处理,CPU负载可忽略不计。
- 功耗:核心板、蓝牙模块、继电器模块(线圈未吸合时)的静态总电流约50-100mA。继电器吸合时,线圈电流会增加(约70mA/路)。整体可由USB口或5V/1A适配器轻松驱动。
性能优化方向:
- 提高波特率:将串口波特率从9600提升到115200或更高,可以加快指令传输速度,减少延迟感。
- 指令协议优化:当前单字符指令简单,但易误操作。可改为带校验的帧结构,如
[帧头][指令码][数据][校验和][帧尾],提高可靠性。 - 增加看门狗:在
main函数的循环中喂狗,防止程序跑飞导致设备失控。 - 低功耗模式:如果由电池供电,可以在无操作时让STM32进入睡眠模式,蓝牙模块唤醒后再激活MCU。
10. 常见问题与排查方法
在开发过程中,你可能会遇到以下问题。下表提供了系统的排查思路。
| 问题现象 | 可能原因 | 排查步骤 | 解决方案 |
|---|---|---|---|
| 蓝牙APP搜不到设备 | 1. 蓝牙模块未供电或损坏。 2. 模块未进入配对状态(指示灯非快闪)。 3. 手机蓝牙未打开或距离过远。 | 1. 检查模块VCC、GND是否接好,电压是否正常(5V)。 2. 观察模块指示灯状态。 3. 重启手机蓝牙,靠近模块。 | 1. 确保供电正确。 2. 重新上电,确认模块处于可配对模式。 3. 更换手机或蓝牙模块测试。 |
| APP连接后立即断开 | 1. 波特率不匹配。 2. 模块与STM32的TX/RX接反。 3. STM32串口未初始化或初始化错误。 | 1. 用串口助手分别测试模块和STM32的收发。 2. 检查接线。 3. 检查CubeMX串口配置和程序初始化代码。 | 1. 统一配置波特率(如9600)。 2. 交叉连接TX/RX。 3. 确保 HAL_UART_Init成功,并启动了接收中断。 |
| 发送指令,继电器无反应 | 1. 指令字符错误(大小写)。 2. STM32 GPIO引脚配置错误(非输出模式)。 3. 继电器模块触发方式不匹配(高/低电平)。 | 1. 用串口助手监听STM32的RX引脚,确认收到正确字符。 2. 用万用表测量控制引脚电压变化。 3. 查阅继电器模块手册。 | 1. 核对APP发送字符与程序判断字符。 2. 检查CubeMX中GPIO配置。 3. 修改程序中的输出电平逻辑( SET/RESET互换)。 |
| 继电器状态随机切换或抖动 | 1. 电源干扰或功率不足。 2. GPIO引脚受干扰。 3. 蓝牙数据受到干扰,收到错误指令。 | 1. 测量电源电压在负载动作时是否跌落严重。 2. 在控制引脚加一个下拉电阻(如10kΩ到GND)。 3. 在串口中断回调中增加指令有效性校验。 | 1. 使用更稳定、功率更大的电源,并在电源入口加滤波电容。 2. 软件上可对指令进行连续多次判断再执行。 |
| 程序下载失败 | 1. BOOT模式设置错误。 2. 下载器驱动或连线问题。 3. 芯片已写保护。 | 1. 确认BOOT0=1(下载),BOOT1=0。 2. 检查USB转TTL驱动,TX/RX是否交叉连接。 3. 使用STM32CubeProgrammer尝试解除保护。 | 1. 严格按照下载模式操作。 2. 更换下载线或接口。 3. 使用编程器解除读保护。 |
11. 项目扩展与进阶思路
这个双路继电器控制项目是一个完美的起点,你可以基于它进行多种扩展,打造更强大的控制系统。
- 增加控制路数:STM32F103C8T6有大量空闲GPIO,可以轻松控制8路、16路甚至更多的继电器。只需在程序中定义更多的控制引脚和状态变量,并扩展指令协议(如
C/c控制第三路)。 - 加入传感器反馈:接入温湿度传感器(如DHT11)、光照传感器、人体红外传感器等。STM32读取传感器数据后,不仅可以通过蓝牙上报给APP,还可以实现本地自动控制逻辑(如温度过高自动打开风扇)。
- 改进通信协议:使用更严谨的帧协议,例如
0xAA 0x01 0x01 0xAC表示打开第一路,其中包含帧头、地址、命令、校验和,大大提高抗干扰能力和可靠性。 - 更换通信方式:
- Wi-Fi:使用ESP8266/ESP32模块,通过MQTT协议接入家庭局域网或物联网平台(如阿里云、Home Assistant),实现远程控制。
- 4G Cat.1:使用移远EC200S等模块,直接通过蜂窝网络控制,无距离限制。
- 设计专用PCB:将STM32最小系统、继电器驱动电路、电源电路、蓝牙模块接口集成到一块PCB上,使产品更紧凑、可靠。
- 开发更专业的APP:使用Android Studio或Flutter开发功能更全的APP,包括场景模式(一键全开/全关)、定时任务、状态历史记录、用户登录等。
- 接入语音控制:通过APP集成语音识别SDK,或使用独立的语音模块(如LD3320),实现“小爱同学,打开客厅灯”这样的语音控制。
12. 总结与下一步
这个“STM32单片机蓝牙APP控制2路继电器”项目,成功串联了嵌入式硬件、单片机编程、无线通信和移动应用开发几个关键环节。它的价值不在于复杂度,而在于完整的闭环实现和极高的可扩展性。
你应该最先验证的是蓝牙通信链路和基本的GPIO控制,这是整个系统的基石。最容易踩的坑通常是电平匹配、串口交叉接线和电源干扰。按照本文的步骤,从硬件连接到CubeMX配置,再到指令逻辑编写和APP测试,一步步走下来,成功点亮LED的那一刻,你会对物联网控制有最直观的理解。
接下来,你可以尝试修改指令协议,增加一路继电器控制,或者尝试用手机APP做一个简单的界面来同时显示继电器状态和控制开关。当你熟悉了整个流程后,将其改造成Wi-Fi控制、加入温控逻辑,或是集成到你的毕业设计、创新项目中,就会变得水到渠成。建议将本文的代码和配置作为基础模板收藏,在未来的项目中随时复用和修改。