我正在使用 C# 为 Xamarin Android 应用程序开发数字仪表板。作为该项目的一部分,我收到十六进制字符串形式的 RPM 响应。现在,我需要从这些响应中提取最后 4 位数字,将它们转换为十进制,然后以编程方式将结果除以 4 就像如果响应采用这种格式 41 0C 11 F1 我想提取这个 11 F1 但值可以有所不同。这是我的 mainactivity.cs
using Android.App;
using Android.OS;
using Android.Widget;
using Android.Views;
using Java.Util;
using System;
using System.Threading;
using System.Timers;
using Xamarin.Essentials;
using System.Threading.Tasks;
using Android.Bluetooth;
using System.Reflection;
using In.UnicodeLabs.KdGaugeViewLib;
namespace App6
{
[Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
public class MainActivity : Activity
{
private TextView rpmLabel;
private TextView speedLabel;
private ObdManager obdManager;
private BluetoothSocket btnSocket;
private TextView obdInfoLabel;
private Button requestButton;
private TextView rawRpmLabel;
private TextView rawSpeedLabel;
private TextView anotherRpmDoubleLabel;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
rpmLabel = FindViewById<TextView>(Resource.Id.rpmLabel);
speedLabel = FindViewById<TextView>(Resource.Id.speedLabel);
rawRpmLabel = FindViewById<TextView>(Resource.Id.rawRpmLabel);
rawSpeedLabel = FindViewById<TextView>(Resource.Id.rawSpeedLabel);
anotherRpmDoubleLabel = FindViewById<TextView>(Resource.Id.anotherRpmDoubleLabel);
InitializeBluetooth();
obdManager = new ObdManager(btnSocket);
// Set up a timer to periodically request and update RPM and speed (every 0.1 seconds)
System.Timers.Timer updateTimer = new System.Timers.Timer(2000);
updateTimer.Elapsed += OnUpdateTimerElapsed;
updateTimer.Start();
}
private void OnUpdateTimerElapsed(object sender, ElapsedEventArgs e)
{
// Request RPM
obdManager.SendCommand("010C", OnRPMReceived);
obdManager.SendCommand("010C", OnRAWRpmReceived);
// Request speed
obdManager.SendCommand("010D", OnSpeedReceived);
}
private void OnRPMReceived(string rpmdata)
{
try
{
// Parse the RPM value
string rpmValue = ObdProtocol.ParseRPM(rpmdata);
// Update the raw RPM label with the parsed raw RPM data
RunOnUiThread(() => rawRpmLabel.Text = $"Raw RPM Data: {rpmValue}");
// Optionally, you can also update the RPM label with the same value
RunOnUiThread(() => rpmLabel.Text = $"RPM: {rpmValue}");
//obdManager.SendCommand("010D", OnRealRAWRpmReceived);
// RunOnUiThread(() => anotherRpmDoubleLabel.Text = $"RPM: {rpmdata}");
// Update the new label with the parsed RPM data as a double
}
catch (Exception ex)
{
Console.WriteLine($"Error updating RPM label: {ex.Message}");
}
}
/*public static double ParseRealRPM(string realrpmdata)
{
// Find the RPM value in the response
int startIndex = realrpmdata.LastIndexOf("010C") + 4;
// Check if the start index is valid
if (startIndex < 0 || startIndex + 4 > realrpmdata.Length)
{
// Handle invalid data or return a default value
return 0.0; // You can adjust the default value based on your needs
}
// Get the last 4 digits of the response
string rpmHex = realrpmdata.Substring(startIndex, 4);
try
{
// Convert the hexadecimal value to decimal
int rpmDecimal = Convert.ToInt32(rpmHex, 16);
// Divide the decimal value by 4
double realrpmValue = rpmDecimal / 4.0;
return realrpmValue;
}
catch (FormatException ex)
{
// Handle the FormatException (non-parsable characters)
// Log the error or return a default value
Console.WriteLine($"Error parsing RPM value: {ex.Message}");
return 0.0; // You can adjust the default value based on your needs
}
}*/
/* public void OnRealRAWRpmReceived(string realrpmdata)
{
RunOnUiThread(() => anotherRpmDoubleLabel.Text = $"RealRPM: {ParseRealRPM(realrpmdata)}");
}*/
private void UpdateRPMUI(double actualRPM)
{
RunOnUiThread(() => rpmLabel.Text = $"RPM: {actualRPM:F2} RPM");
}
private void OnSpeedReceived(string speeddata)
{
try
{
// Update the speed label with raw speed data
RunOnUiThread(() => rawSpeedLabel.Text = $"Raw Speed Data: {speeddata}");
// Parse and display the converted speed value
int speedValue = ObdProtocol.ParseSpeed(speeddata);
UpdateSpeedUI(speedValue);
}
catch (Exception ex)
{
Console.WriteLine($"Error parsing Speed data: {ex.Message}");
}
}
private void UpdateSpeedUI(int speedValue)
{
RunOnUiThread(() => speedLabel.Text = $"Speed: {speedValue} km/h");
}
/* public void OnRAWRpmReceived(string rpmData)
{
// Check if the response contains "010C" (the command)
int index = rpmData.IndexOf("010C");
if (index != -1)
{
// If "010C" is found, extract the RPM data after it
string rpmDataWithoutCommand = rpmData.Substring(index + 8); // Assuming "010C" is 4 characters long
// Extract the last 4 digits of the response
string lastFourDigitsHex = rpmDataWithoutCommand.Substring(0, 8);
// Convert the last 4 hexadecimal digits to decimal
int decimalValue = Convert.ToInt32(lastFourDigitsHex, 16);
// Divide the decimal value by 4
double result = decimalValue / 4.0;
// Now you can update the UI with the result
RunOnUiThread(() => rawRpmLabel.Text = $"Raw RPM Data: {result}");
}
else
{
// If "010C" is not found, handle the response as needed
Console.WriteLine("Response does not contain RPM data.");
}
}*/
public void OnRAWRpmReceived(string rpmData)
{
// Check if the response contains "010C" (the command)
int index = rpmData.IndexOf("010C");
if (index != -1)
{
// If "010C" is found, extract the RPM data after it
string rpmDataWithoutCommand = rpmData.Substring(index + 4); // Assuming "010C" is 4 characters long
// Now you can update the UI with the extracted RPM data
RunOnUiThread(() => rawRpmLabel.Text = $"Raw RPM Data: {rpmDataWithoutCommand}");
}
else
{
// If "010C" is not found, handle the response as needed
Console.WriteLine("Response does not contain RPM data.");
}
}
/* public void OnRAWRpmReceived(string rpmData)
{
// Check if the response contains "410C" (the command)
int index = rpmData.IndexOf("410C");
if (index != -1)
{
// If "410C" is found, extract the RPM data after it
string rpmDataWithoutCommand = rpmData.Substring(index + 4); // Assuming "410C" is 4 characters long
// Extract the last 4 digits of the response
string lastFourDigitsHex = rpmDataWithoutCommand.Substring(0, 4);
// Convert the last 4 hexadecimal digits to decimal
int decimalValue = Convert.ToInt32(lastFourDigitsHex, 16);
// Divide the decimal value by 4
double result = decimalValue / 4.0;
// Now you can update the UI with the result
RunOnUiThread(() => rawRpmLabel.Text = $"Raw RPM Data: {result}");
}
else
{
// If "410C" is not found, handle the response as needed
Console.WriteLine("Response does not contain RPM data.");
}
}*/
/*public void OnRAWRpmReceived(string rpmdata)
{
try
{
// Parse the RPM value as a string
string rpmHex = ObdProtocol.ParseRPM(rpmdata);
// Convert the string to decimal and divide by 4.0
double rpmValue = Convert.ToInt32(rpmHex, 16) / 4.0;
// Update the raw RPM label with the parsed raw RPM data
RunOnUiThread(() => rawRpmLabel.Text = $"Raw RPM{rpmValue} data");
}
catch (Exception ex)
{
// Handle any exceptions that might occur during parsing or UI update
Console.WriteLine($"Error processing RPM data: {ex.Message}");
}
}*/
/*VAJNO RABOTESHT KOD ZA SKOROST V 16-TICEN KOD private void OnRawSpeedReceived(string rawspeeddata)
{
RunOnUiThread(() => rawSpeedLabel.Text = $"Raw Speed Data: {rawspeeddata}");
}*/
public void OnRawSpeedReceived(string rawSpeedData)
{
// Check if the response contains "010D" (the command for speed)
int index = rawSpeedData.IndexOf("010D");
if (index != -1)
{
// If "010D" is found, extract the speed data after it
string speedDataWithoutCommand = rawSpeedData.Substring(index + 4); // Assuming "010D" is 4 characters long
// Now you can update the UI with the extracted speed data
RunOnUiThread(() => rawSpeedLabel.Text = $"Raw Speed Data: {speedDataWithoutCommand}");
}
else
{
// If "010D" is not found, handle the response as needed
Console.WriteLine("Response does not contain speed data.");
}
}
private void InitializeBluetooth()
{
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.DefaultAdapter;
if (bluetoothAdapter == null)
{
return;
}
if (!bluetoothAdapter.IsEnabled)
{
Android.Content.Intent enableBtIntent = new Android.Content.Intent(BluetoothAdapter.ActionRequestEnable);
StartActivityForResult(enableBtIntent, requestCode: 1);
return;
}
BluetoothDevice obdDevice = FindOBDIIBluetoothDevice("00:1D:A5:68:98:8A");
if (obdDevice != null)
{
try
{
UUID sppUuid = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB");
btnSocket = obdDevice.CreateRfcommSocketToServiceRecord(sppUuid);
btnSocket.Connect();
// Connection successful, show a message to the user
MainThread.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(this, $"Bluetooth connection to {obdDevice.Name} established.", ToastLength.Long).Show();
});
// Set the protocol to "AUTO" after connection
// Request protocol information after the connection is successful
}
catch (Exception ex)
{
// Connection failed, show a message to the user
MainThread.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(this, $"Bluetooth connection error: {ex.Message}", ToastLength.Long).Show();
});
}
}
else
{
// Device not found, show a message to the user
MainThread.BeginInvokeOnMainThread(() =>
{
Toast.MakeText(this, "Bluetooth device not found.", ToastLength.Long).Show();
});
}
}
private BluetoothDevice FindOBDIIBluetoothDevice(string deviceAddress)
{
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.DefaultAdapter;
if (bluetoothAdapter == null || !bluetoothAdapter.IsEnabled)
{
return null;
}
foreach (BluetoothDevice device in bluetoothAdapter.BondedDevices)
{
if (device.Address == deviceAddress)
{
return device;
}
}
// If not found among bonded devices, try discovering new devices
if (bluetoothAdapter.IsDiscovering)
{
bluetoothAdapter.CancelDiscovery();
}
bluetoothAdapter.StartDiscovery();
// Wait for discovery to complete (you might want to implement a BroadcastReceiver for better handling)
System.Threading.Thread.Sleep(5000); // Adjust this timeout as needed
foreach (BluetoothDevice device in bluetoothAdapter.BondedDevices)
{
if (device.Address == deviceAddress)
{
return device;
}
}
return null;
}
}
}
和我的 obdprotocol 类
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace App6
{
public class ObdProtocol
{
/* public static double ParseRPM(string rpmdata)
{
// Find the RPM value in the response
int startIndex = rpmdata.IndexOf("010C") + 6;
string rpmHex = rpmdata.Substring(startIndex, 4);
// Convert the hex RPM value to decimal
int rpmDecimal = Convert.ToInt32(rpmHex, 16);
// Calculate the actual RPM value as per the specified formula
double actualRPM = rpmDecimal * 0.25; // Multiply by 0.25 to get RPM in its physical units
return actualRPM;
}*/
/* WOOORKS public static string ParseRPM(string rpmdata)
{
// Find the RPM value in the response
//changed from 010C to 410C int startIndex = rpmdata.IndexOf("410C") + 6;
string rpmHex = rpmdata.Substring(startIndex, 4);
return rpmHex;
}*/
public static string ParseRPM(string rpmdata)
{
// Find the RPM value in the response
//hanged from 010C to 410C
int startIndex = rpmdata.IndexOf("410C") + 6;
string rpmHex = rpmdata.Substring(startIndex, 4);
return rpmHex;
}
/* public static double ParseRealRPM(string realrpmdata)
{
// Find the RPM value in the response
int startIndex = realrpmdata.LastIndexOf("010C") + 4;
// Get the last 4 digits of the response
string rpmHex = realrpmdata.Substring(startIndex, 4);
// Convert the hexadecimal value to decimal
int rpmDecimal = Convert.ToInt32(rpmHex, 16);
// Divide the decimal value by 4
double realrpmValue = rpmDecimal / 4.0;
return realrpmValue;
}*/
public static int ParseSpeed(string speeddata)
{
// Example: assuming data is in the format "2131 7F", where 7F is speed
int startIndex = speeddata.IndexOf("410D") + 8;
string speedHex = speeddata.Substring(startIndex, 2);
return Convert.ToInt32(speedHex, 16);
}
}
}
以下是 CAN 车辆对 Pid 0x0C rpm 的典型字符串响应
“7E804410C0AF4”。
此响应表明命令 410C 已发送至车辆的 Ecu,并且 Ecu 已响应上述字符串。最后两个字节包含计算转速所需的信息。他们是0A F4。
使用 SAE 约定,我们将它们称为 dataA 和 dataB,声明为 int。 处理它们的最简单方法如下。 2 字节响应是 dataA = 000A 和 dataB = 00F4。所以
rpm = dataA << 8 | dataB.
现在你有了两个字节的数字表示。
Pid 0x0C 的缩放位为每位 1/4 rpm。现在除以 4。因此,rpm 范围为 0 – 16383.75rpm。
处理 Pid 字符串响应的最简单方法是为每个 Pid 提供一个事件处理程序。例如
public EngineRPMSensorReceivedEventArgs(byte[] message) : base(message){…}
然后当您解析 OBD 字符串响应时
case 0x0C: // Engine rpm
OnEngineRPMSensorReceived(new EngineRPMSensorReceivedEventArgs(pidMessage));
break;
同一车辆对 Pid 0x0D 速度 7E803410D19 的响应。 dataA 为 19。单字节响应。缩放位 1 km/h。因此19公里/小时。最大值 255 公里/小时。
case 0x0D: // Vehicle speed
OnVehicleSpeedSensorReceived(new VehicleSpeedSensorReceivedEventArgs(pidMessage));
break;
处理 OBD 数据始终是十六进制字符串到数字,因此您需要使用最有效的技术来处理转换。 C# Sharp 拥有您需要的所有位操作工具,无需求助于 Convert.ToInt32() 等。
不要忘记我的示例响应是针对 CAN 车辆的,与早期协议的响应完全不同(数据相同但标头不同)。