Android 蓝牙和BLE应用开发经验参考

Android 蓝牙和BLE应用开发经验参考

传统蓝牙和BLE的区别

技术规范

经典蓝牙(2.1 &3.0)

低功耗蓝牙(4.0)

无线电频率

2.4GHz

2.4GHz

距离

10米/100米

30米

数据速率

1-3Mbps

1Mbps

应用吞吐量

0.7-2.1Mbps

0.2Mbps

发送数据的总时间

100ms

<6ms

耗电量

1

0.01至0.5

最大操作电流

<30mA

<15mA(最高运行时为15 mA)

主要用途

手机,游戏机,耳机,立体声音频流,汽车和PC等

手机,游戏机,PC,表,体育和健身,医疗保健,汽车,家用电子,自动化和工业等

 

蓝牙的应用开发参考

蓝牙基础

l  蓝牙是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。蓝牙技术最初由电信巨头爱立信公司于1994年创制,当时是作为RS232数据线的替代方案。蓝牙可连接多个设备,克服了数据同步的难题。

l  如今蓝牙由蓝牙技术联盟(Bluetooth SpecialInterest Group,简称SIG)管理

l  所有的蓝牙标准版本都支持向下兼容

l  蓝牙最新的版本号是蓝牙4.2,还有未发布的蓝牙5.0

l  一个蓝牙主设备最能多和7个蓝牙从设备进行通信

l  蓝牙的数据传输速率在1Mbps以内

l  蓝牙的理论最大通信距离是100米,传输距离越大,功耗越高,现在市面上的流行蓝牙设备的传输距离大约在30米以内,传输距离在10米左右的蓝牙设备最多。

l  蓝牙使用的频率是2.4GHZ

蓝牙应用场景

l  蓝牙应用在手机上

l  蓝牙应用在PC上,现在很多PC都带有蓝牙模块

l  蓝牙应用于其它数字设备,如数字照相机、数字摄像机等

l  篮牙技术构成的电子钱包和电子锁,这方面现在慢慢被主流的NFC所替代

l  篮牙技术在嵌入式设备上的应用如蓝牙音箱,蓝牙耳机,微波炉、洗衣机、电冰箱、空调机等

蓝牙开发基础

常用的蓝牙术语

l  扫描

l  绑定

l  配对

l  连接

蓝牙的通讯模型

蓝牙和普通的网络通信一样,是基于socket进行通讯的,采用的是典型的C/S模型

常用的蓝牙协议(Profile)

Profile名称

主要用途

SPP即Serial Port Profile串口通讯协议

主要用于蓝牙基础数据流的传输

A2DP即Advanced Audio Distribution Profile 蓝牙音频传输模型协议

主要用来播放高品质的音乐,主要应用场景是蓝牙音箱,蓝牙耳机

AVRCP即Audio Video Remote Control Profile音频/视频远程控制协议

主要用于蓝牙设备的远程控件,比如控制蓝牙耳机的播放,暂停,继续等

HFP即Hands Free Profile 免提协议

主要用于车载蓝牙中,可以实现免提功能,可以控制电话的接听,挂断,拒接,音频拨号等

HDP即Health Device Profile 健康设备协议

主要用于蓝牙血压计,蓝牙体重称等

OPP即Object Push Profile 对象推送协议

主要用于手机与手机或者手机与电脑之间通过蓝牙进行文件操作,比如通过蓝牙发送文件

 

蓝牙开发核心类

类名

解释

BluetoothAdapter

蓝牙适配器类

BluetoothDevice

蓝牙设备信息类

BluetoothSocket

蓝牙客户端Socket类

BluetoothServerSocket

蓝牙服务端Socket类

BluetoothHeadset

蓝牙HFP协议支持类

BluetoothA2dp

蓝牙A2DP协议支持类

BluetoothHealth

蓝牙HDP协议支持类

 

蓝牙开发示例

public static String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";//SPP Profile UUID,这个UUID是蓝牙SIG组织定义的官方ID

BluetoothSocketclientSocket = null;
BluetoothServerSocket serverSocket = null;

