From 8fa6ded0799049392e4a0c6562b35645ad315142 Mon Sep 17 00:00:00 2001 From: Gregor Baues Date: Tue, 10 Nov 2020 16:36:34 +0100 Subject: [PATCH] Automatic MAC Address handling for Ethernet/WiFi --- ArduinoUniqueID.cpp | 121 ++++++++++++++++++++++++++++++++++++++++++ ArduinoUniqueID.h | 104 ++++++++++++++++++++++++++++++++++++ CommandStation-EX.ino | 2 +- EthernetSetup.cpp | 10 ++-- NetworkInterface.cpp | 5 +- NetworkSetup.cpp | 54 ++++++++++++++++++- NetworkSetup.h | 20 ++++++- WifiSetup.cpp | 22 ++++++++ 8 files changed, 331 insertions(+), 7 deletions(-) create mode 100644 ArduinoUniqueID.cpp create mode 100644 ArduinoUniqueID.h diff --git a/ArduinoUniqueID.cpp b/ArduinoUniqueID.cpp new file mode 100644 index 0000000..08bcd69 --- /dev/null +++ b/ArduinoUniqueID.cpp @@ -0,0 +1,121 @@ +/* + * © 2020 Gregor Baues, Luiz Henrique Cassettari. All rights reserved. + * + * This is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * See the GNU General Public License for more details + */ +#include "ArduinoUniqueID.h" + +ArduinoUniqueID::ArduinoUniqueID() +{ +#if defined(ARDUINO_ARCH_AVR) + for (size_t i = 0; i < UniqueIDsize; i++) + { + id[i] = boot_signature_byte_get(0x0E + i + (UniqueIDsize == 9 && i > 5 ? 1 : 0)); + } +#elif defined(ARDUINO_ARCH_ESP8266) + uint32_t chipid = ESP.getChipId(); + id[0] = 0; + id[1] = 0; + id[2] = 0; + id[3] = 0; + id[4] = chipid >> 24; + id[5] = chipid >> 16; + id[6] = chipid >> 8; + id[7] = chipid; + +#elif defined(ARDUINO_ARCH_ESP32) + uint64_t chipid = ESP.getEfuseMac(); + id[0] = 0; + id[1] = 0; + id[2] = chipid; + id[3] = chipid >> 8; + id[4] = chipid >> 16; + id[5] = chipid >> 24; + id[6] = chipid >> 32; + id[7] = chipid >> 40; + +#elif defined(ARDUINO_ARCH_SAM) + unsigned int status ; + /* Send the Start Read unique Identifier command (STUI) by writing the Flash Command Register with the STUI command.*/ + EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_STUI; + do + { + status = EFC1->EEFC_FSR ; + } while ( (status & EEFC_FSR_FRDY) == EEFC_FSR_FRDY ) ; + + /* The Unique Identifier is located in the first 128 bits of the Flash memory mapping. So, at the address 0x400000-0x400003. */ + uint32_t pdwUniqueID[4]; + pdwUniqueID[0] = *(uint32_t *)IFLASH1_ADDR; + pdwUniqueID[1] = *(uint32_t *)(IFLASH1_ADDR + 4); + pdwUniqueID[2] = *(uint32_t *)(IFLASH1_ADDR + 8); + pdwUniqueID[3] = *(uint32_t *)(IFLASH1_ADDR + 12); + for (int i = 0; i < 4; i++) + { + id[i*4+0] = (uint8_t)(pdwUniqueID[i] >> 24); + id[i*4+1] = (uint8_t)(pdwUniqueID[i] >> 16); + id[i*4+2] = (uint8_t)(pdwUniqueID[i] >> 8); + id[i*4+3] = (uint8_t)(pdwUniqueID[i] >> 0); + } + + /* To stop the Unique Identifier mode, the user needs to send the Stop Read unique Identifier + command (SPUI) by writing the Flash Command Register with the SPUI command. */ + EFC1->EEFC_FCR = (0x5A << 24) | EFC_FCMD_SPUI ; + + /* When the Stop read Unique Unique Identifier command (SPUI) has been performed, the + FRDY bit in the Flash Programming Status Register (EEFC_FSR) rises. */ + do + { + status = EFC1->EEFC_FSR ; + } while ( (status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY ); + +#elif defined(ARDUINO_ARCH_SAMD) + + // from section 9.3.3 of the datasheet + #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) + + uint32_t pdwUniqueID[4]; + pdwUniqueID[0] = SERIAL_NUMBER_WORD_0; + pdwUniqueID[1] = SERIAL_NUMBER_WORD_1; + pdwUniqueID[2] = SERIAL_NUMBER_WORD_2; + pdwUniqueID[3] = SERIAL_NUMBER_WORD_3; + + for (int i = 0; i < 4; i++) + { + id[i*4+0] = (uint8_t)(pdwUniqueID[i] >> 24); + id[i*4+1] = (uint8_t)(pdwUniqueID[i] >> 16); + id[i*4+2] = (uint8_t)(pdwUniqueID[i] >> 8); + id[i*4+3] = (uint8_t)(pdwUniqueID[i] >> 0); + } + +#elif defined(ARDUINO_ARCH_STM32) + uint32_t pdwUniqueID[3]; + pdwUniqueID[0] = HAL_GetUIDw0(); + pdwUniqueID[1] = HAL_GetUIDw1(); + pdwUniqueID[2] = HAL_GetUIDw2(); + for (int i = 0; i < 3; i++) + { + id[i*4+0] = (uint8_t)(pdwUniqueID[i] >> 24); + id[i*4+1] = (uint8_t)(pdwUniqueID[i] >> 16); + id[i*4+2] = (uint8_t)(pdwUniqueID[i] >> 8); + id[i*4+3] = (uint8_t)(pdwUniqueID[i] >> 0); + } +#endif +} + +ArduinoUniqueID _UniqueID; \ No newline at end of file diff --git a/ArduinoUniqueID.h b/ArduinoUniqueID.h new file mode 100644 index 0000000..a5f9bbc --- /dev/null +++ b/ArduinoUniqueID.h @@ -0,0 +1,104 @@ +/* + * © 2020 Gregor Baues, Luiz Henrique Cassettari. All rights reserved. + * + * This is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * See the GNU General Public License for more details + */ + +#ifndef _ARDUINO_UNIQUE_ID_H_ +#define _ARDUINO_UNIQUE_ID_H_ + +#include + +#if defined(ARDUINO_ARCH_AVR) +#include +#ifndef SIGRD +#define SIGRD 5 +#endif +#elif defined(ARDUINO_ARCH_ESP8266) +#elif defined(ARDUINO_ARCH_ESP32) +#elif defined(ARDUINO_ARCH_SAM) +#elif defined(ARDUINO_ARCH_SAMD) +#elif defined(ARDUINO_ARCH_STM32) +#else +#error "ArduinoUniqueID only works on AVR, SAM, SAMD, STM32 and ESP Architecture" +#endif + +#if defined(ARDUINO_ARCH_AVR) + +#if defined(__AVR_ATmega328PB__) +#define UniqueIDsize 10 +#else +#define UniqueIDsize 9 +#endif + +#define UniqueIDbuffer UniqueIDsize + +#elif defined(ARDUINO_ARCH_ESP8266) +#define UniqueIDsize 4 +#define UniqueIDbuffer 8 +#elif defined(ARDUINO_ARCH_ESP32) +#define UniqueIDsize 6 +#define UniqueIDbuffer 8 +#elif defined(ARDUINO_ARCH_SAM) +#define UniqueIDsize 16 +#define UniqueIDbuffer 16 +#elif defined(ARDUINO_ARCH_SAMD) +#define UniqueIDsize 16 +#define UniqueIDbuffer 16 +#elif defined(ARDUINO_ARCH_STM32) +#define UniqueIDsize 12 +#define UniqueIDbuffer 12 +#endif + +#define UniqueID8 (_UniqueID.id + UniqueIDbuffer - 8) +#define UniqueID (_UniqueID.id + UniqueIDbuffer - UniqueIDsize) + +#define UniqueIDdump(stream) \ + { \ + stream.print("UniqueID: "); \ + for (size_t i = 0; i < UniqueIDsize; i++) \ + { \ + if (UniqueID[i] < 0x10) \ + stream.print("0"); \ + stream.print(UniqueID[i], HEX); \ + stream.print(" "); \ + } \ + stream.println(); \ + } + +#define UniqueID8dump(stream) \ + { \ + stream.print("UniqueID: "); \ + for (size_t i = 0; i < 8; i++) \ + { \ + if (UniqueID8[i] < 0x10) \ + stream.print("0"); \ + stream.print(UniqueID8[i], HEX); \ + stream.print(" "); \ + } \ + stream.println(); \ + } + +class ArduinoUniqueID +{ + public: + ArduinoUniqueID(); + uint8_t id[UniqueIDbuffer]; +}; + +extern ArduinoUniqueID _UniqueID; + +#endif \ No newline at end of file diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index b717392..1c38758 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -86,7 +86,7 @@ void setup() // nwi1.setup(ETHERNET, TCP); // ETHERNET/TCP on Port 2560 nwi2.setup(ETHERNET, TCP, 23); // ETHERNET/TCP on Port 23 for the CLI // nwi1.setup(ETHERNET, TCP, 8888); // ETHERNET/TCP on Port 8888 - // nwi2.setup(WIFI, TCP); // WIFI/TCP on Port 2560 + nwi2.setup(WIFI, TCP); // WIFI/TCP on Port 2560 // nwi1.setHttpCallback(httpRequestHandler); // HTTP callback DIAG(F("\nNetwork Setup done ...\n")); diff --git a/EthernetSetup.cpp b/EthernetSetup.cpp index 6c081c2..614d5b5 100644 --- a/EthernetSetup.cpp +++ b/EthernetSetup.cpp @@ -20,10 +20,13 @@ #include #include "NetworkDiag.h" +#include "NetworkSetup.h" #include "EthernetSetup.h" byte EthernetSetup::setup() { + INFO(F("Initialize MAC Address ...")); + NetworkSetup::genMacAddress(); INFO(F("Initialize Ethernet with DHCP")); if (Ethernet.begin(mac) == 0) @@ -61,7 +64,7 @@ byte EthernetSetup::setup() maxConnections = 8; } - INFO(F("Network Protocol: [%s]"), protocol ? "UDP" : "TCP"); + INFO(F("Network Protocol: [%s]"), protocol ? "UDP" : "TCP"); switch (protocol) { case UDPR: @@ -102,8 +105,9 @@ byte EthernetSetup::setup() if (connected) { ip = Ethernet.localIP(); - INFO(F("Local IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]); - INFO(F("Listening on port: [%d]"), port); + NetworkSetup::printMacAddress(NetworkSetup::mac); + INFO(F("Local IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]); + INFO(F("Listening on port: [%d]"), port); dnsip = Ethernet.dnsServerIP(); INFO(F("DNS server IP address: [%d.%d.%d.%d] "), dnsip[0], dnsip[1], dnsip[2], dnsip[3]); INFO(F("Number of connections: [%d]"), maxConnections); diff --git a/NetworkInterface.cpp b/NetworkInterface.cpp index d9dc552..21f0674 100644 --- a/NetworkInterface.cpp +++ b/NetworkInterface.cpp @@ -20,6 +20,7 @@ #include #include "NetworkDiag.h" +#include "NetworkSetup.h" #include "NetworkInterface.h" #include "Transport.h" #include "EthernetSetup.h" @@ -76,6 +77,8 @@ void NetworkInterface::setup(transportType transport, protocolType protocol, uin bool ok = false; _nLogLevel = 4; // set the log level to ERROR during setup to get proper information + NetworkSetup::setDeviceId(); + INFO(F("[%s] Transport Setup In Progress ..."), transport ? "Ethernet" : "Wifi"); // configure the Transport and get Ethernet/Wifi server up and running @@ -130,7 +133,7 @@ void NetworkInterface::setup(transportType transport, protocolType protocol, uin } INFO(F("[%s] Transport %s ..."), transport ? "Ethernet" : "Wifi", ok ? "OK" : "Failed"); - _nLogLevel = 0; // set loging back to silent; + // _nLogLevel = 0; // set loging back to silent; } void NetworkInterface::setup(transportType tt, protocolType pt) diff --git a/NetworkSetup.cpp b/NetworkSetup.cpp index 0784efc..6203a4f 100644 --- a/NetworkSetup.cpp +++ b/NetworkSetup.cpp @@ -18,7 +18,59 @@ */ #include +#include "ArduinoUniqueID.h" #include "NetworkSetup.h" +#include "NetworkDiag.h" + +bool NetworkSetup::deviceIdSet = false; +bool NetworkSetup::macAddressSet = false; +char NetworkSetup::_deviceId[MAXDEVICEID] = {0}; +uint8_t NetworkSetup::mac[6] = MAC_ADDRESS; // default MacAddress +uint8_t NetworkSetup::apWifiMacAddress[6] = MAC_ADDRESS; // default MacAddress +uint8_t NetworkSetup::stWifiMacAddress[6] = MAC_ADDRESS; // default MacAddress + +static void array_to_string(byte array[], unsigned int len, char buffer[]) +{ + for (unsigned int i = 0; i < len; i++) + { + byte nib1 = (array[i] >> 4) & 0x0F; + byte nib2 = (array[i] >> 0) & 0x0F; + buffer[i * 2 + 0] = nib1 < 0xA ? '0' + nib1 : 'A' + nib1 - 0xA; + buffer[i * 2 + 1] = nib2 < 0xA ? '0' + nib2 : 'A' + nib2 - 0xA; + } + buffer[len * 2] = '\0'; +} + +void NetworkSetup::setDeviceId() +{ + array_to_string(UniqueID, UniqueIDsize, _deviceId); + DBG(F("Unique device ID: %s\n"), _deviceId); + deviceIdSet = true; +} + +void NetworkSetup::printMacAddress(uint8_t a[]) { + INFO(F("MAC Address: [%x:%x:%x:%x:%x:%x]"),a[0],a[1],a[2],a[3],a[4],a[5]); +} + +/** + * @brief generates Mac Addresses for Ethernet and WiFi + * + */ +void NetworkSetup::genMacAddress() { + + if (!deviceIdSet) NetworkSetup::setDeviceId(); + if (!macAddressSet) { + for (byte i = 0; i < 6; i++) + { + mac[i] = UniqueID[i]; + }; + } + macAddressSet = true; +} NetworkSetup::NetworkSetup() {} -NetworkSetup::~NetworkSetup() {} \ No newline at end of file +NetworkSetup::~NetworkSetup() {} + + + // WiFi.apMacAddress(apWifiMacAddress); + // WiFi.macAddress(stWifiMacAddress); \ No newline at end of file diff --git a/NetworkSetup.h b/NetworkSetup.h index 784c610..d7ab986 100644 --- a/NetworkSetup.h +++ b/NetworkSetup.h @@ -25,18 +25,36 @@ #include "NetworkConfig.h" #include "NetworkInterface.h" +#define MAXDEVICEID 20 + class NetworkSetup { private: + + + static char _deviceId[MAXDEVICEID]; + static bool macAddressSet; + static bool deviceIdSet; + public: IPAddress dnsip; IPAddress ip; - uint8_t mac[6] = MAC_ADDRESS; + static uint8_t mac[6]; // Default if not set automatically for EthernetShield + static uint8_t apWifiMacAddress[6]; // for the WiFi AP + static uint8_t stWifiMacAddress[6]; // for the normal WiFi connection + uint8_t maxConnections; bool connected; // semantics is that the server has successfullt started or not; client connections will be started in the Transport object protocolType protocol; uint16_t port = LISTEN_PORT; // Default port + static void setDeviceId(); + char *getDeviceId() { + return _deviceId; + } + static void genMacAddress(); + static void printMacAddress(uint8_t a[]); + NetworkSetup(); ~NetworkSetup(); }; diff --git a/WifiSetup.cpp b/WifiSetup.cpp index 7fdef60..e7e899f 100644 --- a/WifiSetup.cpp +++ b/WifiSetup.cpp @@ -23,6 +23,8 @@ #include "NetworkSetup.h" #include "WifiSetup.h" +void reverseArray(uint8_t arr[], int start, int end); + bool WifiSetup::setup() { /** * @todo setup using SoftwareSerial or any other Hardware Serial port on the mega (i.e. 2 or 3); @@ -47,6 +49,11 @@ bool WifiSetup::setup() { } INFO(F("Network Protocol: [%s]"), protocol ? "UDP" : "TCP"); + INFO(F("Initialize MAC Addresses ... ")); + WiFi.apMacAddress(apWifiMacAddress); + reverseArray(apWifiMacAddress, 0, 5); // the MAc is provided in reverse order ... + WiFi.macAddress(stWifiMacAddress); + reverseArray(stWifiMacAddress, 0, 5); // Setup the protocol handler switch (protocol) @@ -96,6 +103,9 @@ bool WifiSetup::setup() { if (connected) { ip = WiFi.localIP(); + NetworkSetup::printMacAddress(apWifiMacAddress); + NetworkSetup::printMacAddress(stWifiMacAddress); + INFO(F("Local IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]); INFO(F("Listening on port: [%d]"), port); dnsip = WiFi.dnsServer1(); @@ -107,6 +117,18 @@ bool WifiSetup::setup() { }; +void reverseArray(uint8_t arr[], int start, int end) +{ + while (start < end) + { + uint8_t temp = arr[start]; + arr[start] = arr[end]; + arr[end] = temp; + start++; + end--; + } +} + WifiSetup::WifiSetup() {} WifiSetup::WifiSetup(uint16_t p, protocolType pt ) { port = p; protocol = pt; } WifiSetup::~WifiSetup() {} \ No newline at end of file