diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23500d5..8e95371 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,5 @@ name: ESP32 Build & Quality Check + permissions: contents: read @@ -8,80 +9,106 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: # ============================================================================ # Code Quality & Linting # ============================================================================ quality-check: - name: 'Arduino Lint Check' + name: Arduino Lint Check runs-on: ubuntu-latest timeout-minutes: 10 steps: - - name: Checkout Repository - uses: actions/checkout@v5 + - uses: actions/checkout@v5 - - name: Setup Arduino CLI - uses: arduino/setup-arduino-cli@v2 + - uses: arduino/setup-arduino-cli@v2 - - name: Install ESP32 core + - name: Cache Arduino Core + uses: actions/cache@v4 + with: + path: | + ~/.arduino15/packages + ~/.arduino15/cache + key: arduino-core-${{ runner.os }}-esp32-v1 + restore-keys: | + arduino-core-${{ runner.os }}- + + - name: Install ESP32 Core run: | - arduino-cli core update-index > /dev/null - arduino-cli core install esp32:esp32 > /dev/null + 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 - - name: Arduino Lint - uses: arduino/arduino-lint-action@v2 + - uses: arduino/arduino-lint-action@v2 with: path: ${{ github.workspace }} compliance: strict library-manager: update verbose: true -# ============================================================================ + # ============================================================================ # Compilation Test # ============================================================================ compile-test: - name: 'Compile Example Sketches' + name: Compile Example Sketches runs-on: ubuntu-latest timeout-minutes: 15 strategy: fail-fast: false matrix: - examples: - - "examples/dshot300/dshot300.ino" - - "examples/command_manager/command_manager.ino" - - "examples/web_control/web_control.ino" - - "examples/web_client/web_client.ino" + example: + - examples/dshot300/dshot300.ino + - examples/command_manager/command_manager.ino + - examples/web_control/web_control.ino + - examples/web_client/web_client.ino steps: - - name: Checkout Repository - uses: actions/checkout@v5 + - uses: actions/checkout@v5 - - name: Setup Arduino CLI - uses: arduino/setup-arduino-cli@v2 + - uses: arduino/setup-arduino-cli@v2 + + - name: Cache Arduino Core & Libraries + uses: actions/cache@v4 + with: + path: | + ~/.arduino15/packages + ~/.arduino15/cache + ~/Arduino/libraries + key: arduino-full-${{ runner.os }}-esp32-v1 + restore-keys: | + arduino-full-${{ runner.os }}- - name: Install ESP32 Core and Dependencies run: | - arduino-cli core update-index + 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 - arduino-cli lib install "ArduinoJson" - # Workround for ESPAsyncWebServer - git clone https://github.com/ESP32Async/ESPAsyncWebServer ~/Arduino/libraries/ESPAsyncWebServer - git clone https://github.com/ESP32Async/AsyncTCP ~/Arduino/libraries/AsyncTCP - + mkdir -p ~/Arduino/libraries + + # Cached repository check + if [ ! -d ~/Arduino/libraries/ESPAsyncWebServer ]; then + git clone --depth=1 https://github.com/ESP32Async/ESPAsyncWebServer ~/Arduino/libraries/ESPAsyncWebServer + fi + + if [ ! -d ~/Arduino/libraries/AsyncTCP ]; then + git clone --depth=1 https://github.com/ESP32Async/AsyncTCP ~/Arduino/libraries/AsyncTCP + fi + - name: Compile Sketch run: | - arduino-cli compile --fqbn esp32:esp32:esp32 --library ${{ github.workspace }} ${{ matrix.examples}} + arduino-cli compile \ + --fqbn esp32:esp32:esp32 \ + --library ${{ github.workspace }} \ + ${{ matrix.example }} # ============================================================================ # Build Status Report # ============================================================================ build-summary: - name: 'Build Summary' + name: Build Summary runs-on: ubuntu-latest if: always() needs: [quality-check, compile-test] @@ -90,32 +117,21 @@ jobs: - name: Create Build Summary run: | echo "# 🔧 DShotRMT Build Report" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Check | Status | Details |" >> $GITHUB_STEP_SUMMARY + echo "| Check | Status | Details |" >> $GITHUB_STEP_SUMMARY echo "|-------|--------|---------|" >> $GITHUB_STEP_SUMMARY - - # Quality Check Status - if [[ "${{ needs.quality-check.result }}" == "success" ]]; then - echo "| 📋 Quality Check | ✅ Passed | Arduino Lint completed successfully |" >> $GITHUB_STEP_SUMMARY - else - echo "| 📋 Quality Check | ❌ Failed | Check Arduino Lint report |" >> $GITHUB_STEP_SUMMARY - fi - - # Compile Test Status - if [[ "${{ needs.compile-test.result }}" == "success" ]]; then - echo "| 🔨 Compilation | ✅ Passed | All examples compiled successfully |" >> $GITHUB_STEP_SUMMARY - else - echo "| 🔨 Compilation | ❌ Failed | Compilation errors detected |" >> $GITHUB_STEP_SUMMARY - fi - + + [[ "${{ needs.quality-check.result }}" == "success" ]] \ + && echo "| 📋 Quality Check | ✅ Passed | Arduino Lint completed successfully |" >> $GITHUB_STEP_SUMMARY \ + || echo "| 📋 Quality Check | ❌ Failed | Check Arduino Lint report |" >> $GITHUB_STEP_SUMMARY + + [[ "${{ needs.compile-test.result }}" == "success" ]] \ + && echo "| 🔨 Compilation | ✅ Passed | All examples compiled successfully |" >> $GITHUB_STEP_SUMMARY \ + || echo "| 🔨 Compilation | ❌ Failed | Compilation errors detected |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY - - # Overall Status - if [[ "${{ needs.quality-check.result }}" == "success" && - "${{ needs.compile-test.result }}" == "success" ]]; then - echo "## 🎉 All Checks Passed!" >> $GITHUB_STEP_SUMMARY - echo "Your DShotRMT library is ready for deployment." >> $GITHUB_STEP_SUMMARY - else - echo "## ⚠️ Action Required" >> $GITHUB_STEP_SUMMARY - echo "Please review the failed checks and address any issues." - fi + + [[ "${{ needs.quality-check.result }}" == "success" && "${{ needs.compile-test.result }}" == "success" ]] \ + && echo "## 🎉 All Checks Passed!" >> $GITHUB_STEP_SUMMARY \ + && echo "Your DShotRMT library is ready for deployment." >> $GITHUB_STEP_SUMMARY \ + || echo "## ⚠️ Action Required" >> $GITHUB_STEP_SUMMARY \ + && echo "Please review the failed checks and address any issues." >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore index b2a6323..2b2b912 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,3 @@ -.vs -.vscode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - # Local History for Visual Studio Code .history/ diff --git a/.vscode/arduino.json b/.vscode/arduino.json new file mode 100644 index 0000000..b6596e1 --- /dev/null +++ b/.vscode/arduino.json @@ -0,0 +1,7 @@ +{ + "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" +} \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..8add28d --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,899 @@ +{ + "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" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b961c02 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "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" + } +} \ No newline at end of file diff --git a/README.md b/README.md index fd1382b..44bb558 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ The library requires these additional libraries for full functionality: **Core DShotRMT (always required):** - ESP32 Arduino Core -**Web Interface Example (dshot300.ino):** +**Web Interface Example (web_control.ino / web_client.ino):** ```ini lib_deps = https://github.com/derdoktor667/DShotRMT @@ -101,92 +101,6 @@ void loop() { } } ``` - -### Web Control Interface - -```cpp -#include -#include -#include - -DShotRMT motor(17, DSHOT300, false); -AsyncWebServer server(80); -AsyncWebSocket ws("/ws"); - -void setup() { - // Initialize motor - motor.begin(); - - // Create WiFi Access Point - WiFi.softAP("DShotRMT Control", "12345678"); - - // Setup web interface - ws.onEvent(onWsEvent); - server.addHandler(&ws); - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send_P(200, "text/html", index_html); - }); - server.begin(); - - // Access at http://10.10.10.1 -} - -void loop() { - // Handle WebSocket communication and motor control - ws.cleanupClients(); -} -``` - -### Advanced Command Management - -```cpp -#include -#include - -DShotRMT motor(17, DSHOT300, false); -DShotCommandManager cmdManager(motor); - -void setup() { - motor.begin(); - cmdManager.begin(); -} - -void loop() { - // High-level ESC control - cmdManager.stopMotor(); - cmdManager.activateBeacon(1); - cmdManager.setSpinDirection(false); - cmdManager.executeInitSequence(); -} -``` - -### Bidirectional DShot (RPM Telemetry) - -```cpp -#include - -// Enable bidirectional mode for telemetry -DShotRMT motor(17, DSHOT300, true); - -void setup() { - Serial.begin(115200); - motor.begin(); -} - -void loop() { - // Send throttle - motor.sendThrottle(1000); - - // Get telemetry data - dshot_telemetry_result_t telemetry = motor.getTelemetry(14); // 14 magnets - if (telemetry.success) { - Serial.printf("eRPM: %u, Motor RPM: %u\n", - telemetry.erpm, - telemetry.motor_rpm); - } -} -``` - --- ## 🌐 Web Control Interface @@ -228,7 +142,7 @@ Make sure you are using these libraries for [ESPAsyncWebServer](https://github.c The library includes comprehensive examples: -### 1. Basic DShot Control with Web Interface (`dshot300.ino`) +### 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 @@ -257,8 +171,8 @@ Interactive ESC control with full menu system: 0 - Emergency Stop Advanced Commands: - cmd - Send DShot command (0-47) - throttle - Set throttle (48-2047) + cmd - Send DShot command (0 - 47) + throttle - Set throttle (48 - 2047) repeat cmd count - Repeat command ``` @@ -274,6 +188,8 @@ Advanced Commands: | 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 @@ -294,14 +210,14 @@ DShotRMT motor(17, DSHOT300, true); | Command | Value | Description | Usage | |---------|-------|-------------|-------| | MOTOR_STOP | 0 | Stop motor | Always available | -| BEACON1 - 5 | 1 - 5 | Motor beeping | Motor identification | +| 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 | -| LED0-3_ON/OFF | 22 - 29 | LED control | BLHeli32 only | +| 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 | @@ -341,7 +257,7 @@ The library utilizes the ESP32's RMT (Remote Control) peripheral for precise sig ### Advantages - **Hardware Timing:** No CPU intervention during transmission - **Concurrent Operation:** Multiple channels can run simultaneously -- **DMA Support:** Efficient memory-to-peripheral transfers +- **DMA Support:** Efficient, automatic memory-to-peripheral transfers --- diff --git a/examples/command_manager/command_manager.ino b/examples/command_manager/command_manager.ino index 13b2228..4bc7bdd 100644 --- a/examples/command_manager/command_manager.ino +++ b/examples/command_manager/command_manager.ino @@ -308,13 +308,6 @@ 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("GPIO Pin: %d\n", motor01.getGPIO()); - USB_SERIAL.printf("DShot Mode: DSHOT%d\n", - motor01.getMode() == DSHOT150 ? 150 : motor01.getMode() == DSHOT300 ? 300 - : motor01.getMode() == DSHOT600 ? 600 - : motor01.getMode() == DSHOT1200 ? 1200 - : 0); - USB_SERIAL.printf("Bidirectional: %s\n", motor01.is_bidirectional() ? "YES" : "NO"); USB_SERIAL.printf("Free heap: %u bytes\n", ESP.getFreeHeap()); USB_SERIAL.printf("Uptime: %lu seconds\n", millis() / 1000); } diff --git a/examples/web_client/web_client.ino b/examples/web_client/web_client.ino index 79c9004..3bf66f7 100644 --- a/examples/web_client/web_client.ino +++ b/examples/web_client/web_client.ino @@ -10,6 +10,7 @@ #include #include +#include #include #include diff --git a/examples/web_control/web_control.ino b/examples/web_control/web_control.ino index 3f1e400..5db9332 100644 --- a/examples/web_control/web_control.ino +++ b/examples/web_control/web_control.ino @@ -10,6 +10,7 @@ #include #include +#include #include #include diff --git a/library.properties b/library.properties index 346113b..753180c 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DShotRMT -version=0.7.6 +version=0.8.0 author=Wastl Kraus maintainer=Wastl Kraus license=MIT @@ -8,5 +8,5 @@ paragraph=This library can control a BlHeli_S by using encoded DShot commands. F category=Signal Input/Output url=https://github.com/derdoktor667/DShotRMT architectures=esp32 -provides_includes=DShotRMT.h, DShotCommandManager.h, dshot_commands.h, web_content.h +provides_includes=DShotRMT.h, DShotCommandManager.h, dshot_commands.h depends=ArduinoJson \ No newline at end of file diff --git a/src/DShotCommandManager.h b/src/DShotCommandManager.h index 1c7dcff..9b05fc3 100644 --- a/src/DShotCommandManager.h +++ b/src/DShotCommandManager.h @@ -10,7 +10,6 @@ #include #include -#include // Command item typedef struct diff --git a/src/DShotRMT.cpp b/src/DShotRMT.cpp index d64cdec..e46a521 100644 --- a/src/DShotRMT.cpp +++ b/src/DShotRMT.cpp @@ -8,27 +8,41 @@ #include "DShotRMT.h" +// Static Data & Helper Functions // Timing parameters for each DShot mode -// Format: {frame_length_us, ticks_per_bit, ticks_one_high, ticks_one_low, ticks_zero_high, ticks_zero_low} -static constexpr dshot_timing_t DSHOT_TIMINGS[] = { - {0, 0, 0, 0, 0, 0}, // DSHOT_OFF - {128, 64, 48, 16, 24, 40}, // DSHOT150 - {64, 32, 24, 8, 12, 20}, // DSHOT300 - {32, 16, 12, 4, 6, 10}, // DSHOT600 - {16, 8, 6, 2, 3, 5} // DSHOT1200 -}; +// Format: {bit_length_us, t1h_length_us} +static constexpr dshot_timing_us_t DSHOT_TIMING_US[] = { + {0.00, 0.00}, + {6.67, 5.00}, + {3.33, 2.50}, + {1.67, 1.25}, + {0.83, 0.67}}; +// Helper function to print DShot results and telemetry +void printDShotResult(dshot_result_t &result, Stream &output) +{ + output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", result.msg); + + // Print telemetry data if available + if (result.success && (result.erpm > 0 || result.motor_rpm > 0)) + { + output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm); + } + + output.println(); +} + +// Constructors & Destructor // Constructor with GPIO number DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) : _gpio(gpio), _mode(mode), _is_bidirectional(is_bidirectional), - _last_erpm_atomic(0), - _telemetry_ready_flag(false), + _dshot_timing(DSHOT_TIMING_US[mode]), _frame_timer_us(0), - _timing_config(DSHOT_TIMINGS[mode]), + _rmt_ticks{0}, _last_throttle(DSHOT_CMD_MOTOR_STOP), - _last_transmission_time(0), + _last_transmission_time_us(0), _parsed_packet(0), _packet{0}, _bitPositions{0}, @@ -39,30 +53,27 @@ DShotRMT::DShotRMT(gpio_num_t gpio, dshot_mode_t mode, bool is_bidirectional) _dshot_encoder(nullptr), _tx_channel_config{}, _rx_channel_config{}, - _transmit_config{}, - _receive_config{} + _rmt_tx_config{}, + _rmt_rx_config{}, + _rx_event_callbacks{}, + _last_erpm_atomic(0), + _telemetry_ready_flag_atomic(false) { - // Calculate frame timing including switch/pause time - _frame_timer_us = _timing_config.frame_length_us + DSHOT_PAUSE_US; - - // Double frame time for bidirectional mode (includes response time) - if (_is_bidirectional) - { - _frame_timer_us = (_frame_timer_us << 1); - } + // Configure RMT ticks for DShot timings + _preCalculateRMTTicks(); } // Constructor using pin number DShotRMT::DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional) - : DShotRMT((gpio_num_t)pin_nr, mode, is_bidirectional) + : DShotRMT(static_cast(pin_nr), mode, is_bidirectional) { // Delegates to primary constructor with type cast } -// Destructor for "better" code +// Destructor DShotRMT::~DShotRMT() { - // ...TX + // Cleanup TX channel if (_rmt_tx_channel) { if (rmt_disable(_rmt_tx_channel) == DSHOT_OK) @@ -72,7 +83,7 @@ DShotRMT::~DShotRMT() } } - // ...RX + // Cleanup RX channel if (_rmt_rx_channel) { if (rmt_disable(_rmt_rx_channel) == DSHOT_OK) @@ -82,7 +93,7 @@ DShotRMT::~DShotRMT() } } - // ...Encoder + // Cleanup encoder if (_dshot_encoder) { rmt_del_encoder(_dshot_encoder); @@ -90,10 +101,12 @@ DShotRMT::~DShotRMT() } } -// Init DShotRMT + +// Public Core Functions +// Initialize DShotRMT dshot_result_t DShotRMT::begin() { - // Init RX channel first + // Init RX channel first (for bidirectional mode) if (_is_bidirectional) { if (!_initRXChannel().success) @@ -120,105 +133,6 @@ dshot_result_t DShotRMT::begin() return {true, INIT_SUCCESS}; } -// Init RMT TX channel -dshot_result_t DShotRMT::_initTXChannel() -{ - // Configure TX channel - _tx_channel_config.gpio_num = _gpio; - _tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; - _tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; - _tx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; - _tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH; - - // Config RMT TX - _transmit_config.loop_count = 0; // No automatic loops - real-time calculation - _transmit_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) - { - return {false, TX_INIT_FAILED}; - } - - // - if (rmt_enable(_rmt_tx_channel) != DSHOT_OK) - { - return {false, TX_INIT_FAILED}; - } - - return {true, TX_INIT_SUCCESS}; -} - -// Init RMT RX channel -dshot_result_t DShotRMT::_initRXChannel() -{ - - // Direct RMT symbol processing - Performance optimized - _rx_event_callbacks.on_recv_done = _rmt_rx_done_callback; - - // Config RMT RX - _rx_channel_config.gpio_num = _gpio; - _rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; - _rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; - _rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; - - // Config RMT RX parameters - _receive_config.signal_range_min_ns = DSHOT_PULSE_MIN; - _receive_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) - { - return {false, RX_INIT_FAILED}; - } - - // - if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) - { - return {false, RX_INIT_FAILED}; - } - - return {true, RX_INIT_SUCCESS}; -} - -// Callback for RMT RX -bool DShotRMT::_rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data) -{ - DShotRMT *instance = static_cast(user_data); - - // ISR check for valid data - 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); - - if (erpm != DSHOT_NULL_PACKET) - { - // Atomic writes - thread-safe - instance->_last_erpm_atomic = erpm; - instance->_telemetry_ready_flag = true; - } - } - - return false; -} - -// Initialize DShot encoder -dshot_result_t DShotRMT::_initDShotEncoder() -{ - // Create copy encoder configuration - rmt_copy_encoder_config_t encoder_config = {}; - - // Create encoder instance - if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) - { - return {false, ENCODER_INIT_FAILED}; - } - - return {true, TX_INIT_SUCCESS}; -} - // Send throttle value dshot_result_t DShotRMT::sendThrottle(uint16_t throttle) { @@ -258,7 +172,7 @@ dshot_result_t DShotRMT::sendCommand(uint16_t command) dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) { // Result container with unified structure - dshot_result_t result = {false, TELEMETRY_FAILED, NO_DSHOT_ERPM, NO_DSHOT_RPM}; + dshot_result_t result = {false, TELEMETRY_FAILED, NO_DSHOT_TELEMETRY, NO_DSHOT_TELEMETRY}; // Check if bidirectional mode is enabled if (!_is_bidirectional) @@ -267,17 +181,17 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) return result; } - // - if (_telemetry_ready_flag) + // Check for new telemetry data + if (_telemetry_ready_flag_atomic) { - _telemetry_ready_flag = false; + _telemetry_ready_flag_atomic = false; uint16_t erpm = _last_erpm_atomic; - // + // Calculate motor RPM from eRPM if (erpm != DSHOT_NULL_PACKET && magnet_count >= 1) { - uint8_t pole_pairs = max(MIN_POLE_PAIRS, (magnet_count / MAGNETS_PER_POLE_PAIR)); + uint8_t pole_pairs = max(POLE_PAIRS_MIN, (magnet_count / MAGNETS_PER_POLE_PAIR)); uint32_t motor_rpm = (erpm / pole_pairs); result.success = true; @@ -290,216 +204,7 @@ dshot_result_t DShotRMT::getTelemetry(uint16_t magnet_count) return result; } -// Build a complete DShot packet -dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t value) -{ - // Init packet structure - 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 & 0b0000011111111111; - packet.telemetric_request = _is_bidirectional ? 1 : 0; - - // CRC is calculated over 11bit - uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request; - - packet.checksum = _calculateCRC(data); - - return packet; -} - -// Parse DShot packet into 16-bit format -uint16_t DShotRMT::_parseDShotPacket(const dshot_packet_t &packet) -{ - // Parse DShot frame into "raw" 16 bit value - uint16_t data_and_telemetry = (packet.throttle_value << 1) | packet.telemetric_request; - uint16_t parsed_packet = (data_and_telemetry << 4) | packet.checksum; - - return parsed_packet; -} - -// Calculate CRC -uint16_t DShotRMT::_calculateCRC(const uint16_t data) -{ - // DShot CRC - uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & DSHOT_CRC_MASK; - - // Invert CRC for bidirectional DShot mode - if (_is_bidirectional) - { - crc = (~crc) & DSHOT_CRC_MASK; - } - - return crc; -} - -// Per calculate bits - Performance optimized -void DShotRMT::_preCalculateBitPositions() -{ - for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i) - { - _bitPositions[i] = DSHOT_BITS_PER_FRAME - 1 - i; - } -} - -// Transmit DShot packet via RMT -dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) -{ - // Check timing requirements - if (!_timer_signal()) - { - return {false, TIMING_CORRECTION}; - } - - // Enable RMT RX before RMT TX - 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[DSHOT_BITS_PER_FRAME]; - - if (rmt_receive(_rmt_rx_channel, rx_symbols, rx_size_bytes, &_receive_config) != DSHOT_OK) - { - return {false, RECEIVER_FAILED}; - } - } - - // Local for performance - rmt_symbol_word_t tx_symbols[DSHOT_BITS_PER_FRAME]; - - // Encode DShot packet into RMT symbols - _encodeDShotFrame(packet, tx_symbols); - - // Calculate transmission data size - 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, &_transmit_config) != DSHOT_OK) - { - return {false, TRANSMISSION_FAILED}; - } - - // Re-enable RMT RX - 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}; -} - -// Encode DShot packet into RMT symbol format (placed in IRAM for performance) -bool DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols) -{ - _parsed_packet = _parseDShotPacket(packet); - - // Decode MSB - for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i) - { - // Use precalculated bit positions - Performace optimized - int bit_position = _bitPositions[i]; - - bool bit = (_parsed_packet >> bit_position) & 0b0000000000000001; - symbols[i].level0 = _level0; - symbols[i].duration0 = bit ? _timing_config.ticks_one_high : _timing_config.ticks_zero_high; - symbols[i].level1 = _level1; - symbols[i].duration1 = bit ? _timing_config.ticks_one_low : _timing_config.ticks_zero_low; - } - - return DSHOT_OK; -} - -// Decodes a DShot telemetry frame from received RMT symbols. -uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) -{ - uint32_t gcr_value = 0; - - // Decode GCR symbols into a 21-bit value. - // '1' has a longer low pulse (duration0 > duration1). - // '0' has a longer high pulse (duration1 > duration0). - for (size_t i = 0; i < GCR_BITS_PER_FRAME; ++i) - { - bool bit_is_one = symbols[i].duration0 > symbols[i].duration1; - gcr_value = (gcr_value << 1) | bit_is_one; - } - - // Perform GCR decoding: data = gcr ^ (gcr >> 1). - uint32_t decoded_frame = gcr_value ^ (gcr_value >> 1); - - // Extract 16 data bits and 4 CRC bits from 20-bit frame. - // The first bit of the GCR frame is a start bit and is discarded. - uint16_t data_and_crc = (decoded_frame & DSHOT_FULL_PACKET); - - // Cutting 4 bits? - uint16_t received_data = data_and_crc >> 4; - - // Masking CRC - uint16_t received_crc = data_and_crc & DSHOT_CRC_MASK; - - // Telemetry request bit has to be 1 - if (!(received_data & (1 << 11))) - { - return DSHOT_NULL_PACKET; - } - - // Calculate expected CRC - uint16_t data_for_crc = received_data; - uint16_t calculated_crc = _calculateCRC(data_for_crc); - - // Validate CRC - if (received_crc != calculated_crc) - { - return DSHOT_NULL_PACKET; - } - - // Return the eRPM value (first 11 bits of received data). - return received_data & DSHOT_THROTTLE_MAX; -} - -// Check if enough time has passed for next transmission -bool DShotRMT::_timer_signal() -{ - uint64_t current_time = esp_timer_get_time(); - - // Handle potential overflow - uint64_t elapsed = current_time - _last_transmission_time; - - return elapsed >= _frame_timer_us; -} - -// Reset transmission timer to current time -bool DShotRMT::_timer_reset() -{ - _last_transmission_time = esp_timer_get_time(); - - return DSHOT_OK; -} - +// Public Info & Debug Functions // Print timing diagnostic information to specified stream void DShotRMT::printDShotInfo(Stream &output) const { @@ -507,11 +212,11 @@ void DShotRMT::printDShotInfo(Stream &output) const output.println(" === DShot Signal Info === "); // Current DShot mode - output.printf("Current Mode: DSHOT%d\n", - _mode == DSHOT150 ? 150 : - _mode == DSHOT300 ? 300 : - _mode == DSHOT600 ? 600 : - _mode == DSHOT1200 ? 1200 : 0); + 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"); @@ -521,7 +226,7 @@ void DShotRMT::printDShotInfo(Stream &output) const // Print bit by bit for (int i = DSHOT_BITS_PER_FRAME - 1; i >= 0; --i) { - if ((_parsed_packet >> i) & 1) + if ((_parsed_packet >> i) & 0b0000000000000001) { output.print("1"); } @@ -547,16 +252,330 @@ void DShotRMT::printCpuInfo(Stream &output) const output.printf("APB Freq = %lu Hz\n", getApbFrequency()); } -// --- HELPERS --- -void printDShotResult(dshot_result_t &result, Stream &output) +// Private Initialization Functions +// Initialize RMT TX channel +dshot_result_t DShotRMT::_initTXChannel() { - output.printf("Status: %s - %s", result.success ? "SUCCESS" : "FAILED", result.msg); + // Configure TX channel + _tx_channel_config.gpio_num = _gpio; + _tx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; + _tx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; + _tx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; + _tx_channel_config.trans_queue_depth = RMT_QUEUE_DEPTH; - // Print telemetry data if available - if (result.success && (result.erpm > 0 || result.motor_rpm > 0)) + // Config RMT TX + _rmt_tx_config.loop_count = 0; // No automatic loops - real-time calculation + _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) { - output.printf(" | eRPM: %u, Motor RPM: %u", result.erpm, result.motor_rpm); + return {false, TX_INIT_FAILED}; } - output.println(); + // Enable TX channel + if (rmt_enable(_rmt_tx_channel) != DSHOT_OK) + { + return {false, TX_INIT_FAILED}; + } + + return {true, TX_INIT_SUCCESS}; +} + +// Initialize RMT RX channel +dshot_result_t DShotRMT::_initRXChannel() +{ + // Direct RMT symbol processing - Performance optimized + _rx_event_callbacks.on_recv_done = _on_rx_done; + + // Config RMT RX + _rx_channel_config.gpio_num = _gpio; + _rx_channel_config.clk_src = DSHOT_CLOCK_SRC_DEFAULT; + _rx_channel_config.resolution_hz = DSHOT_RMT_RESOLUTION; + _rx_channel_config.mem_block_symbols = RMT_BUFFER_SYMBOLS; + + // Config RMT RX parameters + _rmt_rx_config.signal_range_min_ns = DSHOT_PULSE_MIN; + _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) + { + return {false, RX_INIT_FAILED}; + } + + // Enable RX channel + if (rmt_enable(_rmt_rx_channel) != DSHOT_OK) + { + return {false, RX_INIT_FAILED}; + } + + return {true, RX_INIT_SUCCESS}; +} + +// Initialize DShot encoder +dshot_result_t DShotRMT::_initDShotEncoder() +{ + // Create copy encoder configuration + rmt_copy_encoder_config_t encoder_config = {}; + + // Create encoder instance + if (rmt_new_copy_encoder(&encoder_config, &_dshot_encoder) != DSHOT_OK) + { + return {false, ENCODER_INIT_FAILED}; + } + + return {true, ENCODER_INIT_SUCCESS}; +} + +// Private Packet Management Functions +// Build a complete DShot packet from a valid value +dshot_packet_t DShotRMT::_buildDShotPacket(const uint16_t &value) +{ + // Init packet structure + 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.telemetric_request = _is_bidirectional ? 1 : 0; + + // CRC is calculated over 12bit + uint16_t data = (packet.throttle_value << 1) | packet.telemetric_request; + + packet.checksum = _calculateCRC(data); + + return packet; +} + +// Parse DShot packet into 16-bit format +uint16_t DShotRMT::_parseDShotPacket(const dshot_packet_t &packet) +{ + // Parse DShot frame into "raw" 16 bit value + uint16_t data_and_telemetry = (packet.throttle_value << 1) | packet.telemetric_request; + return (data_and_telemetry << 4) | packet.checksum; +} + +// Calculate CRC +uint16_t DShotRMT::_calculateCRC(const uint16_t &data) +{ + // DShot CRC + uint16_t crc = (data ^ (data >> 4) ^ (data >> 8)) & DSHOT_CRC_MASK; + + // Invert CRC for bidirectional DShot mode + if (_is_bidirectional) + { + crc = (~crc) & DSHOT_CRC_MASK; + } + + return crc; +} + +// Configure RMT ticks for DShot timings +void DShotRMT::_preCalculateRMTTicks() +{ + // Convert DShot timings (us) to RMT ticks + _rmt_ticks.bit_length_ticks = static_cast(_dshot_timing.bit_length_us * RMT_TICKS_PER_US); + _rmt_ticks.t1h_ticks = static_cast(_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.t1l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t1h_ticks; + _rmt_ticks.t0l_ticks = _rmt_ticks.bit_length_ticks - _rmt_ticks.t0h_ticks; + + // Pause between frames is frame time in us, some padding and about 30 us is added by hardware + _frame_timer_us = (static_cast(_dshot_timing.bit_length_us * DSHOT_BITS_PER_FRAME) << 1) + DSHOT_PADDING_US; + + // Double frame time for bidirectional mode (includes response time) + if (_is_bidirectional) + { + _frame_timer_us = (_frame_timer_us << 1); + } +} + +// Precalculate bit positions for performance optimization +void DShotRMT::_preCalculateBitPositions() +{ + for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i) + { + _bitPositions[i] = DSHOT_BITS_PER_FRAME - 1 - i; + } +} + +// Private Frame Processing Functions +// Transmit DShot packet via RMT +dshot_result_t DShotRMT::_sendDShotFrame(const dshot_packet_t &packet) +{ + // Check timing requirements + if (!_timer_signal()) + { + return {false, TIMING_CORRECTION}; + } + + // 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]; + + // Encode DShot packet into RMT symbols + _encodeDShotFrame(packet, tx_symbols); + + // Calculate transmission data size + 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) + { + return {false, TRANSMISSION_FAILED}; + } + + // Re-enable RMT RX + 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}; +} + +// Encode DShot packet into RMT symbol format (placed in IRAM for performance) +dshot_result_t IRAM_ATTR DShotRMT::_encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols) +{ + _parsed_packet = _parseDShotPacket(packet); + + // Decode MSB + for (int i = 0; i < DSHOT_BITS_PER_FRAME; ++i) + { + // Use precalculated bit positions - Performance optimized + int bit_position = _bitPositions[i]; + + bool bit = (_parsed_packet >> bit_position) & 0b0000000000000001; + symbols[i].level0 = _level0; + symbols[i].duration0 = bit ? _rmt_ticks.t1h_ticks : _rmt_ticks.t0h_ticks; + symbols[i].level1 = _level1; + symbols[i].duration1 = bit ? _rmt_ticks.t1l_ticks : _rmt_ticks.t0l_ticks; + } + + return {true, ENCODING_SUCCESS}; +} + +// Decode DShot telemetry frame from received RMT symbols +uint16_t DShotRMT::_decodeDShotFrame(const rmt_symbol_word_t *symbols) +{ + uint32_t gcr_value = 0; + + // Decode GCR symbols into a 21-bit value. + // '1' has a longer low pulse (duration0 > duration1). + // '0' has a longer high pulse (duration1 > duration0). + for (size_t i = 0; i < GCR_BITS_PER_FRAME; ++i) + { + bool bit_is_one = symbols[i].duration0 > symbols[i].duration1; + gcr_value = (gcr_value << 1) | bit_is_one; + } + + // Perform GCR decoding: data = gcr ^ (gcr >> 1). + uint32_t decoded_frame = gcr_value ^ (gcr_value >> 1); + + // Extract 16 data bits and 4 CRC bits from 20-bit frame. + // The first bit of the GCR frame is a start bit and is discarded. + uint16_t data_and_crc = (decoded_frame & DSHOT_FULL_PACKET); + + // Extract data (first 12 bits) and CRC (last 4 bits) + uint16_t received_data = data_and_crc >> 4; + uint16_t received_crc = data_and_crc & DSHOT_CRC_MASK; + + // Telemetry request bit has to be 1 + if (!(received_data & (1 << 11))) + { + return DSHOT_NULL_PACKET; + } + + // Calculate expected CRC + uint16_t data_for_crc = received_data; + uint16_t calculated_crc = _calculateCRC(data_for_crc); + + // Validate CRC + if (received_crc != calculated_crc) + { + return DSHOT_NULL_PACKET; + } + + // Return the eRPM value (first 11 bits of received data). + return received_data & DSHOT_THROTTLE_MAX; +} + +// Private Timing Control Functions +// Check if enough time has passed for next transmission +bool IRAM_ATTR DShotRMT::_timer_signal() +{ + uint64_t current_time = esp_timer_get_time(); + + // Handle potential overflow + uint64_t elapsed = current_time - _last_transmission_time_us; + + return elapsed >= _frame_timer_us; +} + +// Reset transmission timer to current time +bool DShotRMT::_timer_reset() +{ + _last_transmission_time_us = esp_timer_get_time(); + + return DSHOT_OK; +} + +// Static Callback Functions +// Callback for RMT RX +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(user_data); + + // ISR check for valid data + 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); + + if (erpm != DSHOT_NULL_PACKET) + { + // Atomic writes - thread-safe + instance->_last_erpm_atomic = erpm; + instance->_telemetry_ready_flag_atomic = true; + } + } + + return false; } diff --git a/src/DShotRMT.h b/src/DShotRMT.h index 3a4d466..5c82f64 100644 --- a/src/DShotRMT.h +++ b/src/DShotRMT.h @@ -10,42 +10,20 @@ #include #include -#include #include #include #include +#include -// DShot Protocol Constants +// DShot Protocol Constants & Types static constexpr auto DSHOT_THROTTLE_FAILSAFE = 0; static constexpr auto DSHOT_THROTTLE_MIN = 48; static constexpr auto DSHOT_THROTTLE_MAX = 2047; static constexpr auto DSHOT_BITS_PER_FRAME = 16; -static constexpr auto DSHOT_PAUSE_US = 30; // Additional frame pause time -static constexpr auto DSHOT_NULL_PACKET = 0b0000000000000000; -static constexpr auto DSHOT_FULL_PACKET = 0b1111111111111111; -static constexpr auto DSHOT_CRC_MASK = 0b0000000000001111; -static constexpr auto DSHOT_RX_TIMEOUT_MS = 2; // Never reached, just a timeeout -static constexpr auto GCR_BITS_PER_FRAME = 21; // Number of GCR bits in a DShot answer frame (1 start + 16 data + 4 CRC) static constexpr auto DEFAULT_MOTOR_MAGNET_COUNT = 14; -static constexpr auto MAGNETS_PER_POLE_PAIR = 2; -static constexpr auto MIN_POLE_PAIRS = 1; -static constexpr auto NO_DSHOT_ERPM = 0; -static constexpr auto NO_DSHOT_RPM = 0; - -// RMT Configuration Constants -constexpr auto DSHOT_CLOCK_SRC_DEFAULT = RMT_CLK_SRC_DEFAULT; -constexpr auto DSHOT_RMT_RESOLUTION = 10 * 1000 * 1000; // 10 MHz resolution -constexpr auto RMT_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; -constexpr auto RMT_BUFFER_SYMBOLS = 64; -constexpr auto RMT_QUEUE_DEPTH = 1; - -// Smallest pulse for DShot1200 is 2us. Largest for DShot150 is 40us. -// The range is set from 3us (3000ns) to 60us (60000ns) to be safe across all modes. -constexpr uint32_t DSHOT_PULSE_MIN = 3000; -constexpr uint32_t DSHOT_PULSE_MAX = 60000; // DShot Modes -typedef enum +typedef enum dshot_mode { DSHOT_OFF, DSHOT150, @@ -54,8 +32,8 @@ typedef enum DSHOT1200 } dshot_mode_t; -// DShot Packet -typedef struct +// DShot Packet Structure +typedef struct dshot_packet { uint16_t throttle_value : 11; bool telemetric_request : 1; @@ -63,18 +41,24 @@ typedef struct } dshot_packet_t; // DShot Timing Configuration -typedef struct +typedef struct dshot_timing { - uint32_t frame_length_us; - uint16_t ticks_per_bit; - uint16_t ticks_one_high; - uint16_t ticks_one_low; - uint16_t ticks_zero_high; - uint16_t ticks_zero_low; -} dshot_timing_t; + double bit_length_us; + double t1h_lenght_us; +} dshot_timing_us_t; -// Unified DShot result structure -typedef struct +// RMT Timing Configuration +typedef struct rmt_ticks +{ + uint16_t bit_length_ticks; + uint16_t t1h_ticks; + uint16_t t1l_ticks; + uint16_t t0h_ticks; + uint16_t t0l_ticks; +} rmt_ticks_t; + +// Unified DShot Result Structure +typedef struct dshot_result { bool success; const char *msg; @@ -82,46 +66,41 @@ typedef struct uint16_t motor_rpm; } dshot_result_t; -// Naming convention +// Command Type Alias typedef dshotCommands_e dshot_commands_t; -// --- HELPERS --- +// Helper Functions void printDShotResult(dshot_result_t &result, Stream &output = Serial); // +// DShotRMT Main Class class DShotRMT { public: - // Constructor with GPIO enum + // Constructors & Destructor explicit DShotRMT(gpio_num_t gpio = GPIO_NUM_16, dshot_mode_t mode = DSHOT300, bool is_bidirectional = false); - - // Constructor with pin number DShotRMT(uint16_t pin_nr, dshot_mode_t mode, bool is_bidirectional); - - // Destructor for "better" code + ~DShotRMT(); + // Public Core Functions // Initialize the RMT module and DShot config dshot_result_t begin(); - - // Send throttle value (48-2047) + + // Send throttle value (48 - 2047) dshot_result_t sendThrottle(uint16_t throttle); - - // Send DShot command (0-47) + + // Send DShot command (0 - 47) dshot_result_t sendCommand(uint16_t command); - - // --- GETTERS --- - gpio_num_t getGPIO() const { return _gpio; } - uint16_t getDShotPacket() const { return _parsed_packet; } - bool is_bidirectional() const { return _is_bidirectional; } - dshot_mode_t getMode() const { return _mode; } + + // Get telemetry data (bidirectional mode only) dshot_result_t getTelemetry(uint16_t magnet_count = DEFAULT_MOTOR_MAGNET_COUNT); - - // --- INFO --- + + // Public Info & Debug Functions void printDShotInfo(Stream &output = Serial) const; void printCpuInfo(Stream &output = Serial) const; - - // --- DEPRECATED METHODS --- + + // Deprecated Methods [[deprecated("Use sendThrottle() instead")]] bool setThrottle(uint16_t throttle) { @@ -144,67 +123,30 @@ public: } private: - // --- CONFIG --- - gpio_num_t _gpio; - dshot_mode_t _mode; - bool _is_bidirectional; - uint32_t _frame_timer_us; - const dshot_timing_t &_timing_config; - uint16_t _last_throttle; - - // --- TIMING & PACKET VARIABLES --- - uint64_t _last_transmission_time; - uint16_t _parsed_packet; - dshot_packet_t _packet; - uint8_t _bitPositions[DSHOT_BITS_PER_FRAME]; - uint16_t _level0; - uint16_t _level1; - - // --- RMT HARDWARE HANDLES --- - rmt_channel_handle_t _rmt_tx_channel; - rmt_channel_handle_t _rmt_rx_channel; - rmt_encoder_handle_t _dshot_encoder; - - // --- RMT CONFIG STRUCTURES --- - rmt_tx_channel_config_t _tx_channel_config; - rmt_rx_channel_config_t _rx_channel_config; - rmt_transmit_config_t _transmit_config; - rmt_receive_config_t _receive_config; - - // --- INITS --- - dshot_result_t _initTXChannel(); - dshot_result_t _initRXChannel(); - dshot_result_t _initDShotEncoder(); - - // --- PACKET MANAGEMENT --- - dshot_packet_t _buildDShotPacket(const uint16_t value); - uint16_t _parseDShotPacket(const dshot_packet_t &packet); - uint16_t _calculateCRC(const uint16_t data); - void _preCalculateBitPositions(); - - // --- FRAME PROCESSING --- - dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); - bool IRAM_ATTR _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); - uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); - - // --- TIMING CONTROL --- - bool IRAM_ATTR _timer_signal(); - bool _timer_reset(); - - // -- CALLBACKS --- - rmt_rx_event_callbacks_t _rx_event_callbacks; - volatile rmt_symbol_word_t _rx_symbols_direct[GCR_BITS_PER_FRAME]; - volatile uint16_t _last_erpm_atomic; - volatile bool _telemetry_ready_flag; - static bool IRAM_ATTR _rmt_rx_done_callback(rmt_channel_handle_t rmt_rx_channel, const rmt_rx_done_event_data_t *edata, void *user_data); - - // --- DSHOT DEFAULTS --- - static constexpr auto const DSHOT_TELEMETRY_INVALID = (0xffff); - - // --- CONSTANTS & ERROR MESSAGES --- + // 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_FULL_PACKET = 0b1111111111111111; + 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_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_BUFFER_SIZE = DSHOT_BITS_PER_FRAME; + 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 RMT_BUFFER_SYMBOLS = 64; + 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 POLE_PAIRS_MIN = 1; + static constexpr auto const MAGNETS_PER_POLE_PAIR = 2; + 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_MAX = 60000; // 60us maximum pulse + static constexpr auto const DSHOT_TELEMETRY_INVALID = DSHOT_THROTTLE_MAX; + // Error Messages static constexpr char const *NONE = ""; static constexpr char const *UNKNOWN_ERROR = "Unknown Error!"; static constexpr char const *INIT_SUCCESS = "SignalGeneratorRMT initialized successfully"; @@ -213,9 +155,9 @@ private: static constexpr char const *TX_INIT_FAILED = "TX RMT channel init failed!"; static constexpr char const *RX_INIT_SUCCESS = "RX RMT channel initialized successfully"; static constexpr char const *RX_INIT_FAILED = "RX RMT channel init failed!"; - static constexpr char const *RX_BUFFER_FAILED = "RX RMT buffer init failed!"; static constexpr char const *ENCODER_INIT_SUCCESS = "RMT encoder initialized successfully"; static constexpr char const *ENCODER_INIT_FAILED = "RMT encoder init failed!"; + static constexpr char const *ENCODING_SUCCESS = "Packet encoded successfully"; static constexpr char const *TRANSMISSION_SUCCESS = "Transmission successfully"; static constexpr char const *TRANSMISSION_FAILED = "Transmission failed!"; static constexpr char const *RECEIVER_FAILED = "RMT receiver failed!"; @@ -226,4 +168,61 @@ private: 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 *TIMING_CORRECTION = "Timing correction!"; + + // Core Configuration Variables + gpio_num_t _gpio; + dshot_mode_t _mode; + bool _is_bidirectional; + const dshot_timing_us_t &_dshot_timing; + uint64_t _frame_timer_us; + + // Timing & Packet Variables + rmt_ticks_t _rmt_ticks; + uint16_t _last_throttle; + uint64_t _last_transmission_time_us; + uint16_t _parsed_packet; + dshot_packet_t _packet; + uint8_t _bitPositions[DSHOT_BITS_PER_FRAME]; + uint16_t _level0; + uint16_t _level1; + + // RMT Hardware Handles + rmt_channel_handle_t _rmt_tx_channel; + rmt_channel_handle_t _rmt_rx_channel; + rmt_encoder_handle_t _dshot_encoder; + + // RMT Configuration Structures + rmt_tx_channel_config_t _tx_channel_config; + rmt_rx_channel_config_t _rx_channel_config; + rmt_transmit_config_t _rmt_tx_config; + rmt_receive_config_t _rmt_rx_config; + + // Bidirectional / Telemetry Variables + rmt_rx_event_callbacks_t _rx_event_callbacks; + std::atomic _last_erpm_atomic; + std::atomic _telemetry_ready_flag_atomic; + + // Private Initialization Functions + dshot_result_t _initTXChannel(); + dshot_result_t _initRXChannel(); + dshot_result_t _initDShotEncoder(); + + // Private Packet Management Functions + dshot_packet_t _buildDShotPacket(const uint16_t &value); + uint16_t _parseDShotPacket(const dshot_packet_t &packet); + uint16_t _calculateCRC(const uint16_t &data); + void _preCalculateRMTTicks(); + void _preCalculateBitPositions(); + + // Private Frame Processing Functions + dshot_result_t _sendDShotFrame(const dshot_packet_t &packet); + dshot_result_t _encodeDShotFrame(const dshot_packet_t &packet, rmt_symbol_word_t *symbols); + uint16_t _decodeDShotFrame(const rmt_symbol_word_t *symbols); + + // Private Timing Control Functions + bool _timer_signal(); + bool _timer_reset(); + + // 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); }; diff --git a/src/web_content.h b/src/web_content.h index 65bbe01..2f7459d 100644 --- a/src/web_content.h +++ b/src/web_content.h @@ -1,15 +1,15 @@ /** * @file web_content.h - * @brief DShotRMT_Control Website content with Arming Switch + * @brief DShot signal generation using ESP32 RMT with bidirectional support * @author Wastl Kraus - * @date 2025-09-09 + * @date 2025-09-13 * @license MIT */ #pragma once // Web Site Content -const char index_html[] PROGMEM = R"rawliteral( +static constexpr char index_html[] = R"rawliteral( @@ -357,5 +357,4 @@ const char index_html[] PROGMEM = R"rawliteral( - -)rawliteral"; \ No newline at end of file +)rawliteral";