手机当显示器,Arduino 当采集端,App Inventor 负责把蓝牙数据变成看得见的界面——这套组合我前后做了好几次,最近一次从零开始接线路、写代码、搭 App 到调通,实际动手时间比预想中短很多。关键点在于把数据格式、串口时序、蓝牙模块的脾气摸清楚,剩下的就是照葫芦画瓢。
我一直很喜欢这种“小链路”的玩法:一个传感器测温度湿度,通过串口发给蓝牙模块,再通过无线电飞到手机里,最后在 App 的标签上显示出来。看上去简单,但里面涉及传感器采集、电平匹配、串口通信、蓝牙透传、手机端字符串解析,正好是一条完整的物联网数据链路。适合课程设计、科创比赛,也适合想搞明白蓝牙串口通信是怎么回事的新手。
如果你手里正好有一块 Arduino Uno 或 Nano,再买个 DHT11 和 HC-05 蓝牙模块,一个下午绝对能跑通。看完这篇,你不光能复现一个蓝牙温湿度计,还能顺带搞懂以后遇到“手机和硬件通信”这类需求,该从哪里下手。
1. 方案定夺:为什么是 App Inventor + Arduino + HC-05
1.1 需求拆解:手机实时显示温湿度到底要解决什么问题
这个项目的核心需求一句话就能讲清楚:让手机屏幕上实时显示 Arduino 采集到的温度、湿度数值。你要解决的其实是一条数据链路的问题,链路如下:
DHT11 温湿度传感器 → Arduino 数字引脚读取 → 串口发送 → HC-05 蓝牙模块 → 蓝牙空中传输 → 手机蓝牙接收 → App 解析并显示。
这条链路上任何一环断了,你都会面对“明明程序没问题,但手机就是没数据”的尴尬。所以动手之前,先把链路里的每个环节都列出来,再逐个确认接法、配置和协议。这种思维方式比单纯做这个项目更值钱,以后换个传感器、换个无线模块,照样能套用。
我用生活里的例子来类比一下:数据就是一封信,传感器负责写内容,Arduino 是写信的人,串口是信封,HC-05 蓝牙模块是邮递员,手机是收件人,App 是帮你拆信和念信的人。想要完整收到内容,写信格式要统一、信封地址要对、邮递员路线要通、收件人得愿意开门。所以后面对话提到的“数据格式”“波特率”“配对”“权限”这些词,本质上都是在确保这条邮路通畅。
1.2 横向对比:为什么不直接用 ESP32 或者 OLED
做硬件方案,最忌讳一上来就选一个看起来很流行但实际手上没有的东西。我当时手头就是 Arduino Uno 加上一堆传感器,所以第一选择就是在 Arduino 生态里解决。下面这张对比表能看出不同方案的区别:
| 方案 | 硬件成本 | 开发量 | 手机参与 | 适合场景 |
|---|---|---|---|---|
| Arduino + HC-05 + App Inventor | 低 | 低 | 实时显示,可加控制 | 课程设计、快速原型、入门教学 |
| ESP32 + BLE/WiFi 直连手机 | 中 | 中 | 实时显示,可加控制 | 已熟悉 ESP32、想上物联网 |
| Arduino + OLED 屏幕 | 低 | 低 | 无手机参与 | 固定现场、本地显示 |
| Arduino + 串口屏 | 中 | 低 | 无手机参与 | 产品原型、界面要求较高 |
用 App Inventor 而不是写原生 Android,主要原因是快。App Inventor 的图形化编程方式,对于一个只需要显示两三个数值的轻量 App 来说再合适不过。它自带的蓝牙客户端组件,支持经典蓝牙的 SPP(串口透传协议),而 HC-05 正是最典型的 SPP 蓝牙模块,两者之间不需要额外写协议栈。如果你用原生开发,光处理蓝牙权限、连接状态、线程回调这些事,就够喝一壶了。
当然,如果你已经有 ESP32,也可以直接用 BLE 做这个项目。不过 ESP32 的 BLE 在 App Inventor 里配置起来比经典蓝牙麻烦一些,还要处理 GATT 服务、特征值这些概念。对于“只是想把传感器数据传上手机”这个需求,HC-05 这种“串口变蓝牙”的模块,几乎没有学习门槛。
1.3 硬件清单和购买避坑
具体硬件如下:
- Arduino Uno 或 Nano 开发板一块,Uno 最常用,Nano 更小巧,用起来没本质区别。
- DHT11 温湿度传感器一个。精度大概 ±2℃,演示足够;想要更准就买 DHT22,价格贵一点,引脚基本兼容。
- HC-05 蓝牙模块一个。注意尽量买“带底板”的版本,后面会解释原因。
- 面包板一块、杜邦线若干。
- Android 手机一台。注意 App Inventor 的蓝牙客户端组件基于经典蓝牙 SPP,iOS 不支持这种模式,所以必须用安卓手机做测试。
买 HC-05 的时候,一定要看清是不是带底板的。带底板的模块长条形状,上面有排针,正面通常有一颗 LED 指示灯和几个引脚,直接插杜邦线就能用。裸模块只有一个小小的芯片板,引脚是焊盘,得自己焊线。带底板最大的好处是集成了电平转换和稳压电路,你直接用 Arduino 的 5V 给它供电、直接接 TX/RX,基本不会出问题。如果是裸模块,RXD引脚上的电平逻辑只有 3.3V,而 Arduino 的 TX 输出是 5V,长期直连有烧毁风险,需要自己加分压电阻。所以新手预算够的话,买带底板的 HC-05 能省掉一个不小的麻烦。
1.4 关于“自动连”的理解和设计取舍
标题里写了“手机自动连 Arduino”,这里需要澄清一下:HC-05 本身没有“自动连”的概念,它上电之后会处于可配对状态,手机端需要主动发起连接。所谓自动连,指的是 App 打开之后自动从已配对列表里找到 HC-05 并建立连接,而不是每次都要手动点。
我的做法是在 App 初始化时读取手机蓝牙的已配对设备列表,在列表里寻找名字包含 “HC-05” 的设备,找到后直接调用连接方法。如果找不到,就退回到手动选择模式。这个设计既保证了效率,也不会因为蓝牙设备名被改过而彻底卡死。
还有一种更稳的取巧做法:第一次手动连接成功后,把 HC-05 的蓝牙 MAC 地址记到 App 的小数据库里,下次打开 App 直接按 MAC 地址连接。这样就不用每次都扫描列表,即使你改了模块名字也能连上。后面我会在 App 环节详细说。
2. 硬件接线与 Arduino 端采集代码
2.1 DHT11 接线和上拉电阻的理解
DHT11 是典型的单总线数字传感器,数据脚只有一根。接线方式非常简单:
- DHT11 的
VCC接 Arduino 的 5V - DHT11 的
GND接 Arduino 的 GND - DHT11 的
DATA接 Arduino 的数字引脚 D2
如果你买的是裸的 DHT11,没有集成上拉电阻,最好在DATA和VCC之间接一个 4.7kΩ 到 10kΩ 的上拉电阻。原因是 DHT11 的数据引脚是开漏结构,空闲状态下需要外部电阻把电平拉到高电平,传感器才能正常发起通信。买模块版本的一般已经集成好这个电阻,插上就用。
这里有一个特别容易忽略的坑:DHT11 的数据线不能太长,控制在 20 厘米以内比较稳。如果接线过长,寄生电容会让信号波形变得很差,轻则读取失败,重则持续返回nan。在面包板上测试时,尽量把 DHT11 放在离 Arduino 近一点的地方,后续做真实部署时再考虑换更抗干扰的传感器。
另外,四针的 DHT11 会多一个悬空引脚,千万别把它当成第二根信号线,留空就行。接反电源会导致传感器立刻发热损坏,插之前多看几遍引脚图。
2.2 HC-05 接线和电平匹配,以及上传程序的坑
HC-05 的接线同样不复杂,但有几个细节必须注意。带底板的模块建议这样接:
- HC-05 的
VCC接 Arduino 的 5V - HC-05 的
GND接 Arduino 的 GND - HC-05 的
TXD接 Arduino 的 RX,也就是数字引脚 D0 - HC-05 的
RXD接 Arduino 的 TX,也就是数字引脚 D1
这里要解释一下为什么需要“TXD 接 RX、RXD 接 TX”。串口通信是交叉连接的,A 设备的发送引脚要连 B 设备的接收引脚,数据才可能被正确读到。很多人第一次接线容易接成 TX 接 TX、RX 接 RX,结果手机能连上但什么都收不到。这不是模块坏了,而是你把写信的人和收信的人放到了同一边,信自然送不出去。
如果是裸模块,就必须考虑电平匹配。Arduino Uno 的 TX/RX 引脚是 5V 逻辑,而 HC-05 串口模块的 RXD 一般按 3.3V 逻辑设计,直接用 5V 电平去驱动有可能损坏模块。带底板的模块通常已经把电平转换电路做进去了,可以直接接 5V 引脚,这也是我让大家优先买带底板版本的原因。如果你手里只有裸模块,最简单的办法是在 Arduino TX 到 HC-05 RXD 之间串一个 1kΩ 电阻做限流分压,虽然不是教科书式的精密分压,但大部分情况下能正常工作。
还有一个特别重要的实操提醒:往 Arduino 上传程序的时候,一定要把 HC-05 模块从杜邦线上拔掉,或者至少把它的 TX/RX 两根线拔掉。为什么?因为 Arduino Uno 上传程序靠的就是 D0/D1 这对串口引脚,HC-05 也挂在同一对引脚上,两边同时收发数据就会产生总线冲突。轻则 IDE 一直卡在“上传中”,重则上传失败后模块进入奇怪的死状态,让你以为蓝牙坏了。我吃过这个亏,后来养成了习惯:刷程序前先拔模块,刷完再插回去。
2.3 Arduino 端代码:定义一种手机好解析的数据格式
Arduino 端代码并不长,核心步骤是初始化 DHT11,然后每两秒读一次温湿度,通过串口以固定格式输出。这里直接用 DHT 库,在 Arduino IDE 的库管理器里搜索 “DHT sensor library” 安装即可,记得把依赖库 Adafruit Unified Sensor 也一并安装。
完整代码如下:
#include <DHT.h> #define DHTPIN 2 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); void setup() { Serial.begin(9600); dht.begin(); } void loop() { float t = dht.readTemperature(); float h = dht.readHumidity(); if (isnan(t) || isnan(h)) { Serial.println("temp=0.0,hum=0.0"); delay(2000); return; } Serial.print("temp="); Serial.print(t); Serial.print(",hum="); Serial.println(h); delay(2000); }提到数据格式,temp=26.3,hum=58.2这种key=value格式是我比较推荐的。它有两个好处:一是人能直接看懂,用串口监视器调试的时候,扫一眼就知道数据对不对;二是手机端解析起来非常简单,按逗号分割成两段,再按等号分割就能拿到数值。如果你以后想加光照传感器、土壤湿度传感器,直接在后面追加,light=xxx就行,手机端解析逻辑改动很少。
代码里的isnan判断非常关键。DHT11 偶尔会因为时序干扰读到nan,也就是 NaN(Not a Number),这时候如果不处理,串口会输出temp=nan,hum=nan,手机端解析后显示出来就是一个奇怪的结果。我在这里把出错情况统一输出成 0.0,手机端看到 0.0 就知道传感器有问题,排查起来方便。还有一种做法是直接打印错误提示,比如temp=err,hum=err,手机端可以判断并显示“传感器故障”,体验会更好。
波特率设置成 9600,是因为 HC-05 模块的默认通信波特率就是 9600。如果你之前用 AT 指令改过模块波特率,记得把这里也改成对应的值,否则手机能连上但收到的全是乱码。
2.4 先用串口监视器验证,别急着连蓝牙
很多人第一次做这个项目,上来就把蓝牙模块、传感器、Arduino 全接好,然后打开 App 发现没数据,整个人就懵了。我自己的经验是分步验证,先把 Arduino 这部分跑通,再进入蓝牙联调。
具体做法:拔掉 HC-05 模块,只保留 DHT11 和 Arduino,用 USB 线把 Arduino 连到电脑,打开 Arduino IDE 的串口监视器,波特率选 9600。正常情况下,每两秒会刷新一行temp=xx.x,hum=xx.x。如果能看到这个输出,说明传感器、接线和代码都没问题,后面接上蓝牙只是一个“数据搬移”的过程。如果看不到,先查 DHT11 接线、库是否正确,或者用万用表测一下 VCC 和 GND 之间的电压。
这块为什么重要?因为如果一开始就把所有模块堆在一起,一旦没数据,你根本分不清是传感器的问题、串口的问题、蓝牙的问题还是 App 的问题。分步验证能帮你把变量隔离开,这也是做硬件项目最实用的排障策略。
3. App Inventor 手机端:从零搭一个蓝牙接收显示界面
3.1 界面组件怎么摆
打开 App Inventor 网站,新建项目,把项目名命名成BleTempHum或者你自己喜欢的名字。接下来是组件设计,不需要花里胡哨,先把功能跑通,再谈美化。
我的布局是:
- 屏幕标题设为
蓝牙温湿度计 - 一个垂直布局,里面依次放三个标签:
LabelStatus,显示连接状态,初始文本是未连接LabelTemp,显示温度,初始文本温度:--LabelHum,显示湿度,初始文本湿度:--
- 一个按钮
ButtonConnect,文本是连接HC-05 - 一个列表选择器
ListPickerDevices,用来手动选择蓝牙设备,文本可以是选择蓝牙设备 - 两个非可见组件:
BluetoothClient1,在 Connectivity 分类下Clock1,把TimerInterval设为 500,TimerEnabled设为 true
时钟组件的意义很多人不理解。蓝牙串口是持续的数据流,App 需要一个机制周期性去读取缓冲区里的数据。App Inventor 不像原生开发有回调函数,最直接的做法就是用一个时钟定时器,每隔 500 毫秒去读一次蓝牙数据。500 毫秒是我试下来比较舒服的值,刷新感觉不到滞后,又不会让手机太费电。如果你觉得数字跳动太快,改成 1000 毫秒也没问题。
3.2 自动连接逻辑:找到 HC-05 并连接
自动连接是整个项目里最有“智能感”的部分。App 打开后,不应该让用户自己去设置里面找蓝牙,而是自动发现已经配对过的 HC-05 并连接。
我的实现思路是:
- 屏幕初始化时,读取手机蓝牙的已配对设备列表,用
BluetoothClient1.BluetoothAddressesAndNames拿到一个列表,里面每个元素是一个“地址 名称”格式的字符串。 - 遍历这个列表,查找名称里包含
HC-05的项。 - 如果找到了,就把那一项按空格拆开,把蓝牙地址取出来,调用
BluetoothClient1.Connect(address)。 - 连接成功后将
LabelStatus改成已连接,否则改成连接失败。 - 列表里没有 HC-05 时,提示用户先去系统蓝牙设置里配对,或者让用户通过列表选择器手动选择。
这个逻辑在 App Inventor 里就是用几个循环和条件判断块拼出来。App Inventor 的列表遍历块比较直白,把BluetoothAddressesAndNames赋值给一个全局变量,然后用for each item in list去判断,如果列表项里包含HC-05,就用split at space取出第一段地址,然后连接。第一次做这个逻辑的时候,容易忽略数据项里可能会有多个空格,所以拆分的时候注意只取前两段。
如果你想让连接更保险,可以采用“固定地址”方案:先把 HC-05 和 Android 手机配对好,然后到手机蓝牙设置里查看 HC-05 的 MAC 地址,再把这个地址直接写死在 App 的变量里。这样屏幕初始化后不需要扫描列表,直接按地址连接。优点是逻辑简单,连接速度快;缺点是换了手机或换了 HC-05 模块后要改代码重新打包。
3.3 数据接收与解析:处理半包和脏数据
蓝牙数据接收部分,我用时钟定时器不断读取BluetoothClient1.ReceiveText。这个组件方法会返回当前蓝牙缓冲区中的内容。如果没读到数据,返回的是空字符串。
但这里有个很常见的坑:你读取的时候,Arduino 发过来的数据可能不是完整的一行。串口数据是字节流,手机可能在程序发了半行之后就去读了。比如 Arduino 发的是temp=26.3,hum=58.2\n,而 App 第一次读到的可能是temp=26.3,hum=5,第二次读到8.2\n。如果直接把读到的内容拿来解析,就会因为数据不完整而失败。
解决办法是维护一个“接收缓冲区”变量。每次时钟触发时,把读到的字符串追加到这个缓冲区变量后面,然后检查缓冲区里是否包含换行符\n。如果包含,就把换行符之前的一整行取出来解析,解析完后把这一行从缓冲区里删掉。这个思路和单片机上处理串口分包粘包的思路一模一样,本质上是一个队列。
解析部分也很简单。拿到一行完整数据,比如temp=26.3,hum=58.2,先按逗号,分割成两段,分别得到temp=26.3和hum=58.2,再对每一段按等号=分割,取第二个元素转换成数字,最后更新到对应的标签里。
我第一次做的时候没搞缓冲区,直接用标签显示原始文本,结果经常看到“8.2”或者“26.3,hum”这种残缺内容,一度以为蓝牙传输有问题。后来意识到是读数据的时机问题,加上缓冲区后就再也没出现过。这也是整个 App 端最值得学的一个细节。
3.4 打包安装和高版本安卓权限问题
App Inventor 做好之后,点Build | Android App (.apk),生成一个安装包。你可以用手机浏览器扫二维码直接下载安装,也可以把 APK 文件传给别人安装。
安装后不要急着打开,先确认手机蓝牙已开启,以及 HC-05 模块已经和手机配对成功。Android 系统从 6.0 开始对蓝牙相关权限管理得比较严格,到了 Android 12 以后,扫描和连接附近设备还需要“附近设备”权限。App Inventor 打包出来的 APK 不一定总是能自动弹出权限申请,所以如果 App 无法搜索或连接蓝牙,第一件事是去手机“设置-应用-权限管理”里,把这个 App 的位置权限、附近设备权限、蓝牙权限都手动打开。
这里多说一句:如果你手头有旧 Android 手机,用旧手机做演示会省心很多。并不是说高版本不能做,只是权限弹窗和一些兼容性问题会让你觉得“明明代码没问题,为什么就是连不上”。做课程设计或者给老师演示的时候,稳定比先进更重要。
4. 联调过程与常见问题速查
4.1 一次完整的联调流程
我每次做这类项目,都会严格按下面的步骤走一遍,基本不翻车。
第一步,去掉蓝牙模块,只用 USB 串口监视器确认 DHT11 有数据输出。然后关掉串口监视器,拔掉 USB 线,给 Arduino 改用外部 5V 电源供电,或者直接插充电宝的 USB 口。
第二步,接上 HC-05,注意 TX/RX 交叉连接。给 HC-05 上电后,观察模块上的 LED 指示灯。HC-05 有两种灯的状态:一种是不停快速闪烁,表示等待配对;另一种是慢闪,大概两秒闪一次,表示已经和手机建立连接。如果你看到慢闪,说明手机已经连上模块,那问题大概率出在 App 端。
第三步,用 Android 手机的系统蓝牙搜索 HC-05,输入默认密码1234配对。注意 HC-05 的默认配对密码是 1234,但有些商家会改成 0000。如果 1234 不对,试试 0000,还不行就查一下卖家说明。
第四步,打开 App,点击连接按钮或让 App 自动扫描 HC-05。连接成功后,状态标签会变成“已连接”,两秒内温度湿度标签应该开始刷新。
第五步,如果数据没有刷新,打开 Arduino 串口监视器日志或检查手机端是否选了错误的设备,再对照后面的排查表。
4.2 高频问题:HC-05 连不上手机
HC-05 连不上手机是最多人在评论区问的问题,我总结一下常见原因。
第一是供电问题。HC-05 模块对供电电流比较敏感,用电脑 USB 口供电经常因为电流波动导致连接不稳定,表现为配对成功后马上掉线,或者 LED 灯快闪但手机搜不到。解决方案是改用独立的 5V USB 充电器,或者换一个质量好一点的电源口。
第二是模块进入异常状态。有些 HC-05 模块之前可能被改过波特率、改过设备名,甚至进入了 AT 指令模式。你可以试着按住模块上的按键再上电,让模块进入 AT 指令模式,然后用 USB 转 TTL 模块发送AT+ORGL恢复出厂设置。不过这个过程对新手来说有点绕,如果模块是从商家买的,先问卖家要一份 AT 指令手册。
第三是手机之前配对过旧的设备记录,导致模块和手机之间的配对信息冲突。遇到这种情况,去手机蓝牙设置里把 HC-05 的配对记录删掉,关掉蓝牙再打开,重新搜索配对一次再看。
第四是 App 连接失败但系统蓝牙能连。这种基本是 App 没有权限或者选择的设备地址不对。手动点一下列表选择器,确认选中的设备 MAC 地址和 HC-05 一致。
4.3 数据乱码或显示 NaN,先查这三件事
乱码和NaN是两个不同的问题,但经常被混在一起说。
乱码的根源绝大多数是波特率不一致。Arduino 端Serial.begin(9600)是 9600,而 HC-05 模块如果被改成 4800 或 115200,那手机上收到的就是一堆看不懂的字符。解决办法就是把两边统一到同一个波特率。可以先用 AT 指令查 HC-05 的当前波特率,再改 Arduino 代码,或者用AT+UART=9600,0,0把模块重置成 9600。
显示NaN则是传感器读数的问题。最直接的原因是 DHT11 读到了无效数据。常见情况有三个:接线松动,数据引脚接触不良;采样间隔小于 1 秒,违反 DHT11 的时序要求;供电不稳导致信号质量差。我建议在代码里把采样间隔设置为 2000 毫秒,并且做好isnan判断,这样即使读错了也不会让手机显示乱码。不要在这件事上省时间,既然要上手机显示,Arduino 端把数据格式做干净是第一位的。
4.4 快速排查速查表
下面这张表覆盖了我遇到过的绝大多数问题:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 手机搜索不到HC-05 | 蓝牙模块未上电/损坏/距离太远 | 检查LED指示灯、更换供电、把模块靠近手机 |
| 能配对但App连不上 | App缺少权限/选错设备 | 手动授权附近设备权限、重新选择设备 |
| 已连接但收不到数据 | TX/RX接反/Arduino没运行/波特率不匹配 | 交叉检查接线、用USB串口先验证Arduino输出 |
| 已连接但全是乱码 | 波特率不一致 | 将Arduino和HC-05统一为9600 |
| 数据偶尔显示0.0 | DHT11读取失败/接线松动 | 检查传感器接线,增大采样间隔 |
| 连接后马上断开 | 供电电流不足 | 换独立5V电源,避免电脑USB口供电 |
| App闪退 | APK与系统不兼容 | 更换Android版本测试,或重新打包 |
每次联调出问题,先看现象表格对应到哪一行,再按行排查,不要一上来就怀疑模块坏了。调试硬件项目最忌讳的是“病急乱投医”。
4.5 几个让项目更完整的小扩展
这个项目跑通之后,可以继续往后面加东西。最简单的扩展是给温湿度显示加上时间戳,让标签显示“12:05:30 温度:26.3℃”。在 App Inventor 里可以用时钟组件读取系统时间,然后拼接到文本里。
再往上走,可以做成“异常提醒”。比如温度超过某个阈值时,手机弹窗提示。App Inventor 里有通知组件,在解析完数据后加一个条件判断,温度大于设定值就调用弹窗方法。这样它就不只是个温湿度计,而是一个简易的环境报警器。
如果你是做比赛或课程设计,还可以让 App 发送控制指令到 Arduino,比如做一个按钮,按下后手机发送led=1,Arduino 端解析串口接收到的字符串,并控制继电器或 LED 的开关。这样就变成了一个双向通信项目,整个演示效果会提升一个档次。其实数据链路还是一样的,只是把数据方向从“Arduino 到手机”变成了“手机到 Arduino”。
另外,如果你觉得 HC-05 模块太老,也可以试试基于 ESP32 的蓝牙或 WiFi 方案,但在 App Inventor 里处理 BLE 会比经典蓝牙复杂。建议先把这个项目吃透,理解了串口和蓝牙透传的本质,再升级到 ESP32 就不会觉得难了。
整个做下来,我最想强调的还是那句话:把数据格式和采样时序提前设计好,能省掉后面 80% 的排障时间。DHT11 的精度有限,追求准确数据可以换 DHT22 或 SHT30,但作为蓝牙通信练手,HC-05 这条链路足够经典。最后再分享一个小技巧:如果你准备在教室里演示,提前把 HC-05 和手机配对好,并让 Arduino 用充电宝供电,现场打开 App 就能自动连上,五分钟内就能看到屏幕上温度湿度开始刷新,效果非常稳。