Initial commit

ESP32-S3 firmware acting as BMC for another board, controlling an ATX power supply and providing access to UART.

Co-authored with GLM-5
This commit is contained in:
2026-03-11 17:39:43 +01:00
commit 064e8812a4
21 changed files with 4235 additions and 0 deletions

362
main/ethernet_manager.c Normal file
View File

@@ -0,0 +1,362 @@
#include "ethernet_manager.h"
#include "config.h"
#include "esp_log.h"
#include "esp_event.h"
#include "esp_eth_driver.h"
#include "esp_eth_mac.h"
#include "esp_eth_phy.h"
#include "esp_netif.h"
#include "driver/spi_master.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "lwip/ip_addr.h"
#include "esp_eth_phy_w5500.h"
#include "esp_eth_mac_w5500.h"
#include <string.h>
static const char *TAG = TAG_ETH;
// Ethernet handles
static esp_eth_handle_t eth_handle = NULL;
static esp_eth_mac_t *eth_mac = NULL;
static esp_eth_phy_t *eth_phy = NULL;
static esp_netif_t *eth_netif = NULL;
// State tracking
static bool eth_initialized = false;
static bool eth_connected = false;
static bool eth_has_ip = false;
static SemaphoreHandle_t eth_mutex = NULL;
// Event callback
static eth_event_callback_t event_callback = NULL;
// Current configuration
static bmc_eth_config_t current_config = {
.use_dhcp = true,
.static_ip = BMC_STATIC_IP,
.netmask = BMC_NETMASK,
.gateway = BMC_GATEWAY,
};
// ============================================================================
// Event Handler
// ============================================================================
static void eth_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet link up");
eth_connected = true;
if (event_callback) {
event_callback(ETH_EVENT_CONNECTED);
}
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet link down");
eth_connected = false;
eth_has_ip = false;
if (event_callback) {
event_callback(ETH_EVENT_DISCONNECTED);
}
break;
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet stopped");
break;
case IP_EVENT_ETH_GOT_IP: {
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
ESP_LOGI(TAG, "Netmask: " IPSTR, IP2STR(&event->ip_info.netmask));
ESP_LOGI(TAG, "Gateway: " IPSTR, IP2STR(&event->ip_info.gw));
eth_has_ip = true;
if (event_callback) {
event_callback(ETH_EVENT_GOT_IP);
}
} break;
default:
break;
}
}
// ============================================================================
// SPI Configuration for W5500
// ============================================================================
static esp_err_t init_spi_for_ethernet(void) {
spi_bus_config_t buscfg = {
.miso_io_num = ETH_SPI_MISO_GPIO,
.mosi_io_num = ETH_SPI_MOSI_GPIO,
.sclk_io_num = ETH_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096,
};
esp_err_t ret = spi_bus_initialize(ETH_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret));
return ret;
}
ESP_LOGI(TAG, "SPI bus initialized for W5500");
return ESP_OK;
}
// ============================================================================
// Public Functions
// ============================================================================
esp_err_t ethernet_manager_init(void) {
if (eth_initialized) {
ESP_LOGW(TAG, "Ethernet manager already initialized");
return ESP_OK;
}
ESP_LOGI(TAG, "Initializing Ethernet manager (W5500 SPI)");
// Create mutex
eth_mutex = xSemaphoreCreateMutex();
if (!eth_mutex) {
ESP_LOGE(TAG, "Failed to create mutex");
return ESP_ERR_NO_MEM;
}
// Initialize TCP/IP stack
ESP_ERROR_CHECK(esp_netif_init());
// Create default event loop if not already created
esp_err_t loop_ret = esp_event_loop_create_default();
if (loop_ret != ESP_OK && loop_ret != ESP_ERR_INVALID_STATE) {
ESP_LOGE(TAG, "Failed to create event loop: %s", esp_err_to_name(loop_ret));
return loop_ret;
}
// Create default ETH netif
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
eth_netif = esp_netif_new(&netif_cfg);
// Register event handlers
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &eth_event_handler, NULL));
// Initialize SPI bus
esp_err_t ret = init_spi_for_ethernet();
if (ret != ESP_OK) {
return ret;
}
// Configure W5500 MAC address
uint8_t mac_addr[6] = ETH_MAC_ADDR;
// Create SPI device configuration for W5500
spi_device_interface_config_t devcfg = {
.mode = 0,
.clock_speed_hz = 36 * 1000 * 1000, // 36 MHz
.queue_size = 20,
.spics_io_num = ETH_SPI_CS_GPIO,
};
// Create W5500 MAC instance using SPI (new API uses spi_host and device config pointer)
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(ETH_SPI_HOST, &devcfg);
w5500_config.int_gpio_num = ETH_SPI_INT_GPIO;
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.rx_task_stack_size = 4096;
mac_config.rx_task_prio = 15;
eth_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);
if (!eth_mac) {
ESP_LOGE(TAG, "Failed to create W5500 MAC");
return ESP_FAIL;
}
// Create W5500 PHY instance
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = 1; // W5500 uses address 1
phy_config.reset_gpio_num = ETH_SPI_RST_GPIO;
phy_config.reset_timeout_ms = 1000;
eth_phy = esp_eth_phy_new_w5500(&phy_config);
if (!eth_phy) {
ESP_LOGE(TAG, "Failed to create W5500 PHY");
return ESP_FAIL;
}
// Create Ethernet driver
esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(eth_mac, eth_phy);
ret = esp_eth_driver_install(&eth_config, &eth_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to install Ethernet driver: %s", esp_err_to_name(ret));
return ret;
}
// Set MAC address
ret = esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "Failed to set MAC address: %s", esp_err_to_name(ret));
}
// Attach Ethernet driver to TCP/IP stack
ret = esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle));
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to attach Ethernet to netif: %s", esp_err_to_name(ret));
esp_eth_driver_uninstall(eth_handle);
return ret;
}
// Start Ethernet driver
ret = esp_eth_start(eth_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start Ethernet: %s", esp_err_to_name(ret));
return ret;
}
eth_initialized = true;
ESP_LOGI(TAG, "Ethernet manager initialized successfully");
return ESP_OK;
}
bool ethernet_is_connected(void) {
return eth_connected && eth_has_ip;
}
esp_err_t ethernet_get_ip(char *ip_str) {
if (!eth_initialized || !ip_str) {
return ESP_ERR_INVALID_STATE;
}
esp_netif_ip_info_t ip_info;
esp_err_t ret = esp_netif_get_ip_info(eth_netif, &ip_info);
if (ret == ESP_OK) {
snprintf(ip_str, 16, IPSTR, IP2STR(&ip_info.ip));
}
return ret;
}
esp_err_t ethernet_get_network_info(char *ip, char *netmask, char *gateway) {
if (!eth_initialized) {
return ESP_ERR_INVALID_STATE;
}
esp_netif_ip_info_t ip_info;
esp_err_t ret = esp_netif_get_ip_info(eth_netif, &ip_info);
if (ret == ESP_OK) {
if (ip)
snprintf(ip, 16, IPSTR, IP2STR(&ip_info.ip));
if (netmask)
snprintf(netmask, 16, IPSTR, IP2STR(&ip_info.netmask));
if (gateway)
snprintf(gateway, 16, IPSTR, IP2STR(&ip_info.gw));
}
return ret;
}
esp_err_t ethernet_get_mac(char *mac_str) {
if (!eth_initialized || !mac_str) {
return ESP_ERR_INVALID_STATE;
}
uint8_t mac[6];
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac);
if (ret == ESP_OK) {
snprintf(mac_str, 18, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
return ret;
}
esp_err_t ethernet_set_static_ip(const bmc_eth_config_t *config) {
if (!eth_initialized || !config) {
return ESP_ERR_INVALID_STATE;
}
xSemaphoreTake(eth_mutex, portMAX_DELAY);
// Stop DHCP
esp_netif_dhcpc_stop(eth_netif);
// Set static IP
esp_netif_ip_info_t ip_info;
ip_info.ip.addr = ipaddr_addr(config->static_ip);
ip_info.netmask.addr = ipaddr_addr(config->netmask);
ip_info.gw.addr = ipaddr_addr(config->gateway);
esp_err_t ret = esp_netif_set_ip_info(eth_netif, &ip_info);
if (ret == ESP_OK) {
memcpy(&current_config, config, sizeof(bmc_eth_config_t));
current_config.use_dhcp = false;
ESP_LOGI(TAG, "Static IP configured: %s", config->static_ip);
} else {
ESP_LOGE(TAG, "Failed to set static IP: %s", esp_err_to_name(ret));
}
xSemaphoreGive(eth_mutex);
return ret;
}
esp_err_t ethernet_enable_dhcp(void) {
if (!eth_initialized) {
return ESP_ERR_INVALID_STATE;
}
xSemaphoreTake(eth_mutex, portMAX_DELAY);
esp_err_t ret = esp_netif_dhcpc_start(eth_netif);
if (ret == ESP_OK) {
current_config.use_dhcp = true;
ESP_LOGI(TAG, "DHCP enabled");
} else {
ESP_LOGE(TAG, "Failed to enable DHCP: %s", esp_err_to_name(ret));
}
xSemaphoreGive(eth_mutex);
return ret;
}
int ethernet_get_link_speed(void) {
if (!eth_initialized || !eth_connected) {
return 0;
}
eth_speed_t speed;
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &speed);
if (ret == ESP_OK) {
return (speed == ETH_SPEED_100M) ? 100 : 10;
}
return 0;
}
bool ethernet_is_full_duplex(void) {
if (!eth_initialized || !eth_connected) {
return false;
}
eth_duplex_t duplex;
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &duplex);
return (ret == ESP_OK && duplex == ETH_DUPLEX_FULL);
}
esp_err_t ethernet_register_event_callback(eth_event_callback_t callback) {
event_callback = callback;
return ESP_OK;
}