物联网

在iOS和iPadOS、Android以及Windows中开发基于Wi-Fi、BLE、MQTT等的物联网技术。
万物可以互联,万物也必须互联。

SWARM Bluetooth Low Energy 多设备低功耗蓝牙技术

低功耗蓝牙Bluetooth Low Energy )技术被广泛地使用在智能手表、运动手环、健康监测、共享单车、智能家电等设备的通讯中,使智能设备轻松地与手机、平板和电脑等交换数据信息。

公元 935-985 年间的丹麦和挪威的国王 Harald Blatand 非常爱吃蓝莓,因此牙齿被染成蓝色。 Harald Blatand 国王骁勇善战,统治丹麦期间,持续对外征战,统一了今天的挪威、瑞典和丹麦广大北欧地区。早年,他曾是北欧海盗精神的发扬者,而当时北欧地区的主要信仰是奥丁神( Odin ),即众神之王
蓝牙技术联盟 SIG(Special Interest Group)行业协会,用这个似乎古怪的名字来体现和映衬 SIG 希望统一无线技术领域的雄心壮志。
蓝牙的图标取自 Harald Blatand 国王名字的两个首字母 HB 的古北欧字母的结合。

与经典蓝牙相比较,低功耗蓝牙:

  • 功耗低,使用纽扣电池就可运行数月至数年
  • 小体积、低成本
  • 与现有的大部分手机、平板电脑和计算机兼容
  • 2.4GHz
  • 最大通讯距离100米
  • 通讯速度快,发送简单的数据,不用于传送语音、图像等大量数据
其实低功耗蓝牙技术是新蓝牙( 4.0 及以后)技术的一部分。目前,我们使用的手机中的蓝牙都是双模方式,即同时具备经典蓝牙和低功耗蓝牙;运动手表、健康监测设备等都是单一的低功耗蓝牙。
最新的蓝牙( 5.1 )技术,将具备厘米级的定位功能。


多设备网络结构( Swarm Network Topology )
  • 中心设备 Central:手机、平板或电脑
  • 外围设备 Peripheral:机器人模型、智能手表、运动手环、健康监测设备、共享单车、智能家电等
低功耗蓝牙的连接都是建立在 GATT ( Generic Attribute Profile )协议之上。
UUID ( Universally Unique IDentifier ) 是一个采用128位的2进制数字表示的唯一识别码,在蓝牙通讯中,用来标识不同的服务和特征值等。


Central in Android with Java to control BLE swarmusing android.bluetooth

1. 定义变量
Code in BluetoothLeActivity:
BluetoothManager mBluetoothManager = getSystemService( Context.BLUETOOTH_SERVICE );

BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter( );

BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback(){};

2. 搜索 BLE 设备
Code in BluetoothLeActivity:
// 开始扫描
mBluetoothAdapter.startLeScan( mLeScanCallback );

// 发现设备后自动触发回调函数
mLeScanCallback. onLeScan  ( BluetoothDevice device, int rssi , byte[] scanRecord )  {
         // 存储设备名称、物理地址、信号强度等信息
         mName[] = device.getName();
         mAddress[] = device.getAddress();
         mRSSI[] = rssi;
}

3. 绑定 Android Service ( BluetoothLeActivity ) 和 Android Activity ( BluetoothLeService )
Code in BluetoothLeActivity:
BluetoothLeService mBluetoothLeService;
ServiceConnection mServiceConnection =
new ServiceConnection() {};
Intent mGattServiceIntent =
new Intent( this, mBluetoothLeService.class );
bindService( mGattServiceIntent, mServiceConnection, BIN_AUTO_CREATE );
// 自动触发
mServiceConnection.onServiceConnected   ( ComponentName componentName, IBinder service ) { 
        mBluetoothLeService = service.getService();
        mBluetoothLeService.initialize();
}
Code in BluetoothLeService:
initialize() {
  BluetoothManager nBluetoothManager =
  getSystemService   
  ( Context.BLUETOOTH_SERVICE );
  BluetoothAdapter nBluetoothAdapter =
  nBluetoothManager.getAdapter();
}

// 自动触发
Ibinder nIBinder = new LocalBinder(); 
onBind( Intent intent ) {
	return nIBinder; 
}
LocalBinder(  ) { 
   	BluetoothLeService getService(){
	  	return BluetoothLeServie.this
	}
}

