概述

  • 中心角色和外围角色
    在BLE中存在两个角色,一个是中心角色(Central),一个是外围角色(Peripheral),蓝牙设备或手机都可以单独作为Central或Peripheral角色。外设角色的作用是为中心角色提供各种数据,中心角色可以扫描并接收多个外设角色数据( 外围角色中的设备进行广播,中心角色的设备扫描寻找广播),数据以服务(Service)和特征(Characteristic)的形式呈现。其中Ble中心角色的API在Android 4.3得到支持,而外围角色的API在Android 5.0才得到支持
  • 协议、服务、特征、描述符
    一份协议(BluetoothGatt)由一个或多个服务(BluetoothGattService)构成,一个服务由零个或多个特征(BluetoothGattCharacteristic)构成,一个特征可以包含零个或多个描述符(BluetoothGattDescriptor)。每一个服务、特征、描述符都有一个UUID作为唯一识别符,识别符有通用的,也可以自定义,也可以随机生成,固定格式00000000-0000-0000-0000-000000000000(8-4-4-4-12),一般来说自定义的UUID只有前8位有变化,后面的基本是固定的0000-1000-8000-00805f9b34fb,所以一个自定义的UUID一般看起来就像这样 “0000????-0000-1000-8000-00805f9b34fb” 通配符就表示4个16进制数。每一个特征都有其属性和权限(Read | Write | Notify | Indicate),特征根据属性可读可写。在每个Ble蓝牙设备中,都会有两个默认的服务如下:
//Generic Access(Generic Attribute Profile 通用属性规范GATT)
service:00001801-0000-1000-8000-00805f9b34fb
characteristic:00002a05-0000-1000-8000-00805f9b34fb//Generic Attribute (Generic Access Profile 通用接入规范GAP)
service:00001800-0000-1000-8000-00805f9b34fb
characteristic:00002a00-0000-1000-8000-00805f9b34fb
characteristic:00002a01-0000-1000-8000-00805f9b34fb
characteristic:00002aa6-0000-1000-8000-00805f9b34fb
  • 适配器,扫描器:
    每一台支持蓝牙的手机中都会有一个蓝牙适配器,由蓝牙管理器管理着,从其中获得蓝牙适配器。适配器中自带扫描器,使用扫描器可以扫描周边的蓝牙设备。

Ble中常用类

  • BluetoothDeivce:蓝牙设备,代表一个具体的蓝牙外设
  • BluetoothManager:蓝牙管理器,主要用于获取蓝牙适配器和管理所有和蓝牙相关的东西
  • BluetoothAdapter:蓝牙适配器,每一台支持蓝牙功能的手机都有一个蓝牙适配器,一般来说,只有一个,可以通过BluetoothManager获取
  • BluetoothLeScanner:蓝牙适配器里面的扫描器,用于扫描BLE外设,可以通过BluetoothAdapter获取
  • BluetoothGatt:通用属性协议,定义了BLE通讯的基本规则,就是通过把数据包装成服务和特征的约定过程,可以通过建立连接获取
  • BluetoothGattService:服务,描述了一个BLE设备的一项基本功能,由零或多个特征组构成,可以通过BluetoothGatt获取并自定义(现有的约定服务可以在bluetooth.org上找到)
  • BluetoothGattCallback:作为中央的回调类,用于回调GATT通信的各种状态和结果
  • BluetoothGattServerCallback:作为周边的回调类,用于回调GATT通信的各种状态和结果
  • BluetoothGattCharacteristic:特征,里面包含了一组或多组数据,是GATT通信中的最小数据单元。
  • BluetoothGattDescriptor:特征描述符,对特征的额外描述,包括但不仅限于特征的单位,属性等。
  • BluetoothProfile:一个通用的规范,按照这个规范来收发数据

使用流程

中心设备:判断蓝牙是否可用->打开蓝牙->开始扫描->获取被扫描到的设备->连接设备->发现服务->获取到指定特征->写入特征值
外围设备:判断蓝牙是否可用->打开蓝牙->创建广播数据->发送广播->添加服务至广播->根据监听获取写入的数据
下图是中心设备的使用流程图 来源

