diff --git a/libraries/ESP8266TimerInterrupt/CONTRIBUTING.md b/libraries/ESP8266TimerInterrupt/CONTRIBUTING.md new file mode 100644 index 0000000..fbc4991 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/CONTRIBUTING.md @@ -0,0 +1,53 @@ +## Contributing to ESP8266TimerInterrupt + +### Reporting Bugs + +Please report bugs in [ESP8266TimerInterrupt Issues](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues) if you find them. + +However, before reporting a bug please check through the following: + +* [Existing Open Issues](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues) - someone might have already encountered this. + +If you don't find anything, please [open a new issue](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues/new). + +### How to submit a bug report + +Please ensure to specify the following: + +* Arduino IDE version (e.g. 1.8.19) or Platform.io version +* `ESP8266` Core Version (e.g. ESP8266 core v3.0.2) +* Contextual information (e.g. what you were trying to achieve) +* Simplest possible steps to reproduce +* Anything that might be relevant in your opinion, such as: + * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` + * Network configuration + + +### Example + +``` +Arduino IDE version: v1.8.19 +ESP8266 Core Version v3.0.2 +ESP8266_NODEMCU +OS: Ubuntu 20.04 LTS +Linux xy-Inspiron-3593 5.13.0-35-generic #40~20.04.1-Ubuntu SMP Mon Mar 7 09:18:32 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux + +Context: +I encountered a crash while trying to use the Timer Interrupt. + +Steps to reproduce: +1. ... +2. ... +3. ... +4. ... +``` +### Sending Feature Requests + +Feel free to post feature requests. It's helpful if you can explain exactly why the feature would be useful. + +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. + +### Sending Pull Requests + +Pull Requests with changes and fixes are also welcome! + diff --git a/libraries/ESP8266TimerInterrupt/LICENSE b/libraries/ESP8266TimerInterrupt/LICENSE new file mode 100644 index 0000000..4a9150f --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Khoi Hoang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/libraries/ESP8266TimerInterrupt/README.md b/libraries/ESP8266TimerInterrupt/README.md new file mode 100644 index 0000000..b203be3 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/README.md @@ -0,0 +1,891 @@ +# ESP8266TimerInterrupt Library + +[![arduino-library-badge](https://www.ardu-badge.com/badge/ESP8266TimerInterrupt.svg?)](https://www.ardu-badge.com/ESP8266TimerInterrupt) +[![GitHub release](https://img.shields.io/github/release/khoih-prog/ESP8266TimerInterrupt.svg)](https://github.com/khoih-prog/ESP8266TimerInterrupt/releases) +[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/ESP8266TimerInterrupt/blob/master/LICENSE) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) +[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/ESP8266TimerInterrupt.svg)](http://github.com/khoih-prog/ESP8266TimerInterrupt/issues) + +Donate to my libraries using BuyMeACoffee + + +--- +--- + +## Table of Contents + +* [Important Change from v1.6.0](#Important-Change-from-v160) +* [Why do we need this ESP8266TimerInterrupt library](#why-do-we-need-this-esp8266timerinterrupt-library) + * [Features](#features) + * [Why using ISR-based Hardware Timer Interrupt is better](#why-using-isr-based-hardware-timer-interrupt-is-better) + * [Currently supported Boards](#currently-supported-boards) + * [Important Notes about ISR](#important-notes-about-isr) +* [Changelog](changelog.md) +* [Prerequisites](#prerequisites) +* [Installation](#installation) + * [Use Arduino Library Manager](#use-arduino-library-manager) + * [Manual Install](#manual-install) + * [VS Code & PlatformIO](#vs-code--platformio) +* [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) +* [HOWTO Use PWM analogWrite() with ESP8266 running Timer1 Interrupt](#howto-use-pwm-analogwrite-with-esp8266-running-timer1-interrupt) + * [1. ESP8266 has only 2 hardware timers, named Timer0 and Timer1](#1-esp8266-has-only-2-hardware-timers-named-timer0-and-timer1) + * [2. ESP8266 hardware timers' functions](#2-esp8266-hardware-timers-functions) + * [3. How to use PWM analogWrite() functions while using this library](#3-how-to-use-pwm-analogwrite-functions-while-using-this-library) +* [More useful Information](#more-useful-information) +* [Usage](#usage) + * [1. Using only Hardware Timer directly](#1-using-only-hardware-timer-directly) + * [1.1 Init Hardware Timer](#11-init-hardware-timer) + * [1.2 Set Hardware Timer Interval and attach Timer Interrupt Handler function](#12-set-hardware-timer-interval-and-attach-timer-interrupt-handler-function) + * [1.3 Set Hardware Timer Frequency and attach Timer Interrupt Handler function](#13-set-hardware-timer-frequency-and-attach-timer-interrupt-handler-function) + * [2. Using 16 ISR_based Timers from 1 Hardware Timer](#2-using-16-isr_based-timers-from-1-hardware-timer) + * [2.1 Important Note](#21-important-note) + * [2.2 Init Hardware Timer and ISR-based Timer](#22-init-hardware-timer-and-isr-based-timer) + * [2.3 Set Hardware Timer Interval and attach Timer Interrupt Handler functions](#23-set-hardware-timer-interval-and-attach-timer-interrupt-handler-functions) + * [2.4 Set One-Shot Hardware Timer Interval](#24-set-One-Shot-hardware-timer-interval) +* [Examples](#examples) + * [ 1. Argument_None](examples/Argument_None) + * [ 2. **Change_Interval**](examples/Change_Interval). + * [ 3. ISR_RPM_Measure](examples/ISR_RPM_Measure) + * [ 4. RPM_Measure](examples/RPM_Measure) + * [ 5. SwitchDebounce](examples/SwitchDebounce) + * [ 6. TimerInterruptTest](examples/TimerInterruptTest) + * [ 7. ISR_16_Timers_Array](examples/ISR_16_Timers_Array) + * [ 8. ISR_16_Timers_Array_Complex](examples/ISR_16_Timers_Array_Complex) + * [ 9. ISR_16_Timers_Array_OneShot](examples/ISR_16_Timers_Array_OneShot) **New** + * [10. multiFileProject](examples/multiFileProject) **New** +* [Example Change_Interval](#example-change_interval) +* [Debug Terminal Output Samples](#debug-terminal-output-samples) + * [1. TimerInterruptTest on ESP8266_NODEMCU_ESP12E](#1-timerinterrupttest-on-esp8266_nodemcu_esp12e) + * [2. Change_Interval on ESP8266_NODEMCU_ESP12E](#2-change_interval-on-esp8266_nodemcu_esp12e) + * [3. ISR_16_Timers_Array on ESP8266_NODEMCU_ESP12E](#3-isr_16_timers_array-on-esp8266_nodemcu_esp12e) + * [4. ISR_16_Timers_Array_Complex on ESP8266_NODEMCU_ESP12E](#4-isr_16_timers_array_complex-on-esp8266_nodemcu_esp12e) + * [5. ISR_16_Timers_Array_OneShot on ESP8266_NODEMCU_ESP12E](#5-ISR_16_Timers_Array_OneShot-on-esp8266_nodemcu_esp12e) +* [Debug](#debug) +* [Troubleshooting](#troubleshooting) +* [Issues](#issues) +* [TO DO](#to-do) +* [DONE](#done) +* [Contributions and Thanks](#contributions-and-thanks) +* [Contributing](#contributing) +* [License](#license) +* [Copyright](#copyright) + +--- +--- + +### Important Change from v1.6.0 + +Please have a look at [HOWTO Fix `Multiple Definitions` Linker Error](#howto-fix-multiple-definitions-linker-error) + + +--- +--- + +### Why do we need this [ESP8266TimerInterrupt library](https://github.com/khoih-prog/ESP8266TimerInterrupt) + +### Features + +This library enables you to use Interrupt from Hardware Timers on an ESP8266-based board. + +As **Hardware Timers are rare, and very precious assets** of any board, this library now enables you to use up to **16 ISR-based Timers, while consuming only 1 Hardware Timer**. Timers' interval is very long (**ulong millisecs**). + +Now with these new **16 ISR-based timers**, the maximum interval is **practically unlimited** (limited only by unsigned long miliseconds) while **the accuracy is nearly perfect** compared to software timers. + +The most important feature is they're ISR-based timers. Therefore, their executions are **not blocked by bad-behaving functions / tasks**. This important feature is absolutely necessary for mission-critical tasks. + +The [**ISR_Timer_Complex**](examples/ISR_Timer_Complex) example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs of each type of timers. + +Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet and Blynk services. You can also have many `(up to 16)` timers to use. + +This non-being-blocked important feature is absolutely necessary for mission-critical tasks. + +You'll see blynkTimer Software is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task +in loop(), using delay() function as an example. The elapsed time then is very unaccurate + +#### Why using ISR-based Hardware Timer Interrupt is better + +Imagine you have a system with a **mission-critical** function, measuring water level and control the sump pump or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is **blocking** the loop() or setup(). + +So your function **might not be executed, and the result would be disastrous.** + +You'd prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.). + +The correct choice is to use a Hardware Timer with **Interrupt** to call your function. + +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. + +Functions using normal software timers, relying on loop() and calling millis(), won't work if the loop() or setup() is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services. + +The catch is **your function is now part of an ISR (Interrupt Service Routine), and must be lean / mean, and follow certain rules.** More to read on: + +[**HOWTO Attach Interrupt**](https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/) + +--- + +### Currently supported Boards + +1. ESP8266-based boards + +--- + +### Important Notes about ISR + +1. Inside the attached function, **delay() won’t work and the value returned by millis() will not increment.** Serial data received while in the function may be lost. You should declare as **volatile any variables that you modify within the attached function.** + +2. Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile. + +--- +--- + + +## Prerequisites + +1. [`Arduino IDE 1.8.19+` for Arduino](https://github.com/arduino/Arduino). [![GitHub release](https://img.shields.io/github/release/arduino/Arduino.svg)](https://github.com/arduino/Arduino/releases/latest) +2. [`ESP8266 Core 3.0.2+`](https://github.com/esp8266/Arduino) for ESP8266-based boards. [![Latest release](https://img.shields.io/github/release/esp8266/Arduino.svg)](https://github.com/esp8266/Arduino/releases/latest/). To use ESP8266 core 2.7.1+ for LittleFS. +3. [`SimpleTimer library`](https://github.com/jfturcot/SimpleTimer) to use with some examples. + + +--- +--- + +## Installation + +### Use Arduino Library Manager + +The best and easiest way is to use `Arduino Library Manager`. Search for [**ESP8266TimerInterrupt**](https://github.com/khoih-prog/ESP8266TimerInterrupt), then select / install the latest version. +You can also use this link [![arduino-library-badge](https://www.ardu-badge.com/badge/ESP8266TimerInterrupt.svg?)](https://www.ardu-badge.com/ESP8266TimerInterrupt) for more detailed instructions. + +### Manual Install + +Another way to install is to: + +1. Navigate to [**ESP8266TimerInterrupt**](https://github.com/khoih-prog/ESP8266TimerInterrupt) page. +2. Download the latest release `ESP8266TimerInterrupt-master.zip`. +3. Extract the zip file to `ESP8266TimerInterrupt-master` directory +4. Copy whole `ESP8266TimerInterrupt-master` folder to Arduino libraries' directory such as `~/Arduino/libraries/`. + +### VS Code & PlatformIO + +1. Install [VS Code](https://code.visualstudio.com/) +2. Install [PlatformIO](https://platformio.org/platformio-ide) +3. Install [**ESP8266TimerInterrupt** library](https://registry.platformio.org/libraries/khoih-prog/ESP8266TimerInterrupt) by using [Library Manager](https://registry.platformio.org/libraries/khoih-prog/ESP8266TimerInterrupt/installation). Search for **ESP8266TimerInterrupt** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) +4. Use included [platformio.ini](platformio/platformio.ini) file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at [Project Configuration File](https://docs.platformio.org/page/projectconf.html) + +--- +--- + +### HOWTO Fix `Multiple Definitions` Linker Error + +The current library implementation, using `xyz-Impl.h` instead of standard `xyz.cpp`, possibly creates certain `Multiple Definitions` Linker error in certain use cases. + +You can use + +``` +#include "ESP8266TimerInterrupt.h" //https://github.com/khoih-prog/ESP8266TimerInterrupt +#include "ESP8266_ISR_Timer.hpp" //https://github.com/khoih-prog/ESP8266TimerInterrupt +``` + +in many files. But be sure to use the following `#include ` **in just 1 `.h`, `.cpp` or `.ino` file**, which must **not be included in any other file**, to avoid `Multiple Definitions` Linker Error + +``` +// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error +#include "ESP8266_ISR_Timer.h" //https://github.com/khoih-prog/ESP8266TimerInterrupt +``` + +Check the new [**multiFileProject** example](examples/multiFileProject) for a `HOWTO` demo. + +Have a look at the discussion in [Different behaviour using the src_cpp or src_h lib #80](https://github.com/khoih-prog/ESPAsync_WiFiManager/discussions/80) + +--- +--- + +### HOWTO Use PWM analogWrite() with ESP8266 running Timer1 Interrupt + +Please have a look at [ESP8266TimerInterrupt Issue 8: **ESP8266Timer and PWM --> wdt reset**](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues/8) to have more detailed description and solution of the issue. + +#### 1. ESP8266 has only 2 hardware timers, named Timer0 and Timer1 + +#### 2. ESP8266 hardware timers' functions + +- Timer0 has been used for WiFi and it's not advisable to use while using WiFi (if not using WiFi, why select ESP8266 ??) +- Timer1 is used by this [**ESP8266TimerInterrupt Library**](https://github.com/khoih-prog/ESP8266TimerInterrupt) + +#### 3. How to use PWM analogWrite() functions while using this library + + 1. If possible, use software timer instead of [**ESP8266TimerInterrupt Hardware Timer1**](https://github.com/khoih-prog/ESP8266TimerInterrupt) + 2. If using [**ESP8266TimerInterrupt Hardware Timer1**](https://github.com/khoih-prog/ESP8266TimerInterrupt) is a must, you can either + - use external DAC such as AD5662, AD5667, AD5696. + - use software PWM such as mentioned in [ESP8266 PWM REVISITED (AND REIMPLEMENTED)](https://lurchi.wordpress.com/2016/06/29/esp8266-pwm-revisited-and-reimplemented/) + + +--- +--- + +## More useful Information + +The ESP8266 timers are **badly designed**, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. +The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. +The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. +Using 256 prescaler, maximum timer1 interval is only **26.843542 seconds !!!** + +The timer1 counters can be configured to support automatic reload. + +--- + +Now with these new **`16 ISR-based timers`**, the maximum interval is **practically unlimited** (limited only by unsigned long miliseconds). + +The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers. Therefore, their executions are not blocked by bad-behaving functions / tasks. + +This important feature is absolutely necessary for mission-critical tasks. + +The [ISR_Timer_Complex example](examples/ISR_Timer_Complex) will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs of each type of timers. + +Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet and Blynk services. You can also have many `(up to 16)` timers to use. This non-being-blocked important feature is absolutely necessary for mission-critical tasks. + +You'll see blynkTimer Software is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task in loop(), using delay() function as an example. The elapsed time then is very unaccurate + +--- +--- + +## Usage + +The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + +The ESP8266 has two hardware timers, but Timer0 has been used for WiFi and it's not advisable to use. Only Timer1 is available. + +The Timer1's 23-bit counter can terribly count only up to 8,388,607. So the timer1 maximum interval is very short. +Using 256 prescaler, maximum Timer1 interval is only 26.843542 seconds !!! + +### 1. Using only Hardware Timer directly + +### 1.1 Init Hardware Timer + +``` +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +// Init ESP8266 only and only Timer 1 +ESP8266Timer ITimer; +``` + +### 1.2 Set Hardware Timer Interval and attach Timer Interrupt Handler function + +Use one of these functions with **interval in unsigned long milliseconds** + +``` +// interval (in microseconds) +bool setInterval(unsigned long interval, timer_callback callback) + +// interval (in microseconds) +bool attachInterruptInterval(unsigned long interval, timer_callback callback) +``` + +as follows + +``` +void IRAM_ATTR TimerHandler() +{ + // Doing something here inside ISR +} + +#define TIMER_INTERVAL_MS 1000 + +void setup() +{ + .... + + // Interval in microsecs + if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + lastMillis = millis(); + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(lastMillis); + } + else + Serial.println(F("Can't set ITimer correctly. Select another freq. or interval")); +} +``` + +### 1.3 Set Hardware Timer Frequency and attach Timer Interrupt Handler function + +Use one of these functions with **frequency in float Hz** + +``` +// frequency (in hertz) +bool setFrequency(float frequency, timer_callback callback) + +// frequency (in hertz) +bool attachInterrupt(float frequency, timer_callback callback) +``` + +as follows + +``` +void TimerHandler() +{ + // Doing something here inside ISR +} + +#define TIMER_FREQ_HZ 5555.555 + +void setup() +{ + .... + + // Frequency in float Hz + if (ITimer.attachInterrupt(TIMER_FREQ_HZ, TimerHandler)) + Serial.println("Starting ITimer OK, millis() = " + String(millis())); + else + Serial.println("Can't set ITimer. Select another freq. or timer"); +} +``` + + +### 2. Using 16 ISR_based Timers from 1 Hardware Timer + +### 2.1 Important Note + +The 16 ISR_based Timers, designed for long timer intervals, only support using **unsigned long millisec intervals**. If you have to use much higher frequency or sub-millisecond interval, you have to use the Hardware Timers directly as in [1.3 Set Hardware Timer Frequency and attach Timer Interrupt Handler function](#13-set-hardware-timer-frequency-and-attach-timer-interrupt-handler-function) + +### 2.2 Init Hardware Timer and ISR-based Timer + +``` +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" +#include "ESP8266_ISR_Timer.h" + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +// Init ESP8266_ISR_Timer +ESP8266_ISR_Timer ISR_Timer; +``` + +### 2.3 Set Hardware Timer Interval and attach Timer Interrupt Handler functions + +``` +void IRAM_ATTR TimerHandler() +{ + ISR_timer.run(); +} + +#define HW_TIMER_INTERVAL_MS 50L + +#define TIMER_INTERVAL_2S 2000L +#define TIMER_INTERVAL_5S 5000L +#define TIMER_INTERVAL_11S 11000L +#define TIMER_INTERVAL_101S 101000L + +// In AVR, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething2s() +{ + // Doing something here inside ISR every 2 seconds +} + +void doingSomething5s() +{ + // Doing something here inside ISR every 5 seconds +} + +void doingSomething11s() +{ + // Doing something here inside ISR every 11 seconds +} + +void doingSomething101s() +{ + // Doing something here inside ISR every 101 seconds +} + +void setup() +{ + .... + + // Interval in microsecs + if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + startMillis = millis(); + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(startMillis); + } + else + Serial.println(F("Can't set ITimer. Select another freq. or timer")); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each ISR_Timer + ISR_timer.setInterval(TIMER_INTERVAL_2S, doingSomething2s); + ISR_timer.setInterval(TIMER_INTERVAL_5S, doingSomething5s); + ISR_timer.setInterval(TIMER_INTERVAL_11S, doingSomething11s); + ISR_timer.setInterval(TIMER_INTERVAL_101S, doingSomething101s); +} +``` + +### 2.4 Set One-Shot Hardware Timer Interval + +https://github.com/khoih-prog/ESP8266TimerInterrupt/blob/f497e733eda4a749067e037747c73d8c188a59d9/examples/ISR_16_Timers_Array_OneShot/ISR_16_Timers_Array_OneShot.ino#L414-L421 + + +--- +--- + + +### Examples: + + 1. [Argument_None](examples/Argument_None) + 2. [ISR_RPM_Measure](examples/ISR_RPM_Measure) + 3. [RPM_Measure](examples/RPM_Measure) + 4. [SwitchDebounce](examples/SwitchDebounce) + 5. [TimerInterruptTest](examples/TimerInterruptTest) + 6. [Change_Interval](examples/Change_Interval). + 7. [**ISR_16_Timers_Array**](examples/ISR_16_Timers_Array) + 8. [**ISR_16_Timers_Array_Complex**](examples/ISR_16_Timers_Array_Complex) + 9. [**ISR_16_Timers_Array_OneShot**](examples/ISR_16_Timers_Array_OneShot) **New** +10. [**multiFileProject**](examples/multiFileProject) **New** + +--- +--- + +### Example [Change_Interval](examples/Change_Interval) + +https://github.com/khoih-prog/ESP8266TimerInterrupt/blob/f497e733eda4a749067e037747c73d8c188a59d9/examples/Change_Interval/Change_Interval.ino#L32-L130 + + +--- +--- + +### Debug Terminal Output Samples + +### 1. TimerInterruptTest on ESP8266_NODEMCU_ESP12E + +The following is the sample terminal output when running example [TimerInterruptTest](examples/TimerInterruptTest) on **ESP8266_NODEMCU_ESP12E** to demonstrate the accuracy of Hardware Timers. + +``` +Starting TimerInterruptTest on ESP8266_NODEMCU_ESP12E +ESP8266TimerInterrupt v1.6.0 +CPU Frequency = 160 MHz +ESP8266TimerInterrupt: _fre = 312500.00, _count = 312500 +Starting ITimer OK, millis() = 262 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +Delta ms = 1000 +``` + +--- + +### 2. Change_Interval on ESP8266_NODEMCU_ESP12E + +The following is the sample terminal output when running example [Change_Interval](examples/Change_Interval) on **ESP8266_NODEMCU_ESP12E** to demonstrate how to change Timer Interval on-the-fly + +``` +Starting Change_Interval on ESP8266_NODEMCU_ESP12E +ESP8266TimerInterrupt v1.6.0 +CPU Frequency = 160 MHz +Starting ITimer OK, millis() = 162 +Time = 10001, TimerCount = 19 +Time = 20002, TimerCount = 39 +Changing Interval, Timer = 1000 +Time = 30003, TimerCount = 49 +Time = 40004, TimerCount = 59 +Changing Interval, Timer = 500 +Time = 50005, TimerCount = 79 +Time = 60006, TimerCount = 99 +Changing Interval, Timer = 1000 +Time = 70007, TimerCount = 109 +Time = 80008, TimerCount = 119 +Changing Interval, Timer = 500 +Time = 90009, TimerCount = 139 +Time = 100010, TimerCount = 159 +Changing Interval, Timer = 1000 +Time = 110011, TimerCount = 169 +Time = 120012, TimerCount = 179 +Changing Interval, Timer = 500 +Time = 130013, TimerCount = 199 +Time = 140014, TimerCount = 219 +Changing Interval, Timer = 1000 +Time = 150015, TimerCount = 229 +Time = 160016, TimerCount = 239 +Changing Interval, Timer = 500 +Time = 170017, TimerCount = 259 +Time = 180018, TimerCount = 279 +``` + +--- + +### 3. ISR_16_Timers_Array on ESP8266_NODEMCU_ESP12E + +The following is the sample terminal output when running example [ISR_16_Timers_Array](examples/ISR_16_Timers_Array) on **ESP8266_NODEMCU_ESP12E** to demonstrate of ISR Hardware Timer, especially when system is very busy or blocked. The 16 independent ISR timers are programmed to be activated repetitively after certain intervals, is activated exactly after that programmed interval !!! + +``` +Starting ISR_16_Timers_Array on ESP8266_NODEMCU_ESP12E +ESP8266TimerInterrupt v1.6.0 +CPU Frequency = 160 MHz +Starting ITimer OK, millis() = 175 +1s: Delta ms = 1003, ms = 1178 +1s: Delta ms = 999, ms = 2177 +2s: Delta ms = 2002, ms = 2177 +1s: Delta ms = 1000, ms = 3177 +3s: Delta ms = 3002, ms = 3177 +1s: Delta ms = 1000, ms = 4177 +2s: Delta ms = 2000, ms = 4177 +4s: Delta ms = 4002, ms = 4177 +1s: Delta ms = 1000, ms = 5177 +5s: Delta ms = 5002, ms = 5177 +1s: Delta ms = 1000, ms = 6177 +2s: Delta ms = 2001, ms = 6178 +3s: Delta ms = 3001, ms = 6178 +6s: Delta ms = 6003, ms = 6178 +1s: Delta ms = 1000, ms = 7177 +7s: Delta ms = 7002, ms = 7177 +1s: Delta ms = 1000, ms = 8177 +2s: Delta ms = 1999, ms = 8177 +4s: Delta ms = 4000, ms = 8177 +8s: Delta ms = 8002, ms = 8177 +1s: Delta ms = 1000, ms = 9177 +3s: Delta ms = 2999, ms = 9177 +9s: Delta ms = 9002, ms = 9177 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10002 +1s: Delta ms = 1000, ms = 10177 +2s: Delta ms = 2000, ms = 10177 +5s: Delta ms = 5001, ms = 10178 +10s: Delta ms = 10006, ms = 10181 +1s: Delta ms = 1000, ms = 11177 +11s: Delta ms = 11003, ms = 11178 +1s: Delta ms = 1000, ms = 12177 +2s: Delta ms = 2000, ms = 12177 +3s: Delta ms = 3000, ms = 12177 +4s: Delta ms = 4000, ms = 12177 +6s: Delta ms = 5999, ms = 12177 +12s: Delta ms = 12005, ms = 12180 +1s: Delta ms = 1000, ms = 13177 +13s: Delta ms = 13002, ms = 13177 +1s: Delta ms = 1000, ms = 14177 +2s: Delta ms = 2000, ms = 14177 +7s: Delta ms = 7000, ms = 14177 +14s: Delta ms = 14002, ms = 14177 +1s: Delta ms = 1000, ms = 15177 +3s: Delta ms = 3000, ms = 15177 +5s: Delta ms = 4999, ms = 15177 +15s: Delta ms = 15002, ms = 15177 +1s: Delta ms = 1000, ms = 16177 +2s: Delta ms = 2000, ms = 16177 +4s: Delta ms = 4001, ms = 16178 +8s: Delta ms = 8001, ms = 16178 +16s: Delta ms = 16003, ms = 16178 +1s: Delta ms = 1000, ms = 17177 +1s: Delta ms = 1000, ms = 18177 +2s: Delta ms = 2000, ms = 18177 +3s: Delta ms = 3000, ms = 18177 +6s: Delta ms = 6000, ms = 18177 +9s: Delta ms = 9001, ms = 18178 +1s: Delta ms = 1000, ms = 19177 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000 +``` + +--- + +### 4. ISR_16_Timers_Array_Complex on ESP8266_NODEMCU_ESP12E + +The following is the sample terminal output when running example [ISR_16_Timers_Array_Complex](examples/ISR_16_Timers_Array_Complex) on **ESP8266_NODEMCU_ESP12E** to demonstrate the ISR Hardware Timer, especially when system is very busy or blocked. The 16 independent ISR timers are programmed to be activated repetitively after certain intervals, is activated exactly after that programmed interval !!! + + +``` +Starting ISR_16_Timers_Array_Complex on ESP8266_NODEMCU_ESP12E +ESP8266TimerInterrupt v1.6.0 +CPU Frequency = 160 MHz +Starting ITimer OK, millis() = 177 +SimpleTimer : 2, ms : 10179, Dms : 10000 +Timer : 0, programmed : 5000, actual : 5008 +Timer : 1, programmed : 10000, actual : 0 +Timer : 2, programmed : 15000, actual : 0 +Timer : 3, programmed : 20000, actual : 0 +Timer : 4, programmed : 25000, actual : 0 +Timer : 5, programmed : 30000, actual : 0 +Timer : 6, programmed : 35000, actual : 0 +Timer : 7, programmed : 40000, actual : 0 +Timer : 8, programmed : 45000, actual : 0 +Timer : 9, programmed : 50000, actual : 0 +Timer : 10, programmed : 55000, actual : 0 +Timer : 11, programmed : 60000, actual : 0 +Timer : 12, programmed : 65000, actual : 0 +Timer : 13, programmed : 70000, actual : 0 +Timer : 14, programmed : 75000, actual : 0 +Timer : 15, programmed : 80000, actual : 0 +SimpleTimer : 2, ms : 20232, Dms : 10053 +Timer : 0, programmed : 5000, actual : 5000 +Timer : 1, programmed : 10000, actual : 10000 +Timer : 2, programmed : 15000, actual : 15008 +Timer : 3, programmed : 20000, actual : 20008 +Timer : 4, programmed : 25000, actual : 0 +Timer : 5, programmed : 30000, actual : 0 +Timer : 6, programmed : 35000, actual : 0 +Timer : 7, programmed : 40000, actual : 0 +Timer : 8, programmed : 45000, actual : 0 +Timer : 9, programmed : 50000, actual : 0 +Timer : 10, programmed : 55000, actual : 0 +Timer : 11, programmed : 60000, actual : 0 +Timer : 12, programmed : 65000, actual : 0 +Timer : 13, programmed : 70000, actual : 0 +Timer : 14, programmed : 75000, actual : 0 +Timer : 15, programmed : 80000, actual : 0 +SimpleTimer : 2, ms : 30286, Dms : 10054 +Timer : 0, programmed : 5000, actual : 5000 +Timer : 1, programmed : 10000, actual : 10000 +Timer : 2, programmed : 15000, actual : 15000 +Timer : 3, programmed : 20000, actual : 20008 +Timer : 4, programmed : 25000, actual : 25008 +Timer : 5, programmed : 30000, actual : 30008 +Timer : 6, programmed : 35000, actual : 0 +Timer : 7, programmed : 40000, actual : 0 +Timer : 8, programmed : 45000, actual : 0 +Timer : 9, programmed : 50000, actual : 0 +Timer : 10, programmed : 55000, actual : 0 +Timer : 11, programmed : 60000, actual : 0 +Timer : 12, programmed : 65000, actual : 0 +Timer : 13, programmed : 70000, actual : 0 +Timer : 14, programmed : 75000, actual : 0 +Timer : 15, programmed : 80000, actual : 0 +SimpleTimer : 2, ms : 40341, Dms : 10055 +Timer : 0, programmed : 5000, actual : 5000 +Timer : 1, programmed : 10000, actual : 10000 +Timer : 2, programmed : 15000, actual : 15000 +Timer : 3, programmed : 20000, actual : 20000 +Timer : 4, programmed : 25000, actual : 25008 +Timer : 5, programmed : 30000, actual : 30008 +Timer : 6, programmed : 35000, actual : 35008 +Timer : 7, programmed : 40000, actual : 40008 +Timer : 8, programmed : 45000, actual : 0 +Timer : 9, programmed : 50000, actual : 0 +Timer : 10, programmed : 55000, actual : 0 +Timer : 11, programmed : 60000, actual : 0 +Timer : 12, programmed : 65000, actual : 0 +Timer : 13, programmed : 70000, actual : 0 +Timer : 14, programmed : 75000, actual : 0 +Timer : 15, programmed : 80000, actual : 0 +SimpleTimer : 2, ms : 50396, Dms : 10055 +Timer : 0, programmed : 5000, actual : 5000 +Timer : 1, programmed : 10000, actual : 10000 +Timer : 2, programmed : 15000, actual : 15000 +Timer : 3, programmed : 20000, actual : 20000 +Timer : 4, programmed : 25000, actual : 25000 +Timer : 5, programmed : 30000, actual : 30008 +Timer : 6, programmed : 35000, actual : 35008 +Timer : 7, programmed : 40000, actual : 40008 +Timer : 8, programmed : 45000, actual : 45008 +Timer : 9, programmed : 50000, actual : 50008 +Timer : 10, programmed : 55000, actual : 0 +Timer : 11, programmed : 60000, actual : 0 +Timer : 12, programmed : 65000, actual : 0 +Timer : 13, programmed : 70000, actual : 0 +Timer : 14, programmed : 75000, actual : 0 +Timer : 15, programmed : 80000, actual : 0 +SimpleTimer : 2, ms : 60452, Dms : 10056 +Timer : 0, programmed : 5000, actual : 5000 +Timer : 1, programmed : 10000, actual : 10000 +Timer : 2, programmed : 15000, actual : 15000 +Timer : 3, programmed : 20000, actual : 20000 +Timer : 4, programmed : 25000, actual : 25000 +Timer : 5, programmed : 30000, actual : 30000 +Timer : 6, programmed : 35000, actual : 35008 +Timer : 7, programmed : 40000, actual : 40008 +Timer : 8, programmed : 45000, actual : 45008 +Timer : 9, programmed : 50000, actual : 50008 +Timer : 10, programmed : 55000, actual : 55008 +Timer : 11, programmed : 60000, actual : 60008 +Timer : 12, programmed : 65000, actual : 0 +Timer : 13, programmed : 70000, actual : 0 +Timer : 14, programmed : 75000, actual : 0 +Timer : 15, programmed : 80000, actual : 0 +SimpleTimer : 2, ms : 70509, Dms : 10057 +Timer : 0, programmed : 5000, actual : 5000 +Timer : 1, programmed : 10000, actual : 10000 +Timer : 2, programmed : 15000, actual : 15000 +Timer : 3, programmed : 20000, actual : 20000 +Timer : 4, programmed : 25000, actual : 25000 +Timer : 5, programmed : 30000, actual : 30000 +Timer : 6, programmed : 35000, actual : 35000 +Timer : 7, programmed : 40000, actual : 40008 +Timer : 8, programmed : 45000, actual : 45008 +Timer : 9, programmed : 50000, actual : 50008 +Timer : 10, programmed : 55000, actual : 55008 +Timer : 11, programmed : 60000, actual : 60008 +Timer : 12, programmed : 65000, actual : 65008 +Timer : 13, programmed : 70000, actual : 70008 +Timer : 14, programmed : 75000, actual : 0 +Timer : 15, programmed : 80000, actual : 0 +SimpleTimer : 2, ms : 80566, Dms : 10057 +Timer : 0, programmed : 5000, actual : 5000 +Timer : 1, programmed : 10000, actual : 10000 +Timer : 2, programmed : 15000, actual : 15000 +Timer : 3, programmed : 20000, actual : 20000 +Timer : 4, programmed : 25000, actual : 25000 +Timer : 5, programmed : 30000, actual : 30000 +Timer : 6, programmed : 35000, actual : 35000 +Timer : 7, programmed : 40000, actual : 40000 +Timer : 8, programmed : 45000, actual : 45008 +Timer : 9, programmed : 50000, actual : 50008 +Timer : 10, programmed : 55000, actual : 55008 +Timer : 11, programmed : 60000, actual : 60008 +Timer : 12, programmed : 65000, actual : 65008 +Timer : 13, programmed : 70000, actual : 70008 +Timer : 14, programmed : 75000, actual : 75008 +Timer : 15, programmed : 80000, actual : 80008 +``` + +--- + +### 5. ISR_16_Timers_Array_OneShot on ESP8266_NODEMCU_ESP12E + +The following is the sample terminal output when running example [ISR_16_Timers_Array_OneShot](examples/ISR_16_Timers_Array_OneShot) on **ESP8266_NODEMCU_ESP12E** to demonstrate the **One-Shot** ISR Hardware Timer, especially when system is very busy or blocked. The 16 independent ISR timers are programmed to be activated repetitively after certain intervals, is activated exactly after that programmed interval !!! + + +``` +Starting ISR_16_Timers_Array_OneShot on ESP8266_NODEMCU_ESP12E +ESP8266TimerInterrupt v1.6.0 +CPU Frequency = 160 MHz +Starting ITimer OK, millis() = 365 +1s: Delta ms = 1002, ms = 1367 +2s: Delta ms = 2002, ms = 2367 +3s: Delta ms = 3002, ms = 3367 +4s: Delta ms = 4002, ms = 4367 +5s: Delta ms = 5002, ms = 5367 +6s: Delta ms = 6002, ms = 6367 +7s: Delta ms = 7002, ms = 7367 +8s: Delta ms = 8002, ms = 8367 +9s: Delta ms = 9002, ms = 9367 +10s: Delta ms = 10002, ms = 10367 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10002 +11s: Delta ms = 11002, ms = 11367 +12s: Delta ms = 12002, ms = 12367 +13s: Delta ms = 13002, ms = 13367 +14s: Delta ms = 14002, ms = 14367 +15s: Delta ms = 15002, ms = 15367 +16s: Delta ms = 16002, ms = 16367 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10001 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000 +simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000 +``` + +--- +--- + +### Debug + +Debug is enabled by default on Serial. + +You can also change the debugging level (_TIMERINTERRUPT_LOGLEVEL_) from 0 to 4 + +```cpp +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 0 +#define _TIMERINTERRUPT_LOGLEVEL_ 0 +``` + +--- + +### Troubleshooting + +If you get compilation errors, more often than not, you may need to install a newer version of the core for Arduino boards. + +Sometimes, the library will only work if you update the board core to the latest version because I am using newly added functions. + +--- +--- + + +### Issues + +Submit issues to: [ESP8266TimerInterrupt issues](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues) + +--- +--- + +## TO DO + +1. Search for bug and improvement. + +--- + +## DONE + + 1. Basic hardware timers for ESP8266. + 2. More hardware-initiated software-enabled timers + 3. Longer time interval + 4. Similar features for remaining Arduino boards such as AVR, Teensy, SAMD21, SAMD51, SAM-DUE, nRF52, ESP32, STM32, etc. + 5. Update to match new ESP8266 core v3.0.0 + 6. Fix compiler errors due to conflict to some libraries. + 7. Add complex examples. + 8. Update to match new ESP8266 core v3.0.2 + 9. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories +10. Add feature to select among highest, medium or lowest accuracy for Timers for shortest, medium or longest time +11. Convert to `h-only` style. +12. Optimize code by using passing by `reference` instead of by `value` +13. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project +14. Add example [ISR_16_Timers_Array_OneShot](examples/ISR_16_Timers_Array_OneShot) to demo how to use `one-shot ISR-based timer` + +--- +--- + +## Contributions and thanks + +1. Thanks to [Holger Lembke](https://github.com/holgerlembke) to report [ESP8266TimerInterrupt Issue 8: **ESP8266Timer and PWM --> wdt reset**](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues/8), leading to the [HOWTO Use PWM analogWrite() with ESP8266 running Timer1 Interrupt](https://github.com/khoih-prog/ESP8266TimerInterrupt#howto-use-pwm-analogwrite-with-esp8266-running-timer1-interrupt) notes. +2. Thanks to [Eugene](https://github.com/RushOnline) to make bug-fixing PR and discussion in [bugfix: reattachInterrupt() pass wrong frequency value to setFrequency() #19](https://github.com/khoih-prog/ESP8266TimerInterrupt/pull/19), leading to v1.5.0 +3. Thanks to [absalom-muc](https://github.com/absalom-muc) to make enhancement request in [One shot operating mode #20](https://github.com/khoih-prog/ESP8266TimerInterrupt/issues/20), leading to v1.6.0 to add example to demo how to use `one-shot ISR-based timer` + + + + + + + +
holgerlembke
Holger Lembke

