如何在 kotlin 上的 MPAndroidChart 中绘制从蓝牙模块接收的实时传感器值?

How to Plot real-Time Sensor Value that Received from Bluetooth Modul in MPAndroidChart on kotlin?

我有一个传感器,通过将它连接到 Arduino 板上,我可以在 Arduino 上以图表形式在串行绘图仪中看到传感器信号 IDE。
我想通过蓝牙模块在我的APP上实时看到相同的信号。
我正在使用 MPAndroidChart 库在 Kotlin 中绘制图表。
要使用 MPAndroidChart 绘制图表以及有关如何使用 MPAndroidChart 的更多信息,
我检查了Google中发布的示例程序代码从Github中的以下link播放 https://github.com/PhilJay/MPAndroidChart
并将 Java 代码转换为 Kotlin,现在我可以用随机数绘制图表了。
但我计划使用通过蓝牙接收的传感器数据绘制我自己的图表。
我写了一个 ReceiveData 函数,我想用这个方法获取数据并将其提供给 addEntry 函数,这样就不用绘制图形了随机数,我用蓝牙数据绘制图表。
但是我不知道该怎么办。
通过在

中调用feedMultiple函数
btn_startTest.setOnClickListener { feedMultiple() }

并在 addEntry 中使用以下代码绘制随机数图表。

data.addEntry(Entry(set.entryCount.toFloat(), (Math.random() * 40).toFloat() + 30f), 0)

但我遇到的问题是,我不知道如何将通过蓝牙接收到的数据传输addEntry函数并使用它们代替随机数。
这是我的应用程序的完整代码:

class ElectromyographyAnalysis : AppCompatActivity(), OnChartValueSelectedListener {

    companion object {
        val TAG = "EMGSensor"
        val APP_NAME = "EMGSensor"

        var m_myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb")
        var m_bluetoothSocket: BluetoothSocket? = null
        lateinit var m_progress: ProgressDialog
        lateinit var m_bluetoothAdapter: BluetoothAdapter
        var m_isConnected: Boolean = false
        lateinit var m_address: String

        var xVal: Int = 0
        var yVal: Int = 0
    }

    lateinit var emgChart: LineChart


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.electromyography_analysis_layout)

        title = "Electromyography Analysis"

        m_address = intent.getStringExtra(SelectDeviceActivity.EXTRA_ADDRESS).toString()

        ConnectToDevice(this).execute()

        //add this new
        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )


        emgChart = findViewById(R.id.emg_lineChart)
        emgChart.setOnChartValueSelectedListener(this)

        // enable description text
        emgChart.description.isEnabled = true

        // enable touch gestures
        emgChart.setTouchEnabled(true)

        // enable scaling and dragging

        // enable scaling and dragging
        emgChart.isDragEnabled = true
        emgChart.setScaleEnabled(true)
        emgChart.setDrawGridBackground(false)

        // if disabled, scaling can be done on x- and y-axis separately
        emgChart.setPinchZoom(true)

        // set an alternative background color
        emgChart.setBackgroundColor(Color.LTGRAY)

        val data = LineData()
        data.setValueTextColor(Color.WHITE)

        //add empty data

        // add empty data
        emgChart.data = data

        // get the legend (only possible after setting data)

        // get the legend (only possible after setting data)
        val l: Legend = emgChart.legend

        // modify the legend ...

        // modify the legend ...
        l.form = LegendForm.LINE
        //l.typeface =
        l.textColor = Color.WHITE

        val xl: XAxis = emgChart.xAxis
        //xl.typeface = tfLight
        xl.textColor = Color.WHITE
        xl.setDrawGridLines(false)
        xl.setAvoidFirstLastClipping(true)
        xl.isEnabled = true

        val leftAxis: YAxis = emgChart.getAxisLeft()
        //leftAxis.typeface = tfLight
        leftAxis.textColor = Color.WHITE
        leftAxis.axisMaximum = 100f
        leftAxis.axisMinimum = 0f
        leftAxis.setDrawGridLines(true)

        val rightAxis: YAxis = emgChart.getAxisRight()
        rightAxis.isEnabled = false

        btn_startTest.setOnClickListener { feedMultiple() }
        
    }

    private fun addEntry() {
        val data: LineData = emgChart.data
        if (data != null) {
            var set = data.getDataSetByIndex(0)
            // set.addEntry(...); // can be called as well
            if (set == null) {
                set = createSet()
                data.addDataSet(set)
            }
            data.addEntry(Entry(set.entryCount.toFloat(), (Math.random() * 40).toFloat() + 30f), 0)
            data.notifyDataChanged()

            // let the chart know it's data has changed
            emgChart.notifyDataSetChanged()

            // limit the number of visible entries
            emgChart.setVisibleXRangeMaximum(120f)
            // chart.setVisibleYRange(30, AxisDependency.LEFT);

            // move to the latest entry
            emgChart.moveViewToX(data.entryCount.toFloat())

            // this automatically refreshes the chart (calls invalidate())
            // chart.moveViewTo(data.getXValCount()-7, 55f,
            // AxisDependency.LEFT);
        }
    }

    private fun createSet(): LineDataSet {
        val set = LineDataSet(null, "Dynamic Data")
        set.axisDependency = AxisDependency.LEFT
        set.color = ColorTemplate.getHoloBlue()
        set.setCircleColor(Color.WHITE)
        set.lineWidth = 2f
        set.circleRadius = 4f
        set.fillAlpha = 65
        set.fillColor = ColorTemplate.getHoloBlue()
        set.highLightColor = Color.rgb(244, 117, 117)
        set.valueTextColor = Color.WHITE
        set.valueTextSize = 9f
        set.setDrawValues(false)
        return set
    }

    private var thread: Thread? = null

    private fun feedMultiple() {
        if (thread != null)
            thread!!.interrupt()

        val runnable = Runnable { addEntry() }
        thread = Thread {
            for (i in 0..999) {

                // Don't generate garbage runnable inside the loop.
                runOnUiThread(runnable)
                try {
                    Thread.sleep(25)
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }
            }
        }
        thread!!.start()
    }

    private fun receiveData() {

        val buffer = ByteArray(1024)
        var bytes: Int
        val handler = Handler()
        var stopWorker = false
        Log.d(TAG, "Inside ReceiveData")

        val workerThread = Thread {
            while (!Thread.currentThread().isInterrupted && !stopWorker) {

                try {
                    bytes = m_bluetoothSocket!!.inputStream.read(buffer)
                    if (bytes > 0) {
                        val incomingMessage = String(buffer, 0, bytes)
                        Log.d(TAG, "InputStream : $incomingMessage")
                        yVal = incomingMessage.toInt()
                       
                    } else {
                        Toast.makeText(this , "bytes is less than zero" , Toast.LENGTH_SHORT).show()

                    }
                } catch (ex: IOException) {
                    stopWorker = true
                }
            }
        }
        workerThread.start()
    }

