23

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
  • 58,859
  • 17
  • 112
  • 227
Alexandru Irimiea
  • 487
  • 3
  • 6
  • 11
  • Please don't ask a question, then on the basis of the answer decide you really wanted to ask, or have another, question. This wastes the time of the people you are asking for help. For this reason, I have rolled your edit back. If you have another question, ask another question. – goldilocks Feb 04 '16 at 16:03
  • @goldilocks: You are correct. I should have split this question in two. – Alexandru Irimiea Feb 04 '16 at 16:22
  • What about 'ZERO'? no MAC address here... any other unique ID? – fcm Jun 06 '16 at 13:08

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
  • This is not a complete answer, since it only deals with linux and does not mention windows at all. It would be interesting to know how to so this on windows too. – Andrew Savinykh Feb 05 '16 at 03:06
  • @Savinykh ... linux and Mac OS. Awk, grep and cut are tools available on both OS, but I agree Windows is left out as it lacks these handy command line tools. – ripat Feb 05 '16 at 07:10
  • 11
    You can't run Mac OS on the Pi, so that's not relevant. You can only run the headless Win10 IOT edition on the Pi I believe. Realistically 99% of Pi users are using Linux. – pjc50 Feb 05 '16 at 09:55
  • @ripat, can you run Mac OS on raspberry pi though? I somehow doubt it. – Andrew Savinykh Feb 05 '16 at 13:17
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
  • 7
  • 14
  • this is the better way of answering this question in my opinion. MAC is unique. you can hash it a bit if you want, but just relying on the MAC address can be done on any platform. – Havnar Feb 18 '16 at 14:42
  • @Havnar Better than that, this translates the MAC address directly into an globally routable IPv6 address that can be used to send packets to the Pi directly (any application layer protocol, as long as it works over IPv6 which is most of them, will work here) – Maxthon Chan Feb 19 '16 at 02:53
  • I don't know of many people who actually use ipv6 at home or in production really. – Havnar Feb 19 '16 at 09:42
  • @Havnar I have deployed IPv6 tunnel in my home network to the point that all devices within my home, with the exception of the router itself, can function without IPv4 enabled. – Maxthon Chan Feb 19 '16 at 17:55
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
  • 59,890
  • 31
  • 101
  • 209
  • Though, of course the As and Zeros, without an Ethernet connection, will not have a MAC from that to use! A WiFi adapter will, but moving the adapter between Pis will mean the unique ID will follow the adapter! – SlySven Feb 04 '16 at 23:05
  • @SlySven I don't have a Zero (and can't think of any valid reason yet), but I should modify my script to ignore missing MAC. I assume there will be no /sys/class/net/eth0/address – Milliways Feb 04 '16 at 23:18
  • Another way to identify any Pi's with a unique id is to get its dbus machine-id which is not interface dependent cat /var/lib/dbus/machine-id read more – ripat Feb 05 '16 at 08:13
  • ... forgot to credit the "read more" link in my comment above to its author: Lennart Poettering, the father of systemd. – ripat Feb 05 '16 at 08:23
  • @ripat machine-id is not a hardware dependent ID. If you clone a SD Card (which is common on Pi) all clones will have the same machine-id – Milliways Nov 07 '20 at 11:34
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 Saniyan
  • 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,855
  • 15
  • 61
  • 119
  • Might as well just use a socketed I2C EEPROM chip and program an unique ID into that. AT24C32 are fairly cheap when bought in bulk. – Maxthon Chan Feb 19 '16 at 17:57