問了幾個月後,我設法解決了我的問題。那時我忘了分享我的解決方案。無論如何,遲到比從未更好!
這是我在BLE上測試android-TI sensortag通信時編寫的一些代碼。
完整的例子可在https://github.com/maxhelskens/SmergyBicycle
package com.example.max.smergy;
import android.annotation.TargetApi;
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.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@TargetApi(21)
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
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");
/* magneto Service */
private static final UUID MAGNETO_SERVICE = UUID.fromString("F000AA80-0451-4000-B000-000000000000");
private static final UUID MAGNETO_DATA_CHAR = UUID.fromString("f000aa81-0451-4000-b000-000000000000");
private static final UUID MAGNETO_CONFIG_CHAR = UUID.fromString("f000aa82-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 mGatt;
private TextView mTemperature, mHumidity, mPressure;
private ProgressDialog mProgress;
private int REQUEST_ENABLE_BT = 1;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setProgressBarIndeterminate(true);
// Find the toolbar view inside the activity layout
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
// Sets the Toolbar to act as the ActionBar for this Activity window.
// Make sure the toolbar exists in the activity and is not null
setSupportActionBar(toolbar);
/*
* 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);
//Check if BLE is supported
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.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();
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
if (Build.VERSION.SDK_INT >= 21) {
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
filters = new ArrayList<ScanFilter>();
}
}
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);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
@Override
protected void onStop() {
super.onStop();
//Disconnect from any active tag connection
if (mGatt != null) {
mGatt.close();
mGatt = null;
}
}
@Override
protected void onDestroy() {
if (mGatt == null) {
return;
}
mGatt.close();
mGatt = null;
super.onDestroy();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_CANCELED) {
//Bluetooth not enabled.
finish();
return;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
// Menu icons are inflated just as they were with actionbar
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.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.bluetoothSearch:
mDevices.clear();
startScan();
//scanLeDevice(true);
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
*/
connectToDevice(device);
//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() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
setProgressBarIndeterminateVisibility(true);
mHandler.postDelayed(mStopRunnable, 2500);
}
private void stopScan() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
setProgressBarIndeterminateVisibility(false);
}
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
Log.i("onLeScan", device.toString());
mDevices.put(device.hashCode(), device);
//Update the overflow menu
invalidateOptionsMenu();
}
};
/*
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}, SCAN_PERIOD);
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mLEScanner.startScan(filters, settings, mScanCallback);
}
} else {
if (Build.VERSION.SDK_INT < 21) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
} else {
mLEScanner.stopScan(mScanCallback);
}
}
}
*/
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
Log.i("callbackType", String.valueOf(callbackType));
Log.i("result", result.toString());
BluetoothDevice btDevice = result.getDevice();
mDevices.put(btDevice.hashCode(), btDevice);
//Update the overflow menu
invalidateOptionsMenu();
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
@Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
public void connectToDevice(BluetoothDevice device) {
if (mGatt == null) {
mGatt = device.connectGatt(this, false, gattCallback);
stopScan();
}
}
/*
* 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 final BluetoothGattCallback gattCallback = 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 0:
Log.d(TAG, "Enabling humidity");
characteristic = gatt.getService(HUMIDITY_SERVICE)
.getCharacteristic(HUMIDITY_CONFIG_CHAR);
characteristic.setValue(new byte[] {0x01});
break;
case 1:
Log.d(TAG, "Enabling magneto");
characteristic = gatt.getService(MAGNETO_SERVICE)
.getCharacteristic(MAGNETO_CONFIG_CHAR);
characteristic.setValue(new byte[] {(byte)0x7F, (byte)0x00});
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 0:
Log.d(TAG, "Reading humidity");
characteristic = gatt.getService(HUMIDITY_SERVICE)
.getCharacteristic(HUMIDITY_DATA_CHAR);
break;
case 1:
Log.d(TAG, "Reading magneto");
characteristic = gatt.getService(MAGNETO_SERVICE)
.getCharacteristic(MAGNETO_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 0:
Log.d(TAG, "Set notify humidity");
characteristic = gatt.getService(HUMIDITY_SERVICE)
.getCharacteristic(HUMIDITY_DATA_CHAR);
break;
case 1:
Log.d(TAG, "Set notify magneto");
characteristic = gatt.getService(MAGNETO_SERVICE)
.getCharacteristic(MAGNETO_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.i("onConnectionStateChange", "Status: " + status);
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.close();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
List<BluetoothGattService> services = gatt.getServices();
Log.i("onServicesDiscovered", services.toString());
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) {
Log.i("onCharacteristicRead", characteristic.toString());
//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 (MAGNETO_DATA_CHAR.equals(characteristic.getUuid())) {
mHandler.sendMessage(Message.obtain(null, MSG_MAGNETO, 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 (MAGNETO_DATA_CHAR.equals(characteristic.getUuid())) {
mHandler.sendMessage(Message.obtain(null, MSG_MAGNETO, 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_MAGNETO = 104;
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_MAGNETO:
characteristic = (BluetoothGattCharacteristic) msg.obj;
if (characteristic.getValue() == null) {
Log.w(TAG, "Error obtaining magneto value");
return;
}
updateMagnetoValues(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));
}
ArrayList<Double> magnets = new ArrayList<>();
double average = 0;
int count = -1;
int wait5 = 0;
private void updateMagnetoValues(BluetoothGattCharacteristic characteristic) {
double magnet = SensorTagData.extractMagnetoX(characteristic);
Log.e("MAGNETO", "" + magnet);
magnet = Math.abs(magnet);
if (wait5 == 0) {
if (magnet > average + 300 || magnet < average - 300) {
wait5 = 6;
count ++;
mPressure.setText("" + count);
}
}
else {
wait5 --;
}
if (magnets.size() >= 5) {
magnets.remove(0);
}
magnets.add(magnet);
double sum = 0;
for (int i = 0; i < magnets.size(); i++) {
sum += magnets.get(i);
}
average = sum/magnets.size();
average = magnets.get(2);
//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));
}
}
BLE設備發送數據時,無論是通過通知服務。或者您可以閱讀該服務,其中大部分數據都是十六進制格式,因此您必須將其轉換。所以代碼更具體的答案 – AAnkit