RushOnline
Eugene

absalom-muc
absalom-muc

+ + +--- + +## Contributing + +If you want to contribute to this project: +- Report bugs and errors +- Ask for enhancements +- Create issues and pull requests +- Tell other people about this library + +--- + +## License + +- The library is licensed under [MIT](https://github.com/khoih-prog/ESP8266TimerInterrupt/blob/master/LICENSE) + +--- + +## Copyright + +Copyright 2019- Khoi Hoang + + diff --git a/libraries/ESP8266TimerInterrupt/changelog.md b/libraries/ESP8266TimerInterrupt/changelog.md new file mode 100644 index 0000000..0d28e8f --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/changelog.md @@ -0,0 +1,86 @@ +# ESP8266TimerInterrupt Library + +[![arduino-library-badge](https://www.ardu-badge.com/badge/ESP8266TimerInterrupt.svg?)](https://www.ardu-badge.com/ESP8266TimerInterrupt) +[![GitHub release](https://img.shields.io/github/release/khoih-prog/ESP8266TimerInterrupt.svg)](https://github.com/khoih-prog/ESP8266TimerInterrupt/releases) +[![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/khoih-prog/ESP8266TimerInterrupt/blob/master/LICENSE) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](#Contributing) +[![GitHub issues](https://img.shields.io/github/issues/khoih-prog/ESP8266TimerInterrupt.svg)](http://github.com/khoih-prog/ESP8266TimerInterrupt/issues) + +--- +--- + +* [Changelog](#changelog) + * [Releases v1.6.0](#releases-v160) + * [Releases v1.5.0](#releases-v150) + * [Releases v1.4.1](#releases-v141) + * [Releases v1.4.0](#releases-v140) + * [Releases v1.3.0](#releases-v130) + * [Releases v1.2.0](#releases-v120) + * [Releases v1.1.1](#releases-v111) + * [Releases v1.1.0](#releases-v110) + * [Releases v1.0.3](#releases-v103) + * [Releases v1.0.2](#releases-v102) + +--- +--- + +## Changelog + +### Releases v1.6.0 + +1. Add example [ISR_16_Timers_Array_OneShot](examples/ISR_16_Timers_Array_OneShot) to demo how to use `one-shot ISR-based timer` +2. Add example [multiFileProject](examples/multiFileProject) to demo for multiple-file project +3. Optimize code by using passing by `reference` instead of by `value` + +### Releases v1.5.0 + +1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories +2. Add feature to select among highest, medium or lowest accuracy for Timers for shortest, medium or longest time +3. Fix reattachInterrupt() bug. Check [bugfix: reattachInterrupt() pass wrong frequency value to setFrequency() #19](https://github.com/khoih-prog/ESP8266TimerInterrupt/pull/19) +4. Update examples + +### Releases v1.4.1 + +1. Examples modified and tested with core v3.0.2 +2. Add instructions in `README.md` +3. Add `changelog.md` +4. Delete Blynk-related examples + +### Releases v1.4.0 + +1. Fix compiler errors due to conflict to some libraries. +2. Add complex examples. + +### Releases v1.3.0 + +1. Update to match new ESP8266 core v3.0.0 + +### Releases v1.2.0 + +1. Add better debug feature. +2. Optimize code and examples to reduce RAM usage + +### Releases v1.1.1 + +1. Add [**Change_Interval**](examples/Change_Interval) example to show how to change TimerInterval on-the-fly +2. Add Version String + +### Releases v1.1.0 + +1. Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. +2. Update examples. +3. Enhance README. + +### Releases v1.0.3 + +1. Restructure code. +2. Fix example. +3. Enhance README. + +### Releases v1.0.2 + +1. Basic hardware timers for ESP8266. +2. Fix compatibility issue causing compiler error while using Arduino IDEs before 1.8.10 and ESP8266 cores 2.5.2 and before +3. More hardware-initiated software-enabled timers +4. Longer time interval + diff --git a/libraries/ESP8266TimerInterrupt/examples/Argument_None/Argument_None.ino b/libraries/ESP8266TimerInterrupt/examples/Argument_None/Argument_None.ino new file mode 100644 index 0000000..438f1ff --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/Argument_None/Argument_None.ino @@ -0,0 +1,104 @@ +/**************************************************************************************************************************** + Argument_None.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ + +/* Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. +*/ + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 0 +#define _TIMERINTERRUPT_LOGLEVEL_ 0 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" + +#ifndef LED_BUILTIN + #define LED_BUILTIN 2 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED +#endif + +volatile uint32_t lastMillis = 0; + +void IRAM_ATTR TimerHandler() +{ + static bool toggle = false; + static bool started = false; + + if (!started) + { + started = true; + pinMode(LED_BUILTIN, OUTPUT); + } + +#if (TIMER_INTERRUPT_DEBUG > 0) + Serial.print("Delta ms = "); Serial.println(millis() - lastMillis); + lastMillis = millis(); +#endif + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; +} + +#define TIMER_INTERVAL_MS 1000 + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(300); + + Serial.print(F("\nStarting Argument_None on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + lastMillis = millis(); + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(lastMillis); + } + else + Serial.println(F("Can't set ITimer correctly. Select another freq. or interval")); +} + +void loop() +{ + +} diff --git a/libraries/ESP8266TimerInterrupt/examples/Change_Interval/Change_Interval.ino b/libraries/ESP8266TimerInterrupt/examples/Change_Interval/Change_Interval.ino new file mode 100644 index 0000000..d278d6d --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/Change_Interval/Change_Interval.ino @@ -0,0 +1,130 @@ +/**************************************************************************************************************************** + Change_Interval.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ + +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. +*/ + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 0 +#define _TIMERINTERRUPT_LOGLEVEL_ 0 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" + +#ifndef LED_BUILTIN + #define LED_BUILTIN D4 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED +#endif + + +#define TIMER_INTERVAL_MS 500 //1000 + +volatile uint32_t TimerCount = 0; + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +void printResult(uint32_t currTime) +{ + Serial.print(F("Time = ")); Serial.print(currTime); + Serial.print(F(", TimerCount = ")); Serial.println(TimerCount); +} + +void TimerHandler() +{ + static bool toggle = false; + + // Flag for checking to be sure ISR is working as Serial.print is not OK here in ISR + TimerCount++; + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; +} + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + while (!Serial); + + delay(300); + + Serial.print(F("\nStarting Change_Interval on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(millis()); + } + else + Serial.println(F("Can't set ITimer. Select another freq. or timer")); +} + +#define CHECK_INTERVAL_MS 10000L +#define CHANGE_INTERVAL_MS 20000L + +void loop() +{ + static uint32_t lastTime = 0; + static uint32_t lastChangeTime = 0; + static uint32_t currTime; + static uint32_t multFactor = 0; + + currTime = millis(); + + if (currTime - lastTime > CHECK_INTERVAL_MS) + { + printResult(currTime); + lastTime = currTime; + + if (currTime - lastChangeTime > CHANGE_INTERVAL_MS) + { + //setInterval(unsigned long interval, timerCallback callback) + multFactor = (multFactor + 1) % 2; + + ITimer.setInterval(TIMER_INTERVAL_MS * 1000 * (multFactor + 1), TimerHandler); + + Serial.print(F("Changing Interval, Timer = ")); Serial.println(TIMER_INTERVAL_MS * (multFactor + 1)); + + lastChangeTime = currTime; + } + } +} diff --git a/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino b/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino new file mode 100644 index 0000000..0c320d8 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array/ISR_16_Timers_Array.ino @@ -0,0 +1,438 @@ +/**************************************************************************************************************************** + ISR_16_Timers_Array.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state + + This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. + Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet + and Blynk services. You can also have many (up to 16) timers to use. + This non-being-blocked important feature is absolutely necessary for mission-critical tasks. + You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate + In this super simple example, you don't see much different after Blynk is connected, because of no competing task is + written +*/ + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +// Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 2 +#define _TIMERINTERRUPT_LOGLEVEL_ 0 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" +#include "ESP8266_ISR_Timer.h" + +#include // https://github.com/jfturcot/SimpleTimer + +#ifndef LED_BUILTIN + #define LED_BUILTIN 2 +#endif + +#ifndef LED_BLUE + #define LED_BLUE 25 +#endif + +#ifndef LED_RED + #define LED_RED 27 +#endif + +#define HW_TIMER_INTERVAL_MS 1L + +volatile uint32_t startMillis = 0; + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +// Init BlynkTimer +ESP8266_ISR_Timer ISR_Timer; + +#define LED_TOGGLE_INTERVAL_MS 2000L + +void IRAM_ATTR TimerHandler() +{ + static bool toggle = false; + static bool started = false; + static int timeRun = 0; + + ISR_Timer.run(); + + // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s + if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS) ) + { + timeRun = 0; + + if (!started) + { + started = true; + pinMode(LED_BUILTIN, OUTPUT); + } + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; + } +} + +#define NUMBER_ISR_TIMERS 16 + +// You can assign any interval for any timer here, in milliseconds +uint32_t TimerInterval[NUMBER_ISR_TIMERS] = +{ + 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L, + 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L +}; + +typedef void (*irqCallback) (); + +#if (TIMER_INTERRUPT_DEBUG > 0) +void printStatus(uint16_t index, unsigned long deltaMillis, unsigned long currentMillis) +{ + Serial.print(TimerInterval[index] / 1000); Serial.print("s: Delta ms = "); Serial.print(deltaMillis); + Serial.print(", ms = "); Serial.println(currentMillis); +} +#endif + +// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething0() +{ +#if (TIMER_INTERRUPT_DEBUG > 0) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(0, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething1() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(1, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething2() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(2, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething3() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(3, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething4() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(4, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething5() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(5, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething6() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(6, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething7() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(7, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething8() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(8, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething9() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(9, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething10() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(10, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething11() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(11, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething12() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(12, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething13() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(13, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething14() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(14, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething15() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(15, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = +{ + doingSomething0, doingSomething1, doingSomething2, doingSomething3, + doingSomething4, doingSomething5, doingSomething6, doingSomething7, + doingSomething8, doingSomething9, doingSomething10, doingSomething11, + doingSomething12, doingSomething13, doingSomething14, doingSomething15 +}; + +//////////////////////////////////////////////// + + +#define SIMPLE_TIMER_MS 2000L + +// Init SimpleTimer +SimpleTimer simpleTimer; + +// Here is software Timer, you can do somewhat fancy stuffs without many issues. +// But always avoid +// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead +// 2. Very long "do", "while", "for" loops without predetermined exit time. +void simpleTimerDoingSomething2s() +{ + static unsigned long previousMillis = startMillis; + + Serial.print(F("simpleTimerDoingSomething2s: Delta programmed ms = ")); Serial.print(SIMPLE_TIMER_MS); + Serial.print(F(", actual = ")); Serial.println(millis() - previousMillis); + + previousMillis = millis(); +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(300); + + Serial.print(F("\nStarting ISR_16_Timers_Array on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + startMillis = millis(); + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(startMillis); + } + else + Serial.println(F("Can't set ITimer. Select another freq. or timer")); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each SAMD_ISR_Timer + for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) + { + ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); + } + + // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. + simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); +} + +#define BLOCKING_TIME_MS 10000L + +void loop() +{ + // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer + // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer + // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) + // While that of ISR_Timer is still prefect. + delay(BLOCKING_TIME_MS); + + // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary + // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. + simpleTimer.run(); +} diff --git a/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino b/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino new file mode 100644 index 0000000..cb73d5f --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino @@ -0,0 +1,367 @@ +/**************************************************************************************************************************** + ISR_16_Timers_Array_Complex.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. + Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet + and Blynk services. You can also have many (up to 16) timers to use. + This non-being-blocked important feature is absolutely necessary for mission-critical tasks. + You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate + In this super simple example, you don't see much different after Blynk is connected, because of no competing task is + written +*/ + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +// Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 0 +#define _TIMERINTERRUPT_LOGLEVEL_ 4 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" +#include "ESP8266_ISR_Timer.h" + +#include // https://github.com/jfturcot/SimpleTimer + +#ifndef LED_BUILTIN + #define LED_BUILTIN 2 +#endif + +#ifndef LED_BLUE + #define LED_BLUE 25 +#endif + +#ifndef LED_RED + #define LED_RED 27 +#endif + +#define HW_TIMER_INTERVAL_US 10000L + +volatile uint32_t startMillis = 0; + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +// Init ESP8266_ISR_Timer +ESP8266_ISR_Timer ISR_Timer; + +#define LED_TOGGLE_INTERVAL_MS 2000L + +void IRAM_ATTR TimerHandler() +{ + static bool toggle = false; + static int timeRun = 0; + + ISR_Timer.run(); + + // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s + if (++timeRun == ((LED_TOGGLE_INTERVAL_MS * 1000) / HW_TIMER_INTERVAL_US) ) + { + timeRun = 0; + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; + } +} + +///////////////////////////////////////////////// + +#define NUMBER_ISR_TIMERS 16 + +typedef void (*irqCallback) (); + +///////////////////////////////////////////////// + +#define USE_COMPLEX_STRUCT true + +#if USE_COMPLEX_STRUCT + +typedef struct +{ + irqCallback irqCallbackFunc; + uint32_t TimerInterval; + unsigned long deltaMillis; + unsigned long previousMillis; +} ISRTimerData; + +// In ESP8266, avoid doing something fancy in ISR, for example Serial.print() +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash + +void doingSomething(int index); + +#else + +volatile unsigned long deltaMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +volatile unsigned long previousMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +// You can assign any interval for any timer here, in milliseconds +uint32_t TimerInterval[NUMBER_ISR_TIMERS] = +{ + 5000L, 10000L, 15000L, 20000L, 25000L, 30000L, 35000L, 40000L, + 45000L, 50000L, 55000L, 60000L, 65000L, 70000L, 75000L, 80000L +}; + +void doingSomething(int index) +{ + unsigned long currentMillis = millis(); + + deltaMillis[index] = currentMillis - previousMillis[index]; + previousMillis[index] = currentMillis; +} + +#endif + +//////////////////////////////////// +// Shared +//////////////////////////////////// + +void doingSomething0() +{ + doingSomething(0); +} + +void doingSomething1() +{ + doingSomething(1); +} + +void doingSomething2() +{ + doingSomething(2); +} + +void doingSomething3() +{ + doingSomething(3); +} + +void doingSomething4() +{ + doingSomething(4); +} + +void doingSomething5() +{ + doingSomething(5); +} + +void doingSomething6() +{ + doingSomething(6); +} + +void doingSomething7() +{ + doingSomething(7); +} + +void doingSomething8() +{ + doingSomething(8); +} + +void doingSomething9() +{ + doingSomething(9); +} + +void doingSomething10() +{ + doingSomething(10); +} + +void doingSomething11() +{ + doingSomething(11); +} + +void doingSomething12() +{ + doingSomething(12); +} + +void doingSomething13() +{ + doingSomething(13); +} + +void doingSomething14() +{ + doingSomething(14); +} + +void doingSomething15() +{ + doingSomething(15); +} + +#if USE_COMPLEX_STRUCT + +ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] = +{ + //irqCallbackFunc, TimerInterval, deltaMillis, previousMillis + { doingSomething0, 5000L, 0, 0 }, + { doingSomething1, 10000L, 0, 0 }, + { doingSomething2, 15000L, 0, 0 }, + { doingSomething3, 20000L, 0, 0 }, + { doingSomething4, 25000L, 0, 0 }, + { doingSomething5, 30000L, 0, 0 }, + { doingSomething6, 35000L, 0, 0 }, + { doingSomething7, 40000L, 0, 0 }, + { doingSomething8, 45000L, 0, 0 }, + { doingSomething9, 50000L, 0, 0 }, + { doingSomething10, 55000L, 0, 0 }, + { doingSomething11, 60000L, 0, 0 }, + { doingSomething12, 65000L, 0, 0 }, + { doingSomething13, 70000L, 0, 0 }, + { doingSomething14, 75000L, 0, 0 }, + { doingSomething15, 80000L, 0, 0 } +}; + +void doingSomething(int index) +{ + unsigned long currentMillis = millis(); + + curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis; + curISRTimerData[index].previousMillis = currentMillis; +} + +#else + +irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = +{ + doingSomething0, doingSomething1, doingSomething2, doingSomething3, + doingSomething4, doingSomething5, doingSomething6, doingSomething7, + doingSomething8, doingSomething9, doingSomething10, doingSomething11, + doingSomething12, doingSomething13, doingSomething14, doingSomething15 +}; + +#endif +/////////////////////////////////////////// + +#define SIMPLE_TIMER_MS 2000L + +// Init SimpleTimer +SimpleTimer simpleTimer; + +// Here is software Timer, you can do somewhat fancy stuffs without many issues. +// But always avoid +// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead +// 2. Very long "do", "while", "for" loops without predetermined exit time. +void simpleTimerDoingSomething2s() +{ + static unsigned long previousMillis = startMillis; + + unsigned long currMillis = millis(); + + Serial.print(F("SimpleTimer : ")); Serial.print(SIMPLE_TIMER_MS / 1000); + Serial.print(F(", ms : ")); Serial.print(currMillis); + Serial.print(F(", Dms : ")); Serial.println(currMillis - previousMillis); + + for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) + { +#if USE_COMPLEX_STRUCT + Serial.print(F("Timer : ")); Serial.print(i); + Serial.print(F(", programmed : ")); Serial.print(curISRTimerData[i].TimerInterval); + Serial.print(F(", actual : ")); Serial.println(curISRTimerData[i].deltaMillis); +#else + Serial.print(F("Timer : ")); Serial.print(i); + Serial.print(F(", programmed : ")); Serial.print(TimerInterval[i]); + Serial.print(F(", actual : ")); Serial.println(deltaMillis[i]); +#endif + } + + previousMillis = currMillis; +} + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + + Serial.begin(115200); + while (!Serial); + + delay(300); + + Serial.print(F("\nStarting ISR_16_Timers_Array_Complex on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler)) + { + startMillis = millis(); + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(startMillis); + } + else + Serial.println(F("Can't set ITimer. Select another freq. or timer")); + + startMillis = millis(); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each ISR_Timer + for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) + { +#if USE_COMPLEX_STRUCT + curISRTimerData[i].previousMillis = startMillis; + ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc); +#else + previousMillis[i] = millis(); + ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); +#endif + } + + // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. + simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); +} + +#define BLOCKING_TIME_MS 10000L + +void loop() +{ + // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer + // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer + // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) + // While that of ISR_Timer is still prefect. + delay(BLOCKING_TIME_MS); + + // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary + // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. + simpleTimer.run(); +} diff --git a/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array_OneShot/ISR_16_Timers_Array_OneShot.ino b/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array_OneShot/ISR_16_Timers_Array_OneShot.ino new file mode 100644 index 0000000..67d58f3 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/ISR_16_Timers_Array_OneShot/ISR_16_Timers_Array_OneShot.ino @@ -0,0 +1,440 @@ +/**************************************************************************************************************************** + ISR_16_Timers_Array_OneShot.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ +/* + Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + We use interrupt to detect whenever the SW is active, set a flag then use timer to count the time between active state + + This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs. + Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet + and Blynk services. You can also have many (up to 16) timers to use. + This non-being-blocked important feature is absolutely necessary for mission-critical tasks. + You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate + In this super simple example, you don't see much different after Blynk is connected, because of no competing task is + written +*/ + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +// Don't define TIMER_INTERRUPT_DEBUG > 2. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 2 +#define _TIMERINTERRUPT_LOGLEVEL_ 0 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" +#include "ESP8266_ISR_Timer.h" + +#include // https://github.com/jfturcot/SimpleTimer + +#ifndef LED_BUILTIN + #define LED_BUILTIN 2 +#endif + +#ifndef LED_BLUE + #define LED_BLUE 25 +#endif + +#ifndef LED_RED + #define LED_RED 27 +#endif + +#define HW_TIMER_INTERVAL_MS 1L + +volatile uint32_t startMillis = 0; + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +// Init BlynkTimer +ESP8266_ISR_Timer ISR_Timer; + +#define LED_TOGGLE_INTERVAL_MS 2000L + +void IRAM_ATTR TimerHandler() +{ + static bool toggle = false; + static bool started = false; + static int timeRun = 0; + + ISR_Timer.run(); + + // Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s + if (++timeRun == (LED_TOGGLE_INTERVAL_MS / HW_TIMER_INTERVAL_MS) ) + { + timeRun = 0; + + if (!started) + { + started = true; + pinMode(LED_BUILTIN, OUTPUT); + } + + //timer interrupt toggles pin LED_BUILTIN + digitalWrite(LED_BUILTIN, toggle); + toggle = !toggle; + } +} + +#define NUMBER_ISR_TIMERS 16 + +// You can assign any interval for any timer here, in milliseconds +uint32_t TimerInterval[NUMBER_ISR_TIMERS] = +{ + 1000L, 2000L, 3000L, 4000L, 5000L, 6000L, 7000L, 8000L, + 9000L, 10000L, 11000L, 12000L, 13000L, 14000L, 15000L, 16000L +}; + +typedef void (*irqCallback) (); + +#if (TIMER_INTERRUPT_DEBUG > 0) +void printStatus(uint16_t index, unsigned long deltaMillis, unsigned long currentMillis) +{ + Serial.print(TimerInterval[index] / 1000); Serial.print("s: Delta ms = "); Serial.print(deltaMillis); + Serial.print(", ms = "); Serial.println(currentMillis); +} +#endif + +// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument +// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment +// Or you can get this run-time error / crash +void doingSomething0() +{ +#if (TIMER_INTERRUPT_DEBUG > 0) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(0, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething1() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(1, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething2() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(2, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething3() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(3, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething4() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(4, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething5() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(5, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething6() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(6, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething7() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(7, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething8() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(8, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething9() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(9, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething10() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(10, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething11() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(11, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething12() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(12, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething13() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(13, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething14() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(14, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +void doingSomething15() +{ +#if (TIMER_INTERRUPT_DEBUG > 1) + static unsigned long previousMillis = startMillis; + + unsigned long currentMillis = millis(); + unsigned long deltaMillis = currentMillis - previousMillis; + + printStatus(15, deltaMillis, currentMillis); + + previousMillis = currentMillis; +#endif +} + +irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = +{ + doingSomething0, doingSomething1, doingSomething2, doingSomething3, + doingSomething4, doingSomething5, doingSomething6, doingSomething7, + doingSomething8, doingSomething9, doingSomething10, doingSomething11, + doingSomething12, doingSomething13, doingSomething14, doingSomething15 +}; + +//////////////////////////////////////////////// + + +#define SIMPLE_TIMER_MS 2000L + +// Init SimpleTimer +SimpleTimer simpleTimer; + +// Here is software Timer, you can do somewhat fancy stuffs without many issues. +// But always avoid +// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead +// 2. Very long "do", "while", "for" loops without predetermined exit time. +void simpleTimerDoingSomething2s() +{ + static unsigned long previousMillis = startMillis; + + Serial.print(F("simpleTimerDoingSomething2s: Delta programmed ms = ")); Serial.print(SIMPLE_TIMER_MS); + Serial.print(F(", actual = ")); Serial.println(millis() - previousMillis); + + previousMillis = millis(); +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(300); + + Serial.print(F("\nStarting ISR_16_Timers_Array_OneShot on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + startMillis = millis(); + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(startMillis); + } + else + Serial.println(F("Can't set ITimer. Select another freq. or timer")); + + // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary + // You can use up to 16 timer for each SAMD_ISR_Timer + for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++) + { + //ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); + // Use this for one shot ISR TImer + ISR_Timer.setTimeout(TimerInterval[i], irqCallbackFunc[i]); + } + + // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. + simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); +} + +#define BLOCKING_TIME_MS 10000L + +void loop() +{ + // This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer + // You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer + // The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) + // While that of ISR_Timer is still prefect. + delay(BLOCKING_TIME_MS); + + // You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary + // You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. + simpleTimer.run(); +} diff --git a/libraries/ESP8266TimerInterrupt/examples/ISR_RPM_Measure/ISR_RPM_Measure.ino b/libraries/ESP8266TimerInterrupt/examples/ISR_RPM_Measure/ISR_RPM_Measure.ino new file mode 100644 index 0000000..ee89db4 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/ISR_RPM_Measure/ISR_RPM_Measure.ino @@ -0,0 +1,159 @@ +/**************************************************************************************************************************** + ISR_RPM_Measure.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ + +/* Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + We use interrupt to detect whenever the SW is active, set a flag + then use timer to count the time between active state +*/ + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 2 +#define _TIMERINTERRUPT_LOGLEVEL_ 0 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" + +#define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266 + +unsigned int interruptPin = PIN_D1; + +#define TIMER_INTERVAL_MS 1 +#define DEBOUNCING_INTERVAL_MS 80 + +#define LOCAL_DEBUG 1 + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +volatile unsigned long rotationTime = 0; +float RPM = 0.00; +float avgRPM = 0.00; + +volatile int debounceCounter; + +volatile bool activeState = false; + +void IRAM_ATTR detectRotation() +{ + activeState = true; +} + +void IRAM_ATTR TimerHandler() +{ + if ( activeState ) + { + // Reset to prepare for next round of interrupt + activeState = false; + + if (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS ) + { + + //min time between pulses has passed + RPM = (float) ( 60000.0f / ( rotationTime * TIMER_INTERVAL_MS ) ); + + avgRPM = ( 2 * avgRPM + RPM) / 3, + +#if (TIMER_INTERRUPT_DEBUG > 1) + Serial.print("RPM = "); Serial.print(avgRPM); + Serial.print(", rotationTime ms = "); Serial.println(rotationTime * TIMER_INTERVAL_MS); +#endif + + rotationTime = 0; + debounceCounter = 0; + } + else + debounceCounter++; + } + else + { + debounceCounter++; + } + + if (rotationTime >= 5000) + { + // If idle, set RPM to 0, don't increase rotationTime + RPM = 0; + +#if (TIMER_INTERRUPT_DEBUG > 1) + Serial.print("RPM = "); Serial.print(RPM); Serial.print(", rotationTime = "); Serial.println(rotationTime); +#endif + + rotationTime = 0; + } + else + { + rotationTime++; + } +} + +void setup() +{ + pinMode(interruptPin, INPUT_PULLUP); + + Serial.begin(115200); + while (!Serial); + + delay(300); + + Serial.print(F("\nStarting ISR_RPM_Measure on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(millis()); + } + else + Serial.println(F("Can't set ITimer. Select another freq. or timer")); + + // Assumming the interruptPin will go LOW + attachInterrupt(digitalPinToInterrupt(interruptPin), detectRotation, FALLING); +} + +void loop() +{ + +} diff --git a/libraries/ESP8266TimerInterrupt/examples/RPM_Measure/RPM_Measure.ino b/libraries/ESP8266TimerInterrupt/examples/RPM_Measure/RPM_Measure.ino new file mode 100644 index 0000000..16673f9 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/RPM_Measure/RPM_Measure.ino @@ -0,0 +1,146 @@ +/**************************************************************************************************************************** + RPM_Measure.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ + +/* Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + RPM Measuring uses high frequency hardware timer 1Hz == 1ms) to measure the time from of one rotation, in ms + then convert to RPM. One rotation is detected by reading the state of a magnetic REED SW or IR LED Sensor + Asssuming LOW is active. + For example: Max speed is 600RPM => 10 RPS => minimum 100ms a rotation. We'll use 80ms for debouncing + If the time between active state is less than 8ms => consider noise. + RPM = 60000 / (rotation time in ms) + + You can also use interrupt to detect whenever the SW is active, set a flag + then use timer to count the time between active state +*/ +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 2 +#define _TIMERINTERRUPT_LOGLEVEL_ 0 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" + +#define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266 + +unsigned int SWPin = PIN_D1; + +#define TIMER_INTERVAL_MS 1 +#define DEBOUNCING_INTERVAL_MS 80 + +#define LOCAL_DEBUG 1 + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +volatile unsigned long rotationTime = 0; + +float RPM = 0.00; +float avgRPM = 0.00; + +volatile int debounceCounter; + +void IRAM_ATTR TimerHandler() +{ + static bool started = false; + + if (!started) + { + started = true; + pinMode(SWPin, INPUT_PULLUP); + } + + if ( !digitalRead(SWPin) && (debounceCounter >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS ) ) + { + //min time between pulses has passed + RPM = (float) ( 60000.0f / ( rotationTime * TIMER_INTERVAL_MS ) ); + + avgRPM = ( 2 * avgRPM + RPM) / 3, + +#if (LOCAL_DEBUG > 0) + Serial.print("RPM = "); Serial.print(avgRPM); + Serial.print(", rotationTime ms = "); Serial.println(rotationTime * TIMER_INTERVAL_MS); +#endif + + rotationTime = 0; + debounceCounter = 0; + } + else + { + debounceCounter++; + } + + if (rotationTime >= 5000) + { + // If idle, set RPM to 0, don't increase rotationTime + RPM = 0; + +#if (LOCAL_DEBUG > 0) + Serial.print("RPM = "); Serial.print(RPM); Serial.print(", rotationTime = "); Serial.println(rotationTime); +#endif + + rotationTime = 0; + } + else + { + rotationTime++; + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(200); + + Serial.print(F("\nStarting RPM_Measure on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(millis()); + } + else + Serial.println(F("Can't set ITimer. Select another freq. or timer")); +} + +void loop() +{ + +} diff --git a/libraries/ESP8266TimerInterrupt/examples/SwitchDebounce/SwitchDebounce.ino b/libraries/ESP8266TimerInterrupt/examples/SwitchDebounce/SwitchDebounce.ino new file mode 100644 index 0000000..30cd290 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/SwitchDebounce/SwitchDebounce.ino @@ -0,0 +1,179 @@ +/**************************************************************************************************************************** + SwitchDebounce.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ +/* Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. + + Switch Debouncing uses high frequency hardware timer 50Hz == 20ms) to measure the time from the SW is pressed, + debouncing time is 100ms => SW is considered pressed if timer count is > 5, then call / flag SW is pressed + When the SW is released, timer will count (debounce) until more than 50ms until consider SW is released. + We can set to flag or call a function whenever SW is pressed more than certain predetermined time, even before + SW is released. +*/ +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 1 +#define _TIMERINTERRUPT_LOGLEVEL_ 0 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" + +#define PIN_D1 5 // Pin D1 mapped to pin GPIO5 of ESP8266 + +unsigned int SWPin = PIN_D1; + +#define TIMER_INTERVAL_MS 20 +#define DEBOUNCING_INTERVAL_MS 100 +#define LONG_PRESS_INTERVAL_MS 5000 + +//#define LOCAL_DEBUG 1 + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +volatile bool SWPressed = false; +volatile bool SWLongPressed = false; + +void IRAM_ATTR TimerHandler() +{ + static unsigned int debounceCountSWPressed = 0; + static unsigned int debounceCountSWReleased = 0; + +#if (TIMER_INTERRUPT_DEBUG > 0) + static unsigned long SWPressedTime; + static unsigned long SWReleasedTime; + + static unsigned long currentMillis; +#endif + + currentMillis = millis(); + + if ( (!digitalRead(SWPin)) ) + { + // Start debouncing counting debounceCountSWPressed and clear debounceCountSWReleased + debounceCountSWReleased = 0; + + if (++debounceCountSWPressed >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS) + { + // Call and flag SWPressed + if (!SWPressed) + { +#if (TIMER_INTERRUPT_DEBUG > 0) + SWPressedTime = currentMillis; + + Serial.print("SW Press, from millis() = "); Serial.println(SWPressedTime - DEBOUNCING_INTERVAL_MS); +#endif + + SWPressed = true; + // Do something for SWPressed here in ISR + // But it's better to use outside software timer to do your job instead of inside ISR + //Your_Response_To_Press(); + } + + if (debounceCountSWPressed >= LONG_PRESS_INTERVAL_MS / TIMER_INTERVAL_MS) + { + // Call and flag SWLongPressed + if (!SWLongPressed) + { +#if (TIMER_INTERRUPT_DEBUG > 0) + Serial.print("SW Long Pressed, total time ms = "); Serial.print(currentMillis); + Serial.print(" - "); Serial.print(SWPressedTime - DEBOUNCING_INTERVAL_MS); + Serial.print(" = "); Serial.println(currentMillis - SWPressedTime + DEBOUNCING_INTERVAL_MS); +#endif + + SWLongPressed = true; + // Do something for SWLongPressed here in ISR + // But it's better to use outside software timer to do your job instead of inside ISR + //Your_Response_To_Long_Press(); + } + } + } + } + else + { + // Start debouncing counting debounceCountSWReleased and clear debounceCountSWPressed + if ( SWPressed && (++debounceCountSWReleased >= DEBOUNCING_INTERVAL_MS / TIMER_INTERVAL_MS)) + { + // Call and flag SWPressed +#if (TIMER_INTERRUPT_DEBUGEBUG > 0) + SWReleasedTime = millis(); + + // Call and flag SWPressed + Serial.print("SW Released, from millis() = "); Serial.println(millis()); +#endif + + SWPressed = false; + SWLongPressed = false; + + // Do something for !SWPressed here in ISR + // But it's better to use outside software timer to do your job instead of inside ISR + //Your_Response_To_Release(); + + // Call and flag SWPressed +#if (TIMER_INTERRUPT_DEBUG > 0) + Serial.print("SW Pressed total time ms = "); Serial.println(millis() - SWPressedTime); +#endif + + debounceCountSWPressed = 0; + } + } +} + +void setup() +{ + pinMode(SWPin, INPUT_PULLUP); + + Serial.begin(115200); + while (!Serial); + + delay(200); + + Serial.print(F("\nStarting SwitchDebounce on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(millis()); + } + else + Serial.println(F("Can't set ITimer. Select another freq. or timer")); +} + +void loop() +{ + +} diff --git a/libraries/ESP8266TimerInterrupt/examples/TimerInterruptTest/TimerInterruptTest.ino b/libraries/ESP8266TimerInterrupt/examples/TimerInterruptTest/TimerInterruptTest.ino new file mode 100644 index 0000000..7ebb21a --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/TimerInterruptTest/TimerInterruptTest.ino @@ -0,0 +1,106 @@ +/**************************************************************************************************************************** + TimerInterruptTest.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. +*****************************************************************************************************************************/ +/* Notes: + Special design is necessary to share data between interrupt code and the rest of your program. + Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume + variable can not spontaneously change. Because your function may change variables while your program is using them, + the compiler needs this hint. But volatile alone is often not enough. + + When accessing shared variables, usually interrupts must be disabled. Even with volatile, + if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. + If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled + or the entire sequence of your code which accesses the data. +*/ + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h" +// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4 +// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define TIMER_INTERRUPT_DEBUG 1 +#define _TIMERINTERRUPT_LOGLEVEL_ 1 + +// Select a Timer Clock +#define USING_TIM_DIV1 false // for shortest and most accurate timer +#define USING_TIM_DIV16 false // for medium time and medium accurate timer +#define USING_TIM_DIV256 true // for longest timer but least accurate. Default + +#include "ESP8266TimerInterrupt.h" + +#define BUILTIN_LED 2 // Pin D4 mapped to pin GPIO2/TXD1 of ESP8266, NodeMCU and WeMoS, control on-board LED + +volatile bool statusLed = false; +volatile uint32_t lastMillis = 0; + +#define TIMER_INTERVAL_MS 1000 + +// Init ESP8266 timer 1 +ESP8266Timer ITimer; + +//======================================================================= +void IRAM_ATTR TimerHandler() +{ + static bool started = false; + + if (!started) + { + started = true; + pinMode(BUILTIN_LED, OUTPUT); + } + + digitalWrite(BUILTIN_LED, statusLed); //Toggle LED Pin + statusLed = !statusLed; + +#if (TIMER_INTERRUPT_DEBUG > 0) + Serial.println("Delta ms = " + String(millis() - lastMillis)); + lastMillis = millis(); +#endif +} +//======================================================================= +// Setup +//======================================================================= +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(300); + + Serial.print(F("\nStarting TimerInterruptTest on ")); Serial.println(ARDUINO_BOARD); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + if (ITimer.attachInterruptInterval(TIMER_INTERVAL_MS * 1000, TimerHandler)) + { + lastMillis = millis(); + Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(lastMillis); + } + else + Serial.println(F("Can't set ITimer correctly. Select another freq. or interval")); + +} +//======================================================================= +// MAIN LOOP +//======================================================================= +void loop() +{ +} diff --git a/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.cpp b/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.cpp new file mode 100644 index 0000000..add7cc1 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.cpp @@ -0,0 +1,12 @@ +/**************************************************************************************************************************** + multiFileProject.cpp + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#include "multiFileProject.h" diff --git a/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.h b/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.h new file mode 100644 index 0000000..428a506 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.h @@ -0,0 +1,16 @@ +/**************************************************************************************************************************** + multiFileProject.h + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#pragma once + +// Can be included as many times as necessary, without `Multiple Definitions` Linker Error +#include "ESP8266TimerInterrupt.h" +#include "ESP8266_ISR_Timer.hpp" diff --git a/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.ino b/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.ino new file mode 100644 index 0000000..1fede00 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/examples/multiFileProject/multiFileProject.ino @@ -0,0 +1,46 @@ +/**************************************************************************************************************************** + multiFileProject.ino + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license +*****************************************************************************************************************************/ + +// To demo how to include files in multi-file Projects + +#ifndef ESP8266 + #error This code is designed to run on ESP8266 platform! Please check your Tools->Board setting. +#endif + +#define ESP8266_TIMER_INTERRUPT_VERSION_MIN_TARGET "ESP8266TimerInterrupt v1.6.0" +#define ESP8266_TIMER_INTERRUPT_VERSION_MIN 1006000 + +#include "multiFileProject.h" + +// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error +#include "ESP8266TimerInterrupt.h" +#include "ESP8266_ISR_Timer.h" + + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + Serial.println("\nStart multiFileProject"); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION); + +#if defined(ESP8266_TIMER_INTERRUPT_VERSION_MIN) + if (ESP8266_TIMER_INTERRUPT_VERSION_INT < ESP8266_TIMER_INTERRUPT_VERSION_MIN) + { + Serial.print("Warning. Must use this example on Version equal or later than : "); + Serial.println(ESP8266_TIMER_INTERRUPT_VERSION_MIN_TARGET); + } +#endif +} + +void loop() +{ + // put your main code here, to run repeatedly: +} diff --git a/libraries/ESP8266TimerInterrupt/keywords.txt b/libraries/ESP8266TimerInterrupt/keywords.txt new file mode 100644 index 0000000..c202410 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/keywords.txt @@ -0,0 +1,63 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +ESP8266TimerInterrupt KEYWORD1 +ESP8266Timer KEYWORD1 +ESP8266_ISR_Timer KEYWORD1 +ISRTimer KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +setFrequency KEYWORD2 +setInterval KEYWORD2 +attachInterrupt KEYWORD2 +attachInterruptInterval KEYWORD2 +detachInterrupt KEYWORD2 +disableTimer KEYWORD2 +reattachInterrupt KEYWORD2 +enableTimer KEYWORD2 +stopTimer KEYWORD2 +restartTimer KEYWORD2 +init KEYWORD2 +run KEYWORD2 +setTimeout KEYWORD2 +setTimer KEYWORD2 +changeInterval KEYWORD2 +deleteTimer KEYWORD2 +restartTimer KEYWORD2 +isEnabled KEYWORD2 +enable KEYWORD2 +disable KEYWORD2 +enableAll KEYWORD2 +disableAll KEYWORD2 +toggle KEYWORD2 +getNumTimers KEYWORD2 +getNumAvailableTimers KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +ESP8266_TIMER_INTERRUPT_VERSION LITERAL1 +ESP8266_TIMER_INTERRUPT_VERSION_MAJOR LITERAL1 +ESP8266_TIMER_INTERRUPT_VERSION_MINOR LITERAL1 +ESP8266_TIMER_INTERRUPT_VERSION_PATCH LITERAL1 +ESP8266_TIMER_INTERRUPT_VERSION_INT LITERAL1 + +MAX_ESP8266_NUM_TIMERS LITERAL1 +MAX_ESP8266_COUNT LITERAL1 + +TIM_DIV1_CLOCK LITERAL1 +TIM_DIV16_CLOCK LITERAL1 +TIM_DIV256_CLOCK LITERAL1 + +TIM_CLOCK_FREQ LITERAL1 +TIM_DIV LITERAL1 + +USING_TIM_DIV1 LITERAL1 +USING_TIM_DIV16 LITERAL1 +USING_TIM_DIV256 LITERAL1 + diff --git a/libraries/ESP8266TimerInterrupt/library.json b/libraries/ESP8266TimerInterrupt/library.json new file mode 100644 index 0000000..f5f66c7 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/library.json @@ -0,0 +1,30 @@ +{ + "name": "ESP8266TimerInterrupt", + "version": "1.6.0", + "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", + "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.", + "authors": + { + "name": "Khoi Hoang", + "url": "https://github.com/khoih-prog", + "maintainer": true + }, + "repository": + { + "type": "git", + "url": "https://github.com/khoih-prog/ESP8266TimerInterrupt" + }, + "homepage": "https://github.com/khoih-prog/ESP8266TimerInterrupt", + "export": { + "exclude": [ + "linux", + "extras", + "tests" + ] + }, + "license": "MIT", + "frameworks": "*", + "platforms": "espressif8266", + "examples": "examples/*/*/*.ino", + "headers": ["ESP8266TimerInterrupt.h", "ESP8266_ISR_Timer.h", "ESP8266_ISR_Timer.hpp"] +} diff --git a/libraries/ESP8266TimerInterrupt/library.properties b/libraries/ESP8266TimerInterrupt/library.properties new file mode 100644 index 0000000..645145a --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/library.properties @@ -0,0 +1,12 @@ +name=ESP8266TimerInterrupt +version=1.6.0 +author=Khoi Hoang +maintainer=Khoi Hoang +sentence=This library enables you to use Interrupt from Hardware Timers on an ESP8266-based board. +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. +category=Device Control +url=https://github.com/khoih-prog/ESP8266TimerInterrupt +architectures=esp8266 +repository=https://github.com/khoih-prog/ESP8266TimerInterrupt +license=MIT +includes=ESP8266TimerInterrupt.h,ESP8266_ISR_Timer.h,ESP8266_ISR_Timer.hpp diff --git a/libraries/ESP8266TimerInterrupt/platformio/platformio.ini b/libraries/ESP8266TimerInterrupt/platformio/platformio.ini new file mode 100644 index 0000000..a2b5295 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/platformio/platformio.ini @@ -0,0 +1,84 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +; ============================================================ +; chose environment: +; ESP8266 +; ============================================================ +default_envs = ESP8266 + +[env] +; ============================================================ +; Serial configuration +; choose upload speed, serial-monitor speed +; ============================================================ +upload_speed = 921600 +;upload_port = COM11 +;monitor_speed = 9600 +;monitor_port = COM11 + +; Checks for the compatibility with frameworks and dev/platforms +lib_compat_mode = strict + +lib_deps = + + +build_flags = +; set your debug output (default=Serial) + -D DEBUG_ESP_PORT=Serial +; comment the folowing line to enable WiFi debugging + -D NDEBUG + +[env:ESP8266] +platform = espressif8266 +framework = arduino +; ============================================================ +; Board configuration +; choose your board by uncommenting one of the following lines +; ============================================================ +;board = gen4iod +;board = huzzah +;board = oak +;board = esp_wroom_02 +;board = espduino +;board = espectro +;board = espino +;board = espresso_lite_v1 +;board = espresso_lite_v2 +;board = esp12e +;board = esp01_1m +;board = esp01 +;board = esp07 +;board = esp8285 +;board = heltec_wifi_kit_8 +;board = inventone +;board = nodemcu +board = nodemcuv2 +;board = modwifi +;board = phoenix_v1 +;board = phoenix_v2 +;board = sparkfunBlynk +;board = thing +;board = thingdev +;board = esp210 +;board = espinotee +;board = d1 +;board = d1_mini +;board = d1_mini_lite +;board = d1_mini_pro +;board = wifi_slot +;board = wifiduino +;board = wifinfo +;board = wio_link +;board = wio_node +;board = xinabox_cw01 +;board = esp32doit-devkit-v1 + diff --git a/libraries/ESP8266TimerInterrupt/src/ESP8266TimerInterrupt.h b/libraries/ESP8266TimerInterrupt/src/ESP8266TimerInterrupt.h new file mode 100644 index 0000000..0253bf0 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/src/ESP8266TimerInterrupt.h @@ -0,0 +1,249 @@ +/**************************************************************************************************************************** + ESP8266TimerInterrupt.h + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.6.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 23/11/2019 Initial coding + 1.0.1 K Hoang 25/11/2019 New release fixing compiler error + 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked + 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. + 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. + 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval + 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage + 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 + 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. + 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md + 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer + 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code +*****************************************************************************************************************************/ + +#pragma once + +#ifndef ESP8266TIMERINTERRUPT_H +#define ESP8266TIMERINTERRUPT_H + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +#ifndef ESP8266_TIMER_INTERRUPT_VERSION + #define ESP8266_TIMER_INTERRUPT_VERSION "ESP8266TimerInterrupt v1.6.0" + + + #define ESP8266_TIMER_INTERRUPT_VERSION_MAJOR 1 + #define ESP8266_TIMER_INTERRUPT_VERSION_MINOR 6 + #define ESP8266_TIMER_INTERRUPT_VERSION_PATCH 0 + + #define ESP8266_TIMER_INTERRUPT_VERSION_INT 1006000 + +#endif + +#ifndef TIMER_INTERRUPT_DEBUG + #define TIMER_INTERRUPT_DEBUG 0 +#endif + +#if defined(ARDUINO) + #if ARDUINO >= 100 + #include + #else + #include + #endif +#endif + +#include "TimerInterrupt_Generic_Debug.h" + +/* From /arduino-1.8.10/hardware/esp8266com/esp8266/cores/esp8266/esp8266_peri.h + + #define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr))) + #define ESP8266_DREG(addr) *((volatile uint32_t *)(0x3FF00000+(addr))) + #define ESP8266_CLOCK 80000000UL + + //CPU Register + #define CPU2X ESP8266_DREG(0x14) //when bit 0 is set, F_CPU = 160MHz +*/ + +/* From /arduino-1.8.10/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h + + //timer dividers + enum TIM_DIV_ENUM { + TIM_DIV1 = 0, // 80 / 160 MHz (80 / 160 ticks/us - 104857.588 us max) + TIM_DIV16 = 1, // 5 / 10 MHz (5 / 10 ticks/us - 1677721.4 us max) + TIM_DIV256 = 3 // 312.5 / 625 Khz (1 tick = 3.2 / 1.6 us - 26843542.4 us max) + }; + + //timer int_types + #define TIM_EDGE 0 + #define TIM_LEVEL 1 + //timer reload values + #define TIM_SINGLE 0 //on interrupt routine you need to write a new value to start the timer again + #define TIM_LOOP 1 //on interrupt the counter will start with the same value again + +*/ + +// ESP8266 only has one usable timer1, max count is only 8,388,607. So to get longer time, we use max available 256 divider +class ESP8266TimerInterrupt; + +typedef ESP8266TimerInterrupt ESP8266Timer; + +#define MAX_ESP8266_NUM_TIMERS 1 +#define MAX_ESP8266_COUNT 8388607 + +typedef void (*timer_callback) (); + +#define TIM_DIV1_CLOCK (80000000UL) // 80000000 / 1 = 80.0 MHz +#define TIM_DIV16_CLOCK (5000000UL) // 80000000 / 16 = 5.0 MHz +#define TIM_DIV256_CLOCK (312500UL) // 80000000 / 256 = 312.5 KHz + +#if ( defined(USING_TIM_DIV1) && USING_TIM_DIV1 ) + #warning Using TIM_DIV1_CLOCK for shortest and most accurate timer + #define TIM_CLOCK_FREQ TIM_DIV1_CLOCK + #define TIM_DIV TIM_DIV1 +#elif ( defined(USING_TIM_DIV16) && USING_TIM_DIV16 ) + #warning Using TIM_DIV16_CLOCK for medium time and medium accurate timer + #define TIM_CLOCK_FREQ TIM_DIV16_CLOCK + #define TIM_DIV TIM_DIV16 +#elif ( defined(USING_TIM_DIV256) && USING_TIM_DIV256 ) + #warning Using TIM_DIV256_CLOCK for longest timer but least accurate + #define TIM_CLOCK_FREQ TIM_DIV256_CLOCK + #define TIM_DIV TIM_DIV256 +#else + #warning Default to using TIM_DIV256_CLOCK for longest timer but least accurate + #define TIM_CLOCK_FREQ TIM_DIV256_CLOCK + #define TIM_DIV TIM_DIV256 +#endif + +class ESP8266TimerInterrupt +{ + private: + timer_callback _callback; // pointer to the callback function + float _frequency; // Timer frequency + uint32_t _timerCount; // count to activate timer + + public: + + ESP8266TimerInterrupt() + { + _frequency = 0; + _timerCount = 0; + _callback = NULL; + }; + + // frequency (in hertz) + bool setFrequency(const float& frequency, const timer_callback& callback) + { + bool isOKFlag = true; + float minFreq = (float) TIM_CLOCK_FREQ / MAX_ESP8266_COUNT; + + // ESP8266 only has one usable timer1, max count is only 8,388,607. So to get longer time, we use max available 256 divider + // Will use later if very low frequency is needed. + + if (frequency < minFreq) + { + TISR_LOGERROR3(F("ESP8266TimerInterrupt: Too long Timer, smallest frequency ="), minFreq, F(" for TIM_CLOCK_FREQ ="), TIM_CLOCK_FREQ); + + return false; + } + + _frequency = frequency; + _timerCount = (uint32_t) (TIM_CLOCK_FREQ / frequency); + _callback = callback; + + if ( _timerCount > MAX_ESP8266_COUNT) + { + _timerCount = MAX_ESP8266_COUNT; + // Flag error + isOKFlag = false; + } + + // count up + TISR_LOGWARN3(F("ESP8266TimerInterrupt: Timer _fre ="), _frequency, F(", _count ="), _timerCount); + + // Clock to timer (prescaler) is always 80MHz, even F_CPU is 160 MHz + + timer1_attachInterrupt(callback); + + timer1_write(_timerCount); + + // Interrupt on EGDE, autoloop + //timer1_enable(TIM_DIV256, TIM_EDGE, TIM_LOOP); + timer1_enable(TIM_DIV, TIM_EDGE, TIM_LOOP); + + return isOKFlag; + } + + // interval (in microseconds) + bool setInterval(const unsigned long& interval, const timer_callback& callback) + { + return setFrequency((float) (1000000.0f / interval), callback); + } + + bool attachInterrupt(const float& frequency, const timer_callback& callback) + { + return setFrequency(frequency, callback); + } + + // interval (in microseconds) + bool attachInterruptInterval(const unsigned long& interval, const timer_callback& callback) + { + return setFrequency( (float) ( 1000000.0f / interval), callback); + } + + void detachInterrupt() + { + timer1_disable(); + } + + void disableTimer() + { + timer1_disable(); + } + + void reattachInterrupt() + { + if ( (_frequency > 0) && (_timerCount > 0) && (_callback != NULL) ) + setFrequency(_frequency, _callback); + } + + void enableTimer() + { + reattachInterrupt(); + } + + // Just stop clock source, clear the count + void stopTimer() + { + timer1_disable(); + } + + // Just reconnect clock source, start current count from 0 + void restartTimer() + { + enableTimer(); + } +}; // class ESP8266TimerInterrupt + +#endif // ESP8266TIMERINTERRUPT_H diff --git a/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer-Impl.h b/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer-Impl.h new file mode 100644 index 0000000..4dddb94 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer-Impl.h @@ -0,0 +1,366 @@ +/**************************************************************************************************************************** + ESP8266_ISR_Timer-Impl.h + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.6.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 23/11/2019 Initial coding + 1.0.1 K Hoang 25/11/2019 New release fixing compiler error + 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked + 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. + 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. + 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval + 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage + 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 + 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. + 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md + 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer + 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code +*****************************************************************************************************************************/ + +#pragma once + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +#ifndef ISR_TIMER_GENERIC_IMPL_H +#define ISR_TIMER_GENERIC_IMPL_H + +//#include "ESP8266_ISR_Timer.h" +#include + +// Select time function: +//static inline unsigned long elapsed() { return micros(); } +static inline unsigned long elapsed() +{ + return millis(); +} + + +ESP8266_ISR_Timer::ESP8266_ISR_Timer() + : numTimers (-1) +{ +} + +void IRAM_ATTR ESP8266_ISR_Timer::init() +{ + unsigned long current_millis = millis(); //elapsed(); + + for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) + { + memset((void*) &timer[i], 0, sizeof (timer_t)); + timer[i].prev_millis = current_millis; + } + + numTimers = 0; +} + + +void IRAM_ATTR ESP8266_ISR_Timer::run() +{ + uint8_t i; + unsigned long current_millis; + + // get current time + current_millis = millis(); //elapsed(); + + for (i = 0; i < MAX_NUMBER_TIMERS; i++) + { + + timer[i].toBeCalled = TIMER_DEFCALL_DONTRUN; + + // no callback == no timer, i.e. jump over empty slots + if (timer[i].callback != NULL) + { + // is it time to process this timer ? + // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592 + + if ((current_millis - timer[i].prev_millis) >= timer[i].delay) + { + unsigned long skipTimes = (current_millis - timer[i].prev_millis) / timer[i].delay; + // update time + timer[i].prev_millis += timer[i].delay * skipTimes; + + // check if the timer callback has to be executed + if (timer[i].enabled) + { + // "run forever" timers must always be executed + if (timer[i].maxNumRuns == TIMER_RUN_FOREVER) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; + } + // other timers get executed the specified number of times + else if (timer[i].numRuns < timer[i].maxNumRuns) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNONLY; + timer[i].numRuns++; + + // after the last run, delete the timer + if (timer[i].numRuns >= timer[i].maxNumRuns) + { + timer[i].toBeCalled = TIMER_DEFCALL_RUNANDDEL; + } + } + } + } + } + } + + for (i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].toBeCalled == TIMER_DEFCALL_DONTRUN) + continue; + + if (timer[i].hasParam) + (*(timer_callback_p)timer[i].callback)(timer[i].param); + else + (*(timer_callback)timer[i].callback)(); + + if (timer[i].toBeCalled == TIMER_DEFCALL_RUNANDDEL) + deleteTimer(i); + } +} + + +// find the first available slot +// return -1 if none found +int8_t IRAM_ATTR ESP8266_ISR_Timer::findFirstFreeSlot() +{ + // all slots are used + if (numTimers >= MAX_NUMBER_TIMERS) +{ + return -1; + } + + // return the first slot with no callback (i.e. free) + for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) +{ + if (timer[i].callback == NULL) +{ + return i; + } + } + + // no free slots found + return -1; +} + + +int8_t IRAM_ATTR ESP8266_ISR_Timer::setupTimer(const unsigned long& d, void* f, void* p, bool h, const unsigned& n) +{ + int freeTimer; + + if (numTimers < 0) + { + init(); + } + + freeTimer = findFirstFreeSlot(); + + if (freeTimer < 0) + { + return -1; + } + + if (f == NULL) + { + return -1; + } + + timer[freeTimer].delay = d; + timer[freeTimer].callback = f; + timer[freeTimer].param = p; + timer[freeTimer].hasParam = h; + timer[freeTimer].maxNumRuns = n; + timer[freeTimer].enabled = true; + timer[freeTimer].prev_millis = elapsed(); + + numTimers++; + + return freeTimer; +} + + +int IRAM_ATTR ESP8266_ISR_Timer::setTimer(const unsigned long& d, const timer_callback& f, const unsigned& n) +{ + return setupTimer(d, (void *)f, NULL, false, n); +} + +int IRAM_ATTR ESP8266_ISR_Timer::setTimer(const unsigned long& d, const timer_callback_p& f, void* p, const unsigned& n) +{ + return setupTimer(d, (void *)f, p, true, n); +} + +int IRAM_ATTR ESP8266_ISR_Timer::setInterval(const unsigned long& d, const timer_callback& f) +{ + return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_FOREVER); +} + +int IRAM_ATTR ESP8266_ISR_Timer::setInterval(const unsigned long& d, const timer_callback_p& f, void* p) +{ + return setupTimer(d, (void *)f, p, true, TIMER_RUN_FOREVER); +} + +int IRAM_ATTR ESP8266_ISR_Timer::setTimeout(const unsigned long& d, const timer_callback& f) +{ + return setupTimer(d, (void *)f, NULL, false, TIMER_RUN_ONCE); +} + +int IRAM_ATTR ESP8266_ISR_Timer::setTimeout(const unsigned long& d, const timer_callback_p& f, void* p) +{ + return setupTimer(d, (void *)f, p, true, TIMER_RUN_ONCE); +} + +bool IRAM_ATTR ESP8266_ISR_Timer::changeInterval(const unsigned& numTimer, const unsigned long& d) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return false; + } + + // Updates interval of existing specified timer + if (timer[numTimer].callback != NULL) + { + timer[numTimer].delay = d; + timer[numTimer].prev_millis = elapsed(); + return true; + } + // false return for non-used numTimer, no callback + return false; +} + +void IRAM_ATTR ESP8266_ISR_Timer::deleteTimer(const unsigned& timerId) +{ + if (timerId >= MAX_NUMBER_TIMERS) + { + return; + } + + // nothing to delete if no timers are in use + if (numTimers == 0) + { + return; + } + + // don't decrease the number of timers if the + // specified slot is already empty + if (timer[timerId].callback != NULL) + { + memset((void*) &timer[timerId], 0, sizeof (timer_t)); + timer[timerId].prev_millis = elapsed(); + + // update number of timers + numTimers--; + } +} + + +// function contributed by code@rowansimms.com +void IRAM_ATTR ESP8266_ISR_Timer::restartTimer(const unsigned& numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].prev_millis = elapsed(); +} + + +bool IRAM_ATTR ESP8266_ISR_Timer::isEnabled(const unsigned& numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return false; + } + + return timer[numTimer].enabled; +} + + +void IRAM_ATTR ESP8266_ISR_Timer::enable(const unsigned& numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = true; +} + + +void IRAM_ATTR ESP8266_ISR_Timer::disable(const unsigned& numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = false; +} + +void IRAM_ATTR ESP8266_ISR_Timer::enableAll() +{ + // Enable all timers with a callback assigned (used) + for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) + { + timer[i].enabled = true; + } + } +} + +void IRAM_ATTR ESP8266_ISR_Timer::disableAll() +{ + // Disable all timers with a callback assigned (used) + for (uint8_t i = 0; i < MAX_NUMBER_TIMERS; i++) + { + if (timer[i].callback != NULL && timer[i].numRuns == TIMER_RUN_FOREVER) + { + timer[i].enabled = false; + } + } +} + +void IRAM_ATTR ESP8266_ISR_Timer::toggle(const unsigned& numTimer) +{ + if (numTimer >= MAX_NUMBER_TIMERS) + { + return; + } + + timer[numTimer].enabled = !timer[numTimer].enabled; +} + + +int8_t IRAM_ATTR ESP8266_ISR_Timer::getNumTimers() +{ + return numTimers; +} + +#endif // ISR_TIMER_GENERIC_IMPL_H diff --git a/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer.h b/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer.h new file mode 100644 index 0000000..6fe6c64 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer.h @@ -0,0 +1,53 @@ +/**************************************************************************************************************************** + ESP8266_ISR_Timer.h + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.6.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 23/11/2019 Initial coding + 1.0.1 K Hoang 25/11/2019 New release fixing compiler error + 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked + 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. + 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. + 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval + 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage + 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 + 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. + 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md + 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer + 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code +*****************************************************************************************************************************/ + +#pragma once + +#ifndef ISR_TIMER_GENERIC_H +#define ISR_TIMER_GENERIC_H + +#include "ESP8266_ISR_Timer.hpp" +#include "ESP8266_ISR_Timer-Impl.h" + +#endif // ISR_TIMER_GENERIC_H + diff --git a/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer.hpp b/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer.hpp new file mode 100644 index 0000000..c69460f --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/src/ESP8266_ISR_Timer.hpp @@ -0,0 +1,216 @@ +/**************************************************************************************************************************** + ESP8266_ISR_Timer.hpp + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.6.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 23/11/2019 Initial coding + 1.0.1 K Hoang 25/11/2019 New release fixing compiler error + 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked + 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. + 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. + 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval + 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage + 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 + 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. + 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md + 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer + 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code +*****************************************************************************************************************************/ + +#pragma once + +#ifndef ISR_TIMER_GENERIC_HPP +#define ISR_TIMER_GENERIC_HPP + +#if !defined(ESP8266) + #error This code is designed to run on ESP8266 and ESP8266-based boards! Please check your Tools->Board setting. +#endif + +#ifndef ESP8266_TIMER_INTERRUPT_VERSION + #define ESP8266_TIMER_INTERRUPT_VERSION "ESP8266TimerInterrupt v1.6.0" + + + #define ESP8266_TIMER_INTERRUPT_VERSION_MAJOR 1 + #define ESP8266_TIMER_INTERRUPT_VERSION_MINOR 6 + #define ESP8266_TIMER_INTERRUPT_VERSION_PATCH 0 + + #define ESP8266_TIMER_INTERRUPT_VERSION_INT 1006000 + +#endif + +#include "TimerInterrupt_Generic_Debug.h" + +#include + +#ifdef ESP8266 + extern "C" + { + #include "ets_sys.h" + #include "os_type.h" + #include "mem.h" + } +#else + #include +#endif + +#if defined(ARDUINO) + #if ARDUINO >= 100 + #include + #else + #include + #endif +#endif + +#define ESP8266_ISR_Timer ISRTimer + +typedef void (*timer_callback)(); +typedef void (*timer_callback_p)(void *); + +class ESP8266_ISR_Timer +{ + + public: + + // maximum number of timers + #define MAX_NUMBER_TIMERS 16 + + // setTimer() constants + #define TIMER_RUN_FOREVER 0 + #define TIMER_RUN_ONCE 1 + + // constructor + ESP8266_ISR_Timer(); + + void IRAM_ATTR init(); + + // this function must be called inside loop() + void IRAM_ATTR run(); + + // Timer will call function 'f' every 'd' milliseconds forever + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int IRAM_ATTR setInterval(const unsigned long& d, const timer_callback& f); + + // Timer will call function 'f' with parameter 'p' every 'd' milliseconds forever + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int IRAM_ATTR setInterval(const unsigned long& d, const timer_callback_p& f, void* p); + + // Timer will call function 'f' after 'd' milliseconds one time + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int IRAM_ATTR setTimeout(const unsigned long& d, const timer_callback& f); + + // Timer will call function 'f' with parameter 'p' after 'd' milliseconds one time + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int IRAM_ATTR setTimeout(const unsigned long& d, const timer_callback_p& f, void* p); + + // Timer will call function 'f' every 'd' milliseconds 'n' times + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int IRAM_ATTR setTimer(const unsigned long& d, const timer_callback& f, const unsigned& n); + + // Timer will call function 'f' with parameter 'p' every 'd' milliseconds 'n' times + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int IRAM_ATTR setTimer(const unsigned long& d, const timer_callback_p& f, void* p, const unsigned& n); + + // updates interval of the specified timer + bool IRAM_ATTR changeInterval(const unsigned& numTimer, const unsigned long& d); + + // destroy the specified timer + void IRAM_ATTR deleteTimer(const unsigned& timerId); + + // restart the specified timer + void IRAM_ATTR restartTimer(const unsigned& numTimer); + + // returns true if the specified timer is enabled + bool IRAM_ATTR isEnabled(const unsigned& numTimer); + + // enables the specified timer + void IRAM_ATTR enable(const unsigned& numTimer); + + // disables the specified timer + void IRAM_ATTR disable(const unsigned& numTimer); + + // enables all timers + void IRAM_ATTR enableAll(); + + // disables all timers + void IRAM_ATTR disableAll(); + + // enables the specified timer if it's currently disabled, + // and vice-versa + void IRAM_ATTR toggle(const unsigned& numTimer); + + // returns the number of used timers + int8_t IRAM_ATTR getNumTimers(); + + // returns the number of available timers + int8_t IRAM_ATTR getNumAvailableTimers() + { + if (numTimers <= 0) + return MAX_NUMBER_TIMERS; + else + return MAX_NUMBER_TIMERS - numTimers; + }; + + private: + // deferred call constants +#define TIMER_DEFCALL_DONTRUN 0 // don't call the callback function +#define TIMER_DEFCALL_RUNONLY 1 // call the callback function but don't delete the timer +#define TIMER_DEFCALL_RUNANDDEL 2 // call the callback function and delete the timer + // low level function to initialize and enable a new timer + // returns the timer number (numTimer) on success or + // -1 on failure (f == NULL) or no free timers + int8_t IRAM_ATTR setupTimer(const unsigned long& d, void* f, void* p, bool h, const unsigned& n); + + // find the first available slot + int8_t IRAM_ATTR findFirstFreeSlot(); + + typedef struct + { + unsigned long prev_millis; // value returned by the millis() function in the previous run() call + void* callback; // pointer to the callback function + void* param; // function parameter + bool hasParam; // true if callback takes a parameter + unsigned long delay; // delay value + unsigned maxNumRuns; // number of runs to be executed + unsigned numRuns; // number of executed runs + bool enabled; // true if enabled + unsigned toBeCalled; // deferred function call (sort of) - N.B.: only used in run() + } timer_t; + + volatile timer_t timer[MAX_NUMBER_TIMERS]; + + // actual number of timers in use (-1 means uninitialized) + volatile int8_t numTimers; +}; + +#endif // ISR_TIMER_GENERIC_HPP + diff --git a/libraries/ESP8266TimerInterrupt/src/TimerInterrupt_Generic_Debug.h b/libraries/ESP8266TimerInterrupt/src/TimerInterrupt_Generic_Debug.h new file mode 100644 index 0000000..d213ee7 --- /dev/null +++ b/libraries/ESP8266TimerInterrupt/src/TimerInterrupt_Generic_Debug.h @@ -0,0 +1,90 @@ +/**************************************************************************************************************************** + TimerInterrupt_Generic_Debug.h + For ESP8266 boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/ESP8266TimerInterrupt + Licensed under MIT license + + The ESP8266 timers are badly designed, using only 23-bit counter along with maximum 256 prescaler. They're only better than UNO / Mega. + The ESP8266 has two hardware timers, but timer0 has been used for WiFi and it's not advisable to use. Only timer1 is available. + The timer1's 23-bit counter terribly can count only up to 8,388,607. So the timer1 maximum interval is very short. + Using 256 prescaler, maximum timer1 interval is only 26.843542 seconds !!! + + Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long miliseconds) + The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers + Therefore, their executions are not blocked by bad-behaving functions / tasks. + This important feature is absolutely necessary for mission-critical tasks. + + Based on SimpleTimer - A timer library for Arduino. + Author: mromani@ottotecnica.com + Copyright (c) 2010 OTTOTECNICA Italy + + Based on BlynkTimer.h + Author: Volodymyr Shymanskyy + + Version: 1.6.0 + + Version Modified By Date Comments + ------- ----------- ---------- ----------- + 1.0.0 K Hoang 23/11/2019 Initial coding + 1.0.1 K Hoang 25/11/2019 New release fixing compiler error + 1.0.2 K.Hoang 26/11/2019 Permit up to 16 super-long-time, super-accurate ISR-based timers to avoid being blocked + 1.0.3 K.Hoang 17/05/2020 Restructure code. Fix example. Enhance README. + 1.1.0 K.Hoang 27/10/2020 Restore cpp code besides Impl.h code to use if Multiple-Definition linker error. + 1.1.1 K.Hoang 06/12/2020 Add Version String and Change_Interval example to show how to change TimerInterval + 1.2.0 K.Hoang 08/01/2021 Add better debug feature. Optimize code and examples to reduce RAM usage + 1.3.0 K.Hoang 18/05/2021 Update to match new ESP8266 core v3.0.0 + 1.4.0 K.Hoang 01/06/2021 Add complex examples. Fix compiler errors due to conflict to some libraries. + 1.4.1 K.Hoang 22/11/2021 Tested with core v3.0.2. Add instructions in README.md + 1.5.0 K.Hoang 18/01/2022 Fix `multiple-definitions` linker error. Fix bug and add more accurate but shorter timer + 1.6.0 K.Hoang 13/02/2022 Add example to demo how to use one-shot ISR-based timers. Optimize code + *****************************************************************************************************************************/ + +#pragma once + +#ifndef TIMERINTERRUPT_GENERIC_DEBUG_H +#define TIMERINTERRUPT_GENERIC_DEBUG_H + +#ifdef TIMERINTERRUPT_DEBUG_PORT + #define TISR_DBG_PORT TIMERINTERRUPT_DEBUG_PORT +#else + #define TISR_DBG_PORT Serial +#endif + +// Change _TIMERINTERRUPT_LOGLEVEL_ to set tracing and logging verbosity +// 0: DISABLED: no logging +// 1: ERROR: errors +// 2: WARN: errors and warnings +// 3: INFO: errors, warnings and informational (default) +// 4: DEBUG: errors, warnings, informational and debug + +#ifndef _TIMERINTERRUPT_LOGLEVEL_ + #define _TIMERINTERRUPT_LOGLEVEL_ 1 +#endif + +#define TISR_LOGERROR(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); } +#define TISR_LOGERROR0(x) if(_TIMERINTERRUPT_LOGLEVEL_>0) { TISR_DBG_PORT.print(x); } +#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); } +#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); } +#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); } + +#define TISR_LOGWARN(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); } +#define TISR_LOGWARN0(x) if(_TIMERINTERRUPT_LOGLEVEL_>1) { TISR_DBG_PORT.print(x); } +#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); } +#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); } +#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); } + +#define TISR_LOGINFO(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); } +#define TISR_LOGINFO0(x) if(_TIMERINTERRUPT_LOGLEVEL_>2) { TISR_DBG_PORT.print(x); } +#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); } +#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); } +#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); } + +#define TISR_LOGDEBUG(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print("[TISR] "); TISR_DBG_PORT.println(x); } +#define TISR_LOGDEBUG0(x) if(_TIMERINTERRUPT_LOGLEVEL_>3) { TISR_DBG_PORT.print(x); } +#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); } +#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); } +#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); } + +#endif //TIMERINTERRUPT_GENERIC_DEBUG_H