权限声明

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
//获取扫描结果权限,Android6.0以上需要动态权限(这两个权限是在经典蓝牙的扫描中需要声明的)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
//或者
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />//当android:required为true的时候,app只能强制运行在支持BLE功能的设备商,为false的时候,可以运行在所有设备上,但某些方法需要手动检测,否则可能存在隐性BUG
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>

Ble可用性判断

不是任何设备都支持BLE,最开始要确定设备是否支持,还要确定蓝牙已经打开。

  • Android系统从4.3以后才支持,这是先决条件
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){... ...
}
  • 判断蓝牙是否可用
//小于等于API17时直接使用BluetoothAdapter.getDefaultAdapter()来获取Adapter
private BluetoothAdapter getAdapter(){if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);mBluetoothAdapter = mBluetoothManager.getAdapter();} else {mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();}return mBluetoothAdapter;
}
if(mBluetoothAdapter == null){//蓝牙不可用
}//Ble不可用
private boolean checkIfSupportBle(){return getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}

蓝牙开启

//开启蓝牙功能需要一小段时间,所以不能执行开启蓝牙立即执行其他操作,这时蓝牙实际还没有开启,回出现异常,所以后续操作应该在蓝牙状态广播中处理
private void enableBluetooth(){if (mBluetoothAdapter == null && !mBluetoothAdapter.isEnabled()) {//使用系统弹框来启动蓝牙,REQUEST_ENABLE_BT为自定义的开启蓝牙请求码Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);//或者静默打开bluetoothAdapter.enable();}
}//静默关闭
bluetoothAdapter.disable();

蓝牙开关广播 更多蓝牙相关广播

//广播的Action
BluetoothAdapter.ACTION_STATE_CHANGED//四种状态值
BluetoothAdapter.STATE_ON  //已开启
BluetoothAdapter.STATE_OFF //已关闭
BluetoothAdapter.STATE_TURNING_ON //正在开启
BluetoothAdapter.STATE_TURNING_OFF //正在关闭//当前状态值的获取
int curState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);

扫描具体使用流程

  • 经典通用方式
//此过程大概持续10秒,当扫描到蓝牙设备后,会发出广播,只要在需要的地方注册接收广播,就可以获得扫描结果。这种方法可以扫描出所有蓝牙设备,包括BLE,但貌似不同手机有不同体验。
private void startDiscover(){ mBluetoothAdapter.startDiscover();
} //注册此广播,监听BluetoothDevice.ACTION_FOUND,以接收系统消息取得扫描结果
private class DeviceReceiver extends 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); mDevices.add(device ); } }
}
  • Ble扫描方式
//Ble蓝牙扫描方式在5.0之后发生了变更
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();//过滤器设置ArrayList<ScanFilter> filters = new ArrayList<>();ScanFilter.Builder filterBuilder = new ScanFilter.Builder();filters.add(filterBuilder);//扫描结果回调设置ScanCallback scanCallback = new ScanCallback(){        public void onScanResult(int callbackType, ScanResult result){//scanResult.getDevice()取得device}};//扫描参数设置ScanSettings.Builder settingsBuilder = new ScanSettings.Builder();//这种扫描方式占用资源比较高,建议当应用处于前台时使用该模式settingsBuilder.setScanMode(SCAN_MODE_LOW_LATENCY);// 开启扫描,第三项参数见下面,该操作为异步操作的,但是会消耗大量资源,一般扫描时长为12秒,建议找到需要的设备后,执行取消扫描bluetoothLeScanner.startScan(scanCallback, filter, settingsBuilder.build());
}else{//扫描结果回调设置  BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback(){public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord){}};//该方法已被声明为过时方法 adapter.startLeScan(leScanCallback)
}
  • 停止扫描
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){bluetoothLeScanner.stopScan(mScanCallback);
}else{bluetoothAdapter.stopLeScan(mLeScanCallback);
}

设备信息获取

  • 根据蓝牙地址获取蓝牙设备
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
  • 已绑定设备获取
