Created
August 13, 2021 07:02
-
-
Save BEOKS/da53896099ec7819fc4ddc629283ed13 to your computer and use it in GitHub Desktop.
This gist is for connecting HM-10 Bluetooth Device with BLE(Bluetooth Low Eenergy) mode on Android. Public Reference is little bit complicated, so I made simple Helper Class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.myapplication | |
import android.app.Activity | |
import android.app.Service | |
import android.bluetooth.* | |
import android.content.Context | |
import android.content.pm.PackageManager | |
import android.widget.Toast | |
import android.bluetooth.BluetoothAdapter.LeScanCallback | |
import android.content.Intent | |
import android.os.Handler | |
import android.os.IBinder | |
import android.util.Log | |
/** | |
* ************************************** | |
* BLE(Bluetooth Low Energy) with Android | |
* ************************************** | |
* | |
* Warning! | |
* writeMessage and onMessageReceived method is not implemented yet! | |
* | |
* This class support android and HM-10 Bluetooth module connection with BLE mode. | |
* You can use this with simple four method, ( searchModule, connect, writeMessage and onMessageReceived ) | |
* if you want to use another ble module then, you have to modify UUID for Service Connection. | |
* | |
* Before Start, you have to set Permission in Manifest | |
* <uses-permission android:name="android.permission.BLUETOOTH"/> | |
* <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> | |
* <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> | |
* | |
* @constructor | |
* Before start, you have to set application context, | |
* then constructor will load required modules for BLE communication | |
* | |
* @author JAE SEONG LEE /lee01042000@gmail.com | |
* | |
* @Reference | |
* https://developer.android.com/guide/topics/connectivity/bluetooth/ble-overview | |
* https://developer.android.com/guide/topics/connectivity/bluetooth-le | |
*/ | |
enum class BLEState{STATE_BLE_NOT_SUPPORTED,STATE_DISCONNECTED , STATE_CONNECTING, STATE_CONNECTED } | |
interface Communicable{ | |
/** | |
* You can get bluetooth module information which pairing is possible | |
* | |
* @param timeLimit time limit for searching bluetooth module (default : 10s) | |
* @return | |
* <"Bluetooth Name","Mac Address"> pair set | |
* if error is occurred, it will return null | |
*/ | |
fun searchModule(timeLimit: Long=10000) : Set<BluetoothDevice>? | |
/** | |
* You can connect target BLE module with mac Address | |
* @param macAddress target BLE MAC address, you can get from searchModule Method | |
*/ | |
fun connect(macAddress: String) : BLEState | |
/** | |
* You can send string message to HM-10 | |
*/ | |
fun writeMessage(message : String) : Any | |
/** | |
* if you put listener in onMessageReceived Method, | |
* when a message is sent from the HM-10 to Android, | |
* the received byte array is delivered to the listener and executed. | |
* @param listener Lambda for process receiver mesage | |
*/ | |
fun onMessageReceived(listener : (ByteArray) -> Nothing) : Nothing | |
} | |
class BleHelper(private val context : Context) : Communicable { | |
public val REQUEST_ENABLE_BT = 111111 | |
public val TAG = "BleHelper" | |
private var state: BLEState = BLEState.STATE_BLE_NOT_SUPPORTED; | |
private val bluetoothManager: BluetoothManager? = | |
context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager | |
private val bluetoothAdapter: BluetoothAdapter? by lazy(LazyThreadSafetyMode.NONE) { | |
val bluetoothManager = | |
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager | |
bluetoothManager.adapter | |
} | |
private var bluetoothDeviceSet: MutableSet<BluetoothDevice> = mutableSetOf() | |
private val BluetoothAdapter.isDisabled: Boolean get() = !isEnabled | |
override fun searchModule(timeLimit: Long): Set<BluetoothDevice>? { | |
//check BLE connection | |
if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { | |
Toast.makeText(context, "BLE not Supported", Toast.LENGTH_SHORT).show() | |
state = BLEState.STATE_BLE_NOT_SUPPORTED | |
return null | |
} | |
if (bluetoothManager == null) { | |
Log.i(TAG, "bluetoothAdapter is null") | |
return null | |
} | |
bluetoothAdapter?.takeIf { it.isDisabled }?.apply { | |
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) | |
(context as Activity).startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT) | |
} | |
scanLeDevice(true, bluetoothManager.adapter, timeLimit) | |
Thread.sleep(timeLimit) | |
return bluetoothDeviceSet | |
} | |
override fun connect(macAddress: String): BLEState { | |
var bluetoothGatt: BluetoothGatt? = null | |
val devices = bluetoothDeviceSet.filter { it -> it.address == macAddress } | |
if(bluetoothGatt==null){ | |
return BLEState.STATE_BLE_NOT_SUPPORTED | |
} | |
bluetoothGatt = devices[0].connectGatt(context, false, getGattCallback(bluetoothGatt)) | |
if(bluetoothGatt.discoverServices()){ | |
return BLEState.STATE_CONNECTED | |
} | |
else{ | |
return BLEState.STATE_DISCONNECTED | |
} | |
} | |
override fun writeMessage(message: String): Any { | |
TODO("Not yet implemented") | |
} | |
override fun onMessageReceived(listener: (ByteArray) -> Nothing): Nothing { | |
TODO("Not yet implemented") | |
} | |
/************************************************************************************/ | |
private var handler: Handler = Handler() | |
fun scanLeDevice(enable: Boolean, bluetoothAdapter: BluetoothAdapter, timeLimit: Long) { | |
val leScanCallback = | |
LeScanCallback { device, rssi, scanRecord -> | |
run { | |
if (bluetoothDeviceSet.filter { it -> it.address == device.address } | |
.isEmpty()) { | |
bluetoothDeviceSet.add(device) | |
} | |
} | |
} | |
if (enable) { | |
handler.postDelayed({ | |
bluetoothAdapter.stopLeScan(leScanCallback) | |
}, timeLimit) | |
bluetoothAdapter.startLeScan(leScanCallback) | |
} else { | |
bluetoothAdapter.stopLeScan(leScanCallback) | |
} | |
} | |
private var connectionState = BLEState.STATE_DISCONNECTED | |
// Various callback methods defined by the BLE API. | |
fun getGattCallback(bluetoothGatt: BluetoothGatt) = object : BluetoothGattCallback() { | |
override fun onConnectionStateChange( | |
gatt: BluetoothGatt, | |
status: Int, | |
newState: Int | |
) { | |
val intentAction: String | |
when (newState) { | |
BluetoothProfile.STATE_CONNECTED -> { | |
connectionState = BLEState.STATE_CONNECTED | |
Log.i("BluetoothLeService", "Connected to GATT server.") | |
Log.i( | |
"BluetoothLeService", "Attempting to start service discovery: " + | |
bluetoothGatt?.discoverServices() | |
) | |
} | |
BluetoothProfile.STATE_DISCONNECTED -> { | |
connectionState = BLEState.STATE_DISCONNECTED | |
Log.i("BluetoothLeService", "Disconnected from GATT server.") | |
} | |
} | |
} | |
// New services discovered | |
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { | |
when (status) { | |
Log.w("BluetoothLeService", "onServicesDiscovered received: $status") | |
} | |
} | |
override fun onCharacteristicChanged( | |
gatt: BluetoothGatt?, | |
characteristic: BluetoothGattCharacteristic? | |
) { | |
super.onCharacteristicChanged(gatt, characteristic) | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment