Storing MAC Address in EEPROM via Serial ASCII input.

I'm writing a sketch where my Ethernet parameters can be stored in EEPROM. I've managed to get the code mostly working so that through a Serial Menu the IP, Subnet, and Gateway can be stored and then retreived on boot from EEPROM. Now I'm trying to figure out how to store the MAC address. I'm inputting the MAC as a string of ASCII input in the following format ##,##,##,##,##,## where each # is a representing one HEX Byte of the MAC address. How do I store each # in EEPROM? When I did the IP and other params, I stored each ASCII char as an int after doing an ASCII to decimal convertion. I guess it should be obvious, but how do I deal with the HEX value. Is there a simple way of converting each ASCII char to a HEX value? Or can I simply store them as a char and somehow retrieve the char at boot to populate the "byte mac[] = statement"? If I do store them as HEX, when the boot retrieves the value do I need to do anything special as I 'fill' in the byte mac[].

Thanks

As I read my own post I figured out that each # is representing 1/2 of a byte. I still have the same question, but now I only need to know how to manipulate half of it :)

When I did the IP and other params, I stored each ASCII char as an int after doing an ASCII to decimal convertion.

Where is the code that does this? Why not use the same approach for the MAC address?

The only difference will be that you would subtract '0' from some characters ('0' to '9') and 'A' from others ('A' to 'F').

if you can initialize the ethernetshield with the mac address you can just use those numbers to write too eeprom.

Here a simple function that converts 2 chars to one byte so it needs only one eeprom location. ( It is not the most efficient but it works.)

char ar[] = "0123456789ABCDEF";

byte ascii2byte(char a, char b)
{
   byte v = 0;
   for(uint8_t i=0; i < 16; i++) 
   { 
     if (ar[i] == a) v += (i * 16);
     if (ar[i] == b) v += i;
   }
   return v;
}

HEX is just a representation of a byte/int/long, just like BIN or DEC is. The value itself does not change

BOoting: some pseudocode how I would do it, to get the idea.

in setup() 
{
   x = readEeprom(0);
   if (x == 1) MAC = readMAcFromEEPROM();
}

void loop()
{
   ...
   if (IhaveAMACaddress) 
   {
      writeMacToEEPROM(byteArray);
      writeEEPROM(0, 1);
   }
  ...
}

Hey Paul thanks for helping me out again. I haven't studied Ascii to HEX conversion(obvoiusly) so I didn't understand the "subrtact 'A' from the values for A thru F.

So I played with differnent values of x in the sketch below and I quickly came to some conclusions of what needs to be done. First if the ascii representation for Hex A is written as a little "a" or as a caps "A", the conversion will be different. Is there a standard to always use lower case or upper case chars? Also I found I need to subtract '7' if I used a upper case ascii "A", not subtracting 'A' as you mentioned.

I'll mess with this a bit more, but I also intend to 'scope out' robtillaart's answer as well.

int x=0;
void setup()
{
  Serial.begin (9600);
}

void loop()
{
  x= ('B');
  Serial.print("value of Char A is  ");
  Serial.println(x);
  Serial.print("value of  Char minus 0 a is  ");
  Serial.println(x -'7');
  Serial.println();
  
}

If I were you I would just store each byte of the address (IPv4 or MAC or any other address) in the EEPROM directly, without trying to convert it to ascii text or BCD or anything else. Then your load/save code is trivially simple and just needs you to read and write the appropriate number of bytes. It’s not as if you are ever going to be able to access the EEPROM without going through a sketch, so encoding it in a human-readable form achieves nothing useful that I can see.

If I were you I would just store each byte of the address (IPv4 or MAC or any other address) in the EEPROM directly, without trying to convert it to ascii text or BCD or anything else.

I didn't know this was possible. As the ip[] is retrieved from EEPROM I just assumed that the arduino would need an int rather than a stored ascii char. Would your method involve converting to an int after retrieving the value from EEPROM?

First if the ascii representation for Hex A is written as a little “a” or as a caps “A”, the conversion will be different.

Yes. But, it is easy enough to manage that.

char MACstring[] = "ab,cd,ef,AB,12,CD";
byte MACvalues[6];
byte index = 0;
byte val = 0;
byte num;

for(byte i=0; i<strlen(MACString); i++)
{
   char ltr = MACString[i];
   if(ltr >= 'a' && ltr <= 'f')
      num = ltr - 'a' + 10;
   else if(ltr >= 'A' && ltr <= 'F')
      num = ltr - 'A' + 10;
   else if((ltr >= '0' && ltr <= '9')
      num = ltr - '0';
   else if(ltr == ',')
   {
      val *= 16;
      val += num;
      MACvalues[index++] = val;
      val = 0;
   }
}

At the end of this loop, MACvalues should contain 0xab, 0xcd, 0xef, 0xAB, 0x12, and 0xCD. You can then store the 6 bytes in EEPROM for later retrieval.

mfriesen: I didn't know this was possible. As the ip[] is retrieved from EEPROM I just assumed that the arduino would need an int rather than a stored ascii char. Would your method involve converting to an int after retrieving the value from EEPROM?

I don't know whether you have said what format you need the address in at runtime in order to use it. I have assumed you are using a binary representation of the address i.e. an IPv4 address is four bytes, Ethernet MAC address is six bytes, and so on. If your application needs to use them in some other form, that's fine too. Perhaps you need to provide a dotted decimal IPv4 address as an ASCII string.

I'm suggesting that whatever format you need it to be in to use it, should be the format you store it in. Take the received address in whatever format it is given to you, decode it as necessary to get it into the representation you need to use it. For example, if you're given a dotted decimal string and need a four-byte IPv4 address, you need to parse the string. Once you have the data in the format you want to use, just copy the bytes that hold that value in RAM to EEPROM - don't bother encoding or decoding at this stage. The only encoding/decoding needed is to convert from whatever format the address is being given to you, to whatever format you need to actually use the address.

I'd suggest the opposite approach - always store in a binary format, convert to ASCII human-readable version only when needed.

And for exactly the same reason that we do this with integers and floating point numbers.

Actually I found a really interesting thing as I was playing with stored MAC addresses. I had thought the sticker on the Ethernet Shield was a method of the manufacturers to distribute shields without actually having to burn unique MAC addresses on individual shields. I thought that by putting the MAC address from the sticker into the sketch, that value would get loaded on boot. It turns out the shield ignores the sketch values and uses the burned in values. (Checked by looking at the stats from my ASUS router). My need for loading a unique MAC address is not required since the sheild manufacturers are sending out product ready to go.

I'm not sure if this has always been the case. I assume not since the sketch command allows for MAC entries to be added. Maybe there is a way to force the MAC values from the sketch, but for my purposes if the shields are coming preloaded, I don't need to do this.

Thanks for everyones input.

mfriesen: I'm not sure if this has always been the case.

Each manufacturer has a pool of addresses they can assign to physical interface adapters to ensure uniqueness. However, some adapters allow the interface to be re-bound to a different address.