Documente BluetoothLeService

This commit is contained in:
Louis-Guillaume DUBOIS 2017-06-09 16:13:09 +02:00
parent 9bdd404fc6
commit 3e78c1e9b0
4 changed files with 88 additions and 10 deletions

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2013 The Android Open Source Project * Copyright (C) 2013 The Android Open Source Project
* Copyright (C) 2017 Louis-Guillaume Dubois * Copyright (C) 2017 CentraleSupélec
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -57,6 +57,7 @@ public class BluetoothLeService extends Service {
private static final int STATE_CONNECTING = 1; private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2; private static final int STATE_CONNECTED = 2;
// Nom des actions envoyées lors des Intents broadcastés
public final static String ACTION_GATT_CONNECTED = public final static String ACTION_GATT_CONNECTED =
"fr.cenralesupelec.students.clientble.ACTION_GATT_CONNECTED"; "fr.cenralesupelec.students.clientble.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED = public final static String ACTION_GATT_DISCONNECTED =
@ -73,6 +74,8 @@ public class BluetoothLeService extends Service {
// Implements callback methods for GATT events that the app cares about. For example, // Implements callback methods for GATT events that the app cares about. For example,
// connection change and services discovered. // connection change and services discovered.
// Envoie des Intents broadcastés pour permettre à SimpleDetailActivity de récupérer
// les valeurs reçues de lappareil BLE.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override @Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
@ -103,6 +106,13 @@ public class BluetoothLeService extends Service {
} }
} }
/**
* Renvoie dans une Intent broadcastée la valeur lue de la caractéristique
* demandée.
* @param gatt
* @param characteristic
* @param status
*/
@Override @Override
public void onCharacteristicRead(BluetoothGatt gatt, public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, BluetoothGattCharacteristic characteristic,
@ -111,8 +121,10 @@ public class BluetoothLeService extends Service {
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
if (GattConstants.SENSOR_CHARACTERISTIC_UUID.equals(uuid)) { if (GattConstants.SENSOR_CHARACTERISTIC_UUID.equals(uuid)) {
// Valeur du potentiomètre.
broadcastUpdate(ACTION_SENSOR_VALUE_AVAILABLE, characteristic); broadcastUpdate(ACTION_SENSOR_VALUE_AVAILABLE, characteristic);
} else if (GattConstants.WRITABLE_CHARACTERISTIC_UUID.equals(uuid)) { } else if (GattConstants.WRITABLE_CHARACTERISTIC_UUID.equals(uuid)) {
// Valeur de la caractéristique longue.
broadcastUpdate(ACTION_WRITABLE_VALUE_AVAILABLE, characteristic); broadcastUpdate(ACTION_WRITABLE_VALUE_AVAILABLE, characteristic);
} else { } else {
Log.w(TAG, "UUID non reconnue."); Log.w(TAG, "UUID non reconnue.");
@ -120,6 +132,13 @@ public class BluetoothLeService extends Service {
} }
} }
/**
* Renvoie dans une Intent broadcastée la valeur écrite de la caractéristique
* demandée.
* @param gatt
* @param characteristic
* @param status
*/
@Override @Override
public void onCharacteristicWrite(BluetoothGatt gatt, public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, BluetoothGattCharacteristic characteristic,
@ -128,8 +147,10 @@ public class BluetoothLeService extends Service {
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "Réusssite de lécriture de la caractéristique."); Log.d(TAG, "Réusssite de lécriture de la caractéristique.");
if (GattConstants.SENSOR_CHARACTERISTIC_UUID.equals(uuid)) { if (GattConstants.SENSOR_CHARACTERISTIC_UUID.equals(uuid)) {
// Valeur du potentiomètre ne devrait pas se produire (lecture seule.)
broadcastUpdate(ACTION_SENSOR_VALUE_AVAILABLE, characteristic); broadcastUpdate(ACTION_SENSOR_VALUE_AVAILABLE, characteristic);
} else if (GattConstants.WRITABLE_CHARACTERISTIC_UUID.equals(uuid)) { } else if (GattConstants.WRITABLE_CHARACTERISTIC_UUID.equals(uuid)) {
// Valeur de la caractéristique longue et éditable.
broadcastUpdate(ACTION_WRITABLE_VALUE_AVAILABLE, characteristic); broadcastUpdate(ACTION_WRITABLE_VALUE_AVAILABLE, characteristic);
} }
} else { } else {
@ -137,6 +158,12 @@ public class BluetoothLeService extends Service {
} }
} }
/**
* Renvoie dans une Intent broadcastée la valeur mise à jour dune caractéristique
* (en cas de notification par exemple.)
* @param gatt
* @param characteristic
*/
@Override @Override
public void onCharacteristicChanged(BluetoothGatt gatt, public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) { BluetoothGattCharacteristic characteristic) {
@ -152,50 +179,81 @@ public class BluetoothLeService extends Service {
} }
}; };
/**
* Méthode denvoi dune Intent broadcastée.
* @param action nom de laction
*/
private void broadcastUpdate(final String action) { private void broadcastUpdate(final String action) {
Log.d(TAG, "broadcastUpdate(String) appelé."); Log.d(TAG, "broadcastUpdate(String) appelé.");
final Intent intent = new Intent(action); final Intent intent = new Intent(action);
sendBroadcast(intent); sendBroadcast(intent);
} }
/**
* Méthode denvoi dune Intent broadcastée, avec la valeur dune caractéristique.
* @param action nom de laction
* @param characteristic caractéristique lue, écrite ou mise à jour (notifiée)
*/
private void broadcastUpdate(final String action, private void broadcastUpdate(final String action,
final BluetoothGattCharacteristic characteristic) { final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action); final Intent intent = new Intent(action);
Log.d(TAG, "broadcastUpdate(String, BluetoothGattChar.) appelé."); Log.d(TAG, "broadcastUpdate(String, BluetoothGattChar.) appelé.");
// Valeur brute de la caractéristique.
final byte[] data = characteristic.getValue(); final byte[] data = characteristic.getValue();
if (data != null && data.length > 0) { if (data != null && data.length > 0) {
// Si cest la valeur du potentiomètre
if (GattConstants.SENSOR_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) { if (GattConstants.SENSOR_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
// Lecture de lentier non signé, dun ou deux octets (lecture par le CAN sur 16 bits.)
final long value = final long value =
(data.length == 2) ? (data[0] << 8) & 0x0000ff00 | (data[1] << 0) & 0x000000ff (data.length == 2) ? (data[0] << 8) & 0x0000ff00 | (data[1] << 0) & 0x000000ff
: (data[0] << 0) & 0x000000ff; : (data[0] << 0) & 0x000000ff;
final long max = 65535; // 2^16 - 1 final long max = 65535; // 2^16 - 1 : valeur maximale (16 bits)
// Envoi dun pourcentage
final double percent = ((double) (100 * value)) / ((double) max); final double percent = ((double) (100 * value)) / ((double) max);
// Envoi sous forme dune chaîne de caractère, avec la date, pour affichage direct.
final String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG).format(new Date()); final String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.LONG).format(new Date());
intent.putExtra(EXTRA_DATA, String.format("%.3f %%\n(%s)", percent, date)); intent.putExtra(EXTRA_DATA, String.format("%.3f %%\n(%s)", percent, date));
} else { } else {
// Sinon, caractéristique longue éditable.
final StringBuilder stringBuilder = new StringBuilder(data.length); final StringBuilder stringBuilder = new StringBuilder(data.length);
//stringBuilder.append(String.format("%d", data));/ // Représentation au format hexadécimal.
//stringBuilder.append(" --- ");
for (byte byteChar : data) for (byte byteChar : data)
stringBuilder.append(String.format("%02X ", byteChar)); stringBuilder.append(String.format("%02X ", byteChar));
Log.d(TAG, String.format(stringBuilder.toString())); Log.d(TAG, String.format(stringBuilder.toString()));
// Envoi de la représentation ASCII puis sur une autre ligne, en hexadécimal.
intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
} }
} }
sendBroadcast(intent); sendBroadcast(intent);
} }
/**
* Classe permettant à une activité dappeler les méthodes du service.
*/
public class LocalBinder extends Binder { public class LocalBinder extends Binder {
BluetoothLeService getService() { BluetoothLeService getService() {
return BluetoothLeService.this; return BluetoothLeService.this;
} }
} }
/**
* Demande de lien à une activité.
* @param intent
* @return
*/
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return mBinder; return mBinder;
} }
/**
* Arrêt du lien avec une activité.
* @param intent
* @return
*/
@Override @Override
public boolean onUnbind(Intent intent) { public boolean onUnbind(Intent intent) {
// After using a given device, you should make sure that BluetoothGatt.close() is called // After using a given device, you should make sure that BluetoothGatt.close() is called
@ -205,6 +263,9 @@ public class BluetoothLeService extends Service {
return super.onUnbind(intent); return super.onUnbind(intent);
} }
/**
* Instance de la classe de liaison avec une activité.
*/
private final IBinder mBinder = new LocalBinder(); private final IBinder mBinder = new LocalBinder();
/** /**
@ -314,6 +375,11 @@ public class BluetoothLeService extends Service {
mBluetoothGatt.readCharacteristic(characteristic); mBluetoothGatt.readCharacteristic(characteristic);
} }
/**
* Écriture dune caractéristique sur le serveur BLE de lappareil connecté.
* @param characteristic caractéristique écrire
* @param data données brutes à envoyer pour écriture
*/
public void writeCharacterisitic(BluetoothGattCharacteristic characteristic, byte[] data) { public void writeCharacterisitic(BluetoothGattCharacteristic characteristic, byte[] data) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) { if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized"); Log.w(TAG, "BluetoothAdapter not initialized");
@ -335,7 +401,9 @@ public class BluetoothLeService extends Service {
Log.w(TAG, "BluetoothAdapter not initialized"); Log.w(TAG, "BluetoothAdapter not initialized");
return; return;
} }
Log.d(TAG, "setChar.Notification() appelé"); Log.d(TAG, "setCharacteristicNotification() appelé");
// Ne pas oublier décrire le descripteur de la caractéristique pour que les serveur
// BLE de lappareil connecté envoie les notifications.
if (GattConstants.SENSOR_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) { if (GattConstants.SENSOR_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(GattConstants.CHARACTERISTIC_CONFIG_UUID); BluetoothGattDescriptor descriptor = characteristic.getDescriptor(GattConstants.CHARACTERISTIC_CONFIG_UUID);
descriptor.setValue( descriptor.setValue(
@ -343,12 +411,16 @@ public class BluetoothLeService extends Service {
: BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
); );
try { try {
// Parfois plusieurs essais sont nécessaires (linterface BLE peut être
// occupée avec dautres opérations.)
while (!mBluetoothGatt.writeDescriptor(descriptor)) while (!mBluetoothGatt.writeDescriptor(descriptor))
sleep(500); sleep(500);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
// Demande à lappareil Android découter et de prendre en compte les notifications
// envoyées par lappareil connecté.
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled); mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
} }
@ -363,6 +435,10 @@ public class BluetoothLeService extends Service {
return mBluetoothGatt.getServices(); return mBluetoothGatt.getServices();
} }
/**
* Retourne notre service privé.
* @return
*/
public BluetoothGattService getPrivateService() { public BluetoothGattService getPrivateService() {
if (mBluetoothGatt == null) return null; if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getService(GattConstants.PRIVATE_SERVICE_UUID); return mBluetoothGatt.getService(GattConstants.PRIVATE_SERVICE_UUID);

View file

@ -41,6 +41,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
/** /**
* Activité proposée dans lapplication de démonstration, non utilisée dans notre application.
*
* For a given BLE device, this Activity provides the user interface to connect, display data, * For a given BLE device, this Activity provides the user interface to connect, display data,
* and display GATT services and characteristics supported by the device. The Activity * and display GATT services and characteristics supported by the device. The Activity
* communicates with {@code BluetoothLeService}, which in turn interacts with the * communicates with {@code BluetoothLeService}, which in turn interacts with the

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2013 The Android Open Source Project * Copyright (C) 2013 The Android Open Source Project
* Copyright (C) 2017 Louis-Guillaume Dubois * Copyright (C) 2017 CentraleSupélec
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View file

@ -21,7 +21,7 @@ import java.util.HashMap;
import java.util.UUID; import java.util.UUID;
/** /**
* This class includes a small subset of standard GATT attributes for demonstration purposes. * Stockage des UUID des services et caractéristiques BLE interrogées.
*/ */
public class GattConstants { public class GattConstants {
private static HashMap<String, String> attributes = new HashMap(); private static HashMap<String, String> attributes = new HashMap();
@ -40,7 +40,7 @@ public class GattConstants {
UUID.fromString(SENSOR_CHARACTERISTIC_UUID_STRING); UUID.fromString(SENSOR_CHARACTERISTIC_UUID_STRING);
// UUID (v4, cf. supra) de notre caractéristiuqe privée, lisible, éditable et notifiable, // UUID (v4, cf. supra) de notre caractéristiuqe privée, lisible, éditable et notifiable,
// de trois octets. // de vingt octets.
public static final String WRITABLE_CHARACTERISTIC_UUID_STRING = public static final String WRITABLE_CHARACTERISTIC_UUID_STRING =
"c093685d-005f-4d3c-8240-6d3020a2c608"; "c093685d-005f-4d3c-8240-6d3020a2c608";
public static final UUID WRITABLE_CHARACTERISTIC_UUID = public static final UUID WRITABLE_CHARACTERISTIC_UUID =
@ -54,7 +54,7 @@ public class GattConstants {
public static final UUID CHARACTERISTIC_CONFIG_UUID = public static final UUID CHARACTERISTIC_CONFIG_UUID =
UUID.fromString(CHARACTERISTIC_CONFIG_UUID_STRING); UUID.fromString(CHARACTERISTIC_CONFIG_UUID_STRING);
// UUID de services connus utilisés dans DeviceControlActivity // UUID de services connus utilisés dans DeviceControlActivity uniquement
static { static {
// Sample Services. // Sample Services.
attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information"); attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information");
@ -73,7 +73,7 @@ public class GattConstants {
attributes.put(WRITABLE_CHARACTERISTIC_UUID_STRING, "3-byte rw notif. char."); attributes.put(WRITABLE_CHARACTERISTIC_UUID_STRING, "3-byte rw notif. char.");
} }
// recherche du nom de services connus utilisé dans DeviceControlActivity // recherche du nom de services connus utilisé dans DeviceControlActivity uniquement
public static String lookup(String uuid, String defaultName) { public static String lookup(String uuid, String defaultName) {
String name = attributes.get(uuid); String name = attributes.get(uuid);
return name == null ? defaultName : name; return name == null ? defaultName : name;