mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2026-01-27 12:50:39 +03:00
421 lines
14 KiB
C++
421 lines
14 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
|
*
|
|
* https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/network/esp_eth.html#basic-ethernet-concepts
|
|
*/
|
|
#include "sdkconfig.h"
|
|
|
|
#if (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500)
|
|
|
|
#include "defines.h"
|
|
#include "Helper.h"
|
|
|
|
#include "connect_eth.h"
|
|
|
|
#include <string>
|
|
#include <esp_event.h>
|
|
#include <netdb.h>
|
|
#include <esp_system.h>
|
|
#include <esp_wnm.h>
|
|
#include <esp_rrm.h>
|
|
#include <esp_mbo.h>
|
|
#include <esp_mac.h>
|
|
#include <esp_log.h>
|
|
#include <esp_eth.h>
|
|
#include <esp_netif.h>
|
|
#include <esp_netif_sntp.h>
|
|
|
|
#include "driver/gpio.h"
|
|
#include <driver/spi_master.h>
|
|
|
|
// define `gpio_pad_select_gpip` for newer versions of IDF
|
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0))
|
|
#include "esp_rom_gpio.h"
|
|
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
|
|
#endif
|
|
|
|
#include "time_sntp.h"
|
|
#include "ClassLogFile.h"
|
|
|
|
#include "statusled.h"
|
|
#include "read_network_config.h"
|
|
|
|
#include "interface_mqtt.h"
|
|
|
|
static const char *TAG = "ETH w5500";
|
|
|
|
static bool gpio_isr_svc_init_by_eth = false; // indicates that we initialized the GPIO ISR service
|
|
|
|
esp_eth_handle_t my_w5500_handle = NULL;
|
|
esp_netif_t *my_w5500_netif = NULL;
|
|
esp_eth_netif_glue_handle_t my_w5500_netif_glue = NULL;
|
|
|
|
static bool eth_initialized = false;
|
|
static bool eth_connected = false;
|
|
static bool eth_connection_uccessful = false;
|
|
static int eth_reconnect_cnt = 0;
|
|
|
|
/**
|
|
* @brief SPI bus initialization (to be used by Ethernet SPI modules)
|
|
*
|
|
* @return
|
|
* - ESP_OK on success
|
|
*/
|
|
static esp_err_t spi_bus_init(void)
|
|
{
|
|
esp_err_t retVal = ESP_OK;
|
|
|
|
// Configure IO Pad as General Purpose IO,
|
|
// so that it can be connected to internal Matrix,
|
|
// then combined with one or more peripheral signals.
|
|
gpio_pad_select_gpio(ETH_SPI_EN);
|
|
|
|
ESP_ERROR_CHECK(gpio_set_direction(ETH_SPI_EN, GPIO_MODE_OUTPUT));
|
|
ESP_ERROR_CHECK(gpio_set_level(ETH_SPI_EN, 1));
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(500));
|
|
|
|
if (ETH_SPI_INT0_GPIO != GPIO_NUM_NC)
|
|
{
|
|
// Install GPIO ISR handler to be able to service SPI Eth modules interrupts
|
|
ESP_LOGI(TAG, "spi_bus_init(): Install GPIO ISR handler...");
|
|
retVal = gpio_install_isr_service(0);
|
|
|
|
if (retVal == ESP_OK)
|
|
{
|
|
gpio_isr_svc_init_by_eth = true;
|
|
ESP_LOGI(TAG, "spi_bus_init(): GPIO ISR handler install successful");
|
|
}
|
|
else if (retVal == ESP_ERR_INVALID_STATE)
|
|
{
|
|
ESP_LOGW(TAG, "spi_bus_init(): GPIO ISR handler has been already installed");
|
|
retVal = ESP_OK; // ISR handler has been already installed so no issues
|
|
}
|
|
else
|
|
{
|
|
ESP_LOGE(TAG, "spi_bus_init(): GPIO ISR handler install failed");
|
|
return retVal;
|
|
}
|
|
}
|
|
|
|
// Init SPI bus
|
|
spi_bus_config_t buscfg = {
|
|
.mosi_io_num = ETH_SPI_MOSI_GPIO,
|
|
.miso_io_num = ETH_SPI_MISO_GPIO,
|
|
.sclk_io_num = ETH_SPI_SCLK_GPIO,
|
|
.quadwp_io_num = GPIO_NUM_NC,
|
|
.quadhd_io_num = GPIO_NUM_NC,
|
|
};
|
|
|
|
retVal = spi_bus_initialize(ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "spi_bus_init(): SPI host #%d init failed", ETH_SPI_HOST);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
|
{
|
|
if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_START)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Started");
|
|
// Typically nothing special here
|
|
}
|
|
else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_STOP)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Stopped");
|
|
eth_connected = false;
|
|
}
|
|
else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_CONNECTED)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Ethernet Link Up");
|
|
|
|
uint8_t mac_addr[6] = {0};
|
|
/* we can get the ethernet driver handle from event data */
|
|
esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data;
|
|
|
|
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr));
|
|
ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
|
eth_connected = true; // Not IP-ready until IP_EVENT_ETH_GOT_IP
|
|
}
|
|
else if (event_base == ETH_EVENT && event_id == ETHERNET_EVENT_DISCONNECTED)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_WARN, TAG, "Ethernet Link Down");
|
|
eth_connected = false;
|
|
// Optionally, try to reconnect or handle fallback LED:
|
|
set_status_led(WLAN_CONN, 1, false);
|
|
eth_reconnect_cnt++;
|
|
}
|
|
else if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP)
|
|
{
|
|
// We have a valid IP
|
|
eth_connection_uccessful = true;
|
|
eth_connected = true;
|
|
eth_reconnect_cnt = 0;
|
|
|
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
|
|
|
network_config.ipaddress = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.ip));
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + network_config.ipaddress);
|
|
|
|
network_config.netmask = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.netmask));
|
|
network_config.gateway = std::string(ip4addr_ntoa((const ip4_addr *)&event->ip_info.gw));
|
|
|
|
esp_netif_dns_info_t dnsInfo;
|
|
ESP_ERROR_CHECK(esp_netif_get_dns_info(event->esp_netif, ESP_NETIF_DNS_MAIN, &dnsInfo));
|
|
network_config.dns = std::string(ip4addr_ntoa((const ip4_addr *)&dnsInfo.ip));
|
|
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Assigned IP: " + network_config.ipaddress + ", Subnet: " + network_config.netmask + ", Gateway: " + network_config.gateway + ", DNS: " + network_config.dns);
|
|
|
|
if (getMQTTisEnabled())
|
|
{
|
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
|
MQTT_Init(); // Init when Ethernet is getting connected
|
|
}
|
|
|
|
// Optionally set a status LED
|
|
set_status_led(WLAN_CONN, 0, true); // e.g., 0 means "ok"
|
|
}
|
|
}
|
|
|
|
esp_err_t eth_init_W5500(void)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet init...");
|
|
|
|
// Set log level for netif component to WARN level (default: INFO; only relevant for serial console)
|
|
// ********************************************
|
|
esp_log_level_set("netif", ESP_LOG_WARN);
|
|
|
|
esp_err_t retVal = esp_netif_init();
|
|
if (retVal != ESP_OK)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_init: Error: " + std::to_string(retVal));
|
|
return retVal;
|
|
}
|
|
|
|
retVal = esp_event_loop_create_default();
|
|
if (retVal != ESP_OK)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_event_loop_create_default: Error: " + std::to_string(retVal));
|
|
return retVal;
|
|
}
|
|
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "SPI init");
|
|
retVal = spi_bus_init();
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to init spi bus, error=0x%x", retVal);
|
|
return retVal;
|
|
}
|
|
|
|
spi_device_interface_config_t devcfg = {
|
|
.mode = 0, // SPI mode 0
|
|
.clock_speed_hz = ETH_SPI_CLOCK_MHZ * 1000 * 1000,
|
|
.spics_io_num = ETH_SPI_CS0_GPIO,
|
|
.queue_size = 30,
|
|
};
|
|
|
|
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(ETH_SPI_HOST, &devcfg);
|
|
// Interrupt GPIO number, set -1 to not use interrupt and to poll rx status periodically
|
|
w5500_config.int_gpio_num = ETH_SPI_INT0_GPIO;
|
|
// Period in ms to poll rx status when interrupt mode is not used
|
|
if (gpio_isr_svc_init_by_eth == false)
|
|
{
|
|
w5500_config.poll_period_ms = ETH_SPI_POLLING0_MS;
|
|
}
|
|
|
|
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
|
esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
|
|
|
// Update PHY config based on board specific configuration
|
|
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
|
|
phy_config.phy_addr = ETH_SPI_PHY_ADDR0; // alter the PHY address
|
|
phy_config.reset_gpio_num = ETH_SPI_PHY_RST0_GPIO; // alter the GPIO used for PHY reset
|
|
esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config);
|
|
|
|
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
|
|
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Driver install...");
|
|
retVal = esp_eth_driver_install(ð_config, &my_w5500_handle);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to install Ethernet driver, error=0x%x", retVal);
|
|
return retVal;
|
|
}
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Driver installed");
|
|
|
|
uint8_t base_mac_addr[6];
|
|
retVal = esp_efuse_mac_get_default(base_mac_addr);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to get efuse base MAC, error=0x%x", retVal);
|
|
return retVal;
|
|
}
|
|
|
|
uint8_t local_mac[6];
|
|
retVal = esp_derive_local_mac(local_mac, base_mac_addr);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to derive local MAC address from universal MAC address, error=0x%x", retVal);
|
|
return retVal;
|
|
}
|
|
|
|
retVal = esp_eth_ioctl(my_w5500_handle, ETH_CMD_S_MAC_ADDR, local_mac);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to set W5500 MAC, error=0x%x", retVal);
|
|
return retVal;
|
|
}
|
|
|
|
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
|
|
my_w5500_netif = esp_netif_new(&netif_cfg);
|
|
my_w5500_netif_glue = esp_eth_new_netif_glue(my_w5500_handle);
|
|
|
|
if (!network_config.ipaddress.empty() && !network_config.gateway.empty() && !network_config.netmask.empty())
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> IP: " + network_config.ipaddress + ", Gateway: " + std::string(network_config.gateway) + ", Netmask: " + std::string(network_config.netmask));
|
|
esp_netif_dhcpc_stop(my_w5500_netif); // Stop DHCP service
|
|
|
|
esp_netif_ip_info_t ip_info;
|
|
int a, b, c, d;
|
|
string_to_ip4(network_config.ipaddress.c_str(), a, b, c, d);
|
|
IP4_ADDR(&ip_info.ip, a, b, c, d); // Set static IP address
|
|
|
|
string_to_ip4(network_config.gateway.c_str(), a, b, c, d);
|
|
IP4_ADDR(&ip_info.gw, a, b, c, d); // Set gateway
|
|
|
|
string_to_ip4(network_config.netmask.c_str(), a, b, c, d);
|
|
IP4_ADDR(&ip_info.netmask, a, b, c, d); // Set netmask
|
|
|
|
esp_netif_set_ip_info(my_w5500_netif, &ip_info); // Set static IP configuration
|
|
|
|
if (network_config.dns.empty())
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "No DNS server, use gateway");
|
|
network_config.dns = network_config.gateway;
|
|
}
|
|
else
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Manual interface config -> DNS: " + network_config.dns);
|
|
}
|
|
|
|
esp_netif_dns_info_t dns_info;
|
|
ip4_addr_t ip;
|
|
ip.addr = esp_ip4addr_aton(network_config.dns.c_str());
|
|
ip_addr_set_ip4_u32(&dns_info.ip, ip.addr);
|
|
|
|
retVal = esp_netif_set_dns_info(my_w5500_netif, ESP_NETIF_DNS_MAIN, &dns_info);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_ERROR, TAG, "esp_netif_set_dns_info: Error: " + std::to_string(retVal));
|
|
return retVal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "Automatic interface config --> Use DHCP service");
|
|
}
|
|
|
|
// Register an event handler to the system event loop (legacy).
|
|
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL));
|
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_event_handler, NULL));
|
|
|
|
retVal = esp_netif_attach(my_w5500_netif, my_w5500_netif_glue);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to attaches esp_netif instance to the io driver handle, error=0x%x", retVal);
|
|
return retVal;
|
|
}
|
|
|
|
retVal = esp_eth_start(my_w5500_handle);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to start Ethernet driver, error=0x%x", retVal);
|
|
return retVal;
|
|
}
|
|
|
|
eth_initialized = true;
|
|
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet init done");
|
|
return ESP_OK;
|
|
}
|
|
|
|
void eth_deinit_W5500(void)
|
|
{
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet deinit...");
|
|
|
|
esp_err_t retVal = ESP_OK;
|
|
eth_initialized = false;
|
|
|
|
ESP_LOGD(TAG, "esp_eth_stop(my_w5500_handle)");
|
|
ESP_ERROR_CHECK(esp_eth_stop(my_w5500_handle));
|
|
|
|
ESP_LOGD(TAG, "esp_eth_del_netif_glue(my_w5500_netif_glue)");
|
|
ESP_ERROR_CHECK(esp_eth_del_netif_glue(my_w5500_netif_glue));
|
|
|
|
ESP_LOGD(TAG, "esp_netif_destroy(my_w5500_netif)");
|
|
esp_netif_destroy(my_w5500_netif);
|
|
|
|
ESP_LOGD(TAG, "esp_netif_deinit()");
|
|
ESP_ERROR_CHECK(esp_netif_deinit());
|
|
|
|
esp_eth_mac_t *mac = NULL;
|
|
esp_eth_phy_t *phy = NULL;
|
|
|
|
ESP_LOGD(TAG, "esp_eth_get_mac_instance(my_w5500_handle, &mac)");
|
|
ESP_ERROR_CHECK(esp_eth_get_mac_instance(my_w5500_handle, &mac));
|
|
|
|
ESP_LOGD(TAG, "esp_eth_get_phy_instance(my_w5500_handle, &phy)");
|
|
ESP_ERROR_CHECK(esp_eth_get_phy_instance(my_w5500_handle, &phy));
|
|
|
|
ESP_LOGD(TAG, "esp_eth_driver_uninstall(my_w5500_handle)");
|
|
retVal = esp_eth_driver_uninstall(my_w5500_handle);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Ethernet driver %p uninstall failed", my_w5500_handle);
|
|
}
|
|
|
|
ESP_LOGD(TAG, "spi_bus_free(ETH_SPI_HOST)");
|
|
retVal = spi_bus_free(ETH_SPI_HOST);
|
|
if (retVal != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "spi_bus_free failed");
|
|
}
|
|
|
|
// We installed the GPIO ISR service so let's uninstall it too.
|
|
// BE CAREFUL HERE though since the service might be used by other functionality!
|
|
if (gpio_isr_svc_init_by_eth == true)
|
|
{
|
|
ESP_LOGW(TAG, "uninstalling GPIO ISR service!");
|
|
gpio_uninstall_isr_service();
|
|
}
|
|
|
|
ESP_LOGD(TAG, "free(my_w5500_handle)");
|
|
free(my_w5500_handle);
|
|
|
|
ESP_LOGD(TAG, "esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, eth_event_handler)");
|
|
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, eth_event_handler));
|
|
|
|
ESP_LOGD(TAG, "esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)");
|
|
ESP_ERROR_CHECK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
|
|
|
|
ESP_LOGD(TAG, "esp_event_loop_delete_default()");
|
|
ESP_ERROR_CHECK(esp_event_loop_delete_default());
|
|
|
|
ESP_LOGD(TAG, "gpio_set_level(ETH_SPI_EN, 0)");
|
|
ESP_ERROR_CHECK(gpio_set_level(ETH_SPI_EN, 0));
|
|
|
|
LogFile.WriteToFile(ESP_LOG_INFO, TAG, "W5500 Ethernet deinit done");
|
|
}
|
|
|
|
bool getETHisConnected(void)
|
|
{
|
|
return eth_connected;
|
|
}
|
|
|
|
#endif // (CONFIG_ETH_ENABLED && CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_SPI_ETHERNET_W5500)
|