diff --git a/docs/DeviceProfiles/opendtu_fusion.json b/docs/DeviceProfiles/opendtu_fusion.json index f33dc47d..990f4c46 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,122 @@ "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 + } + }, + { + "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 + } } -] \ No newline at end of file +] diff --git a/include/NetworkSettings.h b/include/NetworkSettings.h index 433867e9..ea3869cb 100644 --- a/include/NetworkSettings.h +++ b/include/NetworkSettings.h @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include "W5500.h" #include #include #include @@ -23,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() @@ -53,7 +54,7 @@ public: 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: @@ -83,6 +84,7 @@ private: 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 8b35ac71..8fce33e9 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; @@ -72,6 +79,7 @@ public: bool isValidNrf24Config() const; bool isValidCmt2300Config() const; + bool isValidW5500Config() const; bool isValidEthConfig() const; bool isValidHuaweiConfig() const; diff --git a/include/W5500.h b/include/W5500.h new file mode 100644 index 00000000..d85cb016 --- /dev/null +++ b/include/W5500.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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: + 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/include/WebApi.h b/include/WebApi.h index 0a433b8c..c995ecfc 100644 --- a/include/WebApi.h +++ b/include/WebApi.h @@ -38,6 +38,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 cf7beecc..b3194319 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 4a29fff5..e02f9a8c 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); @@ -27,6 +28,7 @@ private: void onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len); AsyncWebSocket _ws; + AuthenticationMiddleware _simpleDigestAuth; uint32_t _lastPublishOnBatteryFull = 0; uint32_t _lastPublishVictron = 0; diff --git a/lib/CMT2300a/cmt_spi3.c b/lib/CMT2300a/cmt_spi3.c deleted file mode 100644 index 59aad36f..00000000 --- a/lib/CMT2300a/cmt_spi3.c +++ /dev/null @@ -1,142 +0,0 @@ -#include "cmt_spi3.h" -#include -#include -#include // for esp_rom_gpio_connect_out_signal - -SemaphoreHandle_t paramLock = NULL; -#define SPI_PARAM_LOCK() \ - do { \ - } 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 uint32_t spi_speed) -{ - paramLock = xSemaphoreCreateMutex(); - - 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, - .max_transfer_sz = 32, - }; - spi_device_interface_config_t devcfg = { - .command_bits = 1, - .address_bits = 7, - .dummy_bits = 0, - .mode = 0, // SPI mode 0 - .cs_ena_pretrans = 1, - .cs_ena_posttrans = 1, - .clock_speed_hz = spi_speed, - .spics_io_num = pin_cs, - .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, - .queue_size = 1, - .pre_cb = NULL, - .post_cb = NULL, - }; - - 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 = { - .command_bits = 0, - .address_bits = 0, - .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 - .clock_speed_hz = spi_speed, - .spics_io_num = pin_fcs, - .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, - .queue_size = 1, - .pre_cb = NULL, - .post_cb = NULL, - }; - 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) -{ - uint8_t tx_data; - tx_data = ~dat; - spi_transaction_t t = { - .cmd = 1, - .addr = ~addr, - .length = 8, - .tx_buffer = &tx_data, - .rx_buffer = NULL - }; - 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) -{ - uint8_t rx_data; - spi_transaction_t t = { - .cmd = 0, - .addr = ~addr, - .length = 8, - .rxlength = 8, - .tx_buffer = NULL, - .rx_buffer = &rx_data - }; - SPI_PARAM_LOCK(); - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_reg, &t)); - SPI_PARAM_UNLOCK(); - delayMicroseconds(100); - return rx_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 - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); - delayMicroseconds(4); // > 4 us - } - SPI_PARAM_UNLOCK(); -} - -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 - }; - - SPI_PARAM_LOCK(); - for (uint8_t i = 0; i < len; i++) { - ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); - delayMicroseconds(4); // > 4 us - buf[i] = rx_data; - } - SPI_PARAM_UNLOCK(); -} diff --git a/lib/CMT2300a/cmt_spi3.cpp b/lib/CMT2300a/cmt_spi3.cpp new file mode 100644 index 00000000..28fdc8ae --- /dev/null +++ b/lib/CMT2300a/cmt_spi3.cpp @@ -0,0 +1,155 @@ +#include "cmt_spi3.h" +#include +#include +#include + +SemaphoreHandle_t paramLock = NULL; +#define SPI_PARAM_LOCK() \ + do { \ + } while (xSemaphoreTake(paramLock, portMAX_DELAY) != pdPASS) +#define SPI_PARAM_UNLOCK() xSemaphoreGive(paramLock) + +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) +{ + paramLock = xSemaphoreCreateMutex(); + + auto bus_config = std::make_shared( + static_cast(pin_sdio), + GPIO_NUM_NC, + static_cast(pin_clk) + ); + + 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 = 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 = -1, // CS handled by callbacks + .flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_3WIRE, + .queue_size = 1, + .pre_cb = pre_cb, + .post_cb = post_cb, + }; + + spi = SpiManagerInst.alloc_device("", bus_config, device_config); + if (!spi) + ESP_ERROR_CHECK(ESP_FAIL); + + 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)); + + 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_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, reinterpret_cast(&trans))); + SPI_PARAM_UNLOCK(); +} + +uint8_t cmt_spi3_read(const uint8_t addr) +{ + uint8_t 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, reinterpret_cast(&trans))); + SPI_PARAM_UNLOCK(); + return data; +} + +void cmt_spi3_write_fifo(const uint8_t* buf, const uint16_t len) +{ + spi_transaction_t trans { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = 8, + .rxlength = 0, + .user = &cs_fifo, // CS for FIFO access + .tx_buffer = nullptr, + .rx_buffer = nullptr, + }; + + SPI_PARAM_LOCK(); + spi_device_acquire_bus(spi, portMAX_DELAY); + for (uint8_t i = 0; i < len; i++) { + trans.tx_buffer = buf + i; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans)); + } + spi_device_release_bus(spi); + SPI_PARAM_UNLOCK(); +} + +void cmt_spi3_read_fifo(uint8_t* buf, const uint16_t len) +{ + spi_transaction_t trans { + .flags = 0, + .cmd = 0, + .addr = 0, + .length = 0, + .rxlength = 8, + .user = &cs_fifo, // CS for FIFO access + .tx_buffer = nullptr, + .rx_buffer = nullptr, + }; + + SPI_PARAM_LOCK(); + spi_device_acquire_bus(spi, portMAX_DELAY); + for (uint8_t i = 0; i < len; i++) { + trans.rx_buffer = buf + i; + ESP_ERROR_CHECK(spi_device_polling_transmit(spi, &trans)); + } + spi_device_release_bus(spi); + SPI_PARAM_UNLOCK(); +} diff --git a/lib/CMT2300a/cmt_spi3.h b/lib/CMT2300a/cmt_spi3.h index 6d3a67b6..16655dba 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 diff --git a/lib/SpiManager/library.json b/lib/SpiManager/library.json new file mode 100644 index 00000000..22e5ddc9 --- /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 00000000..0dcb5e4f --- /dev/null +++ b/lib/SpiManager/src/SpiBus.cpp @@ -0,0 +1,52 @@ +// 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) +{ + 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 00000000..1ca79c7c --- /dev/null +++ b/lib/SpiManager/src/SpiBus.h @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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 00000000..64234d65 --- /dev/null +++ b/lib/SpiManager/src/SpiBusConfig.cpp @@ -0,0 +1,71 @@ +// 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) +{ + 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 00000000..736b8951 --- /dev/null +++ b/lib/SpiManager/src/SpiBusConfig.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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 00000000..e353d04b --- /dev/null +++ b/lib/SpiManager/src/SpiCallback.cpp @@ -0,0 +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; + }; + + 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 00000000..98222b1a --- /dev/null +++ b/lib/SpiManager/src/SpiCallback.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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 00000000..d727a96e --- /dev/null +++ b/lib/SpiManager/src/SpiManager.cpp @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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]) + 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; +} + +#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) + 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 00000000..1e8f6e1b --- /dev/null +++ b/lib/SpiManager/src/SpiManager.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#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; + +#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); + +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; diff --git a/platformio.ini b/platformio.ini index ddae0e7e..a77d18a4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -19,7 +19,7 @@ extra_configs = custom_ci_action = generic_esp32_4mb_no_ota,generic_esp32_8mb,generic_esp32s3,generic_esp32s3_usb framework = arduino -platform = espressif32@6.8.1 +platform = espressif32@6.9.0 platform_packages = platformio/tool-mklittlefs @@ -243,6 +243,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 @@ -266,3 +267,32 @@ build_flags = ${env.build_flags} -DCMT_SDIO=5 -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 + +[env:opendtufusionv2_poe] +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/InverterSettings.cpp b/src/InverterSettings.cpp index 3be1927c..d2d92919 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/NetworkSettings.cpp b/src/NetworkSettings.cpp index 2dd54580..b59be66d 100644 --- a/src/NetworkSettings.cpp +++ b/src/NetworkSettings.cpp @@ -8,10 +8,10 @@ #include "SyslogLogger.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)) @@ -32,6 +32,23 @@ 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 = 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 + 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(); scheduler.addTask(_loopTask); @@ -100,7 +117,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; @@ -171,11 +188,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() @@ -405,6 +417,9 @@ 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 1ab17b17..b69842c0 100644 --- a/src/PinMapping.cpp +++ b/src/PinMapping.cpp @@ -84,6 +84,54 @@ #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 + +#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 + #ifndef VICTRON_PIN_TX #define VICTRON_PIN_TX -1 #endif @@ -197,6 +245,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 @@ -286,6 +341,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 @@ -359,9 +421,21 @@ 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; + return _pinMapping.eth_enabled + && _pinMapping.eth_mdc >= 0 + && _pinMapping.eth_mdio >= 0; } bool PinMappingClass::isValidHuaweiConfig() const diff --git a/src/W5500.cpp b/src/W5500.cpp new file mode 100644 index 00000000..bf539434 --- /dev/null +++ b/src/W5500.cpp @@ -0,0 +1,153 @@ +// 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); + +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); + gpio_set_direction(static_cast(pin_rst), GPIO_MODE_OUTPUT); + + gpio_reset_pin(static_cast(pin_cs)); + gpio_reset_pin(static_cast(pin_int)); + + 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 + .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 = SpiManagerInst.alloc_device("", bus_config, device_config); + if (!spi) + return nullptr; + + // Reset sequence + delayMicroseconds(500); + gpio_set_level(static_cast(pin_rst), 1); + delayMicroseconds(1000); + + if (!connection_check_spi(spi)) + return nullptr; + if (!connection_check_interrupt(static_cast(pin_int))) + return nullptr; + + // 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))); +} + +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); +} + +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; +} diff --git a/src/WebApi.cpp b/src/WebApi.cpp index c2dcf3f1..ffd212dd 100644 --- a/src/WebApi.cpp +++ b/src/WebApi.cpp @@ -47,6 +47,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_device.cpp b/src/WebApi_device.cpp index 96864151..405c1dea 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; diff --git a/src/WebApi_security.cpp b/src/WebApi_security.cpp index ddd8bb50..6be21ca6 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 1f1efcb2..51035f6f 100644 --- a/src/WebApi_ws_console.cpp +++ b/src/WebApi_ws_console.cpp @@ -21,16 +21,30 @@ 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; } + + _ws.enable(false); + _simpleDigestAuth.setPassword(config.Security.Password); + _ws.addMiddleware(&_simpleDigestAuth); + _ws.closeAll(); + _ws.enable(true); } 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 aacb9659..04a3647d 100644 --- a/src/WebApi_ws_live.cpp +++ b/src/WebApi_ws_live.cpp @@ -40,18 +40,31 @@ 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; } + + _ws.enable(false); + _simpleDigestAuth.setPassword(config.Security.Password); + _ws.addMiddleware(&_simpleDigestAuth); + _ws.closeAll(); + _ws.enable(true); } 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::generateOnBatteryJsonResponse(JsonVariant& root, bool all) diff --git a/src/main.cpp b/src/main.cpp index 6faa1378..0be39e41 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,12 +38,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 diff --git a/webapp/package.json b/webapp/package.json index ac5161ff..5f954fce 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", @@ -40,11 +40,11 @@ "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", - "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 f804bf6d..b6569670 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" @@ -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" @@ -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"