低功耗蓝牙初探

低功耗蓝牙

蓝牙官网

谷歌BLE引文原文:Bluetooth Low Energy

【原创】Android 5.0BLE低功耗蓝牙从设备应用

获取BluetoothAdapter

   //Android 4.3引入BluetoothManager概念
   final BluetoothManager bluetoothManager =
           (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
   mBluetoothAdapter = bluetoothManager.getAdapter();

检查是否支持蓝牙开启了蓝牙

 //确保蓝牙在设备上启用,如果不是这样,显示一个对话框要求用户开启蓝牙
 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
     Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
     startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
  }

检查是否支持外围设备

(5.0才开始支持,不保证所有大于5.0的设备都支持)

  if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
      BluetoothLeAdvertiser mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
      if (mBluetoothLeAdvertiser != null) {
          Toast.makeText(this, "support peripheral", Toast.LENGTH_SHORT).show();
          break;
      }
  }
  Toast.makeText(this, "the device not support peripheral", Toast.LENGTH_SHORT).show();

扫描周围低功耗设备

  private static boolean mScanning;
     private static Handler mHandler = new Handler();
     // 10秒后停止扫描
     private static final long SCAN_PERIOD = 10000;

     @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
     public static void scanLeDevice(final boolean enable) {
         if (enable) {
             // Stops scanning after a pre-defined scan period.
             mHandler.postDelayed(new Runnable() {

                 @Override
                 public void run() {
                     mScanning = false;
                     mBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
                 }
             }, SCAN_PERIOD);

             mScanning = true;
             ScanSettings.Builder scanSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED);
             mBluetoothAdapter.getBluetoothLeScanner().startScan(null, scanSettings.build(), mScanCallback);
         } else {
             mScanning = false;
             mBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
         }
     }


    // 设备扫描回调
    private static List<BluetoothDevice> devices = new ArrayList<>();

       @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
       private static ScanCallback mScanCallback = new ScanCallback() {

           @Override
           public void onScanResult(int callbackType, ScanResult result) {
               super.onScanResult(callbackType, result);
               BluetoothDevice device = result.getDevice();
               if (!devices.contains(device)) {  //判断是否已经添加
                   devices.add(device);
                   LogUtil.d(result.getDevice().toString() + "  rssi = " + result.getRssi());
               }
           }

           @Override
           public void onBatchScanResults(List<ScanResult> results) {
               super.onBatchScanResults(results);
               LogUtil.d("onBatchScanResults = " + results.size());
           }

           @Override
           public void onScanFailed(int errorCode) {
               super.onScanFailed(errorCode);
               LogUtil.d("onScanFailed = " + errorCode);
           }
       };

注意:只能扫描BLE设备或者扫描传统蓝牙设备,不能同时扫描BLE和传统蓝牙设备。

与设备连接

更确切的讲是与设备上的GATT服务连接

“`
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void connectBle(String address) {
BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(address);
remoteDevice.connectGatt(mContext, false, bluetoothGattCallback);
}

 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 private static BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
     @Override
     public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
         super.onConnectionStateChange(gatt, status, newState);
         LogUtil.d(" onConnectionStateChange()  called with: gatt = [" + gatt + "], status = [" + status + "], newState = [" + newState + "]");
         if (newState == BluetoothProfile.STATE_CONNECTED) {
             gatt.discoverServices();//连接成功,开始搜索服务,一定要调用此方法,否则获取不到服务
         }
     }

     @Override
     public void onServicesDiscovered(BluetoothGatt gatt, int status) {
         super.onServicesDiscovered(gatt, status);
         LogUtil.d(" onServicesDiscovered() called with: gatt = [" + gatt + "], status = [" + status + "]");
         List<BluetoothGattService> services = gatt.getServices();
         for (BluetoothGattService service : services) {
             LogUtil.d("service = " + service.getUuid());
             List<BluetoothGattService> includedServices = service.getIncludedServices();
             for (BluetoothGattService includedService : includedServices) {
                 LogUtil.d("       includedService = " + includedService.getUuid());
             }

             List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
             for (BluetoothGattCharacteristic characteristic : characteristics) {
                 LogUtil.d("       -characteristic = " + characteristic.getUuid() + "  Value()" + characteristic.getValue() + "  writhType = " + characteristic.getWriteType());
             }
         }
     }

     @Override
     public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
         super.onCharacteristicRead(gatt, characteristic, status);
         LogUtil.d(" onCharacteristicRead");
     }

     @Override
     public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
         super.onCharacteristicWrite(gatt, characteristic, status);
         LogUtil.d(" onCharacteristicWrite");
     }

     @Override
     public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
         super.onCharacteristicChanged(gatt, characteristic);
         LogUtil.d("onCharacteristicChanged");
     }

     @Override
     public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
         super.onDescriptorRead(gatt, descriptor, status);
         LogUtil.d("onDescriptorRead");
     }

     @Override
     public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
         super.onDescriptorWrite(gatt, descriptor, status);
         LogUtil.d("onDescriptorWrite");
     }

     @Override
     public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
         super.onReliableWriteCompleted(gatt, status);
         LogUtil.d("onReliableWriteCompleted");
     }

     @Override
     public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
         super.onReadRemoteRssi(gatt, rssi, status);
         LogUtil.d("onReadRemoteRssi");
     }

     @Override
     public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
         super.onMtuChanged(gatt, mtu, status);
         LogUtil.d("onMtuChanged");
     }
 };

“`

