From 9238ac9b33941a24a47741a1d3c3ab7fd36156f2 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Mon, 18 Sep 2023 19:02:17 +0100 Subject: [PATCH 01/70] Merge RTD to ESP-Docs --- docs/Makefile | 28 -------- docs/_static/arduino-esp32_versions.js | 16 +++++ docs/{source => }/_static/arduino-ide.png | Bin .../_static/arduino_i2c_master.png | Bin .../_static/arduino_i2c_slave.png | Bin docs/{source => }/_static/cross.png | Bin .../_static/esp32-c3_devkitM-1_pinlayout.png | Bin .../_static/esp32-s2_saola1_pinlayout.png | Bin .../_static/esp32_devkitC_pinlayout.png | Bin .../_static/external_library_test_pr.png | Bin .../external_library_test_schedule.png | Bin docs/{source => }/_static/gpio_output.png | Bin docs/{source => }/_static/gpio_pullup.png | Bin docs/{source => }/_static/green_checkmark.png | Bin .../install_guide_boards_manager_esp32.png | Bin .../install_guide_boards_manager_url.png | Bin .../_static/install_guide_preferences.png | Bin docs/{source => }/_static/logo_arduino.png | Bin docs/{source => }/_static/logo_espressif.png | Bin docs/{source => }/_static/logo_linux.png | Bin docs/{source => }/_static/logo_macos.png | Bin docs/{source => }/_static/logo_pio.png | Bin docs/{source => }/_static/logo_windows.png | Bin docs/{source => }/_static/ota_esp32_login.png | Bin .../{source => }/_static/ota_esp32_upload.png | Bin .../_static/ota_esp32_verbose.png | Bin .../_static/ota_export_to_binary.png | Bin docs/{source => }/_static/soc-module.png | Bin .../tutorials/basic/tutorial_basic_ide.png | Bin .../tutorial_peripheral_diagram.png | Bin docs/{source => }/_static/usb_msc_drive.png | Bin docs/{source => }/_static/warning.png | Bin docs/{source => }/_static/wifi_esp32_ap.png | Bin docs/{source => }/_static/wifi_esp32_sta.png | Bin docs/{source => }/_static/win-gui-1.png | Bin docs/{source => }/_static/win-gui-2.png | Bin docs/{source => }/_static/win-gui-3.png | Bin docs/{source => }/_static/win-gui-4.png | Bin docs/{source => }/_static/win-gui-5.png | Bin .../{source => }/_static/win-gui-update-1.png | Bin .../{source => }/_static/win-gui-update-2.png | Bin docs/conf_common.py | 30 ++++++++ docs/{source => en}/advanced_utils.rst | 0 docs/{source => en}/api/adc.rst | 0 docs/{source => en}/api/ble.rst | 0 docs/{source => en}/api/bluetooth.rst | 0 docs/{source => en}/api/dac.rst | 0 docs/{source => en}/api/deepsleep.rst | 0 docs/{source => en}/api/espnow.rst | 0 docs/{source => en}/api/ethernet.rst | 0 docs/{source => en}/api/gpio.rst | 0 docs/{source => en}/api/hall_sensor.rst | 0 docs/{source => en}/api/i2c.rst | 4 +- docs/{source => en}/api/i2s.rst | 0 docs/{source => en}/api/insights.rst | 0 docs/{source => en}/api/ledc.rst | 0 docs/{source => en}/api/preferences.rst | 0 docs/{source => en}/api/pulse_counter.rst | 0 docs/{source => en}/api/rainmaker.rst | 0 docs/{source => en}/api/reset_reason.rst | 0 docs/{source => en}/api/rmt.rst | 0 docs/{source => en}/api/sdio.rst | 0 docs/{source => en}/api/sdmmc.rst | 0 docs/{source => en}/api/sigmadelta.rst | 0 docs/{source => en}/api/spi.rst | 0 docs/{source => en}/api/timer.rst | 0 docs/{source => en}/api/touch.rst | 0 docs/{source => en}/api/usb.rst | 0 docs/{source => en}/api/usb_cdc.rst | 0 docs/{source => en}/api/usb_msc.rst | 0 docs/{source => en}/api/wifi.rst | 4 +- .../boards/ESP32-C3-DevKitM-1.rst | 2 +- .../{source => en}/boards/ESP32-DevKitC-1.rst | 2 +- .../boards/ESP32-S2-Saola-1.rst | 2 +- docs/{source => en}/boards/boards.rst | 4 +- docs/{source => en}/boards/generic.rst | 0 docs/{source => en}/common/datasheet.inc | 0 docs/en/conf.py | 25 +++++++ docs/{source => en}/contributing.rst | 0 docs/{source => en}/esp-idf_component.rst | 0 .../external_libraries_test.rst | 10 +-- docs/{source => en}/faq.rst | 0 docs/{source => en}/getting_started.rst | 10 +-- docs/{source => en}/guides/core_debug.rst | 0 .../guides/docs_contributing.rst | 2 +- docs/{source => en}/guides/guides.rst | 0 docs/{source => en}/guides/tools_menu.rst | 2 +- docs/{source => en}/index.rst | 0 docs/{source => en}/installing.rst | 30 ++++---- docs/{source => en}/lib_builder.rst | 0 docs/{source => en}/libraries.rst | 0 docs/{source => en}/make.rst | 0 docs/{source => en}/ota_web_update.rst | 8 +-- docs/{source => en}/troubleshooting.rst | 0 docs/{source => en}/tutorials/basic.rst | 2 +- docs/{source => en}/tutorials/blink.rst | 0 .../tutorials/cdc_dfu_flash.rst | 0 docs/{source => en}/tutorials/io_mux.rst | 2 +- .../tutorials/partition_table.rst | 0 docs/{source => en}/tutorials/preferences.rst | 0 docs/{source => en}/tutorials/tutorials.rst | 0 docs/make.bat | 35 --------- docs/requirements.txt | 7 +- docs/source/conf.py | 68 ------------------ 104 files changed, 115 insertions(+), 178 deletions(-) delete mode 100644 docs/Makefile create mode 100644 docs/_static/arduino-esp32_versions.js rename docs/{source => }/_static/arduino-ide.png (100%) rename docs/{source => }/_static/arduino_i2c_master.png (100%) rename docs/{source => }/_static/arduino_i2c_slave.png (100%) rename docs/{source => }/_static/cross.png (100%) rename docs/{source => }/_static/esp32-c3_devkitM-1_pinlayout.png (100%) rename docs/{source => }/_static/esp32-s2_saola1_pinlayout.png (100%) rename docs/{source => }/_static/esp32_devkitC_pinlayout.png (100%) rename docs/{source => }/_static/external_library_test_pr.png (100%) rename docs/{source => }/_static/external_library_test_schedule.png (100%) rename docs/{source => }/_static/gpio_output.png (100%) rename docs/{source => }/_static/gpio_pullup.png (100%) rename docs/{source => }/_static/green_checkmark.png (100%) rename docs/{source => }/_static/install_guide_boards_manager_esp32.png (100%) rename docs/{source => }/_static/install_guide_boards_manager_url.png (100%) rename docs/{source => }/_static/install_guide_preferences.png (100%) rename docs/{source => }/_static/logo_arduino.png (100%) rename docs/{source => }/_static/logo_espressif.png (100%) rename docs/{source => }/_static/logo_linux.png (100%) rename docs/{source => }/_static/logo_macos.png (100%) rename docs/{source => }/_static/logo_pio.png (100%) rename docs/{source => }/_static/logo_windows.png (100%) rename docs/{source => }/_static/ota_esp32_login.png (100%) rename docs/{source => }/_static/ota_esp32_upload.png (100%) rename docs/{source => }/_static/ota_esp32_verbose.png (100%) rename docs/{source => }/_static/ota_export_to_binary.png (100%) rename docs/{source => }/_static/soc-module.png (100%) rename docs/{source => }/_static/tutorials/basic/tutorial_basic_ide.png (100%) rename docs/{source => }/_static/tutorials/peripherals/tutorial_peripheral_diagram.png (100%) rename docs/{source => }/_static/usb_msc_drive.png (100%) rename docs/{source => }/_static/warning.png (100%) rename docs/{source => }/_static/wifi_esp32_ap.png (100%) rename docs/{source => }/_static/wifi_esp32_sta.png (100%) rename docs/{source => }/_static/win-gui-1.png (100%) rename docs/{source => }/_static/win-gui-2.png (100%) rename docs/{source => }/_static/win-gui-3.png (100%) rename docs/{source => }/_static/win-gui-4.png (100%) rename docs/{source => }/_static/win-gui-5.png (100%) rename docs/{source => }/_static/win-gui-update-1.png (100%) rename docs/{source => }/_static/win-gui-update-2.png (100%) create mode 100644 docs/conf_common.py rename docs/{source => en}/advanced_utils.rst (100%) rename docs/{source => en}/api/adc.rst (100%) rename docs/{source => en}/api/ble.rst (100%) rename docs/{source => en}/api/bluetooth.rst (100%) rename docs/{source => en}/api/dac.rst (100%) rename docs/{source => en}/api/deepsleep.rst (100%) rename docs/{source => en}/api/espnow.rst (100%) rename docs/{source => en}/api/ethernet.rst (100%) rename docs/{source => en}/api/gpio.rst (100%) rename docs/{source => en}/api/hall_sensor.rst (100%) rename docs/{source => en}/api/i2c.rst (99%) rename docs/{source => en}/api/i2s.rst (100%) rename docs/{source => en}/api/insights.rst (100%) rename docs/{source => en}/api/ledc.rst (100%) rename docs/{source => en}/api/preferences.rst (100%) rename docs/{source => en}/api/pulse_counter.rst (100%) rename docs/{source => en}/api/rainmaker.rst (100%) rename docs/{source => en}/api/reset_reason.rst (100%) rename docs/{source => en}/api/rmt.rst (100%) rename docs/{source => en}/api/sdio.rst (100%) rename docs/{source => en}/api/sdmmc.rst (100%) rename docs/{source => en}/api/sigmadelta.rst (100%) rename docs/{source => en}/api/spi.rst (100%) rename docs/{source => en}/api/timer.rst (100%) rename docs/{source => en}/api/touch.rst (100%) rename docs/{source => en}/api/usb.rst (100%) rename docs/{source => en}/api/usb_cdc.rst (100%) rename docs/{source => en}/api/usb_msc.rst (100%) rename docs/{source => en}/api/wifi.rst (99%) rename docs/{source => en}/boards/ESP32-C3-DevKitM-1.rst (98%) rename docs/{source => en}/boards/ESP32-DevKitC-1.rst (98%) rename docs/{source => en}/boards/ESP32-S2-Saola-1.rst (98%) rename docs/{source => en}/boards/boards.rst (97%) rename docs/{source => en}/boards/generic.rst (100%) rename docs/{source => en}/common/datasheet.inc (100%) create mode 100644 docs/en/conf.py rename docs/{source => en}/contributing.rst (100%) rename docs/{source => en}/esp-idf_component.rst (100%) rename docs/{source => en}/external_libraries_test.rst (94%) rename docs/{source => en}/faq.rst (100%) rename docs/{source => en}/getting_started.rst (95%) rename docs/{source => en}/guides/core_debug.rst (100%) rename docs/{source => en}/guides/docs_contributing.rst (99%) rename docs/{source => en}/guides/guides.rst (100%) rename docs/{source => en}/guides/tools_menu.rst (99%) rename docs/{source => en}/index.rst (100%) rename docs/{source => en}/installing.rst (94%) rename docs/{source => en}/lib_builder.rst (100%) rename docs/{source => en}/libraries.rst (100%) rename docs/{source => en}/make.rst (100%) rename docs/{source => en}/ota_web_update.rst (93%) rename docs/{source => en}/troubleshooting.rst (100%) rename docs/{source => en}/tutorials/basic.rst (97%) rename docs/{source => en}/tutorials/blink.rst (100%) rename docs/{source => en}/tutorials/cdc_dfu_flash.rst (100%) rename docs/{source => en}/tutorials/io_mux.rst (98%) rename docs/{source => en}/tutorials/partition_table.rst (100%) rename docs/{source => en}/tutorials/preferences.rst (100%) rename docs/{source => en}/tutorials/tutorials.rst (100%) delete mode 100644 docs/make.bat delete mode 100644 docs/source/conf.py diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index ba5447de969..00000000000 --- a/docs/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build - -LINKCHECKDIR = build/linkcheck - -.PHONY: checklinks - checklinks: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(LINKCHECKDIR) - @echo - @echo "Check finished. Report is in $(LINKCHECKDIR)." - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/arduino-esp32_versions.js b/docs/_static/arduino-esp32_versions.js new file mode 100644 index 00000000000..b4b772d5c45 --- /dev/null +++ b/docs/_static/arduino-esp32_versions.js @@ -0,0 +1,16 @@ +var DOCUMENTATION_VERSIONS = { + DEFAULTS: { has_targets: false, + supported_targets: [ "esp32" ] + }, + VERSIONS: [ + { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, + ], + IDF_TARGETS: [ + { text: "ESP32", value: "esp32"}, + { text: "ESP32-S2", value: "esp32s2"}, + { text: "ESP32-S3", value: "esp32s3"}, + { text: "ESP32-C3", value: "esp32c3"}, + { text: "ESP32-H2", value: "esp32h2"}, + { text: "ESP32C6", value: "esp32c6"}, + ] +}; \ No newline at end of file diff --git a/docs/source/_static/arduino-ide.png b/docs/_static/arduino-ide.png similarity index 100% rename from docs/source/_static/arduino-ide.png rename to docs/_static/arduino-ide.png diff --git a/docs/source/_static/arduino_i2c_master.png b/docs/_static/arduino_i2c_master.png similarity index 100% rename from docs/source/_static/arduino_i2c_master.png rename to docs/_static/arduino_i2c_master.png diff --git a/docs/source/_static/arduino_i2c_slave.png b/docs/_static/arduino_i2c_slave.png similarity index 100% rename from docs/source/_static/arduino_i2c_slave.png rename to docs/_static/arduino_i2c_slave.png diff --git a/docs/source/_static/cross.png b/docs/_static/cross.png similarity index 100% rename from docs/source/_static/cross.png rename to docs/_static/cross.png diff --git a/docs/source/_static/esp32-c3_devkitM-1_pinlayout.png b/docs/_static/esp32-c3_devkitM-1_pinlayout.png similarity index 100% rename from docs/source/_static/esp32-c3_devkitM-1_pinlayout.png rename to docs/_static/esp32-c3_devkitM-1_pinlayout.png diff --git a/docs/source/_static/esp32-s2_saola1_pinlayout.png b/docs/_static/esp32-s2_saola1_pinlayout.png similarity index 100% rename from docs/source/_static/esp32-s2_saola1_pinlayout.png rename to docs/_static/esp32-s2_saola1_pinlayout.png diff --git a/docs/source/_static/esp32_devkitC_pinlayout.png b/docs/_static/esp32_devkitC_pinlayout.png similarity index 100% rename from docs/source/_static/esp32_devkitC_pinlayout.png rename to docs/_static/esp32_devkitC_pinlayout.png diff --git a/docs/source/_static/external_library_test_pr.png b/docs/_static/external_library_test_pr.png similarity index 100% rename from docs/source/_static/external_library_test_pr.png rename to docs/_static/external_library_test_pr.png diff --git a/docs/source/_static/external_library_test_schedule.png b/docs/_static/external_library_test_schedule.png similarity index 100% rename from docs/source/_static/external_library_test_schedule.png rename to docs/_static/external_library_test_schedule.png diff --git a/docs/source/_static/gpio_output.png b/docs/_static/gpio_output.png similarity index 100% rename from docs/source/_static/gpio_output.png rename to docs/_static/gpio_output.png diff --git a/docs/source/_static/gpio_pullup.png b/docs/_static/gpio_pullup.png similarity index 100% rename from docs/source/_static/gpio_pullup.png rename to docs/_static/gpio_pullup.png diff --git a/docs/source/_static/green_checkmark.png b/docs/_static/green_checkmark.png similarity index 100% rename from docs/source/_static/green_checkmark.png rename to docs/_static/green_checkmark.png diff --git a/docs/source/_static/install_guide_boards_manager_esp32.png b/docs/_static/install_guide_boards_manager_esp32.png similarity index 100% rename from docs/source/_static/install_guide_boards_manager_esp32.png rename to docs/_static/install_guide_boards_manager_esp32.png diff --git a/docs/source/_static/install_guide_boards_manager_url.png b/docs/_static/install_guide_boards_manager_url.png similarity index 100% rename from docs/source/_static/install_guide_boards_manager_url.png rename to docs/_static/install_guide_boards_manager_url.png diff --git a/docs/source/_static/install_guide_preferences.png b/docs/_static/install_guide_preferences.png similarity index 100% rename from docs/source/_static/install_guide_preferences.png rename to docs/_static/install_guide_preferences.png diff --git a/docs/source/_static/logo_arduino.png b/docs/_static/logo_arduino.png similarity index 100% rename from docs/source/_static/logo_arduino.png rename to docs/_static/logo_arduino.png diff --git a/docs/source/_static/logo_espressif.png b/docs/_static/logo_espressif.png similarity index 100% rename from docs/source/_static/logo_espressif.png rename to docs/_static/logo_espressif.png diff --git a/docs/source/_static/logo_linux.png b/docs/_static/logo_linux.png similarity index 100% rename from docs/source/_static/logo_linux.png rename to docs/_static/logo_linux.png diff --git a/docs/source/_static/logo_macos.png b/docs/_static/logo_macos.png similarity index 100% rename from docs/source/_static/logo_macos.png rename to docs/_static/logo_macos.png diff --git a/docs/source/_static/logo_pio.png b/docs/_static/logo_pio.png similarity index 100% rename from docs/source/_static/logo_pio.png rename to docs/_static/logo_pio.png diff --git a/docs/source/_static/logo_windows.png b/docs/_static/logo_windows.png similarity index 100% rename from docs/source/_static/logo_windows.png rename to docs/_static/logo_windows.png diff --git a/docs/source/_static/ota_esp32_login.png b/docs/_static/ota_esp32_login.png similarity index 100% rename from docs/source/_static/ota_esp32_login.png rename to docs/_static/ota_esp32_login.png diff --git a/docs/source/_static/ota_esp32_upload.png b/docs/_static/ota_esp32_upload.png similarity index 100% rename from docs/source/_static/ota_esp32_upload.png rename to docs/_static/ota_esp32_upload.png diff --git a/docs/source/_static/ota_esp32_verbose.png b/docs/_static/ota_esp32_verbose.png similarity index 100% rename from docs/source/_static/ota_esp32_verbose.png rename to docs/_static/ota_esp32_verbose.png diff --git a/docs/source/_static/ota_export_to_binary.png b/docs/_static/ota_export_to_binary.png similarity index 100% rename from docs/source/_static/ota_export_to_binary.png rename to docs/_static/ota_export_to_binary.png diff --git a/docs/source/_static/soc-module.png b/docs/_static/soc-module.png similarity index 100% rename from docs/source/_static/soc-module.png rename to docs/_static/soc-module.png diff --git a/docs/source/_static/tutorials/basic/tutorial_basic_ide.png b/docs/_static/tutorials/basic/tutorial_basic_ide.png similarity index 100% rename from docs/source/_static/tutorials/basic/tutorial_basic_ide.png rename to docs/_static/tutorials/basic/tutorial_basic_ide.png diff --git a/docs/source/_static/tutorials/peripherals/tutorial_peripheral_diagram.png b/docs/_static/tutorials/peripherals/tutorial_peripheral_diagram.png similarity index 100% rename from docs/source/_static/tutorials/peripherals/tutorial_peripheral_diagram.png rename to docs/_static/tutorials/peripherals/tutorial_peripheral_diagram.png diff --git a/docs/source/_static/usb_msc_drive.png b/docs/_static/usb_msc_drive.png similarity index 100% rename from docs/source/_static/usb_msc_drive.png rename to docs/_static/usb_msc_drive.png diff --git a/docs/source/_static/warning.png b/docs/_static/warning.png similarity index 100% rename from docs/source/_static/warning.png rename to docs/_static/warning.png diff --git a/docs/source/_static/wifi_esp32_ap.png b/docs/_static/wifi_esp32_ap.png similarity index 100% rename from docs/source/_static/wifi_esp32_ap.png rename to docs/_static/wifi_esp32_ap.png diff --git a/docs/source/_static/wifi_esp32_sta.png b/docs/_static/wifi_esp32_sta.png similarity index 100% rename from docs/source/_static/wifi_esp32_sta.png rename to docs/_static/wifi_esp32_sta.png diff --git a/docs/source/_static/win-gui-1.png b/docs/_static/win-gui-1.png similarity index 100% rename from docs/source/_static/win-gui-1.png rename to docs/_static/win-gui-1.png diff --git a/docs/source/_static/win-gui-2.png b/docs/_static/win-gui-2.png similarity index 100% rename from docs/source/_static/win-gui-2.png rename to docs/_static/win-gui-2.png diff --git a/docs/source/_static/win-gui-3.png b/docs/_static/win-gui-3.png similarity index 100% rename from docs/source/_static/win-gui-3.png rename to docs/_static/win-gui-3.png diff --git a/docs/source/_static/win-gui-4.png b/docs/_static/win-gui-4.png similarity index 100% rename from docs/source/_static/win-gui-4.png rename to docs/_static/win-gui-4.png diff --git a/docs/source/_static/win-gui-5.png b/docs/_static/win-gui-5.png similarity index 100% rename from docs/source/_static/win-gui-5.png rename to docs/_static/win-gui-5.png diff --git a/docs/source/_static/win-gui-update-1.png b/docs/_static/win-gui-update-1.png similarity index 100% rename from docs/source/_static/win-gui-update-1.png rename to docs/_static/win-gui-update-1.png diff --git a/docs/source/_static/win-gui-update-2.png b/docs/_static/win-gui-update-2.png similarity index 100% rename from docs/source/_static/win-gui-update-2.png rename to docs/_static/win-gui-update-2.png diff --git a/docs/conf_common.py b/docs/conf_common.py new file mode 100644 index 00000000000..9aab8d4a224 --- /dev/null +++ b/docs/conf_common.py @@ -0,0 +1,30 @@ +# --------------------------------------------------------------- + +from esp_docs.conf_docs import * # noqa: F403,F401 + +languages = ["en"] + +# link roles config +github_repo = "espressif/arduino-esp32" + +# context used by sphinx_idf_theme +html_context["github_user"] = "espressif" +html_context["github_repo"] = "arduino-esp32" + +html_static_path = ["../_static"] + +# Conditional content + +extensions += ['sphinx_copybutton', + 'sphinx_tabs.tabs', + 'esp_docs.esp_extensions.dummy_build_system', + ] + +ESP32_DOCS = [ + "index.rst", +] + +# Extra options required by sphinx_idf_theme +project_slug = "arduino-esp32" + +versions_url = "./_static/arduino-esp32_versions.js" diff --git a/docs/source/advanced_utils.rst b/docs/en/advanced_utils.rst similarity index 100% rename from docs/source/advanced_utils.rst rename to docs/en/advanced_utils.rst diff --git a/docs/source/api/adc.rst b/docs/en/api/adc.rst similarity index 100% rename from docs/source/api/adc.rst rename to docs/en/api/adc.rst diff --git a/docs/source/api/ble.rst b/docs/en/api/ble.rst similarity index 100% rename from docs/source/api/ble.rst rename to docs/en/api/ble.rst diff --git a/docs/source/api/bluetooth.rst b/docs/en/api/bluetooth.rst similarity index 100% rename from docs/source/api/bluetooth.rst rename to docs/en/api/bluetooth.rst diff --git a/docs/source/api/dac.rst b/docs/en/api/dac.rst similarity index 100% rename from docs/source/api/dac.rst rename to docs/en/api/dac.rst diff --git a/docs/source/api/deepsleep.rst b/docs/en/api/deepsleep.rst similarity index 100% rename from docs/source/api/deepsleep.rst rename to docs/en/api/deepsleep.rst diff --git a/docs/source/api/espnow.rst b/docs/en/api/espnow.rst similarity index 100% rename from docs/source/api/espnow.rst rename to docs/en/api/espnow.rst diff --git a/docs/source/api/ethernet.rst b/docs/en/api/ethernet.rst similarity index 100% rename from docs/source/api/ethernet.rst rename to docs/en/api/ethernet.rst diff --git a/docs/source/api/gpio.rst b/docs/en/api/gpio.rst similarity index 100% rename from docs/source/api/gpio.rst rename to docs/en/api/gpio.rst diff --git a/docs/source/api/hall_sensor.rst b/docs/en/api/hall_sensor.rst similarity index 100% rename from docs/source/api/hall_sensor.rst rename to docs/en/api/hall_sensor.rst diff --git a/docs/source/api/i2c.rst b/docs/en/api/i2c.rst similarity index 99% rename from docs/source/api/i2c.rst rename to docs/en/api/i2c.rst index f53d8f27a3c..31a07f88046 100644 --- a/docs/source/api/i2c.rst +++ b/docs/en/api/i2c.rst @@ -19,7 +19,7 @@ The I2C can be used in two different modes: * **I2C Master Mode** * In this mode, the ESP32 generates the clock signal and initiates the communication with the slave device. -.. figure:: ../_static/arduino_i2c_master.png +.. figure:: ../../_static/arduino_i2c_master.png :align: center :width: 720 :figclass: align-center @@ -27,7 +27,7 @@ The I2C can be used in two different modes: * **I2C Slave Mode** * The slave mode, the clock is generated by the master device and responds to the master if the destination address is the same as the destination. -.. figure:: ../_static/arduino_i2c_slave.png +.. figure:: ../../_static/arduino_i2c_slave.png :align: center :width: 520 :figclass: align-center diff --git a/docs/source/api/i2s.rst b/docs/en/api/i2s.rst similarity index 100% rename from docs/source/api/i2s.rst rename to docs/en/api/i2s.rst diff --git a/docs/source/api/insights.rst b/docs/en/api/insights.rst similarity index 100% rename from docs/source/api/insights.rst rename to docs/en/api/insights.rst diff --git a/docs/source/api/ledc.rst b/docs/en/api/ledc.rst similarity index 100% rename from docs/source/api/ledc.rst rename to docs/en/api/ledc.rst diff --git a/docs/source/api/preferences.rst b/docs/en/api/preferences.rst similarity index 100% rename from docs/source/api/preferences.rst rename to docs/en/api/preferences.rst diff --git a/docs/source/api/pulse_counter.rst b/docs/en/api/pulse_counter.rst similarity index 100% rename from docs/source/api/pulse_counter.rst rename to docs/en/api/pulse_counter.rst diff --git a/docs/source/api/rainmaker.rst b/docs/en/api/rainmaker.rst similarity index 100% rename from docs/source/api/rainmaker.rst rename to docs/en/api/rainmaker.rst diff --git a/docs/source/api/reset_reason.rst b/docs/en/api/reset_reason.rst similarity index 100% rename from docs/source/api/reset_reason.rst rename to docs/en/api/reset_reason.rst diff --git a/docs/source/api/rmt.rst b/docs/en/api/rmt.rst similarity index 100% rename from docs/source/api/rmt.rst rename to docs/en/api/rmt.rst diff --git a/docs/source/api/sdio.rst b/docs/en/api/sdio.rst similarity index 100% rename from docs/source/api/sdio.rst rename to docs/en/api/sdio.rst diff --git a/docs/source/api/sdmmc.rst b/docs/en/api/sdmmc.rst similarity index 100% rename from docs/source/api/sdmmc.rst rename to docs/en/api/sdmmc.rst diff --git a/docs/source/api/sigmadelta.rst b/docs/en/api/sigmadelta.rst similarity index 100% rename from docs/source/api/sigmadelta.rst rename to docs/en/api/sigmadelta.rst diff --git a/docs/source/api/spi.rst b/docs/en/api/spi.rst similarity index 100% rename from docs/source/api/spi.rst rename to docs/en/api/spi.rst diff --git a/docs/source/api/timer.rst b/docs/en/api/timer.rst similarity index 100% rename from docs/source/api/timer.rst rename to docs/en/api/timer.rst diff --git a/docs/source/api/touch.rst b/docs/en/api/touch.rst similarity index 100% rename from docs/source/api/touch.rst rename to docs/en/api/touch.rst diff --git a/docs/source/api/usb.rst b/docs/en/api/usb.rst similarity index 100% rename from docs/source/api/usb.rst rename to docs/en/api/usb.rst diff --git a/docs/source/api/usb_cdc.rst b/docs/en/api/usb_cdc.rst similarity index 100% rename from docs/source/api/usb_cdc.rst rename to docs/en/api/usb_cdc.rst diff --git a/docs/source/api/usb_msc.rst b/docs/en/api/usb_msc.rst similarity index 100% rename from docs/source/api/usb_msc.rst rename to docs/en/api/usb_msc.rst diff --git a/docs/source/api/wifi.rst b/docs/en/api/wifi.rst similarity index 99% rename from docs/source/api/wifi.rst rename to docs/en/api/wifi.rst index eb32b4af53e..d5632ae7717 100644 --- a/docs/source/api/wifi.rst +++ b/docs/en/api/wifi.rst @@ -21,7 +21,7 @@ Working as AP In this mode, the ESP32 is configured as an Access Point (AP) and it's capable of receiving incoming connections from other devices (stations) by providing a Wi-Fi network. -.. figure:: ../_static/wifi_esp32_ap.png +.. figure:: ../../_static/wifi_esp32_ap.png :align: center :width: 520 :figclass: align-center @@ -33,7 +33,7 @@ Working as STA The STA mode is used to connect the ESP32 to a Wi-Fi network, provided by an Access Point. -.. figure:: ../_static/wifi_esp32_sta.png +.. figure:: ../../_static/wifi_esp32_sta.png :align: center :width: 520 :figclass: align-center diff --git a/docs/source/boards/ESP32-C3-DevKitM-1.rst b/docs/en/boards/ESP32-C3-DevKitM-1.rst similarity index 98% rename from docs/source/boards/ESP32-C3-DevKitM-1.rst rename to docs/en/boards/ESP32-C3-DevKitM-1.rst index 7ce9475d69b..11e6e45d55f 100644 --- a/docs/source/boards/ESP32-C3-DevKitM-1.rst +++ b/docs/en/boards/ESP32-C3-DevKitM-1.rst @@ -88,7 +88,7 @@ No. Name Type [1]_ Function Pin Layout ---------- -.. figure:: ../_static/esp32-c3_devkitM-1_pinlayout.png +.. figure:: ../../_static/esp32-c3_devkitM-1_pinlayout.png :align: center :width: 600 :alt: ESP32-C3-DevKitM-1 (click to enlarge) diff --git a/docs/source/boards/ESP32-DevKitC-1.rst b/docs/en/boards/ESP32-DevKitC-1.rst similarity index 98% rename from docs/source/boards/ESP32-DevKitC-1.rst rename to docs/en/boards/ESP32-DevKitC-1.rst index dcd5632eab7..4bb86696227 100644 --- a/docs/source/boards/ESP32-DevKitC-1.rst +++ b/docs/en/boards/ESP32-DevKitC-1.rst @@ -96,7 +96,7 @@ No. Name Type Function Pin Layout ---------- -.. figure:: ../_static/esp32_devkitC_pinlayout.png +.. figure:: ../../_static/esp32_devkitC_pinlayout.png :align: center :width: 600 :alt: ESP32-DevKitC-1 (click to enlarge) diff --git a/docs/source/boards/ESP32-S2-Saola-1.rst b/docs/en/boards/ESP32-S2-Saola-1.rst similarity index 98% rename from docs/source/boards/ESP32-S2-Saola-1.rst rename to docs/en/boards/ESP32-S2-Saola-1.rst index fd41772c234..1a06a6d87bb 100644 --- a/docs/source/boards/ESP32-S2-Saola-1.rst +++ b/docs/en/boards/ESP32-S2-Saola-1.rst @@ -100,7 +100,7 @@ No. Name Type Function Pin Layout ---------- -.. figure:: ../_static/esp32-s2_saola1_pinlayout.png +.. figure:: ../../_static/esp32-s2_saola1_pinlayout.png :align: center :width: 600 :alt: ESP32-S2-Saola-1 (click to enlarge) diff --git a/docs/source/boards/boards.rst b/docs/en/boards/boards.rst similarity index 97% rename from docs/source/boards/boards.rst rename to docs/en/boards/boards.rst index 344e9c1c9da..9a1d2217dac 100644 --- a/docs/source/boards/boards.rst +++ b/docs/en/boards/boards.rst @@ -28,7 +28,7 @@ For each family, we have SoC variants with some differentiation. The differences The modules use the SoC internally, including the external flash, PSRAM (in some models) and other essential electronic components. Essentially, all modules from the same family use the same SoC. -.. figure:: ../_static/soc-module.png +.. figure:: ../../_static/soc-module.png :align: center :width: 250 :alt: ESP32 SoC and Module (click to enlarge) @@ -58,7 +58,7 @@ Before buying: Keep in mind that for some "must have" features when choosing the Espressif --------- -.. figure:: ../_static/logo_espressif.png +.. figure:: ../../_static/logo_espressif.png :align: center :width: 250 :alt: Espressif Logo diff --git a/docs/source/boards/generic.rst b/docs/en/boards/generic.rst similarity index 100% rename from docs/source/boards/generic.rst rename to docs/en/boards/generic.rst diff --git a/docs/source/common/datasheet.inc b/docs/en/common/datasheet.inc similarity index 100% rename from docs/source/common/datasheet.inc rename to docs/en/common/datasheet.inc diff --git a/docs/en/conf.py b/docs/en/conf.py new file mode 100644 index 00000000000..ea8f20e4604 --- /dev/null +++ b/docs/en/conf.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# +# English Language RTD & Sphinx config file +# +# Uses ../conf_common.py for most non-language-specific settings. + +# Importing conf_common adds all the non-language-specific +# parts to this conf module + +try: + from conf_common import * # noqa: F403,F401 +except ImportError: + import os + import sys + sys.path.insert(0, os.path.abspath('../')) + from conf_common import * # noqa: F403,F401 + +# General information about the project. +project = u'Arduino ESP32' +copyright = u'2016 - 2023, Espressif Systems (Shanghai) Co., Ltd' +pdf_title = u'Arduino ESP32 Documentation Guide' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +language = 'en' \ No newline at end of file diff --git a/docs/source/contributing.rst b/docs/en/contributing.rst similarity index 100% rename from docs/source/contributing.rst rename to docs/en/contributing.rst diff --git a/docs/source/esp-idf_component.rst b/docs/en/esp-idf_component.rst similarity index 100% rename from docs/source/esp-idf_component.rst rename to docs/en/esp-idf_component.rst diff --git a/docs/source/external_libraries_test.rst b/docs/en/external_libraries_test.rst similarity index 94% rename from docs/source/external_libraries_test.rst rename to docs/en/external_libraries_test.rst index 2064987df07..0d7da671cd2 100644 --- a/docs/source/external_libraries_test.rst +++ b/docs/en/external_libraries_test.rst @@ -104,7 +104,7 @@ You can check the results in `LIBRARIES_TEST.md`_. The results file example: -.. image:: _static/external_library_test_schedule.png +.. image:: ../_static/external_library_test_schedule.png :width: 600 Pull Request test result @@ -113,18 +113,18 @@ Pull Request test result If the test run on Pull Request, it will compile all libraries and sketches 2 times (before/after changes in PR) to see, if the PR is breaking/fixing libraries. In the table the results are in order ``BEFORE -> AFTER``. -.. image:: _static/external_library_test_pr.png +.. image:: ../_static/external_library_test_pr.png :width: 600 -.. |success| image:: _static/green_checkmark.png +.. |success| image:: ../_static/green_checkmark.png :height: 2ex :class: no-scaled-link -.. |warning| image:: _static/warning.png +.. |warning| image:: ../_static/warning.png :height: 2ex :class: no-scaled-link -.. |fail| image:: _static/cross.png +.. |fail| image:: ../_static/cross.png :height: 2ex :class: no-scaled-link diff --git a/docs/source/faq.rst b/docs/en/faq.rst similarity index 100% rename from docs/source/faq.rst rename to docs/en/faq.rst diff --git a/docs/source/getting_started.rst b/docs/en/getting_started.rst similarity index 95% rename from docs/source/getting_started.rst rename to docs/en/getting_started.rst index 4093b415c07..6b2d4aca959 100644 --- a/docs/source/getting_started.rst +++ b/docs/en/getting_started.rst @@ -62,9 +62,9 @@ Supported Operating Systems | Windows | Linux | macOS | +-------------------+-------------------+-------------------+ -.. |windows-logo| image:: _static/logo_windows.png -.. |linux-logo| image:: _static/logo_linux.png -.. |macos-logo| image:: _static/logo_macos.png +.. |windows-logo| image:: ../_static/logo_windows.png +.. |linux-logo| image:: ../_static/logo_linux.png +.. |macos-logo| image:: ../_static/logo_macos.png Supported IDEs --------------------------- @@ -77,8 +77,8 @@ Here is the list of supported IDE for Arduino ESP32 support integration. | Arduino IDE | PlatformIO | +-------------------+-------------------+ -.. |arduino-logo| image:: _static/logo_arduino.png -.. |pio-logo| image:: _static/logo_pio.png +.. |arduino-logo| image:: ../_static/logo_arduino.png +.. |pio-logo| image:: ../_static/logo_pio.png See `Installing Guides `_ for more details on how to install the Arduino ESP32 support. diff --git a/docs/source/guides/core_debug.rst b/docs/en/guides/core_debug.rst similarity index 100% rename from docs/source/guides/core_debug.rst rename to docs/en/guides/core_debug.rst diff --git a/docs/source/guides/docs_contributing.rst b/docs/en/guides/docs_contributing.rst similarity index 99% rename from docs/source/guides/docs_contributing.rst rename to docs/en/guides/docs_contributing.rst index be5a54e4b37..3bcffc0bf74 100644 --- a/docs/source/guides/docs_contributing.rst +++ b/docs/en/guides/docs_contributing.rst @@ -309,7 +309,7 @@ After that, you can use the following structure to include the image in the docs .. code-block:: - .. figure:: ../_static/arduino_i2c_master.png + .. figure:: ../../_static/arduino_i2c_master.png :align: center :width: 720 :figclass: align-center diff --git a/docs/source/guides/guides.rst b/docs/en/guides/guides.rst similarity index 100% rename from docs/source/guides/guides.rst rename to docs/en/guides/guides.rst diff --git a/docs/source/guides/tools_menu.rst b/docs/en/guides/tools_menu.rst similarity index 99% rename from docs/source/guides/tools_menu.rst rename to docs/en/guides/tools_menu.rst index 96b3bcc3674..41c8878bf6e 100644 --- a/docs/source/guides/tools_menu.rst +++ b/docs/en/guides/tools_menu.rst @@ -240,7 +240,7 @@ The USB Mass Storage Class, or USB MSC, is a class used for storage devices, lik This option can be used to ``Enable`` or ``Disable`` this function at the boot. If this option is ``Enabled``, once the device is connected via USB, one new storage device will appear in the system as a storage drive. Use this new storage drive to write and read files or to drop a new firmware binary to flash the device. -.. figure:: ../_static/usb_msc_drive.png +.. figure:: ../../_static/usb_msc_drive.png :align: center :width: 720 :figclass: align-center diff --git a/docs/source/index.rst b/docs/en/index.rst similarity index 100% rename from docs/source/index.rst rename to docs/en/index.rst diff --git a/docs/source/installing.rst b/docs/en/installing.rst similarity index 94% rename from docs/source/installing.rst rename to docs/en/installing.rst index c3bd268949b..5f29ce10028 100644 --- a/docs/source/installing.rst +++ b/docs/en/installing.rst @@ -13,7 +13,7 @@ To install Arduino-ESP32 support, you can use one of the following options. Installing using Arduino IDE ---------------------------- -.. figure:: _static/logo_arduino.png +.. figure:: ../_static/logo_arduino.png :align: center :width: 200 :figclass: align-center @@ -42,21 +42,21 @@ To start the installation process using the Boards Managaer, follow these steps: - Start Arduino and open the Preferences window. -.. figure:: _static/install_guide_preferences.png +.. figure:: ../_static/install_guide_preferences.png :align: center :width: 600 :figclass: align-center - Enter one of the release links above into *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas. -.. figure:: _static/install_guide_boards_manager_url.png +.. figure:: ../_static/install_guide_boards_manager_url.png :align: center :width: 600 :figclass: align-center - Open Boards Manager from Tools > Board menu and install *esp32* platform (and do not forget to select your ESP32 board from Tools > Board menu after installation). -.. figure:: _static/install_guide_boards_manager_esp32.png +.. figure:: ../_static/install_guide_boards_manager_esp32.png :align: center :width: 600 :figclass: align-center @@ -66,7 +66,7 @@ To start the installation process using the Boards Managaer, follow these steps: Installing using PlatformIO --------------------------- -.. figure:: _static/logo_pio.png +.. figure:: ../_static/logo_pio.png :align: center :width: 200 :figclass: align-center @@ -164,7 +164,7 @@ Steps to install Arduino ESP32 support on Windows: - Select ``Clone Existing Repository`` -.. figure:: _static/win-gui-1.png +.. figure:: ../_static/win-gui-1.png :align: center :width: 600 :figclass: align-center @@ -177,13 +177,13 @@ Steps to install Arduino ESP32 support on Windows: **Step 2** -.. figure:: _static/win-gui-2.png +.. figure:: ../_static/win-gui-2.png :align: center :figclass: align-center **Step 3** -.. figure:: _static/win-gui-3.png +.. figure:: ../_static/win-gui-3.png :align: center :figclass: align-center @@ -192,7 +192,7 @@ Steps to install Arduino ESP32 support on Windows: **Step 4** -.. figure:: _static/win-gui-4.png +.. figure:: ../_static/win-gui-4.png :align: center :figclass: align-center @@ -200,7 +200,7 @@ Steps to install Arduino ESP32 support on Windows: **Step 5** -.. figure:: _static/win-gui-5.png +.. figure:: ../_static/win-gui-5.png :align: center :figclass: align-center @@ -210,7 +210,7 @@ Steps to install Arduino ESP32 support on Windows: 4. Select the COM port that the board is attached to 5. Compile and upload (You might need to hold the boot button while uploading) -.. figure:: _static/arduino-ide.png +.. figure:: ../_static/arduino-ide.png :align: center :figclass: align-center @@ -219,27 +219,27 @@ How to update to the latest code 1. Start ``Git GUI`` and you should see the repository under ``Open Recent Repository``. Click on it! -.. figure:: _static/win-gui-update-1.png +.. figure:: ../_static/win-gui-update-1.png :align: center :figclass: align-center 1. From menu ``Remote`` select ``Fetch from`` > ``origin`` -.. figure:: _static/win-gui-update-2.png +.. figure:: ../_static/win-gui-update-2.png :align: center :figclass: align-center 1. Wait for git to pull any changes and close ``Git GUI`` 2. Open ``[ARDUINO_SKETCHBOOK_DIR]/hardware/espressif/esp32/tools`` and double-click ``get.exe`` -.. figure:: _static/win-gui-4.png +.. figure:: ../_static/win-gui-4.png :align: center :figclass: align-center Linux ----- -.. figure:: _static/logo_linux.png +.. figure:: ../_static/logo_linux.png :align: center :width: 200 :figclass: align-center diff --git a/docs/source/lib_builder.rst b/docs/en/lib_builder.rst similarity index 100% rename from docs/source/lib_builder.rst rename to docs/en/lib_builder.rst diff --git a/docs/source/libraries.rst b/docs/en/libraries.rst similarity index 100% rename from docs/source/libraries.rst rename to docs/en/libraries.rst diff --git a/docs/source/make.rst b/docs/en/make.rst similarity index 100% rename from docs/source/make.rst rename to docs/en/make.rst diff --git a/docs/source/ota_web_update.rst b/docs/en/ota_web_update.rst similarity index 93% rename from docs/source/ota_web_update.rst rename to docs/en/ota_web_update.rst index b637f0206b3..17cbe3583f5 100644 --- a/docs/source/ota_web_update.rst +++ b/docs/en/ota_web_update.rst @@ -36,14 +36,14 @@ Prepare the sketch and configuration for initial upload with a serial port - Update ssid and pass in the sketch so the module can join your Wi-Fi network - Open File > Preferences, look for “Show verbose output during:” and check out “compilation” option -.. figure:: _static/ota_esp32_verbose.png +.. figure:: ../_static/ota_esp32_verbose.png :align: center :figclass: align-center - Upload sketch (Ctrl+U) - Now open web browser and enter the url, i.e. http://esp32.local. Once entered, browser should display a form -.. figure:: _static/ota_esp32_login.png +.. figure:: ../_static/ota_esp32_login.png :align: center :figclass: align-center @@ -56,7 +56,7 @@ Prepare the sketch and configuration for initial upload with a serial port Now click on the Login button and browser will display an upload form -.. figure:: _static/ota_esp32_upload.png +.. figure:: ../_static/ota_esp32_upload.png :align: center :figclass: align-center @@ -67,7 +67,7 @@ Exporting Binary file of the Firmware (Code) - Open up the Code, for Exporting up Binary file - Now go to Sketch > export compiled Binary -.. figure:: _static/ota_export_to_binary.png +.. figure:: ../_static/ota_export_to_binary.png :align: center :figclass: align-center diff --git a/docs/source/troubleshooting.rst b/docs/en/troubleshooting.rst similarity index 100% rename from docs/source/troubleshooting.rst rename to docs/en/troubleshooting.rst diff --git a/docs/source/tutorials/basic.rst b/docs/en/tutorials/basic.rst similarity index 97% rename from docs/source/tutorials/basic.rst rename to docs/en/tutorials/basic.rst index 34d9a05c094..8b932987bea 100644 --- a/docs/source/tutorials/basic.rst +++ b/docs/en/tutorials/basic.rst @@ -21,7 +21,7 @@ Here are the steps for this tutorial. 1. Open the Arduino IDE -.. figure:: ../_static/tutorials/basic/tutorial_basic_ide.png +.. figure:: ../../_static/tutorials/basic/tutorial_basic_ide.png :align: center :width: 600 :alt: Arduino IDE (click to enlarge) diff --git a/docs/source/tutorials/blink.rst b/docs/en/tutorials/blink.rst similarity index 100% rename from docs/source/tutorials/blink.rst rename to docs/en/tutorials/blink.rst diff --git a/docs/source/tutorials/cdc_dfu_flash.rst b/docs/en/tutorials/cdc_dfu_flash.rst similarity index 100% rename from docs/source/tutorials/cdc_dfu_flash.rst rename to docs/en/tutorials/cdc_dfu_flash.rst diff --git a/docs/source/tutorials/io_mux.rst b/docs/en/tutorials/io_mux.rst similarity index 98% rename from docs/source/tutorials/io_mux.rst rename to docs/en/tutorials/io_mux.rst index 12e0a9c9c0c..03b10449013 100644 --- a/docs/source/tutorials/io_mux.rst +++ b/docs/en/tutorials/io_mux.rst @@ -23,7 +23,7 @@ GPIO Matrix and Pin Mux The ESP32 architecture includes the capability of configuring some peripherals to any of the GPIOs pins, managed by the `IO MUX GPIO`_. Essentially, this capability means that we can route the internal peripheral into a different physical pin using the IO MUX and the GPIO Matrix. -.. figure:: ../_static/tutorials/peripherals/tutorial_peripheral_diagram.png +.. figure:: ../../_static/tutorials/peripherals/tutorial_peripheral_diagram.png :align: center :width: 600 :figclass: align-center diff --git a/docs/source/tutorials/partition_table.rst b/docs/en/tutorials/partition_table.rst similarity index 100% rename from docs/source/tutorials/partition_table.rst rename to docs/en/tutorials/partition_table.rst diff --git a/docs/source/tutorials/preferences.rst b/docs/en/tutorials/preferences.rst similarity index 100% rename from docs/source/tutorials/preferences.rst rename to docs/en/tutorials/preferences.rst diff --git a/docs/source/tutorials/tutorials.rst b/docs/en/tutorials/tutorials.rst similarity index 100% rename from docs/source/tutorials/tutorials.rst rename to docs/en/tutorials/tutorials.rst diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 6247f7e2317..00000000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/requirements.txt b/docs/requirements.txt index 9e40946ec2b..97b552574b9 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,3 @@ -# This is a list of python packages used to generate documentation. This file is used with pip: -# pip install --user -r requirements.txt -# -# matplotlib is currently required only by the script generate_chart.py -sphinx-copybutton==0.3.0 +esp-docs==1.4.* +sphinx-copybutton==0.5.0 sphinx-tabs==3.2.0 \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 402c1e293d5..00000000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,68 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'Arduino-ESP32' -copyright = '2022, Espressif' -author = 'Espressif' - -# The full version, including alpha/beta/rc tags -release = '2.0.6' - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx_copybutton', - 'sphinx_tabs.tabs' -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'default' -html_logo = '_static/logo_espressif.png' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Style -# pygments_style = "sphinx" From bdd9d1c0b864f7e7cbf4829eb595ebeedf4a1bea Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Tue, 19 Sep 2023 08:40:05 +0100 Subject: [PATCH 02/70] Documentation CI build changed to ESP-Docs --- .github/workflows/docs.yml | 4 ++-- .readthedocs.yaml | 2 +- docs/conf_common.py | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f01fb76ee84..de383bce1bb 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,5 +34,5 @@ jobs: sudo apt install python3-pip python3-setuptools # GitHub CI installs pip3 and setuptools outside the path. # Update the path to include them and run. - PATH=/home/runner/.local/bin:$PATH pip3 install --user -r ./docs/requirements.txt - cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" make html + PATH=/home/runner/.local/bin:$PATH pip install -r requirements.txt --prefer-binary + cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 259c19f98fd..498a30c3f4f 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -13,7 +13,7 @@ build: # Build documentation in the docs/ directory with Sphinx sphinx: - configuration: docs/source/conf.py + configuration: docs/en/conf.py python: install: diff --git a/docs/conf_common.py b/docs/conf_common.py index 9aab8d4a224..705659a3b66 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -4,6 +4,8 @@ languages = ["en"] + + # link roles config github_repo = "espressif/arduino-esp32" From 58d7bf9951a8007fe205f9b0f5cb14c5526df704 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Tue, 19 Sep 2023 08:44:13 +0100 Subject: [PATCH 03/70] Fix documentation CI build --- .github/workflows/docs.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index de383bce1bb..f99568aa764 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -34,5 +34,6 @@ jobs: sudo apt install python3-pip python3-setuptools # GitHub CI installs pip3 and setuptools outside the path. # Update the path to include them and run. - PATH=/home/runner/.local/bin:$PATH pip install -r requirements.txt --prefer-binary - cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 + cd ./docs + PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary + PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 From 11e53c535bb31e13167706edee12bc555cc951d0 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Tue, 19 Sep 2023 09:01:24 +0100 Subject: [PATCH 04/70] Docs build target fix --- .github/workflows/docs.yml | 4 ++-- docs/conf_common.py | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f99568aa764..705005e6b6d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,4 +1,4 @@ -name: ReadTheDocs CI +name: Documentation Build CI on: push: @@ -16,7 +16,7 @@ on: jobs: build-docs: - name: Build ReadTheDocs + name: Build ESP-Docs runs-on: ubuntu-22.04 defaults: run: diff --git a/docs/conf_common.py b/docs/conf_common.py index 705659a3b66..8522c680667 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -4,7 +4,14 @@ languages = ["en"] - +idf_targets = [ + "esp32", + "esp32s2", + "esp32s3", + "esp32c3", + "esp32c6", + "esp32h2", +] # link roles config github_repo = "espressif/arduino-esp32" From e515a6a1d11780fbd8780ad658ae91fb8baf99d0 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Tue, 19 Sep 2023 15:22:44 +0100 Subject: [PATCH 05/70] Added CI for deploy docs --- .github/workflows/docs.yml | 2 +- .github/workflows/docs_deploy.yml | 46 +++++++++++++++++++ ...uino-esp32_versions.js => docs_version.js} | 2 +- docs/conf_common.py | 21 ++++++++- docs/en/conf.py | 5 +- docs/en/index.rst | 2 +- docs/utils.sh | 18 ++++++++ 7 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/docs_deploy.yml rename docs/_static/{arduino-esp32_versions.js => docs_version.js} (92%) create mode 100644 docs/utils.sh diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 705005e6b6d..98699a680cd 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -36,4 +36,4 @@ jobs: # Update the path to include them and run. cd ./docs PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary - PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 + PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 esp32s2 esp32s3 esp32c6 esp32h2 diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml new file mode 100644 index 00000000000..282ab264a16 --- /dev/null +++ b/.github/workflows/docs_deploy.yml @@ -0,0 +1,46 @@ +name: Documentation Deploy CI + +on: + workflow_dispatch: + release: + types: + - created + paths: + - 'docs/**' + - '.github/workflows/docs_deploy.yml' + +jobs: + + deploy-preview-docs: + name: Deploy Documentation + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Deploy Preview + env: + DOCS_BUILD_DIR: "./docs/_build/" + DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }} + DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_PREV_SERVER }} + DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PREV_SERVER_USER }} + DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PREV_PATH }} + DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_PREV_URL }} + run: | + sudo apt update + sudo apt install python3-pip python3-setuptools + source ./docs/utils.sh + add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER + export GIT_VER=$(git describe --always) + echo "PIP install requirements..." + pip3 install --user -r ./docs/requirements.txt + echo "Building the Docs..." + cd ./docs && build-docs -l en + echo "Deploy the Docs..." + deploy-docs diff --git a/docs/_static/arduino-esp32_versions.js b/docs/_static/docs_version.js similarity index 92% rename from docs/_static/arduino-esp32_versions.js rename to docs/_static/docs_version.js index b4b772d5c45..a511b79dc31 100644 --- a/docs/_static/arduino-esp32_versions.js +++ b/docs/_static/docs_version.js @@ -11,6 +11,6 @@ var DOCUMENTATION_VERSIONS = { { text: "ESP32-S3", value: "esp32s3"}, { text: "ESP32-C3", value: "esp32c3"}, { text: "ESP32-H2", value: "esp32h2"}, - { text: "ESP32C6", value: "esp32c6"}, + { text: "ESP32-C6", value: "esp32c6"}, ] }; \ No newline at end of file diff --git a/docs/conf_common.py b/docs/conf_common.py index 8522c680667..3320558c3db 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -33,7 +33,26 @@ "index.rst", ] +ESP32S2_DOCS = ESP32_DOCS + +ESP32C3_DOCS = ESP32S2_DOCS + +ESP32S3_DOCS = ESP32S2_DOCS + +ESP32C6_DOCS = ESP32S2_DOCS + +ESP32H2_DOCS = ESP32S2_DOCS + +conditional_include_dict = { + "esp32": ESP32_DOCS, + "esp32s2": ESP32S2_DOCS, + "esp32c3": ESP32C3_DOCS, + "esp32s3": ESP32S3_DOCS, + "esp32c6": ESP32C6_DOCS, + "esp32h2": ESP32H2_DOCS, +} + # Extra options required by sphinx_idf_theme project_slug = "arduino-esp32" -versions_url = "./_static/arduino-esp32_versions.js" +versions_url = "./_static/docs_version.js" diff --git a/docs/en/conf.py b/docs/en/conf.py index ea8f20e4604..4d1208f256a 100644 --- a/docs/en/conf.py +++ b/docs/en/conf.py @@ -15,9 +15,12 @@ sys.path.insert(0, os.path.abspath('../')) from conf_common import * # noqa: F403,F401 +import datetime +current_year = datetime.datetime.now().year + # General information about the project. project = u'Arduino ESP32' -copyright = u'2016 - 2023, Espressif Systems (Shanghai) Co., Ltd' +copyright = u'2016 - {}, Espressif Systems (Shanghai) Co., Ltd'.format(current_year) pdf_title = u'Arduino ESP32 Documentation Guide' # The language for content autogenerated by Sphinx. Refer to documentation diff --git a/docs/en/index.rst b/docs/en/index.rst index f504dc342e0..345e75bd5ee 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -1,5 +1,5 @@ ############################################# -Welcome to ESP32 Arduino Core's documentation +Welcome to {IDF_TARGET_NAME} Arduino Core's documentation ############################################# Here you will find all the relevant information about the project. diff --git a/docs/utils.sh b/docs/utils.sh new file mode 100644 index 00000000000..91e283d462f --- /dev/null +++ b/docs/utils.sh @@ -0,0 +1,18 @@ +# Bash helper functions for adding SSH keys + +function add_ssh_keys() { + local key_string="${1}" + mkdir -p ~/.ssh + chmod 700 ~/.ssh + echo -n "${key_string}" >~/.ssh/id_rsa_base64 + base64 -w 0 ~/.ssh/id_rsa_base64 | base64 -di >~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa +} + +function add_doc_server_ssh_keys() { + local key_string="${1}" + local server_url="${2}" + local server_user="${3}" + add_ssh_keys "${key_string}" + echo -e "Host ${server_url}\n\tStrictHostKeyChecking no\n\tUser ${server_user}\n" >>~/.ssh/config +} \ No newline at end of file From 7326a41c1f524bb35a3e0cd12de2080af6c143ff Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Tue, 19 Sep 2023 15:32:33 +0100 Subject: [PATCH 06/70] Fix index.rst title wirh ESP target --- docs/en/index.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/index.rst b/docs/en/index.rst index 345e75bd5ee..3ed77588b26 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -1,8 +1,8 @@ -############################################# -Welcome to {IDF_TARGET_NAME} Arduino Core's documentation -############################################# +####################################### +Welcome to Arduino Core's documentation +####################################### -Here you will find all the relevant information about the project. +Here you will find all the relevant information about the project based on the {IDF_TARGET_NAME}. .. note:: This is a work in progress documentation and we will appreciate your help! We are looking for contributors! From e2f52dbcf7478f7ab7056dad8fc438022d24e6c7 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Mon, 25 Sep 2023 15:03:59 +0100 Subject: [PATCH 07/70] Added versions to JS file and docs as artifact --- .github/workflows/docs.yml | 5 +++++ docs/_static/arduino_versions.js | 28 ++++++++++++++++++++++++++++ docs/_static/docs_version.js | 16 ---------------- docs/conf_common.py | 6 +----- 4 files changed, 34 insertions(+), 21 deletions(-) create mode 100644 docs/_static/arduino_versions.js delete mode 100644 docs/_static/docs_version.js diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 98699a680cd..e0dc48653ef 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -37,3 +37,8 @@ jobs: cd ./docs PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 esp32s2 esp32s3 esp32c6 esp32h2 + - name: Archive Docs + uses: actions/upload-artifact@v2 + with: + name: docs + path: docs \ No newline at end of file diff --git a/docs/_static/arduino_versions.js b/docs/_static/arduino_versions.js new file mode 100644 index 00000000000..5d6e2d1451d --- /dev/null +++ b/docs/_static/arduino_versions.js @@ -0,0 +1,28 @@ +var DOCUMENTATION_VERSIONS = { + DEFAULTS: { has_targets: false, + supported_targets: [ "esp32" ] + }, + VERSIONS: [ + { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, + // 2.0.13 + { name: "release-2.0.13", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, + // 2.0.12 + { name: "release-2.0.12", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, + // 2.0.11 + { name: "release-2.0.11", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, + // 2.0.10 + { name: "release-2.0.10", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, + // 2.0.9 + { name: "release-2.0.9", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, + // 2.0.8 + { name: "release-2.0.8", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, + ], + IDF_TARGETS: [ + { text: "ESP32", value: "esp32"}, + { text: "ESP32-S2", value: "esp32s2"}, + { text: "ESP32-S3", value: "esp32s3"}, + { text: "ESP32-C3", value: "esp32c3"}, + { text: "ESP32-H2", value: "esp32h2"}, + { text: "ESP32-C6", value: "esp32c6"}, + ] +}; diff --git a/docs/_static/docs_version.js b/docs/_static/docs_version.js deleted file mode 100644 index a511b79dc31..00000000000 --- a/docs/_static/docs_version.js +++ /dev/null @@ -1,16 +0,0 @@ -var DOCUMENTATION_VERSIONS = { - DEFAULTS: { has_targets: false, - supported_targets: [ "esp32" ] - }, - VERSIONS: [ - { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, - ], - IDF_TARGETS: [ - { text: "ESP32", value: "esp32"}, - { text: "ESP32-S2", value: "esp32s2"}, - { text: "ESP32-S3", value: "esp32s3"}, - { text: "ESP32-C3", value: "esp32c3"}, - { text: "ESP32-H2", value: "esp32h2"}, - { text: "ESP32-C6", value: "esp32c6"}, - ] -}; \ No newline at end of file diff --git a/docs/conf_common.py b/docs/conf_common.py index 3320558c3db..ded9c5673cb 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -34,13 +34,9 @@ ] ESP32S2_DOCS = ESP32_DOCS - ESP32C3_DOCS = ESP32S2_DOCS - ESP32S3_DOCS = ESP32S2_DOCS - ESP32C6_DOCS = ESP32S2_DOCS - ESP32H2_DOCS = ESP32S2_DOCS conditional_include_dict = { @@ -55,4 +51,4 @@ # Extra options required by sphinx_idf_theme project_slug = "arduino-esp32" -versions_url = "./_static/docs_version.js" +versions_url = "./_static/arduino_versions.js" From f7151e0e6689d055914104cd2620c9dfea940cbf Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Mon, 4 Dec 2023 11:42:11 +0800 Subject: [PATCH 08/70] Resolving ADC conflict issue --- docs/en/api/adc.rst | 167 +++++++++++++++++++++++++++++++------------- 1 file changed, 120 insertions(+), 47 deletions(-) diff --git a/docs/en/api/adc.rst b/docs/en/api/adc.rst index 7e8265afb8f..8ab252e181b 100644 --- a/docs/en/api/adc.rst +++ b/docs/en/api/adc.rst @@ -11,13 +11,17 @@ to a digital form so that it can be read and processed by a microcontroller. ADCs are very useful in control and monitoring applications since most sensors (e.g., temperature, pressure, force) produce analogue output voltages. -.. note:: Each SoC or module has a different number of ADC's with a different number of channels and pins availible. Refer to datasheet of each board for more info. +.. note:: Each SoC or module has a different number of ADC's with a different number of channels and pins available. Refer to datasheet of each board for more info. Arduino-ESP32 ADC API --------------------- -ADC common API -************** +ADC OneShot mode +**************** + + +The ADC OneShot mode API is fully compatible with Arduino's ``analogRead`` function. +When you call the ``analogRead`` or ``analogReadMillivolts`` function, it returns the result of a single conversion on the requested pin. analogRead ^^^^^^^^^^ @@ -30,12 +34,12 @@ This function is used to get the ADC raw value for a given pin/ADC channel. * ``pin`` GPIO pin to read analog value -This function will return analog raw value. +This function will return analog raw value (non-calibrated). analogReadMillivolts ^^^^^^^^^^^^^^^^^^^^ -This function is used to get ADC value for a given pin/ADC channel in millivolts. +This function is used to get ADC raw value for a given pin/ADC channel and convert it to calibrated result in millivolts. .. code-block:: arduino @@ -43,7 +47,7 @@ This function is used to get ADC value for a given pin/ADC channel in millivolts * ``pin`` GPIO pin to read analog value -This function will return analog value in millivolts. +This function will return analog value in millivolts (calibrated). analogReadResolution ^^^^^^^^^^^^^^^^^^^^ @@ -62,19 +66,6 @@ Range is 1 - 16 .The default value will be used, if this function is not used. * ``bits`` sets analog read resolution -analogSetClockDiv -^^^^^^^^^^^^^^^^^ - -This function is used to set the divider for the ADC clock. - -Range is 1 - 255. Default value is 1. - -.. code-block:: arduino - - void analogSetClockDiv(uint8_t clockDiv); - -* ``clockDiv`` sets the divider for ADC clock. - analogSetAttenuation ^^^^^^^^^^^^^^^^^^^^ @@ -95,7 +86,7 @@ The measurable input voltage differs for each chip, see table below for detailed ``ADC_ATTEN_DB_0`` 100 mV ~ 950 mV ``ADC_ATTEN_DB_2_5`` 100 mV ~ 1250 mV ``ADC_ATTEN_DB_6`` 150 mV ~ 1750 mV - ``ADC_ATTEN_DB_11`` 150 mV ~ 2450 mV + ``ADC_ATTEN_DB_11`` 150 mV ~ 3100 mV ===================== =========================================== .. tab:: ESP32-S2 @@ -148,24 +139,12 @@ This function is used to set the attenuation for a specific pin/ADC channel. For * ``pin`` selects specific pin for attenuation settings. * ``attenuation`` sets the attenuation. - -adcAttachPin -^^^^^^^^^^^^ - -This function is used to attach the pin to ADC (it will also clear any other analog mode that could be on) - -.. code-block:: arduino - - bool adcAttachPin(uint8_t pin); - -This function will return ``true`` if configuration is successful. Else returns ``false``. - -ADC API specific for ESP32 chip -******************************* analogSetWidth ^^^^^^^^^^^^^^ +.. note:: This function is only available for ESP32 chip. + This function is used to set the hardware sample bits and read resolution. Default is 12bit (0 - 4095). Range is 9 - 12. @@ -173,36 +152,130 @@ Range is 9 - 12. .. code-block:: arduino void analogSetWidth(uint8_t bits); - -analogSetVRefPin + +ADC Continuous mode +******************* + +ADC Continuous mode is an API designed for performing analog conversions on multiple pins in the background, +with the feature of receiving a callback upon completion of these conversions to access the results. + +This API allows you to specify the desired number of conversions per pin within a single cycle, along with its corresponding sampling rate. +The outcome of the ``analogContinuousRead`` function is an array of ``adc_continuous_data_t`` structures. +These structures hold both the raw average value and the average value in millivolts for each pin. + +analogContinuous ^^^^^^^^^^^^^^^^ -This function is used to set pin to use for ADC calibration if the esp is not already calibrated (pins 25, 26 or 27). +This function is used to configure ADC continuous peripheral on selected pins. .. code-block:: arduino - void analogSetVRefPin(uint8_t pin); + bool analogContinuous(uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void)); -* ``pin`` GPIO pin to set VRefPin for ADC calibration +* ``pins[]`` array of pins to be set up +* ``pins_count`` count of pins in array +* ``conversions_per_pin`` sets how many conversions per pin will run each ADC cycle +* ``sampling_freq_hz`` sets sampling frequency of ADC in Hz +* ``userFunc`` sets callback function to be called after adc conversion is done (can be set to ``NULL``) -hallRead -^^^^^^^^ +This function will return ``true`` if configuration is successful. +If ``false`` is returned, error occurs and ADC continuous was not configured. -This function is used to get the ADC value of the HALL sensor conneted to pins 36(SVP) and 39(SVN). +analogContinuousRead +^^^^^^^^^^^^^^^^^^^^ + +This function is used to read ADC continuous data to the result buffer. The result buffer is an array of ``adc_continuos_data_t``. .. code-block:: arduino - int hallRead(); - -This function will return the hall sensor value. + typedef struct { + uint8_t pin; /*! AnalogReadSerial. .. literalinclude:: ../../../libraries/ESP32/examples/AnalogRead/AnalogRead.ino :language: arduino -Or you can run Arduino example 01.Basics -> AnalogReadSerial. +Here is an example of how to use the ADC in Continuous mode. + +.. literalinclude:: ../../../libraries/ESP32/examples/AnalogReadContinuous/AnalogReadContinuous.ino + :language: arduino From f9a5f4b9bfec83e7bf63b84eece22ac83340ea18 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Thu, 7 Dec 2023 10:21:12 +0800 Subject: [PATCH 09/70] Merge conflicts resolved in the new docs --- docs/en/api/wifi.rst | 107 ++- docs/en/boards/boards.rst | 6 + docs/en/conf.py | 5 +- docs/en/getting_started.rst | 2 + docs/en/index.rst | 1 + docs/en/installing.rst | 2 + .../migration_guides/2.x_to_3.0.rst | 0 .../migration_guides/migration_guides.rst | 0 docs/en/ota_web_update.rst | 3 + docs/source/api/adc.rst | 281 -------- docs/source/api/wifi.rst | 675 ------------------ docs/source/boards/boards.rst | 120 ---- docs/source/conf.py | 72 -- docs/source/getting_started.rst | 155 ---- docs/source/index.rst | 23 - docs/source/installing.rst | 363 ---------- docs/source/ota_web_update.rst | 79 -- 17 files changed, 123 insertions(+), 1771 deletions(-) rename docs/{source => en}/migration_guides/2.x_to_3.0.rst (100%) rename docs/{source => en}/migration_guides/migration_guides.rst (100%) delete mode 100644 docs/source/api/adc.rst delete mode 100644 docs/source/api/wifi.rst delete mode 100644 docs/source/boards/boards.rst delete mode 100644 docs/source/conf.py delete mode 100644 docs/source/getting_started.rst delete mode 100644 docs/source/index.rst delete mode 100644 docs/source/installing.rst delete mode 100644 docs/source/ota_web_update.rst diff --git a/docs/en/api/wifi.rst b/docs/en/api/wifi.rst index d5632ae7717..9a4c1b17aba 100644 --- a/docs/en/api/wifi.rst +++ b/docs/en/api/wifi.rst @@ -50,6 +50,102 @@ Common API Here are the common APIs that are used for both modes, AP and STA. +onEvent (and removeEvent) +************************* + +Registers a caller-supplied function to be called when WiFi events +occur. Several forms are available. + +Function pointer callback taking the event ID: + +.. code-block:: arduino + + typedef void (*WiFiEventCb)(arduino_event_id_t); + wifi_event_id_t onEvent(WiFiEventCb, arduino_event_id_t = ARDUINO_EVENT_MAX); + +Function pointer callback taking an event-ID-and-info struct: + +.. code-block:: arduino + + typedef struct{ + arduino_event_id_t event_id; + arduino_event_info_t event_info; + } arduino_event_t; + + typedef void (*WiFiEventSysCb)(arduino_event_t *); + wifi_event_id_t onEvent(WiFiEventSysCb, arduino_event_id_t = ARDUINO_EVENT_MAX); + +Callback using ``std::function`` taking event ID and info separately: + +.. code-block:: arduino + + typedef std::function WiFiEventFuncCb; + wifi_event_id_t onEvent(WiFiEventFuncCb, arduino_event_id_t = ARDUINO_EVENT_MAX); + +A similar set of functions are available to remove callbacks: + +.. code-block:: arduino + + void removeEvent(WiFiEventCb, arduino_event_id_t = ARDUINO_EVENT_MAX); + void removeEvent(WiFiEventSysCb, arduino_event_id_t = ARDUINO_EVENT_MAX); + void removeEvent(wifi_event_id_t = ARDUINO_EVENT_MAX); + +In all cases, the subscribing function accepts an optional event type to +invoke the callback only for that specific event; with the default +``ARDUINO_EVENT_MAX``, the callback will be invoked for all WiFi events. + +Any callback function is given the event type in a parameter. +Some of the possible callback function formats also take an +``arduino_event_info_t`` (or use ``arduino_event_t`` which includes both +ID and info) which is a union of structs with additional information +about different event types. + +See +`WiFiGeneric.h `_ +for the list of event types and "info" substructures, and also see a full +example of event handling: `events example`_. + +.. warning:: + + Event callback functions are invoked on a separate + `thread `_ + (`FreeRTOS task `_) + independent of the main application thread that runs ``setup()`` and + ``loop()``. Callback functions must therefore be + `thread-safe `_; + they must not access shared/global variables directly without locking, + and must only call similarly thread-safe functions. + + Some core operations like ``Serial.print()`` are thread-safe but many + functions are not. Notably, ``WiFi.onEvent()`` and ``WiFi.removeEvent()`` + are not thread-safe and should never be invoked from a callback thread. + +setHostname (and getHostname) +***************************** + +Sets the name the DHCP client uses to identify itself. In a typical network +setup this will be the name that shows up in the Wi-Fi router's device list. +The hostname must be no longer than 32 characters. + +.. code-block:: arduino + + setHostname(const char *hostname); + +If the hostname is never specified, a default one will be assigned based +on the chip type and MAC address. The current hostname (default or custom) +may be retrieved: + +.. code-block:: arduino + + const char *getHostname(); + +.. warning:: + + The ``setHostname()`` function must be called BEFORE WiFi is started with + ``WiFi.begin()``, ``WiFi.softAP()``, ``WiFi.mode()``, or ``WiFi.run()``. + To change the name, reset WiFi with ``WiFi.mode(WIFI_MODE_NULL)``, + then proceed with ``WiFi.setHostname(...)`` and restart WiFi from scratch. + useStaticBuffers **************** @@ -552,6 +648,8 @@ To see how to use the ``WiFiScan``, take a look at the ``WiFiScan.ino`` example Examples -------- +`Complete list of WiFi examples `_. + .. _ap example: Wi-Fi AP Example @@ -568,5 +666,10 @@ Wi-Fi STA Example .. literalinclude:: ../../../libraries/WiFi/examples/WiFiClient/WiFiClient.ino :language: arduino -References ----------- +.. _events example: + +Wi-Fi Events Example +******************** + +.. literalinclude:: ../../../libraries/WiFi/examples/WiFiClientEvents/WiFiClientEvents.ino + :language: arduino diff --git a/docs/en/boards/boards.rst b/docs/en/boards/boards.rst index 9a1d2217dac..7323ba2a933 100644 --- a/docs/en/boards/boards.rst +++ b/docs/en/boards/boards.rst @@ -22,6 +22,9 @@ The ESP32 is divided by family: * Wi-Fi only * ESP32-C * Wi-Fi and BLE 5 + * IEEE 802.15.4 (only in ESP32-C6) +* ESP32-H + * BLE and IEEE 802.15.4 For each family, we have SoC variants with some differentiation. The differences are more about the embedded flash and its size and the number of the cores (dual or single). @@ -71,6 +74,9 @@ Espressif ESP32-S2-Saola-1 ESP32-C3-DevKitM-1 +.. note:: + Only a few development boards are described on this documentation page. For more information about other Espressif development boards please refer to the `Espressif website `_. + Third Party ----------- diff --git a/docs/en/conf.py b/docs/en/conf.py index 4d1208f256a..9979662dae0 100644 --- a/docs/en/conf.py +++ b/docs/en/conf.py @@ -25,4 +25,7 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -language = 'en' \ No newline at end of file +language = 'en' + +# Tracking ID for Google Analytics +google_analytics_id = 'G-F58JM78930' \ No newline at end of file diff --git a/docs/en/getting_started.rst b/docs/en/getting_started.rst index 6b2d4aca959..9267f1b427f 100644 --- a/docs/en/getting_started.rst +++ b/docs/en/getting_started.rst @@ -41,6 +41,8 @@ ESP32 Yes Yes `ESP32`_ ESP32-S2 Yes Yes `ESP32-S2`_ ESP32-C3 Yes Yes `ESP32-C3`_ ESP32-S3 Yes Yes `ESP32-S3`_ +ESP32-C6 No Yes `ESP32-C6`_ +ESP32-H2 No Yes `ESP32-H2`_ ======== ====== =========== =================================== See `Boards `_ for more details about ESP32 development boards. diff --git a/docs/en/index.rst b/docs/en/index.rst index 3ed77588b26..f7f93f9cc92 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -16,6 +16,7 @@ Here you will find all the relevant information about the project based on the { Guides Tutorials Advanced Utilities + Migration Guides FAQ Troubleshooting Contributing diff --git a/docs/en/installing.rst b/docs/en/installing.rst index 5f29ce10028..3acabd5505c 100644 --- a/docs/en/installing.rst +++ b/docs/en/installing.rst @@ -73,6 +73,8 @@ Installing using PlatformIO PlatformIO is a professional collaborative platform for embedded development. It has out-of-the-box support for ESP32 SoCs and allows working with Arduino ESP32 as well as ESP-IDF from Espressif without changing your development environment. PlatformIO includes lots of instruments for the most common development tasks such as debugging, unit testing, and static code analysis. +.. warning:: Integration of the Arduino Core ESP32 project in PlatformIO is maintained by PlatformIO developers. Arduino Core ESP32 Project Team cannot support PlatformIO-specific issues. Please report these issues in official `PlatformIO repositories `_. + A detailed overview of the PlatformIO ecosystem and its philosophy can be found in `the official documentation `_. PlatformIO can be used in two flavors: diff --git a/docs/source/migration_guides/2.x_to_3.0.rst b/docs/en/migration_guides/2.x_to_3.0.rst similarity index 100% rename from docs/source/migration_guides/2.x_to_3.0.rst rename to docs/en/migration_guides/2.x_to_3.0.rst diff --git a/docs/source/migration_guides/migration_guides.rst b/docs/en/migration_guides/migration_guides.rst similarity index 100% rename from docs/source/migration_guides/migration_guides.rst rename to docs/en/migration_guides/migration_guides.rst diff --git a/docs/en/ota_web_update.rst b/docs/en/ota_web_update.rst index 17cbe3583f5..179101770a0 100644 --- a/docs/en/ota_web_update.rst +++ b/docs/en/ota_web_update.rst @@ -8,6 +8,9 @@ OTAWebUpdate is done with a web browser that can be useful in the following typi - after deployment if user is unable to expose Firmware for OTA from external update server - provide updates after deployment to small quantity of modules when setting an update server is not practicable +For more information about the update process, please refer to the `OTA API reference `_ +section of the ESP-IDF documentation. + Requirements ------------ diff --git a/docs/source/api/adc.rst b/docs/source/api/adc.rst deleted file mode 100644 index 8ab252e181b..00000000000 --- a/docs/source/api/adc.rst +++ /dev/null @@ -1,281 +0,0 @@ -### -ADC -### - -About ------ - -ADC (analog to digital converter) is a very common peripheral used to convert an analog signal such as voltage -to a digital form so that it can be read and processed by a microcontroller. - -ADCs are very useful in control and monitoring applications since most sensors -(e.g., temperature, pressure, force) produce analogue output voltages. - -.. note:: Each SoC or module has a different number of ADC's with a different number of channels and pins available. Refer to datasheet of each board for more info. - -Arduino-ESP32 ADC API ---------------------- - -ADC OneShot mode -**************** - - -The ADC OneShot mode API is fully compatible with Arduino's ``analogRead`` function. -When you call the ``analogRead`` or ``analogReadMillivolts`` function, it returns the result of a single conversion on the requested pin. - -analogRead -^^^^^^^^^^ - -This function is used to get the ADC raw value for a given pin/ADC channel. - -.. code-block:: arduino - - uint16_t analogRead(uint8_t pin); - -* ``pin`` GPIO pin to read analog value - -This function will return analog raw value (non-calibrated). - -analogReadMillivolts -^^^^^^^^^^^^^^^^^^^^ - -This function is used to get ADC raw value for a given pin/ADC channel and convert it to calibrated result in millivolts. - -.. code-block:: arduino - - uint32_t analogReadMilliVolts(uint8_t pin); - -* ``pin`` GPIO pin to read analog value - -This function will return analog value in millivolts (calibrated). - -analogReadResolution -^^^^^^^^^^^^^^^^^^^^ - -This function is used to set the resolution of ``analogRead`` return value. Default is 12 bits (range from 0 to 4095) -for all chips except ESP32S3 where default is 13 bits (range from 0 to 8191). -When different resolution is set, the values read will be shifted to match the given resolution. - -Range is 1 - 16 .The default value will be used, if this function is not used. - -.. note:: For the ESP32, the resolution is between 9 to12 and it will change the ADC hardware resolution. Else value will be shifted. - -.. code-block:: arduino - - void analogReadResolution(uint8_t bits); - -* ``bits`` sets analog read resolution - -analogSetAttenuation -^^^^^^^^^^^^^^^^^^^^ - -This function is used to set the attenuation for all channels. - -Input voltages can be attenuated before being input to the ADCs. -There are 4 available attenuation options, the higher the attenuation is, the higher the measurable input voltage could be. - -The measurable input voltage differs for each chip, see table below for detailed information. - -.. tabs:: - - .. tab:: ESP32 - - ===================== =========================================== - Attenuation Measurable input voltage range - ===================== =========================================== - ``ADC_ATTEN_DB_0`` 100 mV ~ 950 mV - ``ADC_ATTEN_DB_2_5`` 100 mV ~ 1250 mV - ``ADC_ATTEN_DB_6`` 150 mV ~ 1750 mV - ``ADC_ATTEN_DB_11`` 150 mV ~ 3100 mV - ===================== =========================================== - - .. tab:: ESP32-S2 - - ===================== =========================================== - Attenuation Measurable input voltage range - ===================== =========================================== - ``ADC_ATTEN_DB_0`` 0 mV ~ 750 mV - ``ADC_ATTEN_DB_2_5`` 0 mV ~ 1050 mV - ``ADC_ATTEN_DB_6`` 0 mV ~ 1300 mV - ``ADC_ATTEN_DB_11`` 0 mV ~ 2500 mV - ===================== =========================================== - - .. tab:: ESP32-C3 - - ===================== =========================================== - Attenuation Measurable input voltage range - ===================== =========================================== - ``ADC_ATTEN_DB_0`` 0 mV ~ 750 mV - ``ADC_ATTEN_DB_2_5`` 0 mV ~ 1050 mV - ``ADC_ATTEN_DB_6`` 0 mV ~ 1300 mV - ``ADC_ATTEN_DB_11`` 0 mV ~ 2500 mV - ===================== =========================================== - - .. tab:: ESP32-S3 - - ===================== =========================================== - Attenuation Measurable input voltage range - ===================== =========================================== - ``ADC_ATTEN_DB_0`` 0 mV ~ 950 mV - ``ADC_ATTEN_DB_2_5`` 0 mV ~ 1250 mV - ``ADC_ATTEN_DB_6`` 0 mV ~ 1750 mV - ``ADC_ATTEN_DB_11`` 0 mV ~ 3100 mV - ===================== =========================================== - -.. code-block:: arduino - - void analogSetAttenuation(adc_attenuation_t attenuation); - -* ``attenuation`` sets the attenuation. - -analogSetPinAttenuation -^^^^^^^^^^^^^^^^^^^^^^^ - -This function is used to set the attenuation for a specific pin/ADC channel. For more information refer to `analogSetAttenuation`_. - -.. code-block:: arduino - - void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation); - -* ``pin`` selects specific pin for attenuation settings. -* ``attenuation`` sets the attenuation. - -analogSetWidth -^^^^^^^^^^^^^^ - -.. note:: This function is only available for ESP32 chip. - -This function is used to set the hardware sample bits and read resolution. -Default is 12bit (0 - 4095). -Range is 9 - 12. - -.. code-block:: arduino - - void analogSetWidth(uint8_t bits); - -ADC Continuous mode -******************* - -ADC Continuous mode is an API designed for performing analog conversions on multiple pins in the background, -with the feature of receiving a callback upon completion of these conversions to access the results. - -This API allows you to specify the desired number of conversions per pin within a single cycle, along with its corresponding sampling rate. -The outcome of the ``analogContinuousRead`` function is an array of ``adc_continuous_data_t`` structures. -These structures hold both the raw average value and the average value in millivolts for each pin. - -analogContinuous -^^^^^^^^^^^^^^^^ - -This function is used to configure ADC continuous peripheral on selected pins. - -.. code-block:: arduino - - bool analogContinuous(uint8_t pins[], size_t pins_count, uint32_t conversions_per_pin, uint32_t sampling_freq_hz, void (*userFunc)(void)); - -* ``pins[]`` array of pins to be set up -* ``pins_count`` count of pins in array -* ``conversions_per_pin`` sets how many conversions per pin will run each ADC cycle -* ``sampling_freq_hz`` sets sampling frequency of ADC in Hz -* ``userFunc`` sets callback function to be called after adc conversion is done (can be set to ``NULL``) - -This function will return ``true`` if configuration is successful. -If ``false`` is returned, error occurs and ADC continuous was not configured. - -analogContinuousRead -^^^^^^^^^^^^^^^^^^^^ - -This function is used to read ADC continuous data to the result buffer. The result buffer is an array of ``adc_continuos_data_t``. - -.. code-block:: arduino - - typedef struct { - uint8_t pin; /*! AnalogReadSerial. - -.. literalinclude:: ../../../libraries/ESP32/examples/AnalogRead/AnalogRead.ino - :language: arduino - -Here is an example of how to use the ADC in Continuous mode. - -.. literalinclude:: ../../../libraries/ESP32/examples/AnalogReadContinuous/AnalogReadContinuous.ino - :language: arduino diff --git a/docs/source/api/wifi.rst b/docs/source/api/wifi.rst deleted file mode 100644 index 1fd5651f38b..00000000000 --- a/docs/source/api/wifi.rst +++ /dev/null @@ -1,675 +0,0 @@ -######### -Wi-Fi API -######### - -About ------ - -The Wi-Fi API provides support for the 802.11b/g/n protocol driver. This API includes: - -* Station mode (STA mode or Wi-Fi client mode). ESP32 connects to an access point - -* AP mode (aka Soft-AP mode or Access Point mode). Devices connect to the ESP32 - -* Security modes (WPA2, WPA3 etc.) - -* Scanning for access points - -Working as AP -************* - -In this mode, the ESP32 is configured as an Access Point (AP) and it's capable of receiving incoming connections from other devices (stations) by providing -a Wi-Fi network. - -.. figure:: ../_static/wifi_esp32_ap.png - :align: center - :width: 520 - :figclass: align-center - -This mode can be used for serving an HTTP or HTTPS server inside the ESP32, for example. - -Working as STA -************** - -The STA mode is used to connect the ESP32 to a Wi-Fi network, provided by an Access Point. - -.. figure:: ../_static/wifi_esp32_sta.png - :align: center - :width: 520 - :figclass: align-center - -This is the mode to be used if you want to connect your project to the Internet. - -API Description ---------------- - -Here is the description of the WiFi API. - -Common API ----------- - -Here are the common APIs that are used for both modes, AP and STA. - -onEvent (and removeEvent) -************************* - -Registers a caller-supplied function to be called when WiFi events -occur. Several forms are available. - -Function pointer callback taking the event ID: - -.. code-block:: arduino - - typedef void (*WiFiEventCb)(arduino_event_id_t); - wifi_event_id_t onEvent(WiFiEventCb, arduino_event_id_t = ARDUINO_EVENT_MAX); - -Function pointer callback taking an event-ID-and-info struct: - -.. code-block:: arduino - - typedef struct{ - arduino_event_id_t event_id; - arduino_event_info_t event_info; - } arduino_event_t; - - typedef void (*WiFiEventSysCb)(arduino_event_t *); - wifi_event_id_t onEvent(WiFiEventSysCb, arduino_event_id_t = ARDUINO_EVENT_MAX); - -Callback using ``std::function`` taking event ID and info separately: - -.. code-block:: arduino - - typedef std::function WiFiEventFuncCb; - wifi_event_id_t onEvent(WiFiEventFuncCb, arduino_event_id_t = ARDUINO_EVENT_MAX); - -A similar set of functions are available to remove callbacks: - -.. code-block:: arduino - - void removeEvent(WiFiEventCb, arduino_event_id_t = ARDUINO_EVENT_MAX); - void removeEvent(WiFiEventSysCb, arduino_event_id_t = ARDUINO_EVENT_MAX); - void removeEvent(wifi_event_id_t = ARDUINO_EVENT_MAX); - -In all cases, the subscribing function accepts an optional event type to -invoke the callback only for that specific event; with the default -``ARDUINO_EVENT_MAX``, the callback will be invoked for all WiFi events. - -Any callback function is given the event type in a parameter. -Some of the possible callback function formats also take an -``arduino_event_info_t`` (or use ``arduino_event_t`` which includes both -ID and info) which is a union of structs with additional information -about different event types. - -See -`WiFiGeneric.h `_ -for the list of event types and "info" substructures, and also see a full -example of event handling: `events example`_. - -.. warning:: - - Event callback functions are invoked on a separate - `thread `_ - (`FreeRTOS task `_) - independent of the main application thread that runs ``setup()`` and - ``loop()``. Callback functions must therefore be - `thread-safe `_; - they must not access shared/global variables directly without locking, - and must only call similarly thread-safe functions. - - Some core operations like ``Serial.print()`` are thread-safe but many - functions are not. Notably, ``WiFi.onEvent()`` and ``WiFi.removeEvent()`` - are not thread-safe and should never be invoked from a callback thread. - -setHostname (and getHostname) -***************************** - -Sets the name the DHCP client uses to identify itself. In a typical network -setup this will be the name that shows up in the Wi-Fi router's device list. -The hostname must be no longer than 32 characters. - -.. code-block:: arduino - - setHostname(const char *hostname); - -If the hostname is never specified, a default one will be assigned based -on the chip type and MAC address. The current hostname (default or custom) -may be retrieved: - -.. code-block:: arduino - - const char *getHostname(); - -.. warning:: - - The ``setHostname()`` function must be called BEFORE WiFi is started with - ``WiFi.begin()``, ``WiFi.softAP()``, ``WiFi.mode()``, or ``WiFi.run()``. - To change the name, reset WiFi with ``WiFi.mode(WIFI_MODE_NULL)``, - then proceed with ``WiFi.setHostname(...)`` and restart WiFi from scratch. - -useStaticBuffers -**************** - -This function is used to set the memory allocation mode for the Wi-Fi buffers. - -.. code-block:: arduino - - static void useStaticBuffers(bool bufferMode); - -* Set ``true`` to use the Wi-Fi buffers memory allocation as **static**. -* Set ``false`` to set the buffers memory allocation to **dynamic**. - -The use of dynamic allocation is recommended to save memory and reduce resources usage. However, the dynamic performs slightly slower than the static allocation. -Use static allocation if you want to have more performance and if your application is multi-tasking. - -By default, the memory allocation will be set to **dynamic** if this function is not being used. - -setDualAntennaConfig -******************** - -Configures the Dual antenna functionallity. This function should be used only on the **ESP32-WROOM-DA** module or any other ESP32 with RF switch. - -.. code-block:: arduino - - bool setDualAntennaConfig(uint8_t gpio_ant1, uint8_t gpio_ant2, wifi_rx_ant_t rx_mode, wifi_tx_ant_t tx_mode); - - -* ``gpio_ant1`` Configure the GPIO number for the antenna 1 connected to the RF switch (default ``GPIO2`` on ESP32-WROOM-DA) -* ``gpio_ant2`` Configure the GPIO number for the antenna 2 connected to the RF switch (default ``GPIO25`` on ESP32-WROOM-DA) -* ``rx_mode`` Set the RX antenna mode. See wifi_rx_ant_t for the options. -* ``tx_mode`` Set the TX antenna mode. See wifi_tx_ant_t for the options. - -Return ``true`` if the configuration was successful. - -For the ``rx_mode`` you can use the following configuration: - -* ``WIFI_RX_ANT0`` Selects the antenna 1 for all RX activity. -* ``WIFI_RX_ANT1`` Selects the antenna 2 for all RX activity. -* ``WIFI_RX_ANT_AUTO`` Selects the antenna for RX automatically. - -For the ``tx_mode`` you can use the following configuration: - -* ``WIFI_TX_ANT0`` Selects the antenna 1 for all TX activity. -* ``WIFI_TX_ANT1`` Selects the antenna 2 for all TX activity. -* ``WIFI_TX_ANT_AUTO`` Selects the antenna for TX automatically. - -WiFiAP ------- - -The ``WiFiAP`` is used to configure and manage the Wi-Fi as an Access Point. This is where you can find the related functions for the AP. - -Basic Usage -*********** - -To start the Wi-Fi as an Access Point. - -.. code-block:: arduino - - WiFi.softAP(ssid, password); - -Please see the full WiFiAP example in: `ap example`_. - -AP Configuration ----------------- - -softAP -****** - -Use the function ``softAP`` to configure the Wi-Fi AP characteristics: - -.. code-block:: arduino - - bool softAP(const char* ssid, const char* passphrase = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4, bool ftm_responder = false); - -Where: - -* ``ssid`` sets the Wi-Fi network SSID. -* ``passphrase`` sets the Wi-Fi network password. If the network is open, set as ``NULL``. -* ``channel`` configures the Wi-Fi channel. -* ``ssid_hidden`` sets the network as hidden. -* ``max_connection`` sets the maximum number of simultaneous connections. The default is 4. -* ``ftm_responder`` sets the Wi-Fi FTM responder feature. **Only for ESP32-S2 and ESP32-C3 SoC!** - -Return ``true`` if the configuration was successful. - -softAPConfig -************ - -Function used to configure the IP as static (fixed) as well as the gateway and subnet. - -.. code-block:: arduino - - bool softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet); - -Where: - -* ``local_ip`` sets the local IP address. -* ``gateway`` sets the gateway IP. -* ``subnet`` sets the subnet mask. - -The function will return ``true`` if the configuration is successful. - -AP Connection -------------- - -softAPdisconnect -**************** - -Function used to force the AP disconnection. - -.. code-block:: arduino - - bool softAPdisconnect(bool wifioff = false); - -Where: - -* ``wifioff`` sets the Wi-Fi off if ``true``. - -The function will return ``true`` if the configuration is successful. - - -softAPgetStationNum -******************* - -This function returns the number of clients connected to the AP. - -.. code-block:: arduino - - uint8_t softAPgetStationNum(); - -softAPIP -******** - -Function to get the AP IPv4 address. - -.. code-block:: arduino - - IPAddress softAPIP(); - -The function will return the AP IP address in ``IPAddress`` format. - -softAPBroadcastIP -***************** - -Function to get the AP IPv4 broadcast address. - -.. code-block:: arduino - - IPAddress softAPBroadcastIP(); - -The function will return the AP broadcast address in ``IPAddress`` format. - -softAPNetworkID -*************** - -Get the softAP network ID. - -.. code-block:: arduino - - IPAddress softAPNetworkID(); - -The function will return the AP network address in ``IPAddress`` format. - -softAPSubnetCIDR -**************** - -Get the softAP subnet CIDR. - -.. code-block:: arduino - - uint8_t softAPSubnetCIDR(); - -softAPSubnetMask -**************** - -Get the softAP subnet mask. - -.. code-block:: arduino - - IPAddress softAPSubnetMask(); - -softAPenableIpV6 -**************** - -Function used to enable the IPv6 support. - -.. code-block:: arduino - - bool softAPenableIpV6(); - -The function will return ``true`` if the configuration is successful. - -softAPIPv6 -********** - -Function to get the IPv6 address. - -.. code-block:: arduino - - IPv6Address softAPIPv6(); - -The function will return the AP IPv6 address in ``IPv6Address`` format. - -softAPgetHostname -***************** - -Function to get the AP hostname. - -.. code-block:: arduino - - const char * softAPgetHostname(); - -softAPsetHostname -***************** - -Function to set the AP hostname. - -.. code-block:: arduino - - bool softAPsetHostname(const char * hostname); - -Where: - -* ``hostname`` sets the device hostname. - -The function will return ``true`` if the configuration is successful. - - -softAPmacAddress -**************** - -Function to define the AP MAC address. - -.. code-block:: arduino - - uint8_t* softAPmacAddress(uint8_t* mac); - -Where: - -* ``mac`` sets the new MAC address. - -Function to get the AP MAC address. - -.. code-block:: arduino - - String softAPmacAddress(void); - -softAPSSID -********** - -Function to get the AP SSID. - -.. code-block:: arduino - - String softAPSSID(void) const; - -Returns the AP SSID. - -WiFiSTA -------- - -The ``WiFiSTA`` is used to configure and manage the Wi-Fi as Station. The related functions for the STA are here. - -Basic Usage -*********** - -The following code shows the basic usage of the WifiSTA functionality. - -.. code-block:: arduino - - WiFi.begin(ssid, password); - -Where the ``ssid`` and ``password`` are from the network you want to connect the ESP32. - -To check if the connection is successful, you can use: - -.. code-block:: arduino - - while (WiFi.status() != WL_CONNECTED) { - delay(500); - Serial.print("."); - } - -After a successful connection, you can print the IP address given by the network. - -.. code-block:: arduino - - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - -Please see the full example of the WiFiSTA in: `sta example`_. - -STA Configuration ------------------ - -begin -***** - -- Functions ``begin`` are used to configure and start the Wi-Fi. - -.. code-block:: arduino - - wl_status_t begin(const char* ssid, const char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true); - -Where: - -* ``ssid`` sets the AP SSID. -* ``passphrase`` sets the AP password. Set as ``NULL`` for open networks. -* ``channel`` sets the Wi-Fi channel. -* ``uint8_t* bssid`` sets the AP BSSID. -* ``connect`` sets ``true`` to connect to the configured network automatically. - -.. code-block:: arduino - - wl_status_t begin(char* ssid, char *passphrase = NULL, int32_t channel = 0, const uint8_t* bssid = NULL, bool connect = true); - -Where: - -* ``ssid`` sets the AP SSID. -* ``passphrase`` sets the AP password. Set as ``NULL`` for open networks. -* ``channel`` sets the Wi-Fi channel. -* ``bssid`` sets the AP BSSID. -* ``connect`` sets ``true`` to connect to the configured network automatically. - -Function to start the connection after being configured. - -.. code-block:: arduino - - wl_status_t begin(); - -config -****** - -Function ``config`` is used to configure Wi-Fi. After configuring, you can call function ``begin`` to start the Wi-Fi process. - -.. code-block:: arduino - - bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000); - -Where: - -* ``local_ip`` sets the local IP. -* ``gateway`` sets the gateway IP. -* ``subnet`` sets the subnet mask. -* ``dns1`` sets the DNS. -* ``dns2`` sets the DNS alternative option. - -The function will return ``true`` if the configuration is successful. - -The ``IPAddress`` format is defined by 4 bytes as described here: - -.. code-block:: arduino - - IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); - -Example: - -.. code-block:: arduino - - IPAddress local_ip(192, 168, 10, 20); - -See the ``WiFiClientStaticIP.ino`` for more details on how to use this feature. - -STA Connection --------------- - -reconnect -********* - -Function used to reconnect the Wi-Fi connection. - -.. code-block:: arduino - - bool reconnect(); - -disconnect -********** - -Function to force disconnection. - -.. code-block:: arduino - - bool disconnect(bool wifioff = false, bool eraseap = false); - -Where: - -* ``wifioff`` use ``true`` to turn the Wi-Fi radio off. -* ``eraseap`` use ``true`` to erase the AP configuration from the NVS memory. - -The function will return ``true`` if the configuration is successful. - -isConnected -*********** - -Function used to get the connection state. - -.. code-block:: arduino - - bool isConnected(); - -Return the connection state. - -setAutoConnect -************** - -Function is deprecated. - -getAutoConnect -************** - -Function is deprecated. - -setAutoReconnect -**************** - -Function used to set the automatic reconnection if the connection is lost. - -.. code-block:: arduino - - bool setAutoReconnect(bool autoReconnect); - -Where: - -* ``autoConnect`` is set to ``true`` to enable this option. - -getAutoReconnect -**************** - -Function used to get the automatic reconnection if the connection is lost. - -.. code-block:: arduino - - bool getAutoReconnect(); - -The function will return ``true`` if this setting is enabled. - -setMinSecurity -************** - -Function used to set the minimum security for AP to be considered connectable. - -.. code-block:: arduino - - bool setMinSecurity(wifi_auth_mode_t minSecurity); - -Where: - -* ``minSecurity`` is the minimum security for AP to be considered connectable. Default is ``WIFI_AUTH_WPA2_PSK``. - -WiFiMulti ---------- - -The ``WiFiMulti`` allows you to add more than one option for the AP connection while running as a station. - -To add the AP, use the following function. You can add multiple AP's and this library will handle the connection. - -.. code-block:: arduino - - bool addAP(const char* ssid, const char *passphrase = NULL); - -After adding the AP's, run by the following function. - -.. code-block:: arduino - - uint8_t run(uint32_t connectTimeout=5000); - -To see how to use the ``WiFiMulti``, take a look at the ``WiFiMulti.ino`` example available. - -WiFiScan --------- - -To perform the Wi-Fi scan for networks, you can use the following functions: - -Start scan WiFi networks available. - -.. code-block:: arduino - - int16_t scanNetworks(bool async = false, bool show_hidden = false, bool passive = false, uint32_t max_ms_per_chan = 300, uint8_t channel = 0); - -Called to get the scan state in Async mode. - -.. code-block:: arduino - - int16_t scanComplete(); - -Delete last scan result from RAM. - -.. code-block:: arduino - - void scanDelete(); - -Loads all infos from a scanned wifi in to the ptr parameters. - -.. code-block:: arduino - - bool getNetworkInfo(uint8_t networkItem, String &ssid, uint8_t &encryptionType, int32_t &RSSI, uint8_t* &BSSID, int32_t &channel); - -To see how to use the ``WiFiScan``, take a look at the ``WiFiScan.ino`` example available. - -Examples --------- - -`Complete list of WiFi examples `_. - -.. _ap example: - -Wi-Fi AP Example -**************** - -.. literalinclude:: ../../../libraries/WiFi/examples/WiFiAccessPoint/WiFiAccessPoint.ino - :language: arduino - -.. _sta example: - -Wi-Fi STA Example -***************** - -.. literalinclude:: ../../../libraries/WiFi/examples/WiFiClient/WiFiClient.ino - :language: arduino - -.. _events example: - -Wi-Fi Events Example -******************** - -.. literalinclude:: ../../../libraries/WiFi/examples/WiFiClientEvents/WiFiClientEvents.ino - :language: arduino diff --git a/docs/source/boards/boards.rst b/docs/source/boards/boards.rst deleted file mode 100644 index 6214fe4ebd1..00000000000 --- a/docs/source/boards/boards.rst +++ /dev/null @@ -1,120 +0,0 @@ -###### -Boards -###### - -Development Boards ------------------- - -You will need a development board or a custom board with the ESP32 (see Supported SoC's) to start playing. -There is a bunch of different types and models widely available on the Internet. You need to choose one that covers all your requirements. - -To help you on this selection, we point out some facts about choosing the proper boards to help you to save money and time. - -**One ESP32 to rule them all!** - -One important information that usually bring about some confusion is regarding the different models of the ESP32 SoC and modules. - -The ESP32 is divided by family: - -* ESP32 - * Wi-Fi and BLE -* ESP32-S - * Wi-Fi only -* ESP32-C - * Wi-Fi and BLE 5 -* ESP32-H - * BLE and IEEE 802.15.4 - -For each family, we have SoC variants with some differentiation. The differences are more about the embedded flash and its size and the number of the cores (dual or single). - -The modules use the SoC internally, including the external flash, PSRAM (in some models) and other essential electronic components. Essentially, all -modules from the same family use the same SoC. - -.. figure:: ../_static/soc-module.png - :align: center - :width: 250 - :alt: ESP32 SoC and Module (click to enlarge) - :figclass: align-center - -**For example:** - -The SoC partnumber is the ESP32-D0WD-V3 and it's the same SoC used inside of the ESP32-WROVER (with PSRAM) and ESP32-WROOM modules. This means that the -same characteristics are present in both modules' core. - -For more detailed information regarding the SoC's and modules, see the `Espressif Product Selector`_. - -Now that you know that the module can be different but the heart is the same, you can choose your development board. - -Before buying: Keep in mind that for some "must have" features when choosing the best board for your needs: - -- Embedded USB-to-Serial - - This is very convenient for programming and monitoring the logs with the terminal via USB. -- Breadboard friendly - - If you are prototyping, this will be very useful to connect your board directly on the breadboard. -- open-source/open-hardware - - Check if the schematics are available for download. This helps a lot on prototyping. -- Support - - Some of the manufacturers offer a very good level of support, with examples and demo projects. - - -Espressif ---------- - -.. figure:: ../_static/logo_espressif.png - :align: center - :width: 250 - :alt: Espressif Logo - :figclass: align-center - -.. toctree:: - :maxdepth: 1 - - ESP32-DevKitC - ESP32-S2-Saola-1 - ESP32-C3-DevKitM-1 - -.. note:: - Only a few development boards are described on this documentation page. For more information about other Espressif development boards please refer to the `Espressif website `_. - -Third Party ------------ - -Add here the third party boards, listed by vendors. - -.. note:: - All the information must be provided by the vendor. If your favorite board is not here, consider - creating an `issue on GitHub `_ and directly - link/mention the vendor in the issue description. - -LOLIN -***** - -* |board_lolin_d32| -* |board_lolin_d32_pro| - -Generic Vendor -************** - - .. toctree:: - :maxdepth: 1 - - Generic Board Name - - .. note:: - Create one file per board or one file with multiple boards. Do not add board information/description on this file. - -.. include:: ../common/datasheet.inc - -Resources ---------- - -.. _Espressif Systems: https://www.espressif.com -.. _Espressif Product Selector: https://products.espressif.com/ - -.. |board_lolin_d32| raw:: html - - LOLIN D32 - -.. |board_lolin_d32_pro| raw:: html - - LOLIN D32 Pro diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 47b30b912ed..00000000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,72 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import os -import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = 'Arduino-ESP32' -copyright = '2023, Espressif' -author = 'Espressif' - -# The full version, including alpha/beta/rc tags -release = '2.0.14' - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx_rtd_theme', - 'sphinx_copybutton', - 'sphinx_tabs.tabs' -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = "sphinx_rtd_theme" -html_logo = '_static/logo_espressif.png' - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Style -# pygments_style = "sphinx" - -# Tracking ID for Google Analytics -google_analytics_id = 'G-F58JM78930' diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst deleted file mode 100644 index 3540dda685b..00000000000 --- a/docs/source/getting_started.rst +++ /dev/null @@ -1,155 +0,0 @@ -############### -Getting Started -############### - -About Arduino ESP32 -------------------- - -Welcome to the Arduino ESP32 support documentation! Here you will find important information on how to use the project. - -First Things First ------------------- - -.. note:: - Before continuing, we must be clear that this project is supported by `Espressif Systems`_ and the community. - Everyone is more than welcome to contribute back to this project. - -ESP32 is a single 2.4 GHz Wi-Fi-and-Bluetooth SoC (System On a Chip) designed by `Espressif Systems`_. - -ESP32 is designed for mobile, wearable electronics, and Internet-of-Things (IoT) applications. It features all the state-of-the-art characteristics -of low-power chips, including fine-grained clock gating, multiple power modes,and dynamic power scaling. For instance, in a low-power IoT sensor -hub application scenario, ESP32 is woken-up periodically and only when a specified condition is detected. Low-duty cycle is used to minimize the -amount of energy that the chip expends. - -The output of the power amplifier is also adjustable, thus contributing to an optimal trade-off between communication range, data rate and -power consumption. - -The ESP32 series is available as a chip or module. - - -.. _supported_socs: - -Supported SoC's ---------------- - -Here are the ESP32 series supported by the Arduino-ESP32 project: - -======== ====== =========== =================================== -SoC Stable Development Datasheet -======== ====== =========== =================================== -ESP32 Yes Yes `ESP32`_ -ESP32-S2 Yes Yes `ESP32-S2`_ -ESP32-C3 Yes Yes `ESP32-C3`_ -ESP32-S3 Yes Yes `ESP32-S3`_ -ESP32-C6 No Yes `ESP32-C6`_ -ESP32-H2 No Yes `ESP32-H2`_ -======== ====== =========== =================================== - -See `Boards `_ for more details about ESP32 development boards. - -Arduino Core Reference ----------------------- - -This documentation is built on the ESP32 and we are not going to cover the common Arduino API. To see the Arduino reference documentation, -please consider reading the official documentation. - -Arduino Official Documentation: `Arduino Reference`_. - -Supported Operating Systems ---------------------------- - -+-------------------+-------------------+-------------------+ -| |windows-logo| | |linux-logo| | |macos-logo| | -+-------------------+-------------------+-------------------+ -| Windows | Linux | macOS | -+-------------------+-------------------+-------------------+ - -.. |windows-logo| image:: _static/logo_windows.png -.. |linux-logo| image:: _static/logo_linux.png -.. |macos-logo| image:: _static/logo_macos.png - -Supported IDEs ---------------------------- - -Here is the list of supported IDE for Arduino ESP32 support integration. - -+-------------------+-------------------+ -| |arduino-logo| | |pio-logo| | -+-------------------+-------------------+ -| Arduino IDE | PlatformIO | -+-------------------+-------------------+ - -.. |arduino-logo| image:: _static/logo_arduino.png -.. |pio-logo| image:: _static/logo_pio.png - -See `Installing Guides `_ for more details on how to install the Arduino ESP32 support. - -Support -------- - -This is an open project and it's supported by the community. Fell free to ask for help in one of the community channels. - -Community ---------- - -The Arduino community is huge! You can find a lot of useful content on the Internet. -Here are some community channels where you may find information and ask for some help, if needed. - -- `ESP32 Forum`_: Official Espressif Forum. -- `ESP32 Forum - Arduino`_: Official Espressif Forum for Arduino related discussions. -- `ESP32 Forum - Hardware`_: Official Espressif Forum for Hardware related discussions. -- `Gitter`_ -- `Espressif MCUs (Discord)`_ -- `ESP32 on Reddit`_ - -Issues Reporting ----------------- - -Before opening a new issue, please read this: - -Be sure to search for a similar reported issue. This avoids duplicating or creating noise in the GitHub Issues reporting. -We also have the troubleshooting guide to save your time on the most common issues reported by users. - -For more details about creating new Issue, see the `Issue Template `_. - -If you have any new idea, see the `Feature request Template `_. - -First Steps ------------ - -Here are the first steps to get the Arduino ESP32 support running. - -To install Arduino-ESP32, please see the dedicated section on the Installation guide. We recommend you install it using the boards manager. - -.. toctree:: - :maxdepth: 2 - - How to Install - Development Boards - -Examples --------- - -After installing the toolchain into your environment, you will be able to see all the dedicated examples for the ESP32. These examples are located -in the examples menu or inside each library folder. - - https://github.com/espressif/arduino-esp32/tree/master/libraries - -There is also a `list of examples `_ managed outside of Espressif, so check them out. - -.. include:: common/datasheet.inc - -Resources ---------- - -.. _Espressif Systems: https://www.espressif.com -.. _Espressif Product Selector: https://products.espressif.com/ -.. _Arduino.cc: https://www.arduino.cc/en/Main/Software -.. _Arduino Reference: https://www.arduino.cc/reference/en/ -.. _ESP32 Forum: https://esp32.com -.. _ESP32 Forum - Arduino: https://esp32.com/viewforum.php?f=19 -.. _ESP32 Forum - Hardware: https://esp32.com/viewforum.php?f=12 -.. _Gitter: https://gitter.im/espressif/arduino-esp32 -.. _Adafruit (Discord): https://discord.gg/adafruit -.. _Espressif MCUs (Discord): https://discord.gg/nKxMTnkD -.. _ESP32 on Reddit: https://www.reddit.com/r/esp32 diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index 4016f36f3d4..00000000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,23 +0,0 @@ -############################################# -Welcome to ESP32 Arduino Core's documentation -############################################# - -Here you will find all the relevant information about the project. - -.. note:: - This is a work in progress documentation and we will appreciate your help! We are looking for contributors! - -.. toctree:: - :maxdepth: 1 - :caption: Contents: - - Getting Started - Libraries - Guides - Tutorials - Advanced Utilities - Migration Guides - FAQ - Troubleshooting - Contributing - External Libraries Testing diff --git a/docs/source/installing.rst b/docs/source/installing.rst deleted file mode 100644 index 3b5554baf32..00000000000 --- a/docs/source/installing.rst +++ /dev/null @@ -1,363 +0,0 @@ -########## -Installing -########## - -This guide will show how to install the Arduino-ESP32 support. - -Before Installing ------------------ - -We recommend you install the support using your favorite IDE, but other options are available depending on your operating system. -To install Arduino-ESP32 support, you can use one of the following options. - -Installing using Arduino IDE ----------------------------- - -.. figure:: _static/logo_arduino.png - :align: center - :width: 200 - :figclass: align-center - -This is the way to install Arduino-ESP32 directly from the Arduino IDE. - -.. note:: - For overview of SoC's support, take a look on `Supported Soc's table `_ where you can find if the particular chip is under stable or development release. - -- Stable release link:: - - https://espressif.github.io/arduino-esp32/package_esp32_index.json - -- Development release link:: - - https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json - - -.. note:: - Starting with the Arduino IDE version 1.6.4, Arduino allows installation of third-party platform - packages using Boards Manager. We have packages available for Windows, macOS, and Linux. - -To start the installation process using the Boards Managaer, follow these steps: - -- Install the current upstream Arduino IDE at the 1.8 level or later. The current version is at the `arduino.cc`_ website. - -- Start Arduino and open the Preferences window. - -.. figure:: _static/install_guide_preferences.png - :align: center - :width: 600 - :figclass: align-center - -- Enter one of the release links above into *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas. - -.. figure:: _static/install_guide_boards_manager_url.png - :align: center - :width: 600 - :figclass: align-center - -- Open Boards Manager from Tools > Board menu and install *esp32* platform (and do not forget to select your ESP32 board from Tools > Board menu after installation). - -.. figure:: _static/install_guide_boards_manager_esp32.png - :align: center - :width: 600 - :figclass: align-center - -- Restart Arduino IDE. - -Installing using PlatformIO ---------------------------- - -.. figure:: _static/logo_pio.png - :align: center - :width: 200 - :figclass: align-center - -PlatformIO is a professional collaborative platform for embedded development. It has out-of-the-box support for ESP32 SoCs and allows working with Arduino ESP32 as well as ESP-IDF from Espressif without changing your development environment. PlatformIO includes lots of instruments for the most common development tasks such as debugging, unit testing, and static code analysis. - -.. warning:: Integration of the Arduino Core ESP32 project in PlatformIO is maintained by PlatformIO developers. Arduino Core ESP32 Project Team cannot support PlatformIO-specific issues. Please report these issues in official `PlatformIO repositories `_. - -A detailed overview of the PlatformIO ecosystem and its philosophy can be found in `the official documentation `_. - -PlatformIO can be used in two flavors: - -- `PlatformIO IDE `_ is a toolset for embedded C/C++ development available on Windows, macOS and Linux platforms - -- `PlatformIO Core (CLI) `_ is a command-line tool that consists of a multi-platform build system, platform and library managers and other integration components. It can be used with a variety of code development environments and allows integration with cloud platforms and web services - -To install PlatformIO, you can follow this Getting Started, provided at `docs.platformio.org`_. - -Using the stable code -********************* - -.. note:: - A detailed overview of supported development boards, examples and frameworks can be found on `the official Espressif32 dev-platform page `_ in the PlatformIO Registry. - -The most reliable and easiest way to get started is to use the latest stable version of the ESP32 development platform that passed all tests/verifications and can be used in production. - -Create a new project and select one of the available boards. You can change after by changing the `platformio.ini `_ file. - -- For ESP32 - -.. code-block:: bash - - [env:esp32dev] - platform = espressif32 - board = esp32dev - framework = arduino - -- For ESP32-S2 (ESP32-S2-Saola-1 board) - -.. code-block:: bash - - [env:esp32-s2-saola-1] - platform = espressif32 - board = esp32-s2-saola-1 - framework = arduino - -- For ESP32-C3 (ESP32-C3-DevKitM-1 board) - -.. code-block:: bash - - [env:esp32-c3-devkitm-1] - platform = espressif32 - board = esp32-c3-devkitm-1 - framework = arduino - -How to update to the latest code -******************************** - -To test the latest Arduino ESP32, you need to change your project *platformio.ini* accordingly. -The following configuration uses the upstream version of the Espressif development platform and the latest Arduino core directly from the Espressif GitHub repository: - -.. code-block:: bash - - [env:esp32-c3-devkitm-1] - platform = https://github.com/platformio/platform-espressif32.git - board = esp32-c3-devkitm-1 - framework = arduino - platform_packages = - framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32#master - - -To get more information about PlatformIO, see the following links: - -- `PlatformIO Core (CLI) `_ - -- `PlatformIO Home `_ - -- `Tutorials and Examples `_ - -- `Library Management `_ - - -Windows (manual installation) ------------------------------ - -.. warning:: Arduino ESP32 core v2.x.x cannot be used on Windows 8.x x86 (32 bits), Windows 7 or earlier. The Windows 32 bits OS is no longer supported by this toolchain. - - The Arduino ESP32 v1.0.6 still works on WIN32. You might want to install python 3.8.x because it is the latest release supported by Windows 7. - -Steps to install Arduino ESP32 support on Windows: - -**Step 1** - -1. Download and install the latest Arduino IDE ``Windows Installer`` from [arduino.cc](https://www.arduino.cc/en/Main/Software) -2. Download and install Git from [git-scm.com](https://git-scm.com/download/win) -3. Start ``Git GUI`` and do the following steps: - -- Select ``Clone Existing Repository`` - -.. figure:: _static/win-gui-1.png - :align: center - :width: 600 - :figclass: align-center - -- Select source and destination - - Sketchbook Directory: Usually ``C:/Users/[YOUR_USER_NAME]/Documents/Arduino`` and is listed underneath the "Sketchbook location" in Arduino preferences. - - Source Location: ``https://github.com/espressif/arduino-esp32.git`` - - Target Directory: ``[ARDUINO_SKETCHBOOK_DIR]/hardware/espressif/esp32`` - - Click ``Clone`` to start cloning the repository - -**Step 2** - -.. figure:: _static/win-gui-2.png - :align: center - :figclass: align-center - -**Step 3** - -.. figure:: _static/win-gui-3.png - :align: center - :figclass: align-center - -- open a `Git Bash` session pointing to ``[ARDUINO_SKETCHBOOK_DIR]/hardware/espressif/esp32`` and execute ```git submodule update --init --recursive``` -- Open ``[ARDUINO_SKETCHBOOK_DIR]/hardware/espressif/esp32/tools`` and double-click ``get.exe`` - -**Step 4** - -.. figure:: _static/win-gui-4.png - :align: center - :figclass: align-center - -- When ```get.exe``` finishes, you should see the following files in the directory - -**Step 5** - -.. figure:: _static/win-gui-5.png - :align: center - :figclass: align-center - -1. Plug your ESP32 board and wait for the drivers to install (or install manually any that might be required) -2. Start Arduino IDE -3. Select your board in ``Tools > Board`` menu -4. Select the COM port that the board is attached to -5. Compile and upload (You might need to hold the boot button while uploading) - -.. figure:: _static/arduino-ide.png - :align: center - :figclass: align-center - -How to update to the latest code -******************************** - -1. Start ``Git GUI`` and you should see the repository under ``Open Recent Repository``. Click on it! - -.. figure:: _static/win-gui-update-1.png - :align: center - :figclass: align-center - -1. From menu ``Remote`` select ``Fetch from`` > ``origin`` - -.. figure:: _static/win-gui-update-2.png - :align: center - :figclass: align-center - -1. Wait for git to pull any changes and close ``Git GUI`` -2. Open ``[ARDUINO_SKETCHBOOK_DIR]/hardware/espressif/esp32/tools`` and double-click ``get.exe`` - -.. figure:: _static/win-gui-4.png - :align: center - :figclass: align-center - -Linux ------ - -.. figure:: _static/logo_linux.png - :align: center - :width: 200 - :figclass: align-center - -Debian/Ubuntu -************* - -- Install latest Arduino IDE from `arduino.cc`_. - -- Open Terminal and execute the following command (copy -> paste and hit enter): - -.. code-block:: bash - - sudo usermod -a -G dialout $USER && \ - sudo apt-get install git && \ - wget https://bootstrap.pypa.io/get-pip.py && \ - sudo python3 get-pip.py && \ - sudo pip3 install pyserial && \ - mkdir -p ~/Arduino/hardware/espressif && \ - cd ~/Arduino/hardware/espressif && \ - git clone https://github.com/espressif/arduino-esp32.git esp32 && \ - cd esp32/tools && \ - python3 get.py - -- Restart Arduino IDE. - -- If you have Arduino installed to ~/, modify the installation as follows, beginning at `mkdir -p ~/Arduino/hardware`: - -.. code-block:: bash - - cd ~/Arduino/hardware - mkdir -p espressif && \ - cd espressif && \ - git clone https://github.com/espressif/arduino-esp32.git esp32 && \ - cd esp32/tools && \ - python3 get.py - -Fedora -****** - -- Install the latest Arduino IDE from `arduino.cc`_. - -.. note:: - Command ``$ sudo dnf -y install arduino`` will most likely install an older release. - -- Open Terminal and execute the following command (copy -> paste and hit enter): - -.. code-block:: bash - - sudo usermod -a -G dialout $USER && \ - sudo dnf install git python3-pip python3-pyserial && \ - mkdir -p ~/Arduino/hardware/espressif && \ - cd ~/Arduino/hardware/espressif && \ - git clone https://github.com/espressif/arduino-esp32.git esp32 && \ - cd esp32/tools && \ - python get.py - -- Restart Arduino IDE. - -openSUSE -******** - -- Install the latest Arduino IDE from `arduino.cc`_. - -- Open Terminal and execute the following command (copy -> paste and hit enter): - -.. code-block:: bash - - sudo usermod -a -G dialout $USER && \ - if [ `python --version 2>&1 | grep '2.7' | wc -l` = "1" ]; then \ - sudo zypper install git python-pip python-pyserial; \ - else \ - sudo zypper install git python3-pip python3-pyserial; \ - fi && \ - mkdir -p ~/Arduino/hardware/espressif && \ - cd ~/Arduino/hardware/espressif && \ - git clone https://github.com/espressif/arduino-esp32.git esp32 && \ - cd esp32/tools && \ - python get.py - -- Restart Arduino IDE. - -macOS ------ - -- Install the latest Arduino IDE from `arduino.cc`_. - -- Open Terminal and execute the following command (copy -> paste and hit enter): - -.. code-block:: bash - - mkdir -p ~/Documents/Arduino/hardware/espressif && \ - cd ~/Documents/Arduino/hardware/espressif && \ - git clone https://github.com/espressif/arduino-esp32.git esp32 && \ - cd esp32/tools && \ - python get.py - -Where ``~/Documents/Arduino`` represents your sketch book location as per "Arduino" > "Preferences" > "Sketchbook location" (in the IDE once started). Adjust the command above accordingly. - -- If you get the error below, install through the command line dev tools with `xcode-select --install` and try the command above again: - -.. code-block:: bash - - xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun - -- Run the command: - -.. code-block:: bash - - xcode-select --install - -- Try ``python3`` instead of ``python`` if you get the error: ``IOError: [Errno socket error] [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590)`` when running ``python get.py`` - -- If you get the following error when running ``python get.py`` urllib.error.URLError: Applications > Python3.6 folder (or any other python version), and run the following scripts: Install Certificates.command and Update Shell Profile.command - -- Restart Arduino IDE. - -.. _Arduino.cc: https://www.arduino.cc/en/Main/Software -.. _docs.platformio.org: https://docs.platformio.org/en/latest/integration/ide/pioide.html diff --git a/docs/source/ota_web_update.rst b/docs/source/ota_web_update.rst deleted file mode 100644 index be0a6d5e263..00000000000 --- a/docs/source/ota_web_update.rst +++ /dev/null @@ -1,79 +0,0 @@ -############## -OTA Web Update -############## - -OTAWebUpdate is done with a web browser that can be useful in the following typical scenarios: - -- Once the application developed and loading directly from Arduino IDE is inconvenient or not possible -- after deployment if user is unable to expose Firmware for OTA from external update server -- provide updates after deployment to small quantity of modules when setting an update server is not practicable - -For more information about the update process, please refer to the `OTA API reference `_ -section of the ESP-IDF documentation. - -Requirements ------------- - -- The ESP and the computer must be connected to the same network - -Implementation --------------- - -The sample implementation has been done using: - -- Example sketch ```OTAWebUpdater.ino```. -- ESP32 Board. - -You can also use another module if it meets Flash chip size of the sketch - -Before you begin, please make sure that you have the following software installed: - -- Arduino IDE -- Host software depending on O/S you use - - `Avahi `_ for Linux - - `Bonjour `_ for Windows - - Mac OSX and iOS - support is already built in / no any extra s/w is required - -Prepare the sketch and configuration for initial upload with a serial port -- Start Arduino IDE and load sketch OTAWebUpdater.ino available under File > Examples > OTAWebUpdater.ino -- Update ssid and pass in the sketch so the module can join your Wi-Fi network -- Open File > Preferences, look for “Show verbose output during:” and check out “compilation” option - -.. figure:: _static/ota_esp32_verbose.png - :align: center - :figclass: align-center - -- Upload sketch (Ctrl+U) -- Now open web browser and enter the url, i.e. http://esp32.local. Once entered, browser should display a form - -.. figure:: _static/ota_esp32_login.png - :align: center - :figclass: align-center - -* username = admin - -* password = admin - -.. note:: - *If entering “http://ESP32.local” does not work, try replacing “ESP32” with module’s IP address. This workaround is useful in case the host software installed does not work*. - -Now click on the Login button and browser will display an upload form - -.. figure:: _static/ota_esp32_upload.png - :align: center - :figclass: align-center - -For Uploading the New Firmware, you need to provide the Binary File of your Code. - -Exporting Binary file of the Firmware (Code) -- Open up the Arduino IDE -- Open up the Code, for Exporting up Binary file -- Now go to Sketch > export compiled Binary - -.. figure:: _static/ota_export_to_binary.png - :align: center - :figclass: align-center - -- Binary file is exported to the same Directory where your code is present - -Once you are comfortable with this procedure, go ahead and modify OTAWebUpdater.ino sketch to print some additional messages and compile it. Then, export the new binary file and upload it using web browser to see entered changes on a Serial Monitor. From 36aaf87d8ae61500796d73e94455a9bf6cbc654e Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Thu, 7 Dec 2023 10:28:15 +0800 Subject: [PATCH 10/70] Fixed the path for the ResetReason example --- docs/en/api/reset_reason.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/api/reset_reason.rst b/docs/en/api/reset_reason.rst index 49bf9397797..006c2c1d530 100644 --- a/docs/en/api/reset_reason.rst +++ b/docs/en/api/reset_reason.rst @@ -15,5 +15,5 @@ To get started with Reset Reason, you can try: Reset Reason ************ -.. literalinclude:: ../../../libraries/ESP32/examples/ResetReason/ResetReason.ino +.. literalinclude:: ../../../libraries/ESP32/examples/ResetReason/ResetReason/ResetReason.ino :language: arduino \ No newline at end of file From 9c8ebf55d98a79265b351216cc315de9291e83e7 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Thu, 7 Dec 2023 10:37:49 +0800 Subject: [PATCH 11/70] Added new releases to the Arduino versions file --- docs/_static/arduino_versions.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/_static/arduino_versions.js b/docs/_static/arduino_versions.js index 5d6e2d1451d..628368db213 100644 --- a/docs/_static/arduino_versions.js +++ b/docs/_static/arduino_versions.js @@ -4,18 +4,16 @@ var DOCUMENTATION_VERSIONS = { }, VERSIONS: [ { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, + // 2.0.14 + { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, + // 2.0.14 + { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, + // 2.0.14 + { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, + // 2.0.14 + { name: "release-2.0.14", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, // 2.0.13 { name: "release-2.0.13", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, - // 2.0.12 - { name: "release-2.0.12", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, - // 2.0.11 - { name: "release-2.0.11", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, - // 2.0.10 - { name: "release-2.0.10", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, - // 2.0.9 - { name: "release-2.0.9", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, - // 2.0.8 - { name: "release-2.0.8", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, ], IDF_TARGETS: [ { text: "ESP32", value: "esp32"}, From 4b94eadfb69266ed4166faa52e8cd5b6e7ea7d8b Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Thu, 7 Dec 2023 12:32:03 +0800 Subject: [PATCH 12/70] Fixed the JSON wrong link --- docs/en/external_libraries_test.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/external_libraries_test.rst b/docs/en/external_libraries_test.rst index 0d7da671cd2..0a5a34a9e1a 100644 --- a/docs/en/external_libraries_test.rst +++ b/docs/en/external_libraries_test.rst @@ -129,4 +129,4 @@ In the table the results are in order ``BEFORE -> AFTER``. :class: no-scaled-link .. _LIBRARIES_TEST.md: https://github.com/espressif/arduino-esp32/blob/gh-pages/LIBRARIES_TEST.md -.. _lib.json: https://github.com/espressif/arduino-esp32/.github/workflow/lib.json \ No newline at end of file +.. _lib.json: https://github.com/espressif/arduino-esp32/blob/master/.github/workflows/lib.json From a19317b6b14bb49329f39a495854aa565507ce01 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Thu, 7 Dec 2023 18:26:16 +0800 Subject: [PATCH 13/70] Removed the version and target selection for now --- .github/workflows/docs.yml | 2 +- .github/workflows/docs_deploy.yml | 19 +++++++---- docs/_static/arduino_versions.js | 10 ------ docs/conf_common.py | 54 +++++++++++++++---------------- docs/en/index.rst | 2 +- 5 files changed, 42 insertions(+), 45 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e0dc48653ef..2ca6b0a3cad 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -36,7 +36,7 @@ jobs: # Update the path to include them and run. cd ./docs PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary - PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 esp32s2 esp32s3 esp32c6 esp32h2 + PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en - name: Archive Docs uses: actions/upload-artifact@v2 with: diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml index 282ab264a16..16d7408a4e4 100644 --- a/.github/workflows/docs_deploy.yml +++ b/.github/workflows/docs_deploy.yml @@ -1,16 +1,15 @@ name: Documentation Deploy CI +// Deploy to production on each merge to master on: - workflow_dispatch: - release: - types: - - created + push: + branches: + - master paths: - 'docs/**' - '.github/workflows/docs_deploy.yml' jobs: - deploy-preview-docs: name: Deploy Documentation runs-on: ubuntu-22.04 @@ -26,8 +25,16 @@ jobs: python-version: '3.10' - name: Deploy Preview env: + # Deploy to production server + # DOCS_BUILD_DIR: "./docs/_build/" + # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }} + # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }} + # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} + # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} + # Deploy to preview server DOCS_BUILD_DIR: "./docs/_build/" - DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }} + DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_PREV_DEPLOY_KEY }} DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_PREV_SERVER }} DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PREV_SERVER_USER }} DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PREV_PATH }} diff --git a/docs/_static/arduino_versions.js b/docs/_static/arduino_versions.js index 628368db213..825bc0cbc27 100644 --- a/docs/_static/arduino_versions.js +++ b/docs/_static/arduino_versions.js @@ -4,16 +4,6 @@ var DOCUMENTATION_VERSIONS = { }, VERSIONS: [ { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, - // 2.0.14 - { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, - // 2.0.14 - { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, - // 2.0.14 - { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6" ] }, - // 2.0.14 - { name: "release-2.0.14", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, - // 2.0.13 - { name: "release-2.0.13", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] }, ], IDF_TARGETS: [ { text: "ESP32", value: "esp32"}, diff --git a/docs/conf_common.py b/docs/conf_common.py index ded9c5673cb..eceaebb8a25 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -4,14 +4,14 @@ languages = ["en"] -idf_targets = [ - "esp32", - "esp32s2", - "esp32s3", - "esp32c3", - "esp32c6", - "esp32h2", -] +# idf_targets = [ +# "esp32", +# "esp32s2", +# "esp32s3", +# "esp32c3", +# "esp32c6", +# "esp32h2", +# ] # link roles config github_repo = "espressif/arduino-esp32" @@ -29,26 +29,26 @@ 'esp_docs.esp_extensions.dummy_build_system', ] -ESP32_DOCS = [ - "index.rst", -] - -ESP32S2_DOCS = ESP32_DOCS -ESP32C3_DOCS = ESP32S2_DOCS -ESP32S3_DOCS = ESP32S2_DOCS -ESP32C6_DOCS = ESP32S2_DOCS -ESP32H2_DOCS = ESP32S2_DOCS - -conditional_include_dict = { - "esp32": ESP32_DOCS, - "esp32s2": ESP32S2_DOCS, - "esp32c3": ESP32C3_DOCS, - "esp32s3": ESP32S3_DOCS, - "esp32c6": ESP32C6_DOCS, - "esp32h2": ESP32H2_DOCS, -} +# ESP32_DOCS = [ +# "index.rst", +# ] + +# ESP32S2_DOCS = ESP32_DOCS +# ESP32C3_DOCS = ESP32S2_DOCS +# ESP32S3_DOCS = ESP32S2_DOCS +# ESP32C6_DOCS = ESP32S2_DOCS +# ESP32H2_DOCS = ESP32S2_DOCS + +# conditional_include_dict = { +# "esp32": ESP32_DOCS, +# "esp32s2": ESP32S2_DOCS, +# "esp32c3": ESP32C3_DOCS, +# "esp32s3": ESP32S3_DOCS, +# "esp32c6": ESP32C6_DOCS, +# "esp32h2": ESP32H2_DOCS, +# } # Extra options required by sphinx_idf_theme project_slug = "arduino-esp32" -versions_url = "./_static/arduino_versions.js" +# versions_url = "./_static/arduino_versions.js" diff --git a/docs/en/index.rst b/docs/en/index.rst index f7f93f9cc92..11a4f8539c5 100644 --- a/docs/en/index.rst +++ b/docs/en/index.rst @@ -2,7 +2,7 @@ Welcome to Arduino Core's documentation ####################################### -Here you will find all the relevant information about the project based on the {IDF_TARGET_NAME}. +Here you will find all the relevant information about the project based on the Arduino Core ESP32. .. note:: This is a work in progress documentation and we will appreciate your help! We are looking for contributors! From 91da8c6fa10a9e5ac11e466fb32df29eebac9117 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Fri, 8 Dec 2023 11:31:53 +0800 Subject: [PATCH 14/70] Documentation build and deploy CI changed --- .github/workflows/docs.yml | 46 +++++++++++++++++++++++++-- .github/workflows/docs_deploy.yml | 53 ------------------------------- 2 files changed, 44 insertions(+), 55 deletions(-) delete mode 100644 .github/workflows/docs_deploy.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2ca6b0a3cad..1152b97278e 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,4 +1,4 @@ -name: Documentation Build CI +name: Documentation Build and Deploy CI on: push: @@ -41,4 +41,46 @@ jobs: uses: actions/upload-artifact@v2 with: name: docs - path: docs \ No newline at end of file + path: docs + + deploy-preview-docs: + name: Deploy Documentation + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Deploy Preview + env: + # Deploy to production server + # DOCS_BUILD_DIR: "./docs/_build/" + # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }} + # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }} + # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} + # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} + # Deploy to preview server + DOCS_DIR: "./docs/_build/" + DOCS_KEY: ${{ secrets.DOCS_KEY }} + DOCS_PATH: ${{ secrets.DOCS_PATH }} + DOCS_SERVER: ${{ secrets.DOCS_SERVER }} + DOCS_URL: ${{ secrets.DOCS_URL }} + DOCS_USER: ${{ secrets.DOCS_USER }} + run: | + sudo apt update + sudo apt install python3-pip python3-setuptools + source ./docs/utils.sh + add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER + export GIT_VER=$(git describe --always) + echo "PIP install requirements..." + pip3 install --user -r ./docs/requirements.txt + echo "Building the Docs..." + cd ./docs && build-docs -l en + echo "Deploy the Docs..." + deploy-docs diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml deleted file mode 100644 index 16d7408a4e4..00000000000 --- a/.github/workflows/docs_deploy.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Documentation Deploy CI - -// Deploy to production on each merge to master -on: - push: - branches: - - master - paths: - - 'docs/**' - - '.github/workflows/docs_deploy.yml' - -jobs: - deploy-preview-docs: - name: Deploy Documentation - runs-on: ubuntu-22.04 - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.10' - - name: Deploy Preview - env: - # Deploy to production server - # DOCS_BUILD_DIR: "./docs/_build/" - # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }} - # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} - # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }} - # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} - # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} - # Deploy to preview server - DOCS_BUILD_DIR: "./docs/_build/" - DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_PREV_DEPLOY_KEY }} - DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_PREV_SERVER }} - DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PREV_SERVER_USER }} - DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PREV_PATH }} - DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_PREV_URL }} - run: | - sudo apt update - sudo apt install python3-pip python3-setuptools - source ./docs/utils.sh - add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER - export GIT_VER=$(git describe --always) - echo "PIP install requirements..." - pip3 install --user -r ./docs/requirements.txt - echo "Building the Docs..." - cd ./docs && build-docs -l en - echo "Deploy the Docs..." - deploy-docs From f99d7848e8ab8c835a29ddcc29f770763308464c Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Fri, 8 Dec 2023 11:37:00 +0800 Subject: [PATCH 15/70] Fixed env vars from server side --- .github/workflows/docs.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1152b97278e..6cef7b8fe71 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -66,12 +66,13 @@ jobs: # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} # Deploy to preview server - DOCS_DIR: "./docs/_build/" - DOCS_KEY: ${{ secrets.DOCS_KEY }} - DOCS_PATH: ${{ secrets.DOCS_PATH }} - DOCS_SERVER: ${{ secrets.DOCS_SERVER }} + + DOCS_BUILD_DIR: "./docs/_build/" + DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }} + DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} + DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} DOCS_URL: ${{ secrets.DOCS_URL }} - DOCS_USER: ${{ secrets.DOCS_USER }} + DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }} run: | sudo apt update sudo apt install python3-pip python3-setuptools From 9a72b42788f88f5002dc4561b0245f79ab995d1a Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Fri, 8 Dec 2023 11:39:00 +0800 Subject: [PATCH 16/70] Fixed env vars from server side URL base --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6cef7b8fe71..e4d354c20de 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -71,7 +71,7 @@ jobs: DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }} DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} - DOCS_URL: ${{ secrets.DOCS_URL }} + DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }} run: | sudo apt update From b75b6a3e93556782d0f9e27dfc5d876e407e9d09 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Fri, 8 Dec 2023 11:47:44 +0800 Subject: [PATCH 17/70] Deploy on preview server --- .github/workflows/docs.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e4d354c20de..8e6c2f31da5 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -68,16 +68,16 @@ jobs: # Deploy to preview server DOCS_BUILD_DIR: "./docs/_build/" - DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }} - DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} - DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} - DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} - DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }} + DOCS_PREVIEW_PRIVATEKEY: ${{ secrets.DOCS_KEY }} + DOCS_PREVIEW_PATH: ${{ secrets.DOCS_PATH }} + DOCS_PREVIEW_SERVER: ${{ secrets.DOCS_SERVER }} + DOCS_PREVIEW_URL_BASE: ${{ secrets.DOCS_URL }} + DOCS_PREVIEW_USER: ${{ secrets.DOCS_USER }} run: | sudo apt update sudo apt install python3-pip python3-setuptools source ./docs/utils.sh - add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER + add_doc_server_ssh_keys $DOCS_PREVIEW_PRIVATEKEY $DOCS_PREVIEW_SERVER $DOCS_PREVIEW_USER export GIT_VER=$(git describe --always) echo "PIP install requirements..." pip3 install --user -r ./docs/requirements.txt From 637c4e945f12e1c9a49e9f5824aeea9e4e2c1d20 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Fri, 8 Dec 2023 11:56:09 +0800 Subject: [PATCH 18/70] Deploy on preview server add type var --- .github/workflows/docs.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8e6c2f31da5..ec4fff94995 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -66,18 +66,18 @@ jobs: # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} # Deploy to preview server - - DOCS_BUILD_DIR: "./docs/_build/" - DOCS_PREVIEW_PRIVATEKEY: ${{ secrets.DOCS_KEY }} - DOCS_PREVIEW_PATH: ${{ secrets.DOCS_PATH }} - DOCS_PREVIEW_SERVER: ${{ secrets.DOCS_SERVER }} - DOCS_PREVIEW_URL_BASE: ${{ secrets.DOCS_URL }} - DOCS_PREVIEW_USER: ${{ secrets.DOCS_USER }} + TYPE: "preview" + DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/" + DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }} + DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} + DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} + DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }} run: | sudo apt update sudo apt install python3-pip python3-setuptools source ./docs/utils.sh - add_doc_server_ssh_keys $DOCS_PREVIEW_PRIVATEKEY $DOCS_PREVIEW_SERVER $DOCS_PREVIEW_USER + add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER export GIT_VER=$(git describe --always) echo "PIP install requirements..." pip3 install --user -r ./docs/requirements.txt From 28916413d56a8f3b67ff57bd5e5439688d5d26b3 Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Fri, 8 Dec 2023 12:28:51 +0800 Subject: [PATCH 19/70] Deploy on preview server - Change the URL --- .github/workflows/docs.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ec4fff94995..2318efc06fa 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -70,8 +70,10 @@ jobs: DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/" DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }} DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} - DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + DOCS_DEPLOY_SERVER: "preview-docs.espressif.com" DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} + # DOCS_DEPLOY_URL_BASE: "https://docs.espressif.com/projects/arduino-esp32" DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }} run: | sudo apt update From b997f4cabab7f78add85c79be17d954018bcd0bb Mon Sep 17 00:00:00 2001 From: pedrominatel Date: Mon, 11 Dec 2023 11:48:03 +0800 Subject: [PATCH 20/70] Deploy on preview server CI changes --- .github/workflows/docs.yml | 4 +- .github/workflows/docs_deploy.yml | 87 +++++++++++++++++++++++++++++++ .github/workflows/docs_legacy.yml | 38 ++++++++++++++ 3 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/docs_deploy.yml create mode 100644 .github/workflows/docs_legacy.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2318efc06fa..ec4fff94995 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -70,10 +70,8 @@ jobs: DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/" DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }} DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} - # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} - DOCS_DEPLOY_SERVER: "preview-docs.espressif.com" + DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} - # DOCS_DEPLOY_URL_BASE: "https://docs.espressif.com/projects/arduino-esp32" DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }} run: | sudo apt update diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml new file mode 100644 index 00000000000..ec4fff94995 --- /dev/null +++ b/.github/workflows/docs_deploy.yml @@ -0,0 +1,87 @@ +name: Documentation Build and Deploy CI + +on: + push: + branches: + - master + - release/* + paths: + - 'docs/**' + - '.github/workflows/docs.yml' + pull_request: + paths: + - 'docs/**' + - '.github/workflows/docs.yml' + +jobs: + + build-docs: + name: Build ESP-Docs + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Build + run: | + sudo apt update + sudo apt install python3-pip python3-setuptools + # GitHub CI installs pip3 and setuptools outside the path. + # Update the path to include them and run. + cd ./docs + PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary + PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en + - name: Archive Docs + uses: actions/upload-artifact@v2 + with: + name: docs + path: docs + + deploy-preview-docs: + name: Deploy Documentation + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + - name: Deploy Preview + env: + # Deploy to production server + # DOCS_BUILD_DIR: "./docs/_build/" + # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }} + # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }} + # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} + # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} + # Deploy to preview server + TYPE: "preview" + DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/" + DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }} + DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} + DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }} + DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }} + run: | + sudo apt update + sudo apt install python3-pip python3-setuptools + source ./docs/utils.sh + add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER + export GIT_VER=$(git describe --always) + echo "PIP install requirements..." + pip3 install --user -r ./docs/requirements.txt + echo "Building the Docs..." + cd ./docs && build-docs -l en + echo "Deploy the Docs..." + deploy-docs diff --git a/.github/workflows/docs_legacy.yml b/.github/workflows/docs_legacy.yml new file mode 100644 index 00000000000..d725f8a1d1b --- /dev/null +++ b/.github/workflows/docs_legacy.yml @@ -0,0 +1,38 @@ +name: Build the Docs Legacy ReadTheDocs CI + +on: + push: + branches: + - master + - release/* + paths: + - 'docs/**' + - '.github/workflows/docs_legacy.yml' + pull_request: + paths: + - 'docs/**' + - '.github/workflows/docs_legacy.yml' + +jobs: + + build-docs: + name: Build ReadTheDocs + runs-on: ubuntu-22.04 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Build + run: | + sudo apt update + sudo apt install python3-pip python3-setuptools + # GitHub CI installs pip3 and setuptools outside the path. + # Update the path to include them and run. + PATH=/home/runner/.local/bin:$PATH pip3 install --user -r ./docs/requirements.txt + cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" make html From 7f5db80feb77d8da29450cea5c8853be54174e81 Mon Sep 17 00:00:00 2001 From: Linar Yusupov Date: Mon, 11 Dec 2023 13:49:20 +0300 Subject: [PATCH 21/70] 3.0.0-alpha3 escaping fix (#8968) --- platform.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform.txt b/platform.txt index 00dcde59e69..0ff2eae7395 100644 --- a/platform.txt +++ b/platform.txt @@ -147,7 +147,7 @@ recipe.hooks.prebuild.6.pattern.windows=cmd /c if not exist "{build.path}\build_ # Set -DARDUINO_CORE_BUILD only on core file compilation file_opts.path={build.path}/file_opts recipe.hooks.prebuild.set_core_build_flag.pattern=/usr/bin/env bash -c ": > '{file_opts.path}'" -recipe.hooks.core.prebuild.set_core_build_flag.pattern=/usr/bin/env bash -c "echo '-DARDUINO_CORE_BUILD' > '{file_opts.path}'" +recipe.hooks.core.prebuild.set_core_build_flag.pattern=/usr/bin/env bash -c "echo -DARDUINO_CORE_BUILD > '{file_opts.path}'" recipe.hooks.core.postbuild.set_core_build_flag.pattern=/usr/bin/env bash -c ": > '{file_opts.path}'" recipe.hooks.prebuild.set_core_build_flag.pattern.windows=cmd /c type nul > "{file_opts.path}" From 7cea268dee3b512631f2227cf920f6b5e244aa11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Barto=C5=A1ka?= <76958047+VojtechBartoska@users.noreply.github.com> Date: Mon, 11 Dec 2023 18:51:05 +0800 Subject: [PATCH 22/70] Adding Shared GitHub DangerJS (#8983) --- .github/workflows/dangerjs.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/dangerjs.yml diff --git a/.github/workflows/dangerjs.yml b/.github/workflows/dangerjs.yml new file mode 100644 index 00000000000..9f7360bc34f --- /dev/null +++ b/.github/workflows/dangerjs.yml @@ -0,0 +1,22 @@ +name: DangerJS Pull Request linter +on: + pull_request_target: + types: [opened, edited, reopened, synchronize] + +permissions: + pull-requests: write + contents: write + +jobs: + pull-request-style-linter: + runs-on: ubuntu-latest + steps: + - name: Check out PR head + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: DangerJS pull request linter + uses: espressif/shared-github-dangerjs@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 42ed9c03731007fba6ddce0d8b05f71e68d616d3 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 11 Dec 2023 08:02:35 -0300 Subject: [PATCH 23/70] Update reset_reason.rst (#8982) --- docs/source/api/reset_reason.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/api/reset_reason.rst b/docs/source/api/reset_reason.rst index 49bf9397797..813216909b0 100644 --- a/docs/source/api/reset_reason.rst +++ b/docs/source/api/reset_reason.rst @@ -15,5 +15,5 @@ To get started with Reset Reason, you can try: Reset Reason ************ -.. literalinclude:: ../../../libraries/ESP32/examples/ResetReason/ResetReason.ino - :language: arduino \ No newline at end of file +.. literalinclude:: ../../../libraries/ESP32/examples/ResetReason/ResetReason/ResetReason.ino + :language: arduino From 8d1a84557c99135742374a77df1e6b4b2f6d0e6d Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 11 Dec 2023 08:34:40 -0300 Subject: [PATCH 24/70] Wifi async scan example and fix (#8981) * Create WiFiScanAsync.ino * Create .skip.esp32h2 * Create README.md * Update README.md - adds C6 * Update wifi.rst with new example * avoid timeout with Async Mode --------- Co-authored-by: Me No Dev --- docs/source/api/wifi.rst | 2 +- libraries/WiFi/examples/WiFiScan/README.md | 5 +- .../WiFi/examples/WiFiScanAsync/.skip.esp32h2 | 1 + .../WiFi/examples/WiFiScanAsync/README.md | 75 +++++++++++++ .../examples/WiFiScanAsync/WiFiScanAsync.ino | 102 ++++++++++++++++++ libraries/WiFi/src/WiFiScan.cpp | 10 +- 6 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 libraries/WiFi/examples/WiFiScanAsync/.skip.esp32h2 create mode 100644 libraries/WiFi/examples/WiFiScanAsync/README.md create mode 100644 libraries/WiFi/examples/WiFiScanAsync/WiFiScanAsync.ino diff --git a/docs/source/api/wifi.rst b/docs/source/api/wifi.rst index 1fd5651f38b..c906cdaa588 100644 --- a/docs/source/api/wifi.rst +++ b/docs/source/api/wifi.rst @@ -643,7 +643,7 @@ Loads all infos from a scanned wifi in to the ptr parameters. bool getNetworkInfo(uint8_t networkItem, String &ssid, uint8_t &encryptionType, int32_t &RSSI, uint8_t* &BSSID, int32_t &channel); -To see how to use the ``WiFiScan``, take a look at the ``WiFiScan.ino`` example available. +To see how to use the ``WiFiScan``, take a look at the ``WiFiScan.ino`` or ``WiFiScanAsync.ino`` example available. Examples -------- diff --git a/libraries/WiFi/examples/WiFiScan/README.md b/libraries/WiFi/examples/WiFiScan/README.md index 188ae7453a0..7a4a7ec8350 100644 --- a/libraries/WiFi/examples/WiFiScan/README.md +++ b/libraries/WiFi/examples/WiFiScan/README.md @@ -6,8 +6,8 @@ This example demonstrates how to use the WiFi library to scan available WiFi net Currently this example supports the following targets. -| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 | ESP32-C6 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | ## How to Use Example @@ -61,4 +61,5 @@ Before creating a new issue, be sure to try the Troubleshooting and to check if * ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf) * ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf) * ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf) +* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) * Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com) diff --git a/libraries/WiFi/examples/WiFiScanAsync/.skip.esp32h2 b/libraries/WiFi/examples/WiFiScanAsync/.skip.esp32h2 new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/libraries/WiFi/examples/WiFiScanAsync/.skip.esp32h2 @@ -0,0 +1 @@ + diff --git a/libraries/WiFi/examples/WiFiScanAsync/README.md b/libraries/WiFi/examples/WiFiScanAsync/README.md new file mode 100644 index 00000000000..195605bce60 --- /dev/null +++ b/libraries/WiFi/examples/WiFiScanAsync/README.md @@ -0,0 +1,75 @@ +# WiFiScanAsync Example + +This example demonstrates how to use the WiFi library to scan available WiFi networks in asynchronous mode and print the results. + +## Supported Targets + +Currently this example supports the following targets. + +| Supported Targets | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 | ESP32-C6 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | + +## How to Use Example + +* How to install the Arduino IDE: [Install Arduino IDE](https://github.com/espressif/arduino-esp32/tree/master/docs/arduino-ide). + +#### Using Arduino IDE + +* Before Compile/Verify, select the correct board: `Tools -> Board`. +* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port. + +#### Using Platform IO + +* Select the COM port: `Devices` or setting the `upload_port` option on the `platformio.ini` file. + +## Example/Log Output + +``` +Setup done +Scan start +Loop running... +Loop running... +Loop running... +Loop running... +Loop running... +Loop running... +Loop running... +Loop running... +Loop running... + +Scan done +17 networks found +Nr | SSID | RSSI | CH | Encryption + 1 | IoTNetwork | -62 | 1 | WPA2 + 2 | WiFiSSID | -62 | 1 | WPA2-EAP + 3 | B3A7992 | -63 | 6 | WPA+WPA2 + 4 | WiFi | -63 | 6 | WPA3 + 5 | IoTNetwork2 | -64 | 11 | WPA2+WPA3 +... +``` + +## Troubleshooting + +***Important: Be sure you're using a good quality USB cable and you have enought power source for your project.*** + +* **Programming Fail:** If the programming/flash procedure fails, try to reduce the serial connection speed. +* **COM port not detected:** Check the USB cable connection and the USB to Serial driver installation. + +If the error persist, you can ask help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute). + +## Contribute + +To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst) + +If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome! + +Before creating a new issue, be sure to try the Troubleshooting and to check if the same issue was already created by someone else. + +## Resources + +* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32) +* ESP32 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf) +* ESP32-S2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf) +* ESP32-C3 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf) +* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) +* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com) diff --git a/libraries/WiFi/examples/WiFiScanAsync/WiFiScanAsync.ino b/libraries/WiFi/examples/WiFiScanAsync/WiFiScanAsync.ino new file mode 100644 index 00000000000..9f4408cdfe1 --- /dev/null +++ b/libraries/WiFi/examples/WiFiScanAsync/WiFiScanAsync.ino @@ -0,0 +1,102 @@ +/* + This sketch demonstrates how to scan WiFi networks in Async Mode. + The API is based on the Arduino WiFi Shield library, but has significant changes as newer WiFi functions are supported. + E.g. the return value of `encryptionType()` different because more modern encryption is supported. +*/ +#include "WiFi.h" + +void startWiFiScan() { + Serial.println("Scan start"); + // WiFi.scanNetworks will return immediately in Async Mode. + WiFi.scanNetworks(true); // 'true' turns Async Mode ON +} + +void printScannedNetworks(uint16_t networksFound) { + if (networksFound == 0) { + Serial.println("no networks found"); + } else { + Serial.println("\nScan done"); + Serial.print(networksFound); + Serial.println(" networks found"); + Serial.println("Nr | SSID | RSSI | CH | Encryption"); + for (int i = 0; i < networksFound; ++i) { + // Print SSID and RSSI for each network found + Serial.printf("%2d", i + 1); + Serial.print(" | "); + Serial.printf("%-32.32s", WiFi.SSID(i).c_str()); + Serial.print(" | "); + Serial.printf("%4ld", WiFi.RSSI(i)); + Serial.print(" | "); + Serial.printf("%2ld", WiFi.channel(i)); + Serial.print(" | "); + switch (WiFi.encryptionType(i)) + { + case WIFI_AUTH_OPEN: + Serial.print("open"); + break; + case WIFI_AUTH_WEP: + Serial.print("WEP"); + break; + case WIFI_AUTH_WPA_PSK: + Serial.print("WPA"); + break; + case WIFI_AUTH_WPA2_PSK: + Serial.print("WPA2"); + break; + case WIFI_AUTH_WPA_WPA2_PSK: + Serial.print("WPA+WPA2"); + break; + case WIFI_AUTH_WPA2_ENTERPRISE: + Serial.print("WPA2-EAP"); + break; + case WIFI_AUTH_WPA3_PSK: + Serial.print("WPA3"); + break; + case WIFI_AUTH_WPA2_WPA3_PSK: + Serial.print("WPA2+WPA3"); + break; + case WIFI_AUTH_WAPI_PSK: + Serial.print("WAPI"); + break; + default: + Serial.print("unknown"); + } + Serial.println(); + delay(10); + } + Serial.println(""); + // Delete the scan result to free memory for code below. + WiFi.scanDelete(); + } +} + +void setup() { + Serial.begin(115200); + + // Set WiFi to station mode and disconnect from an AP if it was previously connected. + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + + Serial.println("Setup done"); + startWiFiScan(); +} + +void loop() { + // check WiFi Scan Async process + int16_t WiFiScanStatus = WiFi.scanComplete(); + if (WiFiScanStatus < 0) { // it is busy scanning or got an error + if (WiFiScanStatus == WIFI_SCAN_FAILED) { + Serial.println("WiFi Scan has failed. Starting again."); + startWiFiScan(); + } + // other option is status WIFI_SCAN_RUNNING - just wait. + } else { // Found Zero or more Wireless Networks + printScannedNetworks(WiFiScanStatus); + startWiFiScan(); // start over... + } + + // Loop can do something else... + delay(250); + Serial.println("Loop running..."); +} diff --git a/libraries/WiFi/src/WiFiScan.cpp b/libraries/WiFi/src/WiFiScan.cpp index 37e86a198df..e3604c9723f 100644 --- a/libraries/WiFi/src/WiFiScan.cpp +++ b/libraries/WiFi/src/WiFiScan.cpp @@ -141,11 +141,6 @@ void * WiFiScanClass::_getScanInfoByIndex(int i) */ int16_t WiFiScanClass::scanComplete() { - if (WiFiScanClass::_scanStarted && (millis()-WiFiScanClass::_scanStarted) > WiFiScanClass::_scanTimeout) { //Check is scan was started and if the delay expired, return WIFI_SCAN_FAILED in this case - WiFiGenericClass::clearStatusBits(WIFI_SCANNING_BIT); - return WIFI_SCAN_FAILED; - } - if(WiFiGenericClass::getStatusBits() & WIFI_SCAN_DONE_BIT) { return WiFiScanClass::_scanCount; } @@ -153,6 +148,11 @@ int16_t WiFiScanClass::scanComplete() if(WiFiGenericClass::getStatusBits() & WIFI_SCANNING_BIT) { return WIFI_SCAN_RUNNING; } + // last one to avoid time affecting Async mode + if (WiFiScanClass::_scanStarted && (millis()-WiFiScanClass::_scanStarted) > WiFiScanClass::_scanTimeout) { //Check is scan was started and if the delay expired, return WIFI_SCAN_FAILED in this case + WiFiGenericClass::clearStatusBits(WIFI_SCANNING_BIT); + return WIFI_SCAN_FAILED; + } return WIFI_SCAN_FAILED; } From 02b384a54a5ace17ac82d97ddd8f6549e5aabb9e Mon Sep 17 00:00:00 2001 From: David McCurley <44048235+mrengineer7777@users.noreply.github.com> Date: Wed, 13 Dec 2023 02:22:54 -0600 Subject: [PATCH 25/70] Replace new with malloc for non-class calls (#7868) * Resolve potential crashes * Update Esp.cpp Resolved possible crash in EspClass::getSketchMD5(). --- cores/esp32/Esp.cpp | 19 ++++++++++--------- libraries/BLE/src/BLEAdvertising.cpp | 13 ++++++++----- libraries/WebServer/src/WebServer.cpp | 14 +++++++------- libraries/WiFi/src/WiFiUdp.cpp | 8 ++++---- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/cores/esp32/Esp.cpp b/cores/esp32/Esp.cpp index 0d812b2fcb0..aa73e698bbe 100644 --- a/cores/esp32/Esp.cpp +++ b/cores/esp32/Esp.cpp @@ -225,30 +225,31 @@ String EspClass::getSketchMD5() const esp_partition_t *running = esp_ota_get_running_partition(); if (!running) { log_e("Partition could not be found"); - return String(); } + const size_t bufSize = SPI_FLASH_SEC_SIZE; - std::unique_ptr buf(new uint8_t[bufSize]); - uint32_t offset = 0; - if(!buf.get()) { + uint8_t *pb = (uint8_t *)malloc(bufSize); + if(!pb) { log_e("Not enough memory to allocate buffer"); - return String(); } + uint32_t offset = 0; + MD5Builder md5; md5.begin(); - while( lengthLeft > 0) { + while(lengthLeft > 0) { size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize; - if (!ESP.flashRead(running->address + offset, reinterpret_cast(buf.get()), (readBytes + 3) & ~3)) { + if (!ESP.flashRead(running->address + offset, (uint32_t *)pb, (readBytes + 3) & ~3)) { + free(pb); log_e("Could not read buffer from flash"); - return String(); } - md5.add(buf.get(), readBytes); + md5.add(pb, readBytes); lengthLeft -= readBytes; offset += readBytes; } + free(pb); md5.calculate(); result = md5.toString(); return result; diff --git a/libraries/BLE/src/BLEAdvertising.cpp b/libraries/BLE/src/BLEAdvertising.cpp index b09f96b3ba0..3e934d28f1e 100644 --- a/libraries/BLE/src/BLEAdvertising.cpp +++ b/libraries/BLE/src/BLEAdvertising.cpp @@ -231,7 +231,12 @@ void BLEAdvertising::start() { int numServices = m_serviceUUIDs.size(); if (numServices > 0) { m_advData.service_uuid_len = 16 * numServices; - m_advData.p_service_uuid = new uint8_t[m_advData.service_uuid_len]; + m_advData.p_service_uuid = (uint8_t *)malloc(m_advData.service_uuid_len); + if(!m_advData.p_service_uuid) { + log_e(">> start failed: out of memory"); + return; + } + uint8_t* p = m_advData.p_service_uuid; for (int i = 0; i < numServices; i++) { log_d("- advertising service: %s", m_serviceUUIDs[i].toString().c_str()); @@ -276,10 +281,8 @@ void BLEAdvertising::start() { // If we had services to advertise then we previously allocated some storage for them. // Here we release that storage. - if (m_advData.service_uuid_len > 0) { - delete[] m_advData.p_service_uuid; - m_advData.p_service_uuid = nullptr; - } + free(m_advData.p_service_uuid); //TODO change this variable to local scope? + m_advData.p_service_uuid = nullptr; // Start advertising. errRc = ::esp_ble_gap_start_advertising(&m_advParams); diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index bc0d172fd85..8af04ac3f91 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -135,26 +135,26 @@ bool WebServer::authenticate(const char * username, const char * password){ authReq = authReq.substring(6); authReq.trim(); char toencodeLen = strlen(username)+strlen(password)+1; - char *toencode = new char[toencodeLen + 1]; + char *toencode = (char *)malloc[toencodeLen + 1]; if(toencode == NULL){ authReq = ""; return false; } - char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; + char *encoded = (char *)malloc(base64_encode_expected_len(toencodeLen)+1); if(encoded == NULL){ authReq = ""; - delete[] toencode; + free(toencode); return false; } sprintf(toencode, "%s:%s", username, password); if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) { authReq = ""; - delete[] toencode; - delete[] encoded; + free(toencode); + free(encoded); return true; } - delete[] toencode; - delete[] encoded; + free(toencode); + free(encoded);; } else if(authReq.startsWith(F("Digest"))) { authReq = authReq.substring(7); log_v("%s", authReq.c_str()); diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp index 0d75739afb9..6b053bbbd14 100644 --- a/libraries/WiFi/src/WiFiUdp.cpp +++ b/libraries/WiFi/src/WiFiUdp.cpp @@ -44,11 +44,12 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){ server_port = port; - tx_buffer = new char[1460]; + tx_buffer = (char *)malloc(1460); if(!tx_buffer){ log_e("could not create tx buffer: %d", errno); return 0; } + tx_buffer_len = 0; if ((udp_server=socket(AF_INET, SOCK_DGRAM, 0)) == -1){ log_e("could not create socket: %d", errno); @@ -100,7 +101,7 @@ uint8_t WiFiUDP::beginMulticast(IPAddress a, uint16_t p){ void WiFiUDP::stop(){ if(tx_buffer){ - delete[] tx_buffer; + free(tx_buffer); tx_buffer = NULL; } tx_buffer_len = 0; @@ -136,13 +137,12 @@ int WiFiUDP::beginPacket(){ // allocate tx_buffer if is necessary if(!tx_buffer){ - tx_buffer = new char[1460]; + tx_buffer = (char *)malloc(1460); if(!tx_buffer){ log_e("could not create tx buffer: %d", errno); return 0; } } - tx_buffer_len = 0; // check whereas socket is already open From c5297bfa3a0cb3cca7a561f6636879c9f4f4acb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Wed, 13 Dec 2023 12:32:54 +0100 Subject: [PATCH 26/70] fix: Windows runner build skip and exit on error (#8991) --- .github/scripts/on-push.sh | 18 ++++++------------ .github/scripts/sketch_utils.sh | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/scripts/on-push.sh b/.github/scripts/on-push.sh index 4227e920d21..c3e32d06e83 100755 --- a/.github/scripts/on-push.sh +++ b/.github/scripts/on-push.sh @@ -73,22 +73,16 @@ if [ "$BUILD_PIO" -eq 0 ]; then SKETCHES_ESP32="\ $ARDUINO_ESP32_PATH/libraries/WiFiClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino\ - $ARDUINO_ESP32_PATH/libraries/BLE/examples/BLE_server/BLE_server.ino\ + $ARDUINO_ESP32_PATH/libraries/BLE/examples/Server/Server.ino\ $ARDUINO_ESP32_PATH/libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino\ $ARDUINO_ESP32_PATH/libraries/Insights/examples/MinimalDiagnostics/MinimalDiagnostics.ino\ " - SKETCHES_ESP32XX="\ - $ARDUINO_ESP32_PATH/libraries/WiFiClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino\ - $ARDUINO_ESP32_PATH/libraries/WiFi/examples/WiFiClient/WiFiClient.ino\ - $ARDUINO_ESP32_PATH/libraries/Insights/examples/MinimalDiagnostics/MinimalDiagnostics.ino\ - " - build "esp32s3" $FQBN_ESP32S3 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32 - build "esp32s2" $FQBN_ESP32S2 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32XX - build "esp32c3" $FQBN_ESP32C3 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32XX - build "esp32c6" $FQBN_ESP32C6 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32XX - build "esp32h2" $FQBN_ESP32H2 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32XX + build "esp32s2" $FQBN_ESP32S2 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32 + build "esp32c3" $FQBN_ESP32C3 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32 + build "esp32c6" $FQBN_ESP32C6 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32 + build "esp32h2" $FQBN_ESP32H2 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32 build "esp32" $FQBN_ESP32 $CHUNK_INDEX $CHUNKS_CNT $SKETCHES_ESP32 else source ${SCRIPTS_DIR}/install-platformio-esp32.sh @@ -98,7 +92,7 @@ else build_pio_sketch "$BOARD" "$OPTIONS" "$PLATFORMIO_ESP32_PATH/libraries/WiFi/examples/WiFiClient/WiFiClient.ino" && \ build_pio_sketch "$BOARD" "$OPTIONS" "$PLATFORMIO_ESP32_PATH/libraries/WiFiClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino" && \ build_pio_sketch "$BOARD" "$OPTIONS" "$PLATFORMIO_ESP32_PATH/libraries/BluetoothSerial/examples/SerialToSerialBT/SerialToSerialBT.ino" && \ - build_pio_sketch "$BOARD" "$OPTIONS" "$PLATFORMIO_ESP32_PATH/libraries/BLE/examples/BLE_server/BLE_server.ino" && \ + build_pio_sketch "$BOARD" "$OPTIONS" "$PLATFORMIO_ESP32_PATH/libraries/BLE/examples/Server/Server.ino" && \ build_pio_sketch "$BOARD" "$OPTIONS" "$PLATFORMIO_ESP32_PATH/libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino" # Basic sanity testing for other series diff --git a/.github/scripts/sketch_utils.sh b/.github/scripts/sketch_utils.sh index 42b3669bb8b..79fb568b797 100755 --- a/.github/scripts/sketch_utils.sh +++ b/.github/scripts/sketch_utils.sh @@ -126,6 +126,11 @@ function build_sketch(){ # build_sketch [ex # of configuration built in case of a multiconfiguration test. sketchname=$(basename $sketchdir) + + if [[ -n $target ]] && [[ -f "$sketchdir/.skip.$target" ]]; then + echo "Skipping $sketchname for target $target" + exit 0 + fi ARDUINO_CACHE_DIR="$HOME/.arduino/cache.tmp" if [ -n "$ARDUINO_BUILD_DIR" ]; then @@ -159,6 +164,12 @@ function build_sketch(){ # build_sketch [ex --build-cache-path "$ARDUINO_CACHE_DIR" \ --build-path "$build_dir" \ $xtra_opts "${sketchdir}" + + exit_status=$? + if [ $exit_status -ne 0 ]; then + echo ""ERROR: Compilation failed with error code $exit_status"" + exit $exit_status + fi elif [ -f "$ide_path/arduino-builder" ]; then echo "Building $sketchname with arduino-builder and FQBN=$currfqbn" echo "Build path = $build_dir" @@ -173,6 +184,11 @@ function build_sketch(){ # build_sketch [ex -build-path "$build_dir" \ $xtra_opts "${sketchdir}/${sketchname}.ino" + exit_status=$? + if [ $exit_status -ne 0 ]; then + echo ""ERROR: Compilation failed with error code $exit_status"" + exit $exit_status + fi # $ide_path/arduino-builder -compile -logger=human -core-api-version=10810 \ # -fqbn=\"$currfqbn\" \ # -warnings="all" \ From 29cde94bb919a7c842a9babbfef147117820cb34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:02:54 +0100 Subject: [PATCH 27/70] ESPLwIPClient::setTimeout conflict fix with Stream::setTimeout (#6676) * Removed virtual + moved socketOptions ot read/write * Removed no needed code + edit * removed Client::getTimeout * removed setTimeout from WifiClient - read/write timeouts in constructor now * Changed seconds to miliseconds in other classes relaed + examples * Applied same changes for WifiClientSecure * Added 0 init values to constructor * Seconds are not rounded now * removed +500 for previous rounding + unnecessary comments removed. * fix rebased code in WifiClientSecure * Fix rebased code * Fix rebase code --- libraries/HTTPClient/src/HTTPClient.cpp | 4 +- .../httpUpdateSecure/httpUpdateSecure.ino | 2 +- libraries/WebServer/src/WebServer.cpp | 2 +- libraries/WiFi/src/WiFiClient.cpp | 45 +++++++++++-------- libraries/WiFi/src/WiFiClient.h | 4 +- .../WiFiClientSecure/src/WiFiClientSecure.cpp | 27 ++++++++++- 6 files changed, 58 insertions(+), 26 deletions(-) diff --git a/libraries/HTTPClient/src/HTTPClient.cpp b/libraries/HTTPClient/src/HTTPClient.cpp index 62a9c44901b..1733d0cede1 100644 --- a/libraries/HTTPClient/src/HTTPClient.cpp +++ b/libraries/HTTPClient/src/HTTPClient.cpp @@ -495,7 +495,7 @@ void HTTPClient::setTimeout(uint16_t timeout) { _tcpTimeout = timeout; if(connected()) { - _client->setTimeout((timeout + 500) / 1000); + _client->setTimeout(timeout); } } @@ -1165,7 +1165,7 @@ bool HTTPClient::connect(void) } // set Timeout for WiFiClient and for Stream::readBytesUntil() and Stream::readStringUntil() - _client->setTimeout((_tcpTimeout + 500) / 1000); + _client->setTimeout(_tcpTimeout); log_d(" connected to %s:%u", _host.c_str(), _port); diff --git a/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino b/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino index 2028d192c06..e528a9fdb1c 100644 --- a/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino +++ b/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino @@ -96,7 +96,7 @@ void loop() { client.setCACert(rootCACertificate); // Reading data over SSL may be slow, use an adequate timeout - client.setTimeout(12000 / 1000); // timeout argument is defined in seconds for setTimeout + client.setTimeout(12000); // timeout argument is defined in miliseconds for setTimeout // The line below is optional. It can be used to blink the LED on the board during flashing // The LED will be on during download of one buffer of data from the network. The LED will diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index 8af04ac3f91..0d99f3680a1 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -303,7 +303,7 @@ void WebServer::handleClient() { if (_parseRequest(_currentClient)) { // because HTTP_MAX_SEND_WAIT is expressed in milliseconds, // it must be divided by 1000 - _currentClient.setTimeout(HTTP_MAX_SEND_WAIT / 1000); + _currentClient.setTimeout(HTTP_MAX_SEND_WAIT); /* / 1000 removed, WifiClient setTimeout changed to ms */ _contentLength = CONTENT_LENGTH_NOT_SET; _handleRequest(); diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp index 91247d17386..52c73b67300 100644 --- a/libraries/WiFi/src/WiFiClient.cpp +++ b/libraries/WiFi/src/WiFiClient.cpp @@ -211,6 +211,8 @@ void WiFiClient::stop() clientSocketHandle = NULL; _rxBuffer = NULL; _connected = false; + _lastReadTimeout = 0; + _lastWriteTimeout = 0; } int WiFiClient::connect(IPAddress ip, uint16_t port) @@ -331,25 +333,6 @@ int WiFiClient::getSocketOption(int level, int option, const void* value, size_t return res; } - -int WiFiClient::setTimeout(uint32_t seconds) -{ - Client::setTimeout(seconds * 1000); // This should be here? - _timeout = seconds * 1000; - if(fd() >= 0) { - struct timeval tv; - tv.tv_sec = seconds; - tv.tv_usec = 0; - if(setSocketOption(SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) < 0) { - return -1; - } - return setSocketOption(SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval)); - } - else { - return 0; - } -} - int WiFiClient::setOption(int option, int *value) { return setSocketOption(IPPROTO_TCP, option, (const void*)value, sizeof(int)); @@ -418,6 +401,18 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) tv.tv_usec = WIFI_CLIENT_SELECT_TIMEOUT_US; retry--; + if(_lastWriteTimeout != _timeout){ + if(fd() >= 0){ + struct timeval timeout_tv; + timeout_tv.tv_sec = _timeout / 1000; + timeout_tv.tv_usec = (_timeout % 1000) * 1000; + if(setSocketOption(SO_SNDTIMEO, (char *)&timeout_tv, sizeof(struct timeval)) >= 0) + { + _lastWriteTimeout = _timeout; + } + } + } + if(select(socketFileDescriptor + 1, NULL, &set, NULL, &tv) < 0) { return 0; } @@ -477,6 +472,18 @@ size_t WiFiClient::write(Stream &stream) int WiFiClient::read(uint8_t *buf, size_t size) { + if(_lastReadTimeout != _timeout){ + if(fd() >= 0){ + struct timeval timeout_tv; + timeout_tv.tv_sec = _timeout / 1000; + timeout_tv.tv_usec = (_timeout % 1000) * 1000; + if(setSocketOption(SO_RCVTIMEO, (char *)&timeout_tv, sizeof(struct timeval)) >= 0) + { + _lastReadTimeout = _timeout; + } + } + } + int res = -1; if (_rxBuffer) { res = _rxBuffer->read(buf, size); diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h index cdb0e0539e9..7ff753ef3e2 100644 --- a/libraries/WiFi/src/WiFiClient.h +++ b/libraries/WiFi/src/WiFiClient.h @@ -33,7 +33,6 @@ class ESPLwIPClient : public Client public: virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) = 0; virtual int connect(const char *host, uint16_t port, int32_t timeout) = 0; - virtual int setTimeout(uint32_t seconds) = 0; }; class WiFiClient : public ESPLwIPClient @@ -43,6 +42,8 @@ class WiFiClient : public ESPLwIPClient std::shared_ptr _rxBuffer; bool _connected; int _timeout; + int _lastWriteTimeout; + int _lastReadTimeout; public: WiFiClient *next; @@ -91,7 +92,6 @@ class WiFiClient : public ESPLwIPClient int getSocketOption(int level, int option, const void* value, size_t size); int setOption(int option, int *value); int getOption(int option, int *value); - int setTimeout(uint32_t seconds); int setNoDelay(bool nodelay); bool getNoDelay(); diff --git a/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp b/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp index 4d4702ba84d..b3c64129d75 100644 --- a/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp +++ b/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp @@ -54,6 +54,8 @@ WiFiClientSecure::WiFiClientSecure(int sock) { _connected = false; _timeout = 30000; // Same default as ssl_client + _lastReadTimeout = 0; + _lastWriteTimeout = 0; sslclient = new sslclient_context; ssl_init(sslclient); @@ -94,6 +96,8 @@ void WiFiClientSecure::stop() sslclient->socket = -1; _connected = false; _peek = -1; + _lastReadTimeout = 0; + _lastWriteTimeout = 0; } stop_ssl_socket(sslclient, _CA_cert, _cert, _private_key); } @@ -199,6 +203,16 @@ size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) if (!_connected) { return 0; } + if(_lastWriteTimeout != _timeout){ + struct timeval timeout_tv; + timeout_tv.tv_sec = _timeout / 1000; + timeout_tv.tv_usec = (_timeout % 1000) * 1000; + if(setSocketOption(SO_SNDTIMEO, (char *)&timeout_tv, sizeof(struct timeval)) >= 0) + { + _lastWriteTimeout = _timeout; + } + } + int res = send_ssl_data(sslclient, buf, size); if (res < 0) { stop(); @@ -209,6 +223,18 @@ size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) int WiFiClientSecure::read(uint8_t *buf, size_t size) { + if(_lastReadTimeout != _timeout){ + if(fd() >= 0){ + struct timeval timeout_tv; + timeout_tv.tv_sec = _timeout / 1000; + timeout_tv.tv_usec = (_timeout % 1000) * 1000; + if(setSocketOption(SO_RCVTIMEO, (char *)&timeout_tv, sizeof(struct timeval)) >= 0) + { + _lastReadTimeout = _timeout; + } + } + } + int peeked = 0; int avail = available(); if ((!buf && size) || avail <= 0) { @@ -396,4 +422,3 @@ int WiFiClientSecure::fd() const { return sslclient->socket; } - From 07fa3441c6ae70e7ec3345895496456fe06c869c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:06:31 +0100 Subject: [PATCH 28/70] fix: Remove setTimeout (#8998) --- .../WiFiClientSecure/src/WiFiClientSecure.cpp | 16 ---------------- .../WiFiClientSecure/src/WiFiClientSecure.h | 1 - 2 files changed, 17 deletions(-) diff --git a/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp b/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp index b3c64129d75..2f9da58f9ad 100644 --- a/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp +++ b/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp @@ -401,22 +401,6 @@ void WiFiClientSecure::setAlpnProtocols(const char **alpn_protos) { _alpn_protos = alpn_protos; } -int WiFiClientSecure::setTimeout(uint32_t seconds) -{ - _timeout = seconds * 1000; - if (sslclient->socket >= 0) { - struct timeval tv; - tv.tv_sec = seconds; - tv.tv_usec = 0; - if(setSocketOption(SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) < 0) { - return -1; - } - return setSocketOption(SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval)); - } - else { - return 0; - } -} int WiFiClientSecure::fd() const { diff --git a/libraries/WiFiClientSecure/src/WiFiClientSecure.h b/libraries/WiFiClientSecure/src/WiFiClientSecure.h index 6c967fbd0e6..8c130f450cc 100644 --- a/libraries/WiFiClientSecure/src/WiFiClientSecure.h +++ b/libraries/WiFiClientSecure/src/WiFiClientSecure.h @@ -80,7 +80,6 @@ class WiFiClientSecure : public WiFiClient void setAlpnProtocols(const char **alpn_protos); const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); }; bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); }; - int setTimeout(uint32_t seconds); int fd() const; operator bool() From 6c919f40f14eb1873ac672698cb2cc34bc82c828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Andr=C3=A1ssy?= <10706773+JAndrassy@users.noreply.github.com> Date: Thu, 14 Dec 2023 16:16:07 +0100 Subject: [PATCH 29/70] WiFiClients.setConnectionTimeout added (#8863) --- libraries/WiFi/src/WiFiClient.cpp | 5 +++++ libraries/WiFi/src/WiFiClient.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp index 52c73b67300..864c33322f3 100644 --- a/libraries/WiFi/src/WiFiClient.cpp +++ b/libraries/WiFi/src/WiFiClient.cpp @@ -348,6 +348,11 @@ int WiFiClient::getOption(int option, int *value) return res; } +void WiFiClient::setConnectionTimeout(uint32_t milliseconds) +{ + _timeout = milliseconds; +} + int WiFiClient::setNoDelay(bool nodelay) { int flag = nodelay; diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h index 7ff753ef3e2..06e77c7cd1d 100644 --- a/libraries/WiFi/src/WiFiClient.h +++ b/libraries/WiFi/src/WiFiClient.h @@ -33,6 +33,7 @@ class ESPLwIPClient : public Client public: virtual int connect(IPAddress ip, uint16_t port, int32_t timeout) = 0; virtual int connect(const char *host, uint16_t port, int32_t timeout) = 0; + virtual void setConnectionTimeout(uint32_t milliseconds) = 0; }; class WiFiClient : public ESPLwIPClient @@ -92,6 +93,7 @@ class WiFiClient : public ESPLwIPClient int getSocketOption(int level, int option, const void* value, size_t size); int setOption(int option, int *value); int getOption(int option, int *value); + void setConnectionTimeout(uint32_t milliseconds); int setNoDelay(bool nodelay); bool getNoDelay(); From 0aefc94470836a45889301e2c9d13e32a266156c Mon Sep 17 00:00:00 2001 From: Benjamin Karic Date: Fri, 15 Dec 2023 00:04:41 +0100 Subject: [PATCH 30/70] change(senseBox MCU-S2 ESP32-S2): Remove analog bit resolution change in init (#9000) --- variants/sensebox_mcu_esp32s2/variant.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/variants/sensebox_mcu_esp32s2/variant.cpp b/variants/sensebox_mcu_esp32s2/variant.cpp index 7da523dc5eb..311f69b8d35 100644 --- a/variants/sensebox_mcu_esp32s2/variant.cpp +++ b/variants/sensebox_mcu_esp32s2/variant.cpp @@ -49,7 +49,6 @@ void initVariant(void) //enable PD-Sensor by default pinMode(PD_ENABLE, OUTPUT); - analogReadResolution(12); digitalWrite(PD_ENABLE, HIGH); } From 71b1d767afc0450a979b9d2c6420d5dcd634fc54 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Mon, 18 Dec 2023 06:35:12 -0300 Subject: [PATCH 31/70] Fixes Malloc (#9012) Fixes `malloc()` call preventing it from using function pointer reference instead of actually calling the function itself. --- libraries/WebServer/src/WebServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index 0d99f3680a1..ea9b4d5692e 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -135,7 +135,7 @@ bool WebServer::authenticate(const char * username, const char * password){ authReq = authReq.substring(6); authReq.trim(); char toencodeLen = strlen(username)+strlen(password)+1; - char *toencode = (char *)malloc[toencodeLen + 1]; + char *toencode = (char *)malloc(toencodeLen + 1); if(toencode == NULL){ authReq = ""; return false; From 44f83b0455e47645faf929e5af7cf18b68151cde Mon Sep 17 00:00:00 2001 From: Sly Gryphon Date: Mon, 18 Dec 2023 22:19:12 +1000 Subject: [PATCH 32/70] Add v6 support to IPAddress to match ArduinoCore-API (#7174) * feat(ipaddress): add support for ipv6 type, following arduinocore api * feat(ipaddress): align with latest arduinocore api --------- Co-authored-by: Me No Dev --- cores/esp32/IPAddress.cpp | 289 +++++++++++++++++++++++++++++++++++--- cores/esp32/IPAddress.h | 54 ++++--- 2 files changed, 302 insertions(+), 41 deletions(-) diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp index 0575363f254..002dccb3fcd 100644 --- a/cores/esp32/IPAddress.cpp +++ b/cores/esp32/IPAddress.cpp @@ -20,78 +20,244 @@ #include #include #include +#include -IPAddress::IPAddress() +IPAddress::IPAddress() : IPAddress(IPv4) {} + +IPAddress::IPAddress(IPType ip_type) { - _address.dword = 0; + _type = ip_type; + memset(_address.bytes, 0, sizeof(_address.bytes)); } IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { - _address.bytes[0] = first_octet; - _address.bytes[1] = second_octet; - _address.bytes[2] = third_octet; - _address.bytes[3] = fourth_octet; + _type = IPv4; + memset(_address.bytes, 0, sizeof(_address.bytes)); + _address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet; + _address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet; + _address.bytes[IPADDRESS_V4_BYTES_INDEX + 2] = third_octet; + _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = fourth_octet; +} + +IPAddress::IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12, uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16) { + _type = IPv6; + _address.bytes[0] = o1; + _address.bytes[1] = o2; + _address.bytes[2] = o3; + _address.bytes[3] = o4; + _address.bytes[4] = o5; + _address.bytes[5] = o6; + _address.bytes[6] = o7; + _address.bytes[7] = o8; + _address.bytes[8] = o9; + _address.bytes[9] = o10; + _address.bytes[10] = o11; + _address.bytes[11] = o12; + _address.bytes[12] = o13; + _address.bytes[13] = o14; + _address.bytes[14] = o15; + _address.bytes[15] = o16; } IPAddress::IPAddress(uint32_t address) { - _address.dword = address; + // IPv4 only + _type = IPv4; + memset(_address.bytes, 0, sizeof(_address.bytes)); + _address.dword[IPADDRESS_V4_DWORD_INDEX] = address; + + // NOTE on conversion/comparison and uint32_t: + // These conversions are host platform dependent. + // There is a defined integer representation of IPv4 addresses, + // based on network byte order (will be the value on big endian systems), + // e.g. http://2398766798 is the same as http://142.250.70.206, + // However on little endian systems the octets 0x83, 0xFA, 0x46, 0xCE, + // in that order, will form the integer (uint32_t) 3460758158 . +} + +IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {} + +IPAddress::IPAddress(IPType ip_type, const uint8_t *address) +{ + _type = ip_type; + if (ip_type == IPv4) { + memset(_address.bytes, 0, sizeof(_address.bytes)); + memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t)); + } else { + memcpy(_address.bytes, address, sizeof(_address.bytes)); + } } -IPAddress::IPAddress(const uint8_t *address) +IPAddress::IPAddress(const char *address) { - memcpy(_address.bytes, address, sizeof(_address.bytes)); + fromString(address); } IPAddress& IPAddress::operator=(const uint8_t *address) { - memcpy(_address.bytes, address, sizeof(_address.bytes)); + // IPv4 only conversion from byte pointer + _type = IPv4; + memset(_address.bytes, 0, sizeof(_address.bytes)); + memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t)); + return *this; +} + +IPAddress& IPAddress::operator=(const char *address) +{ + fromString(address); return *this; } IPAddress& IPAddress::operator=(uint32_t address) { - _address.dword = address; + // IPv4 conversion + // See note on conversion/comparison and uint32_t + _type = IPv4; + memset(_address.bytes, 0, sizeof(_address.bytes)); + _address.dword[IPADDRESS_V4_DWORD_INDEX] = address; return *this; } +bool IPAddress::operator==(const IPAddress& addr) const +{ + return (addr._type == _type) + && (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0); +} + bool IPAddress::operator==(const uint8_t* addr) const { - return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; + // IPv4 only comparison to byte pointer + // Can't support IPv6 as we know our type, but not the length of the pointer + return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0; +} + +uint8_t IPAddress::operator[](int index) const { + if (_type == IPv4) { + return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index]; + } + return _address.bytes[index]; +} + +uint8_t& IPAddress::operator[](int index) { + if (_type == IPv4) { + return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index]; + } + return _address.bytes[index]; } size_t IPAddress::printTo(Print& p) const { size_t n = 0; - for(int i = 0; i < 3; i++) { - n += p.print(_address.bytes[i], DEC); + + if (_type == IPv6) { + // IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case + int8_t longest_start = -1; + int8_t longest_length = 1; + int8_t current_start = -1; + int8_t current_length = 0; + for (int8_t f = 0; f < 8; f++) { + if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) { + if (current_start == -1) { + current_start = f; + current_length = 1; + } else { + current_length++; + } + if (current_length > longest_length) { + longest_start = current_start; + longest_length = current_length; + } + } else { + current_start = -1; + } + } + for (int f = 0; f < 8; f++) { + if (f < longest_start || f >= longest_start + longest_length) { + uint8_t c1 = _address.bytes[f * 2] >> 4; + uint8_t c2 = _address.bytes[f * 2] & 0xf; + uint8_t c3 = _address.bytes[f * 2 + 1] >> 4; + uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf; + if (c1 > 0) { + n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10)); + } + if (c1 > 0 || c2 > 0) { + n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10)); + } + if (c1 > 0 || c2 > 0 || c3 > 0) { + n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10)); + } + n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10)); + if (f < 7) { + n += p.print(':'); + } + } else if (f == longest_start) { + if (longest_start == 0) { + n += p.print(':'); + } + n += p.print(':'); + } + } + return n; + } + + // IPv4 + for (int i =0; i < 3; i++) + { + n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC); n += p.print('.'); } - n += p.print(_address.bytes[3], DEC); + n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC); return n; } -String IPAddress::toString() const +String IPAddress::toString4() const { char szRet[16]; - sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]); + snprintf(szRet, sizeof(szRet), "%u.%u.%u.%u", _address.bytes[IPADDRESS_V4_BYTES_INDEX], _address.bytes[IPADDRESS_V4_BYTES_INDEX + 1], _address.bytes[IPADDRESS_V4_BYTES_INDEX + 2], _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3]); return String(szRet); } +String IPAddress::toString6() const +{ + StreamString s; + s.reserve(40); + printTo(s); + return s; +} + +String IPAddress::toString() const +{ + if (_type == IPv4) { + return toString4(); + } else { + return toString6(); + } +} + bool IPAddress::fromString(const char *address) +{ + if (!fromString4(address)) + { + return fromString6(address); + } + return true; +} + +bool IPAddress::fromString4(const char *address) { // TODO: add support for "a", "a.b", "a.b.c" formats - uint16_t acc = 0; // Accumulator + int16_t acc = -1; // Accumulator uint8_t dots = 0; + memset(_address.bytes, 0, sizeof(_address.bytes)); while (*address) { char c = *address++; if (c >= '0' && c <= '9') { - acc = acc * 10 + (c - '0'); + acc = (acc < 0) ? (c - '0') : acc * 10 + (c - '0'); if (acc > 255) { // Value out of [0..255] range return false; @@ -100,11 +266,15 @@ bool IPAddress::fromString(const char *address) else if (c == '.') { if (dots == 3) { - // Too much dots (there must be 3 dots) + // Too many dots (there must be 3 dots) return false; } - _address.bytes[dots++] = acc; - acc = 0; + if (acc < 0) { + /* No value between dots, e.g. '1..' */ + return false; + } + _address.bytes[IPADDRESS_V4_BYTES_INDEX + dots++] = acc; + acc = -1; } else { @@ -117,7 +287,80 @@ bool IPAddress::fromString(const char *address) // Too few dots (there must be 3 dots) return false; } - _address.bytes[3] = acc; + if (acc < 0) { + /* No value between dots, e.g. '1..' */ + return false; + } + _address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = acc; + _type = IPv4; + return true; +} + +bool IPAddress::fromString6(const char *address) { + uint32_t acc = 0; // Accumulator + int colons = 0, double_colons = -1; + + while (*address) + { + char c = tolower(*address++); + if (isalnum(c) && c <= 'f') { + if (c >= 'a') + c -= 'a' - '0' - 10; + acc = acc * 16 + (c - '0'); + if (acc > 0xffff) + // Value out of range + return false; + } + else if (c == ':') { + if (*address == ':') { + if (double_colons >= 0) { + // :: allowed once + return false; + } + if (*address != '\0' && *(address + 1) == ':') { + // ::: not allowed + return false; + } + // remember location + double_colons = colons + !!acc; + address++; + } else if (*address == '\0') { + // can't end with a single colon + return false; + } + if (colons == 7) + // too many separators + return false; + _address.bytes[colons * 2] = acc >> 8; + _address.bytes[colons * 2 + 1] = acc & 0xff; + colons++; + acc = 0; + } + else + // Invalid char + return false; + } + + if (double_colons == -1 && colons != 7) { + // Too few separators + return false; + } + if (double_colons > -1 && colons > 6) { + // Too many segments (double colon must be at least one zero field) + return false; + } + _address.bytes[colons * 2] = acc >> 8; + _address.bytes[colons * 2 + 1] = acc & 0xff; + colons++; + + if (double_colons != -1) { + for (int i = colons * 2 - double_colons * 2 - 1; i >= 0; i--) + _address.bytes[16 - colons * 2 + double_colons * 2 + i] = _address.bytes[double_colons * 2 + i]; + for (int i = double_colons * 2; i < 16 - colons * 2 + double_colons * 2; i++) + _address.bytes[i] = 0; + } + + _type = IPv6; return true; } diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h index 3bedd4f8749..329ca92afe9 100644 --- a/cores/esp32/IPAddress.h +++ b/cores/esp32/IPAddress.h @@ -26,13 +26,23 @@ // A class to make it easier to handle and pass around IP addresses +#define IPADDRESS_V4_BYTES_INDEX 12 +#define IPADDRESS_V4_DWORD_INDEX 3 + +enum IPType +{ + IPv4, + IPv6 +}; + class IPAddress: public Printable { private: union { - uint8_t bytes[4]; // IPv4 address - uint32_t dword; + uint8_t bytes[16]; + uint32_t dword[4]; } _address; + IPType _type; // Access the raw byte array containing the address. Because this returns a pointer // to the internal structure rather than a copy of the address this function should only @@ -40,57 +50,65 @@ class IPAddress: public Printable // stored. uint8_t* raw_address() { - return _address.bytes; + return _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _address.bytes; } public: // Constructors IPAddress(); + IPAddress(IPType ip_type); IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); + IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12, uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16); IPAddress(uint32_t address); IPAddress(const uint8_t *address); + IPAddress(IPType ip_type, const uint8_t *address); + // If IPv4 fails tries IPv6 see fromString function + IPAddress(const char *address); virtual ~IPAddress() {} bool fromString(const char *address); bool fromString(const String &address) { return fromString(address.c_str()); } - // Overloaded cast operator to allow IPAddress objects to be used where a pointer - // to a four-byte uint8_t array is expected + // Overloaded cast operator to allow IPAddress objects to be used where a + // uint32_t is expected operator uint32_t() const { - return _address.dword; - } - bool operator==(const IPAddress& addr) const - { - return _address.dword == addr._address.dword; + return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0; } + + bool operator==(const IPAddress& addr) const; bool operator==(const uint8_t* addr) const; // Overloaded index operator to allow getting and setting individual octets of the address - uint8_t operator[](int index) const - { - return _address.bytes[index]; - } - uint8_t& operator[](int index) - { - return _address.bytes[index]; - } + uint8_t operator[](int index) const; + uint8_t& operator[](int index); // Overloaded copy operators to allow initialisation of IPAddress objects from other types IPAddress& operator=(const uint8_t *address); IPAddress& operator=(uint32_t address); + // If IPv4 fails tries IPv6 see fromString function + IPAddress& operator=(const char *address); virtual size_t printTo(Print& p) const; String toString() const; + IPType type() const { return _type; } + friend class EthernetClass; friend class UDP; friend class Client; friend class Server; friend class DhcpClass; friend class DNSClient; + +protected: + bool fromString4(const char *address); + bool fromString6(const char *address); + String toString4() const; + String toString6() const; }; // changed to extern because const declaration creates copies in BSS of INADDR_NONE for each CPP unit that includes it extern IPAddress INADDR_NONE; +extern IPAddress IN6ADDR_ANY; #endif From d91271019ce0fb545ed9c2b6f51d27c86520f273 Mon Sep 17 00:00:00 2001 From: vortigont Date: Mon, 18 Dec 2023 21:47:04 +0900 Subject: [PATCH 33/70] DNSServer refactoring, switch to AsyncUDP (#7482) * DNSServer: switch to AsyncUDP instead of WiFiUDP AsyncUDP offers event driven approch for handling udp dns req's WiFiUDP hooks to loop() for packet processing and making useless malloc's each run * DNSServer code refactoring get rid of intermediate mem buffers and extra data copies, most of the data could be referenced or copied from the source packet - removed _buffer member - replaced DNSQuestion.QName from uint8_t[] to char* added sanity checks for mem bounds optimize label/packet length calculations other code cleanup * DNSServer drop dynamically allocated member structs DNSHeader and DNSQuestion structs could be created on stack no need to keep it as obj members * DNSServer: labels min length checks, simplified labels parser * DNSServer use default settings for catch-all setup - default constructor and start() method simply runs a catch-all DNS setup - avoid string comparison for domain reqs in catch-all mode - use IPaddress class for _resolvedIP (looking for IPv6 support in future) * CaptivePortal example refactored - use webserver instead of simple tcp setver - use redirects to allows CaptivePortal detection pop-ups in modern systems * DNSServer status getters added add isUp() method - returns 'true' if server is up and UDP socket is listening for UDP req's add isCaptive() method - returns 'true' if server runs in catch-all (captive portal mode) some doxygen comments added start() method now keeps existing IP address if any --------- Co-authored-by: Lucas Saavedra Vaz Co-authored-by: Me No Dev --- .../examples/CaptivePortal/CaptivePortal.ino | 81 +++--- libraries/DNSServer/src/DNSServer.cpp | 240 ++++++++---------- libraries/DNSServer/src/DNSServer.h | 130 ++++++++-- 3 files changed, 252 insertions(+), 199 deletions(-) diff --git a/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino b/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino index 9221af1eaa2..7e292e1adfb 100644 --- a/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino +++ b/libraries/DNSServer/examples/CaptivePortal/CaptivePortal.ino @@ -1,52 +1,59 @@ +/* +This example enables catch-all Captive portal for ESP32 Access-Point +It will allow modern devices/OSes to detect that WiFi connection is +limited and offer a user to access a banner web-page. +There is no need to find and open device's IP address/URL, i.e. http://192.168.4.1/ +This works for Android, Ubuntu, FireFox, Windows, maybe others... +*/ + +#include #include #include +#include + -const byte DNS_PORT = 53; -IPAddress apIP(8,8,4,4); // The default android DNS DNSServer dnsServer; -WiFiServer server(80); +WebServer server(80); + +static const char responsePortal[] = R"===( +ESP32 CaptivePortal +

Hello World!

This is a captive portal example page. All unknown http requests will +be redirected here.

+)==="; + +// index page handler +void handleRoot() { + server.send(200, "text/plain", "Hello from esp32!"); +} -String responseHTML = "" - "CaptivePortal" - "

Hello World!

This is a captive portal example. All requests will " - "be redirected here.

"; +// this will redirect unknown http req's to our captive portal page +// based on this redirect various systems could detect that WiFi AP has a captive portal page +void handleNotFound() { + server.sendHeader("Location", "/portal"); + server.send(302, "text/plain", "redirect to captive portal"); +} -void setup() { +void setup() { + Serial.begin(115200); WiFi.mode(WIFI_AP); WiFi.softAP("ESP32-DNSServer"); - WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); - // if DNSServer is started with "*" for domain name, it will reply with - // provided IP to all DNS request - dnsServer.start(DNS_PORT, "*", apIP); + // by default DNSServer is started serving any "*" domain name. It will reply + // AccessPoint's IP to all DNS request (this is requred for Captive Portal detection) + dnsServer.start(); + + // serve a simple root page + server.on("/", handleRoot); + + // serve portal page + server.on("/portal",[](){server.send(200, "text/html", responsePortal);}); + // all unknown pages are redirected to captive portal + server.onNotFound(handleNotFound); server.begin(); } void loop() { - dnsServer.processNextRequest(); - WiFiClient client = server.available(); // listen for incoming clients - - if (client) { - String currentLine = ""; - while (client.connected()) { - if (client.available()) { - char c = client.read(); - if (c == '\n') { - if (currentLine.length() == 0) { - client.println("HTTP/1.1 200 OK"); - client.println("Content-type:text/html"); - client.println(); - client.print(responseHTML); - break; - } else { - currentLine = ""; - } - } else if (c != '\r') { - currentLine += c; - } - } - } - client.stop(); - } + server.handleClient(); + delay(5); // give CPU some idle time } diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index aaa3d33344b..a8114733460 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -1,6 +1,8 @@ #include "DNSServer.h" #include #include +#include + // #define DEBUG_ESP_DNS #ifdef DEBUG_ESP_PORT @@ -9,45 +11,37 @@ #define DEBUG_OUTPUT Serial #endif -DNSServer::DNSServer() -{ - _ttl = htonl(DNS_DEFAULT_TTL); - _errorReplyCode = DNSReplyCode::NonExistentDomain; - _dnsHeader = (DNSHeader*) malloc( sizeof(DNSHeader) ) ; - _dnsQuestion = (DNSQuestion*) malloc( sizeof(DNSQuestion) ) ; - _buffer = NULL; - _currentPacketSize = 0; - _port = 0; -} +#define DNS_MIN_REQ_LEN 17 // minimal size for DNS request asking ROOT = DNS_HEADER_SIZE + 1 null byte for Name + 4 bytes type/class -DNSServer::~DNSServer() -{ - if (_dnsHeader) { - free(_dnsHeader); - _dnsHeader = NULL; - } - if (_dnsQuestion) { - free(_dnsQuestion); - _dnsQuestion = NULL; - } - if (_buffer) { - free(_buffer); - _buffer = NULL; - } +DNSServer::DNSServer() : _port(DNS_DEFAULT_PORT), _ttl(htonl(DNS_DEFAULT_TTL)), _errorReplyCode(DNSReplyCode::NonExistentDomain){} + +DNSServer::DNSServer(const String &domainName) : _port(DNS_DEFAULT_PORT), _ttl(htonl(DNS_DEFAULT_TTL)), _errorReplyCode(DNSReplyCode::NonExistentDomain), _domainName(domainName){}; + + +bool DNSServer::start(){ + if (_resolvedIP.operator uint32_t() == 0){ // no address is set, try to obtain AP interface's IP + if (WiFi.getMode() & WIFI_AP){ + _resolvedIP = WiFi.softAPIP(); + } else return false; // won't run if WiFi is not in AP mode + } + + _udp.close(); + _udp.onPacket([this](AsyncUDPPacket& pkt){ this->_handleUDP(pkt); }); + return _udp.listen(_port); } -bool DNSServer::start(const uint16_t &port, const String &domainName, - const IPAddress &resolvedIP) -{ +bool DNSServer::start(uint16_t port, const String &domainName, const IPAddress &resolvedIP){ _port = port; - _buffer = NULL; - _domainName = domainName; - _resolvedIP[0] = resolvedIP[0]; - _resolvedIP[1] = resolvedIP[1]; - _resolvedIP[2] = resolvedIP[2]; - _resolvedIP[3] = resolvedIP[3]; - downcaseAndRemoveWwwPrefix(_domainName); - return _udp.begin(_port) == 1; + if (domainName != "*"){ + _domainName = domainName; + downcaseAndRemoveWwwPrefix(_domainName); + } else + _domainName.clear(); + + _resolvedIP = resolvedIP; + _udp.close(); + _udp.onPacket([this](AsyncUDPPacket& pkt){ this->_handleUDP(pkt); }); + return _udp.listen(_port); } void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode) @@ -62,9 +56,7 @@ void DNSServer::setTTL(const uint32_t &ttl) void DNSServer::stop() { - _udp.stop(); - free(_buffer); - _buffer = NULL; + _udp.close(); } void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) @@ -73,151 +65,125 @@ void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) domainName.replace("www.", ""); } -void DNSServer::processNextRequest() +void DNSServer::_handleUDP(AsyncUDPPacket& pkt) { - _currentPacketSize = _udp.parsePacket(); - if (_currentPacketSize) - { - // Allocate buffer for the DNS query - if (_buffer != NULL) - free(_buffer); - _buffer = (unsigned char*)malloc(_currentPacketSize * sizeof(char)); - if (_buffer == NULL) - return; + if (pkt.length() < DNS_MIN_REQ_LEN) return; // truncated packet or not a DNS req + + // get DNS header (beginning of message) + DNSHeader dnsHeader; + DNSQuestion dnsQuestion; + memcpy( &dnsHeader, pkt.data(), DNS_HEADER_SIZE ); + if (dnsHeader.QR != DNS_QR_QUERY) return; // ignore non-query mesages - // Put the packet received in the buffer and get DNS header (beginning of message) - // and the question - _udp.read(_buffer, _currentPacketSize); - memcpy( _dnsHeader, _buffer, DNS_HEADER_SIZE ) ; - if ( requestIncludesOnlyOneQuestion() ) + if ( requestIncludesOnlyOneQuestion(dnsHeader) ) { +/* // The QName has a variable length, maximum 255 bytes and is comprised of multiple labels. // Each label contains a byte to describe its length and the label itself. The list of // labels terminates with a zero-valued byte. In "github.com", we have two labels "github" & "com" - // Iterate through the labels and copy them as they come into a single buffer (for simplicity's sake) - _dnsQuestion->QNameLength = 0 ; - while ( _buffer[ DNS_HEADER_SIZE + _dnsQuestion->QNameLength ] != 0 ) - { - memcpy( (void*) &_dnsQuestion->QName[_dnsQuestion->QNameLength], (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength], _buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength] + 1 ) ; - _dnsQuestion->QNameLength += _buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength] + 1 ; - } - _dnsQuestion->QName[_dnsQuestion->QNameLength] = 0 ; - _dnsQuestion->QNameLength++ ; - - // Copy the QType and QClass - memcpy( &_dnsQuestion->QType, (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength], sizeof(_dnsQuestion->QType) ) ; - memcpy( &_dnsQuestion->QClass, (void*) &_buffer[DNS_HEADER_SIZE + _dnsQuestion->QNameLength + sizeof(_dnsQuestion->QType)], sizeof(_dnsQuestion->QClass) ) ; +*/ + const char * enoflbls = strchr(reinterpret_cast(pkt.data()) + DNS_HEADER_SIZE, 0); // find end_of_label marker + ++enoflbls; // advance after null terminator + dnsQuestion.QName = pkt.data() + DNS_HEADER_SIZE; // we can reference labels from the request + dnsQuestion.QNameLength = enoflbls - (char*)pkt.data() - DNS_HEADER_SIZE; + /* + check if we aint going out of pkt bounds + proper dns req should have label terminator at least 4 bytes before end of packet + */ + if (dnsQuestion.QNameLength > pkt.length() - DNS_HEADER_SIZE - sizeof(dnsQuestion.QType) - sizeof(dnsQuestion.QClass)) return; // malformed packet + + // Copy the QType and QClass + memcpy( &dnsQuestion.QType, enoflbls, sizeof(dnsQuestion.QType) ); + memcpy( &dnsQuestion.QClass, enoflbls + sizeof(dnsQuestion.QType), sizeof(dnsQuestion.QClass) ); } - - if (_dnsHeader->QR == DNS_QR_QUERY && - _dnsHeader->OPCode == DNS_OPCODE_QUERY && - requestIncludesOnlyOneQuestion() && - (_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName) + // will reply with IP only to "*" or if doman matches without www. subdomain + if (dnsHeader.OPCode == DNS_OPCODE_QUERY && + requestIncludesOnlyOneQuestion(dnsHeader) && + (_domainName.isEmpty() || + getDomainNameWithoutWwwPrefix(static_cast(dnsQuestion.QName), dnsQuestion.QNameLength) == _domainName) ) { - replyWithIP(); - } - else if (_dnsHeader->QR == DNS_QR_QUERY) - { - replyWithCustomCode(); + replyWithIP(pkt, dnsHeader, dnsQuestion); + return; } - free(_buffer); - _buffer = NULL; - } + // otherwise reply with custom code + replyWithCustomCode(pkt, dnsHeader); } -bool DNSServer::requestIncludesOnlyOneQuestion() +bool DNSServer::requestIncludesOnlyOneQuestion(DNSHeader& dnsHeader) { - return ntohs(_dnsHeader->QDCount) == 1 && - _dnsHeader->ANCount == 0 && - _dnsHeader->NSCount == 0 && - _dnsHeader->ARCount == 0; + return ntohs(dnsHeader.QDCount) == 1 && + dnsHeader.ANCount == 0 && + dnsHeader.NSCount == 0 && + dnsHeader.ARCount == 0; } -String DNSServer::getDomainNameWithoutWwwPrefix() +String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char* start, size_t len) { - // Error checking : if the buffer containing the DNS request is a null pointer, return an empty domain - String parsedDomainName = ""; - if (_buffer == NULL) - return parsedDomainName; - - // Set the start of the domain just after the header (12 bytes). If equal to null character, return an empty domain - unsigned char *start = _buffer + DNS_OFFSET_DOMAIN_NAME; - if (*start == 0) - { - return parsedDomainName; - } + String parsedDomainName(start, --len); // exclude trailing null byte from labels length, String constructor will add it anyway int pos = 0; - while(true) + while(posQR = DNS_QR_RESPONSE; - _dnsHeader->ANCount = _dnsHeader->QDCount; - _udp.write( (unsigned char*) _dnsHeader, DNS_HEADER_SIZE ) ; + dnsHeader.QR = DNS_QR_RESPONSE; + dnsHeader.ANCount = dnsHeader.QDCount; + rpl.write( (unsigned char*) &dnsHeader, DNS_HEADER_SIZE ) ; // Write the question - _udp.write(_dnsQuestion->QName, _dnsQuestion->QNameLength) ; - _udp.write( (unsigned char*) &_dnsQuestion->QType, 2 ) ; - _udp.write( (unsigned char*) &_dnsQuestion->QClass, 2 ) ; + rpl.write(dnsQuestion.QName, dnsQuestion.QNameLength) ; + rpl.write( (uint8_t*) &dnsQuestion.QType, 2 ) ; + rpl.write( (uint8_t*) &dnsQuestion.QClass, 2 ) ; // Write the answer // Use DNS name compression : instead of repeating the name in this RNAME occurence, // set the two MSB of the byte corresponding normally to the length to 1. The following // 14 bits must be used to specify the offset of the domain name in the message // (<255 here so the first byte has the 6 LSB at 0) - _udp.write((uint8_t) 0xC0); - _udp.write((uint8_t) DNS_OFFSET_DOMAIN_NAME); + rpl.write((uint8_t) 0xC0); + rpl.write((uint8_t) DNS_OFFSET_DOMAIN_NAME); // DNS type A : host address, DNS class IN for INternet, returning an IPv4 address uint16_t answerType = htons(DNS_TYPE_A), answerClass = htons(DNS_CLASS_IN), answerIPv4 = htons(DNS_RDLENGTH_IPV4) ; - _udp.write((unsigned char*) &answerType, 2 ); - _udp.write((unsigned char*) &answerClass, 2 ); - _udp.write((unsigned char*) &_ttl, 4); // DNS Time To Live - _udp.write((unsigned char*) &answerIPv4, 2 ); - _udp.write(_resolvedIP, sizeof(_resolvedIP)); // The IP address to return - _udp.endPacket(); + rpl.write((unsigned char*) &answerType, 2 ); + rpl.write((unsigned char*) &answerClass, 2 ); + rpl.write((unsigned char*) &_ttl, 4); // DNS Time To Live + rpl.write((unsigned char*) &answerIPv4, 2 ); + uint32_t ip = _resolvedIP; + rpl.write(reinterpret_cast(&ip), sizeof(uint32_t)); // The IPv4 address to return + + _udp.sendTo(rpl, req.remoteIP(), req.remotePort()); #ifdef DEBUG_ESP_DNS DEBUG_OUTPUT.printf("DNS responds: %s for %s\n", - IPAddress(_resolvedIP).toString().c_str(), getDomainNameWithoutWwwPrefix().c_str() ); + _resolvedIP.toString().c_str(), getDomainNameWithoutWwwPrefix(static_cast(dnsQuestion.QName), dnsQuestion.QNameLength).c_str() ); #endif } -void DNSServer::replyWithCustomCode() +void DNSServer::replyWithCustomCode(AsyncUDPPacket& req, DNSHeader& dnsHeader) { - _dnsHeader->QR = DNS_QR_RESPONSE; - _dnsHeader->RCode = (unsigned char)_errorReplyCode; - _dnsHeader->QDCount = 0; + dnsHeader.QR = DNS_QR_RESPONSE; + dnsHeader.RCode = static_cast(_errorReplyCode); + dnsHeader.QDCount = 0; - _udp.beginPacket(_udp.remoteIP(), _udp.remotePort()); - _udp.write((unsigned char*)_dnsHeader, sizeof(DNSHeader)); - _udp.endPacket(); + AsyncUDPMessage rpl(sizeof(DNSHeader)); + rpl.write(reinterpret_cast(&dnsHeader), sizeof(DNSHeader)); + _udp.sendTo(rpl, req.remoteIP(), req.remotePort()); } diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index 1250f5ce960..860507ac9ec 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -1,15 +1,15 @@ -#ifndef DNSServer_h -#define DNSServer_h -#include +#pragma once +#include #define DNS_QR_QUERY 0 #define DNS_QR_RESPONSE 1 #define DNS_OPCODE_QUERY 0 #define DNS_DEFAULT_TTL 60 // Default Time To Live : time interval in seconds that the resource record should be cached before being discarded -#define DNS_OFFSET_DOMAIN_NAME 12 // Offset in bytes to reach the domain name in the DNS message #define DNS_HEADER_SIZE 12 +#define DNS_OFFSET_DOMAIN_NAME DNS_HEADER_SIZE // Offset in bytes to reach the domain name labels in the DNS message +#define DNS_DEFAULT_PORT 53 -enum class DNSReplyCode +enum class DNSReplyCode:uint16_t { NoError = 0, FormError = 1, @@ -59,14 +59,14 @@ struct DNSHeader uint16_t Flags; }; uint16_t QDCount; // number of question entries - uint16_t ANCount; // number of answer entries + uint16_t ANCount; // number of ANswer entries uint16_t NSCount; // number of authority entries - uint16_t ARCount; // number of resource entries + uint16_t ARCount; // number of Additional Resource entries }; struct DNSQuestion { - uint8_t QName[256] ; //need 1 Byte for zero termination! + const uint8_t* QName; uint16_t QNameLength ; uint16_t QType ; uint16_t QClass ; @@ -75,36 +75,116 @@ struct DNSQuestion class DNSServer { public: + /** + * @brief Construct a new DNSServer object + * by default server is configured to run in "Captive-portal" mode + * it must be started with start() call to establish a listening socket + * + */ DNSServer(); - ~DNSServer(); - void processNextRequest(); + + /** + * @brief Construct a new DNSServer object + * builds DNS server with default parameters + * @param domainName - domain name to serve + */ + DNSServer(const String &domainName); + ~DNSServer(){}; // default d-tor + + // Copy semantics not implemented (won't run on same UDP port anyway) + DNSServer(const DNSServer&) = delete; + DNSServer& operator=(const DNSServer&) = delete; + + + /** + * @brief stub, left for compatibility with an old version + * does nothing actually + * + */ + void processNextRequest(){}; + + /** + * @brief Set the Error Reply Code for all req's not matching predifined domain + * + * @param replyCode + */ void setErrorReplyCode(const DNSReplyCode &replyCode); + + /** + * @brief set TTL for successfull replies + * + * @param ttl in seconds + */ void setTTL(const uint32_t &ttl); - // Returns true if successful, false if there are no sockets available - bool start(const uint16_t &port, + /** + * @brief (re)Starts a server with current configuration or with default parameters + * if it's the first call. + * Defaults are: + * port: 53 + * domainName: any + * ip: WiFi AP's IP address + * + * @return true on success + * @return false if IP or socket error + */ + bool start(); + + /** + * @brief (re)Starts a server with provided configuration + * + * @return true on success + * @return false if IP or socket error + */ + bool start(uint16_t port, const String &domainName, const IPAddress &resolvedIP); - // stops the DNS server + + /** + * @brief stops the server and close UDP socket + * + */ void stop(); + /** + * @brief returns true if DNS server runs in captive-portal mode + * i.e. all requests are served with AP's ip address + * + * @return true if catch-all mode active + * @return false otherwise + */ + inline bool isCaptive() const { return _domainName.isEmpty(); }; + + /** + * @brief returns 'true' if server is up and UDP socket is listening for UDP req's + * + * @return true if server is up + * @return false otherwise + */ + inline bool isUp() { return _udp.connected(); }; + private: - WiFiUDP _udp; + AsyncUDP _udp; uint16_t _port; - String _domainName; - unsigned char _resolvedIP[4]; - int _currentPacketSize; - unsigned char* _buffer; - DNSHeader* _dnsHeader; uint32_t _ttl; DNSReplyCode _errorReplyCode; - DNSQuestion* _dnsQuestion ; + String _domainName; + IPAddress _resolvedIP; void downcaseAndRemoveWwwPrefix(String &domainName); - String getDomainNameWithoutWwwPrefix(); - bool requestIncludesOnlyOneQuestion(); - void replyWithIP(); - void replyWithCustomCode(); + + /** + * @brief Get the Domain Name Without Www Prefix object + * scan labels in DNS packet and build a string of a domain name + * truncate any www. label if found + * @param start a pointer to the start of labels records in DNS packet + * @param len labels length + * @return String + */ + String getDomainNameWithoutWwwPrefix(const unsigned char* start, size_t len); + inline bool requestIncludesOnlyOneQuestion(DNSHeader& dnsHeader); + void replyWithIP(AsyncUDPPacket& req, DNSHeader& dnsHeader, DNSQuestion& dnsQuestion); + inline void replyWithCustomCode(AsyncUDPPacket& req, DNSHeader& dnsHeader); + void _handleUDP(AsyncUDPPacket& pkt); }; -#endif From c7a109bac4dda7cc76b8ed7e6aaf07f36bd15660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 19 Dec 2023 21:11:48 +0100 Subject: [PATCH 34/70] feat: add functions to enable/disable GPIOs interrupt (#9025) --- cores/esp32/esp32-hal-gpio.c | 8 ++++++++ cores/esp32/esp32-hal-gpio.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/cores/esp32/esp32-hal-gpio.c b/cores/esp32/esp32-hal-gpio.c index 456f12c833e..a60342760a7 100644 --- a/cores/esp32/esp32-hal-gpio.c +++ b/cores/esp32/esp32-hal-gpio.c @@ -256,6 +256,14 @@ extern void __detachInterrupt(uint8_t pin) gpio_set_intr_type((gpio_num_t)pin, GPIO_INTR_DISABLE); } +extern void enableInterrupt(uint8_t pin) { + gpio_intr_enable((gpio_num_t)pin); +} + +extern void disableInterrupt(uint8_t pin) { + gpio_intr_disable((gpio_num_t)pin); +} + extern void pinMode(uint8_t pin, uint8_t mode) __attribute__ ((weak, alias("__pinMode"))); extern void digitalWrite(uint8_t pin, uint8_t val) __attribute__ ((weak, alias("__digitalWrite"))); diff --git a/cores/esp32/esp32-hal-gpio.h b/cores/esp32/esp32-hal-gpio.h index 69b55178d3f..a45302d9e99 100644 --- a/cores/esp32/esp32-hal-gpio.h +++ b/cores/esp32/esp32-hal-gpio.h @@ -78,6 +78,8 @@ int digitalRead(uint8_t pin); void attachInterrupt(uint8_t pin, void (*)(void), int mode); void attachInterruptArg(uint8_t pin, void (*)(void*), void * arg, int mode); void detachInterrupt(uint8_t pin); +void enableInterrupt(uint8_t pin); +void disableInterrupt(uint8_t pin); int8_t digitalPinToTouchChannel(uint8_t pin); int8_t digitalPinToAnalogChannel(uint8_t pin); From e9ee9c118bc75a5d094492aab4e96b40ef809c68 Mon Sep 17 00:00:00 2001 From: Rodrigo Garcia Date: Tue, 19 Dec 2023 17:12:40 -0300 Subject: [PATCH 35/70] Improve Log Messages in GPIO HAL (#9011) * Improve Log Messages * uses pin# in log messages * Uses IO # in messages --- cores/esp32/esp32-hal-gpio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cores/esp32/esp32-hal-gpio.c b/cores/esp32/esp32-hal-gpio.c index a60342760a7..df0e6a29984 100644 --- a/cores/esp32/esp32-hal-gpio.c +++ b/cores/esp32/esp32-hal-gpio.c @@ -104,14 +104,14 @@ extern void ARDUINO_ISR_ATTR __pinMode(uint8_t pin, uint8_t mode) #endif if (pin >= SOC_GPIO_PIN_COUNT) { - log_e("Invalid pin selected"); + log_e("Invalid IO %i selected", pin); return; } if(perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) == NULL){ perimanSetBusDeinit(ESP32_BUS_TYPE_GPIO, gpioDetachBus); if(!perimanClearPinBus(pin)){ - log_e("Deinit of previous bus failed"); + log_e("Deinit of previous bus from IO %i failed", pin); return; } } @@ -140,7 +140,7 @@ extern void ARDUINO_ISR_ATTR __pinMode(uint8_t pin, uint8_t mode) } if(gpio_config(&conf) != ESP_OK) { - log_e("GPIO config failed"); + log_e("IO %i config failed", pin); return; } if(perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) == NULL){ @@ -164,7 +164,7 @@ extern void ARDUINO_ISR_ATTR __digitalWrite(uint8_t pin, uint8_t val) if(perimanGetPinBus(pin, ESP32_BUS_TYPE_GPIO) != NULL){ gpio_set_level((gpio_num_t)pin, val); } else { - log_e("Pin is not set as GPIO."); + log_e("IO %i is not set as GPIO.", pin); } } @@ -174,7 +174,7 @@ extern int ARDUINO_ISR_ATTR __digitalRead(uint8_t pin) return gpio_get_level((gpio_num_t)pin); } else { - log_e("Pin is not set as GPIO."); + log_e("IO %i is not set as GPIO.", pin); return 0; } } @@ -204,7 +204,7 @@ extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, interrupt_initialized = (err == ESP_OK) || (err == ESP_ERR_INVALID_STATE); } if(!interrupt_initialized) { - log_e("GPIO ISR Service Failed To Start"); + log_e("IO %i ISR Service Failed To Start", pin); return; } From ad4f0acea9bbed154bc18a4f37ae07ff34f1c576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Andr=C3=A1ssy?= <10706773+JAndrassy@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:22:40 +0100 Subject: [PATCH 36/70] Revert "WiFiSTA - allow using DHCP again after disconnecting static IP (#8848)" (#8873) This reverts commit a9a72d1fdcb99eb434340378b3b15aefdf44c0d8. Co-authored-by: Rodrigo Garcia --- libraries/WiFi/src/WiFiSTA.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/WiFi/src/WiFiSTA.cpp b/libraries/WiFi/src/WiFiSTA.cpp index 505940015f9..bf80d37393b 100644 --- a/libraries/WiFi/src/WiFiSTA.cpp +++ b/libraries/WiFi/src/WiFiSTA.cpp @@ -373,7 +373,6 @@ bool WiFiSTAClass::disconnect(bool wifioff, bool eraseap) wifi_sta_config(&conf); if(WiFi.getMode() & WIFI_MODE_STA){ - _useStaticIp = false; if(eraseap){ if(esp_wifi_set_config((wifi_interface_t)ESP_IF_WIFI_STA, &conf)){ log_e("clear config failed!"); From 9d0274b3db9d76cc3a87cd3526dbf2539c81bb64 Mon Sep 17 00:00:00 2001 From: "Limor \"Ladyada\" Fried" Date: Wed, 20 Dec 2023 09:23:55 -0500 Subject: [PATCH 37/70] Pin fixes for the Camera board (#9021) * update pins * re-fix pinout that was reverted * more pins * Update esp32-hal-tinyusb.c --------- Co-authored-by: Ha Thach --- .../bootloader-tinyuf2.bin | Bin 22112 -> 22112 bytes .../adafruit_camera_esp32s3/pins_arduino.h | 15 ++++++--------- variants/adafruit_camera_esp32s3/tinyuf2.bin | Bin 202400 -> 193520 bytes variants/adafruit_camera_esp32s3/variant.cpp | 11 ++++++++++- .../bootloader-tinyuf2.bin | Bin 22256 -> 22256 bytes .../adafruit_metro_esp32s3/pins_arduino.h | 2 +- variants/adafruit_metro_esp32s3/tinyuf2.bin | Bin 174736 -> 174736 bytes variants/adafruit_metro_esp32s3/variant.cpp | 3 ++- 8 files changed, 19 insertions(+), 12 deletions(-) diff --git a/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin index 721a1a1c70c28d6d71cdd8f0950752f510e2b33a..f0169612e1334529dae9678a0120c8959bc8212c 100644 GIT binary patch delta 56 zcmV-80LTB}tO4Mx0kBCG2Q@l0GCDIbvrrZ9Kp`CR>{4Pi`mIA` diff --git a/variants/adafruit_camera_esp32s3/pins_arduino.h b/variants/adafruit_camera_esp32s3/pins_arduino.h index 26c72ad392c..62132dcadc3 100644 --- a/variants/adafruit_camera_esp32s3/pins_arduino.h +++ b/variants/adafruit_camera_esp32s3/pins_arduino.h @@ -24,7 +24,7 @@ static const uint8_t LED_BUILTIN = PIN_NEOPIXEL+SOC_GPIO_PIN_COUNT; #define RGB_BRIGHTNESS 64 -//static const uint8_t TFT_BACKLIGHT = 41; +static const uint8_t TFT_BACKLIGHT = 45; static const uint8_t TFT_DC = 40; static const uint8_t TFT_CS = 39; static const uint8_t TFT_RESET = 38; @@ -32,10 +32,10 @@ static const uint8_t TFT_RST = 38; static const uint8_t SD_CS = 48; static const uint8_t SD_CHIP_SELECT = 48; -static const uint8_t SPEAKER = 41; +static const uint8_t SPEAKER = 46; -static const uint8_t SDA = 33; -static const uint8_t SCL = 34; +static const uint8_t SCL = 33; +static const uint8_t SDA = 34; static const uint8_t SS = 48; static const uint8_t MOSI = 35; @@ -55,19 +55,16 @@ static const uint8_t DAC2 = 18; #define AWEXP_SPKR_SD 0 #define AWEXP_BUTTON_SEL 1 -#define AWEXP_BACKLIGHT 2 -#define AWEXP_CAM_PWDN 7 #define AWEXP_SD_DET 8 #define AWEXP_SD_PWR 9 -#define AWEXP_CAM_RST 10 #define AWEXP_BUTTON_OK 11 #define AWEXP_BUTTON_RIGHT 12 #define AWEXP_BUTTON_UP 13 #define AWEXP_BUTTON_LEFT 14 #define AWEXP_BUTTON_DOWN 15 -#define PWDN_GPIO_NUM -1 // connected through expander -#define RESET_GPIO_NUM -1 // connected through expander +#define RESET_GPIO_NUM 47 +#define PWDN_GPIO_NUM 21 #define XCLK_GPIO_NUM 8 #define SIOD_GPIO_NUM SDA #define SIOC_GPIO_NUM SCL diff --git a/variants/adafruit_camera_esp32s3/tinyuf2.bin b/variants/adafruit_camera_esp32s3/tinyuf2.bin index 21e91284e05caf503c9a0f2decefad67f18edcc4..0247a171331c8b9ac80f659b67751ff0377b25f2 100644 GIT binary patch delta 69677 zcmc$`3s_WD`!~GTo+B{oa2gR%XAg`F1PTMipeQpVA)X3R(aH+Z!9!XesI0VM$P|qV z+&*pX(VhXJ(4;^u%|n5jX<@04mHBAlfkX~+5Ih0S_ggbV`aIwN{a@erUhnn(x%kb! z*1guc*S*$WhkNb4?`^M8v|aNWa$U-fnEyn)@dm>%9)SCgs2Mgf_?4m|gFN-|QS)Nw z#wS=FD>6m_ zx3s_~GDFt=b}2)~pUz3M=2#eW0Z6k{PqdMkKU(0~x1HpK2bNJT{qjy*ZyTQ`D|?(N;Ir>^{Nz ziX?43V>a%-B>8hdVA^YAXD_xao6STIk9ll(bku`^!C&?tzI%X*)$yTYgl&I}_1IlE zsc^{d{1^6l_1AkO?rfxu{6UKOBoyuTjjK`DeR41G` z_2TzV+Ax%1)amg?g6y=Al2I@{kDW;3V_uoY_q-9u?wi8vSL&_Po{cvmnW2>CrIdA3 zk(-L$RN|&mHVR4RF&zZW`#OL2erCri0ydh?@>Y{Z0kcZU*P38aEAb(@;0ny6G@C4RceSn})k- zgquF%rjc$M<)*{kbOh>mDiH0aF>b1N(^xl+b5pti(Jp?>P2=6v;HC*~I?_!?xoM)C zj&{>AZfZ?JU<<~{{}8PIQx&`Gwf>K_+@*l^f3EAD3P_ySefR9#IST8vns{Tt{di+2 z5Dg>)0lo3Yv=zimadttx@hGsel5xiS_@PyddEY}kj3}lr?WmjI1{ysdGJ%=Ed|(Bz z1*irZ=EsjPDj_@+h*u>BM*~IUF9Siwo?+#~YhVv!w zu6}5*=A++xludh$x4ai6b#2o8ruRDdg7B)aP4%No+h2c1CX-JtOcYll zL(%C}zP2zmG%@l-l05E25~DwnQ~;WYwR2_6iKK&%o=A!Va$>QT8dv(Unag%RQItVO z=VB*9dEoEg+fJWA&@p0eDM5+Looi9xT&P*bi6wA{dnVQKn
  • vjyDDaVyRqCS7^f@ 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

    _c|$H zxJ{X<57&e}(KcD2DvV5tI0dK?P0wp^G zWY>mN^^y}!-+&5wfgg})hr_TNy88E%0gBrMxPL$jZFMw-ck5sylvILGxHeFOOQ;d9 z-vp0z7wQDvEq12c45Z0VhSy{`IG-3S^svQEkbW$#&4xTkD? z71>7-Qvq=*h&*l=dAttCtI%cB(qwqGjt(c^Kvc4)E^Bk0;BlJ`P{ko5=xaCC2r;)= zJvX6FFf_4V^I+%hj_sE?l1zEOTNj!(w5LvbhZ>A#!s>e(z+z!0a+i@Ml)I{KcpCm@ zozQWc)sEd!m*rmzt(`W=fNS#DENV9x$m|-y^$t7UcL9>Ei}(dyVg0L)Z=SA=gu|$? z-`-g*#NJ`wSB|56W9oz-?y!@&X`uRDcGa9q)oBJef+&o7ocvM^9Q5Pr&c6xQqDocY z`!kYno#*7pJ3s1+^*H~xQfbl8cQDh}W++S>i!TCPU$w+TGvuKzw~Td_n7?~pA%hctgbaJu0Yn2 zIKfXrT#(jI)}fi;49R_&CH|CpO|A7QO08Dxv?XbL_9w7cliWkriBHs4tP?80b@sw} z@a3M8lB5`)tr@#McmDo4h@xJXDn=t@bxLZIV#4};Vf*I~$4|c(@3%h3d;gv1?JTF% z3jQ1y#oehAp5x%rE5BOdO^%z-2i(Qu(`xgUyo zal)YH?)RDrMF%a;Ov^}}?ye<}-QUYz4R<$Ot5HX!I*?tGC)`CGC!QbF0*=gqbLjyb zUN?^7fXqBK;5a#OzPpbTA#)w{p-65#pFAFrxcwp5K^P2~^=E4;U}fLGkYn?_?-p4s z&Ptxw`P~%?WRPK3?u1MwjWwuFB=1@%Xin`}gkc1joxg|5J9IIi9{4x7e zl;IjbuyKNM8MA(ni?uTRjJzLFmBaT{k>!tp)7`{EbrJ9D;sT~bWj?0LjKN|K%IyDN zWhT{Rjr_lqiJ;1i9ahGu$e*_Vl7gQBw#w_j6B3b_K^2&aws6Xy2DV5;F@-Wde;NuU zROmFKp+dL+uh5Y-SwmI-hXuT-(A$;wEHH0arZKq-g{IsWD$kz>LX+)6UGwLm(6v

    K(zHt(7+^1JYNlYAL+J!pGRU_b=KEZ356r?6<~(&ASSW9dUb?b zVp*jyNx@CyCe{dR6`bBUx>{?P5v17vy&SK?o^M_x`^H@sld+Csi#Y$R|}G z7FwR#Rd>mWO8C}XueM&U#`aT{J{H&|)ZSKXxx0UxOw6vb?yI7W9<*L03kjjUqYc-D z{jam)t}5&9DlE#o93WwzGZ&-ot_G2o#L=OP3PxtOcO;HXqs?DnZH>2VrXvwiRHjDeO}UGdc*LLEsnwuB1TogM`ZwmI1J(6!rnYu5cg^$B|DU zj4VQc{{8A9V|A2!xMdMc(zyYZ^m=tE7yJ1gTxd$@l0m^FTdJX%#x{CGYMMgLj|~$d zMnEf7dZ*}9nE1<*r90SXi=I_h&noB*J(0WfC0wKBXTbJHa`qkU%uN+J?&R5O-t_}h zoT*Uh0R>A&Rv>QwA$w=Pyc*lYphC_YW(`0{mLgQMxavF}#to9iSQsc!ksO?)g@0od zXZWQG3VXQQynP;q{;K%$N(4JCu{U&16xZ-$6|Vd@N_L_sE*T`bDfqSmR;C!ns;O3kw^F?=;Wd^tQ=@zW}5#USM#u1O1K zM9YfyIFk|_4|y*LLgV6|RKjMCq#z$_MU_?@F;rS5l~p}3$X<)uYDF3Nd5z>2W?QOk z{Dxy8f#g|A^EJ@$Ltj-%g+Oy}7P07QI}t8d=8rgVlq{vl`GD*OgY Iv+S(6$^Xx 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 B(w_hT delta 77857 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%0AysWz>d>usQD2 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
    lZ`U>nZ;EWyHpwhT;N&_^V
    zsWb9A8DbKc!5o*li(J^U)u?{Cb3F;fA3hO
    zy|S|P;0^ek^hd|SX-$T!$p|({-#Ut>turS6X}et~@NSu7NT
    ztYV<8VO@)p;35K!$nu~JYzhzkWQrMKE_=OJ-NMAI@e)HRF<=!Dthj9e5T&$o*8T3A
    zORR;?_c*I2Y1w@Bh*g*}Tyu;;8OQ=zn@vpP{Kw=KE{tS@$ZphpcRXO(8@IT4<9=E
    zmlb7XL*|V`ir8~aqPnQD)Lqi@HNxvX%v!e%k3~%21`2*9U30o?uOp&&6Y>3Bt}B>E
    zzBP9O
    z&m%IaD5jyGu$)u-DJu>l*B}ZE5(7mvCLShu1g&)wZ$(TEXTqa_JhpCNiPNm=It>)xc+_gTF>z??SJCTS4Zdpmc7q4egS+
    zJHcnr;!G2!Y6TPML@}$LBqqP5H9%U&gKeHB#D>P0WW%j9Gr4yZ7JDGT%`G^?%j{>L!|97RD`%0Db6EV
    z^NPcogP2SsCP#Jbb;TzJC~E;YoO}|5@RAui(V_Cj9w+Kfdn0?ly~ly}blZ}Y-$|*t
    zW?9a>_$t*i6ZKJh`fZ*4{M%H>jZtO{qc)7bZ-0QR0RS@)mxcq_Z-MEk<8FGA&7Hvh
    z;?jqC4ySO5CViV~I9rc^yRF2-@Yxd!zV
    z&1KAQv=SYzRy&DTrHrQ*sUOqgJ9-}tFOnk0Ok4)Hi*trf&=@*LpwtQCYK0NvkCa%9
    z=o}lF3*ZSN_t46S^d&!wic5F-WmHI7;>yi9sCSN13ZXn)CvA4E4ogJ0-P%X{>v_17
    zOvX&0o8mHo!w-I}_>b9Mw60HQkNlNCa*pTPLPu!-A%2VnIE)wXYae=9RBzGvSpH5|
    zI&}CLf$_ai!sJfm)JM)fs*MxjHeLCR4)cld)CGg|!*{wyuaJvRwCUoVc9(B>qMmw&+TW
    z4lP8lzm>vYca
    zno=Paxzyuq46|yS#pEPAYD$>MT!XCecXo+261RjtV3n16e5EcAMJ{35+XYvW01{Si
    zb<9K*dhuG}17aY~!WumsrQWcVJN8o*cI_w|rD7)SI3Bc**kn6|u8~X@=*nUpUL`?l
    zHMpv&YN%Iemq*8s)+JF_M4cbuVS=a#pW`)3NfJ0ifCp77*72RKo}qE+A0a`7mYCz=
    z0)3!B=d3E}v(7BlspOr5A>uX|F@58Y!=A^6k}hw*zv`zRiclSreq;4>>$
    zQa7R09Esah#M%aU`~dk})70&2l{q#(4e&^p|k%r?&8zYtoUSsth5!H68`@}A(i
    zjFdfjdCQft_xf*UgJl_u#+o6+y4h_KHNFFd=l5Eqs3Zz@2+|a;%g~_a1vm
    zIL7utIl`*v$k;nmjSqd6|DEXiRdZ~XW%cO3#6+IUH||9
    
    delta 2633
    zcmY*b4^R}>8Grllgk#Y|0pXCiceoyh8gK821%%;FBc(J>IE>VgGW?0>SR+kM)l53H
    z$3vrMVrC?3oj7BryN6eh^s30jG1i-MXo*sZO4^t@!7`3(E7}%^NMnb6+xPBFn(oa0
    z-n-xTz2Eok_rAA#Q&DOvN^$oX`uW0sZgfF#`;S(8FXG#DICHe}ec%OGzhRDI=3kgG
    zj$!))e5b0IxdfWO#T>_su&~51z_!)iVtku^isu{2gJu8*4Is!r&MwNjMvYcF
    z=+Vjt>p=5%JbKd^kTTGpy@8^BCv!P(7rtRkq=1Qi{exi!~?6
    zE&^gLXDdmNs|Y!yD5n)*Q3dEFKk+0`z-^&%s-!+RfhHbIou+miyV9PPw3K$fCyA^2Lh{!@i%{AJmVF8heF;OZ|
    z&0+hycD~;%ud`PEAhMZlGuNrp;%2tFZ?NY>dS3+*SYw~(^|JLlQKXes&I#jed$Kg#7*>Am
    z^G-6tB;&OY_PA%%anu?$BA*%jO?a31`fsA;+p7PZkZXP-XSR?
    zDmMGs2Z4g0?%{O0J04r-vAMCG8hV3f4|(aD9iI3Bt?;9
    zz|5zQrpMBw>5;V@jwAIUhv&Z72;ike@=5%d%x%Zz6N{DywMDE_BpkB_D#->PLFy3%
    zcN2U5RAduQq`tw>8pwDPwREAVDd1+cWvm9+Ksj)m82@X?wgI?EfD<;XvVYHUyqK-{vRR}
    zN6xr^6I%Lm8s^Tt%=v54mY?1UNJMROGLs4-)@hPI&9|5DAoG?6^spXnHC?Yq@p{x~
    z?llb#P`6b8w-alWgRhV=(8ls+=p@rdGjD|sS$b-r6e%RiRR}*}9x?IRbr+`F&+dPd
    z-uDb}UwAy@p{qO~d&VSHl1DFLY3(s8V+O9oig0&rF$2_4yE!a4MmGD!|(V>tf
    zfx-bwe9ELKnb+Q;y0TF(b*RtM(I=dsG&^S53Cw0-_H4%2e8dOfT*Rm11dgUa9<@JS
    zl9=XC;dnzN{Fc5W!!rhM)*%IqDJg^J1->C7|8?;~QM|^GKE_*-v=Y-h*!T&<^DE5!
    zJ2AezL6h@@yr6i6!G73lOu;fK!fB!0s*QY`vtW+5MlS!4BVAtJ7r4aO&g;E&vJ^ZY
    z<4FkdV}?&(VJ0qoO<))=N@^$9h<{+DlX@=$uao>^H2+*We~`Co7Z}W4Azbnd@imbl
    z5|30@>ehKKTmaxjVlQOW3;J1lz4_F&UHtY_r@q{MFnf?<2El^(kWAUuF4MkZ#%QXO
    z1|8!W9pgeLO~=e2lyNO2jL}RyqIZl_DnS9hAiru`Kld{_h5DKX)p=Ic`)3Y&YsF)NW>fl^a16bcV3QBkZgNm+VVmQLJp)I-Vd4%0P1
    z$>8IUEe9AkCBXY6(I^vfo76N?wur|Rj$1^7v-Au2`{TCF5g&736a}?mQV~{HSsluW
    zr;W4ZABA92CK=^)SP6H!E(m;jQc#&(0I$w=xHAf2huz|`3(~)5;a9Ux=yF^3%ZX3e
    zhn(ZJdHr9`;?kV+@M*4ddm`t!UZ{jQ@d_Ecv+zbesbK!pfVN~(a*}=|*SR;Fc09QC
    h%WlxNFzsf0f8=Ke{vz((clqeu&>M}PZ=BZ_{V&L~96kU5
    
    diff --git a/variants/adafruit_metro_esp32s3/pins_arduino.h b/variants/adafruit_metro_esp32s3/pins_arduino.h
    index 9963d1aa476..e9769f113f6 100644
    --- a/variants/adafruit_metro_esp32s3/pins_arduino.h
    +++ b/variants/adafruit_metro_esp32s3/pins_arduino.h
    @@ -14,7 +14,7 @@
     #define BUILTIN_LED  LED_BUILTIN // backward compatibility
     
     // Neopixel
    -#define PIN_NEOPIXEL    45
    +#define PIN_NEOPIXEL    46
     // RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API neopixelWrite() and digitalWrite() for blinking
     #define RGB_BUILTIN (PIN_NEOPIXEL+SOC_GPIO_PIN_COUNT)
     #define RGB_BRIGHTNESS 64
    diff --git a/variants/adafruit_metro_esp32s3/tinyuf2.bin b/variants/adafruit_metro_esp32s3/tinyuf2.bin
    index d57e5b75b55353e5d6f200e26ed04c88b8b5d881..25a5ba66b1e8020665347240932ecc49070f6c42 100644
    GIT binary patch
    delta 1753
    zcmY+EYfw{16vuZr1VTtaglJGK32O0y3JTtU3%Nvm6;!N3K_Z|81+;B|An0g=s1!ed
    zFsXB~&=EzeGxaqMHrAFITdhj^a>P1xY9G@MGgNWJDy<{h+kezAy_4Vk&pCT`@7Z&A
    z)AED51;0od+d3&iGi&a=S({87ic9915?d{^$HmW2o|~8)mq1=VD~lZlLqcoA48z8N
    zz9)a(wEmdV7I3_D$bKikAT~VWo5o*n6}ZdC@4KOO?aIw^8|d@|jlJ@1TXnHxPGVwQ
    zoO@Z~N0Z%;bBh?Yx$^Qy{Y�H>+I*)@YaA>gTSrhU;jp>p(^6|4MN^t0)YyXw~WK
    z9VN+z7e%->SKjlJ@wn=$VyFA+^Ai~Nsp_R_pkqKd(1S=|aP?GOrFQpTM^GT$;cngO
    z^rM;X(0YYJ+mXU}C-8z1mvQfz)PpM-Z^~r6QQvF!LSwDe@(`M{g7N6(j1NE$LQg2D
    z<@#hI5(6RUbG_ys#DZ41gByaGGP98JOt-P|e$6>O{f#N7z>oAiV^pTk$6_U_q+PVs
    zb4*F2sIoho@uO0&KkYMqJfHE(M8>y)ORz6OZ$L+(^PyX%o&cJmoq}D>0Ht3Li(S&G
    zsNlQ?my`>lnu%MWGwF7yV71{+OmE?*5J5-%m)m;Ln64kRB291C*zO=xi+
    z{LthV1+9{vsc4k54DKUPjbYVhLEH$HWv9FbN)|Al3aUVQo*>RaOwSqD`EpSHYS@vO
    zBf@Wj-GRK@zzaL8g%s)Q6h-oWSuV}S9(?N&M)n8PV&4j)O2wfe8mT>yMx<;(Li0i_
    zfTWmIUjg^!8u%ArmqUl36U>Yk!d?o^fbK=C0rolRK_Hz>A4@tljWL#pj3;9>2cw5!
    z$H0C9-GUBS(ipFh)~V@4=QxDh;9da?_6T&Qh4BZ#2F_wvKSA4p0}ZTQ!}u?Y88_x4
    zhWxLAG9A|lYz0@rD2QK#j@%`d&O5~NE%HAFCqXsh9Z(OneleTD37iE$J|xREK9xhz
    zmSLITL@RWJ+(&E_Op%O1G|ZW|gz;i<1hh4vix
    zw;(bDA+3nFgP%eCGSmugX|XK)i?II%c<<%-*H9Pu9t37Eo&??j&A^+%BzF*yy__Yg>O7B`;taQ
    z$a}Es&^$Cy?uFftKZDyK44>*%sX|LPYX@Ntfno4S`cq3M;CBYeeDp65i}@hK
    z&U?t%vKHq78jhH+S8eMUw*x}XJ|(1c2O%vWOCd^c2GioHWk^dX6U1&E5z8>O0#t%(
    za7~StLz|=#yp0Y>#t<5neGQAB0F9u3lOR$e8svXu`D8k^3?Tb&;G1>Z%J})ag6NAS
    zHUDA4hr;Qc
    z?=hUxmh595v5du}86VW;pXk4I<&byy=7#q(-*pta_MA3OA7pLH@ne^4%h>hSecwNv%0E$Gc^O`Np0$jqCZD@>DkSV#5uo=lIB;
    ziQzptUAOx5yLJD?H2A!Z62F-a&-Rz*97^-|JLWU$^i|A$PF?=kvDdFEHE``pW(c!T<6fnRVej5>RYmWlqpPRTmpJ90K|jPVBg;L
    z@xI{l!?eoZP!|+p8=9lq!3oL>5M3iC8kuw-T*DRjGK|$39SdUU=rfbF?SvGANV7I_(O=Fpcg;_
    zD9dG;d01PPs9RPC}~3I0KK9H{!!R-ph0M~mGKW?bEp-%
    z8nHF7zgB;Xq-nHAHR|Y4%PfWQw~#GDwh49;?5EILbYN@&N<*9mP!bN6~b{6*#wn0q?RveHcZH=?XPNPm>0DOBh%2g2SQx
    zBvH9{0dY)=cPBuoyIgD{8x*TDAQ!vsG%KakN%7
    z8fZ$sV+P(CCOAQ&HbdGDU!M8$*yr>TAzNXuNAu7?(GL3#{wv%C5%^RC)ujenW4H@5
    z2p)iL^^t)l;ksy}>B#(&*?4iY@UoCW``tf7=a=C=K=p`+dgUp{0RSP#`Uz=XONa-Q
    zXmZs>(e$JA3JlAxmX%f`Q7SOC8Y~2h!1p>72;HpyiQVWu)e=KfN>5?&-C!-avp`lT
    zQOx3RAr9NA?H)!y1)*8DoQ$_UmX%O!+-LW5@u6@($g?po4@1a)ZeO`6qor&5
    U;;zh-`8NwSN&TmH(w0^K1GXjt`Tzg`
    
    diff --git a/variants/adafruit_metro_esp32s3/variant.cpp b/variants/adafruit_metro_esp32s3/variant.cpp
    index 811a6d508c9..485f08b4aef 100644
    --- a/variants/adafruit_metro_esp32s3/variant.cpp
    +++ b/variants/adafruit_metro_esp32s3/variant.cpp
    @@ -30,7 +30,8 @@ extern "C" {
     
     // Initialize variant/board, called before setup()
     void initVariant(void) {
    -  // default SD_CS to input pullup
    +  // default SD_CS to input pullup (we cannot have built in pullup since its
    +  // a strapping pin!)
       pinMode(SS, INPUT_PULLUP);
     }
     
    
    From edf5ecffc85b075cf07c0d20caccb90f48d7c307 Mon Sep 17 00:00:00 2001
    From: Rodrigo Garcia 
    Date: Wed, 20 Dec 2023 11:25:12 -0300
    Subject: [PATCH 38/70] Fixes ESP32-S2 CDC Debug Logging (#7284)
    
    ---
     cores/esp32/USBCDC.cpp | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/cores/esp32/USBCDC.cpp b/cores/esp32/USBCDC.cpp
    index da0d5587af6..c99659dadac 100644
    --- a/cores/esp32/USBCDC.cpp
    +++ b/cores/esp32/USBCDC.cpp
    @@ -79,7 +79,7 @@ void tud_cdc_tx_complete_cb(uint8_t itf){
     
     static void ARDUINO_ISR_ATTR cdc0_write_char(char c){
         if(devices[0] != NULL){
    -        devices[0]->write(c);
    +        tud_cdc_n_write_char(0, c);
         }
     }
     
    
    From 5d97e02ad777100430dd443ac3e3458e5d5f72df Mon Sep 17 00:00:00 2001
    From: Rodrigo Garcia 
    Date: Wed, 20 Dec 2023 11:26:35 -0300
    Subject: [PATCH 39/70] undeprecate available() (#9027)
    
    ---
     libraries/WiFi/src/WiFiServer.h | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/libraries/WiFi/src/WiFiServer.h b/libraries/WiFi/src/WiFiServer.h
    index b9db726b9b7..c51986701c4 100644
    --- a/libraries/WiFi/src/WiFiServer.h
    +++ b/libraries/WiFi/src/WiFiServer.h
    @@ -45,7 +45,7 @@ class WiFiServer : public Server {
           log_v("WiFiServer::WiFiServer(addr=%s, port=%d, ...)", addr.toString().c_str(), port);
         }
         ~WiFiServer(){ end();}
    -    WiFiClient available() __attribute__((deprecated("Renamed to accept().")));
    +    WiFiClient available();
         WiFiClient accept();
         void begin(uint16_t port=0);
         void begin(uint16_t port, int reuse_enable);
    
    From 812a59aadd037ef0a64e7b04427f237ecc4e418c Mon Sep 17 00:00:00 2001
    From: Neale Petrillo <82894027+econeale@users.noreply.github.com>
    Date: Wed, 20 Dec 2023 09:35:31 -0500
    Subject: [PATCH 40/70] Added example to demonstrate using HardwareSerial with
     RS485 interfaces (#8941)
    
    * Added Serial as RS485 interface demo
    
    * Added more detail to initial comment
    
    * Switched to UART_MODE definitions from uart_types.h in order to accomodate some versions of Arduino core
    
    * Update libraries/ESP32/examples/Serial/RS485_Echo_Demo/RS485_Echo_Demo.ino
    
    Co-authored-by: Lucas Saavedra Vaz 
    
    * Update libraries/ESP32/examples/Serial/RS485_Echo_Demo/RS485_Echo_Demo.ino
    
    Co-authored-by: Lucas Saavedra Vaz 
    
    * Update libraries/ESP32/examples/Serial/RS485_Echo_Demo/RS485_Echo_Demo.ino
    
    Co-authored-by: Lucas Saavedra Vaz 
    
    * Removed HardwareSerial.h include. Switched RTS pin to GPIO 4 for broader compatability.
    
    * using 115200 for UART0 - console
    
    ---------
    
    Co-authored-by: Lucas Saavedra Vaz 
    Co-authored-by: Rodrigo Garcia 
    ---
     .../RS485_Echo_Demo/RS485_Echo_Demo.ino       | 48 +++++++++++++++++++
     1 file changed, 48 insertions(+)
     create mode 100644 libraries/ESP32/examples/Serial/RS485_Echo_Demo/RS485_Echo_Demo.ino
    
    diff --git a/libraries/ESP32/examples/Serial/RS485_Echo_Demo/RS485_Echo_Demo.ino b/libraries/ESP32/examples/Serial/RS485_Echo_Demo/RS485_Echo_Demo.ino
    new file mode 100644
    index 00000000000..a19be7ffe25
    --- /dev/null
    +++ b/libraries/ESP32/examples/Serial/RS485_Echo_Demo/RS485_Echo_Demo.ino
    @@ -0,0 +1,48 @@
    +/*
    +  This Sketch demonstrates how to use the Hardware Serial peripheral to communicate over an RS485 bus. 
    +
    +  Data received on the primary serial port is relayed to the bus acting as an RS485 interface and vice versa.
    +
    +  UART to RS485 translation hardware (e.g., MAX485, MAX33046E, ADM483) is assumed to be configured in half-duplex
    +  mode with collision detection as described in
    +  https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html#circuit-a-collision-detection-circuit
    +
    +  To use the script open the Arduino serial monitor (or alternative serial monitor on the Arduino port). Then,
    +  using an RS485 tranciver, connect another serial monitor to the RS485 port. Entering data on one terminal
    +  should be displayed on the other terminal.
    +*/
    +#include "hal/uart_types.h"
    +
    +#define RS485_RX_PIN 16
    +#define RS485_TX_PIN 5
    +#define RS485_RTS_PIN 4
    +
    +#define RS485 Serial1
    +
    +void setup() {
    +  Serial.begin(115200);
    +  while(!Serial) { delay(10); }
    +
    +  RS485.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN);
    +  while(!RS485) { delay(10); }
    +  if(!RS485.setPins(-1, -1, -1, RS485_RTS_PIN)){
    +    Serial.print("Failed to set RS485 pins");
    +  }
    +
    +  // Certain versions of Arduino core don't define MODE_RS485_HALF_DUPLEX and so fail to compile.
    +  // By using UART_MODE_RS485_HALF_DUPLEX defined in hal/uart_types.h we work around this problem.
    +  // If using a newer IDF and Arduino core you can ommit including hal/uart_types.h and use MODE_RS485_HALF_DUPLEX
    +  // defined in esp32-hal-uart.h (included during other build steps) instead.
    +  if(!RS485.setMode(UART_MODE_RS485_HALF_DUPLEX)) {
    +   Serial.print("Failed to set RS485 mode");
    +  }
    +}
    +
    +void loop() {
    +  if (RS485.available()) {
    +    Serial.write(RS485.read());
    +  }
    +  if (Serial.available()) {
    +    RS485.write(Serial.read());
    +  }
    +}
    
    From f6e12eb7e900fd6484e186b4b681f5b9504d2e85 Mon Sep 17 00:00:00 2001
    From: a1ext 
    Date: Wed, 20 Dec 2023 15:54:04 +0100
    Subject: [PATCH 41/70] fix(core): fixed String::lastIndexOf bug (#9003)
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    Co-authored-by: Rodrigo Garcia 
    Co-authored-by: Vojtěch Bartoška <76958047+VojtechBartoska@users.noreply.github.com>
    ---
     cores/esp32/WString.cpp | 5 ++++-
     1 file changed, 4 insertions(+), 1 deletion(-)
    
    diff --git a/cores/esp32/WString.cpp b/cores/esp32/WString.cpp
    index 6f0a4fc68a8..990a2824a51 100644
    --- a/cores/esp32/WString.cpp
    +++ b/cores/esp32/WString.cpp
    @@ -677,7 +677,10 @@ int String::lastIndexOf(char ch, unsigned int fromIndex) const {
         wbuffer()[fromIndex + 1] = tempchar;
         if(temp == NULL)
             return -1;
    -    return temp - buffer();
    +    const int rv = temp - buffer();
    +    if(rv >= len())
    +        return -1;
    +    return rv;
     }
     
     int String::lastIndexOf(const String &s2) const {
    
    From 4d469c8f8674eb2317019fb739804e5f5800a9c9 Mon Sep 17 00:00:00 2001
    From: lbernstone 
    Date: Thu, 28 Dec 2023 02:46:09 -1000
    Subject: [PATCH 42/70] Added missing identifiers in esp_vfs_littlefs_conf_t
     (#9046)
    
    ---
     libraries/LittleFS/src/LittleFS.cpp | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/libraries/LittleFS/src/LittleFS.cpp b/libraries/LittleFS/src/LittleFS.cpp
    index f23a3cfe611..f20b4f16b4c 100644
    --- a/libraries/LittleFS/src/LittleFS.cpp
    +++ b/libraries/LittleFS/src/LittleFS.cpp
    @@ -81,7 +81,9 @@ bool LittleFSFS::begin(bool formatOnFail, const char * basePath, uint8_t maxOpen
         esp_vfs_littlefs_conf_t conf = {
           .base_path = basePath,
           .partition_label = partitionLabel_,
    +      .partition = NULL,
           .format_if_mount_failed = false,
    +      .read_only = false,
           .dont_mount = false,
           .grow_on_mount = true
         };
    
    From b2e7338a5e8529b5ede0a8fc7d95703074be093c Mon Sep 17 00:00:00 2001
    From: Michael 
    Date: Thu, 28 Dec 2023 23:46:47 +1100
    Subject: [PATCH 43/70] SD library: Fix format (#9034)
    
    Pass correct work buffer size to f_mkfs
    ---
     libraries/SD/src/sd_diskio.cpp | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/libraries/SD/src/sd_diskio.cpp b/libraries/SD/src/sd_diskio.cpp
    index f6a8df5e42b..1de4dd70078 100644
    --- a/libraries/SD/src/sd_diskio.cpp
    +++ b/libraries/SD/src/sd_diskio.cpp
    @@ -818,7 +818,7 @@ bool sdcard_mount(uint8_t pdrv, const char* path, uint8_t max_files, bool format
                 }
                 //FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len);
                 const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, 0};
    -            res = f_mkfs(drv, &opt, work, sizeof(work));
    +            res = f_mkfs(drv, &opt, work, sizeof(BYTE) * FF_MAX_SS);
                 free(work);
                 if (res != FR_OK) {
                     log_e("f_mkfs failed: %s", fferr2str[res]);
    
    From 73dd8a29a3f4e5dd747e9a496ba65778037b27db Mon Sep 17 00:00:00 2001
    From: Rodrigo Garcia 
    Date: Mon, 8 Jan 2024 09:00:04 -0300
    Subject: [PATCH 44/70] Fixes EspClass::deepSleep(64 bits) (#9077)
    
    * Fixes EspClass::deepSleep(64 bits)
    
    This will change time in microseconds from 32 bits to 64 bits as defined in IDF.
    
    * updates function declaration
    ---
     cores/esp32/Esp.cpp | 2 +-
     cores/esp32/Esp.h   | 2 +-
     2 files changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/cores/esp32/Esp.cpp b/cores/esp32/Esp.cpp
    index aa73e698bbe..ecbf4877d90 100644
    --- a/cores/esp32/Esp.cpp
    +++ b/cores/esp32/Esp.cpp
    @@ -131,7 +131,7 @@ unsigned long long operator"" _GB(unsigned long long x)
     
     EspClass ESP;
     
    -void EspClass::deepSleep(uint32_t time_us)
    +void EspClass::deepSleep(uint64_t time_us)
     {
         esp_deep_sleep(time_us);
     }
    diff --git a/cores/esp32/Esp.h b/cores/esp32/Esp.h
    index 478158ddf1f..82dd25e3c25 100644
    --- a/cores/esp32/Esp.h
    +++ b/cores/esp32/Esp.h
    @@ -86,7 +86,7 @@ class EspClass
         const char * getSdkVersion(); //version of ESP-IDF
         const char * getCoreVersion();//version of this core
     
    -    void deepSleep(uint32_t time_us);
    +    void deepSleep(uint64_t time_us);
     
         uint32_t getFlashChipSize();
         uint32_t getFlashChipSpeed();
    
    From c040f9528def26a96e8ac4dd67c1cee603f88df2 Mon Sep 17 00:00:00 2001
    From: Clemens Kirchgatterer 
    Date: Mon, 8 Jan 2024 13:01:59 +0100
    Subject: [PATCH 45/70] sd_diskio.cpp: return 0 instead of false when return
     type != bool (#9075)
    
    ---
     libraries/SD/src/sd_diskio.cpp | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/libraries/SD/src/sd_diskio.cpp b/libraries/SD/src/sd_diskio.cpp
    index 1de4dd70078..c6943ce4c8b 100644
    --- a/libraries/SD/src/sd_diskio.cpp
    +++ b/libraries/SD/src/sd_diskio.cpp
    @@ -233,7 +233,7 @@ char sdWriteBytes(uint8_t pdrv, const char* buffer, char token)
         ardu_sdcard_t * card = s_cards[pdrv];
         unsigned short crc = (card->supports_crc)?CRC16(buffer, 512):0xFFFF;
         if (!sdWait(pdrv, 500)) {
    -        return false;
    +        return 0;
         }
     
         card->spi->write(token);
    @@ -424,7 +424,7 @@ unsigned long sdGetSectorsCount(uint8_t pdrv)
     {
         for (int f = 0; f < 3; f++) {
             if(!sdSelectCard(pdrv)) {
    -            return false;
    +            return 0;
             }
     
             if (!sdCommand(pdrv, SEND_CSD, 0, NULL)) {
    
    From 6c5b54e085d7cab86709e37dc0d7d63ad9444f44 Mon Sep 17 00:00:00 2001
    From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
    Date: Mon, 8 Jan 2024 14:33:05 +0200
    Subject: [PATCH 46/70] Bump tj-actions/changed-files from 36 to 41 in
     /.github/workflows (#9060)
    
    Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 36 to 41.
    - [Release notes](https://github.com/tj-actions/changed-files/releases)
    - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md)
    - [Commits](https://github.com/tj-actions/changed-files/compare/v36...v41)
    
    ---
    updated-dependencies:
    - dependency-name: tj-actions/changed-files
      dependency-type: direct:production
    ...
    
    Signed-off-by: dependabot[bot] 
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    ---
     .github/workflows/build_py_tools.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/build_py_tools.yml b/.github/workflows/build_py_tools.yml
    index 907a3438bb8..46ecf6da756 100644
    --- a/.github/workflows/build_py_tools.yml
    +++ b/.github/workflows/build_py_tools.yml
    @@ -22,7 +22,7 @@ jobs:
               fetch-depth: 2
               ref: ${{ github.event.pull_request.head.ref }}
           - name: Verify Python Tools Changed
    -        uses: tj-actions/changed-files@v36
    +        uses: tj-actions/changed-files@v41
             id: verify-changed-files
             with:
               fetch_depth: '2'
    
    From 53aa8c8a5c0fcf705eebaf3b6539f6cc9a274ec1 Mon Sep 17 00:00:00 2001
    From: powerfeatherdev <139665272+powerfeatherdev@users.noreply.github.com>
    Date: Mon, 8 Jan 2024 20:35:43 +0800
    Subject: [PATCH 47/70] ESP32-S3 PowerFeather Fixes (#9052)
    
    * Fix and add pin definitions
    
    Add pins D7, D8
    Correct pin numbers for ALARM and EN
    
    * Enable CDC on boot
    
    * Use partition schemes appropriate for flash size
    
    Also adds another partition scheme based of off default_8MB, but fat
    partition.
    ---
     boards.txt                                   | 45 ++++++--------------
     tools/partitions/default_ffat_8MB.csv        |  7 +++
     tools/partitions/large_ffat_8MB.csv          |  7 +++
     tools/partitions/large_spiffs_8MB.csv        |  7 +++
     variants/esp32s3_powerfeather/pins_arduino.h |  6 ++-
     5 files changed, 39 insertions(+), 33 deletions(-)
     create mode 100644 tools/partitions/default_ffat_8MB.csv
     create mode 100644 tools/partitions/large_ffat_8MB.csv
     create mode 100644 tools/partitions/large_spiffs_8MB.csv
    
    diff --git a/boards.txt b/boards.txt
    index f1256a1ed28..903948aedb5 100644
    --- a/boards.txt
    +++ b/boards.txt
    @@ -29364,7 +29364,7 @@ esp32s3_powerfeather.build.variant=esp32s3_powerfeather
     esp32s3_powerfeather.build.board=ESP32S3_POWERFEATHER
     
     esp32s3_powerfeather.build.usb_mode=1
    -esp32s3_powerfeather.build.cdc_on_boot=0
    +esp32s3_powerfeather.build.cdc_on_boot=1
     esp32s3_powerfeather.build.msc_on_boot=0
     esp32s3_powerfeather.build.dfu_on_boot=0
     esp32s3_powerfeather.build.f_cpu=240000000L
    @@ -29377,8 +29377,9 @@ esp32s3_powerfeather.build.partitions=default_8MB
     esp32s3_powerfeather.build.defines=
     esp32s3_powerfeather.build.loop_core=
     esp32s3_powerfeather.build.event_core=
    +esp32s3_powerfeather.build.flash_type=qio
     esp32s3_powerfeather.build.psram_type=qspi
    -esp32s3_powerfeather.build.memory_type={build.boot}_{build.psram_type}
    +esp32s3_powerfeather.build.memory_type={build.flash_type}_{build.psram_type}
     
     esp32s3_powerfeather.menu.PSRAM.disabled=Disabled
     esp32s3_powerfeather.menu.PSRAM.disabled.build.defines=
    @@ -29418,10 +29419,10 @@ esp32s3_powerfeather.menu.USBMode.hwcdc.build.usb_mode=1
     esp32s3_powerfeather.menu.USBMode.default=USB-OTG (TinyUSB)
     esp32s3_powerfeather.menu.USBMode.default.build.usb_mode=0
     
    -esp32s3_powerfeather.menu.CDCOnBoot.default=Disabled
    -esp32s3_powerfeather.menu.CDCOnBoot.default.build.cdc_on_boot=0
     esp32s3_powerfeather.menu.CDCOnBoot.cdc=Enabled
     esp32s3_powerfeather.menu.CDCOnBoot.cdc.build.cdc_on_boot=1
    +esp32s3_powerfeather.menu.CDCOnBoot.default=Disabled
    +esp32s3_powerfeather.menu.CDCOnBoot.default.build.cdc_on_boot=0
     
     esp32s3_powerfeather.menu.MSCOnBoot.default=Disabled
     esp32s3_powerfeather.menu.MSCOnBoot.default.build.msc_on_boot=0
    @@ -29440,39 +29441,21 @@ esp32s3_powerfeather.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB)
     esp32s3_powerfeather.menu.UploadMode.cdc.upload.use_1200bps_touch=true
     esp32s3_powerfeather.menu.UploadMode.cdc.upload.wait_for_upload_port=true
     
    -esp32s3_powerfeather.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)
    -esp32s3_powerfeather.menu.PartitionScheme.default.build.partitions=default
    -esp32s3_powerfeather.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS)
    -esp32s3_powerfeather.menu.PartitionScheme.defaultffat.build.partitions=default_ffat
     esp32s3_powerfeather.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS)
     esp32s3_powerfeather.menu.PartitionScheme.default_8MB.build.partitions=default_8MB
     esp32s3_powerfeather.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336
    -esp32s3_powerfeather.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS)
    -esp32s3_powerfeather.menu.PartitionScheme.minimal.build.partitions=minimal
    -esp32s3_powerfeather.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS)
    -esp32s3_powerfeather.menu.PartitionScheme.no_ota.build.partitions=no_ota
    -esp32s3_powerfeather.menu.PartitionScheme.no_ota.upload.maximum_size=2097152
    -esp32s3_powerfeather.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS)
    -esp32s3_powerfeather.menu.PartitionScheme.noota_3g.build.partitions=noota_3g
    -esp32s3_powerfeather.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576
    -esp32s3_powerfeather.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS)
    -esp32s3_powerfeather.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat
    -esp32s3_powerfeather.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152
    -esp32s3_powerfeather.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS)
    -esp32s3_powerfeather.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat
    -esp32s3_powerfeather.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576
    -esp32s3_powerfeather.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS)
    -esp32s3_powerfeather.menu.PartitionScheme.huge_app.build.partitions=huge_app
    -esp32s3_powerfeather.menu.PartitionScheme.huge_app.upload.maximum_size=3145728
    -esp32s3_powerfeather.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS)
    -esp32s3_powerfeather.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs
    -esp32s3_powerfeather.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080
    +esp32s3_powerfeather.menu.PartitionScheme.default_ffat_8MB=8M with ffat (3MB APP/1.5MB FATFS)
    +esp32s3_powerfeather.menu.PartitionScheme.default_ffat_8MB.build.partitions=default_ffat_8MB
    +esp32s3_powerfeather.menu.PartitionScheme.default_ffat_8MB.upload.maximum_size=3342336
    +esp32s3_powerfeather.menu.PartitionScheme.large_spiffs_8MB=Large SPIFFS (1.2MB APP/5.3MB SPIFFS)
    +esp32s3_powerfeather.menu.PartitionScheme.large_spiffs_8MB.build.partitions=large_spiffs_8MB
    +esp32s3_powerfeather.menu.PartitionScheme.large_spiffs_8MB.upload.maximum_size=1310720
    +esp32s3_powerfeather.menu.PartitionScheme.large_ffat_8MB=Large FFAT (1.2MB APP/5.3MB FATFS)
    +esp32s3_powerfeather.menu.PartitionScheme.large_ffat_8MB.build.partitions=large_ffat_8MB
    +esp32s3_powerfeather.menu.PartitionScheme.large_ffat_8MB.upload.maximum_size=1310720
     esp32s3_powerfeather.menu.PartitionScheme.max_app_8MB=Maximum APP (7.9MB APP No OTA/No FS)
     esp32s3_powerfeather.menu.PartitionScheme.max_app_8MB.build.partitions=max_app_8MB
     esp32s3_powerfeather.menu.PartitionScheme.max_app_8MB.upload.maximum_size=8257536
    -esp32s3_powerfeather.menu.PartitionScheme.rainmaker=RainMaker
    -esp32s3_powerfeather.menu.PartitionScheme.rainmaker.build.partitions=rainmaker
    -esp32s3_powerfeather.menu.PartitionScheme.rainmaker.upload.maximum_size=3145728
     
     esp32s3_powerfeather.menu.CPUFreq.240=240MHz (WiFi)
     esp32s3_powerfeather.menu.CPUFreq.240.build.f_cpu=240000000L
    diff --git a/tools/partitions/default_ffat_8MB.csv b/tools/partitions/default_ffat_8MB.csv
    new file mode 100644
    index 00000000000..2791bf7912e
    --- /dev/null
    +++ b/tools/partitions/default_ffat_8MB.csv
    @@ -0,0 +1,7 @@
    +# Name,   Type, SubType, Offset,  Size, Flags
    +nvs,      data, nvs,     0x9000,  0x5000,
    +otadata,  data, ota,     0xe000,  0x2000,
    +app0,     app,  ota_0,   0x10000, 0x330000,
    +app1,     app,  ota_1,   0x340000,0x330000,
    +ffat,     data, fat,     0x670000,0x180000,
    +coredump, data, coredump,0x7F0000,0x10000,
    diff --git a/tools/partitions/large_ffat_8MB.csv b/tools/partitions/large_ffat_8MB.csv
    new file mode 100644
    index 00000000000..20632d6c512
    --- /dev/null
    +++ b/tools/partitions/large_ffat_8MB.csv
    @@ -0,0 +1,7 @@
    +# Name,   Type, SubType, Offset,  Size, Flags
    +nvs,      data, nvs,     0x9000,  0x5000,
    +otadata,  data, ota,     0xe000,  0x2000,
    +app0,     app,  ota_0,   0x10000, 0x140000,
    +app1,     app,  ota_1,   0x150000,0x140000,
    +ffat,     data, fat,     0x290000,0x560000,
    +coredump, data, coredump,0x7F0000,0x10000,
    diff --git a/tools/partitions/large_spiffs_8MB.csv b/tools/partitions/large_spiffs_8MB.csv
    new file mode 100644
    index 00000000000..ecf87c6d280
    --- /dev/null
    +++ b/tools/partitions/large_spiffs_8MB.csv
    @@ -0,0 +1,7 @@
    +# Name,   Type, SubType, Offset,  Size, Flags
    +nvs,      data, nvs,     0x9000,  0x5000,
    +otadata,  data, ota,     0xe000,  0x2000,
    +app0,     app,  ota_0,   0x10000, 0x140000,
    +app1,     app,  ota_1,   0x150000,0x140000,
    +spiffs,   data, spiffs,  0x290000,0x560000,
    +coredump, data, coredump,0x7F0000,0x10000,
    diff --git a/variants/esp32s3_powerfeather/pins_arduino.h b/variants/esp32s3_powerfeather/pins_arduino.h
    index d1c486901c7..2066cf3e002 100644
    --- a/variants/esp32s3_powerfeather/pins_arduino.h
    +++ b/variants/esp32s3_powerfeather/pins_arduino.h
    @@ -9,12 +9,12 @@
     #define USB_PRODUCT        "ESP32-S3 PowerFeather"
     #define USB_SERIAL         ""
     
    -static const uint8_t ALARM = 7;
    +static const uint8_t ALARM = 21;
     static const uint8_t INT =   5;
     
     static const uint8_t LED =   46;
     static const uint8_t BTN =   0;
    -static const uint8_t EN =    13;
    +static const uint8_t EN =    7;
     
     static const uint8_t TX =    44;
     static const uint8_t RX =    42;
    @@ -37,6 +37,8 @@ static const uint8_t A5 =    1;
     
     static const uint8_t D5 =    15;
     static const uint8_t D6 =    16;
    +static const uint8_t D7 =    37;
    +static const uint8_t D8 =    6;
     static const uint8_t D9 =    17;
     static const uint8_t D10 =   18;
     static const uint8_t D11 =   45;
    
    From 8a1e4638f791fe13123945931a4de9eeefd0f4ef Mon Sep 17 00:00:00 2001
    From: =?UTF-8?q?Jan=20Proch=C3=A1zka?=
     <90197375+P-R-O-C-H-Y@users.noreply.github.com>
    Date: Mon, 8 Jan 2024 13:36:56 +0100
    Subject: [PATCH 48/70] LEDC - Allow custom channel selection (#9031)
    
    * feat(ledc): Allow custom channel selection
    
    * fix(ledc): Fix check of maximum channel
    
    * docs(ledc): Add ledcAttachChannel to docs
    
    * feat(ledc): Change channel to uint8_t + add log message
    ---
     cores/esp32/esp32-hal-ledc.c | 22 +++++++++++++++++-----
     cores/esp32/esp32-hal-ledc.h |  1 +
     docs/source/api/ledc.rst     | 20 ++++++++++++++++++++
     3 files changed, 38 insertions(+), 5 deletions(-)
    
    diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c
    index c96d1b0a650..76d522c2313 100644
    --- a/cores/esp32/esp32-hal-ledc.c
    +++ b/cores/esp32/esp32-hal-ledc.c
    @@ -56,12 +56,11 @@ static bool ledcDetachBus(void * bus){
         return true;
     }
     
    -bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution)
    +bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel)
     {
    -    int free_channel = ~ledc_handle.used_channels & (ledc_handle.used_channels+1);
    -    if (free_channel == 0 || resolution > LEDC_MAX_BIT_WIDTH)
    +    if (channel >= LEDC_CHANNELS || resolution > LEDC_MAX_BIT_WIDTH)
         {
    -        log_e("No more LEDC channels available! (maximum %u) or bit width too big (maximum %u)", LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH);
    +        log_e("Channel %u is not available! (maximum %u) or bit width too big (maximum %u)", channel, LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH);
             return false;
         }
     
    @@ -71,7 +70,6 @@ bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution)
             return false;
         }
     
    -    int channel = log2(free_channel & -free_channel);
         uint8_t group=(channel/8), timer=((channel/2)%4);
     
         ledc_timer_config_t ledc_timer = {
    @@ -115,8 +113,22 @@ bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution)
             return false;
         }
     
    +    log_i("LEDC attached to pin %u (channel %u, resolution %u)", pin, channel, resolution);
         return true;
     }
    +
    +bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution)
    +{
    +    uint8_t free_channel = ~ledc_handle.used_channels & (ledc_handle.used_channels+1);
    +    if (free_channel == 0 || resolution > LEDC_MAX_BIT_WIDTH){
    +        log_e("No more LEDC channels available! (maximum %u) or bit width too big (maximum %u)", LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH);
    +        return false;
    +    }
    +    int channel = log2(free_channel & -free_channel);
    +
    +    return ledcAttachChannel(pin, freq, resolution, channel);
    +}
    +
     bool ledcWrite(uint8_t pin, uint32_t duty)
     {
         ledc_channel_handle_t *bus = (ledc_channel_handle_t*)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
    diff --git a/cores/esp32/esp32-hal-ledc.h b/cores/esp32/esp32-hal-ledc.h
    index ca172844247..c81da6dced2 100644
    --- a/cores/esp32/esp32-hal-ledc.h
    +++ b/cores/esp32/esp32-hal-ledc.h
    @@ -45,6 +45,7 @@ typedef struct {
     
     //channel 0-15 resolution 1-16bits freq limits depend on resolution
     bool        ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
    +bool        ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
     bool        ledcWrite(uint8_t pin, uint32_t duty);
     uint32_t    ledcWriteTone(uint8_t pin, uint32_t freq);
     uint32_t    ledcWriteNote(uint8_t pin, note_t note, uint8_t octave);
    diff --git a/docs/source/api/ledc.rst b/docs/source/api/ledc.rst
    index b69f581f5bf..1abeea2f6ee 100644
    --- a/docs/source/api/ledc.rst
    +++ b/docs/source/api/ledc.rst
    @@ -27,6 +27,7 @@ ledcAttach
     **********
     
     This function is used to setup LEDC pin with given frequency and resolution.
    +LEDC channel will be selected automatically.
     
     .. code-block:: arduino
     
    @@ -41,6 +42,25 @@ This function is used to setup LEDC pin with given frequency and resolution.
     This function will return ``true`` if configuration is successful.
     If ``false`` is returned, error occurs and LEDC channel was not configured.
     
    +ledcAttachChannel
    +*****************
    +
    +This function is used to setup LEDC pin with given frequency, resolution and channel.
    +
    +.. code-block:: arduino
    +
    +    bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
    +
    +* ``pin`` select LEDC pin.
    +* ``freq`` select frequency of pwm.
    +* ``resolution`` select resolution for LEDC channel. 
    +* ``channel`` select LEDC channel.
    + 
    +  * range is 1-14 bits (1-20 bits for ESP32).
    +  
    +This function will return ``true`` if configuration is successful.
    +If ``false`` is returned, error occurs and LEDC channel was not configured.
    +
     ledcWrite
     *********
     
    
    From c1a4055869c4062f7336e1310d369dd59af94629 Mon Sep 17 00:00:00 2001
    From: TANAKA Masayuki 
    Date: Mon, 8 Jan 2024 21:59:58 +0900
    Subject: [PATCH 49/70] change(esp32): Added clearing of queue with unTone()
     (#9055)
    
    unTone() does not stop until the queue is exhausted. Therefore, we added clearing the queue.
    ---
     cores/esp32/Tone.cpp | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/cores/esp32/Tone.cpp b/cores/esp32/Tone.cpp
    index 77a254cd1d1..772b2b6bba1 100644
    --- a/cores/esp32/Tone.cpp
    +++ b/cores/esp32/Tone.cpp
    @@ -95,6 +95,7 @@ void noTone(uint8_t pin){
             .frequency = 0, // Ignored
             .duration = 0, // Ignored
           };
    +      xQueueReset(_tone_queue); // clear queue
           xQueueSend(_tone_queue, &tone_msg, portMAX_DELAY);
         }
       }
    
    From 21c710cbfc98a2fe1ec1af20d29d522a29a1ea47 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 12 Jan 2024 11:26:27 +0000
    Subject: [PATCH 50/70] Docs deploy CI updated for production deployment
    
    ---
     .github/workflows/docs.yml                    | 87 -------------------
     .../{docs_legacy.yml => docs_build.yml}       | 23 +++--
     .github/workflows/docs_deploy.yml             | 57 ++----------
     .github/workflows/docs_preview.yml            | 46 ++++++++++
     docs/utils.sh                                 |  2 +-
     5 files changed, 66 insertions(+), 149 deletions(-)
     delete mode 100644 .github/workflows/docs.yml
     rename .github/workflows/{docs_legacy.yml => docs_build.yml} (56%)
     create mode 100644 .github/workflows/docs_preview.yml
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    deleted file mode 100644
    index ec4fff94995..00000000000
    --- a/.github/workflows/docs.yml
    +++ /dev/null
    @@ -1,87 +0,0 @@
    -name: Documentation Build and Deploy CI
    -
    -on:
    -  push:
    -    branches:
    -    - master
    -    - release/*
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs.yml'
    -  pull_request:
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs.yml'
    -
    -jobs:
    -
    -  build-docs:
    -    name: Build ESP-Docs
    -    runs-on: ubuntu-22.04
    -    defaults:
    -      run:
    -        shell: bash
    -    steps:
    -    - uses: actions/checkout@v3
    -      with:
    -        submodules: true
    -    - uses: actions/setup-python@v4
    -      with:
    -        python-version: '3.10'
    -    - name: Build
    -      run: |
    -        sudo apt update
    -        sudo apt install python3-pip python3-setuptools
    -        # GitHub CI installs pip3 and setuptools outside the path.
    -        # Update the path to include them and run.
    -        cd ./docs
    -        PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    -        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en
    -    - name: Archive Docs
    -      uses: actions/upload-artifact@v2
    -      with:
    -        name: docs
    -        path: docs
    -
    -  deploy-preview-docs:
    -    name: Deploy Documentation
    -    runs-on: ubuntu-22.04
    -    defaults:
    -      run:
    -        shell: bash
    -    steps:
    -    - uses: actions/checkout@v2
    -      with:
    -        submodules: true
    -    - uses: actions/setup-python@v2
    -      with:
    -        python-version: '3.10'
    -    - name: Deploy Preview
    -      env:
    -        # Deploy to production server
    -        # DOCS_BUILD_DIR: "./docs/_build/"
    -        # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    -        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }}
    -        # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        # Deploy to preview server
    -        TYPE: "preview"
    -        DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
    -        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    -        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
    -      run: |
    -        sudo apt update
    -        sudo apt install python3-pip python3-setuptools
    -        source ./docs/utils.sh
    -        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    -        export GIT_VER=$(git describe --always)
    -        echo "PIP install requirements..."
    -        pip3 install --user -r ./docs/requirements.txt
    -        echo "Building the Docs..."
    -        cd ./docs && build-docs -l en
    -        echo "Deploy the Docs..."
    -        deploy-docs
    diff --git a/.github/workflows/docs_legacy.yml b/.github/workflows/docs_build.yml
    similarity index 56%
    rename from .github/workflows/docs_legacy.yml
    rename to .github/workflows/docs_build.yml
    index d725f8a1d1b..c18120c079f 100644
    --- a/.github/workflows/docs_legacy.yml
    +++ b/.github/workflows/docs_build.yml
    @@ -1,22 +1,15 @@
    -name: Build the Docs Legacy ReadTheDocs CI
    +name: Documentation Build and Deploy CI
     
     on:
    -  push:
    -    branches:
    -    - master
    -    - release/*
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs_legacy.yml'
       pull_request:
         paths:
         - 'docs/**'
    -    - '.github/workflows/docs_legacy.yml'
    +    - '.github/workflows/docs.yml'
     
     jobs:
     
       build-docs:
    -    name: Build ReadTheDocs
    +    name: Build Documentation
         runs-on: ubuntu-22.04
         defaults:
           run:
    @@ -34,5 +27,11 @@ jobs:
             sudo apt install python3-pip python3-setuptools
             # GitHub CI installs pip3 and setuptools outside the path.
             # Update the path to include them and run.
    -        PATH=/home/runner/.local/bin:$PATH pip3 install --user -r ./docs/requirements.txt
    -        cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" make html
    +        cd ./docs
    +        PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    +        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en
    +    - name: Archive Docs
    +      uses: actions/upload-artifact@v2
    +      with:
    +        name: docs
    +        path: docs
    diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml
    index ec4fff94995..ebc8acac792 100644
    --- a/.github/workflows/docs_deploy.yml
    +++ b/.github/workflows/docs_deploy.yml
    @@ -1,50 +1,17 @@
    -name: Documentation Build and Deploy CI
    +name: Documentation Build and Deploy Production CI
     
     on:
       push:
         branches:
    -    - master
         - release/*
         paths:
         - 'docs/**'
         - '.github/workflows/docs.yml'
    -  pull_request:
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs.yml'
     
     jobs:
     
    -  build-docs:
    -    name: Build ESP-Docs
    -    runs-on: ubuntu-22.04
    -    defaults:
    -      run:
    -        shell: bash
    -    steps:
    -    - uses: actions/checkout@v3
    -      with:
    -        submodules: true
    -    - uses: actions/setup-python@v4
    -      with:
    -        python-version: '3.10'
    -    - name: Build
    -      run: |
    -        sudo apt update
    -        sudo apt install python3-pip python3-setuptools
    -        # GitHub CI installs pip3 and setuptools outside the path.
    -        # Update the path to include them and run.
    -        cd ./docs
    -        PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    -        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en
    -    - name: Archive Docs
    -      uses: actions/upload-artifact@v2
    -      with:
    -        name: docs
    -        path: docs
    -
    -  deploy-preview-docs:
    -    name: Deploy Documentation
    +  deploy-prod-docs:
    +    name: Deploy Documentation Production
         runs-on: ubuntu-22.04
         defaults:
           run:
    @@ -59,20 +26,12 @@ jobs:
         - name: Deploy Preview
           env:
             # Deploy to production server
    -        # DOCS_BUILD_DIR: "./docs/_build/"
    -        # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    -        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }}
    -        # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        # Deploy to preview server
    -        TYPE: "preview"
             DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
    -        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    -        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_PROD_PRIVATEKEY }}
    +        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PROD_PATH }}
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_PROD_SERVER }}
    +        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_PROD_URL_BASE }}
    +        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PROD_USER }}
           run: |
             sudo apt update
             sudo apt install python3-pip python3-setuptools
    diff --git a/.github/workflows/docs_preview.yml b/.github/workflows/docs_preview.yml
    new file mode 100644
    index 00000000000..41de91838b3
    --- /dev/null
    +++ b/.github/workflows/docs_preview.yml
    @@ -0,0 +1,46 @@
    +name: Documentation Build and Deploy CI
    +
    +on:
    +  push:
    +    branches:
    +    - master
    +    paths:
    +    - 'docs/**'
    +    - '.github/workflows/docs.yml'
    +
    +jobs:
    +
    +  deploy-preview-docs:
    +    name: Deploy Documentation Preview
    +    runs-on: ubuntu-22.04
    +    defaults:
    +      run:
    +        shell: bash
    +    steps:
    +    - uses: actions/checkout@v3
    +      with:
    +        submodules: true
    +    - uses: actions/setup-python@v4
    +      with:
    +        python-version: '3.10'
    +    - name: Deploy Preview
    +      env:
    +        # Deploy to preview server
    +        DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    +        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    +        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
    +      run: |
    +        sudo apt update
    +        sudo apt install python3-pip python3-setuptools
    +        source ./docs/utils.sh
    +        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    +        export GIT_VER=$(git describe --always)
    +        echo "PIP install requirements..."
    +        pip3 install --user -r ./docs/requirements.txt
    +        echo "Building the Docs..."
    +        cd ./docs && build-docs -l en
    +        echo "Deploy the Docs..."
    +        deploy-docs
    diff --git a/docs/utils.sh b/docs/utils.sh
    index 91e283d462f..0f9574e57a5 100644
    --- a/docs/utils.sh
    +++ b/docs/utils.sh
    @@ -5,7 +5,7 @@ function add_ssh_keys() {
       mkdir -p ~/.ssh
       chmod 700 ~/.ssh
       echo -n "${key_string}" >~/.ssh/id_rsa_base64
    -  base64 -w 0 ~/.ssh/id_rsa_base64 | base64 -di >~/.ssh/id_rsa
    +  base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 >~/.ssh/id_rsa
       chmod 600 ~/.ssh/id_rsa
     }
     
    
    From e4714fcfc52d0410d206a87e1c22cb199ea9e2d4 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Mon, 18 Sep 2023 19:02:17 +0100
    Subject: [PATCH 51/70] Merge RTD to ESP-Docs
    
    ---
     docs/Makefile                                 |  28 -------
     docs/_static/arduino-esp32_versions.js        |  16 ++++
     docs/{source => }/_static/arduino-ide.png     | Bin
     .../_static/arduino_i2c_master.png            | Bin
     .../_static/arduino_i2c_slave.png             | Bin
     docs/{source => }/_static/cross.png           | Bin
     .../_static/esp32-c3_devkitM-1_pinlayout.png  | Bin
     .../_static/esp32-s2_saola1_pinlayout.png     | Bin
     .../_static/esp32_devkitC_pinlayout.png       | Bin
     .../_static/external_library_test_pr.png      | Bin
     .../external_library_test_schedule.png        | Bin
     docs/{source => }/_static/gpio_output.png     | Bin
     docs/{source => }/_static/gpio_pullup.png     | Bin
     docs/{source => }/_static/green_checkmark.png | Bin
     .../install_guide_boards_manager_esp32.png    | Bin
     .../install_guide_boards_manager_url.png      | Bin
     .../_static/install_guide_preferences.png     | Bin
     docs/{source => }/_static/logo_arduino.png    | Bin
     docs/{source => }/_static/logo_espressif.png  | Bin
     docs/{source => }/_static/logo_linux.png      | Bin
     docs/{source => }/_static/logo_macos.png      | Bin
     docs/{source => }/_static/logo_pio.png        | Bin
     docs/{source => }/_static/logo_windows.png    | Bin
     docs/{source => }/_static/ota_esp32_login.png | Bin
     .../{source => }/_static/ota_esp32_upload.png | Bin
     .../_static/ota_esp32_verbose.png             | Bin
     .../_static/ota_export_to_binary.png          | Bin
     docs/{source => }/_static/soc-module.png      | Bin
     .../tutorials/basic/tutorial_basic_ide.png    | Bin
     .../tutorial_peripheral_diagram.png           | Bin
     docs/{source => }/_static/usb_msc_drive.png   | Bin
     docs/{source => }/_static/warning.png         | Bin
     docs/{source => }/_static/wifi_esp32_ap.png   | Bin
     docs/{source => }/_static/wifi_esp32_sta.png  | Bin
     docs/{source => }/_static/win-gui-1.png       | Bin
     docs/{source => }/_static/win-gui-2.png       | Bin
     docs/{source => }/_static/win-gui-3.png       | Bin
     docs/{source => }/_static/win-gui-4.png       | Bin
     docs/{source => }/_static/win-gui-5.png       | Bin
     .../{source => }/_static/win-gui-update-1.png | Bin
     .../{source => }/_static/win-gui-update-2.png | Bin
     docs/conf_common.py                           |  30 ++++++++
     docs/{source => en}/advanced_utils.rst        |   0
     docs/{source => en}/api/adc.rst               |   0
     docs/{source => en}/api/ble.rst               |   0
     docs/{source => en}/api/bluetooth.rst         |   0
     docs/{source => en}/api/dac.rst               |   0
     docs/{source => en}/api/deepsleep.rst         |   0
     docs/{source => en}/api/espnow.rst            |   0
     docs/{source => en}/api/ethernet.rst          |   0
     docs/{source => en}/api/gpio.rst              |   0
     docs/{source => en}/api/i2c.rst               |   4 +-
     docs/{source => en}/api/i2s.rst               |   0
     docs/{source => en}/api/insights.rst          |   0
     docs/{source => en}/api/ledc.rst              |   0
     docs/{source => en}/api/preferences.rst       |   0
     docs/{source => en}/api/pulse_counter.rst     |   0
     docs/{source => en}/api/rainmaker.rst         |   0
     docs/{source => en}/api/reset_reason.rst      |   0
     docs/{source => en}/api/rmt.rst               |   0
     docs/{source => en}/api/sdio.rst              |   0
     docs/{source => en}/api/sdmmc.rst             |   0
     docs/{source => en}/api/sigmadelta.rst        |   0
     docs/{source => en}/api/spi.rst               |   0
     docs/{source => en}/api/timer.rst             |   0
     docs/{source => en}/api/touch.rst             |   0
     docs/{source => en}/api/usb.rst               |   0
     docs/{source => en}/api/usb_cdc.rst           |   0
     docs/{source => en}/api/usb_msc.rst           |   0
     docs/{source => en}/api/wifi.rst              |  11 ++-
     .../boards/ESP32-C3-DevKitM-1.rst             |   2 +-
     .../{source => en}/boards/ESP32-DevKitC-1.rst |   2 +-
     .../boards/ESP32-S2-Saola-1.rst               |   2 +-
     docs/{source => en}/boards/boards.rst         |   8 +-
     docs/{source => en}/boards/generic.rst        |   0
     docs/{source => en}/common/datasheet.inc      |   0
     docs/en/conf.py                               |  25 ++++++
     docs/{source => en}/contributing.rst          |   0
     docs/{source => en}/esp-idf_component.rst     |   0
     .../external_libraries_test.rst               |  10 +--
     docs/{source => en}/faq.rst                   |   0
     docs/{source => en}/getting_started.rst       |  10 +--
     docs/{source => en}/guides/core_debug.rst     |   0
     .../guides/docs_contributing.rst              |   2 +-
     docs/{source => en}/guides/guides.rst         |   0
     docs/{source => en}/guides/tools_menu.rst     |   2 +-
     docs/{source => en}/index.rst                 |   0
     docs/{source => en}/installing.rst            |  30 ++++----
     docs/{source => en}/lib_builder.rst           |   0
     docs/{source => en}/libraries.rst             |   0
     docs/{source => en}/make.rst                  |   0
     .../migration_guides/2.x_to_3.0.rst           |   0
     .../migration_guides/migration_guides.rst     |   0
     docs/{source => en}/ota_web_update.rst        |   8 +-
     docs/{source => en}/troubleshooting.rst       |   0
     docs/{source => en}/tutorials/basic.rst       |   2 +-
     docs/{source => en}/tutorials/blink.rst       |   0
     .../tutorials/cdc_dfu_flash.rst               |   0
     docs/{source => en}/tutorials/io_mux.rst      |   2 +-
     .../tutorials/partition_table.rst             |   0
     docs/{source => en}/tutorials/preferences.rst |   0
     docs/{source => en}/tutorials/tutorials.rst   |   0
     docs/make.bat                                 |  35 ---------
     docs/requirements.txt                         |   6 +-
     docs/source/conf.py                           |  72 ------------------
     105 files changed, 121 insertions(+), 186 deletions(-)
     delete mode 100644 docs/Makefile
     create mode 100644 docs/_static/arduino-esp32_versions.js
     rename docs/{source => }/_static/arduino-ide.png (100%)
     rename docs/{source => }/_static/arduino_i2c_master.png (100%)
     rename docs/{source => }/_static/arduino_i2c_slave.png (100%)
     rename docs/{source => }/_static/cross.png (100%)
     rename docs/{source => }/_static/esp32-c3_devkitM-1_pinlayout.png (100%)
     rename docs/{source => }/_static/esp32-s2_saola1_pinlayout.png (100%)
     rename docs/{source => }/_static/esp32_devkitC_pinlayout.png (100%)
     rename docs/{source => }/_static/external_library_test_pr.png (100%)
     rename docs/{source => }/_static/external_library_test_schedule.png (100%)
     rename docs/{source => }/_static/gpio_output.png (100%)
     rename docs/{source => }/_static/gpio_pullup.png (100%)
     rename docs/{source => }/_static/green_checkmark.png (100%)
     rename docs/{source => }/_static/install_guide_boards_manager_esp32.png (100%)
     rename docs/{source => }/_static/install_guide_boards_manager_url.png (100%)
     rename docs/{source => }/_static/install_guide_preferences.png (100%)
     rename docs/{source => }/_static/logo_arduino.png (100%)
     rename docs/{source => }/_static/logo_espressif.png (100%)
     rename docs/{source => }/_static/logo_linux.png (100%)
     rename docs/{source => }/_static/logo_macos.png (100%)
     rename docs/{source => }/_static/logo_pio.png (100%)
     rename docs/{source => }/_static/logo_windows.png (100%)
     rename docs/{source => }/_static/ota_esp32_login.png (100%)
     rename docs/{source => }/_static/ota_esp32_upload.png (100%)
     rename docs/{source => }/_static/ota_esp32_verbose.png (100%)
     rename docs/{source => }/_static/ota_export_to_binary.png (100%)
     rename docs/{source => }/_static/soc-module.png (100%)
     rename docs/{source => }/_static/tutorials/basic/tutorial_basic_ide.png (100%)
     rename docs/{source => }/_static/tutorials/peripherals/tutorial_peripheral_diagram.png (100%)
     rename docs/{source => }/_static/usb_msc_drive.png (100%)
     rename docs/{source => }/_static/warning.png (100%)
     rename docs/{source => }/_static/wifi_esp32_ap.png (100%)
     rename docs/{source => }/_static/wifi_esp32_sta.png (100%)
     rename docs/{source => }/_static/win-gui-1.png (100%)
     rename docs/{source => }/_static/win-gui-2.png (100%)
     rename docs/{source => }/_static/win-gui-3.png (100%)
     rename docs/{source => }/_static/win-gui-4.png (100%)
     rename docs/{source => }/_static/win-gui-5.png (100%)
     rename docs/{source => }/_static/win-gui-update-1.png (100%)
     rename docs/{source => }/_static/win-gui-update-2.png (100%)
     create mode 100644 docs/conf_common.py
     rename docs/{source => en}/advanced_utils.rst (100%)
     rename docs/{source => en}/api/adc.rst (100%)
     rename docs/{source => en}/api/ble.rst (100%)
     rename docs/{source => en}/api/bluetooth.rst (100%)
     rename docs/{source => en}/api/dac.rst (100%)
     rename docs/{source => en}/api/deepsleep.rst (100%)
     rename docs/{source => en}/api/espnow.rst (100%)
     rename docs/{source => en}/api/ethernet.rst (100%)
     rename docs/{source => en}/api/gpio.rst (100%)
     rename docs/{source => en}/api/i2c.rst (99%)
     rename docs/{source => en}/api/i2s.rst (100%)
     rename docs/{source => en}/api/insights.rst (100%)
     rename docs/{source => en}/api/ledc.rst (100%)
     rename docs/{source => en}/api/preferences.rst (100%)
     rename docs/{source => en}/api/pulse_counter.rst (100%)
     rename docs/{source => en}/api/rainmaker.rst (100%)
     rename docs/{source => en}/api/reset_reason.rst (100%)
     rename docs/{source => en}/api/rmt.rst (100%)
     rename docs/{source => en}/api/sdio.rst (100%)
     rename docs/{source => en}/api/sdmmc.rst (100%)
     rename docs/{source => en}/api/sigmadelta.rst (100%)
     rename docs/{source => en}/api/spi.rst (100%)
     rename docs/{source => en}/api/timer.rst (100%)
     rename docs/{source => en}/api/touch.rst (100%)
     rename docs/{source => en}/api/usb.rst (100%)
     rename docs/{source => en}/api/usb_cdc.rst (100%)
     rename docs/{source => en}/api/usb_msc.rst (100%)
     rename docs/{source => en}/api/wifi.rst (99%)
     rename docs/{source => en}/boards/ESP32-C3-DevKitM-1.rst (98%)
     rename docs/{source => en}/boards/ESP32-DevKitC-1.rst (98%)
     rename docs/{source => en}/boards/ESP32-S2-Saola-1.rst (98%)
     rename docs/{source => en}/boards/boards.rst (94%)
     rename docs/{source => en}/boards/generic.rst (100%)
     rename docs/{source => en}/common/datasheet.inc (100%)
     create mode 100644 docs/en/conf.py
     rename docs/{source => en}/contributing.rst (100%)
     rename docs/{source => en}/esp-idf_component.rst (100%)
     rename docs/{source => en}/external_libraries_test.rst (94%)
     rename docs/{source => en}/faq.rst (100%)
     rename docs/{source => en}/getting_started.rst (95%)
     rename docs/{source => en}/guides/core_debug.rst (100%)
     rename docs/{source => en}/guides/docs_contributing.rst (99%)
     rename docs/{source => en}/guides/guides.rst (100%)
     rename docs/{source => en}/guides/tools_menu.rst (99%)
     rename docs/{source => en}/index.rst (100%)
     rename docs/{source => en}/installing.rst (95%)
     rename docs/{source => en}/lib_builder.rst (100%)
     rename docs/{source => en}/libraries.rst (100%)
     rename docs/{source => en}/make.rst (100%)
     rename docs/{source => en}/migration_guides/2.x_to_3.0.rst (100%)
     rename docs/{source => en}/migration_guides/migration_guides.rst (100%)
     rename docs/{source => en}/ota_web_update.rst (94%)
     rename docs/{source => en}/troubleshooting.rst (100%)
     rename docs/{source => en}/tutorials/basic.rst (97%)
     rename docs/{source => en}/tutorials/blink.rst (100%)
     rename docs/{source => en}/tutorials/cdc_dfu_flash.rst (100%)
     rename docs/{source => en}/tutorials/io_mux.rst (98%)
     rename docs/{source => en}/tutorials/partition_table.rst (100%)
     rename docs/{source => en}/tutorials/preferences.rst (100%)
     rename docs/{source => en}/tutorials/tutorials.rst (100%)
     delete mode 100644 docs/make.bat
     delete mode 100644 docs/source/conf.py
    
    diff --git a/docs/Makefile b/docs/Makefile
    deleted file mode 100644
    index ba5447de969..00000000000
    --- a/docs/Makefile
    +++ /dev/null
    @@ -1,28 +0,0 @@
    -# Minimal makefile for Sphinx documentation
    -#
    -
    -# You can set these variables from the command line, and also
    -# from the environment for the first two.
    -SPHINXOPTS    ?=
    -SPHINXBUILD   ?= sphinx-build
    -SOURCEDIR     = source
    -BUILDDIR      = build
    -
    -LINKCHECKDIR  = build/linkcheck
    -
    -.PHONY: checklinks
    -	checklinks:
    -	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(LINKCHECKDIR)
    -	@echo
    -	@echo "Check finished. Report is in $(LINKCHECKDIR)."
    -
    -# Put it first so that "make" without argument is like "make help".
    -help:
    -	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
    -
    -.PHONY: help Makefile
    -
    -# Catch-all target: route all unknown targets to Sphinx using the new
    -# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
    -%: Makefile
    -	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
    diff --git a/docs/_static/arduino-esp32_versions.js b/docs/_static/arduino-esp32_versions.js
    new file mode 100644
    index 00000000000..b4b772d5c45
    --- /dev/null
    +++ b/docs/_static/arduino-esp32_versions.js
    @@ -0,0 +1,16 @@
    +var DOCUMENTATION_VERSIONS = {
    +    DEFAULTS: { has_targets: false,
    +                supported_targets: [ "esp32" ]
    +              },
    +    VERSIONS: [
    +      { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    +    ],
    +    IDF_TARGETS: [
    +       { text: "ESP32", value: "esp32"},
    +       { text: "ESP32-S2", value: "esp32s2"},
    +       { text: "ESP32-S3", value: "esp32s3"},
    +       { text: "ESP32-C3", value: "esp32c3"},
    +       { text: "ESP32-H2", value: "esp32h2"},
    +       { text: "ESP32C6", value: "esp32c6"},
    +    ]
    +};
    \ No newline at end of file
    diff --git a/docs/source/_static/arduino-ide.png b/docs/_static/arduino-ide.png
    similarity index 100%
    rename from docs/source/_static/arduino-ide.png
    rename to docs/_static/arduino-ide.png
    diff --git a/docs/source/_static/arduino_i2c_master.png b/docs/_static/arduino_i2c_master.png
    similarity index 100%
    rename from docs/source/_static/arduino_i2c_master.png
    rename to docs/_static/arduino_i2c_master.png
    diff --git a/docs/source/_static/arduino_i2c_slave.png b/docs/_static/arduino_i2c_slave.png
    similarity index 100%
    rename from docs/source/_static/arduino_i2c_slave.png
    rename to docs/_static/arduino_i2c_slave.png
    diff --git a/docs/source/_static/cross.png b/docs/_static/cross.png
    similarity index 100%
    rename from docs/source/_static/cross.png
    rename to docs/_static/cross.png
    diff --git a/docs/source/_static/esp32-c3_devkitM-1_pinlayout.png b/docs/_static/esp32-c3_devkitM-1_pinlayout.png
    similarity index 100%
    rename from docs/source/_static/esp32-c3_devkitM-1_pinlayout.png
    rename to docs/_static/esp32-c3_devkitM-1_pinlayout.png
    diff --git a/docs/source/_static/esp32-s2_saola1_pinlayout.png b/docs/_static/esp32-s2_saola1_pinlayout.png
    similarity index 100%
    rename from docs/source/_static/esp32-s2_saola1_pinlayout.png
    rename to docs/_static/esp32-s2_saola1_pinlayout.png
    diff --git a/docs/source/_static/esp32_devkitC_pinlayout.png b/docs/_static/esp32_devkitC_pinlayout.png
    similarity index 100%
    rename from docs/source/_static/esp32_devkitC_pinlayout.png
    rename to docs/_static/esp32_devkitC_pinlayout.png
    diff --git a/docs/source/_static/external_library_test_pr.png b/docs/_static/external_library_test_pr.png
    similarity index 100%
    rename from docs/source/_static/external_library_test_pr.png
    rename to docs/_static/external_library_test_pr.png
    diff --git a/docs/source/_static/external_library_test_schedule.png b/docs/_static/external_library_test_schedule.png
    similarity index 100%
    rename from docs/source/_static/external_library_test_schedule.png
    rename to docs/_static/external_library_test_schedule.png
    diff --git a/docs/source/_static/gpio_output.png b/docs/_static/gpio_output.png
    similarity index 100%
    rename from docs/source/_static/gpio_output.png
    rename to docs/_static/gpio_output.png
    diff --git a/docs/source/_static/gpio_pullup.png b/docs/_static/gpio_pullup.png
    similarity index 100%
    rename from docs/source/_static/gpio_pullup.png
    rename to docs/_static/gpio_pullup.png
    diff --git a/docs/source/_static/green_checkmark.png b/docs/_static/green_checkmark.png
    similarity index 100%
    rename from docs/source/_static/green_checkmark.png
    rename to docs/_static/green_checkmark.png
    diff --git a/docs/source/_static/install_guide_boards_manager_esp32.png b/docs/_static/install_guide_boards_manager_esp32.png
    similarity index 100%
    rename from docs/source/_static/install_guide_boards_manager_esp32.png
    rename to docs/_static/install_guide_boards_manager_esp32.png
    diff --git a/docs/source/_static/install_guide_boards_manager_url.png b/docs/_static/install_guide_boards_manager_url.png
    similarity index 100%
    rename from docs/source/_static/install_guide_boards_manager_url.png
    rename to docs/_static/install_guide_boards_manager_url.png
    diff --git a/docs/source/_static/install_guide_preferences.png b/docs/_static/install_guide_preferences.png
    similarity index 100%
    rename from docs/source/_static/install_guide_preferences.png
    rename to docs/_static/install_guide_preferences.png
    diff --git a/docs/source/_static/logo_arduino.png b/docs/_static/logo_arduino.png
    similarity index 100%
    rename from docs/source/_static/logo_arduino.png
    rename to docs/_static/logo_arduino.png
    diff --git a/docs/source/_static/logo_espressif.png b/docs/_static/logo_espressif.png
    similarity index 100%
    rename from docs/source/_static/logo_espressif.png
    rename to docs/_static/logo_espressif.png
    diff --git a/docs/source/_static/logo_linux.png b/docs/_static/logo_linux.png
    similarity index 100%
    rename from docs/source/_static/logo_linux.png
    rename to docs/_static/logo_linux.png
    diff --git a/docs/source/_static/logo_macos.png b/docs/_static/logo_macos.png
    similarity index 100%
    rename from docs/source/_static/logo_macos.png
    rename to docs/_static/logo_macos.png
    diff --git a/docs/source/_static/logo_pio.png b/docs/_static/logo_pio.png
    similarity index 100%
    rename from docs/source/_static/logo_pio.png
    rename to docs/_static/logo_pio.png
    diff --git a/docs/source/_static/logo_windows.png b/docs/_static/logo_windows.png
    similarity index 100%
    rename from docs/source/_static/logo_windows.png
    rename to docs/_static/logo_windows.png
    diff --git a/docs/source/_static/ota_esp32_login.png b/docs/_static/ota_esp32_login.png
    similarity index 100%
    rename from docs/source/_static/ota_esp32_login.png
    rename to docs/_static/ota_esp32_login.png
    diff --git a/docs/source/_static/ota_esp32_upload.png b/docs/_static/ota_esp32_upload.png
    similarity index 100%
    rename from docs/source/_static/ota_esp32_upload.png
    rename to docs/_static/ota_esp32_upload.png
    diff --git a/docs/source/_static/ota_esp32_verbose.png b/docs/_static/ota_esp32_verbose.png
    similarity index 100%
    rename from docs/source/_static/ota_esp32_verbose.png
    rename to docs/_static/ota_esp32_verbose.png
    diff --git a/docs/source/_static/ota_export_to_binary.png b/docs/_static/ota_export_to_binary.png
    similarity index 100%
    rename from docs/source/_static/ota_export_to_binary.png
    rename to docs/_static/ota_export_to_binary.png
    diff --git a/docs/source/_static/soc-module.png b/docs/_static/soc-module.png
    similarity index 100%
    rename from docs/source/_static/soc-module.png
    rename to docs/_static/soc-module.png
    diff --git a/docs/source/_static/tutorials/basic/tutorial_basic_ide.png b/docs/_static/tutorials/basic/tutorial_basic_ide.png
    similarity index 100%
    rename from docs/source/_static/tutorials/basic/tutorial_basic_ide.png
    rename to docs/_static/tutorials/basic/tutorial_basic_ide.png
    diff --git a/docs/source/_static/tutorials/peripherals/tutorial_peripheral_diagram.png b/docs/_static/tutorials/peripherals/tutorial_peripheral_diagram.png
    similarity index 100%
    rename from docs/source/_static/tutorials/peripherals/tutorial_peripheral_diagram.png
    rename to docs/_static/tutorials/peripherals/tutorial_peripheral_diagram.png
    diff --git a/docs/source/_static/usb_msc_drive.png b/docs/_static/usb_msc_drive.png
    similarity index 100%
    rename from docs/source/_static/usb_msc_drive.png
    rename to docs/_static/usb_msc_drive.png
    diff --git a/docs/source/_static/warning.png b/docs/_static/warning.png
    similarity index 100%
    rename from docs/source/_static/warning.png
    rename to docs/_static/warning.png
    diff --git a/docs/source/_static/wifi_esp32_ap.png b/docs/_static/wifi_esp32_ap.png
    similarity index 100%
    rename from docs/source/_static/wifi_esp32_ap.png
    rename to docs/_static/wifi_esp32_ap.png
    diff --git a/docs/source/_static/wifi_esp32_sta.png b/docs/_static/wifi_esp32_sta.png
    similarity index 100%
    rename from docs/source/_static/wifi_esp32_sta.png
    rename to docs/_static/wifi_esp32_sta.png
    diff --git a/docs/source/_static/win-gui-1.png b/docs/_static/win-gui-1.png
    similarity index 100%
    rename from docs/source/_static/win-gui-1.png
    rename to docs/_static/win-gui-1.png
    diff --git a/docs/source/_static/win-gui-2.png b/docs/_static/win-gui-2.png
    similarity index 100%
    rename from docs/source/_static/win-gui-2.png
    rename to docs/_static/win-gui-2.png
    diff --git a/docs/source/_static/win-gui-3.png b/docs/_static/win-gui-3.png
    similarity index 100%
    rename from docs/source/_static/win-gui-3.png
    rename to docs/_static/win-gui-3.png
    diff --git a/docs/source/_static/win-gui-4.png b/docs/_static/win-gui-4.png
    similarity index 100%
    rename from docs/source/_static/win-gui-4.png
    rename to docs/_static/win-gui-4.png
    diff --git a/docs/source/_static/win-gui-5.png b/docs/_static/win-gui-5.png
    similarity index 100%
    rename from docs/source/_static/win-gui-5.png
    rename to docs/_static/win-gui-5.png
    diff --git a/docs/source/_static/win-gui-update-1.png b/docs/_static/win-gui-update-1.png
    similarity index 100%
    rename from docs/source/_static/win-gui-update-1.png
    rename to docs/_static/win-gui-update-1.png
    diff --git a/docs/source/_static/win-gui-update-2.png b/docs/_static/win-gui-update-2.png
    similarity index 100%
    rename from docs/source/_static/win-gui-update-2.png
    rename to docs/_static/win-gui-update-2.png
    diff --git a/docs/conf_common.py b/docs/conf_common.py
    new file mode 100644
    index 00000000000..9aab8d4a224
    --- /dev/null
    +++ b/docs/conf_common.py
    @@ -0,0 +1,30 @@
    +# ---------------------------------------------------------------
    +
    +from esp_docs.conf_docs import *  # noqa: F403,F401
    +
    +languages = ["en"]
    +
    +# link roles config
    +github_repo = "espressif/arduino-esp32"
    +
    +# context used by sphinx_idf_theme
    +html_context["github_user"] = "espressif"
    +html_context["github_repo"] = "arduino-esp32"
    +
    +html_static_path = ["../_static"]
    +
    +# Conditional content
    +
    +extensions += ['sphinx_copybutton',
    +               'sphinx_tabs.tabs',
    +               'esp_docs.esp_extensions.dummy_build_system',
    +               ]
    +
    +ESP32_DOCS = [
    +    "index.rst",
    +]
    +
    +# Extra options required by sphinx_idf_theme
    +project_slug = "arduino-esp32"
    +
    +versions_url = "./_static/arduino-esp32_versions.js"
    diff --git a/docs/source/advanced_utils.rst b/docs/en/advanced_utils.rst
    similarity index 100%
    rename from docs/source/advanced_utils.rst
    rename to docs/en/advanced_utils.rst
    diff --git a/docs/source/api/adc.rst b/docs/en/api/adc.rst
    similarity index 100%
    rename from docs/source/api/adc.rst
    rename to docs/en/api/adc.rst
    diff --git a/docs/source/api/ble.rst b/docs/en/api/ble.rst
    similarity index 100%
    rename from docs/source/api/ble.rst
    rename to docs/en/api/ble.rst
    diff --git a/docs/source/api/bluetooth.rst b/docs/en/api/bluetooth.rst
    similarity index 100%
    rename from docs/source/api/bluetooth.rst
    rename to docs/en/api/bluetooth.rst
    diff --git a/docs/source/api/dac.rst b/docs/en/api/dac.rst
    similarity index 100%
    rename from docs/source/api/dac.rst
    rename to docs/en/api/dac.rst
    diff --git a/docs/source/api/deepsleep.rst b/docs/en/api/deepsleep.rst
    similarity index 100%
    rename from docs/source/api/deepsleep.rst
    rename to docs/en/api/deepsleep.rst
    diff --git a/docs/source/api/espnow.rst b/docs/en/api/espnow.rst
    similarity index 100%
    rename from docs/source/api/espnow.rst
    rename to docs/en/api/espnow.rst
    diff --git a/docs/source/api/ethernet.rst b/docs/en/api/ethernet.rst
    similarity index 100%
    rename from docs/source/api/ethernet.rst
    rename to docs/en/api/ethernet.rst
    diff --git a/docs/source/api/gpio.rst b/docs/en/api/gpio.rst
    similarity index 100%
    rename from docs/source/api/gpio.rst
    rename to docs/en/api/gpio.rst
    diff --git a/docs/source/api/i2c.rst b/docs/en/api/i2c.rst
    similarity index 99%
    rename from docs/source/api/i2c.rst
    rename to docs/en/api/i2c.rst
    index f53d8f27a3c..31a07f88046 100644
    --- a/docs/source/api/i2c.rst
    +++ b/docs/en/api/i2c.rst
    @@ -19,7 +19,7 @@ The I2C can be used in two different modes:
     * **I2C Master Mode**
         * In this mode, the ESP32 generates the clock signal and initiates the communication with the slave device.
     
    -.. figure:: ../_static/arduino_i2c_master.png
    +.. figure:: ../../_static/arduino_i2c_master.png
         :align: center
         :width: 720
         :figclass: align-center
    @@ -27,7 +27,7 @@ The I2C can be used in two different modes:
     * **I2C Slave Mode**
         * The slave mode, the clock is generated by the master device and responds to the master if the destination address is the same as the destination.
     
    -.. figure:: ../_static/arduino_i2c_slave.png
    +.. figure:: ../../_static/arduino_i2c_slave.png
         :align: center
         :width: 520
         :figclass: align-center
    diff --git a/docs/source/api/i2s.rst b/docs/en/api/i2s.rst
    similarity index 100%
    rename from docs/source/api/i2s.rst
    rename to docs/en/api/i2s.rst
    diff --git a/docs/source/api/insights.rst b/docs/en/api/insights.rst
    similarity index 100%
    rename from docs/source/api/insights.rst
    rename to docs/en/api/insights.rst
    diff --git a/docs/source/api/ledc.rst b/docs/en/api/ledc.rst
    similarity index 100%
    rename from docs/source/api/ledc.rst
    rename to docs/en/api/ledc.rst
    diff --git a/docs/source/api/preferences.rst b/docs/en/api/preferences.rst
    similarity index 100%
    rename from docs/source/api/preferences.rst
    rename to docs/en/api/preferences.rst
    diff --git a/docs/source/api/pulse_counter.rst b/docs/en/api/pulse_counter.rst
    similarity index 100%
    rename from docs/source/api/pulse_counter.rst
    rename to docs/en/api/pulse_counter.rst
    diff --git a/docs/source/api/rainmaker.rst b/docs/en/api/rainmaker.rst
    similarity index 100%
    rename from docs/source/api/rainmaker.rst
    rename to docs/en/api/rainmaker.rst
    diff --git a/docs/source/api/reset_reason.rst b/docs/en/api/reset_reason.rst
    similarity index 100%
    rename from docs/source/api/reset_reason.rst
    rename to docs/en/api/reset_reason.rst
    diff --git a/docs/source/api/rmt.rst b/docs/en/api/rmt.rst
    similarity index 100%
    rename from docs/source/api/rmt.rst
    rename to docs/en/api/rmt.rst
    diff --git a/docs/source/api/sdio.rst b/docs/en/api/sdio.rst
    similarity index 100%
    rename from docs/source/api/sdio.rst
    rename to docs/en/api/sdio.rst
    diff --git a/docs/source/api/sdmmc.rst b/docs/en/api/sdmmc.rst
    similarity index 100%
    rename from docs/source/api/sdmmc.rst
    rename to docs/en/api/sdmmc.rst
    diff --git a/docs/source/api/sigmadelta.rst b/docs/en/api/sigmadelta.rst
    similarity index 100%
    rename from docs/source/api/sigmadelta.rst
    rename to docs/en/api/sigmadelta.rst
    diff --git a/docs/source/api/spi.rst b/docs/en/api/spi.rst
    similarity index 100%
    rename from docs/source/api/spi.rst
    rename to docs/en/api/spi.rst
    diff --git a/docs/source/api/timer.rst b/docs/en/api/timer.rst
    similarity index 100%
    rename from docs/source/api/timer.rst
    rename to docs/en/api/timer.rst
    diff --git a/docs/source/api/touch.rst b/docs/en/api/touch.rst
    similarity index 100%
    rename from docs/source/api/touch.rst
    rename to docs/en/api/touch.rst
    diff --git a/docs/source/api/usb.rst b/docs/en/api/usb.rst
    similarity index 100%
    rename from docs/source/api/usb.rst
    rename to docs/en/api/usb.rst
    diff --git a/docs/source/api/usb_cdc.rst b/docs/en/api/usb_cdc.rst
    similarity index 100%
    rename from docs/source/api/usb_cdc.rst
    rename to docs/en/api/usb_cdc.rst
    diff --git a/docs/source/api/usb_msc.rst b/docs/en/api/usb_msc.rst
    similarity index 100%
    rename from docs/source/api/usb_msc.rst
    rename to docs/en/api/usb_msc.rst
    diff --git a/docs/source/api/wifi.rst b/docs/en/api/wifi.rst
    similarity index 99%
    rename from docs/source/api/wifi.rst
    rename to docs/en/api/wifi.rst
    index c906cdaa588..745804512b1 100644
    --- a/docs/source/api/wifi.rst
    +++ b/docs/en/api/wifi.rst
    @@ -21,7 +21,7 @@ Working as AP
     In this mode, the ESP32 is configured as an Access Point (AP) and it's capable of receiving incoming connections from other devices (stations) by providing
     a Wi-Fi network.
     
    -.. figure:: ../_static/wifi_esp32_ap.png
    +.. figure:: ../../_static/wifi_esp32_ap.png
         :align: center
         :width: 520
         :figclass: align-center
    @@ -33,7 +33,7 @@ Working as STA
     
     The STA mode is used to connect the ESP32 to a Wi-Fi network, provided by an Access Point.
     
    -.. figure:: ../_static/wifi_esp32_sta.png
    +.. figure:: ../../_static/wifi_esp32_sta.png
         :align: center
         :width: 520
         :figclass: align-center
    @@ -643,7 +643,7 @@ Loads all infos from a scanned wifi in to the ptr parameters.
     
         bool getNetworkInfo(uint8_t networkItem, String &ssid, uint8_t &encryptionType, int32_t &RSSI, uint8_t* &BSSID, int32_t &channel);
     
    -To see how to use the ``WiFiScan``, take a look at the ``WiFiScan.ino`` or ``WiFiScanAsync.ino`` example available.
    +To see how to use the ``WiFiScan``, take a look at the ``WiFiScan.ino`` example available.
     
     Examples
     --------
    @@ -666,10 +666,13 @@ Wi-Fi STA Example
     .. literalinclude:: ../../../libraries/WiFi/examples/WiFiClient/WiFiClient.ino
         :language: arduino
     
    +References
    +----------
    +
     .. _events example:
     
     Wi-Fi Events Example
     ********************
     
     .. literalinclude:: ../../../libraries/WiFi/examples/WiFiClientEvents/WiFiClientEvents.ino
    -    :language: arduino
    +    :language: arduino
    \ No newline at end of file
    diff --git a/docs/source/boards/ESP32-C3-DevKitM-1.rst b/docs/en/boards/ESP32-C3-DevKitM-1.rst
    similarity index 98%
    rename from docs/source/boards/ESP32-C3-DevKitM-1.rst
    rename to docs/en/boards/ESP32-C3-DevKitM-1.rst
    index 7ce9475d69b..11e6e45d55f 100644
    --- a/docs/source/boards/ESP32-C3-DevKitM-1.rst
    +++ b/docs/en/boards/ESP32-C3-DevKitM-1.rst
    @@ -88,7 +88,7 @@ No.  Name  Type [1]_   Function
     Pin Layout
     ----------
     
    -.. figure:: ../_static/esp32-c3_devkitM-1_pinlayout.png
    +.. figure:: ../../_static/esp32-c3_devkitM-1_pinlayout.png
         :align: center
         :width: 600
         :alt: ESP32-C3-DevKitM-1 (click to enlarge)
    diff --git a/docs/source/boards/ESP32-DevKitC-1.rst b/docs/en/boards/ESP32-DevKitC-1.rst
    similarity index 98%
    rename from docs/source/boards/ESP32-DevKitC-1.rst
    rename to docs/en/boards/ESP32-DevKitC-1.rst
    index dcd5632eab7..4bb86696227 100644
    --- a/docs/source/boards/ESP32-DevKitC-1.rst
    +++ b/docs/en/boards/ESP32-DevKitC-1.rst
    @@ -96,7 +96,7 @@ No.  Name  Type   Function
     Pin Layout
     ----------
     
    -.. figure:: ../_static/esp32_devkitC_pinlayout.png
    +.. figure:: ../../_static/esp32_devkitC_pinlayout.png
         :align: center
         :width: 600
         :alt: ESP32-DevKitC-1 (click to enlarge)
    diff --git a/docs/source/boards/ESP32-S2-Saola-1.rst b/docs/en/boards/ESP32-S2-Saola-1.rst
    similarity index 98%
    rename from docs/source/boards/ESP32-S2-Saola-1.rst
    rename to docs/en/boards/ESP32-S2-Saola-1.rst
    index fd41772c234..1a06a6d87bb 100644
    --- a/docs/source/boards/ESP32-S2-Saola-1.rst
    +++ b/docs/en/boards/ESP32-S2-Saola-1.rst
    @@ -100,7 +100,7 @@ No.  Name  Type   Function
     Pin Layout
     ----------
     
    -.. figure:: ../_static/esp32-s2_saola1_pinlayout.png
    +.. figure:: ../../_static/esp32-s2_saola1_pinlayout.png
         :align: center
         :width: 600
         :alt: ESP32-S2-Saola-1 (click to enlarge)
    diff --git a/docs/source/boards/boards.rst b/docs/en/boards/boards.rst
    similarity index 94%
    rename from docs/source/boards/boards.rst
    rename to docs/en/boards/boards.rst
    index 6214fe4ebd1..3730f6ef2d1 100644
    --- a/docs/source/boards/boards.rst
    +++ b/docs/en/boards/boards.rst
    @@ -21,16 +21,16 @@ The ESP32 is divided by family:
     * ESP32-S
         * Wi-Fi only
     * ESP32-C
    -    * Wi-Fi and BLE 5
    +    * Wi-Fi, BLE 5 and IEEE 802.15.4 (Zigbee and Thread) - C6 only
     * ESP32-H
    -    * BLE and IEEE 802.15.4  
    +    * BLE and IEEE 802.15.4 (Zigbee and Thread)
     
     For each family, we have SoC variants with some differentiation. The differences are more about the embedded flash and its size and the number of the cores (dual or single).
     
     The modules use the SoC internally, including the external flash, PSRAM (in some models) and other essential electronic components. Essentially, all
     modules from the same family use the same SoC.
     
    -.. figure:: ../_static/soc-module.png
    +.. figure:: ../../_static/soc-module.png
         :align: center
         :width: 250
         :alt: ESP32 SoC and Module (click to enlarge)
    @@ -60,7 +60,7 @@ Before buying: Keep in mind that for some "must have" features when choosing the
     Espressif
     ---------
     
    -.. figure:: ../_static/logo_espressif.png
    +.. figure:: ../../_static/logo_espressif.png
         :align: center
         :width: 250
         :alt: Espressif Logo
    diff --git a/docs/source/boards/generic.rst b/docs/en/boards/generic.rst
    similarity index 100%
    rename from docs/source/boards/generic.rst
    rename to docs/en/boards/generic.rst
    diff --git a/docs/source/common/datasheet.inc b/docs/en/common/datasheet.inc
    similarity index 100%
    rename from docs/source/common/datasheet.inc
    rename to docs/en/common/datasheet.inc
    diff --git a/docs/en/conf.py b/docs/en/conf.py
    new file mode 100644
    index 00000000000..ea8f20e4604
    --- /dev/null
    +++ b/docs/en/conf.py
    @@ -0,0 +1,25 @@
    +# -*- coding: utf-8 -*-
    +#
    +# English Language RTD & Sphinx config file
    +#
    +# Uses ../conf_common.py for most non-language-specific settings.
    +
    +# Importing conf_common adds all the non-language-specific
    +# parts to this conf module
    +
    +try:
    +    from conf_common import *  # noqa: F403,F401
    +except ImportError:
    +    import os
    +    import sys
    +    sys.path.insert(0, os.path.abspath('../'))
    +    from conf_common import *  # noqa: F403,F401
    +
    +# General information about the project.
    +project = u'Arduino ESP32'
    +copyright = u'2016 - 2023, Espressif Systems (Shanghai) Co., Ltd'
    +pdf_title = u'Arduino ESP32 Documentation Guide'
    +
    +# The language for content autogenerated by Sphinx. Refer to documentation
    +# for a list of supported languages.
    +language = 'en'
    \ No newline at end of file
    diff --git a/docs/source/contributing.rst b/docs/en/contributing.rst
    similarity index 100%
    rename from docs/source/contributing.rst
    rename to docs/en/contributing.rst
    diff --git a/docs/source/esp-idf_component.rst b/docs/en/esp-idf_component.rst
    similarity index 100%
    rename from docs/source/esp-idf_component.rst
    rename to docs/en/esp-idf_component.rst
    diff --git a/docs/source/external_libraries_test.rst b/docs/en/external_libraries_test.rst
    similarity index 94%
    rename from docs/source/external_libraries_test.rst
    rename to docs/en/external_libraries_test.rst
    index 2064987df07..0d7da671cd2 100644
    --- a/docs/source/external_libraries_test.rst
    +++ b/docs/en/external_libraries_test.rst
    @@ -104,7 +104,7 @@ You can check the results in `LIBRARIES_TEST.md`_.
     
     The results file example:
     
    -.. image:: _static/external_library_test_schedule.png
    +.. image:: ../_static/external_library_test_schedule.png
       :width: 600
     
     Pull Request test result
    @@ -113,18 +113,18 @@ Pull Request test result
     If the test run on Pull Request, it will compile all libraries and sketches 2 times (before/after changes in PR) to see, if the PR is breaking/fixing libraries.
     In the table the results are in order ``BEFORE -> AFTER``.
     
    -.. image:: _static/external_library_test_pr.png
    +.. image:: ../_static/external_library_test_pr.png
       :width: 600
     
    -.. |success| image:: _static/green_checkmark.png
    +.. |success| image:: ../_static/green_checkmark.png
        :height: 2ex
        :class: no-scaled-link
     
    -.. |warning| image:: _static/warning.png
    +.. |warning| image:: ../_static/warning.png
        :height: 2ex
        :class: no-scaled-link
     
    -.. |fail| image:: _static/cross.png
    +.. |fail| image:: ../_static/cross.png
        :height: 2ex
        :class: no-scaled-link
     
    diff --git a/docs/source/faq.rst b/docs/en/faq.rst
    similarity index 100%
    rename from docs/source/faq.rst
    rename to docs/en/faq.rst
    diff --git a/docs/source/getting_started.rst b/docs/en/getting_started.rst
    similarity index 95%
    rename from docs/source/getting_started.rst
    rename to docs/en/getting_started.rst
    index 3540dda685b..9267f1b427f 100644
    --- a/docs/source/getting_started.rst
    +++ b/docs/en/getting_started.rst
    @@ -64,9 +64,9 @@ Supported Operating Systems
     | Windows           | Linux             | macOS             |
     +-------------------+-------------------+-------------------+
     
    -.. |windows-logo| image:: _static/logo_windows.png
    -.. |linux-logo| image:: _static/logo_linux.png
    -.. |macos-logo| image:: _static/logo_macos.png
    +.. |windows-logo| image:: ../_static/logo_windows.png
    +.. |linux-logo| image:: ../_static/logo_linux.png
    +.. |macos-logo| image:: ../_static/logo_macos.png
     
     Supported IDEs
     ---------------------------
    @@ -79,8 +79,8 @@ Here is the list of supported IDE for Arduino ESP32 support integration.
     | Arduino IDE       | PlatformIO        |
     +-------------------+-------------------+
     
    -.. |arduino-logo| image:: _static/logo_arduino.png
    -.. |pio-logo| image:: _static/logo_pio.png
    +.. |arduino-logo| image:: ../_static/logo_arduino.png
    +.. |pio-logo| image:: ../_static/logo_pio.png
     
     See `Installing Guides `_ for more details on how to install the Arduino ESP32 support.
     
    diff --git a/docs/source/guides/core_debug.rst b/docs/en/guides/core_debug.rst
    similarity index 100%
    rename from docs/source/guides/core_debug.rst
    rename to docs/en/guides/core_debug.rst
    diff --git a/docs/source/guides/docs_contributing.rst b/docs/en/guides/docs_contributing.rst
    similarity index 99%
    rename from docs/source/guides/docs_contributing.rst
    rename to docs/en/guides/docs_contributing.rst
    index be5a54e4b37..3bcffc0bf74 100644
    --- a/docs/source/guides/docs_contributing.rst
    +++ b/docs/en/guides/docs_contributing.rst
    @@ -309,7 +309,7 @@ After that, you can use the following structure to include the image in the docs
     
     .. code-block::
     
    -    .. figure:: ../_static/arduino_i2c_master.png
    +    .. figure:: ../../_static/arduino_i2c_master.png
             :align: center
             :width: 720
             :figclass: align-center
    diff --git a/docs/source/guides/guides.rst b/docs/en/guides/guides.rst
    similarity index 100%
    rename from docs/source/guides/guides.rst
    rename to docs/en/guides/guides.rst
    diff --git a/docs/source/guides/tools_menu.rst b/docs/en/guides/tools_menu.rst
    similarity index 99%
    rename from docs/source/guides/tools_menu.rst
    rename to docs/en/guides/tools_menu.rst
    index 96b3bcc3674..41c8878bf6e 100644
    --- a/docs/source/guides/tools_menu.rst
    +++ b/docs/en/guides/tools_menu.rst
    @@ -240,7 +240,7 @@ The USB Mass Storage Class, or USB MSC, is a class used for storage devices, lik
     This option can be used to ``Enable`` or ``Disable`` this function at the boot. If this option is ``Enabled``, once the device is connected via USB, one new storage device will appear in the system as a storage drive.
     Use this new storage drive to write and read files or to drop a new firmware binary to flash the device.
     
    -.. figure:: ../_static/usb_msc_drive.png
    +.. figure:: ../../_static/usb_msc_drive.png
         :align: center
         :width: 720
         :figclass: align-center
    diff --git a/docs/source/index.rst b/docs/en/index.rst
    similarity index 100%
    rename from docs/source/index.rst
    rename to docs/en/index.rst
    diff --git a/docs/source/installing.rst b/docs/en/installing.rst
    similarity index 95%
    rename from docs/source/installing.rst
    rename to docs/en/installing.rst
    index 3b5554baf32..3acabd5505c 100644
    --- a/docs/source/installing.rst
    +++ b/docs/en/installing.rst
    @@ -13,7 +13,7 @@ To install Arduino-ESP32 support, you can use one of the following options.
     Installing using Arduino IDE
     ----------------------------
     
    -.. figure:: _static/logo_arduino.png
    +.. figure:: ../_static/logo_arduino.png
        :align: center
        :width: 200
        :figclass: align-center
    @@ -42,21 +42,21 @@ To start the installation process using the Boards Managaer, follow these steps:
     
     -  Start Arduino and open the Preferences window.
     
    -.. figure:: _static/install_guide_preferences.png
    +.. figure:: ../_static/install_guide_preferences.png
        :align: center
        :width: 600
        :figclass: align-center
     
     -  Enter one of the release links above into *Additional Board Manager URLs* field. You can add multiple URLs, separating them with commas.
     
    -.. figure:: _static/install_guide_boards_manager_url.png
    +.. figure:: ../_static/install_guide_boards_manager_url.png
        :align: center
        :width: 600
        :figclass: align-center
     
     -  Open Boards Manager from Tools > Board menu and install *esp32* platform (and do not forget to select your ESP32 board from Tools > Board menu after installation).
     
    -.. figure:: _static/install_guide_boards_manager_esp32.png
    +.. figure:: ../_static/install_guide_boards_manager_esp32.png
        :align: center
        :width: 600
        :figclass: align-center
    @@ -66,7 +66,7 @@ To start the installation process using the Boards Managaer, follow these steps:
     Installing using PlatformIO
     ---------------------------
     
    -.. figure:: _static/logo_pio.png
    +.. figure:: ../_static/logo_pio.png
        :align: center
        :width: 200
        :figclass: align-center
    @@ -166,7 +166,7 @@ Steps to install Arduino ESP32 support on Windows:
     
     - Select ``Clone Existing Repository``
     
    -.. figure:: _static/win-gui-1.png
    +.. figure:: ../_static/win-gui-1.png
        :align: center
        :width: 600
        :figclass: align-center
    @@ -179,13 +179,13 @@ Steps to install Arduino ESP32 support on Windows:
     
     **Step 2**
     
    -.. figure:: _static/win-gui-2.png
    +.. figure:: ../_static/win-gui-2.png
        :align: center
        :figclass: align-center
     
     **Step 3**
     
    -.. figure:: _static/win-gui-3.png
    +.. figure:: ../_static/win-gui-3.png
        :align: center
        :figclass: align-center
     
    @@ -194,7 +194,7 @@ Steps to install Arduino ESP32 support on Windows:
     
     **Step 4**
     
    -.. figure:: _static/win-gui-4.png
    +.. figure:: ../_static/win-gui-4.png
        :align: center
        :figclass: align-center
     
    @@ -202,7 +202,7 @@ Steps to install Arduino ESP32 support on Windows:
     
     **Step 5**
     
    -.. figure:: _static/win-gui-5.png
    +.. figure:: ../_static/win-gui-5.png
        :align: center
        :figclass: align-center
     
    @@ -212,7 +212,7 @@ Steps to install Arduino ESP32 support on Windows:
     4. Select the COM port that the board is attached to
     5. Compile and upload (You might need to hold the boot button while uploading)
     
    -.. figure:: _static/arduino-ide.png
    +.. figure:: ../_static/arduino-ide.png
        :align: center
        :figclass: align-center
     
    @@ -221,27 +221,27 @@ How to update to the latest code
     
     1. Start ``Git GUI`` and you should see the repository under ``Open Recent Repository``. Click on it!
     
    -.. figure:: _static/win-gui-update-1.png
    +.. figure:: ../_static/win-gui-update-1.png
        :align: center
        :figclass: align-center
     
     1. From menu ``Remote`` select ``Fetch from`` > ``origin``
     
    -.. figure:: _static/win-gui-update-2.png
    +.. figure:: ../_static/win-gui-update-2.png
        :align: center
        :figclass: align-center
     
     1. Wait for git to pull any changes and close ``Git GUI``
     2. Open ``[ARDUINO_SKETCHBOOK_DIR]/hardware/espressif/esp32/tools`` and double-click ``get.exe``
     
    -.. figure:: _static/win-gui-4.png
    +.. figure:: ../_static/win-gui-4.png
        :align: center
        :figclass: align-center
     
     Linux
     -----
     
    -.. figure:: _static/logo_linux.png
    +.. figure:: ../_static/logo_linux.png
        :align: center
        :width: 200
        :figclass: align-center
    diff --git a/docs/source/lib_builder.rst b/docs/en/lib_builder.rst
    similarity index 100%
    rename from docs/source/lib_builder.rst
    rename to docs/en/lib_builder.rst
    diff --git a/docs/source/libraries.rst b/docs/en/libraries.rst
    similarity index 100%
    rename from docs/source/libraries.rst
    rename to docs/en/libraries.rst
    diff --git a/docs/source/make.rst b/docs/en/make.rst
    similarity index 100%
    rename from docs/source/make.rst
    rename to docs/en/make.rst
    diff --git a/docs/source/migration_guides/2.x_to_3.0.rst b/docs/en/migration_guides/2.x_to_3.0.rst
    similarity index 100%
    rename from docs/source/migration_guides/2.x_to_3.0.rst
    rename to docs/en/migration_guides/2.x_to_3.0.rst
    diff --git a/docs/source/migration_guides/migration_guides.rst b/docs/en/migration_guides/migration_guides.rst
    similarity index 100%
    rename from docs/source/migration_guides/migration_guides.rst
    rename to docs/en/migration_guides/migration_guides.rst
    diff --git a/docs/source/ota_web_update.rst b/docs/en/ota_web_update.rst
    similarity index 94%
    rename from docs/source/ota_web_update.rst
    rename to docs/en/ota_web_update.rst
    index be0a6d5e263..179101770a0 100644
    --- a/docs/source/ota_web_update.rst
    +++ b/docs/en/ota_web_update.rst
    @@ -39,14 +39,14 @@ Prepare the sketch and configuration for initial upload with a serial port
     - Update ssid and pass in the sketch so the module can join your Wi-Fi network
     - Open File > Preferences, look for “Show verbose output during:” and check out “compilation” option
     
    -.. figure:: _static/ota_esp32_verbose.png
    +.. figure:: ../_static/ota_esp32_verbose.png
         :align: center
         :figclass: align-center
     
     - Upload sketch (Ctrl+U)
     - Now open web browser and enter the url, i.e. http://esp32.local. Once entered, browser should display a form
     
    -.. figure:: _static/ota_esp32_login.png
    +.. figure:: ../_static/ota_esp32_login.png
         :align: center
         :figclass: align-center
     
    @@ -59,7 +59,7 @@ Prepare the sketch and configuration for initial upload with a serial port
     
     Now click on the Login button and browser will display an upload form
     
    -.. figure:: _static/ota_esp32_upload.png
    +.. figure:: ../_static/ota_esp32_upload.png
         :align: center
         :figclass: align-center
     
    @@ -70,7 +70,7 @@ Exporting Binary file of the Firmware (Code)
     - Open up the Code, for Exporting up Binary file
     - Now go to Sketch > export compiled Binary
     
    -.. figure:: _static/ota_export_to_binary.png
    +.. figure:: ../_static/ota_export_to_binary.png
         :align: center
         :figclass: align-center
     
    diff --git a/docs/source/troubleshooting.rst b/docs/en/troubleshooting.rst
    similarity index 100%
    rename from docs/source/troubleshooting.rst
    rename to docs/en/troubleshooting.rst
    diff --git a/docs/source/tutorials/basic.rst b/docs/en/tutorials/basic.rst
    similarity index 97%
    rename from docs/source/tutorials/basic.rst
    rename to docs/en/tutorials/basic.rst
    index 34d9a05c094..8b932987bea 100644
    --- a/docs/source/tutorials/basic.rst
    +++ b/docs/en/tutorials/basic.rst
    @@ -21,7 +21,7 @@ Here are the steps for this tutorial.
     
     1. Open the Arduino IDE
     
    -.. figure:: ../_static/tutorials/basic/tutorial_basic_ide.png
    +.. figure:: ../../_static/tutorials/basic/tutorial_basic_ide.png
         :align: center
         :width: 600
         :alt: Arduino IDE (click to enlarge)
    diff --git a/docs/source/tutorials/blink.rst b/docs/en/tutorials/blink.rst
    similarity index 100%
    rename from docs/source/tutorials/blink.rst
    rename to docs/en/tutorials/blink.rst
    diff --git a/docs/source/tutorials/cdc_dfu_flash.rst b/docs/en/tutorials/cdc_dfu_flash.rst
    similarity index 100%
    rename from docs/source/tutorials/cdc_dfu_flash.rst
    rename to docs/en/tutorials/cdc_dfu_flash.rst
    diff --git a/docs/source/tutorials/io_mux.rst b/docs/en/tutorials/io_mux.rst
    similarity index 98%
    rename from docs/source/tutorials/io_mux.rst
    rename to docs/en/tutorials/io_mux.rst
    index 12e0a9c9c0c..03b10449013 100644
    --- a/docs/source/tutorials/io_mux.rst
    +++ b/docs/en/tutorials/io_mux.rst
    @@ -23,7 +23,7 @@ GPIO Matrix and Pin Mux
     The ESP32 architecture includes the capability of configuring some peripherals to any of the GPIOs pins, managed by the `IO MUX GPIO`_.
     Essentially, this capability means that we can route the internal peripheral into a different physical pin using the IO MUX and the GPIO Matrix.
     
    -.. figure:: ../_static/tutorials/peripherals/tutorial_peripheral_diagram.png
    +.. figure:: ../../_static/tutorials/peripherals/tutorial_peripheral_diagram.png
         :align: center
         :width: 600
         :figclass: align-center
    diff --git a/docs/source/tutorials/partition_table.rst b/docs/en/tutorials/partition_table.rst
    similarity index 100%
    rename from docs/source/tutorials/partition_table.rst
    rename to docs/en/tutorials/partition_table.rst
    diff --git a/docs/source/tutorials/preferences.rst b/docs/en/tutorials/preferences.rst
    similarity index 100%
    rename from docs/source/tutorials/preferences.rst
    rename to docs/en/tutorials/preferences.rst
    diff --git a/docs/source/tutorials/tutorials.rst b/docs/en/tutorials/tutorials.rst
    similarity index 100%
    rename from docs/source/tutorials/tutorials.rst
    rename to docs/en/tutorials/tutorials.rst
    diff --git a/docs/make.bat b/docs/make.bat
    deleted file mode 100644
    index 6247f7e2317..00000000000
    --- a/docs/make.bat
    +++ /dev/null
    @@ -1,35 +0,0 @@
    -@ECHO OFF
    -
    -pushd %~dp0
    -
    -REM Command file for Sphinx documentation
    -
    -if "%SPHINXBUILD%" == "" (
    -	set SPHINXBUILD=sphinx-build
    -)
    -set SOURCEDIR=source
    -set BUILDDIR=build
    -
    -if "%1" == "" goto help
    -
    -%SPHINXBUILD% >NUL 2>NUL
    -if errorlevel 9009 (
    -	echo.
    -	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
    -	echo.installed, then set the SPHINXBUILD environment variable to point
    -	echo.to the full path of the 'sphinx-build' executable. Alternatively you
    -	echo.may add the Sphinx directory to PATH.
    -	echo.
    -	echo.If you don't have Sphinx installed, grab it from
    -	echo.http://sphinx-doc.org/
    -	exit /b 1
    -)
    -
    -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
    -goto end
    -
    -:help
    -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
    -
    -:end
    -popd
    diff --git a/docs/requirements.txt b/docs/requirements.txt
    index da0d937707a..97b552574b9 100644
    --- a/docs/requirements.txt
    +++ b/docs/requirements.txt
    @@ -1,7 +1,3 @@
    -# This is a list of python packages used to generate documentation. This file is used with pip:
    -# pip install --user -r requirements.txt
    -#
    -# matplotlib is currently required only by the script generate_chart.py
    -sphinx-rtd-theme
    +esp-docs==1.4.*
     sphinx-copybutton==0.5.0
     sphinx-tabs==3.2.0
    \ No newline at end of file
    diff --git a/docs/source/conf.py b/docs/source/conf.py
    deleted file mode 100644
    index 47b30b912ed..00000000000
    --- a/docs/source/conf.py
    +++ /dev/null
    @@ -1,72 +0,0 @@
    -# Configuration file for the Sphinx documentation builder.
    -#
    -# This file only contains a selection of the most common options. For a full
    -# list see the documentation:
    -# https://www.sphinx-doc.org/en/master/usage/configuration.html
    -
    -# -- Path setup --------------------------------------------------------------
    -
    -# If extensions (or modules to document with autodoc) are in another directory,
    -# add these directories to sys.path here. If the directory is relative to the
    -# documentation root, use os.path.abspath to make it absolute, like shown here.
    -#
    -import os
    -import sys
    -# sys.path.insert(0, os.path.abspath('.'))
    -
    -
    -# -- Project information -----------------------------------------------------
    -
    -project = 'Arduino-ESP32'
    -copyright = '2023, Espressif'
    -author = 'Espressif'
    -
    -# The full version, including alpha/beta/rc tags
    -release = '2.0.14'
    -
    -# -- General configuration ---------------------------------------------------
    -
    -# Add any Sphinx extension module names here, as strings. They can be
    -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
    -# ones.
    -extensions = [
    -    'sphinx_rtd_theme',
    -    'sphinx_copybutton',
    -    'sphinx_tabs.tabs'
    -]
    -
    -# Add any paths that contain templates here, relative to this directory.
    -templates_path = ['_templates']
    -
    -# The suffix(es) of source filenames.
    -# You can specify multiple suffix as a list of string:
    -#
    -# source_suffix = ['.rst', '.md']
    -source_suffix = '.rst'
    -
    -# The master toctree document.
    -master_doc = 'index'
    -
    -# List of patterns, relative to source directory, that match files and
    -# directories to ignore when looking for source files.
    -# This pattern also affects html_static_path and html_extra_path.
    -exclude_patterns = []
    -
    -# -- Options for HTML output -------------------------------------------------
    -
    -# The theme to use for HTML and HTML Help pages.  See the documentation for
    -# a list of builtin themes.
    -#
    -html_theme = "sphinx_rtd_theme"
    -html_logo = '_static/logo_espressif.png'
    -
    -# Add any paths that contain custom static files (such as style sheets) here,
    -# relative to this directory. They are copied after the builtin static files,
    -# so a file named "default.css" will overwrite the builtin "default.css".
    -html_static_path = ['_static']
    -
    -# Style
    -# pygments_style = "sphinx"
    -
    -# Tracking ID for Google Analytics
    -google_analytics_id = 'G-F58JM78930'
    
    From 83e491655d74524b55590d5a51a44d668b1c9595 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Tue, 19 Sep 2023 08:40:05 +0100
    Subject: [PATCH 52/70] Documentation CI build changed to ESP-Docs
    
    ---
     .github/workflows/docs.yml | 4 ++--
     .readthedocs.yaml          | 2 +-
     docs/conf_common.py        | 2 ++
     3 files changed, 5 insertions(+), 3 deletions(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index f01fb76ee84..de383bce1bb 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -34,5 +34,5 @@ jobs:
             sudo apt install python3-pip python3-setuptools
             # GitHub CI installs pip3 and setuptools outside the path.
             # Update the path to include them and run.
    -        PATH=/home/runner/.local/bin:$PATH pip3 install --user -r ./docs/requirements.txt
    -        cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" make html
    +        PATH=/home/runner/.local/bin:$PATH pip install -r requirements.txt --prefer-binary
    +        cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32
    diff --git a/.readthedocs.yaml b/.readthedocs.yaml
    index 115b9e7122a..729ffebbf2c 100644
    --- a/.readthedocs.yaml
    +++ b/.readthedocs.yaml
    @@ -13,7 +13,7 @@ build:
     
     # Build documentation in the docs/ directory with Sphinx
     sphinx:
    -  configuration: docs/source/conf.py
    +  configuration: docs/en/conf.py
     
     python:
       install:
    diff --git a/docs/conf_common.py b/docs/conf_common.py
    index 9aab8d4a224..705659a3b66 100644
    --- a/docs/conf_common.py
    +++ b/docs/conf_common.py
    @@ -4,6 +4,8 @@
     
     languages = ["en"]
     
    +
    +
     # link roles config
     github_repo = "espressif/arduino-esp32"
     
    
    From ddcc9c659889bc79483eb306848dd27b346c852f Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Tue, 19 Sep 2023 08:44:13 +0100
    Subject: [PATCH 53/70] Fix documentation CI build
    
    ---
     .github/workflows/docs.yml | 5 +++--
     1 file changed, 3 insertions(+), 2 deletions(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index de383bce1bb..f99568aa764 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -34,5 +34,6 @@ jobs:
             sudo apt install python3-pip python3-setuptools
             # GitHub CI installs pip3 and setuptools outside the path.
             # Update the path to include them and run.
    -        PATH=/home/runner/.local/bin:$PATH pip install -r requirements.txt --prefer-binary
    -        cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32
    +        cd ./docs
    +        PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    +        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32
    
    From 0d61c1abd049f2a094f79258a844004c1c9af49f Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Tue, 19 Sep 2023 09:01:24 +0100
    Subject: [PATCH 54/70] Docs build target fix
    
    ---
     .github/workflows/docs.yml | 4 ++--
     docs/conf_common.py        | 9 ++++++++-
     2 files changed, 10 insertions(+), 3 deletions(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index f99568aa764..705005e6b6d 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -1,4 +1,4 @@
    -name: ReadTheDocs CI
    +name: Documentation Build CI
     
     on:
       push:
    @@ -16,7 +16,7 @@ on:
     jobs:
     
       build-docs:
    -    name: Build ReadTheDocs
    +    name: Build ESP-Docs
         runs-on: ubuntu-22.04
         defaults:
           run:
    diff --git a/docs/conf_common.py b/docs/conf_common.py
    index 705659a3b66..8522c680667 100644
    --- a/docs/conf_common.py
    +++ b/docs/conf_common.py
    @@ -4,7 +4,14 @@
     
     languages = ["en"]
     
    -
    +idf_targets = [
    +    "esp32",
    +    "esp32s2",
    +    "esp32s3",
    +    "esp32c3",
    +    "esp32c6",
    +    "esp32h2",
    +]
     
     # link roles config
     github_repo = "espressif/arduino-esp32"
    
    From a3d05da71b40eb2800706a2c012172e6d3b1ee96 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Tue, 19 Sep 2023 15:22:44 +0100
    Subject: [PATCH 55/70] Added CI for deploy docs
    
    ---
     .github/workflows/docs.yml                    |  2 +-
     .github/workflows/docs_deploy.yml             | 46 +++++++++++++++++++
     ...uino-esp32_versions.js => docs_version.js} |  2 +-
     docs/conf_common.py                           | 21 ++++++++-
     docs/en/conf.py                               |  5 +-
     docs/en/index.rst                             |  2 +-
     docs/utils.sh                                 | 18 ++++++++
     7 files changed, 91 insertions(+), 5 deletions(-)
     create mode 100644 .github/workflows/docs_deploy.yml
     rename docs/_static/{arduino-esp32_versions.js => docs_version.js} (92%)
     create mode 100644 docs/utils.sh
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index 705005e6b6d..98699a680cd 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -36,4 +36,4 @@ jobs:
             # Update the path to include them and run.
             cd ./docs
             PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    -        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32
    +        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 esp32s2 esp32s3 esp32c6 esp32h2
    diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml
    new file mode 100644
    index 00000000000..282ab264a16
    --- /dev/null
    +++ b/.github/workflows/docs_deploy.yml
    @@ -0,0 +1,46 @@
    +name: Documentation Deploy CI
    +
    +on:
    +  workflow_dispatch:
    +  release:
    +    types:
    +      - created
    +    paths:
    +    - 'docs/**'
    +    - '.github/workflows/docs_deploy.yml'
    +
    +jobs:
    +
    +  deploy-preview-docs:
    +    name: Deploy Documentation
    +    runs-on: ubuntu-22.04
    +    defaults:
    +      run:
    +        shell: bash
    +    steps:
    +    - uses: actions/checkout@v2
    +      with:
    +        submodules: true
    +    - uses: actions/setup-python@v2
    +      with:
    +        python-version: '3.10'
    +    - name: Deploy Preview
    +      env:
    +        DOCS_BUILD_DIR: "./docs/_build/"
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_PREV_SERVER }}
    +        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PREV_SERVER_USER }}
    +        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PREV_PATH }}
    +        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_PREV_URL }}
    +      run: |
    +        sudo apt update
    +        sudo apt install python3-pip python3-setuptools
    +        source ./docs/utils.sh
    +        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    +        export GIT_VER=$(git describe --always)
    +        echo "PIP install requirements..."
    +        pip3 install --user -r ./docs/requirements.txt
    +        echo "Building the Docs..."
    +        cd ./docs && build-docs -l en
    +        echo "Deploy the Docs..."
    +        deploy-docs
    diff --git a/docs/_static/arduino-esp32_versions.js b/docs/_static/docs_version.js
    similarity index 92%
    rename from docs/_static/arduino-esp32_versions.js
    rename to docs/_static/docs_version.js
    index b4b772d5c45..a511b79dc31 100644
    --- a/docs/_static/arduino-esp32_versions.js
    +++ b/docs/_static/docs_version.js
    @@ -11,6 +11,6 @@ var DOCUMENTATION_VERSIONS = {
            { text: "ESP32-S3", value: "esp32s3"},
            { text: "ESP32-C3", value: "esp32c3"},
            { text: "ESP32-H2", value: "esp32h2"},
    -       { text: "ESP32C6", value: "esp32c6"},
    +       { text: "ESP32-C6", value: "esp32c6"},
         ]
     };
    \ No newline at end of file
    diff --git a/docs/conf_common.py b/docs/conf_common.py
    index 8522c680667..3320558c3db 100644
    --- a/docs/conf_common.py
    +++ b/docs/conf_common.py
    @@ -33,7 +33,26 @@
         "index.rst",
     ]
     
    +ESP32S2_DOCS = ESP32_DOCS
    +
    +ESP32C3_DOCS = ESP32S2_DOCS
    +
    +ESP32S3_DOCS = ESP32S2_DOCS
    +
    +ESP32C6_DOCS = ESP32S2_DOCS
    +
    +ESP32H2_DOCS = ESP32S2_DOCS
    +
    +conditional_include_dict = {
    +    "esp32": ESP32_DOCS,
    +    "esp32s2": ESP32S2_DOCS,
    +    "esp32c3": ESP32C3_DOCS,
    +    "esp32s3": ESP32S3_DOCS,
    +    "esp32c6": ESP32C6_DOCS,
    +    "esp32h2": ESP32H2_DOCS,
    +}
    +
     # Extra options required by sphinx_idf_theme
     project_slug = "arduino-esp32"
     
    -versions_url = "./_static/arduino-esp32_versions.js"
    +versions_url = "./_static/docs_version.js"
    diff --git a/docs/en/conf.py b/docs/en/conf.py
    index ea8f20e4604..4d1208f256a 100644
    --- a/docs/en/conf.py
    +++ b/docs/en/conf.py
    @@ -15,9 +15,12 @@
         sys.path.insert(0, os.path.abspath('../'))
         from conf_common import *  # noqa: F403,F401
     
    +import datetime
    +current_year = datetime.datetime.now().year
    +
     # General information about the project.
     project = u'Arduino ESP32'
    -copyright = u'2016 - 2023, Espressif Systems (Shanghai) Co., Ltd'
    +copyright = u'2016 - {}, Espressif Systems (Shanghai) Co., Ltd'.format(current_year)
     pdf_title = u'Arduino ESP32 Documentation Guide'
     
     # The language for content autogenerated by Sphinx. Refer to documentation
    diff --git a/docs/en/index.rst b/docs/en/index.rst
    index 4016f36f3d4..9a54af0a198 100644
    --- a/docs/en/index.rst
    +++ b/docs/en/index.rst
    @@ -1,5 +1,5 @@
     #############################################
    -Welcome to ESP32 Arduino Core's documentation
    +Welcome to {IDF_TARGET_NAME} Arduino Core's documentation
     #############################################
     
     Here you will find all the relevant information about the project.
    diff --git a/docs/utils.sh b/docs/utils.sh
    new file mode 100644
    index 00000000000..91e283d462f
    --- /dev/null
    +++ b/docs/utils.sh
    @@ -0,0 +1,18 @@
    +# Bash helper functions for adding SSH keys
    +
    +function add_ssh_keys() {
    +  local key_string="${1}"
    +  mkdir -p ~/.ssh
    +  chmod 700 ~/.ssh
    +  echo -n "${key_string}" >~/.ssh/id_rsa_base64
    +  base64 -w 0 ~/.ssh/id_rsa_base64 | base64 -di >~/.ssh/id_rsa
    +  chmod 600 ~/.ssh/id_rsa
    +}
    +
    +function add_doc_server_ssh_keys() {
    +  local key_string="${1}"
    +  local server_url="${2}"
    +  local server_user="${3}"
    +  add_ssh_keys "${key_string}"
    +  echo -e "Host ${server_url}\n\tStrictHostKeyChecking no\n\tUser ${server_user}\n" >>~/.ssh/config
    +}
    \ No newline at end of file
    
    From 43e057b738f0b1546013c44370fc86dcc5c311ef Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Tue, 19 Sep 2023 15:32:33 +0100
    Subject: [PATCH 56/70] Fix index.rst title wirh ESP target
    
    ---
     docs/en/index.rst | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/docs/en/index.rst b/docs/en/index.rst
    index 9a54af0a198..f7f93f9cc92 100644
    --- a/docs/en/index.rst
    +++ b/docs/en/index.rst
    @@ -1,8 +1,8 @@
    -#############################################
    -Welcome to {IDF_TARGET_NAME} Arduino Core's documentation
    -#############################################
    +#######################################
    +Welcome to Arduino Core's documentation
    +#######################################
     
    -Here you will find all the relevant information about the project.
    +Here you will find all the relevant information about the project based on the {IDF_TARGET_NAME}.
     
     .. note::
        This is a work in progress documentation and we will appreciate your help! We are looking for contributors! 
    
    From ee5a6261859a7f434d04a8f35d5b18e07d00e4d8 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Mon, 25 Sep 2023 15:03:59 +0100
    Subject: [PATCH 57/70] Added versions to JS file and docs as artifact
    
    ---
     .github/workflows/docs.yml       |  5 +++++
     docs/_static/arduino_versions.js | 28 ++++++++++++++++++++++++++++
     docs/_static/docs_version.js     | 16 ----------------
     docs/conf_common.py              |  6 +-----
     4 files changed, 34 insertions(+), 21 deletions(-)
     create mode 100644 docs/_static/arduino_versions.js
     delete mode 100644 docs/_static/docs_version.js
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index 98699a680cd..e0dc48653ef 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -37,3 +37,8 @@ jobs:
             cd ./docs
             PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
             PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 esp32s2 esp32s3 esp32c6 esp32h2
    +    - name: Archive Docs
    +      uses: actions/upload-artifact@v2
    +      with:
    +        name: docs
    +        path: docs
    \ No newline at end of file
    diff --git a/docs/_static/arduino_versions.js b/docs/_static/arduino_versions.js
    new file mode 100644
    index 00000000000..5d6e2d1451d
    --- /dev/null
    +++ b/docs/_static/arduino_versions.js
    @@ -0,0 +1,28 @@
    +var DOCUMENTATION_VERSIONS = {
    +    DEFAULTS: { has_targets: false,
    +                supported_targets: [ "esp32" ]
    +              },
    +    VERSIONS: [
    +      { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    +      // 2.0.13
    +      { name: "release-2.0.13", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    +      // 2.0.12
    +      { name: "release-2.0.12", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    +      // 2.0.11
    +      { name: "release-2.0.11", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    +      // 2.0.10
    +      { name: "release-2.0.10", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    +      // 2.0.9
    +      { name: "release-2.0.9", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    +      // 2.0.8
    +      { name: "release-2.0.8", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    +    ],
    +    IDF_TARGETS: [
    +       { text: "ESP32", value: "esp32"},
    +       { text: "ESP32-S2", value: "esp32s2"},
    +       { text: "ESP32-S3", value: "esp32s3"},
    +       { text: "ESP32-C3", value: "esp32c3"},
    +       { text: "ESP32-H2", value: "esp32h2"},
    +       { text: "ESP32-C6", value: "esp32c6"},
    +    ]
    +};
    diff --git a/docs/_static/docs_version.js b/docs/_static/docs_version.js
    deleted file mode 100644
    index a511b79dc31..00000000000
    --- a/docs/_static/docs_version.js
    +++ /dev/null
    @@ -1,16 +0,0 @@
    -var DOCUMENTATION_VERSIONS = {
    -    DEFAULTS: { has_targets: false,
    -                supported_targets: [ "esp32" ]
    -              },
    -    VERSIONS: [
    -      { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    -    ],
    -    IDF_TARGETS: [
    -       { text: "ESP32", value: "esp32"},
    -       { text: "ESP32-S2", value: "esp32s2"},
    -       { text: "ESP32-S3", value: "esp32s3"},
    -       { text: "ESP32-C3", value: "esp32c3"},
    -       { text: "ESP32-H2", value: "esp32h2"},
    -       { text: "ESP32-C6", value: "esp32c6"},
    -    ]
    -};
    \ No newline at end of file
    diff --git a/docs/conf_common.py b/docs/conf_common.py
    index 3320558c3db..ded9c5673cb 100644
    --- a/docs/conf_common.py
    +++ b/docs/conf_common.py
    @@ -34,13 +34,9 @@
     ]
     
     ESP32S2_DOCS = ESP32_DOCS
    -
     ESP32C3_DOCS = ESP32S2_DOCS
    -
     ESP32S3_DOCS = ESP32S2_DOCS
    -
     ESP32C6_DOCS = ESP32S2_DOCS
    -
     ESP32H2_DOCS = ESP32S2_DOCS
     
     conditional_include_dict = {
    @@ -55,4 +51,4 @@
     # Extra options required by sphinx_idf_theme
     project_slug = "arduino-esp32"
     
    -versions_url = "./_static/docs_version.js"
    +versions_url = "./_static/arduino_versions.js"
    
    From 984d499f6d0d899e1ac8b69d073b269ec842f7e4 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Thu, 7 Dec 2023 10:21:12 +0800
    Subject: [PATCH 58/70] Merge conflicts resolved in the new docs
    
    ---
     docs/en/api/wifi.rst | 2 +-
     docs/en/conf.py      | 5 ++++-
     2 files changed, 5 insertions(+), 2 deletions(-)
    
    diff --git a/docs/en/api/wifi.rst b/docs/en/api/wifi.rst
    index 745804512b1..ebae513e821 100644
    --- a/docs/en/api/wifi.rst
    +++ b/docs/en/api/wifi.rst
    @@ -675,4 +675,4 @@ Wi-Fi Events Example
     ********************
     
     .. literalinclude:: ../../../libraries/WiFi/examples/WiFiClientEvents/WiFiClientEvents.ino
    -    :language: arduino
    \ No newline at end of file
    +    :language: arduino
    diff --git a/docs/en/conf.py b/docs/en/conf.py
    index 4d1208f256a..9979662dae0 100644
    --- a/docs/en/conf.py
    +++ b/docs/en/conf.py
    @@ -25,4 +25,7 @@
     
     # The language for content autogenerated by Sphinx. Refer to documentation
     # for a list of supported languages.
    -language = 'en'
    \ No newline at end of file
    +language = 'en'
    +
    +# Tracking ID for Google Analytics
    +google_analytics_id = 'G-F58JM78930'
    \ No newline at end of file
    
    From 8ef25bf0836951ef9e731a4c9a2c915859103f0a Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Thu, 7 Dec 2023 10:37:49 +0800
    Subject: [PATCH 59/70] Added new releases to the Arduino versions file
    
    ---
     docs/_static/arduino_versions.js | 18 ++++++++----------
     1 file changed, 8 insertions(+), 10 deletions(-)
    
    diff --git a/docs/_static/arduino_versions.js b/docs/_static/arduino_versions.js
    index 5d6e2d1451d..628368db213 100644
    --- a/docs/_static/arduino_versions.js
    +++ b/docs/_static/arduino_versions.js
    @@ -4,18 +4,16 @@ var DOCUMENTATION_VERSIONS = {
                   },
         VERSIONS: [
           { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    +      // 2.0.14
    +      { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    +      // 2.0.14
    +      { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    +      // 2.0.14
    +      { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    +      // 2.0.14
    +      { name: "release-2.0.14", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
           // 2.0.13
           { name: "release-2.0.13", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    -      // 2.0.12
    -      { name: "release-2.0.12", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    -      // 2.0.11
    -      { name: "release-2.0.11", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    -      // 2.0.10
    -      { name: "release-2.0.10", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    -      // 2.0.9
    -      { name: "release-2.0.9", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    -      // 2.0.8
    -      { name: "release-2.0.8", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
         ],
         IDF_TARGETS: [
            { text: "ESP32", value: "esp32"},
    
    From 78e026287f4f19eb13900bdfd8c0abe83b0bf30a Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Thu, 7 Dec 2023 12:32:03 +0800
    Subject: [PATCH 60/70] Fixed the JSON wrong link
    
    ---
     docs/en/external_libraries_test.rst | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/docs/en/external_libraries_test.rst b/docs/en/external_libraries_test.rst
    index 0d7da671cd2..0a5a34a9e1a 100644
    --- a/docs/en/external_libraries_test.rst
    +++ b/docs/en/external_libraries_test.rst
    @@ -129,4 +129,4 @@ In the table the results are in order ``BEFORE -> AFTER``.
        :class: no-scaled-link
     
     .. _LIBRARIES_TEST.md: https://github.com/espressif/arduino-esp32/blob/gh-pages/LIBRARIES_TEST.md
    -.. _lib.json: https://github.com/espressif/arduino-esp32/.github/workflow/lib.json
    \ No newline at end of file
    +.. _lib.json: https://github.com/espressif/arduino-esp32/blob/master/.github/workflows/lib.json
    
    From 9d3572134a91e81f2098fb9a96ede7770d5fd0c3 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Thu, 7 Dec 2023 18:26:16 +0800
    Subject: [PATCH 61/70] Removed the version and target selection for now
    
    ---
     .github/workflows/docs.yml        |  2 +-
     .github/workflows/docs_deploy.yml | 19 +++++++----
     docs/_static/arduino_versions.js  | 10 ------
     docs/conf_common.py               | 54 +++++++++++++++----------------
     docs/en/index.rst                 |  2 +-
     5 files changed, 42 insertions(+), 45 deletions(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index e0dc48653ef..2ca6b0a3cad 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -36,7 +36,7 @@ jobs:
             # Update the path to include them and run.
             cd ./docs
             PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    -        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en -t esp32 esp32s2 esp32s3 esp32c6 esp32h2
    +        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en
         - name: Archive Docs
           uses: actions/upload-artifact@v2
           with:
    diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml
    index 282ab264a16..16d7408a4e4 100644
    --- a/.github/workflows/docs_deploy.yml
    +++ b/.github/workflows/docs_deploy.yml
    @@ -1,16 +1,15 @@
     name: Documentation Deploy CI
     
    +// Deploy to production on each merge to master
     on:
    -  workflow_dispatch:
    -  release:
    -    types:
    -      - created
    +  push:
    +    branches:
    +      - master
         paths:
         - 'docs/**'
         - '.github/workflows/docs_deploy.yml'
     
     jobs:
    -
       deploy-preview-docs:
         name: Deploy Documentation
         runs-on: ubuntu-22.04
    @@ -26,8 +25,16 @@ jobs:
             python-version: '3.10'
         - name: Deploy Preview
           env:
    +        # Deploy to production server
    +        # DOCS_BUILD_DIR: "./docs/_build/"
    +        # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    +        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }}
    +        # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    +        # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    +        # Deploy to preview server
             DOCS_BUILD_DIR: "./docs/_build/"
    -        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_PREV_DEPLOY_KEY }}
             DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_PREV_SERVER }}
             DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PREV_SERVER_USER }}
             DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PREV_PATH }}
    diff --git a/docs/_static/arduino_versions.js b/docs/_static/arduino_versions.js
    index 628368db213..825bc0cbc27 100644
    --- a/docs/_static/arduino_versions.js
    +++ b/docs/_static/arduino_versions.js
    @@ -4,16 +4,6 @@ var DOCUMENTATION_VERSIONS = {
                   },
         VERSIONS: [
           { name: "latest", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    -      // 2.0.14
    -      { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    -      // 2.0.14
    -      { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    -      // 2.0.14
    -      { name: "release-3.0.0-alpha1", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32h2", "esp32c6"  ] },
    -      // 2.0.14
    -      { name: "release-2.0.14", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
    -      // 2.0.13
    -      { name: "release-2.0.13", has_targets: true, supported_targets: [ "esp32", "esp32s2", "esp32s3", "esp32c3" ] },
         ],
         IDF_TARGETS: [
            { text: "ESP32", value: "esp32"},
    diff --git a/docs/conf_common.py b/docs/conf_common.py
    index ded9c5673cb..eceaebb8a25 100644
    --- a/docs/conf_common.py
    +++ b/docs/conf_common.py
    @@ -4,14 +4,14 @@
     
     languages = ["en"]
     
    -idf_targets = [
    -    "esp32",
    -    "esp32s2",
    -    "esp32s3",
    -    "esp32c3",
    -    "esp32c6",
    -    "esp32h2",
    -]
    +# idf_targets = [
    +#     "esp32",
    +#     "esp32s2",
    +#     "esp32s3",
    +#     "esp32c3",
    +#     "esp32c6",
    +#     "esp32h2",
    +# ]
     
     # link roles config
     github_repo = "espressif/arduino-esp32"
    @@ -29,26 +29,26 @@
                    'esp_docs.esp_extensions.dummy_build_system',
                    ]
     
    -ESP32_DOCS = [
    -    "index.rst",
    -]
    -
    -ESP32S2_DOCS = ESP32_DOCS
    -ESP32C3_DOCS = ESP32S2_DOCS
    -ESP32S3_DOCS = ESP32S2_DOCS
    -ESP32C6_DOCS = ESP32S2_DOCS
    -ESP32H2_DOCS = ESP32S2_DOCS
    -
    -conditional_include_dict = {
    -    "esp32": ESP32_DOCS,
    -    "esp32s2": ESP32S2_DOCS,
    -    "esp32c3": ESP32C3_DOCS,
    -    "esp32s3": ESP32S3_DOCS,
    -    "esp32c6": ESP32C6_DOCS,
    -    "esp32h2": ESP32H2_DOCS,
    -}
    +# ESP32_DOCS = [
    +#     "index.rst",
    +# ]
    +
    +# ESP32S2_DOCS = ESP32_DOCS
    +# ESP32C3_DOCS = ESP32S2_DOCS
    +# ESP32S3_DOCS = ESP32S2_DOCS
    +# ESP32C6_DOCS = ESP32S2_DOCS
    +# ESP32H2_DOCS = ESP32S2_DOCS
    +
    +# conditional_include_dict = {
    +#     "esp32": ESP32_DOCS,
    +#     "esp32s2": ESP32S2_DOCS,
    +#     "esp32c3": ESP32C3_DOCS,
    +#     "esp32s3": ESP32S3_DOCS,
    +#     "esp32c6": ESP32C6_DOCS,
    +#     "esp32h2": ESP32H2_DOCS,
    +# }
     
     # Extra options required by sphinx_idf_theme
     project_slug = "arduino-esp32"
     
    -versions_url = "./_static/arduino_versions.js"
    +# versions_url = "./_static/arduino_versions.js"
    diff --git a/docs/en/index.rst b/docs/en/index.rst
    index f7f93f9cc92..11a4f8539c5 100644
    --- a/docs/en/index.rst
    +++ b/docs/en/index.rst
    @@ -2,7 +2,7 @@
     Welcome to Arduino Core's documentation
     #######################################
     
    -Here you will find all the relevant information about the project based on the {IDF_TARGET_NAME}.
    +Here you will find all the relevant information about the project based on the Arduino Core ESP32.
     
     .. note::
        This is a work in progress documentation and we will appreciate your help! We are looking for contributors! 
    
    From 1bf5b2b3d2cf59b377780c30d4e91f74114e998d Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 8 Dec 2023 11:31:53 +0800
    Subject: [PATCH 62/70] Documentation build and deploy CI changed
    
    ---
     .github/workflows/docs.yml        | 46 +++++++++++++++++++++++++--
     .github/workflows/docs_deploy.yml | 53 -------------------------------
     2 files changed, 44 insertions(+), 55 deletions(-)
     delete mode 100644 .github/workflows/docs_deploy.yml
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index 2ca6b0a3cad..1152b97278e 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -1,4 +1,4 @@
    -name: Documentation Build CI
    +name: Documentation Build and Deploy CI
     
     on:
       push:
    @@ -41,4 +41,46 @@ jobs:
           uses: actions/upload-artifact@v2
           with:
             name: docs
    -        path: docs
    \ No newline at end of file
    +        path: docs
    +
    +  deploy-preview-docs:
    +    name: Deploy Documentation
    +    runs-on: ubuntu-22.04
    +    defaults:
    +      run:
    +        shell: bash
    +    steps:
    +    - uses: actions/checkout@v2
    +      with:
    +        submodules: true
    +    - uses: actions/setup-python@v2
    +      with:
    +        python-version: '3.10'
    +    - name: Deploy Preview
    +      env:
    +        # Deploy to production server
    +        # DOCS_BUILD_DIR: "./docs/_build/"
    +        # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    +        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }}
    +        # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    +        # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    +        # Deploy to preview server
    +        DOCS_DIR: "./docs/_build/"
    +        DOCS_KEY: ${{ secrets.DOCS_KEY }}
    +        DOCS_PATH: ${{ secrets.DOCS_PATH }}
    +        DOCS_SERVER: ${{ secrets.DOCS_SERVER }}
    +        DOCS_URL: ${{ secrets.DOCS_URL }}
    +        DOCS_USER: ${{ secrets.DOCS_USER }}
    +      run: |
    +        sudo apt update
    +        sudo apt install python3-pip python3-setuptools
    +        source ./docs/utils.sh
    +        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    +        export GIT_VER=$(git describe --always)
    +        echo "PIP install requirements..."
    +        pip3 install --user -r ./docs/requirements.txt
    +        echo "Building the Docs..."
    +        cd ./docs && build-docs -l en
    +        echo "Deploy the Docs..."
    +        deploy-docs
    diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml
    deleted file mode 100644
    index 16d7408a4e4..00000000000
    --- a/.github/workflows/docs_deploy.yml
    +++ /dev/null
    @@ -1,53 +0,0 @@
    -name: Documentation Deploy CI
    -
    -// Deploy to production on each merge to master
    -on:
    -  push:
    -    branches:
    -      - master
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs_deploy.yml'
    -
    -jobs:
    -  deploy-preview-docs:
    -    name: Deploy Documentation
    -    runs-on: ubuntu-22.04
    -    defaults:
    -      run:
    -        shell: bash
    -    steps:
    -    - uses: actions/checkout@v2
    -      with:
    -        submodules: true
    -    - uses: actions/setup-python@v2
    -      with:
    -        python-version: '3.10'
    -    - name: Deploy Preview
    -      env:
    -        # Deploy to production server
    -        # DOCS_BUILD_DIR: "./docs/_build/"
    -        # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    -        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }}
    -        # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        # Deploy to preview server
    -        DOCS_BUILD_DIR: "./docs/_build/"
    -        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_PREV_DEPLOY_KEY }}
    -        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_PREV_SERVER }}
    -        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PREV_SERVER_USER }}
    -        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PREV_PATH }}
    -        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_PREV_URL }}
    -      run: |
    -        sudo apt update
    -        sudo apt install python3-pip python3-setuptools
    -        source ./docs/utils.sh
    -        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    -        export GIT_VER=$(git describe --always)
    -        echo "PIP install requirements..."
    -        pip3 install --user -r ./docs/requirements.txt
    -        echo "Building the Docs..."
    -        cd ./docs && build-docs -l en
    -        echo "Deploy the Docs..."
    -        deploy-docs
    
    From a58bca169d62e38e600d5fa488fd4844b56788d6 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 8 Dec 2023 11:37:00 +0800
    Subject: [PATCH 63/70] Fixed env vars from server side
    
    ---
     .github/workflows/docs.yml | 11 ++++++-----
     1 file changed, 6 insertions(+), 5 deletions(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index 1152b97278e..6cef7b8fe71 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -66,12 +66,13 @@ jobs:
             # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
             # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
             # Deploy to preview server
    -        DOCS_DIR: "./docs/_build/"
    -        DOCS_KEY: ${{ secrets.DOCS_KEY }}
    -        DOCS_PATH: ${{ secrets.DOCS_PATH }}
    -        DOCS_SERVER: ${{ secrets.DOCS_SERVER }}
    +        
    +        DOCS_BUILD_DIR: "./docs/_build/"
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    +        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
             DOCS_URL: ${{ secrets.DOCS_URL }}
    -        DOCS_USER: ${{ secrets.DOCS_USER }}
    +        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
           run: |
             sudo apt update
             sudo apt install python3-pip python3-setuptools
    
    From ee3a17e72e678e109f689370e777f6314ce3cae6 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 8 Dec 2023 11:39:00 +0800
    Subject: [PATCH 64/70] Fixed env vars from server side URL base
    
    ---
     .github/workflows/docs.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index 6cef7b8fe71..e4d354c20de 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -71,7 +71,7 @@ jobs:
             DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
             DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
             DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        DOCS_URL: ${{ secrets.DOCS_URL }}
    +        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
             DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
           run: |
             sudo apt update
    
    From f46c8ef96a4360642f3e2eb093985e91bd023c92 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 8 Dec 2023 11:47:44 +0800
    Subject: [PATCH 65/70] Deploy on preview server
    
    ---
     .github/workflows/docs.yml | 12 ++++++------
     1 file changed, 6 insertions(+), 6 deletions(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index e4d354c20de..8e6c2f31da5 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -68,16 +68,16 @@ jobs:
             # Deploy to preview server
             
             DOCS_BUILD_DIR: "./docs/_build/"
    -        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    -        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
    +        DOCS_PREVIEW_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    +        DOCS_PREVIEW_PATH: ${{ secrets.DOCS_PATH }}
    +        DOCS_PREVIEW_SERVER: ${{ secrets.DOCS_SERVER }}
    +        DOCS_PREVIEW_URL_BASE: ${{ secrets.DOCS_URL }}
    +        DOCS_PREVIEW_USER: ${{ secrets.DOCS_USER }}
           run: |
             sudo apt update
             sudo apt install python3-pip python3-setuptools
             source ./docs/utils.sh
    -        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    +        add_doc_server_ssh_keys $DOCS_PREVIEW_PRIVATEKEY $DOCS_PREVIEW_SERVER $DOCS_PREVIEW_USER
             export GIT_VER=$(git describe --always)
             echo "PIP install requirements..."
             pip3 install --user -r ./docs/requirements.txt
    
    From fa97d349533794c9dcd870a2aa268f14714ddd59 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 8 Dec 2023 11:56:09 +0800
    Subject: [PATCH 66/70] Deploy on preview server add type var
    
    ---
     .github/workflows/docs.yml | 16 ++++++++--------
     1 file changed, 8 insertions(+), 8 deletions(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index 8e6c2f31da5..ec4fff94995 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -66,18 +66,18 @@ jobs:
             # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
             # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
             # Deploy to preview server
    -        
    -        DOCS_BUILD_DIR: "./docs/_build/"
    -        DOCS_PREVIEW_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    -        DOCS_PREVIEW_PATH: ${{ secrets.DOCS_PATH }}
    -        DOCS_PREVIEW_SERVER: ${{ secrets.DOCS_SERVER }}
    -        DOCS_PREVIEW_URL_BASE: ${{ secrets.DOCS_URL }}
    -        DOCS_PREVIEW_USER: ${{ secrets.DOCS_USER }}
    +        TYPE: "preview"
    +        DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    +        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    +        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
           run: |
             sudo apt update
             sudo apt install python3-pip python3-setuptools
             source ./docs/utils.sh
    -        add_doc_server_ssh_keys $DOCS_PREVIEW_PRIVATEKEY $DOCS_PREVIEW_SERVER $DOCS_PREVIEW_USER
    +        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
             export GIT_VER=$(git describe --always)
             echo "PIP install requirements..."
             pip3 install --user -r ./docs/requirements.txt
    
    From f0bc041a81c5fe7959ddeb692d259b9ab42eed69 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 8 Dec 2023 12:28:51 +0800
    Subject: [PATCH 67/70] Deploy on preview server - Change the URL
    
    ---
     .github/workflows/docs.yml | 4 +++-
     1 file changed, 3 insertions(+), 1 deletion(-)
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index ec4fff94995..2318efc06fa 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -70,8 +70,10 @@ jobs:
             DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
             DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
             DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        DOCS_DEPLOY_SERVER: "preview-docs.espressif.com"
             DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    +        # DOCS_DEPLOY_URL_BASE: "https://docs.espressif.com/projects/arduino-esp32"
             DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
           run: |
             sudo apt update
    
    From ba0335c0bc2a187977053e80ecaed146e5341c7c Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Mon, 11 Dec 2023 11:48:03 +0800
    Subject: [PATCH 68/70] Deploy on preview server CI changes
    
    ---
     .github/workflows/docs.yml        |  4 +-
     .github/workflows/docs_deploy.yml | 87 +++++++++++++++++++++++++++++++
     .github/workflows/docs_legacy.yml | 38 ++++++++++++++
     3 files changed, 126 insertions(+), 3 deletions(-)
     create mode 100644 .github/workflows/docs_deploy.yml
     create mode 100644 .github/workflows/docs_legacy.yml
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    index 2318efc06fa..ec4fff94995 100644
    --- a/.github/workflows/docs.yml
    +++ b/.github/workflows/docs.yml
    @@ -70,10 +70,8 @@ jobs:
             DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
             DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
             DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        DOCS_DEPLOY_SERVER: "preview-docs.espressif.com"
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
             DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        # DOCS_DEPLOY_URL_BASE: "https://docs.espressif.com/projects/arduino-esp32"
             DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
           run: |
             sudo apt update
    diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml
    new file mode 100644
    index 00000000000..ec4fff94995
    --- /dev/null
    +++ b/.github/workflows/docs_deploy.yml
    @@ -0,0 +1,87 @@
    +name: Documentation Build and Deploy CI
    +
    +on:
    +  push:
    +    branches:
    +    - master
    +    - release/*
    +    paths:
    +    - 'docs/**'
    +    - '.github/workflows/docs.yml'
    +  pull_request:
    +    paths:
    +    - 'docs/**'
    +    - '.github/workflows/docs.yml'
    +
    +jobs:
    +
    +  build-docs:
    +    name: Build ESP-Docs
    +    runs-on: ubuntu-22.04
    +    defaults:
    +      run:
    +        shell: bash
    +    steps:
    +    - uses: actions/checkout@v3
    +      with:
    +        submodules: true
    +    - uses: actions/setup-python@v4
    +      with:
    +        python-version: '3.10'
    +    - name: Build
    +      run: |
    +        sudo apt update
    +        sudo apt install python3-pip python3-setuptools
    +        # GitHub CI installs pip3 and setuptools outside the path.
    +        # Update the path to include them and run.
    +        cd ./docs
    +        PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    +        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en
    +    - name: Archive Docs
    +      uses: actions/upload-artifact@v2
    +      with:
    +        name: docs
    +        path: docs
    +
    +  deploy-preview-docs:
    +    name: Deploy Documentation
    +    runs-on: ubuntu-22.04
    +    defaults:
    +      run:
    +        shell: bash
    +    steps:
    +    - uses: actions/checkout@v2
    +      with:
    +        submodules: true
    +    - uses: actions/setup-python@v2
    +      with:
    +        python-version: '3.10'
    +    - name: Deploy Preview
    +      env:
    +        # Deploy to production server
    +        # DOCS_BUILD_DIR: "./docs/_build/"
    +        # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    +        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }}
    +        # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    +        # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    +        # Deploy to preview server
    +        TYPE: "preview"
    +        DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    +        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    +        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
    +      run: |
    +        sudo apt update
    +        sudo apt install python3-pip python3-setuptools
    +        source ./docs/utils.sh
    +        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    +        export GIT_VER=$(git describe --always)
    +        echo "PIP install requirements..."
    +        pip3 install --user -r ./docs/requirements.txt
    +        echo "Building the Docs..."
    +        cd ./docs && build-docs -l en
    +        echo "Deploy the Docs..."
    +        deploy-docs
    diff --git a/.github/workflows/docs_legacy.yml b/.github/workflows/docs_legacy.yml
    new file mode 100644
    index 00000000000..d725f8a1d1b
    --- /dev/null
    +++ b/.github/workflows/docs_legacy.yml
    @@ -0,0 +1,38 @@
    +name: Build the Docs Legacy ReadTheDocs CI
    +
    +on:
    +  push:
    +    branches:
    +    - master
    +    - release/*
    +    paths:
    +    - 'docs/**'
    +    - '.github/workflows/docs_legacy.yml'
    +  pull_request:
    +    paths:
    +    - 'docs/**'
    +    - '.github/workflows/docs_legacy.yml'
    +
    +jobs:
    +
    +  build-docs:
    +    name: Build ReadTheDocs
    +    runs-on: ubuntu-22.04
    +    defaults:
    +      run:
    +        shell: bash
    +    steps:
    +    - uses: actions/checkout@v3
    +      with:
    +        submodules: true
    +    - uses: actions/setup-python@v4
    +      with:
    +        python-version: '3.10'
    +    - name: Build
    +      run: |
    +        sudo apt update
    +        sudo apt install python3-pip python3-setuptools
    +        # GitHub CI installs pip3 and setuptools outside the path.
    +        # Update the path to include them and run.
    +        PATH=/home/runner/.local/bin:$PATH pip3 install --user -r ./docs/requirements.txt
    +        cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" make html
    
    From 46d6a41565beac80eeebd5ffb3cfcdc30cdc00b8 Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 12 Jan 2024 11:26:27 +0000
    Subject: [PATCH 69/70] Docs deploy CI updated for production deployment
    
    ---
     .github/workflows/docs.yml                    | 87 -------------------
     .../{docs_legacy.yml => docs_build.yml}       | 23 +++--
     .github/workflows/docs_deploy.yml             | 57 ++----------
     .github/workflows/docs_preview.yml            | 46 ++++++++++
     docs/utils.sh                                 |  2 +-
     5 files changed, 66 insertions(+), 149 deletions(-)
     delete mode 100644 .github/workflows/docs.yml
     rename .github/workflows/{docs_legacy.yml => docs_build.yml} (56%)
     create mode 100644 .github/workflows/docs_preview.yml
    
    diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
    deleted file mode 100644
    index ec4fff94995..00000000000
    --- a/.github/workflows/docs.yml
    +++ /dev/null
    @@ -1,87 +0,0 @@
    -name: Documentation Build and Deploy CI
    -
    -on:
    -  push:
    -    branches:
    -    - master
    -    - release/*
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs.yml'
    -  pull_request:
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs.yml'
    -
    -jobs:
    -
    -  build-docs:
    -    name: Build ESP-Docs
    -    runs-on: ubuntu-22.04
    -    defaults:
    -      run:
    -        shell: bash
    -    steps:
    -    - uses: actions/checkout@v3
    -      with:
    -        submodules: true
    -    - uses: actions/setup-python@v4
    -      with:
    -        python-version: '3.10'
    -    - name: Build
    -      run: |
    -        sudo apt update
    -        sudo apt install python3-pip python3-setuptools
    -        # GitHub CI installs pip3 and setuptools outside the path.
    -        # Update the path to include them and run.
    -        cd ./docs
    -        PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    -        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en
    -    - name: Archive Docs
    -      uses: actions/upload-artifact@v2
    -      with:
    -        name: docs
    -        path: docs
    -
    -  deploy-preview-docs:
    -    name: Deploy Documentation
    -    runs-on: ubuntu-22.04
    -    defaults:
    -      run:
    -        shell: bash
    -    steps:
    -    - uses: actions/checkout@v2
    -      with:
    -        submodules: true
    -    - uses: actions/setup-python@v2
    -      with:
    -        python-version: '3.10'
    -    - name: Deploy Preview
    -      env:
    -        # Deploy to production server
    -        # DOCS_BUILD_DIR: "./docs/_build/"
    -        # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    -        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }}
    -        # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        # Deploy to preview server
    -        TYPE: "preview"
    -        DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
    -        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    -        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
    -      run: |
    -        sudo apt update
    -        sudo apt install python3-pip python3-setuptools
    -        source ./docs/utils.sh
    -        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    -        export GIT_VER=$(git describe --always)
    -        echo "PIP install requirements..."
    -        pip3 install --user -r ./docs/requirements.txt
    -        echo "Building the Docs..."
    -        cd ./docs && build-docs -l en
    -        echo "Deploy the Docs..."
    -        deploy-docs
    diff --git a/.github/workflows/docs_legacy.yml b/.github/workflows/docs_build.yml
    similarity index 56%
    rename from .github/workflows/docs_legacy.yml
    rename to .github/workflows/docs_build.yml
    index d725f8a1d1b..c18120c079f 100644
    --- a/.github/workflows/docs_legacy.yml
    +++ b/.github/workflows/docs_build.yml
    @@ -1,22 +1,15 @@
    -name: Build the Docs Legacy ReadTheDocs CI
    +name: Documentation Build and Deploy CI
     
     on:
    -  push:
    -    branches:
    -    - master
    -    - release/*
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs_legacy.yml'
       pull_request:
         paths:
         - 'docs/**'
    -    - '.github/workflows/docs_legacy.yml'
    +    - '.github/workflows/docs.yml'
     
     jobs:
     
       build-docs:
    -    name: Build ReadTheDocs
    +    name: Build Documentation
         runs-on: ubuntu-22.04
         defaults:
           run:
    @@ -34,5 +27,11 @@ jobs:
             sudo apt install python3-pip python3-setuptools
             # GitHub CI installs pip3 and setuptools outside the path.
             # Update the path to include them and run.
    -        PATH=/home/runner/.local/bin:$PATH pip3 install --user -r ./docs/requirements.txt
    -        cd ./docs && PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" make html
    +        cd ./docs
    +        PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    +        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en
    +    - name: Archive Docs
    +      uses: actions/upload-artifact@v2
    +      with:
    +        name: docs
    +        path: docs
    diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml
    index ec4fff94995..ebc8acac792 100644
    --- a/.github/workflows/docs_deploy.yml
    +++ b/.github/workflows/docs_deploy.yml
    @@ -1,50 +1,17 @@
    -name: Documentation Build and Deploy CI
    +name: Documentation Build and Deploy Production CI
     
     on:
       push:
         branches:
    -    - master
         - release/*
         paths:
         - 'docs/**'
         - '.github/workflows/docs.yml'
    -  pull_request:
    -    paths:
    -    - 'docs/**'
    -    - '.github/workflows/docs.yml'
     
     jobs:
     
    -  build-docs:
    -    name: Build ESP-Docs
    -    runs-on: ubuntu-22.04
    -    defaults:
    -      run:
    -        shell: bash
    -    steps:
    -    - uses: actions/checkout@v3
    -      with:
    -        submodules: true
    -    - uses: actions/setup-python@v4
    -      with:
    -        python-version: '3.10'
    -    - name: Build
    -      run: |
    -        sudo apt update
    -        sudo apt install python3-pip python3-setuptools
    -        # GitHub CI installs pip3 and setuptools outside the path.
    -        # Update the path to include them and run.
    -        cd ./docs
    -        PATH=/home/runner/.local/bin:$PATH pip3 install -r requirements.txt --prefer-binary
    -        PATH=/home/runner/.local/bin:$PATH SPHINXOPTS="-W" build-docs -l en
    -    - name: Archive Docs
    -      uses: actions/upload-artifact@v2
    -      with:
    -        name: docs
    -        path: docs
    -
    -  deploy-preview-docs:
    -    name: Deploy Documentation
    +  deploy-prod-docs:
    +    name: Deploy Documentation Production
         runs-on: ubuntu-22.04
         defaults:
           run:
    @@ -59,20 +26,12 @@ jobs:
         - name: Deploy Preview
           env:
             # Deploy to production server
    -        # DOCS_BUILD_DIR: "./docs/_build/"
    -        # DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_DEPLOY_KEY }}
    -        # DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        # DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_SERVER_USER }}
    -        # DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        # DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        # Deploy to preview server
    -        TYPE: "preview"
             DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
    -        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    -        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    -        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    -        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    -        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_PROD_PRIVATEKEY }}
    +        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PROD_PATH }}
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_PROD_SERVER }}
    +        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_PROD_URL_BASE }}
    +        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PROD_USER }}
           run: |
             sudo apt update
             sudo apt install python3-pip python3-setuptools
    diff --git a/.github/workflows/docs_preview.yml b/.github/workflows/docs_preview.yml
    new file mode 100644
    index 00000000000..41de91838b3
    --- /dev/null
    +++ b/.github/workflows/docs_preview.yml
    @@ -0,0 +1,46 @@
    +name: Documentation Build and Deploy CI
    +
    +on:
    +  push:
    +    branches:
    +    - master
    +    paths:
    +    - 'docs/**'
    +    - '.github/workflows/docs.yml'
    +
    +jobs:
    +
    +  deploy-preview-docs:
    +    name: Deploy Documentation Preview
    +    runs-on: ubuntu-22.04
    +    defaults:
    +      run:
    +        shell: bash
    +    steps:
    +    - uses: actions/checkout@v3
    +      with:
    +        submodules: true
    +    - uses: actions/setup-python@v4
    +      with:
    +        python-version: '3.10'
    +    - name: Deploy Preview
    +      env:
    +        # Deploy to preview server
    +        DOCS_BUILD_DIR: "${CI_PROJECT_DIR}/docs/_build/"
    +        DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }}
    +        DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }}
    +        DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }}
    +        DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_URL }}
    +        DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }}
    +      run: |
    +        sudo apt update
    +        sudo apt install python3-pip python3-setuptools
    +        source ./docs/utils.sh
    +        add_doc_server_ssh_keys $DOCS_DEPLOY_PRIVATEKEY $DOCS_DEPLOY_SERVER $DOCS_DEPLOY_SERVER_USER
    +        export GIT_VER=$(git describe --always)
    +        echo "PIP install requirements..."
    +        pip3 install --user -r ./docs/requirements.txt
    +        echo "Building the Docs..."
    +        cd ./docs && build-docs -l en
    +        echo "Deploy the Docs..."
    +        deploy-docs
    diff --git a/docs/utils.sh b/docs/utils.sh
    index 91e283d462f..0f9574e57a5 100644
    --- a/docs/utils.sh
    +++ b/docs/utils.sh
    @@ -5,7 +5,7 @@ function add_ssh_keys() {
       mkdir -p ~/.ssh
       chmod 700 ~/.ssh
       echo -n "${key_string}" >~/.ssh/id_rsa_base64
    -  base64 -w 0 ~/.ssh/id_rsa_base64 | base64 -di >~/.ssh/id_rsa
    +  base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 >~/.ssh/id_rsa
       chmod 600 ~/.ssh/id_rsa
     }
     
    
    From 73d5027d3e8671799b4340df7dcd78e672f7deda Mon Sep 17 00:00:00 2001
    From: pedrominatel 
    Date: Fri, 12 Jan 2024 15:26:49 +0000
    Subject: [PATCH 70/70] Merge issue on the filename fixed
    
    ---
     docs/en/boards/{boards.rst~HEAD => boards.rst} | 0
     1 file changed, 0 insertions(+), 0 deletions(-)
     rename docs/en/boards/{boards.rst~HEAD => boards.rst} (100%)
    
    diff --git a/docs/en/boards/boards.rst~HEAD b/docs/en/boards/boards.rst
    similarity index 100%
    rename from docs/en/boards/boards.rst~HEAD
    rename to docs/en/boards/boards.rst