如何在 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 保存事件,您可以在应用程序的任何地方使用它,也可以在活动之间传递它。
但要做到这一点,您需要注册和订阅。让我们从第一个开始。
将 GreenRobot 事件总线添加到您的 gradle:
中
implementation 'org.greenrobot:eventbus:3.2.0'
创建 EventBus(我会制作一个新的对象文件来使 eventbus 有序):
data class DataEvent(var dataToSend: Float){}
当然,您需要注册并订阅此活动。以及如何?
在你的蓝牙接收器中注册你的事件总线你需要注册数据来订阅(你从蓝牙获取数据包的地方要清楚):
val eventData: DataEvent = DataEvent(yourData)
EventBus.getDefault().post(eventData)
订阅,您现在当然需要订阅,否则您将看不到任何数据被保存到您的活动中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() 方法并绘制它们。
但是,要做到这一点,您需要稍微更改一下您的 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);
}
}
您将不需要 feedMultiple Thread,您还将拥有性能更好的实时图表。
我有一个传感器,通过将它连接到 Arduino 板上,我可以在 Arduino 上以图表形式在串行绘图仪中看到传感器信号 IDE。
我想通过蓝牙模块在我的APP上实时看到相同的信号。
我正在使用 MPAndroidChart 库在 Kotlin 中绘制图表。
要使用 MPAndroidChart 绘制图表以及有关如何使用 MPAndroidChart 的更多信息,
我检查了Google中发布的示例程序代码从Github中的以下link播放
https://github.com/PhilJay/MPAndroidChart
并将 Java 代码转换为 Kotlin,现在我可以用随机数绘制图表了。
但我计划使用通过蓝牙接收的传感器数据绘制我自己的图表。
我写了一个 ReceiveData 函数,我想用这个方法获取数据并将其提供给 addEntry 函数,这样就不用绘制图形了随机数,我用蓝牙数据绘制图表。
但是我不知道该怎么办。
通过在
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 保存事件,您可以在应用程序的任何地方使用它,也可以在活动之间传递它。
但要做到这一点,您需要注册和订阅。让我们从第一个开始。
将 GreenRobot 事件总线添加到您的 gradle:
中implementation 'org.greenrobot:eventbus:3.2.0'
创建 EventBus(我会制作一个新的对象文件来使 eventbus 有序):
data class DataEvent(var dataToSend: Float){}
当然,您需要注册并订阅此活动。以及如何?
在你的蓝牙接收器中注册你的事件总线你需要注册数据来订阅(你从蓝牙获取数据包的地方要清楚):
val eventData: DataEvent = DataEvent(yourData) EventBus.getDefault().post(eventData)
订阅,您现在当然需要订阅,否则您将看不到任何数据被保存到您的活动中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() 方法并绘制它们。
但是,要做到这一点,您需要稍微更改一下您的 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); } }
您将不需要 feedMultiple Thread,您还将拥有性能更好的实时图表。