Reputation: 55
I want to know if there is any way to convert a string like "5ABBF13A000A01"
to the next struct using struct and union method:
struct xSensorData2{
unsigned char flags;
unsigned int refresh_rate;
unsigned int timestamp;
};
Data should be:
flags = 0x01 (last byte);
refresh_rate = 0x000A (two bytes);
timestamp = 5ABBF13A (four bytes);
I'm thinking in next struct of data:
struct xData{
union{
struct{
unsigned char flags:8;
unsigned int refresh_rate:16;
unsigned int timestamp:32;
};
char pcBytes[8];
};
}__attribute__ ((packed));
But I got a struct of 12 bytes, I think it's because bit fields don't work for different types of data. I should just convert string to array of hex, copy to pcBytes
and have access to each field.
Update: In stm32 platform i used this code:
bool bDecode_HexString(char *p)
{
uint64_t data = 0; /* exact width type for conversion
*/
char *endptr = p; /* endptr for strtoul validation */
errno = 0; /* reset errno */
data = strtoull (p, &endptr, 16); /* convert input to uint64_t */
if (p == endptr && data == 0) { /* validate digits converted */
fprintf (stderr, "error: no digits converted.\n");
return false;
}
if (errno) { /* validate no error occurred during conversion */
fprintf (stderr, "error: conversion failure.\n");
return false;
}
//printf ("data: 0x%" PRIx64 "\n\n", data); /* output conerted string */
sensor.flags = data & 0xFF; /* set flags */
sensor.rate = (data >> CHAR_BIT) & 0xFFFF; /* set rate */
sensor.tstamp = (data >> (3 * CHAR_BIT)) & 0xFFFFFFFF; /* set timestamp */
return true;
/* output sensor struct */
// printf ("sensor.flags : 0x%02" PRIx8 "\nsensor.rate : 0x%04" PRIx16
// "\nsensor.tstamp: 0x%08" PRIx32 "\n", sensor.flags, sensor.rate,
// sensor.tstamp);
}
and call the function:
char teste[50] = "5ABBF13A000A01";
bDecode_HexString(teste);
I get data = 0x3a000a01005abbf1
Upvotes: 1
Views: 2717
Reputation: 84521
If you are still struggling with the separation of your input into flags, rate & timestamp
, then the suggestions in the comments regarding using an unsigned type to hold your input string converted to a value, and using the exact width types provided in <stdint.h>
, will avoid potential problems inherent in manipulating signed types (e.g. potential sign-extension, etc.)
If you want to separate the values and coordinate those in struct, that is 100% fine. The work come in separating the individual flags, rate & timestamp
. How you choose to store them so they are convenient within your code is up to you. A simple struct utilizing exact-width type could be:
typedef struct { /* struct holding sensor data */
uint8_t flags;
uint16_t rate;
uint32_t tstamp;
} sensor_t;
In a conversion from char *
to uint64_t
with strtoull
, you have two primary validation checks:
utilize both the pointer to the string to convert and the endptr
parameter to validate that digits were in fact converted (strtoull
sets endptr
to point 1-character after the last digit converted). You use this to compare endptr
with the original pointer for the data converted to confirm that a conversion took place (if no digits were converted the original pointer and endptr
will be the same and the value returned will be zero); and
you set errno = 0;
before the conversion and then check again after the conversion to insure no error occurred during the conversion itself. If strtoull
encounters an error, value exceeds range, etc.., errno
is set to a positive value.
(and if you have specific range validations, e.g. you want to store the result in a size less than that of the conversion, like uint32_t
instead of uint64_t
, you need to validate the final value can be stored in the smaller type)
A simple approach would be:
uint64_t data = 0; /* exact width type for conversion */
...
char *p = argc > 1 ? argv[1] : "0x5ABBF13A000A01", /* input */
*endptr = p; /* endptr for strtoul validation */
errno = 0; /* reset errno */
...
data = strtoull (p, &endptr, 0); /* convert input to uint64_t */
if (p == endptr && data == 0) { /* validate digits converted */
fprintf (stderr, "error: no digits converted.\n");
return 1;
}
if (errno) { /* validate no error occurred during conversion */
fprintf (stderr, "error: conversion failure.\n");
return 1;
}
printf ("data: 0x%" PRIx64 "\n\n", data); /* output conerted string */
Finally, separating the value in data
into the individual values of flags, rate & timestamp
, can be done with simple shifts & ANDS, e.g.
sensor_t sensor = { .flags = 0 }; /* declare instance of sensor */
...
sensor.flags = data & 0xFF; /* set flags */
sensor.rate = (data >> CHAR_BIT) & 0xFFFF; /* set rate */
sensor.tstamp = (data >> (3 * CHAR_BIT)) & 0xFFFFFFFF; /* set timestamp */
/* output sensor struct */
printf ("sensor.flags : 0x%02" PRIx8 "\nsensor.rate : 0x%04" PRIx16
"\nsensor.tstamp: 0x%08" PRIx32 "\n", sensor.flags, sensor.rate,
sensor.tstamp);
Putting it altogether, you could do something similar to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <limits.h>
typedef struct { /* struct holding sensor data */
uint8_t flags;
uint16_t rate;
uint32_t tstamp;
} sensor_t;
int main (int argc, char **argv) {
uint64_t data = 0; /* exact width type for conversion */
sensor_t sensor = { .flags = 0 }; /* declare instace of sensor */
char *p = argc > 1 ? argv[1] : "0x5ABBF13A000A01", /* input */
*endptr = p; /* endptr for strtoul validation */
errno = 0; /* reset errno */
data = strtoull (p, &endptr, 0); /* convert input to uint64_t */
if (p == endptr && data == 0) { /* validate digits converted */
fprintf (stderr, "error: no digits converted.\n");
return 1;
}
if (errno) { /* validate no error occurred during conversion */
fprintf (stderr, "error: conversion failure.\n");
return 1;
}
printf ("data: 0x%" PRIx64 "\n\n", data); /* output conerted string */
sensor.flags = data & 0xFF; /* set flags */
sensor.rate = (data >> CHAR_BIT) & 0xFFFF; /* set rate */
sensor.tstamp = (data >> (3 * CHAR_BIT)) & 0xFFFFFFFF; /* set timestamp */
/* output sensor struct */
printf ("sensor.flags : 0x%02" PRIx8 "\nsensor.rate : 0x%04" PRIx16
"\nsensor.tstamp: 0x%08" PRIx32 "\n", sensor.flags, sensor.rate,
sensor.tstamp);
return 0;
}
Example Use/Output
$ ./bin/struct_sensor_bitwise
data: 0x5abbf13a000a01
sensor.flags : 0x01
sensor.rate : 0x000a
sensor.tstamp: 0x5abbf13a
Look things over and let me know if you have further questions.
Upvotes: 2
Reputation: 2603
Here, you have a string of length 14, representing a 7-byte value, consisting of 14 hexadecimal values. Consider this code using strtol
, which hexadecimally converts your string into a long int, and then decodes it digit-wise.
uint64_t n = strtoul(str, NULL, 16); // Convert to hexadecimal
int flags = n % 0x10;
int refresh_rate = (n / 0x100) % 0x100000;
int timestamp = n / 0x1000000;
Here is a test case (#import <stdlib.h>
):
char str[16] = "5ABBF13EE00AFF";
uint64_t n = strtoul(str, NULL, 16); // Convert to hexadecimal
// Use divide and mod to extract digit segment
int flags = n % 0x100;
int refresh_rate = (n / 0x100) % 0x10000;
int timestamp = n / 0x1000000;
// Print timestamp, refresh rate, and flags
printf("%p %p %p", timestamp, refresh_rate, flags);
The expected result is 0x5abbf13e 0xe00a 0xff
as expected.
Upvotes: 1