feat: add support for W5500 SPI Ethernet in gateway
- Introduced configuration options for wired Ethernet support in Kconfig and sdkconfig. - Implemented Ethernet initialization and event handling in GatewayNetworkService. - Enhanced app_main to manage Ethernet alongside Wi-Fi. - Updated GatewayRuntime to store Ethernet information. - Modified CMakeLists and include files to accommodate new Ethernet dependencies. - Ensured backward compatibility by allowing Ethernet initialization failures to be ignored. Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -7,8 +7,12 @@
|
||||
|
||||
#include "cJSON.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_eth_driver.h"
|
||||
#include "esp_eth_mac_spi.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_mac.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_netif_ip_addr.h"
|
||||
#include "esp_smartconfig.h"
|
||||
@@ -268,6 +272,19 @@ esp_err_t GatewayNetworkService::start() {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (config_.ethernet_enabled) {
|
||||
err = startEthernet();
|
||||
if (err != ESP_OK) {
|
||||
if (config_.ethernet_ignore_init_failure) {
|
||||
ESP_LOGW(kTag, "Ethernet init failed; Ethernet is disabled for this boot: %s",
|
||||
esp_err_to_name(err));
|
||||
config_.ethernet_enabled = false;
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config_.espnow_setup_startup_enabled) {
|
||||
err = startSetupAp();
|
||||
if (err != ESP_OK) {
|
||||
@@ -320,7 +337,8 @@ esp_err_t GatewayNetworkService::start() {
|
||||
}
|
||||
|
||||
started_ = true;
|
||||
ESP_LOGI(kTag, "network service started http=%d udp=%d", config_.http_enabled,
|
||||
ESP_LOGI(kTag, "network service started eth=%d wifi=%d http=%d udp=%d",
|
||||
config_.ethernet_enabled, config_.wifi_enabled, config_.http_enabled,
|
||||
config_.udp_enabled);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -341,6 +359,234 @@ esp_err_t GatewayNetworkService::ensureNetworkStack() {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::startEthernet() {
|
||||
if (ethernet_started_) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_ETH_SPI_ETHERNET_W5500
|
||||
if (eth_netif_ == nullptr) {
|
||||
esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_ETH();
|
||||
eth_netif_ = esp_netif_new(&netif_config);
|
||||
if (eth_netif_ == nullptr) {
|
||||
ESP_LOGE(kTag, "failed to create Ethernet netif");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ethernet_event_handlers_registered_) {
|
||||
esp_err_t err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID,
|
||||
&GatewayNetworkService::HandleEthernetEvent, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to register Ethernet event handler: %s", esp_err_to_name(err));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP,
|
||||
&GatewayNetworkService::HandleEthernetEvent, this);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to register Ethernet IP event handler: %s", esp_err_to_name(err));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_unregister(
|
||||
ETH_EVENT, ESP_EVENT_ANY_ID, &GatewayNetworkService::HandleEthernetEvent));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
ethernet_event_handlers_registered_ = true;
|
||||
}
|
||||
|
||||
if (config_.ethernet_spi_int_gpio >= 0) {
|
||||
esp_err_t err = gpio_install_isr_service(0);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to install GPIO ISR service for Ethernet: %s", esp_err_to_name(err));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
const auto spi_host = static_cast<spi_host_device_t>(config_.ethernet_spi_host);
|
||||
spi_bus_config_t bus_config = {};
|
||||
bus_config.miso_io_num = config_.ethernet_spi_miso_gpio;
|
||||
bus_config.mosi_io_num = config_.ethernet_spi_mosi_gpio;
|
||||
bus_config.sclk_io_num = config_.ethernet_spi_sclk_gpio;
|
||||
bus_config.quadwp_io_num = -1;
|
||||
bus_config.quadhd_io_num = -1;
|
||||
|
||||
esp_err_t err = spi_bus_initialize(spi_host, &bus_config, SPI_DMA_CH_AUTO);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGE(kTag, "failed to initialize Ethernet SPI host %d: %s", config_.ethernet_spi_host,
|
||||
esp_err_to_name(err));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
|
||||
spi_device_interface_config_t spi_device_config = {};
|
||||
spi_device_config.mode = 0;
|
||||
spi_device_config.clock_speed_hz = static_cast<int>(config_.ethernet_spi_clock_mhz) * 1000 * 1000;
|
||||
spi_device_config.spics_io_num = config_.ethernet_spi_cs_gpio;
|
||||
spi_device_config.queue_size = 20;
|
||||
|
||||
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
|
||||
mac_config.rx_task_stack_size = config_.ethernet_rx_task_stack_size;
|
||||
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
|
||||
phy_config.phy_addr = config_.ethernet_phy_addr;
|
||||
phy_config.reset_gpio_num = config_.ethernet_phy_reset_gpio;
|
||||
|
||||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_host, &spi_device_config);
|
||||
w5500_config.int_gpio_num = config_.ethernet_spi_int_gpio;
|
||||
w5500_config.poll_period_ms = config_.ethernet_poll_period_ms;
|
||||
if (w5500_config.int_gpio_num < 0 && w5500_config.poll_period_ms == 0) {
|
||||
w5500_config.poll_period_ms = 100;
|
||||
}
|
||||
|
||||
eth_mac_ = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
|
||||
if (eth_mac_ == nullptr) {
|
||||
ESP_LOGE(kTag, "failed to create W5500 Ethernet MAC");
|
||||
stopEthernet();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
eth_phy_ = esp_eth_phy_new_w5500(&phy_config);
|
||||
if (eth_phy_ == nullptr) {
|
||||
ESP_LOGE(kTag, "failed to create W5500 Ethernet PHY");
|
||||
stopEthernet();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(eth_mac_, eth_phy_);
|
||||
err = esp_eth_driver_install(ð_config, ð_handle_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to install Ethernet driver: %s", esp_err_to_name(err));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
|
||||
uint8_t eth_mac[6] = {};
|
||||
err = esp_read_mac(eth_mac, ESP_MAC_ETH);
|
||||
if (err == ESP_OK) {
|
||||
err = esp_eth_ioctl(eth_handle_, ETH_CMD_S_MAC_ADDR, eth_mac);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to set Ethernet MAC address: %s", esp_err_to_name(err));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
EthernetInfo info;
|
||||
info.mac = MacToHex(eth_mac);
|
||||
runtime_.setEthernetInfo(std::move(info));
|
||||
} else {
|
||||
ESP_LOGW(kTag, "failed to read Ethernet MAC address: %s", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
eth_glue_ = esp_eth_new_netif_glue(eth_handle_);
|
||||
if (eth_glue_ == nullptr) {
|
||||
ESP_LOGE(kTag, "failed to create Ethernet netif glue");
|
||||
stopEthernet();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
err = esp_netif_attach(eth_netif_, eth_glue_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to attach Ethernet netif: %s", esp_err_to_name(err));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_eth_start(eth_handle_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "failed to start Ethernet: %s", esp_err_to_name(err));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
|
||||
ethernet_started_ = true;
|
||||
err = probeEthernetStartup();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(kTag, "Ethernet startup probe failed: %s", esp_err_to_name(err));
|
||||
stopEthernet();
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_LOGI(kTag,
|
||||
"Ethernet W5500 started spi_host=%d sclk=%d mosi=%d miso=%d cs=%d int=%d reset=%d",
|
||||
config_.ethernet_spi_host, config_.ethernet_spi_sclk_gpio,
|
||||
config_.ethernet_spi_mosi_gpio, config_.ethernet_spi_miso_gpio,
|
||||
config_.ethernet_spi_cs_gpio, config_.ethernet_spi_int_gpio,
|
||||
config_.ethernet_phy_reset_gpio);
|
||||
return ESP_OK;
|
||||
#else
|
||||
ESP_LOGW(kTag, "Ethernet requested but W5500 support is not enabled in esp-eth");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::probeEthernetStartup() {
|
||||
if (eth_handle_ == nullptr || !ethernet_started_) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_eth_stop(eth_handle_);
|
||||
ethernet_started_ = false;
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_eth_start(eth_handle_);
|
||||
if (err == ESP_OK) {
|
||||
ethernet_started_ = true;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void GatewayNetworkService::stopEthernet() {
|
||||
if (ethernet_event_handlers_registered_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_unregister(
|
||||
IP_EVENT, IP_EVENT_ETH_GOT_IP, &GatewayNetworkService::HandleEthernetEvent));
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_event_handler_unregister(
|
||||
ETH_EVENT, ESP_EVENT_ANY_ID, &GatewayNetworkService::HandleEthernetEvent));
|
||||
ethernet_event_handlers_registered_ = false;
|
||||
}
|
||||
|
||||
if (eth_handle_ != nullptr) {
|
||||
const esp_err_t stop_err = esp_eth_stop(eth_handle_);
|
||||
if (stop_err != ESP_OK && stop_err != ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(kTag, "failed to stop Ethernet during disable: %s", esp_err_to_name(stop_err));
|
||||
}
|
||||
ethernet_started_ = false;
|
||||
}
|
||||
|
||||
if (eth_glue_ != nullptr) {
|
||||
const esp_err_t glue_err = esp_eth_del_netif_glue(eth_glue_);
|
||||
if (glue_err != ESP_OK) {
|
||||
ESP_LOGW(kTag, "failed to delete Ethernet netif glue: %s", esp_err_to_name(glue_err));
|
||||
} else {
|
||||
eth_glue_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (eth_handle_ != nullptr) {
|
||||
const esp_err_t uninstall_err = esp_eth_driver_uninstall(eth_handle_);
|
||||
if (uninstall_err != ESP_OK) {
|
||||
ESP_LOGW(kTag, "failed to uninstall Ethernet driver: %s", esp_err_to_name(uninstall_err));
|
||||
} else {
|
||||
eth_handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (eth_phy_ != nullptr && eth_handle_ == nullptr) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(eth_phy_->del(eth_phy_));
|
||||
eth_phy_ = nullptr;
|
||||
}
|
||||
if (eth_mac_ != nullptr && eth_handle_ == nullptr) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(eth_mac_->del(eth_mac_));
|
||||
eth_mac_ = nullptr;
|
||||
}
|
||||
|
||||
if (eth_netif_ != nullptr && eth_glue_ == nullptr) {
|
||||
esp_netif_destroy(eth_netif_);
|
||||
eth_netif_ = nullptr;
|
||||
}
|
||||
|
||||
runtime_.clearEthernetInfo();
|
||||
}
|
||||
|
||||
esp_err_t GatewayNetworkService::startWifi() {
|
||||
if (wifi_started_) {
|
||||
return ESP_OK;
|
||||
@@ -778,6 +1024,69 @@ void GatewayNetworkService::HandleEspNowReceive(const esp_now_recv_info_t* info,
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayNetworkService::HandleEthernetEvent(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data) {
|
||||
auto* service = static_cast<GatewayNetworkService*>(arg);
|
||||
if (service != nullptr) {
|
||||
service->handleEthernetEvent(event_base, event_id, event_data);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayNetworkService::handleEthernetEvent(esp_event_base_t event_base, int32_t event_id,
|
||||
void* event_data) {
|
||||
if (event_base == ETH_EVENT) {
|
||||
esp_eth_handle_t handle = eth_handle_;
|
||||
if (event_data != nullptr) {
|
||||
handle = *static_cast<esp_eth_handle_t*>(event_data);
|
||||
}
|
||||
|
||||
if (event_id == ETHERNET_EVENT_CONNECTED) {
|
||||
uint8_t mac[6] = {};
|
||||
if (handle != nullptr && esp_eth_ioctl(handle, ETH_CMD_G_MAC_ADDR, mac) == ESP_OK) {
|
||||
const std::string mac_hex = MacToHex(mac);
|
||||
EthernetInfo info = runtime_.deviceInfo().eth.value_or(EthernetInfo{});
|
||||
info.mac = mac_hex;
|
||||
runtime_.setEthernetInfo(std::move(info));
|
||||
ESP_LOGI(kTag, "Ethernet link up mac=%s", mac_hex.c_str());
|
||||
} else {
|
||||
ESP_LOGI(kTag, "Ethernet link up");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_id == ETHERNET_EVENT_DISCONNECTED) {
|
||||
runtime_.clearEthernetIp();
|
||||
ESP_LOGI(kTag, "Ethernet link down");
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_id == ETHERNET_EVENT_START) {
|
||||
ESP_LOGI(kTag, "Ethernet driver started");
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_id == ETHERNET_EVENT_STOP) {
|
||||
runtime_.clearEthernetIp();
|
||||
ESP_LOGI(kTag, "Ethernet driver stopped");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_base == IP_EVENT && event_id == IP_EVENT_ETH_GOT_IP && event_data != nullptr) {
|
||||
auto* event = static_cast<ip_event_got_ip_t*>(event_data);
|
||||
char ip[16] = {0};
|
||||
esp_ip4addr_ntoa(&event->ip_info.ip, ip, sizeof(ip));
|
||||
EthernetInfo info = runtime_.deviceInfo().eth.value_or(EthernetInfo{});
|
||||
uint8_t mac[6] = {};
|
||||
if (eth_handle_ != nullptr && esp_eth_ioctl(eth_handle_, ETH_CMD_G_MAC_ADDR, mac) == ESP_OK) {
|
||||
info.mac = MacToHex(mac);
|
||||
}
|
||||
info.ip = ip;
|
||||
runtime_.setEthernetInfo(std::move(info));
|
||||
ESP_LOGI(kTag, "Ethernet got IP %s", ip);
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayNetworkService::handleWifiEvent(esp_event_base_t event_base, int32_t event_id,
|
||||
void* event_data) {
|
||||
if (!config_.wifi_enabled) {
|
||||
@@ -1168,6 +1477,15 @@ std::string GatewayNetworkService::deviceInfoJson() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (info.eth.has_value()) {
|
||||
cJSON* eth = cJSON_CreateObject();
|
||||
if (eth != nullptr) {
|
||||
cJSON_AddStringToObject(eth, "mac", info.eth->mac.c_str());
|
||||
cJSON_AddStringToObject(eth, "IP", info.eth->ip.c_str());
|
||||
cJSON_AddItemToObject(root, "ethInfo", eth);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string rendered = PrintJson(root);
|
||||
cJSON_Delete(root);
|
||||
return rendered;
|
||||
|
||||
Reference in New Issue
Block a user