commit
607b2ef2fc
|
|
@ -15,31 +15,78 @@ jobs:
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Code Quality & Linting
|
# Code Quality & Linting
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
quality-check:
|
|
||||||
name: Arduino Lint Check
|
quality-check-linux:
|
||||||
|
name: Arduino Lint Check (Linux)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- uses: arduino/setup-arduino-cli@v2
|
- uses: arduino/setup-arduino-cli@v2
|
||||||
|
- name: Restore Arduino Core Cache
|
||||||
- name: Cache Arduino Core
|
id: cache-core-linux
|
||||||
uses: actions/cache@v4
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.arduino15/packages
|
~/.arduino15/packages
|
||||||
~/.arduino15/cache
|
~/.arduino15/cache
|
||||||
key: arduino-core-${{ runner.os }}-esp32-v1
|
key: arduino-core-linux-esp32-v1
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
arduino-core-${{ runner.os }}-
|
arduino-core-linux-
|
||||||
|
- name: Ensure ESP32 Core is installed (Linux)
|
||||||
- name: Install ESP32 Core
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
||||||
arduino-cli core install esp32:esp32
|
if ! arduino-cli core list | grep -q 'esp32'; then
|
||||||
|
arduino-cli core install esp32:esp32
|
||||||
|
fi
|
||||||
|
- name: Save Arduino Core Cache
|
||||||
|
if: always() && steps.cache-core-linux.outputs.cache-hit != 'true'
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.arduino15/packages
|
||||||
|
~/.arduino15/cache
|
||||||
|
key: arduino-core-linux-esp32-v1
|
||||||
|
- uses: arduino/arduino-lint-action@v2
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}
|
||||||
|
compliance: strict
|
||||||
|
library-manager: update
|
||||||
|
verbose: true
|
||||||
|
|
||||||
|
quality-check-windows:
|
||||||
|
name: Arduino Lint Check (Windows)
|
||||||
|
runs-on: windows-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: arduino/setup-arduino-cli@v2
|
||||||
|
- name: Restore Arduino Core Cache
|
||||||
|
id: cache-core-win
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.arduino15/packages
|
||||||
|
~/.arduino15/cache
|
||||||
|
key: arduino-core-windows-esp32-v1
|
||||||
|
restore-keys: |
|
||||||
|
arduino-core-windows-
|
||||||
|
- name: Ensure ESP32 Core is installed (Windows)
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
||||||
|
if (-not (arduino-cli core list | Select-String 'esp32')) {
|
||||||
|
arduino-cli core install esp32:esp32
|
||||||
|
}
|
||||||
|
- name: Save Arduino Core Cache
|
||||||
|
if: always() && steps.cache-core-win.outputs.cache-hit != 'true'
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.arduino15/packages
|
||||||
|
~/.arduino15/cache
|
||||||
|
key: arduino-core-windows-esp32-v1
|
||||||
- uses: arduino/arduino-lint-action@v2
|
- uses: arduino/arduino-lint-action@v2
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}
|
path: ${{ github.workspace }}
|
||||||
|
|
@ -50,59 +97,101 @@ jobs:
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Compilation Test
|
# Compilation Test
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
compile-test:
|
|
||||||
name: Compile Example Sketches
|
compile-test-linux:
|
||||||
|
name: Compile Sketches (Linux)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
example:
|
|
||||||
- examples/dshot300/dshot300.ino
|
|
||||||
- examples/command_manager/command_manager.ino
|
|
||||||
- examples/web_control/web_control.ino
|
|
||||||
- examples/web_client/web_client.ino
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- uses: arduino/setup-arduino-cli@v2
|
- uses: arduino/setup-arduino-cli@v2
|
||||||
|
- name: Restore Arduino Core & Libraries Cache
|
||||||
- name: Cache Arduino Core & Libraries
|
id: cache-all-linux
|
||||||
uses: actions/cache@v4
|
uses: actions/cache/restore@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.arduino15/packages
|
~/.arduino15/packages
|
||||||
~/.arduino15/cache
|
~/.arduino15/cache
|
||||||
~/Arduino/libraries
|
~/Arduino/libraries
|
||||||
key: arduino-full-${{ runner.os }}-esp32-v1
|
key: arduino-full-linux-esp32-v1
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
arduino-full-${{ runner.os }}-
|
arduino-full-linux-
|
||||||
|
- name: Ensure ESP32 Core and Dependencies (Linux)
|
||||||
- name: Install ESP32 Core and Dependencies
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
||||||
arduino-cli core install esp32:esp32
|
if ! arduino-cli core list | grep -q 'esp32'; then
|
||||||
arduino-cli lib install "ArduinoJson"
|
arduino-cli core install esp32:esp32
|
||||||
|
fi
|
||||||
mkdir -p ~/Arduino/libraries
|
mkdir -p ~/Arduino/libraries
|
||||||
|
if [ ! -d ~/Arduino/libraries/ArduinoJson ]; then
|
||||||
# Cached repository check
|
git clone --depth=1 https://github.com/bblanchon/ArduinoJson.git ~/Arduino/libraries/ArduinoJson
|
||||||
|
fi
|
||||||
if [ ! -d ~/Arduino/libraries/ESPAsyncWebServer ]; then
|
if [ ! -d ~/Arduino/libraries/ESPAsyncWebServer ]; then
|
||||||
git clone --depth=1 https://github.com/ESP32Async/ESPAsyncWebServer ~/Arduino/libraries/ESPAsyncWebServer
|
git clone --depth=1 https://github.com/ESP32Async/ESPAsyncWebServer ~/Arduino/libraries/ESPAsyncWebServer
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d ~/Arduino/libraries/AsyncTCP ]; then
|
if [ ! -d ~/Arduino/libraries/AsyncTCP ]; then
|
||||||
git clone --depth=1 https://github.com/ESP32Async/AsyncTCP ~/Arduino/libraries/AsyncTCP
|
git clone --depth=1 https://github.com/ESP32Async/AsyncTCP ~/Arduino/libraries/AsyncTCP
|
||||||
fi
|
fi
|
||||||
|
- name: Save Arduino Core & Libraries Cache
|
||||||
|
if: always() && steps.cache-all-linux.outputs.cache-hit != 'true'
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.arduino15/packages
|
||||||
|
~/.arduino15/cache
|
||||||
|
~/Arduino/libraries
|
||||||
|
key: arduino-full-linux-esp32-v1
|
||||||
- name: Compile Sketch
|
- name: Compile Sketch
|
||||||
run: |
|
run: |
|
||||||
arduino-cli compile \
|
arduino-cli compile --fqbn esp32:esp32:esp32 --library ${{ github.workspace }} examples/dshot300/dshot300.ino
|
||||||
--fqbn esp32:esp32:esp32 \
|
|
||||||
--library ${{ github.workspace }} \
|
compile-test-windows:
|
||||||
${{ matrix.example }}
|
name: Compile Sketches (Windows)
|
||||||
|
runs-on: windows-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: arduino/setup-arduino-cli@v2
|
||||||
|
- name: Restore Arduino Core & Libraries Cache
|
||||||
|
id: cache-all-win
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.arduino15/packages
|
||||||
|
~/.arduino15/cache
|
||||||
|
~/Arduino/libraries
|
||||||
|
key: arduino-full-windows-esp32-v1
|
||||||
|
restore-keys: |
|
||||||
|
arduino-full-windows-
|
||||||
|
- name: Ensure ESP32 Core and Dependencies (Windows)
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
|
||||||
|
if (-not (arduino-cli core list | Select-String 'esp32')) {
|
||||||
|
arduino-cli core install esp32:esp32
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -Path "$env:USERPROFILE/Arduino/libraries/ArduinoJson")) {
|
||||||
|
git clone --depth=1 https://github.com/bblanchon/ArduinoJson.git "$env:USERPROFILE/Arduino/libraries/ArduinoJson"
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -Path "$env:USERPROFILE/Arduino/libraries/ESPAsyncWebServer")) {
|
||||||
|
git clone --depth=1 https://github.com/ESP32Async/ESPAsyncWebServer "$env:USERPROFILE/Arduino/libraries/ESPAsyncWebServer"
|
||||||
|
}
|
||||||
|
if (-not (Test-Path -Path "$env:USERPROFILE/Arduino/libraries/AsyncTCP")) {
|
||||||
|
git clone --depth=1 https://github.com/ESP32Async/AsyncTCP "$env:USERPROFILE/Arduino/libraries/AsyncTCP"
|
||||||
|
}
|
||||||
|
- name: Save Arduino Core & Libraries Cache
|
||||||
|
if: always() && steps.cache-all-win.outputs.cache-hit != 'true'
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.arduino15/packages
|
||||||
|
~/.arduino15/cache
|
||||||
|
~/Arduino/libraries
|
||||||
|
key: arduino-full-windows-esp32-v1
|
||||||
|
- name: Compile Sketch
|
||||||
|
run: |
|
||||||
|
arduino-cli compile --fqbn esp32:esp32:esp32 --library ${{ github.workspace }} examples/dshot300/dshot300.ino
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# Build Status Report
|
# Build Status Report
|
||||||
|
|
@ -111,8 +200,11 @@ jobs:
|
||||||
name: Build Summary
|
name: Build Summary
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: always()
|
if: always()
|
||||||
needs: [quality-check, compile-test]
|
needs:
|
||||||
|
- quality-check-linux
|
||||||
|
- quality-check-windows
|
||||||
|
- compile-test-linux
|
||||||
|
- compile-test-windows
|
||||||
steps:
|
steps:
|
||||||
- name: Create Build Summary
|
- name: Create Build Summary
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -120,18 +212,19 @@ jobs:
|
||||||
echo "| Check | Status | Details |" >> $GITHUB_STEP_SUMMARY
|
echo "| Check | Status | Details |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "|-------|--------|---------|" >> $GITHUB_STEP_SUMMARY
|
echo "|-------|--------|---------|" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
[[ "${{ needs.quality-check.result }}" == "success" ]] \
|
[[ "${{ needs.quality-check-linux.result }}" == "success" && "${{ needs.quality-check-windows.result }}" == "success" ]] \
|
||||||
&& echo "| 📋 Quality Check | ✅ Passed | Arduino Lint completed successfully |" >> $GITHUB_STEP_SUMMARY \
|
&& echo "| 📋 Quality Check | ✅ Passed | Arduino Lint completed successfully |" >> $GITHUB_STEP_SUMMARY \
|
||||||
|| echo "| 📋 Quality Check | ❌ Failed | Check Arduino Lint report |" >> $GITHUB_STEP_SUMMARY
|
|| echo "| 📋 Quality Check | ❌ Failed | Check Arduino Lint report |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
[[ "${{ needs.compile-test.result }}" == "success" ]] \
|
[[ "${{ needs.compile-test-linux.result }}" == "success" && "${{ needs.compile-test-windows.result }}" == "success" ]] \
|
||||||
&& echo "| 🔨 Compilation | ✅ Passed | All examples compiled successfully |" >> $GITHUB_STEP_SUMMARY \
|
&& echo "| 🔨 Compilation | ✅ Passed | All examples compiled successfully |" >> $GITHUB_STEP_SUMMARY \
|
||||||
|| echo "| 🔨 Compilation | ❌ Failed | Compilation errors detected |" >> $GITHUB_STEP_SUMMARY
|
|| echo "| 🔨 Compilation | ❌ Failed | Compilation errors detected |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
[[ "${{ needs.quality-check.result }}" == "success" && "${{ needs.compile-test.result }}" == "success" ]] \
|
[[ "${{ needs.quality-check-linux.result }}" == "success" && "${{ needs.quality-check-windows.result }}" == "success" && "${{ needs.compile-test-linux.result }}" == "success" && "${{ needs.compile-test-windows.result }}" == "success" ]] \
|
||||||
&& echo "## 🎉 All Checks Passed!" >> $GITHUB_STEP_SUMMARY \
|
&& echo "## 🎉 All Checks Passed!" >> $GITHUB_STEP_SUMMARY \
|
||||||
&& echo "Your DShotRMT library is ready for deployment." >> $GITHUB_STEP_SUMMARY \
|
&& echo "Your DShotRMT library is ready for deployment." >> $GITHUB_STEP_SUMMARY \
|
||||||
|| echo "## ⚠️ Action Required" >> $GITHUB_STEP_SUMMARY \
|
|| echo "## ⚠️ Action Required" >> $GITHUB_STEP_SUMMARY \
|
||||||
&& echo "Please review the failed checks and address any issues." >> $GITHUB_STEP_SUMMARY
|
&& echo "Please review the failed checks and address any issues." >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
|
@ -1,14 +1,26 @@
|
||||||
|
.vs
|
||||||
|
.vscode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
# Local History for Visual Studio Code
|
||||||
.history/
|
.history/
|
||||||
|
|
||||||
# Built Visual Studio Code Extensions
|
# Built Visual Studio Code Extensions
|
||||||
*.vsix
|
*.vsix
|
||||||
|
|
||||||
# Builds
|
# Caching ESP32 Builds
|
||||||
*.code-workspace
|
|
||||||
buildCache
|
buildCache
|
||||||
build
|
build
|
||||||
examples/dshot300/debug.cfg
|
examples/dshot300/debug.cfg
|
||||||
examples/dshot300/esp32.svd
|
examples/dshot300/esp32.svd
|
||||||
examples/dshot300/debug_custom.json
|
examples/dshot300/debug_custom.json
|
||||||
examples/dshot300/debug.svd
|
examples/dshot300/debug.svd
|
||||||
|
|
||||||
|
GEMINI.md
|
||||||
|
.gemini/
|
||||||
|
gha-creds-*.json
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"configuration": "JTAGAdapter=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,LoopCore=1,EventsCore=1,DebugLevel=none,EraseFlash=all,ZigbeeMode=default",
|
|
||||||
"board": "esp32:esp32:esp32",
|
|
||||||
"sketch": "examples/dshot300/dshot300.ino",
|
|
||||||
"output": "build",
|
|
||||||
"port": "/dev/ttyUSB0"
|
|
||||||
}
|
|
||||||
|
|
@ -1,899 +0,0 @@
|
||||||
{
|
|
||||||
"version": 4,
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "ESP32",
|
|
||||||
"compilerPath": "~/.arduino15/packages/esp32/tools/esp-x32/2411/bin/xtensa-esp32-elf-g++",
|
|
||||||
"compilerArgs": [
|
|
||||||
"-MMD",
|
|
||||||
"-w",
|
|
||||||
"-Werror=return-type",
|
|
||||||
"-iprefix"
|
|
||||||
],
|
|
||||||
"intelliSenseMode": "linux-gcc-x64",
|
|
||||||
"includePath": [
|
|
||||||
"~/.arduino15/packages/esp32/hardware/esp32/3.3.0/cores/esp32/**",
|
|
||||||
"~/.arduino15/packages/esp32/hardware/esp32/3.3.0/variants/esp32/**",
|
|
||||||
"~/.arduino15/packages/esp32/hardware/esp32/3.3.0/libraries/**",
|
|
||||||
"~/.arduino15/packages/esp32/hardware/esp32/3.3.0/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/xtensa-esp-elf/esp32/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/backward/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/esp-x32/2411/lib/gcc/xtensa-esp-elf/14.2.0/include/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/esp-x32/2411/lib/gcc/xtensa-esp-elf/14.2.0/include-fixed/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.5-b66b5448-v1/esp32/include/**",
|
|
||||||
"~/.arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.5-b66b5448-v1/esp32/include/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/",
|
|
||||||
"~/Arduino/libraries/**",
|
|
||||||
"${workspaceFolder}/**"
|
|
||||||
],
|
|
||||||
"forcedInclude": [
|
|
||||||
"~/.arduino15/packages/esp32/hardware/esp32/3.3.0/cores/esp32/Arduino.h"
|
|
||||||
],
|
|
||||||
"cStandard": "c17",
|
|
||||||
"cppStandard": "c++20",
|
|
||||||
"defines": [
|
|
||||||
"F_CPU=240000000L",
|
|
||||||
"ESP32",
|
|
||||||
"ARDUINO=10607",
|
|
||||||
"ARDUINO_ESP32_DEV",
|
|
||||||
"ARDUINO_ARCH_ESP32",
|
|
||||||
"ARDUINO_BOARD=\"ESP32_DEV\"",
|
|
||||||
"ARDUINO_VARIANT=\"esp32\"",
|
|
||||||
"ARDUINO_PARTITION_default",
|
|
||||||
"ARDUINO_HOST_OS=\"linux\"",
|
|
||||||
"ARDUINO_FQBN=\"esp32:esp32:esp32:JTAGAdapter=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,LoopCore=1,EventsCore=1,DebugLevel=none,EraseFlash=none,ZigbeeMode=default\"",
|
|
||||||
"ESP32=ESP32",
|
|
||||||
"CORE_DEBUG_LEVEL=0",
|
|
||||||
"ARDUINO_RUNNING_CORE=1",
|
|
||||||
"ARDUINO_EVENT_RUNNING_CORE=1",
|
|
||||||
"ARDUINO_USB_CDC_ON_BOOT=0",
|
|
||||||
"__DBL_MIN_EXP__=(-1021)",
|
|
||||||
"__XCHAL_HAVE_FP=1",
|
|
||||||
"__cpp_nontype_template_parameter_auto=201606L",
|
|
||||||
"__UINT_LEAST16_MAX__=0xffff",
|
|
||||||
"__ATOMIC_ACQUIRE=2",
|
|
||||||
"__FLT_MIN__=1.1754943508222875e-38F",
|
|
||||||
"__GCC_IEC_559_COMPLEX=0",
|
|
||||||
"__XCHAL_HAVE_PREDICTED_BRANCHES=0",
|
|
||||||
"__cpp_aggregate_nsdmi=201304L",
|
|
||||||
"__UINT_LEAST8_TYPE__=unsigned char",
|
|
||||||
"__INTMAX_C(c)=c ## LL",
|
|
||||||
"__XCHAL_HAVE_ADDX=1",
|
|
||||||
"__CHAR_BIT__=8",
|
|
||||||
"__XCHAL_DCACHE_LINESIZE=16",
|
|
||||||
"__XTENSA_MARCH_EARLIEST=260003",
|
|
||||||
"__XCHAL_DCACHE_LINEWIDTH=4",
|
|
||||||
"__UINT8_MAX__=0xff",
|
|
||||||
"__WINT_MAX__=0xffffffffU",
|
|
||||||
"__FLT32_MIN_EXP__=(-125)",
|
|
||||||
"__cpp_static_assert=201411L",
|
|
||||||
"__ORDER_LITTLE_ENDIAN__=1234",
|
|
||||||
"__SIZE_MAX__=0xffffffffU",
|
|
||||||
"__WCHAR_MAX__=0xffff",
|
|
||||||
"__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1=1",
|
|
||||||
"__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2=1",
|
|
||||||
"__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4=1",
|
|
||||||
"__DBL_DENORM_MIN__=double(4.9406564584124654e-324L)",
|
|
||||||
"__GCC_ATOMIC_CHAR_LOCK_FREE=2",
|
|
||||||
"__GCC_IEC_559=0",
|
|
||||||
"__FLT32X_DECIMAL_DIG__=17",
|
|
||||||
"__FLT_EVAL_METHOD__=0",
|
|
||||||
"__cpp_binary_literals=201304L",
|
|
||||||
"__FLT64_DECIMAL_DIG__=17",
|
|
||||||
"__cpp_noexcept_function_type=201510L",
|
|
||||||
"__GCC_ATOMIC_CHAR32_T_LOCK_FREE=2",
|
|
||||||
"__cpp_variadic_templates=200704L",
|
|
||||||
"__UINT_FAST64_MAX__=0xffffffffffffffffULL",
|
|
||||||
"__SIG_ATOMIC_TYPE__=int",
|
|
||||||
"__DBL_MIN_10_EXP__=(-307)",
|
|
||||||
"__FINITE_MATH_ONLY__=0",
|
|
||||||
"__cpp_variable_templates=201304L",
|
|
||||||
"__XCHAL_HAVE_L32R=1",
|
|
||||||
"__FLT32X_MAX_EXP__=1024",
|
|
||||||
"__GNUC_PATCHLEVEL__=0",
|
|
||||||
"__FLT32_HAS_DENORM__=1",
|
|
||||||
"__UINT_FAST8_MAX__=0xffffffffU",
|
|
||||||
"__cpp_rvalue_reference=200610L",
|
|
||||||
"__XCHAL_HAVE_LOOPS=1",
|
|
||||||
"__cpp_nested_namespace_definitions=201411L",
|
|
||||||
"__XCHAL_DEBUGLEVEL=6",
|
|
||||||
"__INT8_C(c)=c",
|
|
||||||
"__XCHAL_HAVE_DFP_RECIP=0",
|
|
||||||
"__INT_LEAST8_WIDTH__=8",
|
|
||||||
"__cpp_variadic_using=201611L",
|
|
||||||
"__UINT_LEAST64_MAX__=0xffffffffffffffffULL",
|
|
||||||
"__INT_LEAST8_MAX__=0x7f",
|
|
||||||
"__cpp_attributes=200809L",
|
|
||||||
"__cpp_capture_star_this=201603L",
|
|
||||||
"__SHRT_MAX__=0x7fff",
|
|
||||||
"__LDBL_MAX__=1.7976931348623157e+308L",
|
|
||||||
"__cpp_if_constexpr=201606L",
|
|
||||||
"__XCHAL_ICACHE_LINESIZE=16",
|
|
||||||
"__LDBL_IS_IEC_60559__=1",
|
|
||||||
"__UINT_LEAST8_MAX__=0xff",
|
|
||||||
"__GCC_ATOMIC_BOOL_LOCK_FREE=2",
|
|
||||||
"__UINTMAX_TYPE__=long long unsigned int",
|
|
||||||
"__cpp_nsdmi=200809L",
|
|
||||||
"__FLT_EVAL_METHOD_TS_18661_3__=0",
|
|
||||||
"__UINT32_MAX__=0xffffffffUL",
|
|
||||||
"__GXX_EXPERIMENTAL_CXX0X__=1",
|
|
||||||
"__LDBL_MAX_EXP__=1024",
|
|
||||||
"__WINT_MIN__=0U",
|
|
||||||
"__FLT32X_IS_IEC_60559__=1",
|
|
||||||
"__XCHAL_HAVE_THREADPTR=1",
|
|
||||||
"__INT_LEAST16_WIDTH__=16",
|
|
||||||
"__SCHAR_MAX__=0x7f",
|
|
||||||
"__WCHAR_MIN__=0",
|
|
||||||
"__XCHAL_ICACHE_LINEWIDTH=4",
|
|
||||||
"__INT64_C(c)=c ## LL",
|
|
||||||
"__GCC_ATOMIC_POINTER_LOCK_FREE=2",
|
|
||||||
"__ATOMIC_SEQ_CST=5",
|
|
||||||
"__SIZEOF_INT__=4",
|
|
||||||
"__FLT32X_MANT_DIG__=53",
|
|
||||||
"__GCC_ATOMIC_CHAR16_T_LOCK_FREE=2",
|
|
||||||
"__cpp_aligned_new=201606L",
|
|
||||||
"__XCHAL_HAVE_FP_RECIP=1",
|
|
||||||
"__FLT32_MAX_10_EXP__=38",
|
|
||||||
"__STDC_HOSTED__=1",
|
|
||||||
"__XCHAL_HAVE_MUL32=1",
|
|
||||||
"__XTENSA_EL__=1",
|
|
||||||
"__cpp_decltype_auto=201304L",
|
|
||||||
"__DBL_DIG__=15",
|
|
||||||
"__XCHAL_HAVE_MUL16=1",
|
|
||||||
"__FLT_EPSILON__=1.1920928955078125e-7F",
|
|
||||||
"__GXX_WEAK__=1",
|
|
||||||
"__SHRT_WIDTH__=16",
|
|
||||||
"__FLT32_IS_IEC_60559__=1",
|
|
||||||
"__LDBL_MIN__=2.2250738585072014e-308L",
|
|
||||||
"__DBL_IS_IEC_60559__=1",
|
|
||||||
"__cpp_threadsafe_static_init=200806L",
|
|
||||||
"__cpp_enumerator_attributes=201411L",
|
|
||||||
"__XCHAL_HAVE_MMU=0",
|
|
||||||
"__FLT32X_HAS_INFINITY__=1",
|
|
||||||
"__INT32_MAX__=0x7fffffffL",
|
|
||||||
"__XCHAL_HAVE_DIV32=1",
|
|
||||||
"__INT_WIDTH__=32",
|
|
||||||
"__XTENSA_MARCH_LATEST=260003",
|
|
||||||
"__DECIMAL_DIG__=17",
|
|
||||||
"__FLT64_EPSILON__=2.2204460492503131e-16F64",
|
|
||||||
"__INT16_MAX__=0x7fff",
|
|
||||||
"__FLT64_MIN_EXP__=(-1021)",
|
|
||||||
"__XCHAL_DCACHE_SIZE=0",
|
|
||||||
"__LDBL_HAS_QUIET_NAN__=1",
|
|
||||||
"__cpp_return_type_deduction=201304L",
|
|
||||||
"__XCHAL_HAVE_BOOLEANS=1",
|
|
||||||
"__FLT64_MANT_DIG__=53",
|
|
||||||
"__XTHAL_ABI_WINDOWED=0",
|
|
||||||
"__GNUC__=14",
|
|
||||||
"__GXX_RTTI=1",
|
|
||||||
"__FLT_HAS_DENORM__=1",
|
|
||||||
"__SIZEOF_LONG_DOUBLE__=8",
|
|
||||||
"__XCHAL_HAVE_CONST16=0",
|
|
||||||
"__BIGGEST_ALIGNMENT__=16",
|
|
||||||
"__STDC_UTF_16__=1",
|
|
||||||
"__FLT64_MAX_10_EXP__=308",
|
|
||||||
"__cpp_delegating_constructors=200604L",
|
|
||||||
"__DBL_MAX__=double(1.7976931348623157e+308L)",
|
|
||||||
"__cpp_raw_strings=200710L",
|
|
||||||
"__INT_FAST32_MAX__=0x7fffffff",
|
|
||||||
"__DBL_HAS_INFINITY__=1",
|
|
||||||
"__cpp_deduction_guides=201703L",
|
|
||||||
"__HAVE_SPECULATION_SAFE_VALUE=1",
|
|
||||||
"__cpp_fold_expressions=201603L",
|
|
||||||
"__INTPTR_WIDTH__=32",
|
|
||||||
"__UINT_LEAST32_MAX__=0xffffffffUL",
|
|
||||||
"__FLT32X_HAS_DENORM__=1",
|
|
||||||
"__INT_FAST16_TYPE__=int",
|
|
||||||
"__XCHAL_HAVE_RELEASE_SYNC=1",
|
|
||||||
"__LDBL_HAS_DENORM__=1",
|
|
||||||
"__cplusplus=201703L",
|
|
||||||
"__cpp_ref_qualifiers=200710L",
|
|
||||||
"__INT_LEAST32_MAX__=0x7fffffffL",
|
|
||||||
"__DEPRECATED=1",
|
|
||||||
"__cpp_rvalue_references=200610L",
|
|
||||||
"__DBL_MAX_EXP__=1024",
|
|
||||||
"__WCHAR_WIDTH__=16",
|
|
||||||
"__FLT32_MAX__=3.4028234663852886e+38F32",
|
|
||||||
"__GCC_ATOMIC_LONG_LOCK_FREE=2",
|
|
||||||
"__PTRDIFF_MAX__=0x7fffffff",
|
|
||||||
"__FLT32_HAS_QUIET_NAN__=1",
|
|
||||||
"__GNUG__=14",
|
|
||||||
"__LONG_LONG_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__SIZEOF_SIZE_T__=4",
|
|
||||||
"__SIZEOF_WINT_T__=4",
|
|
||||||
"__FLT32X_DIG__=15",
|
|
||||||
"__LONG_LONG_WIDTH__=64",
|
|
||||||
"__cpp_initializer_lists=200806L",
|
|
||||||
"__FLT32_MAX_EXP__=128",
|
|
||||||
"__XCHAL_HAVE_MINMAX=1",
|
|
||||||
"__cpp_hex_float=201603L",
|
|
||||||
"__XCHAL_NUM_IBREAK=2",
|
|
||||||
"__GXX_ABI_VERSION=1019",
|
|
||||||
"__FLT_MIN_EXP__=(-125)",
|
|
||||||
"__cpp_lambdas=200907L",
|
|
||||||
"__INT_FAST64_TYPE__=long long int",
|
|
||||||
"__FP_FAST_FMAF=1",
|
|
||||||
"__FLT64_DENORM_MIN__=4.9406564584124654e-324F64",
|
|
||||||
"__DBL_MIN__=double(2.2250738585072014e-308L)",
|
|
||||||
"__SIZEOF_POINTER__=4",
|
|
||||||
"__DBL_HAS_QUIET_NAN__=1",
|
|
||||||
"__FLT32X_EPSILON__=2.2204460492503131e-16F32x",
|
|
||||||
"__XSHAL_HAVE_TEXT_SECTION_LITERALS=1",
|
|
||||||
"__FLT64_MIN_10_EXP__=(-307)",
|
|
||||||
"__REGISTER_PREFIX__",
|
|
||||||
"__UINT16_MAX__=0xffff",
|
|
||||||
"__XSHAL_USE_ABSOLUTE_LITERALS=0",
|
|
||||||
"__LDBL_HAS_INFINITY__=1",
|
|
||||||
"__FLT32_MIN__=1.1754943508222875e-38F32",
|
|
||||||
"__UINT8_TYPE__=unsigned char",
|
|
||||||
"__FLT_DIG__=6",
|
|
||||||
"__NO_INLINE__=1",
|
|
||||||
"__DEC_EVAL_METHOD__=2",
|
|
||||||
"__FLT_MANT_DIG__=24",
|
|
||||||
"__LDBL_DECIMAL_DIG__=17",
|
|
||||||
"__VERSION__=\"14.2.0\"",
|
|
||||||
"__UINT64_C(c)=c ## ULL",
|
|
||||||
"__XCHAL_NUM_AREGS=64",
|
|
||||||
"__cpp_unicode_characters=201411L",
|
|
||||||
"__XCHAL_HAVE_XEA3=0",
|
|
||||||
"__GCC_ATOMIC_INT_LOCK_FREE=2",
|
|
||||||
"__XCHAL_HAVE_DENSITY=1",
|
|
||||||
"__FLT32_MANT_DIG__=24",
|
|
||||||
"__FLOAT_WORD_ORDER__=__ORDER_LITTLE_ENDIAN__",
|
|
||||||
"__XCHAL_HAVE_CLAMPS=0",
|
|
||||||
"__XCHAL_HAVE_DFP_RSQRT=0",
|
|
||||||
"__cpp_aggregate_bases=201603L",
|
|
||||||
"__XCHAL_HAVE_NSA=1",
|
|
||||||
"__XCHAL_HAVE_WINDOWED=1",
|
|
||||||
"__SCHAR_WIDTH__=8",
|
|
||||||
"__INT32_C(c)=c ## L",
|
|
||||||
"__ORDER_PDP_ENDIAN__=3412",
|
|
||||||
"__INT_FAST32_TYPE__=int",
|
|
||||||
"__UINT_LEAST16_TYPE__=short unsigned int",
|
|
||||||
"__DBL_HAS_DENORM__=1",
|
|
||||||
"__XCHAL_HAVE_DEBUG=1",
|
|
||||||
"__cpp_rtti=199711L",
|
|
||||||
"__SIZE_TYPE__=unsigned int",
|
|
||||||
"__UINT64_MAX__=0xffffffffffffffffULL",
|
|
||||||
"__FLT_IS_IEC_60559__=1",
|
|
||||||
"__GNUC_WIDE_EXECUTION_CHARSET_NAME=\"UTF-16LE\"",
|
|
||||||
"__INT8_TYPE__=signed char",
|
|
||||||
"__cpp_digit_separators=201309L",
|
|
||||||
"__ELF__=1",
|
|
||||||
"__XSHAL_ABI=0",
|
|
||||||
"__xtensa__=1",
|
|
||||||
"__FLT_RADIX__=2",
|
|
||||||
"__INT_LEAST16_TYPE__=short int",
|
|
||||||
"__LDBL_EPSILON__=2.2204460492503131e-16L",
|
|
||||||
"__UINTMAX_C(c)=c ## ULL",
|
|
||||||
"__FLT32X_MIN__=2.2250738585072014e-308F32x",
|
|
||||||
"__XCHAL_HAVE_DFP_SQRT=0",
|
|
||||||
"__SIG_ATOMIC_MAX__=0x7fffffff",
|
|
||||||
"__XCHAL_HAVE_MAC16=1",
|
|
||||||
"__GCC_ATOMIC_WCHAR_T_LOCK_FREE=2",
|
|
||||||
"__USER_LABEL_PREFIX__",
|
|
||||||
"__SIZEOF_PTRDIFF_T__=4",
|
|
||||||
"__XCHAL_MMU_MIN_PTE_PAGE_SIZE=1",
|
|
||||||
"__XCHAL_DCACHE_IS_WRITEBACK=0",
|
|
||||||
"__SIZEOF_LONG__=4",
|
|
||||||
"__LDBL_DIG__=15",
|
|
||||||
"__FLT64_IS_IEC_60559__=1",
|
|
||||||
"__XCHAL_MAX_INSTRUCTION_SIZE=3",
|
|
||||||
"__FLT32X_MIN_EXP__=(-1021)",
|
|
||||||
"__INT_FAST16_MAX__=0x7fffffff",
|
|
||||||
"__GCC_CONSTRUCTIVE_SIZE=32",
|
|
||||||
"__FLT64_DIG__=15",
|
|
||||||
"__UINT_FAST32_MAX__=0xffffffffU",
|
|
||||||
"__UINT_LEAST64_TYPE__=long long unsigned int",
|
|
||||||
"__FLT_HAS_QUIET_NAN__=1",
|
|
||||||
"__FLT_MAX_10_EXP__=38",
|
|
||||||
"__FLT_HAS_INFINITY__=1",
|
|
||||||
"__GNUC_EXECUTION_CHARSET_NAME=\"UTF-8\"",
|
|
||||||
"__CHAR_UNSIGNED__=1",
|
|
||||||
"__cpp_unicode_literals=200710L",
|
|
||||||
"__UINT_FAST16_TYPE__=unsigned int",
|
|
||||||
"__INT_FAST32_WIDTH__=32",
|
|
||||||
"__CHAR16_TYPE__=short unsigned int",
|
|
||||||
"__PRAGMA_REDEFINE_EXTNAME=1",
|
|
||||||
"__SIZE_WIDTH__=32",
|
|
||||||
"__INT_LEAST16_MAX__=0x7fff",
|
|
||||||
"__INT64_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__FLT32_DENORM_MIN__=1.4012984643248171e-45F32",
|
|
||||||
"__SIG_ATOMIC_WIDTH__=32",
|
|
||||||
"__INT_LEAST64_TYPE__=long long int",
|
|
||||||
"__INT16_TYPE__=short int",
|
|
||||||
"__INT_LEAST8_TYPE__=signed char",
|
|
||||||
"__cpp_structured_bindings=201606L",
|
|
||||||
"__INT_FAST8_MAX__=0x7fffffff",
|
|
||||||
"__INTPTR_MAX__=0x7fffffff",
|
|
||||||
"__cpp_sized_deallocation=201309L",
|
|
||||||
"__cpp_guaranteed_copy_elision=201606L",
|
|
||||||
"__FLT64_HAS_QUIET_NAN__=1",
|
|
||||||
"__FLT32_MIN_10_EXP__=(-37)",
|
|
||||||
"__EXCEPTIONS=1",
|
|
||||||
"__UINT16_C(c)=c",
|
|
||||||
"__XCHAL_M_STAGE=3",
|
|
||||||
"__PTRDIFF_WIDTH__=32",
|
|
||||||
"__LDBL_MANT_DIG__=53",
|
|
||||||
"__cpp_range_based_for=201603L",
|
|
||||||
"__FLT64_HAS_INFINITY__=1",
|
|
||||||
"__STDCPP_DEFAULT_NEW_ALIGNMENT__=8",
|
|
||||||
"__SIG_ATOMIC_MIN__=(-__SIG_ATOMIC_MAX__ - 1)",
|
|
||||||
"__XCHAL_ICACHE_SIZE=0",
|
|
||||||
"__cpp_nontype_template_args=201411L",
|
|
||||||
"__INTPTR_TYPE__=int",
|
|
||||||
"__UINT16_TYPE__=short unsigned int",
|
|
||||||
"__WCHAR_TYPE__=short unsigned int",
|
|
||||||
"__XCHAL_HAVE_DEPBITS=0",
|
|
||||||
"__SIZEOF_FLOAT__=4",
|
|
||||||
"__UINTPTR_MAX__=0xffffffffU",
|
|
||||||
"__INT_FAST64_WIDTH__=64",
|
|
||||||
"__cpp_decltype=200707L",
|
|
||||||
"__FLT32_DECIMAL_DIG__=9",
|
|
||||||
"__INT_FAST64_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1",
|
|
||||||
"__FLT_NORM_MAX__=3.4028234663852886e+38F",
|
|
||||||
"__XCHAL_HAVE_DFP=0",
|
|
||||||
"__FLT32_HAS_INFINITY__=1",
|
|
||||||
"__UINT_FAST64_TYPE__=long long unsigned int",
|
|
||||||
"__cpp_inline_variables=201606L",
|
|
||||||
"__INT_MAX__=0x7fffffff",
|
|
||||||
"__XCHAL_HAVE_EXCLUSIVE=0",
|
|
||||||
"__STDCPP_THREADS__=1",
|
|
||||||
"__INT64_TYPE__=long long int",
|
|
||||||
"__XCHAL_HAVE_DFP_DIV=0",
|
|
||||||
"__FLT_MAX_EXP__=128",
|
|
||||||
"__XCHAL_INST_FETCH_WIDTH=4",
|
|
||||||
"__DBL_MANT_DIG__=53",
|
|
||||||
"__cpp_inheriting_constructors=201511L",
|
|
||||||
"__INT_LEAST64_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__FP_FAST_FMAF32=1",
|
|
||||||
"__WINT_TYPE__=unsigned int",
|
|
||||||
"__UINT_LEAST32_TYPE__=long unsigned int",
|
|
||||||
"__SIZEOF_SHORT__=2",
|
|
||||||
"__FLT32_NORM_MAX__=3.4028234663852886e+38F32",
|
|
||||||
"__LDBL_MIN_EXP__=(-1021)",
|
|
||||||
"__XCHAL_HAVE_S32C1I=1",
|
|
||||||
"__FLT64_MAX__=1.7976931348623157e+308F64",
|
|
||||||
"__WINT_WIDTH__=32",
|
|
||||||
"__cpp_template_auto=201606L",
|
|
||||||
"__INT_LEAST64_WIDTH__=64",
|
|
||||||
"__FLT32X_MAX_10_EXP__=308",
|
|
||||||
"__cpp_namespace_attributes=201411L",
|
|
||||||
"__WCHAR_UNSIGNED__=1",
|
|
||||||
"__LDBL_MAX_10_EXP__=308",
|
|
||||||
"__ATOMIC_RELAXED=0",
|
|
||||||
"__DBL_EPSILON__=double(2.2204460492503131e-16L)",
|
|
||||||
"__XCHAL_HAVE_SEXT=1",
|
|
||||||
"__INT_LEAST32_TYPE__=long int",
|
|
||||||
"__XTENSA_WINDOWED_ABI__=1",
|
|
||||||
"__UINT8_C(c)=c",
|
|
||||||
"__FLT64_MAX_EXP__=1024",
|
|
||||||
"__SIZEOF_WCHAR_T__=2",
|
|
||||||
"__XCHAL_HAVE_FP_POSTINC=1",
|
|
||||||
"__FLT64_NORM_MAX__=1.7976931348623157e+308F64",
|
|
||||||
"__INTMAX_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__INT_FAST8_TYPE__=int",
|
|
||||||
"__XCHAL_HAVE_MUL32_HIGH=1",
|
|
||||||
"__GNUC_STDC_INLINE__=1",
|
|
||||||
"__FLT64_HAS_DENORM__=1",
|
|
||||||
"__FLT32_EPSILON__=1.1920928955078125e-7F32",
|
|
||||||
"__DBL_DECIMAL_DIG__=17",
|
|
||||||
"__STDC_UTF_32__=1",
|
|
||||||
"__XCHAL_HAVE_FP_DIV=1",
|
|
||||||
"__INT_FAST8_WIDTH__=32",
|
|
||||||
"__FLT32X_MAX__=1.7976931348623157e+308F32x",
|
|
||||||
"__DBL_NORM_MAX__=double(1.7976931348623157e+308L)",
|
|
||||||
"__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__",
|
|
||||||
"__GCC_DESTRUCTIVE_SIZE=32",
|
|
||||||
"__XTENSA__=1",
|
|
||||||
"__INTMAX_WIDTH__=64",
|
|
||||||
"__ORDER_BIG_ENDIAN__=4321",
|
|
||||||
"__XTHAL_ABI_CALL0=1",
|
|
||||||
"__cpp_runtime_arrays=198712L",
|
|
||||||
"__FLT32_DIG__=6",
|
|
||||||
"__UINT64_TYPE__=long long unsigned int",
|
|
||||||
"__UINT32_C(c)=c ## UL",
|
|
||||||
"__cpp_alias_templates=200704L",
|
|
||||||
"__FLT_DENORM_MIN__=1.4012984643248171e-45F",
|
|
||||||
"__INT8_MAX__=0x7f",
|
|
||||||
"__LONG_WIDTH__=32",
|
|
||||||
"__UINT_FAST32_TYPE__=unsigned int",
|
|
||||||
"__FLT32X_NORM_MAX__=1.7976931348623157e+308F32x",
|
|
||||||
"__CHAR32_TYPE__=long unsigned int",
|
|
||||||
"__FLT_MAX__=3.4028234663852886e+38F",
|
|
||||||
"__cpp_constexpr=201603L",
|
|
||||||
"__XCHAL_HAVE_FP_RSQRT=1",
|
|
||||||
"__INT32_TYPE__=long int",
|
|
||||||
"__SIZEOF_DOUBLE__=8",
|
|
||||||
"__cpp_exceptions=199711L",
|
|
||||||
"__FLT_MIN_10_EXP__=(-37)",
|
|
||||||
"__FLT64_MIN__=2.2250738585072014e-308F64",
|
|
||||||
"__INT_LEAST32_WIDTH__=32",
|
|
||||||
"__INTMAX_TYPE__=long long int",
|
|
||||||
"__XCHAL_HAVE_ABS=1",
|
|
||||||
"__FLT32X_HAS_QUIET_NAN__=1",
|
|
||||||
"__ATOMIC_CONSUME=1",
|
|
||||||
"__XCHAL_NUM_DBREAK=2",
|
|
||||||
"__XCHAL_HAVE_WIDE_BRANCHES=0",
|
|
||||||
"__GNUC_MINOR__=2",
|
|
||||||
"__INT_FAST16_WIDTH__=32",
|
|
||||||
"__UINTMAX_MAX__=0xffffffffffffffffULL",
|
|
||||||
"__FLT32X_DENORM_MIN__=4.9406564584124654e-324F32x",
|
|
||||||
"__cpp_template_template_args=201611L",
|
|
||||||
"__DBL_MAX_10_EXP__=308",
|
|
||||||
"__LDBL_DENORM_MIN__=4.9406564584124654e-324L",
|
|
||||||
"__INT16_C(c)=c",
|
|
||||||
"__STDC__=1",
|
|
||||||
"__PTRDIFF_TYPE__=int",
|
|
||||||
"__LONG_MAX__=0x7fffffffL",
|
|
||||||
"__XCHAL_HAVE_FP_SQRT=1",
|
|
||||||
"__UINT32_TYPE__=long unsigned int",
|
|
||||||
"__FLT32X_MIN_10_EXP__=(-307)",
|
|
||||||
"__UINTPTR_TYPE__=unsigned int",
|
|
||||||
"__LDBL_MIN_10_EXP__=(-307)",
|
|
||||||
"__cpp_generic_lambdas=201304L",
|
|
||||||
"__SIZEOF_LONG_LONG__=8",
|
|
||||||
"__cpp_USER_defined_literals=200809L",
|
|
||||||
"__GCC_ATOMIC_LLONG_LOCK_FREE=1",
|
|
||||||
"__FLT_DECIMAL_DIG__=9",
|
|
||||||
"__UINT_FAST16_MAX__=0xffffffffU",
|
|
||||||
"__LDBL_NORM_MAX__=1.7976931348623157e+308L",
|
|
||||||
"__GCC_ATOMIC_SHORT_LOCK_FREE=2",
|
|
||||||
"__XCHAL_HAVE_BE=0",
|
|
||||||
"__UINT_FAST8_TYPE__=unsigned int",
|
|
||||||
"__cpp_init_captures=201304L",
|
|
||||||
"__ATOMIC_ACQ_REL=4",
|
|
||||||
"__ATOMIC_RELEASE=3",
|
|
||||||
"RMT_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB",
|
|
||||||
"USBCON"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Arduino",
|
|
||||||
"compilerPath": "/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/bin/xtensa-esp32-elf-g++",
|
|
||||||
"compilerArgs": [
|
|
||||||
"-MMD",
|
|
||||||
"-w",
|
|
||||||
"-Werror=return-type",
|
|
||||||
"-iprefix"
|
|
||||||
],
|
|
||||||
"intelliSenseMode": "gcc-x64",
|
|
||||||
"includePath": [
|
|
||||||
"/home/derdoktor667/Github/DShotRMT/examples/dshot300",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.5-b66b5448-v1/esp32/qio_qspi/include",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/hardware/esp32/3.3.0/cores/esp32",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/hardware/esp32/3.3.0/variants/esp32",
|
|
||||||
"/home/derdoktor667/Arduino/libraries/DShotRMT/src",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/xtensa-esp-elf/esp32",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include/c++/14.2.0/backward",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/lib/gcc/xtensa-esp-elf/14.2.0/include",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/lib/gcc/xtensa-esp-elf/14.2.0/include-fixed",
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/tools/esp-x32/2411/xtensa-esp-elf/include"
|
|
||||||
],
|
|
||||||
"forcedInclude": [
|
|
||||||
"/home/derdoktor667/.arduino15/packages/esp32/hardware/esp32/3.3.0/cores/esp32/Arduino.h"
|
|
||||||
],
|
|
||||||
"cStandard": "c11",
|
|
||||||
"cppStandard": "c++11",
|
|
||||||
"defines": [
|
|
||||||
"F_CPU=240000000L",
|
|
||||||
"ARDUINO=10607",
|
|
||||||
"ARDUINO_ESP32_DEV",
|
|
||||||
"ARDUINO_ARCH_ESP32",
|
|
||||||
"ARDUINO_BOARD=\"ESP32_DEV\"",
|
|
||||||
"ARDUINO_VARIANT=\"esp32\"",
|
|
||||||
"ARDUINO_PARTITION_default",
|
|
||||||
"ARDUINO_HOST_OS=\"linux\"",
|
|
||||||
"ARDUINO_FQBN=\"esp32:esp32:esp32:JTAGAdapter=default,PSRAM=disabled,PartitionScheme=default,CPUFreq=240,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,LoopCore=1,EventsCore=1,DebugLevel=none,EraseFlash=all,ZigbeeMode=default\"",
|
|
||||||
"ESP32=ESP32",
|
|
||||||
"CORE_DEBUG_LEVEL=0",
|
|
||||||
"ARDUINO_RUNNING_CORE=1",
|
|
||||||
"ARDUINO_EVENT_RUNNING_CORE=1",
|
|
||||||
"ARDUINO_USB_CDC_ON_BOOT=0",
|
|
||||||
"__DBL_MIN_EXP__=(-1021)",
|
|
||||||
"__XCHAL_HAVE_FP=1",
|
|
||||||
"__cpp_nontype_template_parameter_auto=201606L",
|
|
||||||
"__UINT_LEAST16_MAX__=0xffff",
|
|
||||||
"__ATOMIC_ACQUIRE=2",
|
|
||||||
"__FLT_MIN__=1.1754943508222875e-38F",
|
|
||||||
"__GCC_IEC_559_COMPLEX=0",
|
|
||||||
"__XCHAL_HAVE_PREDICTED_BRANCHES=0",
|
|
||||||
"__cpp_aggregate_nsdmi=201304L",
|
|
||||||
"__UINT_LEAST8_TYPE__=unsigned char",
|
|
||||||
"__INTMAX_C(c)=c ## LL",
|
|
||||||
"__XCHAL_HAVE_ADDX=1",
|
|
||||||
"__CHAR_BIT__=8",
|
|
||||||
"__XCHAL_DCACHE_LINESIZE=16",
|
|
||||||
"__XTENSA_MARCH_EARLIEST=260003",
|
|
||||||
"__XCHAL_DCACHE_LINEWIDTH=4",
|
|
||||||
"__UINT8_MAX__=0xff",
|
|
||||||
"__WINT_MAX__=0xffffffffU",
|
|
||||||
"__FLT32_MIN_EXP__=(-125)",
|
|
||||||
"__cpp_static_assert=201411L",
|
|
||||||
"__ORDER_LITTLE_ENDIAN__=1234",
|
|
||||||
"__SIZE_MAX__=0xffffffffU",
|
|
||||||
"__WCHAR_MAX__=0xffff",
|
|
||||||
"__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1=1",
|
|
||||||
"__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2=1",
|
|
||||||
"__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4=1",
|
|
||||||
"__DBL_DENORM_MIN__=double(4.9406564584124654e-324L)",
|
|
||||||
"__GCC_ATOMIC_CHAR_LOCK_FREE=2",
|
|
||||||
"__GCC_IEC_559=0",
|
|
||||||
"__FLT32X_DECIMAL_DIG__=17",
|
|
||||||
"__FLT_EVAL_METHOD__=0",
|
|
||||||
"__cpp_binary_literals=201304L",
|
|
||||||
"__FLT64_DECIMAL_DIG__=17",
|
|
||||||
"__cpp_noexcept_function_type=201510L",
|
|
||||||
"__GCC_ATOMIC_CHAR32_T_LOCK_FREE=2",
|
|
||||||
"__cpp_variadic_templates=200704L",
|
|
||||||
"__UINT_FAST64_MAX__=0xffffffffffffffffULL",
|
|
||||||
"__SIG_ATOMIC_TYPE__=int",
|
|
||||||
"__DBL_MIN_10_EXP__=(-307)",
|
|
||||||
"__FINITE_MATH_ONLY__=0",
|
|
||||||
"__cpp_variable_templates=201304L",
|
|
||||||
"__XCHAL_HAVE_L32R=1",
|
|
||||||
"__FLT32X_MAX_EXP__=1024",
|
|
||||||
"__GNUC_PATCHLEVEL__=0",
|
|
||||||
"__FLT32_HAS_DENORM__=1",
|
|
||||||
"__UINT_FAST8_MAX__=0xffffffffU",
|
|
||||||
"__cpp_rvalue_reference=200610L",
|
|
||||||
"__XCHAL_HAVE_LOOPS=1",
|
|
||||||
"__cpp_nested_namespace_definitions=201411L",
|
|
||||||
"__XCHAL_DEBUGLEVEL=6",
|
|
||||||
"__INT8_C(c)=c",
|
|
||||||
"__XCHAL_HAVE_DFP_RECIP=0",
|
|
||||||
"__INT_LEAST8_WIDTH__=8",
|
|
||||||
"__cpp_variadic_using=201611L",
|
|
||||||
"__UINT_LEAST64_MAX__=0xffffffffffffffffULL",
|
|
||||||
"__INT_LEAST8_MAX__=0x7f",
|
|
||||||
"__cpp_attributes=200809L",
|
|
||||||
"__cpp_capture_star_this=201603L",
|
|
||||||
"__SHRT_MAX__=0x7fff",
|
|
||||||
"__LDBL_MAX__=1.7976931348623157e+308L",
|
|
||||||
"__cpp_if_constexpr=201606L",
|
|
||||||
"__XCHAL_ICACHE_LINESIZE=16",
|
|
||||||
"__LDBL_IS_IEC_60559__=1",
|
|
||||||
"__UINT_LEAST8_MAX__=0xff",
|
|
||||||
"__GCC_ATOMIC_BOOL_LOCK_FREE=2",
|
|
||||||
"__UINTMAX_TYPE__=long long unsigned int",
|
|
||||||
"__cpp_nsdmi=200809L",
|
|
||||||
"__FLT_EVAL_METHOD_TS_18661_3__=0",
|
|
||||||
"__UINT32_MAX__=0xffffffffUL",
|
|
||||||
"__GXX_EXPERIMENTAL_CXX0X__=1",
|
|
||||||
"__LDBL_MAX_EXP__=1024",
|
|
||||||
"__WINT_MIN__=0U",
|
|
||||||
"__FLT32X_IS_IEC_60559__=1",
|
|
||||||
"__XCHAL_HAVE_THREADPTR=1",
|
|
||||||
"__INT_LEAST16_WIDTH__=16",
|
|
||||||
"__SCHAR_MAX__=0x7f",
|
|
||||||
"__WCHAR_MIN__=0",
|
|
||||||
"__XCHAL_ICACHE_LINEWIDTH=4",
|
|
||||||
"__INT64_C(c)=c ## LL",
|
|
||||||
"__GCC_ATOMIC_POINTER_LOCK_FREE=2",
|
|
||||||
"__ATOMIC_SEQ_CST=5",
|
|
||||||
"__SIZEOF_INT__=4",
|
|
||||||
"__FLT32X_MANT_DIG__=53",
|
|
||||||
"__GCC_ATOMIC_CHAR16_T_LOCK_FREE=2",
|
|
||||||
"__cpp_aligned_new=201606L",
|
|
||||||
"__XCHAL_HAVE_FP_RECIP=1",
|
|
||||||
"__FLT32_MAX_10_EXP__=38",
|
|
||||||
"__STDC_HOSTED__=1",
|
|
||||||
"__XCHAL_HAVE_MUL32=1",
|
|
||||||
"__XTENSA_EL__=1",
|
|
||||||
"__cpp_decltype_auto=201304L",
|
|
||||||
"__DBL_DIG__=15",
|
|
||||||
"__XCHAL_HAVE_MUL16=1",
|
|
||||||
"__FLT_EPSILON__=1.1920928955078125e-7F",
|
|
||||||
"__GXX_WEAK__=1",
|
|
||||||
"__SHRT_WIDTH__=16",
|
|
||||||
"__FLT32_IS_IEC_60559__=1",
|
|
||||||
"__LDBL_MIN__=2.2250738585072014e-308L",
|
|
||||||
"__DBL_IS_IEC_60559__=1",
|
|
||||||
"__cpp_threadsafe_static_init=200806L",
|
|
||||||
"__cpp_enumerator_attributes=201411L",
|
|
||||||
"__XCHAL_HAVE_MMU=0",
|
|
||||||
"__FLT32X_HAS_INFINITY__=1",
|
|
||||||
"__INT32_MAX__=0x7fffffffL",
|
|
||||||
"__XCHAL_HAVE_DIV32=1",
|
|
||||||
"__INT_WIDTH__=32",
|
|
||||||
"__XTENSA_MARCH_LATEST=260003",
|
|
||||||
"__DECIMAL_DIG__=17",
|
|
||||||
"__FLT64_EPSILON__=2.2204460492503131e-16F64",
|
|
||||||
"__INT16_MAX__=0x7fff",
|
|
||||||
"__FLT64_MIN_EXP__=(-1021)",
|
|
||||||
"__XCHAL_DCACHE_SIZE=0",
|
|
||||||
"__LDBL_HAS_QUIET_NAN__=1",
|
|
||||||
"__cpp_return_type_deduction=201304L",
|
|
||||||
"__XCHAL_HAVE_BOOLEANS=1",
|
|
||||||
"__FLT64_MANT_DIG__=53",
|
|
||||||
"__XTHAL_ABI_WINDOWED=0",
|
|
||||||
"__GNUC__=14",
|
|
||||||
"__GXX_RTTI=1",
|
|
||||||
"__FLT_HAS_DENORM__=1",
|
|
||||||
"__SIZEOF_LONG_DOUBLE__=8",
|
|
||||||
"__XCHAL_HAVE_CONST16=0",
|
|
||||||
"__BIGGEST_ALIGNMENT__=16",
|
|
||||||
"__STDC_UTF_16__=1",
|
|
||||||
"__FLT64_MAX_10_EXP__=308",
|
|
||||||
"__cpp_delegating_constructors=200604L",
|
|
||||||
"__DBL_MAX__=double(1.7976931348623157e+308L)",
|
|
||||||
"__cpp_raw_strings=200710L",
|
|
||||||
"__INT_FAST32_MAX__=0x7fffffff",
|
|
||||||
"__DBL_HAS_INFINITY__=1",
|
|
||||||
"__cpp_deduction_guides=201703L",
|
|
||||||
"__HAVE_SPECULATION_SAFE_VALUE=1",
|
|
||||||
"__cpp_fold_expressions=201603L",
|
|
||||||
"__INTPTR_WIDTH__=32",
|
|
||||||
"__UINT_LEAST32_MAX__=0xffffffffUL",
|
|
||||||
"__FLT32X_HAS_DENORM__=1",
|
|
||||||
"__INT_FAST16_TYPE__=int",
|
|
||||||
"__XCHAL_HAVE_RELEASE_SYNC=1",
|
|
||||||
"__LDBL_HAS_DENORM__=1",
|
|
||||||
"__cplusplus=201703L",
|
|
||||||
"__cpp_ref_qualifiers=200710L",
|
|
||||||
"__INT_LEAST32_MAX__=0x7fffffffL",
|
|
||||||
"__DEPRECATED=1",
|
|
||||||
"__cpp_rvalue_references=200610L",
|
|
||||||
"__DBL_MAX_EXP__=1024",
|
|
||||||
"__WCHAR_WIDTH__=16",
|
|
||||||
"__FLT32_MAX__=3.4028234663852886e+38F32",
|
|
||||||
"__GCC_ATOMIC_LONG_LOCK_FREE=2",
|
|
||||||
"__PTRDIFF_MAX__=0x7fffffff",
|
|
||||||
"__FLT32_HAS_QUIET_NAN__=1",
|
|
||||||
"__GNUG__=14",
|
|
||||||
"__LONG_LONG_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__SIZEOF_SIZE_T__=4",
|
|
||||||
"__SIZEOF_WINT_T__=4",
|
|
||||||
"__FLT32X_DIG__=15",
|
|
||||||
"__LONG_LONG_WIDTH__=64",
|
|
||||||
"__cpp_initializer_lists=200806L",
|
|
||||||
"__FLT32_MAX_EXP__=128",
|
|
||||||
"__XCHAL_HAVE_MINMAX=1",
|
|
||||||
"__cpp_hex_float=201603L",
|
|
||||||
"__XCHAL_NUM_IBREAK=2",
|
|
||||||
"__GXX_ABI_VERSION=1019",
|
|
||||||
"__FLT_MIN_EXP__=(-125)",
|
|
||||||
"__cpp_lambdas=200907L",
|
|
||||||
"__INT_FAST64_TYPE__=long long int",
|
|
||||||
"__FP_FAST_FMAF=1",
|
|
||||||
"__FLT64_DENORM_MIN__=4.9406564584124654e-324F64",
|
|
||||||
"__DBL_MIN__=double(2.2250738585072014e-308L)",
|
|
||||||
"__SIZEOF_POINTER__=4",
|
|
||||||
"__DBL_HAS_QUIET_NAN__=1",
|
|
||||||
"__FLT32X_EPSILON__=2.2204460492503131e-16F32x",
|
|
||||||
"__XSHAL_HAVE_TEXT_SECTION_LITERALS=1",
|
|
||||||
"__FLT64_MIN_10_EXP__=(-307)",
|
|
||||||
"__REGISTER_PREFIX__",
|
|
||||||
"__UINT16_MAX__=0xffff",
|
|
||||||
"__XSHAL_USE_ABSOLUTE_LITERALS=0",
|
|
||||||
"__LDBL_HAS_INFINITY__=1",
|
|
||||||
"__FLT32_MIN__=1.1754943508222875e-38F32",
|
|
||||||
"__UINT8_TYPE__=unsigned char",
|
|
||||||
"__FLT_DIG__=6",
|
|
||||||
"__NO_INLINE__=1",
|
|
||||||
"__DEC_EVAL_METHOD__=2",
|
|
||||||
"__FLT_MANT_DIG__=24",
|
|
||||||
"__LDBL_DECIMAL_DIG__=17",
|
|
||||||
"__VERSION__=\"14.2.0\"",
|
|
||||||
"__UINT64_C(c)=c ## ULL",
|
|
||||||
"__XCHAL_NUM_AREGS=64",
|
|
||||||
"__cpp_unicode_characters=201411L",
|
|
||||||
"__XCHAL_HAVE_XEA3=0",
|
|
||||||
"__GCC_ATOMIC_INT_LOCK_FREE=2",
|
|
||||||
"__XCHAL_HAVE_DENSITY=1",
|
|
||||||
"__FLT32_MANT_DIG__=24",
|
|
||||||
"__FLOAT_WORD_ORDER__=__ORDER_LITTLE_ENDIAN__",
|
|
||||||
"__XCHAL_HAVE_CLAMPS=0",
|
|
||||||
"__XCHAL_HAVE_DFP_RSQRT=0",
|
|
||||||
"__cpp_aggregate_bases=201603L",
|
|
||||||
"__XCHAL_HAVE_NSA=1",
|
|
||||||
"__XCHAL_HAVE_WINDOWED=1",
|
|
||||||
"__SCHAR_WIDTH__=8",
|
|
||||||
"__INT32_C(c)=c ## L",
|
|
||||||
"__ORDER_PDP_ENDIAN__=3412",
|
|
||||||
"__INT_FAST32_TYPE__=int",
|
|
||||||
"__UINT_LEAST16_TYPE__=short unsigned int",
|
|
||||||
"__DBL_HAS_DENORM__=1",
|
|
||||||
"__XCHAL_HAVE_DEBUG=1",
|
|
||||||
"__cpp_rtti=199711L",
|
|
||||||
"__SIZE_TYPE__=unsigned int",
|
|
||||||
"__UINT64_MAX__=0xffffffffffffffffULL",
|
|
||||||
"__FLT_IS_IEC_60559__=1",
|
|
||||||
"__GNUC_WIDE_EXECUTION_CHARSET_NAME=\"UTF-16LE\"",
|
|
||||||
"__INT8_TYPE__=signed char",
|
|
||||||
"__cpp_digit_separators=201309L",
|
|
||||||
"__ELF__=1",
|
|
||||||
"__XSHAL_ABI=0",
|
|
||||||
"__xtensa__=1",
|
|
||||||
"__FLT_RADIX__=2",
|
|
||||||
"__INT_LEAST16_TYPE__=short int",
|
|
||||||
"__LDBL_EPSILON__=2.2204460492503131e-16L",
|
|
||||||
"__UINTMAX_C(c)=c ## ULL",
|
|
||||||
"__FLT32X_MIN__=2.2250738585072014e-308F32x",
|
|
||||||
"__XCHAL_HAVE_DFP_SQRT=0",
|
|
||||||
"__SIG_ATOMIC_MAX__=0x7fffffff",
|
|
||||||
"__XCHAL_HAVE_MAC16=1",
|
|
||||||
"__GCC_ATOMIC_WCHAR_T_LOCK_FREE=2",
|
|
||||||
"__USER_LABEL_PREFIX__",
|
|
||||||
"__SIZEOF_PTRDIFF_T__=4",
|
|
||||||
"__XCHAL_MMU_MIN_PTE_PAGE_SIZE=1",
|
|
||||||
"__XCHAL_DCACHE_IS_WRITEBACK=0",
|
|
||||||
"__SIZEOF_LONG__=4",
|
|
||||||
"__LDBL_DIG__=15",
|
|
||||||
"__FLT64_IS_IEC_60559__=1",
|
|
||||||
"__XCHAL_MAX_INSTRUCTION_SIZE=3",
|
|
||||||
"__FLT32X_MIN_EXP__=(-1021)",
|
|
||||||
"__INT_FAST16_MAX__=0x7fffffff",
|
|
||||||
"__GCC_CONSTRUCTIVE_SIZE=32",
|
|
||||||
"__FLT64_DIG__=15",
|
|
||||||
"__UINT_FAST32_MAX__=0xffffffffU",
|
|
||||||
"__UINT_LEAST64_TYPE__=long long unsigned int",
|
|
||||||
"__FLT_HAS_QUIET_NAN__=1",
|
|
||||||
"__FLT_MAX_10_EXP__=38",
|
|
||||||
"__FLT_HAS_INFINITY__=1",
|
|
||||||
"__GNUC_EXECUTION_CHARSET_NAME=\"UTF-8\"",
|
|
||||||
"__CHAR_UNSIGNED__=1",
|
|
||||||
"__cpp_unicode_literals=200710L",
|
|
||||||
"__UINT_FAST16_TYPE__=unsigned int",
|
|
||||||
"__INT_FAST32_WIDTH__=32",
|
|
||||||
"__CHAR16_TYPE__=short unsigned int",
|
|
||||||
"__PRAGMA_REDEFINE_EXTNAME=1",
|
|
||||||
"__SIZE_WIDTH__=32",
|
|
||||||
"__INT_LEAST16_MAX__=0x7fff",
|
|
||||||
"__INT64_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__FLT32_DENORM_MIN__=1.4012984643248171e-45F32",
|
|
||||||
"__SIG_ATOMIC_WIDTH__=32",
|
|
||||||
"__INT_LEAST64_TYPE__=long long int",
|
|
||||||
"__INT16_TYPE__=short int",
|
|
||||||
"__INT_LEAST8_TYPE__=signed char",
|
|
||||||
"__cpp_structured_bindings=201606L",
|
|
||||||
"__INT_FAST8_MAX__=0x7fffffff",
|
|
||||||
"__INTPTR_MAX__=0x7fffffff",
|
|
||||||
"__cpp_sized_deallocation=201309L",
|
|
||||||
"__cpp_guaranteed_copy_elision=201606L",
|
|
||||||
"__FLT64_HAS_QUIET_NAN__=1",
|
|
||||||
"__FLT32_MIN_10_EXP__=(-37)",
|
|
||||||
"__EXCEPTIONS=1",
|
|
||||||
"__UINT16_C(c)=c",
|
|
||||||
"__XCHAL_M_STAGE=3",
|
|
||||||
"__PTRDIFF_WIDTH__=32",
|
|
||||||
"__LDBL_MANT_DIG__=53",
|
|
||||||
"__cpp_range_based_for=201603L",
|
|
||||||
"__FLT64_HAS_INFINITY__=1",
|
|
||||||
"__STDCPP_DEFAULT_NEW_ALIGNMENT__=8",
|
|
||||||
"__SIG_ATOMIC_MIN__=(-__SIG_ATOMIC_MAX__ - 1)",
|
|
||||||
"__XCHAL_ICACHE_SIZE=0",
|
|
||||||
"__cpp_nontype_template_args=201411L",
|
|
||||||
"__INTPTR_TYPE__=int",
|
|
||||||
"__UINT16_TYPE__=short unsigned int",
|
|
||||||
"__WCHAR_TYPE__=short unsigned int",
|
|
||||||
"__XCHAL_HAVE_DEPBITS=0",
|
|
||||||
"__SIZEOF_FLOAT__=4",
|
|
||||||
"__UINTPTR_MAX__=0xffffffffU",
|
|
||||||
"__INT_FAST64_WIDTH__=64",
|
|
||||||
"__cpp_decltype=200707L",
|
|
||||||
"__FLT32_DECIMAL_DIG__=9",
|
|
||||||
"__INT_FAST64_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1",
|
|
||||||
"__FLT_NORM_MAX__=3.4028234663852886e+38F",
|
|
||||||
"__XCHAL_HAVE_DFP=0",
|
|
||||||
"__FLT32_HAS_INFINITY__=1",
|
|
||||||
"__UINT_FAST64_TYPE__=long long unsigned int",
|
|
||||||
"__cpp_inline_variables=201606L",
|
|
||||||
"__INT_MAX__=0x7fffffff",
|
|
||||||
"__XCHAL_HAVE_EXCLUSIVE=0",
|
|
||||||
"__STDCPP_THREADS__=1",
|
|
||||||
"__INT64_TYPE__=long long int",
|
|
||||||
"__XCHAL_HAVE_DFP_DIV=0",
|
|
||||||
"__FLT_MAX_EXP__=128",
|
|
||||||
"__XCHAL_INST_FETCH_WIDTH=4",
|
|
||||||
"__DBL_MANT_DIG__=53",
|
|
||||||
"__cpp_inheriting_constructors=201511L",
|
|
||||||
"__INT_LEAST64_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__FP_FAST_FMAF32=1",
|
|
||||||
"__WINT_TYPE__=unsigned int",
|
|
||||||
"__UINT_LEAST32_TYPE__=long unsigned int",
|
|
||||||
"__SIZEOF_SHORT__=2",
|
|
||||||
"__FLT32_NORM_MAX__=3.4028234663852886e+38F32",
|
|
||||||
"__LDBL_MIN_EXP__=(-1021)",
|
|
||||||
"__XCHAL_HAVE_S32C1I=1",
|
|
||||||
"__FLT64_MAX__=1.7976931348623157e+308F64",
|
|
||||||
"__WINT_WIDTH__=32",
|
|
||||||
"__cpp_template_auto=201606L",
|
|
||||||
"__INT_LEAST64_WIDTH__=64",
|
|
||||||
"__FLT32X_MAX_10_EXP__=308",
|
|
||||||
"__cpp_namespace_attributes=201411L",
|
|
||||||
"__WCHAR_UNSIGNED__=1",
|
|
||||||
"__LDBL_MAX_10_EXP__=308",
|
|
||||||
"__ATOMIC_RELAXED=0",
|
|
||||||
"__DBL_EPSILON__=double(2.2204460492503131e-16L)",
|
|
||||||
"__XCHAL_HAVE_SEXT=1",
|
|
||||||
"__INT_LEAST32_TYPE__=long int",
|
|
||||||
"__XTENSA_WINDOWED_ABI__=1",
|
|
||||||
"__UINT8_C(c)=c",
|
|
||||||
"__FLT64_MAX_EXP__=1024",
|
|
||||||
"__SIZEOF_WCHAR_T__=2",
|
|
||||||
"__XCHAL_HAVE_FP_POSTINC=1",
|
|
||||||
"__FLT64_NORM_MAX__=1.7976931348623157e+308F64",
|
|
||||||
"__INTMAX_MAX__=0x7fffffffffffffffLL",
|
|
||||||
"__INT_FAST8_TYPE__=int",
|
|
||||||
"__XCHAL_HAVE_MUL32_HIGH=1",
|
|
||||||
"__GNUC_STDC_INLINE__=1",
|
|
||||||
"__FLT64_HAS_DENORM__=1",
|
|
||||||
"__FLT32_EPSILON__=1.1920928955078125e-7F32",
|
|
||||||
"__DBL_DECIMAL_DIG__=17",
|
|
||||||
"__STDC_UTF_32__=1",
|
|
||||||
"__XCHAL_HAVE_FP_DIV=1",
|
|
||||||
"__INT_FAST8_WIDTH__=32",
|
|
||||||
"__FLT32X_MAX__=1.7976931348623157e+308F32x",
|
|
||||||
"__DBL_NORM_MAX__=double(1.7976931348623157e+308L)",
|
|
||||||
"__BYTE_ORDER__=__ORDER_LITTLE_ENDIAN__",
|
|
||||||
"__GCC_DESTRUCTIVE_SIZE=32",
|
|
||||||
"__XTENSA__=1",
|
|
||||||
"__INTMAX_WIDTH__=64",
|
|
||||||
"__ORDER_BIG_ENDIAN__=4321",
|
|
||||||
"__XTHAL_ABI_CALL0=1",
|
|
||||||
"__cpp_runtime_arrays=198712L",
|
|
||||||
"__FLT32_DIG__=6",
|
|
||||||
"__UINT64_TYPE__=long long unsigned int",
|
|
||||||
"__UINT32_C(c)=c ## UL",
|
|
||||||
"__cpp_alias_templates=200704L",
|
|
||||||
"__FLT_DENORM_MIN__=1.4012984643248171e-45F",
|
|
||||||
"__INT8_MAX__=0x7f",
|
|
||||||
"__LONG_WIDTH__=32",
|
|
||||||
"__UINT_FAST32_TYPE__=unsigned int",
|
|
||||||
"__FLT32X_NORM_MAX__=1.7976931348623157e+308F32x",
|
|
||||||
"__CHAR32_TYPE__=long unsigned int",
|
|
||||||
"__FLT_MAX__=3.4028234663852886e+38F",
|
|
||||||
"__cpp_constexpr=201603L",
|
|
||||||
"__XCHAL_HAVE_FP_RSQRT=1",
|
|
||||||
"__INT32_TYPE__=long int",
|
|
||||||
"__SIZEOF_DOUBLE__=8",
|
|
||||||
"__cpp_exceptions=199711L",
|
|
||||||
"__FLT_MIN_10_EXP__=(-37)",
|
|
||||||
"__FLT64_MIN__=2.2250738585072014e-308F64",
|
|
||||||
"__INT_LEAST32_WIDTH__=32",
|
|
||||||
"__INTMAX_TYPE__=long long int",
|
|
||||||
"__XCHAL_HAVE_ABS=1",
|
|
||||||
"__FLT32X_HAS_QUIET_NAN__=1",
|
|
||||||
"__ATOMIC_CONSUME=1",
|
|
||||||
"__XCHAL_NUM_DBREAK=2",
|
|
||||||
"__XCHAL_HAVE_WIDE_BRANCHES=0",
|
|
||||||
"__GNUC_MINOR__=2",
|
|
||||||
"__INT_FAST16_WIDTH__=32",
|
|
||||||
"__UINTMAX_MAX__=0xffffffffffffffffULL",
|
|
||||||
"__FLT32X_DENORM_MIN__=4.9406564584124654e-324F32x",
|
|
||||||
"__cpp_template_template_args=201611L",
|
|
||||||
"__DBL_MAX_10_EXP__=308",
|
|
||||||
"__LDBL_DENORM_MIN__=4.9406564584124654e-324L",
|
|
||||||
"__INT16_C(c)=c",
|
|
||||||
"__STDC__=1",
|
|
||||||
"__PTRDIFF_TYPE__=int",
|
|
||||||
"__LONG_MAX__=0x7fffffffL",
|
|
||||||
"__XCHAL_HAVE_FP_SQRT=1",
|
|
||||||
"__UINT32_TYPE__=long unsigned int",
|
|
||||||
"__FLT32X_MIN_10_EXP__=(-307)",
|
|
||||||
"__UINTPTR_TYPE__=unsigned int",
|
|
||||||
"__LDBL_MIN_10_EXP__=(-307)",
|
|
||||||
"__cpp_generic_lambdas=201304L",
|
|
||||||
"__SIZEOF_LONG_LONG__=8",
|
|
||||||
"__cpp_user_defined_literals=200809L",
|
|
||||||
"__GCC_ATOMIC_LLONG_LOCK_FREE=1",
|
|
||||||
"__FLT_DECIMAL_DIG__=9",
|
|
||||||
"__UINT_FAST16_MAX__=0xffffffffU",
|
|
||||||
"__LDBL_NORM_MAX__=1.7976931348623157e+308L",
|
|
||||||
"__GCC_ATOMIC_SHORT_LOCK_FREE=2",
|
|
||||||
"__XCHAL_HAVE_BE=0",
|
|
||||||
"__UINT_FAST8_TYPE__=unsigned int",
|
|
||||||
"__cpp_init_captures=201304L",
|
|
||||||
"__ATOMIC_ACQ_REL=4",
|
|
||||||
"__ATOMIC_RELEASE=3",
|
|
||||||
"USBCON"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"C_Cpp.errorSquiggles": "enabled",
|
|
||||||
"C_Cpp.default.compilerPath": "~/.arduino15/packages/esp32/tools/esp-x32/2411/bin/xtensa-esp32-elf-g++",
|
|
||||||
"files.associations": {
|
|
||||||
".fantomasignore": "ignore",
|
|
||||||
"string_view": "cpp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
355
README.md
355
README.md
|
|
@ -1,313 +1,118 @@
|
||||||
[](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml)
|
# DShotRMT - ESP32 RMT DShot Driver
|
||||||
|
|
||||||
# DShotRMT - ESP32 Library (Rewrite for ESP-IDF 5)
|
[](https://github.com/derdoktor667/DShotRMT/actions/workflows/ci.yml)
|
||||||
|
|
||||||
A modern, robust C++ library for generating DShot signals on the ESP32 using the new ESP-IDF 5 RMT encoder API (`rmt_tx.h` / `rmt_rx.h`).
|
A C++ library for generating DShot signals on ESP32 microcontrollers using the RMT (Remote Control) peripheral. It's designed for both Arduino and ESP-IDF projects, providing a simple and efficient way to control brushless motors.
|
||||||
Supports all standard DShot modes (150, 300, 600, 1200) and features continuous frame transmission with configurable timing.
|
|
||||||
|
|
||||||
**Now with BiDirectional DShot support, advanced command management, and modern web control interface!**
|
This library is a rewrite using the modern ESP-IDF 5 RMT encoder API (`rmt_tx.h` / `rmt_rx.h`) for improved performance and flexibility. The legacy version using the old `rmt.h` API is available in the `oldAPI` branch.
|
||||||
|
|
||||||
> The legacy version (using the old `rmt.h` API) is still available in the `oldAPI` branch.
|
## 🚀 Core Features
|
||||||
|
|
||||||
---
|
- **Multiple DShot Modes:** Supports DSHOT150, DSHOT300, DSHOT600, and DSHOT1200.
|
||||||
|
- **Bidirectional DShot:** Implemented, but currently not officially supported due to instability and external hardware requirements.
|
||||||
## 🚀 Features
|
- **Hardware-Timed Signals:** Precise signal generation using the ESP32 RMT peripheral, ensuring stable and reliable motor control.
|
||||||
|
- **Simple API:** Easy-to-use C++ class with intuitive methods like `sendThrottlePercent()`.
|
||||||
- **All DShot Modes:** DSHOT150, DSHOT300 (default), DSHOT600, DSHOT1200
|
- **Efficient and Lightweight:** The core library has no external dependencies.
|
||||||
- **BiDirectional DShot:** Full support for RPM telemetry feedback
|
- **Arduino and ESP-IDF Compatible:** Can be used in both Arduino and ESP-IDF projects.
|
||||||
- **Web Control Interface:** Modern responsive web UI with WiFi access point
|
|
||||||
- **Advanced Command Manager:** High-level API for ESC configuration and control
|
|
||||||
- **Safety Features:** Arming/disarming system with motor lockout protection
|
|
||||||
- **Dual Control Options:** Web interface and serial console control
|
|
||||||
- **Real-time Telemetry:** Live RPM monitoring and data display
|
|
||||||
- **Hardware-Timed Signals:** Independent, precise signal generation using ESP32 RMT peripheral
|
|
||||||
- **Configurable Timing:** Ensures ESCs can reliably detect frame boundaries
|
|
||||||
- **Error Handling:** Comprehensive result reporting with success/failure status
|
|
||||||
- **Simple API:** Easy integration into your Arduino or ESP-IDF project
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 Installation
|
## 📦 Installation
|
||||||
|
|
||||||
### Arduino IDE
|
### Arduino IDE
|
||||||
1. Search "Arduino Library Manager" for "DShotRMT"
|
|
||||||
|
|
||||||
or
|
1. Open the Arduino Library Manager (`Sketch` > `Include Library` > `Manage Libraries...`).
|
||||||
|
2. Search for "DShotRMT" and click "Install".
|
||||||
1. Clone this repository or download as ZIP
|
3. Alternatively, you can clone this repository or download it as a ZIP file and place it in your Arduino libraries folder (`~/Arduino/libraries/DShotRMT/`).
|
||||||
2. Place in your Arduino libraries folder (`~/Arduino/libraries/DShotRMT/`)
|
|
||||||
3. Restart Arduino IDE
|
|
||||||
|
|
||||||
### PlatformIO
|
### PlatformIO
|
||||||
Add to your `platformio.ini`:
|
|
||||||
|
Add the following to your `platformio.ini` file:
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/derdoktor667/DShotRMT.git
|
https://github.com/derdoktor667/DShotRMT.git
|
||||||
```
|
```
|
||||||
|
|
||||||
### Manual Installation
|
## ⚡ Quick Start
|
||||||
```sh
|
|
||||||
git clone https://github.com/derdoktor667/DShotRMT.git
|
Here's a basic example of how to use the `DShotRMT` library to control a motor:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <DShotRMT.h>
|
||||||
|
|
||||||
|
// Define the GPIO pin connected to the motor ESC
|
||||||
|
const gpio_num_t MOTOR_PIN = GPIO_NUM_27;
|
||||||
|
|
||||||
|
// Create a DShotRMT instance for DSHOT300
|
||||||
|
DShotRMT motor(MOTOR_PIN, DSHOT300);
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
// Initialize the DShot motor
|
||||||
|
motor.begin();
|
||||||
|
|
||||||
|
Serial.println("Motor initialized. Ramping up to 25% throttle...");
|
||||||
|
|
||||||
|
// Ramp up to 25% throttle over 2.5 seconds
|
||||||
|
for (int i = 0; i <= 25; i++) {
|
||||||
|
motor.sendThrottlePercent(i);
|
||||||
|
delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Stopping motor.");
|
||||||
|
motor.sendThrottlePercent(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
// Your main code here
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dependencies
|
## 🎮 Examples
|
||||||
|
|
||||||
The library requires these additional libraries for full functionality:
|
The `examples` folder contains more advanced examples:
|
||||||
|
|
||||||
**Core DShotRMT (always required):**
|
- **`throttle_percent`:** A focused example showing how to control motor speed using percentage values (0-100) via the serial monitor.
|
||||||
- ESP32 Arduino Core
|
- **`dshot300`:** A more advanced example demonstrating how to send raw DShot commands and receive telemetry via the serial monitor.
|
||||||
|
- **`web_control`:** A full-featured web application for controlling a motor from a web browser. It creates a WiFi access point and serves a web page with a throttle slider and arming switch.
|
||||||
|
- **`web_client`:** A variation of the `web_control` example that connects to an existing WiFi network instead of creating its own access point.
|
||||||
|
|
||||||
|
### Dependencies for Web Examples
|
||||||
|
|
||||||
|
The `web_control` and `web_client` examples require the following additional libraries:
|
||||||
|
|
||||||
|
- [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
|
||||||
|
- [ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer)
|
||||||
|
- [AsyncTCP](https://github.com/ESP32Async/AsyncTCP)
|
||||||
|
|
||||||
|
You can install these libraries using the Arduino Library Manager or by adding them to your `platformio.ini` file:
|
||||||
|
|
||||||
**Web Interface Example (web_control.ino / web_client.ino):**
|
|
||||||
```ini
|
```ini
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/derdoktor667/DShotRMT
|
https://github.com/derdoktor667/DShotRMT.git
|
||||||
https://github.com/bblanchon/ArduinoJson
|
https://github.com/bblanchon/ArduinoJson
|
||||||
https://github.com/ESP32Async/ESPAsyncWebServer
|
https://github.com/ESP32Async/ESPAsyncWebServer
|
||||||
https://github.com/ESP32Async/AsyncTCP
|
https://github.com/ESP32Async/AsyncTCP
|
||||||
```
|
```
|
||||||
|
|
||||||
**Command Manager Example:**
|
## 📚 API Reference
|
||||||
- No additional dependencies required
|
|
||||||
|
|
||||||
---
|
The main class is `DShotRMT`. Here are the most important methods:
|
||||||
|
|
||||||
## ⚡ Quick Start
|
- `DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional = false)`: Constructor to create a new DShotRMT instance. (Note: Bidirectional DShot is currently not officially supported.)
|
||||||
|
- `begin()`: Initializes the RMT peripheral and the DShot encoder.
|
||||||
|
- `sendThrottlePercent(float percent)`: Sends a throttle value as a percentage (0.0-100.0).
|
||||||
|
- `sendThrottle(uint16_t throttle)`: Sends a raw throttle value (48-2047) to the motor.
|
||||||
|
- `sendCommand(uint16_t command)`: Sends a DShot command (0-47) to the motor.
|
||||||
|
- `getTelemetry(uint16_t magnet_count)`: Receives and parses telemetry data from the motor (for bidirectional DShot, which is currently not officially supported).
|
||||||
|
|
||||||
### Basic Usage (DShotRMT)
|
For more details, please refer to the `DShotRMT.h` header file.
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <DShotRMT.h>
|
|
||||||
|
|
||||||
// Create motor instance (GPIO 17, DSHOT300, non-bidirectional)
|
|
||||||
DShotRMT motor(17, DSHOT300, false);
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
|
|
||||||
// Initialize the motor
|
|
||||||
dshot_result_t result = motor.begin();
|
|
||||||
if (result.success) {
|
|
||||||
Serial.println("Motor initialized successfully");
|
|
||||||
} else {
|
|
||||||
Serial.printf("Motor init failed: %s\n", result.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
// Send throttle value (48-2047)
|
|
||||||
dshot_result_t result = motor.sendThrottle(1000);
|
|
||||||
if (!result.success) {
|
|
||||||
Serial.printf("Throttle command failed: %s\n", result.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🌐 Web Control Interface
|
|
||||||
|
|
||||||
The DShotRMT library now includes a modern web interface for wireless motor control:
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- **Responsive Design:** Works on mobile phones, tablets, and desktop computers
|
|
||||||
- **WiFi Access Point:** Creates hotspot "DShotRMT Control" (Password: 12345678)
|
|
||||||
- **Safety System:** Arming/disarming switch prevents accidental motor activation
|
|
||||||
- **Real-time Control:** Instant throttle response via WebSocket communication
|
|
||||||
- **Live Telemetry:** Real-time RPM display (bidirectional mode only)
|
|
||||||
- **Auto-reconnect:** Automatically reconnects on connection loss
|
|
||||||
|
|
||||||
### Web Interface Access
|
|
||||||
1. Connect to WiFi network: **"DShotRMT Control"**
|
|
||||||
2. Password: **12345678**
|
|
||||||
3. Open browser and navigate to: **http://10.10.10.1**
|
|
||||||
|
|
||||||
### Safety Features
|
|
||||||
- Motor control is **disabled by default** (disarmed state)
|
|
||||||
- Toggle the **ARMING SWITCH** to enable motor control
|
|
||||||
- Throttle slider is **locked** when disarmed
|
|
||||||
- **Emergency stop** resets all values to safe state
|
|
||||||
|
|
||||||
### Technical Implementation
|
|
||||||
- **AsyncWebServer** for HTTP requests
|
|
||||||
- **WebSocket** communication for real-time data
|
|
||||||
- **JSON** message format for data exchange
|
|
||||||
- **WiFi SoftAP** mode for standalone operation
|
|
||||||
- **Automatic client cleanup** prevents memory leaks
|
|
||||||
|
|
||||||
### ⚠️ Known Issus
|
|
||||||
Make sure you are using these libraries for [ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) and [AsyncTCP](https://github.com/ESP32Async/AsyncTCP) to use "web_control.ino" example sketch.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📚 Examples
|
|
||||||
|
|
||||||
The library includes comprehensive examples:
|
|
||||||
|
|
||||||
### 1. Basic DShot Control with Web Interface (`web_control.ino`)
|
|
||||||
- **Web Control Interface:** Modern responsive web UI accessible at `http://10.10.10.1`
|
|
||||||
- **WiFi Access Point:** Creates hotspot "DShotRMT Control" for wireless control
|
|
||||||
- **Safety Features:** Arming/disarming system with motor safety lockout
|
|
||||||
- **Real-time Data:** Live RPM telemetry display (bidirectional mode)
|
|
||||||
- **Dual Control:** Both web interface and serial console control
|
|
||||||
- **WebSocket Communication:** Real-time bidirectional data exchange
|
|
||||||
|
|
||||||
**Web Interface Features:**
|
|
||||||
- Responsive design optimized for mobile and desktop
|
|
||||||
- Visual arming switch with safety lockout
|
|
||||||
- Smooth throttle slider with real-time feedback
|
|
||||||
- Live RPM monitoring display
|
|
||||||
- Automatic reconnection on connection loss
|
|
||||||
|
|
||||||
### 2. Advanced Command Management (`command_manager.ino`)
|
|
||||||
Interactive ESC control with full menu system:
|
|
||||||
```
|
|
||||||
=== DShot Command Manager Menu ===
|
|
||||||
1 - Stop Motor
|
|
||||||
2 - Activate Beacon 1
|
|
||||||
3 - Set Normal Spin Direction
|
|
||||||
4 - Set Reversed Spin Direction
|
|
||||||
5 - Get ESC Info
|
|
||||||
6 - Turn LED 0 ON
|
|
||||||
7 - Turn LED 0 OFF
|
|
||||||
0 - Emergency Stop
|
|
||||||
|
|
||||||
Advanced Commands:
|
|
||||||
cmd <number> - Send DShot command (0 - 47)
|
|
||||||
throttle <value> - Set throttle (48 - 2047)
|
|
||||||
repeat cmd <num> count <count> - Repeat command
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔧 Hardware Configuration
|
|
||||||
|
|
||||||
### Supported DShot Modes
|
|
||||||
|
|
||||||
| DSHOT | Bitrate | TH1 | TH0 | Bit Time (µs) | Frame Time (µs) |
|
|
||||||
|-------|-------------|-------|--------|---------------|-----------------|
|
|
||||||
| 150 | 150 kbit/s | 5.00 | 2.50 | 6.67 | ~106.72 |
|
|
||||||
| 300 | 300 kbit/s | 2.50 | 1.25 | 3.33 | ~53.28 |
|
|
||||||
| 600 | 600 kbit/s | 1.25 | 0.625 | 1.67 | ~26.72 |
|
|
||||||
|
|
||||||
For DShot, T1H length is always double T0H length.
|
|
||||||
|
|
||||||
### GPIO Configuration
|
|
||||||
```cpp
|
|
||||||
// Using GPIO number
|
|
||||||
DShotRMT motor(17, DSHOT300);
|
|
||||||
|
|
||||||
// Using GPIO enum
|
|
||||||
DShotRMT motor(GPIO_NUM_17, DSHOT300);
|
|
||||||
|
|
||||||
// With bidirectional support
|
|
||||||
DShotRMT motor(17, DSHOT300, true);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 DShot Commands
|
|
||||||
|
|
||||||
| Command | Value | Description | Usage |
|
|
||||||
|---------|-------|-------------|-------|
|
|
||||||
| MOTOR_STOP | 0 | Stop motor | Always available |
|
|
||||||
| BEACON 1 - 5 | 1 - 5 | Motor beeping | Motor identification |
|
|
||||||
| ESC_INFO | 6 | Request ESC info | Get ESC version/settings |
|
|
||||||
| SPIN_DIRECTION_1/2 | 7 - 8 | Set spin direction | Motor configuration |
|
|
||||||
| 3D_MODE_OFF/ON | 9 - 10 | 3D mode control | Bidirectional flight |
|
|
||||||
| SAVE_SETTINGS | 12 | Save to EEPROM | Permanent configuration |
|
|
||||||
| EXTENDED_TELEMETRY_ENABLE/DISABLE | 13 - 14 | Telemetry control | Data transmission |
|
|
||||||
| SPIN_DIRECTION_NORMAL/REVERSED | 20 - 21 | Spin direction | Alias commands |
|
|
||||||
| LED 0-3_ON/OFF | 22 - 29 | LED control | BLHeli32 only |
|
|
||||||
| AUDIO_STREAM_MODE | 30 | Audio mode toggle | KISS ESCs |
|
|
||||||
| SILENT_MODE | 31 | Silent mode toggle | KISS ESCs |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📚 DShot Protocol Details
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Packet Structure
|
|
||||||
Each DShot frame consists of 16 bits:
|
|
||||||
- **11 bits:** Throttle/command value (0-2047)
|
|
||||||
- **1 bit:** Telemetry request flag
|
|
||||||
- **4 bits:** CRC checksum
|
|
||||||
|
|
||||||
### Checksum Calculation
|
|
||||||
```cpp
|
|
||||||
// Standard DShot CRC
|
|
||||||
uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & 0x0F;
|
|
||||||
|
|
||||||
// Bidirectional DShot (inverted CRC)
|
|
||||||
uint16_t crc = (~(data ^ (data >> 4) ^ (data >> 8))) & 0x0F;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bidirectional DShot
|
|
||||||
- **Inverted Logic:** High/low levels are inverted
|
|
||||||
- **GCR Encoding:** Telemetry uses Group Code Recording
|
|
||||||
- **21-bit Response:** 1 start + 16 data + 4 CRC bits
|
|
||||||
- **eRPM Data:** Electrical RPM transmitted back to controller
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ ESP32 RMT Peripheral
|
|
||||||
|
|
||||||
The library utilizes the ESP32's RMT (Remote Control) peripheral for precise signal generation:
|
|
||||||
|
|
||||||
### Advantages
|
|
||||||
- **Hardware Timing:** No CPU intervention during transmission
|
|
||||||
- **Concurrent Operation:** Multiple channels can run simultaneously
|
|
||||||
- **DMA Support:** Efficient, automatic memory-to-peripheral transfers
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📖 References & Documentation
|
|
||||||
|
|
||||||
### DShot Protocol
|
|
||||||
- [DSHOT – the missing Handbook](https://brushlesswhoop.com/dshot-and-bidirectional-dshot/)
|
|
||||||
- [DSHOT in the Dark](https://dmrlawson.co.uk/index.php/2017/12/04/dshot-in-the-dark/)
|
|
||||||
- [Betaflight DShot Implementation](https://github.com/betaflight/betaflight)
|
|
||||||
|
|
||||||
### ESP32 Documentation
|
|
||||||
- [ESP32 Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf)
|
|
||||||
- [ESP-IDF RMT Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html)
|
|
||||||
- [Arduino ESP32 Core](https://github.com/espressif/arduino-esp32)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 Contributing
|
||||||
|
|
||||||
We welcome contributions! Please:
|
Contributions are welcome! Please fork the repository, create a feature branch, and submit a pull request.
|
||||||
|
|
||||||
1. Fork the repository
|
|
||||||
2. Create a feature branch
|
|
||||||
3. Make your changes with tests
|
|
||||||
4. Submit a pull request
|
|
||||||
|
|
||||||
### Development Guidelines
|
|
||||||
- Follow existing code style
|
|
||||||
- Add documentation for new features
|
|
||||||
- Include examples where appropriate
|
|
||||||
- Test with real hardware when possible
|
|
||||||
|
|
||||||
### Reporting Issues
|
|
||||||
When reporting issues, please include:
|
|
||||||
- ESP32 board type and version
|
|
||||||
- Arduino/ESP-IDF version
|
|
||||||
- ESC type and firmware
|
|
||||||
- Complete error messages
|
|
||||||
- Minimal reproduction code
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📄 License
|
## 📄 License
|
||||||
|
|
||||||
MIT License – see [LICENSE](LICENSE)
|
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 👤 Author
|
|
||||||
|
|
||||||
**Wastl Kraus**
|
|
||||||
- GitHub: [@derdoktor667](https://github.com/derdoktor667)
|
|
||||||
- Website: [wir-sind-die-matrix.de](https://wir-sind-die-matrix.de)
|
|
||||||
|
|
@ -1,345 +0,0 @@
|
||||||
/*
|
|
||||||
* command_manager.ino
|
|
||||||
* Example sketch for DShotCommandManager
|
|
||||||
* Author: Wastl Kraus
|
|
||||||
* Date: 2025-09-04
|
|
||||||
* License: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <DShotRMT.h>
|
|
||||||
#include <DShotCommandManager.h>
|
|
||||||
|
|
||||||
// USB serial port settings
|
|
||||||
static constexpr auto &USB_SERIAL = Serial0;
|
|
||||||
static constexpr auto USB_SERIAL_BAUD = 115200;
|
|
||||||
|
|
||||||
// Motor configuration
|
|
||||||
static constexpr auto MOTOR01_PIN = 17;
|
|
||||||
static constexpr auto IS_BIDIRECTIONAL = false;
|
|
||||||
|
|
||||||
// Motor magnet count for RPM calculation
|
|
||||||
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
|
||||||
|
|
||||||
// Create motor and command manager instances
|
|
||||||
DShotRMT motor01(MOTOR01_PIN, DSHOT300, IS_BIDIRECTIONAL);
|
|
||||||
DShotCommandManager commandManager(motor01);
|
|
||||||
|
|
||||||
// Global variable to store the desired continuous throttle value
|
|
||||||
static volatile uint16_t throttle_now = 0;
|
|
||||||
|
|
||||||
// Helper function to print telemetry results
|
|
||||||
void printTelemetryResult(const dshot_result_t &result)
|
|
||||||
{
|
|
||||||
if (result.success && (result.erpm > 0 || result.motor_rpm > 0))
|
|
||||||
{
|
|
||||||
USB_SERIAL.printf("Telemetry: eRPM=%u, Motor RPM=%u\n", result.erpm, result.motor_rpm);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
USB_SERIAL.printf("Telemetry: FAILED - %s\n", result.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
// Start USB Serial Port
|
|
||||||
USB_SERIAL.begin(USB_SERIAL_BAUD);
|
|
||||||
|
|
||||||
// Initialize DShot
|
|
||||||
motor01.begin();
|
|
||||||
|
|
||||||
// Init Command Manager
|
|
||||||
commandManager.begin();
|
|
||||||
|
|
||||||
// Print Menu
|
|
||||||
printMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
// Time Measurement
|
|
||||||
static uint64_t last_stats_print = 0;
|
|
||||||
|
|
||||||
// Check for serial input
|
|
||||||
if (USB_SERIAL.available() > 0)
|
|
||||||
{
|
|
||||||
String input = USB_SERIAL.readStringUntil('\n');
|
|
||||||
input.trim();
|
|
||||||
handleUserInput(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Continuously send the stored throttle value
|
|
||||||
if (throttle_now != 0)
|
|
||||||
{
|
|
||||||
dshot_result_t result = motor01.sendThrottle(throttle_now);
|
|
||||||
|
|
||||||
// Only print errors to avoid spam
|
|
||||||
if (!result.success)
|
|
||||||
{
|
|
||||||
printResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print motor stats every 2 seconds
|
|
||||||
if (esp_timer_get_time() - last_stats_print >= 2000000)
|
|
||||||
{
|
|
||||||
motor01.printDShotInfo();
|
|
||||||
|
|
||||||
// Get Motor RPM
|
|
||||||
if (IS_BIDIRECTIONAL)
|
|
||||||
{
|
|
||||||
dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
|
|
||||||
printTelemetryResult(telem_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time Stamp
|
|
||||||
last_stats_print = esp_timer_get_time();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
void handleUserInput(const String &input)
|
|
||||||
{
|
|
||||||
dshot_result_t cmd_result;
|
|
||||||
|
|
||||||
if (input == "1")
|
|
||||||
{
|
|
||||||
// Stop motor command should also reset the continuous throttle value
|
|
||||||
throttle_now = 0;
|
|
||||||
USB_SERIAL.print("Stopping motor... ");
|
|
||||||
cmd_result = commandManager.stopMotor();
|
|
||||||
printResult(cmd_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "2")
|
|
||||||
{
|
|
||||||
USB_SERIAL.print("Activating beacon 1... ");
|
|
||||||
cmd_result = commandManager.activateBeacon(1);
|
|
||||||
printResult(cmd_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "3")
|
|
||||||
{
|
|
||||||
USB_SERIAL.print("Setting normal spin direction... ");
|
|
||||||
cmd_result = commandManager.setSpinDirection(false);
|
|
||||||
printResult(cmd_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "4")
|
|
||||||
{
|
|
||||||
USB_SERIAL.print("Setting reversed spin direction... ");
|
|
||||||
cmd_result = commandManager.setSpinDirection(true);
|
|
||||||
printResult(cmd_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "5")
|
|
||||||
{
|
|
||||||
USB_SERIAL.print("Getting ESC Info... ");
|
|
||||||
cmd_result = commandManager.requestESCInfo();
|
|
||||||
printResult(cmd_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "6")
|
|
||||||
{
|
|
||||||
USB_SERIAL.print("Turning LED 0 ON... ");
|
|
||||||
cmd_result = commandManager.setLED(0, true);
|
|
||||||
printResult(cmd_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "7")
|
|
||||||
{
|
|
||||||
USB_SERIAL.print("Turning LED 0 OFF... ");
|
|
||||||
cmd_result = commandManager.setLED(0, false);
|
|
||||||
printResult(cmd_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "h" || input == "help")
|
|
||||||
{
|
|
||||||
printMenu();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "info")
|
|
||||||
{
|
|
||||||
motor01.printDShotInfo();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "rpm" && IS_BIDIRECTIONAL)
|
|
||||||
{
|
|
||||||
dshot_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
|
|
||||||
printTelemetryResult(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.startsWith("cmd "))
|
|
||||||
{
|
|
||||||
// Direct command execution: "cmd 5" sends command 5
|
|
||||||
int cmd_num = input.substring(4).toInt();
|
|
||||||
|
|
||||||
if (DShotCommandManager::isValidCommand(static_cast<dshot_commands_t>(cmd_num)))
|
|
||||||
{
|
|
||||||
USB_SERIAL.printf("Sending command %d (%s)... ", cmd_num,
|
|
||||||
DShotCommandManager::getCommandName(static_cast<dshot_commands_t>(cmd_num)));
|
|
||||||
cmd_result = commandManager.sendCommand(static_cast<dshot_commands_t>(cmd_num));
|
|
||||||
printResult(cmd_result);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
USB_SERIAL.printf("Invalid command number: %d (valid range: 0 - %d)\n", cmd_num, DSHOT_CMD_MAX);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.startsWith("throttle "))
|
|
||||||
{
|
|
||||||
// Throttle control: "throttle 1000" sets throttle to 1000
|
|
||||||
int throttle_value = input.substring(9).toInt();
|
|
||||||
|
|
||||||
if (throttle_value >= DSHOT_THROTTLE_MIN && throttle_value <= DSHOT_THROTTLE_MAX)
|
|
||||||
{
|
|
||||||
throttle_now = throttle_value;
|
|
||||||
USB_SERIAL.printf("Setting continuous throttle to %d\n", throttle_now);
|
|
||||||
|
|
||||||
// Send first throttle command and show result
|
|
||||||
dshot_result_t result = motor01.sendThrottle(throttle_now);
|
|
||||||
printResult(result);
|
|
||||||
|
|
||||||
if (result.success)
|
|
||||||
{
|
|
||||||
USB_SERIAL.println("Continuous throttle mode enabled. Send '0' or 'throttle 0' to stop.");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (throttle_value == 0)
|
|
||||||
{
|
|
||||||
throttle_now = 0;
|
|
||||||
USB_SERIAL.println("Continuous throttle stopped.");
|
|
||||||
|
|
||||||
// Send stop command
|
|
||||||
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
|
|
||||||
printResult(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
USB_SERIAL.printf("Invalid throttle value: %d (valid range: %d-%d, use 0 to stop)\n",
|
|
||||||
throttle_value, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input == "0")
|
|
||||||
{
|
|
||||||
// Quick stop
|
|
||||||
throttle_now = 0;
|
|
||||||
USB_SERIAL.print("Emergency stop... ");
|
|
||||||
dshot_result_t result = motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
|
|
||||||
printResult(result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.startsWith("repeat "))
|
|
||||||
{
|
|
||||||
// Repeat command: "repeat cmd 5 count 10" - sends command 5 ten times
|
|
||||||
String params = input.substring(7);
|
|
||||||
|
|
||||||
if (!params.startsWith("cmd "))
|
|
||||||
{
|
|
||||||
USB_SERIAL.println("Usage: repeat cmd <number> count <repeat_count>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int space_pos = params.indexOf(' ', 4);
|
|
||||||
|
|
||||||
if (space_pos <= 0 || !params.substring(space_pos + 1).startsWith("count "))
|
|
||||||
{
|
|
||||||
USB_SERIAL.println("Usage: repeat cmd <number> count <repeat_count>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cmd_num = params.substring(4, space_pos).toInt();
|
|
||||||
int repeat_count = params.substring(space_pos + 7).toInt();
|
|
||||||
|
|
||||||
if (!DShotCommandManager::isValidCommand(static_cast<dshot_commands_t>(cmd_num)) ||
|
|
||||||
repeat_count <= 0 || repeat_count > 100)
|
|
||||||
{
|
|
||||||
USB_SERIAL.println("Invalid command or repeat count (1-100)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
USB_SERIAL.printf("Sending command %d (%s) %d times... ", cmd_num,
|
|
||||||
DShotCommandManager::getCommandName(static_cast<dshot_commands_t>(cmd_num)),
|
|
||||||
repeat_count);
|
|
||||||
cmd_result = commandManager.sendCommand(static_cast<dshot_commands_t>(cmd_num), repeat_count);
|
|
||||||
printResult(cmd_result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unknown command
|
|
||||||
USB_SERIAL.printf("Unknown command: '%s'. Type 'h' or 'help' for help.\n", input.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
void printResult(const dshot_result_t &result)
|
|
||||||
{
|
|
||||||
if (result.success)
|
|
||||||
{
|
|
||||||
USB_SERIAL.printf("SUCCESS\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
USB_SERIAL.printf("FAILED - %s \n", result.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
void printSystemStatus()
|
|
||||||
{
|
|
||||||
USB_SERIAL.println("\n=== System Status ===");
|
|
||||||
USB_SERIAL.printf("Current throttle: %u\n", throttle_now);
|
|
||||||
USB_SERIAL.printf("Continuous mode: %s\n", throttle_now > 0 ? "ACTIVE" : "INACTIVE");
|
|
||||||
USB_SERIAL.printf("Free heap: %u bytes\n", ESP.getFreeHeap());
|
|
||||||
USB_SERIAL.printf("Uptime: %lu seconds\n", millis() / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
void printMenu()
|
|
||||||
{
|
|
||||||
USB_SERIAL.println("**********************************************");
|
|
||||||
USB_SERIAL.println(" DShot Command Manager Menu ");
|
|
||||||
USB_SERIAL.println("**********************************************");
|
|
||||||
USB_SERIAL.println(" 1 - Stop Motor");
|
|
||||||
USB_SERIAL.println(" 2 - Activate Beacon 1");
|
|
||||||
USB_SERIAL.println(" 3 - Set Normal Spin");
|
|
||||||
USB_SERIAL.println(" 4 - Set Reversed Spin");
|
|
||||||
USB_SERIAL.println(" 5 - Get ESC Info");
|
|
||||||
USB_SERIAL.println(" 6 - Turn LED 0 ON");
|
|
||||||
USB_SERIAL.println(" 7 - Turn LED 0 OFF");
|
|
||||||
USB_SERIAL.println(" 0 - Emergency Stop");
|
|
||||||
USB_SERIAL.println("**********************************************");
|
|
||||||
USB_SERIAL.println(" cmd <number> - Send Command (0 - 47)");
|
|
||||||
USB_SERIAL.println(" throttle <value> - Set throttle (48 - 2047)");
|
|
||||||
USB_SERIAL.println("**********************************************");
|
|
||||||
USB_SERIAL.println(" info - Show DShot signal info");
|
|
||||||
USB_SERIAL.println(" status - Show system status");
|
|
||||||
if (IS_BIDIRECTIONAL)
|
|
||||||
{
|
|
||||||
USB_SERIAL.println(" rpm - Get telemetry data");
|
|
||||||
}
|
|
||||||
USB_SERIAL.println(" h / help - Show this Menu");
|
|
||||||
USB_SERIAL.println("**********************************************");
|
|
||||||
USB_SERIAL.println("EXAMPLE INPUT:");
|
|
||||||
USB_SERIAL.println(" cmd 5 - Get ESC Info");
|
|
||||||
USB_SERIAL.println(" throttle 1000 - Set throttle to 1000");
|
|
||||||
USB_SERIAL.println("**********************************************");
|
|
||||||
}
|
|
||||||
|
|
@ -14,20 +14,22 @@ static constexpr auto &USB_SERIAL = Serial0;
|
||||||
static constexpr auto USB_SERIAL_BAUD = 115200;
|
static constexpr auto USB_SERIAL_BAUD = 115200;
|
||||||
|
|
||||||
// Motor configuration - Pin number or GPIO_PIN
|
// Motor configuration - Pin number or GPIO_PIN
|
||||||
// static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_17;
|
static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_27;
|
||||||
static constexpr auto MOTOR01_PIN = 17;
|
// static constexpr auto MOTOR01_PIN = 17;
|
||||||
|
|
||||||
// Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200)
|
// Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200)
|
||||||
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
||||||
|
|
||||||
// BiDirectional DShot Support (default: false)
|
// BiDirectional DShot Support (default: false)
|
||||||
static constexpr auto IS_BIDIRECTIONAL = false;
|
// Note: Bidirectional DShot is currently not officially supported
|
||||||
|
// due to instability and external hardware requirements.
|
||||||
|
static constexpr auto IS_BIDIRECTIONAL = false;
|
||||||
|
|
||||||
// Motor magnet count for RPM calculation
|
// Motor magnet count for RPM calculation
|
||||||
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
||||||
// Creates the motor instance
|
// Creates the motor instance
|
||||||
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL);
|
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, MOTOR01_MAGNET_COUNT);
|
||||||
|
|
||||||
//
|
//
|
||||||
void setup()
|
void setup()
|
||||||
|
|
@ -50,6 +52,8 @@ void loop()
|
||||||
{
|
{
|
||||||
// Safety first
|
// Safety first
|
||||||
static uint16_t throttle = DSHOT_CMD_MOTOR_STOP;
|
static uint16_t throttle = DSHOT_CMD_MOTOR_STOP;
|
||||||
|
|
||||||
|
// Initialize the esc with "0"
|
||||||
static bool continuous_throttle = true;
|
static bool continuous_throttle = true;
|
||||||
|
|
||||||
// Time Measurement
|
// Time Measurement
|
||||||
|
|
@ -83,7 +87,7 @@ void loop()
|
||||||
// Get Motor RPM if bidirectional
|
// Get Motor RPM if bidirectional
|
||||||
if (IS_BIDIRECTIONAL)
|
if (IS_BIDIRECTIONAL)
|
||||||
{
|
{
|
||||||
dshot_result_t telem_result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
|
dshot_result_t telem_result = motor01.getTelemetry();
|
||||||
printDShotResult(telem_result);
|
printDShotResult(telem_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,7 +136,7 @@ void handleSerialInput(const String &input, uint16_t &throttle, bool &continuous
|
||||||
}
|
}
|
||||||
else if (input == "rpm" && IS_BIDIRECTIONAL)
|
else if (input == "rpm" && IS_BIDIRECTIONAL)
|
||||||
{
|
{
|
||||||
dshot_result_t result = motor01.getTelemetry(MOTOR01_MAGNET_COUNT);
|
dshot_result_t result = motor01.getTelemetry();
|
||||||
printDShotResult(result);
|
printDShotResult(result);
|
||||||
}
|
}
|
||||||
else if (input.startsWith("cmd "))
|
else if (input.startsWith("cmd "))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* @file throttle_percent.ino
|
||||||
|
* @brief Demo sketch for DShotRMT library using percentage throttle.
|
||||||
|
* @author Wastl Kraus
|
||||||
|
* @date 2025-09-20
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <DShotRMT.h>
|
||||||
|
|
||||||
|
// USB serial port settings
|
||||||
|
static constexpr auto &USB_SERIAL = Serial0;
|
||||||
|
static constexpr auto USB_SERIAL_BAUD = 115200;
|
||||||
|
|
||||||
|
// Motor configuration - Pin number or GPIO_PIN
|
||||||
|
static constexpr gpio_num_t MOTOR01_PIN = GPIO_NUM_27;
|
||||||
|
|
||||||
|
// Supported: DSHOT150, DSHOT300, DSHOT600, (DSHOT1200)
|
||||||
|
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
||||||
|
|
||||||
|
// BiDirectional DShot Support (default: false)
|
||||||
|
// Note: Bidirectional DShot is currently not officially supported
|
||||||
|
// due to instability and external hardware requirements.
|
||||||
|
static constexpr auto IS_BIDIRECTIONAL = false;
|
||||||
|
|
||||||
|
// Motor magnet count for RPM calculation
|
||||||
|
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
||||||
|
// Creates the motor instance
|
||||||
|
DShotRMT motor01(MOTOR01_PIN, DSHOT_MODE, IS_BIDIRECTIONAL, MOTOR01_MAGNET_COUNT);
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
void handleSerialInput(const String &input);
|
||||||
|
void printMenu();
|
||||||
|
|
||||||
|
//
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// Starts the USB Serial Port
|
||||||
|
USB_SERIAL.begin(USB_SERIAL_BAUD);
|
||||||
|
|
||||||
|
// Initialize DShot Signal
|
||||||
|
motor01.begin();
|
||||||
|
|
||||||
|
// Print CPU Info
|
||||||
|
motor01.printCpuInfo();
|
||||||
|
|
||||||
|
//
|
||||||
|
printMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// Handle serial input
|
||||||
|
if (USB_SERIAL.available() > 0)
|
||||||
|
{
|
||||||
|
String input = USB_SERIAL.readStringUntil('\n');
|
||||||
|
input.trim();
|
||||||
|
|
||||||
|
if (input.length() > 0)
|
||||||
|
{
|
||||||
|
handleSerialInput(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void printMenu()
|
||||||
|
{
|
||||||
|
USB_SERIAL.println(" ");
|
||||||
|
USB_SERIAL.println("*******************************************");
|
||||||
|
USB_SERIAL.println(" DShotRMT Percent Demo ");
|
||||||
|
USB_SERIAL.println("*******************************************");
|
||||||
|
USB_SERIAL.println(" <value> - Set throttle (0 - 100)");
|
||||||
|
USB_SERIAL.println(" 0 - Stop motor");
|
||||||
|
USB_SERIAL.println("*******************************************");
|
||||||
|
USB_SERIAL.println(" cmd <number> - Send DShot command (0 - 47)");
|
||||||
|
USB_SERIAL.println(" info - Show motor info");
|
||||||
|
if (IS_BIDIRECTIONAL)
|
||||||
|
{
|
||||||
|
USB_SERIAL.println(" rpm - Get telemetry data");
|
||||||
|
}
|
||||||
|
USB_SERIAL.println("*******************************************");
|
||||||
|
USB_SERIAL.println(" h / help - Show this Menu");
|
||||||
|
USB_SERIAL.println("*******************************************");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
void handleSerialInput(const String &input)
|
||||||
|
{
|
||||||
|
if (input == "0")
|
||||||
|
{
|
||||||
|
// Stop motor
|
||||||
|
dshot_result_t result = motor01.sendThrottlePercent(0.0f);
|
||||||
|
printDShotResult(result);
|
||||||
|
}
|
||||||
|
else if (input == "info")
|
||||||
|
{
|
||||||
|
motor01.printDShotInfo();
|
||||||
|
}
|
||||||
|
else if (input == "rpm" && IS_BIDIRECTIONAL)
|
||||||
|
{
|
||||||
|
dshot_result_t result = motor01.getTelemetry();
|
||||||
|
printDShotResult(result);
|
||||||
|
}
|
||||||
|
else if (input.startsWith("cmd "))
|
||||||
|
{
|
||||||
|
// Send DShot command
|
||||||
|
int cmd_num = input.substring(4).toInt();
|
||||||
|
|
||||||
|
if (cmd_num >= DSHOT_CMD_MOTOR_STOP && cmd_num <= DSHOT_CMD_MAX)
|
||||||
|
{
|
||||||
|
dshot_result_t result = motor01.sendCommand(cmd_num);
|
||||||
|
printDShotResult(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
USB_SERIAL.printf("Invalid command: %d (valid range: 0 - %d)\n", cmd_num, DSHOT_CMD_MAX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (input == "h" || input == "help")
|
||||||
|
{
|
||||||
|
printMenu();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Parse input throttle value as a percentage
|
||||||
|
float throttle_percent = input.toFloat();
|
||||||
|
|
||||||
|
if (throttle_percent >= 0.0f && throttle_percent <= 100.0f)
|
||||||
|
{
|
||||||
|
dshot_result_t result = motor01.sendThrottlePercent(throttle_percent);
|
||||||
|
printDShotResult(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
USB_SERIAL.println(" ");
|
||||||
|
USB_SERIAL.printf("Invalid input: '%s'\n", input.c_str());
|
||||||
|
USB_SERIAL.printf("Valid throttle range: 0.0 - 100.0\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,10 +6,21 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/******************************************************************
|
||||||
|
* SECURITY WARNING
|
||||||
|
* This example provides a web interface to control a motor
|
||||||
|
* without any authentication. It is intended for use on a
|
||||||
|
* trusted local network only.
|
||||||
|
*
|
||||||
|
* DO NOT EXPOSE THIS DEVICE DIRECTLY TO THE INTERNET.
|
||||||
|
******************************************************************/
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <Update.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
#include <DShotRMT.h>
|
#include <DShotRMT.h>
|
||||||
|
#include <ota_update.h>
|
||||||
#include <web_content.h>
|
#include <web_content.h>
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
|
|
@ -34,7 +45,7 @@ static constexpr auto MOTOR01_PIN = 17;
|
||||||
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
||||||
|
|
||||||
// BiDirectional DShot Support (default: false)
|
// BiDirectional DShot Support (default: false)
|
||||||
static constexpr auto IS_BIDIRECTIONAL = false;
|
static constexpr auto IS_BIDIRECTIONAL = false; // Note: Bidirectional DShot is currently not officially supported due to instability and external hardware requirements.
|
||||||
|
|
||||||
// Motor magnet count for RPM calculation
|
// Motor magnet count for RPM calculation
|
||||||
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
@ -62,6 +73,8 @@ void handleWebSocketMessage(void *arg, uint8_t *data, size_t len);
|
||||||
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
|
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
|
||||||
bool connectToWiFi();
|
bool connectToWiFi();
|
||||||
void printWiFiStatus();
|
void printWiFiStatus();
|
||||||
|
void setupOTA();
|
||||||
|
void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||||
|
|
||||||
//
|
//
|
||||||
void setup()
|
void setup()
|
||||||
|
|
@ -79,15 +92,18 @@ void setup()
|
||||||
|
|
||||||
if (wifi_connected)
|
if (wifi_connected)
|
||||||
{
|
{
|
||||||
|
// Setup OTA first
|
||||||
|
setupOTA();
|
||||||
|
|
||||||
// Init WebSockets and Webserver
|
// Init WebSockets and Webserver
|
||||||
USB_SERIAL.println("\nStarting Webserver...");
|
USB_SERIAL.println("\nStarting Webserver...");
|
||||||
|
|
||||||
ws.onEvent(onWsEvent);
|
ws.onEvent(onWsEvent);
|
||||||
server.addHandler(&ws);
|
server.addHandler(&ws);
|
||||||
|
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
{ request->send_P(200, "text/html", index_html); });
|
{ request->send_P(200, "text/html", index_html); });
|
||||||
|
|
||||||
server.begin();
|
server.begin();
|
||||||
USB_SERIAL.println("HTTP server started.");
|
USB_SERIAL.println("HTTP server started.");
|
||||||
|
|
||||||
|
|
@ -96,7 +112,7 @@ void setup()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
USB_SERIAL.println("\n*** WARNING: WiFi connection failed! ***");
|
USB_SERIAL.println("\n*** WARNING: WiFi connection failed! ***");
|
||||||
USB_SERIAL.println("*** Web interface not available ***");
|
USB_SERIAL.println("*** Web interface and OTA not available ***");
|
||||||
USB_SERIAL.println("*** Only serial control available ***");
|
USB_SERIAL.println("*** Only serial control available ***");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,6 +231,87 @@ void loop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup OTA Update functionality
|
||||||
|
void setupOTA()
|
||||||
|
{
|
||||||
|
USB_SERIAL.println("Setting up OTA Update...");
|
||||||
|
|
||||||
|
// Serve OTA update page
|
||||||
|
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||||
|
{ request->send_P(200, "text/html", ota_html); });
|
||||||
|
|
||||||
|
// Handle OTA update upload
|
||||||
|
server.on("/update", HTTP_POST, [](AsyncWebServerRequest *request)
|
||||||
|
{
|
||||||
|
bool shouldReboot = !Update.hasError();
|
||||||
|
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain",
|
||||||
|
shouldReboot ? "OK" : "FAIL");
|
||||||
|
|
||||||
|
response->addHeader("Connection", "close");
|
||||||
|
request->send(response);
|
||||||
|
|
||||||
|
if (shouldReboot) {
|
||||||
|
USB_SERIAL.println("OTA Update successful! Rebooting...");
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
} else {
|
||||||
|
USB_SERIAL.println("OTA Update failed!");
|
||||||
|
} }, handleOTAUpload);
|
||||||
|
|
||||||
|
USB_SERIAL.println("OTA Update ready at: /update");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle OTA upload process
|
||||||
|
void handleOTAUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)
|
||||||
|
{
|
||||||
|
static unsigned long ota_progress_millis = 0;
|
||||||
|
|
||||||
|
if (!index)
|
||||||
|
{
|
||||||
|
// Safety: Ensure motor is stopped during update
|
||||||
|
motor01.sendCommand(DSHOT_CMD_MOTOR_STOP);
|
||||||
|
setArmingStatus(false);
|
||||||
|
|
||||||
|
USB_SERIAL.printf("OTA Update Start: %s\n", filename.c_str());
|
||||||
|
|
||||||
|
if (!Update.begin(UPDATE_SIZE_UNKNOWN))
|
||||||
|
{
|
||||||
|
Update.printError(USB_SERIAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
{
|
||||||
|
if (Update.write(data, len) != len)
|
||||||
|
{
|
||||||
|
Update.printError(USB_SERIAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print progress every 2 seconds to avoid spam
|
||||||
|
if (millis() - ota_progress_millis > 2000)
|
||||||
|
{
|
||||||
|
size_t progress = index + len;
|
||||||
|
USB_SERIAL.printf("OTA Progress: %zu bytes\n", progress);
|
||||||
|
ota_progress_millis = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (final)
|
||||||
|
{
|
||||||
|
if (Update.end(true))
|
||||||
|
{
|
||||||
|
USB_SERIAL.printf("OTA Update Success: %zu bytes\n", index + len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Update.printError(USB_SERIAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to WiFi network
|
// Connect to WiFi network
|
||||||
bool connectToWiFi()
|
bool connectToWiFi()
|
||||||
{
|
{
|
||||||
|
|
@ -262,6 +359,7 @@ void printWiFiStatus()
|
||||||
USB_SERIAL.printf("MAC Address: %s\n", WiFi.macAddress().c_str());
|
USB_SERIAL.printf("MAC Address: %s\n", WiFi.macAddress().c_str());
|
||||||
USB_SERIAL.println("***********************************************");
|
USB_SERIAL.println("***********************************************");
|
||||||
USB_SERIAL.printf("Web Interface: http://%s\n", WiFi.localIP().toString().c_str());
|
USB_SERIAL.printf("Web Interface: http://%s\n", WiFi.localIP().toString().c_str());
|
||||||
|
USB_SERIAL.printf("OTA Update: http://%s/update\n", WiFi.localIP().toString().c_str());
|
||||||
USB_SERIAL.println("***********************************************");
|
USB_SERIAL.println("***********************************************");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -305,10 +403,12 @@ void printMenu()
|
||||||
if (wifi_connected)
|
if (wifi_connected)
|
||||||
{
|
{
|
||||||
USB_SERIAL.printf(" Web Interface: http://%s \n", WiFi.localIP().toString().c_str());
|
USB_SERIAL.printf(" Web Interface: http://%s \n", WiFi.localIP().toString().c_str());
|
||||||
|
USB_SERIAL.printf(" OTA Update: http://%s/update \n", WiFi.localIP().toString().c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
USB_SERIAL.println(" Web Interface: NOT AVAILABLE ");
|
USB_SERIAL.println(" Web Interface: NOT AVAILABLE ");
|
||||||
|
USB_SERIAL.println(" OTA Update: NOT AVAILABLE ");
|
||||||
}
|
}
|
||||||
|
|
||||||
USB_SERIAL.println("***********************************************");
|
USB_SERIAL.println("***********************************************");
|
||||||
|
|
@ -321,6 +421,7 @@ void printMenu()
|
||||||
USB_SERIAL.println(" info - Show motor info");
|
USB_SERIAL.println(" info - Show motor info");
|
||||||
USB_SERIAL.println(" wifi - Show WiFi status");
|
USB_SERIAL.println(" wifi - Show WiFi status");
|
||||||
USB_SERIAL.println(" reconnect - Reconnect to WiFi");
|
USB_SERIAL.println(" reconnect - Reconnect to WiFi");
|
||||||
|
USB_SERIAL.println(" ota - Show OTA info");
|
||||||
if (IS_BIDIRECTIONAL)
|
if (IS_BIDIRECTIONAL)
|
||||||
{
|
{
|
||||||
USB_SERIAL.println(" rpm - Get telemetry data");
|
USB_SERIAL.println(" rpm - Get telemetry data");
|
||||||
|
|
@ -362,6 +463,24 @@ void handleSerialInput(const String &input)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (input == "ota")
|
||||||
|
{
|
||||||
|
if (wifi_connected)
|
||||||
|
{
|
||||||
|
USB_SERIAL.println(" ");
|
||||||
|
USB_SERIAL.println("=== OTA UPDATE INFO ===");
|
||||||
|
USB_SERIAL.printf("OTA Update URL: http://%s/update\n", WiFi.localIP().toString().c_str());
|
||||||
|
USB_SERIAL.printf("Free Sketch Space: %u bytes\n", ESP.getFreeSketchSpace());
|
||||||
|
USB_SERIAL.printf("Sketch Size: %u bytes\n", ESP.getSketchSize());
|
||||||
|
USB_SERIAL.println("========================");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
USB_SERIAL.println("OTA Update not available - WiFi not connected!");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (input == "reconnect")
|
if (input == "reconnect")
|
||||||
{
|
{
|
||||||
USB_SERIAL.println("Reconnecting to WiFi...");
|
USB_SERIAL.println("Reconnecting to WiFi...");
|
||||||
|
|
@ -431,6 +550,7 @@ void handleSerialInput(const String &input)
|
||||||
if (wifi_connected)
|
if (wifi_connected)
|
||||||
{
|
{
|
||||||
USB_SERIAL.printf("IP Address: %s\n", WiFi.localIP().toString().c_str());
|
USB_SERIAL.printf("IP Address: %s\n", WiFi.localIP().toString().c_str());
|
||||||
|
USB_SERIAL.printf("OTA URL: http://%s/update\n", WiFi.localIP().toString().c_str());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,15 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/******************************************************************
|
||||||
|
* SECURITY WARNING
|
||||||
|
* This example provides a web interface to control a motor
|
||||||
|
* without any authentication. It is intended for use on a
|
||||||
|
* trusted local network only.
|
||||||
|
*
|
||||||
|
* DO NOT EXPOSE THIS DEVICE DIRECTLY TO THE INTERNET.
|
||||||
|
******************************************************************/
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
|
|
@ -35,7 +44,7 @@ static constexpr auto MOTOR01_PIN = 17;
|
||||||
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
static constexpr dshot_mode_t DSHOT_MODE = DSHOT300;
|
||||||
|
|
||||||
// BiDirectional DShot Support (default: false)
|
// BiDirectional DShot Support (default: false)
|
||||||
static constexpr auto IS_BIDIRECTIONAL = false;
|
static constexpr auto IS_BIDIRECTIONAL = false; // Note: Bidirectional DShot is currently not officially supported due to instability and external hardware requirements.
|
||||||
|
|
||||||
// Motor magnet count for RPM calculation
|
// Motor magnet count for RPM calculation
|
||||||
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
static constexpr auto MOTOR01_MAGNET_COUNT = 14;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
name=DShotRMT
|
name=DShotRMT
|
||||||
version=0.8.0
|
version=0.8.3
|
||||||
author=Wastl Kraus <wir-sind-die-matrix.de>
|
author=Wastl Kraus <wir-sind-die-matrix.de>
|
||||||
maintainer=Wastl Kraus <wir-sind-die-matrix.de>
|
maintainer=Wastl Kraus <wir-sind-die-matrix.de>
|
||||||
license=MIT
|
license=MIT
|
||||||
sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S.
|
sentence=DShotRMT Library supporting all DShot Types and speeds. Tested with BlHeli_S.
|
||||||
paragraph=This library can control a BlHeli_S by using encoded DShot commands. Features bidirectional DShot support for RPM telemetry.
|
paragraph=This library can control a BlHeli_S by using encoded DShot commands.
|
||||||
category=Signal Input/Output
|
category=Signal Input/Output
|
||||||
url=https://github.com/derdoktor667/DShotRMT
|
url=https://github.com/derdoktor667/DShotRMT
|
||||||
architectures=esp32
|
architectures=esp32
|
||||||
provides_includes=DShotRMT.h, DShotCommandManager.h, dshot_commands.h
|
provides_includes=DShotRMT.h, dshot_commands.h, web_content.h, ota_update.h
|
||||||
depends=ArduinoJson
|
|
||||||
|
|
@ -1,347 +0,0 @@
|
||||||
/*
|
|
||||||
* DShotCommandManager.cpp
|
|
||||||
* Advanced DShot command management for DShotRMT library
|
|
||||||
* Author: Wastl Kraus
|
|
||||||
* Date: 2025-09-04
|
|
||||||
* License: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <DShotCommandManager.h>
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
DShotCommandManager::DShotCommandManager(DShotRMT &dshot_instance)
|
|
||||||
: _dshot(dshot_instance),
|
|
||||||
_total_commands_sent(0),
|
|
||||||
_failed_commands(0),
|
|
||||||
_last_command_timestamp(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init command manager
|
|
||||||
dshot_result_t DShotCommandManager::begin()
|
|
||||||
{
|
|
||||||
dshot_result_t result;
|
|
||||||
result.success = true;
|
|
||||||
result.msg = "Success";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- BASIC COMMAND METHODS ---
|
|
||||||
dshot_result_t DShotCommandManager::sendCommand(dshot_commands_t command, uint16_t repeat_count)
|
|
||||||
{
|
|
||||||
return sendCommandWithDelay(command, repeat_count, DEFAULT_COMMAND_DELAY_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
dshot_result_t DShotCommandManager::sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms)
|
|
||||||
{
|
|
||||||
dshot_result_t result = {false, "Unknown error"};
|
|
||||||
|
|
||||||
if (!isValidCommand(command))
|
|
||||||
{
|
|
||||||
result.msg = "Invalid command";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool all_successful = true;
|
|
||||||
|
|
||||||
// Send command multiple times with delay
|
|
||||||
for (uint16_t i = 0; i < repeat_count; i++)
|
|
||||||
{
|
|
||||||
dshot_result_t single_result = _executeCommand(command);
|
|
||||||
|
|
||||||
if (!single_result.success)
|
|
||||||
{
|
|
||||||
all_successful = false;
|
|
||||||
result.msg = single_result.msg;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add delay between repetitions (except for last repetition)
|
|
||||||
if (i < repeat_count - 1)
|
|
||||||
{
|
|
||||||
_delay_ms(delay_ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
result.success = all_successful;
|
|
||||||
|
|
||||||
if (result.success)
|
|
||||||
{
|
|
||||||
result.msg = "Success";
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- MOTOR CONTROL COMMANDS ---
|
|
||||||
dshot_result_t DShotCommandManager::stopMotor()
|
|
||||||
{
|
|
||||||
return sendCommand(DSHOT_CMD_MOTOR_STOP);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
dshot_result_t DShotCommandManager::set3DMode(bool enable)
|
|
||||||
{
|
|
||||||
dshot_commands_t command = enable ? DSHOT_CMD_3D_MODE_ON : DSHOT_CMD_3D_MODE_OFF;
|
|
||||||
return sendCommandWithDelay(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
dshot_result_t DShotCommandManager::setSpinDirection(bool reversed)
|
|
||||||
{
|
|
||||||
dshot_commands_t command = reversed ? DSHOT_CMD_SPIN_DIRECTION_REVERSED : DSHOT_CMD_SPIN_DIRECTION_NORMAL;
|
|
||||||
return sendCommandWithDelay(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
dshot_result_t DShotCommandManager::saveSettings()
|
|
||||||
{
|
|
||||||
return sendCommandWithDelay(DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_MS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- TELEMETRY COMMANDS ---
|
|
||||||
dshot_result_t DShotCommandManager::setExtendedTelemetry(bool enable)
|
|
||||||
{
|
|
||||||
dshot_commands_t command = enable ? DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE : DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE;
|
|
||||||
return sendCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
dshot_result_t DShotCommandManager::requestESCInfo()
|
|
||||||
{
|
|
||||||
return sendCommand(DSHOT_CMD_ESC_INFO);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- LED CONTROL COMMANDS ---
|
|
||||||
dshot_result_t DShotCommandManager::setLED(uint8_t led_number, bool state)
|
|
||||||
{
|
|
||||||
if (led_number > 3)
|
|
||||||
{
|
|
||||||
dshot_result_t result = {false, "Invalid LED number (0-3)"};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
dshot_commands_t command;
|
|
||||||
if (state)
|
|
||||||
{
|
|
||||||
// LED ON commands
|
|
||||||
switch (led_number)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
command = DSHOT_CMD_LED0_ON;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
command = DSHOT_CMD_LED1_ON;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
command = DSHOT_CMD_LED2_ON;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
command = DSHOT_CMD_LED3_ON;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// LED OFF commands
|
|
||||||
switch (led_number)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
command = DSHOT_CMD_LED0_OFF;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
command = DSHOT_CMD_LED1_OFF;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
command = DSHOT_CMD_LED2_OFF;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
command = DSHOT_CMD_LED3_OFF;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sendCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- BEACON COMMANDS ---
|
|
||||||
dshot_result_t DShotCommandManager::activateBeacon(uint8_t beacon_number)
|
|
||||||
{
|
|
||||||
if (beacon_number < 1 || beacon_number > 5)
|
|
||||||
{
|
|
||||||
dshot_result_t result = {false, "Invalid beacon number (1-5)"};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
dshot_commands_t command = static_cast<dshot_commands_t>(DSHOT_CMD_BEACON1 + beacon_number - 1);
|
|
||||||
return sendCommand(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- KISS ESC SPECIFIC COMMANDS ---
|
|
||||||
dshot_result_t DShotCommandManager::setAudioStreamMode(bool enable)
|
|
||||||
{
|
|
||||||
// KISS audio stream mode is a toggle command
|
|
||||||
return sendCommand(DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
dshot_result_t DShotCommandManager::setSilentMode(bool enable)
|
|
||||||
{
|
|
||||||
// KISS silent mode is a toggle command
|
|
||||||
return sendCommand(DSHOT_CMD_SILENT_MODE_ON_OFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- SEQUENCE COMMANDS ---
|
|
||||||
dshot_result_t DShotCommandManager::executeSequence(const dshot_commandmanager_item_t *sequence, size_t sequence_length)
|
|
||||||
{
|
|
||||||
dshot_result_t result = {true, "Success"};
|
|
||||||
uint64_t total_start_time = esp_timer_get_time();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < sequence_length; i++)
|
|
||||||
{
|
|
||||||
dshot_result_t item_result = sendCommandWithDelay(
|
|
||||||
sequence[i].command,
|
|
||||||
sequence[i].repeat_count,
|
|
||||||
DEFAULT_COMMAND_DELAY_MS);
|
|
||||||
|
|
||||||
if (!item_result.success)
|
|
||||||
{
|
|
||||||
result.success = false;
|
|
||||||
result.msg = item_result.msg;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add delay after command if specified
|
|
||||||
if (sequence[i].delay_ms > 0)
|
|
||||||
{
|
|
||||||
_delay_ms(sequence[i].delay_ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t total_end_time = esp_timer_get_time();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
dshot_result_t DShotCommandManager::executeInitSequence()
|
|
||||||
{
|
|
||||||
// Basic ESC initialization sequence
|
|
||||||
dshot_commandmanager_item_t init_sequence[] = {
|
|
||||||
{DSHOT_CMD_MOTOR_STOP, 5, 100}, // Stop motor, repeat 5 times, wait 100ms
|
|
||||||
{DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE, 1, 50}, // Enable telemetry, wait 50ms
|
|
||||||
{DSHOT_CMD_ESC_INFO, 1, 100} // Request ESC info, wait 100ms
|
|
||||||
};
|
|
||||||
|
|
||||||
return executeSequence(init_sequence, sizeof(init_sequence) / sizeof(init_sequence[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
dshot_result_t DShotCommandManager::executeCalibrationSequence()
|
|
||||||
{
|
|
||||||
// Basic ESC calibration sequence
|
|
||||||
dshot_commandmanager_item_t calibration_sequence[] = {
|
|
||||||
{DSHOT_CMD_MOTOR_STOP, 10, 500}, // Ensure motor is stopped
|
|
||||||
{DSHOT_CMD_SPIN_DIRECTION_NORMAL, 10, 100}, // Set normal spin direction
|
|
||||||
{DSHOT_CMD_3D_MODE_OFF, 10, 100}, // Disable 3D mode
|
|
||||||
{DSHOT_CMD_SAVE_SETTINGS, 10, 1000}, // Save settings
|
|
||||||
{DSHOT_CMD_MOTOR_STOP, 5, 100} // Final stop
|
|
||||||
};
|
|
||||||
|
|
||||||
return executeSequence(calibration_sequence, sizeof(calibration_sequence) / sizeof(calibration_sequence[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- UTILITY METHODS ---
|
|
||||||
const char *DShotCommandManager::getCommandName(dshot_commands_t command)
|
|
||||||
{
|
|
||||||
switch (command)
|
|
||||||
{
|
|
||||||
case DSHOT_CMD_MOTOR_STOP:
|
|
||||||
return "MOTOR_STOP";
|
|
||||||
case DSHOT_CMD_BEACON1:
|
|
||||||
return "BEACON1";
|
|
||||||
case DSHOT_CMD_BEACON2:
|
|
||||||
return "BEACON2";
|
|
||||||
case DSHOT_CMD_BEACON3:
|
|
||||||
return "BEACON3";
|
|
||||||
case DSHOT_CMD_BEACON4:
|
|
||||||
return "BEACON4";
|
|
||||||
case DSHOT_CMD_BEACON5:
|
|
||||||
return "BEACON5";
|
|
||||||
case DSHOT_CMD_ESC_INFO:
|
|
||||||
return "ESC_INFO";
|
|
||||||
case DSHOT_CMD_SPIN_DIRECTION_1:
|
|
||||||
return "SPIN_DIRECTION_1";
|
|
||||||
case DSHOT_CMD_SPIN_DIRECTION_2:
|
|
||||||
return "SPIN_DIRECTION_2";
|
|
||||||
case DSHOT_CMD_3D_MODE_OFF:
|
|
||||||
return "3D_MODE_OFF";
|
|
||||||
case DSHOT_CMD_3D_MODE_ON:
|
|
||||||
return "3D_MODE_ON";
|
|
||||||
case DSHOT_CMD_SETTINGS_REQUEST:
|
|
||||||
return "SETTINGS_REQUEST";
|
|
||||||
case DSHOT_CMD_SAVE_SETTINGS:
|
|
||||||
return "SAVE_SETTINGS";
|
|
||||||
case DSHOT_CMD_EXTENDED_TELEMETRY_ENABLE:
|
|
||||||
return "EXTENDED_TELEMETRY_ENABLE";
|
|
||||||
case DSHOT_CMD_EXTENDED_TELEMETRY_DISABLE:
|
|
||||||
return "EXTENDED_TELEMETRY_DISABLE";
|
|
||||||
case DSHOT_CMD_SPIN_DIRECTION_NORMAL:
|
|
||||||
return "SPIN_DIRECTION_NORMAL";
|
|
||||||
case DSHOT_CMD_SPIN_DIRECTION_REVERSED:
|
|
||||||
return "SPIN_DIRECTION_REVERSED";
|
|
||||||
case DSHOT_CMD_LED0_ON:
|
|
||||||
return "LED0_ON";
|
|
||||||
case DSHOT_CMD_LED1_ON:
|
|
||||||
return "LED1_ON";
|
|
||||||
case DSHOT_CMD_LED2_ON:
|
|
||||||
return "LED2_ON";
|
|
||||||
case DSHOT_CMD_LED3_ON:
|
|
||||||
return "LED3_ON";
|
|
||||||
case DSHOT_CMD_LED0_OFF:
|
|
||||||
return "LED0_OFF";
|
|
||||||
case DSHOT_CMD_LED1_OFF:
|
|
||||||
return "LED1_OFF";
|
|
||||||
case DSHOT_CMD_LED2_OFF:
|
|
||||||
return "LED2_OFF";
|
|
||||||
case DSHOT_CMD_LED3_OFF:
|
|
||||||
return "LED3_OFF";
|
|
||||||
case DSHOT_CMD_AUDIO_STREAM_MODE_ON_OFF:
|
|
||||||
return "AUDIO_STREAM_MODE_ON_OFF";
|
|
||||||
case DSHOT_CMD_SILENT_MODE_ON_OFF:
|
|
||||||
return "SILENT_MODE_ON_OFF";
|
|
||||||
default:
|
|
||||||
return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
bool DShotCommandManager::isValidCommand(dshot_commands_t command)
|
|
||||||
{
|
|
||||||
return (command >= DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- PRIVATE METHODS ---
|
|
||||||
dshot_result_t DShotCommandManager::_executeCommand(dshot_commands_t command)
|
|
||||||
{
|
|
||||||
uint64_t start_time = esp_timer_get_time();
|
|
||||||
|
|
||||||
// Execute the command using the DShotRMT instance
|
|
||||||
dshot_result_t result = _dshot.sendCommand(static_cast<uint16_t>(command));
|
|
||||||
|
|
||||||
uint64_t end_time = esp_timer_get_time();
|
|
||||||
_last_command_timestamp = end_time;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
void DShotCommandManager::_delay_ms(uint32_t delay_ms)
|
|
||||||
{
|
|
||||||
if (delay_ms > 0)
|
|
||||||
{
|
|
||||||
delay(delay_ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
/*
|
|
||||||
* DShotCommandManager.h
|
|
||||||
* Advanced DShot command management for DShotRMT library
|
|
||||||
* Author: Wastl Kraus
|
|
||||||
* Date: 2025-09-04
|
|
||||||
* License: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <DShotRMT.h>
|
|
||||||
|
|
||||||
// Command item
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
dshot_commands_t command;
|
|
||||||
uint16_t repeat_count;
|
|
||||||
uint32_t delay_ms;
|
|
||||||
} dshot_commandmanager_item_t;
|
|
||||||
|
|
||||||
// Advanced DShot command manager class
|
|
||||||
class DShotCommandManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Constructor
|
|
||||||
explicit DShotCommandManager(DShotRMT &dshot_instance);
|
|
||||||
|
|
||||||
// Initialize command manager
|
|
||||||
dshot_result_t begin();
|
|
||||||
|
|
||||||
void handleMenuInput(const String &input, Stream &output = Serial);
|
|
||||||
|
|
||||||
// Send a single DShot command
|
|
||||||
dshot_result_t sendCommand(dshot_commands_t command, uint16_t repeat_count = 1);
|
|
||||||
|
|
||||||
// Send command with specified delay between repetitions
|
|
||||||
dshot_result_t sendCommandWithDelay(dshot_commands_t command, uint16_t repeat_count, uint32_t delay_ms);
|
|
||||||
|
|
||||||
// --- MOTOR CONTROL COMMANDS ---
|
|
||||||
// Stop motor (send MOTOR_STOP command)
|
|
||||||
dshot_result_t stopMotor();
|
|
||||||
|
|
||||||
// Enable/disable 3D mode
|
|
||||||
dshot_result_t set3DMode(bool enable);
|
|
||||||
|
|
||||||
// Set motor spin direction
|
|
||||||
dshot_result_t setSpinDirection(bool reversed);
|
|
||||||
|
|
||||||
// Save current settings to ESC
|
|
||||||
dshot_result_t saveSettings();
|
|
||||||
|
|
||||||
// --- TELEMETRY COMMANDS ---
|
|
||||||
// Enable/disable extended telemetry
|
|
||||||
dshot_result_t setExtendedTelemetry(bool enable);
|
|
||||||
|
|
||||||
// Request ESC information
|
|
||||||
dshot_result_t requestESCInfo();
|
|
||||||
|
|
||||||
// --- LED CONTROL COMMANDS (BLHeli32 only) ---
|
|
||||||
|
|
||||||
// Control ESC LEDs (BLHeli32 only)
|
|
||||||
dshot_result_t setLED(uint8_t led_number, bool state);
|
|
||||||
|
|
||||||
// --- BEACON COMMANDS ---
|
|
||||||
// Activate beacon (motor beeping)
|
|
||||||
dshot_result_t activateBeacon(uint8_t beacon_number);
|
|
||||||
|
|
||||||
// --- KISS ESC SPECIFIC COMMANDS ---
|
|
||||||
// Enable/disable audio stream mode (KISS ESCs)
|
|
||||||
dshot_result_t setAudioStreamMode(bool enable);
|
|
||||||
|
|
||||||
// Enable/disable silent mode (KISS ESCs)
|
|
||||||
dshot_result_t setSilentMode(bool enable);
|
|
||||||
|
|
||||||
// --- SEQUENCE COMMANDS ---
|
|
||||||
// Execute a sequence of DShot commands
|
|
||||||
dshot_result_t executeSequence(const dshot_commandmanager_item_t *sequence, size_t sequence_length);
|
|
||||||
|
|
||||||
// Execute ESC initialization sequence
|
|
||||||
dshot_result_t executeInitSequence();
|
|
||||||
|
|
||||||
// Execute ESC calibration sequence
|
|
||||||
dshot_result_t executeCalibrationSequence();
|
|
||||||
|
|
||||||
// --- UTILITY METHODS ---
|
|
||||||
// Get command name as string
|
|
||||||
static const char *getCommandName(dshot_commands_t command);
|
|
||||||
|
|
||||||
// Check if command is valid
|
|
||||||
static bool isValidCommand(dshot_commands_t command);
|
|
||||||
|
|
||||||
// --- GETTERS ---
|
|
||||||
// Get total number of commands sent
|
|
||||||
uint32_t getTotalCommandCount() const { return _total_commands_sent; }
|
|
||||||
|
|
||||||
// Get number of failed commands
|
|
||||||
uint32_t getFailedCommandCount() const { return _failed_commands; }
|
|
||||||
|
|
||||||
// Get reference to underlying DShotRMT instance
|
|
||||||
DShotRMT &getDShotRMT() { return _dshot; }
|
|
||||||
const DShotRMT &getDShotRMT() const { return _dshot; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
// --- PRIVATE MEMBERS ---
|
|
||||||
DShotRMT &_dshot; // Reference to DShotRMT instance
|
|
||||||
uint32_t _total_commands_sent; // Total commands sent counter
|
|
||||||
uint32_t _failed_commands; // Failed commands counter
|
|
||||||
uint64_t _last_command_timestamp; // Timestamp of last command
|
|
||||||
|
|
||||||
// --- PRIVATE METHODS ---
|
|
||||||
// Execute single command with timing
|
|
||||||
dshot_result_t _executeCommand(dshot_commands_t command);
|
|
||||||
|
|
||||||
// Wait for specified delay
|
|
||||||
void _delay_ms(uint32_t delay_ms);
|
|
||||||
|
|
||||||
// --- CONSTANTS ---
|
|
||||||
static constexpr uint32_t DEFAULT_COMMAND_DELAY_MS = 10;
|
|
||||||
static constexpr uint16_t DEFAULT_REPEAT_COUNT = 1;
|
|
||||||
static constexpr uint16_t SETTINGS_COMMAND_REPEATS = 10; // Settings commands need 10 repeats
|
|
||||||
static constexpr uint32_t SETTINGS_COMMAND_DELAY_MS = 5;
|
|
||||||
};
|
|
||||||
464
src/DShotRMT.cpp
464
src/DShotRMT.cpp
|
|
@ -6,17 +6,16 @@
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "DShotRMT.h"
|
#include <DShotRMT.h>
|
||||||
|
|
||||||
// Static Data & Helper Functions
|
|
||||||
// Timing parameters for each DShot mode
|
// Timing parameters for each DShot mode
|
||||||
// Format: {bit_length_us, t1h_length_us}
|
// Format: {bit_length_us, t1h_length_us}
|
||||||
static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = {
|
static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = {
|
||||||
{0.00, 0.00},
|
{0.00, 0.00}, // DSHOT_OFF
|
||||||
{6.67, 5.00},
|
{6.67, 5.00}, // DSHOT150
|
||||||
{3.33, 2.50},
|
{3.33, 2.50}, // DSHOT300
|
||||||
{1.67, 1.25},
|
{1.67, 1.25}, // DSHOT600
|
||||||
{0.83, 0.67}};
|
{0.83, 0.67}}; // DSHOT1200
|
||||||
|
|
||||||
// Helper function to print DShot results and telemetry
|
// Helper function to print DShot results and telemetry
|
||||||
void printDShotResult(dshot_result_t &result, Stream &output)
|
void printDShotResult(dshot_result_t &result, Stream &output)
|
||||||
|
|
@ -34,20 +33,22 @@ void printDShotResult(dshot_result_t &result, Stream &output)
|
||||||
|
|
||||||
// Constructors & Destructor
|
// Constructors & Destructor
|
||||||
// Constructor with GPIO number
|
// Constructor with GPIO number
|
||||||
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
|
DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count)
|
||||||
: _gpio(gpio),
|
: _gpio(gpio),
|
||||||
_mode(mode),
|
_mode(mode),
|
||||||
_is_bidirectional(is_bidirectional),
|
_is_bidirectional(is_bidirectional),
|
||||||
|
_motor_magnet_count(magnet_count),
|
||||||
_dshot_timing(DSHOT_TIMING_US[mode]),
|
_dshot_timing(DSHOT_TIMING_US[mode]),
|
||||||
_frame_timer_us(0),
|
_frame_timer_us(0),
|
||||||
_rmt_ticks{0},
|
_rmt_ticks{0},
|
||||||
_last_throttle(DSHOT_CMD_MOTOR_STOP),
|
_last_throttle(DSHOT_CMD_MOTOR_STOP),
|
||||||
_last_transmission_time_us(0),
|
_last_transmission_time_us(0),
|
||||||
|
_last_command_timestamp(0),
|
||||||
_parsed_packet(0),
|
_parsed_packet(0),
|
||||||
_packet{0},
|
_packet{0},
|
||||||
_bitPositions{0},
|
_bitPositions{0},
|
||||||
_level0(_is_bidirectional ? 0 : 1),
|
_level0(1), // DShot standard: signal is idle-low, so pulses start by going HIGH
|
||||||
_level1(_is_bidirectional ? 1 : 0),
|
_level1(0), // DShot standard: signal returns to LOW after the high pulse
|
||||||
_rmt_tx_channel(nullptr),
|
_rmt_tx_channel(nullptr),
|
||||||
_rmt_rx_channel(nullptr),
|
_rmt_rx_channel(nullptr),
|
||||||
_dshot_encoder(nullptr),
|
_dshot_encoder(nullptr),
|
||||||
|
|
@ -59,13 +60,17 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional)
|
||||||
_last_erpm_atomic(0),
|
_last_erpm_atomic(0),
|
||||||
_telemetry_ready_flag_atomic(false)
|
_telemetry_ready_flag_atomic(false)
|
||||||
{
|
{
|
||||||
// Configure RMT ticks for DShot timings
|
// Pre-calculate timing and bit positions for performance
|
||||||
_preCalculateRMTTicks();
|
_preCalculateRMTTicks();
|
||||||
|
_preCalculateBitPositions();
|
||||||
|
|
||||||
|
// Activate internal pullup resistor
|
||||||
|
// gpio_set_pull_mode(_gpio, GPIO_PULLUP_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor using pin number
|
// Constructor using pin number
|
||||||
DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional)
|
DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count)
|
||||||
: DShotRMT(static_cast<gpio_num_t>(pin_nr), mode, is_bidirectional)
|
: DShotRMT(static_cast<gpio_num_t>(pin_nr), mode, is_bidirectional, magnet_count)
|
||||||
{
|
{
|
||||||
// Delegates to primary constructor with type cast
|
// Delegates to primary constructor with type cast
|
||||||
}
|
}
|
||||||
|
|
@ -78,8 +83,8 @@ DShotRMT::~DShotRMT()
|
||||||
{
|
{
|
||||||
if (rmt_disable(_rmt_tx_channel) == DSHOT_OK)
|
if (rmt_disable(_rmt_tx_channel) == DSHOT_OK)
|
||||||
{
|
{
|
||||||
rmt_del_channel(_rmt_tx_channel);
|
rmt_del_channel(_rmt_tx_channel);
|
||||||
_rmt_tx_channel = nullptr;
|
_rmt_tx_channel = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,8 +93,8 @@ DShotRMT::~DShotRMT()
|
||||||
{
|
{
|
||||||
if (rmt_disable(_rmt_rx_channel) == DSHOT_OK)
|
if (rmt_disable(_rmt_rx_channel) == DSHOT_OK)
|
||||||
{
|
{
|
||||||
rmt_del_channel(_rmt_rx_channel);
|
rmt_del_channel(_rmt_rx_channel);
|
||||||
_rmt_rx_channel = nullptr;
|
_rmt_rx_channel = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,34 +106,42 @@ DShotRMT::~DShotRMT()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Public Core Functions
|
// Public Core Functions
|
||||||
// Initialize DShotRMT
|
// Initialize DShotRMT
|
||||||
dshot_result_t DShotRMT::begin()
|
dshot_result_t DShotRMT::begin()
|
||||||
{
|
{
|
||||||
// Init RX channel first (for bidirectional mode)
|
|
||||||
if (_is_bidirectional)
|
|
||||||
{
|
|
||||||
if (!_initRXChannel().success)
|
|
||||||
{
|
|
||||||
return {false, RX_INIT_FAILED};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init TX channel
|
|
||||||
if (!_initTXChannel().success)
|
if (!_initTXChannel().success)
|
||||||
{
|
{
|
||||||
return {false, TX_INIT_FAILED};
|
return {false, TX_INIT_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init DShot encoder
|
if (_is_bidirectional)
|
||||||
if (!_initDShotEncoder().success)
|
|
||||||
{
|
{
|
||||||
return {false, ENCODER_INIT_FAILED};
|
if (!_initRXChannel().success)
|
||||||
|
{
|
||||||
|
// Cleanup previously allocated TX channel on failure
|
||||||
|
rmt_disable(_rmt_tx_channel);
|
||||||
|
rmt_del_channel(_rmt_tx_channel);
|
||||||
|
_rmt_tx_channel = nullptr;
|
||||||
|
return {false, RX_INIT_FAILED};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bit positions precalculation
|
if (!_initDShotEncoder().success)
|
||||||
_preCalculateBitPositions();
|
{
|
||||||
|
// Cleanup previously allocated channels on failure
|
||||||
|
rmt_disable(_rmt_tx_channel);
|
||||||
|
rmt_del_channel(_rmt_tx_channel);
|
||||||
|
_rmt_tx_channel = nullptr;
|
||||||
|
|
||||||
|
if (_rmt_rx_channel) {
|
||||||
|
rmt_disable(_rmt_rx_channel);
|
||||||
|
rmt_del_channel(_rmt_rx_channel);
|
||||||
|
_rmt_rx_channel = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {false, ENCODER_INIT_FAILED};
|
||||||
|
}
|
||||||
|
|
||||||
return {true, INIT_SUCCESS};
|
return {true, INIT_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
@ -136,62 +149,112 @@ dshot_result_t DShotRMT::begin()
|
||||||
// Send throttle value
|
// Send throttle value
|
||||||
dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
|
dshot_result_t DShotRMT::sendThrottle(uint16_t throttle)
|
||||||
{
|
{
|
||||||
// Special case: if throttle is 0, use sendCommand() instead
|
// A throttle value of 0 is a disarm command
|
||||||
if (throttle == 0)
|
if (throttle == 0)
|
||||||
{
|
{
|
||||||
return sendCommand(DSHOT_CMD_MOTOR_STOP);
|
return sendCommand(DSHOT_CMD_MOTOR_STOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always store the original throttle value
|
// Constrain throttle to the valid DShot range
|
||||||
_last_throttle = throttle;
|
_last_throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
|
||||||
|
|
||||||
// Constrain throttle for transmission and send
|
|
||||||
uint16_t new_throttle = constrain(throttle, DSHOT_THROTTLE_MIN, DSHOT_THROTTLE_MAX);
|
|
||||||
|
|
||||||
_packet = _buildDShotPacket(new_throttle);
|
|
||||||
|
|
||||||
|
_packet = _buildDShotPacket(_last_throttle);
|
||||||
return _sendDShotFrame(_packet);
|
return _sendDShotFrame(_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send throttle value as a percentage
|
||||||
|
dshot_result_t DShotRMT::sendThrottlePercent(float percent)
|
||||||
|
{
|
||||||
|
if (percent < 0.0f || percent > 100.0f)
|
||||||
|
{
|
||||||
|
return {false, PERCENT_NOT_IN_RANGE};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map percent to DShot throttle range
|
||||||
|
uint16_t throttle = static_cast<uint16_t>(DSHOT_THROTTLE_MIN + ((DSHOT_THROTTLE_MAX - DSHOT_THROTTLE_MIN) / 100.0f) * percent);
|
||||||
|
|
||||||
|
return sendThrottle(throttle);
|
||||||
|
}
|
||||||
|
|
||||||
// Send DShot command to ESC
|
// Send DShot command to ESC
|
||||||
dshot_result_t DShotRMT::sendCommand(uint16_t command)
|
dshot_result_t DShotRMT::sendCommand(uint16_t command)
|
||||||
{
|
{
|
||||||
// Validate command is within DShot specification range
|
if (command > DSHOT_CMD_MAX)
|
||||||
if (command < DSHOT_CMD_MOTOR_STOP || command > DSHOT_CMD_MAX)
|
|
||||||
{
|
{
|
||||||
return {false, COMMAND_NOT_VALID};
|
return {false, COMMAND_NOT_VALID};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build packet and transmit
|
|
||||||
_packet = _buildDShotPacket(command);
|
_packet = _buildDShotPacket(command);
|
||||||
|
|
||||||
return _sendDShotFrame(_packet);
|
return _sendDShotFrame(_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send full DShot commands for setup etc
|
||||||
|
dshot_result_t DShotRMT::sendCommand(dshot_commands_t dshot_command, uint16_t repeat_count, uint16_t delay_us)
|
||||||
|
{
|
||||||
|
dshot_result_t result = {false, UNKNOWN_ERROR};
|
||||||
|
|
||||||
|
if (!_isValidCommand(dshot_command))
|
||||||
|
{
|
||||||
|
result.msg = INVALID_COMMAND;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool all_successful = true;
|
||||||
|
|
||||||
|
// Send command multiple times with delay
|
||||||
|
for (uint16_t i = 0; i < repeat_count; i++)
|
||||||
|
{
|
||||||
|
dshot_result_t single_result = _executeCommand(dshot_command);
|
||||||
|
|
||||||
|
if (!single_result.success)
|
||||||
|
{
|
||||||
|
all_successful = false;
|
||||||
|
result.msg = single_result.msg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add delay between repetitions (except for last repetition)
|
||||||
|
if (i < repeat_count - 1)
|
||||||
|
{
|
||||||
|
delayMicroseconds(delay_us);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
result.success = all_successful;
|
||||||
|
|
||||||
|
if (result.success)
|
||||||
|
{
|
||||||
|
result.msg = COMMAND_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Get telemetry data
|
// Get telemetry data
|
||||||
dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
|
dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
|
||||||
{
|
{
|
||||||
// Result container with unified structure
|
|
||||||
dshot_result_t result = {false, TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
|
dshot_result_t result = {false, TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY};
|
||||||
|
|
||||||
// Check if bidirectional mode is enabled
|
|
||||||
if (!_is_bidirectional)
|
if (!_is_bidirectional)
|
||||||
{
|
{
|
||||||
result.msg = BIDIR_NOT_ENABLED;
|
result.msg = BIDIR_NOT_ENABLED;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for new telemetry data
|
// Use stored magnet count if parameter is 0 (default)
|
||||||
|
uint16_t final_magnet_count = (magnet_count == 0) ? _motor_magnet_count : magnet_count;
|
||||||
|
|
||||||
|
// Check if the callback has set the flag for new data
|
||||||
if (_telemetry_ready_flag_atomic)
|
if (_telemetry_ready_flag_atomic)
|
||||||
{
|
{
|
||||||
_telemetry_ready_flag_atomic = false;
|
_telemetry_ready_flag_atomic = false; // Reset the flag
|
||||||
|
uint16_t erpm = _last_erpm_atomic; // Read the atomic variable
|
||||||
|
|
||||||
uint16_t erpm = _last_erpm_atomic;
|
if (erpm != DSHOT_NULL_PACKET && final_magnet_count >= MAGNETS_PER_POLE_PAIR)
|
||||||
|
|
||||||
// Calculate motor RPM from eRPM
|
|
||||||
if (erpm != DSHOT_NULL_PACKET && magnet_count >= 1)
|
|
||||||
{
|
{
|
||||||
uint8_t pole_pairs = max(POLE_PAIRS_MIN, (magnet_count / MAGNETS_PER_POLE_PAIR));
|
// Calculate motor RPM from eRPM and magnet count
|
||||||
|
uint8_t pole_pairs = final_magnet_count / MAGNETS_PER_POLE_PAIR;
|
||||||
uint32_t motor_rpm = (erpm / pole_pairs);
|
uint32_t motor_rpm = (erpm / pole_pairs);
|
||||||
|
|
||||||
result.success = true;
|
result.success = true;
|
||||||
|
|
@ -204,47 +267,55 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public Info & Debug Functions
|
// Reverse motor direction directly
|
||||||
// Print timing diagnostic information to specified stream
|
dshot_result_t DShotRMT::setMotorSpinDirection(bool reversed)
|
||||||
void DShotRMT::printDShotInfo(Stream &output) const
|
|
||||||
{
|
{
|
||||||
output.println(" ");
|
// Use command as a yes / no switch
|
||||||
output.println(" === DShot Signal Info === ");
|
dshot_commands_t command = reversed ? DSHOT_CMD_SPIN_DIRECTION_REVERSED : DSHOT_CMD_SPIN_DIRECTION_NORMAL;
|
||||||
|
|
||||||
// Current DShot mode
|
return sendCommand(command, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US);
|
||||||
output.printf("Current Mode: DSHOT%d\n",
|
|
||||||
_mode == DSHOT150 ? 150 :
|
|
||||||
_mode == DSHOT300 ? 300 :
|
|
||||||
_mode == DSHOT600 ? 600 :
|
|
||||||
_mode == DSHOT1200 ? 1200 : 0);
|
|
||||||
|
|
||||||
output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO");
|
|
||||||
|
|
||||||
// Packet Info
|
|
||||||
output.printf("Current Packet: ");
|
|
||||||
|
|
||||||
// Print bit by bit
|
|
||||||
for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; --i)
|
|
||||||
{
|
|
||||||
if ((_parsed_packet >> i) & 0b0000000000000001)
|
|
||||||
{
|
|
||||||
output.print("1");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
output.print("0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.printf("\n");
|
|
||||||
|
|
||||||
output.printf("Current Value: %u\n", _packet.throttle_value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print CPU information
|
dshot_result_t DShotRMT::getESCInfo()
|
||||||
|
{
|
||||||
|
return sendCommand(DSHOT_CMD_ESC_INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use with caution
|
||||||
|
dshot_result_t DShotRMT::saveESCSettings()
|
||||||
|
{
|
||||||
|
return sendCommand(DSHOT_CMD_SAVE_SETTINGS, SETTINGS_COMMAND_REPEATS, SETTINGS_COMMAND_DELAY_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Info & Debug Functions
|
||||||
|
void DShotRMT::setMotorMagnetCount(uint16_t magnet_count)
|
||||||
|
{
|
||||||
|
_motor_magnet_count = magnet_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DShotRMT::printDShotInfo(Stream &output) const
|
||||||
|
{
|
||||||
|
output.println("\n === DShot Signal Info === ");
|
||||||
|
output.printf("Current Mode: DSHOT%d\n", _mode == DSHOT150 ? 150 :
|
||||||
|
_mode == DSHOT300 ? 300 :
|
||||||
|
_mode == DSHOT600 ? 600 :
|
||||||
|
_mode == DSHOT1200 ? 1200 : 0);
|
||||||
|
|
||||||
|
output.printf("Bidirectional: %s\n", _is_bidirectional ? "YES" : "NO");
|
||||||
|
output.printf("Current Packet: ");
|
||||||
|
|
||||||
|
for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
output.print((_parsed_packet >> i) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.printf("\nCurrent Value: %u\n", _packet.throttle_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
void DShotRMT::printCpuInfo(Stream &output) const
|
void DShotRMT::printCpuInfo(Stream &output) const
|
||||||
{
|
{
|
||||||
output.println(" ");
|
output.println("\n === CPU Info === ");
|
||||||
output.println(" === CPU Info === ");
|
|
||||||
output.printf("Chip Model: %s\n", ESP.getChipModel());
|
output.printf("Chip Model: %s\n", ESP.getChipModel());
|
||||||
output.printf("Chip Revision: %d\n", ESP.getChipRevision());
|
output.printf("Chip Revision: %d\n", ESP.getChipRevision());
|
||||||
output.printf("CPU Freq = %lu MHz\n", ESP.getCpuFreqMHz());
|
output.printf("CPU Freq = %lu MHz\n", ESP.getCpuFreqMHz());
|
||||||
|
|
@ -252,28 +323,42 @@ void DShotRMT::printCpuInfo(Stream &output) const
|
||||||
output.printf("APB Freq = %lu Hz\n", getApbFrequency());
|
output.printf("APB Freq = %lu Hz\n", getApbFrequency());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple check
|
||||||
|
bool DShotRMT::_isValidCommand(dshot_commands_t command)
|
||||||
|
{
|
||||||
|
return (command >= DSHOT_CMD_MOTOR_STOP && command <= DSHOT_CMD_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
dshot_result_t DShotRMT::_executeCommand(dshot_commands_t command)
|
||||||
|
{
|
||||||
|
uint64_t start_time = esp_timer_get_time();
|
||||||
|
|
||||||
|
// Execute the command using the DShotRMT instance
|
||||||
|
dshot_result_t result = sendCommand(static_cast<uint16_t>(command));
|
||||||
|
|
||||||
|
uint64_t end_time = esp_timer_get_time();
|
||||||
|
_last_command_timestamp = end_time;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Private Initialization Functions
|
// Private Initialization Functions
|
||||||
// Initialize RMT TX channel
|
|
||||||
dshot_result_t DShotRMT::_initTXChannel()
|
dshot_result_t DShotRMT::_initTXChannel()
|
||||||
{
|
{
|
||||||
// Configure TX channel
|
|
||||||
_tx_channel_config.gpio_num = _gpio;
|
_tx_channel_config.gpio_num = _gpio;
|
||||||
_tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT;
|
_tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT;
|
||||||
_tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION;
|
_tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION;
|
||||||
_tx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS;
|
_tx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS;
|
||||||
_tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH;
|
_tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH;
|
||||||
|
|
||||||
// Config RMT TX
|
_rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation
|
||||||
_rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation
|
_rmt_tx_config.flags.eot_level = _is_bidirectional ? 1 : 0;
|
||||||
_rmt_tx_config.flags.eot_level = _is_bidirectional ? 1 : 0; // Telemetric Bit used as bidir flag
|
|
||||||
|
|
||||||
// Create RMT TX channel
|
|
||||||
if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK)
|
if (rmt_new_tx_channel(&_tx_channel_config, &_rmt_tx_channel) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, TX_INIT_FAILED};
|
return {false, TX_INIT_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable TX channel
|
|
||||||
if (rmt_enable(_rmt_tx_channel) != DSHOT_OK)
|
if (rmt_enable(_rmt_tx_channel) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, TX_INIT_FAILED};
|
return {false, TX_INIT_FAILED};
|
||||||
|
|
@ -282,44 +367,55 @@ dshot_result_t DShotRMT::_initTXChannel()
|
||||||
return {true, TX_INIT_SUCCESS};
|
return {true, TX_INIT_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize RMT RX channel
|
|
||||||
dshot_result_t DShotRMT::_initRXChannel()
|
dshot_result_t DShotRMT::_initRXChannel()
|
||||||
{
|
{
|
||||||
// Direct RMT symbol processing - Performance optimized
|
// Double check if bidirectional mode is enabled
|
||||||
_rx_event_callbacks.on_recv_done = _on_rx_done;
|
if (!_is_bidirectional)
|
||||||
|
{
|
||||||
|
return {true, NONE};
|
||||||
|
}
|
||||||
|
|
||||||
// Config RMT RX
|
|
||||||
_rx_channel_config.gpio_num = _gpio;
|
_rx_channel_config.gpio_num = _gpio;
|
||||||
_rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT;
|
_rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT;
|
||||||
_rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION;
|
_rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION;
|
||||||
_rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS;
|
_rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS;
|
||||||
|
|
||||||
// Config RMT RX parameters
|
// Filter for pulses that are within a reasonable range for DShot telemetry
|
||||||
_rmt_rx_config.signal_range_min_ns = DSHOT_PULSE_MIN;
|
_rmt_rx_config.signal_range_min_ns = DSHOT_PULSE_MIN;
|
||||||
_rmt_rx_config.signal_range_max_ns = DSHOT_PULSE_MAX;
|
_rmt_rx_config.signal_range_max_ns = DSHOT_PULSE_MAX;
|
||||||
|
|
||||||
// Create RMT RX channel
|
|
||||||
if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK)
|
if (rmt_new_rx_channel(&_rx_channel_config, &_rmt_rx_channel) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, RX_INIT_FAILED};
|
return {false, RX_INIT_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable RX channel
|
// Register the callback function that will be triggered when a frame is received
|
||||||
|
_rx_event_callbacks.on_recv_done = _on_rx_done;
|
||||||
|
if (rmt_rx_register_event_callbacks(_rmt_rx_channel, &_rx_event_callbacks, this) != DSHOT_OK)
|
||||||
|
{
|
||||||
|
return {false, CALLBACK_REGISTERING_FAILED};
|
||||||
|
}
|
||||||
|
|
||||||
if (rmt_enable(_rmt_rx_channel) != DSHOT_OK)
|
if (rmt_enable(_rmt_rx_channel) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, RX_INIT_FAILED};
|
return {false, RX_INIT_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the receiver to wait for incoming telemetry data
|
||||||
|
rmt_symbol_word_t rx_symbols[GCR_BITS_PER_FRAME];
|
||||||
|
size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
|
||||||
|
if (rmt_receive(_rmt_rx_channel, rx_symbols, rx_size_bytes, &_rmt_rx_config) != DSHOT_OK)
|
||||||
|
{
|
||||||
|
return {false, RECEIVER_FAILED};
|
||||||
|
}
|
||||||
|
|
||||||
return {true, RX_INIT_SUCCESS};
|
return {true, RX_INIT_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize DShot encoder
|
|
||||||
dshot_result_t DShotRMT::_initDShotEncoder()
|
dshot_result_t DShotRMT::_initDShotEncoder()
|
||||||
{
|
{
|
||||||
// Create copy encoder configuration
|
|
||||||
rmt_copy_encoder_config_t encoder_config = {};
|
rmt_copy_encoder_config_t encoder_config = {};
|
||||||
|
|
||||||
// Create encoder instance
|
|
||||||
if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK)
|
if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, ENCODER_INIT_FAILED};
|
return {false, ENCODER_INIT_FAILED};
|
||||||
|
|
@ -329,77 +425,63 @@ dshot_result_t DShotRMT::_initDShotEncoder()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private Packet Management Functions
|
// Private Packet Management Functions
|
||||||
// Build a complete DShot packet from a valid value
|
|
||||||
dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value)
|
dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value)
|
||||||
{
|
{
|
||||||
// Init packet structure
|
|
||||||
dshot_packet_t packet = {};
|
dshot_packet_t packet = {};
|
||||||
|
|
||||||
// Re-check for valid value
|
|
||||||
if (value > DSHOT_THROTTLE_MAX)
|
|
||||||
{
|
|
||||||
// Something is really wrong
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build packet
|
|
||||||
packet.throttle_value = value & DSHOT_THROTTLE_MAX;
|
packet.throttle_value = value & DSHOT_THROTTLE_MAX;
|
||||||
packet.telemetric_request = _is_bidirectional ? 1 : 0;
|
packet.telemetric_request = _is_bidirectional ? 1 : 0;
|
||||||
|
|
||||||
// CRC is calculated over 12bit
|
// The data for CRC calculation includes the 11-bit value and the 1-bit telemetry flag
|
||||||
uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request;
|
uint16_t data_for_crc = (packet.throttle_value << 1) | packet.telemetric_request;
|
||||||
|
packet.checksum = _calculateCRC(data_for_crc);
|
||||||
packet.checksum = _calculateCRC(data);
|
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse DShot packet into 16-bit format
|
|
||||||
uint16_t DShotRMT::_parseDShotPacket(const dshot_packet_t &packet)
|
uint16_t DShotRMT::_parseDShotPacket(const dshot_packet_t &packet)
|
||||||
{
|
{
|
||||||
// Parse DShot frame into "raw" 16 bit value
|
// Combine throttle, telemetry bit, and CRC into a single 16-bit frame
|
||||||
uint16_t data_and_telemetry = (packet.throttle_value << 1) | packet.telemetric_request;
|
uint16_t data_and_telemetry = (packet.throttle_value << 1) | packet.telemetric_request;
|
||||||
return (data_and_telemetry << 4) | packet.checksum;
|
return (data_and_telemetry << 4) | packet.checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate CRC
|
|
||||||
uint16_t DShotRMT::_calculateCRC(const uint16_t &data)
|
uint16_t DShotRMT::_calculateCRC(const uint16_t &data)
|
||||||
{
|
{
|
||||||
// DShot CRC
|
// Standard DShot CRC calculation using XOR
|
||||||
uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & DSHOT_CRC_MASK;
|
uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & DSHOT_CRC_MASK;
|
||||||
|
|
||||||
// Invert CRC for bidirectional DShot mode
|
// For bidirectional DShot, the CRC is inverted
|
||||||
if (_is_bidirectional)
|
if (_is_bidirectional)
|
||||||
{
|
{
|
||||||
crc = (~crc) & DSHOT_CRC_MASK;
|
crc = (~crc) & DSHOT_CRC_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure RMT ticks for DShot timings
|
|
||||||
void DShotRMT::_preCalculateRMTTicks()
|
void DShotRMT::_preCalculateRMTTicks()
|
||||||
{
|
{
|
||||||
// Convert DShot timings (us) to RMT ticks
|
// Pre-calculate all timing values in RMT ticks to save CPU cycles later
|
||||||
_rmt_ticks.bit_length_ticks = static_cast<uint16_t>(_dshot_timing.bit_length_us * RMT_TICKS_PER_US);
|
_rmt_ticks.bit_length_ticks = static_cast<uint16_t>(_dshot_timing.bit_length_us * RMT_TICKS_PER_US);
|
||||||
_rmt_ticks.t1h_ticks = static_cast<uint16_t>(_dshot_timing.t1h_lenght_us * RMT_TICKS_PER_US);
|
_rmt_ticks.t1h_ticks = static_cast<uint16_t>(_dshot_timing.t1h_lenght_us * RMT_TICKS_PER_US);
|
||||||
_rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a 1 is always double that of a 0
|
_rmt_ticks.t0h_ticks = _rmt_ticks.t1h_ticks >> 1; // High time for a 1 is always double of 0
|
||||||
_rmt_ticks.t1l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t1h_ticks;
|
_rmt_ticks.t1l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t1h_ticks;
|
||||||
_rmt_ticks.t0l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t0h_ticks;
|
_rmt_ticks.t0l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t0h_ticks;
|
||||||
|
|
||||||
|
// Calculate the minimum time required between frames
|
||||||
// Pause between frames is frame time in us, some padding and about 30 us is added by hardware
|
// Pause between frames is frame time in us, some padding and about 30 us is added by hardware
|
||||||
_frame_timer_us = (static_cast<uint64_t>(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US;
|
_frame_timer_us = (static_cast<uint64_t>(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US;
|
||||||
|
|
||||||
// Double frame time for bidirectional mode (includes response time)
|
// For bidirectional, double up
|
||||||
if (_is_bidirectional)
|
if (_is_bidirectional)
|
||||||
{
|
{
|
||||||
_frame_timer_us = (_frame_timer_us << 1);
|
_frame_timer_us = (_frame_timer_us << 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Precalculate bit positions for performance optimization
|
|
||||||
void DShotRMT::_preCalculateBitPositions()
|
void DShotRMT::_preCalculateBitPositions()
|
||||||
{
|
{
|
||||||
|
// Pre-calculate bit positions to avoid redundant calculations in the encoding loop
|
||||||
for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i)
|
for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i)
|
||||||
{
|
{
|
||||||
_bitPositions[i] = DSHOT_BITS_PER_FRAME - 1 - i;
|
_bitPositions[i] = DSHOT_BITS_PER_FRAME - 1 - i;
|
||||||
|
|
@ -407,173 +489,125 @@ void DShotRMT::_preCalculateBitPositions()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private Frame Processing Functions
|
// Private Frame Processing Functions
|
||||||
// Transmit DShot packet via RMT
|
|
||||||
dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet)
|
||||||
{
|
{
|
||||||
// Check timing requirements
|
// Ensure enough time has passed since the last transmission
|
||||||
if (!_timer_signal())
|
if (!_timer_signal())
|
||||||
{
|
{
|
||||||
return {false, TIMING_CORRECTION};
|
return {true, NONE};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable RMT RX before RMT TX (bidirectional mode)
|
|
||||||
if (_is_bidirectional)
|
|
||||||
{
|
|
||||||
// Calculate transmission data size
|
|
||||||
size_t rx_size_bytes = GCR_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
|
|
||||||
|
|
||||||
// Performance reasons
|
|
||||||
rmt_symbol_word_t rx_symbols[GCR_BITS_PER_FRAME];
|
|
||||||
|
|
||||||
if (rmt_receive(_rmt_rx_channel, rx_symbols, rx_size_bytes, &_rmt_rx_config) != DSHOT_OK)
|
|
||||||
{
|
|
||||||
return {false, RECEIVER_FAILED};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local for performance
|
|
||||||
rmt_symbol_word_t tx_symbols[DSHOT_BITS_PER_FRAME];
|
rmt_symbol_word_t tx_symbols[DSHOT_BITS_PER_FRAME];
|
||||||
|
dshot_result_t result = _encodeDShotFrame(packet, tx_symbols);
|
||||||
|
|
||||||
// Encode DShot packet into RMT symbols
|
if (!result.success)
|
||||||
_encodeDShotFrame(packet, tx_symbols);
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate transmission data size
|
|
||||||
size_t tx_size_bytes = DSHOT_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
|
size_t tx_size_bytes = DSHOT_BITS_PER_FRAME * sizeof(rmt_symbol_word_t);
|
||||||
|
|
||||||
// TODO: Find out, why this is needed
|
|
||||||
if (_is_bidirectional)
|
|
||||||
{
|
|
||||||
// Disable RMT RX for sending
|
|
||||||
if (rmt_disable(_rmt_rx_channel) != DSHOT_OK)
|
|
||||||
{
|
|
||||||
return {false, RECEIVER_FAILED};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform RMT transmission
|
|
||||||
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_rmt_tx_config) != DSHOT_OK)
|
if (rmt_transmit(_rmt_tx_channel, _dshot_encoder, tx_symbols, tx_size_bytes, &_rmt_tx_config) != DSHOT_OK)
|
||||||
{
|
{
|
||||||
return {false, TRANSMISSION_FAILED};
|
return {false, TRANSMISSION_FAILED};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable RMT RX
|
_timer_reset(); // Reset the timer for the next frame
|
||||||
if (_is_bidirectional)
|
|
||||||
{
|
|
||||||
if (rmt_enable(_rmt_rx_channel) != DSHOT_OK)
|
|
||||||
{
|
|
||||||
return {false, RECEIVER_FAILED};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update timestamp and calculate execution time
|
|
||||||
_timer_reset();
|
|
||||||
|
|
||||||
return {true, TRANSMISSION_SUCCESS};
|
return {true, TRANSMISSION_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode DShot packet into RMT symbol format (placed in IRAM for performance)
|
// This function needs to be fast, as it generates the RMT symbols just before sending
|
||||||
dshot_result_t IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols)
|
dshot_result_t IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols)
|
||||||
{
|
{
|
||||||
_parsed_packet = _parseDShotPacket(packet);
|
_parsed_packet = _parseDShotPacket(packet);
|
||||||
|
|
||||||
// Decode MSB
|
|
||||||
for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i)
|
for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i)
|
||||||
{
|
{
|
||||||
// Use precalculated bit positions - Performance optimized
|
|
||||||
int bit_position = _bitPositions[i];
|
int bit_position = _bitPositions[i];
|
||||||
|
bool bit = (_parsed_packet >> bit_position) & 1;
|
||||||
|
|
||||||
bool bit = (_parsed_packet >> bit_position) & 0b0000000000000001;
|
// A '1' bit has a longer high-time, a '0' bit has a shorter high-time
|
||||||
symbols[i].level0 = _level0;
|
symbols[i].level0 = _level0; // Go HIGH
|
||||||
symbols[i].duration0 = bit ? _rmt_ticks.t1h_ticks : _rmt_ticks.t0h_ticks;
|
symbols[i].duration0 = bit ? _rmt_ticks.t1h_ticks : _rmt_ticks.t0h_ticks;
|
||||||
symbols[i].level1 = _level1;
|
symbols[i].level1 = _level1; // Go LOW
|
||||||
symbols[i].duration1 = bit ? _rmt_ticks.t1l_ticks : _rmt_ticks.t0l_ticks;
|
symbols[i].duration1 = bit ? _rmt_ticks.t1l_ticks : _rmt_ticks.t0l_ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {true, ENCODING_SUCCESS};
|
return {true, ENCODING_SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode DShot telemetry frame from received RMT symbols
|
// Placed in IRAM for high performance, as it's called from an ISR context
|
||||||
uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols)
|
uint16_t IRAM_ATTR DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols)
|
||||||
{
|
{
|
||||||
uint32_t gcr_value = 0;
|
uint32_t gcr_value = 0;
|
||||||
|
|
||||||
// Decode GCR symbols into a 21-bit value.
|
// Step 1: Decode RMT symbols into a 21-bit GCR (Group Code Recording) value.
|
||||||
// '1' has a longer low pulse (duration0 > duration1).
|
// The ESC sends back a signal where the duration determines the bit value.
|
||||||
// '0' has a longer high pulse (duration1 > duration0).
|
|
||||||
for (size_t i = 0; i < GCR_BITS_PER_FRAME; ++i)
|
for (size_t i = 0; i < GCR_BITS_PER_FRAME; ++i)
|
||||||
{
|
{
|
||||||
bool bit_is_one = symbols[i].duration0 > symbols[i].duration1;
|
bool bit_is_one = symbols[i].duration0 > symbols[i].duration1;
|
||||||
gcr_value = (gcr_value << 1) | bit_is_one;
|
gcr_value = (gcr_value << 1) | bit_is_one;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform GCR decoding: data = gcr ^ (gcr >> 1).
|
// Step 2: Perform GCR decoding (GCR = Value ^ (Value >> 1))
|
||||||
uint32_t decoded_frame = gcr_value ^ (gcr_value >> 1);
|
uint32_t decoded_frame = gcr_value ^ (gcr_value >> 1);
|
||||||
|
|
||||||
// Extract 16 data bits and 4 CRC bits from 20-bit frame.
|
// Step 3: Extract the 16-bit DShot frame from the decoded data
|
||||||
// The first bit of the GCR frame is a start bit and is discarded.
|
|
||||||
uint16_t data_and_crc = (decoded_frame & DSHOT_FULL_PACKET);
|
uint16_t data_and_crc = (decoded_frame & DSHOT_FULL_PACKET);
|
||||||
|
|
||||||
// Extract data (first 12 bits) and CRC (last 4 bits)
|
// Step 4: Extract data and CRC from the 16-bit frame
|
||||||
uint16_t received_data = data_and_crc >> 4;
|
uint16_t received_data = data_and_crc >> 4;
|
||||||
uint16_t received_crc = data_and_crc & DSHOT_CRC_MASK;
|
uint16_t received_crc = data_and_crc & DSHOT_CRC_MASK;
|
||||||
|
|
||||||
// Telemetry request bit has to be 1
|
// Step 5: A valid response must have the telemetry request bit set to 1. This is a sanity check.
|
||||||
if (!(received_data & (1 << 11)))
|
if (!((received_data >> 11) & 1))
|
||||||
{
|
{
|
||||||
return DSHOT_NULL_PACKET;
|
return DSHOT_NULL_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate expected CRC
|
// Step 6: Calculate and validate CRC
|
||||||
uint16_t data_for_crc = received_data;
|
uint16_t calculated_crc = _calculateCRC(received_data);
|
||||||
uint16_t calculated_crc = _calculateCRC(data_for_crc);
|
|
||||||
|
|
||||||
// Validate CRC
|
|
||||||
if (received_crc != calculated_crc)
|
if (received_crc != calculated_crc)
|
||||||
{
|
{
|
||||||
return DSHOT_NULL_PACKET;
|
return DSHOT_NULL_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the eRPM value (first 11 bits of received data).
|
// Return the eRPM value (first 11 bits).
|
||||||
return received_data & DSHOT_THROTTLE_MAX;
|
return received_data & DSHOT_THROTTLE_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private Timing Control Functions
|
// Timing Control Functions
|
||||||
// Check if enough time has passed for next transmission
|
|
||||||
bool IRAM_ATTR DShotRMT::_timer_signal()
|
bool IRAM_ATTR DShotRMT::_timer_signal()
|
||||||
{
|
{
|
||||||
|
// Check if the minimum interval between frames has passed
|
||||||
uint64_t current_time = esp_timer_get_time();
|
uint64_t current_time = esp_timer_get_time();
|
||||||
|
|
||||||
// Handle potential overflow
|
|
||||||
uint64_t elapsed = current_time - _last_transmission_time_us;
|
uint64_t elapsed = current_time - _last_transmission_time_us;
|
||||||
|
|
||||||
return elapsed >= _frame_timer_us;
|
return elapsed >= _frame_timer_us;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset transmission timer to current time
|
|
||||||
bool DShotRMT::_timer_reset()
|
bool DShotRMT::_timer_reset()
|
||||||
{
|
{
|
||||||
|
// Record the time of the current transmission
|
||||||
_last_transmission_time_us = esp_timer_get_time();
|
_last_transmission_time_us = esp_timer_get_time();
|
||||||
|
return true;
|
||||||
return DSHOT_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static Callback Functions
|
// Static Callback Functions
|
||||||
// Callback for RMT RX
|
// This function is called by the RMT driver's ISR when a frame is received
|
||||||
bool IRAM_ATTR DShotRMT::_on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data)
|
bool IRAM_ATTR DShotRMT::_on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data)
|
||||||
{
|
{
|
||||||
DShotRMT *instance = static_cast<DShotRMT *>(user_data);
|
DShotRMT *instance = static_cast<DShotRMT *>(user_data);
|
||||||
|
|
||||||
// ISR check for valid data
|
if (edata && edata->num_symbols == GCR_BITS_PER_FRAME)
|
||||||
if (edata && edata->num_symbols >= GCR_BITS_PER_FRAME && edata->num_symbols <= GCR_BITS_PER_FRAME)
|
|
||||||
{
|
{
|
||||||
// Direct decoding
|
|
||||||
uint16_t erpm = instance->_decodeDShotFrame(edata->received_symbols);
|
uint16_t erpm = instance->_decodeDShotFrame(edata->received_symbols);
|
||||||
|
|
||||||
if (erpm != DSHOT_NULL_PACKET)
|
if (erpm != DSHOT_NULL_PACKET)
|
||||||
{
|
{
|
||||||
// Atomic writes - thread-safe
|
// Atomically store the new eRPM value and set the flag
|
||||||
instance->_last_erpm_atomic = erpm;
|
instance->_last_erpm_atomic.store(erpm);
|
||||||
instance->_telemetry_ready_flag_atomic = true;
|
instance->_telemetry_ready_flag_atomic.store(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* @file DShotRMT.h
|
* @file DShotRMT.h
|
||||||
* @brief DShot signal generation using ESP32 RMT with bidirectional support
|
* @brief Optimized DShot signal generation using ESP32 RMT with bidirectional support
|
||||||
* @author Wastl Kraus
|
* @author Wastl Kraus
|
||||||
* @date 2025-06-11
|
* @date 2025-09-18
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -22,6 +22,10 @@ static constexpr auto DSHOT_THROTTLE_MAX = 2047;
|
||||||
static constexpr auto DSHOT_BITS_PER_FRAME = 16;
|
static constexpr auto DSHOT_BITS_PER_FRAME = 16;
|
||||||
static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14;
|
static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14;
|
||||||
|
|
||||||
|
// Custom status codes
|
||||||
|
static constexpr auto DSHOT_OK = 0;
|
||||||
|
static constexpr auto DSHOT_ERROR = 1;
|
||||||
|
|
||||||
// DShot Modes
|
// DShot Modes
|
||||||
typedef enum dshot_mode
|
typedef enum dshot_mode
|
||||||
{
|
{
|
||||||
|
|
@ -62,8 +66,8 @@ typedef struct dshot_result
|
||||||
{
|
{
|
||||||
bool success;
|
bool success;
|
||||||
const char *msg;
|
const char *msg;
|
||||||
uint16_t erpm;
|
uint16_t erpm;
|
||||||
uint16_t motor_rpm;
|
uint16_t motor_rpm;
|
||||||
} dshot_result_t;
|
} dshot_result_t;
|
||||||
|
|
||||||
// Command Type Alias
|
// Command Type Alias
|
||||||
|
|
@ -78,28 +82,27 @@ class DShotRMT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Constructors & Destructor
|
// Constructors & Destructor
|
||||||
explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false);
|
explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
|
||||||
DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional);
|
DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional, uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
|
||||||
|
|
||||||
~DShotRMT();
|
~DShotRMT();
|
||||||
|
|
||||||
// Public Core Functions
|
// Public Core Functions
|
||||||
// Initialize the RMT module and DShot config
|
|
||||||
dshot_result_t begin();
|
dshot_result_t begin();
|
||||||
|
|
||||||
// Send throttle value (48 - 2047)
|
|
||||||
dshot_result_t sendThrottle(uint16_t throttle);
|
dshot_result_t sendThrottle(uint16_t throttle);
|
||||||
|
dshot_result_t sendThrottlePercent(float percent);
|
||||||
// Send DShot command (0 - 47)
|
|
||||||
dshot_result_t sendCommand(uint16_t command);
|
dshot_result_t sendCommand(uint16_t command);
|
||||||
|
dshot_result_t sendCommand(dshot_commands_t dshot_command, uint16_t repeat_count = DEFAULT_CMD_REPEAT_COUNT, uint16_t delay_us = DEFAULT_CMD_DELAY_US);
|
||||||
// Get telemetry data (bidirectional mode only)
|
dshot_result_t getTelemetry(uint16_t magnet_count = 0);
|
||||||
dshot_result_t getTelemetry(uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT);
|
dshot_result_t getESCInfo();
|
||||||
|
dshot_result_t setMotorSpinDirection(bool reversed);
|
||||||
// Public Info & Debug Functions
|
dshot_result_t saveESCSettings();
|
||||||
|
|
||||||
|
// Public Utility & Info Functions
|
||||||
|
void setMotorMagnetCount(uint16_t magnet_count);
|
||||||
void printDShotInfo(Stream &output = Serial) const;
|
void printDShotInfo(Stream &output = Serial) const;
|
||||||
void printCpuInfo(Stream &output = Serial) const;
|
void printCpuInfo(Stream &output = Serial) const;
|
||||||
|
|
||||||
// Deprecated Methods
|
// Deprecated Methods
|
||||||
[[deprecated("Use sendThrottle() instead")]]
|
[[deprecated("Use sendThrottle() instead")]]
|
||||||
bool setThrottle(uint16_t throttle)
|
bool setThrottle(uint16_t throttle)
|
||||||
|
|
@ -123,27 +126,23 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Configuration Constants
|
// Configuration Constants
|
||||||
static constexpr bool DSHOT_OK = 0;
|
|
||||||
static constexpr bool DSHOT_ERROR = 1;
|
|
||||||
|
|
||||||
static constexpr auto const DSHOT_NULL_PACKET = 0b0000000000000000;
|
static constexpr auto const DSHOT_NULL_PACKET = 0b0000000000000000;
|
||||||
static constexpr auto const DSHOT_FULL_PACKET = 0b1111111111111111;
|
static constexpr auto const DSHOT_FULL_PACKET = 0b1111111111111111;
|
||||||
static constexpr auto const DSHOT_CRC_MASK = 0b0000000000001111;
|
static constexpr auto const DSHOT_CRC_MASK = 0b0000000000001111;
|
||||||
static constexpr auto const DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
|
static constexpr auto const DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT;
|
||||||
static constexpr auto const DSHOT_RMT_RESOLUTION = 8 * 1000 * 1000; // 8 MHz resolution
|
static constexpr auto const DSHOT_RMT_RESOLUTION = 8 * 1000 * 1000; // 8 MHz resolution
|
||||||
static constexpr auto const RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / (1 * 1000 * 1000); // RMT Ticks per microsecond
|
static constexpr auto const RMT_TICKS_PER_US = DSHOT_RMT_RESOLUTION / (1 * 1000 * 1000); // RMT Ticks per microsecond
|
||||||
static constexpr auto const RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME;
|
|
||||||
static constexpr auto const DSHOT_RX_TIMEOUT_MS = 2;
|
static constexpr auto const DSHOT_RX_TIMEOUT_MS = 2;
|
||||||
static constexpr auto const DSHOT_PADDING_US = 20; // Add to pause between frames for compatibility
|
static constexpr auto const DSHOT_PADDING_US = 20; // Add to pause between frames for compatibility
|
||||||
static constexpr auto const RMT_BUFFER_SYMBOLS = 64;
|
static constexpr auto const RMT_BUFFER_SYMBOLS = 128;
|
||||||
static constexpr auto const RMT_QUEUE_DEPTH = 1;
|
static constexpr auto const RMT_QUEUE_DEPTH = 1;
|
||||||
static constexpr auto const GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame
|
static constexpr auto const GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame
|
||||||
static constexpr auto const POLE_PAIRS_MIN = 1;
|
static constexpr auto const POLE_PAIRS_MIN = 1;
|
||||||
static constexpr auto const MAGNETS_PER_POLE_PAIR = 2;
|
static constexpr auto const MAGNETS_PER_POLE_PAIR = 2;
|
||||||
static constexpr auto const NO_DSHOT_TELEMETRY = 0;
|
static constexpr auto const NO_DSHOT_TELEMETRY = 0;
|
||||||
static constexpr auto const DSHOT_PULSE_MIN = 3000; // 3us minimum pulse
|
static constexpr auto const DSHOT_PULSE_MIN = 800; // 0.8us minimum pulse
|
||||||
static constexpr auto const DSHOT_PULSE_MAX = 60000; // 60us maximum pulse
|
static constexpr auto const DSHOT_PULSE_MAX = 8000; // 8.0us maximum pulse
|
||||||
static constexpr auto const DSHOT_TELEMETRY_INVALID = DSHOT_THROTTLE_MAX;
|
static constexpr auto const DSHOT_TELEMETRY_INVALID = DSHOT_THROTTLE_MAX;
|
||||||
|
|
||||||
// Error Messages
|
// Error Messages
|
||||||
|
|
@ -162,17 +161,26 @@ private:
|
||||||
static constexpr char const *TRANSMISSION_FAILED = "Transmission failed!";
|
static constexpr char const *TRANSMISSION_FAILED = "Transmission failed!";
|
||||||
static constexpr char const *RECEIVER_FAILED = "RMT receiver failed!";
|
static constexpr char const *RECEIVER_FAILED = "RMT receiver failed!";
|
||||||
static constexpr char const *THROTTLE_NOT_IN_RANGE = "Throttle not in range! (48 - 2047)";
|
static constexpr char const *THROTTLE_NOT_IN_RANGE = "Throttle not in range! (48 - 2047)";
|
||||||
|
static constexpr char const *PERCENT_NOT_IN_RANGE = "Percent not in range! (0.0 - 100.0)";
|
||||||
static constexpr char const *COMMAND_NOT_VALID = "Command not valid! (0 - 47)";
|
static constexpr char const *COMMAND_NOT_VALID = "Command not valid! (0 - 47)";
|
||||||
static constexpr char const *BIDIR_NOT_ENABLED = "Bidirectional DShot not enabled!";
|
static constexpr char const *BIDIR_NOT_ENABLED = "Bidirectional DShot not enabled!";
|
||||||
static constexpr char const *TELEMETRY_SUCCESS = "Valid Telemetric Frame received!";
|
static constexpr char const *TELEMETRY_SUCCESS = "Valid Telemetric Frame received!";
|
||||||
static constexpr char const *TELEMETRY_FAILED = "No valid Telemetric Frame received!";
|
static constexpr char const *TELEMETRY_FAILED = "No valid Telemetric Frame received!";
|
||||||
static constexpr char const *INVALID_MAGNET_COUNT = "Invalid motor magnet count!";
|
static constexpr char const *INVALID_MAGNET_COUNT = "Invalid motor magnet count!";
|
||||||
static constexpr char const *TIMING_CORRECTION = "Timing correction!";
|
static constexpr char const *TIMING_CORRECTION = "Timing correction!";
|
||||||
|
static constexpr char const *CALLBACK_REGISTERING_FAILED = "RMT RX Callback registering failed!";
|
||||||
|
static constexpr char const *INVALID_COMMAND = "Invalid command!";
|
||||||
|
static constexpr char const *COMMAND_SUCCESS = "DShot command sent successfully";
|
||||||
|
|
||||||
|
// --- UTILITY METHODS ---
|
||||||
|
bool _isValidCommand(dshot_commands_t command);
|
||||||
|
dshot_result_t _executeCommand(dshot_commands_t command);
|
||||||
|
|
||||||
// Core Configuration Variables
|
// Core Configuration Variables
|
||||||
gpio_num_t _gpio;
|
gpio_num_t _gpio;
|
||||||
dshot_mode_t _mode;
|
dshot_mode_t _mode;
|
||||||
bool _is_bidirectional;
|
bool _is_bidirectional;
|
||||||
|
uint16_t _motor_magnet_count;
|
||||||
const dshot_timing_us_t &_dshot_timing;
|
const dshot_timing_us_t &_dshot_timing;
|
||||||
uint64_t _frame_timer_us;
|
uint64_t _frame_timer_us;
|
||||||
|
|
||||||
|
|
@ -180,12 +188,13 @@ private:
|
||||||
rmt_ticks_t _rmt_ticks;
|
rmt_ticks_t _rmt_ticks;
|
||||||
uint16_t _last_throttle;
|
uint16_t _last_throttle;
|
||||||
uint64_t _last_transmission_time_us;
|
uint64_t _last_transmission_time_us;
|
||||||
|
uint64_t _last_command_timestamp;
|
||||||
uint16_t _parsed_packet;
|
uint16_t _parsed_packet;
|
||||||
dshot_packet_t _packet;
|
dshot_packet_t _packet;
|
||||||
uint8_t _bitPositions[DSHOT_BITS_PER_FRAME];
|
uint8_t _bitPositions[DSHOT_BITS_PER_FRAME];
|
||||||
uint16_t _level0;
|
uint16_t _level0; // Signal level for the first part of a pulse (always HIGH for DShot)
|
||||||
uint16_t _level1;
|
uint16_t _level1; // Signal level for the second part of a pulse (always LOW for DShot)
|
||||||
|
|
||||||
// RMT Hardware Handles
|
// RMT Hardware Handles
|
||||||
rmt_channel_handle_t _rmt_tx_channel;
|
rmt_channel_handle_t _rmt_tx_channel;
|
||||||
rmt_channel_handle_t _rmt_rx_channel;
|
rmt_channel_handle_t _rmt_rx_channel;
|
||||||
|
|
@ -218,11 +227,17 @@ private:
|
||||||
dshot_result_t _sendDShotFrame(const dshot_packet_t &packet);
|
dshot_result_t _sendDShotFrame(const dshot_packet_t &packet);
|
||||||
dshot_result_t _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols);
|
dshot_result_t _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols);
|
||||||
uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols);
|
uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols);
|
||||||
|
|
||||||
// Private Timing Control Functions
|
// Private Timing Control Functions
|
||||||
bool _timer_signal();
|
bool _timer_signal();
|
||||||
bool _timer_reset();
|
bool _timer_reset();
|
||||||
|
|
||||||
// Static Callback Functions
|
// Static Callback Functions
|
||||||
static bool _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);
|
static bool _on_rx_done(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data);
|
||||||
|
|
||||||
|
// Command Constants
|
||||||
|
static constexpr auto DEFAULT_CMD_DELAY_US = 10;
|
||||||
|
static constexpr auto DEFAULT_CMD_REPEAT_COUNT = 1;
|
||||||
|
static constexpr auto SETTINGS_COMMAND_REPEATS = 10; // Settings commands need 10 repeats
|
||||||
|
static constexpr auto SETTINGS_COMMAND_DELAY_US = 5;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,249 @@
|
||||||
|
/**
|
||||||
|
* @file ota_update.h
|
||||||
|
* @brief DShot signal generation using ESP32 RMT with bidirectional support
|
||||||
|
* @author Wastl Kraus
|
||||||
|
* @date 2025-09-13
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// OTA Update HTML
|
||||||
|
const char *ota_html = R"rawliteral(
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>OTA Update - DShotRMT</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: #ecf0f1;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #34495e;
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="file"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #7f8c8d;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
background-color: #3498db;
|
||||||
|
color: white;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:disabled {
|
||||||
|
background-color: #7f8c8d;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
width: 0%;
|
||||||
|
height: 30px;
|
||||||
|
background-color: #27ae60;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 30px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
background-color: #27ae60;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background-color: #e74c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
background-color: #f39c12;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-link {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-link a {
|
||||||
|
color: #3498db;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-link a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>OTA Firmware Update</h1>
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
WARNING: Stop motors before starting update!
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="upload_form" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="update">Firmware File (.bin):</label>
|
||||||
|
<input type="file" id="update" name="update" accept=".bin" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn" id="uploadBtn">Update!</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="progress" id="progressDiv">
|
||||||
|
<div class="progress-bar" id="progressBar">0%</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message" id="message"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('upload_form').addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const fileInput = document.getElementById('update');
|
||||||
|
const file = fileInput.files[0];
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
showMessage('Choose a valid update file.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.name.endsWith('.bin')) {
|
||||||
|
showMessage('Choose a valid update file.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadFile(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
function uploadFile(file) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('update', file);
|
||||||
|
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
// Progress tracking
|
||||||
|
xhr.upload.addEventListener('progress', function (e) {
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
const percentComplete = (e.loaded / e.total) * 100;
|
||||||
|
updateProgress(percentComplete);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Upload complete
|
||||||
|
xhr.addEventListener('load', function () {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
showMessage('Update successfull! Restarting...', 'success');
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = '/';
|
||||||
|
}, 5000);
|
||||||
|
} else {
|
||||||
|
showMessage('Update failed: ' + xhr.responseText, 'error');
|
||||||
|
resetUpload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Upload error
|
||||||
|
xhr.addEventListener('error', function () {
|
||||||
|
showMessage('Connection error during Update.', 'error');
|
||||||
|
resetUpload();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start upload
|
||||||
|
document.getElementById('uploadBtn').disabled = true;
|
||||||
|
document.getElementById('uploadBtn').textContent = 'buffering...';
|
||||||
|
document.getElementById('progressDiv').style.display = 'block';
|
||||||
|
|
||||||
|
xhr.open('POST', '/update');
|
||||||
|
xhr.send(formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProgress(percent) {
|
||||||
|
const progressBar = document.getElementById('progressBar');
|
||||||
|
progressBar.style.width = percent + '%';
|
||||||
|
progressBar.textContent = Math.round(percent) + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMessage(text, type) {
|
||||||
|
const messageDiv = document.getElementById('message');
|
||||||
|
messageDiv.textContent = text;
|
||||||
|
messageDiv.className = 'message ' + type;
|
||||||
|
messageDiv.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetUpload() {
|
||||||
|
document.getElementById('uploadBtn').disabled = false;
|
||||||
|
document.getElementById('uploadBtn').textContent = 'Update!';
|
||||||
|
document.getElementById('progressDiv').style.display = 'none';
|
||||||
|
document.getElementById('progressBar').style.width = '0%';
|
||||||
|
document.getElementById('progressBar').textContent = '0%';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
)rawliteral";
|
||||||
|
|
@ -217,6 +217,7 @@ static constexpr char index_html[] = R"rawliteral(
|
||||||
<!-- Throttle Section -->
|
<!-- Throttle Section -->
|
||||||
<div class="throttle-section" id="throttleSection">
|
<div class="throttle-section" id="throttleSection">
|
||||||
<div id="throttleValue">0</div>
|
<div id="throttleValue">0</div>
|
||||||
|
<div id="throttlePercent" style="font-size: 1.2rem; color: #bdc3c7; margin-top: -15px; margin-bottom: 15px;">0%</div>
|
||||||
<input type="range" min="48" max="2047" value="0" id="throttleSlider" disabled>
|
<input type="range" min="48" max="2047" value="0" id="throttleSlider" disabled>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -253,6 +254,19 @@ static constexpr char index_html[] = R"rawliteral(
|
||||||
setTimeout(initWebSocket, 2000);
|
setTimeout(initWebSocket, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateThrottleDisplays(rawValue) {
|
||||||
|
const DSHOT_MIN = 48;
|
||||||
|
const DSHOT_MAX = 2047;
|
||||||
|
const clampedValue = Math.max(0, Math.min(DSHOT_MAX, rawValue));
|
||||||
|
document.getElementById('throttleValue').innerText = clampedValue;
|
||||||
|
let percent = 0;
|
||||||
|
if (clampedValue > 0) {
|
||||||
|
percent = (clampedValue - DSHOT_MIN) / (DSHOT_MAX - DSHOT_MIN) * 100;
|
||||||
|
}
|
||||||
|
document.getElementById('throttlePercent').innerText = Math.round(percent) + '%';
|
||||||
|
document.getElementById('throttleSlider').value = clampedValue;
|
||||||
|
}
|
||||||
|
|
||||||
// Getting data from sketch
|
// Getting data from sketch
|
||||||
function onMessage(event) {
|
function onMessage(event) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -265,8 +279,7 @@ static constexpr char index_html[] = R"rawliteral(
|
||||||
// Sync web and serial throttle inputs
|
// Sync web and serial throttle inputs
|
||||||
if (data.throttle !== undefined) {
|
if (data.throttle !== undefined) {
|
||||||
if (isArmed) {
|
if (isArmed) {
|
||||||
document.getElementById('throttleSlider').value = data.throttle;
|
updateThrottleDisplays(data.throttle);
|
||||||
document.getElementById('throttleValue').innerText = data.throttle;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,8 +317,7 @@ static constexpr char index_html[] = R"rawliteral(
|
||||||
|
|
||||||
// If disarmed, set throttle to 0
|
// If disarmed, set throttle to 0
|
||||||
if (!isArmed) {
|
if (!isArmed) {
|
||||||
slider.value = 0;
|
updateThrottleDisplays(0);
|
||||||
sliderValue.innerText = 0;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -325,8 +337,7 @@ static constexpr char index_html[] = R"rawliteral(
|
||||||
armingStatus.className = 'status-disarmed';
|
armingStatus.className = 'status-disarmed';
|
||||||
throttleSection.classList.remove('armed');
|
throttleSection.classList.remove('armed');
|
||||||
slider.disabled = true;
|
slider.disabled = true;
|
||||||
slider.value = 0;
|
updateThrottleDisplays(0);
|
||||||
sliderValue.innerText = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,13 +345,12 @@ static constexpr char index_html[] = R"rawliteral(
|
||||||
slider.addEventListener('input', () => {
|
slider.addEventListener('input', () => {
|
||||||
if (!isArmed) {
|
if (!isArmed) {
|
||||||
slider.disabled = true;
|
slider.disabled = true;
|
||||||
slider.value = 0;
|
updateThrottleDisplays(0);
|
||||||
sliderValue.innerText = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const throttle = slider.value;
|
const throttle = slider.value;
|
||||||
sliderValue.innerText = throttle;
|
updateThrottleDisplays(throttle);
|
||||||
|
|
||||||
const message = JSON.stringify({
|
const message = JSON.stringify({
|
||||||
"throttle": parseInt(throttle),
|
"throttle": parseInt(throttle),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue