Generating unique ID for each sketch upload?

Hi all, not sure where this question belongs, but this seems like good place, though technically, this is more of a compiler/upload feature question.

Is there a way to set a different unique ID every time I upload the firmware to a board? The ID needs to be accessible from the C code of course. Essentially I want to write a unique identfier to every board that I upload a sketch to so I can identify the boards consistently from the computer (not a fixed value that I hard-code in the sketch for every board). Using the serial port name is not good enough because that can change as the boards are unplugged and plugged in again.

Any tricks / ideas out there?

Thanks,

  • K.

use a makefile that calls a random number generator to insert a -DSYMBOLNAME=RANDOMNUMBER into the compilation option

or find out the location where the variable is stored and write an utility to modify it directly inside the .hex file before it gets flashed

or have the sketch fetch it from eeprom and write a utility to generate a randomized .eep file, load the .eep file into the mcu

Could using the on chip EEPROM memory be a part of a solution?

retrolefty:
Could using the on chip EEPROM memory be a part of a solution?

Thanks Frank and retrolefty!

YES! Can the sketch code access the EEPROM during run time? If it has a known factory initial value (e.g. 0), then the code can initialize it to a random number if it sees the initial value during the setup() phase. That would be about perfect!!

For those who are interested - I found the EEPROM library information. Looks like the initial byte value for uninitialized EEPROM memory is 0xff (255) so that's very useful.

Any tricks / ideas out there?

A few weeks ago we had the discussion how to put the sourcecode of a sketch into the Arduino. This ended in adding a unique reference to the source in the code by means of an UUID which is in fact just a 16 bytes unique number. See - http://arduino.cc/forum/index.php/topic,64193.15.html - for the whole discussion.

This technique might be usefull for you. I have not tried to access the UUID array from the sketch, should be possible as it is just access to progmem ...

Rob

robtillaart:
A few weeks ago we had the discussion how to put the sourcecode of a sketch into the Arduino. This ended in adding a unique reference to the source in the code by means of an UUID which is in fact just a 16 bytes unique number. See - http://arduino.cc/forum/index.php/topic,64193.15.html - for the whole discussion.

This technique might be usefull for you. I have not tried to access the UUID array from the sketch, should be possible as it is just access to progmem ...

Rob

Thanks Rob. I actually needed something more than a UUID to identify a sketch. I need to persist a relatively unique ID for each COPY of the sketch that is uploaded to a board. For example, if I upload the same sketch to 10 boards, I need each board to have a relatively unique ID.

Turns out the EEPROM is quite useful for this. It will set an ID in the on-board EEPROM if it had not been set before. I felt a 4-byte value would be good enough since I only needed a relatively unique value. Here's the snippet of code I wrote in case anyone is interested. I just put it in the setup() function - use at your own risk!!

#include <EEPROM.h>

static int boardIdBaseAddress = 0; // or whatever you want

unsigned long setupEeprom()
{
  byte b1 = EEPROM.read(boardIdBaseAddress);
  byte b2 = EEPROM.read(boardIdBaseAddress + 1);
  byte b3 = EEPROM.read(boardIdBaseAddress + 2);
  byte b4 = EEPROM.read(boardIdBaseAddress + 3);
  
  if ((b1 == 0xff) && (b2 == 0xff) && (b3 == 0xff) && (b4 == 0xff))
  {
    b1 = random(256);
    b2 = random(256);
    b3 = random(256);
    b4 = random(256);
    EEPROM.write(boardIdBaseAddress, b1); 
    EEPROM.write(boardIdBaseAddress + 1, b2); 
    EEPROM.write(boardIdBaseAddress + 2, b3); 
    EEPROM.write(boardIdBaseAddress + 3, b4); 
  }
}

PS: Of course, this function is no-longer sketch-specific. It just persists a random 32-bit (unsigned long) value to any board the first time it's run. Also, if these slots have been set by another sketch before, it might just use whatever existing values in the EEPROM. You can add your own sketch signature to the EEPROM and verify that the value was indeed set by your sketch.

PPS: Is the any pre-defined usage for the EEPROM bytes, or does the sketch have full reign over every available byte in the EEPROM?

Problem with the random generator is that it is not so random at all, it is an algorithm and unless every sketch has an unique seed you will get the same signature for every board.

Furthermore if your boards have used the EEPROM before (or in a factory test who knows) your code will probably not write your signature, so you need an erasor sketch that writes 0xFF to EEPROM first otherwise previous values could be identical ... AVRdude can also do that.

It is quite hard to make this process automatic AND robust (failproof). I think I would use some makefile with AVRdude to write the signature to EEPROM after uploading the sketch, and that makefile would increment the EEPROM file every time called. It can keep track of the numbers applied on which day etc.
Or let some SED command change a signature in the source to some sequence number.

How many boards do you intend to label this way?
Rob

robtillaart:
Problem with the random generator is that it is not so random at all, it is an algorithm and unless every sketch has an unique seed you will get the same signature for every board.

