Back to project page accessory-samples.
The source code is released under:
Copyright (c) 2012 Wireless Designs, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in ...
If you think the Android project accessory-samples listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.example.bluetoothgatt; /*from ww w . j a v a 2 s .c o m*/ import android.app.Activity; import android.app.ProgressDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.util.SparseArray; import android.view.Menu; import android.view.MenuItem; import android.view.Window; import android.widget.TextView; import android.widget.Toast; import java.util.UUID; /** * Created by Dave Smith * Double Encore, Inc. * MainActivity */ public class MainActivity extends Activity implements BluetoothAdapter.LeScanCallback { private static final String TAG = "BluetoothGattActivity"; private static final String DEVICE_NAME = "SensorTag"; /* Humidity Service */ private static final UUID HUMIDITY_SERVICE = UUID.fromString("f000aa20-0451-4000-b000-000000000000"); private static final UUID HUMIDITY_DATA_CHAR = UUID.fromString("f000aa21-0451-4000-b000-000000000000"); private static final UUID HUMIDITY_CONFIG_CHAR = UUID.fromString("f000aa22-0451-4000-b000-000000000000"); /* Barometric Pressure Service */ private static final UUID PRESSURE_SERVICE = UUID.fromString("f000aa40-0451-4000-b000-000000000000"); private static final UUID PRESSURE_DATA_CHAR = UUID.fromString("f000aa41-0451-4000-b000-000000000000"); private static final UUID PRESSURE_CONFIG_CHAR = UUID.fromString("f000aa42-0451-4000-b000-000000000000"); private static final UUID PRESSURE_CAL_CHAR = UUID.fromString("f000aa43-0451-4000-b000-000000000000"); /* Client Configuration Descriptor */ private static final UUID CONFIG_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); private BluetoothAdapter mBluetoothAdapter; private SparseArray<BluetoothDevice> mDevices; private BluetoothGatt mConnectedGatt; private TextView mTemperature, mHumidity, mPressure; private ProgressDialog mProgress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.activity_main); setProgressBarIndeterminate(true); /* * We are going to display the results in some text fields */ mTemperature = (TextView) findViewById(R.id.text_temperature); mHumidity = (TextView) findViewById(R.id.text_humidity); mPressure = (TextView) findViewById(R.id.text_pressure); /* * Bluetooth in Android 4.3 is accessed via the BluetoothManager, rather than * the old static BluetoothAdapter.getInstance() */ BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); mBluetoothAdapter = manager.getAdapter(); mDevices = new SparseArray<BluetoothDevice>(); /* * A progress dialog will be needed while the connection process is * taking place */ mProgress = new ProgressDialog(this); mProgress.setIndeterminate(true); mProgress.setCancelable(false); } @Override protected void onResume() { super.onResume(); /* * We need to enforce that Bluetooth is first enabled, and take the * user to settings to enable it if they have not done so. */ if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { //Bluetooth is disabled Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivity(enableBtIntent); finish(); return; } /* * Check for Bluetooth LE Support. In production, our manifest entry will keep this * from installing on these devices, but this will allow test devices or other * sideloads to report whether or not the feature exists. */ if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "No LE Support.", Toast.LENGTH_SHORT).show(); finish(); return; } clearDisplayValues(); } @Override protected void onPause() { super.onPause(); //Make sure dialog is hidden mProgress.dismiss(); //Cancel any scans in progress mHandler.removeCallbacks(mStopRunnable); mHandler.removeCallbacks(mStartRunnable); mBluetoothAdapter.stopLeScan(this); } @Override protected void onStop() { super.onStop(); //Disconnect from any active tag connection if (mConnectedGatt != null) { mConnectedGatt.disconnect(); mConnectedGatt = null; } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Add the "scan" option to the menu getMenuInflater().inflate(R.menu.main, menu); //Add any device elements we've discovered to the overflow menu for (int i=0; i < mDevices.size(); i++) { BluetoothDevice device = mDevices.valueAt(i); menu.add(0, mDevices.keyAt(i), 0, device.getName()); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_scan: mDevices.clear(); startScan(); return true; default: //Obtain the discovered device to connect with BluetoothDevice device = mDevices.get(item.getItemId()); Log.i(TAG, "Connecting to "+device.getName()); /* * Make a connection with the device using the special LE-specific * connectGatt() method, passing in a callback for GATT events */ mConnectedGatt = device.connectGatt(this, false, mGattCallback); //Display progress UI mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Connecting to "+device.getName()+"...")); return super.onOptionsItemSelected(item); } } private void clearDisplayValues() { mTemperature.setText("---"); mHumidity.setText("---"); mPressure.setText("---"); } private Runnable mStopRunnable = new Runnable() { @Override public void run() { stopScan(); } }; private Runnable mStartRunnable = new Runnable() { @Override public void run() { startScan(); } }; private void startScan() { mBluetoothAdapter.startLeScan(this); setProgressBarIndeterminateVisibility(true); mHandler.postDelayed(mStopRunnable, 2500); } private void stopScan() { mBluetoothAdapter.stopLeScan(this); setProgressBarIndeterminateVisibility(false); } /* BluetoothAdapter.LeScanCallback */ @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { Log.i(TAG, "New LE Device: " + device.getName() + " @ " + rssi); /* * We are looking for SensorTag devices only, so validate the name * that each device reports before adding it to our collection */ if (DEVICE_NAME.equals(device.getName())) { mDevices.put(device.hashCode(), device); //Update the overflow menu invalidateOptionsMenu(); } } /* * In this callback, we've created a bit of a state machine to enforce that only * one characteristic be read or written at a time until all of our sensors * are enabled and we are registered to get notifications. */ private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { /* State Machine Tracking */ private int mState = 0; private void reset() { mState = 0; } private void advance() { mState++; } /* * Send an enable command to each sensor by writing a configuration * characteristic. This is specific to the SensorTag to keep power * low by disabling sensors you aren't using. */ private void enableNextSensor(BluetoothGatt gatt) { BluetoothGattCharacteristic characteristic; switch (mState) { case 0: Log.d(TAG, "Enabling pressure cal"); characteristic = gatt.getService(PRESSURE_SERVICE) .getCharacteristic(PRESSURE_CONFIG_CHAR); characteristic.setValue(new byte[] {0x02}); break; case 1: Log.d(TAG, "Enabling pressure"); characteristic = gatt.getService(PRESSURE_SERVICE) .getCharacteristic(PRESSURE_CONFIG_CHAR); characteristic.setValue(new byte[] {0x01}); break; case 2: Log.d(TAG, "Enabling humidity"); characteristic = gatt.getService(HUMIDITY_SERVICE) .getCharacteristic(HUMIDITY_CONFIG_CHAR); characteristic.setValue(new byte[] {0x01}); break; default: mHandler.sendEmptyMessage(MSG_DISMISS); Log.i(TAG, "All Sensors Enabled"); return; } gatt.writeCharacteristic(characteristic); } /* * Read the data characteristic's value for each sensor explicitly */ private void readNextSensor(BluetoothGatt gatt) { BluetoothGattCharacteristic characteristic; switch (mState) { case 0: Log.d(TAG, "Reading pressure cal"); characteristic = gatt.getService(PRESSURE_SERVICE) .getCharacteristic(PRESSURE_CAL_CHAR); break; case 1: Log.d(TAG, "Reading pressure"); characteristic = gatt.getService(PRESSURE_SERVICE) .getCharacteristic(PRESSURE_DATA_CHAR); break; case 2: Log.d(TAG, "Reading humidity"); characteristic = gatt.getService(HUMIDITY_SERVICE) .getCharacteristic(HUMIDITY_DATA_CHAR); break; default: mHandler.sendEmptyMessage(MSG_DISMISS); Log.i(TAG, "All Sensors Enabled"); return; } gatt.readCharacteristic(characteristic); } /* * Enable notification of changes on the data characteristic for each sensor * by writing the ENABLE_NOTIFICATION_VALUE flag to that characteristic's * configuration descriptor. */ private void setNotifyNextSensor(BluetoothGatt gatt) { BluetoothGattCharacteristic characteristic; switch (mState) { case 0: Log.d(TAG, "Set notify pressure cal"); characteristic = gatt.getService(PRESSURE_SERVICE) .getCharacteristic(PRESSURE_CAL_CHAR); break; case 1: Log.d(TAG, "Set notify pressure"); characteristic = gatt.getService(PRESSURE_SERVICE) .getCharacteristic(PRESSURE_DATA_CHAR); break; case 2: Log.d(TAG, "Set notify humidity"); characteristic = gatt.getService(HUMIDITY_SERVICE) .getCharacteristic(HUMIDITY_DATA_CHAR); break; default: mHandler.sendEmptyMessage(MSG_DISMISS); Log.i(TAG, "All Sensors Enabled"); return; } //Enable local notifications gatt.setCharacteristicNotification(characteristic, true); //Enabled remote notifications BluetoothGattDescriptor desc = characteristic.getDescriptor(CONFIG_DESCRIPTOR); desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); gatt.writeDescriptor(desc); } @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { Log.d(TAG, "Connection State Change: "+status+" -> "+connectionState(newState)); if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) { /* * Once successfully connected, we must next discover all the services on the * device before we can read and write their characteristics. */ gatt.discoverServices(); mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Discovering Services...")); } else if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_DISCONNECTED) { /* * If at any point we disconnect, send a message to clear the weather values * out of the UI */ mHandler.sendEmptyMessage(MSG_CLEAR); } else if (status != BluetoothGatt.GATT_SUCCESS) { /* * If there is a failure at any stage, simply disconnect */ gatt.disconnect(); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { Log.d(TAG, "Services Discovered: "+status); mHandler.sendMessage(Message.obtain(null, MSG_PROGRESS, "Enabling Sensors...")); /* * With services discovered, we are going to reset our state machine and start * working through the sensors we need to enable */ reset(); enableNextSensor(gatt); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //For each read, pass the data up to the UI thread to update the display if (HUMIDITY_DATA_CHAR.equals(characteristic.getUuid())) { mHandler.sendMessage(Message.obtain(null, MSG_HUMIDITY, characteristic)); } if (PRESSURE_DATA_CHAR.equals(characteristic.getUuid())) { mHandler.sendMessage(Message.obtain(null, MSG_PRESSURE, characteristic)); } if (PRESSURE_CAL_CHAR.equals(characteristic.getUuid())) { mHandler.sendMessage(Message.obtain(null, MSG_PRESSURE_CAL, characteristic)); } //After reading the initial value, next we enable notifications setNotifyNextSensor(gatt); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { //After writing the enable flag, next we read the initial value readNextSensor(gatt); } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { /* * After notifications are enabled, all updates from the device on characteristic * value changes will be posted here. Similar to read, we hand these up to the * UI thread to update the display. */ if (HUMIDITY_DATA_CHAR.equals(characteristic.getUuid())) { mHandler.sendMessage(Message.obtain(null, MSG_HUMIDITY, characteristic)); } if (PRESSURE_DATA_CHAR.equals(characteristic.getUuid())) { mHandler.sendMessage(Message.obtain(null, MSG_PRESSURE, characteristic)); } if (PRESSURE_CAL_CHAR.equals(characteristic.getUuid())) { mHandler.sendMessage(Message.obtain(null, MSG_PRESSURE_CAL, characteristic)); } } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { //Once notifications are enabled, we move to the next sensor and start over with enable advance(); enableNextSensor(gatt); } @Override public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) { Log.d(TAG, "Remote RSSI: "+rssi); } private String connectionState(int status) { switch (status) { case BluetoothProfile.STATE_CONNECTED: return "Connected"; case BluetoothProfile.STATE_DISCONNECTED: return "Disconnected"; case BluetoothProfile.STATE_CONNECTING: return "Connecting"; case BluetoothProfile.STATE_DISCONNECTING: return "Disconnecting"; default: return String.valueOf(status); } } }; /* * We have a Handler to process event results on the main thread */ private static final int MSG_HUMIDITY = 101; private static final int MSG_PRESSURE = 102; private static final int MSG_PRESSURE_CAL = 103; private static final int MSG_PROGRESS = 201; private static final int MSG_DISMISS = 202; private static final int MSG_CLEAR = 301; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { BluetoothGattCharacteristic characteristic; switch (msg.what) { case MSG_HUMIDITY: characteristic = (BluetoothGattCharacteristic) msg.obj; if (characteristic.getValue() == null) { Log.w(TAG, "Error obtaining humidity value"); return; } updateHumidityValues(characteristic); break; case MSG_PRESSURE: characteristic = (BluetoothGattCharacteristic) msg.obj; if (characteristic.getValue() == null) { Log.w(TAG, "Error obtaining pressure value"); return; } updatePressureValue(characteristic); break; case MSG_PRESSURE_CAL: characteristic = (BluetoothGattCharacteristic) msg.obj; if (characteristic.getValue() == null) { Log.w(TAG, "Error obtaining cal value"); return; } updatePressureCals(characteristic); break; case MSG_PROGRESS: mProgress.setMessage((String) msg.obj); if (!mProgress.isShowing()) { mProgress.show(); } break; case MSG_DISMISS: mProgress.hide(); break; case MSG_CLEAR: clearDisplayValues(); break; } } }; /* Methods to extract sensor data and update the UI */ private void updateHumidityValues(BluetoothGattCharacteristic characteristic) { double humidity = SensorTagData.extractHumidity(characteristic); mHumidity.setText(String.format("%.0f%%", humidity)); } private int[] mPressureCals; private void updatePressureCals(BluetoothGattCharacteristic characteristic) { mPressureCals = SensorTagData.extractCalibrationCoefficients(characteristic); } private void updatePressureValue(BluetoothGattCharacteristic characteristic) { if (mPressureCals == null) return; double pressure = SensorTagData.extractBarometer(characteristic, mPressureCals); double temp = SensorTagData.extractBarTemperature(characteristic, mPressureCals); mTemperature.setText(String.format("%.1f\u00B0C", temp)); mPressure.setText(String.format("%.2f", pressure)); } }