Go Down

Topic: convert from binary string to long (Read 1 time) previous topic - next topic

NATO

Dec 09, 2010, 06:15 pm Last Edit: Dec 09, 2010, 06:16 pm by quemazon Reason: 1
I am trying to read binary data from a motorola GPS. Some of the info such as lat and lon come across as 4 byte long integers. I read those bytes into a 4 character string, but I really need to read them directly into a long variable.

Is there a way that I can read bytes from the serial port directly into a long. Or is there a way I can change the data type of my string to a long? In short, how to I get the 4 byte binary data into a long? Thanks!

Korman

Assuming the byte order is correct, you can read it into a character buffer and then do some pointer magic. If the byte order is wrong, you need to read it in the right place before doing the casting. Assuming your data is read into instr, the code would look something like this:
Code: [Select]
char instr[slen];
// ... Fill somehow instr ...
long mylong = *((long *) instr);


One small warning, when playing around with casting pointer to other types, the compiler does what you tell it to do and you circumvent many protections that would alert you about problems. If you mess up, it's your fault and be hard to figure out. It's a bit like locking the blade guard of a buzzsaw.

Korman

NATO

Thanks for the reply. As I'm new to C and especially pointers, I'm having a hard time wrapping my mind around that last line. Would it be the same as:

Code: [Select]

char instr[slen];
// ... Fill somehow instr ...
long * temp;
temp = &instr;
long mylong = *temp;


I think this is exactly what I was looking for. Thanks again.

Korman

#3
Dec 09, 2010, 07:28 pm Last Edit: Dec 09, 2010, 07:31 pm by Korman Reason: 1
Not exactly, you confuse the types. instr is of type char * or char[] (which is the same), so it's already a pointer and  you shouldn't use the address operator & here. Your code should look like this.
Code: [Select]
char instr[slen];
// ... Fill somehow instr ...
long * temp;
temp = [glow](long *)[/glow]instr; // Here you tell the compiler
     // that you know what you're doing and he should shut up
long mylong = *temp;


The highlighted part is necessary to keep the compiler from complaining about your mixing of incompatible pointer types.

Korman

NATO

I guess this is what's called type casting? I didn't know what it was for until now. Thanks.

Korman

Quote
I guess this is what's called type casting?


Exactly. You tell the compiler to look at a variable in a different way. If the compiler know how to convert from one type to another (like int to float) it will do that for you. With pointers, the compiler will just assume the pointer now points to something of another type and interpret the bytes at this location differently without touching them.
You just have to be a little careful with pointer arithmetic, as adding 1 to a pointer increments the memory location by the size of the type. Here's an example:

Code: [Select]
char str[16] = "abcdefghijklm";
long *lp = (long *) str;
*lp or lp[0] is the long made up from "abcd"
lp[1] or *(lp + 1) is the long made up from "efgh", not "bcde"


That's where it gets confusing.

Korman

MarkT

Quote
Assuming the byte order is correct, you can read it into a character buffer and then do some pointer magic. If the byte order is wrong, you need to read it in the right place before doing the casting. Assuming your data is read into instr, the code would look something like this:

Code: [Select]

char instr[slen];
// ... Fill somehow instr ...
long mylong = *((long *) instr);


One small warning, when playing around with casting pointer to other types, the compiler does what you tell it to do and you circumvent many protections that would alert you about problems. If you mess up, it's your fault and be hard to figure out. It's a bit like locking the blade guard of a buzzsaw.

Korman


There's a better approach IMO that doesn't involve assumptions about endianness (byte order) on your processor, nor require char arrays to be aligned on word boundaries (on many architectures):

Code: [Select]

byte instr[slen];  // use byte which is unsigned to prevent sign-extension
// ... Fill somehow instr ...

long convert_bytes2long (byte b[])
{
 long result = 0L ;
 // can loop whichever direction is appropriate for your device.
 for (byte i = 0 ; i < slen ; i++)
   result = (result << 8) | b[i] ;
 return result ;
}

long mylong = convert_bytes2long (instr) ;


It may take a little more code to do, but it will be portable to other microprocessors with no hidden endianness dependencies or alignment restrictions.  It is also much more obvious to the human reader what's going on.  I think.

PS I haven't tested this code, there might be a bugs ;)
[ I won't respond to messages, use the forum please ]

Go Up