资讯动态

JDY-31蓝牙模块与Arduino串口透传:手机遥控智能小车实战指南

发布时间:2026/9/28 2:01:25 来源:尧图企业网站定制
做手机控制智能小车、遥控开关或者给传感器数据加个无线上报最容易上手的一条路就是 Arduino 加蓝牙模块。JDY-31 属于 BLE 4.2 串口透传模块工作电压宽、功耗低、手机端免配对基本是我手边做无线串口通信的首选。这篇文章我直接把接线、AT 指令配置、Arduino 端代码、Android 端连接全部串起来讲按步骤走一遍5 分钟就能让手机和单片机互相发数据。适合正在做智能小车、平衡车、远程控制开关或者想给 Arduino 项目加“手机遥控”功能的人参考。先说清楚这模块能干什么它本质上是个“蓝牙串口透明传输”器件Arduino 往串口发什么手机上的蓝牙调试软件就能收到什么手机软件发下去的数据也会原样出现在 Arduino 的串口上。整个过程不需要你去了解蓝牙协议栈细节只要会操作串口就能用。下面我从模块选型开始讲把过程中容易踩的坑一并交代清楚。1. 为什么要用 JDY-31从模块特性到选型思路很多初学者一开始会在 HC-05、HC-06 和 JDY-31 之间纠结。我的看法是只要你做的是“手机和单片机通信”这类的项目JDY-31 往往比 HC-05 更适合原因不光是功耗低还在于它的 BLE 特性让手机端连接更省事。1.1 JDY-31 的硬件底子与串口透传原理JDY-31 的核心方案是 TI 的 CC2541支持 BLE 4.2板载 PCB 天线空旷环境下通讯距离能做到 30 米到 60 米左右这对小车控制、小家电遥控来说完全够用。它对外只有四个最基本引脚VCC、GND、TXD、RXD供电范围是 3.6V 到 6V所以接到 Arduino Uno 的 5V 上没有问题平均工作电流大概在几毫安级别功耗比经典蓝牙低很多。串口透传的意思很直白模块引出来的 TXD、RXD 就是一个标准 TTL 电平 UART 接口你在 Arduino 那边用Serial.print()往串口写入数据数据会经过模块内部的蓝牙协议栈打包通过 BLE 无线发送给手机端。反过来手机端通过 BLE 写过来的数据模块会拆包后从 TXD 引脚按原样吐出来Arduino 的Serial.read()就能读到。也就是说对单片机而言JDY-31 就是一个“无线串口”你原来怎么用有线串口现在照着做就可以。这里有个容易忽略的点JDY-31 输出的 TXD 引脚电平是 3.3V而 Arduino Uno 的 RX 引脚在识别高电平时阈值大约是 3V所以 3.3V 的串口信号可以直接被识别实际使用中没问题。反过来 Arduino 的 TX 是 5V 电平JDY-31 的 RXD 内部一般有钳位电路实际直连也能正常工作。我实测过 Uno 直连 JDY-31 是稳定的但如果你做的是正式产品稳妥做法还是在 Arduino TX 和模块 RXD 之间加一个电阻分压比如 10k 串联、20k 下拉把 5V 分到 3.3V 左右。1.2 为什么选 JDY-31而不是 HC-05/HC-06我把三个模块放在一起对比过选型逻辑其实很清楚。对比项JDY-31HC-05HC-06蓝牙版本BLE 4.2蓝牙 2.0蓝牙 2.0功耗低较高较高主从模式支持设置主从一体只能做从机手机配对免配对需要输入 PIN 码需要输入 PIN 码iOS 兼容性好较差较差默认波特率960096009600常见应用手机遥控、数据采集、低功耗设备双机通信、经典蓝牙场景简单从机透传HC-05 的优势是主从一体可以两个模块互相组网适合做“两个单片机之间无线通信”。但如果你只是想让手机连模块HC-05 每次都要输入密码配对而且 iOS 对经典蓝牙的限制非常多经常搜不到或者连上后掉线。JDY-31 是 BLE手机打开蓝牙后直接用调试软件扫描就能看到不需要配对输入 PIN 码Android 和 iOS 都友好很多。我并不是说 HC-05 完全没用如果你要做的项目是“一块 Arduino 无线控制另一块 Arduino”那 HC-05 的经典蓝牙主从模式反而更直接。但只要是手机端参与的项目我建议优先考虑 JDY-31 这类 BLE 透传模块这是从实际兼容性出发得出的经验。2. 接线与 AT 指令配置动手前的关键准备模块拿到手先别急着接 Arduino我建议先把模块接到 USB 转 TTL 上裸测一遍确认模块是好的、AT 指令能正常响应再接 Arduino。这样后面排查问题时至少能确定蓝牙模块本身没有问题。2.1 最小系统接线与注意事项JDY-31 直接通过排针引出使用杜邦线就能连接。接到 Arduino Uno 上有两种方案一种是用硬件串口即模块 TXD 接 Arduino 的 RX0 号引脚模块 RXD 接 Arduino 的 TX1 号引脚另一种是用软串口模块 TXD 接 Arduino 的 2 号引脚RXD 接 3 号引脚这种方案后面我会单独讲。最小系统接线表如下Arduino UnoJDY-315VVCCGNDGNDRX0TXDTX1RXD接好之后模块上电如果 LED 指示灯在闪烁说明模块正在等待 BLE 连接当手机连上模块以后LED 会变成常亮或者慢闪不同批次模块灯的状态定义可能不同但基本可以依据灯的变化判断连接状态。这里必须强调一个新手高频翻车点如果模块接在 Arduino 的 0 号和 1 号引脚上Arduino 上传程序的时候一定要把模块拔掉或者把杜邦线断开否则蓝牙模块会占用串口Arduino IDE 上传固件大概率失败报错信息往往是“无法打开 COM 口”或者上传超时。我第一次做小车的时候不知道这个一度以为是板子坏了折腾了半天才发现是蓝牙模块占着串口。2.2 用 USB 转 TTL 裸调模块先确认 AT 指令正常USB 转 TTL 模块建议选带 3.3V 和 5V 电平切换的型号比如常用的 CP2102 或 CH340 小板。接线时 USB 转 TTL 的 TXD 接 JDY-31 的 RXDUSB 转 TTL 的 RXD 接 JDY-31 的 TXDVCC 接 5VGND 接 GND交叉连接这个点一定不能搞反不然什么数据都收不到。打开电脑上的串口调试助手选择对应的 COM 口波特率设置为 9600注意串口调试助手的发送格式要选“字符”不要选“HEX”。发送内容填AT然后点击发送。如果一切正常模块会返回OK。这一步是确认模块工作状态的最快方法。为什么默认要用 9600JDY-31 出厂波特率就是 96008 位数据位、无校验、1 位停止位这是最常用的 TTL 串口参数组合。如果你之前改过模块波特率就需要用修改后的波特率去连接才能通信。对于新模块我始终建议先用 9600 做第一步验证。裸调的过程中还能顺便确认 AT 指令的格式。JDY-31 的 AT 指令和 HC-05 有明显区别HC-05 设置名称要写成ATNAMExxxJDY-31 直接写ATNAMExxx中间没有等号很多从 HC-05 转过来的人会在这里卡住。JDY-31 的指令规则是指令后面直接跟参数不加等号。2.3 JDY-31 常用 AT 指令速查表AT 指令是配置模块的核心手段我整理了一份常用的 JDY-31 指令表直接按这个操作即可。指令功能说明示例返回示例AT测试通信是否正常ATOKATNAME查询或设置蓝牙名称ATNAMECarOKNAMECarATBAUD查询或设置波特率ATBAUD8OKBAUD8ATPIN查询或设置 PIN 码ATPIN0000OKPIN0000ATROLE查询或设置角色0从机/1主机/2循环ATROLE0OKROLE0ATMAC查询 MAC 地址ATMACOKMACxxxxATVER查询版本信息ATVEROKVER...ATRESTORE恢复出厂设置ATRESTOREOKRESTOREATPWR设置发射功率1低/2中/3高ATPWR3OKPWR3ATADVI设置广播间隔单位 10msATADVI100OKADVI100ATUUID查询或设置自定义服务 UUIDATUUIDOKUUID...ATTYPE查询或设置连接类型ATTYPE2OKTYPE2波特率参数对应关系是1 对应 12002 对应 24003 对应 48004 对应 96005 对应 192006 对应 384007 对应 576008 对应 1152009 对应 230400。如果你把波特率从 9600 改成 115200也就是发送ATBAUD8模块返回OKBAUD8后必须把串口调试助手的波特率也改成 115200重新发AT才能继续通信这是很多新手改完波特率后以为模块坏了的最常见原因。设置名称时要注意名称最好不要包含中文和特殊字符BLE 广播的设备名太长了会影响扫描发现我个人习惯控制在 12 个字符以内。另外模块在蓝牙连接状态下不会响应 AT 指令因为此时串口进入透传模式你在电脑上发的文本会被当成普通数据发给手机端。所以调试 AT 指令前先确认手机没有连接模块。3. Arduino 端串口通信程序实现Arduino 端的程序核心就是把串口读写打通。最直接的方式是让硬件串口直接透传也可以用软串口把蓝牙模块挪到其他引脚避开下载口。两种方式我分别给出可用的代码并解释各自的适用场景。3.1 最简透传代码5 分钟跑通数据链路如果你只是验证手机和 Arduino 能不能互相通信直接用硬件串口最省事。把 JDY-31 接在 0 号、1 号引脚然后烧录这段代码void setup() { Serial.begin(9600); } void loop() { if (Serial.available()) { Serial.write(Serial.read()); } }这段代码的逻辑非常直白Arduino 一旦在串口收到数据立刻把收到的字节原样写回串口。你手机通过蓝牙发给 Arduino 一个AArduino 会把它回发给手机形成一个天然的“回环测试”用来验证整条无线链路是否通畅。如果手机端发文字手机蓝牙软件上能看到同样的文字原样返回就说明 Arduino 的串口收发、JDY-31 的透传、手机 BLE 连接三个环节全部正常。很多教学程序喜欢把代码写成带板载 LED 闪烁的版本但我觉得第一步调试就应该保持最小化回环测试是最可靠的验证手段。它能把问题范围快速收缩到某个环节而不是一上来就怀疑代码逻辑。3.2 用软串口让模块不占用下载端口硬件串口虽然简单但占用了 0 号和 1 号引脚导致每次改代码都要先拔模块很麻烦。更优雅的方案是用软串口把 JDY-31 接到 2 号和 3 号引脚上传程序时不用动杜邦线。#include SoftwareSerial.h SoftwareSerial btSerial(2, 3); // RX, TX void setup() { Serial.begin(9600); btSerial.begin(9600); } void loop() { if (btSerial.available()) { Serial.write(btSerial.read()); } if (Serial.available()) { btSerial.write(Serial.read()); } }注意SoftwareSerial构造函数的参数顺序是(RX, TX)也就是 2 号引脚作为软串口的 RX接 JDY-31 的 TXD3 号引脚作为软串口的 TX接 JDY-31 的 RXD。这段代码实现的是“串口 0 和软串口之间双向转发”你在 Arduino IDE 串口监视器里输入的文字会转发给蓝牙模块手机蓝牙软件发来的文字会显示到串口监视器里。这样既不影响串口监视器调试又不用频繁拔线。软串口在某些情况下会丢数据因为它是靠软件模拟的时序占用 CPU 时间片波特率太高时容易出现乱码。我的经验是软串口波特率控制在 19200 以内最稳妥JDY-31 默认 9600 完全没有压力。3.3 从“收发文字”升级到“控制设备”通信链路通了之后最常见的需求就是手机发指令控制 Arduino 的引脚比如控制 LED 灯开关、控制舵机、控制电机驱动板。下面是一个最基础的 LED 控制例子手机蓝牙软件连接后发送字符1点亮板载 LED发送0熄灭。#include SoftwareSerial.h SoftwareSerial btSerial(2, 3); int ledPin 13; char cmd; void setup() { pinMode(ledPin, OUTPUT); Serial.begin(9600); btSerial.begin(9600); } void loop() { if (btSerial.available()) { cmd btSerial.read(); if (cmd 1) { digitalWrite(ledPin, HIGH); btSerial.println(LED ON); } else if (cmd 0) { digitalWrite(ledPin, LOW); btSerial.println(LED OFF); } } }这段代码里我加了一个反馈动作执行完控制指令后模块会向手机发送一条“LED ON”或“LED OFF”的确认消息。这个设计很多人一开始不在意但实际做项目时非常重要尤其当你控制的是小车轮子或者舵机时没有反馈意味着你无法判断指令到底有没有执行成功。做控制系统永远是“发指令 收反馈”一起做而不是单向发完就完事。4. Android 端如何快速连上并收发数据Android 端分两种情况只想验证功能用现成的 BLE 调试软件想做完整产品用 Android Studio 自己写 App。两种我都覆盖先说最快的验证方式。4.1 现成工具推荐BLE 调试助手与实操流程PC 端的串口调试助手不能直接连 BLE手机端需要装一个支持 BLE 的串口调试软件。我用过的几个中比较推荐nRF Connect专业级 BLE 调试工具能看到完整的服务和特征值适合排查底层问题。BLE 调试助手国内开发者做的界面直观支持常见的串口透传服务适合快速收发数据。Serial Bluetooth Terminal交互风格像终端适合发送自定义文本指令。以 BLE 调试助手为例操作流程是打开手机蓝牙和定位权限打开 App点击扫描按钮。此时 JDY-31 模块应该出现在扫描列表中默认名称一般是 JDY-31-SPP 或者你通过 ATNAME 修改后的名称。点击连接连接成功后进入数据收发界面在输入框填上要发的数据点击发送Arduino 端就能收到。这里有一个非常典型的坑很多人用手机自带的“蓝牙设置”界面去搜索 JDY-31结果怎么也搜不到。原因很简单手机自带蓝牙列表只显示经典蓝牙设备而 JDY-31 是 BLE 设备必须用支持 BLE 扫描的软件才能发现。这不是模块坏了是搜索工具不对。所以遇到“搜不到模块”的情况第一反应应该是换一个 BLE 调试软件而不是怀疑硬件。4.2 自己写 Android AppBLE 连接与数据收发核心代码如果你的毕业设计或比赛项目需要提交 App或者你想把控制界面做成自己的风格那就需要基于 Android BLE API 写应用。下面我给出一个最小可用逻辑包含扫描、连接、发现服务、读写特征值四大部分。首先在 AndroidManifest.xml 中声明蓝牙权限和定位权限。Android 6.0 之后扫描 BLE 设备还需要定位权限Android 12 之后则使用新的蓝牙权限uses-permission android:nameandroid.permission.BLUETOOTH_SCAN / uses-permission android:nameandroid.permission.BLUETOOTH_CONNECT / uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION /扫描 BLE 设备的核心代码BluetoothLeScanner scanner bluetoothAdapter.getBluetoothLeScanner(); ScanCallback scanCallback new ScanCallback() { Override public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice device result.getDevice(); if (device.getName() ! null device.getName().contains(JDY-31)) { // 找到目标设备停止扫描并连接 scanner.stopScan(this); device.connectGatt(context, false, gattCallback); } } }; scanner.startScan(scanCallback);连接成功并发现服务后需要找到 JDY-31 的透传服务和特征值。JDY-31 用的服务 UUID 是0000FFE0-0000-1000-8000-00805F9B34FB特征值 UUID 是0000FFE1-0000-1000-8000-00805F9B34FB这是市面上大部分 BLE 串口透传模块的统一约定UUID SERVICE_UUID UUID.fromString(0000ffe0-0000-1000-8000-00805f9b34fb); UUID CHARACTERISTIC_UUID UUID.fromString(0000ffe1-0000-1000-8000-00805f9b34fb);在onServicesDiscovered回调里获取服务对应的特征值然后开启通知这样 Arduino 发来的数据才会上报给手机 AppOverride public void onServicesDiscovered(BluetoothGatt gatt, int status) { BluetoothGattService service gatt.getService(SERVICE_UUID); BluetoothGattCharacteristic characteristic service.getCharacteristic(CHARACTERISTIC_UUID); gatt.setCharacteristicNotification(characteristic, true); }向 Arduino 发送数据时直接给特征值写入字节BluetoothGattCharacteristic characteristic gatt.getService(SERVICE_UUID) .getCharacteristic(CHARACTERISTIC_UUID); characteristic.setValue(1.getBytes()); gatt.writeCharacteristic(characteristic);接收 Arduino 发来的数据在onCharacteristicChanged回调中读取即可Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { byte[] data characteristic.getValue(); String message new String(data); }Android BLE 开发的版本差异不小新版本 SDK 对writeCharacteristic方法做了变更需要传入回调参数。我的建议是先在真机上跑通一个最简单的发送例子再逐步加功能不要一上来就套复杂架构。另外 BLE 扫描对设备和蓝牙芯片敏感低端安卓手机扫描速度会很慢属于正常现象不是代码问题。5. 常见问题排查与避坑实录我在 JDY-31 上踩过的坑并不少这一节直接把高频问题整理成速查表同时补充一些只有实际动手才会注意到的细节。5.1 问题排查速查表现象可能原因解决方法手机 BLE 软件搜不到模块模块没有上电、距离太远、模块已连接其他设备检查供电断开已连设备靠近再扫描手机自带蓝牙列表搜不到JDY-31 是 BLE 设备普通蓝牙列表不显示使用 nRF Connect 或 BLE 调试助手扫描发送 AT 无返回模块已被手机连接进入了透传模式断开手机连接后重新发 AT串口监视器显示乱码Arduino 与模块波特率不一致统一为 9600 或其他相同波特率Arduino 上传程序总是失败蓝牙模块占用 0/1 号串口拔掉模块上传或改用软串口方案能连上但发不出数据手机 App 没有开启通知在onServicesDiscovered中调用setCharacteristicNotification距离稍远就断连发射功率较低或天线被遮挡发送ATPWR3提高功率调整天线方向模块发热明显供电电压过高或接反检查 VCC 和 GND确认供电范围5.2 断电顺序、首次使用与参数恢复我第一次用 JDY-31 改波特率时犯过一个错发送ATBAUD8后模块没有完成完整的断电重启随后无论怎么发AT都不回。后来才发现是串口调试助手还在用旧波特率。这个问题我印象很深因为从用户视角看模块好像“变砖”了实际上只是参数没同步。改完 ATBAUD 后我的标准操作是断开串口调试助手连接模块断电重新上电把串口调试助手的波特率改成新值再重新打开串口发AT验证。这四步一步都不能省。还有一个容易被忽略的细节是模块的参数复位。如果你把模块改乱了比如忘了新密码、改了名之后连不上发送ATRESTORE可以恢复出厂设置模块会恢复到默认名称、默认 PIN 码、默认波特率 9600。这个指令应该是你对模块操作时的最后一道保险。5.3 供电稳定性问题供电不稳定是蓝牙模块乱码和断连的真正元凶之一。JDY-31 的正常工作电压范围比较宽但 Ardu-ino 的 5V 引脚如果同时驱动了舵机、电机驱动板等大电流设备电源纹波会变得很大导致蓝牙串口收发数据时出现随机乱码。我的实际做法是蓝牙模块尽量单独从 5V 引脚取电电机驱动板从外部电源单独供电并确保共地。如果发现模块发送数据偶尔丢字节优先用示波器直接看 VCC 的纹波没有示波器的话可以先给模块换一个独立的 3.7V 锂电池供电验证一般就能看出是电源问题。6. 关于 JDY-31 和 Android 开发的几个进阶建议把基础通信跑通之后可以做一些明显的功能扩展比如在 Arduino 端给每个指令设计带校验字的通信协议这样可以避免手机误触发的乱码控制或者在 Android 端做一个简单的圆形摇杆 UI通过 BLE 实时发送方向和速度值直接驱动智能小车。JDY-31 的透传能力决定了你不需要关心无线底层怎么处理数据只需要设计好“串口协议”这是整个项目最值得花时间打磨的部分。我自己用得最多的通信协议格式是一个简单的帧结构开头是 0xAA 和 0x55 两个固定头中间放数据长度和数据内容最后放一个累加和校验。这样做以后就算某个字节丢了或者错了接收端也能通过校验识别出来并丢弃错误数据包而不是执行一个错误的动作。小车控制类的项目尤其需要这种协议设计否则手机发一个按键值蓝牙丢一个字节车就可能乱跑。如果你要做的项目对低功耗有要求JDY-31 的 BLE 特性在待机功耗上会比 HC-05 好很多但要注意在 Arduino 端配合睡眠模式使用才能达到理想功耗。模块只负责无线通信MCU 本身不休眠整体功耗还是降不下来。这时候可以考虑用带低功耗模式的 Arduino 兼容板或者直接换用 ESP32 并走蓝牙方案这是另一个方向了。最后再分享一个小技巧JDY-31 出厂默认恢复后如果你不确定当前模块是否已被手机连接可以看模块上的 LED 灯状态。闪烁说明未连接常亮说明已连接。这个判断方法在我排查问题时节省了大量时间希望也能帮到后来的人。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