4. 连接
Code in BluetoothLeActivity:
// 连接第1部设备
mBluetoothLeService.connect( mAddress[0], 1);
// 连接第2部设备
mBluetoothLeService.connect( mAddress[1], 2);
…
Code in BluetoothLeService:
// 每部设备都有自己的 BLE GATT 和 BLE Service
BluetoothGatt[] nBluetoothGatt;

BluetoothGattCallback nBluetoothGattCallback = BluetoothGattCallback(){};


connect( String address, int n ) {  
    nAddress[n-1] = address;    
    BluetoothDevice device = 
    nBluetoothAdapter.getRemoteDevice( address );
    nBluetoothGatt[n-1] = 
    device.connectGatt( Context this, Boolean 
    autoConnect false, BluetoothGattCallback 
    nBluetoothGattCallback);
}


// 自动触发
nBluetoothGattCallback() {
onConnectionStateChange( BluetoothGatt gatt, int status, int newState ) { 
  if( status == BluetoothGatt.GATT_SUCCESS ) {
    if ( newState == STATE_CONNECTED ) {
      if( gatt.getDevice().getAddress() == naddress[0] )   
      { nBluetoothGatt[0]. discoverServices(); }
      if( gatt.getDevice().getAddress() == naddress[1] )   
      { nBluetoothGatt[1]. discoverServices(); }
      …
    }
   }
  }
}

5. 搜索 BLE Service 及 BLE Characteristics
Code in BluetoothLeActivity:
// 自动搜索 BLE Service 和 BLE Characteristics
…
Code in BluetoothLeService:
// 每个 BLE Service 都有自己的 BLE Characteristics
BluetoothGattCharacteristic[] nNotifyCharacteristic;
BluetoothGattCharacteristic[] nWriteCharacteristic;
BluetoothGattCharacteristic[] nReadCharacteristic;


// 自动触发
nBluetoothGattCallback() {
  onServicesDiscovered( BluetoothGatt gatt, int status ) { 
     if ( status == BluetoothGatt.GATT_SUCCESS )  {
      BluetoothGattService service =       
      BluetoothGatt.getService( UUID.fromString(XXXX) );
       if ( service !=  null ) {  //  go on to get characteristic
          if ( gatt.getDevice().getAddress() == nAddress[0] ) {
 	 nNotifyCharacteristic[0] = service.getCharacteristic(UUID.fromString(XXXX));
	 nWriteCharacteristic[0] =   service.getCharacteristic(UUID.fromString(XXXX));
          	 nReadCharacteristic[0] =   service.getCharacteristic(UUID.fromString(XXXX));
          }
         if ( gatt.getDevice().getAddress() == nAddress[1] ) {
	 nNotifyCharacteristic[1] = service.getCharacteristic(UUID.fromString(XXXX));
	 nWriteCharacteristic[1] =   service.getCharacteristic(UUID.fromString(XXXX));
          	 nReadCharacteristic[1] =   service.getCharacteristic(UUID.fromString(XXXX)); 	
         }
       }
     }
  }
}

6. 发送数据
Code in BluetoothLeActivity:
// 向第1部设备发送数据
mBluetoothLeService.writeData( 1, data ) 
// 向第2部设备发送数据
mBluetoothLeService.writeData( 2, data ) 
…
Code in BluetoothLeService:
writeData( int n, byte[] data ) {
  nWriteCharacteristic[n].setValue( data );  
  nBluetoothGatt[n].writeCharacteristic  
  ( nWriteCharacteristic[n] );
}


// 自动触发
nBluetoothGattCallback() { 
  onCharacteristicWrite(BluetoothGatt gatt, 
  BluetoothGattCharacteristic characteristic, 
  int status) {
     if (status == BluetoothGatt.GATT_SUCCESS)   
     {  }
   }
}

7. 接收数据
Code in BluetoothLeActivity:
// 从第1部设备接收数据
mBluetoothLeService.readData ( 1, true );

// 从第2部设备接收数据
mBluetoothLeService.readData ( 2, true );

Code in BluetoothLeService:
readData( int n, Boolean state) {
    if(state) {
      setCharacteristicNotification    
      ( mNotifyCharacteristic[n-1],true );
      nBluetoothGatt[n-1].readCharacteristic 
      ( mNotifyCharacteristic[n-1] );
  }
  else {
      nBluetoothGatt[n-1].   
      setCharacteristicNotification 
      ( mNotifyCharacteristic[n-1],false );
  }
}

