Skip to content

Commit

Permalink
Add Ethernet support for ESP32 (#2361)
Browse files Browse the repository at this point in the history
This PR aims to provide generic Ethernet support for Sming.

Initially this is for the ESP32 using its embedded MAC.

Note that Sming ESP32 requires a patched version of ESP IDF version 4.3. See #2358.
  • Loading branch information
mikee47 authored and slaff committed Sep 27, 2021
1 parent ccea9c3 commit 9af6503
Show file tree
Hide file tree
Showing 22 changed files with 1,103 additions and 9 deletions.
13 changes: 8 additions & 5 deletions Sming/Arch/Esp32/Components/esp32/component.mk
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ SDK_LIBDIRS := \

LIBDIRS += \
$(SDK_COMPONENT_LIBDIR) \
$(SDK_BUILD_BASE)/esp-idf/mbedtls/mbedtls/library \
$(SDK_BUILD_BASE)/esp-idf/$(ESP_VARIANT) \
$(SDK_BUILD_BASE)/esp-idf/$(ESP_VARIANT)/ld \
$(COMPONENT_PATH)/ld \
Expand Down Expand Up @@ -145,6 +146,7 @@ SDK_COMPONENTS := \
esp-tls \
$(ESP_VARIANT) \
esp_common \
esp_eth \
esp_event \
esp_gdbstub \
esp_hw_support \
Expand Down Expand Up @@ -195,7 +197,6 @@ SDK_COMPONENTS += \
bt \
coap \
console \
esp_eth \
esp_http_client \
esp_http_server \
esp_https_ota \
Expand Down Expand Up @@ -331,12 +332,14 @@ export SDK_BUILD_BASE
export SDK_COMPONENT_LIBDIR
export SDK_COMPONENTS

$(SDK_BUILD_COMPLETE): $(SDKCONFIG_H) $(SDK_COMPONENT_LIBS)
$(SDK_BUILD_COMPLETE): $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE)
$(Q) $(SDK_BUILD) bootloader app
$(Q) $(MAKE) --no-print-directory -C $(SDK_DEFAULT_PATH) -f misc.mk copylibs
touch $(SDK_BUILD_COMPLETE)

$(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) $(SDK_COMPONENT_LIBS): $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) $(SDK_COMPONENT_LIBDIR)
$(Q) $(SDK_BUILD) bootloader app
$(Q) $(MAKE) --no-print-directory -C $(SDK_DEFAULT_PATH) -f misc.mk copylibs

$(SDK_COMPONENT_LIBS): $(SDK_BUILD_COMPLETE)

$(SDK_CONFIG_DEFAULTS):
$(Q) cp $@.$(BUILD_TYPE) $@
Expand All @@ -348,7 +351,7 @@ sdk-menuconfig: $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) ##Configure SDK optio
@echo Now run 'make esp32-build'

.PHONY: sdk-defconfig
sdk-defconfig: $(SDK_BUILD_BASE)/include/sdkconfig.h ##Create default SDK config files
sdk-defconfig: $(SDKCONFIG_H) ##Create default SDK config files

.PHONY: sdk-menuconfig-clean
sdk-menuconfig-clean: esp32-clean ##Wipe SDK configuration and revert to defaults
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y
# Mandatory LWIP changes
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192

# Ethernet
#CONFIG_ETH_USE_ESP32_EMAC=y
#CONFIG_ETH_PHY_INTERFACE_RMII=y
#CONFIG_ETH_RMII_CLK_INPUT=y
#CONFIG_ETH_RMII_CLK_IN_GPIO=0
CONFIG_ETH_USE_SPI_ETHERNET=n

# Mandatory Sming framework changes
CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y
# Mandatory LWIP changes
CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192

# Ethernet
#CONFIG_ETH_USE_ESP32_EMAC=y
#CONFIG_ETH_PHY_INTERFACE_RMII=y
#CONFIG_ETH_RMII_CLK_INPUT=y
#CONFIG_ETH_RMII_CLK_IN_GPIO=0
CONFIG_ETH_USE_SPI_ETHERNET=n

# Mandatory Sming framework changes
CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192
Expand Down
2 changes: 1 addition & 1 deletion Sming/Arch/Esp32/Components/esp32/sdk/misc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ include $(SMING_HOME)/build.mk
all:

# List of all generated SDK libraries
SDK_ARCHIVE_DIRS = $(foreach c,$(SDK_COMPONENTS),$(SDK_BUILD_BASE)/esp-idf/$c $(call ListAllSubDirs,$(SDK_BUILD_BASE)/esp-idf/$c))
SDK_ARCHIVE_DIRS = $(foreach c,$(SDK_COMPONENTS),$(SDK_BUILD_BASE)/esp-idf/$c)
SDK_ARCHIVE_LIST = $(sort $(foreach d,$(SDK_ARCHIVE_DIRS),$(wildcard $d/*.a)))

#
Expand Down
239 changes: 239 additions & 0 deletions Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/****
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
* Created 2015 by Skurydin Alexey
* http://github.com/SmingHub/Sming
* All files of the Sming Core are provided under the LGPL v3 license.
*
* EmbeddedEthernet.cpp
*
****/

#include <Platform/EmbeddedEthernet.h>
// #include <freertos/event_groups.h>
#include <esp_eth.h>
#include <esp_event.h>
#include <driver/gpio.h>

using namespace Ethernet;

bool EmbeddedEthernet::begin(const Config& config)
{
#if !CONFIG_ETH_USE_ESP32_EMAC

debug_e("[ETH] Internal MAC not available");
return false;

#else

esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
netif = esp_netif_new(&cfg);

// Set default handlers to process TCP/IP stuffs
ESP_ERROR_CHECK(esp_eth_set_default_handlers(netif));

// And register our own event handlers
enableEventCallback(true);
enableGotIpCallback(true);

eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
if(config.mac.smiMdcPin != PIN_DEFAULT) {
mac_config.smi_mdc_gpio_num = config.mac.smiMdcPin;
}
if(config.mac.smiMdioPin != PIN_DEFAULT) {
mac_config.smi_mdio_gpio_num = config.mac.smiMdioPin;
}
mac = esp_eth_mac_new_esp32(&mac_config);
if(mac == nullptr) {
debug_e("[ETH] Failed to construct MAC");
return false;
}

phy = reinterpret_cast<esp_eth_phy_t*>(phyFactory.create(config.phy));
if(phy == nullptr) {
debug_e("[ETH] Failed to construct PHY");
return false;
}

esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
ESP_ERROR_CHECK(esp_eth_driver_install(&eth_config, &handle));
netif_glue = esp_eth_new_netif_glue(handle);
ESP_ERROR_CHECK(esp_netif_attach(netif, netif_glue));
ESP_ERROR_CHECK(esp_eth_start(handle));

return true;
#endif
}

void EmbeddedEthernet::end()
{
if(handle == nullptr) {
return;
}

ESP_ERROR_CHECK(esp_eth_stop(handle));
ESP_ERROR_CHECK(esp_eth_del_netif_glue(netif_glue));
netif_glue = nullptr;
ESP_ERROR_CHECK(esp_eth_clear_default_handlers(netif));

ESP_ERROR_CHECK(esp_eth_driver_uninstall(handle));
handle = nullptr;

phyFactory.destroy(reinterpret_cast<PhyInstance*>(phy));
phy = nullptr;

ESP_ERROR_CHECK(mac->del(mac));
mac = nullptr;

esp_netif_destroy(netif);
netif = nullptr;

enableEventCallback(false);
enableGotIpCallback(false);
}

void EmbeddedEthernet::enableEventCallback(bool enable)
{
auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
auto ethernet = static_cast<EmbeddedEthernet*>(arg);
ethernet->state = Event(event_id);
if(!ethernet->eventCallback) {
return;
}
ethernet->eventCallback(Event(event_id));
};

if(enable) {
auto err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, handler, this);
ESP_ERROR_CHECK(err);
} else {
esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, handler);
}
}

void EmbeddedEthernet::enableGotIpCallback(bool enable)
{
auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
auto ethernet = static_cast<EmbeddedEthernet*>(arg);
if(!ethernet->gotIpCallback) {
return;
}
ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data;
auto& ip_info = event->ip_info;
ethernet->gotIpCallback(ip_info.ip.addr, ip_info.netmask.addr, ip_info.gw.addr);
};

if(enable) {
auto err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, handler, this);
ESP_ERROR_CHECK(err);
} else {
esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, handler);
}
}

MacAddress EmbeddedEthernet::getMacAddress() const
{
MacAddress addr;
if(mac != nullptr) {
mac->get_addr(mac, &addr[0]);
}
return addr;
}

bool EmbeddedEthernet::setMacAddress(const MacAddress& addr)
{
if(mac == nullptr) {
return false;
}
return mac->set_addr(mac, &const_cast<MacAddress&>(addr)[0]) == ESP_OK;
}

bool EmbeddedEthernet::setSpeed(Speed speed)
{
if(mac == nullptr) {
return false;
}
return mac->set_speed(mac, eth_speed_t(speed)) == ESP_OK;
}

