0

I need help with this project, i use BLE and connect it to smartphone and i can get the RSSI value by "ScanResult" in the ScannerFragment but its a static value in the recyclerview, i dont know what to do to make the RSSI value change in real time as i move my phone away/ closer to the BLE device, so it would jump for example from -50dBm to -60dBm as i move the smartphone away from the BLE device

ScannerFragment


import android.Manifest
import android.app.AlertDialog
import android.bluetooth.*
import android.bluetooth.le.BluetoothLeScanner
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.support.annotation.RequiresApi
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import java.util.*
import kotlin.collections.HashSet

class ScannerFragment : Fragment() {

    private lateinit var startButton: Button
    private lateinit var stopButton: Button
    private lateinit var multilateration: TextView
    private lateinit var recyclerView: RecyclerView
    private lateinit var linearLayoutManager: LinearLayoutManager

    private var btManager: BluetoothManager? = null
    private var btAdapter: BluetoothAdapter? = null
    private var btScanner: BluetoothLeScanner? = null
    var beaconSet: HashSet<Beacon> = HashSet()
    var beaconAdapter: BeaconsAdapter? = null


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_scanner, container, false)
        initViews(view)
        setUpBluetoothManager()
        return view

    }


    companion object {
        private const val REQUEST_ENABLE_BT = 1
        private const val PERMISSION_REQUEST_COARSE_LOCATION = 1
    }

    private fun initViews(view: View) {
        startButton = view.findViewById(R.id.startButton)
        stopButton = view.findViewById(R.id.stopButton)
        recyclerView = view.findViewById(R.id.recyclerView)
        startButton.setOnClickListener { onStartScannerButtonClick() }
        stopButton.setOnClickListener { onStopScannerButtonClick() }
        linearLayoutManager = LinearLayoutManager(context)
        recyclerView.layoutManager = linearLayoutManager
        beaconAdapter = BeaconsAdapter(beaconSet.toList())
        recyclerView.adapter = beaconAdapter
        multilateration = view.findViewById(R.id.multilateration)

    }

    private fun onStartScannerButtonClick() {
        startButton.setBackgroundColor(Color.GREEN)
        startButton.visibility = View.GONE
        stopButton.visibility = View.VISIBLE
        beaconSet.clear()
        btScanner?.startScan(leScanCallback)
    }

    private fun onStopScannerButtonClick() {
        stopButton.setBackgroundColor(Color.RED)
        stopButton.visibility = View.GONE
        startButton.visibility = View.VISIBLE
        //beaconAdapter?.beaconList?.clear()
        beaconSet.clear()
        btScanner?.stopScan(leScanCallback)
    }

    private fun setUpBluetoothManager() {
        btManager = activity?.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        btAdapter = btManager!!.adapter
        btScanner = btAdapter?.bluetoothLeScanner
        if (btAdapter != null && !btAdapter!!.isEnabled) {
            val enableIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT)

        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            checkForLocationPermission()
        }
    }

    @RequiresApi(Build.VERSION_CODES.M)
    private fun checkForLocationPermission() {
        // Make sure we have access coarse location enabled, if not, prompt the user to enable it
        if (requireActivity().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            val builder = AlertDialog.Builder(activity)
            builder.setTitle("This app needs location access")
            builder.setMessage("Please grant location access so this app can detect the BLE.")
            builder.setPositiveButton(android.R.string.ok, null)
            builder.setOnDismissListener {
                requestPermissions(
                    arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
                    PERMISSION_REQUEST_COARSE_LOCATION
                )
            }
            builder.show()
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>, grantResults: IntArray
    ) {
        when (requestCode) {
            PERMISSION_REQUEST_COARSE_LOCATION -> {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    println("coarse location permission granted")
                } else {
                    val builder = AlertDialog.Builder(activity)
                    builder.setTitle("Functionality limited")
                    builder.setMessage("Since location access has not been granted, this app will not be able to discover BLE beacons")
                    builder.setPositiveButton(android.R.string.ok, null)
                    builder.setOnDismissListener { }
                    builder.show()
                }
                return
            }
        }
    }


    private val leScanCallback: ScanCallback = object : ScanCallback() {
        @RequiresApi(Build.VERSION_CODES.O)
        override fun onScanResult(callbackType: Int, result: ScanResult) {

            val scanRecord = result.scanRecord
            val beacon = Beacon(result.rssi)
            beacon.manufacturer = result.device.name
            beacon.macAddress = result.device.address
            beacon.rssi = result.rssi

            if (scanRecord != null) {
                val iBeaconManufactureData = scanRecord.getManufacturerSpecificData(0X004c)
                if (iBeaconManufactureData != null && iBeaconManufactureData.size >= 23) {
                    val iBeaconUUID = Utility.toHexString(iBeaconManufactureData.copyOfRange(2, 18))
                    val major = Integer.parseInt(
                        Utility.toHexString(
                            iBeaconManufactureData.copyOfRange(
                                18,
                                20
                            )
                        ), 16
                    )
                    val minor = Integer.parseInt(
                        Utility.toHexString(
                            iBeaconManufactureData.copyOfRange(
                                20,
                                22
                            )
                        ), 16
                    )
                    beacon.type = Beacon.BeaconType.iBeacon
                    beacon.name = beacon.manufacturer
                    beacon.uuid = iBeaconUUID
                    beacon.major = major
                    beacon.txPower = result.txPower
                    beacon.minor = minor
                    Log.e("BeaconID", "iBeaconUUID:$iBeaconUUID major:$major minor:$minor")

                }
                if(beacon.name == "Beacon_1" || beacon.name == "Beacon_2" || beacon.name == "Beacon_3" || beacon.name == "Beacon_4" ) {
                    beaconSet.add(beacon)
                }
                (recyclerView.adapter as BeaconsAdapter).updateData(beaconSet.toList())

            }
        }
        override fun onScanFailed(errorCode: Int) {
            Log.e("Failed", errorCode.toString())
        }
    }

} 

