2026/9/30 23:59:15

HC-05蓝牙模块与Arduino Uno控制LED完整教程

HC-05蓝牙模块与Arduino Uno控制LED完整教程 最近在帮社团做一个蓝牙小装置需求其实特别朴素手机按一下桌上的Arduino Uno板载LED就亮再按一下它就灭。硬件方案我选了HC-05蓝牙模块因为它是SPP串口透传模块和单片机打交道几乎是零门槛手机端只需要当普通串口用就行不需要处理BLE协议栈也没有UUID、特征值这些麻烦概念。这篇文章是系列第一篇我把从硬件接线、AT指令配置、Arduino端代码到手机调试的完整过程都记录下来适合手里正好有Uno和HC-05、但还不清楚怎么把它们串起来的朋友。跟HC-05打交道这几年踩过的坑我都放在后面专门一节里照着步骤走基本一下午能跑通。1. 项目目标与选型为什么是HC-05 Arduino Uno1.1 先把要做的功能拆干净很多人一上来就找代码代码其实是最后一步。先想清楚这个项目到底要解决什么问题。我要的是“手机无线控制LED亮灭”拆开看就三件事手机端能发出控制指令指令能通过无线链路送到UnoUno解析指令后控制对应引脚的电平。这三点对应到硬件上就是蓝牙模块负责无线传输Uno负责串口接收和GPIO输出板载LED作为被控对象。整个链路是单向的但为了调试方便我在程序里加了回传功能让Uno把收到的指令原样发回手机这样能确认数据真的走到了。1.2 HC-05的串口透传原理HC-05是蓝牙2.0时代的经典模块核心功能简单粗暴它内置了一个蓝牙收发端和一个UART串口端外部单片机只要接上它的TX和RX就能把串口数据“透明”地打包成蓝牙数据发给手机反过来也一样。这里的关键词是“透传”。对Uno来说HC-05就像一个无线延长线你往串口写数据手机那边就能收到手机发来的数据Uno从串口也能读到。中间不需要TCP/IP协议、不需要组网、不需要知道蓝牙底层怎么分包。这也是我选它而不是ESP32Cellular之类方案的主要原因逻辑链路最简单出问题好排查。当然这种简单也有代价。HC-05支持的蓝牙版本较老传输速率不高穿墙能力一般但控制LED这种几个字节的小指令完全够用。如果你要传音频、图片就别用HC-05了。1.3 为什么不先上BLE或ESP32前几年做同类项目大家经常纠结选HC-05还是BLE模块或者是直接上ESP32。我的看法是如果你是第一次做手机控制硬件的项目HC-05的学习曲线最友好。BLE模块虽然新但手机端App要处理BLE的扫描、连接、服务发现、特征值读写概念一下子翻好几倍很容易卡在“连上了但收不到数据”。ESP32功耗和性能更好但它内置Wi-Fi和蓝牙双栈初学者很容易在烧录、配网这些环节上分心。HC-05不是最先进的选择却是最能让你把“串口控制”这件事一次跑通的选择。先跑通再升级这个顺序值得遵循。2. 硬件接线模块针脚、电平匹配与上电顺序2.1 HC-05的六个针脚到底怎么接市面上的HC-05模块常见有两种外壳一种是带底板的引出六针排针一种是纯裸模块引出排针引脚。无论哪种关键引脚都是下面这几个引脚名方向说明VCC电源输入通常接5V部分裸模块兼容3.3VGND电源地必须和Uno共地TXD数据输出接Uno的RX注意电平问题RXD数据输入接Uno的TX注意电平问题STATE状态输出未连接时低电平连接后高电平EN/KEY模式切换接高电平进入AT指令模式很多教程把STATE忽略掉但我建议有条件就接一个Uno的数字引脚比如D4用digitalRead判断蓝牙是否连接成功。后面调试会让你省很多事。2.2 我推荐的Uno接线方案软串口Uno本身只有一组硬件串口也就是D0(RX)和D1(TX)。这个串口同时被USB转串口芯片占用了如果HC-05也接在这两个引脚上上传程序时两者会互相干扰。新手最常见的现象就是HC-05接好后Arduino IDE上传一直失败。所以我的推荐接法是用SoftwareSerial库把D2和D3变成软串口专门给HC-05用硬串口留给电脑。接线如下HC-05 VCC → Uno 5VHC-05 GND → Uno GNDHC-05 TXD → Uno D2HC-05 RXD → Uno D3HC-05 STATE可选→ Uno D4注意Uno的D2是软串口的RXD3是软串口的TX。HC-05的TXD要接Uno的RX也就是D2HC-05的RXD接Uno的TX也就是D3。交叉接别接成直连。2.3 电平匹配和供电细节HC-05的逻辑电平是3.3VUno的I/O电平是5V。绝大多数带底板的HC-05模块已经在板子上做了电平转换直接接5V问题不大。但如果用的是那种超小裸模块最好给Uno TX到HC-05 RXD之间加一个分压电阻。一个稳妥做法串一个1kΩ电阻在信号线上再在模块RXD端并联一个2kΩ电阻到GND。这样5V信号能被分到大约3.3V不至于烧模块引脚。供电这里有个小坑有些同学直接用Arduino的5V引脚给HC-05供电插上USB线后模块的红色LED会亮但等手机搜索时却发现找不到设备。原因可能是USB口供电能力不足或者线材太差导致压降太大。建议使用独立5V电源或者换一根短而粗的USB线实测能解决一多半“搜不到设备”的怪问题。3. 让HC-05进入AT模式并配置参数3.1 进入AT模式的硬条件HC-05出厂时是“从机模式”默认名称通常叫“HC-05”默认配对密码是1234或0000。默认波特率分两类带底板的常见是9600裸模块常见是38400。但真正决定后续能不能用是把模块切到AT指令模式把参数改成自己需要的值。进入AT模式有两种办法按住模块上的黑色小按键再上电这是最常用的方法如果模块没有按键就把EN/KEY引脚接高电平3.3V或5V再上电。判断是否进入AT模式看模块上的LED闪烁速度。正常工作模式下LED一般是每秒闪一次左右AT模式下LED通常变成约2秒慢闪一次。这个状态很关键很多人对着一个没进入AT模式的模块敲AT指令永远只会收到乱码或没反应。3.2 用USB转TTL下发AT指令AT配置阶段不要用Uno的软串口最省事的是USB转TTL模块直接连HC-05。接线方式USB转TTL TXD → HC-05 RXDUSB转TTL RXD → HC-05 TXD5V → VCCGND → GNDEN/KEY接3.3V如果模块没有按键然后打开电脑上的串口调试工具波特率选38400发送“AT”收到“OK”就说明模块正常。如果没反应把波特率改成9600再试。这个“试两个波特率”的经验可以少走很多弯路。3.3 我建议配置的三个参数进入AT模式后至少做三件事AT // 测试返回OK ATNAMEHC05-LED // 改蓝牙名称方便识别 ATROLE0 // 设置为从机角色 ATUART9600,0,0 // 设置串口波特率9600无校验1个停止位ATROLE这个参数要重点解释一下。HC-05支持主机、从机、回环三种角色我们手机连接模块的场景模块必须是从机。如果你设置了主机角色手机就搜不到它了。之前有个同学把模块设成了主机模式折腾了一上午最后发现是ROLE1改成0立刻恢复正常。ATUART这条指令会把模块通信波特率改成9600后面Uno代码里的串口波特率也必须是9600两边不一致就会出现一堆乱码。这里千万不要嫌麻烦跳过等代码里用默认9600、模块里却是38400时你会看到串口监视器里全是乱码指令根本进不去。配置完成后给模块断电再重新上电让参数生效。注意AT模式下设置的参数虽然会保存到模块EEPROM里但有些版本要断电重启才完全生效别省这一步。4. 编写Uno端串口控制LED代码4.1 用SoftwareSerial还是硬串口前面说了Uno的硬件串口D0/D1被USB占用了。如果你把HC-05接在D0/D1上首次上传程序会很不稳定。所以我用了SoftwareSerial库把串口放在D2和D3上。SoftwareSerial是Arduino官方库模拟串口时序性能瓶颈是处理器要不停翻转引脚不适合高波特率和高吞吐量。但9600波特率下控制LED完全没问题占用资源也不严重。如果你用的是Arduino Mega板子有多个硬件串口直接接Serial1更稳。Uno用户请老老实实用软串口。4.2 完整代码和逐段说明下面是我实际烧录测试过的代码#include SoftwareSerial.h // 软串口RX接D2TX接D3对应HC-05的TXD和RXD SoftwareSerial BT(2, 3); // 如果接了STATE引脚可以拿D4来读连接状态 const int btStatePin 4; void setup() { // 板载LED引脚Uno上就是D13 pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); // 调试串口接电脑 Serial.begin(9600); // 蓝牙串口跟HC-05的波特率保持一致 BT.begin(9600); pinMode(btStatePin, INPUT); } void loop() { // 检查HC-05是否在AT使用过程中意外进入异常状态 if (digitalRead(btStatePin) HIGH) { // 已连接这个分支可以留着以后做逻辑 } if (BT.available()) { // 读一个字节 char cmd BT.read(); // 把收到的数据回传确认链路通 BT.print(echo:); BT.println(cmd); // 顺便在电脑串口也打一份方便调试 Serial.print(cmd); Serial.println(cmd); if (cmd 1) { digitalWrite(LED_BUILTIN, HIGH); BT.println(led:on); } else if (cmd 0) { digitalWrite(LED_BUILTIN, LOW); BT.println(led:off); } else { // 其他字符提示一下 BT.println(cmd:unknown); } } }这份代码的逻辑简单到不能再简单但有几个细节值得讲。第一我特意把收到的字符回传。手机端如果能看到echo的内容就证明蓝牙链路和Uno串口都正常出问题能一步定位到是模块还是引脚还是代码。第二我没有用String类型去解析字符串。用char一个字节一个字节判断对于单个字符指令是最高效的写法。如果有人想用“on”“off”这种字符串指令也不是不行但要注意结束符处理稍微复杂一点。第三为什么不用delay(500)放在循环里做LED闪烁因为delay会阻塞整个loop一旦手机发来连续指令Uno要等delay结束才能响应。后面如果你要同时处理多个传感器delay会直接毁掉实时性。留给后续扩展再说。4.3 上传代码前先断开硬件串口吗用软串口的好处就是不需要断开HC-05也能上传程序。但如果你还是用了硬串口方案上传前务必拔掉HC-05连接到D0/D1的两根线否则Arduino IDE会卡在“上传中”报avrdude的错误。另外如果你手里的Uno是替换过主控芯片的比如自己把ATmega328P焊上去的那得先烧好引导程序bootloader再通过USB下载程序。如果Uno上电后电源灯亮但电脑识别不到串口优先怀疑引导程序没烧好。给uno板烧录引导本身是个独立话题这里只提醒一句买Uno尽量选原厂或带引导的全新主控芯片省得入门就受挫。4.4 串口波特率这一关怎么过代码里Serial和BT都用了9600。Serial连电脑的串口监视器必须和Arduino IDE右下角的波特率一致选9600。BT和HC-05的波特率也必须一致前面AT指令里已经设成9600。出现乱码时优先检查这两个9600有没有对不上。我之前遇到过一次“模块能连、手机发指令就是乱码”折腾半天发现是手机上的蓝牙串口App默认是38400改回9600立刻好了。这个坑放在下面排错章节细说。5. 手机端连接与调试配对、串口App、收发验证5.1 配对和默认密码确保HC-05处于正常工作模式且已上电打开手机蓝牙扫描会看到一个名字叫“HC05-LED”的设备如果你按我上面的AT命令改过名的话。点击配对系统会要求输入PIN码一般是1234也可能是0000。有一定概率你之前用其他设备配过手机缓存了旧名称这时候去蓝牙设置里删掉旧设备重新搜一次很快解决问题。有一点很多教程没提HC-05虽然能同时保存几个已配对设备但同一时刻只能和一个手机连接。如果你之前用电脑配过手机再连就要先断开电脑那边。我见过最离奇的情况是模块被邻居手机连上了自己手机一直显示“配对失败”。5.2 手机端的串口调试App怎么选Android上我常用“蓝牙串口助手”这类工具界面里有一个连接按钮选设备后就进入收发界面类似串口调试助手。iOS上没有完美的免费方案但也有一些支持蓝牙SPP的串口工具原理一样。选App的时候有个隐藏坑很多BLE调试工具只支持BLE设备不支持HC-05这种经典蓝牙SPP。你打开App发现列表里搜不到HC-05不是模块坏了而是App压根不扫经典蓝牙设备。换一个明确写着支持SPP/蓝牙2.0的串口助手就行。5.3 实测整个控制链路连接成功后会听到提示音或者看到模块的STATE引脚变高电平同时模块LED闪烁频率会变化。然后打开手机App的串口界面确认波特率选9600发一个字符“1”。正常情况下模块LED变亮App收到一条“echo:1”App再收到一条“led:on”。发“0”LED灭App收到“led:off”。这里有个新手容易误会的地方手机App发出去的是文本字符“1”对应的ASCII码是49Uno读到的就是char类型1。不是二进制的1也不是数字1。如果有人说“我发1没反应”记得检查App里有没有勾选“Hex发送”。如果勾了Hex发“1”实际发的是0x01不是ASCII字符“1”Uno判断cmd1自然就不成立。5.4 收到乱码和频繁断开的处理乱码大概率是波特率不匹配。手机App、HC-05、Uno代码三者必须统一在同一波特率。还有一个容易忽略的点有些HC-05的AT指令默认波特率是38400但工作模式波特率是9600两者不一样。你AT配置完之后重新上电退出AT模式模块才会切回9600。如果你在AT模式下直接测试当然乱码。频繁断开先怀疑供电。HC-05工作时峰值电流不低如果供电电压跌落模块会自动断开。换独立5V供电或者用移动电源给Uno供电往往立竿见影。6. 排错专题HC-05连接不上的完整排查链路6.1 从LED状态判断模块处于什么模式很多同学一上来就说“HC-05蓝牙模块连接不上”问半天也说不清模块灯是什么状态。我建议先把模块的LED当成诊断口上电后LED常亮模块未进入正常工作模式可能是AT模式没退出上电后LED每秒闪一次正常待机等待连接上电后LED约2秒慢闪一次AT模式手机搜不到或者连不上是正常的连接成功后LED可能变成双闪或慢闪。如果你发现模块一直处于AT模式状态重新上电前确保EN/KEY引脚没有接高电平或者按键没有卡住。这一步能排除一多半“连接不上”。6.2 AT指令无响应的三个排查方向发AT没反应按顺序查TX和RX有没有接反。USB转TTL的TXD必须接HC-05的RXDUSB转TTL的RXD必须接HC-05的TXD。接成直连的我见了不下十次。波特率对不对。38400没反应就换9600不要死磕一个。是否真的进入AT模式。看LED是不是慢闪。如果波特率和接线都对AT还没反应用万用表量一下模块VCC引脚是不是5V。有时候接触不良模块其实没工作。6.3 Uno上传失败和串口复用冲突如果你用了硬串口接法上传时HC-05也接着D0/D1会造成冲突。Arduino IDE上传程序失败或avrdude提示“programmer is not responding”时先把HC-05接到D0/D1的线拔掉再上传。上传完再插回去这是最省心的方式。如果你换过主控芯片上传时大概率会卡在“burning bootloader”这步。给Uno板烧录引导不是本项目的重点但要注意ATmega328P新芯片时内部没有引导程序必须先通过ISP下载器烧写bootloader否则USB串口无法下载程序。买芯片时问清楚是否带引导能省很多麻烦。6.4 灯不亮但串口有回显还有一种少见但很有意思的情况手机发了“1”App也收到led:on的回显但板载LED就是不亮。先看代码里的LED_BUILTIN。在Arduino Uno上LED_BUILTIN就是D13板载LED焊在D13和GND之间。如果你用了别的开发板LED_BUILTIN对应的引脚不同甚至根本没有板载LED代码也能编译通过但不会有物理现象。再看是不是引脚被复用了。Uno的D13同时连了SCK如果外接SPI设备可能会因为负载导致LED亮度异常或者直接不亮。控制板载LED时最好把扩展屏、SD卡这类SPI外设先断开再测。最后一个可能性是把引脚设成了INPUT。digitalWrite对INPUT引脚无效代码里务必写pinMode(LED_BUILTIN, OUTPUT)。这属于低级错误但确实发生过。7. 从“点灯”出发后续可以扩展的方向7.1 双机主从通信和状态回传这个项目跑通后很多人会想着让两块Uno之间通过HC-05通信这就是“HC-05双机通信主从信号”的玩法。把其中一块设成主角色另一块设成从角色上电后主模块自动搜索并连接从模块然后两边就能通过串口互相发送字符串了。我建议下一个项目就做这个。它跟手机控制的差异主要在角色配置和状态机设计代码层面反而更简单。有了主从通信基础就能做无线传感节点、遥控小车、多机协同这些进阶玩法。7.2 定时器中断实现LED闪烁等非阻塞写法控制单灯亮灭是第一步第二步就是让LED按要求闪烁。初学者习惯用delay但这会阻塞整个程序。更好的方式是使用millis()函数做非阻塞延时或者直接上定时器中断。Arduino Uno的ATmega328P有多个定时器可以配置定时器中断来翻转引脚。实现LED闪烁只是最基础的演示掌握了定时器中断后你就能同时处理蓝牙指令、传感器采集和LED动画程序结构会从“顺序执行”升级成“状态机驱动”。7.3 把单字节指令升级为带校验的协议现在用“1”和“0”控制开关够用但不抗干扰。蓝牙环境里偶尔会有噪音字符如果你把指令升级成类似“ATLED1\r\n”这种带帧头和结束符的协议再在Uno端写解析函数可靠性会高很多。更严格一点还可以加CRC校验做法就是在指令末尾附加一个校验字节Uno收到后先算校验再执行。这套思路无论以后接传感器、控制舵机、还是做数据采集都是通用的。本质上你已经在写通信协议了这对以后理解物联网项目帮助很大。7.4 下一篇预告手机通过HC-05控制Uno板载LED亮灭到这里已经完成了从硬件到软件的完整闭环。系列下一篇我打算专门写手机端界面不再用串口调试助手而是用App Inventor做一个自己的控制界面按钮亮灭、状态显示、滑块调亮度都能做。到时候你会看到这套串口指令设计可以直接复用不需要改Uno代码。顺便说一句类似“串口控制LED”的思路在STM32、ESP32、甚至FPGA上都能见到。用KeilSTM32Proteus点LED时核心也是UART接收和GPIO输出只是外设寄存器配置方式不同。把这篇里的通信逻辑吃透换平台只是换API的事。所以别觉得点灯太简单它的价值在于帮你打通“无线串口控制”这条主线后面接什么功能只是想象力的问题。