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.

jsotola
  • 1,554
  • 2
  • 12
  • 20

2 Answers2

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

b1n3t
  • 286
  • 1
  • 6
Juraj
  • 18,264
  • 4
  • 31
  • 49
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(&quot;Communication with WiFi module failed!&quot;);
    while (true);
  }

  byte mac[6];
  WiFi.macAddress(mac);
  Serial.print(&quot;MAC: &quot;);
  for (int i = 5; i &gt;= 0; i--) {
    if (mac[i] &lt; 16) {
      Serial.print(&quot;0&quot;);
    }
    Serial.print(mac[i], HEX);
    if (i &gt; 0) {
      Serial.print(&quot;:&quot;);
    }
  }
  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.

Rohit Gupta
  • 618
  • 2
  • 5
  • 18
b1n3t
  • 286
  • 1
  • 6