Arduino101学习笔记(十一)—— 蓝牙BLE
一、BLE技术简介
第四代蓝牙既包括传统的蓝牙,现在标有“蓝牙经典”,和新的低功耗蓝牙(Bluetooth LE,或BLE)。低数据速率,低功耗优化。
蓝牙LE广播就像一个社区公告栏。连接到它的计算机就像是阅读公告板的社区成员一样。每一个无线电作为一个公告板或读者。如果你的收音机是一个公告板(称为蓝牙LE的说法一个外围设备)揭示数据中的所有收音机在社区看。如果你的收音机是一个读者(称为blueooth乐方面中央装置)它读任何的公告板(外围设备),它所关心的信息。
您也可以将外围设备作为客户端服务器事务中的服务器,因为它们包含了读写器所要求的信息。同样,中央设备是蓝牙世界的客户,因为他们阅读的信息可用的外设。
认为一个蓝牙勒外围设备作为一个公告板和中央设备作为一个观众。
中央设备查看服务,获取数据,然后继续。每一个事务都是快速的(几毫秒),所以多个中央设备可以从一个外设获取数据。
由外围设备提供的信息是结构化的服务,其中每一个被细分为特征。您可以将服务视为公告栏上的通知,以及这些通知的各个段落的特点。如果你是一个外围设备,你只需更新每个服务的特性,当它需要更新,不担心中央设备是否读取它们。如果你是一个中心设备,你连接到外围然后读你想要的。如果一个给定的特征是可读写的,然后外周和中枢都可以改变它。
1、通知
蓝牙规范中的一个机制,被称为通知,当数据发生改变让你知道。
当通知上的一个特性是启用的,外围设备写它,新的值自动广播出去,不需要关注中央设备。这是常用的流式数据,如加速度计或其他传感器读数。但一般中央设备可以发送一个ACK来确认包是否被接受
蓝牙的客户端-服务器结构,结合通知的特点,通常被称为publish-and-subscribe model。
2、更新特性
当外设发生显著变化的时候,应该更新特性。例如,当模拟传感器发生显著变化时,更新其特性。
正如写的一个特点,可以定时更新特性,但这样会浪费101的处理能力以及消耗能量。
3、中央和外围设备
中央设备是客户。他们从外围设备读取和写入数据。
外围设备是服务器,提供来自传感器的数据,提供的可读可写和读的特性通知。
4、服务、特点和UUIDs
一个BLE设备会提供服务,换句话说提供特性。您可以定义自己的服务,或使用标准服务。
服务是由独特的数字称为UUID来识别。标准的服务有一个16位的UUID而定制服务有一个128位的UUID。定义服务和特性的能力取决于你使用的无线电和它的固件。
5、服务设计模式
一个特征值可以高达20字节长。这是设计服务的一个关键约束。
鉴于这一限制,你应该考虑如何最好地存储关于您的传感器和执行器的数据最有效地为您的应用程序。最简单的设计模式是不同的特性来存储一个传感器或致动器每个特征值。
Characteristic | Value |
Accelerometer X | 200 |
Accelerometer Y | 134 |
Accelerometer Z | 150 |
这是最昂贵的内存条款,并且读取时间最长。但它最简单进行开发和调试。
您也可以将读数组合成一个单一的特性。 加速度计X,Y,Z 200133150
6、外围设备为中央设备服务的四个特性
阅读:要求外周发送回特性的当前值。通常用于不经常改变的特性,例如用于配置的特性、版本号等。
写:修改特性的值。通常用于那些像命令的东西,例如告诉外围设备打开或关闭电机。
指示和通知:要求外周不断发送更新的价值观的特点,而不需要中央不断要求它。
二、UUID介绍
1、定义
UUID的含义是通用唯一识别码 (Universally Unique Identifier),其目的是让分布式系统中的所有元素都有唯一识别的资讯,每个人都可以建立与其他人不冲突的UUID,并且还要保证在同一时空中所有机器都是唯一的。
2、组成
(1) 当前的时间(如果现在生成一个UUID后,过几秒又生成一个UUID,则第一个部分不同,其他相同。
(2)时钟序列
(3)全局唯一的IEEE机器识别号,如果有网卡,从网卡获取mac地址,没有就从其他地方获取。
在curie中,一个BLE外设在提供服务,进而提供特征,也可以定制自己的特征。标准服务是一个16位的UUID,而定制服务是一个128位的UUID,这个也取决于你使用的无线方式和固件。
三、一些API
1、BLEPeripheral 类库成员函数
/*************************BLEPeripheral ************************/ 功能:给BLE外围设备命名。 语法:BLEPeripheral yourBlePeripheralName 参数:无 返回值:无 /***********************************************************/ BLEPeripheral yourBlePeripheralName ; /*************************begin() ************************/ // 功能:初始化BLE所有功能。 // 语法:yourBlePeripheralName.begin() // 参数:无 // 返回值:初始化成功返回真,否则返回假。 /***********************************************************/ yourBlePeripheralName.begin() /*************************end() ************************/ // 功能:关闭所有BLE外设功能。 // 语法:yourBlePeripheralName.end() // 参数:无 // 返回值:初始化成功返回真,否则返回假。 /***********************************************************/ yourBlePeripheralName.end() /*************************setAdvertisedServiceUuid() ************************/ // 功能:设置广播服务UUID。 // 语法:yourBlePeripheralName.setAdvertisedServiceUuid(const char*advertisedServiceUuid) // 参数:advertisedServiceUuid: TBD // 返回值:无。 /************************************************************************************/ yourBlePeripheralName.setAdvertisedServiceUuid(const char*advertisedServiceUuid) /*************************setLocalName()************************/ // 功能:设置BLE外设的本地名。 // 语法:yourBlePeripheralName.setLocalName(const char* localName) // 参数:localName: the name to be set // 返回值:无。 /*********************************************************************/ yourBlePeripheralName.setLocalName(const char* localName) /*************************setDeviceName()************************/ // 功能:设置BLE外设的设备名称。 // 语法:yourBlePeripheralName.setLocalName(const char* deviceName) // 参数:localName: the name to be set // 返回值:无。 /*********************************************************************/ yourBlePeripheralName.setLocalName(const char* deviceName) /************************setAppearance()************************/ // 功能:待定。 // 语法:yourBlePeripheralName.setAppearance(unsigned int appearance) // 参数:appearance: 待定 // 返回值:无。 /*********************************************************************/ yourBlePeripheralName.setAppearance(unsigned int appearance) /************************setEventHandler()************************/ // 功能:设置回掉函数与触发事件的关系。 // 语法:yourBlePeripheralName.setEventHandler() //参数:event: the chosen matching event. It can assume one of the followingvalues: // BLEConnected // BLEDisconnected // BLEPeripheralEventLast // callback:the name of the function to call in case of match // 返回值:无。 /*********************************************************************/ yourBlePeripheralName.setEventHandler(BLEPeripheralEvent event, BLEPeripheralEventHandler callback) /************************addAttribute()***********************/ //功能:添加外围设备的属性。 //语法:yourBlePeripheralName.addAttribute(BLEAttribute attributeName) //参数:attributeName: 要添加的特征或服务的名称作为属性 // 返回值:无。 /****************************************************************/ yourBlePeripheralName.addAttribute(BLEAttribute attributeName) /************************disconnect()***********************/ //功能:断开连接。 //语法:yourBlePeripheralName.disconnect() //参数:五 //返回值:true/false /****************************************************************/ yourBlePeripheralName.disconnect() /************************central()***********************/ //功能:检查中心连接是否工作。 //语法:yourBlePeripheralName.central() //参数:五 //返回值:true/false /****************************************************************/ yourBlePeripheralName.central() /************************connected()***********************/ //功能:检查设备是否连接。 //语法:yourBlePeripheralName.connected() //参数:五 //返回值:true/false /****************************************************************/ yourBlePeripheralName.connected()
2、BLEDescriptor 类库成员函数
特征值的描述以及定义
/************************BLEDescriptor()***********************/ //功能:描述特征值的属性。 //语法:BLEDescriptor(const char* uuid, const unsigned charvalue[],unsigned char valueSize); // BLEDescriptor(constchar* uuid, const char* value); //参数: UUID: standard 16-bit characteristic UUID // properties: what remote clients will be able to getnotifications if this characteristic changes. It can // assume the followingvalues:BLERead // BLEWrite // BLENotify // uuid: UUID of descriptor // value: value data // valueLength: length of value data in bytes //返回值:无 /****************************************************************/ BLEDescriptor()
3、BLECentral 类库成员函数
BLECentral //功能:BLE中心设备通常访问外围设备的数据。 //语法:BLECentral yourBleCentralName //参数:无 //返回值:无 connected() //功能: 检查设备是否连接 //语法:yourBleCentralName.connected() //参数:/ //返回值:连接成功返回true,否则返回false address() //功能:返回中心设备的地址 //语法:yourBleCentralName.address() //参数:无 //返回值:中心设备地址 disconnect() //功能:断开连接 //语法:yourBleCentralName.disconnect() //参数:如果成功则返回true,否则返回false //返回值:无
4、BLECharacteristic 类库成员函数
BLECharacteristic //功能: 特征包至少包含两个属性:一个特征声明,其中包含有关数据的元数据,和特征值,其中包含数据本身 //特征: names // UUIDs // values // read/write/notifyproperty //下面提供众多的构造函数,可以根据计划使用这些特性。 //语法: · BLEBoolCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLECharCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLEUnsignedCharCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLEShortCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLEUnsignedShortCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLEIntCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLEUnsignedIntCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLELongCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLEUnsignedLongCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLEFloatCharacteristic yourCharacteristicName(UUID, properties, maxLen) · BLEDoubleCharacteristic yourCharacteristicName(UUID, properties, maxLen) //参数: //-UUID:标准的16bit的UUID //-Properties:如果特征值改变,远程客户端就能得到通知。 //可以采用以下值: //BLERead //BLEWrite //BLENotify
5、BLEService 类库成员函数BLEService
功能:可使用BLE服务创建由BLE设备显示的服务
语法:BLEService (const char* uuid)
参数:UUID(由BLE定义的标准16bit或128bit的)
返回值:无
四、一个Demo
1、下载一个App
nRF Master Control Panel (BLE) forAndroid and iOS
2、电路原理
将电位器中间的引脚连接到101的模拟输入I/O空A0上,使用Arduino 101的AD功能对电位器的电压值采样模拟电池电压的变化
3、Code思路
101嵌入了一个低功耗蓝牙模块,因此它是足够的板连接到计算机,并使用串行监视器读取由草图发送的消息。电位计被连接到3.3V,GND和A0至模拟电池的充电。这部分代码实现了标准的BLE电池检测功能
在setup()中,初始化13脚为输出来驱动板载LED,blePeripheral用来初始化板子的外设,如果多个板子运行这个代码,需要修改本地名,这样两者可以被区分,例如:
blePeripheral.setLocalName(“BatteryMonitorSketch”) 改为 blePeripheral.setLocalName(“BatteryMonitorSketch1”);
在主循环中,成功连接中心设备后打开LED,每200ms进行一次连接测试,如果是正常的,updateBatteryLevel被调用。当连接丢失,LED会被关闭
、 /*******************头文件******************/ #include <CurieBLE.h> /*******************宏定义******************/ #define LED_Pin 13 /*******************全局变量******************/ BLEPeripheral BlueMountain; // BLE Peripheral Device (命名板子的名字),一个对象 BLEService batteryService("180F"); // 创建一个电池服务,且UUID为(180F) // BLE Battery Level Characteristic" BLEUnsignedCharCharacteristic batteryLevelChar("2A19", //16BIT的UUID BLERead|BLENotify); //如果这个特征值改变,远程客户端会得到通知 int g_oldBatteryLevel = 0; //从A0读到的电池电量数据 long g_previousMillis = 0; //最后一次检测电池电量的时间,单位:ms void updataBatteryLevel() { /*读取A0口当先的输入电压 用来模拟电池的电量值 */ int battery = analogRead(A0); //读取A0 int batteryLevel = map(battery, 0, 1023, 0, 100); if (batteryLevel != g_oldBatteryLevel) // 如果电量改变了 { // 打印这个值 Serial.print("Battery Level % is now: "); Serial.println(batteryLevel); // 更新电池电量,保存下一个比较值 batteryLevelChar.setValue(batteryLevel); g_oldBatteryLevel = batteryLevel; } } void setup() { // put your setup code here, to run once: Serial.begin(9600); //初始化串口通信 //analogReference(DEFAULT); //配置AD pinMode(LED_Pin,OUTPUT); //配置LED为输出,蓝牙连接成功后亮 /* 给蓝牙设备设置一个本地名 这个名字会出现在广告的数据包中 并且远程设备通过这个名字连接这个设备 这个名字可以更改但可能会在通知包里被截断 */ BlueMountain.setLocalName("BatteryMonitorSketch"); //设置本地名字 BlueMountain.setAdvertisedServiceUuid(batteryService.uuid()); //添加服务的UUID BlueMountain.addAttribute(batteryService); //添加电池服务 BlueMountain.addAttribute(batteryLevelChar); //添加电池电量 batteryLevelChar.setValue(g_oldBatteryLevel); //设定初始值 /* 激活蓝牙,此时它会不断地发射BLE广告数据包, 远程BLE中心设备可收到,直到它接收到一个新的连接*/ BlueMountain.begin(); //开始服务 delay(1000); Serial.println("Bluetooth device active, waiting for connections..."); } // put your main code here, to run repeatedly: void loop() { // 检测BLE外部设备是否连接 BLECentral central = BlueMountain.central(); // 如果中心设备连接了外围设备 if( central ) { Serial.print("Connected to central: "); //在串口上打印中心设备的MAC地址 Serial.println(central.address()); // 打开13号口的LED digitalWrite(LED_Pin, HIGH); while(central.connected()) { long currentMillis = millis(); //200ms,检测一次电池电量。 if(currentMillis - g_previousMillis >= 200 ) { g_previousMillis = currentMillis; updataBatteryLevel(); } } //当连接中断,关闭13号口LED digitalWrite(LED_Pin, LOW); Serial.print("Disconnected from central: "); Serial.println(central.address()); } }