BeaconsAdapter


import android.os.Build
import android.support.annotation.RequiresApi
import android.support.v4.content.ContextCompat
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import java.util.*

class BeaconsAdapter(beacons: List<Beacon>) :
    RecyclerView.Adapter<BeaconsAdapter.BeaconHolder>() {
    var beaconList: MutableList<Beacon> = beacons.toMutableList()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BeaconHolder {
        val inflater = LayoutInflater.from(parent.context)
        return BeaconHolder(inflater, parent)

    }

    override fun onBindViewHolder(holder: BeaconHolder, pos: Int) {
        val beacon: Beacon = beaconList[pos]
        holder.bind(beacon)

    }

    override fun getItemCount() = beaconList.size

    @RequiresApi(Build.VERSION_CODES.N)
    fun updateData(data: List<Beacon>) {
        beaconList.clear()
        beaconList.addAll(data)
        notifyDataSetChanged()

    }

    class BeaconHolder(inflater: LayoutInflater, parent: ViewGroup) :
        RecyclerView.ViewHolder(inflater.inflate(R.layout.scan_result_items, parent, false)) {
        private var image: ImageView? = null
        private var mac: TextView? = null
        private var name: TextView? = null
        private var instanceMajorMinor: TextView? = null
        private var namespaceUUID: TextView? = null
        private var beaconRSSI: TextView? = null
        private val context = parent.context

        init {
            image = itemView.findViewById(R.id.beacon_image)
            mac = itemView.findViewById(R.id.beacon_mac)
            name = itemView.findViewById(R.id.beacon_name)
            instanceMajorMinor = itemView.findViewById(R.id.beacon_instance_major_minor)
            beaconRSSI = itemView.findViewById(R.id.beacon_rssi)
        }

        fun bind(beacon: Beacon) {
            mac?.text = String.format(
                context.getString(R.string.mac),
                beacon.macAddress
            )
            beaconRSSI?.text = String.format(
                context.getString(R.string.rssi),
                beacon.rssi
            )
            if (beacon.type == Beacon.BeaconType.iBeacon) {
                namespaceUUID?.text = String.format(context.getString(R.string.uuid), beacon.uuid)
                name?.text = String.format(context.getString(R.string.name), beacon.name)
                //namespace
                instanceMajorMinor?.text = String.format(
                    context.getString(R.string.major_minor),
                    beacon.major,
                    beacon.minor
                )
                image?.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ibeacon))
                instanceMajorMinor?.visibility = View.VISIBLE
                namespaceUUID?.visibility = View.VISIBLE

            }

        }

    }

}

Beacon


class Beacon(rssi: Int?) {
    enum class BeaconType {
        iBeacon
    }

    var macAddress : String? = null
    var manufacturer: String? = null
    var type: BeaconType = BeaconType.iBeacon
    var uuid: String? = null
    var name: String? = null
    var major: Int? = null
    var minor: Int? = null
    var txPower: Int? = null
    var rssi = rssi
    override fun equals(other: Any?): Boolean{
        if(this === other) return true
        if(other !is Beacon) return false
        if(macAddress != other.macAddress) return false

        return true
    }

    override fun hashCode(): Int {
        return macAddress?.hashCode() ?:0
    }

}
archangel
  • 11
  • 2
  • Does this answer your question? [Reading RSSI value of connected Bluetooth Low Energy device in Android Studio](https://stackoverflow.com/questions/33276664/reading-rssi-value-of-connected-bluetooth-low-energy-device-in-android-studio) – Risto Apr 29 '22 at 11:29
  • sadly not, my app can already read the RSSI value, but the problem is that its not keept updated, its static, meaning that the value does not change when i move closer/ away from the BLE device, which is what i want to achieve. – archangel Apr 29 '22 at 15:50
  • As far as I understand your question, RSSI value changes while scanning for devices, but is 'static' if you connect a device to the smartphone. If this is not the case, you should explain in more detail what you are doing. – Risto May 02 '22 at 11:33
  • I am able to connect the BLE to my phone through my app, but i get static RSSI value, i want it so its dynamic, meaning that the value changes when i move closer or away from the BLE. – archangel May 02 '22 at 12:25
  • But then the behaviour you describe is the expected behaviour and everything is fine ;-) Background: As soon as the smartphone has connected to the BLE device, it stops advertissing. Therefore, leScanCallback is never called again and the RSSI value is of course no longer 'updated'. In the connected state, the RSSI value must be read via another function, and then the [link in my first comment](https://stackoverflow.com/a/33367647/8124605) applies again ... – Risto May 02 '22 at 14:37

0 Answers0