«

Android as Bluetooth Low Energy Peripherial (GATT server).

时间:2024-3-2 18:14     作者:韩俊     分类: Android




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.


标签: android

热门推荐