serial reading problem

Hi guys,
I’m newbie in arduino. I would like to read compass output which in form of (NMEA):

$HCHDM,abc.d,M*

where a is the BCD hundreds
digit of the heading, b is the tens digit, c is the ones digit, and d is the tenths digit.
is an ASCII character in the range 0-9 or A-F that represents the most
significant nibble of the checksum. Similarly, represents the least significant
nibble of the checksum.

I would like to read abc.d values…How could i do that? Any example?

Hi, welcome to the forum.

You should read the complete string to be able to check if the checksum is okay.
I don't understand if that is binary data or readable ASCII ?

Let's start at the beginning: Which Arduino board do you have ? Is that RS-232 ? Do you use a converter to convert it to TTL levels ? What is the baudrate ?

You can read the reference of the Arduino 'Serial' library. Have a look what kind of functions there are and have a look at some examples.

Hi Peter_n. Thanks for your reply. Bytes are encoded as 7 bit ASCII characters in an 8 bit byte with the most significant bit set to zero. I'm using Arduino Mega 22560 board. Yes, it is serial and i converted to TTL level using MAX232. I connect to the Mega using RX1 (UART 1) with baudrate of 4800...I have checked the available function but i'm not really sure how to read the desired value..

The Arduino Mega 2560 board has 4 hardware Serial ports. Since the first serial port is used to upload a sketch, use the next one, with a MAX232. You have all that, excellent 8)

There are maybe five good ways to do this. I'm was thinking to use the ReadBytesUntil() this time.

Create a buffer to hold all the data, it is about 19 bytes, so let's have a buffer of 32.

  char buffer[32];

  int howMany = Serial1.ReadBytesUntil( '\n', buffer, sizeof(buffer));      // stop reading at '\n'

  // test if the data makes sense
  if ( howMany > 15 && buffer[0] == '

I just wrote that ascii_bcd_to_binary function myself. It is totally untested. But I hope I gave a direction how the string can be read and converted.
That function returns an integer value in 1/100 degrees, perhaps using a float number is better.

There are examples for reading NMEA code with an Arduino. I don't know if they are only for GPS or also for other things.

&& buffer[10] == '.')        // add more tests
  {
    // bcd to binary.
    char bcd_buf[8];
    bcd_buf [0] = buffer[11];
    bcd_buf [1] = buffer[9];
    bcd_buf [2] = buffer[8];
    bcd_buf [3] = buffer[7];
    bcd_buf [4] = '\0';            // zero terminator for string.
    int heading = ascii_bcd_to_binary( bcd_buf);
  }

int ascii_bcd_to_binary( char *p)
{
  int x = 0;

while( *p)          // while not zero-terminator
  {
    x *= 10;          // multiply the previous by 10, before adding the new.
    x += *p - '0';    // assuming that it is readable ascii, substract '0' to get 0...9
    p++;                // advance to next
  }

return ( x);
}


I just wrote that ascii_bcd_to_binary function myself. It is totally untested. But I hope I gave a direction how the string can be read and converted.
That function returns an integer value in 1/100 degrees, perhaps using a float number is better.

There are examples for reading NMEA code with an Arduino. I don't know if they are only for GPS or also for other things.
https://github.com/ericbarch/arduino-libraries/tree/master/NMEA
http://guysherman.com/2012/07/18/arduino-and-nmea/

Bytes are encoded as 7 bit ASCII characters in an 8 bit byte with the most significant bit set to zero.

This is just plain ASCII it is not BCD.

So as a starter for 10 just read from Serial1 and write to Serial.

Mark

My preferred solution is to read until a “\r” or ‘\n’ is found, and then use sscanf to extract values from it.

Something like this (the sscanf part is untested)

const size_t INPUT_SIZE = 20;

void loop()
{
	if ( Serial.available() > 0 )
	{
		static char input[ INPUT_SIZE ];
		static uint8_t i;

		char c = Serial.read();

		if ( c != '\n' && i < INPUT_SIZE - 1 )
			input[ i++ ] = c;
		else
		{
			input[ i ] = '\0';
			i = 0;

			char heading[6];
			uint8_t checksum;

			if ( sscanf( input, "$HCHDM,%[^,],M*%2X", heading, &checksum ) == 2 )
			{
				Serial.print( "Heading: " );
				Serial.println( heading );
				Serial.print( "Checksum: " );
				Serial.println( checksum );
			}
		}
	}
}

Why did I store heading in char array is because sscanf float support is disabled by default. So, either enable it or use strtod, if you want a float :wink: