/*
xdrv_82_ethernet.ino - ESP32 (PoE) ethernet support for Tasmota
Copyright (C) 2021 Theo Arends
This program 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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#ifdef ESP32
#ifdef USE_ETHERNET
/*********************************************************************************************\
* Ethernet support for ESP32
*
* Dedicated fixed Phy pins
* GPIO17 - EMAC_CLK_OUT_180
* GPIO19 - EMAC_TXD0(RMII)
* GPIO21 - EMAC_TX_EN(RMII)
* GPIO22 - EMAC_TXD1(RMII)
* GPIO25 - EMAC_RXD0(RMII)
* GPIO26 - EMAC_RXD1(RMII)
* GPIO27 - EMAC_RX_CRS_DV
*
* {"NAME":"Olimex ESP32-PoE","GPIO":[1,1,1,1,1,1,0,0,5536,1,1,1,1,0,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
* GPIO12 = ETH POWER
* GPIO18 = ETH MDIO
* GPIO23 = ETH MDC
* #define ETH_TYPE ETH_PHY_LAN8720
* #define ETH_CLKMODE ETH_CLOCK_GPIO17_OUT
* #define ETH_ADDR 0
*
* {"NAME":"wESP32","GPIO":[0,0,1,0,1,1,0,0,1,1,1,1,5568,5600,1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1}
* GPIO16 = ETH MDC
* GPIO17 = ETH MDIO
* #define ETH_TYPE ETH_PHY_LAN8720
* #define ETH_CLKMODE ETH_CLOCK_GPIO0_IN
* #define ETH_ADDR 0
*
* {"NAME":"WT32-ETH01","GPIO":[1,1,1,1,1,1,0,0,1,0,1,1,3840,576,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1],"FLAG":0,"BASE":1}
* GPIO16 = Force Hi
* GPIO18 = ETH MDIO
* GPIO23 = ETH MDC
* #define ETH_TYPE ETH_PHY_LAN8720
* #define ETH_CLKMODE ETH_CLOCK_GPIO0_IN
* #define ETH_ADDR 1
*
\*********************************************************************************************/
#define XDRV_82 82
/*
// Olimex ESP32-PoE
#define ETH_CLKMODE ETH_CLOCK_GPIO17_OUT
#define ETH_POWER_PIN 12
//********************************************************************************************
#ifndef ETH_ADDR
#define ETH_ADDR 0 // esp_eth.h eth_phy_base_t: 0 = PHY0 .. 31 = PHY31
#endif
#ifndef ETH_TYPE
#define ETH_TYPE ETH_PHY_LAN8720 // ETH.h eth_phy_type_t: 0 = ETH_PHY_LAN8720, 1 = ETH_PHY_TLK110, 2 = ETH_PHY_IP101
#endif
#ifndef ETH_CLKMODE
#define ETH_CLKMODE ETH_CLOCK_GPIO0_IN // esp_eth.h eth_clock_mode_t: 0 = ETH_CLOCK_GPIO0_IN, 1 = ETH_CLOCK_GPIO0_OUT, 2 = ETH_CLOCK_GPIO16_OUT, 3 = ETH_CLOCK_GPIO17_OUT
#endif
*/
#include
char eth_hostname[sizeof(TasmotaGlobal.hostname)];
void EthernetEvent(WiFiEvent_t event) {
switch (event) {
case SYSTEM_EVENT_ETH_START:
AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: " D_ATTEMPTING_CONNECTION));
ETH.setHostname(eth_hostname);
break;
case SYSTEM_EVENT_ETH_CONNECTED:
AddLog_P(LOG_LEVEL_INFO, PSTR("ETH: " D_CONNECTED " at %dMbps%s"),
ETH.linkSpeed(), (ETH.fullDuplex()) ? " Full Duplex" : "");
break;
case SYSTEM_EVENT_ETH_GOT_IP:
AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: Mac %s, IPAddress %s, Hostname %s"),
ETH.macAddress().c_str(), ETH.localIP().toString().c_str(), eth_hostname);
Settings.ip_address[1] = (uint32_t)ETH.gatewayIP();
Settings.ip_address[2] = (uint32_t)ETH.subnetMask();
Settings.ip_address[3] = (uint32_t)ETH.dnsIP();
TasmotaGlobal.global_state.eth_down = 0;
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
AddLog_P(LOG_LEVEL_INFO, PSTR("ETH: Disconnected"));
TasmotaGlobal.global_state.eth_down = 1;
break;
case SYSTEM_EVENT_ETH_STOP:
AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: Stopped"));
TasmotaGlobal.global_state.eth_down = 1;
break;
default:
break;
}
}
void EthernetInit(void) {
if (!Settings.flag4.network_ethernet) { return; }
if (!PinUsed(GPIO_ETH_PHY_MDC) && !PinUsed(GPIO_ETH_PHY_MDIO)) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: No ETH MDC and/or ETH MDIO GPIO defined"));
return;
}
// snprintf_P(Eth.hostname, sizeof(Eth.hostname), PSTR("%s_eth"), TasmotaGlobal.hostname);
strlcpy(eth_hostname, TasmotaGlobal.hostname, sizeof(eth_hostname) -5); // Make sure there is room for "_eth"
strcat(eth_hostname, "_eth");
WiFi.onEvent(EthernetEvent);
int eth_power = (PinUsed(GPIO_ETH_PHY_POWER)) ? Pin(GPIO_ETH_PHY_POWER) : -1;
int eth_mdc = Pin(GPIO_ETH_PHY_MDC);
int eth_mdio = Pin(GPIO_ETH_PHY_MDIO);
if (!ETH.begin(Settings.eth_address, eth_power, eth_mdc, eth_mdio, (eth_phy_type_t)Settings.eth_type, (eth_clock_mode_t)Settings.eth_clk_mode)) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("ETH: Bad PHY type or init error"));
};
}
IPAddress EthernetLocalIP(void) {
return ETH.localIP();
}
char* EthernetHostname(void) {
return eth_hostname;
}
String EthernetMacAddress(void) {
return ETH.macAddress();
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
#define D_CMND_ETHADDRESS "EthAddress"
#define D_CMND_ETHTYPE "EthType"
#define D_CMND_ETHCLOCKMODE "EthClockMode"
const char kEthernetCommands[] PROGMEM = "|" // No prefix
D_CMND_ETHERNET "|" D_CMND_ETHADDRESS "|" D_CMND_ETHTYPE "|" D_CMND_ETHCLOCKMODE;
void (* const EthernetCommand[])(void) PROGMEM = {
&CmndEthernet, &CmndEthAddress, &CmndEthType, &CmndEthClockMode };
void CmndEthernet(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) {
Settings.flag4.network_ethernet = XdrvMailbox.payload;
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndStateText(Settings.flag4.network_ethernet);
}
void CmndEthAddress(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 31)) {
Settings.eth_address = XdrvMailbox.payload;
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndNumber(Settings.eth_address);
}
void CmndEthType(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) {
Settings.eth_type = XdrvMailbox.payload;
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndNumber(Settings.eth_type);
}
void CmndEthClockMode(void)
{
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3)) {
Settings.eth_clk_mode = XdrvMailbox.payload;
TasmotaGlobal.restart_flag = 2;
}
ResponseCmndNumber(Settings.eth_clk_mode);
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdrv82(uint8_t function) {
bool result = false;
switch (function) {
case FUNC_COMMAND:
result = DecodeCommand(kEthernetCommands, EthernetCommand);
break;
case FUNC_INIT:
EthernetInit();
break;
}
return result;
}
#endif // USE_ETHERNET
#endif // ESP32