bool EmbeddedEthernet::setFullDuplex(bool enable)
{
if(mac == nullptr) {
return false;
}
return mac->set_duplex(mac, enable ? ETH_DUPLEX_FULL : ETH_DUPLEX_HALF) == ESP_OK;
}

bool EmbeddedEthernet::setLinkState(bool up)
{
if(mac == nullptr) {
return false;
}
return mac->set_link(mac, up ? ETH_LINK_UP : ETH_LINK_DOWN) == ESP_OK;
}

bool EmbeddedEthernet::setPromiscuous(bool enable)
{
if(mac == nullptr) {
return false;
}
return mac->set_promiscuous(mac, enable) == ESP_OK;
}

void EmbeddedEthernet::setHostname(const String& hostname)
{
ESP_ERROR_CHECK(esp_netif_set_hostname(netif, hostname.c_str()));
}

String EmbeddedEthernet::getHostname() const
{
const char* hostName;
ESP_ERROR_CHECK(esp_netif_get_hostname(netif, &hostName));
return hostName;
}

IpAddress EmbeddedEthernet::getIP() const
{
IpAddress addr;
esp_netif_ip_info_t info;
if(esp_netif_get_ip_info(netif, &info) == ESP_OK) {
addr = info.ip.addr;
}
return addr;
}

extern "C" esp_err_t esp_netif_up(esp_netif_t* esp_netif);

bool EmbeddedEthernet::setIP(IpAddress address, IpAddress netmask, IpAddress gateway)
{
if(!enableDHCP(false)) {
return false;
}
esp_netif_ip_info_t ipinfo{address, netmask, gateway};
if(esp_netif_set_ip_info(netif, &ipinfo) == ESP_OK) {
debug_i("Ethernet IP successfully updated");
esp_netif_up(netif);
} else {
debug_e("Ethernet IP can't be updated");
enableDHCP(true);
}
return true;
}

bool EmbeddedEthernet::isEnabledDHCP() const
{
esp_netif_dhcp_status_t status;
if(esp_netif_dhcps_get_status(netif, &status) != ESP_OK) {
return false;
}

return status == ESP_NETIF_DHCP_STARTED;
}

bool EmbeddedEthernet::enableDHCP(bool enable)
{
if(enable) {
return esp_netif_dhcpc_start(netif) == ESP_OK;
} else {
return esp_netif_dhcpc_stop(netif) == ESP_OK;
}
}
64 changes: 64 additions & 0 deletions Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/****
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
* Created 2015 by Skurydin Alexey
* http://github.com/SmingHub/Sming
* All files of the Sming Core are provided under the LGPL v3 license.
*
* EthernetPhy.cpp
*
* Class implementations for all supported ESP32 Ethernet PHY devices
*
****/

#include <Platform/Ethernet.h>
#include <Network/Ethernet/Rtl8201.h>
#include <Network/Ethernet/Ip101.h>
#include <Network/Ethernet/Lan8720.h>
#include <Network/Ethernet/Dp83848.h>
#include <Network/Ethernet/Ksz8041.h>
#include <esp_eth_phy.h>

#define PHY_IMPL(class_name, func_name) \
Ethernet::PhyInstance* class_name::create(const PhyConfig& config) \
{ \
auto phy_config = get_esp_config(config); \
auto phy = esp_eth_phy_new_##func_name(&phy_config); \
return reinterpret_cast<PhyInstance*>(phy); \
} \
\
void class_name::destroy(PhyInstance* inst) \
{ \
esp_phy_destroy(inst); \
}

namespace
{
eth_phy_config_t get_esp_config(const Ethernet::PhyConfig& config)
{
return eth_phy_config_t{
.phy_addr = config.phyAddr,
.reset_timeout_ms = config.resetTimeout,
.autonego_timeout_ms = config.autoNegTimeout,
.reset_gpio_num = config.resetPin,
};
}

void esp_phy_destroy(Ethernet::PhyInstance* inst)
{
auto phy = reinterpret_cast<esp_eth_phy_t*>(inst);
if(phy != nullptr) {
phy->del(phy);
}
}

} // namespace

namespace Ethernet
{
PHY_IMPL(Ip101, ip101)
PHY_IMPL(Rtl8201, rtl8201)
PHY_IMPL(Lan8720, lan8720)
PHY_IMPL(Dp83848, dp83848)
PHY_IMPL(Ksz8041, ksz8041)

} // namespace Ethernet
Loading

0 comments on commit 9af6503

Please sign in to comment.