当前位置: 代码迷 >> Android >> Android as Bluetooth Low Energy Peripherial (GATT server)
  详细解决方案

Android as Bluetooth Low Energy Peripherial (GATT server)

热度:649   发布时间:2016-04-28 00:46:07.0
Android as Bluetooth Low Energy Peripherial (GATT server).


  I demonstrate how to write a simple BLE peripheral application in Android here. I am bad in Android development, The UI would be very ugly, but the code work:

  Currently(5/25/2015), the code could be running in Nexus 6 or Nexus 9 only based on my test. The other phones or tablets  not support to be a BLE peripheral.  So, if you really interested in the Android as peripheral issue, please open your wallet and buy a GOOGLE official device, thank you.


 How to add a characteristic as notification is little bit complicated, In here I just add read write  ones.
  About the notification, I put code in the lat part of this post.

You should add

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

The 2 lines in your AndroidManifest.xml, like this :


<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.awind.presentsenseperipheral"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="21"        android:targetSdkVersion="21" />    <uses-permission android:name="android.permission.BLUETOOTH" />    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <activity            android:name=".MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>    </application></manifest>



The kernal code are below , note the AdvertiseCallback is callback of BluetoothLeAdvertiser ::startAdvertising, and BluetoothGattServerCallback is callback function of ALL BluetoothGattCharacteristic.


BLEPeripheral.java: (that is what you want)


package com.awind.presentsenseperipheral;import java.util.UUID;import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothDevice;import android.bluetooth.BluetoothGatt;import android.bluetooth.BluetoothGattCharacteristic;import android.bluetooth.BluetoothGattDescriptor;import android.bluetooth.BluetoothGattServer;import android.bluetooth.BluetoothGattServerCallback;import android.bluetooth.BluetoothGattService;import android.bluetooth.BluetoothManager;import android.bluetooth.le.AdvertiseCallback;import android.bluetooth.le.AdvertiseData;import android.bluetooth.le.AdvertiseSettings;import android.bluetooth.le.BluetoothLeAdvertiser;import android.content.Context;import android.content.pm.PackageManager;import android.util.Log;public class BLEPeripheral{  Context mContext;  BluetoothManager mManager; BluetoothAdapter mAdapter;  BluetoothLeAdvertiser  mLeAdvertiser;  BluetoothGattServer  mGattServer;    public static boolean isEnableBluetooth(){  return BluetoothAdapter.getDefaultAdapter().isEnabled(); }  public int init(Context context){    if(null == mManager)   mManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);    if(null == mManager)   return -1;    if(false == context.getPackageManager().    hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))   return -2;                      if(null == mAdapter)   mAdapter = mManager.getAdapter();    if(false == mAdapter.isMultipleAdvertisementSupported())   return -3;     mContext = context;  return 0; }  public void close() {   } public static String getAddress(){return BluetoothAdapter.getDefaultAdapter().getAddress();}  private AdvertiseCallback mAdvCallback = new AdvertiseCallback() {    @Override  public void onStartFailure(int errorCode){   Log.d("advertise","onStartFailure");  }    @Override  public void onStartSuccess(AdvertiseSettings settingsInEffect){   Log.d("advertise","onStartSuccess");  }; };   private final BluetoothGattServerCallback mGattServerCallback    = new BluetoothGattServerCallback(){     @Override        public void onConnectionStateChange(BluetoothDevice device, int status, int newState){   Log.d("GattServer", "Our gatt server connection state changed, new state ");         Log.d("GattServer", Integer.toString(newState));            super.onConnectionStateChange(device, status, newState);        }     @Override        public void onServiceAdded(int status, BluetoothGattService service) {            Log.d("GattServer", "Our gatt server service was added.");            super.onServiceAdded(status, service);        }        @Override        public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {            Log.d("GattServer", "Our gatt characteristic was read.");            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("GattServer", "We have received a write request for one of our hosted characteristics");            Log.d("GattServer", "data = "+ value.toString());             super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);        }        @Override        public void onNotificationSent(BluetoothDevice device, int status)        {         Log.d("GattServer", "onNotificationSent");                   super.onNotificationSent(device, status);                          }                @Override        public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {            Log.d("GattServer", "Our gatt server descriptor was read.");            super.onDescriptorReadRequest(device, requestId, offset, descriptor);                    }        @Override        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {            Log.d("GattServer", "Our gatt server descriptor was written.");            super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);        }        @Override        public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {            Log.d("GattServer", "Our gatt server on execute write.");            super.onExecuteWrite(device, requestId, execute);        }    };  private void addDeviceInfoService(BluetoothGattServer gattServer) {    if(null == gattServer)   return;     //        // device info           //        final String SERVICE_DEVICE_INFORMATION = "0000180a-0000-1000-8000-00805f9b34fb";        final String SOFTWARE_REVISION_STRING = "00002A28-0000-1000-8000-00805f9b34fb";                                                           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("0.0.1").getBytes());                deviceInfoService.addCharacteristic(softwareVerCharacteristic);        gattServer.addService(deviceInfoService); }  public void startAdvertise() {  if(null == mAdapter)   return;    if (null == mLeAdvertiser)    mLeAdvertiser = mAdapter.getBluetoothLeAdvertiser();          if(null == mLeAdvertiser)   return;     AdvertiseSettings.Builder settingBuilder;      settingBuilder = new AdvertiseSettings.Builder();   settingBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);   settingBuilder.setConnectable(true);      settingBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);                       AdvertiseData.Builder advBuilder;                  advBuilder = new AdvertiseData.Builder();                                                   mAdapter.setName("PeripheralAndroid"); //8 characters works, 9+ fails         advBuilder.setIncludeDeviceName(true);                 mGattServer = mManager.openGattServer(mContext, mGattServerCallback);                                                    addDeviceInfoService(mGattServer);                                    final String  SERVICE_A = "0000fff0-0000-1000-8000-00805f9b34fb";         final String  CHAR_READ_1 = "00fff1-0000-1000-8000-00805f9b34fb";         final String  CHAR_READ_2 = "00fff2-0000-1000-8000-00805f9b34fb";         final String  CHAR_WRITE = "00fff3-0000-1000-8000-00805f9b34fb";                                 BluetoothGattCharacteristic read1Characteristic = new BluetoothGattCharacteristic(           UUID.fromString(CHAR_READ_1),            BluetoothGattCharacteristic.PROPERTY_READ,           BluetoothGattCharacteristic.PERMISSION_READ           );                 read1Characteristic.setValue(new String("this is read 1").getBytes());                  BluetoothGattCharacteristic read2Characteristic = new BluetoothGattCharacteristic(           UUID.fromString(CHAR_READ_2),            BluetoothGattCharacteristic.PROPERTY_READ,           BluetoothGattCharacteristic.PERMISSION_READ           );                  read2Characteristic.setValue(new String("this is read 2").getBytes());                           BluetoothGattCharacteristic writeCharacteristic = new BluetoothGattCharacteristic(           UUID.fromString(CHAR_WRITE),            BluetoothGattCharacteristic.PROPERTY_WRITE,           BluetoothGattCharacteristic.PERMISSION_WRITE           );                  /                  BluetoothGattService AService = new BluetoothGattService(           UUID.fromString(SERVICE_A),            BluetoothGattService.SERVICE_TYPE_PRIMARY);                                            AService.addCharacteristic(read1Characteristic);         AService.addCharacteristic(read2Characteristic);         AService.addCharacteristic(writeCharacteristic);                 // Add notify characteristic here !!!                  mGattServer.addService(AService);                            mLeAdvertiser.startAdvertising(settingBuilder.build(),            advBuilder.build(), mAdvCallback);          }  public void stopAdvertise() {  if(null != mLeAdvertiser)   mLeAdvertiser.stopAdvertising(mAdvCallback);    mLeAdvertiser = null;   }}



MainActivity.java : (UI part)


package com.awind.presentsenseperipheral;import android.app.Activity;import android.bluetooth.BluetoothAdapter;import android.content.Intent;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.widget.CheckBox;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity { private BLEPeripheral blePeri;  private CheckBox  adverstiseCheckBox;     @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                adverstiseCheckBox = (CheckBox) findViewById(R.id.advertise_checkBox);                blePeri = new BLEPeripheral();                        adverstiseCheckBox.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                          if(true == adverstiseCheckBox.isChecked())             {              TextView textView;              textView = (TextView)findViewById(R.id.status_text);              textView.setText("advertising");              blePeri.startAdvertise();                           }             else             {              TextView textView;              textView = (TextView)findViewById(R.id.status_text);              textView.setText("disable");              blePeri.stopAdvertise();                           }            }        });                adverstiseCheckBox.setEnabled(false);             if(false == BLEPeripheral.isEnableBluetooth())     {            Intent intentBtEnabled = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);             // The REQUEST_ENABLE_BT constant passed to startActivityForResult() is a locally defined integer (which must be greater than 0), that the system passes back to you in your onActivityResult()             // implementation as the requestCode parameter.             int REQUEST_ENABLE_BT = 1;            startActivityForResult(intentBtEnabled, REQUEST_ENABLE_BT);                        Toast.makeText(this, "Please enable bluetooth and execute the application agagin.",     Toast.LENGTH_LONG).show();     }                   }    @Override    public void onResume(){             super.onResume();                               int sts;        sts = blePeri.init(this);                                if(0  > sts)     {      if(-1 == sts)       Toast.makeText(this, "this device is without bluetooth module",         Toast.LENGTH_LONG).show();            if(-2 == sts)       Toast.makeText(this, "this device do not support Bluetooth low energy",          Toast.LENGTH_LONG).show();            if(-3 == sts)       Toast.makeText(this, "this device do not support to be a BLE peripheral, " +         "please buy nexus 6 or 9 then try again",          Toast.LENGTH_LONG).show();            finish();     }                   TextView textView;     textView = (TextView)findViewById(R.id.mac_text);          textView.setText(BLEPeripheral.getAddress());          adverstiseCheckBox.setEnabled(true);       }                @Override    protected void onStop() {        super.onStop();          }                @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.main, menu);        return true;    }        @Override    public boolean onOptionsItemSelected(MenuItem item) {        // Handle action bar item clicks here. The action bar will        // automatically handle clicks on the Home/Up button, so long        // as you specify a parent activity in AndroidManifest.xml.        int id = item.getItemId();        if (id == R.id.action_settings) {            return true;        }        return super.onOptionsItemSelected(item);    }}



