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' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs_build.yml similarity index 64% rename from .github/workflows/docs.yml rename to .github/workflows/docs_build.yml index f01fb76ee84..2de8f347265 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs_build.yml @@ -1,4 +1,4 @@ -name: ReadTheDocs CI +name: Documentation Build and Deploy 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: @@ -34,5 +34,12 @@ 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 new file mode 100644 index 00000000000..7a1123454ae --- /dev/null +++ b/.github/workflows/docs_deploy.yml @@ -0,0 +1,49 @@ +name: Documentation Build and Production Deploy CI + +on: + push: + branches: + - release/* + paths: + - 'docs/**' + - '.github/workflows/docs.yml' + +jobs: + + deploy-prod-docs: + name: Deploy Documentation on Production + 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 Documentation + env: + # Deploy to production server + # DOCS_BUILD_DIR: "./docs/_build/" + DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_PROD_PRIVATEKEY }} + DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PROD_PATH }} + DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_PROD_USER }} + DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_PROD_URL_BASE }} + # Deploy to preview server + 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..." + export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/docs/ + cd $GITHUB_WORKSPACE/docs + deploy-docs diff --git a/.github/workflows/docs_preview.yml b/.github/workflows/docs_preview.yml new file mode 100644 index 00000000000..3923839ada0 --- /dev/null +++ b/.github/workflows/docs_preview.yml @@ -0,0 +1,48 @@ +name: Documentation Build and Preview Deploy CI + +on: + push: + branches: + - master + paths: + - 'docs/**' + - '.github/workflows/docs.yml' + +jobs: + + deploy-preview-docs: + name: Deploy Documentation + 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: "./docs/_build" + DOCS_DEPLOY_PRIVATEKEY: ${{ secrets.DOCS_KEY }} + DOCS_DEPLOY_PATH: ${{ secrets.DOCS_PATH }} + DOCS_DEPLOY_SERVER: ${{ secrets.DOCS_SERVER }} + DOCS_DEPLOY_SERVER_USER: ${{ secrets.DOCS_USER }} + DOCS_DEPLOY_URL_BASE: ${{ secrets.DOCS_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..." + export DOCS_BUILD_DIR=$GITHUB_WORKSPACE/docs/ + cd $GITHUB_WORKSPACE/docs + deploy-docs diff --git a/CMakeLists.txt b/CMakeLists.txt index 0834ae1ec34..802a8f87d2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,13 +50,14 @@ set(CORE_SRCS cores/esp32/Esp.cpp cores/esp32/FunctionalInterrupt.cpp cores/esp32/HardwareSerial.cpp + cores/esp32/HEXBuilder.cpp cores/esp32/IPAddress.cpp - cores/esp32/IPv6Address.cpp cores/esp32/libb64/cdecode.c cores/esp32/libb64/cencode.c cores/esp32/main.cpp cores/esp32/MD5Builder.cpp cores/esp32/Print.cpp + cores/esp32/SHA1Builder.cpp cores/esp32/stdlib_noniso.c cores/esp32/Stream.cpp cores/esp32/StreamString.cpp diff --git a/README.md b/README.md index 09556346255..09daae01ca0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Arduino core for the ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6 and ESP32-H2 -![Build Status](https://github.com/espressif/arduino-esp32/workflows/ESP32%20Arduino%20CI/badge.svg) [![Documentation Status](https://readthedocs.com/projects/espressif-arduino-esp32/badge/?version=latest)](https://docs.espressif.com/projects/arduino-esp32/en/latest/?badge=latest) -[![External Libraries Test](https://github.com/espressif/arduino-esp32/actions/workflows/lib.yml/badge.svg?branch=master&event=schedule)](https://github.com/espressif/arduino-esp32/actions/workflows/lib.yml?link=http://https://github.com/espressif/arduino-esp32/blob/master/LIBRARIES_TEST.md) +![Build Status](https://github.com/espressif/arduino-esp32/workflows/ESP32%20Arduino%20CI/badge.svg) [![External Libraries Test](https://github.com/espressif/arduino-esp32/actions/workflows/lib.yml/badge.svg?branch=master&event=schedule)](https://github.com/espressif/arduino-esp32/actions/workflows/lib.yml?link=http://https://github.com/espressif/arduino-esp32/blob/master/LIBRARIES_TEST.md) ### Need help or have a question? Join the chat at [Gitter](https://gitter.im/espressif/arduino-esp32) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions) diff --git a/boards.txt b/boards.txt index f1256a1ed28..7d99cc6ce34 100644 --- a/boards.txt +++ b/boards.txt @@ -17897,6 +17897,178 @@ heltec_wireless_stick_lite.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +heltec_wireless_tracker.name=Heltec Wireless Tracker +heltec_wireless_tracker.vid.0=0x303a +heltec_wireless_tracker.pid.0=0x1001 + +heltec_wireless_tracker.bootloader.tool=esptool_py +heltec_wireless_tracker.bootloader.tool.default=esptool_py + +heltec_wireless_tracker.upload.tool=esptool_py +heltec_wireless_tracker.upload.tool.default=esptool_py +heltec_wireless_tracker.upload.tool.network=esp_ota + +heltec_wireless_tracker.upload.maximum_size=3342336 +heltec_wireless_tracker.upload.maximum_data_size=327680 +heltec_wireless_tracker.upload.flags= +heltec_wireless_tracker.upload.extra_flags= +heltec_wireless_tracker.upload.use_1200bps_touch=false +heltec_wireless_tracker.upload.wait_for_upload_port=false + +heltec_wireless_tracker.serial.disableDTR=false +heltec_wireless_tracker.serial.disableRTS=false + +heltec_wireless_tracker.build.tarch=xtensa +heltec_wireless_tracker.build.bootloader_addr=0x0 +heltec_wireless_tracker.build.target=esp32s3 +heltec_wireless_tracker.build.mcu=esp32s3 +heltec_wireless_tracker.build.core=esp32 +heltec_wireless_tracker.build.variant=heltec_wireless_tracker +heltec_wireless_tracker.build.board=HELTEC_WIRELESS_TRACKER + +heltec_wireless_tracker.build.usb_mode=1 +heltec_wireless_tracker.build.cdc_on_boot=0 +heltec_wireless_tracker.build.msc_on_boot=0 +heltec_wireless_tracker.build.dfu_on_boot=0 +heltec_wireless_tracker.build.f_cpu=240000000L +heltec_wireless_tracker.build.flash_size=8MB +heltec_wireless_tracker.build.flash_freq=80m +heltec_wireless_tracker.build.flash_mode=dio +heltec_wireless_tracker.build.boot=qio +heltec_wireless_tracker.build.boot_freq=80m +heltec_wireless_tracker.build.partitions=default_8MB +heltec_wireless_tracker.build.loop_core= +heltec_wireless_tracker.build.event_core= +heltec_wireless_tracker.build.psram_type=qspi +heltec_wireless_tracker.build.memory_type={build.boot}_{build.psram_type} + +heltec_wireless_tracker.menu.LoopCore.1=Core 1 +heltec_wireless_tracker.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +heltec_wireless_tracker.menu.LoopCore.0=Core 0 +heltec_wireless_tracker.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +heltec_wireless_tracker.menu.EventsCore.1=Core 1 +heltec_wireless_tracker.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +heltec_wireless_tracker.menu.EventsCore.0=Core 0 +heltec_wireless_tracker.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +heltec_wireless_tracker.menu.USBMode.hwcdc=Hardware CDC and JTAG +heltec_wireless_tracker.menu.USBMode.hwcdc.build.usb_mode=1 +heltec_wireless_tracker.menu.USBMode.default=USB-OTG (TinyUSB) +heltec_wireless_tracker.menu.USBMode.default.build.usb_mode=0 + +heltec_wireless_tracker.menu.CDCOnBoot.default=Enabled +heltec_wireless_tracker.menu.CDCOnBoot.default.build.cdc_on_boot=1 +heltec_wireless_tracker.menu.CDCOnBoot.cdc=Enabled +heltec_wireless_tracker.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +heltec_wireless_tracker.menu.MSCOnBoot.default=Disabled +heltec_wireless_tracker.menu.MSCOnBoot.default.build.msc_on_boot=0 +heltec_wireless_tracker.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +heltec_wireless_tracker.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +heltec_wireless_tracker.menu.DFUOnBoot.default=Disabled +heltec_wireless_tracker.menu.DFUOnBoot.default.build.dfu_on_boot=0 +heltec_wireless_tracker.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +heltec_wireless_tracker.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +heltec_wireless_tracker.menu.UploadMode.default=UART0 / Hardware CDC +heltec_wireless_tracker.menu.UploadMode.default.upload.use_1200bps_touch=false +heltec_wireless_tracker.menu.UploadMode.default.upload.wait_for_upload_port=false +heltec_wireless_tracker.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +heltec_wireless_tracker.menu.UploadMode.cdc.upload.use_1200bps_touch=true +heltec_wireless_tracker.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +heltec_wireless_tracker.menu.CPUFreq.240=240MHz (WiFi) +heltec_wireless_tracker.menu.CPUFreq.240.build.f_cpu=240000000L +heltec_wireless_tracker.menu.CPUFreq.160=160MHz (WiFi) +heltec_wireless_tracker.menu.CPUFreq.160.build.f_cpu=160000000L +heltec_wireless_tracker.menu.CPUFreq.80=80MHz (WiFi) +heltec_wireless_tracker.menu.CPUFreq.80.build.f_cpu=80000000L +heltec_wireless_tracker.menu.CPUFreq.40=40MHz +heltec_wireless_tracker.menu.CPUFreq.40.build.f_cpu=40000000L +heltec_wireless_tracker.menu.CPUFreq.20=20MHz +heltec_wireless_tracker.menu.CPUFreq.20.build.f_cpu=20000000L +heltec_wireless_tracker.menu.CPUFreq.10=10MHz +heltec_wireless_tracker.menu.CPUFreq.10.build.f_cpu=10000000L + +heltec_wireless_tracker.menu.UploadSpeed.921600=921600 +heltec_wireless_tracker.menu.UploadSpeed.921600.upload.speed=921600 +heltec_wireless_tracker.menu.UploadSpeed.115200=115200 +heltec_wireless_tracker.menu.UploadSpeed.115200.upload.speed=115200 +heltec_wireless_tracker.menu.UploadSpeed.256000.windows=256000 +heltec_wireless_tracker.menu.UploadSpeed.256000.upload.speed=256000 +heltec_wireless_tracker.menu.UploadSpeed.230400.windows.upload.speed=256000 +heltec_wireless_tracker.menu.UploadSpeed.230400=230400 +heltec_wireless_tracker.menu.UploadSpeed.230400.upload.speed=230400 +heltec_wireless_tracker.menu.UploadSpeed.460800.linux=460800 +heltec_wireless_tracker.menu.UploadSpeed.460800.macosx=460800 +heltec_wireless_tracker.menu.UploadSpeed.460800.upload.speed=460800 +heltec_wireless_tracker.menu.UploadSpeed.512000.windows=512000 +heltec_wireless_tracker.menu.UploadSpeed.512000.upload.speed=512000 + +heltec_wireless_tracker.menu.DebugLevel.none=None +heltec_wireless_tracker.menu.DebugLevel.none.build.code_debug=0 +heltec_wireless_tracker.menu.DebugLevel.error=Error +heltec_wireless_tracker.menu.DebugLevel.error.build.code_debug=1 +heltec_wireless_tracker.menu.DebugLevel.warn=Warn +heltec_wireless_tracker.menu.DebugLevel.warn.build.code_debug=2 +heltec_wireless_tracker.menu.DebugLevel.info=Info +heltec_wireless_tracker.menu.DebugLevel.info.build.code_debug=3 +heltec_wireless_tracker.menu.DebugLevel.debug=Debug +heltec_wireless_tracker.menu.DebugLevel.debug.build.code_debug=4 +heltec_wireless_tracker.menu.DebugLevel.verbose=Verbose +heltec_wireless_tracker.menu.DebugLevel.verbose.build.code_debug=5 + +heltec_wireless_tracker.menu.LORAWAN_REGION.0=REGION_EU868 +heltec_wireless_tracker.menu.LORAWAN_REGION.0.build.band=REGION_EU868 +heltec_wireless_tracker.menu.LORAWAN_REGION.1=REGION_EU433 +heltec_wireless_tracker.menu.LORAWAN_REGION.1.build.band=REGION_EU433 +heltec_wireless_tracker.menu.LORAWAN_REGION.2=REGION_CN470 +heltec_wireless_tracker.menu.LORAWAN_REGION.2.build.band=REGION_CN470 +heltec_wireless_tracker.menu.LORAWAN_REGION.3=REGION_US915 +heltec_wireless_tracker.menu.LORAWAN_REGION.3.build.band=REGION_US915 +heltec_wireless_tracker.menu.LORAWAN_REGION.4=REGION_AU915 +heltec_wireless_tracker.menu.LORAWAN_REGION.4.build.band=REGION_AU915 +heltec_wireless_tracker.menu.LORAWAN_REGION.5=REGION_CN779 +heltec_wireless_tracker.menu.LORAWAN_REGION.5.build.band=REGION_CN779 +heltec_wireless_tracker.menu.LORAWAN_REGION.6=REGION_AS923 +heltec_wireless_tracker.menu.LORAWAN_REGION.6.build.band=REGION_AS923 +heltec_wireless_tracker.menu.LORAWAN_REGION.7=REGION_KR920 +heltec_wireless_tracker.menu.LORAWAN_REGION.7.build.band=REGION_KR920 +heltec_wireless_tracker.menu.LORAWAN_REGION.8=REGION_IN865 +heltec_wireless_tracker.menu.LORAWAN_REGION.8.build.band=REGION_IN865 +heltec_wireless_tracker.menu.LORAWAN_REGION.9=REGION_US915_HYBRID +heltec_wireless_tracker.menu.LORAWAN_REGION.9.build.band=REGION_US915_HYBRID + +heltec_wireless_tracker.menu.LoRaWanDebugLevel.0=None +heltec_wireless_tracker.menu.LoRaWanDebugLevel.0.build.LoRaWanDebugLevel=0 +heltec_wireless_tracker.menu.LoRaWanDebugLevel.1=Freq +heltec_wireless_tracker.menu.LoRaWanDebugLevel.1.build.LoRaWanDebugLevel=1 +heltec_wireless_tracker.menu.LoRaWanDebugLevel.2=Freq && DIO +heltec_wireless_tracker.menu.LoRaWanDebugLevel.2.build.LoRaWanDebugLevel=2 +heltec_wireless_tracker.menu.LoRaWanDebugLevel.3=Freq && DIO && PW +heltec_wireless_tracker.menu.LoRaWanDebugLevel.3.build.LoRaWanDebugLevel=3 + +heltec_wireless_tracker.menu.LORAWAN_DEVEUI.0=CUSTOM +heltec_wireless_tracker.menu.LORAWAN_DEVEUI.0.build.LORAWAN_DEVEUI_AUTO=0 +heltec_wireless_tracker.menu.LORAWAN_DEVEUI.1=Generate By ChipID +heltec_wireless_tracker.menu.LORAWAN_DEVEUI.1.build.LORAWAN_DEVEUI_AUTO=1 + +heltec_wireless_tracker.menu.LORAWAN_PREAMBLE_LENGTH.0=8(default) +heltec_wireless_tracker.menu.LORAWAN_PREAMBLE_LENGTH.0.build.LORAWAN_PREAMBLE_LENGTH=8 +heltec_wireless_tracker.menu.LORAWAN_PREAMBLE_LENGTH.1=16(For M00 and M00L) +heltec_wireless_tracker.menu.LORAWAN_PREAMBLE_LENGTH.1.build.LORAWAN_PREAMBLE_LENGTH=16 + +heltec_wireless_tracker.build.defines=-D{build.band} -DLoRaWAN_DEBUG_LEVEL={build.LoRaWanDebugLevel} -DACTIVE_REGION=LORAMAC_{build.band} -DLORAWAN_PREAMBLE_LENGTH={build.LORAWAN_PREAMBLE_LENGTH} -DLORAWAN_DEVEUI_AUTO={build.LORAWAN_DEVEUI_AUTO} -D{build.board} + +heltec_wireless_tracker.menu.EraseFlash.none=Disabled +heltec_wireless_tracker.menu.EraseFlash.none.upload.erase_cmd= +heltec_wireless_tracker.menu.EraseFlash.all=Enabled +heltec_wireless_tracker.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## + espectro32.name=ESPectro32 espectro32.bootloader.tool=esptool_py @@ -29364,7 +29536,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 +29549,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 +29591,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 +29613,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 @@ -29760,3 +29915,119 @@ nano_nora.menu.USBMode.hwcdc.build.openocdscript=esp32s3-builtin.cfg nano_nora.menu.USBMode.hwcdc.build.debugconfig=esp32s3-arduino.json ############################################################## + +makergo_c3_supermini.name=MakerGO ESP32 C3 SuperMini +makergo_c3_supermini.vid.0=0x303a +makergo_c3_supermini.pid.0=0x1001 + +makergo_c3_supermini.bootloader.tool=esptool_py +makergo_c3_supermini.bootloader.tool.default=esptool_py + +makergo_c3_supermini.upload.tool=esptool_py +makergo_c3_supermini.upload.tool.default=esptool_py +makergo_c3_supermini.upload.tool.network=esp_ota + +makergo_c3_supermini.upload.maximum_size=1310720 +makergo_c3_supermini.upload.maximum_data_size=327680 +makergo_c3_supermini.upload.flags= +makergo_c3_supermini.upload.extra_flags= +makergo_c3_supermini.upload.use_1200bps_touch=false +makergo_c3_supermini.upload.wait_for_upload_port=false + +makergo_c3_supermini.serial.disableDTR=true +makergo_c3_supermini.serial.disableRTS=true + +makergo_c3_supermini.build.tarch=riscv32 +makergo_c3_supermini.build.target=esp +makergo_c3_supermini.build.mcu=esp32c3 +makergo_c3_supermini.build.core=esp32 +makergo_c3_supermini.build.variant=makergo_c3_supermini +makergo_c3_supermini.build.board=MAKERGO_C3_SUPERMINI +makergo_c3_supermini.build.bootloader_addr=0x0 + +makergo_c3_supermini.build.cdc_on_boot=1 +makergo_c3_supermini.build.f_cpu=160000000L +makergo_c3_supermini.build.flash_size=4MB +makergo_c3_supermini.build.flash_freq=80m +makergo_c3_supermini.build.flash_mode=dio +makergo_c3_supermini.build.boot=qio +makergo_c3_supermini.build.partitions=default +makergo_c3_supermini.build.defines= + +makergo_c3_supermini.menu.CDCOnBoot.default=Enabled +makergo_c3_supermini.menu.CDCOnBoot.default.build.cdc_on_boot=1 +makergo_c3_supermini.menu.CDCOnBoot.dis_cdc=Disabled +makergo_c3_supermini.menu.CDCOnBoot.dis_cdc.build.cdc_on_boot=0 + +makergo_c3_supermini.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +makergo_c3_supermini.menu.PartitionScheme.default.build.partitions=default +makergo_c3_supermini.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +makergo_c3_supermini.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +makergo_c3_supermini.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +makergo_c3_supermini.menu.PartitionScheme.no_ota.build.partitions=no_ota +makergo_c3_supermini.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +makergo_c3_supermini.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +makergo_c3_supermini.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +makergo_c3_supermini.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +makergo_c3_supermini.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +makergo_c3_supermini.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +makergo_c3_supermini.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +makergo_c3_supermini.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +makergo_c3_supermini.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +makergo_c3_supermini.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +makergo_c3_supermini.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +makergo_c3_supermini.menu.PartitionScheme.huge_app.build.partitions=huge_app +makergo_c3_supermini.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 + + +makergo_c3_supermini.menu.CPUFreq.160=160MHz (WiFi) +makergo_c3_supermini.menu.CPUFreq.160.build.f_cpu=160000000L +makergo_c3_supermini.menu.CPUFreq.80=80MHz (WiFi) +makergo_c3_supermini.menu.CPUFreq.80.build.f_cpu=80000000L +makergo_c3_supermini.menu.CPUFreq.40=40MHz +makergo_c3_supermini.menu.CPUFreq.40.build.f_cpu=40000000L +makergo_c3_supermini.menu.CPUFreq.20=20MHz +makergo_c3_supermini.menu.CPUFreq.20.build.f_cpu=20000000L +makergo_c3_supermini.menu.CPUFreq.10=10MHz +makergo_c3_supermini.menu.CPUFreq.10.build.f_cpu=10000000L + + +makergo_c3_supermini.menu.FlashFreq.80=80MHz +makergo_c3_supermini.menu.FlashFreq.80.build.flash_freq=80m +makergo_c3_supermini.menu.FlashFreq.40=40MHz +makergo_c3_supermini.menu.FlashFreq.40.build.flash_freq=40m + +makergo_c3_supermini.menu.UploadSpeed.921600=921600 +makergo_c3_supermini.menu.UploadSpeed.921600.upload.speed=921600 +makergo_c3_supermini.menu.UploadSpeed.115200=115200 +makergo_c3_supermini.menu.UploadSpeed.115200.upload.speed=115200 +makergo_c3_supermini.menu.UploadSpeed.256000.windows=256000 +makergo_c3_supermini.menu.UploadSpeed.256000.upload.speed=256000 +makergo_c3_supermini.menu.UploadSpeed.230400.windows.upload.speed=256000 +makergo_c3_supermini.menu.UploadSpeed.230400=230400 +makergo_c3_supermini.menu.UploadSpeed.230400.upload.speed=230400 +makergo_c3_supermini.menu.UploadSpeed.460800.linux=460800 +makergo_c3_supermini.menu.UploadSpeed.460800.macosx=460800 +makergo_c3_supermini.menu.UploadSpeed.460800.upload.speed=460800 +makergo_c3_supermini.menu.UploadSpeed.512000.windows=512000 +makergo_c3_supermini.menu.UploadSpeed.512000.upload.speed=512000 + +makergo_c3_supermini.menu.DebugLevel.none=None +makergo_c3_supermini.menu.DebugLevel.none.build.code_debug=0 +makergo_c3_supermini.menu.DebugLevel.error=Error +makergo_c3_supermini.menu.DebugLevel.error.build.code_debug=1 +makergo_c3_supermini.menu.DebugLevel.warn=Warn +makergo_c3_supermini.menu.DebugLevel.warn.build.code_debug=2 +makergo_c3_supermini.menu.DebugLevel.info=Info +makergo_c3_supermini.menu.DebugLevel.info.build.code_debug=3 +makergo_c3_supermini.menu.DebugLevel.debug=Debug +makergo_c3_supermini.menu.DebugLevel.debug.build.code_debug=4 +makergo_c3_supermini.menu.DebugLevel.verbose=Verbose +makergo_c3_supermini.menu.DebugLevel.verbose.build.code_debug=5 + +makergo_c3_supermini.menu.EraseFlash.none=Disabled +makergo_c3_supermini.menu.EraseFlash.none.upload.erase_cmd= +makergo_c3_supermini.menu.EraseFlash.all=Enabled +makergo_c3_supermini.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## \ No newline at end of file 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(); diff --git a/cores/esp32/HEXBuilder.cpp b/cores/esp32/HEXBuilder.cpp new file mode 100644 index 00000000000..f3b0f7a9b3e --- /dev/null +++ b/cores/esp32/HEXBuilder.cpp @@ -0,0 +1,71 @@ +/* + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp32 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +static uint8_t hex_char_to_byte(uint8_t c) +{ + return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) : + (c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) : + (c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0x10; // unknown char is 16 +} + +size_t HEXBuilder::hex2bytes(unsigned char * out, size_t maxlen, String &in) { + return hex2bytes(out, maxlen, in.c_str()); +} + +size_t HEXBuilder::hex2bytes(unsigned char * out, size_t maxlen, const char * in) { + size_t len = 0; + for(;*in;in++) { + uint8_t c = hex_char_to_byte(*in); + // Silently skip anything unknown. + if (c > 15) + continue; + + if (len & 1) { + if (len/2 < maxlen) + out[len/2] |= c; + } else { + if (len/2 < maxlen) + out[len/2] = c<<4; + } + len++; + } + return (len + 1)/2; +} + +size_t HEXBuilder::bytes2hex(char * out, size_t maxlen, const unsigned char * in, size_t len) { + for(size_t i = 0; i < len; i++) { + if (i*2 + 1 < maxlen) { + sprintf(out + (i * 2), "%02x", in[i]); + } + } + return len * 2 + 1; +} + +String HEXBuilder::bytes2hex(const unsigned char * in, size_t len) { + size_t maxlen = len * 2 + 1; + char * out = (char *) malloc(maxlen); + if (!out) return String(); + bytes2hex(out, maxlen, in, len); + String ret = String(out); + free(out); + return ret; +} diff --git a/cores/esp32/HEXBuilder.h b/cores/esp32/HEXBuilder.h new file mode 100644 index 00000000000..b3ec02ae267 --- /dev/null +++ b/cores/esp32/HEXBuilder.h @@ -0,0 +1,34 @@ +/* + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp32 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef HEXBuilder_h +#define HEXBuilder_h + +#include +#include + +class HEXBuilder { +public: + static size_t hex2bytes(unsigned char * out, size_t maxlen, String & in); + static size_t hex2bytes(unsigned char * out, size_t maxlen, const char * in); + + static String bytes2hex(const unsigned char * in, size_t len); + static size_t bytes2hex(char * out, size_t maxlen, const unsigned char * in, size_t len); +}; +#endif diff --git a/cores/esp32/HardwareSerial.cpp b/cores/esp32/HardwareSerial.cpp index 7792b8e039b..10a6bc1ca1d 100644 --- a/cores/esp32/HardwareSerial.cpp +++ b/cores/esp32/HardwareSerial.cpp @@ -114,7 +114,7 @@ _eventTask(NULL) HardwareSerial::~HardwareSerial() { - end(true); // explicit Full UART termination + end(); // explicit Full UART termination #if !CONFIG_DISABLE_HAL_LOCKS if(_lock != NULL){ vSemaphoreDelete(_lock); @@ -329,16 +329,22 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in // map logical pins to GPIO numbers rxPin = digitalPinToGPIONumber(rxPin); txPin = digitalPinToGPIONumber(txPin); + // IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified. + // it will detach previous UART attached pins - if(_uart) { - // in this case it is a begin() over a previous begin() - maybe to change baud rate - // thus do not disable debug output - end(false); // disables IDF UART driver and UART event Task + sets _uart to NULL + // indicates that uartbegin() has to initilize a new IDF driver + if (_testUartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd)) { + _destroyEventTask(); // when IDF uart driver must be restarted, _eventTask must finish too } // IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified. // it will detach previous UART attached pins _uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd); + if (_uart == NULL) { + log_e("UART driver failed to start. Please check the logs."); + HSERIAL_MUTEX_UNLOCK(); + return; + } if (!baud) { // using baud rate as zero, forces it to try to detect the current baud rate in place uartStartDetectBaudrate(_uart); @@ -348,11 +354,14 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in yield(); } - end(false); // disables IDF UART driver and UART event Task + sets _uart to NULL - if(detectedBaudRate) { delay(100); // Give some time... _uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd); + if (_uart == NULL) { + log_e("UART driver failed to start. Please check the logs."); + HSERIAL_MUTEX_UNLOCK(); + return; + } } else { log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible"); _uart = NULL; @@ -389,22 +398,17 @@ void HardwareSerial::updateBaudRate(unsigned long baud) uartSetBaudRate(_uart, baud); } -void HardwareSerial::end(bool fullyTerminate) +void HardwareSerial::end() { // default Serial.end() will completely disable HardwareSerial, // including any tasks or debug message channel (log_x()) - but not for IDF log messages! - if(fullyTerminate) { - _onReceiveCB = NULL; - _onReceiveErrorCB = NULL; - if (uartGetDebug() == _uart_nr) { - uartSetDebug(0); - } - _rxFIFOFull = 0; - uartEnd(_uart_nr); // fully detach all pins and delete the UART driver - } else { - // do not invalidate callbacks, detach pins, invalidate DBG output - uart_driver_delete(_uart_nr); + _onReceiveCB = NULL; + _onReceiveErrorCB = NULL; + if (uartGetDebug() == _uart_nr) { + uartSetDebug(0); } + _rxFIFOFull = 0; + uartEnd(_uart_nr); // fully detach all pins and delete the UART driver _destroyEventTask(); // when IDF uart driver is deleted, _eventTask must finish too _uart = NULL; } @@ -564,3 +568,4 @@ size_t HardwareSerial::setTxBufferSize(size_t new_size) { _txBufferSize = new_size; return _txBufferSize; } + diff --git a/cores/esp32/HardwareSerial.h b/cores/esp32/HardwareSerial.h index 688655f4f21..0bf72d2aff2 100644 --- a/cores/esp32/HardwareSerial.h +++ b/cores/esp32/HardwareSerial.h @@ -252,7 +252,7 @@ class HardwareSerial: public Stream // invert will invert RX/TX polarity // rxfifo_full_thrhd if the UART Flow Control Threshold in the UART FIFO (max 127) void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112); - void end(bool fullyTerminate = true); + void end(void); void updateBaudRate(unsigned long baud); int available(void); int availableForWrite(void); diff --git a/cores/esp32/HashBuilder.h b/cores/esp32/HashBuilder.h new file mode 100644 index 00000000000..ce6f1f1af42 --- /dev/null +++ b/cores/esp32/HashBuilder.h @@ -0,0 +1,60 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef HashBuilder_h +#define HashBuilder_h + +#include +#include + +#include "HEXBuilder.h" + +class HashBuilder : public HEXBuilder +{ +public: + virtual ~HashBuilder() {} + virtual void begin() = 0; + + virtual void add(uint8_t* data, size_t len) = 0; + virtual void add(const char* data) + { + add((uint8_t*)data, strlen(data)); + } + virtual void add(char* data) + { + add((const char*)data); + } + virtual void add(String data) + { + add(data.c_str()); + } + + virtual void addHexString(const char* data) = 0; + virtual void addHexString(char* data) + { + addHexString((const char*)data); + } + virtual void addHexString(String data) + { + addHexString(data.c_str()); + } + + virtual bool addStream(Stream& stream, const size_t maxLen) = 0; + virtual void calculate() = 0; + virtual void getBytes(uint8_t* output) = 0; + virtual void getChars(char* output) = 0; + virtual String toString() = 0; +}; + +#endif diff --git a/cores/esp32/IPAddress.cpp b/cores/esp32/IPAddress.cpp index 002dccb3fcd..0fa5affdea7 100644 --- a/cores/esp32/IPAddress.cpp +++ b/cores/esp32/IPAddress.cpp @@ -1,38 +1,40 @@ /* - IPAddress.cpp - Base class that provides IPAddress - Copyright (c) 2011 Adrian McEwen. All right reserved. + IPAddress.cpp - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ -#include -#include -#include -#include +#include "IPAddress.h" +#include "Print.h" +#include "lwip/netif.h" +#include "StreamString.h" IPAddress::IPAddress() : IPAddress(IPv4) {} IPAddress::IPAddress(IPType ip_type) { _type = ip_type; + _zone = IP6_NO_ZONE; 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) { _type = IPv4; + _zone = IP6_NO_ZONE; memset(_address.bytes, 0, sizeof(_address.bytes)); _address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet; _address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet; @@ -40,7 +42,7 @@ IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_oc _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) { +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, uint8_t z) { _type = IPv6; _address.bytes[0] = o1; _address.bytes[1] = o2; @@ -58,12 +60,14 @@ IPAddress::IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, _address.bytes[13] = o14; _address.bytes[14] = o15; _address.bytes[15] = o16; + _zone = z; } IPAddress::IPAddress(uint32_t address) { // IPv4 only _type = IPv4; + _zone = IP6_NO_ZONE; memset(_address.bytes, 0, sizeof(_address.bytes)); _address.dword[IPADDRESS_V4_DWORD_INDEX] = address; @@ -78,14 +82,16 @@ IPAddress::IPAddress(uint32_t address) IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {} -IPAddress::IPAddress(IPType ip_type, const uint8_t *address) +IPAddress::IPAddress(IPType ip_type, const uint8_t *address, uint8_t z) { _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)); + _zone = 0; } else { memcpy(_address.bytes, address, sizeof(_address.bytes)); + _zone = z; } } @@ -94,151 +100,20 @@ IPAddress::IPAddress(const char *address) fromString(address); } -IPAddress& IPAddress::operator=(const uint8_t *address) -{ - // 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) -{ - // 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 -{ - // 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; - - 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[IPADDRESS_V4_BYTES_INDEX + 3], DEC); - return n; -} - -String IPAddress::toString4() const +IPAddress::IPAddress(const IPAddress& address) { - char szRet[16]; - 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); + *this = address; } -String IPAddress::toString6() const +String IPAddress::toString(bool includeZone) const { StreamString s; - s.reserve(40); - printTo(s); - return s; + printTo(s, includeZone); + return String(s); } -String IPAddress::toString() const -{ - if (_type == IPv4) { - return toString4(); - } else { - return toString6(); - } -} - -bool IPAddress::fromString(const char *address) -{ - if (!fromString4(address)) - { +bool IPAddress::fromString(const char *address) { + if (!fromString4(address)) { return fromString6(address); } return true; @@ -336,6 +211,12 @@ bool IPAddress::fromString6(const char *address) { colons++; acc = 0; } + else if (c == '%') { + _zone = netif_name_to_index(address); + while(*address != '\0'){ + address++; + } + } else // Invalid char return false; @@ -364,5 +245,186 @@ bool IPAddress::fromString6(const char *address) { return true; } -// declared one time - as external in IPAddress.h -IPAddress INADDR_NONE(0, 0, 0, 0); +IPAddress& IPAddress::operator=(const uint8_t *address) +{ + // 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) +{ + // 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; +} + +IPAddress& IPAddress::operator=(const IPAddress& address){ + _type = address.type(); + _zone = address.zone(); + memcpy(_address.bytes, address._address.bytes, sizeof(_address.bytes)); + 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 +{ + // 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 +{ + return printTo(p, false); +} + +size_t IPAddress::printTo(Print& p, bool includeZone) const +{ + size_t n = 0; + + 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(':'); + } + } + // add a zone if zone-id is non-zero + if(_zone > 0 && includeZone){ + n += p.print('%'); + char if_name[NETIF_NAMESIZE]; + netif_index_to_name(_zone, if_name); + n += p.print(if_name); + } + 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[IPADDRESS_V4_BYTES_INDEX + 3], DEC); + return n; +} + +IPAddress::IPAddress(const ip_addr_t *addr){ + from_ip_addr_t(addr); +} + +void IPAddress::to_ip_addr_t(ip_addr_t* addr) const { + if(_type == IPv6){ + addr->type = IPADDR_TYPE_V6; + addr->u_addr.ip6.addr[0] = _address.dword[0]; + addr->u_addr.ip6.addr[1] = _address.dword[1]; + addr->u_addr.ip6.addr[2] = _address.dword[2]; + addr->u_addr.ip6.addr[3] = _address.dword[3]; +#if LWIP_IPV6_SCOPES + addr->u_addr.ip6.zone = _zone; +#endif /* LWIP_IPV6_SCOPES */ + } else { + addr->type = IPADDR_TYPE_V4; + addr->u_addr.ip4.addr = _address.dword[IPADDRESS_V4_DWORD_INDEX]; + } +} + +IPAddress& IPAddress::from_ip_addr_t(const ip_addr_t* addr){ + if(addr->type == IPADDR_TYPE_V6){ + _type = IPv6; + _address.dword[0] = addr->u_addr.ip6.addr[0]; + _address.dword[1] = addr->u_addr.ip6.addr[1]; + _address.dword[2] = addr->u_addr.ip6.addr[2]; + _address.dword[3] = addr->u_addr.ip6.addr[3]; +#if LWIP_IPV6_SCOPES + _zone = addr->u_addr.ip6.zone; +#endif /* LWIP_IPV6_SCOPES */ + } else { + _type = IPv4; + _address.dword[IPADDRESS_V4_DWORD_INDEX] = addr->u_addr.ip4.addr; + } + return *this; +} + +esp_ip6_addr_type_t IPAddress::addr_type() const { + if(_type != IPv6){ + return ESP_IP6_ADDR_IS_UNKNOWN; + } + ip_addr_t addr; + to_ip_addr_t(&addr); + return esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)(&(addr.u_addr.ip6))); +} + +const IPAddress IN6ADDR_ANY(IPv6); +const IPAddress INADDR_NONE(0,0,0,0); diff --git a/cores/esp32/IPAddress.h b/cores/esp32/IPAddress.h index 329ca92afe9..0a3efd01c8d 100644 --- a/cores/esp32/IPAddress.h +++ b/cores/esp32/IPAddress.h @@ -1,82 +1,83 @@ /* - IPAddress.h - Base class that provides IPAddress - Copyright (c) 2011 Adrian McEwen. All right reserved. + IPAddress.h - Base class that provides IPAddress + Copyright (c) 2011 Adrian McEwen. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ -#ifndef IPAddress_h -#define IPAddress_h +#pragma once #include -#include -#include - -// A class to make it easier to handle and pass around IP addresses +#include "Printable.h" +#include "WString.h" +#include "lwip/ip_addr.h" +#include "esp_netif_ip_addr.h" #define IPADDRESS_V4_BYTES_INDEX 12 #define IPADDRESS_V4_DWORD_INDEX 3 -enum IPType -{ +// A class to make it easier to handle and pass around IP addresses + +enum IPType { IPv4, IPv6 }; -class IPAddress: public Printable -{ +class IPAddress : public Printable { private: union { uint8_t bytes[16]; uint32_t dword[4]; } _address; IPType _type; + uint8_t _zone; // 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 // be used when you know that the usage of the returned uint8_t* will be transient and not // stored. - uint8_t* raw_address() - { - return _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _address.bytes; - } + uint8_t* raw_address() { return _type == IPv4 ? &_address.bytes[IPADDRESS_V4_BYTES_INDEX] : _address.bytes; } public: // Constructors + + // Default IPv4 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(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, uint8_t z=0); + // IPv4; see implementation note IPAddress(uint32_t address); + // Default IPv4 IPAddress(const uint8_t *address); - IPAddress(IPType ip_type, const uint8_t *address); + IPAddress(IPType ip_type, const uint8_t *address, uint8_t z=0); // If IPv4 fails tries IPv6 see fromString function IPAddress(const char *address); - virtual ~IPAddress() {} + IPAddress(const IPAddress& address); 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 - // uint32_t is expected - operator uint32_t() const - { - return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0; - } + // Overloaded cast operator to allow IPAddress objects to be used where a uint32_t is expected + // NOTE: IPv4 only; see implementation note + operator uint32_t() const { return _type == IPv4 ? _address.dword[IPADDRESS_V4_DWORD_INDEX] : 0; }; bool operator==(const IPAddress& addr) const; + bool operator!=(const IPAddress& addr) const { return !(*this == addr); }; + + // NOTE: IPv4 only; we don't know the length of the pointer bool operator==(const uint8_t* addr) const; // Overloaded index operator to allow getting and setting individual octets of the address @@ -84,31 +85,35 @@ class IPAddress: public Printable uint8_t& operator[](int index); // Overloaded copy operators to allow initialisation of IPAddress objects from other types + // NOTE: IPv4 only IPAddress& operator=(const uint8_t *address); + // NOTE: IPv4 only; see implementation note IPAddress& operator=(uint32_t address); // If IPv4 fails tries IPv6 see fromString function IPAddress& operator=(const char *address); + IPAddress& operator=(const IPAddress& address); virtual size_t printTo(Print& p) const; - String toString() const; + String toString(bool includeZone = false) const; IPType type() const { return _type; } - friend class EthernetClass; + // Espresif LwIP conversions + IPAddress(const ip_addr_t *addr); + void to_ip_addr_t(ip_addr_t* addr) const; + IPAddress& from_ip_addr_t(const ip_addr_t* addr); + esp_ip6_addr_type_t addr_type() const; + uint8_t zone() const { return (type() == IPv6)?_zone:0; } + size_t printTo(Print& p, bool includeZone) const; + 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 +extern const IPAddress IN6ADDR_ANY; +extern const IPAddress INADDR_NONE; diff --git a/cores/esp32/IPv6Address.cpp b/cores/esp32/IPv6Address.cpp deleted file mode 100644 index 7d3c0de5f53..00000000000 --- a/cores/esp32/IPv6Address.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - IPv6Address.cpp - Base class that provides IPv6Address - Copyright (c) 2011 Adrian McEwen. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include - -IPv6Address::IPv6Address() -{ - memset(_address.bytes, 0, sizeof(_address.bytes)); -} - -IPv6Address::IPv6Address(const uint8_t *address) -{ - memcpy(_address.bytes, address, sizeof(_address.bytes)); -} - -IPv6Address::IPv6Address(const uint32_t *address) -{ - memcpy(_address.bytes, (const uint8_t *)address, sizeof(_address.bytes)); -} - -IPv6Address& IPv6Address::operator=(const uint8_t *address) -{ - memcpy(_address.bytes, address, sizeof(_address.bytes)); - return *this; -} - -bool IPv6Address::operator==(const uint8_t* addr) const -{ - return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0; -} - -size_t IPv6Address::printTo(Print& p) const -{ - size_t n = 0; - for(int i = 0; i < 16; i+=2) { - if(i){ - n += p.print(':'); - } - n += p.printf("%02x", _address.bytes[i]); - n += p.printf("%02x", _address.bytes[i+1]); - - } - return n; -} - -String IPv6Address::toString() const -{ - char szRet[40]; - sprintf(szRet,"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", - _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3], - _address.bytes[4], _address.bytes[5], _address.bytes[6], _address.bytes[7], - _address.bytes[8], _address.bytes[9], _address.bytes[10], _address.bytes[11], - _address.bytes[12], _address.bytes[13], _address.bytes[14], _address.bytes[15]); - return String(szRet); -} - -bool IPv6Address::fromString(const char *address) -{ - //format 0011:2233:4455:6677:8899:aabb:ccdd:eeff - if(strlen(address) != 39){ - return false; - } - char * pos = (char *)address; - size_t i = 0; - for(i = 0; i < 16; i+=2) { - if(!sscanf(pos, "%2hhx", &_address.bytes[i]) || !sscanf(pos+2, "%2hhx", &_address.bytes[i+1])){ - return false; - } - pos += 5; - } - return true; -} diff --git a/cores/esp32/IPv6Address.h b/cores/esp32/IPv6Address.h deleted file mode 100644 index e61d0e7b371..00000000000 --- a/cores/esp32/IPv6Address.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - IPv6Address.h - Base class that provides IPv6Address - Copyright (c) 2011 Adrian McEwen. All right reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef IPv6Address_h -#define IPv6Address_h - -#include -#include -#include - -// A class to make it easier to handle and pass around IP addresses - -class IPv6Address: public Printable -{ -private: - union { - uint8_t bytes[16]; // IPv4 address - uint32_t dword[4]; - } _address; - - // 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 - // be used when you know that the usage of the returned uint8_t* will be transient and not - // stored. - uint8_t* raw_address() - { - return _address.bytes; - } - -public: - // Constructors - IPv6Address(); - IPv6Address(const uint8_t *address); - IPv6Address(const uint32_t *address); - virtual ~IPv6Address() {} - - bool fromString(const char *address); - bool fromString(const String &address) { return fromString(address.c_str()); } - - operator const uint8_t*() const - { - return _address.bytes; - } - operator const uint32_t*() const - { - return _address.dword; - } - bool operator==(const IPv6Address& addr) const - { - return (_address.dword[0] == addr._address.dword[0]) - && (_address.dword[1] == addr._address.dword[1]) - && (_address.dword[2] == addr._address.dword[2]) - && (_address.dword[3] == addr._address.dword[3]); - } - 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]; - } - - // Overloaded copy operators to allow initialisation of IPv6Address objects from other types - IPv6Address& operator=(const uint8_t *address); - - virtual size_t printTo(Print& p) const; - String toString() const; - - friend class UDP; - friend class Client; - friend class Server; -}; - -#endif diff --git a/cores/esp32/MD5Builder.cpp b/cores/esp32/MD5Builder.cpp index e242c3763b9..2198d06a27e 100644 --- a/cores/esp32/MD5Builder.cpp +++ b/cores/esp32/MD5Builder.cpp @@ -16,39 +16,30 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + #include +#include #include -static uint8_t hex_char_to_byte(uint8_t c) -{ - return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) : - (c >= 'A' && c <= 'F') ? (c - ((uint8_t)'A' - 0xA)) : - (c >= '0' && c<= '9') ? (c - (uint8_t)'0') : 0; -} - void MD5Builder::begin(void) { memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN); esp_rom_md5_init(&_ctx); } -void MD5Builder::add(uint8_t * data, uint16_t len) +void MD5Builder::add(uint8_t * data, size_t len) { esp_rom_md5_update(&_ctx, data, len); } void MD5Builder::addHexString(const char * data) { - uint16_t i, len = strlen(data); + size_t len = strlen(data); uint8_t * tmp = (uint8_t*)malloc(len/2); if(tmp == NULL) { return; } - for(i=0; i #include @@ -25,41 +27,27 @@ #include "esp_system.h" #include "esp_rom_md5.h" -class MD5Builder +#include "HashBuilder.h" + +class MD5Builder : public HashBuilder { private: md5_context_t _ctx; uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN]; public: - void begin(void); - void add(uint8_t * data, uint16_t len); - void add(const char * data) - { - add((uint8_t*)data, strlen(data)); - } - void add(char * data) - { - add((const char*)data); - } - void add(String data) - { - add(data.c_str()); - } - void addHexString(const char * data); - void addHexString(char * data) - { - addHexString((const char*)data); - } - void addHexString(String data) - { - addHexString(data.c_str()); - } - bool addStream(Stream & stream, const size_t maxLen); - void calculate(void); - void getBytes(uint8_t * output); - void getChars(char * output); - String toString(void); -}; + void begin(void) override; + using HashBuilder::add; + void add(uint8_t * data, size_t len) override; + + using HashBuilder::addHexString; + void addHexString(const char * data) override; + + bool addStream(Stream & stream, const size_t maxLen) override; + void calculate(void) override; + void getBytes(uint8_t * output) override; + void getChars(char * output) override; + String toString(void) override; +}; #endif diff --git a/cores/esp32/SHA1Builder.cpp b/cores/esp32/SHA1Builder.cpp new file mode 100644 index 00000000000..34f93271321 --- /dev/null +++ b/cores/esp32/SHA1Builder.cpp @@ -0,0 +1,367 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + * Modified for esp32 by Lucas Saavedra Vaz on 11 Jan 2024 + */ + +#include +#include + +// 32-bit integer manipulation macros (big endian) + +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ((uint32_t) (b)[(i) ] << 24) \ + | ((uint32_t) (b)[(i) + 1] << 16) \ + | ((uint32_t) (b)[(i) + 2] << 8) \ + | ((uint32_t) (b)[(i) + 3] ); \ +} +#endif + +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ((n) >> 24); \ + (b)[(i) + 1] = (uint8_t) ((n) >> 16); \ + (b)[(i) + 2] = (uint8_t) ((n) >> 8); \ + (b)[(i) + 3] = (uint8_t) ((n) ); \ +} +#endif + +// Constants + +static const uint8_t sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// Private methods + +void SHA1Builder::process(const uint8_t* data) +{ + uint32_t temp, W[16], A, B, C, D, E; + + GET_UINT32_BE(W[ 0], data, 0); + GET_UINT32_BE(W[ 1], data, 4); + GET_UINT32_BE(W[ 2], data, 8); + GET_UINT32_BE(W[ 3], data, 12); + GET_UINT32_BE(W[ 4], data, 16); + GET_UINT32_BE(W[ 5], data, 20); + GET_UINT32_BE(W[ 6], data, 24); + GET_UINT32_BE(W[ 7], data, 28); + GET_UINT32_BE(W[ 8], data, 32); + GET_UINT32_BE(W[ 9], data, 36); + GET_UINT32_BE(W[10], data, 40); + GET_UINT32_BE(W[11], data, 44); + GET_UINT32_BE(W[12], data, 48); + GET_UINT32_BE(W[13], data, 52); + GET_UINT32_BE(W[14], data, 56); + GET_UINT32_BE(W[15], data, 60); + +#define sha1_S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define sha1_R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + (W[t & 0x0F] = sha1_S(temp,1)) \ +) + +#define sha1_P(a,b,c,d,e,x) \ +{ \ + e += sha1_S(a,5) + sha1_F(b,c,d) + sha1_K + x; b = sha1_S(b,30); \ +} + + A = state[0]; + B = state[1]; + C = state[2]; + D = state[3]; + E = state[4]; + +#define sha1_F(x,y,z) (z ^ (x & (y ^ z))) +#define sha1_K 0x5A827999 + + sha1_P(A, B, C, D, E, W[0]); + sha1_P(E, A, B, C, D, W[1]); + sha1_P(D, E, A, B, C, W[2]); + sha1_P(C, D, E, A, B, W[3]); + sha1_P(B, C, D, E, A, W[4]); + sha1_P(A, B, C, D, E, W[5]); + sha1_P(E, A, B, C, D, W[6]); + sha1_P(D, E, A, B, C, W[7]); + sha1_P(C, D, E, A, B, W[8]); + sha1_P(B, C, D, E, A, W[9]); + sha1_P(A, B, C, D, E, W[10]); + sha1_P(E, A, B, C, D, W[11]); + sha1_P(D, E, A, B, C, W[12]); + sha1_P(C, D, E, A, B, W[13]); + sha1_P(B, C, D, E, A, W[14]); + sha1_P(A, B, C, D, E, W[15]); + sha1_P(E, A, B, C, D, sha1_R(16)); + sha1_P(D, E, A, B, C, sha1_R(17)); + sha1_P(C, D, E, A, B, sha1_R(18)); + sha1_P(B, C, D, E, A, sha1_R(19)); + +#undef sha1_K +#undef sha1_F + +#define sha1_F(x,y,z) (x ^ y ^ z) +#define sha1_K 0x6ED9EBA1 + + sha1_P(A, B, C, D, E, sha1_R(20)); + sha1_P(E, A, B, C, D, sha1_R(21)); + sha1_P(D, E, A, B, C, sha1_R(22)); + sha1_P(C, D, E, A, B, sha1_R(23)); + sha1_P(B, C, D, E, A, sha1_R(24)); + sha1_P(A, B, C, D, E, sha1_R(25)); + sha1_P(E, A, B, C, D, sha1_R(26)); + sha1_P(D, E, A, B, C, sha1_R(27)); + sha1_P(C, D, E, A, B, sha1_R(28)); + sha1_P(B, C, D, E, A, sha1_R(29)); + sha1_P(A, B, C, D, E, sha1_R(30)); + sha1_P(E, A, B, C, D, sha1_R(31)); + sha1_P(D, E, A, B, C, sha1_R(32)); + sha1_P(C, D, E, A, B, sha1_R(33)); + sha1_P(B, C, D, E, A, sha1_R(34)); + sha1_P(A, B, C, D, E, sha1_R(35)); + sha1_P(E, A, B, C, D, sha1_R(36)); + sha1_P(D, E, A, B, C, sha1_R(37)); + sha1_P(C, D, E, A, B, sha1_R(38)); + sha1_P(B, C, D, E, A, sha1_R(39)); + +#undef sha1_K +#undef sha1_F + +#define sha1_F(x,y,z) ((x & y) | (z & (x | y))) +#define sha1_K 0x8F1BBCDC + + sha1_P(A, B, C, D, E, sha1_R(40)); + sha1_P(E, A, B, C, D, sha1_R(41)); + sha1_P(D, E, A, B, C, sha1_R(42)); + sha1_P(C, D, E, A, B, sha1_R(43)); + sha1_P(B, C, D, E, A, sha1_R(44)); + sha1_P(A, B, C, D, E, sha1_R(45)); + sha1_P(E, A, B, C, D, sha1_R(46)); + sha1_P(D, E, A, B, C, sha1_R(47)); + sha1_P(C, D, E, A, B, sha1_R(48)); + sha1_P(B, C, D, E, A, sha1_R(49)); + sha1_P(A, B, C, D, E, sha1_R(50)); + sha1_P(E, A, B, C, D, sha1_R(51)); + sha1_P(D, E, A, B, C, sha1_R(52)); + sha1_P(C, D, E, A, B, sha1_R(53)); + sha1_P(B, C, D, E, A, sha1_R(54)); + sha1_P(A, B, C, D, E, sha1_R(55)); + sha1_P(E, A, B, C, D, sha1_R(56)); + sha1_P(D, E, A, B, C, sha1_R(57)); + sha1_P(C, D, E, A, B, sha1_R(58)); + sha1_P(B, C, D, E, A, sha1_R(59)); + +#undef sha1_K +#undef sha1_F + +#define sha1_F(x,y,z) (x ^ y ^ z) +#define sha1_K 0xCA62C1D6 + + sha1_P(A, B, C, D, E, sha1_R(60)); + sha1_P(E, A, B, C, D, sha1_R(61)); + sha1_P(D, E, A, B, C, sha1_R(62)); + sha1_P(C, D, E, A, B, sha1_R(63)); + sha1_P(B, C, D, E, A, sha1_R(64)); + sha1_P(A, B, C, D, E, sha1_R(65)); + sha1_P(E, A, B, C, D, sha1_R(66)); + sha1_P(D, E, A, B, C, sha1_R(67)); + sha1_P(C, D, E, A, B, sha1_R(68)); + sha1_P(B, C, D, E, A, sha1_R(69)); + sha1_P(A, B, C, D, E, sha1_R(70)); + sha1_P(E, A, B, C, D, sha1_R(71)); + sha1_P(D, E, A, B, C, sha1_R(72)); + sha1_P(C, D, E, A, B, sha1_R(73)); + sha1_P(B, C, D, E, A, sha1_R(74)); + sha1_P(A, B, C, D, E, sha1_R(75)); + sha1_P(E, A, B, C, D, sha1_R(76)); + sha1_P(D, E, A, B, C, sha1_R(77)); + sha1_P(C, D, E, A, B, sha1_R(78)); + sha1_P(B, C, D, E, A, sha1_R(79)); + +#undef sha1_K +#undef sha1_F + + state[0] += A; + state[1] += B; + state[2] += C; + state[3] += D; + state[4] += E; +} + +// Public methods + +void SHA1Builder::begin(void) +{ + total[0] = 0; + total[1] = 0; + + state[0] = 0x67452301; + state[1] = 0xEFCDAB89; + state[2] = 0x98BADCFE; + state[3] = 0x10325476; + state[4] = 0xC3D2E1F0; + + memset(buffer, 0x00, sizeof(buffer)); + memset(hash, 0x00, sizeof(hash)); +} + +void SHA1Builder::add(uint8_t* data, size_t len) +{ + size_t fill; + uint32_t left; + + if(len == 0) + { + return; + } + + left = total[0] & 0x3F; + fill = 64 - left; + + total[0] += (uint32_t) len; + total[0] &= 0xFFFFFFFF; + + if(total[0] < (uint32_t) len) + { + total[1]++; + } + + if(left && len >= fill) + { + memcpy((void *) (buffer + left), data, fill); + process(buffer); + data += fill; + len -= fill; + left = 0; + } + + while(len >= 64) + { + process(data); + data += 64; + len -= 64; + } + + if(len > 0) { + memcpy((void *) (buffer + left), data, len); + } +} + +void SHA1Builder::addHexString(const char * data) +{ + uint16_t len = strlen(data); + uint8_t * tmp = (uint8_t*)malloc(len/2); + if(tmp == NULL) { + return; + } + hex2bytes(tmp, len/2, data); + add(tmp, len/2); + free(tmp); +} + +bool SHA1Builder::addStream(Stream & stream, const size_t maxLen) +{ + const int buf_size = 512; + int maxLengthLeft = maxLen; + uint8_t * buf = (uint8_t*) malloc(buf_size); + + if(!buf) { + return false; + } + + int bytesAvailable = stream.available(); + while((bytesAvailable > 0) && (maxLengthLeft > 0)) { + + // determine number of bytes to read + int readBytes = bytesAvailable; + if(readBytes > maxLengthLeft) { + readBytes = maxLengthLeft ; // read only until max_len + } + if(readBytes > buf_size) { + readBytes = buf_size; // not read more the buffer can handle + } + + // read data and check if we got something + int numBytesRead = stream.readBytes(buf, readBytes); + if(numBytesRead< 1) { + free(buf); + return false; + } + + // Update SHA1 with buffer payload + add(buf, numBytesRead); + + // update available number of bytes + maxLengthLeft -= numBytesRead; + bytesAvailable = stream.available(); + } + free(buf); + return true; +} + +void SHA1Builder::calculate(void) +{ + uint32_t last, padn; + uint32_t high, low; + uint8_t msglen[8]; + + high = (total[0] >> 29) | (total[1] << 3); + low = (total[0] << 3); + + PUT_UINT32_BE(high, msglen, 0); + PUT_UINT32_BE(low, msglen, 4); + + last = total[0] & 0x3F; + padn = (last < 56) ? (56 - last) : (120 - last); + + add((uint8_t*)sha1_padding, padn); + add(msglen, 8); + + PUT_UINT32_BE(state[0], hash, 0); + PUT_UINT32_BE(state[1], hash, 4); + PUT_UINT32_BE(state[2], hash, 8); + PUT_UINT32_BE(state[3], hash, 12); + PUT_UINT32_BE(state[4], hash, 16); +} + +void SHA1Builder::getBytes(uint8_t * output) +{ + memcpy(output, hash, SHA1_HASH_SIZE); +} + +void SHA1Builder::getChars(char * output) +{ + bytes2hex(output, SHA1_HASH_SIZE*2+1, hash, SHA1_HASH_SIZE); +} + +String SHA1Builder::toString(void) +{ + char out[(SHA1_HASH_SIZE * 2) + 1]; + getChars(out); + return String(out); +} diff --git a/cores/esp32/SHA1Builder.h b/cores/esp32/SHA1Builder.h new file mode 100644 index 00000000000..4a0dfe0c100 --- /dev/null +++ b/cores/esp32/SHA1Builder.h @@ -0,0 +1,51 @@ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SHA1Builder_h +#define SHA1Builder_h + +#include +#include + +#include "HashBuilder.h" + +#define SHA1_HASH_SIZE 20 + +class SHA1Builder : public HashBuilder +{ +private: + uint32_t total[2]; /* number of bytes processed */ + uint32_t state[5]; /* intermediate digest state */ + unsigned char buffer[64]; /* data block being processed */ + uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */ + + void process(const uint8_t* data); + +public: + void begin() override; + + using HashBuilder::add; + void add(uint8_t* data, size_t len) override; + + using HashBuilder::addHexString; + void addHexString(const char* data) override; + + bool addStream(Stream& stream, const size_t maxLen) override; + void calculate() override; + void getBytes(uint8_t* output) override; + void getChars(char* output) override; + String toString() override; +}; + +#endif diff --git a/cores/esp32/StreamString.h b/cores/esp32/StreamString.h index dbdf3fb0097..fa983786c70 100644 --- a/cores/esp32/StreamString.h +++ b/cores/esp32/StreamString.h @@ -21,7 +21,8 @@ #ifndef STREAMSTRING_H_ #define STREAMSTRING_H_ - +#include "Stream.h" +#include "WString.h" class StreamString: public Stream, public String { 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); } } 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/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 3d3559f7dec..b22267fbc68 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -1,4 +1,4 @@ -// Copyright 2015-2023 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -33,19 +33,25 @@ #include "hal/gpio_hal.h" #include "esp_rom_gpio.h" -static int s_uart_debug_nr = 0; +static int s_uart_debug_nr = 0; // UART number for debug output struct uart_struct_t { #if !CONFIG_DISABLE_HAL_LOCKS - SemaphoreHandle_t lock; + SemaphoreHandle_t lock; // UART lock #endif - uint8_t num; - bool has_peek; - uint8_t peek_byte; - QueueHandle_t uart_event_queue; // export it by some uartGetEventQueue() function - int8_t _rxPin, _txPin, _ctsPin, _rtsPin; // UART GPIOs + uint8_t num; // UART number for IDF driver API + bool has_peek; // flag to indicate that there is a peek byte pending to be read + uint8_t peek_byte; // peek byte that has been read but not consumed + QueueHandle_t uart_event_queue; // export it by some uartGetEventQueue() function + // configuration data:: Arduino API tipical data + int8_t _rxPin, _txPin, _ctsPin, _rtsPin; // UART GPIOs + uint32_t _baudrate, _config; // UART baudrate and config + // UART ESP32 specific data + uint16_t _rx_buffer_size, _tx_buffer_size; // UART RX and TX buffer sizes + bool _inverted; // UART inverted signal + uint8_t _rxfifo_full_thrhd; // UART RX FIFO full threshold }; #if CONFIG_DISABLE_HAL_LOCKS @@ -54,12 +60,12 @@ struct uart_struct_t { #define UART_MUTEX_UNLOCK() static uart_t _uart_bus_array[] = { - {0, false, 0, NULL, -1, -1, -1, -1}, + {0, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #if SOC_UART_NUM > 1 - {1, false, 0, NULL, -1, -1, -1, -1}, + {1, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #endif #if SOC_UART_NUM > 2 - {2, false, 0, NULL, -1, -1, -1, -1}, + {2, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #endif }; @@ -69,12 +75,12 @@ static uart_t _uart_bus_array[] = { #define UART_MUTEX_UNLOCK() if(uart->lock != NULL) xSemaphoreGive(uart->lock) static uart_t _uart_bus_array[] = { - {NULL, 0, false, 0, NULL, -1, -1, -1, -1}, + {NULL, 0, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #if SOC_UART_NUM > 1 - {NULL, 1, false, 0, NULL, -1, -1, -1, -1}, + {NULL, 1, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #endif #if SOC_UART_NUM > 2 - {NULL, 2, false, 0, NULL, -1, -1, -1, -1}, + {NULL, 2, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #endif }; @@ -97,7 +103,12 @@ static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t // detaches pins and sets Peripheral Manager and UART information if (rxPin >= 0 && uart->_rxPin == rxPin && perimanGetPinBusType(rxPin) == ESP32_BUS_TYPE_UART_RX) { gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rxPin], PIN_FUNC_GPIO); - esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false); + // avoids causing BREAK in the UART line + if (uart->_inverted) { + esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false); + } else { + esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_HIGH, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false); + } uart->_rxPin = -1; // -1 means unassigned/detached if (!perimanClearPinBus(rxPin)) { retCode = false; @@ -355,28 +366,121 @@ bool uartSetHwFlowCtrlMode(uart_t *uart, uart_hw_flowcontrol_t mode, uint8_t thr return retCode; } -uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd) +// This helper function will return true if a new IDF UART driver needs to be restarted and false if the current one can continue its execution +bool _testUartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd) { if(uart_nr >= SOC_UART_NUM) { - return NULL; + return false; // no new driver has to be installed } uart_t* uart = &_uart_bus_array[uart_nr]; - + // verify if is necessary to restart the UART driver if (uart_is_driver_installed(uart_nr)) { - uartEnd(uart_nr); + // some parameters can't be changed unless we end the UART driver + if ( uart->_rx_buffer_size != rx_buffer_size || uart->_tx_buffer_size != tx_buffer_size || uart->_inverted != inverted || uart->_rxfifo_full_thrhd != rxfifo_full_thrhd) { + return true; // the current IDF UART driver must be terminated and a new driver shall be installed + } else { + return false; // The current IDF UART driver can continue its execution + } + } else { + return true; // no IDF UART driver is running and a new driver shall be installed } +} + +uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd) +{ + if(uart_nr >= SOC_UART_NUM) { + log_e("UART number is invalid, please use number from 0 to %u", SOC_UART_NUM - 1); + return NULL; // no new driver was installed + } + uart_t* uart = &_uart_bus_array[uart_nr]; + log_v("UART%d baud(%ld) Mode(%x) rxPin(%d) txPin(%d)", uart_nr, baudrate, config, rxPin, txPin); #if !CONFIG_DISABLE_HAL_LOCKS if(uart->lock == NULL) { uart->lock = xSemaphoreCreateMutex(); if(uart->lock == NULL) { log_e("HAL LOCK error."); - return NULL; + return NULL; // no new driver was installed } } #endif - UART_MUTEX_LOCK(); + if (uart_is_driver_installed(uart_nr)) { + log_v("UART%d Driver already installed.", uart_nr); + // some parameters can't be changed unless we end the UART driver + if ( uart->_rx_buffer_size != rx_buffer_size || uart->_tx_buffer_size != tx_buffer_size || uart->_inverted != inverted || uart->_rxfifo_full_thrhd != rxfifo_full_thrhd) { + log_v("UART%d changing buffer sizes or inverted signal or rxfifo_full_thrhd. IDF driver will be restarted", uart_nr); + uartEnd(uart_nr); + } else { + bool retCode = true; + UART_MUTEX_LOCK(); + //User may just want to change some parameters, such as baudrate, data length, parity, stop bits or pins + if (uart->_baudrate != baudrate) { + if (ESP_OK != uart_set_baudrate(uart_nr, baudrate)) { + log_e("UART%d changing baudrate failed.", uart_nr); + retCode = false; + } else { + log_v("UART%d changed baudrate to %d", uart_nr, baudrate); + uart->_baudrate = baudrate; + } + } + uart_word_length_t data_bits = (config & 0xc) >> 2; + uart_parity_t parity = config & 0x3; + uart_stop_bits_t stop_bits = (config & 0x30) >> 4; + if (retCode && (uart->_config & 0xc) >> 2 != data_bits) { + if (ESP_OK != uart_set_word_length(uart_nr, data_bits)) { + log_e("UART%d changing data length failed.", uart_nr); + retCode = false; + } else { + log_v("UART%d changed data length to %d", uart_nr, data_bits + 5); + } + } + if (retCode && (uart->_config & 0x3) != parity) { + if (ESP_OK != uart_set_parity(uart_nr, parity)) { + log_e("UART%d changing parity failed.", uart_nr); + retCode = false; + } else { + log_v("UART%d changed parity to %s", uart_nr, parity == 0 ? "NONE" : parity == 2 ? "EVEN" : "ODD"); + } + } + if (retCode && (uart->_config & 0xc30) >> 4 != stop_bits) { + if (ESP_OK != uart_set_stop_bits(uart_nr, stop_bits)) { + log_e("UART%d changing stop bits failed.", uart_nr); + retCode = false; + } else { + log_v("UART%d changed stop bits to %d", uart_nr, stop_bits == 3 ? 2 : 1); + } + } + if (retCode) uart->_config = config; + if (retCode && rxPin > 0 && uart->_rxPin != rxPin) { + retCode &= _uartDetachPins(uart_nr, uart->_rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + retCode &= _uartAttachPins(uart_nr, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (!retCode) { + log_e("UART%d changing RX pin failed.", uart_nr); + } else { + log_v("UART%d changed RX pin to %d", uart_nr, rxPin); + } + } + if (retCode && txPin > 0 && uart->_txPin != txPin) { + retCode &= _uartDetachPins(uart_nr, UART_PIN_NO_CHANGE, uart->_txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + retCode &= _uartAttachPins(uart_nr, UART_PIN_NO_CHANGE, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (!retCode) { + log_e("UART%d changing TX pin failed.", uart_nr); + } else { + log_v("UART%d changed TX pin to %d", uart_nr, txPin); + } + } + UART_MUTEX_UNLOCK(); + if (retCode) { + // UART driver was already working, just return the uart_t structure, syaing that no new driver was installed + return uart; + } + // if we reach this point, it means that we need to restart the UART driver + uartEnd(uart_nr); + } + } else { + log_v("UART%d not installed. Starting installation", uart_nr); + } uart_config_t uart_config; uart_config.data_bits = (config & 0xc) >> 2; uart_config.parity = (config & 0x3); @@ -386,7 +490,10 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx uart_config.baud_rate = baudrate; // CLK_APB for ESP32|S2|S3|C3 -- CLK_PLL_F40M for C2 -- CLK_PLL_F48M for H2 -- CLK_PLL_F80M for C6 uart_config.source_clk = UART_SCLK_DEFAULT; + + UART_MUTEX_LOCK(); bool retCode = ESP_OK == uart_driver_install(uart_nr, rx_buffer_size, tx_buffer_size, 20, &(uart->uart_event_queue), 0); + if (retCode) retCode &= ESP_OK == uart_param_config(uart_nr, &uart_config); // Is it right or the idea is to swap rx and tx pins? @@ -395,19 +502,31 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx retCode &= ESP_OK == uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV); } + if (retCode) { + uart->_baudrate = baudrate; + uart->_config = config; + uart->_inverted = inverted; + uart->_rxfifo_full_thrhd = rxfifo_full_thrhd; + uart->_rx_buffer_size = rx_buffer_size; + uart->_tx_buffer_size = tx_buffer_size; + uart->_ctsPin = -1; + uart->_rtsPin = -1; + uart->has_peek = false; + uart->peek_byte = 0; + } UART_MUTEX_UNLOCK(); + // uartSetPins detaches previous pins if new ones are used over a previous begin() if (retCode) retCode &= uartSetPins(uart_nr, rxPin, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); - - if (retCode) uartFlush(uart); - else { + if (!retCode) { uartEnd(uart_nr); uart = NULL; log_e("UART%d initialization error.", uart->num); + } else { + uartFlush(uart); + log_v("UART%d initialization done.", uart->num); } - - log_v("UART%d baud(%ld) Mode(%x) rxPin(%d) txPin(%d)", uart_nr, baudrate, config, rxPin, txPin); - return uart; + return uart; // a new driver was installed } // This function code is under testing - for now just keep it here @@ -658,6 +777,7 @@ void uartSetBaudRate(uart_t* uart, uint32_t baud_rate) if(uart_get_sclk_freq(UART_SCLK_DEFAULT, &sclk_freq) == ESP_OK){ uart_ll_set_baudrate(UART_LL_GET_HW(uart->num), baud_rate, sclk_freq); } + uart->_baudrate = baud_rate; UART_MUTEX_UNLOCK(); } @@ -1025,3 +1145,4 @@ int uart_send_msg_with_break(uint8_t uartNum, uint8_t *msg, size_t msgSize) } #endif /* SOC_UART_SUPPORTED */ + diff --git a/cores/esp32/esp32-hal-uart.h b/cores/esp32/esp32-hal-uart.h index fbb9694c58a..b33c7bc75fe 100644 --- a/cores/esp32/esp32-hal-uart.h +++ b/cores/esp32/esp32-hal-uart.h @@ -32,6 +32,7 @@ extern "C" { struct uart_struct_t; typedef struct uart_struct_t uart_t; +bool _testUartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd); uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd); void uartEnd(uint8_t uart_num); 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/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/_static/arduino_versions.js b/docs/_static/arduino_versions.js new file mode 100644 index 00000000000..825bc0cbc27 --- /dev/null +++ b/docs/_static/arduino_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: "ESP32-C6", value: "esp32c6"}, + ] +}; 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..dd37c809849 --- /dev/null +++ b/docs/conf_common.py @@ -0,0 +1,54 @@ +# --------------------------------------------------------------- + +from esp_docs.conf_docs import * # noqa: F403,F401 + +languages = ["en"] + +# idf_targets = [ +# "esp32", +# "esp32s2", +# "esp32s3", +# "esp32c3", +# "esp32c6", +# "esp32h2", +# ] + +# 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", +# ] + +# 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" 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 92% rename from docs/source/api/ledc.rst rename to docs/en/api/ledc.rst index b69f581f5bf..1abeea2f6ee 100644 --- a/docs/source/api/ledc.rst +++ b/docs/en/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 ********* diff --git a/docs/source/api/preferences.rst b/docs/en/api/preferences.rst similarity index 96% rename from docs/source/api/preferences.rst rename to docs/en/api/preferences.rst index 33fb2b6042e..aae81506aa1 100644 --- a/docs/source/api/preferences.rst +++ b/docs/en/api/preferences.rst @@ -177,6 +177,45 @@ Arduino-esp32 Preferences API **Note** * A message providing the reason for a failed call is sent to the arduino-esp32 ``log_e`` facility. + + +``isKey`` +************* + + Check if a key-value pair from the currently open namespace exists. + + .. code-block:: arduino + + bool isKey(const char * key) + .. + + **Parameters** + * ``key`` (Required) + - the name of the key to be checked. + + **Returns** + * ``true`` if key-value pair exists; ``false`` otherwise. + + **Note** + * Attempting to check a key without a namespace being open will return false. + + +``getType`` +************* + + Returns the type of a key-value pair from the currently open namespace. + + .. code-block:: arduino + + PreferenceType getType(const char * key) + .. + + **Parameters** + * ``key`` (Required) + - the name of the key to be checked. + + **Returns** + * PreferenceType element contaning the type of the key-value pair or PT_INVALID on error. ``putChar, putUChar`` 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 98% rename from docs/source/api/wifi.rst rename to docs/en/api/wifi.rst index c906cdaa588..010a11cfc1c 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 @@ -327,14 +327,14 @@ Get the softAP subnet mask. IPAddress softAPSubnetMask(); -softAPenableIpV6 +softAPenableIPv6 **************** Function used to enable the IPv6 support. .. code-block:: arduino - bool softAPenableIpV6(); + bool softAPenableIPv6(bool enable=true); The function will return ``true`` if the configuration is successful. @@ -345,9 +345,9 @@ Function to get the IPv6 address. .. code-block:: arduino - IPv6Address softAPIPv6(); + IPAddress softAPIPv6(); -The function will return the AP IPv6 address in ``IPv6Address`` format. +The function will return the AP IPv6 address in ``IPAddress`` format. softAPgetHostname ***************** 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 6214fe4ebd1..7115d5bcb4e 100644 --- a/docs/source/boards/boards.rst +++ b/docs/en/boards/boards.rst @@ -30,7 +30,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) @@ -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..9979662dae0 --- /dev/null +++ b/docs/en/conf.py @@ -0,0 +1,31 @@ +# -*- 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 + +import datetime +current_year = datetime.datetime.now().year + +# General information about the project. +project = u'Arduino ESP32' +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 +# for a list of supported languages. +language = 'en' + +# Tracking ID for Google Analytics +google_analytics_id = 'G-F58JM78930' \ 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 93% rename from docs/source/esp-idf_component.rst rename to docs/en/esp-idf_component.rst index efecff594f3..c6dbe68e662 100644 --- a/docs/source/esp-idf_component.rst +++ b/docs/en/esp-idf_component.rst @@ -73,16 +73,17 @@ After the setup you can save and exit: - Close confirmation window [Enter] or [Space] or [Esc] - Quit [Q] -Option 1. Using Arduino setup() and loop() -****************************************** +As the Arduino libraries use C++ features, you will need to swap some file extensions from ``.c`` to ``.cpp``: - In main folder rename file `main.c` to `main.cpp`. +- In main folder open file `CMakeLists.txt` and change `main.c` to `main.cpp` as described below. -- In main folder open file `CMakeList.txt` and change `main.c` to `main.cpp` as described below. +Option 1. Using Arduino setup() and loop() +****************************************** -- Your main.cpp should be formatted like any other sketch. +Your main.cpp should be formatted like any other sketch. Don't forget to include ``Arduino.h``. -.. code-block:: c +.. code-block:: cpp //file: main.cpp #include "Arduino.h" @@ -102,14 +103,14 @@ Option 1. Using Arduino setup() and loop() Option 2. Using ESP-IDF appmain() ********************************* -In main.c or main.cpp you need to implement ``app_main()`` and call ``initArduino();`` in it. +In main.cpp you need to implement ``app_main()`` and call ``initArduino();`` in it. Keep in mind that setup() and loop() will not be called in this case. Furthermore the ``app_main()`` is single execution as a normal function so if you need an infinite loop as in Arduino place it there. .. code-block:: cpp - //file: main.c or main.cpp + //file: main.cpp #include "Arduino.h" extern "C" void app_main() @@ -141,7 +142,7 @@ Build, flash and monitor - After a successful flash, you may need to press the RST button again - - To terminate the serial monitor press [Ctrl] + [ ] ] + - To terminate the serial monitor press ``Ctrl`` + ``]`` Logging To Serial ----------------- @@ -179,7 +180,7 @@ Download the library: .. code-block:: bash - cd ~/esp/esp-idf/components/arduino-esp32/ + cd ~/esp/esp-idf/components/arduino/ git clone --recursive git@github.com:Author/new_library.git libraries/new_library 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 98% rename from docs/source/guides/docs_contributing.rst rename to docs/en/guides/docs_contributing.rst index be5a54e4b37..d0089a5fd9f 100644 --- a/docs/source/guides/docs_contributing.rst +++ b/docs/en/guides/docs_contributing.rst @@ -97,7 +97,7 @@ If everything is ok, you will see some output logs similar to this one: writing output... [100%] tutorials/tutorials generating indices... genindexdone writing additional pages... searchdone - copying images... [100%] tutorials/../_static/tutorials/peripherals/tutorial_peripheral_diagram.png + copying images... [100%] tutorials/../../_static/tutorials/peripherals/tutorial_peripheral_diagram.png copying static files... ... done copying extra files... done dumping search index in English (code: en)... done @@ -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 98% rename from docs/source/libraries.rst rename to docs/en/libraries.rst index 8c5357150a3..37dfad0411b 100644 --- a/docs/source/libraries.rst +++ b/docs/en/libraries.rst @@ -24,7 +24,7 @@ Currently, the Arduino ESP32 supports the following peripherals with Arduino API +---------------+---------------+---------------+---------------+---------------+-----+------------------------+ | GPIO | Yes | Yes | Yes | Yes | | | +---------------+---------------+---------------+---------------+---------------+-----+------------------------+ -| Hall Sensor | Yes | Not Supported | Not Supported | Not Supported | | | +| Hall Sensor | Not Supported | Not Supported | Not Supported | Not Supported | | | +---------------+---------------+---------------+---------------+---------------+-----+------------------------+ | I2C | Yes | Yes | Yes | Yes | | | +---------------+---------------+---------------+---------------+---------------+-----+------------------------+ 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..7c819ff0836 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,15 @@ -# 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 +sphinx-tabs==3.2.0 +sphinxcontrib-actdiag==3.0.0 +sphinxcontrib-applehelp==1.0.4 +sphinxcontrib-blockdiag==3.0.0 +sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-htmlhelp==2.0.1 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-nwdiag==2.0.0 +sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-seqdiag==3.0.0 +sphinxcontrib-serializinghtml==1.1.5 +sphinxcontrib-svg2pdfconverter==1.2.0 +sphinxcontrib-wavedrom==3.0.4 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/utils.sh b/docs/utils.sh new file mode 100644 index 00000000000..0f9574e57a5 --- /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 --decode --ignore-garbage ~/.ssh/id_rsa_base64 >~/.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 diff --git a/libraries/AsyncUDP/src/AsyncUDP.cpp b/libraries/AsyncUDP/src/AsyncUDP.cpp index e533755da7a..1c650f1a721 100644 --- a/libraries/AsyncUDP/src/AsyncUDP.cpp +++ b/libraries/AsyncUDP/src/AsyncUDP.cpp @@ -424,12 +424,12 @@ IPAddress AsyncUDPPacket::localIP() return IPAddress(_localIp.u_addr.ip4.addr); } -IPv6Address AsyncUDPPacket::localIPv6() +IPAddress AsyncUDPPacket::localIPv6() { if(_localIp.type != IPADDR_TYPE_V6){ - return IPv6Address(); + return IPAddress(IPv6); } - return IPv6Address(_localIp.u_addr.ip6.addr); + return IPAddress(IPv6, (const uint8_t *)_localIp.u_addr.ip6.addr, _localIp.u_addr.ip6.zone); } uint16_t AsyncUDPPacket::localPort() @@ -445,12 +445,12 @@ IPAddress AsyncUDPPacket::remoteIP() return IPAddress(_remoteIp.u_addr.ip4.addr); } -IPv6Address AsyncUDPPacket::remoteIPv6() +IPAddress AsyncUDPPacket::remoteIPv6() { if(_remoteIp.type != IPADDR_TYPE_V6){ - return IPv6Address(); + return IPAddress(IPv6); } - return IPv6Address(_remoteIp.u_addr.ip6.addr); + return IPAddress(IPv6, (const uint8_t *)_remoteIp.u_addr.ip6.addr, _remoteIp.u_addr.ip6.zone); } uint16_t AsyncUDPPacket::remotePort() @@ -739,32 +739,28 @@ bool AsyncUDP::listen(uint16_t port) bool AsyncUDP::listen(const IPAddress addr, uint16_t port) { ip_addr_t laddr; - laddr.type = IPADDR_TYPE_V4; - laddr.u_addr.ip4.addr = addr; + addr.to_ip_addr_t(&laddr); return listen(&laddr, port); } bool AsyncUDP::listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if) { ip_addr_t laddr; - laddr.type = IPADDR_TYPE_V4; - laddr.u_addr.ip4.addr = addr; + addr.to_ip_addr_t(&laddr); return listenMulticast(&laddr, port, ttl, tcpip_if); } bool AsyncUDP::connect(const IPAddress addr, uint16_t port) { ip_addr_t daddr; - daddr.type = IPADDR_TYPE_V4; - daddr.u_addr.ip4.addr = addr; + addr.to_ip_addr_t(&daddr); return connect(&daddr, port); } size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if) { ip_addr_t daddr; - daddr.type = IPADDR_TYPE_V4; - daddr.u_addr.ip4.addr = addr; + addr.to_ip_addr_t(&daddr); return writeTo(data, len, &daddr, port, tcpip_if); } @@ -776,44 +772,12 @@ IPAddress AsyncUDP::listenIP() return IPAddress(_pcb->remote_ip.u_addr.ip4.addr); } -bool AsyncUDP::listen(const IPv6Address addr, uint16_t port) -{ - ip_addr_t laddr; - laddr.type = IPADDR_TYPE_V6; - memcpy((uint8_t*)(laddr.u_addr.ip6.addr), (const uint8_t*)addr, 16); - return listen(&laddr, port); -} - -bool AsyncUDP::listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl, tcpip_adapter_if_t tcpip_if) -{ - ip_addr_t laddr; - laddr.type = IPADDR_TYPE_V6; - memcpy((uint8_t*)(laddr.u_addr.ip6.addr), (const uint8_t*)addr, 16); - return listenMulticast(&laddr, port, ttl, tcpip_if); -} - -bool AsyncUDP::connect(const IPv6Address addr, uint16_t port) -{ - ip_addr_t daddr; - daddr.type = IPADDR_TYPE_V6; - memcpy((uint8_t*)(daddr.u_addr.ip6.addr), (const uint8_t*)addr, 16); - return connect(&daddr, port); -} - -size_t AsyncUDP::writeTo(const uint8_t *data, size_t len, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if) -{ - ip_addr_t daddr; - daddr.type = IPADDR_TYPE_V6; - memcpy((uint8_t*)(daddr.u_addr.ip6.addr), (const uint8_t*)addr, 16); - return writeTo(data, len, &daddr, port, tcpip_if); -} - -IPv6Address AsyncUDP::listenIPv6() +IPAddress AsyncUDP::listenIPv6() { if(!_pcb || _pcb->remote_ip.type != IPADDR_TYPE_V6){ - return IPv6Address(); + return IPAddress(IPv6); } - return IPv6Address(_pcb->remote_ip.u_addr.ip6.addr); + return IPAddress(IPv6, (const uint8_t *)_pcb->remote_ip.u_addr.ip6.addr, _pcb->remote_ip.u_addr.ip6.zone); } size_t AsyncUDP::write(const uint8_t *data, size_t len) @@ -866,14 +830,6 @@ size_t AsyncUDP::sendTo(AsyncUDPMessage &message, const IPAddress addr, uint16_t return writeTo(message.data(), message.length(), addr, port, tcpip_if); } -size_t AsyncUDP::sendTo(AsyncUDPMessage &message, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if) -{ - if(!message) { - return 0; - } - return writeTo(message.data(), message.length(), addr, port, tcpip_if); -} - size_t AsyncUDP::send(AsyncUDPMessage &message) { if(!message) { diff --git a/libraries/AsyncUDP/src/AsyncUDP.h b/libraries/AsyncUDP/src/AsyncUDP.h index c9f365d1197..31d075191df 100644 --- a/libraries/AsyncUDP/src/AsyncUDP.h +++ b/libraries/AsyncUDP/src/AsyncUDP.h @@ -2,7 +2,6 @@ #define ESPASYNCUDP_H #include "IPAddress.h" -#include "IPv6Address.h" #include "Print.h" #include "Stream.h" #include @@ -81,10 +80,10 @@ class AsyncUDPPacket : public Stream tcpip_adapter_if_t interface(); IPAddress localIP(); - IPv6Address localIPv6(); + IPAddress localIPv6(); uint16_t localPort(); IPAddress remoteIP(); - IPv6Address remoteIPv6(); + IPAddress remoteIPv6(); uint16_t remotePort(); void remoteMac(uint8_t * mac); @@ -121,22 +120,18 @@ class AsyncUDP : public Print bool listen(const ip_addr_t *addr, uint16_t port); bool listen(const IPAddress addr, uint16_t port); - bool listen(const IPv6Address addr, uint16_t port); bool listen(uint16_t port); bool listenMulticast(const ip_addr_t *addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); bool listenMulticast(const IPAddress addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); - bool listenMulticast(const IPv6Address addr, uint16_t port, uint8_t ttl=1, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); bool connect(const ip_addr_t *addr, uint16_t port); bool connect(const IPAddress addr, uint16_t port); - bool connect(const IPv6Address addr, uint16_t port); void close(); size_t writeTo(const uint8_t *data, size_t len, const ip_addr_t *addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); size_t writeTo(const uint8_t *data, size_t len, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); - size_t writeTo(const uint8_t *data, size_t len, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); size_t write(const uint8_t *data, size_t len); size_t write(uint8_t data); @@ -147,14 +142,13 @@ class AsyncUDP : public Print size_t sendTo(AsyncUDPMessage &message, const ip_addr_t *addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); size_t sendTo(AsyncUDPMessage &message, const IPAddress addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); - size_t sendTo(AsyncUDPMessage &message, const IPv6Address addr, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); size_t send(AsyncUDPMessage &message); size_t broadcastTo(AsyncUDPMessage &message, uint16_t port, tcpip_adapter_if_t tcpip_if=TCPIP_ADAPTER_IF_MAX); size_t broadcast(AsyncUDPMessage &message); IPAddress listenIP(); - IPv6Address listenIPv6(); + IPAddress listenIPv6(); bool connected(); esp_err_t lastErr(); operator bool(); diff --git a/libraries/ESP32/examples/AnalogRead/AnalogRead.ino b/libraries/ESP32/examples/AnalogRead/AnalogRead.ino index c238517d380..647e16ba8bd 100644 --- a/libraries/ESP32/examples/AnalogRead/AnalogRead.ino +++ b/libraries/ESP32/examples/AnalogRead/AnalogRead.ino @@ -2,7 +2,7 @@ void setup() { // initialize serial communication at 115200 bits per second: Serial.begin(115200); - //set the resolution to 12 bits (0-4096) + //set the resolution to 12 bits (0-4095) analogReadResolution(12); } diff --git a/libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino b/libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino new file mode 100644 index 00000000000..3e9ae5bfd53 --- /dev/null +++ b/libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino @@ -0,0 +1,71 @@ +#include + +void setup() { + Serial.begin(115200); + while (!Serial) { delay(10); } + Serial.println("\n\n\nStart."); + + // Convert a HEX string like 6c6c6f20576f726c64 to a binary buffer + { + const char * out = "Hello World"; + const char * hexin = "48656c6c6f20576f726c6400"; // As the string above is \0 terminated too + + unsigned char buff[256]; + size_t len = HEXBuilder::hex2bytes(buff, sizeof(buff), hexin); + + if (len != 1 + strlen(out)) + Serial.println("Odd - length 1 is wrong"); + + if (memcmp(buff, out, len) != 0) + Serial.println("Odd - decode 1 went wrong"); + + // Safe to print this binary buffer -- as we've included a \0 in the hex sequence. + Serial.printf("IN: <%s>\nOUT <%s\\0>\n", hexin, buff); + }; + + { + String helloHEX = "48656c6c6f20576f726c64"; + const char hello[] = "Hello World"; + + unsigned char buff[256]; + size_t len = HEXBuilder::hex2bytes(buff, sizeof(buff), helloHEX); + + if (len != strlen(hello)) + Serial.println("Odd - length 2 is wrong"); + + if (strcmp((char *) buff, hello) != 0) + Serial.println("Odd - decode 2 went wrong"); + } + + { + const unsigned char helloBytes[] = { 0x48, 0x56, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 }; + String helloHEX = "48566c6c6f20576f726c64"; + + + String out = HEXBuilder::bytes2hex(helloBytes, sizeof(helloBytes)); + if (out.length() != 2 * sizeof(helloBytes)) + Serial.println("Odd - length 3 is wrong"); + + // we need to ignore case - as a hex string can be spelled in uppercase and lowercase + if (!out.equalsIgnoreCase(helloHEX)) { + Serial.println("Odd - decode 3 went wrong"); + } + } + + { + const unsigned char helloBytes[] = { 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 }; + const char helloHex[] = "6c6c6f20576f726c64"; + + char buff[256]; + size_t len = HEXBuilder::bytes2hex(buff, sizeof(buff), helloBytes, sizeof(helloBytes)); + if (len != 1 + 2 * sizeof(helloBytes)) + Serial.println("Odd - length 4 is wrong"); + + // we need to ignore case - as a hex string can be spelled in uppercase and lowercase + if (strcasecmp(buff, helloHex)) + Serial.println("Odd - decode 4 went wrong"); + } + Serial.println("Done."); +} + +void loop() {} diff --git a/libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino b/libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino new file mode 100644 index 00000000000..9b439587000 --- /dev/null +++ b/libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino @@ -0,0 +1,96 @@ +#include + +// Occasionally it is useful to compare a password that the user +// has entered to a build in string. However this means that the +// password ends up `in the clear' in the firmware and in your +// source code. +// +// MD5Builder helps you obfuscate this (it is not terribly secure, MD5 +// has been phased out as insecure eons ago) by letting you create an +// MD5 of the data the user entered; and then compare this to an MD5 +// string that you have put in your code. + +void setup() { + Serial.begin(115200); + while (!Serial) { delay(10); } + Serial.println("\n\n\nStart."); + + // Check if a password obfuscated in an MD5 actually + // matches the original string. + // + // echo -n "Hello World" | openssl md5 + { + String md5 = "b10a8db164e0754105b7a99be72e3fe5"; + String password = "Hello World"; + + MD5Builder md; + + md.begin(); + md.add(password); + md.calculate(); + + String result = md.toString(); + + if (!md5.equalsIgnoreCase(result)) + Serial.println("Odd - failing MD5 on String"); + else + Serial.println("OK!"); + } + + // Check that this also work if we add the password not as + // a normal string - but as a string with the HEX values. + { + String passwordAsHex = "48656c6c6f20576f726c64"; + String md5 = "b10a8db164e0754105b7a99be72e3fe5"; + + MD5Builder md; + + md.begin(); + md.addHexString(passwordAsHex); + md.calculate(); + + String result = md.toString(); + + if (!md5.equalsIgnoreCase(result)) { + Serial.println("Odd - failing MD5 on hex string"); + Serial.println(md5); + Serial.println(result); + } + else + Serial.println("OK!"); + + } + + // Check that this also work if we add the password as + // an unsigned byte array. + { + uint8_t password[] = { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 }; + String md5 = "b10a8db164e0754105b7a99be72e3fe5"; + MD5Builder md; + + md.begin(); + md.add(password, sizeof(password)); + md.calculate(); + + String result = md.toString(); + + if (!md5.equalsIgnoreCase(result)) + Serial.println("Odd - failing MD5 on byte array"); + else + Serial.println("OK!"); + + // And also check that we can compare this as pure, raw, bytes + uint8_t raw[16] = { 0xb1, 0x0a, 0x8d, 0xb1, 0x64, 0xe0, 0x75, 0x41, + 0x05, 0xb7, 0xa9, 0x9b, 0xe7, 0x2e, 0x3f, 0xe5 + }; + uint8_t res[16]; + md.getBytes(res); + if (memcmp(raw, res, 16)) + Serial.println("Odd - failing MD5 on byte array when compared as bytes"); + else + Serial.println("OK!"); + + } +} + +void loop() {} diff --git a/libraries/ESP32/examples/Utilities/SHA1Builder/SHA1Builder.ino b/libraries/ESP32/examples/Utilities/SHA1Builder/SHA1Builder.ino new file mode 100644 index 00000000000..40270953473 --- /dev/null +++ b/libraries/ESP32/examples/Utilities/SHA1Builder/SHA1Builder.ino @@ -0,0 +1,97 @@ +#include + +// Occasionally it is useful to compare a password that the user +// has entered to a build in string. However this means that the +// password ends up `in the clear' in the firmware and in your +// source code. +// +// SHA1Builder helps you obfuscate this (This is not much more secure. +// SHA1 is past its retirement age and long obsolte/insecure, but it helps +// a little) by letting you create an (unsalted!) SHA1 of the data the +// user entered; and then compare this to an SHA1 string that you have put +// in your code. + +void setup() { + Serial.begin(115200); + while (!Serial) { delay(10); } + Serial.println("\n\n\nStart."); + + // Check if a password obfuscated in an SHA1 actually + // matches the original string. + // + // echo -n "Hello World" | openssl sha1 + { + String sha1_str = "0a4d55a8d778e5022fab701977c5d840bbc486d0"; + String password = "Hello World"; + + SHA1Builder sha; + + sha.begin(); + sha.add(password); + sha.calculate(); + + String result = sha.toString(); + + if (!sha1_str.equalsIgnoreCase(result)) + Serial.println("Odd - failing SHA1 on String"); + else + Serial.println("OK!"); + } + + // Check that this also work if we add the password not as + // a normal string - but as a string with the HEX values. + { + String passwordAsHex = "48656c6c6f20576f726c64"; + String sha1_str = "0a4d55a8d778e5022fab701977c5d840bbc486d0"; + + SHA1Builder sha; + + sha.begin(); + sha.addHexString(passwordAsHex); + sha.calculate(); + + String result = sha.toString(); + + if (!sha1_str.equalsIgnoreCase(result)) { + Serial.println("Odd - failing SHA1 on hex string"); + Serial.println(sha1_str); + Serial.println(result); + } + else + Serial.println("OK!"); + + } + + // Check that this also work if we add the password as + // an unsigned byte array. + { + uint8_t password[] = { 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64 }; + String sha1_str = "0a4d55a8d778e5022fab701977c5d840bbc486d0"; + SHA1Builder sha; + + sha.begin(); + sha.add(password, sizeof(password)); + sha.calculate(); + + String result = sha.toString(); + + if (!sha1_str.equalsIgnoreCase(result)) + Serial.println("Odd - failing SHA1 on byte array"); + else + Serial.println("OK!"); + + // And also check that we can compare this as pure, raw, bytes + uint8_t raw[20] = { 0x0a, 0x4d, 0x55, 0xa8, 0xd7, 0x78, 0xe5, 0x02, 0x2f, 0xab, + 0x70, 0x19, 0x77, 0xc5, 0xd8, 0x40, 0xbb, 0xc4, 0x86, 0xd0 + }; + uint8_t res[20]; + sha.getBytes(res); + if (memcmp(raw, res, 20)) + Serial.println("Odd - failing SHA1 on byte array when compared as bytes"); + else + Serial.println("OK!"); + + } +} + +void loop() {} diff --git a/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino b/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino index 6f2dfddfb3b..9e582b3236a 100644 --- a/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino +++ b/libraries/ESPmDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino @@ -70,7 +70,7 @@ void browseService(const char * service, const char * proto){ Serial.print(": "); Serial.print(MDNS.hostname(i)); Serial.print(" ("); - Serial.print(MDNS.IP(i)); + Serial.print(MDNS.address(i)); Serial.print(":"); Serial.print(MDNS.port(i)); Serial.println(")"); diff --git a/libraries/ESPmDNS/src/ESPmDNS.cpp b/libraries/ESPmDNS/src/ESPmDNS.cpp index bccc4de5758..21d93aa4e2a 100644 --- a/libraries/ESPmDNS/src/ESPmDNS.cpp +++ b/libraries/ESPmDNS/src/ESPmDNS.cpp @@ -265,7 +265,7 @@ String MDNSResponder::hostname(int idx) { return String(result->hostname); } -IPAddress MDNSResponder::IP(int idx) { +IPAddress MDNSResponder::address(int idx) { mdns_result_t * result = _getResult(idx); if(!result){ log_e("Result %d not found", idx); @@ -281,20 +281,20 @@ IPAddress MDNSResponder::IP(int idx) { return IPAddress(); } -IPv6Address MDNSResponder::IPv6(int idx) { +IPAddress MDNSResponder::addressV6(int idx) { mdns_result_t * result = _getResult(idx); if(!result){ log_e("Result %d not found", idx); - return IPv6Address(); + return IPAddress(IPv6); } mdns_ip_addr_t * addr = result->addr; while(addr){ if(addr->addr.type == MDNS_IP_PROTOCOL_V6){ - return IPv6Address(addr->addr.u_addr.ip6.addr); + return IPAddress(IPv6, (const uint8_t *)addr->addr.u_addr.ip6.addr, addr->addr.u_addr.ip6.zone); } addr = addr->next; } - return IPv6Address(); + return IPAddress(IPv6); } uint16_t MDNSResponder::port(int idx) { diff --git a/libraries/ESPmDNS/src/ESPmDNS.h b/libraries/ESPmDNS/src/ESPmDNS.h index 6a5cb56ffba..01ca7518d2f 100644 --- a/libraries/ESPmDNS/src/ESPmDNS.h +++ b/libraries/ESPmDNS/src/ESPmDNS.h @@ -42,7 +42,6 @@ License (MIT license): #define ESP32MDNS_H #include "Arduino.h" -#include "IPv6Address.h" #include "mdns.h" #include "esp_interface.h" @@ -108,8 +107,8 @@ class MDNSResponder { } String hostname(int idx); - IPAddress IP(int idx); - IPv6Address IPv6(int idx); + IPAddress address(int idx); + IPAddress addressV6(int idx); uint16_t port(int idx); int numTxt(int idx); bool hasTxt(int idx, const char * key); diff --git a/libraries/Ethernet/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp index 40c408ee69a..92d91b40a1a 100644 --- a/libraries/Ethernet/src/ETH.cpp +++ b/libraries/Ethernet/src/ETH.cpp @@ -401,13 +401,13 @@ bool ETHClass::beginSPI(eth_phy_type_t type, uint8_t phy_addr, int cs, int irq, // Init SPI bus if(_pin_sck >= 0 && _pin_miso >= 0 && _pin_mosi >= 0){ - spi_bus_config_t buscfg = { - .mosi_io_num = _pin_mosi, - .miso_io_num = _pin_miso, - .sclk_io_num = _pin_sck, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - }; + spi_bus_config_t buscfg; + memset(&buscfg, 0, sizeof(spi_bus_config_t)); + buscfg.mosi_io_num = _pin_mosi; + buscfg.miso_io_num = _pin_miso; + buscfg.sclk_io_num = _pin_sck; + buscfg.quadwp_io_num = -1; + buscfg.quadhd_io_num = -1; ret = spi_bus_initialize(spi_host, &buscfg, SPI_DMA_CH_AUTO); if(ret != ESP_OK){ log_e("SPI bus initialize failed: %d", ret); @@ -433,13 +433,13 @@ bool ETHClass::beginSPI(eth_phy_type_t type, uint8_t phy_addr, int cs, int irq, phy_config.reset_gpio_num = _pin_rst; // Configure SPI interface for specific SPI module - spi_device_interface_config_t spi_devcfg = { - .mode = 0, - .clock_speed_hz = _spi_freq_mhz * 1000 * 1000, - .input_delay_ns = 20, - .spics_io_num = _pin_cs, - .queue_size = 20, - }; + spi_device_interface_config_t spi_devcfg; + memset(&spi_devcfg, 0, sizeof(spi_device_interface_config_t)); + spi_devcfg.mode = 0; + spi_devcfg.clock_speed_hz = _spi_freq_mhz * 1000 * 1000; + spi_devcfg.input_delay_ns = 20; + spi_devcfg.spics_io_num = _pin_cs; + spi_devcfg.queue_size = 20; esp_eth_mac_t *mac = NULL; esp_eth_phy_t *phy = NULL; @@ -861,24 +861,42 @@ bool ETHClass::setHostname(const char * hostname) return esp_netif_set_hostname(_esp_netif, hostname) == 0; } -bool ETHClass::enableIpV6() +bool ETHClass::enableIPv6(bool en) { - if(_esp_netif == NULL){ - return false; + // if(_esp_netif == NULL){ + // return false; + // } + // return esp_netif_create_ip6_linklocal(_esp_netif) == 0; + if (en) { + WiFiGenericClass::setStatusBits(ETH_WANT_IP6_BIT); + } else { + WiFiGenericClass::clearStatusBits(ETH_WANT_IP6_BIT); } - return esp_netif_create_ip6_linklocal(_esp_netif) == 0; + return true; } -IPv6Address ETHClass::localIPv6() +IPAddress ETHClass::localIPv6() { if(_esp_netif == NULL){ - return IPv6Address(); + return IPAddress(IPv6); } static esp_ip6_addr_t addr; if(esp_netif_get_ip6_linklocal(_esp_netif, &addr)){ - return IPv6Address(); + return IPAddress(IPv6); + } + return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone); +} + +IPAddress ETHClass::globalIPv6() +{ + if(_esp_netif == NULL){ + return IPAddress(IPv6); } - return IPv6Address(addr.addr); + static esp_ip6_addr_t addr; + if(esp_netif_get_ip6_global(_esp_netif, &addr)){ + return IPAddress(IPv6); + } + return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone); } const char * ETHClass::ifkey(void) @@ -1031,6 +1049,28 @@ void ETHClass::printInfo(Print & out){ out.print(dnsIP()); out.println(); + static const char * types[] = { "UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6" }; + esp_ip6_addr_t if_ip6[CONFIG_LWIP_IPV6_NUM_ADDRESSES]; + int v6addrs = esp_netif_get_all_ip6(_esp_netif, if_ip6); + for (int i = 0; i < v6addrs; ++i){ + out.print(" "); + out.print("inet6 "); + IPAddress(IPv6, (const uint8_t *)if_ip6[i].addr, if_ip6[i].zone).printTo(out, true); + out.print(" type "); + out.print(types[esp_netif_ip6_get_addr_type(&if_ip6[i])]); + out.println(); + } + + // out.print(" "); + // out.print("inet6 "); + // localIPv6().printTo(out); + // out.println(); + + // out.print(" "); + // out.print("inet6 "); + // globalIPv6().printTo(out); + // out.println(); + out.println(); } diff --git a/libraries/Ethernet/src/ETH.h b/libraries/Ethernet/src/ETH.h index 7ef39ef561f..7159ebd4574 100644 --- a/libraries/Ethernet/src/ETH.h +++ b/libraries/Ethernet/src/ETH.h @@ -145,8 +145,9 @@ class ETHClass { IPAddress broadcastIP(); IPAddress networkID(); uint8_t subnetCIDR(); - bool enableIpV6(); - IPv6Address localIPv6(); + bool enableIPv6(bool en=true); + IPAddress localIPv6(); + IPAddress globalIPv6(); const char * ifkey(void); const char * desc(void); String impl_name(void); 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 }; diff --git a/libraries/SD/src/sd_diskio.cpp b/libraries/SD/src/sd_diskio.cpp index f6a8df5e42b..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)) { @@ -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]); diff --git a/libraries/WebServer/examples/HttpAuthCallback/.skip.esp32h2 b/libraries/WebServer/examples/HttpAuthCallback/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/WebServer/examples/HttpAuthCallback/HttpAuthCallback.ino b/libraries/WebServer/examples/HttpAuthCallback/HttpAuthCallback.ino new file mode 100644 index 00000000000..8cdded55a1c --- /dev/null +++ b/libraries/WebServer/examples/HttpAuthCallback/HttpAuthCallback.ino @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +const char* ssid = "........"; +const char* password = "........"; + +WebServer server(80); + +typedef struct credentials_t { + String username; + String password; +} credentials_t; + +credentials_t passwdfile[] = { + { "admin", "esp32" }, + { "fred", "41234123" }, + { "charlie", "sdfsd" }, + { "alice", "vambdnkuhj" }, + { "bob", "svcdbjhws12" }, +}; +const size_t N_CREDENTIALS = sizeof(passwdfile) / sizeof(credentials_t); + +String * credentialsHandler(HTTPAuthMethod mode, String username, String params[]) +{ + for (int i = 0; i < N_CREDENTIALS; i++) { + if (username == passwdfile[i].username) + return new String(passwdfile[i].password); + } + return NULL; +} + +void setup() { + Serial.begin(115200); + while (!Serial) { delay(10); } + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Connect Failed! Rebooting..."); + delay(1000); + ESP.restart(); + } + ArduinoOTA.begin(); + + server.on("/", []() { + if (!server.authenticate(&credentialsHandler)) { + server.requestAuthentication(); + return; + } + server.send(200, "text/plain", "Login OK"); + }); + server.begin(); + + Serial.print("Open http://"); + Serial.print(WiFi.localIP()); + Serial.println("/ in your browser to see it working"); +} + +void loop() { + ArduinoOTA.handle(); + server.handleClient(); + delay(2);//allow the cpu to switch to other tasks +} diff --git a/libraries/WebServer/examples/HttpAuthCallbackInline/.skip.esp32h2 b/libraries/WebServer/examples/HttpAuthCallbackInline/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/WebServer/examples/HttpAuthCallbackInline/HttpAuthCallbackInline.ino b/libraries/WebServer/examples/HttpAuthCallbackInline/HttpAuthCallbackInline.ino new file mode 100644 index 00000000000..48f0d3f8127 --- /dev/null +++ b/libraries/WebServer/examples/HttpAuthCallbackInline/HttpAuthCallbackInline.ino @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +const char* ssid = "........"; +const char* password = "........"; + +WebServer server(80); + +typedef struct credentials_t { + char * username; + char * password; +} credentials_t; + +credentials_t passwdfile[] = { + { "admin", "esp32" }, + { "fred", "41234123" }, + { "charlie", "sdfsd" }, + { "alice", "vambdnkuhj" }, + { "bob", "svcdbjhws12" }, + { NULL, NULL } +}; + +void setup() { + Serial.begin(115200); + while (!Serial) { delay(10); } + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Connect Failed! Rebooting..."); + delay(1000); + ESP.restart(); + } + ArduinoOTA.begin(); + + server.on("/", []() { + if (!server.authenticate([](HTTPAuthMethod mode, String username, String params[]) -> String * { + // Scan the password list for the username and return the password if + // we find the username. + // + for (credentials_t * entry = passwdfile; entry->username; entry++) { + if (username == entry->username) { + return new String(entry->password); + }; + }; + // we've not found the user in the list. + return NULL; + })) + { + server.requestAuthentication(); + return; + } + server.send(200, "text/plain", "Login OK"); + }); + server.begin(); + + Serial.print("Open http://"); + Serial.print(WiFi.localIP()); + Serial.println("/ in your browser to see it working"); +} + +void loop() { + ArduinoOTA.handle(); + server.handleClient(); + delay(2);//allow the cpu to switch to other tasks +} diff --git a/libraries/WebServer/examples/HttpBasicAuthSHA1/.skip.esp32h2 b/libraries/WebServer/examples/HttpBasicAuthSHA1/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/WebServer/examples/HttpBasicAuthSHA1/HttpBasicAuthSHA1.ino b/libraries/WebServer/examples/HttpBasicAuthSHA1/HttpBasicAuthSHA1.ino new file mode 100644 index 00000000000..bfc2df6d037 --- /dev/null +++ b/libraries/WebServer/examples/HttpBasicAuthSHA1/HttpBasicAuthSHA1.ino @@ -0,0 +1,72 @@ +#include +#include +#include +#include + +// Rather than specify the password as plaintext; we +// provide it as an (unsalted!) SHA1 hash. This is not +// much more secure (SHA1 is past its retirement age, +// and long obsolte/insecure) - but it helps a little. + +const char* ssid = "........"; +const char* password = "........"; + +WebServer server(80); + +// Passwords as plaintext - human readable and easily visible in +// the sourcecode and in the firmware/binary. +const char* www_username = "admin"; +const char* www_password = "esp32"; + +// The sha1 of 'esp32' (without the trailing \0) expressed as 20 +// bytes of hex. Created by for example 'echo -n esp32 | openssl sha1' +// or http://www.sha1-online.com. +const char* www_username_hex = "hexadmin"; +const char* www_password_hex = "8cb124f8c277c16ec0b2ee00569fd151a08e342b"; + +// The same; but now expressed as a base64 string (e.g. as commonly used +// by webservers). Created by ` echo -n esp32 | openssl sha1 -binary | openssl base64` +const char* www_username_base64 = "base64admin"; +const char* www_password_base64 = "jLEk+MJ3wW7Asu4AVp/RUaCONCs="; + +void setup() { + Serial.begin(115200); + while (!Serial) { delay(10); } + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Connect Failed! Rebooting..."); + delay(1000); + ESP.restart(); + } + ArduinoOTA.begin(); + + server.on("/", []() { + if (server.authenticate(www_username, www_password)) { + server.send(200, "text/plain", "Login against cleartext password OK"); + return; + } + if (server.authenticateBasicSHA1(www_username_hex, www_password_hex)) { + server.send(200, "text/plain", "Login against HEX of the SHA1 of the password OK"); + return; + } + if (server.authenticateBasicSHA1(www_username_base64, www_password_base64)) { + server.send(200, "text/plain", "Login against Base64 of the SHA1 of the password OK"); + return; + } + Serial.println("No/failed authentication"); + return server.requestAuthentication(); + }); + + server.begin(); + + Serial.print("Open http://"); + Serial.print(WiFi.localIP()); + Serial.println("/ in your browser to see it working"); +} + +void loop() { + ArduinoOTA.handle(); + server.handleClient(); + delay(2);//allow the cpu to switch to other tasks +} diff --git a/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/.skip.esp32h2 b/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/.skip.esp32h2 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/HttpBasicAuthSHA1orBearerToken.ino b/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/HttpBasicAuthSHA1orBearerToken.ino new file mode 100644 index 00000000000..ecf04031181 --- /dev/null +++ b/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/HttpBasicAuthSHA1orBearerToken.ino @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include + +// We have two options - we either come in with a bearer +// token - i.e. a special header or API token; or we +// get a normal HTTP style basic auth prompt. +// +// To do a bearer fetch - use something like Swagger or with curl: +// +// curl https://myesp.com/ -H "Authorization: Bearer SecritToken" +// +// We avoid hardcoding this "SecritToken" into the code by +// using a SHA1 instead (which is not paricularly secure). + +// Create the secret token SHA1 with: +// echo -n SecritToken | openssl sha1 + +String secret_token_hex = "d2cce6b472959484a21c3194080c609b8a2c910b"; + +// Wifi credentials + +const char* ssid = "........"; +const char* password = "........"; + +WebServer server(80); + +// Rather than specify the admin password as plaintext; we +// provide it as an (unsalted!) SHA1 hash. This is not +// much more secure (SHA1 is past its retirement age, +// and long obsolte/insecure) - but it helps a little. + +// The sha1 of 'esp32' (without the trailing \0) expressed as 20 +// bytes of hex. Created by for example 'echo -n esp32 | openssl sha1' +// or http://www.sha1-online.com. +const char* www_username_hex = "admin"; +const char* www_password_hex = "8cb124f8c277c16ec0b2ee00569fd151a08e342b"; + +static unsigned char _bearer[20]; + +String* check_bearer_or_auth(HTTPAuthMethod mode, String authReq, String params[]) { + // we expect authReq to be "bearer some-secret" + String lcAuthReq = authReq; + lcAuthReq.toLowerCase(); + if (mode == OTHER_AUTH && (lcAuthReq.startsWith("bearer "))) { + String secret = authReq.substring(7); + secret.trim(); + + uint8_t sha1[20]; + SHA1Builder sha_builder; + + sha_builder.begin(); + sha_builder.add((uint8_t*) secret.c_str(), secret.length()); + sha_builder.calculate(); + sha_builder.getBytes(sha1); + + if (memcmp(_bearer, sha1, sizeof(_bearer)) == 0) { + Serial.println("Bearer token matches"); + return new String("anything non null"); + } else { + Serial.println("Bearer token does not match"); + } + } else if (mode == BASIC_AUTH) { + bool ret = server.authenticateBasicSHA1(www_username_hex, www_password_hex); + if (ret) { + Serial.println("Basic auth succeeded"); + return new String(params[0]); + } else { + Serial.println("Basic auth failed"); + } + } + + // No auth found + return NULL; +}; + +void setup() { + Serial.begin(115200); + while (!Serial) { delay(10); } + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("WiFi Connect Failed! Rebooting..."); + delay(1000); + ESP.restart(); + } + ArduinoOTA.begin(); + + // Convert token to a convenient binary representation. + size_t len = HEXBuilder::hex2bytes(_bearer, sizeof(_bearer), secret_token_hex); + if (len != 20) + Serial.println("Bearer token does not look like a valid SHA1 hex string ?!"); + + server.on("/", []() { + if (!server.authenticate(&check_bearer_or_auth)) { + Serial.println("No/failed authentication"); + return server.requestAuthentication(); + } + Serial.println("Authentication succeeded"); + server.send(200, "text/plain", "Login OK"); + return; + }); + + server.begin(); + + Serial.print("Open http://"); + Serial.print(WiFi.localIP()); + Serial.println("/ in your browser to see it working"); +} + +void loop() { + ArduinoOTA.handle(); + server.handleClient(); + delay(2);//allow the cpu to switch to other tasks +} diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index ea9b4d5692e..dd443dbd8e8 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include "esp_random.h" #include "WiFiServer.h" @@ -31,7 +32,8 @@ #include "FS.h" #include "detail/RequestHandlersImpl.h" #include "MD5Builder.h" - +#include "SHA1Builder.h" +#include "base64.h" static const char AUTHORIZATION_HEADER[] = "Authorization"; static const char qop_auth[] PROGMEM = "qop=auth"; @@ -40,7 +42,6 @@ static const char WWW_Authenticate[] = "WWW-Authenticate"; static const char Content_Length[] = "Content-Length"; static const char ETAG_HEADER[] = "If-None-Match"; - WebServer::WebServer(IPAddress addr, int port) : _corsEnabled(false) , _server(addr, port) @@ -128,91 +129,185 @@ static String md5str(String &in){ return md5.toString(); } -bool WebServer::authenticate(const char * username, const char * password){ - if(hasHeader(FPSTR(AUTHORIZATION_HEADER))) { - String authReq = header(FPSTR(AUTHORIZATION_HEADER)); - if(authReq.startsWith(F("Basic"))){ - authReq = authReq.substring(6); - authReq.trim(); - char toencodeLen = strlen(username)+strlen(password)+1; - char *toencode = (char *)malloc(toencodeLen + 1); - if(toencode == NULL){ - authReq = ""; - return false; - } - char *encoded = (char *)malloc(base64_encode_expected_len(toencodeLen)+1); - if(encoded == NULL){ - authReq = ""; - free(toencode); - return false; - } - sprintf(toencode, "%s:%s", username, password); - if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equalsConstantTime(encoded)) { - authReq = ""; - free(toencode); - free(encoded); - return true; - } - free(toencode); - free(encoded);; - } else if(authReq.startsWith(F("Digest"))) { - authReq = authReq.substring(7); - log_v("%s", authReq.c_str()); - String _username = _extractParam(authReq,F("username=\""),'\"'); - if(!_username.length() || _username != String(username)) { - authReq = ""; - return false; - } - // extracting required parameters for RFC 2069 simpler Digest - String _realm = _extractParam(authReq, F("realm=\""),'\"'); - String _nonce = _extractParam(authReq, F("nonce=\""),'\"'); - String _uri = _extractParam(authReq, F("uri=\""),'\"'); - String _response = _extractParam(authReq, F("response=\""),'\"'); - String _opaque = _extractParam(authReq, F("opaque=\""),'\"'); - - if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) { - authReq = ""; - return false; - } - if((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) { - authReq = ""; - return false; - } - // parameters for the RFC 2617 newer Digest - String _nc,_cnonce; - if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { - _nc = _extractParam(authReq, F("nc="), ','); - _cnonce = _extractParam(authReq, F("cnonce=\""),'\"'); - } - String _H1 = md5str(String(username) + ':' + _realm + ':' + String(password)); - log_v("Hash of user:realm:pass=%s", _H1.c_str()); - String _H2 = ""; - if(_currentMethod == HTTP_GET){ - _H2 = md5str(String(F("GET:")) + _uri); - }else if(_currentMethod == HTTP_POST){ - _H2 = md5str(String(F("POST:")) + _uri); - }else if(_currentMethod == HTTP_PUT){ - _H2 = md5str(String(F("PUT:")) + _uri); - }else if(_currentMethod == HTTP_DELETE){ - _H2 = md5str(String(F("DELETE:")) + _uri); - }else{ - _H2 = md5str(String(F("GET:")) + _uri); - } - log_v("Hash of GET:uri=%s", _H2.c_str()); - String _responsecheck = ""; - if(authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { - _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); - } else { - _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2); - } - log_v("The Proper response=%s", _responsecheck.c_str()); - if(_response == _responsecheck){ +bool WebServer::authenticateBasicSHA1(const char * _username, const char * _sha1Base64orHex) { + return WebServer::authenticate([_username,_sha1Base64orHex](HTTPAuthMethod mode, String username, String params[])->String * { + // rather than work on a password to compare with; we take the sha1 of the + // password received over the wire and compare that to the base64 encoded + // sha1 passed as _sha1base64. That way there is no need to have a + // plaintext password in the code/binary (though note that SHA1 is well + // past its retirement age). When that matches - we `cheat' by returning + // the password we got in the first place; so the normal BasicAuth + // can be completed. Note that this cannot work for a digest auth - + // as there the password in the clear is part of the calculation. + + if (params == nullptr) { + log_e("Something went wrong. params is NULL"); + return NULL; + } + + uint8_t sha1[20]; + char sha1calc[48]; // large enough for base64 and Hex represenation + String ret; + SHA1Builder sha_builder; + base64 b64; + + log_v("Trying to authenticate user %s using SHA1.", username.c_str()); + sha_builder.begin(); + sha_builder.add((uint8_t*) params[0].c_str(), params[0].length()); + sha_builder.calculate(); + sha_builder.getBytes(sha1); + + // we can either decode _sha1base64orHex and then compare the 20 bytes; + // or encode the sha we calculated. We pick the latter as encoding of a + // fixed array of 20 bytes is safer than operating on something external. + if (strlen(_sha1Base64orHex) == 20 * 2) { // 2 chars per byte + sha_builder.bytes2hex(sha1calc, sizeof(sha1calc), sha1, sizeof(sha1)); + log_v("Calculated SHA1 in hex: %s", sha1calc); + } else { + ret = b64.encode(sha1, sizeof(sha1)); + ret.toCharArray(sha1calc, sizeof(sha1calc)); + log_v("Calculated SHA1 in base64: %s", sha1calc); + } + + return ((username.equalsConstantTime(_username)) && + (String((char*)sha1calc).equalsConstantTime(_sha1Base64orHex)) && + (mode == BASIC_AUTH) /* to keep things somewhat time constant. */ + ) ? new String(params[0]) : NULL; + }); +} + +bool WebServer::authenticate(const char * _username, const char * _password) { + return WebServer::authenticate([_username,_password](HTTPAuthMethod mode, String username, String params[])->String * { + return username.equalsConstantTime(_username) ? new String(_password) : NULL; + }); +} + +bool WebServer::authenticate(THandlerFunctionAuthCheck fn) { + if (!hasHeader(FPSTR(AUTHORIZATION_HEADER))) { + return false; + } + + String authReq = header(FPSTR(AUTHORIZATION_HEADER)); + if (authReq.startsWith(AuthTypeBasic)) { + log_v("Trying to authenticate using Basic Auth"); + bool ret = false; + + authReq = authReq.substring(6); // length of AuthTypeBasic including the space at the end. + authReq.trim(); + + /* base64 encoded string is always shorter (or equal) in length */ + char* decoded = (authReq.length() < HTTP_MAX_BASIC_AUTH_LEN) ? new char[authReq.length()] : NULL; + if (decoded) { + char* p; + if (base64_decode_chars(authReq.c_str(), authReq.length(), decoded) && (p = index(decoded, ':')) && p) { authReq = ""; - return true; + /* Note: rfc7617 guarantees that there will not be an escaped colon in the username itself. + * Note: base64_decode_chars() guarantees a terminating \0 + */ + *p = '\0'; + char * _username = decoded, * _password = p + 1; + String params[] = { + _password, + _srealm + }; + String* password = fn(BASIC_AUTH, _username, params); + + if (password) { + ret = password->equalsConstantTime(_password); + // we're more concerned about the password; as the attacker already + // knows the _pasword. Arduino's string handling is simple; it reallocs + // even when smaller; so a memset is enough (no capacity/size). + memset((void *)password->c_str(), 0, password->length()); + delete password; + } } + delete[] decoded; + } + authReq = ""; + log_v("Authentication %s", ret ? "Success" : "Failed"); + return ret; + } else if (authReq.startsWith(AuthTypeDigest)) { + log_v("Trying to authenticate using Digest Auth"); + authReq = authReq.substring(7); + log_v("%s", authReq.c_str()); + + // extracting required parameters for RFC 2069 simpler Digest + String _username = _extractParam(authReq, F("username=\""), '\"'); + String _realm = _extractParam(authReq, F("realm=\""), '\"'); + String _uri = _extractParam(authReq, F("uri=\""), '\"'); + if (!_username.length()) + goto exf; + + String params[] = { + _realm, + _uri + }; + String* password = fn(DIGEST_AUTH, _username, params); + if (!password) + goto exf; + + String _H1 = md5str(String(_username) + ':' + _realm + ':' + * password); + // we're extra concerned; as digest request us to know the password + // in the clear. + memset((void *) password->c_str(), 0, password->length()); + delete password; + _username = ""; + + String _nonce = _extractParam(authReq, F("nonce=\""), '\"'); + String _response = _extractParam(authReq, F("response=\""), '\"'); + String _opaque = _extractParam(authReq, F("opaque=\""), '\"'); + + if ((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) + goto exf; + + if ((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) + goto exf; + + // parameters for the RFC 2617 newer Digest + String _nc, _cnonce; + if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { + _nc = _extractParam(authReq, F("nc="), ','); + _cnonce = _extractParam(authReq, F("cnonce=\""), '\"'); + } + + log_v("Hash of user:realm:pass=%s", _H1.c_str()); + String _H2 = ""; + if (_currentMethod == HTTP_GET) { + _H2 = md5str(String(F("GET:")) + _uri); + } else if (_currentMethod == HTTP_POST) { + _H2 = md5str(String(F("POST:")) + _uri); + } else if (_currentMethod == HTTP_PUT) { + _H2 = md5str(String(F("PUT:")) + _uri); + } else if (_currentMethod == HTTP_DELETE) { + _H2 = md5str(String(F("DELETE:")) + _uri); + } else { + _H2 = md5str(String(F("GET:")) + _uri); + } + log_v("Hash of GET:uri=%s", _H2.c_str()); + String _responsecheck = ""; + if (authReq.indexOf(FPSTR(qop_auth)) != -1 || authReq.indexOf(FPSTR(qop_auth_quoted)) != -1) { + _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _nc + ':' + _cnonce + F(":auth:") + _H2); + } else { + _responsecheck = md5str(_H1 + ':' + _nonce + ':' + _H2); } authReq = ""; + + log_v("The Proper response=%s", _responsecheck.c_str()); + bool ret = _response == _responsecheck; + log_v("Authentication %s", ret ? "Success" : "Failed"); + return ret; + } else if (authReq.length()) { + // OTHER_AUTH + log_v("Trying to authenticate using Other Auth, authReq=%s", authReq.c_str()); + String* ret = fn(OTHER_AUTH, authReq, {}); + if (ret) { + log_v("Authentication Success"); + return true; + } } +exf: + authReq = ""; + log_v("Authentication Failed"); return false; } @@ -232,11 +327,11 @@ void WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, co _srealm = String(realm); } if(mode == BASIC_AUTH) { - sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\""))); + sendHeader(String(FPSTR(WWW_Authenticate)), AuthTypeBasic + String(F(" realm=\"")) + _srealm + String(F("\""))); } else { _snonce=_getRandomHexString(); _sopaque=_getRandomHexString(); - sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\""))); + sendHeader(String(FPSTR(WWW_Authenticate)), AuthTypeDigest + String(F(" realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\""))); } using namespace mime; send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); @@ -411,8 +506,8 @@ void WebServer::_prepareHeader(String& response, int code, const char* content_t } if (_corsEnabled) { sendHeader(String(FPSTR("Access-Control-Allow-Origin")), String("*")); - sendHeader(String(FPSTR("Access-Control-Allow-Methods")), String("*")); - sendHeader(String(FPSTR("Access-Control-Allow-Headers")), String("*")); + sendHeader(String(FPSTR("Access-Control-Allow-Methods")), String("*")); + sendHeader(String(FPSTR("Access-Control-Allow-Headers")), String("*")); } sendHeader(String(F("Connection")), String(F("close"))); @@ -543,9 +638,9 @@ String WebServer::pathArg(unsigned int i) { String WebServer::arg(String name) { for (int j = 0; j < _postArgsLen; ++j) { - if ( _postArgs[j].key == name ) - return _postArgs[j].value; - } + if ( _postArgs[j].key == name ) + return _postArgs[j].value; + } for (int i = 0; i < _currentArgCount; ++i) { if ( _currentArgs[i].key == name ) return _currentArgs[i].value; @@ -571,9 +666,9 @@ int WebServer::args() { bool WebServer::hasArg(String name) { for (int j = 0; j < _postArgsLen; ++j) { - if (_postArgs[j].key == name) - return true; - } + if (_postArgs[j].key == name) + return true; + } for (int i = 0; i < _currentArgCount; ++i) { if (_currentArgs[i].key == name) return true; diff --git a/libraries/WebServer/src/WebServer.h b/libraries/WebServer/src/WebServer.h index aa703059d13..d668c4e70e7 100644 --- a/libraries/WebServer/src/WebServer.h +++ b/libraries/WebServer/src/WebServer.h @@ -34,7 +34,7 @@ enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END, UPLOAD_FILE_ABORTED }; enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE }; -enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; +enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH, OTHER_AUTH }; #define HTTP_DOWNLOAD_UNIT_SIZE 1436 @@ -46,6 +46,7 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; #define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive #define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed #define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection +#define HTTP_MAX_BASIC_AUTH_LEN 256 // maximum length of a basic Auth base64 encoded username:password string #define CONTENT_LENGTH_UNKNOWN ((size_t) -1) #define CONTENT_LENGTH_NOT_SET ((size_t) -2) @@ -82,12 +83,41 @@ class WebServer virtual void close(); void stop(); + const String AuthTypeDigest = F("Digest"); + const String AuthTypeBasic = F("Basic"); + + /* Callbackhandler for authentication. The extra parameters depend on the + * HTTPAuthMethod mode: + * + * BASIC_AUTH enteredUsernameOrReq contains the username entered by the user + * param[0] password entered (in the clear) + * param[1] authentication realm. + * + * To return - the password the user entered password is compared to. Or Null on fail. + * + * DIGEST_AUTH enteredUsernameOrReq contains the username entered by the user + * param[0] autenticaiton realm + * param[1] authentication URI + * + * To return - the password of which the digest will be based on for comparison. Or NULL + * to fail. + * + * OTHER_AUTH enteredUsernameOrReq rest of the auth line. + * params empty array + * + * To return - NULL to fail; or any string. + */ + typedef std::function THandlerFunctionAuthCheck; + + bool authenticate(THandlerFunctionAuthCheck fn); bool authenticate(const char * username, const char * password); + bool authenticateBasicSHA1(const char * _username, const char * _sha1AsBase64orHex); + void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") ); typedef std::function THandlerFunction; void on(const Uri &uri, THandlerFunction fn); - void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); + void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); //ufn handles file uploads void addHandler(RequestHandler* handler); void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); diff --git a/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino b/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino index 771a531a480..be9aa239627 100644 --- a/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino +++ b/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino @@ -89,7 +89,6 @@ void WiFiEvent(WiFiEvent_t event){ break; case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("STA Connected"); - WiFi.enableIpV6(); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP6: Serial.print("STA IPv6: "); @@ -114,6 +113,7 @@ void setup() { Serial.begin(115200); pinMode(0, INPUT_PULLUP); WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread. + WiFi.enableIPv6(); Serial.print("ESP32 SDK: "); Serial.println(ESP.getSdkVersion()); Serial.println("Press the button to select the next mode"); diff --git a/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino b/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino index 3dcba56eec2..c7edd384d82 100644 --- a/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino +++ b/libraries/WiFi/examples/WiFiIPv6/WiFiIPv6.ino @@ -70,16 +70,12 @@ void WiFiEvent(WiFiEvent_t event){ case ARDUINO_EVENT_WIFI_AP_START: //can set ap hostname here WiFi.softAPsetHostname(AP_SSID); - //enable ap ipv6 here - WiFi.softAPenableIpV6(); break; case ARDUINO_EVENT_WIFI_STA_START: //set sta hostname here WiFi.setHostname(AP_SSID); break; case ARDUINO_EVENT_WIFI_STA_CONNECTED: - //enable sta ipv6 here - WiFi.enableIpV6(); break; case ARDUINO_EVENT_WIFI_STA_GOT_IP6: Serial.print("STA IPv6: "); @@ -107,7 +103,11 @@ void setup(){ WiFi.disconnect(true); WiFi.onEvent(WiFiEvent); // Will call WiFiEvent() from another thread. WiFi.mode(WIFI_MODE_APSTA); + //enable ap ipv6 here + WiFi.softAPenableIPv6(); WiFi.softAP(AP_SSID); + //enable sta ipv6 here + WiFi.enableIPv6(); WiFi.begin(STA_SSID, STA_PASS); } diff --git a/libraries/WiFi/src/WiFi.h b/libraries/WiFi/src/WiFi.h index 457f913ae8e..30b9e31f744 100644 --- a/libraries/WiFi/src/WiFi.h +++ b/libraries/WiFi/src/WiFi.h @@ -26,7 +26,6 @@ #include "Print.h" #include "IPAddress.h" -#include "IPv6Address.h" #include "WiFiType.h" #include "WiFiSTA.h" diff --git a/libraries/WiFi/src/WiFiAP.cpp b/libraries/WiFi/src/WiFiAP.cpp index a88547f9a89..b02b929bce9 100644 --- a/libraries/WiFi/src/WiFiAP.cpp +++ b/libraries/WiFi/src/WiFiAP.cpp @@ -411,26 +411,35 @@ bool WiFiAPClass::softAPsetHostname(const char * hostname) * Enable IPv6 on the softAP interface. * @return true on success */ -bool WiFiAPClass::softAPenableIpV6() +bool WiFiAPClass::softAPenableIPv6(bool enable) { - if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){ - return false; + if (enable) { + WiFiGenericClass::setStatusBits(AP_WANT_IP6_BIT); + } else { + WiFiGenericClass::clearStatusBits(AP_WANT_IP6_BIT); } - return esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP)) == ESP_OK; + return true; + // if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){ + // return false; + // } + // return esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP)) == ESP_OK; } /** * Get the softAP interface IPv6 address. - * @return IPv6Address softAP IPv6 + * @return IPAddress softAP IPv6 */ -IPv6Address WiFiAPClass::softAPIPv6() + +IPAddress WiFiAPClass::softAPIPv6() { - esp_ip6_addr_t addr; + static esp_ip6_addr_t addr; if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){ - return IPv6Address(); + return IPAddress(IPv6); } - if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP), &addr)) { - return IPv6Address(); + + if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)){ + return IPAddress(IPv6); } - return IPv6Address(addr.addr); + return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone); } + diff --git a/libraries/WiFi/src/WiFiAP.h b/libraries/WiFi/src/WiFiAP.h index 626a5b5b350..a07fab54335 100644 --- a/libraries/WiFi/src/WiFiAP.h +++ b/libraries/WiFi/src/WiFiAP.h @@ -56,8 +56,8 @@ class WiFiAPClass IPAddress softAPSubnetMask(); uint8_t softAPSubnetCIDR(); - bool softAPenableIpV6(); - IPv6Address softAPIPv6(); + bool softAPenableIPv6(bool enable=true); + IPAddress softAPIPv6(); const char * softAPgetHostname(); bool softAPsetHostname(const char * hostname); diff --git a/libraries/WiFi/src/WiFiClient.cpp b/libraries/WiFi/src/WiFiClient.cpp index 864c33322f3..52aca2f1bd9 100644 --- a/libraries/WiFi/src/WiFiClient.cpp +++ b/libraries/WiFi/src/WiFiClient.cpp @@ -23,6 +23,11 @@ #include #include +#define IN6_IS_ADDR_V4MAPPED(a) \ + ((((__const uint32_t *) (a))[0] == 0) \ + && (((__const uint32_t *) (a))[1] == 0) \ + && (((__const uint32_t *) (a))[2] == htonl (0xffff))) + #define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000) #define WIFI_CLIENT_MAX_WRITE_RETRY (10) #define WIFI_CLIENT_SELECT_TIMEOUT_US (1000000) @@ -197,15 +202,6 @@ WiFiClient::~WiFiClient() stop(); } -WiFiClient & WiFiClient::operator=(const WiFiClient &other) -{ - stop(); - clientSocketHandle = other.clientSocketHandle; - _rxBuffer = other._rxBuffer; - _connected = other._connected; - return *this; -} - void WiFiClient::stop() { clientSocketHandle = NULL; @@ -219,22 +215,33 @@ int WiFiClient::connect(IPAddress ip, uint16_t port) { return connect(ip,port,_timeout); } + int WiFiClient::connect(IPAddress ip, uint16_t port, int32_t timeout_ms) { + struct sockaddr_storage serveraddr = {}; _timeout = timeout_ms; - int sockfd = socket(AF_INET, SOCK_STREAM, 0); + int sockfd = -1; + + if (ip.type() == IPv6) { + struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr; + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + tmpaddr->sin6_family = AF_INET6; + memcpy(tmpaddr->sin6_addr.un.u8_addr, &ip[0], 16); + tmpaddr->sin6_port = htons(port); + tmpaddr->sin6_scope_id = ip.zone(); + } else { + struct sockaddr_in *tmpaddr = (struct sockaddr_in *)&serveraddr; + sockfd = socket(AF_INET, SOCK_STREAM, 0); + tmpaddr->sin_family = AF_INET; + tmpaddr->sin_addr.s_addr = ip; + tmpaddr->sin_port = htons(port); + } if (sockfd < 0) { log_e("socket: %d", errno); return 0; } fcntl( sockfd, F_SETFL, fcntl( sockfd, F_GETFL, 0 ) | O_NONBLOCK ); - uint32_t ip_addr = ip; - struct sockaddr_in serveraddr; - memset((char *) &serveraddr, 0, sizeof(serveraddr)); - serveraddr.sin_family = AF_INET; - memcpy((void *)&serveraddr.sin_addr.s_addr, (const void *)(&ip_addr), 4); - serveraddr.sin_port = htons(port); fd_set fdset; struct timeval tv; FD_ZERO(&fdset); @@ -574,8 +581,24 @@ IPAddress WiFiClient::remoteIP(int fd) const struct sockaddr_storage addr; socklen_t len = sizeof addr; getpeername(fd, (struct sockaddr*)&addr, &len); - struct sockaddr_in *s = (struct sockaddr_in *)&addr; - return IPAddress((uint32_t)(s->sin_addr.s_addr)); + + // IPv4 socket, old way + if (((struct sockaddr*)&addr)->sa_family == AF_INET) { + struct sockaddr_in *s = (struct sockaddr_in *)&addr; + return IPAddress((uint32_t)(s->sin_addr.s_addr)); + } + + // IPv6, but it might be IPv4 mapped address + if (((struct sockaddr*)&addr)->sa_family == AF_INET6) { + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&addr; + if (IN6_IS_ADDR_V4MAPPED(saddr6->sin6_addr.un.u32_addr)) { + return IPAddress(IPv4, (uint8_t*)saddr6->sin6_addr.s6_addr+IPADDRESS_V4_BYTES_INDEX); + } else { + return IPAddress(IPv6, (uint8_t*)(saddr6->sin6_addr.s6_addr), saddr6->sin6_scope_id); + } + } + log_e("WiFiClient::remoteIP Not AF_INET or AF_INET6?"); + return (IPAddress(0,0,0,0)); } uint16_t WiFiClient::remotePort(int fd) const @@ -638,4 +661,3 @@ int WiFiClient::fd() const return clientSocketHandle->fd(); } } - diff --git a/libraries/WiFi/src/WiFiClient.h b/libraries/WiFi/src/WiFiClient.h index 06e77c7cd1d..d78e00fb19e 100644 --- a/libraries/WiFi/src/WiFiClient.h +++ b/libraries/WiFi/src/WiFiClient.h @@ -71,7 +71,6 @@ class WiFiClient : public ESPLwIPClient { return connected(); } - WiFiClient & operator=(const WiFiClient &other); bool operator==(const bool value) { return bool() == value; diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp index 8ef803064c4..2fade00e450 100644 --- a/libraries/WiFi/src/WiFiGeneric.cpp +++ b/libraries/WiFi/src/WiFiGeneric.cpp @@ -42,6 +42,7 @@ extern "C" { #include "lwip/opt.h" #include "lwip/err.h" #include "lwip/dns.h" +#include "lwip/netif.h" #include "dhcpserver/dhcpserver.h" #include "dhcpserver/dhcpserver_options.h" @@ -191,16 +192,17 @@ esp_err_t set_esp_interface_ip(esp_interface_t interface, IPAddress local_ip=IPA lease.start_ip.addr = _byte_swap32(lease.start_ip.addr); lease.end_ip.addr = _byte_swap32(lease.end_ip.addr); log_v("DHCP Server Range: %s to %s", IPAddress(lease.start_ip.addr).toString().c_str(), IPAddress(lease.end_ip.addr).toString().c_str()); - err = esp_netif_dhcps_option( - esp_netif, - ESP_NETIF_OP_SET, - ESP_NETIF_SUBNET_MASK, - (void*)&info.netmask.addr, sizeof(info.netmask.addr) - ); - if(err){ - log_e("DHCPS Set Netmask Failed! 0x%04x", err); - return err; - } + // Following block is commented because it breaks AP DHCPS on recent ESP-IDF + // err = esp_netif_dhcps_option( + // esp_netif, + // ESP_NETIF_OP_SET, + // ESP_NETIF_SUBNET_MASK, + // (void*)&info.netmask.addr, sizeof(info.netmask.addr) + // ); + // if(err){ + // log_e("DHCPS Set Netmask Failed! 0x%04x", err); + // return err; + // } err = esp_netif_dhcps_option( esp_netif, ESP_NETIF_OP_SET, @@ -459,7 +461,13 @@ static void _arduino_event_cb(void* arg, esp_event_base_t event_base, int32_t ev } else if (event_base == IP_EVENT && event_id == IP_EVENT_GOT_IP6) { ip_event_got_ip6_t * event = (ip_event_got_ip6_t*)event_data; esp_interface_t iface = get_esp_netif_interface(event->esp_netif); - log_v("IF[%d] Got IPv6: IP Index: %d, Zone: %d, " IPV6STR, iface, event->ip_index, event->ip6_info.ip.zone, IPV62STR(event->ip6_info.ip)); +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE + char if_name[NETIF_NAMESIZE] = {0,}; + netif_index_to_name(event->ip6_info.ip.zone, if_name); + esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type(&event->ip6_info.ip); + static const char * addr_types[] = { "UNKNOWN", "GLOBAL", "LINK_LOCAL", "SITE_LOCAL", "UNIQUE_LOCAL", "IPV4_MAPPED_IPV6" }; + log_v("IF %s Got IPv6: Interface: %d, IP Index: %d, Type: %s, Zone: %d (%s), Address: " IPV6STR, esp_netif_get_desc(event->esp_netif), iface, event->ip_index, addr_types[addr_type], event->ip6_info.ip.zone, if_name, IPV62STR(event->ip6_info.ip)); +#endif memcpy(&arduino_event.event_info.got_ip6, event_data, sizeof(ip_event_got_ip6_t)); if(iface == ESP_IF_WIFI_STA){ arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_GOT_IP6; @@ -554,6 +562,8 @@ static void _arduino_event_cb(void* arg, esp_event_base_t event_base, int32_t ev } } +static uint32_t _initial_bits = NET_DNS_IDLE_BIT; + static bool _start_network_event_task(){ if(!_arduino_event_group){ _arduino_event_group = xEventGroupCreate(); @@ -561,7 +571,7 @@ static bool _start_network_event_task(){ log_e("Network Event Group Create Failed!"); return false; } - xEventGroupSetBits(_arduino_event_group, WIFI_DNS_IDLE_BIT); + xEventGroupSetBits(_arduino_event_group, _initial_bits); } if(!_arduino_event_queue){ _arduino_event_queue = xQueueCreate(32, sizeof(arduino_event_t*)); @@ -909,21 +919,23 @@ bool WiFiGenericClass::setHostname(const char * hostname) int WiFiGenericClass::setStatusBits(int bits){ if(!_arduino_event_group){ - return 0; + _initial_bits |= bits; + return _initial_bits; } return xEventGroupSetBits(_arduino_event_group, bits); } int WiFiGenericClass::clearStatusBits(int bits){ if(!_arduino_event_group){ - return 0; + _initial_bits &= ~bits; + return _initial_bits; } return xEventGroupClearBits(_arduino_event_group, bits); } int WiFiGenericClass::getStatusBits(){ if(!_arduino_event_group){ - return 0; + return _initial_bits; } return xEventGroupGetBits(_arduino_event_group); } @@ -1051,12 +1063,16 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event) } } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_STOP) { WiFiSTAClass::_setStatus(WL_STOPPED); - clearStatusBits(STA_STARTED_BIT | STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT); + clearStatusBits(STA_STARTED_BIT | STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT | STA_HAS_IP6_GLOBAL_BIT); } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_CONNECTED) { + if (getStatusBits() & STA_WANT_IP6_BIT){ + esp_err_t err = esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA)); + if(err != ESP_OK){ + log_e("Failed to enable IPv6 Link Local on STA: [%d] %s", err, esp_err_to_name(err)); + } + } WiFiSTAClass::_setStatus(WL_IDLE_STATUS); setStatusBits(STA_CONNECTED_BIT); - - //esp_netif_create_ip6_linklocal(esp_netifs[ESP_IF_WIFI_STA]); } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_DISCONNECTED) { uint8_t reason = event->event_info.wifi_sta_disconnected.reason; // Reason 0 causes crash, use reason 1 (UNSPECIFIED) instead @@ -1074,7 +1090,7 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event) } else { WiFiSTAClass::_setStatus(WL_DISCONNECTED); } - clearStatusBits(STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT); + clearStatusBits(STA_CONNECTED_BIT | STA_HAS_IP_BIT | STA_HAS_IP6_BIT | STA_HAS_IP6_GLOBAL_BIT); bool DoReconnect = false; if(reason == WIFI_REASON_ASSOC_LEAVE) { //Voluntarily disconnected. Don't reconnect! @@ -1113,6 +1129,12 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event) } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_START) { setStatusBits(AP_STARTED_BIT); + if (getStatusBits() & AP_WANT_IP6_BIT){ + esp_err_t err = esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_AP)); + if(err != ESP_OK){ + log_e("Failed to enable IPv6 Link Local on AP: [%d] %s", err, esp_err_to_name(err)); + } + } } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_STOP) { clearStatusBits(AP_STARTED_BIT | AP_HAS_CLIENT_BIT); } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_STACONNECTED) { @@ -1126,11 +1148,17 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event) } else if(event->event_id == ARDUINO_EVENT_ETH_START) { setStatusBits(ETH_STARTED_BIT); } else if(event->event_id == ARDUINO_EVENT_ETH_STOP) { - clearStatusBits(ETH_STARTED_BIT | ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT); + clearStatusBits(ETH_STARTED_BIT | ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT | ETH_HAS_IP6_GLOBAL_BIT); } else if(event->event_id == ARDUINO_EVENT_ETH_CONNECTED) { + if (getStatusBits() & ETH_WANT_IP6_BIT){ + esp_err_t err = esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_ETH)); + if(err != ESP_OK){ + log_e("Failed to enable IPv6 Link Local on ETH: [%d] %s", err, esp_err_to_name(err)); + } + } setStatusBits(ETH_CONNECTED_BIT); } else if(event->event_id == ARDUINO_EVENT_ETH_DISCONNECTED) { - clearStatusBits(ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT); + clearStatusBits(ETH_CONNECTED_BIT | ETH_HAS_IP_BIT | ETH_HAS_IP6_BIT | ETH_HAS_IP6_GLOBAL_BIT); } else if(event->event_id == ARDUINO_EVENT_ETH_GOT_IP) { #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG uint8_t * ip = (uint8_t *)&(event->event_info.got_ip.ip_info.ip.addr); @@ -1146,11 +1174,26 @@ esp_err_t WiFiGenericClass::_eventCallback(arduino_event_t *event) clearStatusBits(ETH_HAS_IP_BIT); } else if(event->event_id == ARDUINO_EVENT_WIFI_STA_GOT_IP6) { - setStatusBits(STA_CONNECTED_BIT | STA_HAS_IP6_BIT); + setStatusBits(STA_CONNECTED_BIT); + esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip)); + if(addr_type == ESP_IP6_ADDR_IS_GLOBAL){ + setStatusBits(STA_HAS_IP6_GLOBAL_BIT); + } else if(addr_type == ESP_IP6_ADDR_IS_LINK_LOCAL){ + setStatusBits(STA_HAS_IP6_BIT); + } } else if(event->event_id == ARDUINO_EVENT_WIFI_AP_GOT_IP6) { - setStatusBits(AP_HAS_IP6_BIT); + esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip)); + if(addr_type == ESP_IP6_ADDR_IS_LINK_LOCAL){ + setStatusBits(AP_HAS_IP6_BIT); + } } else if(event->event_id == ARDUINO_EVENT_ETH_GOT_IP6) { - setStatusBits(ETH_CONNECTED_BIT | ETH_HAS_IP6_BIT); + setStatusBits(ETH_CONNECTED_BIT); + esp_ip6_addr_type_t addr_type = esp_netif_ip6_get_addr_type((esp_ip6_addr_t*)&(event->event_info.got_ip6.ip6_info.ip)); + if(addr_type == ESP_IP6_ADDR_IS_GLOBAL){ + setStatusBits(ETH_HAS_IP6_GLOBAL_BIT); + } else if(addr_type == ESP_IP6_ADDR_IS_LINK_LOCAL){ + setStatusBits(ETH_HAS_IP6_BIT); + } } else if(event->event_id == ARDUINO_EVENT_SC_GOT_SSID_PSWD) { WiFi.begin( (const char *)event->event_info.sc_got_ssid_pswd.ssid, @@ -1546,6 +1589,13 @@ bool WiFiGenericClass::setDualAntennaConfig(uint8_t gpio_ant1, uint8_t gpio_ant2 // ------------------------------------------------ Generic Network function --------------------------------------------- // ----------------------------------------------------------------------------------------------------------------------- +typedef struct gethostbynameParameters { + const char *hostname; + ip_addr_t addr; + uint8_t addr_type; + int result; +} gethostbynameParameters_t; + /** * DNS callback * @param name @@ -1554,18 +1604,18 @@ bool WiFiGenericClass::setDualAntennaConfig(uint8_t gpio_ant1, uint8_t gpio_ant2 */ static void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) { + gethostbynameParameters_t *parameters = static_cast(callback_arg); if(ipaddr) { - (*reinterpret_cast(callback_arg)) = ipaddr->u_addr.ip4.addr; + if(parameters->result == 0){ + memcpy(&(parameters->addr), ipaddr, sizeof(ip_addr_t)); + parameters->result = 1; + } + } else { + parameters->result = -1; } - xEventGroupSetBits(_arduino_event_group, WIFI_DNS_DONE_BIT); + xEventGroupSetBits(_arduino_event_group, NET_DNS_DONE_BIT); } -typedef struct gethostbynameParameters { - const char *hostname; - ip_addr_t addr; - void *callback_arg; -} gethostbynameParameters_t; - /** * Callback to execute dns_gethostbyname in lwIP's TCP/IP context * @param param Parameters for dns_gethostbyname call @@ -1573,39 +1623,60 @@ typedef struct gethostbynameParameters { static esp_err_t wifi_gethostbyname_tcpip_ctx(void *param) { gethostbynameParameters_t *parameters = static_cast(param); - return dns_gethostbyname(parameters->hostname, ¶meters->addr, &wifi_dns_found_callback, parameters->callback_arg); + return dns_gethostbyname_addrtype(parameters->hostname, ¶meters->addr, &wifi_dns_found_callback, parameters, parameters->addr_type); } /** - * Resolve the given hostname to an IP address. If passed hostname is an IP address, it will be parsed into IPAddress structure. - * @param aHostname Name to be resolved or string containing IP address + * Resolve the given hostname to an IP address. + * @param aHostname Name to be resolved * @param aResult IPAddress structure to store the returned IP address * @return 1 if aIPAddrString was successfully converted to an IP address, * else error code */ -int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult) +int WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, bool preferV6) { - if (!aResult.fromString(aHostname)) - { - gethostbynameParameters_t params; - params.hostname = aHostname; - params.callback_arg = &aResult; - aResult = static_cast(0); - waitStatusBits(WIFI_DNS_IDLE_BIT, 16000); - clearStatusBits(WIFI_DNS_IDLE_BIT | WIFI_DNS_DONE_BIT); - err_t err = esp_netif_tcpip_exec(wifi_gethostbyname_tcpip_ctx, ¶ms); - if(err == ERR_OK && params.addr.u_addr.ip4.addr) { - aResult = params.addr.u_addr.ip4.addr; - } else if(err == ERR_INPROGRESS) { - waitStatusBits(WIFI_DNS_DONE_BIT, 15000); //real internal timeout in lwip library is 14[s] - clearStatusBits(WIFI_DNS_DONE_BIT); - } - setStatusBits(WIFI_DNS_IDLE_BIT); - if((uint32_t)aResult == 0){ - log_e("DNS Failed for %s", aHostname); + err_t err = ERR_OK; + gethostbynameParameters_t params; + + // This should generally check if we have a global address assigned to one of the interfaces. + // If such address is not assigned, there is no point in trying to get V6 from DNS as we will not be able to reach it. + // That is of course, if 'preferV6' is not set to true + static bool hasGlobalV6 = false; + bool hasGlobalV6Now = (getStatusBits() & NET_HAS_IP6_GLOBAL_BIT) != 0; + if(hasGlobalV6 != hasGlobalV6Now){ + hasGlobalV6 = hasGlobalV6Now; + dns_clear_cache(); + log_d("Clearing DNS cache"); + } + + aResult = static_cast(0); + params.hostname = aHostname; + params.addr_type = (preferV6 || hasGlobalV6)?LWIP_DNS_ADDRTYPE_IPV6_IPV4:LWIP_DNS_ADDRTYPE_IPV4; + params.result = 0; + aResult.to_ip_addr_t(&(params.addr)); + + if (!aResult.fromString(aHostname)) { + waitStatusBits(NET_DNS_IDLE_BIT, 16000); + clearStatusBits(NET_DNS_IDLE_BIT | NET_DNS_DONE_BIT); + + err = esp_netif_tcpip_exec(wifi_gethostbyname_tcpip_ctx, ¶ms); + if (err == ERR_OK) { + aResult.from_ip_addr_t(&(params.addr)); + } else if (err == ERR_INPROGRESS) { + waitStatusBits(NET_DNS_DONE_BIT, 15000); //real internal timeout in lwip library is 14[s] + clearStatusBits(NET_DNS_DONE_BIT); + if (params.result == 1) { + aResult.from_ip_addr_t(&(params.addr)); + err = ERR_OK; + } } + setStatusBits(NET_DNS_IDLE_BIT); + } + if (err == ERR_OK) { + return 1; } - return (uint32_t)aResult != 0; + log_e("DNS Failed for '%s' with error '%d' and result '%d'", aHostname, err, params.result); + return err; } IPAddress WiFiGenericClass::calculateNetworkID(IPAddress ip, IPAddress subnet) { diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h index 38396f5a72e..3de43905416 100644 --- a/libraries/WiFi/src/WiFiGeneric.h +++ b/libraries/WiFi/src/WiFiGeneric.h @@ -32,6 +32,7 @@ #include "esp_netif_types.h" #include "esp_eth_driver.h" #include "wifi_provisioning/manager.h" +#include "lwip/ip_addr.h" ESP_EVENT_DECLARE_BASE(ARDUINO_EVENTS); @@ -111,6 +112,34 @@ typedef void (*WiFiEventSysCb)(arduino_event_t *event); typedef size_t wifi_event_id_t; +// General Flags +static const int NET_DNS_IDLE_BIT = BIT0; +static const int NET_DNS_DONE_BIT = BIT1; +// WiFi Scan Flags +static const int WIFI_SCANNING_BIT = BIT2; +static const int WIFI_SCAN_DONE_BIT = BIT3; +// AP Flags +static const int AP_STARTED_BIT = BIT4; +static const int AP_HAS_IP6_BIT = BIT5; +static const int AP_HAS_CLIENT_BIT = BIT6; +static const int AP_WANT_IP6_BIT = BIT7; +// STA Flags +static const int STA_STARTED_BIT = BIT8; +static const int STA_CONNECTED_BIT = BIT9; +static const int STA_HAS_IP_BIT = BIT10; +static const int STA_HAS_IP6_BIT = BIT11; +static const int STA_HAS_IP6_GLOBAL_BIT = BIT12; +static const int STA_WANT_IP6_BIT = BIT13; +// ETH Flags +static const int ETH_STARTED_BIT = BIT14; +static const int ETH_CONNECTED_BIT = BIT15; +static const int ETH_HAS_IP_BIT = BIT16; +static const int ETH_HAS_IP6_BIT = BIT17; +static const int ETH_HAS_IP6_GLOBAL_BIT = BIT18; +static const int ETH_WANT_IP6_BIT = BIT19; +// Masks +static const int NET_HAS_IP6_GLOBAL_BIT = STA_HAS_IP6_GLOBAL_BIT | ETH_HAS_IP6_GLOBAL_BIT; + typedef enum { WIFI_POWER_19_5dBm = 78,// 19.5dBm WIFI_POWER_19dBm = 76,// 19dBm @@ -126,22 +155,6 @@ typedef enum { WIFI_POWER_MINUS_1dBm = -4// -1dBm } wifi_power_t; -static const int AP_STARTED_BIT = BIT0; -static const int AP_HAS_IP6_BIT = BIT1; -static const int AP_HAS_CLIENT_BIT = BIT2; -static const int STA_STARTED_BIT = BIT3; -static const int STA_CONNECTED_BIT = BIT4; -static const int STA_HAS_IP_BIT = BIT5; -static const int STA_HAS_IP6_BIT = BIT6; -static const int ETH_STARTED_BIT = BIT7; -static const int ETH_CONNECTED_BIT = BIT8; -static const int ETH_HAS_IP_BIT = BIT9; -static const int ETH_HAS_IP6_BIT = BIT10; -static const int WIFI_SCANNING_BIT = BIT11; -static const int WIFI_SCAN_DONE_BIT= BIT12; -static const int WIFI_DNS_IDLE_BIT = BIT13; -static const int WIFI_DNS_DONE_BIT = BIT14; - typedef enum { WIFI_RX_ANT0 = 0, WIFI_RX_ANT1, @@ -216,7 +229,7 @@ class WiFiGenericClass static bool _isReconnectableReason(uint8_t reason); public: - static int hostByName(const char *aHostname, IPAddress &aResult); + static int hostByName(const char *aHostname, IPAddress &aResult, bool preferV6=false); static IPAddress calculateNetworkID(IPAddress ip, IPAddress subnet); static IPAddress calculateBroadcast(IPAddress ip, IPAddress subnet); @@ -226,6 +239,7 @@ class WiFiGenericClass friend class WiFiSTAClass; friend class WiFiScanClass; friend class WiFiAPClass; + friend class ETHClass; }; #endif /* ESP32WIFIGENERIC_H_ */ diff --git a/libraries/WiFi/src/WiFiMulti.cpp b/libraries/WiFi/src/WiFiMulti.cpp index 3d69e481293..22ec1ea46f9 100644 --- a/libraries/WiFi/src/WiFiMulti.cpp +++ b/libraries/WiFi/src/WiFiMulti.cpp @@ -30,6 +30,7 @@ WiFiMulti::WiFiMulti() { + ipv6_support = false; } WiFiMulti::~WiFiMulti() @@ -159,6 +160,9 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) if(bestNetwork.ssid) { log_i("[WIFI] Connecting BSSID: %02X:%02X:%02X:%02X:%02X:%02X SSID: %s Channel: %d (%d)", bestBSSID[0], bestBSSID[1], bestBSSID[2], bestBSSID[3], bestBSSID[4], bestBSSID[5], bestNetwork.ssid, bestChannel, bestNetworkDb); + if (ipv6_support == true) { + WiFi.enableIPv6(); + } WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase, bestChannel, bestBSSID); status = WiFi.status(); @@ -202,3 +206,7 @@ uint8_t WiFiMulti::run(uint32_t connectTimeout) return status; } + +void WiFiMulti::enableIPv6(bool state) { + ipv6_support = state; +} diff --git a/libraries/WiFi/src/WiFiMulti.h b/libraries/WiFi/src/WiFiMulti.h index 38ddb5d9f95..d8551fab9dd 100644 --- a/libraries/WiFi/src/WiFiMulti.h +++ b/libraries/WiFi/src/WiFiMulti.h @@ -42,10 +42,12 @@ class WiFiMulti bool addAP(const char* ssid, const char *passphrase = NULL); + void enableIPv6(bool state); uint8_t run(uint32_t connectTimeout=5000); private: std::vector APlist; + bool ipv6_support; }; #endif /* WIFICLIENTMULTI_H_ */ diff --git a/libraries/WiFi/src/WiFiSTA.cpp b/libraries/WiFi/src/WiFiSTA.cpp index bf80d37393b..fab3bce6563 100644 --- a/libraries/WiFi/src/WiFiSTA.cpp +++ b/libraries/WiFi/src/WiFiSTA.cpp @@ -805,30 +805,50 @@ int8_t WiFiSTAClass::RSSI(void) /** * Enable IPv6 on the station interface. + * Should be called before WiFi.begin() + * * @return true on success */ -bool WiFiSTAClass::enableIpV6() +bool WiFiSTAClass::enableIPv6(bool en) { + if (en) { + WiFiGenericClass::setStatusBits(STA_WANT_IP6_BIT); + } else { + WiFiGenericClass::clearStatusBits(STA_WANT_IP6_BIT); + } + return true; +} + +/** + * Get the station interface link-local IPv6 address. + * @return IPAddress + */ +IPAddress WiFiSTAClass::localIPv6() +{ + static esp_ip6_addr_t addr; if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){ - return false; + return IPAddress(IPv6); + } + if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)){ + return IPAddress(IPv6); } - return esp_netif_create_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA)) == ESP_OK; + return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone); } /** - * Get the station interface IPv6 address. - * @return IPv6Address + * Get the station interface global IPv6 address. + * @return IPAddress */ -IPv6Address WiFiSTAClass::localIPv6() +IPAddress WiFiSTAClass::globalIPv6() { - esp_ip6_addr_t addr; + static esp_ip6_addr_t addr; if(WiFiGenericClass::getMode() == WIFI_MODE_NULL){ - return IPv6Address(); + return IPAddress(IPv6); } - if(esp_netif_get_ip6_linklocal(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)) { - return IPv6Address(); + if(esp_netif_get_ip6_global(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)){ + return IPAddress(IPv6); } - return IPv6Address(addr.addr); + return IPAddress(IPv6, (const uint8_t *)addr.addr, addr.zone); } diff --git a/libraries/WiFi/src/WiFiSTA.h b/libraries/WiFi/src/WiFiSTA.h index 07f01922cef..4fbb789aa04 100644 --- a/libraries/WiFi/src/WiFiSTA.h +++ b/libraries/WiFi/src/WiFiSTA.h @@ -93,8 +93,9 @@ class WiFiSTAClass IPAddress networkID(); uint8_t subnetCIDR(); - bool enableIpV6(); - IPv6Address localIPv6(); + bool enableIPv6(bool en=true); + IPAddress localIPv6(); + IPAddress globalIPv6(); // STA WiFi info static wl_status_t status(); diff --git a/libraries/WiFi/src/WiFiServer.cpp b/libraries/WiFi/src/WiFiServer.cpp index ef59cf14d6d..9d79b5f7c46 100644 --- a/libraries/WiFi/src/WiFiServer.cpp +++ b/libraries/WiFi/src/WiFiServer.cpp @@ -51,8 +51,8 @@ WiFiClient WiFiServer::accept(){ _accepted_sockfd = -1; } else { - struct sockaddr_in _client; - int cs = sizeof(struct sockaddr_in); + struct sockaddr_in6 _client; + int cs = sizeof(struct sockaddr_in6); #ifdef ESP_IDF_VERSION_MAJOR client_sock = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs); #else @@ -80,14 +80,21 @@ void WiFiServer::begin(uint16_t port, int enable){ if(port){ _port = port; } - struct sockaddr_in server; - sockfd = socket(AF_INET , SOCK_STREAM, 0); + struct sockaddr_in6 server; + sockfd = socket(AF_INET6 , SOCK_STREAM, 0); if (sockfd < 0) return; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); - server.sin_family = AF_INET; - server.sin_addr.s_addr = _addr; - server.sin_port = htons(_port); + server.sin6_family = AF_INET6; + if (_addr.type() == IPv4) { + memcpy(server.sin6_addr.s6_addr+11, (uint8_t*)&_addr[0], 4); + server.sin6_addr.s6_addr[10] = 0xFF; + server.sin6_addr.s6_addr[11] = 0xFF; + } else { + memcpy(server.sin6_addr.s6_addr, (uint8_t*)&_addr[0], 16); + } + memset(server.sin6_addr.s6_addr, 0x0, 16); + server.sin6_port = htons(_port); if(bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) return; if(listen(sockfd , _max_clients) < 0) @@ -110,8 +117,8 @@ bool WiFiServer::hasClient() { if (_accepted_sockfd >= 0) { return true; } - struct sockaddr_in _client; - int cs = sizeof(struct sockaddr_in); + struct sockaddr_in6 _client; + int cs = sizeof(struct sockaddr_in6); #ifdef ESP_IDF_VERSION_MAJOR _accepted_sockfd = lwip_accept(sockfd, (struct sockaddr *)&_client, (socklen_t*)&cs); #else @@ -140,4 +147,3 @@ void WiFiServer::close(){ void WiFiServer::stop(){ end(); } - diff --git a/libraries/WiFi/src/WiFiServer.h b/libraries/WiFi/src/WiFiServer.h index c51986701c4..5a022d14829 100644 --- a/libraries/WiFi/src/WiFiServer.h +++ b/libraries/WiFi/src/WiFiServer.h @@ -37,7 +37,6 @@ class WiFiServer : public Server { public: void listenOnLocalhost(){} - // _addr(INADDR_ANY) is the same as _addr() ==> 0.0.0.0 WiFiServer(uint16_t port=80, uint8_t max_clients=4):sockfd(-1),_accepted_sockfd(-1),_addr(),_port(port),_max_clients(max_clients),_listening(false),_noDelay(false) { log_v("WiFiServer::WiFiServer(port=%d, ...)", port); } diff --git a/libraries/WiFi/src/WiFiUdp.cpp b/libraries/WiFi/src/WiFiUdp.cpp index 6b053bbbd14..5d8b513a2d8 100644 --- a/libraries/WiFi/src/WiFiUdp.cpp +++ b/libraries/WiFi/src/WiFiUdp.cpp @@ -51,7 +51,11 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){ } tx_buffer_len = 0; +#if LWIP_IPV6 + if ((udp_server=socket((address.type() == IPv6) ? AF_INET6 : AF_INET, SOCK_DGRAM, 0)) == -1){ +#else if ((udp_server=socket(AF_INET, SOCK_DGRAM, 0)) == -1){ +#endif log_e("could not create socket: %d", errno); return 0; } @@ -63,12 +67,31 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){ return 0; } - struct sockaddr_in addr; - memset((char *) &addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(server_port); - addr.sin_addr.s_addr = (in_addr_t)address; - if(bind(udp_server , (struct sockaddr*)&addr, sizeof(addr)) == -1){ + struct sockaddr_storage serveraddr = {}; + size_t sock_size = 0; +#if LWIP_IPV6 + if (address.type() == IPv6) { + struct sockaddr_in6 *tmpaddr = (struct sockaddr_in6 *)&serveraddr; + ip_addr_t addr; + address.to_ip_addr_t(&addr); + memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in)); + tmpaddr->sin6_family = AF_INET6; + tmpaddr->sin6_port = htons(server_port); + tmpaddr->sin6_scope_id = addr.u_addr.ip6.zone; + inet6_addr_from_ip6addr(&tmpaddr->sin6_addr, ip_2_ip6(&addr)); + tmpaddr->sin6_flowinfo = 0; + sock_size = sizeof(sockaddr_in6); + } else +#endif + { + struct sockaddr_in *tmpaddr = (struct sockaddr_in *)&serveraddr; + memset((char *) tmpaddr, 0, sizeof(struct sockaddr_in)); + tmpaddr->sin_family = AF_INET; + tmpaddr->sin_port = htons(server_port); + tmpaddr->sin_addr.s_addr = (in_addr_t)address; + sock_size = sizeof(sockaddr_in); + } + if(bind(udp_server , (sockaddr*)&serveraddr, sock_size) == -1){ log_e("could not bind socket: %d", errno); stop(); return 0; @@ -78,23 +101,48 @@ uint8_t WiFiUDP::begin(IPAddress address, uint16_t port){ } uint8_t WiFiUDP::begin(uint16_t p){ - return begin(IPAddress(INADDR_ANY), p); + return begin(IPAddress(), p); } -uint8_t WiFiUDP::beginMulticast(IPAddress a, uint16_t p){ - if(begin(IPAddress(INADDR_ANY), p)){ - if((uint32_t)a != 0){ - struct ip_mreq mreq; - mreq.imr_multiaddr.s_addr = (in_addr_t)a; - mreq.imr_interface.s_addr = INADDR_ANY; - if (setsockopt(udp_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { +uint8_t WiFiUDP::beginMulticast(IPAddress address, uint16_t p){ + if(begin(IPAddress(), p)){ + ip_addr_t addr; + address.to_ip_addr_t(&addr); + if (ip_addr_ismulticast(&addr)) { +#if LWIP_IPV6 + if (address.type() == IPv6) { + struct ipv6_mreq mreq; + bool joined = false; + inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(&addr)); + + // iterate on each interface + for (netif* intf = netif_list; intf != nullptr; intf = intf->next) { + mreq.ipv6mr_interface = intf->num + 1; + if (intf->name[0] != 'l' || intf->name[1] != 'o') { // skip 'lo' local interface + int ret = setsockopt(udp_server, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); + if (ret >= 0) { joined = true; } + } + } + if (!joined) { log_e("could not join igmp: %d", errno); stop(); return 0; + } + } else +#endif + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = (in_addr_t)address; + mreq.imr_interface.s_addr = INADDR_ANY; + if (setsockopt(udp_server, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { + log_e("could not join igmp: %d", errno); + stop(); + return 0; + } } - multicast_ip = a; + multicast_ip = address; + return 1; } - return 1; } return 0; } @@ -112,19 +160,38 @@ void WiFiUDP::stop(){ } if(udp_server == -1) return; - if((uint32_t)multicast_ip != 0){ - struct ip_mreq mreq; - mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip; - mreq.imr_interface.s_addr = (in_addr_t)0; - setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); - multicast_ip = IPAddress(INADDR_ANY); + ip_addr_t addr; + multicast_ip.to_ip_addr_t(&addr); + if (!ip_addr_isany(&addr)) { +#if LWIP_IPV6 + if (multicast_ip.type() == IPv6) { + struct ipv6_mreq mreq; + inet6_addr_from_ip6addr(&mreq.ipv6mr_multiaddr, ip_2_ip6(&addr)); + + // iterate on each interface + for (netif* intf = netif_list; intf != nullptr; intf = intf->next) { + mreq.ipv6mr_interface = intf->num + 1; + if (intf->name[0] != 'l' || intf->name[1] != 'o') { // skip 'lo' local interface + setsockopt(udp_server, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)); + } + } + } else +#endif + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = (in_addr_t)multicast_ip; + mreq.imr_interface.s_addr = (in_addr_t)0; + setsockopt(udp_server, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + } + // now common code for v4/v6 + multicast_ip = IPAddress(); } close(udp_server); udp_server = -1; } int WiFiUDP::beginMulticastPacket(){ - if(!server_port || multicast_ip == IPAddress(INADDR_ANY)) + if(!server_port || multicast_ip == IPAddress()) return 0; remote_ip = multicast_ip; remote_port = server_port; @@ -176,14 +243,31 @@ int WiFiUDP::beginPacket(const char *host, uint16_t port){ } int WiFiUDP::endPacket(){ - struct sockaddr_in recipient; - recipient.sin_addr.s_addr = (uint32_t)remote_ip; - recipient.sin_family = AF_INET; - recipient.sin_port = htons(remote_port); - int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr*) &recipient, sizeof(recipient)); - if(sent < 0){ - log_e("could not send data: %d", errno); - return 0; + ip_addr_t addr; + remote_ip.to_ip_addr_t(&addr); + + if (remote_ip.type() == IPv4) { + struct sockaddr_in recipient; + recipient.sin_addr.s_addr = (uint32_t)remote_ip; + recipient.sin_family = AF_INET; + recipient.sin_port = htons(remote_port); + int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr*) &recipient, sizeof(recipient)); + if(sent < 0){ + log_e("could not send data: %d", errno); + return 0; + } + } else { + struct sockaddr_in6 recipient; + recipient.sin6_flowinfo = 0; + recipient.sin6_addr = *(in6_addr*)(ip_addr_t*)(&addr); + recipient.sin6_family = AF_INET6; + recipient.sin6_port = htons(remote_port); + recipient.sin6_scope_id = remote_ip.zone(); + int sent = sendto(udp_server, tx_buffer, tx_buffer_len, 0, (struct sockaddr*) &recipient, sizeof(recipient)); + if(sent < 0){ + log_e("could not send data: %d", errno); + return 0; + } } return 1; } @@ -207,13 +291,14 @@ size_t WiFiUDP::write(const uint8_t *buffer, size_t size){ int WiFiUDP::parsePacket(){ if(rx_buffer) return 0; - struct sockaddr_in si_other; - int slen = sizeof(si_other) , len; + struct sockaddr_storage si_other_storage; // enough storage for v4 and v6 + socklen_t slen = sizeof(sockaddr_storage); + int len; char *buf = (char *)malloc(1460); if(!buf) { return 0; } - if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *) &si_other, (socklen_t *)&slen)) == -1){ + if ((len = recvfrom(udp_server, buf, 1460, MSG_DONTWAIT, (struct sockaddr *) &si_other_storage, (socklen_t *)&slen)) == -1){ free(buf); if(errno == EWOULDBLOCK){ return 0; @@ -221,8 +306,30 @@ int WiFiUDP::parsePacket(){ log_e("could not receive data: %d", errno); return 0; } - remote_ip = IPAddress(si_other.sin_addr.s_addr); - remote_port = ntohs(si_other.sin_port); + if (si_other_storage.ss_family == AF_INET) { + struct sockaddr_in &si_other = (sockaddr_in&) si_other_storage; + remote_ip = IPAddress(si_other.sin_addr.s_addr); + remote_port = ntohs(si_other.sin_port); + } +#if LWIP_IPV6 + else if (si_other_storage.ss_family == AF_INET6) { + struct sockaddr_in6 &si_other = (sockaddr_in6&) si_other_storage; + remote_ip = IPAddress(IPv6, (uint8_t*)&si_other.sin6_addr, si_other.sin6_scope_id); // force IPv6 + ip_addr_t addr; + remote_ip.to_ip_addr_t(&addr); + /* Dual-stack: Unmap IPv4 mapped IPv6 addresses */ + if (remote_ip.type() == IPv6 && ip6_addr_isipv4mappedipv6(ip_2_ip6(&addr))) { + unmap_ipv4_mapped_ipv6(ip_2_ip4(&addr), ip_2_ip6(&addr)); + IP_SET_TYPE_VAL(addr, IPADDR_TYPE_V4); + remote_ip.from_ip_addr_t(&addr); + } + remote_port = ntohs(si_other.sin6_port); + } +#endif // LWIP_IPV6=1 + else { + remote_ip = ip_addr_any.u_addr.ip4.addr; + remote_port = 0; + } if (len > 0) { rx_buffer = new(std::nothrow) cbuf(len); rx_buffer->write(buf, len); diff --git a/tests/periman/periman.ino b/tests/periman/periman.ino new file mode 100644 index 00000000000..dd9d61724a9 --- /dev/null +++ b/tests/periman/periman.ino @@ -0,0 +1,297 @@ +/* Peripheral Manager test + * + * This test is using Serial to check if the peripheral manager is able to + * attach and detach peripherals correctly on shared pins. + * Make sure that the peripheral names contain only letters, numbers and underscores. + * + * This test skips the following peripherals: + * - USB: USB is not able to be detached + * - SDMMC: SDMMC requires a card to be mounted before the pins are attached + * - ETH: ETH requires a ethernet port to be connected before the pins are attached + */ + +#if SOC_I2S_SUPPORTED +#include "ESP_I2S.h" +#endif + +#if SOC_I2C_SUPPORTED +#include "Wire.h" +#endif + +#if SOC_GPSPI_SUPPORTED +#include "SPI.h" +#endif + +/* Definitions */ + +#define UART1_RX_DEFAULT 4 +#define UART1_TX_DEFAULT 5 + +#define ADC1_DEFAULT A4 + +#if CONFIG_IDF_TARGET_ESP32 +# define ADC2_DEFAULT A5 +#else +# define ADC2_DEFAULT A3 +#endif + +#if CONFIG_IDF_TARGET_ESP32 +# define TOUCH1_DEFAULT T0 +# define TOUCH2_DEFAULT T2 +#else +# define TOUCH1_DEFAULT T4 +# define TOUCH2_DEFAULT T5 +#endif + +/* Global variables */ + +bool test_executed = false; +String current_test; +int8_t uart1_rx_pin; +int8_t uart1_tx_pin; + +/* Callback functions */ + +void onReceive_cb(void) { + // This is a callback function that will be activated on UART RX events + size_t available = Serial1.available(); + while (available --) { + Serial.print((char)Serial1.read()); + } +} + +// This function is called by before each test is run +void setup_test(String test_name, int8_t rx_pin = UART1_RX_DEFAULT, int8_t tx_pin = UART1_TX_DEFAULT) { + log_v("Setting up %s test", test_name.c_str()); + + current_test = test_name; + uart1_rx_pin = rx_pin; + uart1_tx_pin = tx_pin; + test_executed = false; + + pinMode(uart1_rx_pin, INPUT_PULLUP); + pinMode(uart1_tx_pin, OUTPUT); + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + log_v("Running %s test", test_name.c_str()); +} + +// This function is called after each test is run +void teardown_test(void) { + log_v("Tearing down %s test", current_test.c_str()); + if (test_executed) { + pinMode(uart1_rx_pin, INPUT_PULLUP); + pinMode(uart1_tx_pin, OUTPUT); + Serial1.print(current_test); + Serial1.println(" test: This should not be printed"); + Serial1.flush(); + + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + } + + Serial1.print(current_test); + Serial1.println(" test: This should be printed"); + Serial1.flush(); + + log_v("Finished %s test", current_test.c_str()); +} + +/* Test functions */ +/* These functions must call "setup_test" and "teardown_test" and set "test_executed" to true + * if the test is executed + */ + +void gpio_test(void) { + setup_test("GPIO"); + test_executed = true; + pinMode(uart1_rx_pin, INPUT); + pinMode(uart1_tx_pin, OUTPUT); + digitalRead(uart1_rx_pin); + digitalWrite(uart1_tx_pin, HIGH); + teardown_test(); +} + +void sigmadelta_test(void) { + setup_test("SigmaDelta"); +#if SOC_SDM_SUPPORTED + test_executed = true; + if (!sigmaDeltaAttach(uart1_rx_pin, 312500)) { + Serial.println("SigmaDelta init failed"); + } + if (!sigmaDeltaAttach(uart1_tx_pin, 312500)) { + Serial.println("SigmaDelta init failed"); + } +#endif + teardown_test(); +} + +void adc_oneshot_test(void) { +#if !SOC_ADC_SUPPORTED + setup_test("ADC_Oneshot"); +#else + setup_test("ADC_Oneshot", ADC1_DEFAULT, ADC2_DEFAULT); + test_executed = true; + analogReadResolution(12); + pinMode(ADC1_DEFAULT, INPUT); + pinMode(ADC2_DEFAULT, INPUT); + analogRead(ADC1_DEFAULT); + analogRead(ADC2_DEFAULT); +#endif + teardown_test(); +} + +#if SOC_ADC_SUPPORTED +volatile bool adc_coversion_done = false; +void ARDUINO_ISR_ATTR adcComplete() { + adc_coversion_done = true; +} +#endif + +void adc_continuous_test(void) { +#if !SOC_ADC_SUPPORTED + setup_test("ADC_Continuous"); +#else + setup_test("ADC_Continuous", ADC1_DEFAULT, ADC2_DEFAULT); + test_executed = true; + uint8_t adc_pins[] = {ADC1_DEFAULT, ADC2_DEFAULT}; + uint8_t adc_pins_count = 2; + adc_continuos_data_t * result = NULL; + + analogContinuousSetWidth(12); + analogContinuousSetAtten(ADC_11db); + + analogContinuous(adc_pins, adc_pins_count, 6, 20000, &adcComplete); + analogContinuousStart(); + + while (adc_coversion_done == false) { + delay(1); + } + + if (!analogContinuousRead(&result, 0)) { + Serial.println("ADC continuous read failed"); + } + + analogContinuousStop(); +#endif + teardown_test(); +} + +void dac_test(void) { +#if !SOC_DAC_SUPPORTED + setup_test("DAC"); +#else + setup_test("DAC", DAC1, DAC2); + test_executed = true; + dacWrite(DAC1, 255); + dacWrite(DAC2, 255); +#endif + teardown_test(); +} + +void ledc_test(void) { + setup_test("LEDC"); +#if SOC_LEDC_SUPPORTED + test_executed = true; + if (!ledcAttach(uart1_rx_pin, 5000, 12)) { + Serial.println("LEDC init failed"); + } + if (!ledcAttach(uart1_tx_pin, 5000, 12)) { + Serial.println("LEDC init failed"); + } +#endif + teardown_test(); +} + +void rmt_test(void) { + setup_test("RMT"); +#if SOC_RMT_SUPPORTED + test_executed = true; + if (!rmtInit(uart1_rx_pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) { + Serial.println("RMT init failed"); + } + if (!rmtInit(uart1_tx_pin, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) { + Serial.println("RMT init failed"); + } +#endif + teardown_test(); +} + +void i2s_test(void) { + setup_test("I2S"); +#if SOC_I2S_SUPPORTED + test_executed = true; + I2SClass i2s; + + i2s.setPins(uart1_rx_pin, uart1_tx_pin, -1); + i2s.setTimeout(1000); + if (!i2s.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)) { + Serial.println("I2S init failed"); + } +#endif + teardown_test(); +} + +void i2c_test(void) { + setup_test("I2C"); +#if SOC_I2C_SUPPORTED + test_executed = true; + if (!Wire.begin(uart1_rx_pin, uart1_tx_pin)) { + Serial.println("I2C init failed"); + } +#endif + teardown_test(); +} + +void spi_test(void) { + setup_test("SPI"); +#if SOC_GPSPI_SUPPORTED + test_executed = true; + SPI.begin(uart1_rx_pin, uart1_tx_pin, -1, -1); +#endif + teardown_test(); +} + +void touch_test(void) { +#if !SOC_TOUCH_SENSOR_SUPPORTED + setup_test("Touch"); +#else + setup_test("Touch", TOUCH1_DEFAULT, TOUCH2_DEFAULT); + test_executed = true; + touchRead(TOUCH1_DEFAULT); + touchRead(TOUCH2_DEFAULT); +#endif + teardown_test(); +} + +/* Main functions */ + +void setup() { + Serial.begin(115200); + while(!Serial) { delay(10); } + + Serial1.setPins(UART1_RX_DEFAULT, UART1_TX_DEFAULT); + Serial1.begin(115200); + while(!Serial1) { delay(10); } + Serial1.onReceive(onReceive_cb); + uart_internal_loopback(1, uart1_rx_pin); + + gpio_test(); + sigmadelta_test(); + ledc_test(); + rmt_test(); + i2s_test(); + i2c_test(); + spi_test(); + adc_oneshot_test(); + adc_continuous_test(); + dac_test(); + touch_test(); + + // Print to Serial1 to avoid buffering issues + Serial1.println("Peripheral Manager test done"); +} + +void loop() {} diff --git a/tests/periman/test_periman.py b/tests/periman/test_periman.py new file mode 100644 index 00000000000..4bce226bfad --- /dev/null +++ b/tests/periman/test_periman.py @@ -0,0 +1,26 @@ +def test_periman(dut): + peripherals = ["GPIO", "SigmaDelta", "LEDC", "RMT", "I2S", "I2C", "SPI", + "ADC_Oneshot", "ADC_Continuous", "DAC", "Touch"] + + pattern = rb"(?:\b\w+\b test: This should(?: not)? be printed|Peripheral Manager test done)" + + while True: + try: + res = dut.expect(pattern, timeout=10) + except: + assert False, f"Could not detect end of test" + + console_output = res.group(0).decode("utf-8") + peripheral = console_output.split()[0] + + if "Peripheral Manager test done" in console_output: + break + + if peripheral in peripherals: + if "not" in console_output: + assert False, f"Peripheral {peripheral} printed when it should not" + peripherals.remove(peripheral) + else: + assert False, f"Unknown peripheral: {peripheral}" + + assert peripherals == [], f"Missing peripherals output: {peripherals}" diff --git a/tests/uart/uart.ino b/tests/uart/uart.ino index d056dcc1b39..c10dfc58b24 100644 --- a/tests/uart/uart.ino +++ b/tests/uart/uart.ino @@ -78,11 +78,11 @@ void start_serial(unsigned long baudrate = 115200) { // This function stops all the available test UARTs void stop_serial(bool hard_stop = false) { #if SOC_UART_NUM >= 2 - Serial1.end(hard_stop); + Serial1.end(/*hard_stop*/); #endif #if SOC_UART_NUM >= 3 - Serial2.end(hard_stop); + Serial2.end(/*hard_stop*/); #endif } @@ -139,6 +139,19 @@ void task_delayed_msg(void *pvParameters) { // This function is automatically called by unity before each test is run void setUp(void) { start_serial(115200); +#if SOC_UART_NUM == 2 + log_d("Setup internal loop-back from and back to Serial1 (UART1) TX >> Serial1 (UART1) RX"); + + Serial1.onReceive([]() {onReceive_cb(Serial1);}); + uart_internal_loopback(1, RX1); +#elif SOC_UART_NUM == 3 + log_d("Setup internal loop-back between Serial1 (UART1) <<--->> Serial2 (UART2)"); + + Serial1.onReceive([]() {onReceive_cb(Serial1);}); + Serial2.onReceive([]() {onReceive_cb(Serial2);}); + uart_internal_loopback(1, RX2); + uart_internal_loopback(2, RX1); +#endif } // This function is automatically called by unity after each test is run @@ -393,7 +406,7 @@ void disabled_uart_calls_test(void) { // This test checks if the pins can be changed and if the message can be transmitted and received correctly after the change void change_pins_test(void) { - stop_serial(); + //stop_serial(); log_d("Disabling UART loopback"); @@ -453,7 +466,7 @@ void auto_baudrate_test(void) { selected_serial = &Serial2; #endif - selected_serial->end(false); + //selected_serial->end(false); log_d("Starting delayed task to send message"); 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/arduino_nano_nora/pins_arduino.h b/variants/arduino_nano_nora/pins_arduino.h index ea5d882573c..e1d0f6aa9af 100644 --- a/variants/arduino_nano_nora/pins_arduino.h +++ b/variants/arduino_nano_nora/pins_arduino.h @@ -76,6 +76,12 @@ static constexpr uint8_t A7 = 14; #endif +// Aliases + +static constexpr uint8_t LEDR = LED_RED; +static constexpr uint8_t LEDG = LED_GREEN; +static constexpr uint8_t LEDB = LED_BLUE; + // alternate pin functions static constexpr uint8_t LED_BUILTIN = D13; 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; diff --git a/variants/heltec_wireless_tracker/pins_arduino.h b/variants/heltec_wireless_tracker/pins_arduino.h new file mode 100644 index 00000000000..8c05fb57dcb --- /dev/null +++ b/variants/heltec_wireless_tracker/pins_arduino.h @@ -0,0 +1,77 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define WIRELESS_TRACKER true + +#define DISPLAY_HEIGHT 80 +#define DISPLAY_WIDTH 160 + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +static const uint8_t LED_BUILTIN = 18; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 5; +static const uint8_t SCL = 6; + +static const uint8_t SS = 8; +static const uint8_t MOSI = 10; +static const uint8_t MISO = 11; +static const uint8_t SCK = 9; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 7; +static const uint8_t A7 = 8; +static const uint8_t A8 = 9; +static const uint8_t A9 = 10; +static const uint8_t A10 = 11; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; +static const uint8_t A13 = 14; +static const uint8_t A14 = 15; +static const uint8_t A15 = 16; +static const uint8_t A16 = 17; +static const uint8_t A17 = 18; +static const uint8_t A18 = 19; +static const uint8_t A19 = 20; + +static const uint8_t T1 = 1; +static const uint8_t T2 = 2; +static const uint8_t T3 = 3; +static const uint8_t T4 = 4; +static const uint8_t T5 = 5; +static const uint8_t T6 = 6; +static const uint8_t T7 = 7; +static const uint8_t T8 = 8; +static const uint8_t T9 = 9; +static const uint8_t T10 = 10; +static const uint8_t T11 = 11; +static const uint8_t T12 = 12; +static const uint8_t T13 = 13; +static const uint8_t T14 = 14; + +static const uint8_t Vext = 3; +static const uint8_t LED = 18; + +static const uint8_t ST7735_CS_PIN = 38; +static const uint8_t ST7735_RST_PIN = 39; +static const uint8_t ST7735_DC_PIN = 40; +static const uint8_t ST7735_SCLK_PIN = 41; +static const uint8_t ST7735_MOSI_PIN = 42; +static const uint8_t ST7735_LED_K_PIN = 21; + +#endif /* Pins_Arduino_h */ diff --git a/variants/makergo_c3_supermini/pins_arduino.h b/variants/makergo_c3_supermini/pins_arduino.h new file mode 100644 index 00000000000..f62df83dbf5 --- /dev/null +++ b/variants/makergo_c3_supermini/pins_arduino.h @@ -0,0 +1,34 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define TX1 0 +#define RX1 1 + +static const uint8_t LED_BUILTIN = 8; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t BOOT_BUILTIN = 9; // built-in boot button + +static const uint8_t TX = 21; +static const uint8_t RX = 20; + +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +static const uint8_t SS = 7; +static const uint8_t MOSI = 6; +static const uint8_t MISO = 5; +static const uint8_t SCK = 4; + +static const uint8_t A0 = 0; +static const uint8_t A1 = 1; +static const uint8_t A2 = 2; +static const uint8_t A3 = 3; +static const uint8_t A4 = 4; +static const uint8_t A5 = 5; + +#endif /* Pins_Arduino_h */ +