diff --git a/cores/esp32/esp32-hal-spi.c b/cores/esp32/esp32-hal-spi.c index 16ff3b65d10..62b64035af9 100644 --- a/cores/esp32/esp32-hal-spi.c +++ b/cores/esp32/esp32-hal-spi.c @@ -24,6 +24,7 @@ #include "soc/gpio_sig_map.h" #include "soc/rtc.h" #include "driver/periph_ctrl.h" +#include "driver/spi_master.h" #include "esp_system.h" #ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ @@ -136,8 +137,11 @@ static spi_t _spi_bus_array[] = { #endif }; #else -#define SPI_MUTEX_LOCK() do {} while (xSemaphoreTake(spi->lock, portMAX_DELAY) != pdPASS) -#define SPI_MUTEX_UNLOCK() xSemaphoreGive(spi->lock) +#define SPI_MUTEX_LOCK() if (spi_handle != NULL && spi->num == spi_host) { spi_lock_error = spi_device_acquire_bus(spi_handle, portMAX_DELAY); }\ + do {} while ((xSemaphoreTake(spi->lock, portMAX_DELAY) != pdPASS));\ + locked_handle = xTaskGetCurrentTaskHandle(); +#define SPI_MUTEX_UNLOCK() if (spi_handle != NULL && spi_lock_error == ESP_OK) { spi_device_release_bus(spi_handle); }\ + if (locked_handle == xTaskGetCurrentTaskHandle()) { xSemaphoreGive(spi->lock); } static spi_t _spi_bus_array[] = { #if CONFIG_IDF_TARGET_ESP32S2 @@ -158,6 +162,11 @@ static spi_t _spi_bus_array[] = { }; #endif +spi_device_handle_t spi_handle = NULL; +TaskHandle_t locked_handle = NULL; +uint8_t spi_host = 0; +esp_err_t spi_lock_error = ESP_FAIL; + void spiAttachSCK(spi_t * spi, int8_t sck) { if(!spi) { @@ -632,6 +641,13 @@ uint8_t spiGetBitOrder(spi_t * spi) return (spi->dev->ctrl.wr_bit_order | spi->dev->ctrl.rd_bit_order) == 0; } +void spiSetDeviceHandle(void * spi_device_handle, uint8_t spi_host_device) +{ + spi_handle = spi_device_handle; + // Increase number by 1, because SPI_HOST_1 is 0, SPI_HOST_2 is 1 and SPI_HOST_3 is 2. + spi_host = spi_host_device + 1U; +} + void spiSetBitOrder(spi_t * spi, uint8_t bitOrder) { if(!spi) { diff --git a/cores/esp32/esp32-hal-spi.h b/cores/esp32/esp32-hal-spi.h index 89ceefccd60..ca975c75143 100644 --- a/cores/esp32/esp32-hal-spi.h +++ b/cores/esp32/esp32-hal-spi.h @@ -95,7 +95,7 @@ void spiWaitReady(spi_t * spi); uint32_t spiGetClockDiv(spi_t * spi); uint8_t spiGetDataMode(spi_t * spi); uint8_t spiGetBitOrder(spi_t * spi); - +void spiSetDeviceHandle(void * spi_device_handle, uint8_t spi_host_device); /* * Non transaction based lock methods (each locks and unlocks when called) diff --git a/libraries/Ethernet/examples/ETH_W5500/.skip.esp32c3 b/libraries/Ethernet/examples/ETH_W5500/.skip.esp32c3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/Ethernet/examples/ETH_W5500/.skip.esp32s2 b/libraries/Ethernet/examples/ETH_W5500/.skip.esp32s2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/Ethernet/examples/ETH_W5500/.skip.esp32s3 b/libraries/Ethernet/examples/ETH_W5500/.skip.esp32s3 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/Ethernet/examples/ETH_W5500/ETH_W5500.ino b/libraries/Ethernet/examples/ETH_W5500/ETH_W5500.ino new file mode 100644 index 00000000000..2ab2cc44108 --- /dev/null +++ b/libraries/Ethernet/examples/ETH_W5500/ETH_W5500.ino @@ -0,0 +1,92 @@ +/* + This sketch shows the Ethernet event usage + +*/ + +#include + +#define MOSI_GPIO 23 +#define MISO_GPIO 38 +#define SCLK_GPIO 18 +#define CS_GPIO 26 +#define INT_GPIO 34 +#define PHY_RST_GPIO 19 + +static bool eth_connected = false; + +void WiFiEvent(WiFiEvent_t event) +{ + switch (event) { + case ARDUINO_EVENT_ETH_START: + Serial.println("ETH Started"); + //set eth hostname here + ETH.setHostname("esp32-ethernet"); + break; + case ARDUINO_EVENT_ETH_CONNECTED: + Serial.println("ETH Connected"); + break; + case ARDUINO_EVENT_ETH_GOT_IP: + Serial.print("ETH MAC: "); + Serial.print(ETH.macAddress()); + Serial.print(", IPv4: "); + Serial.print(ETH.localIP()); + if (ETH.fullDuplex()) { + Serial.print(", FULL_DUPLEX"); + } + Serial.print(", "); + Serial.print(ETH.linkSpeed()); + Serial.println("Mbps"); + eth_connected = true; + break; + case ARDUINO_EVENT_ETH_DISCONNECTED: + Serial.println("ETH Disconnected"); + eth_connected = false; + break; + case ARDUINO_EVENT_ETH_STOP: + Serial.println("ETH Stopped"); + eth_connected = false; + break; + default: + break; + } +} + +void testClient(const char * host, uint16_t port) +{ + Serial.print("\nconnecting to "); + Serial.println(host); + + WiFiClient client; + if (!client.connect(host, port)) { + Serial.println("connection failed"); + return; + } + client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host); + while (client.connected() && !client.available()); + while (client.available()) { + Serial.write(client.read()); + } + + Serial.println("closing connection\n"); + client.stop(); +} + +void setup() +{ + Serial.begin(115200); + WiFi.onEvent(WiFiEvent); + + std::array mac_address; + WiFi.macAddress(mac_address.data()); + ETH.begin_w5500(mac_address.data(), MOSI_GPIO, MISO_GPIO, SCLK_GPIO, CS_GPIO, INT_GPIO, PHY_RST_GPIO); + SPI.setSpiDeviceHandle(ETH.getSpiDeviceHandle(), ETH.getSpiHost()); +} + + +void loop() +{ + if (eth_connected) { + testClient("google.com", 80); + } + delay(10000); +} diff --git a/libraries/Ethernet/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp index 4b9b5edee2f..a4938b6872b 100644 --- a/libraries/Ethernet/src/ETH.cpp +++ b/libraries/Ethernet/src/ETH.cpp @@ -40,6 +40,12 @@ #include "lwip/err.h" #include "lwip/dns.h" + +#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 3 + #include + #include +#endif + extern void tcpipInit(); #if ESP_IDF_VERSION_MAJOR > 3 @@ -399,6 +405,137 @@ bool ETHClass::begin(uint8_t phy_addr, int power, int mdc, int mdio, eth_phy_typ return true; } +#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 3 +bool ETHClass::beginW5500(uint8_t* mac_address, int8_t mosi_gpio, int8_t miso_gpio, int8_t slck_gpio, int8_t cs_gpio, int8_t int_gpio, int8_t phy_rst_gpio, uint32_t mac_stack_size, int spi_max_transfer_size, spi_common_dma_t spi_dma_channel, uint8_t phy_addr, uint8_t spi_clock_mhz, spi_host_device_t spi_host, eth_phy_type_t type) { + if (type != ETH_PHY_W5500) { + log_e("Using this ETH.begin() method does not support other ethernet modules, besides the W5500."); + return false; + } + else if (mac_address == nullptr) { + log_e("Passed mac address needs to be a valid array the w5500 should be initalized with."); + return false; + } + else if (spi_clock_mhz > 80U) { + log_e("Using a higher spi clock speed than 80MHz is not possible."); + return false; + } + else if (spi_clock_mhz > 30U) { + log_w("Using a higher spi clock speed than 30MHz is possible, but not recommended as it might cause distortions."); + } + + // Initializes TCP/IP network interface (should be called only once in application) + // and create default event loop that is running in the background. + tcpipInit(); + esp_err_t error = tcpip_adapter_set_default_eth_handlers(); + if (error != ESP_OK) { + log_e("Method: (tcpip_adapter_set_default_eth_handlers) failed with error: (%s)", esp_err_to_name(error)); + return false; + } + + // Create instance of esp-netif for SPI Ethernet. + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); + esp_netif_config_t cfg_spi = { + .base = &esp_netif_config, + .stack = ESP_NETIF_NETSTACK_DEFAULT_ETH + }; + esp_netif_t *eth_netif = esp_netif_new(&cfg_spi); + + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + mac_config.rx_task_stack_size = mac_stack_size; + + // Install GPIO ISR handler to be able to service SPI Eth modlues interrupts. + error = gpio_install_isr_service(0); + if (error != ESP_OK) { + log_e("Method: (gpio_install_isr_service) failed with error: (%s)", esp_err_to_name(error)); + return false; + } + + spi_device_handle = nullptr; + spi_bus_config_t buscfg = { + .mosi_io_num = mosi_gpio, + .miso_io_num = miso_gpio, + .sclk_io_num = slck_gpio, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = spi_max_transfer_size + }; + error = spi_bus_initialize(spi_host, &buscfg, spi_dma_channel); + if (error != ESP_OK) { + log_e("Method: (spi_bus_initialize) failed with error: (%s)", esp_err_to_name(error)); + return false; + } + spi_device_interface_config_t devcfg = { + .command_bits = 16, + .address_bits = 8, + .mode = 0, + .clock_speed_hz = spi_clock_mhz * 1000 * 1000, + .spics_io_num = cs_gpio, + .queue_size = 20 + }; + error = spi_bus_add_device(spi_host, &devcfg, &spi_device_handle); + if (error != ESP_OK) { + log_e("Method: (spi_bus_add_device) failed with error: (%s)", esp_err_to_name(error)); + return false; + } + + // W5500 ethernet driver is based on spi driver. + eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_device_handle); + w5500_config.int_gpio_num = int_gpio; + + esp_eth_mac_t* eth_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); + if (eth_mac == nullptr) { + log_e("Method: (esp_eth_mac_new_w5500) failed with error: (%s)", esp_err_to_name(ESP_FAIL)); + return false; + } + + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + phy_config.phy_addr = phy_addr; + phy_config.reset_gpio_num = phy_rst_gpio; + + esp_eth_phy_t *eth_phy = esp_eth_phy_new_w5500(&phy_config); + if (eth_phy == nullptr) { + log_e("Method: (esp_eth_phy_new) failed with error: (%s)", esp_err_to_name(ESP_FAIL)); + return false; + } + + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(eth_mac, eth_phy); + eth_handle = nullptr; + + error = esp_eth_driver_install(ð_config, ð_handle); + if (error != ESP_OK || eth_handle == nullptr) { + log_e("Method: (esp_eth_driver_install) failed with error: (%s)", esp_err_to_name(error)); + return false; + } + + // Set the mac address. + esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_address); + + // Attach Ethernet driver to TCP/IP stack. + error = esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)); + if (error != ESP_OK) { + log_e("Method: (esp_netif_attach) failed with error: (%s)", esp_err_to_name(error)); + return false; + } + + // Start Ethernet driver state machine. + error = esp_eth_start(eth_handle); + if (error != ESP_OK) { + log_e("Method: (esp_eth_start) failed with error: (%s)", esp_err_to_name(error)); + return false; + } + + return true; +} + +spi_device_handle_t& ETHClass::getSpiDeviceHandle(void) { + return spi_device_handle; +} + +spi_host_device_t& ETHClass::getSpiHost(void) { + return spi_host_device; +} +#endif + bool ETHClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) { esp_err_t err = ESP_OK; diff --git a/libraries/Ethernet/src/ETH.h b/libraries/Ethernet/src/ETH.h index 7fc14f59cb3..929a3c72973 100644 --- a/libraries/Ethernet/src/ETH.h +++ b/libraries/Ethernet/src/ETH.h @@ -25,6 +25,10 @@ #include "esp_system.h" #include "esp_eth.h" +#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 3 +#include +#endif + #ifndef ETH_PHY_ADDR #define ETH_PHY_ADDR 0 #endif @@ -53,7 +57,7 @@ typedef enum { ETH_CLOCK_GPIO0_IN, ETH_CLOCK_GPIO0_OUT, ETH_CLOCK_GPIO16_OUT, ETH_CLOCK_GPIO17_OUT } eth_clock_mode_t; #endif -typedef enum { ETH_PHY_LAN8720, ETH_PHY_TLK110, ETH_PHY_RTL8201, ETH_PHY_DP83848, ETH_PHY_DM9051, ETH_PHY_KSZ8041, ETH_PHY_KSZ8081, ETH_PHY_MAX } eth_phy_type_t; +typedef enum { ETH_PHY_LAN8720, ETH_PHY_TLK110, ETH_PHY_RTL8201, ETH_PHY_DP83848, ETH_PHY_DM9051, ETH_PHY_KSZ8041, ETH_PHY_KSZ8081, ETH_PHY_W5500, ETH_PHY_MAX } eth_phy_type_t; #define ETH_PHY_IP101 ETH_PHY_TLK110 class ETHClass { @@ -62,6 +66,10 @@ class ETHClass { bool staticIP; #if ESP_IDF_VERSION_MAJOR > 3 esp_eth_handle_t eth_handle; +#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 3 + spi_device_handle_t spi_device_handle = nullptr; + spi_host_device_t spi_host_device = spi_host_device_t::SPI3_HOST; +#endif protected: bool started; @@ -76,6 +84,12 @@ class ETHClass { bool begin(uint8_t phy_addr=ETH_PHY_ADDR, int power=ETH_PHY_POWER, int mdc=ETH_PHY_MDC, int mdio=ETH_PHY_MDIO, eth_phy_type_t type=ETH_PHY_TYPE, eth_clock_mode_t clk_mode=ETH_CLK_MODE, bool use_mac_from_efuse=false); +#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 3 + bool beginW5500(uint8_t* mac_address, int8_t mosi_gpio, int8_t miso_gpio, int8_t slck_gpio, int8_t cs_gpio, int8_t int_gpio, int8_t phy_rst_gpio, uint32_t mac_stack_size = 2048, int spi_max_transfer_size = 0, spi_common_dma_t spi_dma_channel = spi_common_dma_t::SPI_DMA_CH_AUTO, uint8_t phy_addr = 1, uint8_t spi_clock_mhz = 10, spi_host_device_t spi_host = SPI3_HOST, eth_phy_type_t type = ETH_PHY_W5500); + spi_device_handle_t& getSpiDeviceHandle(void); + spi_host_device_t& getSpiHost(void); +#endif + bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000); const char * getHostname(); diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 23a35c0d357..282d8105c38 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -158,6 +158,11 @@ void SPIClass::setClockDivider(uint32_t clockDiv) SPI_PARAM_UNLOCK(); } +void SPIClass::setSpiDeviceHandle(spi_device_handle_t& spi_device_handle, spi_host_device_t& spi_host_device) +{ + spiSetDeviceHandle(spi_device_handle, spi_host_device); +} + uint32_t SPIClass::getClockDivider() { return spiGetClockDiv(_spi); @@ -348,4 +353,3 @@ SPIClass SPI(VSPI); #else SPIClass SPI(FSPI); #endif - diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index 7f07e5beb0d..f167c6d0aba 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -26,6 +26,7 @@ #include "esp32-hal-spi.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" +#include #define SPI_HAS_TRANSACTION @@ -68,7 +69,14 @@ class SPIClass void setDataMode(uint8_t dataMode); void setFrequency(uint32_t freq); void setClockDivider(uint32_t clockDiv); - + + /** + * @brief Sets the spi device handle so that the underlying SPI driver, also respects when the esp-idf SPI driver attempts to use the SPI. + * @param spi_device_handle Spi device handle gettable via. ETH.getSpiDeviceHandle() + * @param spi_device_handle Spi host device gettable via. ETH.getSpiHost() + */ + void setSpiDeviceHandle(spi_device_handle_t& spi_device_handle, spi_host_device_t& spi_host_device); + uint32_t getClockDivider(); void beginTransaction(SPISettings settings); diff --git a/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update_ETH.ino b/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update_ETH.ino new file mode 100644 index 00000000000..7a723c8511b --- /dev/null +++ b/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update_ETH.ino @@ -0,0 +1,99 @@ +// This sketch provide the functionality of OTA Firmware Upgrade +#include "ETH.h" +#include "HttpsOTAUpdate.h" +// This sketch shows how to implement HTTPS firmware update Over The Air. +// Please provide your WiFi credentials, https URL to the firmware image and the server certificate. + +static const char *url = "https://example.com/firmware.bin"; //state url of your firmware image + +static const char *server_certificate = "-----BEGIN CERTIFICATE-----\n" \ + "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \ + "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \ + "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \ + "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \ + "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \ + "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \ + "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \ + "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \ + "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \ + "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \ + "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \ + "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \ + "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \ + "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \ + "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \ + "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \ + "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \ + "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \ + "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \ + "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \ + "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \ + "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \ + "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \ + "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \ + "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \ + "-----END CERTIFICATE-----"; + +// Replace GPIO pins with the GPIO pins of your W5500 +#define MOSI_GPIO 23 +#define MISO_GPIO 38 +#define SCLK_GPIO 18 +#define CS_GPIO 26 +#define INT_GPIO 34 +#define PHY_RST_GPIO 19 + +static HttpsOTAStatus_t otastatus; + +void HttpEvent(HttpEvent_t *event) +{ + switch(event->event_id) { + case HTTP_EVENT_ERROR: + Serial.println("Http Event Error"); + break; + case HTTP_EVENT_ON_CONNECTED: + Serial.println("Http Event On Connected"); + break; + case HTTP_EVENT_HEADER_SENT: + Serial.println("Http Event Header Sent"); + break; + case HTTP_EVENT_ON_HEADER: + Serial.printf("Http Event On Header, key=%s, value=%s\n", event->header_key, event->header_value); + break; + case HTTP_EVENT_ON_DATA: + break; + case HTTP_EVENT_ON_FINISH: + Serial.println("Http Event On Finish"); + break; + case HTTP_EVENT_DISCONNECTED: + Serial.println("Http Event Disconnected"); + break; + } +} + +void setup(){ + + Serial.begin(115200); + std::array mac_address; + WiFi.macAddress(mac_address.data()); + Serial.print("Attempting to connect to W5500"); + ETH.begin_w5500(mac_address.data(), MOSI_GPIO, MISO_GPIO, SCLK_GPIO, CS_GPIO, INT_GPIO, PHY_RST_GPIO); + SPI.setSpiDeviceHandle(ETH.getSpiDeviceHandle(), ETH.getSpiHost()); + + delay(3000); + + HttpsOTA.onHttpEvent(HttpEvent); + Serial.println("Starting OTA"); + HttpsOTA.begin(url, server_certificate, UpdateNetworkInterface::ETHERNET_IFC, false); + + Serial.println("Please Wait it takes some time ..."); +} + +void loop(){ + otastatus = HttpsOTA.status(); + if(otastatus == HTTPS_OTA_SUCCESS) { + Serial.println("Firmware written successfully. To reboot device, call API ESP.restart() or PUSH restart button on device"); + } else if(otastatus == HTTPS_OTA_FAIL) { + Serial.println("Firmware Upgrade Fail"); + } + delay(1000); +} diff --git a/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update.ino b/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update_WiFi.ino similarity index 97% rename from libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update.ino rename to libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update_WiFi.ino index 6e32ee5bd58..fb56c7b80ed 100644 --- a/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update.ino +++ b/libraries/Update/examples/HTTPS_OTA_Update/HTTPS_OTA_Update_WiFi.ino @@ -82,7 +82,7 @@ void setup(){ HttpsOTA.onHttpEvent(HttpEvent); Serial.println("Starting OTA"); - HttpsOTA.begin(url, server_certificate); + HttpsOTA.begin(url, server_certificate, UpdateNetworkInterface::WIFI_STA_IFC, false); Serial.println("Please Wait it takes some time ..."); } diff --git a/libraries/Update/examples/HTTPS_OTA_Update/Readme.md b/libraries/Update/examples/HTTPS_OTA_Update/Readme.md index 27fdb59612b..50ea54075b5 100644 --- a/libraries/Update/examples/HTTPS_OTA_Update/Readme.md +++ b/libraries/Update/examples/HTTPS_OTA_Update/Readme.md @@ -5,14 +5,15 @@ This sketch allows Arduino user to perform Over The Air (OTA) firmware upgrade. ## HttpsOTA.begin(const char * url, const char * server_certificate, bool skip_cert_common_name_check) -Main API which starts firmware upgrade +Begins the Https OTA update process in another task in the background, requires atleast 16KB of free heap space to use the array that will store the HTTPS packets that are downloaded. ### Parameters -* url : URL for the uploaded firmware image -* server_certificate : Provide the ota server certificate for authentication via HTTPS -* skip_cert_common_name_check : Skip any validation of server certificate CN field +* url : Url to the binary that should be downloaded and flashed onto the 2nd ota data partition +* server_certificate : Root server certificate of the website we are downloading the binary from +* update_network_interface : Underlying network interface we want to download the binary over (Ethernet, WLan, etc.). Ethernet requries using the ETH.h base class +* skip_cert_common_name_check : Wheter the certificate check should be skipped, generally not recommended if the update process needs to be secure, because this makes us vulnerable to man in the middle attacks where an attacker pretends to be the server and we might not download the actual binary we want too, but a binary sent by an attacker instead -The default value provided to skip_cert_common_name_check is true +The default value provided to update_network_interface is UpdateNetworkInterface::DEFAULT_INTERFACE and skip_cert_common_name_check is true ## HttpsOTA.onHttpEvent(function) diff --git a/libraries/Update/src/HttpsOTAUpdate.cpp b/libraries/Update/src/HttpsOTAUpdate.cpp index 106559bd3a4..5ecfba82548 100644 --- a/libraries/Update/src/HttpsOTAUpdate.cpp +++ b/libraries/Update/src/HttpsOTAUpdate.cpp @@ -8,6 +8,7 @@ */ #include #include +#include #include #include @@ -16,6 +17,7 @@ #include "esp32-hal-log.h" #include "esp_http_client.h" #include "esp_https_ota.h" +#include "esp_netif.h" #include "HttpsOTAUpdate.h" #define OTA_TASK_STACK_SIZE 9216 @@ -26,6 +28,7 @@ static esp_http_client_config_t config; static HttpEventCb cb; static EventGroupHandle_t ota_status = NULL;//check for ota status static EventBits_t set_bit; +static ifreq freq; const int OTA_IDLE_BIT = BIT0; const int OTA_UPDATING_BIT = BIT1; @@ -34,7 +37,11 @@ const int OTA_FAIL_BIT = BIT3; esp_err_t http_event_handler(esp_http_client_event_t *event) { - cb(event); + // OTA update will not be stopped if the callback wasn't set, + // because we return ESP_OK either way. + if (cb != nullptr) { + cb(event); + } return ESP_OK; } @@ -84,13 +91,18 @@ void HttpsOTAUpdateClass::onHttpEvent(HttpEventCb cbEvent) cb = cbEvent; } -void HttpsOTAUpdateClass::begin(const char *url, const char *cert_pem, bool skip_cert_common_name_check) +void HttpsOTAUpdateClass::begin(const char *url, const char *cert_pem, UpdateNetworkInterface network_interface, bool skip_cert_common_name_check) { config.url = url; config.cert_pem = cert_pem; config.skip_cert_common_name_check = skip_cert_common_name_check; config.event_handler = http_event_handler; + esp_netif_inherent_config_t network_interface_config; + get_netif_config_from_type(network_interface, network_interface_config); + const esp_err_t error = get_bound_interface_name(network_interface_config.if_key, freq); + config.if_name = error != ESP_OK ? nullptr : &freq; + if(!ota_status) { ota_status = xEventGroupCreate(); if(!ota_status) { @@ -104,4 +116,57 @@ void HttpsOTAUpdateClass::begin(const char *url, const char *cert_pem, bool skip } } +esp_err_t HttpsOTAUpdateClass::get_netif_config_from_type(UpdateNetworkInterface network_interface, esp_netif_inherent_config_t& netif_inherent_config) +{ + esp_err_t error = ESP_OK; + switch (network_interface) { + case UpdateNetworkInterface::DEFAULT_INTERFACE: + error = ESP_ERR_ESP_NETIF_IF_NOT_READY; + break; + case UpdateNetworkInterface::WIFI_STA: + netif_inherent_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); + break; + case UpdateNetworkInterface::WIFI_AP: + netif_inherent_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP(); + break; + case UpdateNetworkInterface::ETHERNET: + netif_inherent_config = ESP_NETIF_INHERENT_DEFAULT_ETH(); + break; + case UpdateNetworkInterface::POINT_TO_POINT: + netif_inherent_config = ESP_NETIF_INHERENT_DEFAULT_PPP(); + break; + case UpdateNetworkInterface::SERIAL_LINE: + netif_inherent_config = ESP_NETIF_INHERENT_DEFAULT_SLIP(); + break; + default: + error = ESP_ERR_ESP_NETIF_IF_NOT_READY; + log_e("Unexpected UpdateNetworkInterface argument (%u).", static_cast::type>(network_interface)); + break; + } + return error; +} + +esp_err_t HttpsOTAUpdateClass::get_bound_interface_name(const char* is_key, ifreq& freq) +{ + if (is_key == nullptr) { + return ESP_ERR_INVALID_ARG; + } + log_d("Trying to get network interface with key (%s).", is_key); + + esp_netif_t* netif = esp_netif_get_handle_from_ifkey(is_key); + if (netif == nullptr) { + log_e("Attempting to get network interface returned NULL."); + return ESP_ERR_ESP_NETIF_IF_NOT_READY; + } + + const esp_err_t error = esp_netif_get_netif_impl_name(netif, freq.ifr_name); + if (error != ESP_OK) { + log_e("Error (%s) while getting bound interface name.", esp_err_to_name(error)); + return error; + } + + log_d("Bound interface name is (%s).", freq.ifr_name); + return error; +} + HttpsOTAUpdateClass HttpsOTA; diff --git a/libraries/Update/src/HttpsOTAUpdate.h b/libraries/Update/src/HttpsOTAUpdate.h index 076e4f7894b..aabb8d24bc6 100644 --- a/libraries/Update/src/HttpsOTAUpdate.h +++ b/libraries/Update/src/HttpsOTAUpdate.h @@ -1,6 +1,8 @@ #ifndef HTTPSOTAUPDATE_H #define HTTPSOTAUPDATE_H #include "esp_http_client.h" +#include "esp_netif_types.h" +#include "UpdateNetworkInterface.h" #define HttpEvent_t esp_http_client_event_t typedef enum @@ -15,9 +17,13 @@ typedef enum class HttpsOTAUpdateClass { public: - void begin(const char *url, const char *cert_pem, bool skip_cert_common_name_check = true); + void begin(const char *url, const char *cert_pem, UpdateNetworkInterface network_interface = UpdateNetworkInterface::DEFAULT_INTERFACE, bool skip_cert_common_name_check = false); void onHttpEvent(void (*http_event_cb_t)(HttpEvent_t *)); HttpsOTAStatus_t status(); + + private: + esp_err_t get_netif_config_from_type(UpdateNetworkInterface network_interface, esp_netif_inherent_config_t& netif_inherent_config); + esp_err_t get_bound_interface_name(const char* is_key, ifreq& freq); }; extern HttpsOTAUpdateClass HttpsOTA; diff --git a/libraries/Update/src/UpdateNetworkInterface.h b/libraries/Update/src/UpdateNetworkInterface.h new file mode 100644 index 00000000000..8b43b49ca35 --- /dev/null +++ b/libraries/Update/src/UpdateNetworkInterface.h @@ -0,0 +1,15 @@ +#ifndef UPDATE_NETWORK_INTERFACE +#define UPDATE_NETWORK_INTERFACE + +#include + +enum class UpdateNetworkInterface : const uint8_t { + DEFAULT_IFC, + WIFI_STA_IFC, + WIFI_AP_IFC, + ETHERNET_IFC, + POINT_TO_POINT_IFC, + SERIAL_LINE_IFC +}; + +#endif // UPDATE_NETWORK_INTERFACE