From a02ad8b52cb467c8c14365321e17b30f73d35b54 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Mon, 3 Jul 2023 23:47:37 +0200 Subject: [PATCH 01/58] Remove unnecessary CMT SPI inversions --- lib/CMT2300a/cmt_spi3.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index 59aad36f7..b01947612 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -1,7 +1,6 @@ #include "cmt_spi3.h" #include #include -#include // for esp_rom_gpio_connect_out_signal SemaphoreHandle_t paramLock = NULL; #define SPI_PARAM_LOCK() \ @@ -63,19 +62,16 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin }; ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); - esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false); delay(100); } -void cmt_spi3_write(const uint8_t addr, const uint8_t dat) +void cmt_spi3_write(const uint8_t addr, const uint8_t data) { - uint8_t tx_data; - tx_data = ~dat; spi_transaction_t t = { - .cmd = 1, - .addr = ~addr, + .cmd = 0, + .addr = addr, .length = 8, - .tx_buffer = &tx_data, + .tx_buffer = &data, .rx_buffer = NULL }; SPI_PARAM_LOCK(); @@ -86,35 +82,31 @@ void cmt_spi3_write(const uint8_t addr, const uint8_t dat) uint8_t cmt_spi3_read(const uint8_t addr) { - uint8_t rx_data; + uint8_t data; spi_transaction_t t = { - .cmd = 0, - .addr = ~addr, - .length = 8, + .cmd = 1, + .addr = addr, .rxlength = 8, .tx_buffer = NULL, - .rx_buffer = &rx_data + .rx_buffer = &data }; SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); SPI_PARAM_UNLOCK(); delayMicroseconds(100); - return rx_data; + return data; } void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) { - uint8_t tx_data; - spi_transaction_t t = { .length = 8, - .tx_buffer = &tx_data, // reference to write data .rx_buffer = NULL }; SPI_PARAM_LOCK(); for (uint8_t i = 0; i < len; i++) { - tx_data = ~buf[i]; // negate buffer contents + t.tx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); delayMicroseconds(4); // > 4 us } @@ -123,20 +115,16 @@ void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) { - uint8_t rx_data; - spi_transaction_t t = { - .length = 8, .rxlength = 8, - .tx_buffer = NULL, - .rx_buffer = &rx_data + .tx_buffer = NULL }; SPI_PARAM_LOCK(); for (uint8_t i = 0; i < len; i++) { + t.rx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); delayMicroseconds(4); // > 4 us - buf[i] = rx_data; } SPI_PARAM_UNLOCK(); } From ec47e8978f5e941f712c8a28657af0495539f706 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:27:02 +0200 Subject: [PATCH 02/58] Fix cs_ena_posttrans calculation --- lib/CMT2300a/cmt_spi3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index b01947612..604706ab0 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -52,7 +52,7 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .dummy_bits = 0, .mode = 0, // SPI mode 0 .cs_ena_pretrans = 2, - .cs_ena_posttrans = (uint8_t)(1 / (spi_speed * 10e6 * 2) + 2), // >2 us + .cs_ena_posttrans = (uint8_t)(2 * spi_speed / 1000000), // >2 us .clock_speed_hz = spi_speed, .spics_io_num = pin_fcs, .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, From 992e174bb2ea74414954ecc32716c04e29285e32 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:22:58 +0200 Subject: [PATCH 03/58] Remove unnecessary delays --- lib/CMT2300a/cmt_spi3.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index 604706ab0..80d310350 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -61,8 +61,6 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .post_cb = NULL, }; ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); - - delay(100); } void cmt_spi3_write(const uint8_t addr, const uint8_t data) @@ -77,7 +75,6 @@ void cmt_spi3_write(const uint8_t addr, const uint8_t data) SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); SPI_PARAM_UNLOCK(); - delayMicroseconds(100); } uint8_t cmt_spi3_read(const uint8_t addr) @@ -93,7 +90,6 @@ uint8_t cmt_spi3_read(const uint8_t addr) SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); SPI_PARAM_UNLOCK(); - delayMicroseconds(100); return data; } @@ -108,7 +104,6 @@ void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) for (uint8_t i = 0; i < len; i++) { t.tx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); - delayMicroseconds(4); // > 4 us } SPI_PARAM_UNLOCK(); } @@ -124,7 +119,6 @@ void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) for (uint8_t i = 0; i < len; i++) { t.rx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); - delayMicroseconds(4); // > 4 us } SPI_PARAM_UNLOCK(); } From 851190dbccc0bbbf5aa7fa6580b72712b9b48079 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Fri, 21 Jul 2023 16:57:00 +0200 Subject: [PATCH 04/58] Implement W5500 support --- include/NetworkSettings.h | 2 + include/PinMapping.h | 10 +++- include/W5500.h | 19 ++++++ platformio.ini | 30 ++++++++++ src/NetworkSettings.cpp | 16 +++-- src/PinMapping.cpp | 48 +++++++++++++++ src/W5500.cpp | 119 ++++++++++++++++++++++++++++++++++++++ src/WebApi_device.cpp | 8 +++ 8 files changed, 246 insertions(+), 6 deletions(-) create mode 100644 include/W5500.h create mode 100644 src/W5500.cpp diff --git a/include/NetworkSettings.h b/include/NetworkSettings.h index 433867e97..51ec10757 100644 --- a/include/NetworkSettings.h +++ b/include/NetworkSettings.h @@ -5,6 +5,7 @@ #include #include #include +#include "W5500.h" enum class network_mode { WiFi, @@ -83,6 +84,7 @@ class NetworkSettingsClass { bool _ethConnected = false; std::vector _cbEventList; bool _lastMdnsEnabled = false; + std::unique_ptr _w5500; }; extern NetworkSettingsClass NetworkSettings; diff --git a/include/PinMapping.h b/include/PinMapping.h index e0db88b6f..de94654c9 100644 --- a/include/PinMapping.h +++ b/include/PinMapping.h @@ -26,6 +26,13 @@ struct PinMapping_t { int8_t cmt_gpio3; int8_t cmt_sdio; + int8_t w5500_mosi; + int8_t w5500_miso; + int8_t w5500_sclk; + int8_t w5500_cs; + int8_t w5500_int; + int8_t w5500_rst; + int8_t eth_phy_addr; bool eth_enabled; int eth_power; @@ -49,10 +56,11 @@ class PinMappingClass { bool isValidNrf24Config() const; bool isValidCmt2300Config() const; + bool isValidW5500Config() const; bool isValidEthConfig() const; private: PinMapping_t _pinMapping; }; -extern PinMappingClass PinMapping; \ No newline at end of file +extern PinMappingClass PinMapping; diff --git a/include/W5500.h b/include/W5500.h new file mode 100644 index 000000000..f4ee9d2a0 --- /dev/null +++ b/include/W5500.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +class W5500 { +public: + explicit W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst); + W5500(const W5500&) = delete; + W5500 &operator=(const W5500&) = delete; + ~W5500(); + + String macAddress(); + +private: + esp_eth_handle_t eth_handle; + esp_netif_t *eth_netif; +}; diff --git a/platformio.ini b/platformio.ini index 99b676341..02919348c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -227,6 +227,7 @@ build_flags = ${env.build_flags} -DLED0=17 -DLED1=18 -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 [env:opendtufusionv2] board = esp32-s3-devkitc-1 @@ -250,3 +251,32 @@ build_flags = ${env.build_flags} -DCMT_SDIO=5 -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 + +[env:opendtufusionv2_shield] +board = esp32-s3-devkitc-1 +upload_protocol = esp-builtin +debug_tool = esp-builtin +debug_speed = 12000 +build_flags = ${env.build_flags} + -DHOYMILES_PIN_MISO=48 + -DHOYMILES_PIN_MOSI=35 + -DHOYMILES_PIN_SCLK=36 + -DHOYMILES_PIN_IRQ=47 + -DHOYMILES_PIN_CE=38 + -DHOYMILES_PIN_CS=37 + -DLED0=17 + -DLED1=18 + -DCMT_CLK=6 + -DCMT_CS=4 + -DCMT_FCS=21 + -DCMT_GPIO2=3 + -DCMT_GPIO3=8 + -DCMT_SDIO=5 + -DW5500_MOSI=40 + -DW5500_MISO=41 + -DW5500_SCLK=39 + -DW5500_CS=42 + -DW5500_INT=44 + -DW5500_RST=43 + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index 31313feb8..cab3c7e4f 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -31,6 +31,15 @@ void NetworkSettingsClass::init(Scheduler& scheduler) WiFi.disconnect(true, true); WiFi.onEvent(std::bind(&NetworkSettingsClass::NetworkEvent, this, _1, _2)); + + if (PinMapping.isValidW5500Config()) { + PinMapping_t& pin = PinMapping.get(); + _w5500 = std::make_unique(pin.w5500_mosi, pin.w5500_miso, pin.w5500_sclk, pin.w5500_cs, pin.w5500_int, pin.w5500_rst); + } else 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); + } + setupMode(); scheduler.addTask(_loopTask); @@ -169,11 +178,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() @@ -401,6 +405,8 @@ String NetworkSettingsClass::macAddress() const { switch (_networkMode) { case network_mode::Ethernet: + if (_w5500) + return _w5500->macAddress(); return ETH.macAddress(); break; case network_mode::WiFi: diff --git a/src/PinMapping.cpp b/src/PinMapping.cpp index 74f282855..7514af5f2 100644 --- a/src/PinMapping.cpp +++ b/src/PinMapping.cpp @@ -84,6 +84,30 @@ #define CMT_SDIO -1 #endif +#ifndef W5500_MOSI +#define W5500_MOSI -1 +#endif + +#ifndef W5500_MISO +#define W5500_MISO -1 +#endif + +#ifndef W5500_SCLK +#define W5500_SCLK -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 + PinMappingClass PinMapping; PinMappingClass::PinMappingClass() @@ -103,6 +127,13 @@ PinMappingClass::PinMappingClass() _pinMapping.cmt_gpio3 = CMT_GPIO3; _pinMapping.cmt_sdio = CMT_SDIO; + _pinMapping.w5500_mosi = W5500_MOSI; + _pinMapping.w5500_miso = W5500_MISO; + _pinMapping.w5500_sclk = W5500_SCLK; + _pinMapping.w5500_cs = W5500_CS; + _pinMapping.w5500_int = W5500_INT; + _pinMapping.w5500_rst = W5500_RST; + #ifdef OPENDTU_ETHERNET _pinMapping.eth_enabled = true; #else @@ -164,6 +195,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_mosi = doc[i]["w5500"]["mosi"] | W5500_MOSI; + _pinMapping.w5500_miso = doc[i]["w5500"]["miso"] | W5500_MISO; + _pinMapping.w5500_sclk = doc[i]["w5500"]["sclk"] | W5500_SCLK; + _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 @@ -211,6 +249,16 @@ bool PinMappingClass::isValidCmt2300Config() const && _pinMapping.cmt_sdio >= 0; } +bool PinMappingClass::isValidW5500Config() const +{ + return _pinMapping.w5500_mosi >= 0 + && _pinMapping.w5500_miso >= 0 + && _pinMapping.w5500_sclk >= 0 + && _pinMapping.w5500_cs >= 0 + && _pinMapping.w5500_int >= 0 + && _pinMapping.w5500_rst >= 0; +} + bool PinMappingClass::isValidEthConfig() const { return _pinMapping.eth_enabled; diff --git a/src/W5500.cpp b/src/W5500.cpp new file mode 100644 index 000000000..3ce60b8d9 --- /dev/null +++ b/src/W5500.cpp @@ -0,0 +1,119 @@ +#include "W5500.h" + +#include + +// Internal Arduino functions from WiFiGeneric +void tcpipInit(); +void add_esp_interface_netif(esp_interface_t interface, esp_netif_t *esp_netif); + +W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) : + eth_handle(nullptr), + eth_netif(nullptr) +{ + gpio_reset_pin(static_cast(pin_rst)); + gpio_set_level(static_cast(pin_rst), 0); + gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); + + gpio_reset_pin(static_cast(pin_mosi)); + gpio_reset_pin(static_cast(pin_miso)); + gpio_reset_pin(static_cast(pin_sclk)); + gpio_reset_pin(static_cast(pin_cs)); + + gpio_reset_pin(static_cast(pin_int)); + + esp_err_t err = gpio_install_isr_service(ARDUINO_ISR_FLAG); + if (err != ESP_ERR_INVALID_STATE) // don't raise an error when ISR service is already installed + ESP_ERROR_CHECK(err); + + spi_bus_config_t bus_config { + .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(SPI3_HOST, &bus_config, SPI_DMA_CH_AUTO)); + + spi_device_interface_config_t device_config { + .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 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(SPI3_HOST, &device_config, &spi)); + + // Reset sequence + delayMicroseconds(500); + gpio_set_level(static_cast(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(ð_config, ð_handle)); + + // Configure MAC address + uint8_t mac_addr[6]; + ESP_ERROR_CHECK(esp_read_mac(mac_addr, ESP_MAC_ETH)); + 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)); +} + +W5500::~W5500() { + // TODO(LennartF22): support cleanup at some point? +} + +String W5500::macAddress() { + uint8_t mac_addr[6] = {}; + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + + char mac_addr_str[18]; + 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); +} diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index 42c175f78..b4878817b 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -50,6 +50,14 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) cmtPinObj["gpio2"] = pin.cmt_gpio2; cmtPinObj["gpio3"] = pin.cmt_gpio3; + auto w5500PinObj = curPin["w5500"].to(); + 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(); ethPinObj["enabled"] = pin.eth_enabled; ethPinObj["phy_addr"] = pin.eth_phy_addr; From 9b9c1e29f15e44b85bc29ff79468bc171e47d522 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 17:57:01 +0200 Subject: [PATCH 05/58] Add SpiManager library --- lib/SpiManager/library.json | 13 ++++++ lib/SpiManager/src/SpiBus.cpp | 49 +++++++++++++++++++++ lib/SpiManager/src/SpiBus.h | 45 +++++++++++++++++++ lib/SpiManager/src/SpiBusConfig.cpp | 67 +++++++++++++++++++++++++++++ lib/SpiManager/src/SpiBusConfig.h | 20 +++++++++ lib/SpiManager/src/SpiCallback.cpp | 65 ++++++++++++++++++++++++++++ lib/SpiManager/src/SpiCallback.h | 15 +++++++ lib/SpiManager/src/SpiManager.cpp | 64 +++++++++++++++++++++++++++ lib/SpiManager/src/SpiManager.h | 33 ++++++++++++++ 9 files changed, 371 insertions(+) create mode 100644 lib/SpiManager/library.json create mode 100644 lib/SpiManager/src/SpiBus.cpp create mode 100644 lib/SpiManager/src/SpiBus.h create mode 100644 lib/SpiManager/src/SpiBusConfig.cpp create mode 100644 lib/SpiManager/src/SpiBusConfig.h create mode 100644 lib/SpiManager/src/SpiCallback.cpp create mode 100644 lib/SpiManager/src/SpiCallback.h create mode 100644 lib/SpiManager/src/SpiManager.cpp create mode 100644 lib/SpiManager/src/SpiManager.h diff --git a/lib/SpiManager/library.json b/lib/SpiManager/library.json new file mode 100644 index 000000000..22e5ddc99 --- /dev/null +++ b/lib/SpiManager/library.json @@ -0,0 +1,13 @@ +{ + "name": "SpiManager", + "keywords": "spi", + "description": "Library for managing the allocation of dedicated or shared SPI buses on the ESP32.", + "authors": { + "name": "Lennart Ferlemann" + }, + "version": "0.0.1", + "frameworks": "arduino", + "platforms": [ + "espressif32" + ] +} diff --git a/lib/SpiManager/src/SpiBus.cpp b/lib/SpiManager/src/SpiBus.cpp new file mode 100644 index 000000000..26b361cc4 --- /dev/null +++ b/lib/SpiManager/src/SpiBus.cpp @@ -0,0 +1,49 @@ +#include "SpiBus.h" + +#include "SpiBusConfig.h" +#include "SpiCallback.h" + +SpiBus::SpiBus(const std::string &_id, spi_host_device_t _host_device) : + id(_id), + host_device(_host_device), + cur_config(nullptr) +{ + spi_bus_config_t bus_config { + .mosi_io_num = -1, + .miso_io_num = -1, + .sclk_io_num = -1, + .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 = SPI_MAX_DMA_LEN, + .flags = 0, + .intr_flags = 0 + }; + ESP_ERROR_CHECK(spi_bus_initialize(host_device, &bus_config, SPI_DMA_CH_AUTO)); +} + +SpiBus::~SpiBus() { + ESP_ERROR_CHECK(spi_bus_free(host_device)); +} + +spi_device_handle_t SpiBus::add_device(const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { + if (!SpiCallback::patch(shared_from_this(), bus_config, device_config)) + return nullptr; + + spi_device_handle_t device; + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &device_config, &device)); + return device; +} + +// TODO: add remove_device (with spi_device_acquire_bus) + +void SpiBus::apply_config(SpiBusConfig *config) { + if (cur_config) + cur_config->unpatch(host_device); + cur_config = config; + if (cur_config) + cur_config->patch(host_device); +} diff --git a/lib/SpiManager/src/SpiBus.h b/lib/SpiManager/src/SpiBus.h new file mode 100644 index 000000000..a5fde06c1 --- /dev/null +++ b/lib/SpiManager/src/SpiBus.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include +#include + +class SpiBusConfig; + +class SpiBus : public std::enable_shared_from_this { +public: + explicit SpiBus(const std::string &id, spi_host_device_t host_device); + SpiBus(const SpiBus&) = delete; + SpiBus &operator=(const SpiBus&) = delete; + ~SpiBus(); + + inline __attribute__((always_inline)) void require_config(SpiBusConfig *config) { + if (config == cur_config) + return; + apply_config(config); + } + + inline __attribute__((always_inline)) void free_config(SpiBusConfig *config) { + if (config != cur_config) + return; + apply_config(nullptr); + } + + inline const std::string &get_id() const { + return id; + } + + inline spi_host_device_t get_host_device() const { + return host_device; + } + + spi_device_handle_t add_device(const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); + +private: + void apply_config(SpiBusConfig *config); + + std::string id; + spi_host_device_t host_device; + SpiBusConfig *cur_config; +}; diff --git a/lib/SpiManager/src/SpiBusConfig.cpp b/lib/SpiManager/src/SpiBusConfig.cpp new file mode 100644 index 000000000..c3cc01961 --- /dev/null +++ b/lib/SpiManager/src/SpiBusConfig.cpp @@ -0,0 +1,67 @@ +#include "SpiBusConfig.h" + +#include +#include +#include + +SpiBusConfig::SpiBusConfig(gpio_num_t _pin_mosi, gpio_num_t _pin_miso, gpio_num_t _pin_sclk) : + pin_mosi(_pin_mosi), + pin_miso(_pin_miso), + pin_sclk(_pin_sclk) +{ + if (pin_mosi != GPIO_NUM_NC) { + ESP_ERROR_CHECK(gpio_reset_pin(pin_mosi)); + ESP_ERROR_CHECK(gpio_set_direction(pin_mosi, GPIO_MODE_INPUT_OUTPUT)); + } + + if (pin_miso != GPIO_NUM_NC) { + ESP_ERROR_CHECK(gpio_reset_pin(pin_miso)); + ESP_ERROR_CHECK(gpio_set_direction(pin_miso, GPIO_MODE_INPUT)); + } + + if (pin_sclk != GPIO_NUM_NC) { + ESP_ERROR_CHECK(gpio_reset_pin(pin_sclk)); + ESP_ERROR_CHECK(gpio_set_direction(pin_sclk, GPIO_MODE_INPUT_OUTPUT)); + } +} + +SpiBusConfig::~SpiBusConfig() { + if (pin_mosi != GPIO_NUM_NC) + ESP_ERROR_CHECK(gpio_reset_pin(pin_mosi)); + + if (pin_miso != GPIO_NUM_NC) + ESP_ERROR_CHECK(gpio_reset_pin(pin_miso)); + + if (pin_sclk != GPIO_NUM_NC) + ESP_ERROR_CHECK(gpio_reset_pin(pin_sclk)); +} + +void SpiBusConfig::patch(spi_host_device_t host_device) { + if (pin_mosi != GPIO_NUM_NC) { + esp_rom_gpio_connect_out_signal(pin_mosi, spi_periph_signal[host_device].spid_out, false, false); + esp_rom_gpio_connect_in_signal(pin_mosi, spi_periph_signal[host_device].spid_in, false); + } + + if (pin_miso != GPIO_NUM_NC) + esp_rom_gpio_connect_in_signal(pin_miso, spi_periph_signal[host_device].spiq_in, false); + + if (pin_sclk != GPIO_NUM_NC) { + esp_rom_gpio_connect_out_signal(pin_sclk, spi_periph_signal[host_device].spiclk_out, false, false); + esp_rom_gpio_connect_in_signal(pin_sclk, spi_periph_signal[host_device].spiclk_in, false); + } +} + +void SpiBusConfig::unpatch(spi_host_device_t host_device) { + if (pin_mosi != GPIO_NUM_NC) { + esp_rom_gpio_connect_out_signal(pin_mosi, SIG_GPIO_OUT_IDX, false, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spid_in, false); + } + + if (pin_miso != GPIO_NUM_NC) + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spiq_in, false); + + if (pin_sclk != GPIO_NUM_NC) { + esp_rom_gpio_connect_out_signal(pin_sclk, SIG_GPIO_OUT_IDX, false, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spiclk_in, false); + } +} diff --git a/lib/SpiManager/src/SpiBusConfig.h b/lib/SpiManager/src/SpiBusConfig.h new file mode 100644 index 000000000..e4549ef18 --- /dev/null +++ b/lib/SpiManager/src/SpiBusConfig.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class SpiBusConfig { +public: + explicit SpiBusConfig(gpio_num_t pin_mosi, gpio_num_t pin_miso, gpio_num_t pin_sclk); + SpiBusConfig(const SpiBusConfig&) = delete; + SpiBusConfig &operator=(const SpiBusConfig&) = delete; + ~SpiBusConfig(); + + void patch(spi_host_device_t host_device); + void unpatch(spi_host_device_t host_device); + +private: + gpio_num_t pin_mosi; + gpio_num_t pin_miso; + gpio_num_t pin_sclk; +}; diff --git a/lib/SpiManager/src/SpiCallback.cpp b/lib/SpiManager/src/SpiCallback.cpp new file mode 100644 index 000000000..e281db98c --- /dev/null +++ b/lib/SpiManager/src/SpiCallback.cpp @@ -0,0 +1,65 @@ +#include "SpiCallback.h" + +#include "SpiBus.h" + +#include +#include + +namespace SpiCallback { + namespace { + struct CallbackData { + std::shared_ptr bus; + std::shared_ptr config; + transaction_cb_t inner_pre_cb; + transaction_cb_t inner_post_cb; + }; + + std::array, SPI_MANAGER_CALLBACK_COUNT> instances; + + template + void IRAM_ATTR fn_pre_cb(spi_transaction_t *trans) { + instances[N]->bus->require_config(instances[N]->config.get()); + if (instances[N]->inner_pre_cb) + instances[N]->inner_pre_cb(trans); + } + + template + void IRAM_ATTR fn_post_cb(spi_transaction_t *trans) { + if (instances[N]->inner_post_cb) + instances[N]->inner_post_cb(trans); + } + + template + inline __attribute__((always_inline)) bool alloc(CallbackData *&instance, transaction_cb_t &pre_cb, transaction_cb_t &post_cb) { + if constexpr (N > 0) { + if (alloc(instance, pre_cb, post_cb)) + return true; + if (!instances[N - 1]) { + instances[N - 1].emplace(); + instance = &*instances[N - 1]; + pre_cb = fn_pre_cb; + post_cb = fn_post_cb; + return true; + } + } + return false; + } + } + + bool patch(const std::shared_ptr &bus, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { + CallbackData *instance; + transaction_cb_t pre_cb; + transaction_cb_t post_cb; + if (!alloc(instance, pre_cb, post_cb)) + return false; + + instance->bus = bus; + instance->config = bus_config; + instance->inner_pre_cb = device_config.pre_cb; + instance->inner_post_cb = device_config.post_cb; + device_config.pre_cb = pre_cb; + device_config.post_cb = post_cb; + + return true; + } +} diff --git a/lib/SpiManager/src/SpiCallback.h b/lib/SpiManager/src/SpiCallback.h new file mode 100644 index 000000000..f8d52d0d7 --- /dev/null +++ b/lib/SpiManager/src/SpiCallback.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include + +// Pre and post callbacks for 2 buses with 3 devices each +#define SPI_MANAGER_CALLBACK_COUNT 6 + +class SpiBus; +class SpiBusConfig; + +namespace SpiCallback { + bool patch(const std::shared_ptr &bus, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); +} diff --git a/lib/SpiManager/src/SpiManager.cpp b/lib/SpiManager/src/SpiManager.cpp new file mode 100644 index 000000000..f4dc967a6 --- /dev/null +++ b/lib/SpiManager/src/SpiManager.cpp @@ -0,0 +1,64 @@ +#include "SpiManager.h" + +SpiManager::SpiManager() { +} + +bool SpiManager::register_bus(spi_host_device_t host_device) { + for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { + if (available_buses[i]) + continue; + + available_buses[i] = host_device; + return true; + } + + return false; +} + +bool SpiManager::claim_bus(spi_host_device_t &host_device) { + for (int i = SPI_MANAGER_NUM_BUSES - 1; i >= 0; --i) { + if (!available_buses[i]) + continue; + + host_device = *available_buses[i]; + available_buses[i].reset(); + return true; + } + + return false; +} + +spi_device_handle_t SpiManager::alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { + std::shared_ptr shared_bus = get_shared_bus(bus_id); + if (!shared_bus) + return nullptr; + + return shared_bus->add_device(bus_config, device_config); +} + +std::shared_ptr SpiManager::get_shared_bus(const std::string &bus_id) { + // look for existing shared bus + for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { + if (!shared_buses[i]) + continue; + if (shared_buses[i]->get_id() == bus_id) + return shared_buses[i]; + } + + // create new shared bus + for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { + if (shared_buses[i]) + continue; + + spi_host_device_t host_device; + if (!claim_bus(host_device)) + return nullptr; + + shared_buses[i] = std::make_shared(bus_id, host_device); + return shared_buses[i]; + } + + return nullptr; +} + +SpiManager SpiManagerInst; diff --git a/lib/SpiManager/src/SpiManager.h b/lib/SpiManager/src/SpiManager.h new file mode 100644 index 000000000..92923d5b0 --- /dev/null +++ b/lib/SpiManager/src/SpiManager.h @@ -0,0 +1,33 @@ +#pragma once + +#include "SpiBus.h" +#include "SpiBusConfig.h" + +#include + +#include +#include +#include +#include + +#define SPI_MANAGER_NUM_BUSES SOC_SPI_PERIPH_NUM + +class SpiManager { +public: + explicit SpiManager(); + SpiManager(const SpiManager&) = delete; + SpiManager &operator=(const SpiManager&) = delete; + + bool register_bus(spi_host_device_t host_device); + bool claim_bus(spi_host_device_t &host_device); + + spi_device_handle_t alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); + +private: + std::shared_ptr get_shared_bus(const std::string &bus_id); + + std::array, SPI_MANAGER_NUM_BUSES> available_buses; + std::array, SPI_MANAGER_NUM_BUSES> shared_buses; +}; + +extern SpiManager SpiManagerInst; From 4364daf54c1e57f639f78dc32d4e100b94db487e Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:46:29 +0200 Subject: [PATCH 06/58] Optimize CMT FIFO access --- lib/CMT2300a/cmt_spi3.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index 80d310350..aeae3d462 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -101,10 +101,12 @@ void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) }; SPI_PARAM_LOCK(); + spi_device_acquire_bus(spi_fifo, portMAX_DELAY); for (uint8_t i = 0; i < len; i++) { t.tx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); } + spi_device_release_bus(spi_fifo); SPI_PARAM_UNLOCK(); } @@ -116,9 +118,11 @@ void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) }; SPI_PARAM_LOCK(); + spi_device_acquire_bus(spi_fifo, portMAX_DELAY); for (uint8_t i = 0; i < len; i++) { t.rx_buffer = buf + i; ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); } + spi_device_release_bus(spi_fifo); SPI_PARAM_UNLOCK(); } From 1a583e765d7a4a78f1df5187e80ef30f884063e6 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:42:52 +0200 Subject: [PATCH 07/58] Change cmt_spi3 implementation from C to C++ --- lib/CMT2300a/{cmt_spi3.c => cmt_spi3.cpp} | 54 +++++++++++++++++------ lib/CMT2300a/cmt_spi3.h | 10 ++++- 2 files changed, 50 insertions(+), 14 deletions(-) rename lib/CMT2300a/{cmt_spi3.c => cmt_spi3.cpp} (74%) diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.cpp similarity index 74% rename from lib/CMT2300a/cmt_spi3.c rename to lib/CMT2300a/cmt_spi3.cpp index aeae3d462..aaf00fc92 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -15,7 +15,7 @@ SemaphoreHandle_t paramLock = NULL; spi_device_handle_t spi_reg, spi_fifo; -void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed) +void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed) { paramLock = xSemaphoreCreateMutex(); @@ -25,24 +25,32 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .sclk_io_num = pin_clk, .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 = 32, + .flags = 0, + .intr_flags = 0, }; + ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); + spi_device_interface_config_t devcfg = { .command_bits = 1, .address_bits = 7, .dummy_bits = 0, .mode = 0, // SPI mode 0 + .duty_cycle_pos = 0, .cs_ena_pretrans = 1, .cs_ena_posttrans = 1, .clock_speed_hz = spi_speed, + .input_delay_ns = 0, .spics_io_num = pin_cs, .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, .queue_size = 1, - .pre_cb = NULL, - .post_cb = NULL, + .pre_cb = nullptr, + .post_cb = nullptr, }; - - ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg)); // FiFo @@ -51,14 +59,16 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .address_bits = 0, .dummy_bits = 0, .mode = 0, // SPI mode 0 + .duty_cycle_pos = 0, .cs_ena_pretrans = 2, - .cs_ena_posttrans = (uint8_t)(2 * spi_speed / 1000000), // >2 us + .cs_ena_posttrans = static_cast(2 * spi_speed / 1000000), // >2 us .clock_speed_hz = spi_speed, + .input_delay_ns = 0, .spics_io_num = pin_fcs, .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, .queue_size = 1, - .pre_cb = NULL, - .post_cb = NULL, + .pre_cb = nullptr, + .post_cb = nullptr, }; ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); } @@ -66,11 +76,14 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin void cmt_spi3_write(const uint8_t addr, const uint8_t data) { spi_transaction_t t = { + .flags = 0, .cmd = 0, .addr = addr, .length = 8, + .rxlength = 0, + .user = nullptr, .tx_buffer = &data, - .rx_buffer = NULL + .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); @@ -81,11 +94,14 @@ uint8_t cmt_spi3_read(const uint8_t addr) { uint8_t data; spi_transaction_t t = { + .flags = 0, .cmd = 1, .addr = addr, + .length = 0, .rxlength = 8, - .tx_buffer = NULL, - .rx_buffer = &data + .user = nullptr, + .tx_buffer = nullptr, + .rx_buffer = &data, }; SPI_PARAM_LOCK(); ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); @@ -96,8 +112,14 @@ uint8_t cmt_spi3_read(const uint8_t addr) void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) { spi_transaction_t t = { + .flags = 0, + .cmd = 0, + .addr = 0, .length = 8, - .rx_buffer = NULL + .rxlength = 0, + .user = nullptr, + .tx_buffer = nullptr, + .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); @@ -113,8 +135,14 @@ void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) { spi_transaction_t t = { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = 0, .rxlength = 8, - .tx_buffer = NULL + .user = nullptr, + .tx_buffer = nullptr, + .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); diff --git a/lib/CMT2300a/cmt_spi3.h b/lib/CMT2300a/cmt_spi3.h index 6d3a67b62..16655dbad 100644 --- a/lib/CMT2300a/cmt_spi3.h +++ b/lib/CMT2300a/cmt_spi3.h @@ -3,7 +3,11 @@ #include -void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed); +#ifdef __cplusplus +extern "C" { +#endif + +void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed); void cmt_spi3_write(const uint8_t addr, const uint8_t dat); uint8_t cmt_spi3_read(const uint8_t addr); @@ -11,4 +15,8 @@ uint8_t cmt_spi3_read(const uint8_t addr); void cmt_spi3_write_fifo(const uint8_t* p_buf, const uint16_t len); void cmt_spi3_read_fifo(uint8_t* p_buf, const uint16_t len); +#ifdef __cplusplus +} +#endif + #endif From ece452068754f72dbb857ef69cb4309439dccbcc Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:19:06 +0200 Subject: [PATCH 08/58] Add Arduino SPI translation --- lib/SpiManager/src/SpiManager.cpp | 42 +++++++++++++++++++++++++++++++ lib/SpiManager/src/SpiManager.h | 7 ++++++ 2 files changed, 49 insertions(+) diff --git a/lib/SpiManager/src/SpiManager.cpp b/lib/SpiManager/src/SpiManager.cpp index f4dc967a6..efbd5e0a2 100644 --- a/lib/SpiManager/src/SpiManager.cpp +++ b/lib/SpiManager/src/SpiManager.cpp @@ -1,8 +1,39 @@ #include "SpiManager.h" +#ifdef ARDUINO +#include +#endif + SpiManager::SpiManager() { } +#ifdef ARDUINO + +std::optional SpiManager::to_arduino(spi_host_device_t host_device) { + switch (host_device) { +#if CONFIG_IDF_TARGET_ESP32 + case SPI1_HOST: + return FSPI; + case SPI2_HOST: + return HSPI; + case SPI3_HOST: + return VSPI; +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + case SPI2_HOST: + return FSPI; + case SPI3_HOST: + return HSPI; +#elif CONFIG_IDF_TARGET_ESP32C3 + case SPI2_HOST: + return FSPI; +#endif + default: + return std::nullopt; + } +} + +#endif + bool SpiManager::register_bus(spi_host_device_t host_device) { for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { if (available_buses[i]) @@ -28,6 +59,17 @@ bool SpiManager::claim_bus(spi_host_device_t &host_device) { return false; } +#ifdef ARDUINO + +std::optional SpiManager::claim_bus_arduino() { + spi_host_device_t host_device; + if (!claim_bus(host_device)) + return std::nullopt; + return to_arduino(host_device); +} + +#endif + spi_device_handle_t SpiManager::alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { std::shared_ptr shared_bus = get_shared_bus(bus_id); if (!shared_bus) diff --git a/lib/SpiManager/src/SpiManager.h b/lib/SpiManager/src/SpiManager.h index 92923d5b0..ee3b56a50 100644 --- a/lib/SpiManager/src/SpiManager.h +++ b/lib/SpiManager/src/SpiManager.h @@ -18,8 +18,15 @@ class SpiManager { SpiManager(const SpiManager&) = delete; SpiManager &operator=(const SpiManager&) = delete; +#ifdef ARDUINO + static std::optional to_arduino(spi_host_device_t host_device); +#endif + bool register_bus(spi_host_device_t host_device); bool claim_bus(spi_host_device_t &host_device); +#ifdef ARDUINO + std::optional claim_bus_arduino(); +#endif spi_device_handle_t alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); From 5457db269cf17238c37e4882b69fb1dbc1d41f68 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:45:50 +0200 Subject: [PATCH 09/58] Use SpiManager for nRF, CMT and W5500 --- lib/CMT2300a/cmt_spi3.cpp | 16 ++++++++-------- src/InverterSettings.cpp | 20 +++++--------------- src/W5500.cpp | 10 ++++++++-- src/main.cpp | 9 +++++++++ 4 files changed, 30 insertions(+), 25 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.cpp b/lib/CMT2300a/cmt_spi3.cpp index aaf00fc92..181bbb569 100644 --- a/lib/CMT2300a/cmt_spi3.cpp +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -1,6 +1,7 @@ #include "cmt_spi3.h" #include #include +#include SemaphoreHandle_t paramLock = NULL; #define SPI_PARAM_LOCK() \ @@ -8,17 +9,16 @@ SemaphoreHandle_t paramLock = NULL; } while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS) #define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock) -// for ESP32 this is the so-called HSPI -// for ESP32-S2/S3/C3 this nomenclature does not really exist anymore, -// it is simply the first externally usable hardware SPI master controller -#define SPI_CMT SPI2_HOST - spi_device_handle_t spi_reg, spi_fifo; void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed) { paramLock = xSemaphoreCreateMutex(); + spi_host_device_t host_device; + if (!SpiManagerInst.claim_bus(host_device)) + ESP_ERROR_CHECK(ESP_FAIL); + spi_bus_config_t buscfg = { .mosi_io_num = pin_sdio, .miso_io_num = -1, // single wire MOSI/MISO @@ -33,7 +33,7 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .flags = 0, .intr_flags = 0, }; - ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); + ESP_ERROR_CHECK(spi_bus_initialize(host_device, &buscfg, SPI_DMA_DISABLED)); spi_device_interface_config_t devcfg = { .command_bits = 1, @@ -51,7 +51,7 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .pre_cb = nullptr, .post_cb = nullptr, }; - ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg)); + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg, &spi_reg)); // FiFo spi_device_interface_config_t devcfg2 = { @@ -70,7 +70,7 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .pre_cb = nullptr, .post_cb = nullptr, }; - ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg2, &spi_fifo)); } void cmt_spi3_write(const uint8_t addr, const uint8_t data) diff --git a/src/InverterSettings.cpp b/src/InverterSettings.cpp index 0e903187d..7e7a5f865 100644 --- a/src/InverterSettings.cpp +++ b/src/InverterSettings.cpp @@ -8,20 +8,7 @@ #include "PinMapping.h" #include "SunPosition.h" #include - -// the NRF shall use the second externally usable HW SPI controller -// for ESP32 that is the so-called VSPI, for ESP32-S2/S3 it is now called implicitly -// HSPI, as it has shifted places for these chip generations -// for all generations, this is equivalent to SPI3_HOST in the lower level driver -// For ESP32-C2, the only externally usable HW SPI controller is SPI2, its signal names -// being prefixed with FSPI. -#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 -#define SPI_NRF HSPI -#elif CONFIG_IDF_TARGET_ESP32C3 -#define SPI_NRF FSPI -#else -#define SPI_NRF VSPI -#endif +#include InverterSettingsClass InverterSettings; @@ -44,7 +31,10 @@ void InverterSettingsClass::init(Scheduler& scheduler) if (PinMapping.isValidNrf24Config() || PinMapping.isValidCmt2300Config()) { if (PinMapping.isValidNrf24Config()) { - SPIClass* spiClass = new SPIClass(SPI_NRF); + auto spi_bus = SpiManagerInst.claim_bus_arduino(); + ESP_ERROR_CHECK(spi_bus ? ESP_OK : ESP_FAIL); + + SPIClass* spiClass = new SPIClass(*spi_bus); spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs); Hoymiles.initNRF(spiClass, pin.nrf24_en, pin.nrf24_irq); } diff --git a/src/W5500.cpp b/src/W5500.cpp index 3ce60b8d9..93a9a3161 100644 --- a/src/W5500.cpp +++ b/src/W5500.cpp @@ -1,5 +1,7 @@ #include "W5500.h" +#include + #include // Internal Arduino functions from WiFiGeneric @@ -10,6 +12,10 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i eth_handle(nullptr), eth_netif(nullptr) { + spi_host_device_t host_device; + if (!SpiManagerInst.claim_bus(host_device)) + ESP_ERROR_CHECK(ESP_FAIL); + gpio_reset_pin(static_cast(pin_rst)); gpio_set_level(static_cast(pin_rst), 0); gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); @@ -40,7 +46,7 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i .intr_flags = 0, }; - ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &bus_config, SPI_DMA_CH_AUTO)); + ESP_ERROR_CHECK(spi_bus_initialize(host_device, &bus_config, SPI_DMA_CH_AUTO)); spi_device_interface_config_t device_config { .command_bits = 16, // actually address phase @@ -60,7 +66,7 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i }; spi_device_handle_t spi; - ESP_ERROR_CHECK(spi_bus_add_device(SPI3_HOST, &device_config, &spi)); + ESP_ERROR_CHECK(spi_bus_add_device(host_device, &device_config, &spi)); // Reset sequence delayMicroseconds(500); diff --git a/src/main.cpp b/src/main.cpp index 00ab3f3a4..0377c8b3c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,12 +26,21 @@ #include #include #include +#include + +#include void setup() { // Move all dynamic allocations >512byte to psram (if available) heap_caps_malloc_extmem_enable(512); + // Initialize SpiManager + SpiManagerInst.register_bus(SPI2_HOST); +#if SOC_SPI_PERIPH_NUM > 2 + SpiManagerInst.register_bus(SPI3_HOST); +#endif + // Initialize serial output Serial.begin(SERIAL_BAUDRATE); #if ARDUINO_USB_CDC_ON_BOOT From 36da830f96e49b3b7e2a2843e43c4e2116248ef8 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 22:06:10 +0200 Subject: [PATCH 10/58] Use shared SPI bus for CMT and W5500 --- lib/CMT2300a/cmt_spi3.cpp | 34 +++++++++++++--------------------- src/W5500.cpp | 34 ++++++++-------------------------- 2 files changed, 21 insertions(+), 47 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.cpp b/lib/CMT2300a/cmt_spi3.cpp index 181bbb569..f6f912fbb 100644 --- a/lib/CMT2300a/cmt_spi3.cpp +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -15,25 +15,11 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin { paramLock = xSemaphoreCreateMutex(); - spi_host_device_t host_device; - if (!SpiManagerInst.claim_bus(host_device)) - ESP_ERROR_CHECK(ESP_FAIL); - - spi_bus_config_t buscfg = { - .mosi_io_num = pin_sdio, - .miso_io_num = -1, // single wire MOSI/MISO - .sclk_io_num = pin_clk, - .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 = 32, - .flags = 0, - .intr_flags = 0, - }; - ESP_ERROR_CHECK(spi_bus_initialize(host_device, &buscfg, SPI_DMA_DISABLED)); + auto bus_config = std::make_shared( + static_cast(pin_sdio), + GPIO_NUM_NC, + static_cast(pin_clk) + ); spi_device_interface_config_t devcfg = { .command_bits = 1, @@ -51,7 +37,10 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .pre_cb = nullptr, .post_cb = nullptr, }; - ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg, &spi_reg)); + + spi_reg = SpiManagerInst.alloc_device("", bus_config, devcfg); + if (!spi_reg) + ESP_ERROR_CHECK(ESP_FAIL); // FiFo spi_device_interface_config_t devcfg2 = { @@ -70,7 +59,10 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin .pre_cb = nullptr, .post_cb = nullptr, }; - ESP_ERROR_CHECK(spi_bus_add_device(host_device, &devcfg2, &spi_fifo)); + + spi_fifo = SpiManagerInst.alloc_device("", bus_config, devcfg2); + if (!spi_fifo) + ESP_ERROR_CHECK(ESP_ERR_NOT_SUPPORTED); } void cmt_spi3_write(const uint8_t addr, const uint8_t data) diff --git a/src/W5500.cpp b/src/W5500.cpp index 93a9a3161..3f2742041 100644 --- a/src/W5500.cpp +++ b/src/W5500.cpp @@ -12,41 +12,22 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i eth_handle(nullptr), eth_netif(nullptr) { - spi_host_device_t host_device; - if (!SpiManagerInst.claim_bus(host_device)) - ESP_ERROR_CHECK(ESP_FAIL); - gpio_reset_pin(static_cast(pin_rst)); gpio_set_level(static_cast(pin_rst), 0); gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); - gpio_reset_pin(static_cast(pin_mosi)); - gpio_reset_pin(static_cast(pin_miso)); - gpio_reset_pin(static_cast(pin_sclk)); gpio_reset_pin(static_cast(pin_cs)); - gpio_reset_pin(static_cast(pin_int)); esp_err_t err = gpio_install_isr_service(ARDUINO_ISR_FLAG); if (err != ESP_ERR_INVALID_STATE) // don't raise an error when ISR service is already installed ESP_ERROR_CHECK(err); - spi_bus_config_t bus_config { - .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_device, &bus_config, SPI_DMA_CH_AUTO)); + auto bus_config = std::make_shared( + static_cast(pin_mosi), + static_cast(pin_miso), + static_cast(pin_sclk) + ); spi_device_interface_config_t device_config { .command_bits = 16, // actually address phase @@ -65,8 +46,9 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i .post_cb = nullptr, }; - spi_device_handle_t spi; - ESP_ERROR_CHECK(spi_bus_add_device(host_device, &device_config, &spi)); + spi_device_handle_t spi = SpiManagerInst.alloc_device("", bus_config, device_config); + if (!spi) + ESP_ERROR_CHECK(ESP_FAIL); // Reset sequence delayMicroseconds(500); From 31cf756a7ecbeed133218ea56263ac9a0a302fae Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 21 Sep 2024 23:00:31 +0200 Subject: [PATCH 11/58] Only use a single SPI device for CMT --- lib/CMT2300a/cmt_spi3.cpp | 133 ++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 63 deletions(-) diff --git a/lib/CMT2300a/cmt_spi3.cpp b/lib/CMT2300a/cmt_spi3.cpp index f6f912fbb..28fdc8aee 100644 --- a/lib/CMT2300a/cmt_spi3.cpp +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -9,7 +9,16 @@ SemaphoreHandle_t paramLock = NULL; } while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS) #define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock) -spi_device_handle_t spi_reg, spi_fifo; +static void IRAM_ATTR pre_cb(spi_transaction_t *trans) { + gpio_set_level(*reinterpret_cast(trans->user), 0); +} + +static void IRAM_ATTR post_cb(spi_transaction_t *trans) { + gpio_set_level(*reinterpret_cast(trans->user), 1); +} + +spi_device_handle_t spi; +gpio_num_t cs_reg, cs_fifo; void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int32_t spi_speed) { @@ -21,128 +30,126 @@ void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin static_cast(pin_clk) ); - spi_device_interface_config_t devcfg = { - .command_bits = 1, - .address_bits = 7, + spi_device_interface_config_t device_config { + .command_bits = 0, // set by transactions individually + .address_bits = 0, // set by transactions individually .dummy_bits = 0, .mode = 0, // SPI mode 0 .duty_cycle_pos = 0, - .cs_ena_pretrans = 1, - .cs_ena_posttrans = 1, + .cs_ena_pretrans = 2, // only 1 pre and post cycle would be required for register access + .cs_ena_posttrans = static_cast(2 * spi_speed / 1000000), // >2 us .clock_speed_hz = spi_speed, .input_delay_ns = 0, - .spics_io_num = pin_cs, + .spics_io_num = -1, // CS handled by callbacks .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, .queue_size = 1, - .pre_cb = nullptr, - .post_cb = nullptr, + .pre_cb = pre_cb, + .post_cb = post_cb, }; - spi_reg = SpiManagerInst.alloc_device("", bus_config, devcfg); - if (!spi_reg) + spi = SpiManagerInst.alloc_device("", bus_config, device_config); + if (!spi) ESP_ERROR_CHECK(ESP_FAIL); - // FiFo - spi_device_interface_config_t devcfg2 = { - .command_bits = 0, - .address_bits = 0, - .dummy_bits = 0, - .mode = 0, // SPI mode 0 - .duty_cycle_pos = 0, - .cs_ena_pretrans = 2, - .cs_ena_posttrans = static_cast(2 * spi_speed / 1000000), // >2 us - .clock_speed_hz = spi_speed, - .input_delay_ns = 0, - .spics_io_num = pin_fcs, - .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, - .queue_size = 1, - .pre_cb = nullptr, - .post_cb = nullptr, - }; + cs_reg = static_cast(pin_cs); + ESP_ERROR_CHECK(gpio_reset_pin(cs_reg)); + ESP_ERROR_CHECK(gpio_set_level(cs_reg, 1)); + ESP_ERROR_CHECK(gpio_set_direction(cs_reg, GPIO_MODE_OUTPUT)); - spi_fifo = SpiManagerInst.alloc_device("", bus_config, devcfg2); - if (!spi_fifo) - ESP_ERROR_CHECK(ESP_ERR_NOT_SUPPORTED); + cs_fifo = static_cast(pin_fcs); + ESP_ERROR_CHECK(gpio_reset_pin(cs_fifo)); + ESP_ERROR_CHECK(gpio_set_level(cs_fifo, 1)); + ESP_ERROR_CHECK(gpio_set_direction(cs_fifo, GPIO_MODE_OUTPUT)); } void cmt_spi3_write(const uint8_t addr, const uint8_t data) { - spi_transaction_t t = { - .flags = 0, - .cmd = 0, - .addr = addr, - .length = 8, - .rxlength = 0, - .user = nullptr, - .tx_buffer = &data, - .rx_buffer = nullptr, + spi_transaction_ext_t trans { + .base { + .flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR, + .cmd = 0, + .addr = addr, + .length = 8, + .rxlength = 0, + .user = &cs_reg, // CS for register access + .tx_buffer = &data, + .rx_buffer = nullptr, + }, + .command_bits = 1, + .address_bits = 7, + .dummy_bits = 0, }; SPI_PARAM_LOCK(); - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast(&trans))); SPI_PARAM_UNLOCK(); } uint8_t cmt_spi3_read(const uint8_t addr) { uint8_t data; - spi_transaction_t t = { - .flags = 0, - .cmd = 1, - .addr = addr, - .length = 0, - .rxlength = 8, - .user = nullptr, - .tx_buffer = nullptr, - .rx_buffer = &data, + spi_transaction_ext_t trans { + .base { + .flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR, + .cmd = 1, + .addr = addr, + .length = 0, + .rxlength = 8, + .user = &cs_reg, // CS for register access + .tx_buffer = nullptr, + .rx_buffer = &data, + }, + .command_bits = 1, + .address_bits = 7, + .dummy_bits = 0, }; SPI_PARAM_LOCK(); - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, reinterpret_cast(&trans))); SPI_PARAM_UNLOCK(); return data; } void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) { - spi_transaction_t t = { + spi_transaction_t trans { .flags = 0, .cmd = 0, .addr = 0, .length = 8, .rxlength = 0, - .user = nullptr, + .user = &cs_fifo, // CS for FIFO access .tx_buffer = nullptr, .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); - spi_device_acquire_bus(spi_fifo, portMAX_DELAY); + spi_device_acquire_bus(spi, portMAX_DELAY); for (uint8_t i = 0; i < len; i++) { - t.tx_buffer = buf + i; - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); + trans.tx_buffer = buf + i; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans)); } - spi_device_release_bus(spi_fifo); + spi_device_release_bus(spi); SPI_PARAM_UNLOCK(); } void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) { - spi_transaction_t t = { + spi_transaction_t trans { .flags = 0, .cmd = 0, .addr = 0, .length = 0, .rxlength = 8, - .user = nullptr, + .user = &cs_fifo, // CS for FIFO access .tx_buffer = nullptr, .rx_buffer = nullptr, }; SPI_PARAM_LOCK(); - spi_device_acquire_bus(spi_fifo, portMAX_DELAY); + spi_device_acquire_bus(spi, portMAX_DELAY); for (uint8_t i = 0; i < len; i++) { - t.rx_buffer = buf + i; - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); + trans.rx_buffer = buf + i; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans)); } - spi_device_release_bus(spi_fifo); + spi_device_release_bus(spi); SPI_PARAM_UNLOCK(); } From 7746d01fc088681e5f02445cddd7e8fa98ff0e1e Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Thu, 26 Sep 2024 18:47:27 +0200 Subject: [PATCH 12/58] Apply license headers and automatic code formatting to SpiManager --- lib/SpiManager/src/SpiBus.cpp | 19 +++--- lib/SpiManager/src/SpiBus.h | 24 ++++--- lib/SpiManager/src/SpiBusConfig.cpp | 18 +++--- lib/SpiManager/src/SpiBusConfig.h | 3 +- lib/SpiManager/src/SpiCallback.cpp | 98 +++++++++++++++-------------- lib/SpiManager/src/SpiCallback.h | 4 +- lib/SpiManager/src/SpiManager.cpp | 22 ++++--- lib/SpiManager/src/SpiManager.h | 9 +-- 8 files changed, 111 insertions(+), 86 deletions(-) diff --git a/lib/SpiManager/src/SpiBus.cpp b/lib/SpiManager/src/SpiBus.cpp index 26b361cc4..0dcb5e4f4 100644 --- a/lib/SpiManager/src/SpiBus.cpp +++ b/lib/SpiManager/src/SpiBus.cpp @@ -1,12 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include "SpiBus.h" - #include "SpiBusConfig.h" #include "SpiCallback.h" -SpiBus::SpiBus(const std::string &_id, spi_host_device_t _host_device) : - id(_id), - host_device(_host_device), - cur_config(nullptr) +SpiBus::SpiBus(const std::string& _id, spi_host_device_t _host_device) + : id(_id) + , host_device(_host_device) + , cur_config(nullptr) { spi_bus_config_t bus_config { .mosi_io_num = -1, @@ -25,11 +25,13 @@ SpiBus::SpiBus(const std::string &_id, spi_host_device_t _host_device) : ESP_ERROR_CHECK(spi_bus_initialize(host_device, &bus_config, SPI_DMA_CH_AUTO)); } -SpiBus::~SpiBus() { +SpiBus::~SpiBus() +{ ESP_ERROR_CHECK(spi_bus_free(host_device)); } -spi_device_handle_t SpiBus::add_device(const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { +spi_device_handle_t SpiBus::add_device(const std::shared_ptr& bus_config, spi_device_interface_config_t& device_config) +{ if (!SpiCallback::patch(shared_from_this(), bus_config, device_config)) return nullptr; @@ -40,7 +42,8 @@ spi_device_handle_t SpiBus::add_device(const std::shared_ptr &bus_ // TODO: add remove_device (with spi_device_acquire_bus) -void SpiBus::apply_config(SpiBusConfig *config) { +void SpiBus::apply_config(SpiBusConfig* config) +{ if (cur_config) cur_config->unpatch(host_device); cur_config = config; diff --git a/lib/SpiManager/src/SpiBus.h b/lib/SpiManager/src/SpiBus.h index a5fde06c1..1ca79c7ca 100644 --- a/lib/SpiManager/src/SpiBus.h +++ b/lib/SpiManager/src/SpiBus.h @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include - #include #include @@ -9,37 +9,41 @@ class SpiBusConfig; class SpiBus : public std::enable_shared_from_this { public: - explicit SpiBus(const std::string &id, spi_host_device_t host_device); + explicit SpiBus(const std::string& id, spi_host_device_t host_device); SpiBus(const SpiBus&) = delete; - SpiBus &operator=(const SpiBus&) = delete; + SpiBus& operator=(const SpiBus&) = delete; ~SpiBus(); - inline __attribute__((always_inline)) void require_config(SpiBusConfig *config) { + inline __attribute__((always_inline)) void require_config(SpiBusConfig* config) + { if (config == cur_config) return; apply_config(config); } - inline __attribute__((always_inline)) void free_config(SpiBusConfig *config) { + inline __attribute__((always_inline)) void free_config(SpiBusConfig* config) + { if (config != cur_config) return; apply_config(nullptr); } - inline const std::string &get_id() const { + inline const std::string& get_id() const + { return id; } - inline spi_host_device_t get_host_device() const { + inline spi_host_device_t get_host_device() const + { return host_device; } - spi_device_handle_t add_device(const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); + spi_device_handle_t add_device(const std::shared_ptr& bus_config, spi_device_interface_config_t& device_config); private: - void apply_config(SpiBusConfig *config); + void apply_config(SpiBusConfig* config); std::string id; spi_host_device_t host_device; - SpiBusConfig *cur_config; + SpiBusConfig* cur_config; }; diff --git a/lib/SpiManager/src/SpiBusConfig.cpp b/lib/SpiManager/src/SpiBusConfig.cpp index c3cc01961..64234d658 100644 --- a/lib/SpiManager/src/SpiBusConfig.cpp +++ b/lib/SpiManager/src/SpiBusConfig.cpp @@ -1,13 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include "SpiBusConfig.h" #include #include #include -SpiBusConfig::SpiBusConfig(gpio_num_t _pin_mosi, gpio_num_t _pin_miso, gpio_num_t _pin_sclk) : - pin_mosi(_pin_mosi), - pin_miso(_pin_miso), - pin_sclk(_pin_sclk) +SpiBusConfig::SpiBusConfig(gpio_num_t _pin_mosi, gpio_num_t _pin_miso, gpio_num_t _pin_sclk) + : pin_mosi(_pin_mosi) + , pin_miso(_pin_miso) + , pin_sclk(_pin_sclk) { if (pin_mosi != GPIO_NUM_NC) { ESP_ERROR_CHECK(gpio_reset_pin(pin_mosi)); @@ -25,7 +26,8 @@ SpiBusConfig::SpiBusConfig(gpio_num_t _pin_mosi, gpio_num_t _pin_miso, gpio_num_ } } -SpiBusConfig::~SpiBusConfig() { +SpiBusConfig::~SpiBusConfig() +{ if (pin_mosi != GPIO_NUM_NC) ESP_ERROR_CHECK(gpio_reset_pin(pin_mosi)); @@ -36,7 +38,8 @@ SpiBusConfig::~SpiBusConfig() { ESP_ERROR_CHECK(gpio_reset_pin(pin_sclk)); } -void SpiBusConfig::patch(spi_host_device_t host_device) { +void SpiBusConfig::patch(spi_host_device_t host_device) +{ if (pin_mosi != GPIO_NUM_NC) { esp_rom_gpio_connect_out_signal(pin_mosi, spi_periph_signal[host_device].spid_out, false, false); esp_rom_gpio_connect_in_signal(pin_mosi, spi_periph_signal[host_device].spid_in, false); @@ -51,7 +54,8 @@ void SpiBusConfig::patch(spi_host_device_t host_device) { } } -void SpiBusConfig::unpatch(spi_host_device_t host_device) { +void SpiBusConfig::unpatch(spi_host_device_t host_device) +{ if (pin_mosi != GPIO_NUM_NC) { esp_rom_gpio_connect_out_signal(pin_mosi, SIG_GPIO_OUT_IDX, false, false); esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, spi_periph_signal[host_device].spid_in, false); diff --git a/lib/SpiManager/src/SpiBusConfig.h b/lib/SpiManager/src/SpiBusConfig.h index e4549ef18..736b89519 100644 --- a/lib/SpiManager/src/SpiBusConfig.h +++ b/lib/SpiManager/src/SpiBusConfig.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include @@ -7,7 +8,7 @@ class SpiBusConfig { public: explicit SpiBusConfig(gpio_num_t pin_mosi, gpio_num_t pin_miso, gpio_num_t pin_sclk); SpiBusConfig(const SpiBusConfig&) = delete; - SpiBusConfig &operator=(const SpiBusConfig&) = delete; + SpiBusConfig& operator=(const SpiBusConfig&) = delete; ~SpiBusConfig(); void patch(spi_host_device_t host_device); diff --git a/lib/SpiManager/src/SpiCallback.cpp b/lib/SpiManager/src/SpiCallback.cpp index e281db98c..e353d04bf 100644 --- a/lib/SpiManager/src/SpiCallback.cpp +++ b/lib/SpiManager/src/SpiCallback.cpp @@ -1,65 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include "SpiCallback.h" #include "SpiBus.h" - #include #include namespace SpiCallback { - namespace { - struct CallbackData { - std::shared_ptr bus; - std::shared_ptr config; - transaction_cb_t inner_pre_cb; - transaction_cb_t inner_post_cb; - }; +namespace { + struct CallbackData { + std::shared_ptr bus; + std::shared_ptr config; + transaction_cb_t inner_pre_cb; + transaction_cb_t inner_post_cb; + }; - std::array, SPI_MANAGER_CALLBACK_COUNT> instances; + std::array, SPI_MANAGER_CALLBACK_COUNT> instances; - template - void IRAM_ATTR fn_pre_cb(spi_transaction_t *trans) { - instances[N]->bus->require_config(instances[N]->config.get()); - if (instances[N]->inner_pre_cb) - instances[N]->inner_pre_cb(trans); - } + template + void IRAM_ATTR fn_pre_cb(spi_transaction_t* trans) + { + instances[N]->bus->require_config(instances[N]->config.get()); + if (instances[N]->inner_pre_cb) + instances[N]->inner_pre_cb(trans); + } - template - void IRAM_ATTR fn_post_cb(spi_transaction_t *trans) { - if (instances[N]->inner_post_cb) - instances[N]->inner_post_cb(trans); - } + template + void IRAM_ATTR fn_post_cb(spi_transaction_t* trans) + { + if (instances[N]->inner_post_cb) + instances[N]->inner_post_cb(trans); + } - template - inline __attribute__((always_inline)) bool alloc(CallbackData *&instance, transaction_cb_t &pre_cb, transaction_cb_t &post_cb) { - if constexpr (N > 0) { - if (alloc(instance, pre_cb, post_cb)) - return true; - if (!instances[N - 1]) { - instances[N - 1].emplace(); - instance = &*instances[N - 1]; - pre_cb = fn_pre_cb; - post_cb = fn_post_cb; - return true; - } + template + inline __attribute__((always_inline)) bool alloc(CallbackData*& instance, transaction_cb_t& pre_cb, transaction_cb_t& post_cb) + { + if constexpr (N > 0) { + if (alloc(instance, pre_cb, post_cb)) + return true; + if (!instances[N - 1]) { + instances[N - 1].emplace(); + instance = &*instances[N - 1]; + pre_cb = fn_pre_cb; + post_cb = fn_post_cb; + return true; } - return false; } + return false; } +} - bool patch(const std::shared_ptr &bus, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { - CallbackData *instance; - transaction_cb_t pre_cb; - transaction_cb_t post_cb; - if (!alloc(instance, pre_cb, post_cb)) - return false; +bool patch(const std::shared_ptr& bus, const std::shared_ptr& bus_config, spi_device_interface_config_t& device_config) +{ + CallbackData* instance; + transaction_cb_t pre_cb; + transaction_cb_t post_cb; + if (!alloc(instance, pre_cb, post_cb)) + return false; - instance->bus = bus; - instance->config = bus_config; - instance->inner_pre_cb = device_config.pre_cb; - instance->inner_post_cb = device_config.post_cb; - device_config.pre_cb = pre_cb; - device_config.post_cb = post_cb; + instance->bus = bus; + instance->config = bus_config; + instance->inner_pre_cb = device_config.pre_cb; + instance->inner_post_cb = device_config.post_cb; + device_config.pre_cb = pre_cb; + device_config.post_cb = post_cb; - return true; - } + return true; +} } diff --git a/lib/SpiManager/src/SpiCallback.h b/lib/SpiManager/src/SpiCallback.h index f8d52d0d7..98222b1a9 100644 --- a/lib/SpiManager/src/SpiCallback.h +++ b/lib/SpiManager/src/SpiCallback.h @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include - #include // Pre and post callbacks for 2 buses with 3 devices each @@ -11,5 +11,5 @@ class SpiBus; class SpiBusConfig; namespace SpiCallback { - bool patch(const std::shared_ptr &bus, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); +bool patch(const std::shared_ptr& bus, const std::shared_ptr& bus_config, spi_device_interface_config_t& device_config); } diff --git a/lib/SpiManager/src/SpiManager.cpp b/lib/SpiManager/src/SpiManager.cpp index efbd5e0a2..d727a96ef 100644 --- a/lib/SpiManager/src/SpiManager.cpp +++ b/lib/SpiManager/src/SpiManager.cpp @@ -1,15 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #include "SpiManager.h" #ifdef ARDUINO #include #endif -SpiManager::SpiManager() { +SpiManager::SpiManager() +{ } #ifdef ARDUINO -std::optional SpiManager::to_arduino(spi_host_device_t host_device) { +std::optional SpiManager::to_arduino(spi_host_device_t host_device) +{ switch (host_device) { #if CONFIG_IDF_TARGET_ESP32 case SPI1_HOST: @@ -34,7 +37,8 @@ std::optional SpiManager::to_arduino(spi_host_device_t host_device) { #endif -bool SpiManager::register_bus(spi_host_device_t host_device) { +bool SpiManager::register_bus(spi_host_device_t host_device) +{ for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { if (available_buses[i]) continue; @@ -46,7 +50,8 @@ bool SpiManager::register_bus(spi_host_device_t host_device) { return false; } -bool SpiManager::claim_bus(spi_host_device_t &host_device) { +bool SpiManager::claim_bus(spi_host_device_t& host_device) +{ for (int i = SPI_MANAGER_NUM_BUSES - 1; i >= 0; --i) { if (!available_buses[i]) continue; @@ -61,7 +66,8 @@ bool SpiManager::claim_bus(spi_host_device_t &host_device) { #ifdef ARDUINO -std::optional SpiManager::claim_bus_arduino() { +std::optional SpiManager::claim_bus_arduino() +{ spi_host_device_t host_device; if (!claim_bus(host_device)) return std::nullopt; @@ -70,7 +76,8 @@ std::optional SpiManager::claim_bus_arduino() { #endif -spi_device_handle_t SpiManager::alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config) { +spi_device_handle_t SpiManager::alloc_device(const std::string& bus_id, const std::shared_ptr& bus_config, spi_device_interface_config_t& device_config) +{ std::shared_ptr shared_bus = get_shared_bus(bus_id); if (!shared_bus) return nullptr; @@ -78,7 +85,8 @@ spi_device_handle_t SpiManager::alloc_device(const std::string &bus_id, const st return shared_bus->add_device(bus_config, device_config); } -std::shared_ptr SpiManager::get_shared_bus(const std::string &bus_id) { +std::shared_ptr SpiManager::get_shared_bus(const std::string& bus_id) +{ // look for existing shared bus for (int i = 0; i < SPI_MANAGER_NUM_BUSES; ++i) { if (!shared_buses[i]) diff --git a/lib/SpiManager/src/SpiManager.h b/lib/SpiManager/src/SpiManager.h index ee3b56a50..1e8f6e1b0 100644 --- a/lib/SpiManager/src/SpiManager.h +++ b/lib/SpiManager/src/SpiManager.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include "SpiBus.h" @@ -16,22 +17,22 @@ class SpiManager { public: explicit SpiManager(); SpiManager(const SpiManager&) = delete; - SpiManager &operator=(const SpiManager&) = delete; + SpiManager& operator=(const SpiManager&) = delete; #ifdef ARDUINO static std::optional to_arduino(spi_host_device_t host_device); #endif bool register_bus(spi_host_device_t host_device); - bool claim_bus(spi_host_device_t &host_device); + bool claim_bus(spi_host_device_t& host_device); #ifdef ARDUINO std::optional claim_bus_arduino(); #endif - spi_device_handle_t alloc_device(const std::string &bus_id, const std::shared_ptr &bus_config, spi_device_interface_config_t &device_config); + spi_device_handle_t alloc_device(const std::string& bus_id, const std::shared_ptr& bus_config, spi_device_interface_config_t& device_config); private: - std::shared_ptr get_shared_bus(const std::string &bus_id); + std::shared_ptr get_shared_bus(const std::string& bus_id); std::array, SPI_MANAGER_NUM_BUSES> available_buses; std::array, SPI_MANAGER_NUM_BUSES> shared_buses; From a18e298cddda8c4ef77a486f1812a9ce21e1f0df Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Thu, 26 Sep 2024 19:22:30 +0200 Subject: [PATCH 13/58] Apply automatic code formatting --- include/NetworkSettings.h | 2 +- include/W5500.h | 8 ++++---- src/NetworkSettings.cpp | 5 +++-- src/W5500.cpp | 30 +++++++++++++++++------------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/include/NetworkSettings.h b/include/NetworkSettings.h index 51ec10757..6c1c217d8 100644 --- a/include/NetworkSettings.h +++ b/include/NetworkSettings.h @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "W5500.h" #include #include #include #include -#include "W5500.h" enum class network_mode { WiFi, diff --git a/include/W5500.h b/include/W5500.h index f4ee9d2a0..fc0c66148 100644 --- a/include/W5500.h +++ b/include/W5500.h @@ -1,19 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include - #include +#include class W5500 { public: explicit W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst); W5500(const W5500&) = delete; - W5500 &operator=(const W5500&) = delete; + W5500& operator=(const W5500&) = delete; ~W5500(); String macAddress(); private: esp_eth_handle_t eth_handle; - esp_netif_t *eth_netif; + esp_netif_t* eth_netif; }; diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index f754ea3ff..d1edcc82a 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -7,10 +7,10 @@ #include "MessageOutput.h" #include "PinMapping.h" #include "Utils.h" +#include "__compiled_constants.h" #include "defaults.h" #include #include -#include "__compiled_constants.h" NetworkSettingsClass::NetworkSettingsClass() : _loopTask(TASK_IMMEDIATE, TASK_FOREVER, std::bind(&NetworkSettingsClass::loop, this)) @@ -404,8 +404,9 @@ String NetworkSettingsClass::macAddress() const { switch (_networkMode) { case network_mode::Ethernet: - if (_w5500) + if (_w5500) { return _w5500->macAddress(); + } return ETH.macAddress(); break; case network_mode::WiFi: diff --git a/src/W5500.cpp b/src/W5500.cpp index 3f2742041..0f5b57398 100644 --- a/src/W5500.cpp +++ b/src/W5500.cpp @@ -1,16 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2024 Thomas Basler and others + */ + #include "W5500.h" #include - #include // Internal Arduino functions from WiFiGeneric void tcpipInit(); -void add_esp_interface_netif(esp_interface_t interface, esp_netif_t *esp_netif); +void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif); -W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) : - eth_handle(nullptr), - eth_netif(nullptr) +W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) + : eth_handle(nullptr) + , eth_netif(nullptr) { gpio_reset_pin(static_cast(pin_rst)); gpio_set_level(static_cast(pin_rst), 0); @@ -26,8 +30,7 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i auto bus_config = std::make_shared( static_cast(pin_mosi), static_cast(pin_miso), - static_cast(pin_sclk) - ); + static_cast(pin_sclk)); spi_device_interface_config_t device_config { .command_bits = 16, // actually address phase @@ -65,11 +68,11 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i 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); + 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_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(ð_config, ð_handle)); @@ -90,18 +93,19 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i ESP_ERROR_CHECK(esp_eth_start(eth_handle)); } -W5500::~W5500() { +W5500::~W5500() +{ // TODO(LennartF22): support cleanup at some point? } -String W5500::macAddress() { +String W5500::macAddress() +{ uint8_t mac_addr[6] = {}; esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); char mac_addr_str[18]; 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] - ); + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); return String(mac_addr_str); } From 12b9542f72a0f262be58d269cde3cb26d413e819 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Thu, 26 Sep 2024 20:15:19 +0200 Subject: [PATCH 14/58] Added device profile for OpenDTU Fusion v2 PoE --- docs/DeviceProfiles/opendtu_fusion.json | 59 ++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/docs/DeviceProfiles/opendtu_fusion.json b/docs/DeviceProfiles/opendtu_fusion.json index f33dc47db..bb1e41089 100644 --- a/docs/DeviceProfiles/opendtu_fusion.json +++ b/docs/DeviceProfiles/opendtu_fusion.json @@ -1,6 +1,9 @@ [ { "name": "OpenDTU Fusion v1", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], "nrf24": { "miso": 48, "mosi": 35, @@ -25,6 +28,9 @@ }, { "name": "OpenDTU Fusion v1 with SSD1306 Display", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], "nrf24": { "miso": 48, "mosi": 35, @@ -54,6 +60,9 @@ }, { "name": "OpenDTU Fusion v1 with SH1106 Display", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], "nrf24": { "miso": 48, "mosi": 35, @@ -83,6 +92,9 @@ }, { "name": "OpenDTU Fusion v2 with CMT2300A and NRF24", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], "nrf24": { "miso": 48, "mosi": 35, @@ -115,6 +127,9 @@ }, { "name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SH1106 Display", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], "nrf24": { "miso": 48, "mosi": 35, @@ -152,6 +167,9 @@ }, { "name": "OpenDTU Fusion v2 with CMT2300A, NRF24 and SSD1306 Display", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], "nrf24": { "miso": 48, "mosi": 35, @@ -186,5 +204,44 @@ "data": 2, "clk": 1 } + }, + { + "name": "OpenDTU Fusion v2 PoE", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], + "nrf24": { + "miso": 48, + "mosi": 35, + "clk": 36, + "irq": 47, + "en": 38, + "cs": 37 + }, + "cmt": { + "clk": 6, + "cs": 4, + "fcs": 21, + "sdio": 5, + "gpio2": 3, + "gpio3": 8 + }, + "w5500": { + "mosi": 40, + "miso": 41, + "sclk": 39, + "cs": 42, + "int": 44, + "rst": 43 + }, + "led": { + "led0": 17, + "led1": 18 + }, + "display": { + "type": 0, + "data": 2, + "clk": 1 + } } -] \ No newline at end of file +] From d770566aec64b25a881a83f40012f4406404a425 Mon Sep 17 00:00:00 2001 From: vaterlangen Date: Thu, 26 Sep 2024 21:22:08 +0200 Subject: [PATCH 15/58] increase chunkSizeWarningLimit for webapp build (#1287) increase from 500k (default) to 1024k in order to get rid of the warning messages. --- webapp/vite.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/webapp/vite.config.ts b/webapp/vite.config.ts index 7c9c58057..fcf7fe788 100644 --- a/webapp/vite.config.ts +++ b/webapp/vite.config.ts @@ -45,6 +45,7 @@ export default defineConfig({ outDir: '../webapp_dist', emptyOutDir: true, minify: 'terser', + chunkSizeWarningLimit: 1024, rollupOptions: { output: { // Only create one js file From b43383007a637decac54a331d4b5feecf8741426 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Fri, 27 Sep 2024 17:32:28 +0200 Subject: [PATCH 16/58] Rename NetworkEventCb to DtuNetworkEventCb to prevent further upgrade issues --- include/NetworkSettings.h | 6 +++--- src/NetworkSettings.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/NetworkSettings.h b/include/NetworkSettings.h index 6c1c217d8..ea3869cb3 100644 --- a/include/NetworkSettings.h +++ b/include/NetworkSettings.h @@ -24,10 +24,10 @@ enum class network_event { NETWORK_EVENT_MAX }; -typedef std::function NetworkEventCb; +typedef std::function DtuNetworkEventCb; typedef struct NetworkEventCbList { - NetworkEventCb cb; + DtuNetworkEventCb cb; network_event event; NetworkEventCbList() @@ -54,7 +54,7 @@ class NetworkSettingsClass { bool isConnected() const; network_mode NetworkMode() const; - bool onEvent(NetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX); + bool onEvent(DtuNetworkEventCb cbEvent, const network_event event = network_event::NETWORK_EVENT_MAX); void raiseEvent(const network_event event); private: diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index d1edcc82a..8e0322f9c 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -106,7 +106,7 @@ void NetworkSettingsClass::NetworkEvent(const WiFiEvent_t event, WiFiEventInfo_t } } -bool NetworkSettingsClass::onEvent(NetworkEventCb cbEvent, const network_event event) +bool NetworkSettingsClass::onEvent(DtuNetworkEventCb cbEvent, const network_event event) { if (!cbEvent) { return pdFALSE; From b85e0ab574bf11df48b13b13aece81ee94f1b9ac Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Fri, 27 Sep 2024 17:35:33 +0200 Subject: [PATCH 17/58] Add default values for ethernet pins in case they are not defined for a specific board --- src/PinMapping.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/PinMapping.cpp b/src/PinMapping.cpp index 7514af5f2..67b68a158 100644 --- a/src/PinMapping.cpp +++ b/src/PinMapping.cpp @@ -108,6 +108,30 @@ #define W5500_RST -1 #endif +#ifndef ETH_PHY_ADDR +#define ETH_PHY_ADDR -1 +#endif + +#ifndef ETH_PHY_POWER +#define ETH_PHY_POWER -1 +#endif + +#ifndef ETH_PHY_MDC +#define ETH_PHY_MDC -1 +#endif + +#ifndef ETH_PHY_MDIO +#define ETH_PHY_MDIO -1 +#endif + +#ifndef ETH_PHY_TYPE +#define ETH_PHY_TYPE ETH_PHY_LAN8720 +#endif + +#ifndef ETH_CLK_MODE +#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN +#endif + PinMappingClass PinMapping; PinMappingClass::PinMappingClass() @@ -261,5 +285,7 @@ bool PinMappingClass::isValidW5500Config() const bool PinMappingClass::isValidEthConfig() const { - return _pinMapping.eth_enabled; + return _pinMapping.eth_enabled + && _pinMapping.eth_mdc >= 0 + && _pinMapping.eth_mdio >= 0; } From 8b05bd22b53df9d8ae6e6b9772ed481a5c040fee Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Fri, 27 Sep 2024 18:27:26 +0200 Subject: [PATCH 18/58] Take care of different signature of ETH.begin method in Arduino Core 3.x --- src/NetworkSettings.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index 8e0322f9c..33de9255d 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -37,7 +37,11 @@ void NetworkSettingsClass::init(Scheduler& scheduler) _w5500 = std::make_unique(pin.w5500_mosi, pin.w5500_miso, pin.w5500_sclk, pin.w5500_cs, pin.w5500_int, pin.w5500_rst); } else if (PinMapping.isValidEthConfig()) { PinMapping_t& pin = PinMapping.get(); +#if ESP_ARDUINO_VERSION_MAJOR < 3 ETH.begin(pin.eth_phy_addr, pin.eth_power, pin.eth_mdc, pin.eth_mdio, pin.eth_type, pin.eth_clk_mode); +#else + ETH.begin(pin.eth_type, pin.eth_phy_addr, pin.eth_mdc, pin.eth_mdio, pin.eth_power, pin.eth_clk_mode); +#endif } setupMode(); From 0fcf6061c1732db94809c16bc2296d2cad9b2289 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Fri, 27 Sep 2024 18:30:44 +0200 Subject: [PATCH 19/58] Added required include to work with IDF 5 --- include/W5500.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/W5500.h b/include/W5500.h index fc0c66148..f62c33126 100644 --- a/include/W5500.h +++ b/include/W5500.h @@ -2,6 +2,7 @@ #pragma once #include +#include // required for esp_eth_handle_t #include class W5500 { From d758a347ebfc49a0a6be1ff55015efd35ec8c745 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Fri, 27 Sep 2024 19:36:52 +0200 Subject: [PATCH 20/58] Update espressif32 from 6.8.1 to 6.9.0 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index f9410c912..bc81b1a7d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,7 @@ extra_configs = custom_ci_action = generic,generic_esp32,generic_esp32s3,generic_esp32s3_usb framework = arduino -platform = espressif32@6.8.1 +platform = espressif32@6.9.0 platform_packages = platformio/tool-mklittlefs From 759f899620beeb5fb071793e730880439584b774 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Sat, 28 Sep 2024 00:50:57 +0200 Subject: [PATCH 21/58] webapp: Update dependencies --- webapp/package.json | 6 +- webapp/yarn.lock | 140 ++++++++++++++++++++++---------------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index ac5161ffd..908058cc5 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -19,7 +19,7 @@ "mitt": "^3.0.1", "sortablejs": "^1.15.3", "spark-md5": "^3.0.2", - "vue": "^3.5.9", + "vue": "^3.5.10", "vue-i18n": "9.13.1", "vue-router": "^4.4.5" }, @@ -27,7 +27,7 @@ "@intlify/unplugin-vue-i18n": "^4.0.0", "@tsconfig/node22": "^22.0.0", "@types/bootstrap": "^5.2.10", - "@types/node": "^22.7.2", + "@types/node": "^22.7.4", "@types/pulltorefreshjs": "^0.1.7", "@types/sortablejs": "^1.15.8", "@types/spark-md5": "^3.0.4", @@ -44,7 +44,7 @@ "typescript": "^5.6.2", "vite": "^5.4.8", "vite-plugin-compression": "^0.5.1", - "vite-plugin-css-injected-by-js": "^3.5.1", + "vite-plugin-css-injected-by-js": "^3.5.2", "vue-tsc": "^2.1.6" }, "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" diff --git a/webapp/yarn.lock b/webapp/yarn.lock index f804bf6de..9d68694e9 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -507,10 +507,10 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== -"@types/node@^22.7.2": - version "22.7.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.2.tgz#80ed66c0a5025ffa037587fd69a816f29b54e4c7" - integrity sha512-866lXSrpGpgyHBZUa2m9YNWqHDjjM0aBTJlNtYaGEw4rqY/dcD7deRVTbBBAJelfA7oaGDbNftXF/TL/A6RgoA== +"@types/node@^22.7.4": + version "22.7.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.4.tgz#e35d6f48dca3255ce44256ddc05dee1c23353fcc" + integrity sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg== dependencies: undici-types "~6.19.2" @@ -667,13 +667,13 @@ estree-walker "^2.0.2" source-map-js "^1.0.2" -"@vue/compiler-core@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.9.tgz#d51fbfe6c18479b27fe6b1723344ba0832e4aacb" - integrity sha512-KE1sCdwqSKq0CQ/ltg3XnlMTKeinjegIkuFsuq9DKvNPmqLGdmI51ChZdGBBRXIvEYTLm8X/JxOuBQ1HqF/+PA== +"@vue/compiler-core@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.10.tgz#dc382e4173c5ad6d309887f5cb02983dfd88cfee" + integrity sha512-iXWlk+Cg/ag7gLvY0SfVucU8Kh2CjysYZjhhP70w9qI4MvSox4frrP+vDGvtQuzIcgD8+sxM6lZvCtdxGunTAA== dependencies: "@babel/parser" "^7.25.3" - "@vue/shared" "3.5.9" + "@vue/shared" "3.5.10" entities "^4.5.0" estree-walker "^2.0.2" source-map-js "^1.2.0" @@ -686,13 +686,13 @@ "@vue/compiler-core" "3.2.47" "@vue/shared" "3.2.47" -"@vue/compiler-dom@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.9.tgz#6fa2b7e536ae4c416fc2d60b7e9e33b3410eac7a" - integrity sha512-gEAURwPo902AsJF50vl59VaWR+Cx6cX9SoqLYHu1jq9hDbmQlXvpZyYNIIbxa2JTJ+FD/oBQweVUwuTQv79KTg== +"@vue/compiler-dom@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.10.tgz#233c660289ce289a48e8fe759b07b95f607cd98e" + integrity sha512-DyxHC6qPcktwYGKOIy3XqnHRrrXyWR2u91AjP+nLkADko380srsC2DC3s7Y1Rk6YfOlxOlvEQKa9XXmLI+W4ZA== dependencies: - "@vue/compiler-core" "3.5.9" - "@vue/shared" "3.5.9" + "@vue/compiler-core" "3.5.10" + "@vue/shared" "3.5.10" "@vue/compiler-dom@^3.4.0": version "3.4.21" @@ -702,16 +702,16 @@ "@vue/compiler-core" "3.4.21" "@vue/shared" "3.4.21" -"@vue/compiler-sfc@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.9.tgz#020b7654f1fde7c606a49ec4e4d2838e8e1a43c5" - integrity sha512-kp9qawcTXakYm0TN6YAwH24IurSywoXh4fWhRbLu0at4UVyo994bhEzJlQn82eiyqtut4GjkQodSfn8drFbpZQ== +"@vue/compiler-sfc@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.10.tgz#95e262a5ed836521a5aeee9492cc265ad3f1c787" + integrity sha512-to8E1BgpakV7224ZCm8gz1ZRSyjNCAWEplwFMWKlzCdP9DkMKhRRwt0WkCjY7jkzi/Vz3xgbpeig5Pnbly4Tow== dependencies: "@babel/parser" "^7.25.3" - "@vue/compiler-core" "3.5.9" - "@vue/compiler-dom" "3.5.9" - "@vue/compiler-ssr" "3.5.9" - "@vue/shared" "3.5.9" + "@vue/compiler-core" "3.5.10" + "@vue/compiler-dom" "3.5.10" + "@vue/compiler-ssr" "3.5.10" + "@vue/shared" "3.5.10" estree-walker "^2.0.2" magic-string "^0.30.11" postcss "^8.4.47" @@ -741,13 +741,13 @@ "@vue/compiler-dom" "3.2.47" "@vue/shared" "3.2.47" -"@vue/compiler-ssr@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.9.tgz#e30f8e866589392421abcbfc0e0241470f3ca9a6" - integrity sha512-fb1g2mQv32QzIei76rlXRTz08Grw+ZzBXSQfHo4StGFutm/flyebw3dGJkexKwcU3GjX9s5fIGjEv/cjO8j8Yw== +"@vue/compiler-ssr@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.10.tgz#195f83ae7c52174be37fd7a4a0217132c1c0ed11" + integrity sha512-hxP4Y3KImqdtyUKXDRSxKSRkSm1H9fCvhojEYrnaoWhE4w/y8vwWhnosJoPPe2AXm5sU7CSbYYAgkt2ZPhDz+A== dependencies: - "@vue/compiler-dom" "3.5.9" - "@vue/shared" "3.5.9" + "@vue/compiler-dom" "3.5.10" + "@vue/shared" "3.5.10" "@vue/compiler-vue2@^2.7.16": version "2.7.16" @@ -801,38 +801,38 @@ estree-walker "^2.0.2" magic-string "^0.25.7" -"@vue/reactivity@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.9.tgz#8864a55e4c495666f3c679beb8f734489eeb042e" - integrity sha512-88ApgNZ6yPYpyYkTfXzcbWk6O8+LrPRIpa/U4AdeTzpfRUO+EUt5jemnTBVSlAUNmlYY96xa5feUNEq+BouLog== +"@vue/reactivity@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.10.tgz#81140ef0b05096973356d3c8fc32f48c79940b9c" + integrity sha512-kW08v06F6xPSHhid9DJ9YjOGmwNDOsJJQk0ax21wKaUYzzuJGEuoKNU2Ujux8FLMrP7CFJJKsHhXN9l2WOVi2g== dependencies: - "@vue/shared" "3.5.9" + "@vue/shared" "3.5.10" -"@vue/runtime-core@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.9.tgz#e47f890734039f77dac86328cc059cf8188c5729" - integrity sha512-YAeP0zNkjSl5mEc1NxOg9qoAhLNbREElHAhfYbMXT57oF0ixehEEJWBhg2uvVxslCGh23JhpEAyMvJrJHW9WGg== +"@vue/runtime-core@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.10.tgz#e902eb2640fa6ab4cc4589af263818a898812668" + integrity sha512-9Q86I5Qq3swSkFfzrZ+iqEy7Vla325M7S7xc1NwKnRm/qoi1Dauz0rT6mTMmscqx4qz0EDJ1wjB+A36k7rl8mA== dependencies: - "@vue/reactivity" "3.5.9" - "@vue/shared" "3.5.9" + "@vue/reactivity" "3.5.10" + "@vue/shared" "3.5.10" -"@vue/runtime-dom@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.9.tgz#088746207f74963d09b31ce7b79add0bf96aa337" - integrity sha512-5Oq/5oenpB9lw94moKvOHqBDEaMSyDmcu2HS8AtAT6/pwdo/t9fR9aVtLh6FzYGGqZR9yRfoHAN6P7goblq1aA== +"@vue/runtime-dom@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.10.tgz#dca26d7761147373c6929f1370cf2733aa19f3de" + integrity sha512-t3x7ht5qF8ZRi1H4fZqFzyY2j+GTMTDxRheT+i8M9Ph0oepUxoadmbwlFwMoW7RYCpNQLpP2Yx3feKs+fyBdpA== dependencies: - "@vue/reactivity" "3.5.9" - "@vue/runtime-core" "3.5.9" - "@vue/shared" "3.5.9" + "@vue/reactivity" "3.5.10" + "@vue/runtime-core" "3.5.10" + "@vue/shared" "3.5.10" csstype "^3.1.3" -"@vue/server-renderer@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.9.tgz#3bf0736001623960d120ef01dee5045fad6efadb" - integrity sha512-tbuUsZfMWGazR9LXLNiiDSTwkO8K9sLyR70diY+FbQmKmh7236PPz4jkTxymelV8D89IJUGtbfe4VdmpHkmuxg== +"@vue/server-renderer@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.10.tgz#90462492c30c8cae499b9149d1b90af2ebfe7599" + integrity sha512-IVE97tt2kGKwHNq9yVO0xdh1IvYfZCShvDSy46JIh5OQxP1/EXSpoDqetVmyIzL7CYOWnnmMkVqd7YK2QSWkdw== dependencies: - "@vue/compiler-ssr" "3.5.9" - "@vue/shared" "3.5.9" + "@vue/compiler-ssr" "3.5.10" + "@vue/shared" "3.5.10" "@vue/shared@3.2.47": version "3.2.47" @@ -844,10 +844,10 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.21.tgz#de526a9059d0a599f0b429af7037cd0c3ed7d5a1" integrity sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g== -"@vue/shared@3.5.9": - version "3.5.9" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.9.tgz#713257216ea2cbf4e200cb9ae395c34ae2349385" - integrity sha512-8wiT/m0mnsLhTME0mPgc57jv+4TipRBSAAmheUdYgiOaO6AobZPNOmm87ub4np65VVDgLcWxc+Edc++5Wyz1uA== +"@vue/shared@3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.10.tgz#066f7dde31e09d700123e92e63eaa126cda21a17" + integrity sha512-VkkBhU97Ki+XJ0xvl4C9YJsIZ2uIlQ7HqPpZOS3m9VCvmROPaChZU6DexdMJqvz9tbgG+4EtFVrSuailUq5KGQ== "@vue/tsconfig@^0.5.1": version "0.5.1" @@ -2619,10 +2619,10 @@ vite-plugin-compression@^0.5.1: debug "^4.3.3" fs-extra "^10.0.0" -vite-plugin-css-injected-by-js@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.1.tgz#b9c568c21b131d08e31aa6d368ee39c9d6c1b6c1" - integrity sha512-9ioqwDuEBxW55gNoWFEDhfLTrVKXEEZgl5adhWmmqa88EQGKfTmexy4v1Rh0pAS6RhKQs2bUYQArprB32JpUZQ== +vite-plugin-css-injected-by-js@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.5.2.tgz#1f75d16ad5c05b6b49bf18018099a189ec2e46ad" + integrity sha512-2MpU/Y+SCZyWUB6ua3HbJCrgnF0KACAsmzOQt1UvRVJCGF6S8xdA3ZUhWcWdM9ivG4I5az8PnQmwwrkC2CAQrQ== vite@^5.4.8: version "5.4.8" @@ -2691,16 +2691,16 @@ vue-tsc@^2.1.6: "@vue/language-core" "2.1.6" semver "^7.5.4" -vue@^3.5.9: - version "3.5.9" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.9.tgz#a065952d7a7c0e2cbfec8e016582b055ab984357" - integrity sha512-nHzQhZ5cjFKynAY2beAm7XtJ5C13VKAFTLTgRYXy+Id1KEKBeiK6hO2RcW1hUjdbHMadz1YzxyHgQigOC54wug== +vue@^3.5.10: + version "3.5.10" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.10.tgz#14be9d4655e07be8d5e8295d017815ed14337f96" + integrity sha512-Vy2kmJwHPlouC/tSnIgXVg03SG+9wSqT1xu1Vehc+ChsXsRd7jLkKgMltVEFOzUdBr3uFwBCG+41LJtfAcBRng== dependencies: - "@vue/compiler-dom" "3.5.9" - "@vue/compiler-sfc" "3.5.9" - "@vue/runtime-dom" "3.5.9" - "@vue/server-renderer" "3.5.9" - "@vue/shared" "3.5.9" + "@vue/compiler-dom" "3.5.10" + "@vue/compiler-sfc" "3.5.10" + "@vue/runtime-dom" "3.5.10" + "@vue/server-renderer" "3.5.10" + "@vue/shared" "3.5.10" webpack-sources@^3.2.3: version "3.2.3" From b206cee820d610bf6b33d49f1f0f82933e64a62d Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Sat, 28 Sep 2024 00:52:28 +0200 Subject: [PATCH 22/58] webapp: add app.js.gz --- webapp_dist/js/app.js.gz | Bin 186480 -> 186504 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/webapp_dist/js/app.js.gz b/webapp_dist/js/app.js.gz index 35a47f59f398217885b8a6c5ae00083c2d5cff70..b25783cac7401b735a79162fe5146d5fb55a3bd0 100644 GIT binary patch delta 130341 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

