我有10个基于Raspberry Pi平台的自定义设备,用于分布式任务。在某种程度上,出于某种原因,我想删除其中之一。之后我该如何重新标识板子?
Pi是否有一个芯片可以存储每个板子的唯一ID(如序列号)?如果没有,我是否可以添加一个(只读的)内存(即使不上电也可以存储一个简单的值),以便可以随时通过GPIO与之通信?
还有哪些选择? ?
#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地址通过屏蔽第三个字节(
92
至90
)的最后两位,并在第三个字节之间插入字节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; }
pre >
构建
要构建代码,请在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
评论
请不要问任何问题,然后根据答案确定您确实要问的问题,或有其他问题。这浪费了您寻求帮助的人的时间。因此,我已将您的编辑回退。如果还有其他问题,请问其他问题。@goldilocks:你是正确的。我应该把这个问题一分为二。
那“零”呢?这里没有MAC地址...还有其他唯一ID吗?