1. 为什么选择App Inventor开发BLE物联网控制APP
作为一个玩了十年硬件的工程师,我见过太多创客朋友被安卓开发劝退。去年用ESP32做智能灯控时,我也面临同样困境——硬件调试两小时,APP开发卡两周。直到重新捡起学生时代用过的App Inventor,才发现原来开发蓝牙控制APP可以像搭积木一样简单。
App Inventor是MIT开发的图形化编程工具,最大的优势是零Java基础也能开发功能完整的安卓APP。对于物联网设备控制这类功能明确的应用,它比Android Studio节省90%的开发时间。我实测用传统方法开发一个BLE灯光控制APP需要200行Java代码,而在App Inventor里只需要拖拽15个积木块。
但要注意,官方自带的蓝牙组件仅支持经典蓝牙(2.0/3.0),现在主流物联网设备用的都是**蓝牙低功耗(BLE)**协议。这就需要用到社区开发的BLE扩展组件,它完美支持蓝牙4.0及以上版本,包括常见的NRF52系列、ESP32等芯片。我最近用这个组件做了个温湿度监测器,从开发到上线只用了3个晚上。
2. 开发前的关键准备工作
2.1 硬件选型与配置
我的建议是首选ESP32-C3这类国产芯片,性价比高且文档丰富。上周帮朋友调试一个NRF52840的项目,发现其广播包格式比较特殊,初学者容易踩坑。硬件端需要确认三点:
- 蓝牙协议栈已启用GATT服务
- 自定义服务的UUID不要与标准服务冲突
- 特征值(Characteristic)的读写权限设置正确
以ESP32为例,用Arduino IDE开发时需要先安装BLE库。这里有个小技巧:在setup()函数里一定要加BLE.setMTU(247),否则安卓端一次只能发20字节,处理长数据包会很麻烦。
2.2 开发环境搭建
访问App Inventor官网(http://ai2.appinventor.mit.edu)直接用谷歌账号登录即可。重点说下BLE扩展组件的安装:
- 在项目面板点击"Extensions"
- 输入扩展链接:https://iot.appinventor.mit.edu/#/bluetoothle/bluetoothleintro
- 导入后会在组件面板看到蓝色的BLE图标
第一次使用时,建议先运行官方示例程序测试手机兼容性。我遇到过华为手机需要手动开启定位权限才能扫描设备的情况,这个坑后面会详细讲。
3. 双屏架构设计与设备扫描
3.1 界面布局技巧
采用主屏+设备列表屏的设计最稳妥。我在最新项目中是这样布局的:
- 主屏:放置控制按钮和数据展示标签
- 设备屏:包含扫描按钮、设备列表、返回按钮
关键点是利用Screen1.AnotherScreenName实现屏幕间通信。当用户在设备屏选择某个蓝牙设备后,通过close screen with value将设备MAC地址传回主屏。这里建议用TinyDB组件持久化存储设备信息,避免每次都要重新连接。
3.2 扫描逻辑优化
很多人直接照搬官方示例的扫描代码,实际使用会发现两个问题:
- 安卓10以上需要位置权限
- 持续扫描耗电严重
我的改进方案是:
// 当扫描按钮点击时 如果 未授予位置权限 则 请求权限 否则 设置 BLE1.ScanMode 为 low_latency 设置 BLE1.ScanTime 为 5000 // 只扫5秒 调用 BLE1.StartScanning 结束实测加入ScanTime参数后,APP耗电量从每小时15%降到3%。对于设备较多的环境,建议添加RSSI过滤,只显示信号强度大于-70dBm的设备。
4. BLE通信核心实现
4.1 服务与特征值发现
连接设备后第一步是发现服务,这里有个容易忽略的细节:服务发现是异步操作。很多初学者直接连着调用DiscoverServices和ReadCharacteristic,结果总是失败。正确的做法是:
// 当连接成功时 调用 BLE1.DiscoverServices // 在"ServiceDiscovered"事件处理中 如果 serviceUUID = "6E400001-B5A3..." 则 调用 BLE1.DiscoverCharacteristics(serviceUUID) 结束 // 在"CharacteristicDiscovered"事件中存储特征值UUID建议为每个特征值创建全局变量存储其UUID,我习惯用_TX和_RX后缀区分收发特征。
4.2 数据读写实战
处理多字节数据时,推荐使用ReadBytes和WriteBytes方法。上周做智能锁项目时,需要发送8字节的加密指令,代码示例:
// 发送数据 变量 命令 为 create list 遍历 从1到8 添加 随机数(0,255) 到 命令 结束 调用 BLE1.WriteBytes(serviceUUID, characteristicUUID, 命令) // 接收数据 在 "BytesReceived" 事件中 变量 数据 为 value 如果 列表长度(数据) > 0 则 标签1.文本 = 合并列表项(数据, ",") 结束对于需要可靠传输的场景,务必使用WriteWithResponse方法。我测试发现某些ESP32芯片的WriteWithoutResponse丢包率高达20%。
5. 避坑指南与性能优化
5.1 常见连接问题解决
闪退问题:90%的情况是权限未开启。必须在Screen.Initialize中添加:
如果 未授予蓝牙权限 或 未授予位置权限 则 请求权限 结束写入失败:检查特征值属性是否包含write权限。遇到过某厂商模块需要先写入0x01才能激活写入功能,这个要查硬件手册。
5.2 数据流优化技巧
当需要高频传输(如传感器数据)时,建议:
- 在硬件端启用通知功能(Notify)
- APP端设置
SetIndicate为true - 使用
RegisterForBytes替代轮询
最近做的ECG项目用这个方案,采样率从10Hz提升到了100Hz。另外建议在硬件端做简单数据打包,比如给每帧数据加0xAA头字节和校验尾字节。
最后提醒:测试时务必用release模式打包APK,debug模式的性能只有30%。我有个学生项目在debug下每秒只能发3帧,换成release后直接到30帧,这个差异非常明显。