├── .codespellrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── stale.yml └── workflows │ ├── auto-github-actions.yml │ ├── check-arduino.yml │ ├── report-size-deltas.yml │ └── spell-check.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── changelog.md ├── examples ├── ESP32_ISR_MultiServos │ └── ESP32_ISR_MultiServos.ino ├── ESP32_MultipleRandomServos │ └── ESP32_MultipleRandomServos.ino ├── ESP32_MultipleServos │ └── ESP32_MultipleServos.ino ├── ISR_MultiServos │ └── ISR_MultiServos.ino ├── MultipleRandomServos │ └── MultipleRandomServos.ino ├── MultipleServos │ └── MultipleServos.ino └── multiFileProject │ ├── multiFileProject.cpp │ ├── multiFileProject.h │ └── multiFileProject.ino ├── keywords.txt ├── library.json ├── library.properties ├── platformio └── platformio.ini ├── src ├── ESP32FastTimerInterrupt.hpp ├── ESP32_ISR_Servo.h ├── ESP32_ISR_Servo.hpp ├── ESP32_ISR_Servo_Debug.h └── ESP32_ISR_Servo_Impl.h └── utils ├── astyle_library.conf └── restyle.sh /.codespellrc: -------------------------------------------------------------------------------- 1 | # See: https://github.com/codespell-project/codespell#using-a-config-file 2 | [codespell] 3 | # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: 4 | ignore-words-list = , 5 | check-filenames = 6 | check-hidden = 7 | skip = ./.git,./src,./examples,./Packages_Patches,./LibraryPatches 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### Describe the bug 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | ### Steps to Reproduce 15 | 16 | Steps to reproduce the behavior. Including the [MRE](https://stackoverflow.com/help/minimal-reproducible-example) sketches 17 | 18 | ### Expected behavior 19 | 20 | A clear and concise description of what you expected to happen. 21 | 22 | ### Actual behavior 23 | 24 | A clear and concise description of what you expected to happen. 25 | 26 | ### Debug and AT-command log (if applicable) 27 | 28 | A clear and concise description of what you expected to happen. 29 | 30 | ### Screenshots 31 | 32 | If applicable, add screenshots to help explain your problem. 33 | 34 | ### Information 35 | 36 | Please ensure to specify the following: 37 | 38 | * Arduino IDE version (e.g. 1.8.19) or Platform.io version 39 | * `ESP32` Core Version (e.g. ESP32 core v2.0.5) 40 | * `ESP32` Board type (e.g. ESP32_DEV Module, etc.) 41 | * `ESP32-S2` Board type (e.g. ESP32S2_DEV Module, ESP32_S2_Saola, etc.) 42 | * `ESP32_S3` Board type (e.g. ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, etc.) 43 | * `ESP32-C3` Board type (e.g. ESP32C3_DEV Module, LOLIN_C3_MINI, DFROBOT_BEETLE_ESP32_C3, ADAFRUIT_QTPY_ESP32C3, AirM2M_CORE_ESP32C3, XIAO_ESP32C3, etc.) 44 | * Contextual information (e.g. what you were trying to achieve) 45 | * Simplest possible steps to reproduce 46 | * Anything that might be relevant in your opinion, such as: 47 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 48 | * Network configuration 49 | 50 | 51 | ### Example 52 | 53 | ``` 54 | Arduino IDE version: 1.8.19 55 | ESP32 core v2.0.5 56 | ESP32S3_DEV Module 57 | OS: Ubuntu 20.04 LTS 58 | Linux xy-Inspiron-3593 5.15.0-52-generic #58~20.04.1-Ubuntu SMP Thu Oct 13 13:09:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux 59 | 60 | Context: 61 | I encountered a crash while using this library 62 | Steps to reproduce: 63 | 1. ... 64 | 2. ... 65 | 3. ... 66 | 4. ... 67 | ``` 68 | 69 | ### Additional context 70 | 71 | Add any other context about the problem here. 72 | 73 | --- 74 | 75 | ### Sending Feature Requests 76 | 77 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 78 | 79 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP32_ISR_Servo/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 80 | 81 | --- 82 | 83 | ### Sending Pull Requests 84 | 85 | Pull Requests with changes and fixes are also welcome! 86 | 87 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 88 | 89 | 1. Change directory to the library GitHub 90 | 91 | ``` 92 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESP32_ISR_Servo_GitHub/ 93 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP32_ISR_Servo_GitHub$ 94 | ``` 95 | 96 | 2. Issue astyle command 97 | 98 | ``` 99 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP32_ISR_Servo_GitHub$ bash utils/restyle.sh 100 | ``` 101 | 102 | 103 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file 2 | version: 2 3 | 4 | updates: 5 | # Configure check for outdated GitHub Actions actions in workflows. 6 | # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot 7 | - package-ecosystem: github-actions 8 | directory: / # Check the repository's workflows under /.github/workflows/ 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | daysUntilStale: 60 4 | daysUntilClose: 14 5 | limitPerRun: 30 6 | staleLabel: stale 7 | exemptLabels: 8 | - pinned 9 | - security 10 | - "to be implemented" 11 | - "for reference" 12 | - "move to PR" 13 | - "enhancement" 14 | 15 | only: issues 16 | onlyLabels: [] 17 | exemptProjects: false 18 | exemptMilestones: false 19 | exemptAssignees: false 20 | 21 | markComment: > 22 | [STALE_SET] This issue has been automatically marked as stale because it has not had 23 | recent activity. It will be closed in 14 days if no further activity occurs. Thank you 24 | for your contributions. 25 | 26 | unmarkComment: > 27 | [STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it opening the future. 28 | 29 | closeComment: > 30 | [STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions. 31 | 32 | -------------------------------------------------------------------------------- /.github/workflows/auto-github-actions.yml: -------------------------------------------------------------------------------- 1 | name: auto-github-actions 2 | on: [push] 3 | jobs: 4 | check-bats-version: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v3 8 | - uses: actions/setup-node@v3 9 | with: 10 | node-version: '14' 11 | - run: npm install -g bats 12 | - run: bats -v 13 | -------------------------------------------------------------------------------- /.github/workflows/check-arduino.yml: -------------------------------------------------------------------------------- 1 | name: Check Arduino 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v3 20 | 21 | - name: Arduino Lint 22 | uses: arduino/arduino-lint-action@v1 23 | with: 24 | compliance: specification 25 | library-manager: update 26 | # Always use this setting for official repositories. Remove for 3rd party projects. 27 | official: true 28 | project-type: library 29 | -------------------------------------------------------------------------------- /.github/workflows/report-size-deltas.yml: -------------------------------------------------------------------------------- 1 | name: Report Size Deltas 2 | 3 | on: 4 | schedule: 5 | - cron: '*/5 * * * *' 6 | 7 | jobs: 8 | report: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Comment size deltas reports to PRs 13 | uses: arduino/report-size-deltas@v1 14 | with: 15 | # The name of the workflow artifact created by the "Compile Examples" workflow 16 | sketches-reports-source: sketches-reports 17 | -------------------------------------------------------------------------------- /.github/workflows/spell-check.yml: -------------------------------------------------------------------------------- 1 | name: Spell Check 2 | 3 | on: 4 | pull_request: 5 | push: 6 | schedule: 7 | # run every Tuesday at 3 AM UTC 8 | - cron: "0 3 * * 2" 9 | workflow_dispatch: 10 | repository_dispatch: 11 | 12 | jobs: 13 | spellcheck: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v3 19 | 20 | # See: https://github.com/codespell-project/actions-codespell/blob/master/README.md 21 | - name: Spell check 22 | uses: codespell-project/actions-codespell@master 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to ESP32_ISR_Servo 2 | 3 | ### Reporting Bugs 4 | 5 | Please report bugs in ESP32_ISR_Servo if you find them. 6 | 7 | However, before reporting a bug please check through the following: 8 | 9 | * [Existing Open Issues](https://github.com/khoih-prog/ESP32_ISR_Servo/issues) - someone might have already encountered this. 10 | 11 | If you don't find anything, please [open a new issue](https://github.com/khoih-prog/ESP32_ISR_Servo/issues/new). 12 | 13 | ### How to submit a bug report 14 | 15 | Please ensure to specify the following: 16 | 17 | * Arduino IDE version (e.g. 1.8.19) or Platform.io version 18 | * `ESP32` Core Version (e.g. ESP32 core v2.0.5) 19 | * `ESP32` Board type (e.g. ESP32_DEV Module, etc.) 20 | * `ESP32-S2` Board type (e.g. ESP32S2_DEV Module, ESP32_S2_Saola, etc.) 21 | * `ESP32_S3` Board type (e.g. ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, etc.) 22 | * `ESP32-C3` Board type (e.g. ESP32C3_DEV Module, LOLIN_C3_MINI, DFROBOT_BEETLE_ESP32_C3, ADAFRUIT_QTPY_ESP32C3, AirM2M_CORE_ESP32C3, XIAO_ESP32C3, etc.) 23 | * Contextual information (e.g. what you were trying to achieve) 24 | * Simplest possible steps to reproduce 25 | * Anything that might be relevant in your opinion, such as: 26 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 27 | * Network configuration 28 | 29 | 30 | ### Example 31 | 32 | ``` 33 | Arduino IDE version: 1.8.19 34 | ESP32 core v2.0.5 35 | ESP32S3_DEV Module 36 | OS: Ubuntu 20.04 LTS 37 | Linux xy-Inspiron-3593 5.15.0-52-generic #58~20.04.1-Ubuntu SMP Thu Oct 13 13:09:46 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux 38 | 39 | Context: 40 | I encountered a crash while using this library 41 | Steps to reproduce: 42 | 1. ... 43 | 2. ... 44 | 3. ... 45 | 4. ... 46 | ``` 47 | 48 | ### Additional context 49 | 50 | Add any other context about the problem here. 51 | 52 | --- 53 | 54 | ### Sending Feature Requests 55 | 56 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 57 | 58 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP32_ISR_Servo/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 59 | 60 | --- 61 | 62 | ### Sending Pull Requests 63 | 64 | Pull Requests with changes and fixes are also welcome! 65 | 66 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 67 | 68 | 1. Change directory to the library GitHub 69 | 70 | ``` 71 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESP32_ISR_Servo_GitHub/ 72 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP32_ISR_Servo_GitHub$ 73 | ``` 74 | 75 | 2. Issue astyle command 76 | 77 | ``` 78 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP32_ISR_Servo_GitHub$ bash utils/restyle.sh 79 | ``` 80 | 81 | 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Khoi Hoang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32_ISR_Servo Library 2 | 3 | [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP32_ISR_Servo.svg?)](https://www.ardu-badge.com/ESP32_ISR_Servo) 4 | [![GitHub release](https://img.shields.io/github/release/khoih-prog/ESP32_ISR_Servo.svg)](https://github.com/khoih-prog/ESP32_ISR_Servo/releases) 5 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/ESP32_ISR_Servo/blob/master/LICENSE) 6 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) 7 | [![GitHub issues](https://img.shields.io/github/issues/khoih-prog/ESP32_ISR_Servo.svg)](http://github.com/khoih-prog/ESP32_ISR_Servo/issues) 8 | 9 | 10 | Donate to my libraries using BuyMeACoffee 11 | 12 | 13 | --- 14 | --- 15 | 16 | ## Table of Contents 17 | 18 | * [Important Change from v1.3.0](#Important-Change-from-v130) 19 | * [Important Change from v1.2.0](#Important-Change-from-v120) 20 | * [Why do we need this ESP32_ISR_Servo library](#why-do-we-need-this-esp32_isr_servo-library) 21 | * [Features](#features) 22 | * [Important Notes about using ISR](#important-notes-about-using-isr) 23 | * [Currently supported Boards](#currently-supported-boards) 24 | * [Changelog](changelog.md) 25 | * [Prerequisites](#prerequisites) 26 | * [Installation](#installation) 27 | * [Use Arduino Library Manager](#use-arduino-library-manager) 28 | * [Manual Install](#manual-install) 29 | * [VS Code & PlatformIO](#vs-code--platformio) 30 | * [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) 31 | * [HOWTO Use analogRead() with ESP32 running WiFi and/or BlueTooth (BT/BLE)](#howto-use-analogread-with-esp32-running-wifi-andor-bluetooth-btble) 32 | * [1. ESP32 has 2 ADCs, named ADC1 and ADC2](#1--esp32-has-2-adcs-named-adc1-and-adc2) 33 | * [2. ESP32 ADCs functions](#2-esp32-adcs-functions) 34 | * [3. ESP32 WiFi uses ADC2 for WiFi functions](#3-esp32-wifi-uses-adc2-for-wifi-functions) 35 | * [More useful Information](#more-useful-information) 36 | * [ESP32 Hardware Timers](#esp32-hardware-timers) 37 | * [New functions](#new-functions) 38 | * [What special in this ESP32_ISR_Servo library](#what-special-in-this-esp32_isr_servo-library) 39 | * [HOWTO Usage](#howto-usage) 40 | * [Examples](#examples) 41 | * [ 1. **multiFileProject**](examples/multiFileProject) **New** 42 | * [ 2. ESP32_ISR_MultiServos](examples/ESP32_ISR_MultiServos) 43 | * [ 3. ESP32_MultipleRandomServos](examples/ESP32_MultipleRandomServos) 44 | * [ 4. ESP32_MultipleServos](examples/ESP32_MultipleServos) 45 | * [ 5. ISR_MultiServos](examples/ISR_MultiServos) 46 | * [ 6. MultipleRandomServos](examples/MultipleRandomServos) 47 | * [ 7. MultipleServos](examples/MultipleServos) 48 | * [Example ESP32_ISR_MultiServos](#example-ESP32_ISR_MultiServos) 49 | * [Debug Terminal Output Samples](#debug-terminal-output-samples) 50 | * [1. ESP32_MultipleRandomServos on ESP32_DEV](#1-esp32_multiplerandomservos-on-esp32_dev) 51 | * [2. ESP32_ISR_MultiServos on ESP32_DEV](#2-esp32_isr_multiservos-on-esp32_dev) 52 | * [3. MultipleRandomServos on ESP32S3_DEV](#3-MultipleRandomServos-on-ESP32S3_DEV) 53 | * [4. ESP32_ISR_MultiServos on ESP32S2_DEV](#4-ESP32_ISR_MultiServos-on-ESP32S2_DEV) 54 | * [Debug](#debug) 55 | * [Troubleshooting](#troubleshooting) 56 | * [Issues](#issues) 57 | * [TO DO](#to-do) 58 | * [DONE](#done) 59 | * [Contributions and Thanks](#contributions-and-thanks) 60 | * [Contributing](#contributing) 61 | * [License](#license) 62 | * [Copyright](#copyright) 63 | 64 | --- 65 | --- 66 | 67 | ### Important Change from v1.3.0 68 | 69 | Please use the **new v1.3.0+** for **ESP32 core v2.0.1+**, or the library won't work anymore. 70 | 71 | ### Important Change from v1.2.0 72 | 73 | Please have a look at [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) 74 | 75 | --- 76 | --- 77 | 78 | ### Why do we need this [ESP32_ISR_Servo library](https://github.com/khoih-prog/ESP32_ISR_Servo) 79 | 80 | #### Features 81 | 82 | Imagine you have a system with a **mission-critical function** controlling a **robot arm** or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is blocking the loop() or setup(). 83 | 84 | So your function might not be executed, and the result would be disastrous. 85 | 86 | You'd prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.). 87 | 88 | The correct choice is to use a **Hardware Timer with Interrupt** to call your function. 89 | 90 | These hardware timers, using interrupt, still work even if other functions are blocking. Moreover, they are **much more precise** (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy. 91 | 92 | Functions using normal software timers, relying on loop() and calling millis(), won't work if the **loop() or setup() is blocked by certain operation**. For example, certain function is blocking while it's connecting to WiFi or some services. 93 | 94 | This library enables you to use `1 Hardware Timer` on an ESP32, ESP32_S2, ESP32_C3-based board to control up to `16 independent servo motors`. 95 | 96 | --- 97 | 98 | #### Important Notes about using ISR 99 | 100 | 1. Inside the attached function, delay() won’t work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function. 101 | 102 | 2. Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile. 103 | 104 | 3. Avoid using Serial.print()-related functions inside ISR. Just for temporary debug purpose, but even this also can crash the system any time. Beware. 105 | 106 | 4. Your functions are now part of **ISR (Interrupt Service Routine)**, and must be `lean / mean`, and follow certain rules. More to read on: 107 | 108 | [HOWTO Attach Interrupt](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/) 109 | 110 | --- 111 | 112 | #### Currently supported Boards 113 | 114 | This [**ESP32_ISR_Servo** library](https://github.com/khoih-prog/ESP32_ISR_Servo) currently supports these following boards: 115 | 116 | 1. ESP32 boards, such as `ESP32_DEV`, etc. 117 | 2. ESP32S2-based boards, such as `ESP32S2_DEV`, `ESP32_S2 Saola`, Adafruit QTPY_ESP32S2, etc. 118 | 3. ESP32C3-based boards, such as `ESP32C3_DEV`, etc. **New** 119 | 4. ESP32_S3 (ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, FEATHER_ESP32S3_NOPSRAM and QTPY_ESP32S3_NOPSRAM, etc.) **New** 120 | 121 | --- 122 | --- 123 | 124 | ## Prerequisites 125 | 126 | 1. [`Arduino IDE 1.8.19+` for Arduino](https://github.com/arduino/Arduino). [![GitHub release](https://img.shields.io/github/release/arduino/Arduino.svg)](https://github.com/arduino/Arduino/releases/latest) 127 | 2. [`ESP32 Core 2.0.5+`](https://github.com/espressif/arduino-esp32) for ESP32-based boards. [![Latest release](https://img.shields.io/github/release/espressif/arduino-esp32.svg)](https://github.com/espressif/arduino-esp32/releases/latest/) 128 | 129 | --- 130 | --- 131 | 132 | ## Installation 133 | 134 | ### Use Arduino Library Manager 135 | 136 | The best and easiest way is to use `Arduino Library Manager`. Search for `ESP32_ISR_Servo`, then select / install the latest version. 137 | You can also use this link [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP32_ISR_Servo.svg?)](https://www.ardu-badge.com/ESP32_ISR_Servo) for more detailed instructions. 138 | 139 | ### Manual Install 140 | 141 | Another way to install is to: 142 | 143 | 1. Navigate to [ESP32_ISR_Servo](https://github.com/khoih-prog/ESP32_ISR_Servo) page. 144 | 2. Download the latest release `ESP32_ISR_Servo-master.zip`. 145 | 3. Extract the zip file to `ESP32_ISR_Servo-master` directory 146 | 4. Copy whole `ESP32_ISR_Servo-master` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. 147 | 148 | ### VS Code & PlatformIO 149 | 150 | 1. Install [VS Code](https://code.visualstudio.com/) 151 | 2. Install [PlatformIO](https://platformio.org/platformio-ide) 152 | 3. Install [**ESP32_ISR_Servo** library](https://registry.platformio.org/libraries/khoih-prog/ESP32_ISR_Servo) by using [Library Manager](https://registry.platformio.org/libraries/khoih-prog/ESP32_ISR_Servo/installation). Search for **ESP32_ISR_Servo** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) 153 | 4. Use included [platformio.ini](platformio/platformio.ini) file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at [Project Configuration File](https://docs.platformio.org/page/projectconf.html) 154 | 155 | 156 | --- 157 | --- 158 | 159 | ### HOWTO Fix `Multiple Definitions` Linker Error 160 | 161 | The current library implementation, using `xyz-Impl.h` instead of standard `xyz.cpp`, possibly creates certain `Multiple Definitions` Linker error in certain use cases. 162 | 163 | You can include this `.hpp` file 164 | 165 | ```cpp 166 | // Can be included as many times as necessary, without `Multiple Definitions` Linker Error 167 | #include "ESP32_ISR_Servo.hpp" //https://github.com/khoih-prog/ESP32_ISR_Servo 168 | ``` 169 | 170 | in many files. But be sure to use the following `.h` file **in just 1 `.h`, `.cpp` or `.ino` file**, which must **not be included in any other file**, to avoid `Multiple Definitions` Linker Error 171 | 172 | ```cpp 173 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 174 | #include "ESP32_ISR_Servo.h" //https://github.com/khoih-prog/ESP32_ISR_Servo 175 | ``` 176 | 177 | Check the new [**multiFileProject** example](examples/multiFileProject) for a `HOWTO` demo. 178 | 179 | Have a look at the discussion in [Different behaviour using the src_cpp or src_h lib #80](https://github.com/khoih-prog/ESPAsync_WiFiManager/discussions/80) 180 | 181 | 182 | 183 | --- 184 | --- 185 | 186 | ### HOWTO Use analogRead() with ESP32 running WiFi and/or BlueTooth (BT/BLE) 187 | 188 | Please have a look at [**ESP_WiFiManager Issue 39: Not able to read analog port when using the autoconnect example**](https://github.com/khoih-prog/ESP_WiFiManager/issues/39) to have more detailed description and solution of the issue. 189 | 190 | #### 1. ESP32 has 2 ADCs, named ADC1 and ADC2 191 | 192 | #### 2. ESP32 ADCs functions 193 | 194 | - `ADC1` controls ADC function for pins **GPIO32-GPIO39** 195 | - `ADC2` controls ADC function for pins **GPIO0, 2, 4, 12-15, 25-27** 196 | 197 | #### 3.. ESP32 WiFi uses ADC2 for WiFi functions 198 | 199 | Look in file [**adc_common.c**](https://github.com/espressif/esp-idf/blob/master/components/driver/adc_common.c) 200 | 201 | > In `ADC2`, there're two locks used for different cases: 202 | > 1. lock shared with app and Wi-Fi: 203 | > ESP32: 204 | > When Wi-Fi using the `ADC2`, we assume it will never stop, so app checks the lock and returns immediately if failed. 205 | > ESP32S2: 206 | > The controller's control over the ADC is determined by the arbiter. There is no need to control by lock. 207 | > 208 | > 2. lock shared between tasks: 209 | > when several tasks sharing the `ADC2`, we want to guarantee 210 | > all the requests will be handled. 211 | > Since conversions are short (about 31us), app returns the lock very soon, 212 | > we use a spinlock to stand there waiting to do conversions one by one. 213 | > 214 | > adc2_spinlock should be acquired first, then adc2_wifi_lock or rtc_spinlock. 215 | 216 | 217 | - In order to use `ADC2` for other functions, we have to **acquire complicated firmware locks and very difficult to do** 218 | - So, it's not advisable to use `ADC2` with WiFi/BlueTooth (BT/BLE). 219 | - Use `ADC1`, and pins `GPIO32-GPIO39` 220 | - If somehow it's a must to use those pins serviced by `ADC2` (**GPIO0, 2, 4, 12, 13, 14, 15, 25, 26 and 27**), use the **fix mentioned at the end** of [**ESP_WiFiManager Issue 39: Not able to read analog port when using the autoconnect example**](https://github.com/khoih-prog/ESP_WiFiManager/issues/39) to work with ESP32 WiFi/BlueTooth (BT/BLE). 221 | 222 | --- 223 | --- 224 | 225 | ## More useful Information 226 | 227 | ### ESP32 Hardware Timers 228 | 229 | - **The ESP32 has two timer groups, each one with two general purpose hardware timers.** 230 | - All the timers are based on **64-bit counters and 16-bit prescalers.** 231 | - The timer counters can be configured to count up or down and support automatic reload and software reload. 232 | - They can also generate alarms when they reach a specific value, defined by the software. 233 | - The value of the counter can be read by the software program. 234 | 235 | 236 | ### New functions 237 | 238 | ```cpp 239 | // returns last position in degrees if success, or -1 on wrong servoIndex 240 | int getPosition(unsigned servoIndex); 241 | 242 | // returns pulseWidth in microsecs (within min/max range) if success, or 0 on wrong servoIndex 243 | unsigned int getPulseWidth(unsigned servoIndex); 244 | ``` 245 | 246 | ### What special in this [ESP32_ISR_Servo library](https://github.com/khoih-prog/ESP32_ISR_Servo) 247 | 248 | Now these new **16 ISR-based Servo controllers** just use one ESP32 Hardware Timer. The number 16 is just arbitrarily chosen, and depending on application, you can increase that number to 32, 48, etc. without problem. 249 | 250 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 251 | 252 | Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. 253 | 254 | The [**MultipleServos**](examples/MultipleServos) example, which controls 6 servos independently, will demonstrate the nearly perfect accuracy. 255 | Being ISR-based servo controllers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet and Blynk services. 256 | 257 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 258 | 259 | You'll see blynkTimer Software is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task in loop(), using delay() function as an example. The elapsed time then is very unaccurate 260 | 261 | --- 262 | --- 263 | 264 | ## HOWTO Usage 265 | 266 | How to use: 267 | 268 | ```cpp 269 | #ifndef ESP32 270 | #error This code is designed to run on ESP32 platform, not Arduino nor ESP8266! Please check your Tools->Board setting. 271 | #endif 272 | 273 | #define TIMER_INTERRUPT_DEBUG 1 274 | #define ISR_SERVO_DEBUG 1 275 | 276 | // Select different ESP32 timer number (0-3) to avoid conflict 277 | #define USE_ESP32_TIMER_NO 3 278 | 279 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 280 | #include "ESP32_ISR_Servo.h" 281 | 282 | //See file .../hardware/espressif/esp32/variants/(esp32|doitESP32devkitV1)/pins_arduino.h 283 | #if !defined(LED_BUILTIN) 284 | #define LED_BUILTIN 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 285 | #endif 286 | 287 | #define PIN_LED 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 288 | 289 | #define PIN_D0 0 // Pin D0 mapped to pin GPIO0/BOOT/ADC11/TOUCH1 of ESP32 290 | #define PIN_D1 1 // Pin D1 mapped to pin GPIO1/TX0 of ESP32 291 | #define PIN_D2 2 // Pin D2 mapped to pin GPIO2/ADC12/TOUCH2 of ESP32 292 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 293 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 294 | #define PIN_D5 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 295 | #define PIN_D6 6 // Pin D6 mapped to pin GPIO6/FLASH_SCK of ESP32 296 | #define PIN_D7 7 // Pin D7 mapped to pin GPIO7/FLASH_D0 of ESP32 297 | #define PIN_D8 8 // Pin D8 mapped to pin GPIO8/FLASH_D1 of ESP32 298 | #define PIN_D9 9 // Pin D9 mapped to pin GPIO9/FLASH_D2 of ESP32 299 | 300 | // Published values for SG90 servos; adjust if needed 301 | #define MIN_MICROS 800 //544 302 | #define MAX_MICROS 2450 303 | 304 | int servoIndex1 = -1; 305 | int servoIndex2 = -1; 306 | 307 | void setup() 308 | { 309 | Serial.begin(115200); 310 | while (!Serial); 311 | 312 | delay(200); 313 | 314 | Serial.print(F("\nStarting ISR_MultiServos on ")); Serial.println(ARDUINO_BOARD); 315 | Serial.println(ESP32_ISR_SERVO_VERSION); 316 | 317 | //Select ESP32 timer USE_ESP32_TIMER_NO 318 | ESP32_ISR_Servos.useTimer(USE_ESP32_TIMER_NO); 319 | 320 | servoIndex1 = ESP32_ISR_Servos.setupServo(PIN_D5, MIN_MICROS, MAX_MICROS); 321 | servoIndex2 = ESP32_ISR_Servos.setupServo(PIN_D6, MIN_MICROS, MAX_MICROS); 322 | 323 | if (servoIndex1 != -1) 324 | Serial.println(F("Setup Servo1 OK")); 325 | else 326 | Serial.println(F("Setup Servo1 failed")); 327 | 328 | if (servoIndex2 != -1) 329 | Serial.println(F("Setup Servo2 OK")); 330 | else 331 | Serial.println(F("Setup Servo2 failed")); 332 | } 333 | 334 | void loop() 335 | { 336 | int position; 337 | 338 | if ( ( servoIndex1 != -1) && ( servoIndex2 != -1) ) 339 | { 340 | for (position = 0; position <= 180; position++) 341 | { 342 | // goes from 0 degrees to 180 degrees 343 | // in steps of 1 degree 344 | 345 | if (position % 30 == 0) 346 | { 347 | Serial.print(F("Servo1 pos = ")); Serial.print(position); 348 | Serial.print(F(", Servo2 pos = ")); Serial.println(180 - position); 349 | } 350 | 351 | ESP32_ISR_Servos.setPosition(servoIndex1, position); 352 | ESP32_ISR_Servos.setPosition(servoIndex2, 180 - position); 353 | // waits 30ms for the servo to reach the position 354 | delay(30); 355 | } 356 | 357 | delay(5000); 358 | 359 | for (position = 180; position >= 0; position--) 360 | { 361 | // goes from 180 degrees to 0 degrees 362 | if (position % 30 == 0) 363 | { 364 | Serial.print(F("Servo1 pos = ")); Serial.print(position); 365 | Serial.print(F(", Servo2 pos = ")); Serial.println(180 - position); 366 | } 367 | 368 | ESP32_ISR_Servos.setPosition(servoIndex1, position); 369 | ESP32_ISR_Servos.setPosition(servoIndex2, 180 - position); 370 | // waits 30ms for the servo to reach the position 371 | delay(30); 372 | } 373 | 374 | delay(5000); 375 | } 376 | } 377 | 378 | ``` 379 | 380 | --- 381 | --- 382 | 383 | ### Examples: 384 | 385 | 386 | 1. [multiFileProject](examples/multiFileProject) **New** 387 | 2. [ESP32_ISR_MultiServos](examples/ESP32_ISR_MultiServos) 388 | 3. [ESP32_MultipleRandomServos](examples/ESP32_MultipleRandomServos) 389 | 4. [ESP32_MultipleServos](examples/ESP32_MultipleServos) 390 | 5. [ISR_MultiServos](examples/ISR_MultiServos) 391 | 6. [MultipleRandomServos](examples/MultipleRandomServos) 392 | 7. [MultipleServos](examples/MultipleServos) 393 | 394 | --- 395 | 396 | ### Example [ESP32_ISR_MultiServos](examples/ESP32_ISR_MultiServos) 397 | 398 | https://github.com/khoih-prog/ESP32_ISR_Servo/blob/18250af8451efd44e04fc5430ba07087b828f6b9/examples/ESP32_ISR_MultiServos/ESP32_ISR_MultiServos.ino#L72-L220 399 | 400 | --- 401 | --- 402 | 403 | ### Debug Terminal Output Samples 404 | 405 | ### 1. ESP32_MultipleRandomServos on ESP32_DEV 406 | 407 | ``` 408 | Starting ESP32_MultipleRandomServos on ESP32_DEV 409 | ESP32_ISR_Servo v1.5.0 410 | Setup OK Servo index = 0 411 | Setup OK Servo index = 1 412 | Setup OK Servo index = 2 413 | Setup OK Servo index = 3 414 | Setup OK Servo index = 4 415 | Setup OK Servo index = 5 416 | Servos @ 0 degree 417 | Servos idx = 0, act. pos. (deg) = 0, pulseWidth (us) = 800 418 | Servos idx = 1, act. pos. (deg) = 0, pulseWidth (us) = 800 419 | Servos idx = 2, act. pos. (deg) = 0, pulseWidth (us) = 800 420 | Servos idx = 3, act. pos. (deg) = 0, pulseWidth (us) = 800 421 | Servos idx = 4, act. pos. (deg) = 0, pulseWidth (us) = 800 422 | Servos idx = 5, act. pos. (deg) = 0, pulseWidth (us) = 800 423 | Servos @ 90 degree 424 | Servos idx = 0, act. pos. (deg) = 90, pulseWidth (us) = 1620 425 | Servos idx = 1, act. pos. (deg) = 90, pulseWidth (us) = 1620 426 | Servos idx = 2, act. pos. (deg) = 90, pulseWidth (us) = 1620 427 | Servos idx = 3, act. pos. (deg) = 90, pulseWidth (us) = 1620 428 | Servos idx = 4, act. pos. (deg) = 90, pulseWidth (us) = 1620 429 | Servos idx = 5, act. pos. (deg) = 90, pulseWidth (us) = 1620 430 | Servos @ 180 degree 431 | Servos idx = 0, act. pos. (deg) = 180, pulseWidth (us) = 2450 432 | Servos idx = 1, act. pos. (deg) = 180, pulseWidth (us) = 2450 433 | Servos idx = 2, act. pos. (deg) = 180, pulseWidth (us) = 2450 434 | Servos idx = 3, act. pos. (deg) = 180, pulseWidth (us) = 2450 435 | Servos idx = 4, act. pos. (deg) = 180, pulseWidth (us) = 2450 436 | Servos idx = 5, act. pos. (deg) = 180, pulseWidth (us) = 2450 437 | Servos sweeps from 0-180 degrees 438 | Servos sweeps from 180-0 degrees 439 | Servos, index depending, be somewhere from 0-180 degrees 440 | Servos, index depending, be somewhere from 180-0 degrees 441 | Servos @ 0 degree 442 | Servos idx = 0, act. pos. (deg) = 0, pulseWidth (us) = 800 443 | Servos idx = 1, act. pos. (deg) = 0, pulseWidth (us) = 800 444 | Servos idx = 2, act. pos. (deg) = 0, pulseWidth (us) = 800 445 | Servos idx = 3, act. pos. (deg) = 0, pulseWidth (us) = 800 446 | Servos idx = 4, act. pos. (deg) = 0, pulseWidth (us) = 800 447 | Servos idx = 5, act. pos. (deg) = 0, pulseWidth (us) = 800 448 | Servos @ 90 degree 449 | Servos idx = 0, act. pos. (deg) = 90, pulseWidth (us) = 1620 450 | Servos idx = 1, act. pos. (deg) = 90, pulseWidth (us) = 1620 451 | Servos idx = 2, act. pos. (deg) = 90, pulseWidth (us) = 1620 452 | Servos idx = 3, act. pos. (deg) = 90, pulseWidth (us) = 1620 453 | Servos idx = 4, act. pos. (deg) = 90, pulseWidth (us) = 1620 454 | Servos idx = 5, act. pos. (deg) = 90, pulseWidth (us) = 1620 455 | Servos @ 180 degree 456 | Servos idx = 0, act. pos. (deg) = 180, pulseWidth (us) = 2450 457 | Servos idx = 1, act. pos. (deg) = 180, pulseWidth (us) = 2450 458 | Servos idx = 2, act. pos. (deg) = 180, pulseWidth (us) = 2450 459 | Servos idx = 3, act. pos. (deg) = 180, pulseWidth (us) = 2450 460 | Servos idx = 4, act. pos. (deg) = 180, pulseWidth (us) = 2450 461 | Servos idx = 5, act. pos. (deg) = 180, pulseWidth (us) = 2450 462 | ``` 463 | 464 | --- 465 | 466 | ### 2. ESP32_ISR_MultiServos on ESP32_DEV 467 | 468 | 469 | ``` 470 | Starting ESP32_ISR_MultiServos on ESP32_DEV 471 | ESP32_ISR_Servo v1.5.0 472 | Setup Servo1 OK 473 | Setup Servo2 OK 474 | Servo1 pos = 0, Servo2 pos = 180 475 | Servo1 pos = 30, Servo2 pos = 150 476 | Servo1 pos = 60, Servo2 pos = 120 477 | Servo1 pos = 90, Servo2 pos = 90 478 | Servo1 pos = 120, Servo2 pos = 60 479 | Servo1 pos = 150, Servo2 pos = 30 480 | Servo1 pos = 180, Servo2 pos = 0 481 | Servo1 pos = 180, Servo2 pos = 0 482 | Servo1 pos = 150, Servo2 pos = 30 483 | Servo1 pos = 120, Servo2 pos = 60 484 | Servo1 pos = 90, Servo2 pos = 90 485 | Servo1 pos = 60, Servo2 pos = 120 486 | Servo1 pos = 30, Servo2 pos = 150 487 | Servo1 pos = 0, Servo2 pos = 180 488 | Servo1 pos = 0, Servo2 pos = 180 489 | Servo1 pos = 30, Servo2 pos = 150 490 | Servo1 pos = 60, Servo2 pos = 120 491 | Servo1 pos = 90, Servo2 pos = 90 492 | Servo1 pos = 120, Servo2 pos = 60 493 | Servo1 pos = 150, Servo2 pos = 30 494 | Servo1 pos = 180, Servo2 pos = 0 495 | Servo1 pos = 180, Servo2 pos = 0 496 | Servo1 pos = 150, Servo2 pos = 30 497 | Servo1 pos = 120, Servo2 pos = 60 498 | Servo1 pos = 90, Servo2 pos = 90 499 | Servo1 pos = 60, Servo2 pos = 120 500 | Servo1 pos = 30, Servo2 pos = 150 501 | Servo1 pos = 0, Servo2 pos = 180 502 | Servo1 pos = 0, Servo2 pos = 180 503 | Servo1 pos = 30, Servo2 pos = 150 504 | Servo1 pos = 60, Servo2 pos = 120 505 | Servo1 pos = 90, Servo2 pos = 90 506 | Servo1 pos = 120, Servo2 pos = 60 507 | Servo1 pos = 150, Servo2 pos = 30 508 | ``` 509 | 510 | --- 511 | 512 | ### 3. MultipleRandomServos on ESP32S3_DEV 513 | 514 | ``` 515 | Starting MultipleRandomServos on ESP32S3_DEV 516 | ESP32_ISR_Servo v1.5.0 517 | [ISR_SERVO] ESP32_S3_TimerInterrupt: _timerNo = 3 , _fre = 1000000 518 | [ISR_SERVO] TIMER_BASE_CLK = 80000000 , TIMER_DIVIDER = 80 519 | [ISR_SERVO] _timerIndex = 1 , _timerGroup = 1 520 | [ISR_SERVO] _count = 0 - 10 521 | [ISR_SERVO] timer_set_alarm_value = 10.00 522 | [ISR_SERVO] Starting ITimer OK 523 | Setup OK Servo index = 0 524 | Setup OK Servo index = 1 525 | Setup OK Servo index = 2 526 | Setup OK Servo index = 3 527 | Setup OK Servo index = 4 528 | Setup OK Servo index = 5 529 | Servos @ 0 degree 530 | [ISR_SERVO] Idx = 0 531 | [ISR_SERVO] cnt = 80 , pos = 0 532 | Servos idx = 0, act. pos. (deg) = [ISR_SERVO] Idx = 0 533 | [ISR_SERVO] cnt = 80 , pos = 0 534 | 0, pulseWidth (us) = [ISR_SERVO] Idx = 0 535 | [ISR_SERVO] cnt = 80 , pos = 0 536 | 800 537 | [ISR_SERVO] Idx = 1 538 | [ISR_SERVO] cnt = 80 , pos = 0 539 | Servos idx = 1, act. pos. (deg) = [ISR_SERVO] Idx = 1 540 | [ISR_SERVO] cnt = 80 , pos = 0 541 | 0, pulseWidth (us) = [ISR_SERVO] Idx = 1 542 | [ISR_SERVO] cnt = 80 , pos = 0 543 | 800 544 | [ISR_SERVO] Idx = 2 545 | [ISR_SERVO] cnt = 80 , pos = 0 546 | Servos idx = 2, act. pos. (deg) = [ISR_SERVO] Idx = 2 547 | [ISR_SERVO] cnt = 80 , pos = 0 548 | 0, pulseWidth (us) = [ISR_SERVO] Idx = 2 549 | [ISR_SERVO] cnt = 80 , pos = 0 550 | 800 551 | [ISR_SERVO] Idx = 3 552 | [ISR_SERVO] cnt = 80 , pos = 0 553 | Servos idx = 3, act. pos. (deg) = [ISR_SERVO] Idx = 3 554 | [ISR_SERVO] cnt = 80 , pos = 0 555 | 0, pulseWidth (us) = [ISR_SERVO] Idx = 3 556 | [ISR_SERVO] cnt = 80 , pos = 0 557 | 800 558 | [ISR_SERVO] Idx = 4 559 | [ISR_SERVO] cnt = 80 , pos = 0 560 | Servos idx = 4, act. pos. (deg) = [ISR_SERVO] Idx = 4 561 | [ISR_SERVO] cnt = 80 , pos = 0 562 | 0, pulseWidth (us) = [ISR_SERVO] Idx = 4 563 | [ISR_SERVO] cnt = 80 , pos = 0 564 | 800 565 | [ISR_SERVO] Idx = 5 566 | [ISR_SERVO] cnt = 80 , pos = 0 567 | Servos idx = 5, act. pos. (deg) = [ISR_SERVO] Idx = 5 568 | [ISR_SERVO] cnt = 80 , pos = 0 569 | 0, pulseWidth (us) = [ISR_SERVO] Idx = 5 570 | [ISR_SERVO] cnt = 80 , pos = 0 571 | 800 572 | Servos @ 90 degree 573 | [ISR_SERVO] Idx = 0 574 | [ISR_SERVO] cnt = 162 , pos = 90 575 | Servos idx = 0, act. pos. (deg) = [ISR_SERVO] Idx = 0 576 | [ISR_SERVO] cnt = 162 , pos = 90 577 | 90, pulseWidth (us) = [ISR_SERVO] Idx = 0 578 | [ISR_SERVO] cnt = 162 , pos = 90 579 | 1620 580 | [ISR_SERVO] Idx = 1 581 | [ISR_SERVO] cnt = 162 , pos = 90 582 | Servos idx = 1, act. pos. (deg) = [ISR_SERVO] Idx = 1 583 | [ISR_SERVO] cnt = 162 , pos = 90 584 | 90, pulseWidth (us) = [ISR_SERVO] Idx = 1 585 | [ISR_SERVO] cnt = 162 , pos = 90 586 | 1620 587 | [ISR_SERVO] Idx = 2 588 | [ISR_SERVO] cnt = 162 , pos = 90 589 | Servos idx = 2, act. pos. (deg) = [ISR_SERVO] Idx = 2 590 | [ISR_SERVO] cnt = 162 , pos = 90 591 | 90, pulseWidth (us) = [ISR_SERVO] Idx = 2 592 | [ISR_SERVO] cnt = 162 , pos = 90 593 | 1620 594 | [ISR_SERVO] Idx = 3 595 | [ISR_SERVO] cnt = 162 , pos = 90 596 | Servos idx = 3, act. pos. (deg) = [ISR_SERVO] Idx = 3 597 | [ISR_SERVO] cnt = 162 , pos = 90 598 | 90, pulseWidth (us) = [ISR_SERVO] Idx = 3 599 | [ISR_SERVO] cnt = 162 , pos = 90 600 | 1620 601 | ``` 602 | 603 | --- 604 | 605 | 606 | ### 4. ESP32_ISR_MultiServos on ESP32S2_DEV 607 | 608 | ``` 609 | Starting ESP32_ISR_MultiServos on ESP32S2_DEV 610 | ESP32_ISR_Servo v1.5.0 611 | [ISR_SERVO] ESP32_S2_TimerInterrupt: _timerNo = 3 , _fre = 1000000 612 | [ISR_SERVO] TIMER_BASE_CLK = 80000000 , TIMER_DIVIDER = 80 613 | [ISR_SERVO] _timerIndex = 1 , _timerGroup = 1 614 | [ISR_SERVO] _count = 0 - 10 615 | [ISR_SERVO] timer_set_alarm_value = 10.00 616 | [ISR_SERVO] Starting ITimer OK 617 | Setup Servo1 OK 618 | Setup Servo2 OK 619 | Servo1 pos = 0, Servo2 pos = 180 620 | [ISR_SERVO] Idx = 0 621 | [ISR_SERVO] cnt = 80 , pos = 0 622 | [ISR_SERVO] Idx = 1 623 | [ISR_SERVO] cnt = 245 , pos = 180 624 | [ISR_SERVO] Idx = 0 625 | [ISR_SERVO] cnt = 80 , pos = 1 626 | [ISR_SERVO] Idx = 1 627 | [ISR_SERVO] cnt = 244 , pos = 179 628 | [ISR_SERVO] Idx = 0 629 | [ISR_SERVO] cnt = 81 , pos = 2 630 | [ISR_SERVO] Idx = 1 631 | [ISR_SERVO] cnt = 243 , pos = 178 632 | [ISR_SERVO] Idx = 0 633 | [ISR_SERVO] cnt = 82 , pos = 3 634 | [ISR_SERVO] Idx = 1 635 | [ISR_SERVO] cnt = 242 , pos = 177 636 | [ISR_SERVO] Idx = 0 637 | [ISR_SERVO] cnt = 83 , pos = 4 638 | [ISR_SERVO] Idx = 1 639 | [ISR_SERVO] cnt = 241 , pos = 176 640 | [ISR_SERVO] Idx = 0 641 | [ISR_SERVO] cnt = 84 , pos = 5 642 | ``` 643 | 644 | --- 645 | --- 646 | 647 | ### Debug 648 | 649 | Debug is enabled by default on Serial. 650 | 651 | You can also change the debugging level from 0 to 4. Be careful and using level 2 only for temporary debug purpose only. 652 | 653 | ```cpp 654 | #define ISR_SERVO_DEBUG 1 655 | ``` 656 | 657 | --- 658 | 659 | ### Troubleshooting 660 | 661 | If you get compilation errors, more often than not, you may need to install a newer version of the core for Arduino boards. 662 | 663 | Sometimes, the library will only work if you update the board core to the latest version because I am using newly added functions. 664 | 665 | 666 | --- 667 | --- 668 | 669 | ### Issues 670 | 671 | Submit issues to: [ESP32_ISR_Servo issues](https://github.com/khoih-prog/ESP32_ISR_Servo/issues) 672 | 673 | --- 674 | --- 675 | 676 | ## TO DO 677 | 678 | 1. Search for bug and improvement. 679 | 680 | --- 681 | 682 | ## DONE 683 | 684 | 1. Similar features for Arduino (UNO, Mega, etc...) and ESP8266 685 | 2. Add functions `getPosition()` and `getPulseWidth()` 686 | 3. Optimize the code 687 | 4. Add more complicated examples 688 | 5. Add support to new `ESP32-S3` (ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, etc.) 689 | 6. Add support to new `ESP32-S2` (ESP32S2_DEV, etc.) 690 | 7. Add support to new `ESP32-C3` (ESP32C3_DEV, etc.) 691 | 8. Convert to h-only library. 692 | 9. Optimize library code by using `reference-passing` instead of `value-passing` 693 | 10. Improve accuracy by using `float`, instead of `uint32_t` for `position` in degrees 694 | 11. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project 695 | 12. Fix breaking issue caused by **ESP32 core v2.0.1+** by increasing `TIMER_INTERVAL_MICRO` to `12uS` from `10uS` 696 | 13. Suppress errors and warnings for new ESP32 core v2.0.4 697 | 14. Use `allman astyle` and add `utils` 698 | 699 | --- 700 | --- 701 | 702 | ### Contributions and thanks 703 | 704 | 1. Thanks to [raphweb](https://github.com/raphweb) for the PR [Fixed count >= min comparison for servo enable.](https://github.com/khoih-prog/ESP32_ISR_Servo/pull/1) to fix bug and leading to the new releases v1.1.0 705 | 2. Thanks to [Brent Rubell](https://github.com/brentru) for the PR [Add newer Adafruit boards](https://github.com/khoih-prog/ESP32_ISR_Servo/pull/5) to add support to new Adafruit boards such as QTPY_ESP32S2, FEATHER_ESP32S3_NOPSRAM and QTPY_ESP32S3_NOPSRAM, leading to the new releases v1.3.1 706 | 707 | 708 | 709 | 710 | 711 | 712 |
raphweb
⭐️ raphweb

