2

How to read programmatically the device specific properties from the Arduino MKR WiFi 1010 chip or board?

For example, following properties of the device:

  • any identification number, that can uniquely identify the physical device, integer or GUID or string or any other, how to read that from the code?
  • SerialNumber of the MRK WiFi 1010 device
  • ModelNumber of the MRK WiFi 1010 device

I have tried to check the documentation and examples, however I couldn't find anything at all.

1

2 Answers 2

1

MAC address could be of use to you, but remember that it can be spoofed. Aside from that Serial and Model numbers are not accessible from software layer.

Here is a simple implementation for fetching the MAC address

    #include <WiFiNINA.h>
    
    void setup() {
      Serial.begin(9600);
      while (!Serial);
      
      if (WiFi.status() == WL_NO_MODULE) {
        Serial.println("Communication with WiFi module failed!");
        while (true);
      }
      
      byte mac[6];
      WiFi.macAddress(mac);
      Serial.print("MAC: ");
      for (int i = 5; i >= 0; i--) {
        if (mac[i] < 16) {
          Serial.print("0");
        }
        Serial.print(mac[i], HEX);
        if (i > 0) {
          Serial.print(":");
        }
      }
      Serial.println();
    }
    
    void loop() {}

Another solution that I can think of is writing your own unique identifier such as UUID to EEPROM memory for each board.

If you're generating a UUID in the context of a server or a device with more resources, there are libraries and tools that can create these for you based on various factors to ensure uniqueness (like timestamp, MAC address, etc.). However, with a microcontroller like the one on the Arduino MKR WiFi 1010, you have a more constrained environment, and the process can be more manual.

You can use an online UUID generator or can generate it using Python:

    import uuid
    print(uuid.uuid4())

After generating your UUID's, you can write them on EEPROM but make sure to not over-write EEPROM since it has very limited call boundaries.

Writing to EEPROM

#include <EEPROM.h>

const char* MY_UUID = "550e8400-e29b-41d4-a716-446655440000";
const int EEPROM_UUID_ADDRESS = 0;  // starting address in EEPROM

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

  // Write UUID to EEPROM
  for (int i = 0; i < strlen(MY_UUID); i++) {
    EEPROM.write(EEPROM_UUID_ADDRESS + i, MY_UUID[i]);
  }
  Serial.println("UUID written to EEPROM");
}

void loop() {}

"starting address in EEPROM" is the address of the first box (or byte) where the program begins storing our UUID. By specifying const int EEPROM_UUID_ADDRESS = 0;, we are saying we'll start storing the UUID from the very first byte (box) of the EEPROM. If you were storing other data in EEPROM, you would need to ensure that the addresses don't overlap. For example, if you had other data stored in the first 10 bytes, you might set EEPROM_UUID_ADDRESS to 10 to start storing the UUID from the 11th byte.

Reading from the EEPROM:

#include <EEPROM.h>

const int UUID_LENGTH = 36;  // Length of the UUID string
char readUUID[UUID_LENGTH + 1];  // +1 for the null terminator
const int EEPROM_UUID_ADDRESS = 0;  // starting address in EEPROM

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

  // Read UUID from EEPROM
  for (int i = 0; i < UUID_LENGTH; i++) {
    readUUID[i] = EEPROM.read(EEPROM_UUID_ADDRESS + i);
  }
  readUUID[UUID_LENGTH] = '\0';  // Null terminate the string

  Serial.print("UUID read from EEPROM: ");
  Serial.println(readUUID);
}

void loop() {}

+1 for the null terminator:

In the C and C++ programming languages, strings are represented as an array of characters. The end of the string is marked by a special character known as the "null terminator" or "null character", which is represented by the value '\0' (a byte with all bits set to 0).

When we read or write strings to/from memory (like EEPROM), we often need to account for this null terminator to ensure the string is properly terminated and can be safely used with string functions.

In the code, the UUID is 36 characters long. When we declare an array to hold this UUID with the line:

char readUUID[UUID_LENGTH + 1];

We're allocating space for the 36 characters of the UUID plus an additional character for the null terminator, hence UUID_LENGTH + 1.

Later, after reading the UUID from EEPROM, we ensure this array is a properly terminated string by adding the null terminator:

readUUID[UUID_LENGTH] = '\0';

This ensures that when we use readUUID in functions that expect a string, they'll recognize where the string ends. Without the null terminator, these functions wouldn't know where the string finishes, leading to unpredictable behavior or errors.

I hope that this is helpful.

7
  • 1
    thanks. There are few more ways to get the board information, it is USB device serial number, which is stored in the FTDI chip. However, the Arduino can't read from the FTDI chip, and second way is like this: if defined (__AVR_ATmega2560__) Commented Sep 3, 2023 at 17:31
  • yes, I am aware that FTDI chip contains the USB device serial number but I have came to realise that sometimes chip producers assign the same VID and PID numbers to different boards they produce. if you want to read values from the FTDI chip you can use Python by the way. using serial and ftdi you can get the first available FTDI device serial number and send it to Arduino via COM3 Serial. if you wish i can demonstrate how to do it. using "if defined (AVR_ATmega2560) " would not distinguish between two ATmega boards would it?
    – b1n3t
    Commented Sep 3, 2023 at 17:39
  • 1
    correct, using if defined (__AVR_ATmega2560__), won't allow to distinguish between two ATmega boards. Commented Sep 3, 2023 at 17:41
  • 2
    there is no FTDI on MKR WiFi 1010. the MCU has native USB. and it has an accesible serial number
    – Juraj
    Commented Sep 3, 2023 at 18:21
  • 1
    Ah, thanks for letting us know Juraj, you seem correct. I assumed it uses FTDI as shekhovtsov claimed so. MKR WIFI 1010 uses SAMD21 which has a datasheet: ww1.microchip.com/downloads/en/DeviceDoc/… and it seems like the addresses for serial number are: 0x0080A00C, 0x0080A040, 0x0080A044, 0x0080A048 ----- word 0 to 3. one could just read them like: id[0] = *(volatile uint32_t *)0x0080A00C; and so on...
    – b1n3t
    Commented Sep 3, 2023 at 18:31
6

The IDE shows the serial number so I looked into the SAMD core and found the function which reads the serial number from registers. It is in the CDC.cpp file

uint8_t Serial_::getShortName(char* name) {
    // from section 9.3.3 of the datasheet
    #define SERIAL_NUMBER_WORD_0    *(volatile uint32_t*)(0x0080A00C)
    #define SERIAL_NUMBER_WORD_1    *(volatile uint32_t*)(0x0080A040)
    #define SERIAL_NUMBER_WORD_2    *(volatile uint32_t*)(0x0080A044)
    #define SERIAL_NUMBER_WORD_3    *(volatile uint32_t*)(0x0080A048)

    utox8(SERIAL_NUMBER_WORD_0, &name[0]);
    utox8(SERIAL_NUMBER_WORD_1, &name[8]);
    utox8(SERIAL_NUMBER_WORD_2, &name[16]);
    utox8(SERIAL_NUMBER_WORD_3, &name[24]);
    return 32;
}

from the datasheet of SAMD21:

Each device has a unique 128-bit serial number which is a concatenation of four 32-bit words contained at the following addresses:

The uniqueness of the serial number is guaranteed only when using all 128 bits

Not the answer you're looking for? Browse other questions tagged or ask your own question.