如何在Android 上面实现GATT Server
来源:互联网 发布:matlab在线编程 编辑:程序博客网 时间:2024/05/01 13:29
这篇文章主要介绍如何在android手机上实现gatt server的功能。共分两部分,第一部分是背景介绍,第二部分是程序解释。
1.背景介绍
老话说得好,用理论指导实践,再从实践中总结出理论,由此不断的升华,达到新的高度,我们在这一节介绍一点ble的背景知识。
在蓝牙的BLE中,共有四种模式, 列表如下:
Broadcaster
Observer
Peripheral
Central
有没有发现,其实broadcaster 是peripheral的子集,observer是central的子集,套路很深啊。
在很久以前,我们习惯了用手机作为central或者observer,用一个单独的蓝牙ble芯片来作为broadcaster或者peripheral,大家相安无事,各负其责。
突然有一天,安卓系统(好吧其实iOS也是这样的)也支持了自己作为broadcaster或者peripheral,这世界变化太快。
其实,不管是谁作为peripheral,例如一个ble的芯片,抑或是一个手机,套路总是一样的,过程也是一样的,唯一变化的是我们把开发环境换了一个地方而已。
要想在一个设备上实现peripheral(这篇文章不再提broadcaster了,提了peripheral还讲broadcaster干嘛,吃了满汉全席还再需要吃兰州拉面或者沙县小吃吗),总共分三步:
1)设置一下自己需要advertise的内容,即,要在广播包里广播出来什么东西,例如名字,或者是支持的service什么的。
2)设置一下自己能够给 远端提供什么内容呢?总得搞一个service吧,不管是sig制订的标准的,还是自己定义的,另外自己总得有数据给别人吧,例如你想做一个温度计,那么温度的这个东西总需要定义一下的,即为characteristic加入到自己系统的gatt 数据库(根据不同产品的名字不同命名不同,例如android叫gatt server)中去,抑或是更高级一点加个descriptor。并注册好相应的callback函数,例如远端来读数据了怎么办,来写数据了怎么办。
3)开动start advertise,静静读等待有缘设备过来。
这就完了,就是这么简单。
2.程序解释
这一小节来解释一下android上怎么来实现gatt server。
第一步,先获得相应的权限,这没啥好说的。
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />注意,如果编写ble程序的话可以把location的权限申请也加上去,我还没查为什么需要加,可能是和beacon有关系?
第二步,看本机是否支持蓝牙ble和广播,如果支持就继续往下走,如果不支持就任程序员自由发挥。
{ mBluetoothAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)) .getAdapter(); // Is Bluetooth supported on this device? if (mBluetoothAdapter != null) { // Is Bluetooth turned on? if (mBluetoothAdapter.isEnabled()) { // Are Bluetooth Advertisements supported on this device? if (mBluetoothAdapter.isMultipleAdvertisementSupported()) { // Everything is supported and enabled, load the fragments. setupFragments(); } else { // Bluetooth Advertisements are not supported. showErrorText(R.string.bt_ads_not_supported); } } else { // Prompt user to turn on Bluetooth (logic continues in onActivityResult()). Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, Constants.REQUEST_ENABLE_BT); } } else { // Bluetooth is not supported. showErrorText(R.string.bt_not_supported); } }
第三步,构建要广播的信息的内容。ble的连接和classic的蓝牙连接不同,设备查找方式也不同。在classic蓝牙中,假设a想查找到b设备,a需要一股脑的往周围扔好多的查询的package,然后再等待周围的设备回复,而如果此时b恰巧收到了a发出来的包,则b会在一个random的delay中回复a,此时则a查到了b。而坐ble中,则是b一直往外面扔package,意思是我在这里!周围有人嘛?而若a恰巧听到了b的package,则表示a查找到了b。所以,一个ble设备要想被别人连接,那么它必须的需要往外面发点儿package才行,这一步,就是决定往外面发点儿啥好呢?反正是最低消费,不发白不发。
咱们先看个开始广播这个函数。
public void startAdvertising (AdvertiseSettings settings, AdvertiseData advertiseData, AdvertiseCallback callback)
第一个参数,advertisesettings,主要是用来设置这次广播的一些属性,例如,是要广播的密集一点(那么就比较耗电),或者是功率大一点(也耗电),以及广播多少时间(时间太长了就把电耗完了)等等。
第二个参数,advertisedata,主要是用来设置广播出去的内容是什么。例如是否包含本地设备的名字,是否包含发射功率,要包含的service是什么等等。
第三个参数,advertisecallback,主要是回调一下广播命令发送出去之后的状态,即是否成功?失败的话是什么原因?
所以,我们把这三个参数准备好,然后等待开始广播的时候作为参数传下去就行了。
/** * Returns an AdvertiseData object which includes the Service UUID and Device Name. */ private AdvertiseData buildAdvertiseData() { /** * Note: There is a strict limit of 31 Bytes on packets sent over BLE Advertisements. * This includes everything put into AdvertiseData including UUIDs, device info, & * arbitrary service or manufacturer data. * Attempting to send packets over this limit will result in a failure with error code * AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE. Catch this error in the * onStartFailure() method of an AdvertiseCallback implementation. */ AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder(); dataBuilder.addServiceUuid(Constants.Service_UUID); dataBuilder.setIncludeDeviceName(true); /* For example - this will cause advertising to fail (exceeds size limit) */ //String failureData = "asdghkajsghalkxcjhfa;sghtalksjcfhalskfjhasldkjfhdskf"; //dataBuilder.addServiceData(Constants.Service_UUID, failureData.getBytes()); return dataBuilder.build(); } /** * Returns an AdvertiseSettings object set to use low power (to help preserve battery life) * and disable the built-in timeout since this code uses its own timeout runnable. */ private AdvertiseSettings buildAdvertiseSettings() { AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder(); settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER); settingsBuilder.setTimeout(0); return settingsBuilder.build(); } /** * Custom callback after Advertising succeeds or fails to start. Broadcasts the error code * in an Intent to be picked up by AdvertiserFragment and stops this Service. */ private class SampleAdvertiseCallback extends AdvertiseCallback { @Override public void onStartFailure(int errorCode) { super.onStartFailure(errorCode); Log.d(TAG, "Advertising failed"); sendFailureIntent(errorCode); stopSelf(); } @Override public void onStartSuccess(AdvertiseSettings settingsInEffect) { super.onStartSuccess(settingsInEffect); Log.d(TAG, "Advertising successfully started"); } } /** * Builds and sends a broadcast intent indicating Advertising has failed. Includes the error * code as an extra. This is intended to be picked up by the {@code AdvertiserFragment}. */ private void sendFailureIntent(int errorCode){ Intent failureIntent = new Intent(); failureIntent.setAction(ADVERTISING_FAILED); failureIntent.putExtra(ADVERTISING_FAILED_EXTRA_CODE, errorCode); sendBroadcast(failureIntent); }
第四步,准备自己的东西,即我要给远端什么内容?远端连接过来之后,我如何与之交互?
在这里我们举个最简单的例子,即告诉远端我是一个什么样的设备,我的设备信息版本号什么的。
/*prepare service here*/ private void addDeviceInfoService() { /*UUID can get from SIG website*/ final String SERVICE_DEVICE_INFORMATION = "0000180a-0000-1000-8000-00805f9b34fb"; final String SOFTWARE_REVISION_STRING = "00002A28-0000-1000-8000-00805f9b34fb"; BluetoothGattService previousService = mGattServer.getService( UUID.fromString(SERVICE_DEVICE_INFORMATION)); if(null != previousService) mGattServer.removeService(previousService); BluetoothGattCharacteristic softwareVerCharacteristic = new BluetoothGattCharacteristic( UUID.fromString(SOFTWARE_REVISION_STRING), BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ ); BluetoothGattService deviceInfoService = new BluetoothGattService( UUID.fromString(SERVICE_DEVICE_INFORMATION), BluetoothGattService.SERVICE_TYPE_PRIMARY); softwareVerCharacteristic.setValue(new String(getString(R.string.version)).getBytes()); deviceInfoService.addCharacteristic(softwareVerCharacteristic); mGattServer.addService(deviceInfoService); }
下面的代码就是实现gatt servercallback的回调函数,即准备远端连接来之后,我方如何与远端交互。
rivate final BluetoothGattServerCallback mGattServerCallback = new BluetoothGattServerCallback() { @Override public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { Log.d(TAG, "onConnectionStateChange: gatt server connection state changed, new state " + Integer.toString(newState)); super.onConnectionStateChange(device,status,newState); } @Override public void onServiceAdded(int status, BluetoothGattService service) { Log.d(TAG, "onServiceAdded: " + Integer.toString(status)); super.onServiceAdded(status, service); } @Override public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) { Log.d(TAG, "onCharacteristicReadRequest: " + "requestId" + Integer.toString(requestId) + "offset" + Integer.toString(offset)); super.onCharacteristicReadRequest(device, requestId, offset, characteristic); mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue()); } @Override public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { Log.d(TAG, "onCharacteristicWriteRequest: " + "data = "+ value.toString()); super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value); mGattServer.sendResponse(device, requestId,BluetoothGatt.GATT_SUCCESS,offset,value); /*store data here*/ } @Override public void onNotificationSent(BluetoothDevice device, int status) { Log.d(TAG, "onNotificationSent: status = " + Integer.toString(status)); super.onNotificationSent(device, status); } @Override public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) { Log.d(TAG, "onDescriptorReadRequest: requestId = " + Integer.toString(requestId)); super.onDescriptorReadRequest(device, requestId, offset, descriptor); mGattServer.sendResponse(device, requestId,BluetoothGatt.GATT_SUCCESS,offset, descriptor.getValue()); } @Override public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { Log.d(TAG, "onDescriptorWriteRequest: requestId = " + Integer.toString(requestId)); super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value); mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,offset,value); } @Override public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { Log.d(TAG, "onExecuteWrite: requestId = " + Integer.toString(requestId)); super.onExecuteWrite(device, requestId, execute); /*in case we stored data before, just execute the write action*/ } @Override public void onMtuChanged (BluetoothDevice device, int mtu) { Log.d(TAG, "onMtuChanged: mtu = " + Integer.toString(mtu)); } };
第五步,申请一下adapter以及gattserver等。
/** * Get references to system Bluetooth objects if we don't have them already. */ private void initialize() { if (mBluetoothLeAdvertiser == null) { BluetoothManager mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager != null) { BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter != null) { mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); mGattServer = mBluetoothManager.openGattServer(this,mGattServerCallback); if (mGattServer == null) { Toast.makeText(this, getString(R.string.bt_gatt_server_null), Toast.LENGTH_LONG).show(); } else { addDeviceInfoService();/*build gatt server data here*/ } } else { Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show(); } } else { Toast.makeText(this, getString(R.string.bt_null), Toast.LENGTH_LONG).show(); } } }
第六部,获得某个事件之后,例如click等开始广播,至此,就没啥事儿了。
/** * Starts BLE Advertising. */ private void startAdvertising() { Log.d(TAG, "Service: Starting Advertising"); if (mAdvertiseCallback == null) { AdvertiseSettings settings = buildAdvertiseSettings(); AdvertiseData data = buildAdvertiseData(); mAdvertiseCallback = new SampleAdvertiseCallback(); if (mBluetoothLeAdvertiser != null) { mBluetoothLeAdvertiser.startAdvertising(settings, data, mAdvertiseCallback); } } }
下面等图是在三星盖乐世作为gattserver,苹果手机作为gattclient的测试结果,勉强能用。
- 如何在Android 上面实现GATT Server
- 如何在Android模拟器上面开Telnet
- 如何Android文件安装在虚拟机上面
- Android as Bluetooth Low Energy Peripheral (GATT server).
- Android as Bluetooth Low Energy Peripheral (GATT server).
- ble之gatt server
- 如何在RecyclerView上面实现"拖放"和"滑动删除"-2
- 如何在RecyclerView上面实现“拖放”和“滑动删除”-1
- 如何在android设备上面跑C或C++程序?
- 如何在android模拟器上面安装APK,(Window & Mac)
- 在Android上面如何使用带有心跳检测的Socket
- 在Android上面如何使用带有心跳检测的Socket
- 在Android上面如何使用带有心跳检测的Socket
- BLE gatt从机端实现(Android端)
- 如何让层飘在Flash上面?
- Android BLE Gatt Error
- Android bluedroid GATT简述
- Xvid 在2410 上面实现
- Android 框架练成 教你打造高效的图片加载框架
- Python 黑帽子 snffer ip header decoder
- C/C++中的struct
- iOS_childController篇
- Leetcode-maximum-subarray
- 如何在Android 上面实现GATT Server
- __FILE__,__LINE__
- Acm课程总结
- HDOJ 3501 Calculation 2
- jQuery请求网络(Access-Control-Allow-Origin)
- 开发环境、测试环境、生产环境区别
- float与double类型的范围和精度
- 特别响、非常近——BPMN2新规范与Activiti5
- Android 轻松实现后台搭建+APP版本更新