是否可以为每个基于Raspberry Pi的设备存储硬件ID?

我有10个基于Raspberry Pi平台的自定义设备,用于分布式任务。在某种程度上,出于某种原因,我想删除其中之一。之后我该如何重新标识板子?

Pi是否有一个芯片可以存储每个板子的唯一ID(如序列号)?如果没有,我是否可以添加一个(只读的)内存(即使不上电也可以存储一个简单的值),以便可以随时通过GPIO与之通信?

还有哪些选择? ?

评论

请不要问任何问题,然后根据答案确定您确实要问的问题,或有其他问题。这浪费了您寻求帮助的人的时间。因此,我已将您的编辑回退。如果还有其他问题,请问其他问题。

@goldilocks:你是正确的。我应该把这个问题一分为二。

那“零”呢?这里没有MAC地址...还有其他唯一ID吗?

#1 楼

是的,每个板都有序列号。检索方法如下:
如何获取序列号?

评论


这不是一个完整的答案,因为它仅适用于linux,根本不提及Windows。知道如何在Windows上做到这一点也会很有趣。

–安德鲁(Andrew Savinykh)
16年2月5日,下午3:06

@Savinykh ... Linux和Mac OS。 Awk,grep和cut是两个OS上都可以使用的工具,但我同意Windows被忽略了,因为它缺少这些方便的命令行工具。

–ripat
16-2-5在7:10



您无法在Pi上运行Mac OS,所以这无关紧要。我相信您只能在Pi上运行无头Win10 IOT版本。实际上,有99%的Pi用户正在使用Linux。

– pjc50
16-2-5在9:55



@ripat,虽然可以在树莓派上运行Mac OS吗?我以某种方式怀疑它。

–安德鲁(Andrew Savinykh)
16-2-5在13:17

#2 楼

这是一个更易于使用的方法:通过IPv6 NDP自动配置的MAC地址。这是一种通用方法,适用于任何网络接口。

每个NIC(包括Pi上使用的USB NIC)都有一个48位MAC地址,例如14:cf:92:20:26:3c

每个48位MAC地址通过屏蔽第三个字节(9290)的最后两位,并在第三个字节之间插入字节feff,对EUI-64地址具有唯一的一对一映射和第四个字节。可以将上述48位MAC映射到EUI-64:14cf:90fe:ff20:263c。IPv6地址自动配置过程使用NDP协议在128位地址中发现64位网络地址。此过程将为同一网络中的所有设备提供相同的64位前缀。前面提到的EUI-64用于填充64位站地址,从而产生128位全局唯一的IPv6地址。因此,如果您具有IPv6网络前缀2001:470:d:472::/64,则上述的网卡在该网络中使用时,将被保证具有IPv6全局可路由地址2001:470:d:472:14cf:90fe:ff20:263c。只要将您的管理工具配置为使用IPv6,只需将此地址插入其中,就可以了。

评论


我认为这是回答这个问题的更好方法。 MAC是唯一的。您可以根据需要对其进行哈希处理,但是在任何平台上都可以依靠MAC地址。

–Havnar
16-2-18在14:42

@Havnar更好的是,它可以将MAC地址直接转换为可全局路由的IPv6地址,该地址可用于直接向Pi发送数据包(任何应用层协议,只要它能在大多数IPv6上工作即可)这里)

–陈Max
16-2-19的2:53

我不知道实际上有很多人在家中或在生产中实际使用ipv6。

–Havnar
16年2月19日在9:42

@Havnar我已经在家庭网络中部署了IPv6隧道,以至于我家庭中的所有设备(路由器本身除外)都可以在不启用IPv4的情况下运行。

–陈Max
16-2-19在17:55

#3 楼

与其他答案一样,Pi具有与MAC相关的唯一ID。

在实践中,使用唯一hostname可以更方便地联网。我使用以下脚本基于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 "q4312078q")"  # 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 }' /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 }' $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


评论


当然,如果没有以太网连接,则“零”和“零”将不会有MAC地址可供​​使用! WiFi适配器可以,但是在Pis之间移动适配器意味着唯一的ID将跟随适配器!

– SlySven
16-2-4在23:05

@SlySven我没有零(并且还没有想到任何有效的原因),但是我应该修改脚本以忽略丢失的MAC。我假设不会有/ sys / class / net / eth0 / address

–地铁
16-2-4在23:18

识别具有唯一ID的任何Pi的另一种方法是获取其dbus机器ID,该ID与接口无关cat / var / lib / dbus / machine-id了解更多信息

–ripat
16-2-5在8:13



...忘记将我上面评论中的“阅读更多”链接归功于其作者:systemd之父Lennart Poettering。

–ripat
16年5月5日在8:23

@ripat机器ID不是与硬件相关的ID。如果克隆SD卡(在Pi上很常见),则所有克隆都将具有相同的机器ID。

–地铁
20年11月7日,11:34

#4 楼

如果它没有ID(看起来像Ralph的答案一样),则可以选择I2C序列号芯片。这些确实很容易连接(串行接口)并提供唯一的序列号。

一些示例:Maxim的I²C/ SMBus硅序列号,例如DS2401,DS28CM00
Microchip独特的ID芯片产品,例如24AA02UID,24AA025UID
自编程I²CEEPROM


评论


也可以只使用带插槽的I2C EEPROM芯片,并在其中编写一个唯一的ID。批量购买时,AT24C32相当便宜。

–陈Max
16-2-19在17:57

#5 楼

Raspberry Pi硬件ID
以下C++代码显示了如何以各种方式获取Raspberry Pi硬件ID。
您可以连接字符串并计算其哈希值(例如MD5)以获取Raspberry Pi Fingerprint
(请参阅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;
}
  
构建
要构建代码,请在Raspberry Pi终端上运行以下命令:
 $ g++ \
    rpid.cpp \
    -I/opt/vc/include/ \
    -L/opt/vc/lib/ \
    -lbcm_host \
    -o rpid

$ ./rpid
 

示例输出
我的设备上rpid生成的以下文本:
-------------------- 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
您可以在终端上运行以下命令以提取硬件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
 

资源

https://github.com/raspberrypi/documentation/tree/JamesH65-mailbox_docs/configuration/mailboxes
https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
https://github.c om / raspberrypi / userland / tree / master / host_applications / linux / apps / vcmailbox
https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md
https: //www.raspberrypi.org/documentation/hardware/raspberrypi/otpbits.md
https://github.com/raspberrypi/userland/tree/master/host_applications/linux/libs/bcm_host
https: //github.com/raspberrypi/userland/tree/master/interface/vmcs_host
https://www.raspberrypi.org/documentation/raspbian/applications/vcgencmd.md
如何获取序列号数字?
https://github.com/AndrewFromMelbourne/raspi_serialnumber