InputStreamis;
OutputStream os;

Android蓝牙API历史

Google从Android 3.0开始提供传统蓝牙相关的开发API,蓝牙API在android.bluetooth包下面

蓝牙权限声明

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>

打开蓝牙
通过代码打开
BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
if(adapter.isEnabled())
{
   adapter.disable();//关闭设备蓝牙
}
else{
   adapter.enable();//打开设备蓝牙
}
通过系统蓝牙对话框打开

public static void openBluetoothWithDialog(Activity activity, int seconds) {
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
   activity.startActivityForResult(intent, REQUEST_ENABLE);
}

注册蓝牙状态监听器
public void registerBluetoothReceiver() {
   IntentFilter filter = new IntentFilter();
   filter.addAction(BluetoothDevice.ACTION_FOUND);
   filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
   // filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
   // filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
   filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
   mActivity.registerReceiver(receiver, filter);
}

private BroadcastReceiver receiver = new BroadcastReceiver() {
   @Override
   public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
      if (BluetoothDevice.ACTION_FOUND.equals(action)) {
         BluetoothDevice device = intent
               .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
         deviceList.add(device);
         createBond(device);
      } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
         BluetoothDevice device = intent
               .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
         if(device.getBondState()==BluetoothDevice.BOND_BONDED){
            try {
               clientSocket = BluetoothHelper.connect(device);
               remoteDevice = clientSocket.getRemoteDevice();
               showToast("绑定成功");
               is = clientSocket.getInputStream();
               os = clientSocket.getOutputStream();
               sendMessage("hello boy");
               startMessageLoop();
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
            .equals(action)) {
         showToast("扫描完成");
      }
   }
};
建立蓝牙客户端Socket

public static BluetoothSocket createBluetoothSocket(BluetoothDevice device) {
        BluetoothSocket socket = null;
        UUID uuid = UUID.fromString(SPP_UUID);
        try {
//       Method m =device.getClass().getMethod("createRfcommSocket", newClass[]{int.class});
//       socket = (BluetoothSocket)m.invoke(device, Integer.valueOf(1));
           
socket =device.createRfcommSocketToServiceRecord(uuid);
        } catch (Exception e) {
            try {
                socket =device.createInsecureRfcommSocketToServiceRecord(uuid);
            } catch (Exception ex) {
                socket = null;
            }
        }
        return socket;
    }

建立蓝牙服务端Socket
public static BluetoothServerSocket createServerSocket() {
      BluetoothServerSocket socket = null;
      UUID uuid = UUID.fromString(SPP_UUID);
      BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
      String name=adapter.getName();
      try {
//       Method listenMethod = adapter.getClass().getMethod("listenUsingRfcommOn", new Class[]{int.class});
//       socket = ( BluetoothServerSocket) listenMethod.invoke(adapter, new Object[]{ 29});
         socket = adapter.listenUsingRfcommWithServiceRecord(name, uuid);
      } catch (Exception e) {
         e.printStackTrace();
      }
      return socket;
   }

 

蓝牙扫描

BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
if(adapter.isDiscovering())
{
   adapter.cancelDiscovery();//取消扫描
}
else{
   adapter.startDiscovery();//开始扫描
}

绑定蓝牙设备

public static boolean createBond(BluetoothDevice device) {
        if (device.getBondState() == BluetoothDevice.BOND_NONE) {
         return device.createBond();
//            Method createBondMethod = null;
//            try {
//                createBondMethod =BluetoothDevice.class.getMethod("createBond");
//                Object objResult = createBondMethod.invoke(device);
//                if (objResult != null)
//                    returnBoolean.parseBoolean(objResult.toString());
//                return false;
//            } catch (Exception e) {
//                e.printStackTrace();
//                return false;
//            }
       
}
        return true;
    }

获取系统蓝牙绑定的设备列表