// 通过 Broadcasting 把接收到的数据反馈给 Android Activity 
// 自动触发
nBluetoothGattCallback() {

  // 触发情况1
  onCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
     if ( status == BluetoothGatt.GATT_SUCCESS ) {   
       if ( gatt.getDevice().getAddress() == nAddress[0] ) {
          broadcastUpdate(ACTION_DATA_AVAILABLE_1, 
          characteristic);
       }
       if ( gatt.getDevice().getAddress() == nAddress[1] )  {  
         broadcastUpdate(ACTION_DATA_AVAILABLE_2, 
         characteristic);
       }
     }
  }
  
  // 触发情况2
  onCharacteristicChanged (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
     if ( status == BluetoothGatt.GATT_SUCCESS ) {   
       if ( gatt.getDevice().getAddress() == nAddress[0] ) {
          broadcastUpdate(ACTION_DATA_AVAILABLE_1, 
          characteristic);
       }
       if ( gatt.getDevice().getAddress() == nAddress[1] )  {  
         broadcastUpdate(ACTION_DATA_AVAILABLE_2, 
         characteristic);
       }
     }
  }
  
}

// 把数据从 Android Service 传递到 Android Activity
broadcastUpdate (String action, BluetoothGattCharacteristic characteristic) {
	final Intent intent = new Intent(action);
	if ( UUID.fromString(XXXX).equals( characteristic.getUuid() ) ) {
		final byte[] data = characteristic.getValue();
		if (data != null && data.length > 0) {
			intent.putExtra(EXTRA_DATA,data);
		}
	}
	sendBroadcast(intent);
}

8. 断开连接
Code in BluetoothLeActivity:
// 断开与第1部设备的连接
mBluetoothLEService. Disconnect(1);

// 断开与第2部设备的连接
mBluetoothLEService.disconnect(2);
…

// 取消 ANDROID SERVICE ( BLUETOOTHLEACTIVITY ) 和 ANDROID ACTIVITY ( BLUETOOTHLESERVICE ) 之间的绑定
unbindService( mServiceConnection );
mBluetoothLeService = null;
Code in BluetoothLeService:
disconnect (int n) {

     nBluetoothGatt[n-1].disconnect();

}



Central in iOS and iPadOS with Swift to control BLE swarm using CoreBluetooth

1. 定义变量
// 中心设备
var mCentralManager: CBCentralManager!

// 外围设备
Var mPeripheral_1: CBPeripheral!
Var mPeripheral_2: CBPeripheral!
…

// 外围设备标识
var mUUID_1: UUID
var mUUID_2: UUID
…

// Service
Var mService_1: CBService!
Var mService_2: CBService!
…
  
// Characteristic
Var mWriteCharacteristic_1: CBCharacteristic!
Var mReadCharacteristic_1: CBCharacteristic!
Var mNotifyCharacteristic_1: CBCharacteristic!
…
Var mWriteCharacteristic_2: CBCharacteristic!
Var mReadCharacteristic_2: CBCharacteristic!
Var mNotifyCharacteristic_2: CBCharacteristic!
…

2. 搜索 BLE 设备
// 初始化中心设备
self.mCentralManager = CBCentralManager(delegate: self, queue: nil)
…


// 检查中心设备的蓝牙硬件
// 自动触发
func centralManagerDidUpdateState(_ central: CBCentralManager) {

      switch( central.state ){
       	case CBManagerState.poweredOn:
        // 开始搜索外围 BLE 设备
        self.mCentralManager.scanForPeripherals(withServices: nil, options: nil)
      }

}


// 发现外围 BLE 设备时
// 自动触发
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {

  // 外围设备的 UUID、 名称和信号强度等
  mPeripheral_1 = peripheral
  mUUID_1 = mPeripheral_1.identifier
  
  mPeripheral_2 = peripheral
  mUUID_2 = mPeripheral_2.identifier
  
  …
  
}

3. 连接
// 连接外围设备1
mCentralManager.connect(mperipheral_1, options: nil)

// 连接外围设备2
mCentralManager.connect(mperipheral_2, options: nil)

…

// 连接到外围设备时
// 自动触发
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {

   if( peripheral.identifier == mUUID_1){

   		// 外围设备1已经连接
   }

   if( peripheral.identifier == mUUID_2){

		// 外围设备2已经连接
   }
    
   …
   
}
// 检查外围设备状态

mPeripheral_1.discoverServices(nil)

mPeripheral_2.discoverServices(nil)