Furthermore if your boards have used the EEPROM before (or in a factory test who knows) your code will probably not write your signature, so you need an erasor sketch that writes 0xFF to EEPROM first otherwise previous values could be identical ... AVRdude can also do that.

It is quite hard to make this process automatic AND robust (failproof). I think I would use some makefile with AVRdude to write the signature to EEPROM after uploading the sketch, and that makefile would increment the EEPROM file every time called. It can keep track of the numbers applied on which day etc.
Or let some SED command change a signature in the source to some sequence number.

How many boards do you intend to label this way?
Rob

Hi Rob, I agree this is a hard problem to have a robust solution. The reference does suggest using randomSeed(analogRead(0)) (or some unused analog input). I did notice some noise in the unused analog input but the noise is minimal (+/-5) so collision is likely, plus you need an unused analog input.

To solve the issue with pre-existing values in the EEPROM, I added another EEPROM header that contains a 4-byte signature header that identfies this sketch(e.g. "APP1") so I can at least tell if my sketch was the last one to write to these bytes. If the header doesn't match, then I generate new values.

All-in-all, the hardest paroblem to solve is the run-time random number generation, and I don't want to depend on any external tools, if possible at all.

Thanks,

  • K.

PPS: Is the any pre-defined usage for the EEPROM bytes, or does the sketch have full reign over every available byte in the EEPROM?

You have free reign over the EEPROM

Lefty

The reference does suggest using randomSeed(analogRead(0)) (or some unused analog input).

On a 328 you also have analog(8) to measure internal temperature, but it is not reliable random - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294239019 - but it might be good enough for your application. It won't cost you a free pin as it is internall so allways free....

If your application has a ethernetshield you might consider fetching the serial number from a simple PHP script on a local website or a randomnumber from www.random.org.

Imho: Sometimes it is much easier to solve things with some extra tools than try to build it all in one sketch.

robtillaart:

The reference does suggest using randomSeed(analogRead(0)) (or some unused analog input).

On a 328 you also have analog(8) to measure internal temperature, but it is not reliable random - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294239019 - but it might be good enough for your application. It won't cost you a free pin as it is internall so allways free....

If your application has a ethernetshield you might consider fetching the serial number from a simple PHP script on a local website or a randomnumber from www.random.org.

Imho: Sometimes it is much easier to solve things with some extra tools than try to build it all in one sketch.

Thanks Rob, I think I got a workaround for my particular implementation. Since the ID is not needed until the computer first asks for it, I moved it from setup() to loop() and deferred the EEPROM setup code until the computer requests it. I also implemented a crude counter inside loop(), and used the loop count as the randomSeed() when the EEPROM setup code is run, so it is almost impossible that two different bords will call randomSeed() at the excat same loop count. I think that's good enough for my purposes.

Appreciate everyone's help.

maybe post your code for future reference (possibly minimizing it to its essence)
Thanks in advance,
Rob

Here's the code in a crude, preliminary form. I snipped out a lot of irreevant code so I might have introduced a few errors, but I'm sure folks can figure it out.

static int boardIdBaseAddress = 0;
static int timer = 0;
static byte b5 = 0;
static byte b6 = 0;
static byte b7 = 0;
static byte b8 = 0;

unsigned long setupEeprom()
{
  byte b1 = EEPROM.read(boardIdBaseAddress);
  byte b2 = EEPROM.read(boardIdBaseAddress + 1);
  byte b3 = EEPROM.read(boardIdBaseAddress + 2);
  byte b4 = EEPROM.read(boardIdBaseAddress + 3);
  b5 = EEPROM.read(boardIdBaseAddress + 4);
  b6 = EEPROM.read(boardIdBaseAddress + 5);
  b7 = EEPROM.read(boardIdBaseAddress + 6);
  b8 = EEPROM.read(boardIdBaseAddress + 7);
  
  if ((b1 != 'A') || (b2 != 'P') || (b3 != 'P') || (b4 != '1') || ((b5 == 0xff) && (b6 == 0xff) && (b7 == 0xff) && (b8 == 0xff)))
  {
    b1 = 'A';
    b2 = 'P';
    b3 = 'P';
    b4 = '1';
    randomSeed(timer);
    b5 = random(256);
    b6 = random(256);
    b7 = random(256);
    b8 = random(256);
    EEPROM.write(boardIdBaseAddress, b1); 
    EEPROM.write(boardIdBaseAddress + 1, b2); 
    EEPROM.write(boardIdBaseAddress + 2, b3); 
    EEPROM.write(boardIdBaseAddress + 3, b4); 
    EEPROM.write(boardIdBaseAddress + 4, b5); 
    EEPROM.write(boardIdBaseAddress + 5, b6); 
    EEPROM.write(boardIdBaseAddress + 6, b7); 
    EEPROM.write(boardIdBaseAddress + 7, b8); 
  }
}

void loop()
{

  // ...

  if (computer_asks_for_ID)
  {
    setupEeprom();

    // ID will be in b5, b6, b7, b8
    send_to_computer(b5, b6, b7, b8);
  }

  // ...

  timer++;
}