24

Is there a possibility to store a hardware ID for each Raspberry Pi based device?

Let's say I have 10 custom devices based on Raspberry Pi platform that are used for distributed tasks. At some point, for some reason, I want to erase everything from one of them. How can I re-identify the board after that?

Does Pi have a chip that stores a unique ID like a serial number for every board? If not, can I add a (read-only) memory (that can store a simple value even when not powered) to be able to communicate with it through GPIO at any time?

What are the alternatives?

goldilocks
  • 60,325
  • 17
  • 117
  • 234
Alexandru Irimiea
  • 497
  • 3
  • 7
  • 12

5 Answers5

26

Yes each board has a serial number. Methods to retrieve this are described here: How do I get the serial number?

Ralph
  • 447
  • 5
  • 8
6

Here is an easier to use one: MAC address via IPv6 NDP auto configuration. This is a universal method that is applicable to any network interface.

Every NIC, including the USB one used on the Pi, have an 48-bit MAC address, for example, 14:cf:92:20:26:3c.

Every 48-bit MAC address have a unique one-to-one mapping to an EUI-64 address by masking off the last two bits in the third byte (92 to 90) and insert the bytes feff in between the third and fourth byte. The aforementioned 48-bit MAC can be mapped to the EUI-64: 14cf:90fe:ff20:263c.

The IPv6 address auto configuration process uses NDP protocol to discover the 64-bit network address in the 128-bit address. This process will give all your devices within the same network the same 64-bit prefix. The aforementioned EUI-64 is used to populate the 64-bit station address, resulting in a 128-bit globally unique IPv6 address. So if you have the IPv6 network prefix 2001:470:d:472::/64, the aforementioned network card, when used in this network, will be guaranteed to have an IPv6 globally routable address 2001:470:d:472:14cf:90fe:ff20:263c. As long as your management tool is configured to use IPv6, just plug this address into it and it should be good to go.

Maxthon Chan
  • 1,051
  • 8
  • 14
5

As in the other answer the Pi has a unique ID, which is related to the MAC.

In practice it is more convenient for networking to have a unique hostname. I use the following script to set the names based on CPUID.

#!/bin/bash
# script to set Pi hostname based on MAC (or Serial number)
# 2017-08-18
# This script should be run as root (or with sudo) to change names
# If run by a user it will report changes, but will NOT implement them
# Works for PiB (all models), Pi2, Pi3, PiZeroW with on board networking
# PiA models will set a unique Name based on Serial number

PDIR="$(dirname "$0")"  # directory containing script
CURRENT_HOSTNAME=$(cat /etc/hostname)
# Find MAC of eth0, or if not exist wlan0
if [ -e /sys/class/net/eth0 ]; then
    MAC=$(cat /sys/class/net/eth0/address)
elif [ -e /sys/class/net/enx* ]; then
    MAC=$(cat /sys/class/net/enx*/address)
else
    MAC=$(cat /sys/class/net/wlan0/address)
fi

# NOTE the last 6 bytes of MAC and CPUID are identical
CPUID=$(awk '/Serial/ {print $3}' /proc/cpuinfo | sed 's/^0*//')
echo "Current Name" $CURRENT_HOSTNAME
echo "MAC" $MAC
# If you want to specify hostnames create a file PiNames.txt with MAC hostname list e.g.
# b8:27:eb:01:02:03 MyPi
# If not found a unique Name based on Serial number will be set
NEW_HOSTNAME=$(awk /$MAC/' {print $2}' $PDIR"/PiNames.txt")
echo "Name found" $NEW_HOSTNAME
if [ $NEW_HOSTNAME == "" ]; then
    NEW_HOSTNAME="pi"$CPUID
fi

if [ $NEW_HOSTNAME = $CURRENT_HOSTNAME ]; then
    echo "Name already set"
