diff --git a/app/build.gradle b/app/build.gradle
index 1bc459a..1ccc02f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,6 +22,8 @@ android {
buildConfigField "String", "API_BASE_URL", "\"${props['apiBaseUrl']}\""
buildConfigField "String", "API_KEY", "\"${props['apiKey']}\""
+ buildConfigField "String", "SERVICE_UUID", "\"6e400001-b5a3-f393-e1a9-e50e24dcac9e\""
+ buildConfigField "String", "CHAR_UUID", "\"6e400005-b5a3-f393-e1a9-e50e24dcac9e\""
}
buildTypes {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7508a96..d4ba815 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,6 +17,9 @@
android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
+
+
@@ -47,6 +49,7 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/com/example/sensortestingapp/BLEService.kt b/app/src/main/java/com/example/sensortestingapp/BLEService.kt
new file mode 100644
index 0000000..f9eba08
--- /dev/null
+++ b/app/src/main/java/com/example/sensortestingapp/BLEService.kt
@@ -0,0 +1,69 @@
+package com.example.sensortestingapp
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.IBinder
+import androidx.core.app.NotificationCompat
+import androidx.core.content.ContextCompat
+
+class BLEService : Service() {
+ private val CHANNEL_ID = "BLEService Kotlin"
+
+ companion object {
+
+ fun startService(context: Context, message: String) {
+ val startIntent = Intent(context,
+ BLEService::class.java)
+ startIntent.putExtra("inputExtra", message)
+ ContextCompat.startForegroundService(context, startIntent)
+ }
+
+ fun stopService(context: Context) {
+ val stopIntent = Intent(context, BLEService::class.java)
+ context.stopService(stopIntent)
+ }
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+
+ //do heavy work on a background thread
+ val input = intent?.getStringExtra("inputExtra")
+ createNotificationChannel()
+ val notificationIntent = Intent(this, MainActivity::class.java)
+
+ val pendingIntent: PendingIntent =
+ Intent(this, MainActivity::class.java).let { notificationIntent ->
+ PendingIntent.getActivity(this, 0, notificationIntent,
+ PendingIntent.FLAG_IMMUTABLE)
+ }
+ val notification = NotificationCompat.Builder(this, CHANNEL_ID)
+ .setContentTitle("BLEService Service Kotlin Example")
+ .setContentText(input)
+ //.setSmallIcon(R.drawable.ic_notification)
+ .setContentIntent(pendingIntent)
+ .build()
+
+ startForeground(1, notification)
+ //stopSelf();
+ return START_NOT_STICKY
+ }
+
+ override fun onBind(intent: Intent): IBinder? {
+ return null
+ }
+
+ private fun createNotificationChannel() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val serviceChannel = NotificationChannel(CHANNEL_ID, "BLEService Service Channel",
+ NotificationManager.IMPORTANCE_DEFAULT)
+
+ val manager = getSystemService(NotificationManager::class.java)
+ manager!!.createNotificationChannel(serviceChannel)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/sensortestingapp/ConnectionManager.kt b/app/src/main/java/com/example/sensortestingapp/ConnectionManager.kt
index f6ea2fb..39576ef 100644
--- a/app/src/main/java/com/example/sensortestingapp/ConnectionManager.kt
+++ b/app/src/main/java/com/example/sensortestingapp/ConnectionManager.kt
@@ -1,6 +1,7 @@
package com.example.sensortestingapp
import android.annotation.SuppressLint
+import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothDevice.BOND_BONDED
@@ -14,12 +15,14 @@ import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothProfile
import android.bluetooth.BluetoothProfile.STATE_DISCONNECTED
import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.os.ParcelUuid
import android.util.Log
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
@@ -198,7 +201,7 @@ fun ByteArray.toHexString(): String =
joinToString(separator = "", prefix = "0x") { String.format("%02X", it) }
@SuppressLint("MissingPermission")
-class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) {
+class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) {
private val deviceGattMap = ConcurrentHashMap()
private val operationQueue = ConcurrentLinkedQueue()
@@ -209,6 +212,8 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) {
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()
+ private val scanFilters = listOf( ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(BuildConfig.SERVICE_UUID)).build())
+
private val bleScanner by lazy {
bleAdapter.bluetoothLeScanner
}
@@ -274,6 +279,8 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) {
broadcastReceiver,
IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
)
+
+
}
var isScanning = false
@@ -307,7 +314,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) {
fun startScan() {
if (!isScanning) {
isScanning = true
- bleScanner.startScan(null, scanSettings, scanCallback)
+ bleScanner.startScan( null, scanSettings, scanCallback)
}
}
@@ -428,7 +435,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) {
with(operation) {
Log.w("ConnectionManager", "Connecting to ${device.address}")
device.connectGatt(
- context, false, callback, BluetoothDevice.TRANSPORT_LE
+ context, true, callback, BluetoothDevice.TRANSPORT_LE
)
}
return
@@ -520,6 +527,7 @@ class ConnectionManager(val context: Context, bleAdapter: BluetoothAdapter) {
is SetNotification -> with(operation) {
val characteristic = gatt.getService(serviceId)?.getCharacteristic(charId);
+
if (characteristic == null) {
Log.e("ConnectionManager", "Char $charId (${serviceId}) not found!")
signalEndOfOperation(operation)
diff --git a/app/src/main/java/com/example/sensortestingapp/KirbyDevice.kt b/app/src/main/java/com/example/sensortestingapp/KirbyDevice.kt
index 3db84e9..541f5d8 100644
--- a/app/src/main/java/com/example/sensortestingapp/KirbyDevice.kt
+++ b/app/src/main/java/com/example/sensortestingapp/KirbyDevice.kt
@@ -25,9 +25,8 @@ import java.util.UUID
import java.util.stream.Collectors
-// Kirby service uuid: 6e400001-b5a3-f393-e1a9-e50e24dcac9e
-private val DEMO_SERVICE_UUID = UUID.fromString("6e400001-b5a3-f393-e1a9-e50e24dcac9e")
-private val DEMO_CHAR_UUID = UUID.fromString("6e400005-b5a3-f393-e1a9-e50e24dcac9e")
+private val SERVICE_UUID = UUID.fromString(BuildConfig.SERVICE_UUID)
+private val CHAR_UUID = UUID.fromString(BuildConfig.CHAR_UUID)
enum class DeviceStatus {
CONNECTED, BONDED, SUBSCRIBED, MISSING
@@ -40,8 +39,27 @@ class KirbyDevice(
private val bleDevice: BluetoothDevice,
private val onStateChange: (device: KirbyDevice) -> Unit,
-) : BleListener(bleDevice.address), DeviceListEntry {
- private val queue : RequestQueue = Volley.newRequestQueue(context)
+ ) : BleListener(bleDevice.address), DeviceListEntry {
+ private val queue: RequestQueue = Volley.newRequestQueue(context)
+
+ fun subscribe() {
+
+ connectionManager.enableNotification(
+ bleDevice, SERVICE_UUID, CHAR_UUID
+ )
+ }
+
+ fun readIaq() {
+ connectionManager.readChar(bleDevice, SERVICE_UUID, CHAR_UUID)
+ }
+
+ override fun onSuccessfulCharRead(
+ gatt: BluetoothGatt,
+ characteristic: BluetoothGattCharacteristic
+ ) {
+ addMeasurement(characteristic)
+ onStateChange(this)
+ }
override fun onScanResult(callbackType: Int, result: ScanResult) {
rssi = result.rssi
@@ -51,6 +69,7 @@ class KirbyDevice(
override fun onConnect(gatt: BluetoothGatt) {
statuses.add(DeviceStatus.CONNECTED)
statuses.remove(DeviceStatus.MISSING)
+
onStateChange(this)
}
@@ -65,14 +84,6 @@ class KirbyDevice(
onStateChange(this)
}
- override fun onSuccessfulCharRead(
- gatt: BluetoothGatt,
- characteristic: BluetoothGattCharacteristic
- ) {
- addMeasurement(characteristic)
- onStateChange(this)
- }
-
override fun onCharChange(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
addMeasurement(characteristic)
onStateChange(this)
@@ -82,6 +93,7 @@ class KirbyDevice(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
) {
+
statuses.add(DeviceStatus.SUBSCRIBED)
onStateChange(this)
}
@@ -116,25 +128,23 @@ class KirbyDevice(
override var rssi: Int? = null
private fun addMeasurement(characteristic: BluetoothGattCharacteristic) {
- if (characteristic.service.uuid == DEMO_SERVICE_UUID && characteristic.uuid == DEMO_CHAR_UUID) {
-
- val hexPayload = characteristic.value.toHexString()
- val payload = Payload(hexPayload)
- val base64Payload = Base64.getEncoder().encodeToString(characteristic.value)
- Log.i("BleListener", "Demo char received: $payload")
- measurements.add(payload)
- publishMeasurement(base64Payload)
-
- }
+ val hexPayload = characteristic.value.toHexString()
+ val payload = Payload(hexPayload)
+ val base64Payload = Base64.getEncoder().encodeToString(characteristic.value)
+ Log.i("BleListener", "Char received: $payload")
+ measurements.add(payload)
+ publishMeasurement(base64Payload)
}
private fun publishMeasurement(payload: String) {
- val accessKey = BuildConfig.API_BASE_URL
- val url = BuildConfig.API_KEY
- val eui = "0000${bleDevice.address}"
+ val accessKey = BuildConfig.API_KEY
+ val url = BuildConfig.API_BASE_URL
+ val eui = "0000${bleDevice.address.replace(":", "")}"
val postData = JSONObject()
+
try {
+ Log.i("POST", "Transmitting for $eui: $payload")
postData.put("accessKey", accessKey)
postData.put("metricPayload", payload)
postData.put("eui", eui)
@@ -145,7 +155,7 @@ class KirbyDevice(
val request = JsonObjectRequest(
Request.Method.POST, url, postData,
{ response ->
- Log.i("sendDataResponse","Response is: $response")
+ Log.i("sendDataResponse", "Response is: $response")
}
) { error -> error.printStackTrace() }
@@ -257,7 +267,7 @@ class KirbyDevice(
}
override fun execute() {
- connectionManager.readChar(bleDevice, DEMO_SERVICE_UUID, DEMO_CHAR_UUID)
+ readIaq()
}
})
@@ -272,9 +282,7 @@ class KirbyDevice(
}
override fun execute() {
- connectionManager.enableNotification(
- bleDevice, DEMO_SERVICE_UUID, DEMO_CHAR_UUID
- )
+ subscribe()
}
})
}
@@ -307,7 +315,7 @@ class KirbyDevice(
override fun execute() {
connectionManager.disableNotification(
- bleDevice, DEMO_SERVICE_UUID, DEMO_CHAR_UUID
+ bleDevice, SERVICE_UUID, CHAR_UUID
)
}
})
@@ -341,7 +349,6 @@ data class Payload(
)
-
fun bytesToUInt16(arr: ByteArray, start: Int): Int {
return ByteBuffer.wrap(arr, start, 2)
.order(ByteOrder.LITTLE_ENDIAN).short.toInt() and 0xFFFF
@@ -371,21 +378,18 @@ private fun payloadToMeasurements(payload: Payload): List {
override fun getIcon(): Int? {
return R.drawable.baseline_access_time_24
}
- },
- object : Measurement {
- override fun getLabel(): String {
- return "Payload"
- }
-
- override fun getFormattedValue(): String {
- return payload.payload
- }
-
- override fun getIcon(): Int? {
- return null
- }
-
+ }, object : Measurement {
+ override fun getLabel(): String {
+ return "Payload"
}
- )
+ override fun getFormattedValue(): String {
+ return payload.payload
+ }
+
+ override fun getIcon(): Int? {
+ return null
+ }
+ }
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/sensortestingapp/MainActivity.kt b/app/src/main/java/com/example/sensortestingapp/MainActivity.kt
index 329753a..59e0a42 100644
--- a/app/src/main/java/com/example/sensortestingapp/MainActivity.kt
+++ b/app/src/main/java/com/example/sensortestingapp/MainActivity.kt
@@ -75,7 +75,7 @@ class MainActivity : AppCompatActivity() {
setSupportActionBar(binding.toolbar)
-
+ BLEService.startService(applicationContext, "hello ble service")
binding.fab.setOnClickListener { view ->
if (!hasRequiredRuntimePermissions()) {
@@ -103,10 +103,10 @@ class MainActivity : AppCompatActivity() {
}
private fun isKirbyDevice(device: BluetoothDevice): Boolean {
- Log.i("kby", "found device ${device.name}")
return (device.name ?: "").lowercase().contains("kirby")
}
+
private fun setupDevicesList() {
binding.devicesList.apply {
adapter = deviceListAdapter
@@ -134,7 +134,8 @@ class MainActivity : AppCompatActivity() {
private fun addBondedDevices(): Unit {
bluetoothAdapter.bondedDevices.filter { isKirbyDevice(it) }.forEach {
- newKirbyDevice(it)
+ val kirbyDevice = newKirbyDevice(it)
+ kirbyDevice.subscribe()
}
}
@@ -179,13 +180,15 @@ class MainActivity : AppCompatActivity() {
private fun newKirbyDevice(bleDevice: BluetoothDevice): KirbyDevice {
- val device = KirbyDevice( this.applicationContext, connectionManager, bleDevice) {
+ val device = KirbyDevice(this.applicationContext, connectionManager, bleDevice) {
val i = kirbyDevices.indexOfFirst { d -> d === it }
runOnUiThread {
deviceListAdapter.notifyItemChanged(i)
}
}
connectionManager.register(device)
+ connectionManager.connect(bleDevice)
+ connectionManager.discoverServices(bleDevice)
kirbyDevices.add(device)
deviceListAdapter.notifyItemInserted(kirbyDevices.size - 1)
return device