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.

时间: 2024-10-06 15:21:29

Android as Bluetooth Low Energy Peripherial (GATT server).的相关文章

Android Bluetooth Low Energy官方文档翻译

Android Bluetooth Low Energy官方文档翻译 Android4.3(API18)为Bluetooth Low Energy(简称BLE)的核心功能提供了平台支撑,App能够通过它用来发现设备,查询服务,以及读写特性.与传统的蓝牙相比,BLE设计的最大特征就是低功耗.这使得Android的APP能够与具备低功耗的BLE设备进行通信,比如距离传感器,心跳检测,健身设备等等. 关键术语和概念 下面是一些关于BLE的核心术语和概念 Generic Attribute Profil

Android Bluetooth Low Energy(Android低功耗蓝牙)

Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的API,应用程序通过这些api可以扫描设备.查询services,读写设备的characteristics(属性特征).对比传统的蓝牙,BLE的设计能够显著减低功耗.这让Android应用程序与BLE设备之间的低功耗通讯成为可能,例如距离传感器.心率监视器.健身设备等等. 1.关键术语和概念 1.1 下面是一些BLE关键术语和概念的摘要: * Gener

Android bluetooth low energy (ble) writeCharacteristic delay callback

I am implementing a application on Android using BLE Api (SDK 18), and I have a issue that the transfer data process is delay very slow. This is my log. 03-12 16:20:05.121: D/BluetoothGatt(13578): writeCharacteristic() - uuid: ... 03-12 16:20:06.272:

Bluetooth Low Energy(低功耗蓝牙)-For蓝牙4.x

此文翻译至Android API里的Bluetooth Low Energy,希望对大家有所帮助.谢谢. Android4.3(API版本18)介绍了内置平台支持BLE的中心角色,并且提供了相关API,高大尚的程序员们可以使用这些API来扫描设备.查询服务(指服务端进程).读写特性值(指特定的字符).与经典蓝牙不同的是,BLE的设计是为了提供显著的低功耗支持.这使得Android应用可以仅需很低的功耗与BLE设备进行通信,心跳频率(不是人的心跳,是指发送心跳包检测设备是否还在),监听适配设备等等

Experiments with Bluetooth Low Energy (4.0) under Linux

Experiments with Bluetooth Low Energy (4.0) under Linux With iPhônes liberated from the apple authentication chip by it, and Android also having added support in the latest Android 4.3, Bluetooth Low Energy (4.0) is starting to look more interesting.

How to Implement Bluetooth Low Energy (BLE) in Ice Cream Sandwich

ShareThis - By Vikas Verma Bluetooth low energy (BLE) is a feature of Bluetooth 4.0 wireless radio technology, aimed at new, principally low-power and low-latency, applications for wireless devices within a short range. As I discussed in my previous

Bluetooth Low Energy 介绍

1.简介 BLE(Bluetooth Low Energy,低功耗蓝牙)是对传统蓝牙BR/EDR技术的补充.尽管BLE和传统蓝牙都称之为蓝牙标准,且共享射频,但是,BLE是一个完全不一样的技术.BLE不具备和传统蓝牙BR/EDR的兼容性.它是专为小数据率.离散传输的应用而设计的.通信距离上也有改变,传统蓝牙的传输距离几十米到几百米不等,BLE则规定为100米. 2.低功耗蓝牙(BLE) 低功耗蓝牙分为单模(Bluetooth Smart)和双模(Bluetooth Smart Ready)两种设

Overview and Evaluation of Bluetooth Low Energy: An Emerging Low-Power Wireless Technology

转自:http://www.mdpi.com/1424-8220/12/9/11734/htm Sensors 2012, 12(9), 11734-11753; doi:10.3390/s120911734 Article Carles Gomez 1,*, Joaquim Oller 2 and Josep Paradells 2 1 Universitat Politècnica de Catalunya/Fundació i2Cat, C/Esteve Terradas, 7, Cast

Bluetooth low energy介绍

1. 介绍 Bluetooth low energy,也称BLE(低功耗蓝牙),在4.0规范中提出 BLE分为两种设备 - 单模(single-mode): Logo为「Bluetooth?Smart」 - 双模(dual-mode): Logo为「Bluetooth?Smart Ready」 .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New&quo