老实说,我会使用 EventBus,因此您可以使用变量侦听来自蓝牙设备的数据。

事件总线作为监听器工作。当您从蓝牙设备收到数据时 Android 保存事件,您可以在应用程序的任何地方使用它,也可以在活动之间传递它。

但要做到这一点,您需要注册和订阅。让我们从第一个开始。

  1. 将 GreenRobot 事件总线添加到您的 gradle:

       implementation 'org.greenrobot:eventbus:3.2.0' 
    
  2. 创建 EventBus(我会制作一个新的对象文件来使 eventbus 有序):

    data class DataEvent(var dataToSend: Float){}
    

当然,您需要注册并订阅此活动。以及如何?

  1. 在你的蓝牙接收器中注册你的事件总线你需要注册数据来订阅(你从蓝牙获取数据包的地方要清楚):

    val eventData: DataEvent = DataEvent(yourData)
    EventBus.getDefault().post(eventData)
    
  2. 订阅,您现在当然需要订阅,否则您将看不到任何数据被保存到您的活动中class。 因为您需要绘制数据,所以在您的 ElectromyographyAnalysis Class 中您需要订阅事件:

    @Subscribe(threadMode = ThreadMode.MAIN)
    public fun onDataReceived(event: DataEvent) {
        if (event.dataToSend != null) {
            dataToPlot = event.dataToSend
        }
        addEntry(dataToPlot) // THIS WILL CALL YOUR ADDENTRY()
    }
    

通过这种方式,您可以将蓝牙接收到的每一位都发送到 AddEntry() 方法并绘制它们。

  1. 但是,要做到这一点,您需要稍微更改一下您的 AddEntry():

        private fun addEntry(yourData:DataType) {
              val data: LineData = emgChart.data
    
         if (data != null) {
              var set = data.getDataSetByIndex(0)
             if (set1 == null) {
                 data.addDataSet(set)
             }
    
             data.addEntry(
                 Entry(
                     set.entryCount.toFloat(),
                     datatoplot
                 ), 0
             )
             data.notifyDataChanged()
    
             // let the chart know it's data has changed
             emgChart.notifyDataSetChanged()
    
             // limit the number of visible entries
             emgChart.setVisibleXRangeMaximum(120f)
             // chart.setVisibleYRange(30, AxisDependency.LEFT);
    
             // move to the latest entry
             emgChart.moveViewToX(data.entryCount.toFloat())
    
             // this automatically refreshes the chart (calls invalidate())
             // chart.moveViewTo(data.getXValCount()-7, 55f,
             // AxisDependency.LEFT);
         }
     } 
    
  2. 您将不需要 feedMultiple Thread,您还将拥有性能更好的实时图表。