//字符串转byte
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }



 // 转化字符串为十六进制编码
    public static String toHexString(String s) {
        String str = "";
        for (int i = 0; i < s.length(); i++) {
            int ch = (int) s.charAt(i);
            String s4 = Integer.toHexString(ch);
            str = str + s4;
        }
        return str;
    }
// 转化十六进制编码为字符串
    public static String hexToString(String s) {
        byte[] baKeyword = new byte[s.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(
                        i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            s = new String(baKeyword, "utf-8");// UTF-16le:Not
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return s;
    }

低功耗蓝牙Android 4.3版本以上支持 Api》18

  • Service : 服务
  • Characteristic : 特征

1 Profile,可以理解为一种规范,一种通信协议,profile存在于从机中。SIG规定了一些profile,如心率计,防丢器,HID OVER GATT等等。每个Profile中都包含多个Service。每个Service代表从机的一个能力。

2、Service可以理解为一个服务,在BLE从机中,可以有多个服务,譬如系统电量信息服务,系统信息服务,每个Service又包含多个Characteristic。每个具体的Characteristic值才是BLE通信的主体。比如当前电量是80%,会通过电量的Characteristic特征值存在从机的Profile里面,这样,主机就可以通过这个Characteristic值获得从机的80%电量值。

3、Characteristic BLE主从机通信均通过Characteristic实现。 可以理解为一个标签,通过这个标签可以获取或写入想要的内容。

4、UUID 唯一识别码。上述Service 和 Characteristic 均需要通过一个UUID来识别。UUID为128,但是在BLE中,UUID通常用16位,也就是两个字节来替代。16位UUID和128位UUID可以相互转换,具体如何实现需参考SIG文档。

综上,每个从机均由一个或若干个profile构成,不管是simpleprofile还是防丢器Profile等,而每个profile又由一些列Service组成,每个Service包含若干个Characteristic。 主机和从机之间的通信均是由 Characteristic实现。

蓝牙低能耗技术采用可变连接时间间隔,这个间隔根据具体应用可以设置为几毫秒到几秒不等。另外,因为BLE技术采用非常快速的连接方式,因此平时可以处于“非连接”状态(节省能源),此时链路两端相互间只是知晓对方,只有在必要时才开启链路,然后在尽可能短的时间内关闭链路。

每个蓝牙4.0的设备都是通过服务和特征来展示自己的,一个设备必然包含一个或多个服务,每个服务下面又包含若干个特征。特征是与外界交互的最小单位。比如说,一台蓝牙4.0设备,用特征A来描述自己的出厂信息,用特征B来与收发数据等。

服务和特征都是用UUID来唯一标识的,UUID的概念如果不清楚请自行google,国际蓝牙组织为一些很典型的设备(比如测量心跳和血压的设备)规定了标准的service UUID(特征的UUID比较多,这里就不列举了),如下:

 蓝牙4.0分为标准蓝牙和低功耗蓝牙(BLE),标准蓝牙就是手机上用的那种,低功能耗蓝牙由于其具有最大化的待机时间、快速连接和低峰值的发送和接收特性,被广泛用于智能手表、智能手环等可穿戴设备上。在安卓4.3之前,安卓平台上的BLE开发相当难搞,好在谷歌在4.3之后发布了官方的API。在安卓5.0之后又引入了新的API,原来的API已经被废弃。在新的系统里采用旧API开发的APP仍可使用,但采用新API开发的APP只能在LOLLIPOP即安卓5.0及其以后的版本使用。

  • Android从4.3(API 18)才支持BLE,只支持作为中心设备 (Central) 模式,这就意味着 Android 设备只能主动扫描和链接其他外围设备 (Peripheral)(两个同是中心设备或者两个同是外围设备是无法连接的)。从Android 5.0(API Level 21)开始两种模式都支持(绝大部分)
  • 官方的Demo在6.0设备上会有动态权限问题 java.lang.SecurityException: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to get scan results
    动态申请或者把target降到23以内
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值