for real powermeter

This commit is contained in:
hidaba
2023-12-23 16:54:56 +01:00
parent ea400ca61f
commit c2d5bae2e9
25 changed files with 4357 additions and 0 deletions

View File

@ -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!

View File

@ -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.

View File

@ -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)
<a href="https://www.buymeacoffee.com/khoihprog6" title="Donate to my libraries using BuyMeACoffee"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Donate to my libraries using BuyMeACoffee" style="height: 50px !important;width: 181px !important;" ></a>
<a href="https://www.buymeacoffee.com/khoihprog6" title="Donate to my libraries using BuyMeACoffee"><img src="https://img.shields.io/badge/buy%20me%20a%20coffee-donate-orange.svg?logo=buy-me-a-coffee&logoColor=FFDD00" style="height: 20px !important;width: 200px !important;" ></a>
---
---
## 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() wont 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 <ESP8266_ISR_Timer.h>` **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`
<table>
<tr>
<td align="center"><a href="https://github.com/holgerlembke"><img src="https://github.com/holgerlembke.png" width="100px;" alt="holgerlembke"/><br /><sub><b>Holger Lembke</b></sub></a><br /></td>
<td align="center"><a href="https://github.com/RushOnline"><img src="https://github.com/RushOnline.png" width="100px;" alt="RushOnline"/><br /><sub><b>Eugene</b></sub></a><br /></td>
<td align="center"><a href="https://github.com/absalom-muc"><img src="https://github.com/absalom-muc.png" width="100px;" alt="absalom-muc"/><br /><sub><b>absalom-muc</b></sub></a><br /></td>
</tr>
</table>
---
## 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

View File

@ -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

View File

@ -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()
{
}

View File

@ -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;
}
}
}

View File

@ -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 <SimpleTimer.h> // 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();
}

View File

@ -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 <SimpleTimer.h> // 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();
}

View File

@ -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 <SimpleTimer.h> // 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();
}

View File

@ -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()
{
}

View File

@ -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()
{
}

View File

@ -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()
{
}

View File

@ -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()
{
}

View File

@ -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"

View File

@ -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"

View File

@ -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:
}

View File

@ -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

View File

@ -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"]
}

View File

@ -0,0 +1,12 @@
name=ESP8266TimerInterrupt
version=1.6.0
author=Khoi Hoang <khoih.prog@gmail.com>
maintainer=Khoi Hoang <khoih.prog@gmail.com>
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

View File

@ -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

View File

@ -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 <Arduino.h>
#else
#include <WProgram.h>
#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

View File

@ -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 <string.h>
// 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

View File

@ -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

View File

@ -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 <stddef.h>
#ifdef ESP8266
extern "C"
{
#include "ets_sys.h"
#include "os_type.h"
#include "mem.h"
}
#else
#include <inttypes.h>
#endif
#if defined(ARDUINO)
#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#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

View File

@ -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