public static ArrayList<BluetoothDevice> getBondedDevices()
{
   ArrayList<BluetoothDevice>deviceList=new ArrayList<BluetoothDevice>();
   BluetoothAdapter bluetoothAdapter =BluetoothAdapter.getDefaultAdapter();
   Object[] devices =bluetoothAdapter.getBondedDevices().toArray();
   for (int i = 0; i < devices.length; i++) {
      BluetoothDevice device =(BluetoothDevice) devices[i];
      if(!deviceList.contains(device))
      {
         deviceList.add(device);
      }
   }
   return deviceList;
}

判断蓝牙A2DP的连接状态

public static boolean isA2DPDeviceConnected(Context context)
{
   BluetoothAdapteradapter=BluetoothAdapter.getDefaultAdapter();
   int state=adapter.getProfileConnectionState(BluetoothProfile.A2DP);
   return state==BluetoothProfile.STATE_CONNECTED;
}

蓝牙连接

public static BluetoothSocket connect(BluetoothDevice device) {
    BluetoothSocket socket = null;
    try {
        socket = createBluetoothSocket(device);
        if (socket == null) return null;
        socket.connect();
    } catch (Exception e) {
        socket = null;
        e.printStackTrace();
    }
    return socket;
}

建立服务端消息循环
serverSocket =createServerSocket();

private void startServerListening() {
   new Thread(new Runnable() {
      @Override
     
public void run() {
         while (true) {
            try {
               clientSocket = serverSocket.accept();
               is = clientSocket.getInputStream();
               os = clientSocket.getOutputStream();
               startMessageLoop();
            } catch (Exception e) {
               e.printStackTrace();
            }
         }
      }
   }).start();
}

建立客户端消息循环

private void startMessageLoop() {
      new Thread(new Runnable() {
         @Override
        
public void run() {
            while (true) {
               byte[] buffer = new byte[1024];
               try {
                  int readed = is.read(buffer);
                  String msg = new String(buffer, 0, readed);
                  Stringresult=HexHelper.encodeHexStr(buffer);
                  if(TextUtils.isEmpty(result)) return;
                  if(result.equalsIgnoreCase("aa12040042")){
                     //do something
                 
}
              
} catch (IOException e) {
                  e.printStackTrace();
               }
            }
         }
      }).start();
   }

发送蓝牙消息

//aa12040042

byte[]cmd_get_pid=new byte[]
           {
              (byte)0xaa,
              (byte)0x12,
              (byte)0x04,
              (byte)0x00,
              (byte)0x42
          
};

os.write(cmd_get_pid);
os.flush();

接收蓝牙消息

int readed = is.read(buffer);
String msg = new String(buffer, 0,readed);
String result=HexHelper.encodeHexStr(buffer);
if(TextUtils.isEmpty(result)) return;
if(result.equalsIgnoreCase("aa12040042")){
   //do something
}

BLE的应用开发参考

BLE基础

l  BLE技术是低成本、短距离、可互操作的鲁棒性无线技术,工作在免许可的2.4GHz ISM射频频段。它从一开始就设计为超低功耗(ULP)无线技术。它利用许多智能手段最大限度地降低功耗。蓝牙低功耗技术采用可变连接时间间隔,这个间隔根据具体应用可以设置为几毫秒到几秒不等。另外,因为BLE技术采用非常快速的连接方式,因此平时可以处于“非连接”状态(节省能源),此时链路两端相互间只是知晓对方,只有在必要时才开启链路,然后在尽可能短的时间内关闭链路。

l  BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备

l  低功耗

l  传输距离短

l  传输速度慢

BLE应用场景

l  智能穿戴设备,比如智能手环,智能手表等

l  智能家居,智能灯,智能空调,蓝牙电子称等

l  健康设备,心率监测仪等

BLE开发基础

BLE调试工具
BLE Scanner

BLE Explorer

拼接完整的蓝牙设备UUID
public static final String BASE_ADDRESS="00000000-0000-1000-8000-00805F9B34FB"; //蓝牙UUID的基地址

如上图,假如我们要拼接DeviceInformation Service服务的完整的UUID,我们可以拿蓝牙UUID的基地址的首段+0x180A,

所以我们得到的完整的DeviceInformation Service的UUID是0000180A-0000-1000-8000-00805F9B34FB,我们可以把这个拼接后的UUID用在我们的代码中