private void getBoundDevices(){Set<BluetoothDevice> boundDevices = mBluetoothAdapter.getBondedDevices();for(BluetoothDevice device : boundDevices){//对device进行其他操作,比如连接等。}
}
  • 设备详细信息获取
private void showDetailOfDevice(){//获得设备名称,多个设备可以有同一个名称。String deviceName = bluetoothDevice.getName();//获取设备物理地址,一个设备只能有一个物理地址,每个设备都有每个设备的物理地址,无法相同。String deviceMacAddress =bluetoothDevice.getAddress();//绑定设备bluetoothDevice.createBond();
}

连接

  • 建立连接
//扫描回调的BluetoothDevice用于建立GATT连接,参数2是控制是否自动链接,为true的时候,当设备进入中心范围,会进行自动连接,为false时是主动连接,一般推荐使用主动连接,因为这样连接速度更快。
BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback);//如果已经连接后,但是又断开了连接,需要重新连接时
bluetoothGatt.connect();
  • 连接状态的获取
//连接状态改变会在BluetoothGattCallback的onConnectionStateChange方法中回调,同时也可以随时主动去查询
int state = bluetoothManager.getConnectionState(bluetoothDevice, BluetoothGatt.GATT);
  • 发现服务
//在建立连接成功后进行发现服务,onServicesDiscovered为发现服务的回调
bluetoothGatt.discoverServer();
  • 特征改变
