Help (dynamically) converting MAC address (as text) to uint32_t value

Hi there,

I'm having a heck of a time converting a MAC address to a uint32_t value. I'm new to C so please pardon my lack of understanding.

Some background. This is a datalogger. Here's the process...

  • arduino powers up and sends a broadcast message via xbee-api
  • RPi receives the xbee broadcast packet (via an asynchronous python script) and sends a unicast message back to the MAC address of the arduino
  • the arduino receives a packet with a bunch of data and sets global variables and goes about datalogging, etc

Let's say the datalogger receives this char array:
"1439316593:0013a20040DCCD01:5:300:60:10800:900"

I delimit by ":" and get the MAC address. No problem there.

So now I have a simple char array of "0013a20040DCCD01" and want to pass that to the serialHigh and serialLow of a function and send a unicast packet to that address.

I've removed portions of the code that are irrelevant. This works:

uint32_t sH = 0x0013a200;
uint32_t sL = 0x40DCCD01;
XBeeAddress64 addr64 = XBeeAddress64(sH, sL);
ZBTxRequest zbTx = ZBTxRequest(addr64, (uint8_t *)thisPayload, strlen(thisPayload));

However, due to the dynamic nature of the datalogger, I can't hard code a MAC address in the code - it needs to be dynamic.

So how does one go from a char of "0013a20040DCCD01" to the sH and sL uint32_t values I need?

I've tried too many things to mention without any success. I can delimit values, get values based on sub strings, etc but I'm having trouble understanding how to convert between data types.

Can anyone help guide me here? Thanks!

John

uint32_t sH = 0;
uint32_t sL = 0;
char *MAC =  (the address of the first character of the MAC address in the string);

for (int i=0; i<16; i++) {
    unsigned char nibble = *MAC++;
    if (nibble >= '0' && nibble <= '9') {
        nibble -= '0';
    {
    else if  (nibble >= 'A' && nibble <= 'F') {
        nibble -= 'A';
        nibble += 0xA;
    }
    //  nibble now contains the binary value of the current hex characters
    if (i<8) {
       // First 8 nibbles go into sH
        sH <<= 4;
        sH |= nibble;
    } else {
        // Last 8 nibbles go into sL
        sL <<= 4;
        sL |= nibble;
    }
}

"0013a20040DCCD01" is not a valid MAC address. A MAC address is only 48 bits long. "0013a20040DCCD01" is 64 bits long.

Hello,

Split your string in two (8 chars in each) and use strtoul.

John, my goodness, you're amazing! Almost working. There was a small typo in your code (for bracket was off). I corrected the bracket so it would compile (and may have butchered your code), but something's still off. The sL is calculated correctly, just not the sH.

  for (int i = 0; i < 16; i++) {
    unsigned char nibble = *MAC++;
    if (nibble >= '0' && nibble <= '9') {
      nibble -= '0';
    }
    else if  (nibble >= 'A' && nibble <= 'F') {
      nibble -= 'A';
      nibble += 0xA;
    }
    //  nibble now contains the binary value of the current hex characters
    if (i < 8) {
      // First 8 nibbles go into sH
      sH <<= 4;
      sH |= nibble;
    } else {
      // Last 8 nibbles go into sL
      sL <<= 4;
      sL |= nibble;
    }
  }

These are the values your snippet figured out:
high: 171200
low: 40DCCD01

Again, apologies I'm not quite there yet to troubleshoot your code, but it's close. Any ideas?

christop - Yes, this is a valid MAC for xbee hardware devices. You're probably thinking of an ethernet MAC address.

guix - I'm trying your idea, too. Hang on...

guix, I tried this:

  char *high = "0013a200";
  char *low = "40DCCD01";

  sH = strtoul(high, NULL, 0);
  sL = strtoul(low, NULL, 0);

  Serial.print("high: ");
  Serial.println(sH, HEX);
  Serial.print("low: ");
  Serial.println(sL, HEX);

which yielded:

high: B
low: 28

Am I missing something here?

Yes, hex is base 16, not 0 :slight_smile:

sH = strtoul(high, NULL, 16);
sL = strtoul(low, NULL, 16);

John, my apologies. I had a lowercase 'a' in my MAC address. Toggled the case and it works great.

char *MAC = "0013a20040DCCD01"; // oops!
char *MAC = "0013A20040DCCD01"; // works :slight_smile:

For others, here's the working code:

  char *MAC = "0013A20040DCCD01";

  for (int i = 0; i < 16; i++) {
    unsigned char nibble = *MAC++;
    if (nibble >= '0' && nibble <= '9') {
      nibble -= '0';
    }
    else if  (nibble >= 'A' && nibble <= 'F') {
      nibble -= 'A';
      nibble += 0xA;
    }

    Serial.print("nibble: ");
    Serial.println(nibble);
    //  nibble now contains the binary value of the current hex characters
    if (i < 8) {
      // First 8 nibbles go into sH
      sH <<= 4;
      sH |= nibble;
    } else {
      // Last 8 nibbles go into sL
      sL <<= 4;
      sL |= nibble;
    }
  }

guix, awesome, that worked. Two working examples! Learning more goodies every day. Thanks fellas!

NP

Johnwasser's solution is more elegant, that's what I would use if I had to.

For your lowercase problem, a simple

nibble = toupper( nibble );

would have fixed it :wink:

prolucid:
christop - Yes, this is a valid MAC for xbee hardware devices. You're probably thinking of an ethernet MAC address.

Hm, looks like you're right about that. I learn something new every day!

guix, thanks for the "one upper". Yea, John's code looks more efficient and I can accommodate for case issues with your one liner. Sweet!

johnwasser:

uint32_t sH = 0;

uint32_t sL = 0;
char *MAC =  (the address of the first character of the MAC address in the string);

for (int i=0; i<16; i++) {
    unsigned char nibble = *MAC++;
    if (nibble >= '0' && nibble <= '9') {
        nibble -= '0';
    {
    else if  (nibble >= 'A' && nibble <= 'F') {
        nibble -= 'A';
        nibble += 0xA;
    }
    //  nibble now contains the binary value of the current hex characters
    if (i<8) {
      // First 8 nibbles go into sH
        sH <<= 4;
        sH |= nibble;
    } else {
        // Last 8 nibbles go into sL
        sL <<= 4;
        sL |= nibble;
    }
}

Hi John.
Sorry I'm late for this post. But I need some help. How can this MAC address be saved in a char array? In the NodeDiscovery function, every Router sends back their MAC Address inside an API frame, so I'm able to read them. But every time I want to print them, I see this:
013FFFFFFA2040FFFFFFA1FFFFFFF246 instead the real Address: 0013A20040A1F246

char addr[17];
-Is this 'char addr[17]' the correct way to store the 64Bit Mac Address?

AntonioGR:
Sorry I'm late for this post.

Almost 5 years late, welcome to 2020! You might be better off starting a new thread that properly describes your problem.