// 发现外围设备1的 Service 时
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { 
  if( peripheral == mPeripheral_1 ){             
    if( peripheral.services?.count != 0 )
		for service in peripheral.services! {
			if service.uuid.uuidString.contains(“XXXX"){
			  	mService_1 = service as CBService
           		// 已经发现 Servie “XXXX"
           		// 继续发现 Characteristic
				peripheral.discoverCharacteristics(nil, for: service)                  
           }                
     }
  }
}


// 发现外围设备2的 Service 时
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { 
  if( peripheral == mPeripheral_2 ){             
    if( peripheral.services?.count != 0 )
		for service in peripheral.services! {
			if service.uuid.uuidString.contains(“XXXX"){
			  	mService_2 = service as CBService
           		// 已经发现 Servie “XXXX"
           		// 继续发现 Characteristic
				peripheral.discoverCharacteristics(nil, for: service)                  
           }                
     }
  }
}
// 发现 Characteristic 时

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {

 if( peripheral == mPeripheral_1 ){
  if( service.characteristics!.count != 0 ){
   for Characterisitc in service.characteristics!{
     let mCharacteristic: CBCharacteristic = Characterisitc as CBCharacteristic
     if( mService_1 != nil ){
      if( mService_1 == service ){
       if( mCharacteristic.uuid.uuidString.contains(“xxxx") ){
         mNotifyCharacteristic_1 = mCharacteristic
       }
       if( mCharacteristic.uuid.uuidString.contains(“xxxx") ){
         mWriteCharacteristic_1 = mCharacteristic
       }
	      if( mCharacteristic.uuid.uuidString.contains(“xxxx") ){
         mReadCharacteristic_1 = mCharacteristic
       }
      }
     }
   }
  }
 }

 if( peripheral == mPeripheral_2 ){
  if( service.characteristics!.count != 0 ){
   for Characterisitc in service.characteristics!{
     let mCharacteristic: CBCharacteristic = Characterisitc as CBCharacteristic
     if( mService_2 != nil ){
      if( mService_2 == service ){
       if( mCharacteristic.uuid.uuidString.contains(“xxxx") ){
         mNotifyCharacteristic_2 = mCharacteristic
       }
       if( mCharacteristic.uuid.uuidString.contains(“xxxx") ){
         mWriteCharacteristic_2 = mCharacteristic
       }
	      if( mCharacteristic.uuid.uuidString.contains(“xxxx") ){
         mReadCharacteristic_2 = mCharacteristic
       }
      }
     }
   }
  }
 }
 
 …
 
}

4. 发送数据
// 发送数据到外围设备1

DispatchAfter(after: 0.0) {
	self.mPeripheral_1.setNotifyValue(false, for: self.mNotifyCharacteristic_1)
}
                
DispatchAfter(after: 0.2) {
	self.mPeripheral_1.writeValue(writedata, for: self.mWriteCharacteristic_1, type: CBCharacteristicWriteType.withoutResponse)
}


// 发送数据到外围设备2

DispatchAfter(after: 0.0) {
	self.mPeripheral_2.setNotifyValue(false, for: self.mNotifyCharacteristic_2)
}
                
DispatchAfter(after: 0.2) {
	self.mPeripheral_2.writeValue(writedata, for: self.mWriteCharacteristic_2, type: CBCharacteristicWriteType.withoutResponse)
}

…


5. 接收数据
// 准备从外围设备1接收数据
mPeripheral_1?.readValue(for: mReadCharacteristic_1!)

// 准备从外围设备2接收数据
mPeripheral_2?.readValue(for: mReadCharacteristic_2!)


// 当接收到数据时
// 自动触发
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
  if( peripheral.identifier == mUUID_1){
   readdata1[0] = characteristic.value![0]
   readdata1[1] = characteristic.value![1]
   readdata1[2] = characteristic.value![2]
   readdata1[3] = characteristic.value![3]
   readdata1[4] = characteristic.value![4]
   readdata1[5] = characteristic.value![5]
   … 
  }

  if( peripheral.identifier == mUUID_2){
   readdata2[0] = characteristic.value![0]
   readdata2[1] = characteristic.value![1]
   readdata2[2] = characteristic.value![2]
   readdata2[3] = characteristic.value![3]
   readdata2[4] = characteristic.value![4]
   readdata2[5] = characteristic.value![5]
    …
  }
  
  …
  
}

6. 断开连接

self.mCentralManager.cancelPeripheralConnection(self.mPeripheral_1)

self.mCentralManager.cancelPeripheralConnection(self.mPeripheral_2)

…



所有核心代码