activity_main.xml: (layout, very ugly)


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.awind.presentsenseperipheral.MainActivity" >    <TextView        android:id="@+id/status_text"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentLeft="true"        android:layout_alignParentTop="true"        android:layout_marginLeft="15dp"        android:layout_marginTop="25dp"        android:text="Disable"        android:textAppearance="?android:attr/textAppearanceMedium" />    <TextView        android:id="@+id/mac_text"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignTop="@+id/status_text"        android:layout_marginLeft="18dp"        android:layout_toRightOf="@+id/status_text"        android:text="00:11:22:AA:BB:CC"        android:textAppearance="?android:attr/textAppearanceLarge" />    <CheckBox        android:id="@+id/advertise_checkBox"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignLeft="@+id/status_text"        android:layout_below="@+id/mac_text"        android:layout_marginTop="34dp"        android:text="Advertise" /></RelativeLayout>



    I do not like to write too much explanation in here, One said: if you could implement, you understand it; if you could not, you know about nothing of it .

About notification characteristic:


Replace the line :

// Add nodify characteristic here !!! 
As :


final String  CHAR_NOTIFY = "00fffB-0000-1000-8000-00805f9b34fb";          final BluetoothGattCharacteristic notifyCharacteristic = new BluetoothGattCharacteristic(             UUID.fromString(CHAR_NOTIFY),              BluetoothGattCharacteristic.PROPERTY_NOTIFY,             BluetoothGattCharacteristic.PERMISSION_READ             );                notifyCharacteristic.setValue(new String("0"));        presentSenseService.addCharacteristic(notifyCharacteristic);                  final Handler handler = new Handler();                Thread thread = new Thread() {         int i = 0;                              @Override            public void run() {                             try {                    while(true) {                        sleep(1500);                        handler.post(this);                                                List<BluetoothDevice> connectedDevices                          = mManager.getConnectedDevices(BluetoothProfile.GATT);                                                if(null != connectedDevices)                        {                         notifyCharacteristic.setValue(String.valueOf(i).getBytes());                                                 mGattServer.notifyCharacteristicChanged(connectedDevices.get(0),                          notifyCharacteristic, false);                        }                        i++;                    }                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        };        thread.start();


    That is, create a thread , that updates value and send a signal to BluetoothGattServer to note the value has been change.


  相关解决方案