一、前言
- 最近用NRF52832蓝牙SOC芯片做了一个车用测速的板子,由于目前还没有安卓编程的基础,不会做通过蓝牙控制的APP,后来想到在学校的时候使用App inventor(积木式编程)做了一个蓝牙APP来控制小车,想着这里也可以做来搞搞,可没想到蓝牙一直连接报错507,查阅网上不少资料才知道,App inventor本身的蓝牙组件只支持蓝牙2.0和3.0,但NRF52832是蓝牙5.0,所以没法用。那么需要安装BLE扩展组件,才能使用,而且在浏览多方资料后我发现,App inventor发展至今,除了蓝牙组件,还有其他程序员开发的MQTT扩展组件,这意味着对于没有安卓编程经验的电子工程师来说,可以完全制作出不需要华丽界面,主要使用蓝牙和WiFi的APP来控制单片机,值得研究一下!
- 蓝牙BLE扩展组件:https://iot.appinventor.mit.edu/#/bluetoothle/bluetoothleintro
- App inventor学习的论坛:https://mc.dfrobot.com.cn/forum-212-1.html
- App inventor在线编程:http://app.gzjkw.net/login/
二、使用
- 导入BLE组件
- 设备通常作为从机,所以我们需要制作出一个主机扫描APP,因此这里我们采用双屏幕设计,一个主界面作为控制,另一个界面作为扫描蓝牙设备页面,在该扫描页面进行选择设备,并将选择到的设备利用屏幕返回值操作,返回给主界面进行连接。
- 注意事项1:
- 注意事项2:由于BLE组件的设计已经很完善,即使蓝牙没有打开,或者没有配对,APP也能正常提示打开蓝牙和配对,所以在逻辑设计中尽管用组件就行了,不需要过多的逻辑判断。
- 实例显示
- 上述蓝牙已经连接成功,接下来需要交换数据,蓝牙数据主要通过服务和特征来交换,一个服务可以有多个特征,那怎么知道有什么服务和特征,其实是有一个蓝牙组织联盟规范了一个蓝牙设备应该包含的服务和特征,比如获取设备名,心率服务都是标准制定的服务,当然,厂商也可以自定义服务,比如我们常用的蓝牙串口模块,就是自定义服务。因此需要用到UUID来区分这么多的服务和特征,标准服务的UUID是固定的,自定义服务UUID由厂商制定,想要交换蓝牙数据,必须要知道对应设备的服务和特征的UUID,这里使用nRF Connec软件来获取设备的UUID。
4. 读写操作示例
5. 最后再说一下多字节数据接收和发送,通常蓝牙交换数据不会是单个字节数据发送,而是构建成一帧数据包(多个字节组成),在App inventor上编程较为麻烦,但总体上来说,不管什么数据包格式,都是由一个一个的字节组成,本质上就是一个字节数组格式,那么可以利用BLE的组件的WriteBytes
和RegisterForBytes
等方法来发送或接收处理字节数组,然后再来组合出对应的变量数据。
- 注意事项:
- APP需要赋予所有权限,包括位置和蓝牙权限。
- 以上例程使用了我的蓝牙设备UUID,如果是接入其他蓝牙设备,需要修改例程的UUID
- 蓝牙组件提供了获取UUID的函数,可以设计成获取任意蓝牙设备的UUID。
- 很多同学都说闪退,可以参考下面的方法查找以下问题
- 使用更新版本的APP inventor来完成操作:
三、修订
APP需要打开定位,并授予所有权限!!!
- APP inventor的写入函数如果使用
WriteCharacteristic
方法无法写入数据时,请尝试使用WriteCharacteristicWithResponse
方法,该方法表示为写入应答,拥有write权限的特征基本使用此方法写入。 很多友友使用Arduino进行开发,遇到无法连接蓝牙,或者连接上又断开的问题,下面我提供一个例子给大家参考参考(ESP32C3已测试可用)。
#include <ArduinoBLE.h>
#define BLE_LINK_WAIT_TIMEOUT 3000
uint16_t BLE_LINK_WAIT=BLE_LINK_WAIT_TIMEOUT; //蓝牙连接需要等待的时间,需要等待3S
BLEService ledService("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); //创建服务UUID
BLEByteCharacteristic RXCharacteristic("6e400002-b5a3-f393-e0a9-e50e24dcca9e", BLERead | BLEWrite); //蓝牙接收特征UUID
BLEByteCharacteristic TXCharacteristic("6e400003-b5a3-f393-e0a9-e50e24dcca9e", BLERead | BLENotify); //蓝牙发送特征UUID
const int ledPin = LED_BUILTIN;
void setup() {
Serial.begin(9600);
while (!Serial);
pinMode(ledPin, OUTPUT);
if (!BLE.begin()) {
Serial.println("starting Bluetooth® Low Energy module failed!");
while (1);
}
BLE.setLocalName("BLE_TEST"); //蓝牙名称
ledService.addCharacteristic(TXCharacteristic); //将特征添加到服务下
ledService.addCharacteristic(RXCharacteristic);
RXCharacteristic.setEventHandler(BLEWritten, RXCharacteristicWritten); //注册接收事件,当蓝牙收到数据就会触发RXCharacteristicWritten函数
BLE.addService(ledService); //添加服务
TXCharacteristic.writeValue(0); //初始化特征值
RXCharacteristic.writeValue(0);
BLE.setAdvertisedService(ledService); //将上面配置的名字/服务/特征等等设置到广播中,以便被搜索到,注意这条语句必须最后设置,否则有可能广播时不广播服务UUID
BLE.advertise(); //开启广播
Serial.println("BLE START!!!");
}
uint16_t i=0;
void loop(){
BLEDevice central = BLE.central();
if (central)
{
while(BLE_LINK_WAIT>0) //在这里需要等待,因为下面的操作可能需要耗费大量时间,导致蓝牙可能无法连接,因此先等待连接成功再执行耗时的处理
{ //这跟arduino底层相关,事件的触发会被delay阻塞
BLE_LINK_WAIT--; //所以在loop函数中,尽量将你想要的操作设计得快速
BLE.poll();
delay(1);
return;
}
Serial.print("Connected to central: ");
Serial.println(central.address());
while (central.connected())
{
//delay(5000); //比如这里消耗大量时间,前面蓝牙不等待的话很可能连接失败
delay(500);
TXCharacteristic.writeValue(i++); //每隔500ms主动发送数据
}
// when the central disconnects, turn off the LED:
Serial.print("Disconnected from central: ");
Serial.println(central.address());
BLE_LINK_WAIT=BLE_LINK_WAIT_TIMEOUT; //断开连接后需要恢复等待计时
}
}
//当蓝牙收到数据就会执行这里
void RXCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic)
{
Serial.print("Characteristic event, written: ");
Serial.println(RXCharacteristic.value()); //串口打印接收到的数据
if (RXCharacteristic.value() == '1') //这里用的字符1(0x31)来控制指示灯
{
digitalWrite(ledPin, HIGH);ar
}
else
{
digitalWrite(ledPin, LOW);
}
}
- 新的APP inventor以及apk,Arduino的例子:https://pan.baidu.com/s/1c_K20Zgi2p0teneUNMgCVw?pwd=pi7x