BLE服务,特性,特性扫描器的UUID拼接也是这个道理

常用的BLE术语

l  GATT

l  服务

l  特性

l  特性描述符

l  中央设备

l  外围设备

关键术语和概念

l  Generic Attribute Profile(GATT)—GATT配置文件是一个通用规范,用于在BLE链路上发送和接收被称为“属性”的数据块。目前所有的BLE应用都基于GATT。 蓝牙SIG规定了许多低功耗设备的配置文件。配置文件是设备如何在特定的应用程序中工作的规格说明。注意一个设备可以实现多个配置文件。例如,一个设备可能包括心率监测仪和电量检测。

l  Attribute Protocol(ATT)—GATT在ATT协议基础上建立,也被称为GATT/ATT。ATT对在BLE设备上运行进行了优化,为此,它使用了尽可能少的字节。每个属性通过一个唯一的的统一标识符(UUID)来标识,每个String类型UUID使用128 bit标准格式。属性通过ATT被格式化为characteristics和services。

l  Characteristic 一个characteristic包括一个单一变量和0-n个用来描述characteristic变量的descriptor,characteristic可以被认为是一个类型,类似于类。

l  Descriptor Descriptor用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的测量单位。

l  Service service是characteristic的集合。例如,你可能有一个叫“Heart Rate Monitor(心率监测仪)”的service,它包括了很多characteristics,如“heart rate measurement(心率测量)”等。你可以在bluetooth.org 找到一个目前支持的基于GATT的配置文件和服务列表。

角色和责任

l  中央 VS 外围设备。 适用于BLE连接本身。中央设备扫描,寻找广播;外围设备发出广播。

l  GATT 服务端 VS GATT 客户端。决定了两个设备在建立连接后如何互相交流。

BLE核心类

类名

解释

BluetoothAdapter

蓝牙适配器类

BluetoothDevice

蓝牙设备类

BluetoothGatt

作为中央设备来使用和处理数据,是BLE的核心工具类,可以通过这个类来获取BLE设备提供的服务

BluetoothGattService

BLE服务类

BluetoothGattCharacteristic

BLE特性类

BluetoothGattDescriptor

BLE特性描述器类

BluetoothGattCallback

BLE核心通讯回调类

LeScanCallback

BLE扫描回调类

 

BLE核心类关系图

 

BLE开发示例

List<BluetoothGattService> mGattServiceList=null;
BluetoothDevice mRemoteDevice;
BluetoothGatt mBluetoothGatt;

 

public static final String DEVICE_INFO_SERVICE="00001800-0000-1000-8000-00805F9B34FB";
public static final String SYSTEM_ID_CHAR="00002a04-0000-1000-8000-00805F9B34FB";

 

BLE开发API历史

Google从Android 4.3开始提供BLE的开发API,所以BLE开发只支持Android 4.3以上的手机

蓝牙权限声明

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
扫描BLE设备
BLE设备扫描回调
LeScanCallback leScanCallback=new LeScanCallback()
{
   /**
    * BLE设备扫描结果回调函数
    * @param device 扫描到的BLE设备
    * @param rssi BLE设备的信号强度
    * @param scanRecord BLE设备发出的特定信息,可能通过这个信息来判断扫描到的BLE设备是不是指定类型的BLE设备
    */
   @Override
   public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
      mActivity.runOnUiThread(new Runnable()
      {
         @Override
         public void run() {
            if(!mDeviceList.contains(device))
            {
               mDeviceList.add(device);
               mAdapter.notifyDataSetChanged();
            }
         }
      });
   }
};
开始BLE设备扫描
public static void startLeScan(UUID[] serviceUuids,LeScanCallback callback)
   {
      BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
      if(serviceUuids==null)
      {
         adapter.startLeScan(callback);
      }
      else
      {
         adapter.startLeScan(serviceUuids, callback);
      }
   }
停止BLE设备扫描
public static void stopLeScan(LeScanCallback callback)
{
   BluetoothAdapter adapter=BluetoothAdapter.getDefaultAdapter();
   adapter.stopLeScan(callback);
}
判断特性的属性
判断特性是否可读
public static boolean isBluetoothGattCharacteristicRead(BluetoothGattCharacteristic characteristic)
{
   return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ)== BluetoothGattCharacteristic.PROPERTY_READ;
}
判断特性是否可写
public static boolean isBluetoothGattCharacteristicRead(BluetoothGattCharacteristic characteristic)
{
   return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ)== BluetoothGattCharacteristic.PROPERTY_READ;
}
判断特性是否是Notify特性
public static boolean isBluetoothGattCharacteristicNotify(BluetoothGattCharacteristic characteristic)
{
   return (characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY;
}
连接BLE设备
和BLE设备通讯的回调
BluetoothGattCallback mGattCallback=new BluetoothGattCallback()
{
   @Override
   public void onConnectionStateChange(BluetoothGatt gatt, int status,
         int newState) {
      if(newState==BluetoothGatt.STATE_CONNECTED)
      {
         boolean success=gatt.discoverServices();//查找BLE设备提供的所有服务
         showToast(gatt.getDevice().getName()+" connected");
      }
      else if(newState==BluetoothGatt.STATE_DISCONNECTED)
      {
         if(mGattServiceList!=null)
            mGattServiceList.clear();
         showToast(gatt.getDevice().getName()+" disconnected");
      }
      super.onConnectionStateChange(gatt, status, newState);
   }
   
   @Override
   public void onServicesDiscovered(BluetoothGatt gatt, int status) {
      if(status==BluetoothGatt.GATT_SUCCESS)
      {
         mGattServiceList=gatt.getServices();//获取BLE设备提供的所有服务列表
         BluetoothGattService devInfoService=gatt.getService(UUID.fromString(DEVICE_INFO_SERVICE));
         BluetoothGattCharacteristic systemID=devInfoService.getCharacteristic(UUID.fromString(SYSTEM_ID_CHAR));
         gatt.readCharacteristic(systemID);
      }
      
      super.onServicesDiscovered(gatt, status);
   }
   
   @Override
   public void onCharacteristicChanged(BluetoothGatt gatt,
         BluetoothGattCharacteristic characteristic) {
      try
      {
         byte[]value=characteristic.getValue();
         if(value!=null)
         {
            String str=HexHelper.encodeHexStr(value);
            Log.i("hello", str);
         }
      }
      catch(Exception e)
      {
         Log.i("hello", e.getMessage());
      }
      super.onCharacteristicChanged(gatt, characteristic);
   }

   @Override
   public void onCharacteristicRead(BluetoothGatt gatt,
         BluetoothGattCharacteristic characteristic, int status) {
      if(status==BluetoothGatt.GATT_SUCCESS)
      {
         try
         {
            byte[]value=characteristic.getValue();
            if(value!=null)
            {
               String str=HexHelper.encodeHexStr(value);
               Log.i("hello", str);
            }
         }
         catch(Exception e)
         {
            Log.i("hello", e.getMessage());
         }
      }
      
      super.onCharacteristicRead(gatt, characteristic, status);
   }

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

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

   @Override
   public void onDescriptorWrite(BluetoothGatt gatt,
         BluetoothGattDescriptor descriptor, int status) {
      super.onDescriptorWrite(gatt, descriptor, status);
   }
};
异步连接BLE设备
public void connectGatt(BluetoothDevice device)
{
   mBluetoothGatt=device.connectGatt(App.Instance, false, mGattCallback);
}
断开和BLE设备的连接
public void disconnect() {
    if (mBluetoothGatt != null) {
       mBluetoothGatt.disconnect();
       mBluetoothGatt.close();
       mBluetoothGatt=null;
    }
}
设置BLE通知特性
public void setCharacteristicNotification(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, UUID uuid, boolean enable) {
   if (gatt == null || characteristic==null || uuid==null) return;
   gatt.setCharacteristicNotification(characteristic, enable);
   BluetoothGattDescriptor descriptor = characteristic.getDescriptor(uuid);
   descriptor.setValue(enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
   gatt.writeDescriptor(descriptor);
}
  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值