├── .codespellrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── stale.yml └── workflows │ ├── auto-github-actions.yml │ ├── report-size-deltas.yml │ └── spell-check.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── changelog.md ├── examples ├── Argument_None │ └── Argument_None.ino ├── Change_Interval │ └── Change_Interval.ino ├── ISR_16_Timers_Array │ └── ISR_16_Timers_Array.ino ├── ISR_16_Timers_Array_Complex │ └── ISR_16_Timers_Array_Complex.ino ├── ISR_16_Timers_Array_Complex_OneShot │ └── ISR_16_Timers_Array_Complex_OneShot.ino ├── ISR_16_Timers_Array_OneShot │ └── ISR_16_Timers_Array_OneShot.ino ├── ISR_RPM_Measure │ └── ISR_RPM_Measure.ino ├── RPM_Measure │ └── RPM_Measure.ino ├── SwitchDebounce │ └── SwitchDebounce.ino ├── TimerInterruptTest │ └── TimerInterruptTest.ino └── multiFileProject │ ├── multiFileProject.cpp │ ├── multiFileProject.h │ └── multiFileProject.ino ├── keywords.txt ├── library.json ├── library.properties ├── platformio └── platformio.ini ├── src ├── ESP32TimerInterrupt.h ├── ESP32TimerInterrupt.hpp ├── ESP32_ISR_Timer-Impl.h ├── ESP32_ISR_Timer.h ├── ESP32_ISR_Timer.hpp ├── ESP8266TimerInterrupt.h ├── ESP8266_ISR_Timer-Impl.h ├── ESP8266_ISR_Timer.h ├── ESP8266_ISR_Timer.hpp └── TimerInterrupt_Generic_Debug.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 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 | * `ESP8266` Core Version (e.g. ESP8266 core v3.0.2) 40 | * Contextual information (e.g. what you were trying to achieve) 41 | * Simplest possible steps to reproduce 42 | * Anything that might be relevant in your opinion, such as: 43 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 44 | * Network configuration 45 | 46 | 47 | ### Example 48 | 49 | ``` 50 | Arduino IDE version: v1.8.19 51 | ESP8266 Core Version v3.0.2 52 | ESP8266_NODEMCU 53 | OS: Ubuntu 20.04 LTS 54 | 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 55 | 56 | Context: 57 | I encountered a crash while trying to use the library 58 | 59 | Steps to reproduce: 60 | 1. ... 61 | 2. ... 62 | 3. ... 63 | 4. ... 64 | ``` 65 | 66 | --- 67 | 68 | ### Sending Feature Requests 69 | 70 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 71 | 72 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 73 | 74 | --- 75 | 76 | ### Sending Pull Requests 77 | 78 | Pull Requests with changes and fixes are also welcome! 79 | 80 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 81 | 82 | 1. Change directory to the library GitHub 83 | 84 | ``` 85 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESP8266TimerInterrupt_GitHub/ 86 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP8266TimerInterrupt_GitHub$ 87 | ``` 88 | 89 | 2. Issue astyle command 90 | 91 | ``` 92 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP8266TimerInterrupt_GitHub$ bash utils/restyle.sh 93 | ``` 94 | 95 | -------------------------------------------------------------------------------- /.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 | 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | ### Describe the solution you'd like 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | ### Describe alternatives you've considered 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | ### Additional context 22 | 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /.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/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 ESP8266TimerInterrupt 2 | 3 | ### Reporting Bugs 4 | 5 | Please report bugs in [ESP8266TimerInterrupt Issues](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues) 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/ESP8266TimerInterrupt/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/ESP8266TimerInterrupt/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 | * `ESP8266` Core Version (e.g. ESP8266 core v3.0.2) 19 | * Contextual information (e.g. what you were trying to achieve) 20 | * Simplest possible steps to reproduce 21 | * Anything that might be relevant in your opinion, such as: 22 | * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` 23 | * Network configuration 24 | 25 | 26 | ### Example 27 | 28 | ``` 29 | Arduino IDE version: v1.8.19 30 | ESP8266 Core Version v3.0.2 31 | ESP8266_NODEMCU 32 | OS: Ubuntu 20.04 LTS 33 | 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 34 | 35 | Context: 36 | I encountered a crash while trying to use the library 37 | 38 | Steps to reproduce: 39 | 1. ... 40 | 2. ... 41 | 3. ... 42 | 4. ... 43 | ``` 44 | 45 | --- 46 | 47 | ### Sending Feature Requests 48 | 49 | Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. 50 | 51 | There are usually some outstanding feature requests in the [existing issues list](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement), feel free to add comments to them. 52 | 53 | --- 54 | 55 | ### Sending Pull Requests 56 | 57 | Pull Requests with changes and fixes are also welcome! 58 | 59 | Please use the `astyle` to reformat the updated library code as follows (demo for Ubuntu Linux) 60 | 61 | 1. Change directory to the library GitHub 62 | 63 | ``` 64 | xy@xy-Inspiron-3593:~$ cd Arduino/xy/ESP8266TimerInterrupt_GitHub/ 65 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP8266TimerInterrupt_GitHub$ 66 | ``` 67 | 68 | 2. Issue astyle command 69 | 70 | ``` 71 | xy@xy-Inspiron-3593:~/Arduino/xy/ESP8266TimerInterrupt_GitHub$ bash utils/restyle.sh 72 | ``` 73 | 74 | 75 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # ESP8266TimerInterrupt Library 2 | 3 | [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP8266TimerInterrupt.svg?)](https://www.ardu-badge.com/ESP8266TimerInterrupt) 4 | [![GitHub release](https://img.shields.io/github/release/khoih-prog/ESP8266TimerInterrupt.svg)](https://github.com/khoih-prog/ESP8266TimerInterrupt/releases) 5 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/ESP8266TimerInterrupt/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/ESP8266TimerInterrupt.svg)](http://github.com/khoih-prog/ESP8266TimerInterrupt/issues) 8 | 9 | 10 | Donate to my libraries using BuyMeACoffee 11 | 12 | 13 | --- 14 | --- 15 | 16 | * [Changelog](#changelog) 17 | * [Releases v1.6.0](#releases-v160) 18 | * [Releases v1.5.0](#releases-v150) 19 | * [Releases v1.4.1](#releases-v141) 20 | * [Releases v1.4.0](#releases-v140) 21 | * [Releases v1.3.0](#releases-v130) 22 | * [Releases v1.2.0](#releases-v120) 23 | * [Releases v1.1.1](#releases-v111) 24 | * [Releases v1.1.0](#releases-v110) 25 | * [Releases v1.0.3](#releases-v103) 26 | * [Releases v1.0.2](#releases-v102) 27 | 28 | --- 29 | --- 30 | 31 | ## Changelog 32 | 33 | ### Releases v1.6.0 34 | 35 | 1. Add example [ISR_16_Timers_Array_OneShot](examples/ISR_16_Timers_Array_OneShot) to demo how to use `one-shot ISR-based timer` 36 | 2. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project 37 | 3. Optimize code by using passing by `reference` instead of by `value` 38 | 39 | ### Releases v1.5.0 40 | 41 | 1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories 42 | 2. Add feature to select among highest, medium or lowest accuracy for Timers for shortest, medium or longest time 43 | 3. Fix reattachInterrupt() bug. Check [bugfix: reattachInterrupt() pass wrong frequency value to setFrequency() #19](https://github.com/khoih-prog/ESP8266TimerInterrupt/pull/19) 44 | 4. Update examples 45 | 46 | ### Releases v1.4.1 47 | 48 | 1. Examples modified and tested with core v3.0.2 49 | 2. Add instructions in `README.md` 50 | 3. Add `changelog.md` 51 | 4. Delete Blynk-related examples 52 | 53 | ### Releases v1.4.0 54 | 55 | 1. Fix compiler errors due to conflict to some libraries. 56 | 2. Add complex examples. 57 | 58 | ### Releases v1.3.0 59 | 60 | 1. Update to match new ESP8266 core v3.0.0 61 | 62 | ### Releases v1.2.0 63 | 64 | 1. Add better debug feature. 65 | 2. Optimize code and examples to reduce RAM usage 66 | 67 | ### Releases v1.1.1 68 | 69 | 1. Add [**Change_Interval**](examples/Change_Interval) example to show how to change TimerInterval on-the-fly 70 | 2. Add Version String 71 | 72 | ### Releases v1.1.0 73 | 74 | 1. Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 75 | 2. Update examples. 76 | 3. Enhance README. 77 | 78 | ### Releases v1.0.3 79 | 80 | 1. Restructure code. 81 | 2. Fix example. 82 | 3. Enhance README. 83 | 84 | ### Releases v1.0.2 85 | 86 | 1. Basic hardware timers for ESP8266. 87 | 2. Fix compatibility issue causing compiler error while using Arduino IDEs before 1.8.10 and ESP8266 cores 2.5.2 and before 88 | 3. More hardware-initiated software-enabled timers 89 | 4. Longer time interval 90 | 91 | -------------------------------------------------------------------------------- /examples/Argument_None/Argument_None.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Argument_None.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 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 | #if !defined(ESP8266) 32 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 33 | #endif 34 | 35 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 36 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 37 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 38 | #define TIMER_INTERRUPT_DEBUG 0 39 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 40 | 41 | // Select a Timer Clock 42 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 43 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 44 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 45 | 46 | #include "ESP8266TimerInterrupt.h" 47 | 48 | #ifndef LED_BUILTIN 49 | #define LED_BUILTIN 2 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED 50 | #endif 51 | 52 | volatile uint32_t lastMillis = 0; 53 | 54 | void IRAM_ATTR TimerHandler() 55 | { 56 | static bool toggle = false; 57 | static bool started = false; 58 | 59 | if (!started) 60 | { 61 | started = true; 62 | pinMode(LED_BUILTIN, OUTPUT); 63 | } 64 | 65 | #if (TIMER_INTERRUPT_DEBUG > 0) 66 | Serial.print("Delta ms = "); 67 | Serial.println(millis() - lastMillis); 68 | lastMillis = millis(); 69 | #endif 70 | 71 | //timer interrupt toggles pin LED_BUILTIN 72 | digitalWrite(LED_BUILTIN, toggle); 73 | toggle = !toggle; 74 | } 75 | 76 | #define TIMER_INTERVAL_MS 1000 77 | 78 | // Init ESP8266 timer 1 79 | ESP8266Timer ITimer; 80 | 81 | void setup() 82 | { 83 | Serial.begin(115200); 84 | 85 | while (!Serial && millis() < 5000); 86 | 87 | delay(500); 88 | 89 | Serial.print(F("\nStarting Argument_None on ")); 90 | Serial.println(ARDUINO_BOARD); 91 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 92 | Serial.print(F("CPU Frequency = ")); 93 | Serial.print(F_CPU / 1000000); 94 | Serial.println(F(" MHz")); 95 | 96 | // Interval in microsecs 97 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) 98 | { 99 | lastMillis = millis(); 100 | Serial.print(F("Starting ITimer OK, millis() = ")); 101 | Serial.println(lastMillis); 102 | } 103 | else 104 | Serial.println(F("Can't set ITimer correctly. Select another freq. or interval")); 105 | } 106 | 107 | void loop() 108 | { 109 | 110 | } 111 | -------------------------------------------------------------------------------- /examples/Change_Interval/Change_Interval.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | Change_Interval.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 19 | 20 | /* 21 | Notes: 22 | Special design is necessary to share data between interrupt code and the rest of your program. 23 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 24 | variable can not spontaneously change. Because your function may change variables while your program is using them, 25 | the compiler needs this hint. But volatile alone is often not enough. 26 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 27 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 28 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 29 | or the entire sequence of your code which accesses the data. 30 | */ 31 | 32 | #if !defined(ESP8266) 33 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 34 | #endif 35 | 36 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 37 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 38 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 39 | #define TIMER_INTERRUPT_DEBUG 0 40 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 41 | 42 | // Select a Timer Clock 43 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 44 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 45 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 46 | 47 | #include "ESP8266TimerInterrupt.h" 48 | 49 | #ifndef LED_BUILTIN 50 | #define LED_BUILTIN D4 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED 51 | #endif 52 | 53 | 54 | #define TIMER_INTERVAL_MS 500 //1000 55 | 56 | volatile uint32_t TimerCount = 0; 57 | 58 | // Init ESP8266 timer 1 59 | ESP8266Timer ITimer; 60 | 61 | void printResult(uint32_t currTime) 62 | { 63 | Serial.print(F("Time = ")); 64 | Serial.print(currTime); 65 | Serial.print(F(", TimerCount = ")); 66 | Serial.println(TimerCount); 67 | } 68 | 69 | void TimerHandler() 70 | { 71 | static bool toggle = false; 72 | 73 | // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR 74 | TimerCount++; 75 | 76 | //timer interrupt toggles pin LED_BUILTIN 77 | digitalWrite(LED_BUILTIN, toggle); 78 | toggle = !toggle; 79 | } 80 | 81 | void setup() 82 | { 83 | pinMode(LED_BUILTIN, OUTPUT); 84 | 85 | Serial.begin(115200); 86 | 87 | while (!Serial && millis() < 5000); 88 | 89 | delay(500); 90 | 91 | Serial.print(F("\nStarting Change_Interval on ")); 92 | Serial.println(ARDUINO_BOARD); 93 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 94 | Serial.print(F("CPU Frequency = ")); 95 | Serial.print(F_CPU / 1000000); 96 | Serial.println(F(" MHz")); 97 | 98 | // Interval in microsecs 99 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) 100 | { 101 | Serial.print(F("Starting ITimer OK, millis() = ")); 102 | Serial.println(millis()); 103 | } 104 | else 105 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 106 | } 107 | 108 | #define CHECK_INTERVAL_MS 10000L 109 | #define CHANGE_INTERVAL_MS 20000L 110 | 111 | void loop() 112 | { 113 | static uint32_t lastTime = 0; 114 | static uint32_t lastChangeTime = 0; 115 | static uint32_t currTime; 116 | static uint32_t multFactor = 0; 117 | 118 | currTime = millis(); 119 | 120 | if (currTime - lastTime > CHECK_INTERVAL_MS) 121 | { 122 | printResult(currTime); 123 | lastTime = currTime; 124 | 125 | if (currTime - lastChangeTime > CHANGE_INTERVAL_MS) 126 | { 127 | //setInterval(unsigned long interval, timerCallback callback) 128 | multFactor = (multFactor + 1) % 2; 129 | 130 | ITimer.setInterval(TIMER_INTERVAL_MS * 1000 * (multFactor + 1), TimerHandler); 131 | 132 | Serial.print(F("Changing Interval, Timer = ")); 133 | Serial.println(TIMER_INTERVAL_MS * (multFactor + 1)); 134 | 135 | lastChangeTime = currTime; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_16_Timers_Array.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 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 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms 31 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor 32 | Assuming LOW is active. 33 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing 34 | If the time between active state is less than 8ms => consider noise. 35 | RPM = 60000 / (rotation time in ms) 36 | 37 | We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state 38 | 39 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 40 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 41 | and Blynk services. You can also have many (up to 16) timers to use. 42 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 43 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 44 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 45 | written 46 | */ 47 | 48 | #if !defined(ESP8266) 49 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 50 | #endif 51 | 52 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 53 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 54 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 55 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. 56 | #define TIMER_INTERRUPT_DEBUG 2 57 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 58 | 59 | // Select a Timer Clock 60 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 61 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 62 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 63 | 64 | #include "ESP8266TimerInterrupt.h" 65 | #include "ESP8266_ISR_Timer.h" 66 | 67 | #include // https://github.com/jfturcot/SimpleTimer 68 | 69 | #ifndef LED_BUILTIN 70 | #define LED_BUILTIN 2 71 | #endif 72 | 73 | #ifndef LED_BLUE 74 | #define LED_BLUE 25 75 | #endif 76 | 77 | #ifndef LED_RED 78 | #define LED_RED 27 79 | #endif 80 | 81 | #define HW_TIMER_INTERVAL_MS 1L 82 | 83 | volatile uint32_t startMillis = 0; 84 | 85 | // Init ESP8266 timer 1 86 | ESP8266Timer ITimer; 87 | 88 | // Init BlynkTimer 89 | ESP8266_ISR_Timer ISR_Timer; 90 | 91 | #define LED_TOGGLE_INTERVAL_MS 2000L 92 | 93 | void IRAM_ATTR TimerHandler() 94 | { 95 | static bool toggle = false; 96 | static bool started = false; 97 | static int timeRun = 0; 98 | 99 | ISR_Timer.run(); 100 | 101 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s 102 | if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS) ) 103 | { 104 | timeRun = 0; 105 | 106 | if (!started) 107 | { 108 | started = true; 109 | pinMode(LED_BUILTIN, OUTPUT); 110 | } 111 | 112 | //timer interrupt toggles pin LED_BUILTIN 113 | digitalWrite(LED_BUILTIN, toggle); 114 | toggle = !toggle; 115 | } 116 | } 117 | 118 | #define NUMBER_ISR_TIMERS 16 119 | 120 | // You can assign any interval for any timer here, in milliseconds 121 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] = 122 | { 123 | 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L, 124 | 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L 125 | }; 126 | 127 | typedef void (*irqCallback) (); 128 | 129 | #if (TIMER_INTERRUPT_DEBUG > 0) 130 | void printStatus(uint16_t index, unsigned long deltaMillis, unsigned long currentMillis) 131 | { 132 | Serial.print(TimerInterval[index] / 1000); 133 | Serial.print("s: Delta ms = "); 134 | Serial.print(deltaMillis); 135 | Serial.print(", ms = "); 136 | Serial.println(currentMillis); 137 | } 138 | #endif 139 | 140 | // In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument 141 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment 142 | // Or you can get this run-time error / crash 143 | void doingSomething0() 144 | { 145 | #if (TIMER_INTERRUPT_DEBUG > 0) 146 | static unsigned long previousMillis = startMillis; 147 | 148 | unsigned long currentMillis = millis(); 149 | unsigned long deltaMillis = currentMillis - previousMillis; 150 | 151 | printStatus(0, deltaMillis, currentMillis); 152 | 153 | previousMillis = currentMillis; 154 | #endif 155 | } 156 | 157 | void doingSomething1() 158 | { 159 | #if (TIMER_INTERRUPT_DEBUG > 1) 160 | static unsigned long previousMillis = startMillis; 161 | 162 | unsigned long currentMillis = millis(); 163 | unsigned long deltaMillis = currentMillis - previousMillis; 164 | 165 | printStatus(1, deltaMillis, currentMillis); 166 | 167 | previousMillis = currentMillis; 168 | #endif 169 | } 170 | 171 | void doingSomething2() 172 | { 173 | #if (TIMER_INTERRUPT_DEBUG > 1) 174 | static unsigned long previousMillis = startMillis; 175 | 176 | unsigned long currentMillis = millis(); 177 | unsigned long deltaMillis = currentMillis - previousMillis; 178 | 179 | printStatus(2, deltaMillis, currentMillis); 180 | 181 | previousMillis = currentMillis; 182 | #endif 183 | } 184 | 185 | void doingSomething3() 186 | { 187 | #if (TIMER_INTERRUPT_DEBUG > 1) 188 | static unsigned long previousMillis = startMillis; 189 | 190 | unsigned long currentMillis = millis(); 191 | unsigned long deltaMillis = currentMillis - previousMillis; 192 | 193 | printStatus(3, deltaMillis, currentMillis); 194 | 195 | previousMillis = currentMillis; 196 | #endif 197 | } 198 | 199 | void doingSomething4() 200 | { 201 | #if (TIMER_INTERRUPT_DEBUG > 1) 202 | static unsigned long previousMillis = startMillis; 203 | 204 | unsigned long currentMillis = millis(); 205 | unsigned long deltaMillis = currentMillis - previousMillis; 206 | 207 | printStatus(4, deltaMillis, currentMillis); 208 | 209 | previousMillis = currentMillis; 210 | #endif 211 | } 212 | 213 | void doingSomething5() 214 | { 215 | #if (TIMER_INTERRUPT_DEBUG > 1) 216 | static unsigned long previousMillis = startMillis; 217 | 218 | unsigned long currentMillis = millis(); 219 | unsigned long deltaMillis = currentMillis - previousMillis; 220 | 221 | printStatus(5, deltaMillis, currentMillis); 222 | 223 | previousMillis = currentMillis; 224 | #endif 225 | } 226 | 227 | void doingSomething6() 228 | { 229 | #if (TIMER_INTERRUPT_DEBUG > 1) 230 | static unsigned long previousMillis = startMillis; 231 | 232 | unsigned long currentMillis = millis(); 233 | unsigned long deltaMillis = currentMillis - previousMillis; 234 | 235 | printStatus(6, deltaMillis, currentMillis); 236 | 237 | previousMillis = currentMillis; 238 | #endif 239 | } 240 | 241 | void doingSomething7() 242 | { 243 | #if (TIMER_INTERRUPT_DEBUG > 1) 244 | static unsigned long previousMillis = startMillis; 245 | 246 | unsigned long currentMillis = millis(); 247 | unsigned long deltaMillis = currentMillis - previousMillis; 248 | 249 | printStatus(7, deltaMillis, currentMillis); 250 | 251 | previousMillis = currentMillis; 252 | #endif 253 | } 254 | 255 | void doingSomething8() 256 | { 257 | #if (TIMER_INTERRUPT_DEBUG > 1) 258 | static unsigned long previousMillis = startMillis; 259 | 260 | unsigned long currentMillis = millis(); 261 | unsigned long deltaMillis = currentMillis - previousMillis; 262 | 263 | printStatus(8, deltaMillis, currentMillis); 264 | 265 | previousMillis = currentMillis; 266 | #endif 267 | } 268 | 269 | void doingSomething9() 270 | { 271 | #if (TIMER_INTERRUPT_DEBUG > 1) 272 | static unsigned long previousMillis = startMillis; 273 | 274 | unsigned long currentMillis = millis(); 275 | unsigned long deltaMillis = currentMillis - previousMillis; 276 | 277 | printStatus(9, deltaMillis, currentMillis); 278 | 279 | previousMillis = currentMillis; 280 | #endif 281 | } 282 | 283 | void doingSomething10() 284 | { 285 | #if (TIMER_INTERRUPT_DEBUG > 1) 286 | static unsigned long previousMillis = startMillis; 287 | 288 | unsigned long currentMillis = millis(); 289 | unsigned long deltaMillis = currentMillis - previousMillis; 290 | 291 | printStatus(10, deltaMillis, currentMillis); 292 | 293 | previousMillis = currentMillis; 294 | #endif 295 | } 296 | 297 | void doingSomething11() 298 | { 299 | #if (TIMER_INTERRUPT_DEBUG > 1) 300 | static unsigned long previousMillis = startMillis; 301 | 302 | unsigned long currentMillis = millis(); 303 | unsigned long deltaMillis = currentMillis - previousMillis; 304 | 305 | printStatus(11, deltaMillis, currentMillis); 306 | 307 | previousMillis = currentMillis; 308 | #endif 309 | } 310 | 311 | void doingSomething12() 312 | { 313 | #if (TIMER_INTERRUPT_DEBUG > 1) 314 | static unsigned long previousMillis = startMillis; 315 | 316 | unsigned long currentMillis = millis(); 317 | unsigned long deltaMillis = currentMillis - previousMillis; 318 | 319 | printStatus(12, deltaMillis, currentMillis); 320 | 321 | previousMillis = currentMillis; 322 | #endif 323 | } 324 | 325 | void doingSomething13() 326 | { 327 | #if (TIMER_INTERRUPT_DEBUG > 1) 328 | static unsigned long previousMillis = startMillis; 329 | 330 | unsigned long currentMillis = millis(); 331 | unsigned long deltaMillis = currentMillis - previousMillis; 332 | 333 | printStatus(13, deltaMillis, currentMillis); 334 | 335 | previousMillis = currentMillis; 336 | #endif 337 | } 338 | 339 | void doingSomething14() 340 | { 341 | #if (TIMER_INTERRUPT_DEBUG > 1) 342 | static unsigned long previousMillis = startMillis; 343 | 344 | unsigned long currentMillis = millis(); 345 | unsigned long deltaMillis = currentMillis - previousMillis; 346 | 347 | printStatus(14, deltaMillis, currentMillis); 348 | 349 | previousMillis = currentMillis; 350 | #endif 351 | } 352 | 353 | void doingSomething15() 354 | { 355 | #if (TIMER_INTERRUPT_DEBUG > 1) 356 | static unsigned long previousMillis = startMillis; 357 | 358 | unsigned long currentMillis = millis(); 359 | unsigned long deltaMillis = currentMillis - previousMillis; 360 | 361 | printStatus(15, deltaMillis, currentMillis); 362 | 363 | previousMillis = currentMillis; 364 | #endif 365 | } 366 | 367 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = 368 | { 369 | doingSomething0, doingSomething1, doingSomething2, doingSomething3, 370 | doingSomething4, doingSomething5, doingSomething6, doingSomething7, 371 | doingSomething8, doingSomething9, doingSomething10, doingSomething11, 372 | doingSomething12, doingSomething13, doingSomething14, doingSomething15 373 | }; 374 | 375 | //////////////////////////////////////////////// 376 | 377 | 378 | #define SIMPLE_TIMER_MS 2000L 379 | 380 | // Init SimpleTimer 381 | SimpleTimer simpleTimer; 382 | 383 | // Here is software Timer, you can do somewhat fancy stuffs without many issues. 384 | // But always avoid 385 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead 386 | // 2. Very long "do", "while", "for" loops without predetermined exit time. 387 | void simpleTimerDoingSomething2s() 388 | { 389 | static unsigned long previousMillis = startMillis; 390 | 391 | Serial.print(F("simpleTimerDoingSomething2s: Delta programmed ms = ")); 392 | Serial.print(SIMPLE_TIMER_MS); 393 | Serial.print(F(", actual = ")); 394 | Serial.println(millis() - previousMillis); 395 | 396 | previousMillis = millis(); 397 | } 398 | 399 | void setup() 400 | { 401 | Serial.begin(115200); 402 | 403 | while (!Serial && millis() < 5000); 404 | 405 | delay(500); 406 | 407 | Serial.print(F("\nStarting ISR_16_Timers_Array on ")); 408 | Serial.println(ARDUINO_BOARD); 409 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 410 | Serial.print(F("CPU Frequency = ")); 411 | Serial.print(F_CPU / 1000000); 412 | Serial.println(F(" MHz")); 413 | 414 | // Interval in microsecs 415 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler)) 416 | { 417 | startMillis = millis(); 418 | Serial.print(F("Starting ITimer OK, millis() = ")); 419 | Serial.println(startMillis); 420 | } 421 | else 422 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 423 | 424 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 425 | // You can use up to 16 timer for each SAMD_ISR_Timer 426 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 427 | { 428 | ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); 429 | } 430 | 431 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. 432 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); 433 | } 434 | 435 | #define BLOCKING_TIME_MS 10000L 436 | 437 | void loop() 438 | { 439 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer 440 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer 441 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) 442 | // While that of ISR_Timer is still prefect. 443 | delay(BLOCKING_TIME_MS); 444 | 445 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary 446 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. 447 | simpleTimer.run(); 448 | } 449 | -------------------------------------------------------------------------------- /examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_16_Timers_Array_Complex.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 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 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 31 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 32 | and Blynk services. You can also have many (up to 16) timers to use. 33 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 34 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 35 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 36 | written 37 | */ 38 | 39 | #if !defined(ESP8266) 40 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 41 | #endif 42 | 43 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 44 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 45 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 46 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. 47 | #define TIMER_INTERRUPT_DEBUG 0 48 | #define _TIMERINTERRUPT_LOGLEVEL_ 4 49 | 50 | // Select a Timer Clock 51 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 52 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 53 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 54 | 55 | #include "ESP8266TimerInterrupt.h" 56 | #include "ESP8266_ISR_Timer.h" 57 | 58 | #include // https://github.com/jfturcot/SimpleTimer 59 | 60 | #ifndef LED_BUILTIN 61 | #define LED_BUILTIN 2 62 | #endif 63 | 64 | #ifndef LED_BLUE 65 | #define LED_BLUE 25 66 | #endif 67 | 68 | #ifndef LED_RED 69 | #define LED_RED 27 70 | #endif 71 | 72 | #define HW_TIMER_INTERVAL_US 10000L 73 | 74 | volatile uint32_t startMillis = 0; 75 | 76 | // Init ESP8266 timer 1 77 | ESP8266Timer ITimer; 78 | 79 | // Init ESP8266_ISR_Timer 80 | ESP8266_ISR_Timer ISR_Timer; 81 | 82 | #define LED_TOGGLE_INTERVAL_MS 2000L 83 | 84 | void IRAM_ATTR TimerHandler() 85 | { 86 | static bool toggle = false; 87 | static int timeRun = 0; 88 | 89 | ISR_Timer.run(); 90 | 91 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s 92 | if (++timeRun == ((LED_TOGGLE_INTERVAL_MS * 1000) / HW_TIMER_INTERVAL_US) ) 93 | { 94 | timeRun = 0; 95 | 96 | //timer interrupt toggles pin LED_BUILTIN 97 | digitalWrite(LED_BUILTIN, toggle); 98 | toggle = !toggle; 99 | } 100 | } 101 | 102 | ///////////////////////////////////////////////// 103 | 104 | #define NUMBER_ISR_TIMERS 16 105 | 106 | typedef void (*irqCallback) (); 107 | 108 | ///////////////////////////////////////////////// 109 | 110 | #define USE_COMPLEX_STRUCT true 111 | 112 | #if USE_COMPLEX_STRUCT 113 | 114 | typedef struct 115 | { 116 | irqCallback irqCallbackFunc; 117 | uint32_t TimerInterval; 118 | unsigned long deltaMillis; 119 | unsigned long previousMillis; 120 | } ISRTimerData; 121 | 122 | // In ESP8266, avoid doing something fancy in ISR, for example Serial.print() 123 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment 124 | // Or you can get this run-time error / crash 125 | 126 | void doingSomething(int index); 127 | 128 | #else 129 | 130 | volatile unsigned long deltaMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 131 | volatile unsigned long previousMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 132 | 133 | // You can assign any interval for any timer here, in milliseconds 134 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] = 135 | { 136 | 5000L, 10000L, 15000L, 20000L, 25000L, 30000L, 35000L, 40000L, 137 | 45000L, 50000L, 55000L, 60000L, 65000L, 70000L, 75000L, 80000L 138 | }; 139 | 140 | void doingSomething(int index) 141 | { 142 | unsigned long currentMillis = millis(); 143 | 144 | deltaMillis[index] = currentMillis - previousMillis[index]; 145 | previousMillis[index] = currentMillis; 146 | } 147 | 148 | #endif 149 | 150 | //////////////////////////////////// 151 | // Shared 152 | //////////////////////////////////// 153 | 154 | void doingSomething0() 155 | { 156 | doingSomething(0); 157 | } 158 | 159 | void doingSomething1() 160 | { 161 | doingSomething(1); 162 | } 163 | 164 | void doingSomething2() 165 | { 166 | doingSomething(2); 167 | } 168 | 169 | void doingSomething3() 170 | { 171 | doingSomething(3); 172 | } 173 | 174 | void doingSomething4() 175 | { 176 | doingSomething(4); 177 | } 178 | 179 | void doingSomething5() 180 | { 181 | doingSomething(5); 182 | } 183 | 184 | void doingSomething6() 185 | { 186 | doingSomething(6); 187 | } 188 | 189 | void doingSomething7() 190 | { 191 | doingSomething(7); 192 | } 193 | 194 | void doingSomething8() 195 | { 196 | doingSomething(8); 197 | } 198 | 199 | void doingSomething9() 200 | { 201 | doingSomething(9); 202 | } 203 | 204 | void doingSomething10() 205 | { 206 | doingSomething(10); 207 | } 208 | 209 | void doingSomething11() 210 | { 211 | doingSomething(11); 212 | } 213 | 214 | void doingSomething12() 215 | { 216 | doingSomething(12); 217 | } 218 | 219 | void doingSomething13() 220 | { 221 | doingSomething(13); 222 | } 223 | 224 | void doingSomething14() 225 | { 226 | doingSomething(14); 227 | } 228 | 229 | void doingSomething15() 230 | { 231 | doingSomething(15); 232 | } 233 | 234 | #if USE_COMPLEX_STRUCT 235 | 236 | ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] = 237 | { 238 | //irqCallbackFunc, TimerInterval, deltaMillis, previousMillis 239 | { doingSomething0, 5000L, 0, 0 }, 240 | { doingSomething1, 10000L, 0, 0 }, 241 | { doingSomething2, 15000L, 0, 0 }, 242 | { doingSomething3, 20000L, 0, 0 }, 243 | { doingSomething4, 25000L, 0, 0 }, 244 | { doingSomething5, 30000L, 0, 0 }, 245 | { doingSomething6, 35000L, 0, 0 }, 246 | { doingSomething7, 40000L, 0, 0 }, 247 | { doingSomething8, 45000L, 0, 0 }, 248 | { doingSomething9, 50000L, 0, 0 }, 249 | { doingSomething10, 55000L, 0, 0 }, 250 | { doingSomething11, 60000L, 0, 0 }, 251 | { doingSomething12, 65000L, 0, 0 }, 252 | { doingSomething13, 70000L, 0, 0 }, 253 | { doingSomething14, 75000L, 0, 0 }, 254 | { doingSomething15, 80000L, 0, 0 } 255 | }; 256 | 257 | void doingSomething(int index) 258 | { 259 | unsigned long currentMillis = millis(); 260 | 261 | curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis; 262 | curISRTimerData[index].previousMillis = currentMillis; 263 | } 264 | 265 | #else 266 | 267 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = 268 | { 269 | doingSomething0, doingSomething1, doingSomething2, doingSomething3, 270 | doingSomething4, doingSomething5, doingSomething6, doingSomething7, 271 | doingSomething8, doingSomething9, doingSomething10, doingSomething11, 272 | doingSomething12, doingSomething13, doingSomething14, doingSomething15 273 | }; 274 | 275 | #endif 276 | /////////////////////////////////////////// 277 | 278 | #define SIMPLE_TIMER_MS 2000L 279 | 280 | // Init SimpleTimer 281 | SimpleTimer simpleTimer; 282 | 283 | // Here is software Timer, you can do somewhat fancy stuffs without many issues. 284 | // But always avoid 285 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead 286 | // 2. Very long "do", "while", "for" loops without predetermined exit time. 287 | void simpleTimerDoingSomething2s() 288 | { 289 | static unsigned long previousMillis = startMillis; 290 | 291 | unsigned long currMillis = millis(); 292 | 293 | Serial.print(F("SimpleTimer : ")); 294 | Serial.print(SIMPLE_TIMER_MS / 1000); 295 | Serial.print(F(", ms : ")); 296 | Serial.print(currMillis); 297 | Serial.print(F(", Dms : ")); 298 | Serial.println(currMillis - previousMillis); 299 | 300 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 301 | { 302 | #if USE_COMPLEX_STRUCT 303 | Serial.print(F("Timer : ")); 304 | Serial.print(i); 305 | Serial.print(F(", programmed : ")); 306 | Serial.print(curISRTimerData[i].TimerInterval); 307 | Serial.print(F(", actual : ")); 308 | Serial.println(curISRTimerData[i].deltaMillis); 309 | #else 310 | Serial.print(F("Timer : ")); 311 | Serial.print(i); 312 | Serial.print(F(", programmed : ")); 313 | Serial.print(TimerInterval[i]); 314 | Serial.print(F(", actual : ")); 315 | Serial.println(deltaMillis[i]); 316 | #endif 317 | } 318 | 319 | previousMillis = currMillis; 320 | } 321 | 322 | void setup() 323 | { 324 | pinMode(LED_BUILTIN, OUTPUT); 325 | 326 | Serial.begin(115200); 327 | 328 | while (!Serial && millis() < 5000); 329 | 330 | delay(500); 331 | 332 | Serial.print(F("\nStarting ISR_16_Timers_Array_Complex on ")); 333 | Serial.println(ARDUINO_BOARD); 334 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 335 | Serial.print(F("CPU Frequency = ")); 336 | Serial.print(F_CPU / 1000000); 337 | Serial.println(F(" MHz")); 338 | 339 | // Interval in microsecs 340 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler)) 341 | { 342 | startMillis = millis(); 343 | Serial.print(F("Starting ITimer OK, millis() = ")); 344 | Serial.println(startMillis); 345 | } 346 | else 347 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 348 | 349 | startMillis = millis(); 350 | 351 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 352 | // You can use up to 16 timer for each ISR_Timer 353 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 354 | { 355 | #if USE_COMPLEX_STRUCT 356 | curISRTimerData[i].previousMillis = startMillis; 357 | ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc); 358 | #else 359 | previousMillis[i] = millis(); 360 | ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); 361 | #endif 362 | } 363 | 364 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. 365 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); 366 | } 367 | 368 | #define BLOCKING_TIME_MS 10000L 369 | 370 | void loop() 371 | { 372 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer 373 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer 374 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) 375 | // While that of ISR_Timer is still prefect. 376 | delay(BLOCKING_TIME_MS); 377 | 378 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary 379 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. 380 | simpleTimer.run(); 381 | } 382 | -------------------------------------------------------------------------------- /examples/ISR_16_Timers_Array_Complex_OneShot/ISR_16_Timers_Array_Complex_OneShot.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_16_Timers_Array_Complex_OneShot.ino 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1 10 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1 11 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0 12 | 13 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers. 14 | The timer counters can be configured to count up or down and support automatic reload and software reload. 15 | They can also generate alarms when they reach a specific value, defined by the software. 16 | The value of the counter can be read by the software program. 17 | 18 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 19 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 20 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 21 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 22 | This important feature is absolutely necessary for mission-critical tasks. 23 | *****************************************************************************************************************************/ 24 | /* 25 | Notes: 26 | Special design is necessary to share data between interrupt code and the rest of your program. 27 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 28 | variable can not spontaneously change. Because your function may change variables while your program is using them, 29 | the compiler needs this hint. But volatile alone is often not enough. 30 | When accessing shared variables, usually interrupts must be disabled. Even with volatile, 31 | if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. 32 | If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled 33 | or the entire sequence of your code which accesses the data. 34 | 35 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 36 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 37 | and Blynk services. You can also have many (up to 16) timers to use. 38 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 39 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 40 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 41 | written 42 | */ 43 | 44 | #if !defined( ESP32 ) 45 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 46 | #endif 47 | 48 | // These define's must be placed at the beginning before #include "ESP32TimerInterrupt.h" 49 | #define _TIMERINTERRUPT_LOGLEVEL_ 4 50 | 51 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 52 | #include "ESP32TimerInterrupt.h" 53 | 54 | #include // https://github.com/jfturcot/SimpleTimer 55 | 56 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 57 | 58 | #ifndef LED_BLUE 59 | #define LED_BLUE 25 60 | #endif 61 | 62 | #ifndef LED_RED 63 | #define LED_RED 27 64 | #endif 65 | 66 | // Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868 67 | // Don't use PIN_D2 with ESP32_C3 (crash) 68 | #define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32 69 | #define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32 70 | 71 | #define HW_TIMER_INTERVAL_US 10000L 72 | 73 | volatile uint32_t startMillis = 0; 74 | 75 | // Init ESP32 timer 1 76 | ESP32Timer ITimer(1); 77 | 78 | // Init ESP32_ISR_Timer 79 | ESP32_ISR_Timer ISR_Timer; 80 | 81 | #define LED_TOGGLE_INTERVAL_MS 2000L 82 | 83 | // With core v2.0.0+, you can't use Serial.print/println in ISR or crash. 84 | // and you can't use float calculation inside ISR 85 | // Only OK in core v1.0.6- 86 | bool IRAM_ATTR TimerHandler(void * timerNo) 87 | { 88 | static bool toggle = false; 89 | static int timeRun = 0; 90 | 91 | ISR_Timer.run(); 92 | 93 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s 94 | if (++timeRun == ((LED_TOGGLE_INTERVAL_MS * 1000) / HW_TIMER_INTERVAL_US) ) 95 | { 96 | timeRun = 0; 97 | 98 | //timer interrupt toggles pin PIN_D19 99 | digitalWrite(PIN_D19, toggle); 100 | toggle = !toggle; 101 | } 102 | 103 | return true; 104 | } 105 | 106 | ///////////////////////////////////////////////// 107 | 108 | #define NUMBER_ISR_TIMERS 16 109 | 110 | typedef void (*irqCallback) (); 111 | 112 | ///////////////////////////////////////////////// 113 | 114 | #define USE_COMPLEX_STRUCT true 115 | 116 | #if USE_COMPLEX_STRUCT 117 | 118 | typedef struct 119 | { 120 | irqCallback irqCallbackFunc; 121 | uint32_t TimerInterval; 122 | unsigned long deltaMillis; 123 | unsigned long previousMillis; 124 | } ISRTimerData; 125 | 126 | // In ESP32, avoid doing something fancy in ISR, for example Serial.print() 127 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment 128 | // Or you can get this run-time error / crash 129 | 130 | void doingSomething(int index); 131 | 132 | #else 133 | 134 | volatile unsigned long deltaMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 135 | volatile unsigned long previousMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 136 | 137 | // You can assign any interval for any timer here, in milliseconds 138 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] = 139 | { 140 | 5000L, 10000L, 15000L, 20000L, 25000L, 30000L, 35000L, 40000L, 141 | 45000L, 50000L, 55000L, 60000L, 65000L, 70000L, 75000L, 80000L 142 | }; 143 | 144 | void doingSomething(int index) 145 | { 146 | unsigned long currentMillis = millis(); 147 | 148 | deltaMillis[index] = currentMillis - previousMillis[index]; 149 | previousMillis[index] = currentMillis; 150 | } 151 | 152 | #endif 153 | 154 | //////////////////////////////////// 155 | // Shared 156 | //////////////////////////////////// 157 | 158 | void doingSomething0() 159 | { 160 | doingSomething(0); 161 | } 162 | 163 | void doingSomething1() 164 | { 165 | doingSomething(1); 166 | } 167 | 168 | void doingSomething2() 169 | { 170 | doingSomething(2); 171 | } 172 | 173 | void doingSomething3() 174 | { 175 | doingSomething(3); 176 | } 177 | 178 | void doingSomething4() 179 | { 180 | doingSomething(4); 181 | } 182 | 183 | void doingSomething5() 184 | { 185 | doingSomething(5); 186 | } 187 | 188 | void doingSomething6() 189 | { 190 | doingSomething(6); 191 | } 192 | 193 | void doingSomething7() 194 | { 195 | doingSomething(7); 196 | } 197 | 198 | void doingSomething8() 199 | { 200 | doingSomething(8); 201 | } 202 | 203 | void doingSomething9() 204 | { 205 | doingSomething(9); 206 | } 207 | 208 | void doingSomething10() 209 | { 210 | doingSomething(10); 211 | } 212 | 213 | void doingSomething11() 214 | { 215 | doingSomething(11); 216 | } 217 | 218 | void doingSomething12() 219 | { 220 | doingSomething(12); 221 | } 222 | 223 | void doingSomething13() 224 | { 225 | doingSomething(13); 226 | } 227 | 228 | void doingSomething14() 229 | { 230 | doingSomething(14); 231 | } 232 | 233 | void doingSomething15() 234 | { 235 | doingSomething(15); 236 | } 237 | 238 | #if USE_COMPLEX_STRUCT 239 | 240 | ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] = 241 | { 242 | //irqCallbackFunc, TimerInterval, deltaMillis, previousMillis 243 | { doingSomething0, 5000L, 0, 0 }, 244 | { doingSomething1, 10000L, 0, 0 }, 245 | { doingSomething2, 15000L, 0, 0 }, 246 | { doingSomething3, 20000L, 0, 0 }, 247 | { doingSomething4, 25000L, 0, 0 }, 248 | { doingSomething5, 30000L, 0, 0 }, 249 | { doingSomething6, 35000L, 0, 0 }, 250 | { doingSomething7, 40000L, 0, 0 }, 251 | { doingSomething8, 45000L, 0, 0 }, 252 | { doingSomething9, 50000L, 0, 0 }, 253 | { doingSomething10, 55000L, 0, 0 }, 254 | { doingSomething11, 60000L, 0, 0 }, 255 | { doingSomething12, 65000L, 0, 0 }, 256 | { doingSomething13, 70000L, 0, 0 }, 257 | { doingSomething14, 75000L, 0, 0 }, 258 | { doingSomething15, 80000L, 0, 0 } 259 | }; 260 | 261 | void doingSomething(int index) 262 | { 263 | unsigned long currentMillis = millis(); 264 | 265 | curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis; 266 | curISRTimerData[index].previousMillis = currentMillis; 267 | } 268 | 269 | #else 270 | 271 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = 272 | { 273 | doingSomething0, doingSomething1, doingSomething2, doingSomething3, 274 | doingSomething4, doingSomething5, doingSomething6, doingSomething7, 275 | doingSomething8, doingSomething9, doingSomething10, doingSomething11, 276 | doingSomething12, doingSomething13, doingSomething14, doingSomething15 277 | }; 278 | 279 | #endif 280 | /////////////////////////////////////////// 281 | 282 | #define SIMPLE_TIMER_MS 2000L 283 | 284 | // Init SimpleTimer 285 | SimpleTimer simpleTimer; 286 | 287 | // Here is software Timer, you can do somewhat fancy stuffs without many issues. 288 | // But always avoid 289 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead 290 | // 2. Very long "do", "while", "for" loops without predetermined exit time. 291 | void simpleTimerDoingSomething2s() 292 | { 293 | static unsigned long previousMillis = startMillis; 294 | 295 | unsigned long currMillis = millis(); 296 | 297 | Serial.print(F("SimpleTimer : ")); 298 | Serial.print(SIMPLE_TIMER_MS / 1000); 299 | Serial.print(F(", ms : ")); 300 | Serial.print(currMillis); 301 | Serial.print(F(", Dms : ")); 302 | Serial.println(currMillis - previousMillis); 303 | 304 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 305 | { 306 | #if USE_COMPLEX_STRUCT 307 | Serial.print(F("Timer : ")); 308 | Serial.print(i); 309 | Serial.print(F(", programmed : ")); 310 | Serial.print(curISRTimerData[i].TimerInterval); 311 | Serial.print(F(", actual : ")); 312 | Serial.println(curISRTimerData[i].deltaMillis); 313 | // reset to 0 to be sure the timer is running one-shot or permanent 314 | curISRTimerData[i].deltaMillis = 0; 315 | #else 316 | Serial.print(F("Timer : ")); 317 | Serial.print(i); 318 | Serial.print(F(", programmed : ")); 319 | Serial.print(TimerInterval[i]); 320 | Serial.print(F(", actual : ")); 321 | Serial.println(deltaMillis[i]); 322 | // reset to 0 to be sure the timer is running one-shot or permanent 323 | deltaMillis[i] = 0; 324 | #endif 325 | } 326 | 327 | previousMillis = currMillis; 328 | } 329 | 330 | void setup() 331 | { 332 | pinMode(PIN_D19, OUTPUT); 333 | 334 | Serial.begin(115200); 335 | 336 | while (!Serial && millis() < 5000); 337 | 338 | delay(500); 339 | 340 | Serial.print(F("\nStarting ISR_16_Timers_Array_Complex_OneShot on ")); 341 | Serial.println(ARDUINO_BOARD); 342 | Serial.println(ESP32_TIMER_INTERRUPT_VERSION); 343 | Serial.print(F("CPU Frequency = ")); 344 | Serial.print(F_CPU / 1000000); 345 | Serial.println(F(" MHz")); 346 | 347 | // Interval in microsecs 348 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler)) 349 | { 350 | startMillis = millis(); 351 | Serial.print(F("Starting ITimer OK, millis() = ")); 352 | Serial.println(startMillis); 353 | } 354 | else 355 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 356 | 357 | startMillis = millis(); 358 | 359 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 360 | // You can use up to 16 timer for each ISR_Timer 361 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 362 | { 363 | #if USE_COMPLEX_STRUCT 364 | curISRTimerData[i].previousMillis = startMillis; 365 | 366 | // Set even timer one shot, odd timers run forever 367 | if ( i % 2 ) 368 | ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc); 369 | else 370 | ISR_Timer.setTimeout(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc); 371 | 372 | #else 373 | previousMillis[i] = millis(); 374 | 375 | // Set even timer one shot, odd timers run forever 376 | if ( i % 2 ) 377 | ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); 378 | else 379 | ISR_Timer.setTimeout(TimerInterval[i], irqCallbackFunc[i]); 380 | 381 | #endif 382 | } 383 | 384 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. 385 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); 386 | } 387 | 388 | #define BLOCKING_TIME_MS 10000L 389 | 390 | void loop() 391 | { 392 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer 393 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer 394 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) 395 | // While that of ISR_Timer is still prefect. 396 | delay(BLOCKING_TIME_MS); 397 | 398 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary 399 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. 400 | simpleTimer.run(); 401 | } 402 | -------------------------------------------------------------------------------- /examples/ISR_16_Timers_Array_OneShot/ISR_16_Timers_Array_OneShot.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_16_Timers_Array_OneShot.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 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 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms 31 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor 32 | Assuming LOW is active. 33 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing 34 | If the time between active state is less than 8ms => consider noise. 35 | RPM = 60000 / (rotation time in ms) 36 | 37 | We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state 38 | 39 | This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. 40 | Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet 41 | and Blynk services. You can also have many (up to 16) timers to use. 42 | This non-being-blocked important feature is absolutely necessary for mission-critical tasks. 43 | You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate 44 | In this super simple example, you don't see much different after Blynk is connected, because of no competing task is 45 | written 46 | */ 47 | 48 | #if !defined(ESP8266) 49 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 50 | #endif 51 | 52 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 53 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 54 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 55 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. 56 | #define TIMER_INTERRUPT_DEBUG 2 57 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 58 | 59 | // Select a Timer Clock 60 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 61 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 62 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 63 | 64 | #include "ESP8266TimerInterrupt.h" 65 | #include "ESP8266_ISR_Timer.h" 66 | 67 | #include // https://github.com/jfturcot/SimpleTimer 68 | 69 | #ifndef LED_BUILTIN 70 | #define LED_BUILTIN 2 71 | #endif 72 | 73 | #ifndef LED_BLUE 74 | #define LED_BLUE 25 75 | #endif 76 | 77 | #ifndef LED_RED 78 | #define LED_RED 27 79 | #endif 80 | 81 | #define HW_TIMER_INTERVAL_MS 1L 82 | 83 | volatile uint32_t startMillis = 0; 84 | 85 | // Init ESP8266 timer 1 86 | ESP8266Timer ITimer; 87 | 88 | // Init BlynkTimer 89 | ESP8266_ISR_Timer ISR_Timer; 90 | 91 | #define LED_TOGGLE_INTERVAL_MS 2000L 92 | 93 | void IRAM_ATTR TimerHandler() 94 | { 95 | static bool toggle = false; 96 | static bool started = false; 97 | static int timeRun = 0; 98 | 99 | ISR_Timer.run(); 100 | 101 | // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s 102 | if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS) ) 103 | { 104 | timeRun = 0; 105 | 106 | if (!started) 107 | { 108 | started = true; 109 | pinMode(LED_BUILTIN, OUTPUT); 110 | } 111 | 112 | //timer interrupt toggles pin LED_BUILTIN 113 | digitalWrite(LED_BUILTIN, toggle); 114 | toggle = !toggle; 115 | } 116 | } 117 | 118 | #define NUMBER_ISR_TIMERS 16 119 | 120 | // You can assign any interval for any timer here, in milliseconds 121 | uint32_t TimerInterval[NUMBER_ISR_TIMERS] = 122 | { 123 | 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L, 124 | 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L 125 | }; 126 | 127 | typedef void (*irqCallback) (); 128 | 129 | #if (TIMER_INTERRUPT_DEBUG > 0) 130 | void printStatus(uint16_t index, unsigned long deltaMillis, unsigned long currentMillis) 131 | { 132 | Serial.print(TimerInterval[index] / 1000); 133 | Serial.print("s: Delta ms = "); 134 | Serial.print(deltaMillis); 135 | Serial.print(", ms = "); 136 | Serial.println(currentMillis); 137 | } 138 | #endif 139 | 140 | // In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument 141 | // The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment 142 | // Or you can get this run-time error / crash 143 | void doingSomething0() 144 | { 145 | #if (TIMER_INTERRUPT_DEBUG > 0) 146 | static unsigned long previousMillis = startMillis; 147 | 148 | unsigned long currentMillis = millis(); 149 | unsigned long deltaMillis = currentMillis - previousMillis; 150 | 151 | printStatus(0, deltaMillis, currentMillis); 152 | 153 | previousMillis = currentMillis; 154 | #endif 155 | } 156 | 157 | void doingSomething1() 158 | { 159 | #if (TIMER_INTERRUPT_DEBUG > 1) 160 | static unsigned long previousMillis = startMillis; 161 | 162 | unsigned long currentMillis = millis(); 163 | unsigned long deltaMillis = currentMillis - previousMillis; 164 | 165 | printStatus(1, deltaMillis, currentMillis); 166 | 167 | previousMillis = currentMillis; 168 | #endif 169 | } 170 | 171 | void doingSomething2() 172 | { 173 | #if (TIMER_INTERRUPT_DEBUG > 1) 174 | static unsigned long previousMillis = startMillis; 175 | 176 | unsigned long currentMillis = millis(); 177 | unsigned long deltaMillis = currentMillis - previousMillis; 178 | 179 | printStatus(2, deltaMillis, currentMillis); 180 | 181 | previousMillis = currentMillis; 182 | #endif 183 | } 184 | 185 | void doingSomething3() 186 | { 187 | #if (TIMER_INTERRUPT_DEBUG > 1) 188 | static unsigned long previousMillis = startMillis; 189 | 190 | unsigned long currentMillis = millis(); 191 | unsigned long deltaMillis = currentMillis - previousMillis; 192 | 193 | printStatus(3, deltaMillis, currentMillis); 194 | 195 | previousMillis = currentMillis; 196 | #endif 197 | } 198 | 199 | void doingSomething4() 200 | { 201 | #if (TIMER_INTERRUPT_DEBUG > 1) 202 | static unsigned long previousMillis = startMillis; 203 | 204 | unsigned long currentMillis = millis(); 205 | unsigned long deltaMillis = currentMillis - previousMillis; 206 | 207 | printStatus(4, deltaMillis, currentMillis); 208 | 209 | previousMillis = currentMillis; 210 | #endif 211 | } 212 | 213 | void doingSomething5() 214 | { 215 | #if (TIMER_INTERRUPT_DEBUG > 1) 216 | static unsigned long previousMillis = startMillis; 217 | 218 | unsigned long currentMillis = millis(); 219 | unsigned long deltaMillis = currentMillis - previousMillis; 220 | 221 | printStatus(5, deltaMillis, currentMillis); 222 | 223 | previousMillis = currentMillis; 224 | #endif 225 | } 226 | 227 | void doingSomething6() 228 | { 229 | #if (TIMER_INTERRUPT_DEBUG > 1) 230 | static unsigned long previousMillis = startMillis; 231 | 232 | unsigned long currentMillis = millis(); 233 | unsigned long deltaMillis = currentMillis - previousMillis; 234 | 235 | printStatus(6, deltaMillis, currentMillis); 236 | 237 | previousMillis = currentMillis; 238 | #endif 239 | } 240 | 241 | void doingSomething7() 242 | { 243 | #if (TIMER_INTERRUPT_DEBUG > 1) 244 | static unsigned long previousMillis = startMillis; 245 | 246 | unsigned long currentMillis = millis(); 247 | unsigned long deltaMillis = currentMillis - previousMillis; 248 | 249 | printStatus(7, deltaMillis, currentMillis); 250 | 251 | previousMillis = currentMillis; 252 | #endif 253 | } 254 | 255 | void doingSomething8() 256 | { 257 | #if (TIMER_INTERRUPT_DEBUG > 1) 258 | static unsigned long previousMillis = startMillis; 259 | 260 | unsigned long currentMillis = millis(); 261 | unsigned long deltaMillis = currentMillis - previousMillis; 262 | 263 | printStatus(8, deltaMillis, currentMillis); 264 | 265 | previousMillis = currentMillis; 266 | #endif 267 | } 268 | 269 | void doingSomething9() 270 | { 271 | #if (TIMER_INTERRUPT_DEBUG > 1) 272 | static unsigned long previousMillis = startMillis; 273 | 274 | unsigned long currentMillis = millis(); 275 | unsigned long deltaMillis = currentMillis - previousMillis; 276 | 277 | printStatus(9, deltaMillis, currentMillis); 278 | 279 | previousMillis = currentMillis; 280 | #endif 281 | } 282 | 283 | void doingSomething10() 284 | { 285 | #if (TIMER_INTERRUPT_DEBUG > 1) 286 | static unsigned long previousMillis = startMillis; 287 | 288 | unsigned long currentMillis = millis(); 289 | unsigned long deltaMillis = currentMillis - previousMillis; 290 | 291 | printStatus(10, deltaMillis, currentMillis); 292 | 293 | previousMillis = currentMillis; 294 | #endif 295 | } 296 | 297 | void doingSomething11() 298 | { 299 | #if (TIMER_INTERRUPT_DEBUG > 1) 300 | static unsigned long previousMillis = startMillis; 301 | 302 | unsigned long currentMillis = millis(); 303 | unsigned long deltaMillis = currentMillis - previousMillis; 304 | 305 | printStatus(11, deltaMillis, currentMillis); 306 | 307 | previousMillis = currentMillis; 308 | #endif 309 | } 310 | 311 | void doingSomething12() 312 | { 313 | #if (TIMER_INTERRUPT_DEBUG > 1) 314 | static unsigned long previousMillis = startMillis; 315 | 316 | unsigned long currentMillis = millis(); 317 | unsigned long deltaMillis = currentMillis - previousMillis; 318 | 319 | printStatus(12, deltaMillis, currentMillis); 320 | 321 | previousMillis = currentMillis; 322 | #endif 323 | } 324 | 325 | void doingSomething13() 326 | { 327 | #if (TIMER_INTERRUPT_DEBUG > 1) 328 | static unsigned long previousMillis = startMillis; 329 | 330 | unsigned long currentMillis = millis(); 331 | unsigned long deltaMillis = currentMillis - previousMillis; 332 | 333 | printStatus(13, deltaMillis, currentMillis); 334 | 335 | previousMillis = currentMillis; 336 | #endif 337 | } 338 | 339 | void doingSomething14() 340 | { 341 | #if (TIMER_INTERRUPT_DEBUG > 1) 342 | static unsigned long previousMillis = startMillis; 343 | 344 | unsigned long currentMillis = millis(); 345 | unsigned long deltaMillis = currentMillis - previousMillis; 346 | 347 | printStatus(14, deltaMillis, currentMillis); 348 | 349 | previousMillis = currentMillis; 350 | #endif 351 | } 352 | 353 | void doingSomething15() 354 | { 355 | #if (TIMER_INTERRUPT_DEBUG > 1) 356 | static unsigned long previousMillis = startMillis; 357 | 358 | unsigned long currentMillis = millis(); 359 | unsigned long deltaMillis = currentMillis - previousMillis; 360 | 361 | printStatus(15, deltaMillis, currentMillis); 362 | 363 | previousMillis = currentMillis; 364 | #endif 365 | } 366 | 367 | irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = 368 | { 369 | doingSomething0, doingSomething1, doingSomething2, doingSomething3, 370 | doingSomething4, doingSomething5, doingSomething6, doingSomething7, 371 | doingSomething8, doingSomething9, doingSomething10, doingSomething11, 372 | doingSomething12, doingSomething13, doingSomething14, doingSomething15 373 | }; 374 | 375 | //////////////////////////////////////////////// 376 | 377 | 378 | #define SIMPLE_TIMER_MS 2000L 379 | 380 | // Init SimpleTimer 381 | SimpleTimer simpleTimer; 382 | 383 | // Here is software Timer, you can do somewhat fancy stuffs without many issues. 384 | // But always avoid 385 | // 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead 386 | // 2. Very long "do", "while", "for" loops without predetermined exit time. 387 | void simpleTimerDoingSomething2s() 388 | { 389 | static unsigned long previousMillis = startMillis; 390 | 391 | Serial.print(F("simpleTimerDoingSomething2s: Delta programmed ms = ")); 392 | Serial.print(SIMPLE_TIMER_MS); 393 | Serial.print(F(", actual = ")); 394 | Serial.println(millis() - previousMillis); 395 | 396 | previousMillis = millis(); 397 | } 398 | 399 | void setup() 400 | { 401 | Serial.begin(115200); 402 | 403 | while (!Serial && millis() < 5000); 404 | 405 | delay(500); 406 | 407 | Serial.print(F("\nStarting ISR_16_Timers_Array_OneShot on ")); 408 | Serial.println(ARDUINO_BOARD); 409 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 410 | Serial.print(F("CPU Frequency = ")); 411 | Serial.print(F_CPU / 1000000); 412 | Serial.println(F(" MHz")); 413 | 414 | // Interval in microsecs 415 | if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler)) 416 | { 417 | startMillis = millis(); 418 | Serial.print(F("Starting ITimer OK, millis() = ")); 419 | Serial.println(startMillis); 420 | } 421 | else 422 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 423 | 424 | // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary 425 | // You can use up to 16 timer for each SAMD_ISR_Timer 426 | for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) 427 | { 428 | //ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); 429 | // Use this for one shot ISR TImer 430 | ISR_Timer.setTimeout(TimerInterval[i], irqCallbackFunc[i]); 431 | } 432 | 433 | // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. 434 | simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); 435 | } 436 | 437 | #define BLOCKING_TIME_MS 10000L 438 | 439 | void loop() 440 | { 441 | // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer 442 | // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer 443 | // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) 444 | // While that of ISR_Timer is still prefect. 445 | delay(BLOCKING_TIME_MS); 446 | 447 | // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary 448 | // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. 449 | simpleTimer.run(); 450 | } 451 | -------------------------------------------------------------------------------- /examples/ISR_RPM_Measure/ISR_RPM_Measure.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ISR_RPM_Measure.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 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 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms 31 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor 32 | Assuming LOW is active. 33 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing 34 | If the time between active state is less than 8ms => consider noise. 35 | RPM = 60000 / (rotation time in ms) 36 | 37 | We use interrupt to detect whenever the SW is active, set a flag 38 | then use timer to count the time between active state 39 | */ 40 | 41 | #if !defined(ESP8266) 42 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 43 | #endif 44 | 45 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 46 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 47 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 48 | #define TIMER_INTERRUPT_DEBUG 2 49 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 50 | 51 | // Select a Timer Clock 52 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 53 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 54 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 55 | 56 | #include "ESP8266TimerInterrupt.h" 57 | 58 | #define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266 59 | 60 | unsigned int interruptPin = PIN_D1; 61 | 62 | #define TIMER_INTERVAL_MS 1 63 | #define DEBOUNCING_INTERVAL_MS 80 64 | 65 | #define LOCAL_DEBUG 1 66 | 67 | // Init ESP8266 timer 1 68 | ESP8266Timer ITimer; 69 | 70 | volatile unsigned long rotationTime = 0; 71 | float RPM = 0.00; 72 | float avgRPM = 0.00; 73 | 74 | volatile int debounceCounter; 75 | 76 | volatile bool activeState = false; 77 | 78 | void IRAM_ATTR detectRotation() 79 | { 80 | activeState = true; 81 | } 82 | 83 | void IRAM_ATTR TimerHandler() 84 | { 85 | if ( activeState ) 86 | { 87 | // Reset to prepare for next round of interrupt 88 | activeState = false; 89 | 90 | if (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS ) 91 | { 92 | 93 | //min time between pulses has passed 94 | RPM = (float) ( 60000.0f / ( rotationTime * TIMER_INTERVAL_MS ) ); 95 | 96 | avgRPM = ( 2 * avgRPM + RPM) / 3, 97 | 98 | #if (TIMER_INTERRUPT_DEBUG > 1) 99 | Serial.print("RPM = "); 100 | Serial.print(avgRPM); 101 | Serial.print(", rotationTime ms = "); 102 | Serial.println(rotationTime * TIMER_INTERVAL_MS); 103 | #endif 104 | 105 | rotationTime = 0; 106 | debounceCounter = 0; 107 | } 108 | else 109 | debounceCounter++; 110 | } 111 | else 112 | { 113 | debounceCounter++; 114 | } 115 | 116 | if (rotationTime >= 5000) 117 | { 118 | // If idle, set RPM to 0, don't increase rotationTime 119 | RPM = 0; 120 | 121 | #if (TIMER_INTERRUPT_DEBUG > 1) 122 | Serial.print("RPM = "); 123 | Serial.print(RPM); 124 | Serial.print(", rotationTime = "); 125 | Serial.println(rotationTime); 126 | #endif 127 | 128 | rotationTime = 0; 129 | } 130 | else 131 | { 132 | rotationTime++; 133 | } 134 | } 135 | 136 | void setup() 137 | { 138 | pinMode(interruptPin, INPUT_PULLUP); 139 | 140 | Serial.begin(115200); 141 | 142 | while (!Serial && millis() < 5000); 143 | 144 | delay(500); 145 | 146 | Serial.print(F("\nStarting ISR_RPM_Measure on ")); 147 | Serial.println(ARDUINO_BOARD); 148 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 149 | Serial.print(F("CPU Frequency = ")); 150 | Serial.print(F_CPU / 1000000); 151 | Serial.println(F(" MHz")); 152 | 153 | // Interval in microsecs 154 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) 155 | { 156 | Serial.print(F("Starting ITimer OK, millis() = ")); 157 | Serial.println(millis()); 158 | } 159 | else 160 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 161 | 162 | // Assumming the interruptPin will go LOW 163 | attachInterrupt(digitalPinToInterrupt(interruptPin), detectRotation, FALLING); 164 | } 165 | 166 | void loop() 167 | { 168 | 169 | } 170 | -------------------------------------------------------------------------------- /examples/RPM_Measure/RPM_Measure.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | RPM_Measure.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 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 | RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms 31 | then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor 32 | Assuming LOW is active. 33 | For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing 34 | If the time between active state is less than 8ms => consider noise. 35 | RPM = 60000 / (rotation time in ms) 36 | 37 | You can also use interrupt to detect whenever the SW is active, set a flag 38 | then use timer to count the time between active state 39 | */ 40 | #if !defined(ESP8266) 41 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 42 | #endif 43 | 44 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 45 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 46 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 47 | #define TIMER_INTERRUPT_DEBUG 2 48 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 49 | 50 | // Select a Timer Clock 51 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 52 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 53 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 54 | 55 | #include "ESP8266TimerInterrupt.h" 56 | 57 | #define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266 58 | 59 | unsigned int SWPin = PIN_D1; 60 | 61 | #define TIMER_INTERVAL_MS 1 62 | #define DEBOUNCING_INTERVAL_MS 80 63 | 64 | #define LOCAL_DEBUG 1 65 | 66 | // Init ESP8266 timer 1 67 | ESP8266Timer ITimer; 68 | 69 | volatile unsigned long rotationTime = 0; 70 | 71 | float RPM = 0.00; 72 | float avgRPM = 0.00; 73 | 74 | volatile int debounceCounter; 75 | 76 | void IRAM_ATTR TimerHandler() 77 | { 78 | static bool started = false; 79 | 80 | if (!started) 81 | { 82 | started = true; 83 | pinMode(SWPin, INPUT_PULLUP); 84 | } 85 | 86 | if ( !digitalRead(SWPin) && (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS ) ) 87 | { 88 | //min time between pulses has passed 89 | RPM = (float) ( 60000.0f / ( rotationTime * TIMER_INTERVAL_MS ) ); 90 | 91 | avgRPM = ( 2 * avgRPM + RPM) / 3, 92 | 93 | #if (LOCAL_DEBUG > 0) 94 | Serial.print("RPM = "); 95 | Serial.print(avgRPM); 96 | Serial.print(", rotationTime ms = "); 97 | Serial.println(rotationTime * TIMER_INTERVAL_MS); 98 | #endif 99 | 100 | rotationTime = 0; 101 | debounceCounter = 0; 102 | } 103 | else 104 | { 105 | debounceCounter++; 106 | } 107 | 108 | if (rotationTime >= 5000) 109 | { 110 | // If idle, set RPM to 0, don't increase rotationTime 111 | RPM = 0; 112 | 113 | #if (LOCAL_DEBUG > 0) 114 | Serial.print("RPM = "); 115 | Serial.print(RPM); 116 | Serial.print(", rotationTime = "); 117 | Serial.println(rotationTime); 118 | #endif 119 | 120 | rotationTime = 0; 121 | } 122 | else 123 | { 124 | rotationTime++; 125 | } 126 | } 127 | 128 | void setup() 129 | { 130 | Serial.begin(115200); 131 | 132 | while (!Serial && millis() < 5000); 133 | 134 | delay(500); 135 | 136 | Serial.print(F("\nStarting RPM_Measure on ")); 137 | Serial.println(ARDUINO_BOARD); 138 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 139 | Serial.print(F("CPU Frequency = ")); 140 | Serial.print(F_CPU / 1000000); 141 | Serial.println(F(" MHz")); 142 | 143 | // Interval in microsecs 144 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) 145 | { 146 | Serial.print(F("Starting ITimer OK, millis() = ")); 147 | Serial.println(millis()); 148 | } 149 | else 150 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 151 | } 152 | 153 | void loop() 154 | { 155 | 156 | } 157 | -------------------------------------------------------------------------------- /examples/SwitchDebounce/SwitchDebounce.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | SwitchDebounce.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 19 | /* Notes: 20 | Special design is necessary to share data between interrupt code and the rest of your program. 21 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 22 | variable can not spontaneously change. Because your function may change variables while your program is using them, 23 | the compiler needs this hint. But volatile alone is often not enough. 24 | 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 | Switch Debouncing uses high frequency hardware timer 50Hz == 20ms) to measure the time from the SW is pressed, 31 | debouncing time is 100ms => SW is considered pressed if timer count is > 5, then call / flag SW is pressed 32 | When the SW is released, timer will count (debounce) until more than 50ms until consider SW is released. 33 | We can set to flag or call a function whenever SW is pressed more than certain predetermined time, even before 34 | SW is released. 35 | */ 36 | #if !defined(ESP8266) 37 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 38 | #endif 39 | 40 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 41 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 42 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 43 | #define TIMER_INTERRUPT_DEBUG 1 44 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 45 | 46 | // Select a Timer Clock 47 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 48 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 49 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 50 | 51 | #include "ESP8266TimerInterrupt.h" 52 | 53 | #define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266 54 | 55 | unsigned int SWPin = PIN_D1; 56 | 57 | #define TIMER_INTERVAL_MS 20 58 | #define DEBOUNCING_INTERVAL_MS 100 59 | #define LONG_PRESS_INTERVAL_MS 5000 60 | 61 | //#define LOCAL_DEBUG 1 62 | 63 | // Init ESP8266 timer 1 64 | ESP8266Timer ITimer; 65 | 66 | volatile bool SWPressed = false; 67 | volatile bool SWLongPressed = false; 68 | 69 | void IRAM_ATTR TimerHandler() 70 | { 71 | static unsigned int debounceCountSWPressed = 0; 72 | static unsigned int debounceCountSWReleased = 0; 73 | 74 | #if (TIMER_INTERRUPT_DEBUG > 0) 75 | static unsigned long SWPressedTime; 76 | static unsigned long SWReleasedTime; 77 | 78 | static unsigned long currentMillis; 79 | #endif 80 | 81 | currentMillis = millis(); 82 | 83 | if ( (!digitalRead(SWPin)) ) 84 | { 85 | // Start debouncing counting debounceCountSWPressed and clear debounceCountSWReleased 86 | debounceCountSWReleased = 0; 87 | 88 | if (++debounceCountSWPressed >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS) 89 | { 90 | // Call and flag SWPressed 91 | if (!SWPressed) 92 | { 93 | #if (TIMER_INTERRUPT_DEBUG > 0) 94 | SWPressedTime = currentMillis; 95 | 96 | Serial.print("SW Press, from millis() = "); 97 | Serial.println(SWPressedTime - DEBOUNCING_INTERVAL_MS); 98 | #endif 99 | 100 | SWPressed = true; 101 | // Do something for SWPressed here in ISR 102 | // But it's better to use outside software timer to do your job instead of inside ISR 103 | //Your_Response_To_Press(); 104 | } 105 | 106 | if (debounceCountSWPressed >= LONG_PRESS_INTERVAL_MS / TIMER_INTERVAL_MS) 107 | { 108 | // Call and flag SWLongPressed 109 | if (!SWLongPressed) 110 | { 111 | #if (TIMER_INTERRUPT_DEBUG > 0) 112 | Serial.print("SW Long Pressed, total time ms = "); 113 | Serial.print(currentMillis); 114 | Serial.print(" - "); 115 | Serial.print(SWPressedTime - DEBOUNCING_INTERVAL_MS); 116 | Serial.print(" = "); 117 | Serial.println(currentMillis - SWPressedTime + DEBOUNCING_INTERVAL_MS); 118 | #endif 119 | 120 | SWLongPressed = true; 121 | // Do something for SWLongPressed here in ISR 122 | // But it's better to use outside software timer to do your job instead of inside ISR 123 | //Your_Response_To_Long_Press(); 124 | } 125 | } 126 | } 127 | } 128 | else 129 | { 130 | // Start debouncing counting debounceCountSWReleased and clear debounceCountSWPressed 131 | if ( SWPressed && (++debounceCountSWReleased >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS)) 132 | { 133 | // Call and flag SWPressed 134 | #if (TIMER_INTERRUPT_DEBUGEBUG > 0) 135 | SWReleasedTime = millis(); 136 | 137 | // Call and flag SWPressed 138 | Serial.print("SW Released, from millis() = "); 139 | Serial.println(millis()); 140 | #endif 141 | 142 | SWPressed = false; 143 | SWLongPressed = false; 144 | 145 | // Do something for !SWPressed here in ISR 146 | // But it's better to use outside software timer to do your job instead of inside ISR 147 | //Your_Response_To_Release(); 148 | 149 | // Call and flag SWPressed 150 | #if (TIMER_INTERRUPT_DEBUG > 0) 151 | Serial.print("SW Pressed total time ms = "); 152 | Serial.println(millis() - SWPressedTime); 153 | #endif 154 | 155 | debounceCountSWPressed = 0; 156 | } 157 | } 158 | } 159 | 160 | void setup() 161 | { 162 | pinMode(SWPin, INPUT_PULLUP); 163 | 164 | Serial.begin(115200); 165 | 166 | while (!Serial && millis() < 5000); 167 | 168 | delay(500); 169 | 170 | Serial.print(F("\nStarting SwitchDebounce on ")); 171 | Serial.println(ARDUINO_BOARD); 172 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 173 | Serial.print(F("CPU Frequency = ")); 174 | Serial.print(F_CPU / 1000000); 175 | Serial.println(F(" MHz")); 176 | 177 | // Interval in microsecs 178 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) 179 | { 180 | Serial.print(F("Starting ITimer OK, millis() = ")); 181 | Serial.println(millis()); 182 | } 183 | else 184 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 185 | } 186 | 187 | void loop() 188 | { 189 | 190 | } 191 | -------------------------------------------------------------------------------- /examples/TimerInterruptTest/TimerInterruptTest.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerInterruptTest.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | *****************************************************************************************************************************/ 19 | /* Notes: 20 | Special design is necessary to share data between interrupt code and the rest of your program. 21 | Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume 22 | variable can not spontaneously change. Because your function may change variables while your program is using them, 23 | the compiler needs this hint. But volatile alone is often not enough. 24 | 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 | #if !defined(ESP8266) 32 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 33 | #endif 34 | 35 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 36 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 37 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 38 | #define TIMER_INTERRUPT_DEBUG 1 39 | #define _TIMERINTERRUPT_LOGLEVEL_ 1 40 | 41 | // Select a Timer Clock 42 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 43 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 44 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 45 | 46 | #include "ESP8266TimerInterrupt.h" 47 | 48 | #define BUILTIN_LED 2 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED 49 | 50 | volatile bool statusLed = false; 51 | volatile uint32_t lastMillis = 0; 52 | 53 | #define TIMER_INTERVAL_MS 1000 54 | 55 | // Init ESP8266 timer 1 56 | ESP8266Timer ITimer; 57 | 58 | //======================================================================= 59 | void IRAM_ATTR TimerHandler() 60 | { 61 | static bool started = false; 62 | 63 | if (!started) 64 | { 65 | started = true; 66 | pinMode(BUILTIN_LED, OUTPUT); 67 | } 68 | 69 | digitalWrite(BUILTIN_LED, statusLed); //Toggle LED Pin 70 | statusLed = !statusLed; 71 | 72 | #if (TIMER_INTERRUPT_DEBUG > 0) 73 | Serial.println("Delta ms = " + String(millis() - lastMillis)); 74 | lastMillis = millis(); 75 | #endif 76 | } 77 | //======================================================================= 78 | // Setup 79 | //======================================================================= 80 | void setup() 81 | { 82 | Serial.begin(115200); 83 | 84 | while (!Serial && millis() < 5000); 85 | 86 | delay(500); 87 | 88 | Serial.print(F("\nStarting TimerInterruptTest on ")); 89 | Serial.println(ARDUINO_BOARD); 90 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 91 | Serial.print(F("CPU Frequency = ")); 92 | Serial.print(F_CPU / 1000000); 93 | Serial.println(F(" MHz")); 94 | 95 | // Interval in microsecs 96 | if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) 97 | { 98 | lastMillis = millis(); 99 | Serial.print(F("Starting ITimer OK, millis() = ")); 100 | Serial.println(lastMillis); 101 | } 102 | else 103 | Serial.println(F("Can't set ITimer correctly. Select another freq. or interval")); 104 | 105 | } 106 | //======================================================================= 107 | // MAIN LOOP 108 | //======================================================================= 109 | void loop() 110 | { 111 | } 112 | -------------------------------------------------------------------------------- /examples/multiFileProject/multiFileProject.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | multiFileProject.cpp 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | *****************************************************************************************************************************/ 9 | 10 | // To demo how to include files in multi-file Projects 11 | 12 | #include "multiFileProject.h" 13 | 14 | ESP8266Timer ITimer; 15 | ESP8266_ISR_Timer ISR_Timer; 16 | 17 | void IRAM_ATTR TimerHandler() 18 | { 19 | ISR_Timer.run(); 20 | Serial.println("TimerHandler triggered"); 21 | } 22 | 23 | void doingSomething0() 24 | { 25 | Serial.println("doingSomething0 triggered"); 26 | } 27 | 28 | void setupISR() 29 | { 30 | if (ITimer.attachInterruptInterval(1000 * 1000, TimerHandler)) 31 | { 32 | Serial.print(F("Starting ITimer OK")); 33 | } 34 | else 35 | Serial.println(F("Can't set ITimer. Select another freq. or timer")); 36 | 37 | ISR_Timer.setInterval(3000, doingSomething0); 38 | } 39 | -------------------------------------------------------------------------------- /examples/multiFileProject/multiFileProject.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | multiFileProject.h 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | *****************************************************************************************************************************/ 9 | 10 | // To demo how to include files in multi-file Projects 11 | 12 | #pragma once 13 | 14 | // These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" 15 | // _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 16 | // Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. 17 | // Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. 18 | #define TIMER_INTERRUPT_DEBUG 2 19 | #define _TIMERINTERRUPT_LOGLEVEL_ 0 20 | 21 | // Select a Timer Clock 22 | #define USING_TIM_DIV1 false // for shortest and most accurate timer 23 | #define USING_TIM_DIV16 false // for medium time and medium accurate timer 24 | #define USING_TIM_DIV256 true // for longest timer but least accurate. Default 25 | 26 | // Can be included as many times as necessary, without `Multiple Definitions` Linker Error 27 | #include "ESP8266TimerInterrupt.h" 28 | #include "ESP8266_ISR_Timer.hpp" 29 | 30 | extern ESP8266_ISR_Timer ISR_Timer; // declaration of the global variable ISRTimer 31 | 32 | void setupISR(); 33 | -------------------------------------------------------------------------------- /examples/multiFileProject/multiFileProject.ino: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | multiFileProject.ino 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | *****************************************************************************************************************************/ 9 | 10 | // To demo how to include files in multi-file Projects 11 | 12 | #ifndef ESP8266 13 | #error This code is designed to run on ESP8266 platform! Please check your Tools->Board setting. 14 | #endif 15 | 16 | #define ESP8266_TIMER_INTERRUPT_VERSION_MIN_TARGET "ESP8266TimerInterrupt v1.6.0" 17 | #define ESP8266_TIMER_INTERRUPT_VERSION_MIN 1006000 18 | 19 | #include "multiFileProject.h" 20 | 21 | // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error 22 | #include "ESP8266TimerInterrupt.h" 23 | #include "ESP8266_ISR_Timer.h" 24 | 25 | void doingSomething1() 26 | { 27 | Serial.println("doingSomething1 triggered"); 28 | } 29 | 30 | void setup() 31 | { 32 | Serial.begin(115200); 33 | 34 | while (!Serial && millis() < 5000); 35 | 36 | delay(500); 37 | 38 | Serial.println("\nStart multiFileProject"); 39 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); 40 | 41 | #if defined(ESP8266_TIMER_INTERRUPT_VERSION_MIN) 42 | 43 | if (ESP8266_TIMER_INTERRUPT_VERSION_INT < ESP8266_TIMER_INTERRUPT_VERSION_MIN) 44 | { 45 | Serial.print("Warning. Must use this example on Version equal or later than : "); 46 | Serial.println(ESP8266_TIMER_INTERRUPT_VERSION_MIN_TARGET); 47 | } 48 | 49 | #endif 50 | 51 | setupISR(); // in multifileProject.cpp 52 | 53 | ISR_Timer.setTimeout(5000, doingSomething1); 54 | } 55 | 56 | void loop() 57 | { 58 | // put your main code here, to run repeatedly: 59 | } 60 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Datatypes (KEYWORD1) 3 | ####################################### 4 | 5 | ESP8266TimerInterrupt KEYWORD1 6 | ESP8266Timer KEYWORD1 7 | ESP8266_ISR_Timer KEYWORD1 8 | ISRTimer KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | 14 | setFrequency KEYWORD2 15 | setInterval KEYWORD2 16 | attachInterrupt KEYWORD2 17 | attachInterruptInterval KEYWORD2 18 | detachInterrupt KEYWORD2 19 | disableTimer KEYWORD2 20 | reattachInterrupt KEYWORD2 21 | enableTimer KEYWORD2 22 | stopTimer KEYWORD2 23 | restartTimer KEYWORD2 24 | init KEYWORD2 25 | run KEYWORD2 26 | setTimeout KEYWORD2 27 | setTimer KEYWORD2 28 | changeInterval KEYWORD2 29 | deleteTimer KEYWORD2 30 | restartTimer KEYWORD2 31 | isEnabled KEYWORD2 32 | enable KEYWORD2 33 | disable KEYWORD2 34 | enableAll KEYWORD2 35 | disableAll KEYWORD2 36 | toggle KEYWORD2 37 | getNumTimers KEYWORD2 38 | getNumAvailableTimers KEYWORD2 39 | 40 | ####################################### 41 | # Constants (LITERAL1) 42 | ####################################### 43 | 44 | ESP8266_TIMER_INTERRUPT_VERSION LITERAL1 45 | ESP8266_TIMER_INTERRUPT_VERSION_MAJOR LITERAL1 46 | ESP8266_TIMER_INTERRUPT_VERSION_MINOR LITERAL1 47 | ESP8266_TIMER_INTERRUPT_VERSION_PATCH LITERAL1 48 | ESP8266_TIMER_INTERRUPT_VERSION_INT LITERAL1 49 | 50 | MAX_ESP8266_NUM_TIMERS LITERAL1 51 | MAX_ESP8266_COUNT LITERAL1 52 | 53 | TIM_DIV1_CLOCK LITERAL1 54 | TIM_DIV16_CLOCK LITERAL1 55 | TIM_DIV256_CLOCK LITERAL1 56 | 57 | TIM_CLOCK_FREQ LITERAL1 58 | TIM_DIV LITERAL1 59 | 60 | USING_TIM_DIV1 LITERAL1 61 | USING_TIM_DIV16 LITERAL1 62 | USING_TIM_DIV256 LITERAL1 63 | 64 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP8266TimerInterrupt", 3 | "version": "1.6.0", 4 | "keywords": "timing, device, control, timer, interrupt, timer-interrupt, hardware, isr, isr-based, hardware-timer, isr-timer, isr-based-timer, mission-critical, accuracy, precise, non-blocking, long-timer, esp8266, multi-isr-based-timers", 5 | "description": "This library enables you to use Interrupt from Hardware Timers on an ESP8266-based board. It now supports 16 ISR-based timers, while consuming only 1 Hardware Timer. Timers' interval is very long (ulong millisecs). The most important feature is they're ISR-based timers. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. 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.", 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/ESP8266TimerInterrupt" 16 | }, 17 | "homepage": "https://github.com/khoih-prog/ESP8266TimerInterrupt", 18 | "export": { 19 | "exclude": [ 20 | "linux", 21 | "extras", 22 | "tests" 23 | ] 24 | }, 25 | "license": "MIT", 26 | "frameworks": "*", 27 | "platforms": "espressif8266", 28 | "examples": "examples/*/*/*.ino", 29 | "headers": ["ESP8266TimerInterrupt.h", "ESP8266_ISR_Timer.h", "ESP8266_ISR_Timer.hpp"] 30 | } 31 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP8266TimerInterrupt 2 | version=1.6.0 3 | author=Khoi Hoang 4 | maintainer=Khoi Hoang 5 | sentence=This library enables you to use Interrupt from Hardware Timers on an ESP8266-based board. 6 | paragraph=These ESP8266 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 mandatory if you need to measure some data requiring better accuracy. It now supports 16 ISR-based Timers, while consuming only 1 Hardware Timer. Timers' interval is very long (ulong millisecs). The most important feature is they're ISR-based Timers. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. 7 | category=Device Control 8 | url=https://github.com/khoih-prog/ESP8266TimerInterrupt 9 | architectures=esp8266 10 | repository=https://github.com/khoih-prog/ESP8266TimerInterrupt 11 | license=MIT 12 | includes=ESP8266TimerInterrupt.h,ESP8266_ISR_Timer.h,ESP8266_ISR_Timer.hpp 13 | -------------------------------------------------------------------------------- /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 | ; ESP8266 15 | ; ============================================================ 16 | default_envs = ESP8266 17 | 18 | [env] 19 | ; ============================================================ 20 | ; Serial configuration 21 | ; choose upload speed, serial-monitor speed 22 | ; ============================================================ 23 | upload_speed = 921600 24 | ;upload_port = COM11 25 | ;monitor_speed = 9600 26 | ;monitor_port = COM11 27 | 28 | ; Checks for the compatibility with frameworks and dev/platforms 29 | lib_compat_mode = strict 30 | lib_ldf_mode = chain+ 31 | ;lib_ldf_mode = deep+ 32 | 33 | lib_deps = 34 | 35 | 36 | build_flags = 37 | ; set your debug output (default=Serial) 38 | -D DEBUG_ESP_PORT=Serial 39 | ; comment the following line to enable WiFi debugging 40 | -D NDEBUG 41 | 42 | [env:ESP8266] 43 | platform = espressif8266 44 | framework = arduino 45 | ; ============================================================ 46 | ; Board configuration 47 | ; choose your board by uncommenting one of the following lines 48 | ; ============================================================ 49 | ;board = gen4iod 50 | ;board = huzzah 51 | ;board = oak 52 | ;board = esp_wroom_02 53 | ;board = espduino 54 | ;board = espectro 55 | ;board = espino 56 | ;board = espresso_lite_v1 57 | ;board = espresso_lite_v2 58 | ;board = esp12e 59 | ;board = esp01_1m 60 | ;board = esp01 61 | ;board = esp07 62 | ;board = esp8285 63 | ;board = heltec_wifi_kit_8 64 | ;board = inventone 65 | ;board = nodemcu 66 | board = nodemcuv2 67 | ;board = modwifi 68 | ;board = phoenix_v1 69 | ;board = phoenix_v2 70 | ;board = sparkfunBlynk 71 | ;board = thing 72 | ;board = thingdev 73 | ;board = esp210 74 | ;board = espinotee 75 | ;board = d1 76 | ;board = d1_mini 77 | ;board = d1_mini_lite 78 | ;board = d1_mini_pro 79 | ;board = wifi_slot 80 | ;board = wifiduino 81 | ;board = wifinfo 82 | ;board = wio_link 83 | ;board = wio_node 84 | ;board = xinabox_cw01 85 | ;board = esp32doit-devkit-v1 86 | 87 | -------------------------------------------------------------------------------- /src/ESP32TimerInterrupt.h: -------------------------------------------------------------------------------- 1 | 2 | /**************************************************************************************************************************** 3 | ESP32TimerInterrupt.h 4 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+ 5 | Written by Khoi Hoang 6 | 7 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt 8 | Licensed under MIT license 9 | 10 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1 11 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1 12 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0 13 | 14 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers. 15 | The timer counters can be configured to count up or down and support automatic reload and software reload. 16 | They can also generate alarms when they reach a specific value, defined by the software. 17 | The value of the counter can be read by the software program. 18 | 19 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 20 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 21 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 22 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 23 | This important feature is absolutely necessary for mission-critical tasks. 24 | 25 | Based on SimpleTimer - A timer library for Arduino. 26 | Author: mromani@ottotecnica.com 27 | Copyright (c) 2010 OTTOTECNICA Italy 28 | 29 | Based on BlynkTimer.h 30 | Author: Volodymyr Shymanskyy 31 | 32 | Version: 2.3.0 33 | 34 | Version Modified By Date Comments 35 | ------- ----------- ---------- ----------- 36 | 1.0.0 K Hoang 23/11/2019 Initial coding 37 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library 38 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 39 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README. 40 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 41 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 42 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 43 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2 44 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 45 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1 46 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 47 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library. 48 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 49 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards 50 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 51 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 52 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 53 | *****************************************************************************************************************************/ 54 | 55 | #pragma once 56 | 57 | #ifndef ESP32TIMERINTERRUPT_H 58 | #define ESP32TIMERINTERRUPT_H 59 | 60 | #include "ESP32TimerInterrupt.hpp" 61 | #include "ESP32_ISR_Timer.h" 62 | 63 | #endif // ESP32TIMERINTERRUPT_H 64 | 65 | -------------------------------------------------------------------------------- /src/ESP32TimerInterrupt.hpp: -------------------------------------------------------------------------------- 1 | 2 | /**************************************************************************************************************************** 3 | ESP32TimerInterrupt.hpp 4 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+ 5 | Written by Khoi Hoang 6 | 7 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt 8 | Licensed under MIT license 9 | 10 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1 11 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1 12 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0 13 | 14 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers. 15 | The timer counters can be configured to count up or down and support automatic reload and software reload. 16 | They can also generate alarms when they reach a specific value, defined by the software. 17 | The value of the counter can be read by the software program. 18 | 19 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 20 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 21 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 22 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 23 | This important feature is absolutely necessary for mission-critical tasks. 24 | 25 | Based on SimpleTimer - A timer library for Arduino. 26 | Author: mromani@ottotecnica.com 27 | Copyright (c) 2010 OTTOTECNICA Italy 28 | 29 | Based on BlynkTimer.h 30 | Author: Volodymyr Shymanskyy 31 | 32 | Version: 2.3.0 33 | 34 | Version Modified By Date Comments 35 | ------- ----------- ---------- ----------- 36 | 1.0.0 K Hoang 23/11/2019 Initial coding 37 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library 38 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 39 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README. 40 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 41 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 42 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 43 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2 44 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 45 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1 46 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 47 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library. 48 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 49 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards 50 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 51 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 52 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 53 | *****************************************************************************************************************************/ 54 | 55 | #pragma once 56 | 57 | #ifndef ESP32TIMERINTERRUPT_HPP 58 | #define ESP32TIMERINTERRUPT_HPP 59 | 60 | #if ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \ 61 | ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \ 62 | ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM || ARDUINO_ADAFRUIT_QTPY_ESP32S2 || ARDUINO_ESP32S2_USB || \ 63 | ARDUINO_FEATHERS2NEO || ARDUINO_TINYS2 || ARDUINO_RMP || ARDUINO_LOLIN_S2_MINI || ARDUINO_LOLIN_S2_PICO || \ 64 | ARDUINO_ADAFRUIT_FEATHER_ESP32S2 || ARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT || ARDUINO_atmegazero_esp32s2 || \ 65 | ARDUINO_DYM || ARDUINO_FRANZININHO_WIFI || ARDUINO_FRANZININHO_WIFI_MSC ) 66 | #define USING_ESP32_S2_TIMERINTERRUPT true 67 | 68 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3) 69 | #warning USING_ESP32_S2_TIMERINTERRUPT 70 | #endif 71 | #elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \ 72 | defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_NOPSRAM) || \ 73 | defined(ARDUINO_ADAFRUIT_QTPY_ESP32S3_NOPSRAM) || defined(ARDUINO_ESP32S3_CAM_LCD) || \ 74 | defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3) || defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S3_TFT) || \ 75 | defined(ARDUINO_ESP32_S3_USB_OTG) ) 76 | #define USING_ESP32_S3_TIMERINTERRUPT true 77 | 78 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3) 79 | #warning USING_ESP32_S3_TIMERINTERRUPT 80 | #endif 81 | #elif ( defined(ARDUINO_ESP32C3_DEV) || defined(ARDUINO_LOLIN_C3_MINI) || defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) || \ 82 | defined(ARDUINO_AirM2M_CORE_ESP32C3) || defined(ARDUINO_XIAO_ESP32C3) ) 83 | #define USING_ESP32_C3_TIMERINTERRUPT true 84 | 85 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3) 86 | #warning USING_ESP32_C3_TIMERINTERRUPT 87 | #endif 88 | #elif defined(ESP32) 89 | #define USING_ESP32_TIMERINTERRUPT true 90 | 91 | #if (_TIMERINTERRUPT_LOGLEVEL_ > 3) 92 | #warning USING_ESP32_TIMERINTERRUPT 93 | #endif 94 | #else 95 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 96 | #endif 97 | 98 | #ifndef ESP32_TIMER_INTERRUPT_VERSION 99 | #define ESP32_TIMER_INTERRUPT_VERSION "ESP32TimerInterrupt v2.3.0" 100 | 101 | #define ESP32_TIMER_INTERRUPT_VERSION_MAJOR 2 102 | #define ESP32_TIMER_INTERRUPT_VERSION_MINOR 3 103 | #define ESP32_TIMER_INTERRUPT_VERSION_PATCH 0 104 | 105 | #define ESP32_TIMER_INTERRUPT_VERSION_INT 2003000 106 | #endif 107 | 108 | #ifndef TIMER_INTERRUPT_DEBUG 109 | #define TIMER_INTERRUPT_DEBUG 0 110 | #endif 111 | 112 | #if defined(ARDUINO) 113 | #if ARDUINO >= 100 114 | #include 115 | #else 116 | #include 117 | #endif 118 | #endif 119 | 120 | #include "TimerInterrupt_Generic_Debug.h" 121 | 122 | #include 123 | 124 | /* 125 | //ESP32 core v1.0.6, hw_timer_t defined in esp32/tools/sdk/include/driver/driver/timer.h: 126 | 127 | #define TIMER_BASE_CLK (APB_CLK_FREQ) //Frequency of the clock on the input of the timer groups 128 | 129 | 130 | //@brief Selects a Timer-Group out of 2 available groups 131 | 132 | typedef enum 133 | { 134 | TIMER_GROUP_0 = 0, // Hw timer group 0 135 | TIMER_GROUP_1 = 1, // Hw timer group 1 136 | TIMER_GROUP_MAX, 137 | } timer_group_t; 138 | 139 | 140 | //@brief Select a hardware timer from timer groups 141 | 142 | typedef enum 143 | { 144 | TIMER_0 = 0, // Select timer0 of GROUPx 145 | TIMER_1 = 1, // Select timer1 of GROUPx 146 | TIMER_MAX, 147 | } timer_idx_t; 148 | 149 | 150 | //@brief Decides the direction of counter 151 | 152 | typedef enum 153 | { 154 | TIMER_COUNT_DOWN = 0, //Descending Count from cnt.high|cnt.low 155 | TIMER_COUNT_UP = 1, //Ascending Count from Zero 156 | TIMER_COUNT_MAX 157 | } timer_count_dir_t; 158 | 159 | 160 | //@brief Decides whether timer is on or paused 161 | 162 | typedef enum 163 | { 164 | TIMER_PAUSE = 0, //Pause timer counter 165 | TIMER_START = 1, //Start timer counter 166 | } timer_start_t; 167 | 168 | 169 | //@brief Decides whether to enable alarm mode 170 | 171 | typedef enum 172 | { 173 | TIMER_ALARM_DIS = 0, //Disable timer alarm 174 | TIMER_ALARM_EN = 1, //Enable timer alarm 175 | TIMER_ALARM_MAX 176 | } timer_alarm_t; 177 | 178 | 179 | //@brief Select interrupt type if running in alarm mode. 180 | 181 | typedef enum 182 | { 183 | TIMER_INTR_LEVEL = 0, //Interrupt mode: level mode 184 | //TIMER_INTR_EDGE = 1, //Interrupt mode: edge mode, Not supported Now 185 | TIMER_INTR_MAX 186 | } timer_intr_mode_t; 187 | 188 | 189 | //@brief Select if Alarm needs to be loaded by software or automatically reload by hardware. 190 | 191 | typedef enum 192 | { 193 | TIMER_AUTORELOAD_DIS = 0, //Disable auto-reload: hardware will not load counter value after an alarm event 194 | TIMER_AUTORELOAD_EN = 1, //Enable auto-reload: hardware will load counter value after an alarm event 195 | TIMER_AUTORELOAD_MAX, 196 | } timer_autoreload_t; 197 | 198 | 199 | //@brief Data structure with timer's configuration settings 200 | 201 | typedef struct 202 | { 203 | bool alarm_en; //Timer alarm enable 204 | bool counter_en; //Counter enable 205 | timer_intr_mode_t intr_type; //Interrupt mode 206 | timer_count_dir_t counter_dir; //Counter direction 207 | bool auto_reload; //Timer auto-reload 208 | uint32_t divider; //Counter clock divider. The divider's range is from from 2 to 65536. 209 | } timer_config_t; 210 | 211 | */ 212 | 213 | /* 214 | //ESP32 core v2.0.4, timer_config_t defined in tools/sdk/esp32/include/hal/include/hal/timer_types.h: 215 | #if SOC_TIMER_GROUP_SUPPORT_XTAL 216 | typedef enum { 217 | TIMER_SRC_CLK_APB = 0, // Select APB as the source clock 218 | TIMER_SRC_CLK_XTAL = 1, // Select XTAL as the source clock 219 | } timer_src_clk_t; 220 | #endif 221 | typedef struct { 222 | timer_alarm_t alarm_en; // Timer alarm enable 223 | timer_start_t counter_en; // Counter enable 224 | timer_intr_mode_t intr_type; // Interrupt mode 225 | timer_count_dir_t counter_dir; // Counter direction 226 | timer_autoreload_t auto_reload; // Timer auto-reload 227 | uint32_t divider; // Counter clock divider. The divider's range is from from 2 to 65536 228 | #if SOC_TIMER_GROUP_SUPPORT_XTAL 229 | timer_src_clk_t clk_src; // Use XTAL as source clock 230 | #endif 231 | } timer_config_t; 232 | 233 | */ 234 | 235 | class ESP32TimerInterrupt; 236 | 237 | typedef ESP32TimerInterrupt ESP32Timer; 238 | 239 | #if USING_ESP32_C3_TIMERINTERRUPT 240 | #define MAX_ESP32_NUM_TIMERS 2 241 | #else 242 | #define MAX_ESP32_NUM_TIMERS 4 243 | #endif 244 | 245 | #define TIMER_DIVIDER 80 // Hardware timer clock divider 246 | // TIMER_BASE_CLK = APB_CLK_FREQ = Frequency of the clock on the input of the timer groups 247 | #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds 248 | 249 | 250 | // In esp32/1.0.6/tools/sdk/esp32s2/include/driver/include/driver/timer.h 251 | // typedef bool (*timer_isr_t)(void *); 252 | //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); 253 | //esp_err_t timer_isr_callback_remove(timer_group_t group_num, timer_idx_t timer_num); 254 | //timer_deinit(timer_group_t group_num, timer_idx_t timer_num); 255 | //esp_err_t timer_group_intr_enable(timer_group_t group_num, timer_intr_t intr_mask); 256 | //esp_err_t timer_group_intr_disable(timer_group_t group_num, timer_intr_t intr_mask); 257 | 258 | typedef bool (*esp32_timer_callback) (void *); 259 | 260 | // For ESP32_C3, TIMER_MAX == 1 261 | // For ESP32 and ESP32_S2, TIMER_MAX == 2 262 | 263 | typedef struct 264 | { 265 | timer_idx_t timer_idx; 266 | timer_group_t timer_group; 267 | //int alarm_interval; 268 | //timer_autoreload_t auto_reload; 269 | } timer_info_t; 270 | 271 | // Warning: TIMER_SRC_CLK_XTAL only good for ESP32 272 | // Use TIMER_SRC_CLK_APB for ESP32_C3, ESP32_S2 and ESP32_S3 273 | 274 | class ESP32TimerInterrupt 275 | { 276 | private: 277 | 278 | timer_config_t stdConfig = 279 | { 280 | .alarm_en = TIMER_ALARM_EN, //enable timer alarm 281 | .counter_en = TIMER_START, //starts counting counter once timer_init called 282 | .intr_type = TIMER_INTR_MAX, 283 | .counter_dir = TIMER_COUNT_UP, //counts from 0 to counter value 284 | .auto_reload = TIMER_AUTORELOAD_EN, //reloads counter automatically 285 | .divider = TIMER_DIVIDER, 286 | #if (SOC_TIMER_GROUP_SUPPORT_XTAL) 287 | #if (USING_ESP32_TIMERINTERRUPT) 288 | .clk_src = TIMER_SRC_CLK_XTAL //Use XTAL as source clock 289 | #else 290 | .clk_src = TIMER_SRC_CLK_APB //Use APB as source clock 291 | #endif 292 | #endif 293 | }; 294 | 295 | timer_idx_t _timerIndex; 296 | timer_group_t _timerGroup; 297 | uint32_t interruptFlag; // either TIMER_INTR_T0 or TIMER_INTR_T1 298 | 299 | uint8_t _timerNo; 300 | 301 | esp32_timer_callback _callback; // pointer to the callback function 302 | float _frequency; // Timer frequency 303 | uint64_t _timerCount; // count to activate timer 304 | 305 | //xQueueHandle s_timer_queue; 306 | 307 | public: 308 | 309 | ESP32TimerInterrupt(const uint8_t& timerNo) 310 | { 311 | _callback = NULL; 312 | 313 | if (timerNo < MAX_ESP32_NUM_TIMERS) 314 | { 315 | _timerNo = timerNo; 316 | 317 | #if USING_ESP32_C3_TIMERINTERRUPT 318 | 319 | // Always using TIMER_INTR_T0 320 | _timerIndex = (timer_idx_t) ( (uint32_t) 0 ); 321 | 322 | // timerNo == 0 => Group 0, timerNo == 1 => Group 1 323 | _timerGroup = (timer_group_t) ( (uint32_t) timerNo); 324 | 325 | #else 326 | 327 | _timerIndex = (timer_idx_t) (_timerNo % TIMER_MAX); 328 | 329 | _timerGroup = (timer_group_t) (_timerNo / TIMER_MAX); 330 | 331 | #endif 332 | } 333 | else 334 | { 335 | _timerNo = MAX_ESP32_NUM_TIMERS; 336 | } 337 | }; 338 | 339 | // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 340 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c 341 | bool setFrequency(const float& frequency, const esp32_timer_callback& callback) 342 | { 343 | if (_timerNo < MAX_ESP32_NUM_TIMERS) 344 | { 345 | // select timer frequency is 1MHz for better accuracy. We don't use 16-bit prescaler for now. 346 | // Will use later if very low frequency is needed. 347 | _frequency = TIMER_BASE_CLK / TIMER_DIVIDER; //1000000; 348 | _timerCount = (uint64_t) _frequency / frequency; 349 | // count up 350 | 351 | #if USING_ESP32_S2_TIMERINTERRUPT 352 | TISR_LOGWARN3(F("ESP32_S2_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 353 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 354 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 355 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount)); 356 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 357 | #elif USING_ESP32_S3_TIMERINTERRUPT 358 | // ESP32-S3 is embedded with four 54-bit general-purpose timers, which are based on 16-bit prescalers 359 | // and 54-bit auto-reload-capable up/down-timers 360 | TISR_LOGWARN3(F("ESP32_S3_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 361 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 362 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 363 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount)); 364 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 365 | #else 366 | TISR_LOGWARN3(F("ESP32_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER); 367 | TISR_LOGWARN3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER); 368 | TISR_LOGWARN3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup); 369 | TISR_LOGWARN3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount)); 370 | TISR_LOGWARN1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency); 371 | #endif 372 | 373 | timer_init(_timerGroup, _timerIndex, &stdConfig); 374 | 375 | // Counter value to 0 => counting up to alarm value as .counter_dir == TIMER_COUNT_UP 376 | timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL); 377 | 378 | timer_set_alarm_value(_timerGroup, _timerIndex, TIMER_SCALE / frequency); 379 | 380 | // enable interrupts for _timerGroup, _timerIndex 381 | timer_enable_intr(_timerGroup, _timerIndex); 382 | 383 | _callback = callback; 384 | 385 | // Register the ISR handler 386 | // If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set, the handler function must be declared with IRAM_ATTR attribute 387 | // and can only call functions in IRAM or ROM. It cannot call other timer APIs. 388 | //timer_isr_register(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, ESP_INTR_FLAG_IRAM, NULL); 389 | timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0); 390 | 391 | timer_start(_timerGroup, _timerIndex); 392 | 393 | return true; 394 | } 395 | else 396 | { 397 | #if USING_ESP32_C3_TIMERINTERRUPT 398 | TISR_LOGERROR(F("Error. Timer must be 0-1")); 399 | #else 400 | TISR_LOGERROR(F("Error. Timer must be 0-3")); 401 | #endif 402 | 403 | return false; 404 | } 405 | } 406 | 407 | // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 408 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c 409 | bool setInterval(const unsigned long& interval, const esp32_timer_callback& callback) 410 | { 411 | return setFrequency((float) (1000000.0f / interval), callback); 412 | } 413 | 414 | bool attachInterrupt(const float& frequency, const esp32_timer_callback& callback) 415 | { 416 | return setFrequency(frequency, callback); 417 | } 418 | 419 | // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely 420 | // No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c 421 | bool attachInterruptInterval(const unsigned long& interval, const esp32_timer_callback& callback) 422 | { 423 | return setFrequency( (float) ( 1000000.0f / interval), callback); 424 | } 425 | 426 | void detachInterrupt() 427 | { 428 | #if USING_ESP32_C3_TIMERINTERRUPT 429 | timer_group_intr_disable(_timerGroup, TIMER_INTR_T0); 430 | #else 431 | timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 432 | #endif 433 | } 434 | 435 | void disableTimer() 436 | { 437 | #if USING_ESP32_C3_TIMERINTERRUPT 438 | timer_group_intr_disable(_timerGroup, TIMER_INTR_T0); 439 | #else 440 | timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 441 | #endif 442 | } 443 | 444 | // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely 445 | void reattachInterrupt() 446 | { 447 | #if USING_ESP32_C3_TIMERINTERRUPT 448 | timer_group_intr_enable(_timerGroup, TIMER_INTR_T0); 449 | #else 450 | timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 451 | #endif 452 | } 453 | 454 | // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely 455 | void enableTimer() 456 | { 457 | #if USING_ESP32_C3_TIMERINTERRUPT 458 | timer_group_intr_enable(_timerGroup, TIMER_INTR_T0); 459 | #else 460 | timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1); 461 | #endif 462 | } 463 | 464 | // Just stop clock source, clear the count 465 | void stopTimer() 466 | { 467 | timer_pause(_timerGroup, _timerIndex); 468 | } 469 | 470 | // Just reconnect clock source, start current count from 0 471 | void restartTimer() 472 | { 473 | timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL); 474 | timer_start(_timerGroup, _timerIndex); 475 | } 476 | 477 | int8_t getTimer() __attribute__((always_inline)) 478 | { 479 | return _timerIndex; 480 | }; 481 | 482 | int8_t getTimerGroup() __attribute__((always_inline)) 483 | { 484 | return _timerGroup; 485 | }; 486 | 487 | }; // class ESP32TimerInterrupt 488 | 489 | #include "ESP32_ISR_Timer.hpp" 490 | 491 | #endif // ESP32TIMERINTERRUPT_HPP 492 | 493 | -------------------------------------------------------------------------------- /src/ESP32_ISR_Timer-Impl.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_ISR_Timer-Impl.h 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1 10 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1 11 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0 12 | 13 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers. 14 | The timer counters can be configured to count up or down and support automatic reload and software reload. 15 | They can also generate alarms when they reach a specific value, defined by the software. 16 | The value of the counter can be read by the software program. 17 | 18 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 19 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 20 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 21 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 22 | This important feature is absolutely necessary for mission-critical tasks. 23 | 24 | Based on SimpleTimer - A timer library for Arduino. 25 | Author: mromani@ottotecnica.com 26 | Copyright (c) 2010 OTTOTECNICA Italy 27 | 28 | Based on BlynkTimer.h 29 | Author: Volodymyr Shymanskyy 30 | 31 | Version: 2.3.0 32 | 33 | Version Modified By Date Comments 34 | ------- ----------- ---------- ----------- 35 | 1.0.0 K Hoang 23/11/2019 Initial coding 36 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library 37 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 38 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README. 39 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 40 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 41 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 42 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2 43 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 44 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1 45 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 46 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library. 47 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 48 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards 49 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 50 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 51 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 52 | *****************************************************************************************************************************/ 53 | 54 | #pragma once 55 | 56 | #ifndef ISR_TIMER_GENERIC_IMPL_H 57 | #define ISR_TIMER_GENERIC_IMPL_H 58 | 59 | #include 60 | 61 | ESP32_ISR_Timer::ESP32_ISR_Timer() 62 | : numTimers (-1) 63 | { 64 | } 65 | 66 | void ESP32_ISR_Timer::init() 67 | { 68 | unsigned long current_millis = millis(); //elapsed(); 69 | 70 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 71 | { 72 | memset((void*) &timer[i], 0, sizeof (timer_t)); 73 | timer[i].prev_millis = current_millis; 74 | } 75 | 76 | numTimers = 0; 77 | 78 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 79 | timerMux = portMUX_INITIALIZER_UNLOCKED; 80 | } 81 | 82 | void IRAM_ATTR ESP32_ISR_Timer::run() 83 | { 84 | uint8_t i; 85 | unsigned long current_millis; 86 | 87 | // get current time 88 | current_millis = millis(); //elapsed(); 89 | 90 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 91 | portENTER_CRITICAL_ISR(&timerMux); 92 | 93 | for (i = 0; i < MAX_NUMBER_TIMERS; i++) 94 | { 95 | 96 | timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN; 97 | 98 | // no callback == no timer, i.e. jump over empty slots 99 | if (timer[i].callback != NULL) 100 | { 101 | 102 | // is it time to process this timer ? 103 | // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592 104 | 105 | if ((current_millis - timer[i].prev_millis) >= timer[i].delay) 106 | { 107 | unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay; 108 | 109 | // update time 110 | timer[i].prev_millis += timer[i].delay * skipTimes; 111 | 112 | // check if the timer callback has to be executed 113 | if (timer[i].enabled) 114 | { 115 | 116 | // "run forever" timers must always be executed 117 | if (timer[i].maxNumRuns == TIMER_RUN_FOREVER) 118 | { 119 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; 120 | } 121 | // other timers get executed the specified number of times 122 | else if (timer[i].numRuns < timer[i].maxNumRuns) 123 | { 124 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; 125 | timer[i].numRuns++; 126 | 127 | // after the last run, delete the timer 128 | if (timer[i].numRuns >= timer[i].maxNumRuns) 129 | { 130 | timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL; 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | for (i = 0; i < MAX_NUMBER_TIMERS; i++) 139 | { 140 | if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN) 141 | continue; 142 | 143 | if (timer[i].hasParam) 144 | (*(timer_callback_p)timer[i].callback)(timer[i].param); 145 | else 146 | (*(timer_callback)timer[i].callback)(); 147 | 148 | if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL) 149 | deleteTimer(i); 150 | } 151 | 152 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 153 | portEXIT_CRITICAL_ISR(&timerMux); 154 | 155 | } 156 | 157 | 158 | // find the first available slot 159 | // return -1 if none found 160 | int8_t ESP32_ISR_Timer::findFirstFreeSlot() 161 | { 162 | // all slots are used 163 | if (numTimers >= MAX_NUMBER_TIMERS) 164 | { 165 | return -1; 166 | } 167 | 168 | // return the first slot with no callback (i.e. free) 169 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 170 | { 171 | if (timer[i].callback == NULL) 172 | { 173 | return i; 174 | } 175 | } 176 | 177 | // no free slots found 178 | return -1; 179 | } 180 | 181 | 182 | int8_t ESP32_ISR_Timer::setupTimer(const unsigned long& delay, void* callback, void* param, bool hasParam, 183 | const uint32_t& numRuns) 184 | { 185 | int freeTimer; 186 | 187 | if (numTimers < 0) 188 | { 189 | init(); 190 | } 191 | 192 | freeTimer = findFirstFreeSlot(); 193 | 194 | if (freeTimer < 0) 195 | { 196 | return -1; 197 | } 198 | 199 | if (callback == NULL) 200 | { 201 | return -1; 202 | } 203 | 204 | timer[freeTimer].delay = delay; 205 | timer[freeTimer].callback = callback; 206 | timer[freeTimer].param = param; 207 | timer[freeTimer].hasParam = hasParam; 208 | timer[freeTimer].maxNumRuns = numRuns; 209 | timer[freeTimer].enabled = true; 210 | timer[freeTimer].prev_millis = millis(); 211 | 212 | numTimers++; 213 | 214 | return freeTimer; 215 | } 216 | 217 | 218 | int ESP32_ISR_Timer::setTimer(const unsigned long& delay, const timer_callback& callback, const uint32_t& numRuns) 219 | { 220 | return setupTimer(delay, (void *)callback, NULL, false, numRuns); 221 | } 222 | 223 | int ESP32_ISR_Timer::setTimer(const unsigned long& delay, const timer_callback_p& callback, void* param, 224 | const uint32_t& numRuns) 225 | { 226 | return setupTimer(delay, (void *)callback, param, true, numRuns); 227 | } 228 | 229 | int ESP32_ISR_Timer::setInterval(const unsigned long& delay, const timer_callback& callback) 230 | { 231 | return setupTimer(delay, (void *)callback, NULL, false, TIMER_RUN_FOREVER); 232 | } 233 | 234 | int ESP32_ISR_Timer::setInterval(const unsigned long& delay, const timer_callback_p& callback, void* param) 235 | { 236 | return setupTimer(delay, (void *)callback, param, true, TIMER_RUN_FOREVER); 237 | } 238 | 239 | int ESP32_ISR_Timer::setTimeout(const unsigned long& delay, const timer_callback& callback) 240 | { 241 | return setupTimer(delay, (void *)callback, NULL, false, TIMER_RUN_ONCE); 242 | } 243 | 244 | int ESP32_ISR_Timer::setTimeout(const unsigned long& delay, const timer_callback_p& callback, void* param) 245 | { 246 | return setupTimer(delay, (void *)callback, param, true, TIMER_RUN_ONCE); 247 | } 248 | 249 | bool IRAM_ATTR ESP32_ISR_Timer::changeInterval(const uint8_t& numTimer, const unsigned long& delay) 250 | { 251 | if (numTimer >= MAX_NUMBER_TIMERS) 252 | { 253 | return false; 254 | } 255 | 256 | // Updates interval of existing specified timer 257 | if (timer[numTimer].callback != NULL) 258 | { 259 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 260 | portENTER_CRITICAL(&timerMux); 261 | 262 | timer[numTimer].delay = delay; 263 | timer[numTimer].prev_millis = millis(); 264 | 265 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 266 | portEXIT_CRITICAL(&timerMux); 267 | 268 | return true; 269 | } 270 | 271 | // false return for non-used numTimer, no callback 272 | return false; 273 | } 274 | 275 | void ESP32_ISR_Timer::deleteTimer(const uint8_t& timerId) 276 | { 277 | // nothing to delete if no timers are in use 278 | if ( (timerId >= MAX_NUMBER_TIMERS) || (numTimers == 0) ) 279 | { 280 | return; 281 | } 282 | 283 | // don't decrease the number of timers if the specified slot is already empty 284 | if (timer[timerId].callback != NULL) 285 | { 286 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 287 | portENTER_CRITICAL(&timerMux); 288 | 289 | memset((void*) &timer[timerId], 0, sizeof (timer_t)); 290 | timer[timerId].prev_millis = millis(); 291 | 292 | // update number of timers 293 | numTimers--; 294 | 295 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 296 | portEXIT_CRITICAL(&timerMux); 297 | 298 | } 299 | } 300 | 301 | // function contributed by code@rowansimms.com 302 | void ESP32_ISR_Timer::restartTimer(const uint8_t& numTimer) 303 | { 304 | if (numTimer >= MAX_NUMBER_TIMERS) 305 | { 306 | return; 307 | } 308 | 309 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 310 | portENTER_CRITICAL(&timerMux); 311 | 312 | timer[numTimer].prev_millis = millis(); 313 | 314 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 315 | portEXIT_CRITICAL(&timerMux); 316 | } 317 | 318 | 319 | bool ESP32_ISR_Timer::isEnabled(const uint8_t& numTimer) 320 | { 321 | if (numTimer >= MAX_NUMBER_TIMERS) 322 | { 323 | return false; 324 | } 325 | 326 | return timer[numTimer].enabled; 327 | } 328 | 329 | 330 | void ESP32_ISR_Timer::enable(const uint8_t& numTimer) 331 | { 332 | if (numTimer >= MAX_NUMBER_TIMERS) 333 | { 334 | return; 335 | } 336 | 337 | timer[numTimer].enabled = true; 338 | } 339 | 340 | 341 | void ESP32_ISR_Timer::disable(const uint8_t& numTimer) 342 | { 343 | if (numTimer >= MAX_NUMBER_TIMERS) 344 | { 345 | return; 346 | } 347 | 348 | timer[numTimer].enabled = false; 349 | } 350 | 351 | void ESP32_ISR_Timer::enableAll() 352 | { 353 | // Enable all timers with a callback assigned (used) 354 | 355 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 356 | portENTER_CRITICAL(&timerMux); 357 | 358 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 359 | { 360 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) 361 | { 362 | timer[i].enabled = true; 363 | } 364 | } 365 | 366 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 367 | portEXIT_CRITICAL(&timerMux); 368 | } 369 | 370 | void ESP32_ISR_Timer::disableAll() 371 | { 372 | // Disable all timers with a callback assigned (used) 373 | 374 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 375 | portENTER_CRITICAL(&timerMux); 376 | 377 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 378 | { 379 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) 380 | { 381 | timer[i].enabled = false; 382 | } 383 | } 384 | 385 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during modifying shared vars 386 | portEXIT_CRITICAL(&timerMux); 387 | 388 | } 389 | 390 | void ESP32_ISR_Timer::toggle(const uint8_t& numTimer) 391 | { 392 | if (numTimer >= MAX_NUMBER_TIMERS) 393 | { 394 | return; 395 | } 396 | 397 | timer[numTimer].enabled = !timer[numTimer].enabled; 398 | } 399 | 400 | 401 | int8_t ESP32_ISR_Timer::getNumTimers() 402 | { 403 | return numTimers; 404 | } 405 | 406 | #endif // ISR_TIMER_GENERIC_IMPL_H 407 | 408 | -------------------------------------------------------------------------------- /src/ESP32_ISR_Timer.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_ISR_Timer.h 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1 10 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1 11 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0 12 | 13 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers. 14 | The timer counters can be configured to count up or down and support automatic reload and software reload. 15 | They can also generate alarms when they reach a specific value, defined by the software. 16 | The value of the counter can be read by the software program. 17 | 18 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 19 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 20 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 21 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 22 | This important feature is absolutely necessary for mission-critical tasks. 23 | 24 | Based on SimpleTimer - A timer library for Arduino. 25 | Author: mromani@ottotecnica.com 26 | Copyright (c) 2010 OTTOTECNICA Italy 27 | 28 | Based on BlynkTimer.h 29 | Author: Volodymyr Shymanskyy 30 | 31 | Version: 2.3.0 32 | 33 | Version Modified By Date Comments 34 | ------- ----------- ---------- ----------- 35 | 1.0.0 K Hoang 23/11/2019 Initial coding 36 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library 37 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 38 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README. 39 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 40 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 41 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 42 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2 43 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 44 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1 45 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 46 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library. 47 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 48 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards 49 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 50 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 51 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 52 | *****************************************************************************************************************************/ 53 | 54 | #pragma once 55 | 56 | #ifndef ISR_TIMER_GENERIC_H 57 | #define ISR_TIMER_GENERIC_H 58 | 59 | #include "ESP32_ISR_Timer.hpp" 60 | #include "ESP32_ISR_Timer-Impl.h" 61 | 62 | 63 | #endif // ISR_TIMER_GENERIC_H 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/ESP32_ISR_Timer.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP32_ISR_Timer.hpp 3 | For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+ 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1 10 | 1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1 11 | 2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0 12 | 13 | All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers. 14 | The timer counters can be configured to count up or down and support automatic reload and software reload. 15 | They can also generate alarms when they reach a specific value, defined by the software. 16 | The value of the counter can be read by the software program. 17 | 18 | Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by 19 | unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks. 20 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 21 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 22 | This important feature is absolutely necessary for mission-critical tasks. 23 | 24 | Based on SimpleTimer - A timer library for Arduino. 25 | Author: mromani@ottotecnica.com 26 | Copyright (c) 2010 OTTOTECNICA Italy 27 | 28 | Based on BlynkTimer.h 29 | Author: Volodymyr Shymanskyy 30 | 31 | Version: 2.3.0 32 | 33 | Version Modified By Date Comments 34 | ------- ----------- ---------- ----------- 35 | 1.0.0 K Hoang 23/11/2019 Initial coding 36 | 1.0.1 K Hoang 27/11/2019 No v1.0.1. Bump up to 1.0.2 to match ESP8266_ISR_TimerInterupt library 37 | 1.0.2 K.Hoang 03/12/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 38 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Add examples. Enhance README. 39 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 40 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 41 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 42 | 1.3.0 K.Hoang 06/05/2021 Add support to ESP32-S2 43 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 44 | 1.4.1 K.Hoang 14/11/2021 Avoid using D1 in examples due to issue with core v2.0.0 and v2.0.1 45 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error 46 | 2.0.0 K Hoang 13/02/2022 Add support to new ESP32-S3. Restructure library. 47 | 2.0.1 K Hoang 13/03/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 48 | 2.0.2 K Hoang 16/06/2022 Add support to new Adafruit boards 49 | 2.1.0 K Hoang 03/08/2022 Suppress errors and warnings for new ESP32 core 50 | 2.2.0 K Hoang 11/08/2022 Add support and suppress warnings for ESP32_C3, ESP32_S2 and ESP32_S3 boards 51 | 2.3.0 K Hoang 16/11/2022 Fix doubled time for ESP32_C3, ESP32_S2 and ESP32_S3 52 | *****************************************************************************************************************************/ 53 | 54 | #pragma once 55 | 56 | #ifndef ISR_TIMER_GENERIC_HPP 57 | #define ISR_TIMER_GENERIC_HPP 58 | 59 | #if !defined( ESP32 ) 60 | #error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting. 61 | #endif 62 | 63 | #ifndef ESP32_TIMER_INTERRUPT_VERSION 64 | #define ESP32_TIMER_INTERRUPT_VERSION "ESP32TimerInterrupt v2.2.0" 65 | 66 | #define ESP32_TIMER_INTERRUPT_VERSION_MAJOR 2 67 | #define ESP32_TIMER_INTERRUPT_VERSION_MINOR 2 68 | #define ESP32_TIMER_INTERRUPT_VERSION_PATCH 0 69 | 70 | #define ESP32_TIMER_INTERRUPT_VERSION_INT 2002000 71 | #endif 72 | 73 | #include "TimerInterrupt_Generic_Debug.h" 74 | 75 | #define CONFIG_ESP32_APPTRACE_ENABLE 76 | 77 | #if 0 78 | #ifndef configMINIMAL_STACK_SIZE 79 | #define configMINIMAL_STACK_SIZE 2048 80 | #else 81 | #undef configMINIMAL_STACK_SIZE 82 | #define configMINIMAL_STACK_SIZE 2048 83 | #endif 84 | #endif 85 | 86 | #include 87 | 88 | #include 89 | 90 | #if defined(ARDUINO) 91 | #if ARDUINO >= 100 92 | #include 93 | #else 94 | #include 95 | #endif 96 | #endif 97 | 98 | #define ESP32_ISR_Timer ESP32_ISRTimer 99 | 100 | typedef void (*timer_callback)(); 101 | typedef void (*timer_callback_p)(void *); 102 | 103 | class ESP32_ISR_Timer 104 | { 105 | 106 | public: 107 | // maximum number of timers 108 | #define MAX_NUMBER_TIMERS 16 109 | #define TIMER_RUN_FOREVER 0 110 | #define TIMER_RUN_ONCE 1 111 | 112 | // constructor 113 | ESP32_ISR_Timer(); 114 | 115 | void init(); 116 | 117 | // this function must be called inside loop() 118 | void IRAM_ATTR run(); 119 | 120 | // Timer will call function 'callback' every 'delay' milliseconds forever 121 | // returns the timer number (numTimer) on success or 122 | // -1 on failure (callback == NULL) or no free timers 123 | int setInterval(const unsigned long& delay, const timer_callback& callback); 124 | 125 | // Timer will call function 'callback' with parameter 'param' every 'delay' milliseconds forever 126 | // returns the timer number (numTimer) on success or 127 | // -1 on failure (callback == NULL) or no free timers 128 | int setInterval(const unsigned long& delay, const timer_callback_p& callback, void* param); 129 | 130 | // Timer will call function 'callback' after 'delay' milliseconds one time 131 | // returns the timer number (numTimer) on success or 132 | // -1 on failure (callback == NULL) or no free timers 133 | int setTimeout(const unsigned long& delay, const timer_callback& callback); 134 | 135 | // Timer will call function 'callback' with parameter 'param' after 'delay' milliseconds one time 136 | // returns the timer number (numTimer) on success or 137 | // -1 on failure (callback == NULL) or no free timers 138 | int setTimeout(const unsigned long& delay, const timer_callback_p& callback, void* param); 139 | 140 | // Timer will call function 'callback' every 'delay' milliseconds 'numRuns' times 141 | // returns the timer number (numTimer) on success or 142 | // -1 on failure (callback == NULL) or no free timers 143 | int setTimer(const unsigned long& delay, const timer_callback& callback, const uint32_t& numRuns); 144 | 145 | // Timer will call function 'callback' with parameter 'param' every 'delay' milliseconds 'numRuns' times 146 | // returns the timer number (numTimer) on success or 147 | // -1 on failure (callback == NULL) or no free timers 148 | int setTimer(const unsigned long& delay, const timer_callback_p& callback, void* param, const uint32_t& numRuns); 149 | 150 | // updates interval of the specified timer 151 | bool changeInterval(const uint8_t& numTimer, const unsigned long& delay); 152 | 153 | // destroy the specified timer 154 | void deleteTimer(const uint8_t& numTimer); 155 | 156 | // restart the specified timer 157 | void restartTimer(const uint8_t& numTimer); 158 | 159 | // returns true if the specified timer is enabled 160 | bool isEnabled(const uint8_t& numTimer); 161 | 162 | // enables the specified timer 163 | void enable(const uint8_t& numTimer); 164 | 165 | // disables the specified timer 166 | void disable(const uint8_t& numTimer); 167 | 168 | // enables all timers 169 | void enableAll(); 170 | 171 | // disables all timers 172 | void disableAll(); 173 | 174 | // enables the specified timer if it's currently disabled, and vice-versa 175 | void toggle(const uint8_t& numTimer); 176 | 177 | // returns the number of used timers 178 | int8_t getNumTimers(); 179 | 180 | // returns the number of available timers 181 | uint8_t getNumAvailableTimers() __attribute__((always_inline)) 182 | { 183 | if (numTimers <= 0) 184 | return MAX_NUMBER_TIMERS; 185 | else 186 | return MAX_NUMBER_TIMERS - numTimers; 187 | }; 188 | 189 | private: 190 | // deferred call constants 191 | #define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function 192 | #define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer 193 | #define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer 194 | 195 | // low level function to initialize and enable a new timer 196 | // returns the timer number (numTimer) on success or 197 | // -1 on failure (f == NULL) or no free timers 198 | int8_t setupTimer(const unsigned long& delay, void* callback, void* param, bool hasParam, const uint32_t& numRuns) ; 199 | 200 | // find the first available slot 201 | int8_t findFirstFreeSlot(); 202 | 203 | typedef struct 204 | { 205 | unsigned long prev_millis; // value returned by the millis() function in the previous run() call 206 | void* callback; // pointer to the callback function 207 | void* param; // function parameter 208 | bool hasParam; // true if callback takes a parameter 209 | unsigned long delay; // delay value 210 | uint32_t maxNumRuns; // number of runs to be executed 211 | uint32_t numRuns; // number of executed runs 212 | bool enabled; // true if enabled 213 | unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run() 214 | } timer_t; 215 | 216 | volatile timer_t timer[MAX_NUMBER_TIMERS]; 217 | 218 | // actual number of timers in use (-1 means uninitialized) 219 | volatile int8_t numTimers; 220 | 221 | // ESP32 is a multi core / multi processing chip. It is mandatory to disable task switches during ISR 222 | portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; 223 | }; 224 | 225 | #endif // ISR_TIMER_GENERIC_HPP 226 | 227 | 228 | -------------------------------------------------------------------------------- /src/ESP8266TimerInterrupt.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP8266TimerInterrupt.h 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | 19 | Based on SimpleTimer - A timer library for Arduino. 20 | Author: mromani@ottotecnica.com 21 | Copyright (c) 2010 OTTOTECNICA Italy 22 | 23 | Based on BlynkTimer.h 24 | Author: Volodymyr Shymanskyy 25 | 26 | Version: 1.6.0 27 | 28 | Version Modified By Date Comments 29 | ------- ----------- ---------- ----------- 30 | 1.0.0 K Hoang 23/11/2019 Initial coding 31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. 34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md 40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer 41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 42 | *****************************************************************************************************************************/ 43 | 44 | #pragma once 45 | 46 | #ifndef ESP8266TIMERINTERRUPT_H 47 | #define ESP8266TIMERINTERRUPT_H 48 | 49 | #if !defined(ESP8266) 50 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 51 | #endif 52 | 53 | #ifndef ESP8266_TIMER_INTERRUPT_VERSION 54 | #define ESP8266_TIMER_INTERRUPT_VERSION "ESP8266TimerInterrupt v1.6.0" 55 | 56 | 57 | #define ESP8266_TIMER_INTERRUPT_VERSION_MAJOR 1 58 | #define ESP8266_TIMER_INTERRUPT_VERSION_MINOR 6 59 | #define ESP8266_TIMER_INTERRUPT_VERSION_PATCH 0 60 | 61 | #define ESP8266_TIMER_INTERRUPT_VERSION_INT 1006000 62 | 63 | #endif 64 | 65 | #ifndef TIMER_INTERRUPT_DEBUG 66 | #define TIMER_INTERRUPT_DEBUG 0 67 | #endif 68 | 69 | #if defined(ARDUINO) 70 | #if ARDUINO >= 100 71 | #include 72 | #else 73 | #include 74 | #endif 75 | #endif 76 | 77 | #include "TimerInterrupt_Generic_Debug.h" 78 | 79 | /* From /arduino-1.8.10/hardware/esp8266com/esp8266/cores/esp8266/esp8266_peri.h 80 | 81 | #define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr))) 82 | #define ESP8266_DREG(addr) *((volatile uint32_t *)(0x3FF00000+(addr))) 83 | #define ESP8266_CLOCK 80000000UL 84 | 85 | //CPU Register 86 | #define CPU2X ESP8266_DREG(0x14) //when bit 0 is set, F_CPU = 160MHz 87 | */ 88 | 89 | /* From /arduino-1.8.10/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h 90 | 91 | //timer dividers 92 | enum TIM_DIV_ENUM { 93 | TIM_DIV1 = 0, // 80 / 160 MHz (80 / 160 ticks/us - 104857.588 us max) 94 | TIM_DIV16 = 1, // 5 / 10 MHz (5 / 10 ticks/us - 1677721.4 us max) 95 | TIM_DIV256 = 3 // 312.5 / 625 Khz (1 tick = 3.2 / 1.6 us - 26843542.4 us max) 96 | }; 97 | 98 | //timer int_types 99 | #define TIM_EDGE 0 100 | #define TIM_LEVEL 1 101 | //timer reload values 102 | #define TIM_SINGLE 0 //on interrupt routine you need to write a new value to start the timer again 103 | #define TIM_LOOP 1 //on interrupt the counter will start with the same value again 104 | 105 | */ 106 | 107 | // ESP8266 only has one usable timer1, max count is only 8,388,607. So to get longer time, we use max available 256 divider 108 | class ESP8266TimerInterrupt; 109 | 110 | typedef ESP8266TimerInterrupt ESP8266Timer; 111 | 112 | #define MAX_ESP8266_NUM_TIMERS 1 113 | #define MAX_ESP8266_COUNT 8388607 114 | 115 | typedef void (*timer_callback) (); 116 | 117 | #define TIM_DIV1_CLOCK (80000000UL) // 80000000 / 1 = 80.0 MHz 118 | #define TIM_DIV16_CLOCK (5000000UL) // 80000000 / 16 = 5.0 MHz 119 | #define TIM_DIV256_CLOCK (312500UL) // 80000000 / 256 = 312.5 KHz 120 | 121 | #if ( defined(USING_TIM_DIV1) && USING_TIM_DIV1 ) 122 | #warning Using TIM_DIV1_CLOCK for shortest and most accurate timer 123 | #define TIM_CLOCK_FREQ TIM_DIV1_CLOCK 124 | #define TIM_DIV TIM_DIV1 125 | #elif ( defined(USING_TIM_DIV16) && USING_TIM_DIV16 ) 126 | #warning Using TIM_DIV16_CLOCK for medium time and medium accurate timer 127 | #define TIM_CLOCK_FREQ TIM_DIV16_CLOCK 128 | #define TIM_DIV TIM_DIV16 129 | #elif ( defined(USING_TIM_DIV256) && USING_TIM_DIV256 ) 130 | #warning Using TIM_DIV256_CLOCK for longest timer but least accurate 131 | #define TIM_CLOCK_FREQ TIM_DIV256_CLOCK 132 | #define TIM_DIV TIM_DIV256 133 | #else 134 | #warning Default to using TIM_DIV256_CLOCK for longest timer but least accurate 135 | #define TIM_CLOCK_FREQ TIM_DIV256_CLOCK 136 | #define TIM_DIV TIM_DIV256 137 | #endif 138 | 139 | class ESP8266TimerInterrupt 140 | { 141 | private: 142 | timer_callback _callback; // pointer to the callback function 143 | float _frequency; // Timer frequency 144 | uint32_t _timerCount; // count to activate timer 145 | 146 | public: 147 | 148 | ESP8266TimerInterrupt() 149 | { 150 | _frequency = 0; 151 | _timerCount = 0; 152 | _callback = NULL; 153 | }; 154 | 155 | // frequency (in hertz) 156 | bool setFrequency(const float& frequency, const timer_callback& callback) 157 | { 158 | bool isOKFlag = true; 159 | float minFreq = (float) TIM_CLOCK_FREQ / MAX_ESP8266_COUNT; 160 | 161 | // ESP8266 only has one usable timer1, max count is only 8,388,607. So to get longer time, we use max available 256 divider 162 | // Will use later if very low frequency is needed. 163 | 164 | if (frequency < minFreq) 165 | { 166 | TISR_LOGERROR3(F("ESP8266TimerInterrupt: Too long Timer, smallest frequency ="), minFreq, F(" for TIM_CLOCK_FREQ ="), 167 | TIM_CLOCK_FREQ); 168 | 169 | return false; 170 | } 171 | 172 | _frequency = frequency; 173 | _timerCount = (uint32_t) (TIM_CLOCK_FREQ / frequency); 174 | _callback = callback; 175 | 176 | if ( _timerCount > MAX_ESP8266_COUNT) 177 | { 178 | _timerCount = MAX_ESP8266_COUNT; 179 | // Flag error 180 | isOKFlag = false; 181 | } 182 | 183 | // count up 184 | TISR_LOGWARN3(F("ESP8266TimerInterrupt: Timer _fre ="), _frequency, F(", _count ="), _timerCount); 185 | 186 | // Clock to timer (prescaler) is always 80MHz, even F_CPU is 160 MHz 187 | 188 | timer1_attachInterrupt(callback); 189 | 190 | timer1_write(_timerCount); 191 | 192 | // Interrupt on EDGE, autoloop 193 | //timer1_enable(TIM_DIV256, TIM_EDGE, TIM_LOOP); 194 | timer1_enable(TIM_DIV, TIM_EDGE, TIM_LOOP); 195 | 196 | return isOKFlag; 197 | } 198 | 199 | // interval (in microseconds) 200 | bool setInterval(const unsigned long& interval, const timer_callback& callback) 201 | { 202 | return setFrequency((float) (1000000.0f / interval), callback); 203 | } 204 | 205 | bool attachInterrupt(const float& frequency, const timer_callback& callback) 206 | { 207 | return setFrequency(frequency, callback); 208 | } 209 | 210 | // interval (in microseconds) 211 | bool attachInterruptInterval(const unsigned long& interval, const timer_callback& callback) 212 | { 213 | return setFrequency( (float) ( 1000000.0f / interval), callback); 214 | } 215 | 216 | void detachInterrupt() 217 | { 218 | timer1_disable(); 219 | } 220 | 221 | void disableTimer() 222 | { 223 | timer1_disable(); 224 | } 225 | 226 | void reattachInterrupt() 227 | { 228 | if ( (_frequency > 0) && (_timerCount > 0) && (_callback != NULL) ) 229 | setFrequency(_frequency, _callback); 230 | } 231 | 232 | void enableTimer() 233 | { 234 | reattachInterrupt(); 235 | } 236 | 237 | // Just stop clock source, clear the count 238 | void stopTimer() 239 | { 240 | timer1_disable(); 241 | } 242 | 243 | // Just reconnect clock source, start current count from 0 244 | void restartTimer() 245 | { 246 | enableTimer(); 247 | } 248 | }; // class ESP8266TimerInterrupt 249 | 250 | #endif // ESP8266TIMERINTERRUPT_H 251 | -------------------------------------------------------------------------------- /src/ESP8266_ISR_Timer-Impl.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP8266_ISR_Timer-Impl.h 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | 19 | Based on SimpleTimer - A timer library for Arduino. 20 | Author: mromani@ottotecnica.com 21 | Copyright (c) 2010 OTTOTECNICA Italy 22 | 23 | Based on BlynkTimer.h 24 | Author: Volodymyr Shymanskyy 25 | 26 | Version: 1.6.0 27 | 28 | Version Modified By Date Comments 29 | ------- ----------- ---------- ----------- 30 | 1.0.0 K Hoang 23/11/2019 Initial coding 31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. 34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md 40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer 41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 42 | *****************************************************************************************************************************/ 43 | 44 | #pragma once 45 | 46 | #if !defined(ESP8266) 47 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 48 | #endif 49 | 50 | #ifndef ISR_TIMER_GENERIC_IMPL_H 51 | #define ISR_TIMER_GENERIC_IMPL_H 52 | 53 | //#include "ESP8266_ISR_Timer.h" 54 | #include 55 | 56 | // Select time function: 57 | //static inline unsigned long elapsed() { return micros(); } 58 | static inline unsigned long elapsed() 59 | { 60 | return millis(); 61 | } 62 | 63 | 64 | ESP8266_ISR_Timer::ESP8266_ISR_Timer() 65 | : numTimers (-1) 66 | { 67 | } 68 | 69 | void IRAM_ATTR ESP8266_ISR_Timer::init() 70 | { 71 | unsigned long current_millis = millis(); //elapsed(); 72 | 73 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 74 | { 75 | memset((void*) &timer[i], 0, sizeof (timer_t)); 76 | timer[i].prev_millis = current_millis; 77 | } 78 | 79 | numTimers = 0; 80 | } 81 | 82 | 83 | void IRAM_ATTR ESP8266_ISR_Timer::run() 84 | { 85 | uint8_t i; 86 | unsigned long current_millis; 87 | 88 | // get current time 89 | current_millis = millis(); //elapsed(); 90 | 91 | for (i = 0; i < MAX_NUMBER_TIMERS; i++) 92 | { 93 | 94 | timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN; 95 | 96 | // no callback == no timer, i.e. jump over empty slots 97 | if (timer[i].callback != NULL) 98 | { 99 | // is it time to process this timer ? 100 | // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592 101 | 102 | if ((current_millis - timer[i].prev_millis) >= timer[i].delay) 103 | { 104 | unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay; 105 | // update time 106 | timer[i].prev_millis += timer[i].delay * skipTimes; 107 | 108 | // check if the timer callback has to be executed 109 | if (timer[i].enabled) 110 | { 111 | // "run forever" timers must always be executed 112 | if (timer[i].maxNumRuns == TIMER_RUN_FOREVER) 113 | { 114 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; 115 | } 116 | // other timers get executed the specified number of times 117 | else if (timer[i].numRuns < timer[i].maxNumRuns) 118 | { 119 | timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; 120 | timer[i].numRuns++; 121 | 122 | // after the last run, delete the timer 123 | if (timer[i].numRuns >= timer[i].maxNumRuns) 124 | { 125 | timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL; 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | for (i = 0; i < MAX_NUMBER_TIMERS; i++) 134 | { 135 | if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN) 136 | continue; 137 | 138 | if (timer[i].hasParam) 139 | (*(timer_callback_p)timer[i].callback)(timer[i].param); 140 | else 141 | (*(timer_callback)timer[i].callback)(); 142 | 143 | if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL) 144 | deleteTimer(i); 145 | } 146 | } 147 | 148 | 149 | // find the first available slot 150 | // return -1 if none found 151 | int8_t IRAM_ATTR ESP8266_ISR_Timer::findFirstFreeSlot() 152 | { 153 | // all slots are used 154 | if (numTimers >= MAX_NUMBER_TIMERS) 155 | { 156 | return -1; 157 | } 158 | 159 | // return the first slot with no callback (i.e. free) 160 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 161 | { 162 | if (timer[i].callback == NULL) 163 | { 164 | return i; 165 | } 166 | } 167 | 168 | // no free slots found 169 | return -1; 170 | } 171 | 172 | 173 | int8_t IRAM_ATTR ESP8266_ISR_Timer::setupTimer(const unsigned long& d, void* f, void* p, bool h, const unsigned& n) 174 | { 175 | int freeTimer; 176 | 177 | if (numTimers < 0) 178 | { 179 | init(); 180 | } 181 | 182 | freeTimer = findFirstFreeSlot(); 183 | 184 | if (freeTimer < 0) 185 | { 186 | return -1; 187 | } 188 | 189 | if (f == NULL) 190 | { 191 | return -1; 192 | } 193 | 194 | timer[freeTimer].delay = d; 195 | timer[freeTimer].callback = f; 196 | timer[freeTimer].param = p; 197 | timer[freeTimer].hasParam = h; 198 | timer[freeTimer].maxNumRuns = n; 199 | timer[freeTimer].enabled = true; 200 | timer[freeTimer].prev_millis = elapsed(); 201 | 202 | numTimers++; 203 | 204 | return freeTimer; 205 | } 206 | 207 | 208 | int IRAM_ATTR ESP8266_ISR_Timer::setTimer(const unsigned long& d, const timer_callback& f, const unsigned& n) 209 | { 210 | return setupTimer(d, (void *)f, NULL, false, n); 211 | } 212 | 213 | int IRAM_ATTR ESP8266_ISR_Timer::setTimer(const unsigned long& d, const timer_callback_p& f, void* p, const unsigned& n) 214 | { 215 | return setupTimer(d, (void *)f, p, true, n); 216 | } 217 | 218 | int IRAM_ATTR ESP8266_ISR_Timer::setInterval(const unsigned long& d, const timer_callback& f) 219 | { 220 | return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_FOREVER); 221 | } 222 | 223 | int IRAM_ATTR ESP8266_ISR_Timer::setInterval(const unsigned long& d, const timer_callback_p& f, void* p) 224 | { 225 | return setupTimer(d, (void *)f, p, true, TIMER_RUN_FOREVER); 226 | } 227 | 228 | int IRAM_ATTR ESP8266_ISR_Timer::setTimeout(const unsigned long& d, const timer_callback& f) 229 | { 230 | return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_ONCE); 231 | } 232 | 233 | int IRAM_ATTR ESP8266_ISR_Timer::setTimeout(const unsigned long& d, const timer_callback_p& f, void* p) 234 | { 235 | return setupTimer(d, (void *)f, p, true, TIMER_RUN_ONCE); 236 | } 237 | 238 | bool IRAM_ATTR ESP8266_ISR_Timer::changeInterval(const unsigned& numTimer, const unsigned long& d) 239 | { 240 | if (numTimer >= MAX_NUMBER_TIMERS) 241 | { 242 | return false; 243 | } 244 | 245 | // Updates interval of existing specified timer 246 | if (timer[numTimer].callback != NULL) 247 | { 248 | timer[numTimer].delay = d; 249 | timer[numTimer].prev_millis = elapsed(); 250 | return true; 251 | } 252 | 253 | // false return for non-used numTimer, no callback 254 | return false; 255 | } 256 | 257 | void IRAM_ATTR ESP8266_ISR_Timer::deleteTimer(const unsigned& timerId) 258 | { 259 | if (timerId >= MAX_NUMBER_TIMERS) 260 | { 261 | return; 262 | } 263 | 264 | // nothing to delete if no timers are in use 265 | if (numTimers == 0) 266 | { 267 | return; 268 | } 269 | 270 | // don't decrease the number of timers if the 271 | // specified slot is already empty 272 | if (timer[timerId].callback != NULL) 273 | { 274 | memset((void*) &timer[timerId], 0, sizeof (timer_t)); 275 | timer[timerId].prev_millis = elapsed(); 276 | 277 | // update number of timers 278 | numTimers--; 279 | } 280 | } 281 | 282 | 283 | // function contributed by code@rowansimms.com 284 | void IRAM_ATTR ESP8266_ISR_Timer::restartTimer(const unsigned& numTimer) 285 | { 286 | if (numTimer >= MAX_NUMBER_TIMERS) 287 | { 288 | return; 289 | } 290 | 291 | timer[numTimer].prev_millis = elapsed(); 292 | } 293 | 294 | 295 | bool IRAM_ATTR ESP8266_ISR_Timer::isEnabled(const unsigned& numTimer) 296 | { 297 | if (numTimer >= MAX_NUMBER_TIMERS) 298 | { 299 | return false; 300 | } 301 | 302 | return timer[numTimer].enabled; 303 | } 304 | 305 | 306 | void IRAM_ATTR ESP8266_ISR_Timer::enable(const unsigned& numTimer) 307 | { 308 | if (numTimer >= MAX_NUMBER_TIMERS) 309 | { 310 | return; 311 | } 312 | 313 | timer[numTimer].enabled = true; 314 | } 315 | 316 | 317 | void IRAM_ATTR ESP8266_ISR_Timer::disable(const unsigned& numTimer) 318 | { 319 | if (numTimer >= MAX_NUMBER_TIMERS) 320 | { 321 | return; 322 | } 323 | 324 | timer[numTimer].enabled = false; 325 | } 326 | 327 | void IRAM_ATTR ESP8266_ISR_Timer::enableAll() 328 | { 329 | // Enable all timers with a callback assigned (used) 330 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 331 | { 332 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) 333 | { 334 | timer[i].enabled = true; 335 | } 336 | } 337 | } 338 | 339 | void IRAM_ATTR ESP8266_ISR_Timer::disableAll() 340 | { 341 | // Disable all timers with a callback assigned (used) 342 | for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) 343 | { 344 | if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) 345 | { 346 | timer[i].enabled = false; 347 | } 348 | } 349 | } 350 | 351 | void IRAM_ATTR ESP8266_ISR_Timer::toggle(const unsigned& numTimer) 352 | { 353 | if (numTimer >= MAX_NUMBER_TIMERS) 354 | { 355 | return; 356 | } 357 | 358 | timer[numTimer].enabled = !timer[numTimer].enabled; 359 | } 360 | 361 | 362 | int8_t IRAM_ATTR ESP8266_ISR_Timer::getNumTimers() 363 | { 364 | return numTimers; 365 | } 366 | 367 | #endif // ISR_TIMER_GENERIC_IMPL_H 368 | -------------------------------------------------------------------------------- /src/ESP8266_ISR_Timer.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP8266_ISR_Timer.h 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | 19 | Based on SimpleTimer - A timer library for Arduino. 20 | Author: mromani@ottotecnica.com 21 | Copyright (c) 2010 OTTOTECNICA Italy 22 | 23 | Based on BlynkTimer.h 24 | Author: Volodymyr Shymanskyy 25 | 26 | Version: 1.6.0 27 | 28 | Version Modified By Date Comments 29 | ------- ----------- ---------- ----------- 30 | 1.0.0 K Hoang 23/11/2019 Initial coding 31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. 34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md 40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer 41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 42 | *****************************************************************************************************************************/ 43 | 44 | #pragma once 45 | 46 | #ifndef ISR_TIMER_GENERIC_H 47 | #define ISR_TIMER_GENERIC_H 48 | 49 | #include "ESP8266_ISR_Timer.hpp" 50 | #include "ESP8266_ISR_Timer-Impl.h" 51 | 52 | #endif // ISR_TIMER_GENERIC_H 53 | 54 | -------------------------------------------------------------------------------- /src/ESP8266_ISR_Timer.hpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | ESP8266_ISR_Timer.hpp 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | 19 | Based on SimpleTimer - A timer library for Arduino. 20 | Author: mromani@ottotecnica.com 21 | Copyright (c) 2010 OTTOTECNICA Italy 22 | 23 | Based on BlynkTimer.h 24 | Author: Volodymyr Shymanskyy 25 | 26 | Version: 1.6.0 27 | 28 | Version Modified By Date Comments 29 | ------- ----------- ---------- ----------- 30 | 1.0.0 K Hoang 23/11/2019 Initial coding 31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. 34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md 40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer 41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 42 | *****************************************************************************************************************************/ 43 | 44 | #pragma once 45 | 46 | #ifndef ISR_TIMER_GENERIC_HPP 47 | #define ISR_TIMER_GENERIC_HPP 48 | 49 | #if !defined(ESP8266) 50 | #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. 51 | #endif 52 | 53 | #ifndef ESP8266_TIMER_INTERRUPT_VERSION 54 | #define ESP8266_TIMER_INTERRUPT_VERSION "ESP8266TimerInterrupt v1.6.0" 55 | 56 | 57 | #define ESP8266_TIMER_INTERRUPT_VERSION_MAJOR 1 58 | #define ESP8266_TIMER_INTERRUPT_VERSION_MINOR 6 59 | #define ESP8266_TIMER_INTERRUPT_VERSION_PATCH 0 60 | 61 | #define ESP8266_TIMER_INTERRUPT_VERSION_INT 1006000 62 | 63 | #endif 64 | 65 | #include "TimerInterrupt_Generic_Debug.h" 66 | 67 | #include 68 | 69 | #ifdef ESP8266 70 | extern "C" 71 | { 72 | #include "ets_sys.h" 73 | #include "os_type.h" 74 | #include "mem.h" 75 | } 76 | #else 77 | #include 78 | #endif 79 | 80 | #if defined(ARDUINO) 81 | #if ARDUINO >= 100 82 | #include 83 | #else 84 | #include 85 | #endif 86 | #endif 87 | 88 | #define ESP8266_ISR_Timer ISRTimer 89 | 90 | typedef void (*timer_callback)(); 91 | typedef void (*timer_callback_p)(void *); 92 | 93 | class ESP8266_ISR_Timer 94 | { 95 | 96 | public: 97 | 98 | // maximum number of timers 99 | #define MAX_NUMBER_TIMERS 16 100 | 101 | // setTimer() constants 102 | #define TIMER_RUN_FOREVER 0 103 | #define TIMER_RUN_ONCE 1 104 | 105 | // constructor 106 | ESP8266_ISR_Timer(); 107 | 108 | void IRAM_ATTR init(); 109 | 110 | // this function must be called inside loop() 111 | void IRAM_ATTR run(); 112 | 113 | // Timer will call function 'f' every 'd' milliseconds forever 114 | // returns the timer number (numTimer) on success or 115 | // -1 on failure (f == NULL) or no free timers 116 | int IRAM_ATTR setInterval(const unsigned long& d, const timer_callback& f); 117 | 118 | // Timer will call function 'f' with parameter 'p' every 'd' milliseconds forever 119 | // returns the timer number (numTimer) on success or 120 | // -1 on failure (f == NULL) or no free timers 121 | int IRAM_ATTR setInterval(const unsigned long& d, const timer_callback_p& f, void* p); 122 | 123 | // Timer will call function 'f' after 'd' milliseconds one time 124 | // returns the timer number (numTimer) on success or 125 | // -1 on failure (f == NULL) or no free timers 126 | int IRAM_ATTR setTimeout(const unsigned long& d, const timer_callback& f); 127 | 128 | // Timer will call function 'f' with parameter 'p' after 'd' milliseconds one time 129 | // returns the timer number (numTimer) on success or 130 | // -1 on failure (f == NULL) or no free timers 131 | int IRAM_ATTR setTimeout(const unsigned long& d, const timer_callback_p& f, void* p); 132 | 133 | // Timer will call function 'f' every 'd' milliseconds 'n' times 134 | // returns the timer number (numTimer) on success or 135 | // -1 on failure (f == NULL) or no free timers 136 | int IRAM_ATTR setTimer(const unsigned long& d, const timer_callback& f, const unsigned& n); 137 | 138 | // Timer will call function 'f' with parameter 'p' every 'd' milliseconds 'n' times 139 | // returns the timer number (numTimer) on success or 140 | // -1 on failure (f == NULL) or no free timers 141 | int IRAM_ATTR setTimer(const unsigned long& d, const timer_callback_p& f, void* p, const unsigned& n); 142 | 143 | // updates interval of the specified timer 144 | bool IRAM_ATTR changeInterval(const unsigned& numTimer, const unsigned long& d); 145 | 146 | // destroy the specified timer 147 | void IRAM_ATTR deleteTimer(const unsigned& timerId); 148 | 149 | // restart the specified timer 150 | void IRAM_ATTR restartTimer(const unsigned& numTimer); 151 | 152 | // returns true if the specified timer is enabled 153 | bool IRAM_ATTR isEnabled(const unsigned& numTimer); 154 | 155 | // enables the specified timer 156 | void IRAM_ATTR enable(const unsigned& numTimer); 157 | 158 | // disables the specified timer 159 | void IRAM_ATTR disable(const unsigned& numTimer); 160 | 161 | // enables all timers 162 | void IRAM_ATTR enableAll(); 163 | 164 | // disables all timers 165 | void IRAM_ATTR disableAll(); 166 | 167 | // enables the specified timer if it's currently disabled, 168 | // and vice-versa 169 | void IRAM_ATTR toggle(const unsigned& numTimer); 170 | 171 | // returns the number of used timers 172 | int8_t IRAM_ATTR getNumTimers(); 173 | 174 | // returns the number of available timers 175 | int8_t IRAM_ATTR getNumAvailableTimers() 176 | { 177 | if (numTimers <= 0) 178 | return MAX_NUMBER_TIMERS; 179 | else 180 | return MAX_NUMBER_TIMERS - numTimers; 181 | }; 182 | 183 | private: 184 | // deferred call constants 185 | #define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function 186 | #define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer 187 | #define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer 188 | // low level function to initialize and enable a new timer 189 | // returns the timer number (numTimer) on success or 190 | // -1 on failure (f == NULL) or no free timers 191 | int8_t IRAM_ATTR setupTimer(const unsigned long& d, void* f, void* p, bool h, const unsigned& n); 192 | 193 | // find the first available slot 194 | int8_t IRAM_ATTR findFirstFreeSlot(); 195 | 196 | typedef struct 197 | { 198 | unsigned long prev_millis; // value returned by the millis() function in the previous run() call 199 | void* callback; // pointer to the callback function 200 | void* param; // function parameter 201 | bool hasParam; // true if callback takes a parameter 202 | unsigned long delay; // delay value 203 | unsigned maxNumRuns; // number of runs to be executed 204 | unsigned numRuns; // number of executed runs 205 | bool enabled; // true if enabled 206 | unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run() 207 | } timer_t; 208 | 209 | volatile timer_t timer[MAX_NUMBER_TIMERS]; 210 | 211 | // actual number of timers in use (-1 means uninitialized) 212 | volatile int8_t numTimers; 213 | }; 214 | 215 | #endif // ISR_TIMER_GENERIC_HPP 216 | 217 | -------------------------------------------------------------------------------- /src/TimerInterrupt_Generic_Debug.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************************************************************** 2 | TimerInterrupt_Generic_Debug.h 3 | For ESP8266 boards 4 | Written by Khoi Hoang 5 | 6 | Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt 7 | Licensed under MIT license 8 | 9 | The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. 10 | The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. 11 | The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. 12 | Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! 13 | 14 | Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) 15 | The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers 16 | Therefore, their executions are not blocked by bad-behaving functions / tasks. 17 | This important feature is absolutely necessary for mission-critical tasks. 18 | 19 | Based on SimpleTimer - A timer library for Arduino. 20 | Author: mromani@ottotecnica.com 21 | Copyright (c) 2010 OTTOTECNICA Italy 22 | 23 | Based on BlynkTimer.h 24 | Author: Volodymyr Shymanskyy 25 | 26 | Version: 1.6.0 27 | 28 | Version Modified By Date Comments 29 | ------- ----------- ---------- ----------- 30 | 1.0.0 K Hoang 23/11/2019 Initial coding 31 | 1.0.1 K Hoang 25/11/2019 New release fixing compiler error 32 | 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked 33 | 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. 34 | 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. 35 | 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval 36 | 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage 37 | 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 38 | 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. 39 | 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md 40 | 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer 41 | 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code 42 | *****************************************************************************************************************************/ 43 | 44 | #pragma once 45 | 46 | #ifndef TIMERINTERRUPT_GENERIC_DEBUG_H 47 | #define TIMERINTERRUPT_GENERIC_DEBUG_H 48 | 49 | #ifdef TIMERINTERRUPT_DEBUG_PORT 50 | #define TISR_DBG_PORT TIMERINTERRUPT_DEBUG_PORT 51 | #else 52 | #define TISR_DBG_PORT Serial 53 | #endif 54 | 55 | // Change _TIMERINTERRUPT_LOGLEVEL_ to set tracing and logging verbosity 56 | // 0: DISABLED: no logging 57 | // 1: ERROR: errors 58 | // 2: WARN: errors and warnings 59 | // 3: INFO: errors, warnings and informational (default) 60 | // 4: DEBUG: errors, warnings, informational and debug 61 | 62 | #ifndef _TIMERINTERRUPT_LOGLEVEL_ 63 | #define _TIMERINTERRUPT_LOGLEVEL_ 1 64 | #endif 65 | 66 | #define TISR_LOGERROR(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); } 67 | #define TISR_LOGERROR0(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print(x); } 68 | #define TISR_LOGERROR1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(y); } 69 | #define TISR_LOGERROR2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(z); } 70 | #define TISR_LOGERROR3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(z); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(w); } 71 | 72 | #define TISR_LOGWARN(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); } 73 | #define TISR_LOGWARN0(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print(x); } 74 | #define TISR_LOGWARN1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(y); } 75 | #define TISR_LOGWARN2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(z); } 76 | #define TISR_LOGWARN3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(z); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(w); } 77 | 78 | #define TISR_LOGINFO(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); } 79 | #define TISR_LOGINFO0(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print(x); } 80 | #define TISR_LOGINFO1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(y); } 81 | #define TISR_LOGINFO2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(z); } 82 | #define TISR_LOGINFO3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(z); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(w); } 83 | 84 | #define TISR_LOGDEBUG(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); } 85 | #define TISR_LOGDEBUG0(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print(x); } 86 | #define TISR_LOGDEBUG1(x,y) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(y); } 87 | #define TISR_LOGDEBUG2(x,y,z) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(z); } 88 | #define TISR_LOGDEBUG3(x,y,z,w) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.print(x); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(y); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.print(z); TISR_DBG_PORT.print(" "); TISR_DBG_PORT.println(w); } 89 | 90 | #endif //TIMERINTERRUPT_GENERIC_DEBUG_H 91 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------