чтение свойств устройства или платы с устройства Arduino MKR WiFi 1010

Как программно прочитать специфические свойства устройства с чипа или платы Arduino MKR WiFi 1010?

Например, следующие свойства устройства:

  • любой идентификационный номер, который может однозначно идентифицировать физическое устройство, целое число, GUID, строку или любой другой, как его прочитать из кода?
  • Серийный номер устройства MRK WiFi 1010
  • Номер модели устройства MRK WiFi 1010

Я пытался просмотреть документацию и примеры, но ничего не нашел.

, 👍2

Обсуждение

MAC-адрес уникален. https://www.arduino.cc/reference/en/libraries/wifinina/wifi.macaddress/, @Juraj


2 ответа


Лучший ответ:

1

MAC-адрес может быть вам полезен, но помните, что это может быть подделано. Кроме того, серийные номера и номера моделей не доступны на программном уровне.

Вот простая реализация получения MAC-адреса

    #include <WiFiNINA.h>
    
    void setup() {
      Serial.begin(9600);
      while (!Serial);
      
      if (WiFi.status() == WL_NO_MODULE) {
        Serial.println("Communication with WiFi module failed!");
        while (true);
      }
      
      byte mac[6];
      WiFi.macAddress(mac);
      Serial.print("MAC: ");
      for (int i = 5; i >= 0; i--) {
        if (mac[i] < 16) {
          Serial.print("0");
        }
        Serial.print(mac[i], HEX);
        if (i > 0) {
          Serial.print(":");
        }
      }
      Serial.println();
    }
    
    void loop() {}

Другое решение, которое я могу придумать, — это записать свой собственный уникальный идентификатор, например UUID, в память EEPROM для каждой платы.

Если вы генерируете UUID в контексте сервера или устройства с большим количеством ресурсов, существуют библиотеки и инструменты, которые могут создать их для вас на основе различных факторов для обеспечения уникальности (например, метка времени, MAC-адрес и т. д.). ). Однако с микроконтроллером, подобным тому, что установлен на Arduino MKR WiFi 1010, у вас более ограниченная среда, и процесс может быть более ручным.

Вы можете использовать онлайн-генератор UUID или сгенерировать его с помощью Python:

    import uuid
    print(uuid.uuid4())

После создания UUID вы можете записать их в EEPROM, но не перезаписывайте EEPROM, поскольку границы вызовов очень ограничены.

Запись в EEPROM

#include <EEPROM.h>

const char* MY_UUID = "550e8400-e29b-41d4-a716-446655440000";
const int EEPROM_UUID_ADDRESS = 0;  // начальный адрес в EEPROM

void setup() {
  Serial.begin(9600);

  // Записываем UUID в EEPROM
  for (int i = 0; i < strlen(MY_UUID); i++) {
    EEPROM.write(EEPROM_UUID_ADDRESS + i, MY_UUID[i]);
  }
  Serial.println("UUID written to EEPROM");
}

void loop() {}

"начальный адрес в EEPROM" — это адрес первого поля (или байта), в котором программа начинает хранить наш UUID. Указывая const int EEPROM_UUID_ADDRESS = 0;, мы говорим, что начнем хранить UUID с самого первого байта (поля) EEPROM. Если вы храните в EEPROM другие данные, вам необходимо убедиться, что адреса не перекрываются. Например, если в первых 10 байтах хранятся другие данные, вы можете установить для EEPROM_UUID_ADDRESS значение 10, чтобы начать сохранение UUID с 11-го байта.

Чтение из EEPROM:

#include <EEPROM.h>

const int UUID_LENGTH = 36;  // Длина строки UUID
char readUUID[UUID_LENGTH + 1];  // +1 для нулевого терминатора
const int EEPROM_UUID_ADDRESS = 0;  // начальный адрес в EEPROM

void setup() {
  Serial.begin(9600);

  // Читаем UUID из EEPROM
  for (int i = 0; i < UUID_LENGTH; i++) {
    readUUID[i] = EEPROM.read(EEPROM_UUID_ADDRESS + i);
  }
  readUUID[UUID_LENGTH] = '\0';  // Нуль завершает строку

  Serial.print("UUID read from EEPROM: ");
  Serial.println(readUUID);
}

void loop() {}

+1 за нулевой терминатор:

В языках программирования C и C++ строки представляются как массив символов. Конец строки отмечен специальным символом, известным как «нулевой терминатор». или «нулевой символ», который представлен значением '\0' (байт, все биты которого установлены в 0).