f{VmSbz2MJ<-z#hS}vLneoSYGKM7!R$+zU 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
ADcK1cr44+oBp} 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 From 6f9ded5f20d9b5f87f091f2f18451f301ad35db6 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Thu, 26 Sep 2024 21:45:43 +0200 Subject: [PATCH 23/58] issue template: fix typo --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d41f98240..8f3579300 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -48,7 +48,7 @@ body: description: How did you install OpenDTU? options: - Pre-Compiled binary from GitHub releases - - Pre-Compiles binary from GitHub actions/pull-request + - Pre-Compiled binary from GitHub actions/pull-request - Self-Compiled validations: required: true From 251bb7bd8933bf7359494fad652824a9cb7dc8f5 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:31:15 +0200 Subject: [PATCH 24/58] Add connection check for W5500 before full initialization --- include/W5500.h | 11 +++- src/NetworkSettings.cpp | 6 ++- src/W5500.cpp | 117 +++++++++++++++++++++++++++------------- 3 files changed, 94 insertions(+), 40 deletions(-) diff --git a/include/W5500.h b/include/W5500.h index f62c33126..d85cb016c 100644 --- a/include/W5500.h +++ b/include/W5500.h @@ -2,19 +2,28 @@ #pragma once #include +#include #include // required for esp_eth_handle_t #include +#include + class W5500 { +private: + explicit W5500(spi_device_handle_t spi, gpio_num_t pin_int); + public: - explicit W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst); W5500(const W5500&) = delete; W5500& operator=(const W5500&) = delete; ~W5500(); + static std::unique_ptr setup(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst); String macAddress(); private: + static bool connection_check_spi(spi_device_handle_t spi); + static bool connection_check_interrupt(gpio_num_t pin_int); + esp_eth_handle_t eth_handle; esp_netif_t* eth_netif; }; diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index 33de9255d..75c4bb12a 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -34,7 +34,11 @@ void NetworkSettingsClass::init(Scheduler& scheduler) if (PinMapping.isValidW5500Config()) { PinMapping_t& pin = PinMapping.get(); - _w5500 = std::make_unique(pin.w5500_mosi, pin.w5500_miso, pin.w5500_sclk, pin.w5500_cs, pin.w5500_int, pin.w5500_rst); + _w5500 = W5500::setup(pin.w5500_mosi, pin.w5500_miso, pin.w5500_sclk, pin.w5500_cs, pin.w5500_int, pin.w5500_rst); + if (_w5500) + MessageOutput.println("W5500: Connection successful"); + else + MessageOutput.println("W5500: Connection error!!"); } else if (PinMapping.isValidEthConfig()) { PinMapping_t& pin = PinMapping.get(); #if ESP_ARDUINO_VERSION_MAJOR < 3 diff --git a/src/W5500.cpp b/src/W5500.cpp index 0f5b57398..fc770c447 100644 --- a/src/W5500.cpp +++ b/src/W5500.cpp @@ -12,9 +12,51 @@ void tcpipInit(); void add_esp_interface_netif(esp_interface_t interface, esp_netif_t* esp_netif); -W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) +W5500::W5500(spi_device_handle_t spi, gpio_num_t pin_int) : eth_handle(nullptr) , eth_netif(nullptr) +{ + // 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(ð_config, ð_handle)); + + // Configure MAC address + uint8_t mac_addr[6]; + ESP_ERROR_CHECK(esp_read_mac(mac_addr, ESP_MAC_ETH)); + 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)); +} + +W5500::~W5500() +{ + // TODO(LennartF22): support cleanup at some point? +} + +std::unique_ptr W5500::setup(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, int8_t pin_int, int8_t pin_rst) { gpio_reset_pin(static_cast(pin_rst)); gpio_set_level(static_cast(pin_rst), 0); @@ -51,51 +93,22 @@ W5500::W5500(int8_t pin_mosi, int8_t pin_miso, int8_t pin_sclk, int8_t pin_cs, i spi_device_handle_t spi = SpiManagerInst.alloc_device("", bus_config, device_config); if (!spi) - ESP_ERROR_CHECK(ESP_FAIL); + return nullptr; // Reset sequence delayMicroseconds(500); gpio_set_level(static_cast(pin_rst), 1); delayMicroseconds(1000); - // Arduino function to start networking stack if not already started - tcpipInit(); + if (!connection_check_spi(spi)) + return nullptr; + if (!connection_check_interrupt(static_cast(pin_int))) + return nullptr; - 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(ð_config, ð_handle)); - - // Configure MAC address - uint8_t mac_addr[6]; - ESP_ERROR_CHECK(esp_read_mac(mac_addr, ESP_MAC_ETH)); - 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); + // Return to default state once again after connection check + gpio_reset_pin(static_cast(pin_int)); - ESP_ERROR_CHECK(esp_eth_start(eth_handle)); -} - -W5500::~W5500() -{ - // TODO(LennartF22): support cleanup at some point? + return std::unique_ptr(new W5500(spi, static_cast(pin_int))); } String W5500::macAddress() @@ -109,3 +122,31 @@ String W5500::macAddress() mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); return String(mac_addr_str); } + +bool W5500::connection_check_spi(spi_device_handle_t spi) +{ + spi_transaction_t trans = { + .flags = SPI_TRANS_USE_RXDATA, + .cmd = 0x0039, // actually address (VERSIONR) + .addr = (0b00000 << 3) | (0 << 2) | (0b00 < 0), // actually command (common register, read, VDM) + .length = 8, + .rxlength = 8, + .user = nullptr, + .tx_buffer = nullptr, + .rx_data = {}, + }; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans)); + + // Version number (VERSIONR) is always 0x04 + return *reinterpret_cast(&trans.rx_data) == 0x04; +} + +bool W5500::connection_check_interrupt(gpio_num_t pin_int) +{ + gpio_set_direction(pin_int, GPIO_MODE_INPUT); + gpio_set_pull_mode(pin_int, GPIO_PULLDOWN_ONLY); + int level = gpio_get_level(pin_int); + + // Interrupt line must be high + return level == 1; +} From b05975b97c30fbe4f85270c6b4c47114d6c01305 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Fri, 27 Sep 2024 01:27:42 +0200 Subject: [PATCH 25/58] Prevent warning on GPIO ISR service registration --- src/W5500.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/W5500.cpp b/src/W5500.cpp index fc770c447..bf5394340 100644 --- a/src/W5500.cpp +++ b/src/W5500.cpp @@ -65,10 +65,6 @@ std::unique_ptr W5500::setup(int8_t pin_mosi, int8_t pin_miso, int8_t pin gpio_reset_pin(static_cast(pin_cs)); gpio_reset_pin(static_cast(pin_int)); - esp_err_t err = gpio_install_isr_service(ARDUINO_ISR_FLAG); - if (err != ESP_ERR_INVALID_STATE) // don't raise an error when ISR service is already installed - ESP_ERROR_CHECK(err); - auto bus_config = std::make_shared( static_cast(pin_mosi), static_cast(pin_miso), @@ -105,7 +101,12 @@ std::unique_ptr W5500::setup(int8_t pin_mosi, int8_t pin_miso, int8_t pin if (!connection_check_interrupt(static_cast(pin_int))) return nullptr; - // Return to default state once again after connection check + // Use Arduino functions to temporarily attach interrupt to enable the GPIO ISR service + // (if we used ESP-IDF functions, a warning would be printed the first time anyone uses attachInterrupt) + attachInterrupt(pin_int, nullptr, FALLING); + detachInterrupt(pin_int); + + // Return to default state once again after connection check and temporary interrupt registration gpio_reset_pin(static_cast(pin_int)); return std::unique_ptr(new W5500(spi, static_cast(pin_int))); From cafdb305a3729da4409779701043b37d9d7cf1de Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:37:09 +0200 Subject: [PATCH 26/58] Adjust name of OpenDTU Fusion v2 PoE build environment --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index bc81b1a7d..821112680 100644 --- a/platformio.ini +++ b/platformio.ini @@ -254,7 +254,7 @@ build_flags = ${env.build_flags} -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -[env:opendtufusionv2_shield] +[env:opendtufusionv2_poe] board = esp32-s3-devkitc-1 upload_protocol = esp-builtin debug_tool = esp-builtin From 69d2727106f183ecad6b6786a45d51e734cf60e6 Mon Sep 17 00:00:00 2001 From: LennartF22 <18723691+LennartF22@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:42:05 +0200 Subject: [PATCH 27/58] Add device profiles for OpenDTU Fusion v2 PoE with displays --- docs/DeviceProfiles/opendtu_fusion.json | 78 +++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/docs/DeviceProfiles/opendtu_fusion.json b/docs/DeviceProfiles/opendtu_fusion.json index bb1e41089..990f4c46b 100644 --- a/docs/DeviceProfiles/opendtu_fusion.json +++ b/docs/DeviceProfiles/opendtu_fusion.json @@ -243,5 +243,83 @@ "data": 2, "clk": 1 } + }, + { + "name": "OpenDTU Fusion v2 PoE with SH1106 Display", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], + "nrf24": { + "miso": 48, + "mosi": 35, + "clk": 36, + "irq": 47, + "en": 38, + "cs": 37 + }, + "cmt": { + "clk": 6, + "cs": 4, + "fcs": 21, + "sdio": 5, + "gpio2": 3, + "gpio3": 8 + }, + "w5500": { + "mosi": 40, + "miso": 41, + "sclk": 39, + "cs": 42, + "int": 44, + "rst": 43 + }, + "led": { + "led0": 17, + "led1": 18 + }, + "display": { + "type": 3, + "data": 2, + "clk": 1 + } + }, + { + "name": "OpenDTU Fusion v2 PoE with SSD1306 Display", + "links": [ + {"name": "Information", "url": "https://github.com/markusdd/OpenDTUFusionDocs"} + ], + "nrf24": { + "miso": 48, + "mosi": 35, + "clk": 36, + "irq": 47, + "en": 38, + "cs": 37 + }, + "cmt": { + "clk": 6, + "cs": 4, + "fcs": 21, + "sdio": 5, + "gpio2": 3, + "gpio3": 8 + }, + "w5500": { + "mosi": 40, + "miso": 41, + "sclk": 39, + "cs": 42, + "int": 44, + "rst": 43 + }, + "led": { + "led0": 17, + "led1": 18 + }, + "display": { + "type": 2, + "data": 2, + "clk": 1 + } } ] From ebb225f6c063e1946739cab7674c319a98d66784 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Mon, 30 Sep 2024 14:18:33 +0200 Subject: [PATCH 28/58] Fix: avoid deprecated setAuthentication() to fix memory exhaustion with ESPAsyncWebServer 3.3.0, the setAuthentication() method became deprecated and a replacement method was provided which acts as a shim and uses the new middleware-based approach to setup authentication. in order to eventually apply a changed "read-only access allowed" setting, the setAuthentication() method was called periodically. the shim implementation each time allocates a new AuthenticationMiddleware and adds it to the chain of middlewares, eventually exhausting the memory. we now use the new middleware-based approach ourselves and only add the respective AuthenticatonMiddleware instance once to the respective websocket server instance. a regression where enabling unauthenticated read-only access is not applied until reboot is also fixed. all the AuthenticationMiddleware instances were never removed from the chain of middlewares when calling setAuthentication("", ""). --- include/WebApi.h | 1 + include/WebApi_ws_console.h | 2 ++ include/WebApi_ws_live.h | 2 ++ src/WebApi.cpp | 6 ++++++ src/WebApi_security.cpp | 2 ++ src/WebApi_ws_console.cpp | 23 +++++++++++++++++------ src/WebApi_ws_live.cpp | 22 ++++++++++++++++------ 7 files changed, 46 insertions(+), 12 deletions(-) diff --git a/include/WebApi.h b/include/WebApi.h index b6fdbd089..2932f015a 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -30,6 +30,7 @@ class WebApiClass { public: WebApiClass(); void init(Scheduler& scheduler); + void reload(); static bool checkCredentials(AsyncWebServerRequest* request); static bool checkCredentialsReadonly(AsyncWebServerRequest* request); diff --git a/include/WebApi_ws_console.h b/include/WebApi_ws_console.h index cf7beecce..b3194319d 100644 --- a/include/WebApi_ws_console.h +++ b/include/WebApi_ws_console.h @@ -8,9 +8,11 @@ class WebApiWsConsoleClass { public: WebApiWsConsoleClass(); void init(AsyncWebServer& server, Scheduler& scheduler); + void reload(); private: AsyncWebSocket _ws; + AuthenticationMiddleware _simpleDigestAuth; Task _wsCleanupTask; void wsCleanupTaskCb(); diff --git a/include/WebApi_ws_live.h b/include/WebApi_ws_live.h index 05f8ab8f9..e16372e92 100644 --- a/include/WebApi_ws_live.h +++ b/include/WebApi_ws_live.h @@ -11,6 +11,7 @@ class WebApiWsLiveClass { public: WebApiWsLiveClass(); void init(AsyncWebServer& server, Scheduler& scheduler); + void reload(); private: static void generateInverterCommonJsonResponse(JsonObject& root, std::shared_ptr inv); @@ -24,6 +25,7 @@ class WebApiWsLiveClass { void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); AsyncWebSocket _ws; + AuthenticationMiddleware _simpleDigestAuth; uint32_t _lastPublishStats[INV_MAX_COUNT] = { 0 }; diff --git a/src/WebApi.cpp b/src/WebApi.cpp index 1a5b28709..117c305aa 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -39,6 +39,12 @@ void WebApiClass::init(Scheduler& scheduler) _server.begin(); } +void WebApiClass::reload() +{ + _webApiWsConsole.reload(); + _webApiWsLive.reload(); +} + bool WebApiClass::checkCredentials(AsyncWebServerRequest* request) { CONFIG_T& config = Configuration.get(); diff --git a/src/WebApi_security.cpp b/src/WebApi_security.cpp index ddd8bb507..6be21ca6a 100644 --- a/src/WebApi_security.cpp +++ b/src/WebApi_security.cpp @@ -71,6 +71,8 @@ void WebApiSecurityClass::onSecurityPost(AsyncWebServerRequest* request) WebApi.writeConfig(retMsg); WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); + + WebApi.reload(); } void WebApiSecurityClass::onAuthenticateGet(AsyncWebServerRequest* request) diff --git a/src/WebApi_ws_console.cpp b/src/WebApi_ws_console.cpp index 1f1efcb20..4dd2d6933 100644 --- a/src/WebApi_ws_console.cpp +++ b/src/WebApi_ws_console.cpp @@ -21,16 +21,27 @@ void WebApiWsConsoleClass::init(AsyncWebServer& server, Scheduler& scheduler) scheduler.addTask(_wsCleanupTask); _wsCleanupTask.enable(); + + _simpleDigestAuth.setUsername(AUTH_USERNAME); + _simpleDigestAuth.setRealm("console websocket"); + + reload(); +} + +void WebApiWsConsoleClass::reload() +{ + _ws.removeMiddleware(&_simpleDigestAuth); + + auto const& config = Configuration.get(); + + if (config.Security.AllowReadonly) { return; } + + _simpleDigestAuth.setPassword(config.Security.Password); + _ws.addMiddleware(&_simpleDigestAuth); } void WebApiWsConsoleClass::wsCleanupTaskCb() { // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients _ws.cleanupClients(); - - if (Configuration.get().Security.AllowReadonly) { - _ws.setAuthentication("", ""); - } else { - _ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security.Password); - } } diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 4fa798373..c4b4a1f19 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -36,18 +36,28 @@ void WebApiWsLiveClass::init(AsyncWebServer& server, Scheduler& scheduler) scheduler.addTask(_sendDataTask); _sendDataTask.enable(); + _simpleDigestAuth.setUsername(AUTH_USERNAME); + _simpleDigestAuth.setRealm("live websocket"); + + reload(); +} + +void WebApiWsLiveClass::reload() +{ + _ws.removeMiddleware(&_simpleDigestAuth); + + auto const& config = Configuration.get(); + + if (config.Security.AllowReadonly) { return; } + + _simpleDigestAuth.setPassword(config.Security.Password); + _ws.addMiddleware(&_simpleDigestAuth); } void WebApiWsLiveClass::wsCleanupTaskCb() { // see: https://github.com/me-no-dev/ESPAsyncWebServer#limiting-the-number-of-web-socket-clients _ws.cleanupClients(); - - if (Configuration.get().Security.AllowReadonly) { - _ws.setAuthentication("", ""); - } else { - _ws.setAuthentication(AUTH_USERNAME, Configuration.get().Security.Password); - } } void WebApiWsLiveClass::sendDataTaskCb() From d5d1a9982fa076e5ec69bd7e1dcc4a768bd5f192 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Mon, 30 Sep 2024 15:53:30 +0200 Subject: [PATCH 29/58] Fix: force websocket clients to authenticate when changing the security settings (disabling read-only access or changing the password), existing websocket connections are now closed, forcing the respective clients to authenticate (with the new password). otherwise, existing websocket clients keep connected even though the security settings now expect authentication with a (changed) password. --- src/WebApi_ws_console.cpp | 3 +++ src/WebApi_ws_live.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/WebApi_ws_console.cpp b/src/WebApi_ws_console.cpp index 4dd2d6933..51035f6fa 100644 --- a/src/WebApi_ws_console.cpp +++ b/src/WebApi_ws_console.cpp @@ -36,8 +36,11 @@ void WebApiWsConsoleClass::reload() if (config.Security.AllowReadonly) { return; } + _ws.enable(false); _simpleDigestAuth.setPassword(config.Security.Password); _ws.addMiddleware(&_simpleDigestAuth); + _ws.closeAll(); + _ws.enable(true); } void WebApiWsConsoleClass::wsCleanupTaskCb() diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index c4b4a1f19..29c204a3f 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -50,8 +50,11 @@ void WebApiWsLiveClass::reload() if (config.Security.AllowReadonly) { return; } + _ws.enable(false); _simpleDigestAuth.setPassword(config.Security.Password); _ws.addMiddleware(&_simpleDigestAuth); + _ws.closeAll(); + _ws.enable(true); } void WebApiWsLiveClass::wsCleanupTaskCb() From 99a37fe01c5ef94c4638421a3af010908717d72a Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 30 Sep 2024 18:47:41 +0200 Subject: [PATCH 30/58] webapp: Update dependencies --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 908058cc5..5f954fcec 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -40,7 +40,7 @@ "prettier": "^3.3.3", "pulltorefreshjs": "^0.1.22", "sass": "^1.77.6", - "terser": "^5.34.0", + "terser": "^5.34.1", "typescript": "^5.6.2", "vite": "^5.4.8", "vite-plugin-compression": "^0.5.1", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 9d68694e9..b6569670a 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2506,10 +2506,10 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -terser@^5.34.0: - version "5.34.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.34.0.tgz#62f2496542290bc6d8bf886edaee7fac158e37e4" - integrity sha512-y5NUX+U9HhVsK/zihZwoq4r9dICLyV2jXGOriDAVOeKhq3LKVjgJbGO90FisozXLlJfvjHqgckGmJFBb9KYoWQ== +terser@^5.34.1: + version "5.34.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.34.1.tgz#af40386bdbe54af0d063e0670afd55c3105abeb6" + integrity sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" From a89c1fa45a2dcac3e2356111f256089377efcd83 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Mon, 30 Sep 2024 20:56:57 +0200 Subject: [PATCH 31/58] Revert "Fix: device profile for OpenDTU Fusion with W5500 (#1259)" This reverts commit 27f5a943f6cb6bcc2a796b5261f1638192ced7d6. --- docs/DeviceProfiles/opendtu_fusion.json | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/docs/DeviceProfiles/opendtu_fusion.json b/docs/DeviceProfiles/opendtu_fusion.json index b74058a44..cf20772da 100644 --- a/docs/DeviceProfiles/opendtu_fusion.json +++ b/docs/DeviceProfiles/opendtu_fusion.json @@ -197,18 +197,6 @@ "en": 38, "cs": 37 }, - "cmt": { - "clk": -1, - "cs": -1, - "fcs": -1, - "sdio": -1, - "gpio2": -1, - "gpio3": -1 - }, - "led": { - "led0": 17, - "led1": 18 - }, "w5500": { "sclk": 39, "mosi": 40, @@ -220,14 +208,6 @@ }, { "name": "OpenDTU Fusion v2 with CMT2300A and W5500 ethernet", - "nrf24": { - "miso": -1, - "mosi": -1, - "clk": -1, - "irq": -1, - "en": -1, - "cs": -1 - }, "cmt": { "clk": 6, "cs": 4, @@ -236,10 +216,6 @@ "gpio2": 3, "gpio3": 8 }, - "led": { - "led0": 17, - "led1": 18 - }, "w5500": { "sclk": 39, "mosi": 40, From 0a289bbcabf37a4d0a137461fcc0ac78eae3275d Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Mon, 30 Sep 2024 20:59:22 +0200 Subject: [PATCH 32/58] Revert "Feature: Support for W5500 ethernet module (#1231)" This reverts commit 89d9a40296711ecc9904f4930dfd2fc508ec8238. --- .github/workflows/cpplint.yml | 2 +- docs/DeviceProfiles/opendtu_fusion.json | 40 +------- include/NetworkSettings.h | 1 - include/PinMapping.h | 8 -- lib/ETHSPI/src/ETHSPI.cpp | 127 ------------------------ lib/ETHSPI/src/ETHSPI.h | 26 ----- src/NetworkSettings.cpp | 26 +---- src/PinMapping.cpp | 48 --------- src/WebApi_device.cpp | 8 -- 9 files changed, 7 insertions(+), 279 deletions(-) delete mode 100644 lib/ETHSPI/src/ETHSPI.cpp delete mode 100644 lib/ETHSPI/src/ETHSPI.h diff --git a/.github/workflows/cpplint.yml b/.github/workflows/cpplint.yml index f8d6847ab..8dfba9f25 100644 --- a/.github/workflows/cpplint.yml +++ b/.github/workflows/cpplint.yml @@ -22,4 +22,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 ./lib/ETHSPI + 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 diff --git a/docs/DeviceProfiles/opendtu_fusion.json b/docs/DeviceProfiles/opendtu_fusion.json index cf20772da..f33dc47db 100644 --- a/docs/DeviceProfiles/opendtu_fusion.json +++ b/docs/DeviceProfiles/opendtu_fusion.json @@ -186,43 +186,5 @@ "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 - } } -] +] \ No newline at end of file diff --git a/include/NetworkSettings.h b/include/NetworkSettings.h index 7c0db3589..433867e97 100644 --- a/include/NetworkSettings.h +++ b/include/NetworkSettings.h @@ -83,7 +83,6 @@ class NetworkSettingsClass { bool _ethConnected = false; std::vector _cbEventList; bool _lastMdnsEnabled = false; - bool _spiEth = false; }; extern NetworkSettingsClass NetworkSettings; diff --git a/include/PinMapping.h b/include/PinMapping.h index 0603b4c4d..8b35ac718 100644 --- a/include/PinMapping.h +++ b/include/PinMapping.h @@ -26,13 +26,6 @@ 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; @@ -79,7 +72,6 @@ class PinMappingClass { bool isValidNrf24Config() const; bool isValidCmt2300Config() const; - bool isValidW5500Config() const; bool isValidEthConfig() const; bool isValidHuaweiConfig() const; diff --git a/lib/ETHSPI/src/ETHSPI.cpp b/lib/ETHSPI/src/ETHSPI.cpp deleted file mode 100644 index 2e2ff8189..000000000 --- a/lib/ETHSPI/src/ETHSPI.cpp +++ /dev/null @@ -1,127 +0,0 @@ -//----------------------------------------------------------------------------- -// 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 - -// 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(pin_rst)); - gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); - gpio_set_level(static_cast(pin_rst), 0); - - gpio_reset_pin(static_cast(pin_sclk)); - gpio_reset_pin(static_cast(pin_mosi)); - gpio_reset_pin(static_cast(pin_miso)); - gpio_reset_pin(static_cast(pin_cs)); - gpio_set_pull_mode(static_cast(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(pin_int)); - gpio_set_pull_mode(static_cast(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(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(ð_config, ð_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; diff --git a/lib/ETHSPI/src/ETHSPI.h b/lib/ETHSPI/src/ETHSPI.h deleted file mode 100644 index 38174b2fa..000000000 --- a/lib/ETHSPI/src/ETHSPI.h +++ /dev/null @@ -1,26 +0,0 @@ -//----------------------------------------------------------------------------- -// 2024 Ahoy, https://ahoydtu.de -// adapted to OpenDTU-OnBattery -// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed -//----------------------------------------------------------------------------- - -#pragma once - -#include -#include -#include - -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; diff --git a/src/NetworkSettings.cpp b/src/NetworkSettings.cpp index 507a5cc12..2dd54580b 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -8,10 +8,8 @@ #include "SyslogLogger.h" #include "PinMapping.h" #include "Utils.h" -#include "SPIPortManager.h" #include "defaults.h" #include -#include #include #include "__compiled_constants.h" @@ -34,22 +32,6 @@ void NetworkSettingsClass::init(Scheduler& scheduler) WiFi.disconnect(true, true); WiFi.onEvent(std::bind(&NetworkSettingsClass::NetworkEvent, this, _1, _2)); - - 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); - } 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); @@ -189,6 +171,11 @@ 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() @@ -418,9 +405,6 @@ String NetworkSettingsClass::macAddress() const { switch (_networkMode) { case network_mode::Ethernet: - if (_spiEth) { - return ETHSPI.macAddress(); - } return ETH.macAddress(); break; case network_mode::WiFi: diff --git a/src/PinMapping.cpp b/src/PinMapping.cpp index 48ddcd117..1ab17b17d 100644 --- a/src/PinMapping.cpp +++ b/src/PinMapping.cpp @@ -178,30 +178,6 @@ #define POWERMETER_PIN_RXEN -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 - PinMappingClass PinMapping; PinMappingClass::PinMappingClass() @@ -221,13 +197,6 @@ 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 @@ -317,13 +286,6 @@ 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 @@ -397,16 +359,6 @@ 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; diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index 405c1dea1..96864151d 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -50,14 +50,6 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) cmtPinObj["gpio2"] = pin.cmt_gpio2; cmtPinObj["gpio3"] = pin.cmt_gpio3; - auto w5500PinObj = curPin["w5500"].to(); - 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(); ethPinObj["enabled"] = pin.eth_enabled; ethPinObj["phy_addr"] = pin.eth_phy_addr; From cbad181b991143cb30299944956cce26fb80dc48 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Mon, 30 Sep 2024 21:01:47 +0200 Subject: [PATCH 33/58] Revert "Feature: SPIPortManager allows simultaneous use of CMT2300 and Huawei charger" This reverts commit df53f34b51e38a2d07d7096190c902617681e73d. --- include/SPIPortManager.h | 55 -------------------------- lib/CMT2300a/cmt2300a_hal.c | 4 +- lib/CMT2300a/cmt2300a_hal.h | 3 +- lib/CMT2300a/cmt2300wrapper.cpp | 5 +-- lib/CMT2300a/cmt2300wrapper.h | 4 +- lib/CMT2300a/cmt_spi3.c | 16 +++++--- lib/CMT2300a/cmt_spi3.h | 3 +- lib/Hoymiles/src/Hoymiles.cpp | 4 +- lib/Hoymiles/src/Hoymiles.h | 5 +-- lib/Hoymiles/src/HoymilesRadio_CMT.cpp | 4 +- lib/Hoymiles/src/HoymilesRadio_CMT.h | 3 +- src/Huawei_can.cpp | 7 +--- src/InverterSettings.cpp | 41 ++++++++++--------- src/SPIPortManager.cpp | 46 --------------------- src/main.cpp | 3 -- 15 files changed, 49 insertions(+), 154 deletions(-) delete mode 100644 include/SPIPortManager.h delete mode 100644 src/SPIPortManager.cpp diff --git a/include/SPIPortManager.h b/include/SPIPortManager.h deleted file mode 100644 index d3d58896f..000000000 --- a/include/SPIPortManager.h +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#pragma once - -#include -#include -#include -#include - -/** - * SPI# to SPI ID and SPI_HOST mapping - * - * ESP32 - * | SPI # | SPI ID | SPI_HOST | - * | 2 | 2 | 1 | - * | 3 | 3 | 2 | - * - * ESP32-S3 - * | SPI # | SPI ID | SPI_HOST | - * | 2 | 0 | 1 | - * | 3 | 1 | 2 | - * - * ESP32-C3 - * | SPI # | SPI ID | SPI_HOST | - * | 2 | 0 | 1 | - * - */ - -class SPIPortManagerClass { -public: - void init(); - - std::optional allocatePort(std::string const& owner); - void freePort(std::string const& owner); - spi_host_device_t SPIhostNum(uint8_t spi_num); - -private: - // the amount of SPIs available on supported ESP32 chips - #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3 - static size_t constexpr _num_controllers = 4; - #else - static size_t constexpr _num_controllers = 3; - #endif - - #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 - static int8_t constexpr _offset_spi_num = -2; // FSPI=0, HSPI=1 - static int8_t constexpr _offset_spi_host = 1; // SPI1_HOST=0 but not usable, SPI2_HOST=1 and SPI3_HOST=2, first usable is SPI2_HOST - #else - static int8_t constexpr _offset_spi_num = 0; // HSPI=2, VSPI=3 - static int8_t constexpr _offset_spi_host = -1; // SPI1_HOST=0 but not usable, SPI2_HOST=1 and SPI3_HOST=2, first usable is SPI2_HOST - #endif - - std::array _ports = { "" }; -}; - -extern SPIPortManagerClass SPIPortManager; diff --git a/lib/CMT2300a/cmt2300a_hal.c b/lib/CMT2300a/cmt2300a_hal.c index 73f6cab71..7b07d499f 100644 --- a/lib/CMT2300a/cmt2300a_hal.c +++ b/lib/CMT2300a/cmt2300a_hal.c @@ -26,9 +26,9 @@ * @name CMT2300A_InitSpi * @desc Initializes the CMT2300A SPI interface. * *********************************************************/ -void CMT2300A_InitSpi(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed) +void CMT2300A_InitSpi(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed) { - cmt_spi3_init(spi_host, pin_sdio, pin_clk, pin_cs, pin_fcs, spi_speed); + cmt_spi3_init(pin_sdio, pin_clk, pin_cs, pin_fcs, spi_speed); } /*! ******************************************************** diff --git a/lib/CMT2300a/cmt2300a_hal.h b/lib/CMT2300a/cmt2300a_hal.h index 150e10f09..a465b1490 100644 --- a/lib/CMT2300a/cmt2300a_hal.h +++ b/lib/CMT2300a/cmt2300a_hal.h @@ -23,7 +23,6 @@ #include #include -#include #ifdef __cplusplus extern "C" { @@ -37,7 +36,7 @@ extern "C" { #define CMT2300A_GetTickCount() millis() /* ************************************************************************ */ -void CMT2300A_InitSpi(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed); +void CMT2300A_InitSpi(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed); uint8_t CMT2300A_ReadReg(const uint8_t addr); void CMT2300A_WriteReg(const uint8_t addr, const uint8_t dat); diff --git a/lib/CMT2300a/cmt2300wrapper.cpp b/lib/CMT2300a/cmt2300wrapper.cpp index f04bdeea9..016ef56fd 100644 --- a/lib/CMT2300a/cmt2300wrapper.cpp +++ b/lib/CMT2300a/cmt2300wrapper.cpp @@ -7,9 +7,8 @@ #include "cmt2300a_params_860.h" #include "cmt2300a_params_900.h" -CMT2300A::CMT2300A(const spi_host_device_t spi_host, const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t spi_speed) +CMT2300A::CMT2300A(const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t spi_speed) { - _spi_host = spi_host; _pin_sdio = pin_sdio; _pin_clk = pin_clk; _pin_cs = pin_cs; @@ -267,7 +266,7 @@ void CMT2300A::flush_rx(void) bool CMT2300A::_init_pins() { - CMT2300A_InitSpi(_spi_host, _pin_sdio, _pin_clk, _pin_cs, _pin_fcs, _spi_speed); + CMT2300A_InitSpi(_pin_sdio, _pin_clk, _pin_cs, _pin_fcs, _spi_speed); return true; // assuming pins are connected properly } diff --git a/lib/CMT2300a/cmt2300wrapper.h b/lib/CMT2300a/cmt2300wrapper.h index b818d972b..d1639fe9b 100644 --- a/lib/CMT2300a/cmt2300wrapper.h +++ b/lib/CMT2300a/cmt2300wrapper.h @@ -2,7 +2,6 @@ #pragma once #include -#include #define CMT2300A_ONE_STEP_SIZE 2500 // frequency channel step size for fast frequency hopping operation: One step size is 2.5 kHz. #define FH_OFFSET 100 // value * CMT2300A_ONE_STEP_SIZE = channel frequency offset @@ -19,7 +18,7 @@ enum FrequencyBand_t { class CMT2300A { public: - CMT2300A(const spi_host_device_t spi_host, const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t _spi_speed = CMT_SPI_SPEED); + CMT2300A(const uint8_t pin_sdio, const uint8_t pin_clk, const uint8_t pin_cs, const uint8_t pin_fcs, const uint32_t _spi_speed = CMT_SPI_SPEED); bool begin(void); @@ -129,7 +128,6 @@ class CMT2300A { */ bool _init_radio(); - spi_host_device_t _spi_host; int8_t _pin_sdio; int8_t _pin_clk; int8_t _pin_cs; diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c index 7263f4d17..59aad36f7 100644 --- a/lib/CMT2300a/cmt_spi3.c +++ b/lib/CMT2300a/cmt_spi3.c @@ -1,5 +1,6 @@ #include "cmt_spi3.h" #include +#include #include // for esp_rom_gpio_connect_out_signal SemaphoreHandle_t paramLock = NULL; @@ -8,9 +9,14 @@ SemaphoreHandle_t paramLock = NULL; } while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS) #define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock) +// for ESP32 this is the so-called HSPI +// for ESP32-S2/S3/C3 this nomenclature does not really exist anymore, +// it is simply the first externally usable hardware SPI master controller +#define SPI_CMT SPI2_HOST + spi_device_handle_t spi_reg, spi_fifo; -void cmt_spi3_init(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed) +void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed) { paramLock = xSemaphoreCreateMutex(); @@ -37,8 +43,8 @@ void cmt_spi3_init(const spi_host_device_t spi_host, const int8_t pin_sdio, cons .post_cb = NULL, }; - ESP_ERROR_CHECK(spi_bus_initialize(spi_host, &buscfg, SPI_DMA_DISABLED)); - ESP_ERROR_CHECK(spi_bus_add_device(spi_host, &devcfg, &spi_reg)); + ESP_ERROR_CHECK(spi_bus_initialize(SPI_CMT, &buscfg, SPI_DMA_DISABLED)); + ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg, &spi_reg)); // FiFo spi_device_interface_config_t devcfg2 = { @@ -55,9 +61,9 @@ void cmt_spi3_init(const spi_host_device_t spi_host, const int8_t pin_sdio, cons .pre_cb = NULL, .post_cb = NULL, }; - ESP_ERROR_CHECK(spi_bus_add_device(spi_host, &devcfg2, &spi_fifo)); + ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); - esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[spi_host].spid_out, true, false); + esp_rom_gpio_connect_out_signal(pin_sdio, spi_periph_signal[SPI_CMT].spid_out, true, false); delay(100); } diff --git a/lib/CMT2300a/cmt_spi3.h b/lib/CMT2300a/cmt_spi3.h index 5cce47db7..6d3a67b62 100644 --- a/lib/CMT2300a/cmt_spi3.h +++ b/lib/CMT2300a/cmt_spi3.h @@ -2,9 +2,8 @@ #define __CMT_SPI3_H #include -#include -void cmt_spi3_init(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed); +void cmt_spi3_init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const uint32_t spi_speed); void cmt_spi3_write(const uint8_t addr, const uint8_t dat); uint8_t cmt_spi3_read(const uint8_t addr); diff --git a/lib/Hoymiles/src/Hoymiles.cpp b/lib/Hoymiles/src/Hoymiles.cpp index a0c28cdec..7273648c5 100644 --- a/lib/Hoymiles/src/Hoymiles.cpp +++ b/lib/Hoymiles/src/Hoymiles.cpp @@ -32,9 +32,9 @@ void HoymilesClass::initNRF(SPIClass* initialisedSpiBus, const uint8_t pinCE, co _radioNrf->init(initialisedSpiBus, pinCE, pinIRQ); } -void HoymilesClass::initCMT(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3) +void HoymilesClass::initCMT(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3) { - _radioCmt->init(spi_host, pin_sdio, pin_clk, pin_cs, pin_fcs, pin_gpio2, pin_gpio3); + _radioCmt->init(pin_sdio, pin_clk, pin_cs, pin_fcs, pin_gpio2, pin_gpio3); } void HoymilesClass::loop() diff --git a/lib/Hoymiles/src/Hoymiles.h b/lib/Hoymiles/src/Hoymiles.h index 9c578f3ac..86a7d6ca6 100644 --- a/lib/Hoymiles/src/Hoymiles.h +++ b/lib/Hoymiles/src/Hoymiles.h @@ -9,7 +9,6 @@ #include #include #include -#include #define HOY_SYSTEM_CONFIG_PARA_POLL_INTERVAL (2 * 60 * 1000) // 2 minutes #define HOY_SYSTEM_CONFIG_PARA_POLL_MIN_DURATION (4 * 60 * 1000) // at least 4 minutes between sending limit command and read request. Otherwise eventlog entry @@ -18,7 +17,7 @@ class HoymilesClass { public: void init(); void initNRF(SPIClass* initialisedSpiBus, const uint8_t pinCE, const uint8_t pinIRQ); - void initCMT(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3); + void initCMT(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3); void loop(); void setMessageOutput(Print* output); @@ -55,4 +54,4 @@ class HoymilesClass { Print* _messageOutput = &Serial; }; -extern HoymilesClass Hoymiles; +extern HoymilesClass Hoymiles; \ No newline at end of file diff --git a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp index 1a203cb41..035e52f46 100644 --- a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp +++ b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp @@ -83,11 +83,11 @@ bool HoymilesRadio_CMT::cmtSwitchDtuFreq(const uint32_t to_frequency) return true; } -void HoymilesRadio_CMT::init(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3) +void HoymilesRadio_CMT::init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3) { _dtuSerial.u64 = 0; - _radio.reset(new CMT2300A(spi_host, pin_sdio, pin_clk, pin_cs, pin_fcs)); + _radio.reset(new CMT2300A(pin_sdio, pin_clk, pin_cs, pin_fcs)); _radio->begin(); diff --git a/lib/Hoymiles/src/HoymilesRadio_CMT.h b/lib/Hoymiles/src/HoymilesRadio_CMT.h index b6e54430e..770617fe3 100644 --- a/lib/Hoymiles/src/HoymilesRadio_CMT.h +++ b/lib/Hoymiles/src/HoymilesRadio_CMT.h @@ -9,7 +9,6 @@ #include #include #include -#include // number of fragments hold in buffer #define FRAGMENT_BUFFER_SIZE 30 @@ -42,7 +41,7 @@ struct CountryFrequencyList_t { class HoymilesRadio_CMT : public HoymilesRadio { public: - void init(const spi_host_device_t spi_host, const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3); + void init(const int8_t pin_sdio, const int8_t pin_clk, const int8_t pin_cs, const int8_t pin_fcs, const int8_t pin_gpio2, const int8_t pin_gpio3); void loop(); void setPALevel(const int8_t paLevel); void setInverterTargetFrequency(const uint32_t frequency); diff --git a/src/Huawei_can.cpp b/src/Huawei_can.cpp index adab23f0e..ba7ede5cc 100644 --- a/src/Huawei_can.cpp +++ b/src/Huawei_can.cpp @@ -9,7 +9,6 @@ #include "PowerLimiter.h" #include "Configuration.h" #include "Battery.h" -#include "SPIPortManager.h" #include #include @@ -36,11 +35,7 @@ void HuaweiCanCommunicationTask(void* parameter) { bool HuaweiCanCommClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint32_t frequency) { - - auto oSPInum = SPIPortManager.allocatePort("Huawei CAN"); - if (!oSPInum) { return false; } - - SPI = new SPIClass(*oSPInum); + SPI = new SPIClass(HSPI); SPI->begin(huawei_clk, huawei_miso, huawei_mosi, huawei_cs); pinMode(huawei_cs, OUTPUT); digitalWrite(huawei_cs, HIGH); diff --git a/src/InverterSettings.cpp b/src/InverterSettings.cpp index ffe4d8455..3be1927c7 100644 --- a/src/InverterSettings.cpp +++ b/src/InverterSettings.cpp @@ -7,9 +7,22 @@ #include "MessageOutput.h" #include "PinMapping.h" #include "SunPosition.h" -#include "SPIPortManager.h" #include +// the NRF shall use the second externally usable HW SPI controller +// for ESP32 that is the so-called VSPI, for ESP32-S2/S3 it is now called implicitly +// HSPI, as it has shifted places for these chip generations +// for all generations, this is equivalent to SPI3_HOST in the lower level driver +// For ESP32-C2, the only externally usable HW SPI controller is SPI2, its signal names +// being prefixed with FSPI. +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#define SPI_NRF HSPI +#elif CONFIG_IDF_TARGET_ESP32C3 +#define SPI_NRF FSPI +#else +#define SPI_NRF VSPI +#endif + InverterSettingsClass InverterSettings; InverterSettingsClass::InverterSettingsClass() @@ -24,32 +37,24 @@ void InverterSettingsClass::init(Scheduler& scheduler) const PinMapping_t& pin = PinMapping.get(); // Initialize inverter communication - MessageOutput.println("Initialize Hoymiles interface... "); + MessageOutput.print("Initialize Hoymiles interface... "); Hoymiles.setMessageOutput(&MessageOutput); Hoymiles.init(); if (PinMapping.isValidNrf24Config() || PinMapping.isValidCmt2300Config()) { if (PinMapping.isValidNrf24Config()) { - auto oSPInum = SPIPortManager.allocatePort("NRF24"); - - if (oSPInum) { - SPIClass* spiClass = new SPIClass(*oSPInum); - spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs); - Hoymiles.initNRF(spiClass, pin.nrf24_en, pin.nrf24_irq); - } + SPIClass* spiClass = new SPIClass(SPI_NRF); + spiClass->begin(pin.nrf24_clk, pin.nrf24_miso, pin.nrf24_mosi, pin.nrf24_cs); + Hoymiles.initNRF(spiClass, pin.nrf24_en, pin.nrf24_irq); } if (PinMapping.isValidCmt2300Config()) { - auto oSPInum = SPIPortManager.allocatePort("CMT2300A"); - - if (oSPInum) { - Hoymiles.initCMT(SPIPortManager.SPIhostNum(*oSPInum), pin.cmt_sdio, pin.cmt_clk, pin.cmt_cs, pin.cmt_fcs, pin.cmt_gpio2, pin.cmt_gpio3); - MessageOutput.println(" Setting country mode... "); - Hoymiles.getRadioCmt()->setCountryMode(static_cast(config.Dtu.Cmt.CountryMode)); - MessageOutput.println(" Setting CMT target frequency... "); - Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency); - } + Hoymiles.initCMT(pin.cmt_sdio, pin.cmt_clk, pin.cmt_cs, pin.cmt_fcs, pin.cmt_gpio2, pin.cmt_gpio3); + MessageOutput.println(" Setting country mode... "); + Hoymiles.getRadioCmt()->setCountryMode(static_cast(config.Dtu.Cmt.CountryMode)); + MessageOutput.println(" Setting CMT target frequency... "); + Hoymiles.getRadioCmt()->setInverterTargetFrequency(config.Dtu.Cmt.Frequency); } MessageOutput.println(" Setting radio PA level... "); diff --git a/src/SPIPortManager.cpp b/src/SPIPortManager.cpp deleted file mode 100644 index 8b64c423c..000000000 --- a/src/SPIPortManager.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include "SPIPortManager.h" -#include "MessageOutput.h" - -SPIPortManagerClass SPIPortManager; -static constexpr char TAG[] = "[SPIPortManager]"; - -void SPIPortManagerClass::init() { - MessageOutput.printf("%s SPI0 and SPI1 reserved by 'Flash and PSRAM'\r\n", TAG); - _ports[0] = "Flash"; - _ports[1] = "PSRAM"; -} - -std::optional SPIPortManagerClass::allocatePort(std::string const& owner) -{ - for (size_t i = 0; i < _ports.size(); ++i) { - if (_ports[i] != "") { - MessageOutput.printf("%s SPI%d already in use by '%s'\r\n", TAG, i, _ports[i].c_str()); - continue; - } - - _ports[i] = owner; - - MessageOutput.printf("%s SPI%d now in use by '%s'\r\n", TAG, i, owner.c_str()); - - return i + _offset_spi_num; - } - - MessageOutput.printf("%s Cannot assign another SPI port to '%s'\r\n", TAG, owner.c_str()); - return std::nullopt; -} - -void SPIPortManagerClass::freePort(std::string const& owner) -{ - for (size_t i = 0; i < _ports.size(); ++i) { - if (_ports[i] != owner) { continue; } - - MessageOutput.printf("%s Freeing SPI%d, owner was '%s'\r\n", TAG, i + _offset_spi_num, owner.c_str()); - _ports[i] = ""; - } -} - -spi_host_device_t SPIPortManagerClass::SPIhostNum(uint8_t spi_num) -{ - return (spi_host_device_t)(spi_num + _offset_spi_host); -} diff --git a/src/main.cpp b/src/main.cpp index b9f451afd..6faa1378f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,6 @@ #include "Led_Single.h" #include "MessageOutput.h" #include "SerialPortManager.h" -#include "SPIPortManager.h" #include "VictronMppt.h" #include "Battery.h" #include "Huawei_can.h" @@ -99,9 +98,7 @@ void setup() const auto& pin = PinMapping.get(); MessageOutput.println("done"); - // Initialize PortManagers SerialPortManager.init(); - SPIPortManager.init(); // Initialize WiFi MessageOutput.print("Initialize Network... "); From 1812e6eb6a84d5cfda72f3bc7ee6ed3deb33882a Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Mon, 30 Sep 2024 22:26:31 +0200 Subject: [PATCH 34/58] Fix: prevent unauthorized access to OnBattery websockets it turns out that authentication was never implemented on OpenDTU-OnBattery-specific websocket connections. found while applying https://github.com/tbnobody/OpenDTU/pull/2320 --- include/WebApi_ws_Huawei.h | 2 ++ include/WebApi_ws_battery.h | 2 ++ include/WebApi_ws_vedirect_live.h | 2 ++ src/WebApi.cpp | 3 +++ src/WebApi_ws_Huawei.cpp | 20 ++++++++++++++++++++ src/WebApi_ws_battery.cpp | 20 ++++++++++++++++++++ src/WebApi_ws_vedirect_live.cpp | 20 ++++++++++++++++++++ 7 files changed, 69 insertions(+) diff --git a/include/WebApi_ws_Huawei.h b/include/WebApi_ws_Huawei.h index 43e528e83..9ab9c8b7c 100644 --- a/include/WebApi_ws_Huawei.h +++ b/include/WebApi_ws_Huawei.h @@ -10,6 +10,7 @@ class WebApiWsHuaweiLiveClass { public: WebApiWsHuaweiLiveClass(); void init(AsyncWebServer& server, Scheduler& scheduler); + void reload(); private: void generateCommonJsonResponse(JsonVariant& root); @@ -18,6 +19,7 @@ class WebApiWsHuaweiLiveClass { AsyncWebServer* _server; AsyncWebSocket _ws; + AuthenticationMiddleware _simpleDigestAuth; std::mutex _mutex; diff --git a/include/WebApi_ws_battery.h b/include/WebApi_ws_battery.h index d89e01aec..bc014d390 100644 --- a/include/WebApi_ws_battery.h +++ b/include/WebApi_ws_battery.h @@ -10,6 +10,7 @@ class WebApiWsBatteryLiveClass { public: WebApiWsBatteryLiveClass(); void init(AsyncWebServer& server, Scheduler& scheduler); + void reload(); private: void generateCommonJsonResponse(JsonVariant& root); @@ -18,6 +19,7 @@ class WebApiWsBatteryLiveClass { AsyncWebServer* _server; AsyncWebSocket _ws; + AuthenticationMiddleware _simpleDigestAuth; uint32_t _lastUpdateCheck = 0; static constexpr uint16_t _responseSize = 1024 + 512; diff --git a/include/WebApi_ws_vedirect_live.h b/include/WebApi_ws_vedirect_live.h index b9890834e..7c3bedf60 100644 --- a/include/WebApi_ws_vedirect_live.h +++ b/include/WebApi_ws_vedirect_live.h @@ -12,6 +12,7 @@ class WebApiWsVedirectLiveClass { public: WebApiWsVedirectLiveClass(); void init(AsyncWebServer& server, Scheduler& scheduler); + void reload(); private: void generateCommonJsonResponse(JsonVariant& root, bool fullUpdate); @@ -22,6 +23,7 @@ class WebApiWsVedirectLiveClass { AsyncWebServer* _server; AsyncWebSocket _ws; + AuthenticationMiddleware _simpleDigestAuth; uint32_t _lastFullPublish = 0; uint32_t _lastPublish = 0; diff --git a/src/WebApi.cpp b/src/WebApi.cpp index ffd212dd3..47ce7657e 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -51,6 +51,9 @@ void WebApiClass::reload() { _webApiWsConsole.reload(); _webApiWsLive.reload(); + _webApiWsBatteryLive.reload(); + _webApiWsVedirectLive.reload(); + _webApiWsHuaweiLive.reload(); } bool WebApiClass::checkCredentials(AsyncWebServerRequest* request) diff --git a/src/WebApi_ws_Huawei.cpp b/src/WebApi_ws_Huawei.cpp index f171a18ab..cf4819914 100644 --- a/src/WebApi_ws_Huawei.cpp +++ b/src/WebApi_ws_Huawei.cpp @@ -42,6 +42,26 @@ void WebApiWsHuaweiLiveClass::init(AsyncWebServer& server, Scheduler& scheduler) _sendDataTask.setIterations(TASK_FOREVER); _sendDataTask.setInterval(1 * TASK_SECOND); _sendDataTask.enable(); + + _simpleDigestAuth.setUsername(AUTH_USERNAME); + _simpleDigestAuth.setRealm("AC charger websocket"); + + reload(); +} + +void WebApiWsHuaweiLiveClass::reload() +{ + _ws.removeMiddleware(&_simpleDigestAuth); + + auto const& config = Configuration.get(); + + if (config.Security.AllowReadonly) { return; } + + _ws.enable(false); + _simpleDigestAuth.setPassword(config.Security.Password); + _ws.addMiddleware(&_simpleDigestAuth); + _ws.closeAll(); + _ws.enable(true); } void WebApiWsHuaweiLiveClass::wsCleanupTaskCb() diff --git a/src/WebApi_ws_battery.cpp b/src/WebApi_ws_battery.cpp index 42913abc6..b932bfc4e 100644 --- a/src/WebApi_ws_battery.cpp +++ b/src/WebApi_ws_battery.cpp @@ -42,6 +42,26 @@ void WebApiWsBatteryLiveClass::init(AsyncWebServer& server, Scheduler& scheduler _sendDataTask.setIterations(TASK_FOREVER); _sendDataTask.setInterval(1 * TASK_SECOND); _sendDataTask.enable(); + + _simpleDigestAuth.setUsername(AUTH_USERNAME); + _simpleDigestAuth.setRealm("battery websocket"); + + reload(); +} + +void WebApiWsBatteryLiveClass::reload() +{ + _ws.removeMiddleware(&_simpleDigestAuth); + + auto const& config = Configuration.get(); + + if (config.Security.AllowReadonly) { return; } + + _ws.enable(false); + _simpleDigestAuth.setPassword(config.Security.Password); + _ws.addMiddleware(&_simpleDigestAuth); + _ws.closeAll(); + _ws.enable(true); } void WebApiWsBatteryLiveClass::wsCleanupTaskCb() diff --git a/src/WebApi_ws_vedirect_live.cpp b/src/WebApi_ws_vedirect_live.cpp index abf3376eb..473db8390 100644 --- a/src/WebApi_ws_vedirect_live.cpp +++ b/src/WebApi_ws_vedirect_live.cpp @@ -44,6 +44,26 @@ void WebApiWsVedirectLiveClass::init(AsyncWebServer& server, Scheduler& schedule _sendDataTask.setIterations(TASK_FOREVER); _sendDataTask.setInterval(500 * TASK_MILLISECOND); _sendDataTask.enable(); + + _simpleDigestAuth.setUsername(AUTH_USERNAME); + _simpleDigestAuth.setRealm("vedirect websocket"); + + reload(); +} + +void WebApiWsVedirectLiveClass::reload() +{ + _ws.removeMiddleware(&_simpleDigestAuth); + + auto const& config = Configuration.get(); + + if (config.Security.AllowReadonly) { return; } + + _ws.enable(false); + _simpleDigestAuth.setPassword(config.Security.Password); + _ws.addMiddleware(&_simpleDigestAuth); + _ws.closeAll(); + _ws.enable(true); } void WebApiWsVedirectLiveClass::wsCleanupTaskCb() From 2234ac97039a21eb6c471b0c584c43379e974c0c Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Wed, 2 Oct 2024 10:32:58 +0200 Subject: [PATCH 35/58] Upgrade ESPAsyncWebServer from 3.3.1 to 3.3.7 --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 821112680..2b3a4c952 100644 --- a/platformio.ini +++ b/platformio.ini @@ -41,7 +41,7 @@ build_unflags = -std=gnu++11 lib_deps = - mathieucarbou/ESPAsyncWebServer @ 3.3.1 + mathieucarbou/ESPAsyncWebServer @ 3.3.7 bblanchon/ArduinoJson @ 7.2.0 https://github.com/bertmelis/espMqttClient.git#v1.7.0 nrf24/RF24 @ 1.4.9 From 38b5807ef772a3568704cb2a3546d25fde430c1b Mon Sep 17 00:00:00 2001 From: mbo18 Date: Wed, 2 Oct 2024 10:44:43 +0200 Subject: [PATCH 36/58] Remove icon because device_class is set --- src/MqttHandleHass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index ab4a2ad1f..0363cc8e8 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -61,7 +61,7 @@ void MqttHandleHassClass::publishConfig() publishDtuSensor("IP", "dtu/ip", "", "mdi:network-outline", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); publishDtuSensor("WiFi Signal", "dtu/rssi", "dBm", "", DEVICE_CLS_SIGNAL_STRENGTH, CATEGORY_DIAGNOSTIC); publishDtuSensor("Uptime", "dtu/uptime", "s", "", DEVICE_CLS_DURATION, CATEGORY_DIAGNOSTIC); - publishDtuSensor("Temperature", "dtu/temperature", "°C", "mdi:thermometer", DEVICE_CLS_TEMPERATURE, CATEGORY_DIAGNOSTIC); + publishDtuSensor("Temperature", "dtu/temperature", "°C", "", DEVICE_CLS_TEMPERATURE, CATEGORY_DIAGNOSTIC); publishDtuSensor("Heap Size", "dtu/heap/size", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); publishDtuSensor("Heap Free", "dtu/heap/free", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); publishDtuSensor("Largest Free Heap Block", "dtu/heap/maxalloc", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); From 9df3e30bb2852d0e70f24675d10feef2aa371b96 Mon Sep 17 00:00:00 2001 From: mbo18 Date: Wed, 2 Oct 2024 11:02:52 +0200 Subject: [PATCH 37/58] Remove unused DEVICE_CLASS_TEMP --- include/MqttHandleHass.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/MqttHandleHass.h b/include/MqttHandleHass.h index 1a3acf59c..7dcf9410e 100644 --- a/include/MqttHandleHass.h +++ b/include/MqttHandleHass.h @@ -13,7 +13,6 @@ enum DeviceClassType { DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, - DEVICE_CLS_TEMP, DEVICE_CLS_POWER_FACTOR, DEVICE_CLS_REACTIVE_POWER, DEVICE_CLS_CONNECTIVITY, @@ -22,7 +21,7 @@ enum DeviceClassType { DEVICE_CLS_TEMPERATURE, DEVICE_CLS_RESTART }; -const char* const deviceClass_name[] = { 0, "current", "energy", "power", "voltage", "frequency", "temperature", "power_factor", "reactive_power", "connectivity", "duration", "signal_strength", "temperature", "restart" }; +const char* const deviceClass_name[] = { 0, "current", "energy", "power", "voltage", "frequency", "power_factor", "reactive_power", "connectivity", "duration", "signal_strength", "temperature", "restart" }; enum StateClassType { STATE_CLS_NONE = 0, @@ -55,7 +54,7 @@ const byteAssign_fieldDeviceClass_t deviceFieldAssignment[] = { { FLD_IAC, DEVICE_CLS_CURRENT, STATE_CLS_MEASUREMENT }, { FLD_PAC, DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT }, { FLD_F, DEVICE_CLS_FREQ, STATE_CLS_MEASUREMENT }, - { FLD_T, DEVICE_CLS_TEMP, STATE_CLS_MEASUREMENT }, + { FLD_T, DEVICE_CLS_TEMPERATURE, STATE_CLS_MEASUREMENT }, { FLD_PF, DEVICE_CLS_POWER_FACTOR, STATE_CLS_MEASUREMENT }, { FLD_EFF, DEVICE_CLS_NONE, STATE_CLS_NONE }, { FLD_IRR, DEVICE_CLS_NONE, STATE_CLS_NONE }, From 0c2b6f1a61a25233c1945cd109cb09110e8c5072 Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Wed, 2 Oct 2024 18:13:12 +0200 Subject: [PATCH 38/58] Fix: Add state_class to several Home Assistant sensors state_class was added to yieldtotal, yieldday ac power and temperature for the whole dtu closes: #2324 --- include/MqttHandleHass.h | 18 +++--- src/MqttHandleHass.cpp | 123 +++++++++++++++++++++++---------------- 2 files changed, 82 insertions(+), 59 deletions(-) diff --git a/include/MqttHandleHass.h b/include/MqttHandleHass.h index 7dcf9410e..c0a22c704 100644 --- a/include/MqttHandleHass.h +++ b/include/MqttHandleHass.h @@ -74,21 +74,21 @@ class MqttHandleHassClass { static void publish(const String& subtopic, const String& payload); static void publish(const String& subtopic, const JsonDocument& doc); - static void addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category); + static void addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category); // Binary Sensor - static void publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category); - static void publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category); - static void publishInverterBinarySensor(std::shared_ptr inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category); + static void publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category); + static void publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category); + static void publishInverterBinarySensor(std::shared_ptr inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category); // Sensor - static void publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category); - static void publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category); - static void publishInverterSensor(std::shared_ptr inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category); + static void publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category); + static void publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category); + static void publishInverterSensor(std::shared_ptr inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category); static void publishInverterField(std::shared_ptr inv, const ChannelType_t type, const ChannelNum_t channel, const byteAssign_fieldDeviceClass_t fieldType, const bool clear = false); - static void publishInverterButton(std::shared_ptr inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const CategoryType category); - static void publishInverterNumber(std::shared_ptr inv, const String& name, const String& state_topic, const String& command_topic, const int16_t min, const int16_t max, float step, const String& unit_of_measure, const String& icon, const CategoryType category); + static void publishInverterButton(std::shared_ptr inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const StateClassType state_class, const CategoryType category); + static void publishInverterNumber(std::shared_ptr inv, const String& name, const String& state_topic, const String& command_topic, const int16_t min, const int16_t max, float step, const String& unit_of_measure, const String& icon, const StateClassType state_class, const CategoryType category); static void createInverterInfo(JsonDocument& doc, std::shared_ptr inv); static void createDtuInfo(JsonDocument& doc); diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index 0363cc8e8..7afa87da0 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -58,45 +58,45 @@ void MqttHandleHassClass::publishConfig() const CONFIG_T& config = Configuration.get(); // publish DTU sensors - publishDtuSensor("IP", "dtu/ip", "", "mdi:network-outline", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishDtuSensor("WiFi Signal", "dtu/rssi", "dBm", "", DEVICE_CLS_SIGNAL_STRENGTH, CATEGORY_DIAGNOSTIC); - publishDtuSensor("Uptime", "dtu/uptime", "s", "", DEVICE_CLS_DURATION, CATEGORY_DIAGNOSTIC); - publishDtuSensor("Temperature", "dtu/temperature", "°C", "", DEVICE_CLS_TEMPERATURE, CATEGORY_DIAGNOSTIC); - publishDtuSensor("Heap Size", "dtu/heap/size", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishDtuSensor("Heap Free", "dtu/heap/free", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishDtuSensor("Largest Free Heap Block", "dtu/heap/maxalloc", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishDtuSensor("Lifetime Minimum Free Heap", "dtu/heap/minfree", "Bytes", "mdi:memory", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishDtuSensor("IP", "dtu/ip", "", "mdi:network-outline", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishDtuSensor("WiFi Signal", "dtu/rssi", "dBm", "", DEVICE_CLS_SIGNAL_STRENGTH, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishDtuSensor("Uptime", "dtu/uptime", "s", "", DEVICE_CLS_DURATION, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishDtuSensor("Temperature", "dtu/temperature", "°C", "", DEVICE_CLS_TEMPERATURE, STATE_CLS_MEASUREMENT, CATEGORY_DIAGNOSTIC); + publishDtuSensor("Heap Size", "dtu/heap/size", "Bytes", "mdi:memory", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishDtuSensor("Heap Free", "dtu/heap/free", "Bytes", "mdi:memory", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishDtuSensor("Largest Free Heap Block", "dtu/heap/maxalloc", "Bytes", "mdi:memory", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishDtuSensor("Lifetime Minimum Free Heap", "dtu/heap/minfree", "Bytes", "mdi:memory", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishDtuSensor("Yield Total", "ac/yieldtotal", "kWh", "", DEVICE_CLS_ENERGY, CATEGORY_NONE); - publishDtuSensor("Yield Day", "ac/yieldday", "Wh", "", DEVICE_CLS_ENERGY, CATEGORY_NONE); - publishDtuSensor("AC Power", "ac/power", "W", "", DEVICE_CLS_PWR, CATEGORY_NONE); + publishDtuSensor("Yield Total", "ac/yieldtotal", "kWh", "", DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING, CATEGORY_NONE); + publishDtuSensor("Yield Day", "ac/yieldday", "Wh", "", DEVICE_CLS_ENERGY, STATE_CLS_TOTAL_INCREASING, CATEGORY_NONE); + publishDtuSensor("AC Power", "ac/power", "W", "", DEVICE_CLS_PWR, STATE_CLS_MEASUREMENT, CATEGORY_NONE); - publishDtuBinarySensor("Status", config.Mqtt.Lwt.Topic, config.Mqtt.Lwt.Value_Online, config.Mqtt.Lwt.Value_Offline, DEVICE_CLS_CONNECTIVITY, CATEGORY_DIAGNOSTIC); + publishDtuBinarySensor("Status", config.Mqtt.Lwt.Topic, config.Mqtt.Lwt.Value_Online, config.Mqtt.Lwt.Value_Offline, DEVICE_CLS_CONNECTIVITY, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); // Loop all inverters for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) { auto inv = Hoymiles.getInverterByPos(i); - publishInverterButton(inv, "Turn Inverter Off", "cmd/power", "0", "mdi:power-plug-off", DEVICE_CLS_NONE, CATEGORY_CONFIG); - publishInverterButton(inv, "Turn Inverter On", "cmd/power", "1", "mdi:power-plug", DEVICE_CLS_NONE, CATEGORY_CONFIG); - publishInverterButton(inv, "Restart Inverter", "cmd/restart", "1", "", DEVICE_CLS_RESTART, CATEGORY_CONFIG); - publishInverterButton(inv, "Reset Radio Statistics", "cmd/reset_rf_stats", "1", "", DEVICE_CLS_NONE, CATEGORY_CONFIG); + publishInverterButton(inv, "Turn Inverter Off", "cmd/power", "0", "mdi:power-plug-off", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_CONFIG); + publishInverterButton(inv, "Turn Inverter On", "cmd/power", "1", "mdi:power-plug", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_CONFIG); + publishInverterButton(inv, "Restart Inverter", "cmd/restart", "1", "", DEVICE_CLS_RESTART, STATE_CLS_NONE, CATEGORY_CONFIG); + publishInverterButton(inv, "Reset Radio Statistics", "cmd/reset_rf_stats", "1", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_CONFIG); - publishInverterNumber(inv, "Limit NonPersistent Relative", "status/limit_relative", "cmd/limit_nonpersistent_relative", 0, 100, 0.1, "%", "mdi:speedometer", CATEGORY_CONFIG); - publishInverterNumber(inv, "Limit Persistent Relative", "status/limit_relative", "cmd/limit_persistent_relative", 0, 100, 0.1, "%", "mdi:speedometer", CATEGORY_CONFIG); + publishInverterNumber(inv, "Limit NonPersistent Relative", "status/limit_relative", "cmd/limit_nonpersistent_relative", 0, 100, 0.1, "%", "mdi:speedometer", STATE_CLS_NONE, CATEGORY_CONFIG); + publishInverterNumber(inv, "Limit Persistent Relative", "status/limit_relative", "cmd/limit_persistent_relative", 0, 100, 0.1, "%", "mdi:speedometer", STATE_CLS_NONE, CATEGORY_CONFIG); - publishInverterNumber(inv, "Limit NonPersistent Absolute", "status/limit_absolute", "cmd/limit_nonpersistent_absolute", 0, MAX_INVERTER_LIMIT, 1, "W", "mdi:speedometer", CATEGORY_CONFIG); - publishInverterNumber(inv, "Limit Persistent Absolute", "status/limit_absolute", "cmd/limit_persistent_absolute", 0, MAX_INVERTER_LIMIT, 1, "W", "mdi:speedometer", CATEGORY_CONFIG); + publishInverterNumber(inv, "Limit NonPersistent Absolute", "status/limit_absolute", "cmd/limit_nonpersistent_absolute", 0, MAX_INVERTER_LIMIT, 1, "W", "mdi:speedometer", STATE_CLS_NONE, CATEGORY_CONFIG); + publishInverterNumber(inv, "Limit Persistent Absolute", "status/limit_absolute", "cmd/limit_persistent_absolute", 0, MAX_INVERTER_LIMIT, 1, "W", "mdi:speedometer", STATE_CLS_NONE, CATEGORY_CONFIG); - publishInverterBinarySensor(inv, "Reachable", "status/reachable", "1", "0", DEVICE_CLS_CONNECTIVITY, CATEGORY_DIAGNOSTIC); - publishInverterBinarySensor(inv, "Producing", "status/producing", "1", "0", DEVICE_CLS_NONE, CATEGORY_NONE); + publishInverterBinarySensor(inv, "Reachable", "status/reachable", "1", "0", DEVICE_CLS_CONNECTIVITY, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishInverterBinarySensor(inv, "Producing", "status/producing", "1", "0", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_NONE); - publishInverterSensor(inv, "TX Requests", "radio/tx_request", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishInverterSensor(inv, "RX Success", "radio/rx_success", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishInverterSensor(inv, "RX Fail Receive Nothing", "radio/rx_fail_nothing", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishInverterSensor(inv, "RX Fail Receive Partial", "radio/rx_fail_partial", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishInverterSensor(inv, "RX Fail Receive Corrupt", "radio/rx_fail_corrupt", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); - publishInverterSensor(inv, "TX Re-Request Fragment", "radio/tx_re_request", "", "", DEVICE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishInverterSensor(inv, "TX Requests", "radio/tx_request", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishInverterSensor(inv, "RX Success", "radio/rx_success", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishInverterSensor(inv, "RX Fail Receive Nothing", "radio/rx_fail_nothing", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishInverterSensor(inv, "RX Fail Receive Partial", "radio/rx_fail_partial", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishInverterSensor(inv, "RX Fail Receive Corrupt", "radio/rx_fail_corrupt", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishInverterSensor(inv, "TX Re-Request Fragment", "radio/tx_re_request", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); // Loop all channels for (auto& t : inv->Statistics()->getChannelTypes()) { @@ -142,7 +142,6 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr if (!clear) { const String stateTopic = MqttSettings.getPrefix() + MqttHandleInverter.getTopic(inv, type, channel, fieldType.fieldId); - const char* stateCls = stateClass_name[fieldType.stateClsId]; String name; if (type != TYPE_DC) { @@ -155,7 +154,7 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr JsonDocument root; createInverterInfo(root, inv); - addCommonMetadata(root, unit_of_measure, "", fieldType.deviceClsId, CATEGORY_NONE); + addCommonMetadata(root, unit_of_measure, "", fieldType.deviceClsId, fieldType.stateClsId, CATEGORY_NONE); root["name"] = name; root["stat_t"] = stateTopic; @@ -164,9 +163,6 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr if (Configuration.get().Mqtt.Hass.Expire) { root["exp_aft"] = Hoymiles.getNumInverters() * max(Hoymiles.PollInterval(), Configuration.get().Mqtt.PublishInterval) * inv->getReachableThreshold(); } - if (stateCls != 0) { - root["stat_cla"] = stateCls; - } publish(configTopic, root); } else { @@ -174,7 +170,10 @@ void MqttHandleHassClass::publishInverterField(std::shared_ptr } } -void MqttHandleHassClass::publishInverterButton(std::shared_ptr inv, const String& name, const String& state_topic, const String& payload, const String& icon, const DeviceClassType device_class, const CategoryType category) +void MqttHandleHassClass::publishInverterButton( + std::shared_ptr inv, const String& name, const String& state_topic, const String& payload, + const String& icon, + const DeviceClassType device_class, const StateClassType state_class, const CategoryType category) { const String serial = inv->serialString(); @@ -190,7 +189,7 @@ void MqttHandleHassClass::publishInverterButton(std::shared_ptr inv, const String& name, const String& stateTopic, const String& command_topic, const int16_t min, const int16_t max, float step, - const String& unit_of_measure, const String& icon, const CategoryType category) + const String& unit_of_measure, const String& icon, + const StateClassType state_class, const CategoryType category) { const String serial = inv->serialString(); @@ -221,7 +221,7 @@ void MqttHandleHassClass::publishInverterNumber( JsonDocument root; createInverterInfo(root, inv); - addCommonMetadata(root, unit_of_measure, icon, DEVICE_CLS_NONE, category); + addCommonMetadata(root, unit_of_measure, icon, DEVICE_CLS_NONE, state_class, category); root["name"] = name; root["uniq_id"] = serial + "_" + buttonId; @@ -307,7 +307,10 @@ void MqttHandleHassClass::publish(const String& subtopic, const JsonDocument& do publish(subtopic, buffer); } -void MqttHandleHassClass::addCommonMetadata(JsonDocument& doc, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category) +void MqttHandleHassClass::addCommonMetadata( + JsonDocument& doc, + const String& unit_of_measure, const String& icon, + const DeviceClassType device_class, const StateClassType state_class, const CategoryType category) { if (unit_of_measure != "") { doc["unit_of_meas"] = unit_of_measure; @@ -318,12 +321,18 @@ void MqttHandleHassClass::addCommonMetadata(JsonDocument& doc, const String& uni if (device_class != DEVICE_CLS_NONE) { doc["dev_cla"] = deviceClass_name[device_class]; } + if (state_class != STATE_CLS_NONE) { + doc["stat_cla"] = stateClass_name[state_class];; + } if (category != CATEGORY_NONE) { doc["ent_cat"] = category_name[category]; } } -void MqttHandleHassClass::publishBinarySensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category) +void MqttHandleHassClass::publishBinarySensor( + JsonDocument& doc, + const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, + const DeviceClassType device_class, const StateClassType state_class, const CategoryType category) { String sensor_id = name; sensor_id.toLowerCase(); @@ -335,31 +344,39 @@ void MqttHandleHassClass::publishBinarySensor(JsonDocument& doc, const String& r doc["pl_on"] = payload_on; doc["pl_off"] = payload_off; - addCommonMetadata(doc, "", "", device_class, category); + addCommonMetadata(doc, "", "", device_class, state_class, category); const String configTopic = "binary_sensor/" + root_device + "/" + sensor_id + "/config"; publish(configTopic, doc); } -void MqttHandleHassClass::publishDtuBinarySensor(const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category) +void MqttHandleHassClass::publishDtuBinarySensor( + const String& name, const String& state_topic, const String& payload_on, const String& payload_off, + const DeviceClassType device_class, const StateClassType state_class, const CategoryType category) { const String dtuId = getDtuUniqueId(); JsonDocument root; createDtuInfo(root); - publishBinarySensor(root, dtuId, dtuId, name, state_topic, payload_on, payload_off, device_class, category); + publishBinarySensor(root, dtuId, dtuId, name, state_topic, payload_on, payload_off, device_class, state_class, category); } -void MqttHandleHassClass::publishInverterBinarySensor(std::shared_ptr inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, const DeviceClassType device_class, const CategoryType category) +void MqttHandleHassClass::publishInverterBinarySensor( + std::shared_ptr inv, const String& name, const String& state_topic, const String& payload_on, const String& payload_off, + const DeviceClassType device_class, const StateClassType state_class, const CategoryType category) { const String serial = inv->serialString(); JsonDocument root; createInverterInfo(root, inv); - publishBinarySensor(root, "dtu_" + serial, serial, name, serial + "/" + state_topic, payload_on, payload_off, device_class, category); + publishBinarySensor(root, "dtu_" + serial, serial, name, serial + "/" + state_topic, payload_on, payload_off, device_class, state_class, category); } -void MqttHandleHassClass::publishSensor(JsonDocument& doc, const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category) +void MqttHandleHassClass::publishSensor( + JsonDocument& doc, + const String& root_device, const String& unique_id_prefix, const String& name, const String& state_topic, + const String& unit_of_measure, const String& icon, + const DeviceClassType device_class, const StateClassType state_class, const CategoryType category) { String sensor_id = name; sensor_id.toLowerCase(); @@ -369,7 +386,7 @@ void MqttHandleHassClass::publishSensor(JsonDocument& doc, const String& root_de doc["uniq_id"] = unique_id_prefix + "_" + sensor_id; doc["stat_t"] = MqttSettings.getPrefix() + state_topic; - addCommonMetadata(doc, unit_of_measure, icon, device_class, category); + addCommonMetadata(doc, unit_of_measure, icon, device_class, state_class, category); const CONFIG_T& config = Configuration.get(); doc["avty_t"] = MqttSettings.getPrefix() + config.Mqtt.Lwt.Topic; @@ -380,20 +397,26 @@ void MqttHandleHassClass::publishSensor(JsonDocument& doc, const String& root_de publish(configTopic, doc); } -void MqttHandleHassClass::publishDtuSensor(const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category) +void MqttHandleHassClass::publishDtuSensor( + const String& name, const String& state_topic, + const String& unit_of_measure, const String& icon, + const DeviceClassType device_class, const StateClassType state_class, const CategoryType category) { const String dtuId = getDtuUniqueId(); JsonDocument root; createDtuInfo(root); - publishSensor(root, dtuId, dtuId, name, state_topic, unit_of_measure, icon, device_class, category); + publishSensor(root, dtuId, dtuId, name, state_topic, unit_of_measure, icon, device_class, state_class, category); } -void MqttHandleHassClass::publishInverterSensor(std::shared_ptr inv, const String& name, const String& state_topic, const String& unit_of_measure, const String& icon, const DeviceClassType device_class, const CategoryType category) +void MqttHandleHassClass::publishInverterSensor( + std::shared_ptr inv, const String& name, const String& state_topic, + const String& unit_of_measure, const String& icon, + const DeviceClassType device_class, const StateClassType state_class, const CategoryType category) { const String serial = inv->serialString(); JsonDocument root; createInverterInfo(root, inv); - publishSensor(root, "dtu_" + serial, serial, name, serial + "/" + state_topic, unit_of_measure, icon, device_class, category); + publishSensor(root, "dtu_" + serial, serial, name, serial + "/" + state_topic, unit_of_measure, icon, device_class, state_class, category); } From edfe06e31eae286fbb88da69e9b65420cfa8628a Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Fri, 4 Oct 2024 17:36:17 +0200 Subject: [PATCH 39/58] Feature: Show RSSI of last received packet in radio stats The value is also published via MQTT --- lib/Hoymiles/src/HoymilesRadio_CMT.cpp | 2 +- lib/Hoymiles/src/HoymilesRadio_NRF.cpp | 2 +- lib/Hoymiles/src/inverters/InverterAbstract.cpp | 9 ++++++++- lib/Hoymiles/src/inverters/InverterAbstract.h | 6 +++++- src/MqttHandleHass.cpp | 1 + src/MqttHandleInverter.cpp | 1 + src/WebApi_ws_live.cpp | 1 + webapp/src/locales/de.json | 5 ++++- webapp/src/locales/en.json | 5 ++++- webapp/src/locales/fr.json | 5 ++++- webapp/src/types/LiveDataStatus.ts | 1 + webapp/src/views/HomeView.vue | 12 ++++++++++++ 12 files changed, 43 insertions(+), 7 deletions(-) diff --git a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp index bbd31f212..f6d195881 100644 --- a/lib/Hoymiles/src/HoymilesRadio_CMT.cpp +++ b/lib/Hoymiles/src/HoymilesRadio_CMT.cpp @@ -169,7 +169,7 @@ void HoymilesRadio_CMT::loop() dumpBuf(f.fragment, f.len, false); Hoymiles.getMessageOutput()->printf("| %d dBm\r\n", f.rssi); - inv->addRxFragment(f.fragment, f.len); + inv->addRxFragment(f.fragment, f.len, f.rssi); } else { Hoymiles.getMessageOutput()->println("Inverter Not found!"); } diff --git a/lib/Hoymiles/src/HoymilesRadio_NRF.cpp b/lib/Hoymiles/src/HoymilesRadio_NRF.cpp index 4bf104ade..bd496afee 100644 --- a/lib/Hoymiles/src/HoymilesRadio_NRF.cpp +++ b/lib/Hoymiles/src/HoymilesRadio_NRF.cpp @@ -80,7 +80,7 @@ void HoymilesRadio_NRF::loop() dumpBuf(f.fragment, f.len, false); Hoymiles.getMessageOutput()->printf("| %d dBm\r\n", f.rssi); - inv->addRxFragment(f.fragment, f.len); + inv->addRxFragment(f.fragment, f.len, f.rssi); } else { Hoymiles.getMessageOutput()->println("Inverter Not found!"); } diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.cpp b/lib/Hoymiles/src/inverters/InverterAbstract.cpp index 3e51bbd71..7fe522f3f 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.cpp +++ b/lib/Hoymiles/src/inverters/InverterAbstract.cpp @@ -137,6 +137,11 @@ bool InverterAbstract::getClearEventlogOnMidnight() const return _clearEventlogOnMidnight; } +int8_t InverterAbstract::getLastRssi() const +{ + return _lastRssi; +} + bool InverterAbstract::sendChangeChannelRequest() { return false; @@ -185,8 +190,10 @@ void InverterAbstract::clearRxFragmentBuffer() _rxFragmentRetransmitCnt = 0; } -void InverterAbstract::addRxFragment(const uint8_t fragment[], const uint8_t len) +void InverterAbstract::addRxFragment(const uint8_t fragment[], const uint8_t len, const int8_t rssi) { + _lastRssi = rssi; + if (len < 11) { Hoymiles.getMessageOutput()->printf("FATAL: (%s, %d) fragment too short\r\n", __FILE__, __LINE__); return; diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.h b/lib/Hoymiles/src/inverters/InverterAbstract.h index f139fab3d..2f49f912a 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.h +++ b/lib/Hoymiles/src/inverters/InverterAbstract.h @@ -61,8 +61,10 @@ class InverterAbstract { void setClearEventlogOnMidnight(const bool enabled); bool getClearEventlogOnMidnight() const; + int8_t getLastRssi() const; + void clearRxFragmentBuffer(); - void addRxFragment(const uint8_t fragment[], const uint8_t len); + void addRxFragment(const uint8_t fragment[], const uint8_t len, const int8_t rssi); uint8_t verifyAllFragments(CommandAbstract& cmd); void performDailyTask(); @@ -131,6 +133,8 @@ class InverterAbstract { bool _zeroYieldDayOnMidnight = false; bool _clearEventlogOnMidnight = false; + int8_t _lastRssi = 0; + std::unique_ptr _alarmLogParser; std::unique_ptr _devInfoParser; std::unique_ptr _gridProfileParser; diff --git a/src/MqttHandleHass.cpp b/src/MqttHandleHass.cpp index 7afa87da0..6491c9baf 100644 --- a/src/MqttHandleHass.cpp +++ b/src/MqttHandleHass.cpp @@ -97,6 +97,7 @@ void MqttHandleHassClass::publishConfig() publishInverterSensor(inv, "RX Fail Receive Partial", "radio/rx_fail_partial", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); publishInverterSensor(inv, "RX Fail Receive Corrupt", "radio/rx_fail_corrupt", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); publishInverterSensor(inv, "TX Re-Request Fragment", "radio/tx_re_request", "", "", DEVICE_CLS_NONE, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); + publishInverterSensor(inv, "RSSI", "radio/rssi", "dBm", "", DEVICE_CLS_SIGNAL_STRENGTH, STATE_CLS_NONE, CATEGORY_DIAGNOSTIC); // Loop all channels for (auto& t : inv->Statistics()->getChannelTypes()) { diff --git a/src/MqttHandleInverter.cpp b/src/MqttHandleInverter.cpp index 8e7206635..6185f1935 100644 --- a/src/MqttHandleInverter.cpp +++ b/src/MqttHandleInverter.cpp @@ -50,6 +50,7 @@ void MqttHandleInverterClass::loop() MqttSettings.publish(subtopic + "/radio/rx_fail_nothing", String(inv->RadioStats.RxFailNoAnswer)); MqttSettings.publish(subtopic + "/radio/rx_fail_partial", String(inv->RadioStats.RxFailPartialAnswer)); MqttSettings.publish(subtopic + "/radio/rx_fail_corrupt", String(inv->RadioStats.RxFailCorruptData)); + MqttSettings.publish(subtopic + "/radio/rssi", String(inv->getLastRssi())); if (inv->DevInfo()->getLastUpdate() > 0) { // Bootloader Version diff --git a/src/WebApi_ws_live.cpp b/src/WebApi_ws_live.cpp index 29c204a3f..a50a792a6 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -153,6 +153,7 @@ void WebApiWsLiveClass::generateInverterCommonJsonResponse(JsonObject& root, std root["radio_stats"]["rx_fail_nothing"] = inv->RadioStats.RxFailNoAnswer; root["radio_stats"]["rx_fail_partial"] = inv->RadioStats.RxFailPartialAnswer; root["radio_stats"]["rx_fail_corrupt"] = inv->RadioStats.RxFailCorruptData; + root["radio_stats"]["rssi"] = inv->getLastRssi(); } void WebApiWsLiveClass::generateInverterChannelJsonResponse(JsonObject& root, std::shared_ptr inv) diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 995f6ec7c..0bc177685 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -150,7 +150,10 @@ "RxFailCorrupt": "Empfang Fehler: Beschädigt empfangen", "TxReRequest": "Gesendete Fragment Wiederanforderungen", "StatsReset": "Statistiken zurücksetzen", - "StatsResetting": "Zurücksetzen..." + "StatsResetting": "Zurücksetzen...", + "Rssi": "RSSI des zuletzt empfangenen Paketes", + "RssiHint": "HM-Wechselrichter unterstützen nur RSSI-Werte < -64 dBm und > -64 dBm. In diesem Fall wird -80 dBm und -30 dBm angezeigt.", + "dBm": "{dbm} dBm" }, "eventlog": { "Start": "Beginn", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index cf3c74f07..f760178f7 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -150,7 +150,10 @@ "RxFailCorrupt": "RX Fail: Receive Corrupt", "TxReRequest": "TX Re-Request Fragment", "StatsReset": "Reset Statistics", - "StatsResetting": "Resetting..." + "StatsResetting": "Resetting...", + "Rssi": "RSSI of last received packet", + "RssiHint": "HM inverters only support RSSI values < -64 dBm and > -64 dBm. In this case, -80 dbm and -30 dbm is shown.", + "dBm": "{dbm} dBm" }, "eventlog": { "Start": "Start", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index fcb22a19c..023fc0994 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -150,7 +150,10 @@ "RxFailCorrupt": "RX Fail: Receive Corrupt", "TxReRequest": "TX Re-Request Fragment", "StatsReset": "Reset Statistics", - "StatsResetting": "Resetting..." + "StatsResetting": "Resetting...", + "Rssi": "RSSI of last received packet", + "RssiHint": "HM inverters only support RSSI values < -64 dBm and > -64 dBm. In this case, -80 dbm and -30 dbm is shown.", + "dBm": "{dbm} dBm" }, "eventlog": { "Start": "Départ", diff --git a/webapp/src/types/LiveDataStatus.ts b/webapp/src/types/LiveDataStatus.ts index db95897c0..4f6810700 100644 --- a/webapp/src/types/LiveDataStatus.ts +++ b/webapp/src/types/LiveDataStatus.ts @@ -28,6 +28,7 @@ export interface RadioStatistics { rx_fail_nothing: number; rx_fail_partial: number; rx_fail_corrupt: number; + rssi: number; } export interface Inverter { diff --git a/webapp/src/views/HomeView.vue b/webapp/src/views/HomeView.vue index 0aef275a7..ef8fa8ef2 100644 --- a/webapp/src/views/HomeView.vue +++ b/webapp/src/views/HomeView.vue @@ -291,6 +291,16 @@ {{ $n(inverter.radio_stats.tx_re_request) }} + + + {{ $t('home.Rssi') }} + + + + {{ $t('home.dBm', { dbm: $n(inverter.radio_stats.rssi) }) }} + + +