UUID version 4 Generator

See the code in action on Tinkercad

/*
 * Uuid Version 4
 *
 * UUIDs are unique numbers that are used for identifying individual units,
 * functions, programmes, or whatever you want to tag.
 *
 * In this demo, press the Arduino Reset button to generate a new number.
 *
 * UUIDs can be assigned sequentially from allocated blocks of numbers, but
 * they are most powerful when randomly assigned. UUIDs are such big numbers
 * that, for all effective purposes, no two numbers will ever match.
 *
 * UUIDs are particularly useful in web-aware devices, or radio networks.
 *
 * For a discussion of the use of UUIDs, see
 *   http://en.wikipedia.org/wiki/Universally_Unique_Identifier
 *
 * For implementation details of UUIDs, see
 *   http://tools.ietf.org/html/rfc4122
 
 TrueRandom library
by Peter Knight, Tinker.it! 2010
http://code.google.com/p/tinkerit

 */

#include <EEPROM.h>

class TrueRandomClass {
  public:
    int randomBit(void);
    char randomByte(void);
    void memfill(char* location, int size);
    void uuid(uint8_t* uuidLocation);
  private:
    int randomBitRaw(void);
    int randomBitRaw2(void);
};
extern TrueRandomClass TrueRandom;

int TrueRandomClass::randomBitRaw(void) {
  uint8_t copyAdmux, copyAdcsra, copyAdcsrb, copyPortc, copyDdrc;
  uint16_t i;
  uint8_t bit;
  volatile uint8_t dummy;
  // Store all the registers we'll be playing with
  copyAdmux = ADMUX;
  copyAdcsra = ADCSRA;
  copyAdcsrb = ADCSRB;
  copyPortc = PORTC;
  copyDdrc = DDRC;
  // Perform a conversion on Analog0, using the Vcc reference
  ADMUX = _BV(REFS0);
  #if F_CPU > 16000000
    // ADC is enabled, divide by 32 prescaler
    ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS0);
  #elif F_CPU > 8000000
    // ADC is enabled, divide by 16 prescaler
    ADCSRA = _BV(ADEN) | _BV(ADPS2);
  #else
    // ADC is enabled, divide by 8 prescaler
    ADCSRA = _BV(ADEN) | _BV(ADPS1) | _BV(ADPS0);
  #endif
  // Autotriggering disabled
  ADCSRB = 0;
  // Pull Analog0 to ground
  PORTC &=~_BV(0);
  DDRC |= _BV(0);
  // Release Analog0, apply internal pullup
  DDRC &= ~_BV(0);
  PORTC |= _BV(1);
  // Immediately start a sample conversion on Analog0
  ADCSRA |= _BV(ADSC);
  // Wait for conversion to complete
  while (ADCSRA & _BV(ADSC)) PORTC ^= _BV(0);
  // Xor least significant bits together
  bit = ADCL;
  // We're ignoring the high bits, but we have to read them before the next conversion
  dummy = ADCH;
  // Restore register states
  ADMUX = copyAdmux;
  ADCSRA = copyAdcsra;
  ADCSRB = copyAdcsrb;
  PORTC = copyPortc;
  DDRC = copyDdrc;
  return bit & 1;
}

int TrueRandomClass::randomBitRaw2(void) {
  for(;;) {
    int a = randomBitRaw() | (randomBitRaw()<<1);
    if (a==1) return 0; // 1 to 0 transition: log a zero bit
    if (a==2) return 1; // 0 to 1 transition: log a one bit
    // For other cases, try again.
  }
}

int TrueRandomClass::randomBit(void) {
  for(;;) {
    int a = randomBitRaw2() | (randomBitRaw2()<<1);
    if (a==1) return 0; // 1 to 0 transition: log a zero bit
    if (a==2) return 1; // 0 to 1 transition: log a one bit
    // For other cases, try again.
  }
}

char TrueRandomClass::randomByte(void) {
  char result;
  uint8_t i;
  result = 0;
  for (i=8; i--;) result += result + randomBit();
  return result;
}

void TrueRandomClass::memfill(char* location, int size) {
  for (;size--;) *location++ = randomByte();
}

void TrueRandomClass::uuid(uint8_t* uuidLocation) {
  // Generate a Version 4 UUID according to RFC4122
  memfill((char*)uuidLocation,16);
  // Although the UUID contains 128 bits, only 122 of those are random.
  // The other 6 bits are fixed, to indicate a version number.
  uuidLocation[6] = 0x40 | (0x0F & uuidLocation[6]); 
  uuidLocation[8] = 0x80 | (0x3F & uuidLocation[8]);
}

TrueRandomClass TrueRandom;

byte uuidNumber[16]; // UUIDs in binary form are 16 bytes long

void printHex(byte number) {
  int topDigit = number >> 4;
  int bottomDigit = number & 0x0f;
  // Print high hex digit
  Serial.print( "0123456789ABCDEF"[topDigit] );
  // Low hex digit
  Serial.print( "0123456789ABCDEF"[bottomDigit] );
}

void printUuid(byte* uuidNumber) {
  int i;
  for (i=0; i<16; i++) {
    if (i==4) Serial.print("-");
    if (i==6) Serial.print("-");
    if (i==8) Serial.print("-");
    if (i==10) Serial.print("-");
    printHex(uuidNumber[i]);
  }
}

void printUuidNum(byte* uuidNumber) {
  int i;
  for (i=0; i<16; i++) { 
    printHex(uuidNumber[i]);
  }
}

void array_to_string(byte array[], unsigned int len, char buffer[]) {
    for (unsigned int i = 0; i < len; i++) {
        byte nib1 = (array[i] >> 4) & 0x0F;
        byte nib2 = (array[i] >> 0) & 0x0F;
        buffer[i*2+0] = nib1  < 0xA ? '0' + nib1  : 'A' + nib1  - 0xA;
        buffer[i*2+1] = nib2  < 0xA ? '0' + nib2  : 'A' + nib2  - 0xA;
    }
    buffer[len*2] = '\0';
}

void setup() {
  Serial.begin(9600);

  // Generate a new UUID
  TrueRandom.uuid(uuidNumber);
  Serial.print("The UUID number is ");
  printUuid(uuidNumber);
  Serial.println();
}

void loop() {
}