brentru
⭐️ Brent Rubell

713 | 714 | 715 | --- 716 | 717 | ## Contributing 718 | 719 | If you want to contribute to this project: 720 | - Report bugs and errors 721 | - Ask for enhancements 722 | - Create issues and pull requests 723 | - Tell other people about this library 724 | 725 | --- 726 | 727 | ### License 728 | 729 | - The library is licensed under [MIT](https://github.com/khoih-prog/ESP32_ISR_Servo/blob/master/LICENSE) 730 | 731 | --- 732 | 733 | ## Copyright 734 | 735 | Copyright (C) 2019- Khoi Hoang 736 | 737 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # ESP32_ISR_Servo Library 2 | 3 | [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP32_ISR_Servo.svg?)](https://www.ardu-badge.com/ESP32_ISR_Servo) 4 | [![GitHub release](https://img.shields.io/github/release/khoih-prog/ESP32_ISR_Servo.svg)](https://github.com/khoih-prog/ESP32_ISR_Servo/releases) 5 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/ESP32_ISR_Servo/blob/master/LICENSE) 6 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) 7 | [![GitHub issues](https://img.shields.io/github/issues/khoih-prog/ESP32_ISR_Servo.svg)](http://github.com/khoih-prog/ESP32_ISR_Servo/issues) 8 | 9 | 10 | Donate to my libraries using BuyMeACoffee 11 | 12 | 13 | --- 14 | --- 15 | 16 | ## Table of Contents 17 | 18 | * [Changelog](#changelog) 19 | * [Releases v1.5.0](#releases-v150) 20 | * [Releases v1.4.0](#releases-v140) 21 | * [Releases v1.3.0](#releases-v130) 22 | * [Releases v1.2.1](#releases-v121) 23 | * [Releases v1.2.0](#releases-v120) 24 | * [Releases v1.1.0](#releases-v110) 25 | * [Releases v1.0.2](#releases-v102) 26 | * [Releases v1.0.1](#releases-v101) 27 | 28 | --- 29 | --- 30 | 31 | ## Changelog 32 | 33 | ### Releases v1.5.0 34 | 35 | 1. Fix doubled time for `ESP32_C3, ESP32_S2 and ESP32_S3`. Check [Error in the value defined by TIMER0_INTERVAL_MS #28](https://github.com/khoih-prog/ESP32_ISR_Servo/issues/28) 36 | 2. Modify examples to avoid using `LED_BUILTIN` / `GPIO2` and `GPIO2` as they can cause crash in some boards, such as `ESP32_C3` 37 | 3. Use `allman astyle` and add `utils` 38 | 39 | ### Releases v1.4.0 40 | 41 | 1. Suppress errors and warnings for new ESP32 core v2.0.4+ 42 | 43 | ### Releases v1.3.1 44 | 45 | 1. Add support to new Adafruit boards such as QTPY_ESP32S2, FEATHER_ESP32S3_NOPSRAM and QTPY_ESP32S3_NOPSRAM 46 | 47 | ### Releases v1.3.0 48 | 49 | 1. Fix breaking issue caused by **ESP32 core v2.0.1+** by increasing `TIMER_INTERVAL_MICRO` to `12uS` from `10uS`. Tested OK with ESP32 core v2.0.3 now 50 | 51 | ### Releases v1.2.1 52 | 53 | 1. Fix bug 54 | 55 | ### Releases v1.2.0 56 | 57 | 1. Add support to new `ESP32-S3` (ESP32S3_DEV, ESP32_S3_BOX, UM TINYS3, UM PROS3, UM FEATHERS3, etc.) 58 | 2. Add support to new `ESP32-S2` (ESP32S2_DEV, etc.) 59 | 3. Add support to new `ESP32-C3` (ESP32C3_DEV, etc.) 60 | 4. Convert to h-only library. 61 | 5. Optimize library code by using `reference-passing` instead of `value-passing` 62 | 6. Improve accuracy by using `float`, instead of `uint32_t` for `position` in degrees 63 | 7. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project 64 | 65 | ### Releases v1.1.0 66 | 67 | 1. Fix bug. See [Fixed count >= min comparison for servo enable](https://github.com/khoih-prog/ESP32_ISR_Servo/pull/1) 68 | 2. Clean-up all compiler warnings possible. 69 | 3. Add Table of Contents 70 | 4. Add Version String 71 | 5. Fix and Optimize old examples 72 | 73 | #### Releases v1.0.2 74 | 75 | 1. Add example using [Blynk](http://docs.blynk.cc/) to control servos. 76 | 2. Change example names to avoid duplication. 77 | 78 | #### Releases v1.0.1 79 | 80 | 1. Basic 16 ISR-based servo controllers using 1 hardware timer for ESP32. 81 | 82 | 83 | -------------------------------------------------------------------------------- /examples/ESP32_ISR_MultiServos/ESP32_ISR_MultiServos.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_ISR_MultiServos.ino 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | The ESP32 has two timer groups, each one with two general purpose hardware timers. All the timers 10 | are based on 64 bits counters and 16 bit prescalers 11 | The timer counters can be configured to count up or down and support automatic reload and software reload 12 | They can also generate alarms when they reach a specific value, defined by the software. 13 | The value of the counter can be read by the software program. 14 | 15 | Now these new 16 ISR-based PWM servo contro uses only 1 hardware timer. 16 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 17 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 18 | This important feature is absolutely necessary for mission-critical tasks. 19 | 20 | Notes: 21 | Special design is necessary to share data between interrupt code and the rest of your program. 22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 23 | variable can not spontaneously change. Because your function may change variables while your program is using them, 24 | the compiler needs this hint. But volatile alone is often not enough. 25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 28 | or the entire sequence of your code which accesses the data. 29 | *****************************************************************************************************************************/ 30 | 31 | /**************************************************************************************************************************** 32 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 33 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 34 | and Blynk services. You can also have many (up to 16) timers to use. 35 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 36 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 37 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 38 | written 39 | 40 | From ESP32 Servo Example Using Arduino ESP32 Servo Library 41 | John K. Bennett 42 | March, 2017 43 | 44 | Different servos require different pulse widths to vary servo angle, but the range is 45 | an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos 46 | sweep 180 degrees, so the lowest number in the published range for a particular servo 47 | represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top 48 | of the range represents 180 degrees. So for example, if the range is 1000us to 2000us, 49 | 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800 50 | degrees. 51 | 52 | Circuit: 53 | Servo motors have three wires: power, ground, and signal. The power wire is typically red, 54 | the ground wire is typically black or brown, and the signal wire is typically yellow, 55 | orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw 56 | considerable power, we will connect servo power to the VBat pin of the ESP32 (located 57 | near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS. 58 | 59 | We could also connect servo power to a separate external 60 | power source (as long as we connect all of the grounds (ESP32, servo, and external power). 61 | In this example, we just connect ESP32 ground to servo ground. The servo signal pins 62 | connect to any available GPIO pins on the ESP32 (in this example, we use pins 63 | 22, 19, 23, & 18). 64 | 65 | In this example, we assume four Tower Pro SG90 small servos. 66 | The published min and max for this servo are 500 and 2400, respectively. 67 | These values actually drive the servos a little past 0 and 180, so 68 | if you are particular, adjust the min and max values to match your needs. 69 | Experimentally, 550 and 2350 are pretty close to 0 and 180. 70 | *****************************************************************************************************************************/ 71 | 72 | #ifndef ESP32 73 | #error This code is designed to run on ESP32 platform, not Arduino nor ESP8266! Please check your Tools->Board setting. 74 | #endif 75 | 76 | #define TIMER_INTERRUPT_DEBUG 1 77 | #define ISR_SERVO_DEBUG 1 78 | 79 | // Select different ESP32 timer number (0-3) to avoid conflict 80 | #define USE_ESP32_TIMER_NO 3 81 | 82 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 83 | #include "ESP32_ISR_Servo.h" 84 | 85 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 86 | // Don't use PIN_D2 with ESP32_C3 (crash) 87 | 88 | //See file .../hardware/espressif/esp32/variants/(esp32|doitESP32devkitV1)/pins_arduino.h 89 | #if !defined(LED_BUILTIN) 90 | #define LED_BUILTIN 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 91 | #endif 92 | 93 | #define PIN_LED 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 94 | 95 | #define PIN_D0 0 // Pin D0 mapped to pin GPIO0/BOOT/ADC11/TOUCH1 of ESP32 96 | #define PIN_D1 1 // Pin D1 mapped to pin GPIO1/TX0 of ESP32 97 | #define PIN_D2 2 // Pin D2 mapped to pin GPIO2/ADC12/TOUCH2 of ESP32 98 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 99 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 100 | #define PIN_D5 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 101 | #define PIN_D6 6 // Pin D6 mapped to pin GPIO6/FLASH_SCK of ESP32 102 | #define PIN_D7 7 // Pin D7 mapped to pin GPIO7/FLASH_D0 of ESP32 103 | #define PIN_D8 8 // Pin D8 mapped to pin GPIO8/FLASH_D1 of ESP32 104 | #define PIN_D9 9 // Pin D9 mapped to pin GPIO9/FLASH_D2 of ESP32 105 | 106 | #define PIN_D10 10 // Pin D10 mapped to pin GPIO10/FLASH_D3 of ESP32 107 | #define PIN_D11 11 // Pin D11 mapped to pin GPIO11/FLASH_CMD of ESP32 108 | #define PIN_D12 12 // Pin D12 mapped to pin GPIO12/HSPI_MISO/ADC15/TOUCH5/TDI of ESP32 109 | #define PIN_D13 13 // Pin D13 mapped to pin GPIO13/HSPI_MOSI/ADC14/TOUCH4/TCK of ESP32 110 | #define PIN_D14 14 // Pin D14 mapped to pin GPIO14/HSPI_SCK/ADC16/TOUCH6/TMS of ESP32 111 | #define PIN_D15 15 // Pin D15 mapped to pin GPIO15/HSPI_SS/ADC13/TOUCH3/TDO of ESP32 112 | #define PIN_D16 16 // Pin D16 mapped to pin GPIO16/TX2 of ESP32 113 | #define PIN_D17 17 // Pin D17 mapped to pin GPIO17/RX2 of ESP32 114 | #define PIN_D18 18 // Pin D18 mapped to pin GPIO18/VSPI_SCK of ESP32 115 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO19/VSPI_MISO of ESP32 116 | 117 | #define PIN_D21 21 // Pin D21 mapped to pin GPIO21/SDA of ESP32 118 | #define PIN_D22 22 // Pin D22 mapped to pin GPIO22/SCL of ESP32 119 | #define PIN_D23 23 // Pin D23 mapped to pin GPIO23/VSPI_MOSI of ESP32 120 | #define PIN_D24 24 // Pin D24 mapped to pin GPIO24 of ESP32 121 | #define PIN_D25 25 // Pin D25 mapped to pin GPIO25/ADC18/DAC1 of ESP32 122 | #define PIN_D26 26 // Pin D26 mapped to pin GPIO26/ADC19/DAC2 of ESP32 123 | #define PIN_D27 27 // Pin D27 mapped to pin GPIO27/ADC17/TOUCH7 of ESP32 124 | 125 | #define PIN_D32 32 // Pin D32 mapped to pin GPIO32/ADC4/TOUCH9 of ESP32 126 | #define PIN_D33 33 // Pin D33 mapped to pin GPIO33/ADC5/TOUCH8 of ESP32 127 | #define PIN_D34 34 // Pin D34 mapped to pin GPIO34/ADC6 of ESP32 128 | #define PIN_D35 35 // Pin D35 mapped to pin GPIO35/ADC7 of ESP32 129 | #define PIN_D36 36 // Pin D36 mapped to pin GPIO36/ADC0/SVP of ESP32 130 | #define PIN_D39 39 // Pin D39 mapped to pin GPIO39/ADC3/SVN of ESP32 131 | 132 | #define PIN_RX0 3 // Pin RX0 mapped to pin GPIO3/RX0 of ESP32 133 | #define PIN_TX0 1 // Pin TX0 mapped to pin GPIO1/TX0 of ESP32 134 | 135 | #define PIN_SCL 22 // Pin SCL mapped to pin GPIO22/SCL of ESP32 136 | #define PIN_SDA 21 // Pin SDA mapped to pin GPIO21/SDA of ESP32 137 | 138 | // Published values for SG90 servos; adjust if needed 139 | #define MIN_MICROS 800 //544 140 | #define MAX_MICROS 2450 141 | 142 | int servoIndex1 = -1; 143 | int servoIndex2 = -1; 144 | 145 | void setup() 146 | { 147 | Serial.begin(115200); 148 | 149 | while (!Serial && millis() < 5000); 150 | 151 | delay(500); 152 | 153 | Serial.print(F("\nStarting ESP32_ISR_MultiServos on ")); 154 | Serial.println(ARDUINO_BOARD); 155 | Serial.println(ESP32_ISR_SERVO_VERSION); 156 | 157 | //Select ESP32 timer USE_ESP32_TIMER_NO 158 | ESP32_ISR_Servos.useTimer(USE_ESP32_TIMER_NO); 159 | 160 | servoIndex1 = ESP32_ISR_Servos.setupServo(PIN_D3, MIN_MICROS, MAX_MICROS); 161 | servoIndex2 = ESP32_ISR_Servos.setupServo(PIN_D4, MIN_MICROS, MAX_MICROS); 162 | 163 | if (servoIndex1 != -1) 164 | Serial.println(F("Setup Servo1 OK")); 165 | else 166 | Serial.println(F("Setup Servo1 failed")); 167 | 168 | if (servoIndex2 != -1) 169 | Serial.println(F("Setup Servo2 OK")); 170 | else 171 | Serial.println(F("Setup Servo2 failed")); 172 | } 173 | 174 | void loop() 175 | { 176 | int position; 177 | 178 | if ( ( servoIndex1 != -1) && ( servoIndex2 != -1) ) 179 | { 180 | for (position = 0; position <= 180; position++) 181 | { 182 | // goes from 0 degrees to 180 degrees 183 | // in steps of 1 degree 184 | 185 | if (position % 30 == 0) 186 | { 187 | Serial.print(F("Servo1 pos = ")); 188 | Serial.print(position); 189 | Serial.print(F(", Servo2 pos = ")); 190 | Serial.println(180 - position); 191 | } 192 | 193 | ESP32_ISR_Servos.setPosition(servoIndex1, position); 194 | ESP32_ISR_Servos.setPosition(servoIndex2, 180 - position); 195 | // waits 30ms for the servo to reach the position 196 | delay(30); 197 | } 198 | 199 | delay(5000); 200 | 201 | for (position = 180; position >= 0; position--) 202 | { 203 | // goes from 180 degrees to 0 degrees 204 | if (position % 30 == 0) 205 | { 206 | Serial.print(F("Servo1 pos = ")); 207 | Serial.print(position); 208 | Serial.print(F(", Servo2 pos = ")); 209 | Serial.println(180 - position); 210 | } 211 | 212 | ESP32_ISR_Servos.setPosition(servoIndex1, position); 213 | ESP32_ISR_Servos.setPosition(servoIndex2, 180 - position); 214 | // waits 30ms for the servo to reach the position 215 | delay(30); 216 | } 217 | 218 | delay(5000); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /examples/ESP32_MultipleRandomServos/ESP32_MultipleRandomServos.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_MultipleRandomServos.ino 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | The ESP32 has two timer groups, each one with two general purpose hardware timers. All the timers 10 | are based on 64 bits counters and 16 bit prescalers 11 | The timer counters can be configured to count up or down and support automatic reload and software reload 12 | They can also generate alarms when they reach a specific value, defined by the software. 13 | The value of the counter can be read by the software program. 14 | 15 | Now these new 16 ISR-based PWM servo contro uses only 1 hardware timer. 16 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 17 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 18 | This important feature is absolutely necessary for mission-critical tasks. 19 | 20 | Notes: 21 | Special design is necessary to share data between interrupt code and the rest of your program. 22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 23 | variable can not spontaneously change. Because your function may change variables while your program is using them, 24 | the compiler needs this hint. But volatile alone is often not enough. 25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 28 | or the entire sequence of your code which accesses the data. 29 | *****************************************************************************************************************************/ 30 | /**************************************************************************************************************************** 31 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 32 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 33 | and Blynk services. You can also have many (up to 16) timers to use. 34 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 35 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 36 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 37 | written 38 | 39 | From ESP32 Servo Example Using Arduino ESP32 Servo Library 40 | John K. Bennett 41 | March, 2017 42 | 43 | Different servos require different pulse widths to vary servo angle, but the range is 44 | an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos 45 | sweep 180 degrees, so the lowest number in the published range for a particular servo 46 | represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top 47 | of the range represents 180 degrees. So for example, if the range is 1000us to 2000us, 48 | 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800 49 | degrees. 50 | 51 | Circuit: 52 | Servo motors have three wires: power, ground, and signal. The power wire is typically red, 53 | the ground wire is typically black or brown, and the signal wire is typically yellow, 54 | orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw 55 | considerable power, we will connect servo power to the VBat pin of the ESP32 (located 56 | near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS. 57 | 58 | We could also connect servo power to a separate external 59 | power source (as long as we connect all of the grounds (ESP32, servo, and external power). 60 | In this example, we just connect ESP32 ground to servo ground. The servo signal pins 61 | connect to any available GPIO pins on the ESP32 (in this example, we use pins 62 | 22, 19, 23, & 18). 63 | 64 | In this example, we assume four Tower Pro SG90 small servos. 65 | The published min and max for this servo are 500 and 2400, respectively. 66 | These values actually drive the servos a little past 0 and 180, so 67 | if you are particular, adjust the min and max values to match your needs. 68 | Experimentally, 550 and 2350 are pretty close to 0 and 180. 69 | *****************************************************************************************************************************/ 70 | #ifndef ESP32 71 | #error This code is designed to run on ESP32 platform, not Arduino nor ESP8266! Please check your Tools->Board setting. 72 | #endif 73 | 74 | #define TIMER_INTERRUPT_DEBUG 1 75 | #define ISR_SERVO_DEBUG 1 76 | 77 | // Select different ESP32 timer number (0-3) to avoid conflict 78 | #define USE_ESP32_TIMER_NO 3 79 | 80 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 81 | #include "ESP32_ISR_Servo.h" 82 | 83 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 84 | // Don't use PIN_D2 with ESP32_C3 (crash) 85 | 86 | //See file .../hardware/espressif/esp32/variants/(esp32|doitESP32devkitV1)/pins_arduino.h 87 | #if !defined(LED_BUILTIN) 88 | #define LED_BUILTIN 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 89 | #endif 90 | 91 | #define PIN_LED 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 92 | 93 | #define PIN_D0 0 // Pin D0 mapped to pin GPIO0/BOOT/ADC11/TOUCH1 of ESP32 94 | #define PIN_D1 1 // Pin D1 mapped to pin GPIO1/TX0 of ESP32 95 | #define PIN_D2 2 // Pin D2 mapped to pin GPIO2/ADC12/TOUCH2 of ESP32 96 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 97 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 98 | #define PIN_D5 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 99 | #define PIN_D6 6 // Pin D6 mapped to pin GPIO6/FLASH_SCK of ESP32 100 | #define PIN_D7 7 // Pin D7 mapped to pin GPIO7/FLASH_D0 of ESP32 101 | #define PIN_D8 8 // Pin D8 mapped to pin GPIO8/FLASH_D1 of ESP32 102 | #define PIN_D9 9 // Pin D9 mapped to pin GPIO9/FLASH_D2 of ESP32 103 | 104 | #define PIN_D10 10 // Pin D10 mapped to pin GPIO10/FLASH_D3 of ESP32 105 | #define PIN_D11 11 // Pin D11 mapped to pin GPIO11/FLASH_CMD of ESP32 106 | #define PIN_D12 12 // Pin D12 mapped to pin GPIO12/HSPI_MISO/ADC15/TOUCH5/TDI of ESP32 107 | #define PIN_D13 13 // Pin D13 mapped to pin GPIO13/HSPI_MOSI/ADC14/TOUCH4/TCK of ESP32 108 | #define PIN_D14 14 // Pin D14 mapped to pin GPIO14/HSPI_SCK/ADC16/TOUCH6/TMS of ESP32 109 | #define PIN_D15 15 // Pin D15 mapped to pin GPIO15/HSPI_SS/ADC13/TOUCH3/TDO of ESP32 110 | #define PIN_D16 16 // Pin D16 mapped to pin GPIO16/TX2 of ESP32 111 | #define PIN_D17 17 // Pin D17 mapped to pin GPIO17/RX2 of ESP32 112 | #define PIN_D18 18 // Pin D18 mapped to pin GPIO18/VSPI_SCK of ESP32 113 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO19/VSPI_MISO of ESP32 114 | 115 | #define PIN_D21 21 // Pin D21 mapped to pin GPIO21/SDA of ESP32 116 | #define PIN_D22 22 // Pin D22 mapped to pin GPIO22/SCL of ESP32 117 | #define PIN_D23 23 // Pin D23 mapped to pin GPIO23/VSPI_MOSI of ESP32 118 | #define PIN_D24 24 // Pin D24 mapped to pin GPIO24 of ESP32 119 | #define PIN_D25 25 // Pin D25 mapped to pin GPIO25/ADC18/DAC1 of ESP32 120 | #define PIN_D26 26 // Pin D26 mapped to pin GPIO26/ADC19/DAC2 of ESP32 121 | #define PIN_D27 27 // Pin D27 mapped to pin GPIO27/ADC17/TOUCH7 of ESP32 122 | 123 | #define PIN_D32 32 // Pin D32 mapped to pin GPIO32/ADC4/TOUCH9 of ESP32 124 | #define PIN_D33 33 // Pin D33 mapped to pin GPIO33/ADC5/TOUCH8 of ESP32 125 | #define PIN_D34 34 // Pin D34 mapped to pin GPIO34/ADC6 of ESP32 126 | #define PIN_D35 35 // Pin D35 mapped to pin GPIO35/ADC7 of ESP32 127 | #define PIN_D36 36 // Pin D36 mapped to pin GPIO36/ADC0/SVP of ESP32 128 | #define PIN_D39 39 // Pin D39 mapped to pin GPIO39/ADC3/SVN of ESP32 129 | 130 | #define PIN_RX0 3 // Pin RX0 mapped to pin GPIO3/RX0 of ESP32 131 | #define PIN_TX0 1 // Pin TX0 mapped to pin GPIO1/TX0 of ESP32 132 | 133 | #define PIN_SCL 22 // Pin SCL mapped to pin GPIO22/SCL of ESP32 134 | #define PIN_SDA 21 // Pin SDA mapped to pin GPIO21/SDA of ESP32 135 | 136 | // Published values for SG90 servos; adjust if needed 137 | #define MIN_MICROS 800 //544 138 | #define MAX_MICROS 2450 139 | 140 | #define NUM_SERVOS 6 141 | 142 | typedef struct 143 | { 144 | int servoIndex; 145 | uint8_t servoPin; 146 | } ISR_servo_t; 147 | 148 | ISR_servo_t ISR_servo[NUM_SERVOS] = 149 | { 150 | { -1, PIN_D3 }, { -1, PIN_D4 }, { -1, PIN_D5 }, { -1, PIN_D6 }, { -1, PIN_D7 }, { -1, PIN_D8 } 151 | }; 152 | 153 | void setup() 154 | { 155 | Serial.begin(115200); 156 | 157 | while (!Serial && millis() < 5000); 158 | 159 | delay(500); 160 | 161 | Serial.print(F("\nStarting ESP32_MultipleRandomServos on ")); 162 | Serial.println(ARDUINO_BOARD); 163 | Serial.println(ESP32_ISR_SERVO_VERSION); 164 | 165 | //Select ESP32 timer USE_ESP32_TIMER_NO 166 | ESP32_ISR_Servos.useTimer(USE_ESP32_TIMER_NO); 167 | 168 | for (int index = 0; index < NUM_SERVOS; index++) 169 | { 170 | ISR_servo[index].servoIndex = ESP32_ISR_Servos.setupServo(ISR_servo[index].servoPin, MIN_MICROS, MAX_MICROS); 171 | 172 | if (ISR_servo[index].servoIndex != -1) 173 | { 174 | Serial.print(F("Setup OK Servo index = ")); 175 | Serial.println(ISR_servo[index].servoIndex); 176 | } 177 | else 178 | { 179 | Serial.print(F("Setup Failed Servo index = ")); 180 | Serial.println(ISR_servo[index].servoIndex); 181 | } 182 | } 183 | } 184 | 185 | void printServoInfo(int indexServo) 186 | { 187 | Serial.print(F("Servos idx = ")); 188 | Serial.print(indexServo); 189 | Serial.print(F(", act. pos. (deg) = ")); 190 | Serial.print(ESP32_ISR_Servos.getPosition(ISR_servo[indexServo].servoIndex) ); 191 | Serial.print(F(", pulseWidth (us) = ")); 192 | Serial.println(ESP32_ISR_Servos.getPulseWidth(ISR_servo[indexServo].servoIndex)); 193 | } 194 | 195 | void loop() 196 | { 197 | int position; // position in degrees 198 | 199 | position = 0; 200 | Serial.println(F("Servos @ 0 degree")); 201 | 202 | for (int index = 0; index < NUM_SERVOS; index++) 203 | { 204 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 205 | printServoInfo(index); 206 | } 207 | 208 | // waits 5s between test 209 | delay(5000); 210 | 211 | position = 90; 212 | Serial.println(F("Servos @ 90 degree")); 213 | 214 | for (int index = 0; index < NUM_SERVOS; index++) 215 | { 216 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 217 | printServoInfo(index); 218 | } 219 | 220 | // waits 5s between test 221 | delay(5000); 222 | 223 | position = 180; 224 | Serial.println(F("Servos @ 180 degree")); 225 | 226 | for (int index = 0; index < NUM_SERVOS; index++) 227 | { 228 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 229 | printServoInfo(index); 230 | } 231 | 232 | // waits 5s between test 233 | delay(5000); 234 | 235 | Serial.println(F("Servos sweeps from 0-180 degress")); 236 | 237 | for (position = 0; position <= 180; position += 5) 238 | { 239 | // goes from 0 degrees to 180 degrees 240 | // in steps of 1 degree 241 | for (int index = 0; index < NUM_SERVOS; index++) 242 | { 243 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 244 | } 245 | 246 | // waits 50ms for the servo to reach the position 247 | delay(50); 248 | } 249 | 250 | // waits 5s between test 251 | delay(5000); 252 | 253 | Serial.println(F("Servos sweeps from 180-0 degress")); 254 | 255 | for (position = 180; position >= 0; position -= 5) 256 | { 257 | // goes from 0 degrees to 180 degrees 258 | // in steps of 1 degree 259 | for (int index = 0; index < NUM_SERVOS; index++) 260 | { 261 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 262 | } 263 | 264 | // waits 50ms for the servo to reach the position 265 | delay(50); 266 | } 267 | 268 | // waits 5s between test 269 | delay(5000); 270 | 271 | Serial.println(F("Servos, index depending, be somewhere from 0-180 degress")); 272 | 273 | for (position = 0; position <= 180; position += 5) 274 | { 275 | // goes from 0 degrees to 180 degrees 276 | // in steps of 1 degree 277 | for (int index = 0; index < NUM_SERVOS; index++) 278 | { 279 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, (position + index * (180 / NUM_SERVOS)) % 180 ); 280 | } 281 | 282 | // waits 50ms for the servo to reach the position 283 | delay(50); 284 | } 285 | 286 | delay(5000); 287 | 288 | Serial.println(F("Servos, index depending, be somewhere from 180-0 degress")); 289 | 290 | for (position = 180; position >= 0; position -= 5) 291 | { 292 | // goes from 0 degrees to 180 degrees 293 | // in steps of 1 degree 294 | for (int index = 0; index < NUM_SERVOS; index++) 295 | { 296 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, (position + index * (180 / NUM_SERVOS)) % 180 ); 297 | } 298 | 299 | // waits 50ms for the servo to reach the position 300 | delay(50); 301 | } 302 | 303 | // waits 5s between test 304 | delay(5000); 305 | 306 | } 307 | -------------------------------------------------------------------------------- /examples/ESP32_MultipleServos/ESP32_MultipleServos.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_MultipleServos.ino 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | The ESP32 has two timer groups, each one with two general purpose hardware timers. All the timers 10 | are based on 64 bits counters and 16 bit prescalers 11 | The timer counters can be configured to count up or down and support automatic reload and software reload 12 | They can also generate alarms when they reach a specific value, defined by the software. 13 | The value of the counter can be read by the software program. 14 | 15 | Now these new 16 ISR-based PWM servo contro uses only 1 hardware timer. 16 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 17 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 18 | This important feature is absolutely necessary for mission-critical tasks. 19 | 20 | Notes: 21 | Special design is necessary to share data between interrupt code and the rest of your program. 22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 23 | variable can not spontaneously change. Because your function may change variables while your program is using them, 24 | the compiler needs this hint. But volatile alone is often not enough. 25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 28 | or the entire sequence of your code which accesses the data. 29 | *****************************************************************************************************************************/ 30 | 31 | /**************************************************************************************************************************** 32 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 33 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 34 | and Blynk services. You can also have many (up to 16) timers to use. 35 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 36 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 37 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 38 | written 39 | 40 | From ESP32 Servo Example Using Arduino ESP32 Servo Library 41 | John K. Bennett 42 | March, 2017 43 | 44 | Different servos require different pulse widths to vary servo angle, but the range is 45 | an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos 46 | sweep 180 degrees, so the lowest number in the published range for a particular servo 47 | represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top 48 | of the range represents 180 degrees. So for example, if the range is 1000us to 2000us, 49 | 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800 50 | degrees. 51 | 52 | Circuit: 53 | Servo motors have three wires: power, ground, and signal. The power wire is typically red, 54 | the ground wire is typically black or brown, and the signal wire is typically yellow, 55 | orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw 56 | considerable power, we will connect servo power to the VBat pin of the ESP32 (located 57 | near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS. 58 | 59 | We could also connect servo power to a separate external 60 | power source (as long as we connect all of the grounds (ESP32, servo, and external power). 61 | In this example, we just connect ESP32 ground to servo ground. The servo signal pins 62 | connect to any available GPIO pins on the ESP32 (in this example, we use pins 63 | 22, 19, 23, & 18). 64 | 65 | In this example, we assume four Tower Pro SG90 small servos. 66 | The published min and max for this servo are 500 and 2400, respectively. 67 | These values actually drive the servos a little past 0 and 180, so 68 | if you are particular, adjust the min and max values to match your needs. 69 | Experimentally, 550 and 2350 are pretty close to 0 and 180. 70 | *****************************************************************************************************************************/ 71 | #ifndef ESP32 72 | #error This code is designed to run on ESP32 platform, not Arduino nor ESP8266! Please check your Tools->Board setting. 73 | #endif 74 | 75 | #define TIMER_INTERRUPT_DEBUG 1 76 | #define ISR_SERVO_DEBUG 1 77 | 78 | // Select different ESP32 timer number (0-3) to avoid conflict 79 | #define USE_ESP32_TIMER_NO 3 80 | 81 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 82 | #include "ESP32_ISR_Servo.h" 83 | 84 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 85 | // Don't use PIN_D2 with ESP32_C3 (crash) 86 | 87 | //See file .../hardware/espressif/esp32/variants/(esp32|doitESP32devkitV1)/pins_arduino.h 88 | #if !defined(LED_BUILTIN) 89 | #define LED_BUILTIN 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 90 | #endif 91 | 92 | #define PIN_LED 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 93 | 94 | #define PIN_D0 0 // Pin D0 mapped to pin GPIO0/BOOT/ADC11/TOUCH1 of ESP32 95 | #define PIN_D1 1 // Pin D1 mapped to pin GPIO1/TX0 of ESP32 96 | #define PIN_D2 2 // Pin D2 mapped to pin GPIO2/ADC12/TOUCH2 of ESP32 97 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 98 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 99 | #define PIN_D5 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 100 | #define PIN_D6 6 // Pin D6 mapped to pin GPIO6/FLASH_SCK of ESP32 101 | #define PIN_D7 7 // Pin D7 mapped to pin GPIO7/FLASH_D0 of ESP32 102 | #define PIN_D8 8 // Pin D8 mapped to pin GPIO8/FLASH_D1 of ESP32 103 | #define PIN_D9 9 // Pin D9 mapped to pin GPIO9/FLASH_D2 of ESP32 104 | 105 | #define PIN_D10 10 // Pin D10 mapped to pin GPIO10/FLASH_D3 of ESP32 106 | #define PIN_D11 11 // Pin D11 mapped to pin GPIO11/FLASH_CMD of ESP32 107 | #define PIN_D12 12 // Pin D12 mapped to pin GPIO12/HSPI_MISO/ADC15/TOUCH5/TDI of ESP32 108 | #define PIN_D13 13 // Pin D13 mapped to pin GPIO13/HSPI_MOSI/ADC14/TOUCH4/TCK of ESP32 109 | #define PIN_D14 14 // Pin D14 mapped to pin GPIO14/HSPI_SCK/ADC16/TOUCH6/TMS of ESP32 110 | #define PIN_D15 15 // Pin D15 mapped to pin GPIO15/HSPI_SS/ADC13/TOUCH3/TDO of ESP32 111 | #define PIN_D16 16 // Pin D16 mapped to pin GPIO16/TX2 of ESP32 112 | #define PIN_D17 17 // Pin D17 mapped to pin GPIO17/RX2 of ESP32 113 | #define PIN_D18 18 // Pin D18 mapped to pin GPIO18/VSPI_SCK of ESP32 114 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO19/VSPI_MISO of ESP32 115 | 116 | #define PIN_D21 21 // Pin D21 mapped to pin GPIO21/SDA of ESP32 117 | #define PIN_D22 22 // Pin D22 mapped to pin GPIO22/SCL of ESP32 118 | #define PIN_D23 23 // Pin D23 mapped to pin GPIO23/VSPI_MOSI of ESP32 119 | #define PIN_D24 24 // Pin D24 mapped to pin GPIO24 of ESP32 120 | #define PIN_D25 25 // Pin D25 mapped to pin GPIO25/ADC18/DAC1 of ESP32 121 | #define PIN_D26 26 // Pin D26 mapped to pin GPIO26/ADC19/DAC2 of ESP32 122 | #define PIN_D27 27 // Pin D27 mapped to pin GPIO27/ADC17/TOUCH7 of ESP32 123 | 124 | #define PIN_D32 32 // Pin D32 mapped to pin GPIO32/ADC4/TOUCH9 of ESP32 125 | #define PIN_D33 33 // Pin D33 mapped to pin GPIO33/ADC5/TOUCH8 of ESP32 126 | #define PIN_D34 34 // Pin D34 mapped to pin GPIO34/ADC6 of ESP32 127 | #define PIN_D35 35 // Pin D35 mapped to pin GPIO35/ADC7 of ESP32 128 | #define PIN_D36 36 // Pin D36 mapped to pin GPIO36/ADC0/SVP of ESP32 129 | #define PIN_D39 39 // Pin D39 mapped to pin GPIO39/ADC3/SVN of ESP32 130 | 131 | #define PIN_RX0 3 // Pin RX0 mapped to pin GPIO3/RX0 of ESP32 132 | #define PIN_TX0 1 // Pin TX0 mapped to pin GPIO1/TX0 of ESP32 133 | 134 | #define PIN_SCL 22 // Pin SCL mapped to pin GPIO22/SCL of ESP32 135 | #define PIN_SDA 21 // Pin SDA mapped to pin GPIO21/SDA of ESP32 136 | 137 | // Published values for SG90 servos; adjust if needed 138 | #define MIN_MICROS 800 //544 139 | #define MAX_MICROS 2450 140 | 141 | #define NUM_SERVOS 6 142 | 143 | typedef struct 144 | { 145 | int servoIndex; 146 | uint8_t servoPin; 147 | } ISR_servo_t; 148 | 149 | ISR_servo_t ISR_servo[NUM_SERVOS] = 150 | { 151 | { -1, PIN_D3 }, { -1, PIN_D4 }, { -1, PIN_D5 }, { -1, PIN_D6 }, { -1, PIN_D7 }, { -1, PIN_D8 } 152 | }; 153 | 154 | void setup() 155 | { 156 | Serial.begin(115200); 157 | 158 | while (!Serial && millis() < 5000); 159 | 160 | delay(500); 161 | 162 | Serial.print(F("\nStarting ESP32_MultipleServos on ")); 163 | Serial.println(ARDUINO_BOARD); 164 | Serial.println(ESP32_ISR_SERVO_VERSION); 165 | 166 | //Select ESP32 timer USE_ESP32_TIMER_NO 167 | ESP32_ISR_Servos.useTimer(USE_ESP32_TIMER_NO); 168 | 169 | for (int index = 0; index < NUM_SERVOS; index++) 170 | { 171 | ISR_servo[index].servoIndex = ESP32_ISR_Servos.setupServo(ISR_servo[index].servoPin, MIN_MICROS, MAX_MICROS); 172 | 173 | if (ISR_servo[index].servoIndex != -1) 174 | { 175 | Serial.print(F("Setup OK Servo index = ")); 176 | Serial.println(ISR_servo[index].servoIndex); 177 | } 178 | else 179 | { 180 | Serial.print(F("Setup Failed Servo index = ")); 181 | Serial.println(ISR_servo[index].servoIndex); 182 | } 183 | } 184 | } 185 | 186 | void loop() 187 | { 188 | int position; // position in degrees 189 | 190 | for (position = 0; position <= 180; position += 5) 191 | { 192 | // goes from 0 degrees to 180 degrees 193 | // in steps of 1 degree 194 | for (int index = 0; index < NUM_SERVOS; index++) 195 | { 196 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, (position + index * (180 / NUM_SERVOS)) % 180 ); 197 | } 198 | 199 | // waits 1s for the servo to reach the position 200 | delay(1000); 201 | } 202 | 203 | for (position = 180; position >= 0; position -= 5) 204 | { 205 | // goes from 0 degrees to 180 degrees 206 | // in steps of 1 degree 207 | for (int index = 0; index < NUM_SERVOS; index++) 208 | { 209 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, (position + index * (180 / NUM_SERVOS)) % 180); 210 | } 211 | 212 | // waits 1s for the servo to reach the position 213 | delay(1000); 214 | } 215 | 216 | delay(5000); 217 | } 218 | -------------------------------------------------------------------------------- /examples/ISR_MultiServos/ISR_MultiServos.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | examples/ISR_MultiServos.ino 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | The ESP32 has two timer groups, each one with two general purpose hardware timers. All the timers 10 | are based on 64 bits counters and 16 bit prescalers 11 | The timer counters can be configured to count up or down and support automatic reload and software reload 12 | They can also generate alarms when they reach a specific value, defined by the software. 13 | The value of the counter can be read by the software program. 14 | 15 | Now these new 16 ISR-based PWM servo contro uses only 1 hardware timer. 16 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 17 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 18 | This important feature is absolutely necessary for mission-critical tasks. 19 | 20 | Notes: 21 | Special design is necessary to share data between interrupt code and the rest of your program. 22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 23 | variable can not spontaneously change. Because your function may change variables while your program is using them, 24 | the compiler needs this hint. But volatile alone is often not enough. 25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 28 | or the entire sequence of your code which accesses the data. 29 | *****************************************************************************************************************************/ 30 | 31 | /**************************************************************************************************************************** 32 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 33 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 34 | and Blynk services. You can also have many (up to 16) timers to use. 35 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 36 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 37 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 38 | written 39 | 40 | From ESP32 Servo Example Using Arduino ESP32 Servo Library 41 | John K. Bennett 42 | March, 2017 43 | 44 | Different servos require different pulse widths to vary servo angle, but the range is 45 | an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos 46 | sweep 180 degrees, so the lowest number in the published range for a particular servo 47 | represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top 48 | of the range represents 180 degrees. So for example, if the range is 1000us to 2000us, 49 | 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800 50 | degrees. 51 | 52 | Circuit: 53 | Servo motors have three wires: power, ground, and signal. The power wire is typically red, 54 | the ground wire is typically black or brown, and the signal wire is typically yellow, 55 | orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw 56 | considerable power, we will connect servo power to the VBat pin of the ESP32 (located 57 | near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS. 58 | 59 | We could also connect servo power to a separate external 60 | power source (as long as we connect all of the grounds (ESP32, servo, and external power). 61 | In this example, we just connect ESP32 ground to servo ground. The servo signal pins 62 | connect to any available GPIO pins on the ESP32 (in this example, we use pins 63 | 22, 19, 23, & 18). 64 | 65 | In this example, we assume four Tower Pro SG90 small servos. 66 | The published min and max for this servo are 500 and 2400, respectively. 67 | These values actually drive the servos a little past 0 and 180, so 68 | if you are particular, adjust the min and max values to match your needs. 69 | Experimentally, 550 and 2350 are pretty close to 0 and 180. 70 | *****************************************************************************************************************************/ 71 | 72 | #ifndef ESP32 73 | #error This code is designed to run on ESP32 platform, not Arduino nor ESP8266! Please check your Tools->Board setting. 74 | #endif 75 | 76 | #define TIMER_INTERRUPT_DEBUG 1 77 | #define ISR_SERVO_DEBUG 1 78 | 79 | // Select different ESP32 timer number (0-3) to avoid conflict 80 | #define USE_ESP32_TIMER_NO 3 81 | 82 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 83 | #include "ESP32_ISR_Servo.h" 84 | 85 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 86 | // Don't use PIN_D2 with ESP32_C3 (crash) 87 | 88 | //See file .../hardware/espressif/esp32/variants/(esp32|doitESP32devkitV1)/pins_arduino.h 89 | #if !defined(LED_BUILTIN) 90 | #define LED_BUILTIN 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 91 | #endif 92 | 93 | #define PIN_LED 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 94 | 95 | #define PIN_D0 0 // Pin D0 mapped to pin GPIO0/BOOT/ADC11/TOUCH1 of ESP32 96 | #define PIN_D1 1 // Pin D1 mapped to pin GPIO1/TX0 of ESP32 97 | #define PIN_D2 2 // Pin D2 mapped to pin GPIO2/ADC12/TOUCH2 of ESP32 98 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 99 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 100 | #define PIN_D5 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 101 | #define PIN_D6 6 // Pin D6 mapped to pin GPIO6/FLASH_SCK of ESP32 102 | #define PIN_D7 7 // Pin D7 mapped to pin GPIO7/FLASH_D0 of ESP32 103 | #define PIN_D8 8 // Pin D8 mapped to pin GPIO8/FLASH_D1 of ESP32 104 | #define PIN_D9 9 // Pin D9 mapped to pin GPIO9/FLASH_D2 of ESP32 105 | 106 | #define PIN_D10 10 // Pin D10 mapped to pin GPIO10/FLASH_D3 of ESP32 107 | #define PIN_D11 11 // Pin D11 mapped to pin GPIO11/FLASH_CMD of ESP32 108 | #define PIN_D12 12 // Pin D12 mapped to pin GPIO12/HSPI_MISO/ADC15/TOUCH5/TDI of ESP32 109 | #define PIN_D13 13 // Pin D13 mapped to pin GPIO13/HSPI_MOSI/ADC14/TOUCH4/TCK of ESP32 110 | #define PIN_D14 14 // Pin D14 mapped to pin GPIO14/HSPI_SCK/ADC16/TOUCH6/TMS of ESP32 111 | #define PIN_D15 15 // Pin D15 mapped to pin GPIO15/HSPI_SS/ADC13/TOUCH3/TDO of ESP32 112 | #define PIN_D16 16 // Pin D16 mapped to pin GPIO16/TX2 of ESP32 113 | #define PIN_D17 17 // Pin D17 mapped to pin GPIO17/RX2 of ESP32 114 | #define PIN_D18 18 // Pin D18 mapped to pin GPIO18/VSPI_SCK of ESP32 115 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO19/VSPI_MISO of ESP32 116 | 117 | #define PIN_D21 21 // Pin D21 mapped to pin GPIO21/SDA of ESP32 118 | #define PIN_D22 22 // Pin D22 mapped to pin GPIO22/SCL of ESP32 119 | #define PIN_D23 23 // Pin D23 mapped to pin GPIO23/VSPI_MOSI of ESP32 120 | #define PIN_D24 24 // Pin D24 mapped to pin GPIO24 of ESP32 121 | #define PIN_D25 25 // Pin D25 mapped to pin GPIO25/ADC18/DAC1 of ESP32 122 | #define PIN_D26 26 // Pin D26 mapped to pin GPIO26/ADC19/DAC2 of ESP32 123 | #define PIN_D27 27 // Pin D27 mapped to pin GPIO27/ADC17/TOUCH7 of ESP32 124 | 125 | #define PIN_D32 32 // Pin D32 mapped to pin GPIO32/ADC4/TOUCH9 of ESP32 126 | #define PIN_D33 33 // Pin D33 mapped to pin GPIO33/ADC5/TOUCH8 of ESP32 127 | #define PIN_D34 34 // Pin D34 mapped to pin GPIO34/ADC6 of ESP32 128 | #define PIN_D35 35 // Pin D35 mapped to pin GPIO35/ADC7 of ESP32 129 | #define PIN_D36 36 // Pin D36 mapped to pin GPIO36/ADC0/SVP of ESP32 130 | #define PIN_D39 39 // Pin D39 mapped to pin GPIO39/ADC3/SVN of ESP32 131 | 132 | #define PIN_RX0 3 // Pin RX0 mapped to pin GPIO3/RX0 of ESP32 133 | #define PIN_TX0 1 // Pin TX0 mapped to pin GPIO1/TX0 of ESP32 134 | 135 | #define PIN_SCL 22 // Pin SCL mapped to pin GPIO22/SCL of ESP32 136 | #define PIN_SDA 21 // Pin SDA mapped to pin GPIO21/SDA of ESP32 137 | 138 | // Published values for SG90 servos; adjust if needed 139 | #define MIN_MICROS 800 //544 140 | #define MAX_MICROS 2450 141 | 142 | int servoIndex1 = -1; 143 | int servoIndex2 = -1; 144 | 145 | void setup() 146 | { 147 | Serial.begin(115200); 148 | 149 | while (!Serial && millis() < 5000); 150 | 151 | delay(500); 152 | 153 | Serial.print(F("\nStarting ISR_MultiServos on ")); 154 | Serial.println(ARDUINO_BOARD); 155 | Serial.println(ESP32_ISR_SERVO_VERSION); 156 | 157 | //Select ESP32 timer USE_ESP32_TIMER_NO 158 | ESP32_ISR_Servos.useTimer(USE_ESP32_TIMER_NO); 159 | 160 | servoIndex1 = ESP32_ISR_Servos.setupServo(PIN_D5, MIN_MICROS, MAX_MICROS); 161 | servoIndex2 = ESP32_ISR_Servos.setupServo(PIN_D6, MIN_MICROS, MAX_MICROS); 162 | 163 | if (servoIndex1 != -1) 164 | Serial.println(F("Setup Servo1 OK")); 165 | else 166 | Serial.println(F("Setup Servo1 failed")); 167 | 168 | if (servoIndex2 != -1) 169 | Serial.println(F("Setup Servo2 OK")); 170 | else 171 | Serial.println(F("Setup Servo2 failed")); 172 | } 173 | 174 | void loop() 175 | { 176 | int position; 177 | 178 | if ( ( servoIndex1 != -1) && ( servoIndex2 != -1) ) 179 | { 180 | for (position = 0; position <= 180; position++) 181 | { 182 | // goes from 0 degrees to 180 degrees 183 | // in steps of 1 degree 184 | 185 | if (position % 30 == 0) 186 | { 187 | Serial.print(F("Servo1 pos = ")); 188 | Serial.print(position); 189 | Serial.print(F(", Servo2 pos = ")); 190 | Serial.println(180 - position); 191 | } 192 | 193 | ESP32_ISR_Servos.setPosition(servoIndex1, position); 194 | ESP32_ISR_Servos.setPosition(servoIndex2, 180 - position); 195 | // waits 30ms for the servo to reach the position 196 | delay(30); 197 | } 198 | 199 | delay(5000); 200 | 201 | for (position = 180; position >= 0; position--) 202 | { 203 | // goes from 180 degrees to 0 degrees 204 | if (position % 30 == 0) 205 | { 206 | Serial.print(F("Servo1 pos = ")); 207 | Serial.print(position); 208 | Serial.print(F(", Servo2 pos = ")); 209 | Serial.println(180 - position); 210 | } 211 | 212 | ESP32_ISR_Servos.setPosition(servoIndex1, position); 213 | ESP32_ISR_Servos.setPosition(servoIndex2, 180 - position); 214 | // waits 30ms for the servo to reach the position 215 | delay(30); 216 | } 217 | 218 | delay(5000); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /examples/MultipleRandomServos/MultipleRandomServos.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | examples/MultipleRandomServos.ino 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | The ESP32 has two timer groups, each one with two general purpose hardware timers. All the timers 10 | are based on 64 bits counters and 16 bit prescalers 11 | The timer counters can be configured to count up or down and support automatic reload and software reload 12 | They can also generate alarms when they reach a specific value, defined by the software. 13 | The value of the counter can be read by the software program. 14 | 15 | Now these new 16 ISR-based PWM servo contro uses only 1 hardware timer. 16 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 17 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 18 | This important feature is absolutely necessary for mission-critical tasks. 19 | 20 | Notes: 21 | Special design is necessary to share data between interrupt code and the rest of your program. 22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 23 | variable can not spontaneously change. Because your function may change variables while your program is using them, 24 | the compiler needs this hint. But volatile alone is often not enough. 25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 28 | or the entire sequence of your code which accesses the data. 29 | *****************************************************************************************************************************/ 30 | 31 | /**************************************************************************************************************************** 32 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 33 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 34 | and Blynk services. You can also have many (up to 16) timers to use. 35 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 36 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 37 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 38 | written 39 | 40 | From ESP32 Servo Example Using Arduino ESP32 Servo Library 41 | John K. Bennett 42 | March, 2017 43 | 44 | Different servos require different pulse widths to vary servo angle, but the range is 45 | an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos 46 | sweep 180 degrees, so the lowest number in the published range for a particular servo 47 | represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top 48 | of the range represents 180 degrees. So for example, if the range is 1000us to 2000us, 49 | 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800 50 | degrees. 51 | 52 | Circuit: 53 | Servo motors have three wires: power, ground, and signal. The power wire is typically red, 54 | the ground wire is typically black or brown, and the signal wire is typically yellow, 55 | orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw 56 | considerable power, we will connect servo power to the VBat pin of the ESP32 (located 57 | near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS. 58 | 59 | We could also connect servo power to a separate external 60 | power source (as long as we connect all of the grounds (ESP32, servo, and external power). 61 | In this example, we just connect ESP32 ground to servo ground. The servo signal pins 62 | connect to any available GPIO pins on the ESP32 (in this example, we use pins 63 | 22, 19, 23, & 18). 64 | 65 | In this example, we assume four Tower Pro SG90 small servos. 66 | The published min and max for this servo are 500 and 2400, respectively. 67 | These values actually drive the servos a little past 0 and 180, so 68 | if you are particular, adjust the min and max values to match your needs. 69 | Experimentally, 550 and 2350 are pretty close to 0 and 180. 70 | *****************************************************************************************************************************/ 71 | #ifndef ESP32 72 | #error This code is designed to run on ESP32 platform, not Arduino nor ESP8266! Please check your Tools->Board setting. 73 | #endif 74 | 75 | #define TIMER_INTERRUPT_DEBUG 1 76 | #define ISR_SERVO_DEBUG 1 77 | 78 | // Select different ESP32 timer number (0-3) to avoid conflict 79 | #define USE_ESP32_TIMER_NO 3 80 | 81 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 82 | #include "ESP32_ISR_Servo.h" 83 | 84 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 85 | // Don't use PIN_D2 with ESP32_C3 (crash) 86 | 87 | //See file .../hardware/espressif/esp32/variants/(esp32|doitESP32devkitV1)/pins_arduino.h 88 | #if !defined(LED_BUILTIN) 89 | #define LED_BUILTIN 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 90 | #endif 91 | 92 | #define PIN_LED 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 93 | 94 | #define PIN_D0 0 // Pin D0 mapped to pin GPIO0/BOOT/ADC11/TOUCH1 of ESP32 95 | #define PIN_D1 1 // Pin D1 mapped to pin GPIO1/TX0 of ESP32 96 | #define PIN_D2 2 // Pin D2 mapped to pin GPIO2/ADC12/TOUCH2 of ESP32 97 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 98 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 99 | #define PIN_D5 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 100 | #define PIN_D6 6 // Pin D6 mapped to pin GPIO6/FLASH_SCK of ESP32 101 | #define PIN_D7 7 // Pin D7 mapped to pin GPIO7/FLASH_D0 of ESP32 102 | #define PIN_D8 8 // Pin D8 mapped to pin GPIO8/FLASH_D1 of ESP32 103 | #define PIN_D9 9 // Pin D9 mapped to pin GPIO9/FLASH_D2 of ESP32 104 | 105 | #define PIN_D10 10 // Pin D10 mapped to pin GPIO10/FLASH_D3 of ESP32 106 | #define PIN_D11 11 // Pin D11 mapped to pin GPIO11/FLASH_CMD of ESP32 107 | #define PIN_D12 12 // Pin D12 mapped to pin GPIO12/HSPI_MISO/ADC15/TOUCH5/TDI of ESP32 108 | #define PIN_D13 13 // Pin D13 mapped to pin GPIO13/HSPI_MOSI/ADC14/TOUCH4/TCK of ESP32 109 | #define PIN_D14 14 // Pin D14 mapped to pin GPIO14/HSPI_SCK/ADC16/TOUCH6/TMS of ESP32 110 | #define PIN_D15 15 // Pin D15 mapped to pin GPIO15/HSPI_SS/ADC13/TOUCH3/TDO of ESP32 111 | #define PIN_D16 16 // Pin D16 mapped to pin GPIO16/TX2 of ESP32 112 | #define PIN_D17 17 // Pin D17 mapped to pin GPIO17/RX2 of ESP32 113 | #define PIN_D18 18 // Pin D18 mapped to pin GPIO18/VSPI_SCK of ESP32 114 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO19/VSPI_MISO of ESP32 115 | 116 | #define PIN_D21 21 // Pin D21 mapped to pin GPIO21/SDA of ESP32 117 | #define PIN_D22 22 // Pin D22 mapped to pin GPIO22/SCL of ESP32 118 | #define PIN_D23 23 // Pin D23 mapped to pin GPIO23/VSPI_MOSI of ESP32 119 | #define PIN_D24 24 // Pin D24 mapped to pin GPIO24 of ESP32 120 | #define PIN_D25 25 // Pin D25 mapped to pin GPIO25/ADC18/DAC1 of ESP32 121 | #define PIN_D26 26 // Pin D26 mapped to pin GPIO26/ADC19/DAC2 of ESP32 122 | #define PIN_D27 27 // Pin D27 mapped to pin GPIO27/ADC17/TOUCH7 of ESP32 123 | 124 | #define PIN_D32 32 // Pin D32 mapped to pin GPIO32/ADC4/TOUCH9 of ESP32 125 | #define PIN_D33 33 // Pin D33 mapped to pin GPIO33/ADC5/TOUCH8 of ESP32 126 | #define PIN_D34 34 // Pin D34 mapped to pin GPIO34/ADC6 of ESP32 127 | #define PIN_D35 35 // Pin D35 mapped to pin GPIO35/ADC7 of ESP32 128 | #define PIN_D36 36 // Pin D36 mapped to pin GPIO36/ADC0/SVP of ESP32 129 | #define PIN_D39 39 // Pin D39 mapped to pin GPIO39/ADC3/SVN of ESP32 130 | 131 | #define PIN_RX0 3 // Pin RX0 mapped to pin GPIO3/RX0 of ESP32 132 | #define PIN_TX0 1 // Pin TX0 mapped to pin GPIO1/TX0 of ESP32 133 | 134 | #define PIN_SCL 22 // Pin SCL mapped to pin GPIO22/SCL of ESP32 135 | #define PIN_SDA 21 // Pin SDA mapped to pin GPIO21/SDA of ESP32 136 | 137 | // Published values for SG90 servos; adjust if needed 138 | #define MIN_MICROS 800 //544 139 | #define MAX_MICROS 2450 140 | 141 | #define NUM_SERVOS 6 142 | 143 | typedef struct 144 | { 145 | int servoIndex; 146 | uint8_t servoPin; 147 | } ISR_servo_t; 148 | 149 | ISR_servo_t ISR_servo[NUM_SERVOS] = 150 | { 151 | { -1, PIN_D3 }, { -1, PIN_D4 }, { -1, PIN_D5 }, { -1, PIN_D6 }, { -1, PIN_D7 }, { -1, PIN_D8 } 152 | }; 153 | 154 | void setup() 155 | { 156 | Serial.begin(115200); 157 | 158 | while (!Serial && millis() < 5000); 159 | 160 | delay(500); 161 | 162 | Serial.print(F("\nStarting MultipleRandomServos on ")); 163 | Serial.println(ARDUINO_BOARD); 164 | Serial.println(ESP32_ISR_SERVO_VERSION); 165 | 166 | //Select ESP32 timer USE_ESP32_TIMER_NO 167 | ESP32_ISR_Servos.useTimer(USE_ESP32_TIMER_NO); 168 | 169 | for (int index = 0; index < NUM_SERVOS; index++) 170 | { 171 | ISR_servo[index].servoIndex = ESP32_ISR_Servos.setupServo(ISR_servo[index].servoPin, MIN_MICROS, MAX_MICROS); 172 | 173 | if (ISR_servo[index].servoIndex != -1) 174 | { 175 | Serial.print(F("Setup OK Servo index = ")); 176 | Serial.println(ISR_servo[index].servoIndex); 177 | } 178 | else 179 | { 180 | Serial.print(F("Setup Failed Servo index = ")); 181 | Serial.println(ISR_servo[index].servoIndex); 182 | } 183 | } 184 | } 185 | 186 | void printServoInfo(int indexServo) 187 | { 188 | Serial.print(F("Servos idx = ")); 189 | Serial.print(indexServo); 190 | Serial.print(F(", act. pos. (deg) = ")); 191 | Serial.print(ESP32_ISR_Servos.getPosition(ISR_servo[indexServo].servoIndex) ); 192 | Serial.print(F(", pulseWidth (us) = ")); 193 | Serial.println(ESP32_ISR_Servos.getPulseWidth(ISR_servo[indexServo].servoIndex)); 194 | } 195 | 196 | void loop() 197 | { 198 | int position; // position in degrees 199 | 200 | position = 0; 201 | Serial.println(F("Servos @ 0 degree")); 202 | 203 | for (int index = 0; index < NUM_SERVOS; index++) 204 | { 205 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 206 | printServoInfo(index); 207 | } 208 | 209 | // waits 5s between test 210 | delay(5000); 211 | 212 | position = 90; 213 | Serial.println(F("Servos @ 90 degree")); 214 | 215 | for (int index = 0; index < NUM_SERVOS; index++) 216 | { 217 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 218 | printServoInfo(index); 219 | } 220 | 221 | // waits 5s between test 222 | delay(5000); 223 | 224 | position = 180; 225 | Serial.println(F("Servos @ 180 degree")); 226 | 227 | for (int index = 0; index < NUM_SERVOS; index++) 228 | { 229 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 230 | printServoInfo(index); 231 | } 232 | 233 | // waits 5s between test 234 | delay(5000); 235 | 236 | Serial.println(F("Servos sweeps from 0-180 degress")); 237 | 238 | for (position = 0; position <= 180; position += 1) 239 | { 240 | // goes from 0 degrees to 180 degrees 241 | // in steps of 1 degree 242 | for (int index = 0; index < NUM_SERVOS; index++) 243 | { 244 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 245 | } 246 | 247 | // waits 50ms for the servo to reach the position 248 | delay(50); 249 | } 250 | 251 | // waits 5s between test 252 | delay(5000); 253 | 254 | Serial.println(F("Servos sweeps from 180-0 degress")); 255 | 256 | for (position = 180; position >= 0; position -= 1) 257 | { 258 | // goes from 0 degrees to 180 degrees 259 | // in steps of 1 degree 260 | for (int index = 0; index < NUM_SERVOS; index++) 261 | { 262 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, position ); 263 | } 264 | 265 | // waits 50ms for the servo to reach the position 266 | delay(50); 267 | } 268 | 269 | // waits 5s between test 270 | delay(5000); 271 | 272 | Serial.println(F("Servos, index depending, be somewhere from 0-180 degress")); 273 | 274 | for (position = 0; position <= 180; position += 1) 275 | { 276 | // goes from 0 degrees to 180 degrees 277 | // in steps of 1 degree 278 | for (int index = 0; index < NUM_SERVOS; index++) 279 | { 280 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, (position + index * (180 / NUM_SERVOS)) % 180 ); 281 | } 282 | 283 | // waits 50ms for the servo to reach the position 284 | delay(50); 285 | } 286 | 287 | delay(5000); 288 | 289 | Serial.println(F("Servos, index depending, be somewhere from 180-0 degress")); 290 | 291 | for (position = 180; position >= 0; position -= 1) 292 | { 293 | // goes from 0 degrees to 180 degrees 294 | // in steps of 1 degree 295 | for (int index = 0; index < NUM_SERVOS; index++) 296 | { 297 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, (position + index * (180 / NUM_SERVOS)) % 180 ); 298 | } 299 | 300 | // waits 50ms for the servo to reach the position 301 | delay(50); 302 | } 303 | 304 | // waits 5s between test 305 | delay(5000); 306 | } 307 | -------------------------------------------------------------------------------- /examples/MultipleServos/MultipleServos.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | examples/MultipleServos.ino 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | The ESP32 has two timer groups, each one with two general purpose hardware timers. All the timers 10 | are based on 64 bits counters and 16 bit prescalers 11 | The timer counters can be configured to count up or down and support automatic reload and software reload 12 | They can also generate alarms when they reach a specific value, defined by the software. 13 | The value of the counter can be read by the software program. 14 | 15 | Now these new 16 ISR-based PWM servo contro uses only 1 hardware timer. 16 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 17 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 18 | This important feature is absolutely necessary for mission-critical tasks. 19 | 20 | Notes: 21 | Special design is necessary to share data between interrupt code and the rest of your program. 22 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 23 | variable can not spontaneously change. Because your function may change variables while your program is using them, 24 | the compiler needs this hint. But volatile alone is often not enough. 25 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 26 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 27 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 28 | or the entire sequence of your code which accesses the data. 29 | *****************************************************************************************************************************/ 30 | 31 | /**************************************************************************************************************************** 32 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 33 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 34 | and Blynk services. You can also have many (up to 16) timers to use. 35 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 36 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 37 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 38 | written 39 | 40 | From ESP32 Servo Example Using Arduino ESP32 Servo Library 41 | John K. Bennett 42 | March, 2017 43 | 44 | Different servos require different pulse widths to vary servo angle, but the range is 45 | an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos 46 | sweep 180 degrees, so the lowest number in the published range for a particular servo 47 | represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top 48 | of the range represents 180 degrees. So for example, if the range is 1000us to 2000us, 49 | 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800 50 | degrees. 51 | 52 | Circuit: 53 | Servo motors have three wires: power, ground, and signal. The power wire is typically red, 54 | the ground wire is typically black or brown, and the signal wire is typically yellow, 55 | orange or white. Since the ESP32 can supply limited current at only 3.3V, and servos draw 56 | considerable power, we will connect servo power to the VBat pin of the ESP32 (located 57 | near the USB connector). THIS IS ONLY APPROPRIATE FOR SMALL SERVOS. 58 | 59 | We could also connect servo power to a separate external 60 | power source (as long as we connect all of the grounds (ESP32, servo, and external power). 61 | In this example, we just connect ESP32 ground to servo ground. The servo signal pins 62 | connect to any available GPIO pins on the ESP32 (in this example, we use pins 63 | 22, 19, 23, & 18). 64 | 65 | In this example, we assume four Tower Pro SG90 small servos. 66 | The published min and max for this servo are 500 and 2400, respectively. 67 | These values actually drive the servos a little past 0 and 180, so 68 | if you are particular, adjust the min and max values to match your needs. 69 | Experimentally, 550 and 2350 are pretty close to 0 and 180. 70 | *****************************************************************************************************************************/ 71 | #ifndef ESP32 72 | #error This code is designed to run on ESP32 platform, not Arduino nor ESP8266! Please check your Tools->Board setting. 73 | #endif 74 | 75 | #define TIMER_INTERRUPT_DEBUG 1 76 | #define ISR_SERVO_DEBUG 1 77 | 78 | // Select different ESP32 timer number (0-3) to avoid conflict 79 | #define USE_ESP32_TIMER_NO 3 80 | 81 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 82 | #include "ESP32_ISR_Servo.h" 83 | 84 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 85 | // Don't use PIN_D2 with ESP32_C3 (crash) 86 | 87 | //See file .../hardware/espressif/esp32/variants/(esp32|doitESP32devkitV1)/pins_arduino.h 88 | #if !defined(LED_BUILTIN) 89 | #define LED_BUILTIN 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 90 | #endif 91 | 92 | #define PIN_LED 2 // Pin D2 mapped to pin GPIO2/ADC12 of ESP32, control on-board LED 93 | 94 | #define PIN_D0 0 // Pin D0 mapped to pin GPIO0/BOOT/ADC11/TOUCH1 of ESP32 95 | #define PIN_D1 1 // Pin D1 mapped to pin GPIO1/TX0 of ESP32 96 | #define PIN_D2 2 // Pin D2 mapped to pin GPIO2/ADC12/TOUCH2 of ESP32 97 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 98 | #define PIN_D4 4 // Pin D4 mapped to pin GPIO4/ADC10/TOUCH0 of ESP32 99 | #define PIN_D5 5 // Pin D5 mapped to pin GPIO5/SPISS/VSPI_SS of ESP32 100 | #define PIN_D6 6 // Pin D6 mapped to pin GPIO6/FLASH_SCK of ESP32 101 | #define PIN_D7 7 // Pin D7 mapped to pin GPIO7/FLASH_D0 of ESP32 102 | #define PIN_D8 8 // Pin D8 mapped to pin GPIO8/FLASH_D1 of ESP32 103 | #define PIN_D9 9 // Pin D9 mapped to pin GPIO9/FLASH_D2 of ESP32 104 | 105 | #define PIN_D10 10 // Pin D10 mapped to pin GPIO10/FLASH_D3 of ESP32 106 | #define PIN_D11 11 // Pin D11 mapped to pin GPIO11/FLASH_CMD of ESP32 107 | #define PIN_D12 12 // Pin D12 mapped to pin GPIO12/HSPI_MISO/ADC15/TOUCH5/TDI of ESP32 108 | #define PIN_D13 13 // Pin D13 mapped to pin GPIO13/HSPI_MOSI/ADC14/TOUCH4/TCK of ESP32 109 | #define PIN_D14 14 // Pin D14 mapped to pin GPIO14/HSPI_SCK/ADC16/TOUCH6/TMS of ESP32 110 | #define PIN_D15 15 // Pin D15 mapped to pin GPIO15/HSPI_SS/ADC13/TOUCH3/TDO of ESP32 111 | #define PIN_D16 16 // Pin D16 mapped to pin GPIO16/TX2 of ESP32 112 | #define PIN_D17 17 // Pin D17 mapped to pin GPIO17/RX2 of ESP32 113 | #define PIN_D18 18 // Pin D18 mapped to pin GPIO18/VSPI_SCK of ESP32 114 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO19/VSPI_MISO of ESP32 115 | 116 | #define PIN_D21 21 // Pin D21 mapped to pin GPIO21/SDA of ESP32 117 | #define PIN_D22 22 // Pin D22 mapped to pin GPIO22/SCL of ESP32 118 | #define PIN_D23 23 // Pin D23 mapped to pin GPIO23/VSPI_MOSI of ESP32 119 | #define PIN_D24 24 // Pin D24 mapped to pin GPIO24 of ESP32 120 | #define PIN_D25 25 // Pin D25 mapped to pin GPIO25/ADC18/DAC1 of ESP32 121 | #define PIN_D26 26 // Pin D26 mapped to pin GPIO26/ADC19/DAC2 of ESP32 122 | #define PIN_D27 27 // Pin D27 mapped to pin GPIO27/ADC17/TOUCH7 of ESP32 123 | 124 | #define PIN_D32 32 // Pin D32 mapped to pin GPIO32/ADC4/TOUCH9 of ESP32 125 | #define PIN_D33 33 // Pin D33 mapped to pin GPIO33/ADC5/TOUCH8 of ESP32 126 | #define PIN_D34 34 // Pin D34 mapped to pin GPIO34/ADC6 of ESP32 127 | #define PIN_D35 35 // Pin D35 mapped to pin GPIO35/ADC7 of ESP32 128 | #define PIN_D36 36 // Pin D36 mapped to pin GPIO36/ADC0/SVP of ESP32 129 | #define PIN_D39 39 // Pin D39 mapped to pin GPIO39/ADC3/SVN of ESP32 130 | 131 | #define PIN_RX0 3 // Pin RX0 mapped to pin GPIO3/RX0 of ESP32 132 | #define PIN_TX0 1 // Pin TX0 mapped to pin GPIO1/TX0 of ESP32 133 | 134 | #define PIN_SCL 22 // Pin SCL mapped to pin GPIO22/SCL of ESP32 135 | #define PIN_SDA 21 // Pin SDA mapped to pin GPIO21/SDA of ESP32 136 | 137 | // Published values for SG90 servos; adjust if needed 138 | #define MIN_MICROS 800 //544 139 | #define MAX_MICROS 2450 140 | 141 | #define NUM_SERVOS 6 142 | 143 | typedef struct 144 | { 145 | int servoIndex; 146 | uint8_t servoPin; 147 | } ISR_servo_t; 148 | 149 | ISR_servo_t ISR_servo[NUM_SERVOS] = 150 | { 151 | { -1, PIN_D3 }, { -1, PIN_D4 }, { -1, PIN_D5 }, { -1, PIN_D6 }, { -1, PIN_D7 }, { -1, PIN_D8 } 152 | }; 153 | 154 | void setup() 155 | { 156 | Serial.begin(115200); 157 | 158 | while (!Serial && millis() < 5000); 159 | 160 | delay(500); 161 | 162 | Serial.print(F("\nStarting MultipleServos on ")); 163 | Serial.println(ARDUINO_BOARD); 164 | Serial.println(ESP32_ISR_SERVO_VERSION); 165 | 166 | //Select ESP32 timer USE_ESP32_TIMER_NO 167 | ESP32_ISR_Servos.useTimer(USE_ESP32_TIMER_NO); 168 | 169 | for (int index = 0; index < NUM_SERVOS; index++) 170 | { 171 | ISR_servo[index].servoIndex = ESP32_ISR_Servos.setupServo(ISR_servo[index].servoPin, MIN_MICROS, MAX_MICROS); 172 | 173 | if (ISR_servo[index].servoIndex != -1) 174 | { 175 | Serial.print(F("Setup OK Servo index = ")); 176 | Serial.println(ISR_servo[index].servoIndex); 177 | } 178 | else 179 | { 180 | Serial.print(F("Setup Failed Servo index = ")); 181 | Serial.println(ISR_servo[index].servoIndex); 182 | } 183 | } 184 | } 185 | 186 | void loop() 187 | { 188 | int position; // position in degrees 189 | 190 | for (position = 0; position <= 180; position += 5) 191 | { 192 | // goes from 0 degrees to 180 degrees 193 | // in steps of 1 degree 194 | for (int index = 0; index < NUM_SERVOS; index++) 195 | { 196 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, (position + index * (180 / NUM_SERVOS)) % 180 ); 197 | } 198 | 199 | // waits 1s for the servo to reach the position 200 | delay(1000); 201 | } 202 | 203 | for (position = 180; position >= 0; position -= 5) 204 | { 205 | // goes from 0 degrees to 180 degrees 206 | // in steps of 1 degree 207 | for (int index = 0; index < NUM_SERVOS; index++) 208 | { 209 | ESP32_ISR_Servos.setPosition(ISR_servo[index].servoIndex, (position + index * (180 / NUM_SERVOS)) % 180); 210 | } 211 | 212 | // waits 1s for the servo to reach the position 213 | delay(1000); 214 | } 215 | 216 | delay(5000); 217 | } 218 | -------------------------------------------------------------------------------- /examples/multiFileProject/multiFileProject.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | multiFileProject.cpp 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | *****************************************************************************************************************************/ 9 | 10 | // To demo how to include files in multi-file Projects 11 | 12 | #include "multiFileProject.h" 13 | -------------------------------------------------------------------------------- /examples/multiFileProject/multiFileProject.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | multiFileProject.h 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | *****************************************************************************************************************************/ 9 | 10 | // To demo how to include files in multi-file Projects 11 | 12 | #pragma once 13 | 14 | // Can be included as many times as necessary, without `Multiple Definitions` Linker Error 15 | #include "ESP32_ISR_Servo.hpp" 16 | -------------------------------------------------------------------------------- /examples/multiFileProject/multiFileProject.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | multiFileProject.ino 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | *****************************************************************************************************************************/ 9 | 10 | // To demo how to include files in multi-file Projects 11 | 12 | #ifndef ESP32 13 | #error This code is designed to run on ESP32 platform, not Arduino nor ESP32! Please check your Tools->Board setting. 14 | #endif 15 | 16 | #define ESP32_ISR_SERVO_VERSION_MIN_TARGET "ESP32_ISR_Servo v1.5.0" 17 | #define ESP32_ISR_SERVO_VERSION_MIN 1005000 18 | 19 | #include "multiFileProject.h" 20 | 21 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 22 | #include "ESP32_ISR_Servo.h" 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | 28 | while (!Serial && millis() < 5000); 29 | 30 | delay(500); 31 | Serial.println("\nStart multiFileProject"); 32 | Serial.println(ESP32_ISR_SERVO_VERSION); 33 | 34 | #if defined(ESP32_ISR_SERVO_VERSION_MIN) 35 | 36 | if (ESP32_ISR_SERVO_VERSION_INT < ESP32_ISR_SERVO_VERSION_MIN) 37 | { 38 | Serial.print("Warning. Must use this example on Version equal or later than : "); 39 | Serial.println(ESP32_ISR_SERVO_VERSION_MIN_TARGET); 40 | } 41 | 42 | #endif 43 | } 44 | 45 | void loop() 46 | { 47 | // put your main code here, to run repeatedly: 48 | } 49 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Data types (KEYWORD1) 3 | ####################################### 4 | 5 | ESP32_ISR_Servo KEYWORD1 6 | ESP32_ISR_Servos KEYWORD1 7 | ESP32TimerInterrupt KEYWORD1 8 | ESP32FastTimer KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | 14 | setFrequency KEYWORD2 15 | attachInterruptInterval KEYWORD2 16 | detachInterrupt KEYWORD2 17 | reattachInterrupt KEYWORD2 18 | useTimer KEYWORD2 19 | init KEYWORD2 20 | run KEYWORD2 21 | setupServo KEYWORD2 22 | setPosition KEYWORD2 23 | getPosition KEYWORD2 24 | setPulseWidth KEYWORD2 25 | getPulseWidth KEYWORD2 26 | deleteServo KEYWORD2 27 | isEnabled KEYWORD2 28 | enable KEYWORD2 29 | disable KEYWORD2 30 | enableAll KEYWORD2 31 | disableAll KEYWORD2 32 | toggle KEYWORD2 33 | getNumServos KEYWORD2 34 | getNumAvailableServos KEYWORD2 35 | ESP32_ISR_Servo_Handler KEYWORD2 36 | 37 | ####################################### 38 | # Literals (LITERAL1) 39 | ####################################### 40 | 41 | ISR_SERVO_DEBUG LITERAL1 42 | 43 | ESP32_ISR_SERVO_VERSION LITERAL1 44 | ESP32_ISR_SERVO_VERSION_MAJOR LITERAL1 45 | ESP32_ISR_SERVO_VERSION_MINOR LITERAL1 46 | ESP32_ISR_SERVO_VERSION_PATCH LITERAL1 47 | ESP32_ISR_SERVO_VERSION_INT LITERAL1 48 | 49 | ESP32_MAX_PIN LITERAL1 50 | ESP32_WRONG_PIN LITERAL1 51 | 52 | MIN_PULSE_WIDTH LITERAL1 53 | MAX_PULSE_WIDTH LITERAL1 54 | DEFAULT_PULSE_WIDTH LITERAL1 55 | REFRESH_INTERVAL LITERAL1 56 | 57 | 58 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP32_ISR_Servo", 3 | "version": "1.5.0", 4 | "keywords": "timer, interrupt, isr, hardware, servo, isr-based-servo, servo-control, esp32, esp32-s2, esp32-s3, esp32-c3, mission-critical, precise, non-blocking", 5 | "description": "This library enables you to use 1 Hardware Timer on an ESP32-based board to control up to 16 or more servo motors. Now supporting ESP32, ESP32_S2, ESP32_S3, ESP32_C3-based boards. Tested OK with ESP32 core v2.0.5", 6 | "authors": 7 | { 8 | "name": "Khoi Hoang", 9 | "url": "https://github.com/khoih-prog", 10 | "maintainer": true 11 | }, 12 | "repository": 13 | { 14 | "type": "git", 15 | "url": "https://github.com/khoih-prog/ESP32_ISR_Servo" 16 | }, 17 | "homepage": "https://github.com/khoih-prog", 18 | "export": { 19 | "exclude": [ 20 | "linux", 21 | "extras", 22 | "tests" 23 | ] 24 | }, 25 | "license": "MIT", 26 | "frameworks": "*", 27 | "platforms": "espressif32", 28 | "examples": "examples/*/*/*.ino", 29 | "headers": [ "ESP32_ISR_Servo.h", "ESP32_ISR_Servo.hpp" ] 30 | } 31 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32_ISR_Servo 2 | version=1.5.0 3 | author=Khoi Hoang 4 | maintainer=Khoi Hoang 5 | license=MIT 6 | sentence=This library enables you to use Interrupt from Hardware Timers on an ESP32 board to control multiple servo motors. 7 | paragraph=This library enables you to use 1 Hardware Timer on an ESP32-based board to control 16 or more servo motors. Now supporting ESP32, ESP32_S2, ESP32_S3, ESP32_C3-based boards. Tested OK with ESP32 core v2.0.5 8 | category=Device Control 9 | url=https://github.com/khoih-prog/ESP32_ISR_Servo 10 | architectures=esp32 11 | includes=ESP32_ISR_Servo.h, ESP32_ISR_Servo.hpp 12 | -------------------------------------------------------------------------------- /platformio/platformio.ini: -------------------------------------------------------------------------------- 1 | ;PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | ; ============================================================ 13 | ; chose environment: 14 | ; ESP32 15 | ; esp32s2 16 | ; esp32s3 17 | ; esp32c3 18 | 19 | ; ============================================================ 20 | default_envs = ESP32 21 | 22 | [env] 23 | ; ============================================================ 24 | ; Serial configuration 25 | ; choose upload speed, serial-monitor speed 26 | ; ============================================================ 27 | upload_speed = 921600 28 | ;upload_port = COM11 29 | ;monitor_speed = 9600 30 | ;monitor_port = COM11 31 | 32 | ; Checks for the compatibility with frameworks and dev/platforms 33 | lib_compat_mode = strict 34 | lib_ldf_mode = chain+ 35 | ;lib_ldf_mode = deep+ 36 | 37 | lib_deps = 38 | 39 | build_flags = 40 | ; set your debug output (default=Serial) 41 | -D DEBUG_ESP_PORT=Serial 42 | ; comment the following line to enable WiFi debugging 43 | -D NDEBUG 44 | 45 | [env:ESP32] 46 | platform = espressif32 47 | framework = arduino 48 | 49 | ; ============================================================ 50 | ; Board configuration 51 | ; choose your board by uncommenting one of the following lines 52 | ; ============================================================ 53 | ;board = esp32cam 54 | ;board = alksesp32 55 | ;board = featheresp32 56 | ;board = espea32 57 | ;board = bpi-bit 58 | ;board = d-duino-32 59 | board = esp32doit-devkit-v1 60 | ;board = pocket_32 61 | ;board = fm-devkit 62 | ;board = pico32 63 | ;board = esp32-evb 64 | ;board = esp32-gateway 65 | ;board = esp32-pro 66 | ;board = esp32-poe 67 | ;board = oroca_edubot 68 | ;board = onehorse32dev 69 | ;board = lopy 70 | ;board = lopy4 71 | ;board = wesp32 72 | ;board = esp32thing 73 | ;board = sparkfun_lora_gateway_1-channel 74 | ;board = ttgo-lora32-v1 75 | ;board = ttgo-t-beam 76 | ;board = turta_iot_node 77 | ;board = lolin_d32 78 | ;board = lolin_d32_pro 79 | ;board = lolin32 80 | ;board = wemosbat 81 | ;board = widora-air 82 | ;board = xinabox_cw02 83 | ;board = iotbusio 84 | ;board = iotbusproteus 85 | ;board = nina_w10 86 | 87 | [env:esp32s2] 88 | platform = espressif32 89 | framework = arduino 90 | 91 | ; toolchain download links see 92 | ; refer "name": "xtensa-esp32s2-elf-gcc","version": "gcc8_4_0-esp-2021r1" section of 93 | ; https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json 94 | ; e.g. Windows: https://github.com/espressif/crosstool-NG/releases/download/esp-2021r1/xtensa-esp32s2-elf-gcc8_4_0-esp-2021r1-win32.zip 95 | platform_packages = 96 | toolchain-xtensa32s2@file://C:\Users\Max\Downloads\xtensa-esp32s2-elf 97 | framework-arduinoespressif32@https://github.com/espressif/arduino-esp32.git#a4118ea88987c28aac3a49bcb9cc5d6c0acc6f3f 98 | platformio/tool-esptoolpy @ ~1.30100 99 | framework = arduino 100 | board = esp32dev 101 | board_build.mcu = esp32s2 102 | board_build.partitions = huge_app.csv 103 | board_build.variant = esp32s2 104 | board_build.f_cpu = 240000000L 105 | board_build.f_flash = 80000000L 106 | board_build.flash_mode = qio 107 | board_build.arduino.ldscript = esp32s2_out.ld 108 | build_unflags = 109 | -DARDUINO_ESP32_DEV 110 | -DARDUINO_VARIANT="esp32" 111 | build_flags = 112 | -DARDUINO_ESP32S2_DEV 113 | -DARDUINO_VARIANT="esp32s2" 114 | 115 | 116 | [env:esp32s3] 117 | platform = espressif32 118 | framework = arduino 119 | 120 | board_build.mcu = esp32s3 121 | board_build.partitions = huge_app.csv 122 | board_build.variant = esp32s3 123 | board_build.f_cpu = 240000000L 124 | board_build.f_flash = 80000000L 125 | board_build.flash_mode = qio 126 | board_build.arduino.ldscript = esp32s3_out.ld 127 | build_unflags = 128 | -DARDUINO_ESP32_DEV 129 | -DARDUINO_VARIANT="esp32" 130 | build_flags = 131 | -DARDUINO_ESP32S3_DEV 132 | -DARDUINO_VARIANT="esp32s3" 133 | 134 | 135 | [env:esp32sc3] 136 | platform = espressif32 137 | framework = arduino 138 | 139 | board_build.mcu = esp32c3 140 | board_build.partitions = huge_app.csv 141 | board_build.variant = esp32c3 142 | board_build.f_cpu = 160000000L 143 | board_build.f_flash = 80000000L 144 | board_build.flash_mode = qio 145 | board_build.arduino.ldscript = esp32c3_out.ld 146 | build_unflags = 147 | -DARDUINO_ESP32_DEV 148 | -DARDUINO_VARIANT="esp32" 149 | build_flags = 150 | -DARDUINO_ESP32S3_DEV 151 | -DARDUINO_VARIANT="esp32c3" 152 | -------------------------------------------------------------------------------- /src/ESP32FastTimerInterrupt.hpp: -------------------------------------------------------------------------------- 1 | 2 | /**************************************************************************************************************************** 3 | ESP32FastTimerInterrupt.hpp 4 | For ESP32 boards 5 | Written by Khoi Hoang 6 | 7 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 8 | Licensed under MIT license 9 | 10 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) 11 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 12 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 13 | This important feature is absolutely necessary for mission-critical tasks. 14 | 15 | Version: 1.4.0 16 | 17 | Version Modified By Date Comments 18 | ------- ----------- ---------- ----------- 19 | 1.0.0 K Hoang 12/12/2019 Initial coding 20 | 1.0.1 K Hoang 13/12/2019 Add more features getPosition and getPulseWidth. Optimize. 21 | 1.0.2 K Hoang 20/12/2019 Add more Blynk examples.Change example names to avoid duplication. 22 | 1.1.0 K Hoang 03/01/2021 Fix bug. Add TOC and Version String. 23 | 1.2.0 K Hoang 06/03/2022 Convert to `h-only` style. Optimize code by using passing by `reference` 24 | 1.2.1 K Hoang 07/03/2022 Fix bug 25 | 1.3.0 K Hoang 08/05/2022 Fix issue with ESP32 core v2.0.1+ 26 | 1.3.1 K Hoang 16/06/2022 Add support to new Adafruit boards 27 | 1.4.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 28 | *****************************************************************************************************************************/ 29 | 30 | #pragma once 31 | 32 | #ifndef ESP32FASTTIMERINTERRUPT_HPP 33 | #define ESP32FASTTIMERINTERRUPT_HPP 34 | 35 | #include 36 | 37 | /* 38 | //ESP32 core v1.0.6, hw_timer_t defined in esp32/tools/sdk/include/driver/driver/timer.h: 39 | 40 | #define TIMER_BASE_CLK (APB_CLK_FREQ) //Frequency of the clock on the input of the timer groups 41 | 42 | 43 | //@brief Selects a Timer-Group out of 2 available groups 44 | 45 | typedef enum 46 | { 47 | TIMER_GROUP_0 = 0, // Hw timer group 0 48 | TIMER_GROUP_1 = 1, // Hw timer group 1 49 | TIMER_GROUP_MAX, 50 | } timer_group_t; 51 | 52 | 53 | //@brief Select a hardware timer from timer groups 54 | 55 | typedef enum 56 | { 57 | TIMER_0 = 0, // Select timer0 of GROUPx 58 | TIMER_1 = 1, // Select timer1 of GROUPx 59 | TIMER_MAX, 60 | } timer_idx_t; 61 | 62 | 63 | //@brief Decides the direction of counter 64 | 65 | typedef enum 66 | { 67 | TIMER_COUNT_DOWN = 0, //Descending Count from cnt.high|cnt.low 68 | TIMER_COUNT_UP = 1, //Ascending Count from Zero 69 | TIMER_COUNT_MAX 70 | } timer_count_dir_t; 71 | 72 | 73 | //@brief Decides whether timer is on or paused 74 | 75 | typedef enum 76 | { 77 | TIMER_PAUSE = 0, //Pause timer counter 78 | TIMER_START = 1, //Start timer counter 79 | } timer_start_t; 80 | 81 | 82 | //@brief Decides whether to enable alarm mode 83 | 84 | typedef enum 85 | { 86 | TIMER_ALARM_DIS = 0, //Disable timer alarm 87 | TIMER_ALARM_EN = 1, //Enable timer alarm 88 | TIMER_ALARM_MAX 89 | } timer_alarm_t; 90 | 91 | 92 | //@brief Select interrupt type if running in alarm mode. 93 | 94 | typedef enum 95 | { 96 | TIMER_INTR_LEVEL = 0, //Interrupt mode: level mode 97 | //TIMER_INTR_EDGE = 1, //Interrupt mode: edge mode, Not supported Now 98 | TIMER_INTR_MAX 99 | } timer_intr_mode_t; 100 | 101 | 102 | //@brief Select if Alarm needs to be loaded by software or automatically reload by hardware. 103 | 104 | typedef enum 105 | { 106 | TIMER_AUTORELOAD_DIS = 0, //Disable auto-reload: hardware will not load counter value after an alarm event 107 | TIMER_AUTORELOAD_EN = 1, //Enable auto-reload: hardware will load counter value after an alarm event 108 | TIMER_AUTORELOAD_MAX, 109 | } timer_autoreload_t; 110 | 111 | 112 | //@brief Data structure with timer's configuration settings 113 | 114 | typedef struct 115 | { 116 | bool alarm_en; //Timer alarm enable 117 | bool counter_en; //Counter enable 118 | timer_intr_mode_t intr_type; //Interrupt mode 119 | timer_count_dir_t counter_dir; //Counter direction 120 | bool auto_reload; //Timer auto-reload 121 | uint32_t divider; //Counter clock divider. The divider's range is from from 2 to 65536. 122 | } timer_config_t; 123 | 124 | */ 125 | 126 | /* 127 | //ESP32 core v2.0.4, timer_config_t defined in tools/sdk/esp32/include/hal/include/hal/timer_types.h: 128 | #if SOC_TIMER_GROUP_SUPPORT_XTAL 129 | typedef enum { 130 | TIMER_SRC_CLK_APB = 0, // Select APB as the source clock 131 | TIMER_SRC_CLK_XTAL = 1, // Select XTAL as the source clock 132 | } timer_src_clk_t; 133 | #endif 134 | typedef struct { 135 | timer_alarm_t alarm_en; // Timer alarm enable 136 | timer_start_t counter_en; // Counter enable 137 | timer_intr_mode_t intr_type; // Interrupt mode 138 | timer_count_dir_t counter_dir; // Counter direction 139 | timer_autoreload_t auto_reload; // Timer auto-reload 140 | uint32_t divider; // Counter clock divider. The divider's range is from from 2 to 65536 141 | #if SOC_TIMER_GROUP_SUPPORT_XTAL 142 | timer_src_clk_t clk_src; // Use XTAL as source clock 143 | #endif 144 | } timer_config_t; 145 | 146 | */ 147 | 148 | class ESP32TimerInterrupt; 149 | 150 | //typedef ESP32TimerInterrupt ESP32Timer; 151 | 152 | #if USING_ESP32_C3_TIMERINTERRUPT 153 | #define MAX_ESP32_NUM_TIMERS 2 154 | #else 155 | #define MAX_ESP32_NUM_TIMERS 4 156 | #endif 157 | 158 | #define TIMER_DIVIDER 80 // Hardware timer clock divider 159 | // TIMER_BASE_CLK = APB_CLK_FREQ = Frequency of the clock on the input of the timer groups 160 | #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds 161 | 162 | 163 | // In esp32/1.0.6/tools/sdk/esp32s2/include/driver/include/driver/timer.h 164 | // typedef bool (*timer_isr_t)(void *); 165 | //esp_err_t timer_isr_callback_add(timer_group_t group_num, timer_idx_t timer_num, timer_isr_t isr_handler, void *arg, int intr_alloc_flags); 166 | //esp_err_t timer_isr_callback_remove(timer_group_t group_num, timer_idx_t timer_num); 167 | //timer_deinit(timer_group_t group_num, timer_idx_t timer_num); 168 | //esp_err_t timer_group_intr_enable(timer_group_t group_num, timer_intr_t intr_mask); 169 | //esp_err_t timer_group_intr_disable(timer_group_t group_num, timer_intr_t intr_mask); 170 | 171 | typedef bool (*timer_callback) (void *); 172 | 173 | // For ESP32_C3, TIMER_MAX == 1 174 | // For ESP32 and ESP32_S2, TIMER_MAX == 2 175 | 176 | typedef struct 177 | { 178 | timer_idx_t timer_idx; 179 | timer_group_t timer_group; 180 | //int alarm_interval; 181 | //timer_autoreload_t auto_reload; 182 | } timer_info_t; 183 | 184 | typedef ESP32TimerInterrupt ESP32FastTimer; 185 | 186 | class ESP32TimerInterrupt 187 | { 188 | private: 189 | 190 | timer_config_t stdConfig = 191 | { 192 | .alarm_en = TIMER_ALARM_EN, //enable timer alarm 193 | .counter_en = TIMER_START, //starts counting counter once timer_init called 194 | .intr_type = TIMER_INTR_MAX, 195 | .counter_dir = TIMER_COUNT_UP, //counts from 0 to counter value 196 | .auto_reload = TIMER_AUTORELOAD_EN, //reloads counter automatically 197 | .divider = TIMER_DIVIDER, 198 | #if SOC_TIMER_GROUP_SUPPORT_XTAL 199 | .clk_src = TIMER_SRC_CLK_XTAL //Use XTAL as source clock 200 | #endif 201 | }; 202 | 203 | timer_idx_t _timerIndex; 204 | timer_group_t _timerGroup; 205 | uint32_t interruptFlag; // either TIMER_INTR_T0 or TIMER_INTR_T1 206 | 207 | uint8_t _timerNo; 208 | 209 | timer_callback _callback; // pointer to the callback function 210 | float _frequency; // Timer frequency 211 | uint64_t _timerCount; // count to activate timer 212 | 213 | //xQueueHandle s_timer_queue; 214 | 215 | public: 216 | 217 | ESP32TimerInterrupt(uint8_t timerNo) 218 | { 219 | _callback = NULL; 220 | 221 | if (timerNo < MAX_ESP32_NUM_TIMERS) 222 | { 223 | _timerNo = timerNo; 224 | 225 | #if USING_ESP32_C3_TIMERINTERRUPT 226 | 227 | // Always using TIMER_INTR_T0 228 | _timerIndex = (timer_idx_t) ( (uint32_t) 0 ); 229 | 230 | // timerNo == 0 => Group 0, timerNo == 1 => Group 1 231 | _timerGroup = (timer_group_t) ( (uint32_t) timerNo); 232 | 233 | #else 234 | 235 | _timerIndex = (timer_idx_t) (_timerNo % TIMER_MAX); 236 | 237 | _timerGroup = (timer_group_t) (_timerNo / TIMER_MAX); 238 | 239 | #endif 240 | } 241 | else 242 | { 243 | _timerNo = MAX_ESP32_NUM_TIMERS; 244 | } 245 | }; 246 | 247 | // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 248 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c 249 | bool setFrequency(const float& frequency, timer_callback callback) 250 | { 251 | if (_timerNo < MAX_ESP32_NUM_TIMERS) 252 | { 253 | // select timer frequency is 1MHz for better accuracy. We don't use 16-bit prescaler for now. 254 | // Will use later if very low frequency is needed. 255 | _frequency = TIMER_BASE_CLK / TIMER_DIVIDER; //1000000; 256 | _timerCount = (uint64_t) _frequency / frequency; 257 | // count up 258 | 259 | #if USING_ESP32_S2_TIMERINTERRUPT 260 | ISR_SERVO_LOGERROR3(F("ESP32_S2_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 261 | ISR_SERVO_LOGERROR3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 262 | ISR_SERVO_LOGERROR3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 263 | ISR_SERVO_LOGERROR3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount)); 264 | ISR_SERVO_LOGERROR1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 265 | #elif USING_ESP32_S3_TIMERINTERRUPT 266 | // ESP32-S3 is embedded with four 54-bit general-purpose timers, which are based on 16-bit prescalers 267 | // and 54-bit auto-reload-capable up/down-timers 268 | ISR_SERVO_LOGERROR3(F("ESP32_S3_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 269 | ISR_SERVO_LOGERROR3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 270 | ISR_SERVO_LOGERROR3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 271 | ISR_SERVO_LOGERROR3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount)); 272 | ISR_SERVO_LOGERROR1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 273 | #else 274 | ISR_SERVO_LOGERROR3(F("ESP32_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 275 | ISR_SERVO_LOGERROR3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 276 | ISR_SERVO_LOGERROR3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 277 | ISR_SERVO_LOGERROR3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount)); 278 | ISR_SERVO_LOGERROR1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 279 | #endif 280 | 281 | timer_init(_timerGroup, _timerIndex, &stdConfig); 282 | 283 | // Counter value to 0 => counting up to alarm value as .counter_dir == TIMER_COUNT_UP 284 | timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL); 285 | 286 | timer_set_alarm_value(_timerGroup, _timerIndex, TIMER_SCALE / frequency); 287 | 288 | // enable interrupts for _timerGroup, _timerIndex 289 | timer_enable_intr(_timerGroup, _timerIndex); 290 | 291 | _callback = callback; 292 | 293 | // Register the ISR handler 294 | // If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set, the handler function must be declared with IRAM_ATTR attribute 295 | // and can only call functions in IRAM or ROM. It cannot call other timer APIs. 296 | //timer_isr_register(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, ESP_INTR_FLAG_IRAM, NULL); 297 | timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0); 298 | 299 | timer_start(_timerGroup, _timerIndex); 300 | 301 | return true; 302 | } 303 | else 304 | { 305 | #if USING_ESP32_C3_TIMERINTERRUPT 306 | ISR_SERVO_LOGERROR(F("Error. Timer must be 0-1")); 307 | #else 308 | ISR_SERVO_LOGERROR(F("Error. Timer must be 0-3")); 309 | #endif 310 | 311 | return false; 312 | } 313 | } 314 | 315 | // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 316 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c 317 | bool attachInterruptInterval(const unsigned long& interval, timer_callback callback) 318 | { 319 | return setFrequency( (float) ( 1000000.0f / interval), callback); 320 | } 321 | 322 | void detachInterrupt() 323 | { 324 | #if USING_ESP32_C3_TIMERINTERRUPT 325 | timer_group_intr_disable(_timerGroup, TIMER_INTR_T0); 326 | #else 327 | timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 328 | #endif 329 | } 330 | 331 | void disableTimer() 332 | { 333 | #if USING_ESP32_C3_TIMERINTERRUPT 334 | timer_group_intr_disable(_timerGroup, TIMER_INTR_T0); 335 | #else 336 | timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 337 | #endif 338 | } 339 | 340 | // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely 341 | void reattachInterrupt() 342 | { 343 | #if USING_ESP32_C3_TIMERINTERRUPT 344 | timer_group_intr_enable(_timerGroup, TIMER_INTR_T0); 345 | #else 346 | timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 347 | #endif 348 | } 349 | 350 | }; // class ESP32TimerInterrupt 351 | 352 | 353 | #endif // ESP32FASTTIMERINTERRUPT_HPP 354 | 355 | -------------------------------------------------------------------------------- /src/ESP32_ISR_Servo.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_ISR_Servo.h 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) 10 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 11 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 12 | This important feature is absolutely necessary for mission-critical tasks. 13 | 14 | Version: 1.4.0 15 | 16 | Version Modified By Date Comments 17 | ------- ----------- ---------- ----------- 18 | 1.0.0 K Hoang 12/12/2019 Initial coding 19 | 1.0.1 K Hoang 13/12/2019 Add more features getPosition and getPulseWidth. Optimize. 20 | 1.0.2 K Hoang 20/12/2019 Add more Blynk examples.Change example names to avoid duplication. 21 | 1.1.0 K Hoang 03/01/2021 Fix bug. Add TOC and Version String. 22 | 1.2.0 K Hoang 06/03/2022 Convert to `h-only` style. Optimize code by using passing by `reference` 23 | 1.2.1 K Hoang 07/03/2022 Fix bug 24 | 1.3.0 K Hoang 08/05/2022 Fix issue with ESP32 core v2.0.1+ 25 | 1.3.1 K Hoang 16/06/2022 Add support to new Adafruit boards 26 | 1.4.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 27 | *****************************************************************************************************************************/ 28 | 29 | #pragma once 30 | 31 | #ifndef ESP32_ISR_SERVO_H 32 | #define ESP32_ISR_SERVO_H 33 | 34 | #include "ESP32_ISR_Servo.hpp" 35 | #include "ESP32_ISR_Servo_Impl.h" 36 | 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/ESP32_ISR_Servo.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_ISR_Servo.h 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) 10 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 11 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 12 | This important feature is absolutely necessary for mission-critical tasks. 13 | 14 | Version: 1.4.0 15 | 16 | Version Modified By Date Comments 17 | ------- ----------- ---------- ----------- 18 | 1.0.0 K Hoang 12/12/2019 Initial coding 19 | 1.0.1 K Hoang 13/12/2019 Add more features getPosition and getPulseWidth. Optimize. 20 | 1.0.2 K Hoang 20/12/2019 Add more Blynk examples.Change example names to avoid duplication. 21 | 1.1.0 K Hoang 03/01/2021 Fix bug. Add TOC and Version String. 22 | 1.2.0 K Hoang 06/03/2022 Convert to `h-only` style. Optimize code by using passing by `reference` 23 | 1.2.1 K Hoang 07/03/2022 Fix bug 24 | 1.3.0 K Hoang 08/05/2022 Fix issue with ESP32 core v2.0.1+ 25 | 1.3.1 K Hoang 16/06/2022 Add support to new Adafruit boards 26 | 1.4.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 27 | *****************************************************************************************************************************/ 28 | 29 | #pragma once 30 | 31 | #ifndef ESP32_ISR_SERVO_HPP 32 | #define ESP32_ISR_SERVO_HPP 33 | 34 | #if ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \ 35 | ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \ 36 | ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM || ARDUINO_ADAFRUIT_QTPY_ESP32S2) 37 | #define USING_ESP32_S2_TIMERINTERRUPT true 38 | #elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \ 39 | defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_NOPSRAM) || \ 40 | defined(ARDUINO_ADAFRUIT_QTPY_ESP32S3_NOPSRAM)) 41 | #define USING_ESP32_S3_TIMERINTERRUPT true 42 | #elif ( ARDUINO_ESP32C3_DEV ) 43 | #define USING_ESP32_C3_TIMERINTERRUPT true 44 | #elif defined(ESP32) 45 | #define USING_ESP32_TIMERINTERRUPT true 46 | #else 47 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 48 | #endif 49 | 50 | #if !defined(ESP32_ISR_SERVO_VERSION) 51 | #define ESP32_ISR_SERVO_VERSION "ESP32_ISR_Servo v1.4.0" 52 | 53 | #define ESP32_ISR_SERVO_VERSION_MAJOR 1 54 | #define ESP32_ISR_SERVO_VERSION_MINOR 4 55 | #define ESP32_ISR_SERVO_VERSION_PATCH 0 56 | 57 | #define ESP32_ISR_SERVO_VERSION_INT 1004000 58 | 59 | #endif 60 | 61 | #include 62 | 63 | #include 64 | 65 | #if defined(ARDUINO) 66 | #if ARDUINO >= 100 67 | #include 68 | #else 69 | #include 70 | #endif 71 | #endif 72 | 73 | #ifndef ISR_SERVO_DEBUG 74 | #define ISR_SERVO_DEBUG 0 75 | #endif 76 | 77 | #include "ESP32_ISR_Servo_Debug.h" 78 | 79 | #include "ESP32FastTimerInterrupt.hpp" 80 | 81 | #define ESP32_MAX_PIN 39 82 | #define ESP32_WRONG_PIN 255 83 | 84 | // From Servo.h - Copyright (c) 2009 Michael Margolis. All right reserved. 85 | 86 | #define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo 87 | #define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo 88 | #define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached 89 | #define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds 90 | 91 | extern void IRAM_ATTR ESP32_ISR_Servo_Handler(); 92 | 93 | class ESP32_ISR_Servo 94 | { 95 | 96 | public: 97 | // maximum number of servos 98 | const static int MAX_SERVOS = 16; 99 | 100 | // constructor 101 | ESP32_ISR_Servo(); 102 | 103 | // destructor 104 | ~ESP32_ISR_Servo() 105 | { 106 | if (ESP32_ITimer) 107 | { 108 | ESP32_ITimer->detachInterrupt(); 109 | delete ESP32_ITimer; 110 | } 111 | } 112 | 113 | void IRAM_ATTR run(); 114 | 115 | // useTimer select which timer (0-3) of ESP32 to use for Servos 116 | //Return true if timerN0 in range 117 | bool useTimer(const uint8_t& timerNo) 118 | { 119 | if (timerNo < MAX_ESP32_NUM_TIMERS) 120 | { 121 | _timerNo = timerNo; 122 | return true; 123 | } 124 | return false; 125 | } 126 | 127 | // Bind servo to the timer and pin, return servoIndex 128 | int8_t setupServo(const uint8_t& pin, const uint16_t& min = MIN_PULSE_WIDTH, const uint16_t& max = MAX_PULSE_WIDTH); 129 | 130 | // setPosition will set servo to position in degrees 131 | // by using PWM, turn HIGH 'duration' microseconds within REFRESH_INTERVAL (20000us) 132 | // returns true on success or -1 on wrong servoIndex 133 | bool setPosition(const uint8_t& servoIndex, const uint16_t& position); 134 | 135 | // returns last position in degrees if success, or -1 on wrong servoIndex 136 | int getPosition(const uint8_t& servoIndex); 137 | 138 | // setPulseWidth will set servo PWM Pulse Width in microseconds, correcponding to certain position in degrees 139 | // by using PWM, turn HIGH 'pulseWidth' microseconds within REFRESH_INTERVAL (20000us) 140 | // min and max for each individual servo are enforced 141 | // returns true on success or -1 on wrong servoIndex 142 | bool setPulseWidth(const uint8_t& servoIndex, uint16_t& pulseWidth); 143 | 144 | // returns pulseWidth in microsecs (within min/max range) if success, or 0 on wrong servoIndex 145 | unsigned int getPulseWidth(const uint8_t& servoIndex); 146 | 147 | // destroy the specified servo 148 | void deleteServo(const uint8_t& servoIndex); 149 | 150 | // returns true if the specified servo is enabled 151 | bool isEnabled(const uint8_t& servoIndex); 152 | 153 | // enables the specified servo 154 | bool enable(const uint8_t& servoIndex); 155 | 156 | // disables the specified servo 157 | bool disable(const uint8_t& servoIndex); 158 | 159 | // enables all servos 160 | void enableAll(); 161 | 162 | // disables all servos 163 | void disableAll(); 164 | 165 | // enables the specified servo if it's currently disabled, 166 | // and vice-versa 167 | bool toggle(const uint8_t& servoIndex); 168 | 169 | // returns the number of used servos 170 | int8_t getNumServos(); 171 | 172 | // returns the number of available servos 173 | int8_t getNumAvailableServos() 174 | { 175 | if (numServos <= 0) 176 | return MAX_SERVOS; 177 | else 178 | return MAX_SERVOS - numServos; 179 | }; 180 | 181 | private: 182 | 183 | // Use 10 microsecs timer => not working from core v2.0.1+ 184 | // Use 12 microsecs timer now, just fine enough to control Servo, normally requiring pulse width (PWM) 500-2000us in 20ms. 185 | #define TIMER_INTERVAL_MICRO 12 186 | 187 | void init() 188 | { 189 | 190 | ESP32_ITimer = new ESP32FastTimer(_timerNo); 191 | 192 | // Interval in microsecs 193 | if ( ESP32_ITimer && ESP32_ITimer->attachInterruptInterval(TIMER_INTERVAL_MICRO, (timer_callback) ESP32_ISR_Servo_Handler ) ) 194 | { 195 | ISR_SERVO_LOGERROR("Starting ITimer OK"); 196 | } 197 | else 198 | { 199 | ISR_SERVO_LOGERROR("Fail setup ESP32_ITimer"); } 200 | 201 | for (int servoIndex = 0; servoIndex < MAX_SERVOS; servoIndex++) 202 | { 203 | memset((void*) &servo[servoIndex], 0, sizeof (servo_t)); 204 | servo[servoIndex].count = 0; 205 | servo[servoIndex].enabled = false; 206 | // Intentional bad pin 207 | servo[servoIndex].pin = ESP32_WRONG_PIN; 208 | } 209 | 210 | numServos = 0; 211 | 212 | // Init timerCount 213 | timerCount = 1; 214 | 215 | timerMux = portMUX_INITIALIZER_UNLOCKED; 216 | } 217 | 218 | // find the first available slot 219 | int8_t findFirstFreeSlot(); 220 | 221 | typedef struct 222 | { 223 | uint8_t pin; // pin servo connected to 224 | unsigned long count; // In microsecs 225 | uint16_t position; // In degrees 226 | bool enabled; // true if enabled 227 | uint16_t min; 228 | uint16_t max; 229 | } servo_t; 230 | 231 | volatile servo_t servo[MAX_SERVOS]; 232 | 233 | // actual number of servos in use (-1 means uninitialized) 234 | volatile int8_t numServos; 235 | 236 | // timerCount starts at 1, and counting up to (REFRESH_INTERVAL / TIMER_INTERVAL_MICRO) = (20000 / 10) = 2000 237 | // then reset to 1. Use this to calculate when to turn ON / OFF pulse to servo 238 | // For example, servo1 uses pulse width 1000us => turned ON when timerCount = 1, turned OFF when timerCount = 1000 / TIMER_INTERVAL_MICRO = 100 239 | volatile unsigned long timerCount; 240 | 241 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 242 | portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; 243 | 244 | // For ESP32 timer 245 | uint8_t _timerNo; 246 | ESP32FastTimer* ESP32_ITimer; 247 | }; 248 | 249 | 250 | #endif // ESP32_ISR_SERVO_HPP 251 | -------------------------------------------------------------------------------- /src/ESP32_ISR_Servo_Debug.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_ISR_Servo_Debug.h 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) 10 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 11 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 12 | This important feature is absolutely necessary for mission-critical tasks. 13 | 14 | Version: 1.4.0 15 | 16 | Version Modified By Date Comments 17 | ------- ----------- ---------- ----------- 18 | 1.0.0 K Hoang 12/12/2019 Initial coding 19 | 1.0.1 K Hoang 13/12/2019 Add more features getPosition and getPulseWidth. Optimize. 20 | 1.0.2 K Hoang 20/12/2019 Add more Blynk examples.Change example names to avoid duplication. 21 | 1.1.0 K Hoang 03/01/2021 Fix bug. Add TOC and Version String. 22 | 1.2.0 K Hoang 06/03/2022 Convert to `h-only` style. Optimize code by using passing by `reference` 23 | 1.2.1 K Hoang 07/03/2022 Fix bug 24 | 1.3.0 K Hoang 08/05/2022 Fix issue with ESP32 core v2.0.1+ 25 | 1.3.1 K Hoang 16/06/2022 Add support to new Adafruit boards 26 | 1.4.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 27 | *****************************************************************************************************************************/ 28 | 29 | #pragma once 30 | 31 | #ifndef ESP32_ISR_Servo_Debug_h 32 | #define ESP32_ISR_Servo_Debug_h 33 | 34 | ////////////////////////////////////////// 35 | 36 | #ifndef ISR_SERVO_DEBUG 37 | #define ISR_SERVO_DEBUG 1 38 | #endif 39 | 40 | ////////////////////////////////////////// 41 | 42 | #if !defined(ISR_SERVO_DEBUG_OUTPUT) 43 | #define ISR_SERVO_DEBUG_OUTPUT Serial 44 | #endif 45 | 46 | ////////////////////////////////////////////////////// 47 | 48 | const char ISR_SERVO_MARK[] = "[ISR_SERVO] "; 49 | const char ISR_SERVO_SP[] = " "; 50 | 51 | #define ISR_SERVO_PRINT ISR_SERVO_DEBUG_OUTPUT.print 52 | #define ISR_SERVO_PRINTLN ISR_SERVO_DEBUG_OUTPUT.println 53 | #define ISR_SERVO_FLUSH ISR_SERVO_DEBUG_OUTPUT.flush 54 | 55 | #define ISR_SERVO_PRINT_MARK ISR_SERVO_PRINT(ISR_SERVO_MARK) 56 | #define ISR_SERVO_PRINT_SP ISR_SERVO_PRINT(ISR_SERVO_SP) 57 | 58 | ////////////////////////////////////////////////////// 59 | 60 | #define ISR_SERVO_LOGERROR(x) if(ISR_SERVO_DEBUG>0) { ISR_SERVO_PRINT_MARK; ISR_SERVO_PRINTLN(x); } 61 | #define ISR_SERVO_LOGERROR0(x) if(ISR_SERVO_DEBUG>0) { ISR_SERVO_PRINT(x); } 62 | #define ISR_SERVO_LOGERROR1(x,y) if(ISR_SERVO_DEBUG>0) { ISR_SERVO_PRINT_MARK; ISR_SERVO_PRINT(x); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINTLN(y); } 63 | #define ISR_SERVO_LOGERROR2(x,y,z) if(ISR_SERVO_DEBUG>0) { ISR_SERVO_PRINT_MARK; ISR_SERVO_PRINT(x); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINT(y); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINTLN(z); } 64 | #define ISR_SERVO_LOGERROR3(x,y,z,w) if(ISR_SERVO_DEBUG>0) { ISR_SERVO_PRINT_MARK; ISR_SERVO_PRINT(x); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINT(y); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINT(z); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINTLN(w); } 65 | 66 | ////////////////////////////////////////////////////// 67 | 68 | #define ISR_SERVO_LOGDEBUG(x) if(ISR_SERVO_DEBUG>1) { ISR_SERVO_PRINT_MARK; ISR_SERVO_PRINTLN(x); } 69 | #define ISR_SERVO_LOGDEBUG0(x) if(ISR_SERVO_DEBUG>1) { ISR_SERVO_PRINT(x); } 70 | #define ISR_SERVO_LOGDEBUG1(x,y) if(ISR_SERVO_DEBUG>1) { ISR_SERVO_PRINT_MARK; ISR_SERVO_PRINT(x); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINTLN(y); } 71 | #define ISR_SERVO_LOGDEBUG2(x,y,z) if(ISR_SERVO_DEBUG>1) { ISR_SERVO_PRINT_MARK; ISR_SERVO_PRINT(x); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINT(y); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINTLN(z); } 72 | #define ISR_SERVO_LOGDEBUG3(x,y,z,w) if(ISR_SERVO_DEBUG>1) { ISR_SERVO_PRINT_MARK; ISR_SERVO_PRINT(x); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINT(y); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINT(z); ISR_SERVO_PRINT_SP; ISR_SERVO_PRINTLN(w); } 73 | 74 | ////////////////////////////////////////// 75 | 76 | 77 | #endif // ESP8266_ISR_Servo_Debug_h 78 | -------------------------------------------------------------------------------- /src/ESP32_ISR_Servo_Impl.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_ISR_Servo.cpp 3 | For ESP32 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32_ISR_Servo 7 | Licensed under MIT license 8 | 9 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) 10 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 11 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 12 | This important feature is absolutely necessary for mission-critical tasks. 13 | 14 | Version: 1.4.0 15 | 16 | Version Modified By Date Comments 17 | ------- ----------- ---------- ----------- 18 | 1.0.0 K Hoang 12/12/2019 Initial coding 19 | 1.0.1 K Hoang 13/12/2019 Add more features getPosition and getPulseWidth. Optimize. 20 | 1.0.2 K Hoang 20/12/2019 Add more Blynk examples.Change example names to avoid duplication. 21 | 1.1.0 K Hoang 03/01/2021 Fix bug. Add TOC and Version String. 22 | 1.2.0 K Hoang 06/03/2022 Convert to `h-only` style. Optimize code by using passing by `reference` 23 | 1.2.1 K Hoang 07/03/2022 Fix bug 24 | 1.3.0 K Hoang 08/05/2022 Fix issue with ESP32 core v2.0.1+ 25 | 1.3.1 K Hoang 16/06/2022 Add support to new Adafruit boards 26 | 1.4.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 27 | *****************************************************************************************************************************/ 28 | 29 | #include "ESP32_ISR_Servo.h" 30 | #include 31 | 32 | #ifndef ISR_SERVO_DEBUG 33 | #define ISR_SERVO_DEBUG 1 34 | #endif 35 | 36 | #define DEFAULT_ESP32_TIMER_NO 3 37 | 38 | static ESP32_ISR_Servo ESP32_ISR_Servos; // create servo object to control up to 16 servos 39 | 40 | void IRAM_ATTR ESP32_ISR_Servo_Handler() 41 | { 42 | ESP32_ISR_Servos.run(); 43 | } 44 | 45 | ESP32_ISR_Servo::ESP32_ISR_Servo() 46 | : numServos (-1), _timerNo(DEFAULT_ESP32_TIMER_NO) 47 | { 48 | } 49 | 50 | void IRAM_ATTR ESP32_ISR_Servo::run() 51 | { 52 | static int servoIndex; 53 | 54 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 55 | portENTER_CRITICAL_ISR(&timerMux); 56 | 57 | for (servoIndex = 0; servoIndex < MAX_SERVOS; servoIndex++) 58 | { 59 | if ( servo[servoIndex].enabled && (servo[servoIndex].pin <= ESP32_MAX_PIN) ) 60 | { 61 | if ( timerCount == servo[servoIndex].count ) 62 | { 63 | // PWM to LOW, will be HIGH again when timerCount = 1 64 | digitalWrite(servo[servoIndex].pin, LOW); 65 | } 66 | else if (timerCount == 1) 67 | { 68 | // PWM to HIGH, will be LOW again when timerCount = servo[servoIndex].count 69 | digitalWrite(servo[servoIndex].pin, HIGH); 70 | } 71 | } 72 | } 73 | 74 | // Reset when reaching 20000us / 10us = 2000 75 | if (timerCount++ >= REFRESH_INTERVAL / TIMER_INTERVAL_MICRO) 76 | { 77 | ISR_SERVO_LOGDEBUG("Reset count"); 78 | 79 | timerCount = 1; 80 | } 81 | 82 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 83 | portEXIT_CRITICAL_ISR(&timerMux); 84 | } 85 | 86 | // find the first available slot 87 | // return -1 if none found 88 | int8_t ESP32_ISR_Servo::findFirstFreeSlot() 89 | { 90 | // all slots are used 91 | if (numServos >= MAX_SERVOS) 92 | return -1; 93 | 94 | // return the first slot with no count (i.e. free) 95 | for (int servoIndex = 0; servoIndex < MAX_SERVOS; servoIndex++) 96 | { 97 | if (servo[servoIndex].enabled == false) 98 | { 99 | ISR_SERVO_LOGDEBUG1("Index =", servoIndex); 100 | 101 | return servoIndex; 102 | } 103 | } 104 | 105 | // no free slots found 106 | return -1; 107 | } 108 | 109 | int8_t ESP32_ISR_Servo::setupServo(const uint8_t& pin, const uint16_t& min, const uint16_t& max) 110 | { 111 | int servoIndex; 112 | 113 | if (pin > ESP32_MAX_PIN) 114 | return -1; 115 | 116 | if (numServos < 0) 117 | init(); 118 | 119 | servoIndex = findFirstFreeSlot(); 120 | 121 | if (servoIndex < 0) 122 | return -1; 123 | 124 | servo[servoIndex].pin = pin; 125 | servo[servoIndex].min = min; 126 | servo[servoIndex].max = max; 127 | servo[servoIndex].count = min / TIMER_INTERVAL_MICRO; 128 | servo[servoIndex].position = 0; 129 | servo[servoIndex].enabled = true; 130 | 131 | pinMode(pin, OUTPUT); 132 | 133 | numServos++; 134 | 135 | ISR_SERVO_LOGDEBUG3("Index =", servoIndex, ", count =", servo[servoIndex].count); 136 | ISR_SERVO_LOGDEBUG3("min =", servo[servoIndex].min, ", max =", servo[servoIndex].max); 137 | 138 | return servoIndex; 139 | } 140 | 141 | bool ESP32_ISR_Servo::setPosition(const uint8_t& servoIndex, const uint16_t& position) 142 | { 143 | if (servoIndex >= MAX_SERVOS) 144 | return false; 145 | 146 | // Updates interval of existing specified servo 147 | if ( servo[servoIndex].enabled && (servo[servoIndex].pin <= ESP32_MAX_PIN) ) 148 | { 149 | // ESP32 is a multi core / multi processing chip. 150 | // It is mandatory to disable task switches during modifying shared vars 151 | portENTER_CRITICAL(&timerMux); 152 | 153 | servo[servoIndex].position = position; 154 | servo[servoIndex].count = map(position, 0, 180, servo[servoIndex].min, servo[servoIndex].max) / TIMER_INTERVAL_MICRO; 155 | 156 | // ESP32 is a multi core / multi processing chip. 157 | // It is mandatory to disable task switches during modifying shared vars 158 | portEXIT_CRITICAL(&timerMux); 159 | 160 | ISR_SERVO_LOGERROR1("Idx =", servoIndex); 161 | ISR_SERVO_LOGERROR3("cnt =", servo[servoIndex].count, ", pos =", servo[servoIndex].position); 162 | 163 | return true; 164 | } 165 | 166 | // false return for non-used numServo or bad pin 167 | return false; 168 | } 169 | 170 | // returns last position in degrees if success, or -1 on wrong servoIndex 171 | int ESP32_ISR_Servo::getPosition(const uint8_t& servoIndex) 172 | { 173 | if (servoIndex >= MAX_SERVOS) 174 | return -1; 175 | 176 | // Updates interval of existing specified servo 177 | if ( servo[servoIndex].enabled && (servo[servoIndex].pin <= ESP32_MAX_PIN) ) 178 | { 179 | ISR_SERVO_LOGERROR1("Idx =", servoIndex); 180 | ISR_SERVO_LOGERROR3("cnt =", servo[servoIndex].count, ", pos =", servo[servoIndex].position); 181 | 182 | return (servo[servoIndex].position); 183 | } 184 | 185 | // return 0 for non-used numServo or bad pin 186 | return -1; 187 | } 188 | 189 | 190 | // setPulseWidth will set servo PWM Pulse Width in microseconds, correcponding to certain position in degrees 191 | // by using PWM, turn HIGH 'pulseWidth' microseconds within REFRESH_INTERVAL (20000us) 192 | // min and max for each individual servo are enforced 193 | // returns true on success or -1 on wrong servoIndex 194 | bool ESP32_ISR_Servo::setPulseWidth(const uint8_t& servoIndex, uint16_t& pulseWidth) 195 | { 196 | if (servoIndex >= MAX_SERVOS) 197 | return false; 198 | 199 | // Updates interval of existing specified servo 200 | if ( servo[servoIndex].enabled && (servo[servoIndex].pin <= ESP32_MAX_PIN) ) 201 | { 202 | if (pulseWidth < servo[servoIndex].min) 203 | pulseWidth = servo[servoIndex].min; 204 | else if (pulseWidth > servo[servoIndex].max) 205 | pulseWidth = servo[servoIndex].max; 206 | 207 | // ESP32 is a multi core / multi processing chip. 208 | // It is mandatory to disable task switches during modifying shared vars 209 | portENTER_CRITICAL(&timerMux); 210 | 211 | servo[servoIndex].count = pulseWidth / TIMER_INTERVAL_MICRO; 212 | servo[servoIndex].position = map(pulseWidth, servo[servoIndex].min, servo[servoIndex].max, 0, 180); 213 | 214 | // ESP32 is a multi core / multi processing chip. 215 | // It is mandatory to disable task switches during modifying shared vars 216 | portEXIT_CRITICAL(&timerMux); 217 | 218 | ISR_SERVO_LOGERROR1("Idx =", servoIndex); 219 | ISR_SERVO_LOGERROR3("cnt =", servo[servoIndex].count, ", pos =", servo[servoIndex].position); 220 | 221 | return true; 222 | } 223 | 224 | // false return for non-used numServo or bad pin 225 | return false; 226 | } 227 | 228 | // returns pulseWidth in microsecs (within min/max range) if success, or 0 on wrong servoIndex 229 | unsigned int ESP32_ISR_Servo::getPulseWidth(const uint8_t& servoIndex) 230 | { 231 | if (servoIndex >= MAX_SERVOS) 232 | return 0; 233 | 234 | // Updates interval of existing specified servo 235 | if ( servo[servoIndex].enabled && (servo[servoIndex].pin <= ESP32_MAX_PIN) ) 236 | { 237 | ISR_SERVO_LOGERROR1("Idx =", servoIndex); 238 | ISR_SERVO_LOGERROR3("cnt =", servo[servoIndex].count, ", pos =", servo[servoIndex].position); 239 | 240 | return (servo[servoIndex].count * TIMER_INTERVAL_MICRO ); 241 | } 242 | 243 | // return 0 for non-used numServo or bad pin 244 | return 0; 245 | } 246 | 247 | 248 | void ESP32_ISR_Servo::deleteServo(const uint8_t& servoIndex) 249 | { 250 | if ( (numServos == 0) || (servoIndex >= MAX_SERVOS) ) 251 | { 252 | return; 253 | } 254 | 255 | // ESP32 is a multi core / multi processing chip. 256 | // It is mandatory to disable task switches during modifying shared vars 257 | portENTER_CRITICAL(&timerMux); 258 | 259 | // don't decrease the number of servos if the specified slot is already empty 260 | if (servo[servoIndex].enabled) 261 | { 262 | memset((void*) &servo[servoIndex], 0, sizeof (servo_t)); 263 | 264 | servo[servoIndex].enabled = false; 265 | servo[servoIndex].position = 0; 266 | servo[servoIndex].count = 0; 267 | // Intentional bad pin, good only from 0-16 for Digital, A0=17 268 | servo[servoIndex].pin = ESP32_WRONG_PIN; 269 | 270 | // update number of servos 271 | numServos--; 272 | } 273 | 274 | // ESP32 is a multi core / multi processing chip. 275 | // It is mandatory to disable task switches during modifying shared vars 276 | portEXIT_CRITICAL(&timerMux); 277 | } 278 | 279 | bool ESP32_ISR_Servo::isEnabled(const uint8_t& servoIndex) 280 | { 281 | if (servoIndex >= MAX_SERVOS) 282 | return false; 283 | 284 | if (servo[servoIndex].pin > ESP32_MAX_PIN) 285 | { 286 | // Disable if something wrong 287 | servo[servoIndex].pin = ESP32_WRONG_PIN; 288 | servo[servoIndex].enabled = false; 289 | return false; 290 | } 291 | 292 | return servo[servoIndex].enabled; 293 | } 294 | 295 | bool ESP32_ISR_Servo::enable(const uint8_t& servoIndex) 296 | { 297 | if (servoIndex >= MAX_SERVOS) 298 | return false; 299 | 300 | // ESP32 is a multi core / multi processing chip. 301 | // It is mandatory to disable task switches during modifying shared vars 302 | portENTER_CRITICAL(&timerMux); 303 | 304 | if (servo[servoIndex].pin > ESP32_MAX_PIN) 305 | { 306 | // Disable if something wrong 307 | servo[servoIndex].pin = ESP32_WRONG_PIN; 308 | servo[servoIndex].enabled = false; 309 | return false; 310 | } 311 | 312 | // Bug fix. See "Fixed count >= min comparison for servo enable." 313 | // (https://github.com/khoih-prog/ESP32_ISR_Servo/pull/1) 314 | if ( servo[servoIndex].count >= servo[servoIndex].min / TIMER_INTERVAL_MICRO ) 315 | servo[servoIndex].enabled = true; 316 | 317 | // ESP32 is a multi core / multi processing chip. 318 | // It is mandatory to disable task switches during modifying shared vars 319 | portEXIT_CRITICAL(&timerMux); 320 | 321 | return true; 322 | } 323 | 324 | bool ESP32_ISR_Servo::disable(const uint8_t& servoIndex) 325 | { 326 | if (servoIndex >= MAX_SERVOS) 327 | return false; 328 | 329 | if (servo[servoIndex].pin > ESP32_MAX_PIN) 330 | servo[servoIndex].pin = ESP32_WRONG_PIN; 331 | 332 | servo[servoIndex].enabled = false; 333 | 334 | return true; 335 | } 336 | 337 | void ESP32_ISR_Servo::enableAll() 338 | { 339 | // Enable all servos with a enabled and count != 0 (has PWM) and good pin 340 | 341 | // ESP32 is a multi core / multi processing chip. 342 | // It is mandatory to disable task switches during modifying shared vars 343 | portENTER_CRITICAL(&timerMux); 344 | 345 | for (int servoIndex = 0; servoIndex < MAX_SERVOS; servoIndex++) 346 | { 347 | // Bug fix. See "Fixed count >= min comparison for servo enable." 348 | // (https://github.com/khoih-prog/ESP32_ISR_Servo/pull/1) 349 | if ( (servo[servoIndex].count >= servo[servoIndex].min / TIMER_INTERVAL_MICRO ) && !servo[servoIndex].enabled 350 | && (servo[servoIndex].pin <= ESP32_MAX_PIN) ) 351 | { 352 | servo[servoIndex].enabled = true; 353 | } 354 | } 355 | 356 | // ESP32 is a multi core / multi processing chip. 357 | // It is mandatory to disable task switches during modifying shared vars 358 | portEXIT_CRITICAL(&timerMux); 359 | } 360 | 361 | void ESP32_ISR_Servo::disableAll() 362 | { 363 | // ESP32 is a multi core / multi processing chip. 364 | // It is mandatory to disable task switches during modifying shared vars 365 | portENTER_CRITICAL(&timerMux); 366 | 367 | // Disable all servos 368 | for (int servoIndex = 0; servoIndex < MAX_SERVOS; servoIndex++) 369 | { 370 | servo[servoIndex].enabled = false; 371 | } 372 | 373 | // ESP32 is a multi core / multi processing chip. 374 | // It is mandatory to disable task switches during modifying shared vars 375 | portEXIT_CRITICAL(&timerMux); 376 | } 377 | 378 | bool ESP32_ISR_Servo::toggle(const uint8_t& servoIndex) 379 | { 380 | if (servoIndex >= MAX_SERVOS) 381 | return false; 382 | 383 | // ESP32 is a multi core / multi processing chip. 384 | // It is mandatory to disable task switches during modifying shared vars 385 | portENTER_CRITICAL(&timerMux); 386 | 387 | servo[servoIndex].enabled = !servo[servoIndex].enabled; 388 | 389 | // ESP32 is a multi core / multi processing chip. 390 | // It is mandatory to disable task switches during modifying shared vars 391 | portEXIT_CRITICAL(&timerMux); 392 | 393 | return true; 394 | } 395 | 396 | int8_t ESP32_ISR_Servo::getNumServos() 397 | { 398 | return numServos; 399 | } 400 | -------------------------------------------------------------------------------- /utils/astyle_library.conf: -------------------------------------------------------------------------------- 1 | # Code formatting rules for Arduino libraries, modified from for KH libraries: 2 | # 3 | # https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf 4 | # 5 | 6 | # astyle --style=allman -s2 -t2 -C -S -xW -Y -M120 -f -p -xg -H -xb -c --xC120 -xL *.h *.cpp *.ino 7 | 8 | --mode=c 9 | --lineend=linux 10 | --style=allman 11 | 12 | # -r or -R 13 | #--recursive 14 | 15 | # -c => Converts tabs into spaces 16 | convert-tabs 17 | 18 | # -s2 => 2 spaces indentation 19 | --indent=spaces=2 20 | 21 | # -t2 => tab =2 spaces 22 | #--indent=tab=2 23 | 24 | # -C 25 | --indent-classes 26 | 27 | # -S 28 | --indent-switches 29 | 30 | # -xW 31 | --indent-preproc-block 32 | 33 | # -Y => indent classes, switches (and cases), comments starting at column 1 34 | --indent-col1-comments 35 | 36 | # -M120 => maximum of 120 spaces to indent a continuation line 37 | --max-continuation-indent=120 38 | 39 | # -xC120 => max‑code‑length will break a line if the code exceeds # characters 40 | --max-code-length=120 41 | 42 | # -f => 43 | --break-blocks 44 | 45 | # -p => put a space around operators 46 | --pad-oper 47 | 48 | # -xg => Insert space padding after commas 49 | --pad-comma 50 | 51 | # -H => put a space after if/for/while 52 | pad-header 53 | 54 | # -xb => Break one line headers (e.g. if/for/while) 55 | --break-one-line-headers 56 | 57 | # -c => Converts tabs into spaces 58 | #--convert-tabs 59 | 60 | # if you like one-liners, keep them 61 | #keep-one-line-statements 62 | 63 | # -xV 64 | --attach-closing-while 65 | 66 | #unpad-paren 67 | 68 | # -xp 69 | remove-comment-prefix 70 | 71 | -------------------------------------------------------------------------------- /utils/restyle.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for dir in . ; do 4 | find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.ino" \) -exec astyle --suffix=none --options=./utils/astyle_library.conf \{\} \; 5 | done 6 | 7 | --------------------------------------------------------------------------------