Aside from those who usually develop apps, Bluetooth Low Energy is used for electronic work etc. For those who say "I want to bite a little" (mainly me), Android's BLE API feels a little difficult to use.
I thought it was a difficult reason to do various things, so I developed it with the aim of making the library as simple and easy to use as possible.
This library simplifies operation and requires basic knowledge about BLE. We recommend reading Getting Started with Bluetooth Low Energy (Make: PROJECTS) Kevin Townsend.
For those who are accustomed to it, RxAndroidBLE and [Bletia: Handle Android BLE with modern API](https://qiita.com/izumin5210/ items / 374d532a252c15aeb484) is recommended. It's probably much cleaner and bug-free.
... Why did you make such a library?
All asynchronous processing related to BLE of Android API has been changed to ** synchronous processing. ** ** Originally I should have used promises, but I prioritized simplicity.
So, for example, if you want to communicate with the BBC micro: bit, you can do it with the following code. (Actually, you need to run it in a Thread separate from the UI.)
private LazyBLEWrapper ble = new LazyBLEWrapper();
private final String LEDServiceUUID = "E95DD91D-251D-470A-A062-FA1922DFA9A8";
private final String LEDTextCharacteristicUUID = "E95D93EE-251D-470A-A062-FA1922DFA9A8";
private final String ButtonServiceUUID = "E95D9882-251D-470A-A062-FA1922DFA9A8";
private final String ButtonACharacteristicUUID = "E95DDA90-251D-470A-A062-FA1922DFA9A8";
private final String UARTServiceUUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
private final String UARTTxCharacteristicUUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
private final String UARTRxCharacteristicUUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
private final int timeout = 30*1000;
//scan. It is a prefix match
BluetoothDevice device = ble.scanDevice(this,"BBC micro:bit", timeout);
//Connect
ble.connect(this,device, timeout);
//Callback when disconnected(Any)
ble.setDisconnectCallback(new LazyBLEWrapper.DisconnectCallback() {
@Override
public void onDisconnect() {
Log.e(TAG,"Disconnected!");
}
});
//To LED"Hello World"And write
ble.writeData("Hello World",LEDServiceUUID,LEDTextCharacteristicUUID, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,timeout);
//Acquires and displays the button press status
Integer dat = ble.readDataInt(ButtonServiceUUID,ButtonACharacteristicUUID,BluetoothGattCharacteristic.FORMAT_UINT8,0,timeout);
Log.d(TAG, dat.toString());
//Set Notification
ble.setNotify(ButtonServiceUUID,ButtonACharacteristicUUID,true,timeout);
//Set Indication
ble.setIndicate(UARTServiceUUID,UARTTxCharacteristicUUID,true,timeout);
//Set Callback. This is executed by UIThread.
ble.setNotificationCallback(new LazyBLEWrapper.NotificationCallback() {
@Override
public void onNotification(BluetoothGattCharacteristic characteristic) {
if(ble.isMatchCharacteristicUUID(characteristic,ButtonACharacteristicUUID)) {
Integer ButtonA = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
Log.d(TAG, ButtonA.toString());
}
if(ble.isMatchCharacteristicUUID(characteristic,UARTTxCharacteristicUUID)) {
Log.d(TAG, characteristic.getStringValue(0));
}
}
});
//UART transmission
ble.writeData("Hello UART\n",UARTServiceUUID,UARTRxCharacteristicUUID,BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,timeout);
//Disconnect
ble.disconnect(timeout);
//ble.forceDisconnect();
If you want to use deep functions, you can use the standard API in the first place, so narrow down the functions I implemented it simply focusing on ** frequently used functions **.
So the use case is
Only.
Since it is a synchronous method, you can write it in a simple flow. (Though threads are required) Since it is not asynchronous, all methods with latency have a timeout specification.
The process of acquiring detailed location information authority required when using BLE on Android 6.0 or higher is also included as a bonus.
Characteristic operations (setValue, getValue, etc.) at the time of Notification are not included, so It uses standard Android API methods.
Basically, it is designed to return the Android BLE API object as it is, so If you want to do small things, you can use the API in that area.
Download here For simplicity, Android 5.0 or later is not supported. The license is a zlib license.
We have confirmed the operation only on Nexus 5X (Android 8.0).
Terminal settings and runtime permissions are also provided for simple implementation. It's okay to run these methods in a UI thread. (It will be completed without waiting time.)
v0.13: You no longer keep the Context internally, so you need to pass the context or activity.
//Check if the BLE function of the terminal is enabled. Before communication etc.
boolean isBluetoothFeatureEnabled()
//Make a request to enable the Bluetooth function of the terminal.
void requestTurnOnBlueTooth(AppCompatActivity activity)
//Check if the terminal supports the BLE function.
boolean isBluetoothFeatureSupported(Context context)
//Check if you have detailed location permissions.
//Required for BLE scan.
boolean isPermitted(Context context)
//Request detailed location permissions.
//Originally, it is done by Activity and is not recommended, but for the time being, it can be used when it is troublesome such as when making a prototype.
//Originally, it is necessary to receive the intent and process it, but it works without it, so it is not implemented. Please implement on the Activity side.
void permissionRequest(AppCompatActivity activity)
For the time being, I narrowed it down to the following three types, which I wonder if this is all I need.
v0.13: You no longer keep the Context internally, so you need to pass the context or activity. Also, it has been changed so that the UI thread internally calls BLE-related processing.
//Scan the surrounding peripherals.
//Continue scanning until timed out, ArrayList in order of discovery<BluetoothDevice>Store in and return.
//If not found, returns 0 lists.
ArrayList<BluetoothDevice> scanDevice(Context context, int timeOut) throws IOException
//Scan the surrounding peripherals.
//Checks the device name with a prefix match and returns the first matching device immediately.
//If not found, raise an exception.
BluetoothDevice scanDevice(Context context, String deviceName, int timeOut) throws IOException
//Terminal bonding(Pairing)Search from the list of completed peripherals.
//Checks the device name with a prefix match and returns the first matching device immediately.
//If not found, it returns null.
BluetoothDevice scanBondedDevice(String deviceName)
It seems to be troublesome to handle GATT objects (many callbacks), so I held it. Therefore, it is simple and has no return value.
v0.13: You no longer keep the Context internally, so you need to pass the context or activity. Also, it has been changed so that the UI thread internally calls BLE-related processing. The disconnection process is now more polite and there is a waiting time. For disconnection at the end of the application, we recommend force Disconnect without waiting time. Only one GATT connection can be held internally per instance. v0.14: Now throws an exception when trying to connect when connected.
//Connect to Device and perform a service scan. GATT objects are kept internally.
//(To perform cutting processing etc. automatically)
//Raises an exception if the connection fails.
void connect(Context context, BluetoothDevice device, int timeOut) throws IOException
//Disconnect from Device.
void disconnect(int timeOut)
//Forcibly disconnect from Device(Disconnection process in the old version)
//This can be done in a UI thread
void forceDisconnect()
If it's just characteristic reading and writing, it has been improved in v0.11 to make it simpler. Search for services, search for characteristics, assign values, etc. in one line.
It's a lot of reading, but I matched it with the Android API. All available formats are supported. On the other hand, for writing, float was omitted. Because it was a style that seemed a little difficult to use The decision is that there are not many opportunities to write floating point numbers.
//**********reading***********
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//The latest data is obtained from the peripheral, interpreted with the specified format / offset, and returned as an integer type.
//Raises an exception when communication fails.
int readDataInt(String ServiceUUID,String CharacteristicUUID,int formatType,int offset,int timeOut) throws IOException
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//The latest data is obtained from the peripheral, interpreted with the specified format / offset, and returned as a floating point type.
//Raises an exception when communication fails.
float readDataFloat(String ServiceUUID,String CharacteristicUUID,int formatType,int offset,int timeOut) throws IOException
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//Get the latest data from the peripheral and return it as a byte string.
//Raises an exception when communication fails.
byte[] readDataValue(String ServiceUUID,String CharacteristicUUID,int timeOut) throws IOException
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//Get the latest data from the peripheral, interpret it at the specified offset and return it as a string.
//Raises an exception when communication fails.
String readDataString(String ServiceUUID,String CharacteristicUUID,int offset,int timeOut) throws IOException
//**********writing***********
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//Set the specified character string as the content to the peripheral with the specified writing method.
//Raises an exception when communication fails.
void writeData(String string,String ServiceUUID,String CharacteristicUUID,int writeType,int timeOut) throws IOException
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//Set the specified byte string as the content to the peripheral with the specified writing method.
//Raises an exception when communication fails.
void writeData(byte[] value,String ServiceUUID,String CharacteristicUUID,int writeType,int timeOut) throws IOException
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//Set the specified integer as the content to the peripheral with the specified writing method.
//Raises an exception when communication fails.
void writeData(int num,int formatType,int offset,String ServiceUUID,String CharacteristicUUID,int writeType,int timeOut) throws IOException
The formatType is as follows.
BluetoothGattCharacteristic.FORMAT_FLOAT //(32-bit float)
BluetoothGattCharacteristic.FORMAT_SFLOAT //(16-bit float)
BluetoothGattCharacteristic.FORMAT_SINT16
BluetoothGattCharacteristic.FORMAT_SINT32
BluetoothGattCharacteristic.FORMAT_SINT8
BluetoothGattCharacteristic.FORMAT_UINT16
BluetoothGattCharacteristic.FORMAT_UINT32
BluetoothGattCharacteristic.FORMAT_UINT8
The writeType is as follows.
BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
BluetoothGattCharacteristic.WRITE_TYPE_SIGNED
Since the notification settings are also standard, we have made it possible to write them in abbreviated form. Since setNotify is the setting on the peripheral side and setCallback is the setting on the Android side, both are required.
However, only one callback can be registered with setCallback, and the characteristic is internally created. Since it is a form of checking and branching, setCallback only needs to be done once at the beginning of the application.
From v0.12, Indication is supported.
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//Enables / disables the notification setting on the peripheral side for that characteristic.
//Raises an exception when communication fails.
void setNotify(String ServiceUUID,String CharacteristicUUID,boolean enable,int timeOut) throws IOException
//Get the service characteristic from the specified service UUID string, characteristic UUID string,
//Enable / disable the Indication setting on the peripheral side for that characteristic.
//Raises an exception when communication fails.
void setIndicate(String ServiceUUID,String CharacteristicUUID,boolean enable,int timeOut) throws IOException
v0.14: Please set null to cancel the callback.
//Set the callback when a notification is received from the peripheral.
void setNotificationCallback(final NotificationCallback callback)
//Callback when disconnected
void setDisconnectCallback(final DisconnectCallback callback){
//Check if the characteristic UUID and string UUID are located
//Ture if they match, false otherwise
//It can be used to identify the characteristic sent by Notification or Indication.
boolean isMatchCharacteristicUUID(BluetoothGattCharacteristic characteristic,String CharacteristicUUID)
//Callback interface for notifications
interface NotificationCallback {
void onNotification(BluetoothGattCharacteristic characteristic);
}
//Callback interface on disconnect
interface DisconnectCallback {
void onDisconnect();
}
This is the characteristic operation provided by v0.10. You can get the raw characteristic, so please use this if you want to perform detailed operations.
If you just read and write, grab the service, then the characteristic, It's a little complicated because you need at least 4 lines in the order of operation and issuance.
v0.13: Now runs internally in the UI thread. Enhanced null checking.
//Get the service from the UUID string using the searched service information.
//If not found, it returns null.
BluetoothGattService getService(String serviceUUID)
//Use the searched service information to get the characteristic from the UUID string.
//BluetoothGattService acquired by getService is required to determine the characteristic with the same UUID.
//If not found, it returns null.
BluetoothGattCharacteristic getCharacteristicInService(BluetoothGattService service, String CharacteristicUUID)
//Reflect the contents of the current characteristic in the peripheral.
//Raises an exception when communication fails.
void writeCharacteristic(BluetoothGattCharacteristic characteristic, int timeOut) throws IOException
//Gets the latest characteristic content from the peripheral and returns it.
//(At this time, the original characteristic is probably unchanged.)
//Raises an exception when communication fails.
BluetoothGattCharacteristic readCharacteristic(BluetoothGattCharacteristic characteristic, int timeOut) throws IOException
Descriptor operation. Used internally for Notification settings. Note that I haven't tested much.
v0.13: Now runs internally in the UI thread. Enhanced null checking.
//Write the descriptor to the peripheral.
//Raises an exception when communication fails.
void writeDescriptor(BluetoothGattDescriptor descriptor, int timeOut) throws IOException
//Read descriptors from peripherals.
//Raises an exception when communication fails.
BluetoothGattDescriptor readDescriptor(BluetoothGattDescriptor descriptor, int timeOut) throws IOException
//Enables / disables the notification setting on the peripheral side of a specific characteristic.
//Raises an exception when communication fails.
void setNotification(BluetoothGattCharacteristic characteristic,boolean enable,int timeOut) throws IOException
//Enables / disables the Indication setting on the peripheral side of a specific characteristic.
//Raises an exception when communication fails.
void setIndication(BluetoothGattCharacteristic characteristic,boolean enable,int timeOut) throws IOException
//Pass the Bluetooth Gatt that is held inside. If it is null, it is disconnected.
//Please note that it will be invalidated without permission when communication is disconnected.
BluetoothGatt getGatt()
//Enables or disables very verbose logs.
//Enabled by default(I think it's often used for prototyping)
//By passing false, only fatal logs will be output.
void setDebug(boolean f)
//Check if there is a method running
//boolean getLockState()
//Forced opening
//void forceUnlock()
It's not a library method, but it's a favorite guy.
//Ask the OS to bond with the specified device.
//What kind of bonding is done depends on the design of the peripheral.
device.createBond();
//Set the value characteristically.
//Use with writeCharacteristic.
characteristic.setValue("Hello World");
//Extracts the value from the characteristic in the specified format.
//Use with readCharacteristic.
characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset);
It was created by Android beginners and java beginners for more beginners, I'm a beginner for a few minutes, so I think there are various strange things.
If you find a more useful library or something strange here, please let us know in the comments.
I have referred to the following sites very much.
The various pains I experienced during development were already written two years ago. #kyobashidex that Android BLE talked hard on kyobashi.dex
And here. 5 TIPS FOR BLUETOOTH LOW ENERGY (BLE) ON ANDROID
We have confirmed the operation with Nexus 5X (Android 8.0) and BBC micro: bit. Please refer to this site for the preparation on the micro: bit side. Try BLE communication with Micro: bit using only a browser using the Web Bluetooth API.
AndroidManifest.xml
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<uses-feature android:name="android.hardware.location.gps" />
MainActivity.java
package jp.ne.sakura.sabowl.gpsnmeajp.bletest;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
...
final String LEDServiceUUID = "E95DD91D-251D-470A-A062-FA1922DFA9A8";
final String LEDTextCharacteristicUUID = "E95D93EE-251D-470A-A062-FA1922DFA9A8";
final String ButtonServiceUUID = "E95D9882-251D-470A-A062-FA1922DFA9A8";
final String ButtonACharacteristicUUID = "E95DDA90-251D-470A-A062-FA1922DFA9A8";
private LazyBLEWrapper ble = new LazyBLEWrapper(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
ble.setDebug(true);
if(!ble.isBluetoothFeatureSupported(this))
{
Toast.makeText(this, "It is a terminal that cannot use Bluetooth", Toast.LENGTH_LONG).show();
finish();
}
if(!ble.isPermitted(this)){
ble.permissionRequest(this);
}
}
void connect()
{
try {
//Device detection.
//ArrayList<BluetoothDevice> result = ble.scanDevice(this,5000); //Scan all devices
//BluetoothDevice device = ble.scanDevice(this,"BBC micro:bit", 5000); //Name scan with prefix match
BluetoothDevice device = ble.scanBondedDevice("BBC micro:bit"); //Search from the bonded list.(No detailed position authority required)
//device.createBond();//bonding
//Connect
ble.connect(this,device, 30000);
ble.setDisconnectCallback(new LazyBLEWrapper.DisconnectCallback() {
@Override
public void onDisconnect() {
Log.e(TAG,"Disconnected!");
}
});
//LED writing
ble.writeData("Hello",LEDServiceUUID,LEDTextCharacteristicUUID, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,30000);
//Get button
Integer dat = ble.readDataInt(ButtonServiceUUID,ButtonACharacteristicUUID,BluetoothGattCharacteristic.FORMAT_UINT8,0,30000);
Log.d(TAG, dat.toString());
//Button notification settings
ble.setNotify(ButtonServiceUUID,ButtonACharacteristicUUID,true,30000);
//UART reception settings
ble.setIndicate(UARTServiceUUID,UARTTxCharacteristicUUID,true,30000);
ble.setNotificationCallback(new LazyBLEWrapper.NotificationCallback() {
@Override
public void onNotification(BluetoothGattCharacteristic characteristic) {
if(ble.isMatchCharacteristicUUID(characteristic,ButtonACharacteristicUUID)) {
Integer ButtonA = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
Log.d(TAG, ButtonA.toString());
mText.setText(ButtonA.toString());
}
if(ble.isMatchCharacteristicUUID(characteristic,UARTTxCharacteristicUUID)) {
Log.d(TAG, characteristic.getStringValue(0));
}
}
});
//UART transmission
ble.writeData("Hello UART\n",UARTServiceUUID,UARTRxCharacteristicUUID,BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT,30000);
}catch (Exception e){
Log.e(TAG,"connect",e);
ble.forceDisconnect();
}
}
void disconnect(){
try {
// ble.setNotify(ButtonServiceUUID,ButtonACharacteristicUUID,false,30000);
// ble.setDisconnectCallback(null);
// ble.setNotificationCallback(null);
ble.disconnect(30*1000);
// finish();
}catch (Exception e){
Log.e(TAG,"disconnect",e);
ble.forceDisconnect();
}
}
public void onClick(View v) {
Button btn = (Button)v;
Log.i(TAG,"onClick");
switch( btn.getId() ){
//When the button is pressed
case R.id.buttonConnect:
//BLE processing
if(ble.isBluetoothFeatureEnabled())
{
//Do not run on the main thread as it will lock(Will not come back for the rest of my life))
new Thread(new Runnable() {
@Override
public void run() {
connect();
}
}).start();
}else{
ble.requestTurnOnBlueTooth(this);
}
mText.setText("Wow!");
break;
case R.id.buttonDisconnect:
//Do not run on the main thread as it will lock(Will not come back for the rest of my life))
new Thread(new Runnable() {
@Override
public void run() {
disconnect();
}
}).start();
break;
default:
break;
}
}
Recommended Posts