else
    echo "Setting Name" $NEW_HOSTNAME
    echo $NEW_HOSTNAME > /etc/hostname
    sed -i "/127.0.1.1/s/$CURRENT_HOSTNAME/$NEW_HOSTNAME/" /etc/hosts
fi
Milliways
  • 62,573
  • 32
  • 113
  • 225
2

Raspberry Pi Hardware ID

The following C++ code, shows how to get Raspberry Pi hardware ID(s) in various ways. You can concatenate the strings and calculate it's hash (like MD5) to take Raspberry Pi Fingerprint:

(See https://gist.github.com/amir-saniyan/854db35a789f07e61f48994b07d236df)

rpid.cpp:

#include <cstdint>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <streambuf>
#include <sstream>
#include <string>

#include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h>

// Header: /opt/vc/include/bcm_host.h // Library: /opt/vc/lib/libbcm_host.so #include <bcm_host.h>

// ----------------------------------------------------------------------------------------------------

std::string ReadCPUInfo() { std::ifstream fileStream("/proc/cpuinfo");

if(!fileStream)
{
    throw std::runtime_error(&quot;Could not open: `/proc/cpuinfo`.&quot;);
}

std::string cpuinfo((std::istreambuf_iterator&lt;char&gt;(fileStream)), std::istreambuf_iterator&lt;char&gt;());

fileStream.close();

return cpuinfo;

}

// ----------------------------------------------------------------------------------------------------

std::string ReadSysBoardModel() { std::ifstream fileStream("/sys/firmware/devicetree/base/model");

if(!fileStream)
{
    throw std::runtime_error(&quot;Could not open: `/sys/firmware/devicetree/base/model`.&quot;);
}

std::string model((std::istreambuf_iterator&lt;char&gt;(fileStream)), std::istreambuf_iterator&lt;char&gt;());

fileStream.close();

return model;

}

// ----------------------------------------------------------------------------------------------------

std::string ReadSysBoardSerial() { std::ifstream fileStream("/sys/firmware/devicetree/base/serial-number");

if(!fileStream)
{
    throw std::runtime_error(&quot;Could not open: `/sys/firmware/devicetree/base/serial-number`.&quot;);
}

std::string serial((std::istreambuf_iterator&lt;char&gt;(fileStream)), std::istreambuf_iterator&lt;char&gt;());

fileStream.close();

return serial;

}

// ----------------------------------------------------------------------------------------------------

std::string ReadSysMACAddress() { std::ifstream fileStream("/sys/class/net/eth0/address");

if(!fileStream)
{
    throw std::runtime_error(&quot;Could not open: `/sys/class/net/eth0/address`.&quot;);
}

std::string address((std::istreambuf_iterator&lt;char&gt;(fileStream)), std::istreambuf_iterator&lt;char&gt;());

fileStream.close();

return address;

}

// ----------------------------------------------------------------------------------------------------

std::string ReadMailboxBoardModel() { int fd = open("/dev/vcio", 0);

if (fd == -1)
{
    throw std::runtime_error(&quot;Could not open: `/dev/vcio`.&quot;);
}

uint32_t property[32] =
{
    0x0000001C, // Buffer size in bytes (including the header values, the end tag and padding): 7 * 4 = 28 = 0x1C.
    0x00000000, // Request code: 0x00000000 = process request.
    0x00010001, // Tag identifier: Get board model.
    0x00000004, // Value buffer size in bytes: 1 * 4 = 4 = 0x4.
    0x00000000, // Request code: b31 clear: request, b30-b0: reserved.
    0x00000000, // Value buffer.
    0x00000000  // End tag: 0x0
};

const unsigned long MAJOR_NUM = 100;
if (ioctl(fd, _IOWR(MAJOR_NUM, 0, char*), property) &lt; 0)
{
    close(fd);
    throw std::runtime_error(&quot;`ioctl` failed.&quot;);
}

if (property[1] != 0x80000000)
{
    close(fd);
    throw std::runtime_error(&quot;Request failed.&quot;);
}

close(fd);

std::stringstream stream;
stream &lt;&lt; std::hex &lt;&lt; property[5];
std::string result = stream.str();

return result;

}

// ----------------------------------------------------------------------------------------------------

std::string ReadMailboxBoardRevision() { int fd = open("/dev/vcio", 0);

if (fd == -1)
{
    throw std::runtime_error(&quot;Could not open: `/dev/vcio`.&quot;);
}

uint32_t property[32] =
{
    0x0000001C, // Buffer size in bytes (including the header values, the end tag and padding): 7 * 4 = 28 = 0x1C.
    0x00000000, // Request code: 0x00000000 = process request.
    0x00010002, // Tag identifier: Get board revision.
    0x00000004, // Value buffer size in bytes: 1 * 4 = 4 = 0x4.
    0x00000000, // Request code: b31 clear: request, b30-b0: reserved.
    0x00000000, // Value buffer.
    0x00000000  // End tag: 0x0
};

const unsigned long MAJOR_NUM = 100;
if (ioctl(fd, _IOWR(MAJOR_NUM, 0, char*), property) &lt; 0)
{
    close(fd);
    throw std::runtime_error(&quot;`ioctl` failed.&quot;);
}

if (property[1] != 0x80000000)
{
    close(fd);
    throw std::runtime_error(&quot;Request failed.&quot;);
}

close(fd);

std::stringstream stream;
stream &lt;&lt; std::hex &lt;&lt; property[5];
std::string result = stream.str();

return result;

}

// ----------------------------------------------------------------------------------------------------

std::string ReadMailboxBoardSerial() { int fd = open("/dev/vcio", 0);

if (fd == -1)
{
    throw std::runtime_error(&quot;Could not open: `/dev/vcio`.&quot;);
}

uint32_t property[32] =
{
    0x00000020, // Buffer size in bytes (including the header values, the end tag and padding): 8 * 4 = 32 = 0x20.
    0x00000000, // Request code: 0x00000000 = process request.
    0x00010004, // Tag identifier: Get board serial.
    0x00000008, // Value buffer size in bytes: 2 * 4 = 8 = 0x8.
    0x00000000, // Request code: b31 clear: request, b30-b0: reserved.
    0x00000000, // Value buffer.
    0x00000000, // Value buffer.
    0x00000000  // End tag: 0x0
};

const unsigned long MAJOR_NUM = 100;
if (ioctl(fd, _IOWR(MAJOR_NUM, 0, char*), property) &lt; 0)
{
    close(fd);
    throw std::runtime_error(&quot;`ioctl` failed.&quot;);
}

if (property[1] != 0x80000000)
{
    close(fd);
    throw std::runtime_error(&quot;Request failed.&quot;);
}

close(fd);

std::stringstream stream;
stream &lt;&lt; std::hex &lt;&lt; property[5]; // property[6] ignored.
std::string result = stream.str();

return result;

}

// ----------------------------------------------------------------------------------------------------

std::string ReadMailboxMACAddress() { int fd = open("/dev/vcio", 0);

if (fd == -1)
{
    throw std::runtime_error(&quot;Could not open: `/dev/vcio`.&quot;);
}

uint32_t property[32] =
{
    0x00000020, // Buffer size in bytes (including the header values, the end tag and padding): 8 * 4 = 32 = 0x20.
    0x00000000, // Request code: 0x00000000 = process request.
    0x00010003, // Tag identifier: Get board MAC address.
    0x00000008, // Value buffer size in bytes: 2 * 4 = 8 = 0x8.
    0x00000000, // Request code: b31 clear: request, b30-b0: reserved.
    0x00000000, // Value buffer.
    0x00000000, // Value buffer.
    0x00000000  // End tag: 0x0
};

const unsigned long MAJOR_NUM = 100;
if (ioctl(fd, _IOWR(MAJOR_NUM, 0, char*), property) &lt; 0)
{
    close(fd);
    throw std::runtime_error(&quot;`ioctl` failed.&quot;);
}

if (property[1] != 0x80000000)
{
    close(fd);
    throw std::runtime_error(&quot;Request failed.&quot;);
}

close(fd);

unsigned char* ptr = reinterpret_cast&lt;unsigned char*&gt;(&amp;property[5]);

std::stringstream stream;
stream
    &lt;&lt; std::hex
    &lt;&lt; static_cast&lt;int&gt;(ptr[0]) &lt;&lt; &quot;:&quot;
    &lt;&lt; static_cast&lt;int&gt;(ptr[1]) &lt;&lt; &quot;:&quot;
    &lt;&lt; static_cast&lt;int&gt;(ptr[2]) &lt;&lt; &quot;:&quot;
    &lt;&lt; static_cast&lt;int&gt;(ptr[3]) &lt;&lt; &quot;:&quot;
    &lt;&lt; static_cast&lt;int&gt;(ptr[4]) &lt;&lt; &quot;:&quot;
    &lt;&lt; static_cast&lt;int&gt;(ptr[5]);
std::string result = stream.str();

return result;

}

// ----------------------------------------------------------------------------------------------------

std::string ReadOTPDump() { bcm_host_init();

char buffer[1024] = { 0 };

if (vc_gencmd(buffer, sizeof(buffer), &quot;otp_dump&quot;) != 0)
{
    bcm_host_deinit();
    throw std::runtime_error(&quot;Could not execute `otp_dump` command.&quot;);
}

bcm_host_deinit();

std::string otpDump = buffer;

return otpDump;

}

// ----------------------------------------------------------------------------------------------------

int main() { std::cout << "-------------------- CPU Info: $ cat /proc/cpuinfo --------------------" << std::endl; std::cout << ReadCPUInfo() << std::endl;

std::cout &lt;&lt; &quot;-------------------- Board Model: $ cat /sys/firmware/devicetree/base/model --------------------&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; ReadSysBoardModel() &lt;&lt; std::endl;

std::cout &lt;&lt; &quot;-------------------- Board Serial: $ cat /sys/firmware/devicetree/base/serial-number --------------------&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; ReadSysBoardSerial() &lt;&lt; std::endl;

std::cout &lt;&lt; &quot;-------------------- MAC Address: $ cat /sys/class/net/eth0/address --------------------&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; ReadSysMACAddress() &lt;&lt; std::endl;

std::cout &lt;&lt; &quot;-------------------- Board Model: $ /opt/vc/bin/vcmailbox 0x10001 0x4 0x0 0x0 --------------------&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; ReadMailboxBoardModel() &lt;&lt; std::endl;

std::cout &lt;&lt; &quot;-------------------- Board Revision: $ /opt/vc/bin/vcmailbox 0x10002 0x4 0x0 0x0 --------------------&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; ReadMailboxBoardRevision() &lt;&lt; std::endl;

std::cout &lt;&lt; &quot;-------------------- Board Serial: $ /opt/vc/bin/vcmailbox 0x10004 0x8 0x0 0x0 0x0 --------------------&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; ReadMailboxBoardSerial() &lt;&lt; std::endl;

std::cout &lt;&lt; &quot;-------------------- MAC Address: $ /opt/vc/bin/vcmailbox 0x10003 0x8 0x0 0x0 0x0 --------------------&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; ReadMailboxMACAddress() &lt;&lt; std::endl;

std::cout &lt;&lt; &quot;-------------------- OTP: vcgencmd otp_dump --------------------&quot; &lt;&lt; std::endl;
std::cout &lt;&lt; ReadOTPDump() &lt;&lt; std::endl;

return 0;

}

Build

To build the code, run the following command on Raspberry Pi terminal:

$ g++ \
    rpid.cpp \
    -I/opt/vc/include/ \
    -L/opt/vc/lib/ \
    -lbcm_host \
    -o rpid

$ ./rpid

Sample Output

The following text generated by rpid on my device:

-------------------- CPU Info: $ cat /proc/cpuinfo --------------------
processor   : 0
model name  : ARMv7 Processor rev 4 (v7l)
BogoMIPS    : 38.40
Features    : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part    : 0xd03
CPU revision    : 4

processor : 1 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4

processor : 2 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4

processor : 3 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4

Hardware : BCM2835 Revision : a22082 Serial : 00000000cae4d6b2 Model : Raspberry Pi 3 Model B Rev 1.2

-------------------- Board Model: $ cat /sys/firmware/devicetree/base/model -------------------- Raspberry Pi 3 Model B Rev 1.2 -------------------- Board Serial: $ cat /sys/firmware/devicetree/base/serial-number -------------------- 00000000cae4d6b2 -------------------- MAC Address: $ cat /sys/class/net/eth0/address -------------------- b8:27:eb:e4:d6:b2

-------------------- Board Model: $ /opt/vc/bin/vcmailbox 0x10001 0x4 0x0 0x0 -------------------- 0 -------------------- Board Revision: $ /opt/vc/bin/vcmailbox 0x10002 0x4 0x0 0x0 -------------------- a22082 -------------------- Board Serial: $ /opt/vc/bin/vcmailbox 0x10004 0x8 0x0 0x0 0x0 -------------------- cae4d6b2 -------------------- MAC Address: $ /opt/vc/bin/vcmailbox 0x10003 0x8 0x0 0x0 0x0 -------------------- b8:27:eb:e4:d6:b2 -------------------- OTP: vcgencmd otp_dump -------------------- 08:00000000 09:00000000 10:00000000 11:00000000 12:00000000 13:00000000 14:00000000 15:00000000 16:00280000 17:1020000a 18:1020000a 19:ffffffff 20:ffffffff 21:ffffffff 22:ffffffff 23:ffffffff 24:ffffffff 25:ffffffff 26:ffffffff 27:00002727 28:cae4d6b2 29:351b294d 30:00a22082 31:00000000 32:00000000 33:00000000 34:00000000 35:00000000 36:00000000 37:00000000 38:00000000 39:00000000 40:00000000 41:00000000 42:00000000 43:00000000 44:00000000 45:00000000 46:00000000 47:00000000 48:00000000 49:00000000 50:00000000 51:00000000 52:00000000 53:00000000 54:00000000 55:00000000 56:00000000 57:00000000 58:00000000 59:00000000 60:00000000 61:00000000 62:00000000 63:00000000 64:00000000 65:00000000 66:00000000

Bash

You can run the following command on terminal to extract Hardware ID:

$ cat /proc/cpuinfo
$ cat /sys/firmware/devicetree/base/model
$ cat /sys/firmware/devicetree/base/serial-number
$ cat /sys/class/net/eth0/address
$ /opt/vc/bin/vcmailbox 0x10001 0x4 0x0 0x0
$ /opt/vc/bin/vcmailbox 0x10002 0x4 0x0 0x0
$ /opt/vc/bin/vcmailbox 0x10004 0x8 0x0 0x0 0x0
$ /opt/vc/bin/vcmailbox 0x10003 0x8 0x0 0x0 0x0
$ vcgencmd otp_dump

Resources

Amir
  • 121
  • 2
2

If it wouldn't have an ID (which it has, as it would seem, see Ralph's answer) an alternative could be an I2C serial number chip. Those are really easy to connect (serial interface) and provide a unique serial number.

Some examples:

  • Maxim's I²C/SMBus Silicon Serial Number, e.g. DS2401, DS28CM00
  • Microchip's Unique ID Chip Products, e.g. 24AA02UID, 24AA025UID
  • self-programmed I²C EEPROM
Ghanima
  • 15,958
  • 17
  • 65
  • 125