Skip to content

Instantly share code, notes, and snippets.

@BEOKS
Created August 13, 2021 07:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BEOKS/da53896099ec7819fc4ddc629283ed13 to your computer and use it in GitHub Desktop.
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
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