//该方法一般是在发现服务后,进行设置的,设置该方法的目的是让硬件在特征数据改变的时候,发送数据给app,app则通过onCharacteristicChanged方法回调给用户,从参数中可获取到回调回来的数据
bluetoothGatt.setCharacteristicNotification(characteristic, true);
  • 连接数据回调(在非UI线程
//mBluetoothGattCallback 为所有蓝牙数据回调的处理者,也是整个蓝牙操作当中最为核心的一部分。它里面有很多方法,但并非所有都需要在开发当中用到,这里列出来只是作为部分解析,需要哪个方法,就重写哪个方法,不需要的,直接去掉
private BluetoothGattCallback mBluetoothGattCallback = new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {super.onConnectionStateChange(gatt, status, newState);//当设备与中心连接状态发生改变时,下面是已连接的判断if (status == BluetoothGatt.GATT_SUCCESS && newState== BluetoothProfile.STATE_CONNECTED){...}}@Overridepublic void onServicesDiscovered(BluetoothGatt gatt, int status) {super.onServicesDiscovered(gatt, status);//当发现设备服务时,会回调到此处,下面是遍历所有发现的服务if (status == BluetoothGatt.GATT_SUCCESS) {//gatt.getServices()可以获得外设的所有服务for (BluetoothGattService service : gatt.getServices()) {//每发现一个服务,我们再次遍历服务当中所包含的特征,service.getCharacteristics()可以获得当前服务所包含的所有特征for (BluetoothGattCharacteristic characteristic : service.getCharacteristics()) {//通常可以把所发现的特征放进一个列表当中以便后续操作,如果你想知道每个特征都包含哪些描述符,很简单,再用一个循环去遍历每一个特征的getDescriptor()方法。mCharacteristics.add(characteristic);//UUID获取,或者根据UUID判断是否是自己需要的服务/特征Log.i("", characteristic.getUuid().toString());//打印特征的UUID。}}}}@Overridepublic void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {//读取特征后回调到此处。}@Overridepublic void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {//写入特征后回调到此处,status == BluetoothGatt.GATT_SUCCESS代表写入成功}@Overridepublic void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {//当特征(值)发生变法时回调到此处。}@Overridepublic void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//读取描述符后回调到此处。}@Overridepublic void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {//写入描述符后回调到此处}@Overridepublic void onReliableWriteCompleted(BluetoothGatt gatt, int status) {//暂时没有用过。}@Overridepublic void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {//Rssi表示设备与中心的信号强度,发生变化时回调到此处。}@Overridepublic void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {//暂时没有用过。}
};

数据传输

  • 写入特征值,会回调onCharacteristicWrite
BluetoothGattService service = bluetoothGatt.getService(SERVICE_UUID);
BluetoothGattCharacteristic characteristic = service.getCharacteristic(CHARACTERISTIC_UUID);
characteristic.setValue(writeString);
//设置回复方式
bluetoothGattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
bluetoothGatt.writeCharacteristic(characteristic);
  • 写入描述符
bluetoothGattDescriptor.setValue(writeContent.getBytes());
bluetoothGatt.writeDescriptor(bluetoothGattDescriptor);
  • 读取特征值
bluetoothGatt.readCharacteristic(characteristic);

外围设备实现(被连接者/服务端 )

  • 广播的设定
private AdvertiseSettings buildAdvertiseSettings() {AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder()//设置广播模式:低功耗、平衡、低延时,广播间隔时间依次越来越短.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)//设置是否可以连接,一般不可连接广播应用在iBeacon设备上.setConnectable(true)//设置广播的最长时间,最大时长为LIMITED_ADVERTISING_MAX_MILLIS(180秒).setTimeout(10*1000)//设置广播的信号强度 ADVERTISE_TX_POWER_ULTRA_LOW, ADVERTISE_TX_POWER_LOW,//ADVERTISE_TX_POWER_MEDIUM, ADVERTISE_TX_POWER_HIGH 信号强度依次增强.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);return builder.build();
}
  • 对外广播的数据(数据限制 31 Bytes)
private AdvertiseData buildAdvertiseData() {AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();//添加厂家信息,第一个参数为厂家ID(不足两个字节会自动补0,例如这里为0x34,实际数据则为34,00)//一般情况下无需设置,否则会出现无法被其他设备扫描到的情况.addManufacturerData(0x34, new byte[]{0x56})//添加服务进行广播,即对外广播本设备拥有的服务.addServiceData(...);//是否广播信号强度.setIncludeTxPowerLevel(true)//是否广播设备名称.setIncludeDeviceName(true);return dataBuilder.build();
}
  • 对外广播(即允许被扫描到)
private void advertise() {BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);BluetoothAdapter mAdapter = bluetoothManager.getAdapter(); AdvertiseCallback advertiseCallback = new AdvertiseCallback() {@Overridepublic void onStartSuccess(AdvertiseSettings settingsInEffect) {//广播成功,建议在这里开启服务}@Overridepublic void onStartFailure(int errorCode) {//广播失败}};BluetoothLeAdvertiser mAdvertiser = mAdapter.getBluetoothLeAdvertiser();mAdvertiser.startAdvertising(buildAdvertiseSettings(), buildAdvertiseData(), mAdvertiseCallback);
}
  • 开启服务
//声明需要广播的服务的UUID和特征的UUID,注意不要占用蓝牙设备默认的UUID
UUID UUID_SERVICE = UUID.fromString("00001354-0000-1000-8000-00805f9b34fb");
UUID UUID_CHARACTERISTIC = UUID.fromString("00001355-0000-1000-8000-00805f9b34fb");
UUID UUID_DESCRIPTOR = UUID.fromString("00001356-0000-1000-8000-00805f9b34fb");//外围设备状态、数据回调,详情见后面
BluetoothGattServerCallback serverCallback = new BluetoothGattServerCallback() {...};
//GATT协议服务
BluetoothGattServer server = bluetoothManager.openGattServer(this, serverCallback);//创建一个服务
BluetoothGattService service = new BluetoothGattService(UUID_SERVICE,BluetoothGattService.SERVICE_TYPE_PRIMARY);//创建一个特征,首先此characteristic属性满足BluetoothGattCharacteristic.PROPERTY_WRITY或BluetoothGattCharacteristic.PROPERTY_WRITY_NO_RESPONSE,
//如果其property都不包含这两个,写特征值writeCharacteristic()函数直接返回false,什么都不做处理。其次此characteristic权限应满足
//BluetoothGattCharacteristic.PERMISSION_WRITE,否则onCharacteristicWrite()回调收到GATT_WRITE_NOT_PERMITTED回应
//如果需要既能读,又能写,则可以参考如下写法
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(Constants.UUID_CHARACTERISTIC,BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,BluetoothGattCharacteristic.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ);//创建一个描述符
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR,BluetoothGattDescriptor.PERMISSION_READ);//将描述符添加到特征中,一个特征可以包含0至多个描述符
characteristic.addDescriptor(descriptor);
//将特征添加到服务中,一个服务可以包含1到多个特征
service.addCharacteristic(characteristic);
server.addService(service);
  • BluetoothGattServerCallback
public abstract class BluetoothGattServerCallback {public BluetoothGattServerCallback() {}public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {//连接状态被改变}public void onServiceAdded(int status, BluetoothGattService service) {//添加服务}public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {//被读取特征}public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {//被写入数据,其中device是写入的设备,value是写入的值,responseNeeded指是否需要恢复,如果需要恢复则调用gattServer.sendResponse()方法回复}public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {//被读取描述符}public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {//被写入描述符}public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {}@Overridepublic void onNotificationSent(BluetoothDevice device, int status) {}@Overridepublic void onMtuChanged(BluetoothDevice device, int mtu) {}@Overridepublic void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {}@Overridepublic void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {}
}
  • 广播数据格式
    广播数据(或者扫描应答数据)由一个一个的AD Structure组成,对于未满31bytes的其它数据,则填充为0;每个AD Structure由两部分组成:1byte的长度信息(Data的长度),和剩余的Data信息;
    Data信息又由两部分组成:AD Type(长度不定)指示该AD Structure的类型,以及具体的AD Data。

    例如:

02 01 06 03 03 aa fe 17 16 aa fe 00 -10 00 01 02 03 04 05 06 07 08 09 0a 0b 0e 0f 00 00 00 00

02 01 06是一个AD Structure:Data的长度是02;Data是01 06;AD Type是01(Flags);AD Data是06,表明支持General Discoverable Mode、不支持BR/EDR。
03 03 aa fe是一个AD Structure:Data的长度是03;Data是03 aa fe;AD Type是03(16 bits的Service UUID);AD Data是aa fe,是Eddystone profile的Service UUID。AD Type查询

注意

  • 外围设备广播的数据最多31 byte
  • 写特征一次最多写入20 byte
  • 单次扫描时间不宜过长(建议10~15秒)
  • BluetoothGattCallback的回调是在非UI线程
  • 每个设备所能使用的Gatt连接是有限个数的,所以应该断开连接时也关闭GattbluetoothGatt.close(),然后重新连接
  • 外围设备蓝牙关闭后,中心设备的BluetoothGattCallback的onConnectionStateChange会回调状态码status = 19newState = 0(断开连接)
  • 外围设备蓝牙关闭后,中心设备再进行连接,BluetoothGattCallback的onConnectionStateChange会回调状态码133,并且当外围设备蓝牙开启后再调用中心设备的bluetoothGatt.connect()不能重新连接,仍然是133错误,所以建议针对133错误进行关闭gatt并释放资源

源码地址

作者:丶麦芽
链接:https://www.jianshu.com/p/3d4bfdc72384
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

低功耗蓝牙Ble的详细使用流程相关推荐

  1. 蓝牙:深入浅出低功耗蓝牙(BLE)协议栈

    深入浅出低功耗蓝牙(BLE)协议栈 BLE协议栈为什么要分层?怎么理解BLE"连接"?如果BLE协议只有ATT层没有GATT层会发生什么? 协议栈框架 一般而言,我们把某个协议的实 ...

  2. 低功耗蓝牙BLE外围模式(peripheral)-使用BLE作为服务端

    低功耗蓝牙BLE外围模式(peripheral)-使用BLE作为服务端 Android对外模模式(peripheral)的支持 从Android5.0开始才支持 关键术语和概念 以下是关键BLE术语和 ...

  3. 低功耗蓝牙BLE之连接事件、连接参数和更新方法(程序解读)

    关注公众号"迈微电子研发社",选择"星标★" 低功耗蓝牙BLE之连接参数de更新方法 0. 蓝牙的状态以及基本连接过程 0.1 蓝牙的状态: 0.3 蓝牙的连接 ...

  4. 深入浅出低功耗蓝牙(BLE)协议栈

    深入浅出低功耗蓝牙(BLE)协议栈 BLE协议栈为什么要分层?怎么理解BLE"连接"?如果BLE协议只有ATT层没有GATT层会发生什么? 协议栈框架 一般而言,我们把某个协议的实 ...

  5. java 协议栈_深入浅出讲解低功耗蓝牙(BLE)协议栈

    详解BLE连接建立过程 https://www.cnblogs.com/iini/p/8972635.html 详解BLE 空中包格式-兼BLE Link layer协议解析 https://www. ...

  6. 深入浅出低功耗蓝牙(BLE)协议栈,使用Ubertooth one扫描嗅探低功耗蓝牙

    BLE协议栈为什么要分层?怎么理解BLE"连接"?如果BLE协议只有ATT层没有GATT层会发生什么? 深入浅出低功耗蓝牙BLE协议栈 1. 协议栈框架 2. 如何通过无线发送一个 ...

  7. 泰凌微ble mesh蓝牙模组天猫精灵学习之旅④如何在Android开发低功耗蓝牙ble控制 TB-02 模块,代码工程全部开源!(附带Demo)

    本<泰凌微ble mesh蓝牙模组天猫精灵学习之旅>系列博客学习由半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1.小白也痴迷,如 ...

  8. 【Funpack】低功耗蓝牙 BLE 协议架构

    想要开发蓝牙应用,了解蓝牙协议架构是必不可少的.本文以低功耗蓝牙 BLE 为例,简要介绍 BLE 蓝牙协议架构,帮助开发者快速了解蓝牙协议概况. BLE 协议分层 BLE 协议栈主要由如下几部分组成: ...

  9. 经典蓝牙与低功耗蓝牙BLE开发基础知识:服务、特征、属性、UUID

    经典蓝牙和低功耗蓝牙BLE的区别 蓝牙大致被认为是1.0 2.0 3.0 4.0版本,不过现在已经不再用版本号区分蓝牙了,蓝牙1.0~3.0都是经典蓝牙,在塞班系统就已经开始使用了.而蓝牙4.0开始就 ...

最新文章

  1. Python:Scrapy 框架简单介绍
  2. Codeforces Round #FF 446 C. DZY Loves Fibonacci Numbers
  3. (推荐)叮当——中文语音对话机器人
  4. python 生成随机图片
  5. 中央空调水系统与制冷系统运行参数
  6. Cpp 对象模型探索 / 虚继承带虚函数的基类的子类的内存布局
  7. QT的QMediaPlayer类的使用
  8. BZOJ 1305 dance跳舞(最大流+二分答案)
  9. 由社区推动的 .NET 6 新 API
  10. JavaFX UI控件教程(八)之Choice Box
  11. Payara Micro在Oracle应用容器云上
  12. C++_类和对象_对象特性_拷贝构造函数调用时机---C++语言工作笔记042
  13. 《跟小海龟学Python》部分案例视频集锦
  14. 扬州打工人租房编年史
  15. java printf
  16. TCP/IP模型各个层次的功能和协议
  17. Git教程(一)Git简介——廖雪峰的官方网站
  18. 记录一个好用ER图软件dbschema
  19. Jupyter关联规则挖掘-莫名其妙的问题
  20. Air101-LCD扩展板按键测试程序

热门文章

  1. Android 蓝牙二维码打印,打印图片,二维码加文字布局,蓝牙连接
  2. 零基础python入门书籍推荐读哪些书?
  3. canvas实现圆角图片 (处理原图是长方形或正方形)
  4. C++ 获取string字符串长度的三种方法
  5. 求String长度的三大方法
  6. 亚马逊规则漏洞下的中国创业者机会
  7. STM32---I/O引脚复用以及重映射(GPIO和AFIO)
  8. DDD(domain driven design)-领域驱动设计
  9. UDP下的SOCK5代理实现
  10. 用python实现个卷积神经网络