Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for W5500 ethernet module #1231

Merged
merged 8 commits into from
Sep 13, 2024
Merged
2 changes: 1 addition & 1 deletion .github/workflows/cpplint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ jobs:
pip install cpplint
- name: Linting
run: |
cpplint --repository=. --recursive --filter=-build/c++11,-runtime/references,-readability/braces,-whitespace,-legal,-build/include ./src ./include ./lib/Hoymiles ./lib/MqttSubscribeParser ./lib/TimeoutHelper ./lib/ResetReason
cpplint --repository=. --recursive --filter=-build/c++11,-runtime/references,-readability/braces,-whitespace,-legal,-build/include ./src ./include ./lib/Hoymiles ./lib/MqttSubscribeParser ./lib/TimeoutHelper ./lib/ResetReason ./lib/ETHSPI
40 changes: 39 additions & 1 deletion docs/DeviceProfiles/opendtu_fusion.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,43 @@
"data": 2,
"clk": 1
}
},
{
"name": "OpenDTU Fusion v2 with NRF24 and W5500 ethernet",
"nrf24": {
"miso": 48,
"mosi": 35,
"clk": 36,
"irq": 47,
"en": 38,
"cs": 37
},
"w5500": {
"sclk": 39,
"mosi": 40,
"miso": 41,
"cs": 42,
"rst": 43,
"int": 44
}
},
{
"name": "OpenDTU Fusion v2 with CMT2300A and W5500 ethernet",
"cmt": {
"clk": 6,
"cs": 4,
"fcs": 21,
"sdio": 5,
"gpio2": 3,
"gpio3": 8
},
"w5500": {
"sclk": 39,
"mosi": 40,
"miso": 41,
"cs": 42,
"rst": 43,
"int": 44
}
}
]
]
1 change: 1 addition & 0 deletions include/NetworkSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class NetworkSettingsClass {
bool _ethConnected = false;
std::vector<NetworkEventCbList_t> _cbEventList;
bool _lastMdnsEnabled = false;
bool _spiEth = false;
};

extern NetworkSettingsClass NetworkSettings;
8 changes: 8 additions & 0 deletions include/PinMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ struct PinMapping_t {
int8_t cmt_gpio3;
int8_t cmt_sdio;

int8_t w5500_sclk;
int8_t w5500_mosi;
int8_t w5500_miso;
int8_t w5500_cs;
int8_t w5500_int;
int8_t w5500_rst;

int8_t eth_phy_addr;
bool eth_enabled;
int eth_power;
Expand Down Expand Up @@ -70,6 +77,7 @@ class PinMappingClass {

bool isValidNrf24Config() const;
bool isValidCmt2300Config() const;
bool isValidW5500Config() const;
bool isValidEthConfig() const;
bool isValidHuaweiConfig() const;

Expand Down
127 changes: 127 additions & 0 deletions lib/ETHSPI/src/ETHSPI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// adapted to OpenDTU-OnBattery
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------

#include "ETHSPI.h"

#include <driver/spi_master.h>

// Functions from WiFiGeneric
void tcpipInit();
void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif);

ETHSPIClass::ETHSPIClass() :
eth_handle(nullptr),
eth_netif(nullptr)
{

}

void ETHSPIClass::begin(int8_t pin_sclk, int8_t pin_mosi, int8_t pin_miso, int8_t pin_cs, int8_t pin_int, int8_t pin_rst, spi_host_device_t host_id)
{
gpio_reset_pin(static_cast<gpio_num_t>(pin_rst));
gpio_set_direction(static_cast<gpio_num_t>(pin_rst), GPIO_MODE_OUTPUT);
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 0);

gpio_reset_pin(static_cast<gpio_num_t>(pin_sclk));
gpio_reset_pin(static_cast<gpio_num_t>(pin_mosi));
gpio_reset_pin(static_cast<gpio_num_t>(pin_miso));
gpio_reset_pin(static_cast<gpio_num_t>(pin_cs));
gpio_set_pull_mode(static_cast<gpio_num_t>(pin_miso), GPIO_PULLUP_ONLY);

// Workaround, because calling gpio_install_isr_service directly causes issues with attachInterrupt later
attachInterrupt(digitalPinToInterrupt(pin_int), nullptr, CHANGE);
detachInterrupt(digitalPinToInterrupt(pin_int));
gpio_reset_pin(static_cast<gpio_num_t>(pin_int));
gpio_set_pull_mode(static_cast<gpio_num_t>(pin_int), GPIO_PULLUP_ONLY);

spi_bus_config_t buscfg = {
.mosi_io_num = pin_mosi,
.miso_io_num = pin_miso,
.sclk_io_num = pin_sclk,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.data4_io_num = -1,
.data5_io_num = -1,
.data6_io_num = -1,
.data7_io_num = -1,
.max_transfer_sz = 0, // uses default value internally
.flags = 0,
.intr_flags = 0
};

ESP_ERROR_CHECK(spi_bus_initialize(host_id, &buscfg, SPI_DMA_CH_AUTO));

spi_device_interface_config_t devcfg = {
.command_bits = 16, // actually address phase
.address_bits = 8, // actually command phase
.dummy_bits = 0,
.mode = 0,
.duty_cycle_pos = 0,
.cs_ena_pretrans = 0, // only 0 supported
.cs_ena_posttrans = 0, // only 0 supported
.clock_speed_hz = 20000000, // stable with on OpenDTU Fusion Shield
.input_delay_ns = 0,
.spics_io_num = pin_cs,
.flags = 0,
.queue_size = 20,
.pre_cb = nullptr,
.post_cb = nullptr
};

spi_device_handle_t spi;
ESP_ERROR_CHECK(spi_bus_add_device(host_id, &devcfg, &spi));

// Reset sequence
delayMicroseconds(500);
gpio_set_level(static_cast<gpio_num_t>(pin_rst), 1);
delayMicroseconds(1000);

// Arduino function to start networking stack if not already started
tcpipInit();

ESP_ERROR_CHECK(tcpip_adapter_set_default_eth_handlers()); // ?

eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi);
w5500_config.int_gpio_num = pin_int;

eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
mac_config.rx_task_stack_size = 4096;
esp_eth_mac_t *mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config);

eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.reset_gpio_num = -1;
esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config);

esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
ESP_ERROR_CHECK(esp_eth_driver_install(&eth_config, &eth_handle));

// Configure MAC address
uint8_t mac_addr[6];
ESP_ERROR_CHECK(esp_efuse_mac_get_default(mac_addr));
mac_addr[5] |= 0x03; // derive ethernet MAC address from base MAC address
ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr));

esp_netif_config_t netif_config = ESP_NETIF_DEFAULT_ETH();
eth_netif = esp_netif_new(&netif_config);

ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));

// Add to Arduino
add_esp_interface_netif(ESP_IF_ETH, eth_netif);

ESP_ERROR_CHECK(esp_eth_start(eth_handle));
}

String ETHSPIClass::macAddress()
{
uint8_t mac_addr[6] = {0, 0, 0, 0, 0, 0};
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr);
char mac_addr_str[24];
snprintf(mac_addr_str, sizeof(mac_addr_str), "%02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
return String(mac_addr_str);
}

ETHSPIClass ETHSPI;
26 changes: 26 additions & 0 deletions lib/ETHSPI/src/ETHSPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//-----------------------------------------------------------------------------
// 2024 Ahoy, https://ahoydtu.de
// adapted to OpenDTU-OnBattery
// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed
//-----------------------------------------------------------------------------

#pragma once
schlimmchen marked this conversation as resolved.
Show resolved Hide resolved

#include <Arduino.h>
#include <esp_netif.h>
#include <driver/spi_master.h>

class ETHSPIClass
{
private:
esp_eth_handle_t eth_handle;
esp_netif_t *eth_netif;

public:
ETHSPIClass();

void begin(int8_t pin_sclk, int8_t pin_mosi, int8_t pin_miso, int8_t pin_cs, int8_t pin_int, int8_t pin_rst, spi_host_device_t host_id);
String macAddress();
};

extern ETHSPIClass ETHSPI;
26 changes: 21 additions & 5 deletions src/NetworkSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
#include "MessageOutput.h"
#include "PinMapping.h"
#include "Utils.h"
#include "SPIPortManager.h"
#include "defaults.h"
#include <ESPmDNS.h>
#include <ETHSPI.h>
#include <ETH.h>
#include "__compiled_constants.h"

Expand All @@ -30,6 +32,22 @@ void NetworkSettingsClass::init(Scheduler& scheduler)
WiFi.disconnect(true, true);

WiFi.onEvent(std::bind(&NetworkSettingsClass::NetworkEvent, this, _1));

if (PinMapping.isValidEthConfig()) {
PinMapping_t& pin = PinMapping.get();
ETH.begin(pin.eth_phy_addr, pin.eth_power, pin.eth_mdc, pin.eth_mdio, pin.eth_type, pin.eth_clk_mode);
schlimmchen marked this conversation as resolved.
Show resolved Hide resolved
} else if (PinMapping.isValidW5500Config()) {
auto oSPInum = SPIPortManager.allocatePort("ETHSPI");

if (oSPInum) {
spi_host_device_t host_id = SPIPortManager.SPIhostNum(*oSPInum);
PinMapping_t& pin = PinMapping.get();
ETHSPI.begin(pin.w5500_sclk, pin.w5500_mosi, pin.w5500_miso, pin.w5500_cs, pin.w5500_int, pin.w5500_rst,
host_id);
_spiEth = true;
}
}

setupMode();

