0

I'm trying to read data from a TCP stack and using the data to control external interfaces etc. The data is in u32_t so if I write for example "test" to the interface the hex value corresponds to 0x74657374. I want to convert this data to their corresponding characters so it is easier to use the data. How do I convert hexadecimal values in a u32_t to its char string?

I've tried print the data directly via the %c format specifier but then it only shows the first character of the string.

/* indicate that the packet has been received */
tcp_recved(tpcb, p->len);
// Put actual data in 32 bit unsigned integer.
tempPtr = (u32_t*)p->payload;
// Print length of the actual data.
xil_printf("Received package. Length = %d \r\n", p->len);
// Reverse the data so it corresponds to the data sent.
u32_t reversedTemp = byte_reverse_32(*tempPtr);

// Prints hex value of data
xil_printf("Data: %08x \r\n",reversedTemp);

if (reversedTemp == 0x6C656431) { /* Read "led1" */
    xil_printf("Data Read: led1");
} else if (reversedTemp == 0x74657374) { /* Read "test */
    xil_printf("Data Read: test");
}

So where I'm using the full hex value in the if statement I only want to use the string value. So for checking test it should be == "test" instead of == 0x74657374.

  • 1
    You can't use `==` to compare strings in C anyway, you need to use [`strcmp`](https://en.cppreference.com/w/c/string/byte/strcmp) to compare ***null-terminated*** byte strings. – Some programmer dude Apr 25 '19 at 11:59
  • 1
    For effectiveness, what you have now is pretty optimal. What I recommend is that you stop using [magic numbers](https://en.wikipedia.org/wiki/Magic_number_(programming)) and replace them with symbolic constants (basically preprocessor macros), but otherwise keep the current comparison (then you don't even have to do the reversion step, to save a few CPU cycles, and the code would still make sense). Oh, and you don't really need the pointer, instead do e.g. `u32_t temp = *(u32_t*)p->payload`. – Some programmer dude Apr 25 '19 at 12:02
  • And finally, if you still want to compare using ASCII strings, you could use [`memcmp`](https://en.cppreference.com/w/c/string/byte/memcmp) and not have to bother about null-termination. – Some programmer dude Apr 25 '19 at 12:06
  • I am note sure but doesn't he mean 0x74657374 should equal to the characters t,e,s and t, respectively? And not being an address of to a char array. – Trickzter Apr 25 '19 at 12:06
  • 1
    The fact that it's not an address to a char array doesn't matter since C isn't very type-safe. You can cast values between types willy-nilly and the compiler won't complain at all. Because of this you can create a variable: `u32_t led1 = 0x6C656431;`; then gets its address and cast it to a "pointer to a char array": `(char*)led1`; and it would technically be roughly equivalent to "led1". – Juan Apr 25 '19 at 12:17
  • Yea sure with the declaration of the address of led1 as a char* I agree! – Trickzter Apr 25 '19 at 12:21
  • @Someprogrammerdude Strict aliasing!? – Swordfish Apr 25 '19 at 12:34
  • There is no "hex value", it is just an _integer_. The hex is is just a representation of the integer. – Clifford Apr 25 '19 at 17:09

3 Answers3

0

The easiest solution is to get the address of the variable then cast that to a char*.

if (memcmp("test", tempPtr), 4) {
    // The input was "test"
}

You might run into issues related to endianness. This answer has a soltion to that.

Juan
  • 198
  • 1
  • 14
0

You have a bug here: tempPtr = (u32_t*)p->payload; ... byte_reverse_32(*tempPtr). This is a strict aliasing violation and quite likely also a misaligned access. Don't do this.

There's no apparent need to swap the bytes around to compensate for endianess since you intend to convert them to a string anyway(?). If so, you can do this instead (assuming network big endian, CPU little endian):

tcp_recved(tpcb, p->len);
char str[4+1] =
{
  [0] = p->payload[3];
  [1] = p->payload[2];
  [2] = p->payload[1];
  [3] = p->payload[0];
  [4] = '\0'
};
Lundin
  • 195,001
  • 40
  • 254
  • 396
0

Given :

uint32_t num = 0x74657374 ;

Then:

char str[5] = {0} ;
str[0] = num >> 24 ;
str[1] = (num >> 16) & 0xff ;
str[2] = (num >> 8) & 0xff ;
str[3] = num & 0xff ;

The byte reversal may be unnecessary, you can simply reverse the unpacking to get the string in one step when the first character is in the LSB:

char str[5] = {0} ;
str[0] = num & 0xff ;
str[1] = (num >> 8) & 0xff ;
str[2] = (num >> 16) & 0xff ;
str[3] = num >> 24 ;

For the specific cases of either:

  • a big-endian target and the first character in the MSB, or
  • a little-endian target and the first character in the LSB,

then just:

char str[5] = {0} ;
memcpy( str, &num, 4 ) ;

All that said in this case your existing solution is more efficient than string or memory compare. But again the byte reversal is unnecessary - just reverse the byte order in the integer, and avoid the "magic numbers":

#define TEST 0x74736574u ;  // "tset" ("test" reversed)

...

uint32_t temp = *((u32_t*)p->payload) ;
if( temp == TEST ) ...

The string conversion perhaps remains useful is you want to output the strings for debug or human presentation.

Clifford
  • 88,407
  • 13
  • 85
  • 165