I am able to connect to my BLE device and send data from my Android app, but I am not able to read the data from the BLE (I need to display this data in a graph), but when I try to retrieve the values, I get a null pointer.
Here is the code for the activity page:
package com.example.lightrdetect
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattCharacteristic.*
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.example.lightrdetect.ble.ConnectionEventListener
import com.example.lightrdetect.ble.isReadable
import com.github.mikephil.charting.charts.ScatterChart
import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.ScatterData
import com.github.mikephil.charting.data.ScatterDataSet
import com.punchthrough.blestarterappandroid.ble.ConnectionManager
import kotlinx.android.synthetic.main.activity_home_page.image_lightr
import kotlinx.android.synthetic.main.activity_tracking_page.*
import org.jetbrains.anko.alert
import java.text.SimpleDateFormat
import java.util.*
class TrackingPageActivity : AppCompatActivity() {
private lateinit var device : BluetoothDevice
private val dateFormatter = SimpleDateFormat("MMM d, HH:mm:ss", Locale.FRANCE)
private var listeners: MutableSet<WeakReference<ConnectionEventListener>> = mutableSetOf()
private val deviceGattMap = ConcurrentHashMap<BluetoothDevice, BluetoothGatt>()
private val operationQueue = ConcurrentLinkedQueue<BleOperationType>()
private var pendingOperation: BleOperationType? = null
private val characteristic by lazy {
ConnectionManager.servicesOnDevice(device)?.flatMap { service ->
service.characteristics ?: listOf()
} ?: listOf()
}
private val characteristicProperty by lazy {
characteristic.map { characteristic->
characteristic to mutableListOf<CharacteristicProperty>().apply {
if(characteristic.isNotifiable()) add(CharacteristicProperty.Notifiable)
if (characteristic.isIndicatable()) add(CharacteristicProperty.Indicatable)
if(characteristic.isReadable()) add(CharacteristicProperty.Readable)
}.toList()
}.toMap()
}
private val characteristicAdapter: CharacteristicAdapter by lazy {
CharacteristicAdapter(characteristic){characteristicProperty ->
}
}
companion object{
//var UUID_Read_notification = UUID.fromString("D973F2E1-B19E-11E2-9E96-0800200C9A66")
var UUID_Read = "D973F2E1-B19E-11E2-9E96-0800200C9A66"
}
private var notifyingCharacteristics = mutableListOf<UUID>()
override fun onCreate(savedInstanceState: Bundle?) {
ConnectionManager.registerListener(connectionEventListener)
super.onCreate(savedInstanceState)
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
?: error("Missing BluetoothDevice from Home Page Activity")
setContentView(R.layout.activity_tracking_page)
image_lightr.setOnClickListener {
finish()
}
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN
actionBar?.hide()
supportActionBar?.hide()
ScatterChartData()
}
private fun ScatterChartData(){
readSensor(UUID_Read)
val scatterEntry = ArrayList<Entry>()
scatterEntry.add(Entry(0f, 3f))
val sensorPosition = ArrayList<Entry>()
sensorPosition.add(Entry(0f, 0f))
val scatterDataSet_sensor = ScatterDataSet(sensorPosition, "Sensor")
scatterDataSet_sensor.color = resources.getColor(R.color.white)
scatterDataSet_sensor.setScatterShape(ScatterChart.ScatterShape.CHEVRON_DOWN)
scatterDataSet_sensor.scatterShapeSize = 30f
val scatterDataSet = ScatterDataSet(scatterEntry, "Target")
scatterDataSet.color = resources.getColor(R.color.jaune_woodoo)
scatterDataSet.setScatterShape(ScatterChart.ScatterShape.CIRCLE)
scatterDataSet.valueTextColor = resources.getColor(R.color.transparent )
scatterDataSet.scatterShapeSize = 30f
val scatterlistfinal = ArrayList<ScatterDataSet>()
scatterlistfinal.add(scatterDataSet)
scatterlistfinal.add(scatterDataSet_sensor)
val scatterData = ScatterData(scatterlistfinal as List<ScatterDataSet>)
chart1.data = scatterData
chart1.setBackgroundColor(resources.getColor(R.color.transparent))
chart1.animateXY(1000, 1000)
chart1.legend.isEnabled = false
val xAxis : XAxis = chart1.xAxis
xAxis.position = XAxis.XAxisPosition.TOP
//xAxis.setDrawGridLines(true)
xAxis.axisLineColor = resources.getColor(R.color.white)
xAxis.axisMaximum = 90f
xAxis.axisMinimum = -90f
xAxis.textColor = resources.getColor(R.color.white)
xAxis.axisLineWidth = 5f
val yAxisL : YAxis = chart1.axisLeft
yAxisL.textColor = resources.getColor(R.color.white)
yAxisL.isInverted = true
yAxisL.axisMaximum = 5f
yAxisL.axisMinimum = 0f
yAxisL.axisLineWidth = 0f
yAxisL.setLabelCount(6, true)
yAxisL.axisLineColor = resources.getColor(R.color.transparent)
val yAxisR : YAxis = chart1.axisRight
yAxisR.textColor = resources.getColor(R.color.white)
yAxisR.isInverted = true
yAxisR.axisMaximum = 5f
yAxisR.axisMinimum = 0f
yAxisR.axisLineWidth = 0f
yAxisR.setLabelCount(6, true)
yAxisR.axisLineColor = resources.getColor(R.color.transparent)
}
private fun showCharacteristicOptions(characteristic: BluetoothGattCharacteristic) {
characteristicProperty[characteristic]?.let { properties ->
selector("Select an action to perform", properties.map { it.action }) { _, i ->
when (properties[i]) {
CharacteristicProperty.Readable -> {
//log("Reading from ${characteristic.uuid}")
ConnectionManager.readCharacteristic(device, characteristic)
}
CharacteristicProperty.Notifiable, CharacteristicProperty.Indicatable -> {
if (notifyingCharacteristics.contains(characteristic.uuid)) {
//log("Disabling notifications on ${characteristic.uuid}")
ConnectionManager.disableNotifications(device, characteristic)
} else {
//log("Enabling notifications on ${characteristic.uuid}")
ConnectionManager.enableNotifications(device, characteristic)
}
}
}
}
}
}
private fun readSensor(characteristic: String){
var gattCharacteristic = BluetoothGattCharacteristic(UUID.fromString(characteristic), PROPERTY_READ, PERMISSION_READ_ENCRYPTED)
showCharacteristicOptions(gattCharacteristic)
var data : String
if (gattCharacteristic !=null) {
ConnectionManager.enableNotifications(device, gattCharacteristic)
data = ConnectionManager.readCharacteristic(device, gattCharacteristic).toString()
Log.d("sensor", "value " + data)
}
}
private val connectionEventListener by lazy {
ConnectionEventListener().apply {
onDisconnect = {
runOnUiThread {
alert {
title = "Disconnected"
message = "Disconnected from device."
positiveButton("ok"){onBackPressed()}
}.show()
}
}
onCharacteristicRead = {_, characteristic ->
Log.i("Tracking page","Read from ${characteristic.uuid}: ${characteristic.value.toHexString()}")
}
onNotificationsEnabled = {_,characteristic ->
Log.i("Tracking page","Enabled notifications on ${characteristic.uuid}")
notifyingCharacteristics.add(characteristic.uuid)
}
}
}
private enum class CharacteristicProperty {
Readable,
Writable,
WritableWithoutResponse,
Notifiable,
Indicatable;
val action
get() = when (this) {
Readable -> "Read"
Writable -> "Write"
WritableWithoutResponse -> "Write Without Response"
Notifiable -> "Toggle Notifications"
Indicatable -> "Toggle Indications"
}
}
}
and there is the error that I have
2022-03-17 10:49:54.768 31034-31034/com.example.lightrdetect E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.lightrdetect, PID: 31034
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.lightrdetect/com.example.lightrdetect.TrackingPageActivity}: java.lang.NullPointerException: gattCharacteristic.getValue() must not be null
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3851)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4027)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:247)
at android.app.ActivityThread.main(ActivityThread.java:8676)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: java.lang.NullPointerException: gattCharacteristic.getValue() must not be null
at com.example.lightrdetect.TrackingPageActivity.ScatterChartData(TrackingPageActivity.kt:89)
at com.example.lightrdetect.TrackingPageActivity.onCreate(TrackingPageActivity.kt:78)
at android.app.Activity.performCreate(Activity.java:8215)
at android.app.Activity.performCreate(Activity.java:8199)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3824)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4027)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:247)
at android.app.ActivityThread.main(ActivityThread.java:8676)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
this is a screenshot of the nRF application that shows me all the features:
I checked with the BLE module support and they told me that:
The Android application can write on the Rx characteristic and automatically the data will be sent on the UART (Tera Term or a µC connected on UART) The µC or Tera Term to push data will have to emit on the Tx, this is what the Application code of the ST Serial Port Profile code does, when it receives on the UART an end of string character (CR+LF) (to be set in the Tera Term or in the STM32 Application code). However, for the Android application to receive the data, it must be registered on the notifications (slide 10 of the doc - the slide refers to an Ios mobile on Android to activate the notifications, you must click on the 3 arrows)
I checked with Tera Term, I can see the data on nRF. My question now is how can I read the characteristic notification?
Best regard.