Extracting Last 4 Digits from RPM Response in Xamarin Android (e.g., "41 0C") and Performing Decimal Conversion and Division by 4

44 Views Asked by At

I'm developing a digital dashboard for a Xamarin Android app using C#. As part of this project, I'm receiving RPM responses in the form of hexadecimal strings. Now, I need to extract the last 4 digits from these responses, convert them to decimal, and then divide the result by 4 programmatically Like if response is in this format 41 0C 11 F1 I would like to extract this 11 F1 but the values could be different. here is my mainactitivy.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;
        }
    }
}

and my obdprotocol class

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);
        }
    }
}



1

There are 1 best solutions below

0
user2153142 On

The following is a typical string response from a CAN vehicle for Pid 0x0C rpm

"7E804410C0AF4".

This response shows that the command 410C has been sent to the Ecu of the vehicle and the Ecu has responded with the above string. The last two bytes contain the info required for the calculation of rpm. They are 0A F4.

Using the SAE convention we call them dataA and dataB, declared as int. The simplest way to handle them is the following. The 2-byte response is dataA = 000A and dataB = 00F4. Therefore rpm = dataA << 8 | dataB.

Now you have a numeric representation of the two bytes.

The scaling bit for Pid 0x0C is ¼ rpm per bit. So now divide by 4. Therefore, the rpm range is 0 – 16383.75rpm.

The easiest way to process Pid string responses is to have an event handler for each Pid. E.g.

public EngineRPMSensorReceivedEventArgs(byte[] message) : base(message){…}

Then when you are parsing the OBD string responses

case 0x0C: // Engine rpm
    OnEngineRPMSensorReceived(new EngineRPMSensorReceivedEventArgs(pidMessage));
    break;

Response from the same vehicle for Pid 0x0D speed 7E803410D19. dataA is 19. Single byte response. Scaling bit 1 km/h. Therefore 19km/h. Maximum value 255 km/h.

case 0x0D: // Vehicle speed
    OnVehicleSpeedSensorReceived(new VehicleSpeedSensorReceivedEventArgs(pidMessage));
break;

Processing OBD data is always hex string to numeric, so you need to use the most efficient techniques to handle the conversion. C# sharp has all the bit manipulation tools you need without resorting to things like Convert.ToInt32() etc.

Don't forget that my example responses are for CAN vehicles, totally different responses (data is the same but different headers) for the earlier protocols.