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?

3
  • 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 4, 2016 at 16:03
  • @goldilocks: You are correct. I should have split this question in two. Feb 4, 2016 at 16:22
  • What about 'ZERO'? no MAC address here... any other unique ID?
    – fcm
    Jun 6, 2016 at 13:08

5 Answers 5

26

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

4
  • 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. Feb 5, 2016 at 3: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 5, 2016 at 7: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 5, 2016 at 9:55
  • @ripat, can you run Mac OS on raspberry pi though? I somehow doubt it. Feb 5, 2016 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.

4
  • 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, 2016 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) Feb 19, 2016 at 2:53
  • I don't know of many people who actually use ipv6 at home or in production really.
    – Havnar
    Feb 19, 2016 at 9: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. Feb 19, 2016 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
5
  • 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 4, 2016 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 4, 2016 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 5, 2016 at 8:13
  • ... forgot to credit the "read more" link in my comment above to its author: Lennart Poettering, the father of systemd.
    – ripat
    Feb 5, 2016 at 8: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 7, 2020 at 11:34
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
1
  • 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. Feb 19, 2016 at 17:57
1

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("Could not open: `/proc/cpuinfo`.");
    }

    std::string cpuinfo((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
                 
    fileStream.close();

    return cpuinfo;
}

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

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

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

    std::string model((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
                 
    fileStream.close();

    return model;
}

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

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

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

    std::string serial((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
                 
    fileStream.close();

    return serial;
}

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

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

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

    std::string address((std::istreambuf_iterator<char>(fileStream)), std::istreambuf_iterator<char>());
                 
    fileStream.close();

    return address;
}

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

std::string ReadMailboxBoardModel()
{
    int fd = open("/dev/vcio", 0);
    
    if (fd == -1)
    {
        throw std::runtime_error("Could not open: `/dev/vcio`.");
    }

    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) < 0)
    {
        close(fd);
        throw std::runtime_error("`ioctl` failed.");
    }

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

    close(fd);

    std::stringstream stream;
    stream << std::hex << 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("Could not open: `/dev/vcio`.");
    }

    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) < 0)
    {
        close(fd);
        throw std::runtime_error("`ioctl` failed.");
    }

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

    close(fd);

    std::stringstream stream;
    stream << std::hex << 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("Could not open: `/dev/vcio`.");
    }

    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) < 0)
    {
        close(fd);
        throw std::runtime_error("`ioctl` failed.");
    }

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

    close(fd);

    std::stringstream stream;
    stream << std::hex << 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("Could not open: `/dev/vcio`.");
    }

    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) < 0)
    {
        close(fd);
        throw std::runtime_error("`ioctl` failed.");
    }

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

    close(fd);
    
    unsigned char* ptr = reinterpret_cast<unsigned char*>(&property[5]);

    std::stringstream stream;
    stream
        << std::hex
        << static_cast<int>(ptr[0]) << ":"
        << static_cast<int>(ptr[1]) << ":"
        << static_cast<int>(ptr[2]) << ":"
        << static_cast<int>(ptr[3]) << ":"
        << static_cast<int>(ptr[4]) << ":"
        << static_cast<int>(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), "otp_dump") != 0)
    {
        bcm_host_deinit();
        throw std::runtime_error("Could not execute `otp_dump` command.");
    }
    
    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 << "-------------------- Board Model: $ cat /sys/firmware/devicetree/base/model --------------------" << std::endl;
    std::cout << ReadSysBoardModel() << std::endl;
    
    std::cout << "-------------------- Board Serial: $ cat /sys/firmware/devicetree/base/serial-number --------------------" << std::endl;
    std::cout << ReadSysBoardSerial() << std::endl;
    
    std::cout << "-------------------- MAC Address: $ cat /sys/class/net/eth0/address --------------------" << std::endl;
    std::cout << ReadSysMACAddress() << std::endl;
    
    std::cout << "-------------------- Board Model: $ /opt/vc/bin/vcmailbox 0x10001 0x4 0x0 0x0 --------------------" << std::endl;
    std::cout << ReadMailboxBoardModel() << std::endl;
    
    std::cout << "-------------------- Board Revision: $ /opt/vc/bin/vcmailbox 0x10002 0x4 0x0 0x0 --------------------" << std::endl;
    std::cout << ReadMailboxBoardRevision() << std::endl;
    
    std::cout << "-------------------- Board Serial: $ /opt/vc/bin/vcmailbox 0x10004 0x8 0x0 0x0 0x0 --------------------" << std::endl;
    std::cout << ReadMailboxBoardSerial() << std::endl;
    
    std::cout << "-------------------- MAC Address: $ /opt/vc/bin/vcmailbox 0x10003 0x8 0x0 0x0 0x0 --------------------" << std::endl;
    std::cout << ReadMailboxMACAddress() << std::endl;
        
    std::cout << "-------------------- OTP: vcgencmd otp_dump --------------------" << std::endl;
    std::cout << ReadOTPDump() << 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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