scheduler.addTask(_loopTask);
Expand Down Expand Up @@ -167,11 +185,6 @@ void NetworkSettingsClass::setupMode()
WiFi.mode(WIFI_MODE_NULL);
}
}

if (PinMapping.isValidEthConfig()) {
PinMapping_t& pin = PinMapping.get();
ETH.begin(pin.eth_phy_addr, pin.eth_power, pin.eth_mdc, pin.eth_mdio, pin.eth_type, pin.eth_clk_mode);
}
}

void NetworkSettingsClass::enableAdminMode()
Expand Down Expand Up @@ -399,6 +412,9 @@ String NetworkSettingsClass::macAddress() const
{
switch (_networkMode) {
case network_mode::Ethernet:
if (_spiEth) {
return ETHSPI.macAddress();
}
return ETH.macAddress();
break;
case network_mode::WiFi:
Expand Down
48 changes: 48 additions & 0 deletions src/PinMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,30 @@
#define POWERMETER_PIN_DERE -1
#endif

#ifndef W5500_SCLK
#define W5500_SCLK -1
#endif

#ifndef W5500_MOSI
#define W5500_MOSI -1
#endif

#ifndef W5500_MISO
#define W5500_MISO -1
#endif

#ifndef W5500_CS
#define W5500_CS -1
#endif

#ifndef W5500_INT
#define W5500_INT -1
#endif

#ifndef W5500_RST
#define W5500_RST -1
#endif
schlimmchen marked this conversation as resolved.
Show resolved Hide resolved

PinMappingClass PinMapping;

PinMappingClass::PinMappingClass()
Expand All @@ -189,6 +213,13 @@ PinMappingClass::PinMappingClass()
_pinMapping.cmt_gpio3 = CMT_GPIO3;
_pinMapping.cmt_sdio = CMT_SDIO;

_pinMapping.w5500_sclk = W5500_SCLK;
_pinMapping.w5500_mosi = W5500_MOSI;
_pinMapping.w5500_miso = W5500_MISO;
_pinMapping.w5500_cs = W5500_CS;
_pinMapping.w5500_int = W5500_INT;
_pinMapping.w5500_rst = W5500_RST;

#ifdef OPENDTU_ETHERNET
_pinMapping.eth_enabled = true;
#else
Expand Down Expand Up @@ -276,6 +307,13 @@ bool PinMappingClass::init(const String& deviceMapping)
_pinMapping.cmt_gpio3 = doc[i]["cmt"]["gpio3"] | CMT_GPIO3;
_pinMapping.cmt_sdio = doc[i]["cmt"]["sdio"] | CMT_SDIO;

_pinMapping.w5500_sclk = doc[i]["w5500"]["sclk"] | W5500_SCLK;
_pinMapping.w5500_mosi = doc[i]["w5500"]["mosi"] | W5500_MOSI;
_pinMapping.w5500_miso = doc[i]["w5500"]["miso"] | W5500_MISO;
_pinMapping.w5500_cs = doc[i]["w5500"]["cs"] | W5500_CS;
_pinMapping.w5500_int = doc[i]["w5500"]["int"] | W5500_INT;
_pinMapping.w5500_rst = doc[i]["w5500"]["rst"] | W5500_RST;

#ifdef OPENDTU_ETHERNET
_pinMapping.eth_enabled = doc[i]["eth"]["enabled"] | true;
#else
Expand Down Expand Up @@ -347,6 +385,16 @@ bool PinMappingClass::isValidCmt2300Config() const
&& _pinMapping.cmt_sdio >= 0;
}

bool PinMappingClass::isValidW5500Config() const
{
return _pinMapping.w5500_sclk >= 0
&& _pinMapping.w5500_mosi >= 0
&& _pinMapping.w5500_miso >= 0
&& _pinMapping.w5500_cs >= 0
&& _pinMapping.w5500_int >= 0
&& _pinMapping.w5500_rst >= 0;
}

bool PinMappingClass::isValidEthConfig() const
{
return _pinMapping.eth_enabled;
Expand Down
8 changes: 8 additions & 0 deletions src/WebApi_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request)
cmtPinObj["gpio2"] = pin.cmt_gpio2;
cmtPinObj["gpio3"] = pin.cmt_gpio3;

auto w5500PinObj = curPin["w5500"].to<JsonObject>();
w5500PinObj["sclk"] = pin.w5500_sclk;
w5500PinObj["mosi"] = pin.w5500_mosi;
w5500PinObj["miso"] = pin.w5500_miso;
w5500PinObj["cs"] = pin.w5500_cs;
w5500PinObj["int"] = pin.w5500_int;
w5500PinObj["rst"] = pin.w5500_rst;

auto ethPinObj = curPin["eth"].to<JsonObject>();
ethPinObj["enabled"] = pin.eth_enabled;
ethPinObj["phy_addr"] = pin.eth_phy_addr;
Expand Down
Loading