Serial read ASCII string, convert to HEX and then to DEC as a float.

Hello wherever you are in the world.

I am reading serial data via Serial2 from a car ECU into a string that I have got delimited by a "$" (ASCII 36).

String inputString = "";         // a string to hold incoming data
boolean stringComplete = false;  // whether the string is complete
int buffer[100];
int count = 0;

void setup() {
  // initialize serial:
  Serial.begin(19200);                      // Used for outputting serial data to laptop for debugging
  Serial2.begin(19200);                    // Connected to car computer
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
}

void loop() {
  // print the string when a newline arrives:
  if (stringComplete) {
    Serial.println(inputString); 
    inputString = "36";         // clear the string:
    stringComplete = false;     //Reset the flag
    count = 0;    //Reset the array counter
  }
}

/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX.  This routine is run between each time loop() runs, so using delay inside loop can delay response.  Multiple bytes of data may be available.
 */
void serialEvent2() {                             //Checking serial port 2 on Mega 2560
  while (Serial2.available()) {
    // get the new byte:
    byte inChar = Serial2.read();
    // add it to the inputString:
    inputString += inChar;
    buffer[count] = inChar;
    count++;
    // if the incoming character is a $, set a flag so the main loop can do something about it:
    if (inChar == '

This is what the code is outputting.

36482274181171210000242324330000235300000000000015833404000211812274128132497936
36482274181171210000242324330000235300000000000015833404000211812274128132497936
36482254181171210000242324330000235300000000000015833404000211812254128132498336

To break down the string, here is an explanation of the data.

36482274181171210000242324330000235300000000000015833404000211812274128132497936

36 48 227 4 181 1 7 121 etc etc.....

First byte = 36 (DEC, 24 HEX)
Second byte = 48 (DEC, 30 HEX)
Third byte = 227 (DEC, E3 HEX)
Forth byte = 4 (DEC, 4 HEX)

SO, byte 3 and 4 when reversed give me the battery voltage of the ECU that the serial data is coming out of.
4E3 HEX converted to decimal is 1251 divided by 100 give a voltage of 12.51 volts which is the correct value that I am after

My problem is that I have the data as a String, and no amount of strtoi() or strtoul() combinations that I try give me the expected results when I apply.

This was the code that I was using to get the bytes in the correct order, I realise that temp3 is a string and is probably not correct.

temp3 = "";
  Serial.print("Battery voltage: ");
  //Battery voltage code convert 4E3 from hex to decimal and then display.
  temp3 += buffer[3];
  temp3 += buffer[2];
  Serial.println(temp3);  //so you can see the captured string

The String temp3 would then equal 4E3, but I have no idea about what to do with this to get it to the float value of 12.51...

Any help would be very appreciated, as I have spent 4 days trying to nut this out, and all I have now is a massive headache.) {
      stringComplete = true;
    }
  }
}


This is what the code is outputting.

36482274181171210000242324330000235300000000000015833404000211812274128132497936
36482274181171210000242324330000235300000000000015833404000211812274128132497936
36482254181171210000242324330000235300000000000015833404000211812254128132498336

To break down the string, here is an explanation of the data.

36482274181171210000242324330000235300000000000015833404000211812274128132497936

36 48 227 4 181 1 7 121 etc etc.....

First byte = 36 (DEC, 24 HEX)
Second byte = 48 (DEC, 30 HEX)
Third byte = 227 (DEC, E3 HEX)
Forth byte = 4 (DEC, 4 HEX)

SO, byte 3 and 4 when reversed give me the battery voltage of the ECU that the serial data is coming out of.
4E3 HEX converted to decimal is 1251 divided by 100 give a voltage of 12.51 volts which is the correct value that I am after

My problem is that I have the data as a String, and no amount of strtoi() or strtoul() combinations that I try give me the expected results when I apply.

This was the code that I was using to get the bytes in the correct order, I realise that temp3 is a string and is probably not correct.

§DISCOURSE_HOISTED_CODE_1§


The String temp3 would then equal 4E3, but I have no idea about what to do with this to get it to the float value of 12.51...

Any help would be very appreciated, as I have spent 4 days trying to nut this out, and all I have now is a massive headache.

My problem is that I have the data as a String

Got it in one.

Consider changing to C style strings, which are arrays of chars, and you can manipulate the data by accessing the array.
Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking will show you how to read Serial input into a string. Then you can use the str*** functions on portions of the string. You could even do the conversions on the fly as the data arrives as it is in fixed positions in the data.

You've already got the data in an array as well as the string (why are you bothering with the string even?)

    byte inChar = Serial2.read();
    // add it to the inputString:
    inputString += inChar;
    buffer[count] = inChar;

The "buffer" variable contains your data in raw form. Just access the bytes from that array, and delete any lines referencing inputString.

Thanks , majenko you are correct, I do have the values in raw format in the buffer[] array.
The Serial.print(inputString) is just there for debugging so I can see that the data is accurate.

Here is a link to the ECU datastream protocol, might make things easier. http://www.mrm-racing.se/bag/SM4109datastream.pdf

But I do not know how to arrange the data as low byte first to construct the right values to get the result, hence my question.

How do I take an 8 bit value in buffer[4] (the forth byte) as HEX and add it to another 8 bit value in buffer[3] (the third byte) as HEX to create a 16 bit value and then display this as a float value?

First byte = 36 (DEC, 24 HEX)
Second byte = 48 (DEC, 30 HEX)
Third byte = 227 (DEC, E3 HEX)
Forth byte = 4 (DEC, 4 HEX)

Byte 3 and 4 when reversed give me the battery voltage of the ECU that the serial data is coming out of.

4E3 HEX converted to decimal is 1251 divided by 100 give a voltage of 12.51 volts.

As you can tell, I am not a programmer.... And sorry if I have made this confusing.

Read the buffer, and decide what to do with each byte.
One thing you could do, is "nothing", which means you throw it away.
Get a byte you want to use.
Put those in an other buffer/variable.

You need to understand the math.
If you need a decimal value, convert hex to decimal.
The second byte you've got has a value that is to be multiplied by 16 (decimal), and then added to the first byte you had.
There is no difference to counting with digital decimal numbers, but you need to keep in mind how this math/counting works.
10 decimal means 1 times 10 plus 0 times 1.
10 hex means 1 times 16 plus 0 times 1.

... Or just use bitshifting:

int out = (highByte << 8) | lowByte;

sconno:
To break down the string, here is an explanation of the data.

36482274181171210000242324330000235300000000000015833404000211812274128132497936

36 48 227 4 181 1 7 121 etc etc.....

First byte = 36 (DEC, 24 HEX)
Second byte = 48 (DEC, 30 HEX)
Third byte = 227 (DEC, E3 HEX)
Forth byte = 4 (DEC, 4 HEX)

It seems pretty unlikely that the data stream is actually encoded in decimal ascii like that, because it makes it impossible to decode. Are you actually receiving a sequence of binary values? In that case all you need to do is read the appropriate number of bytes and compose the multi-byte values from the appropriate bytes. You can use the word() function to do that with two-byte values.

Thanks everyone for your valuable input.... I will check out your suggestions and report back.

Thanks so much for everyones input, the code is now working and displaying exactly what it should.

Majenko, your suggestion to use int out = (highByte << 8) | lowByte; was spot on.

      highByte = buffer[2];
      lowByte = buffer[1];
      int volt1 = (highByte << 8) | lowByte;
      float  volt2 = volt1 / 100.0;