Когда мы читаем или записываем строки в/из памяти (например, EEPROM), нам часто необходимо учитывать этот нулевой терминатор, чтобы гарантировать правильное завершение строки и ее безопасное использование со строковыми функциями.

В коде длина UUID составляет 36 символов. Когда мы объявляем массив для хранения этого UUID с помощью строки:

char readUUID[UUID_LENGTH + 1];

Мы выделяем место для 36 символов UUID плюс дополнительный символ для нулевого терминатора, следовательно, UUID_LENGTH + 1.

Позже, после чтения UUID из EEPROM, мы проверяем, что этот массив является правильно завершенной строкой, добавляя нулевой терминатор:

readUUID[UUID_LENGTH] = '\0';

Это гарантирует, что когда мы используем readUUID в функциях, ожидающих строку, они распознают, где заканчивается строка. Без нулевого терминатора эти функции не знали бы, где заканчивается строка, что приводило бы к непредсказуемому поведению или ошибкам.

Надеюсь, это будет полезно.

,

Спасибо. Есть еще несколько способов получить информацию о плате: это серийный номер USB-устройства, который хранится в чипе FTDI. Однако Arduino не может читать данные с чипа FTDI, и второй способ выглядит следующим образом: если определено (__AVR_ATmega2560__), @Semen Shekhovtsov

да, я знаю, что чип FTDI содержит серийный номер USB-устройства, но я понял, что иногда производители чипов присваивают одни и те же номера VID и PID разным платам, которые они производят. если вы хотите прочитать значения из чипа FTDI, вы, кстати, можете использовать Python. используя Serial и ftdi, вы можете получить первый доступный серийный номер устройства FTDI и отправить его в Arduino через последовательный порт COM3. если хотите, я могу показать, как это сделать. использование «если определено (__AVR_ATmega2560__)» не различит две платы ATmega, не так ли?, @b1n3t

правильно, использование если определено (__AVR_ATmega2560__) не позволит различать две платы ATmega., @Semen Shekhovtsov

Хотели бы вы попробовать очень сложный способ прочитать FTDI в вашей ОС и затем отправить его в ArduinoIDE? выглядит как хороший вызов для работы., @b1n3t

Я думаю, что особой разницы между сохранением UUID и серийного номера чипа FTDI в EEPROM нет. Хранить надо точно так же, @Semen Shekhovtsov

на MKR WiFi 1010 нет FTDI. В MCU есть родной USB. и у него есть доступный серийный номер, @Juraj

Ах, спасибо, что сообщили нам, Юрай, похоже, ты прав. Я предположил, что он использует FTDI, как утверждал Шеховцов. MKR WIFI 1010 использует SAMD21, таблица данных которого имеется: https://ww1.microchip.com/downloads/en/DeviceDoc/SAM_D21_DA1_Family_DataSheet_DS40001882F.pdf. и кажется, что адреса серийного номера: 0x0080A00C, 0x0080A040, 0x0080A044, 0x0080A048 ----- слова от 0 до 3. их можно просто прочитать так: id[0] = *( Летучие uint32_t *)0x0080A00C; и так далее..., @b1n3t


6

IDE показывает серийный номер, поэтому я заглянул в ядро SAMD и нашел функцию, которая считывает серийный номер из регистров. Он находится в файле CDC.cpp

uint8_t Serial_::getShortName(char* name) {
    // из раздела 9.3.3 даташита
    #define SERIAL_NUMBER_WORD_0    *(volatile uint32_t*)(0x0080A00C)
    #define SERIAL_NUMBER_WORD_1    *(volatile uint32_t*)(0x0080A040)
    #define SERIAL_NUMBER_WORD_2    *(volatile uint32_t*)(0x0080A044)
    #define SERIAL_NUMBER_WORD_3    *(volatile uint32_t*)(0x0080A048)

    utox8(SERIAL_NUMBER_WORD_0, &name[0]);
    utox8(SERIAL_NUMBER_WORD_1, &name[8]);
    utox8(SERIAL_NUMBER_WORD_2, &name[16]);
    utox8(SERIAL_NUMBER_WORD_3, &name[24]);
    return 32;
}

из технического описания SAMD21:

Каждое устройство имеет уникальный 128-битный серийный номер, который представляет собой объединение четырех 32-битных слов, содержащихся в следующие адреса:

Уникальность серийного номера гарантируется только при использовании всех 128 бит

,