Hardware Timer Interrupt

This is the code FYI

/****************************************************************************************************************************
 * examples/ISR_Timer_Complex.ino
 * For ESP8266 boards
 * Written by Khoi Hoang
 * 
 * Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
 * Licensed under MIT license
 * Version: v1.0.2
 * 
 * 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
*****************************************************************************************************************************/
 
//These define's must be placed at the beginning before #include "ESP8266TimerInterrupt.h"
#define TIMER_INTERRUPT_DEBUG      1

#define BLYNK_PRINT Serial
//#define BLYNK_DEBUG true

#include <ESP8266WiFi.h>

//#define USE_BLYNK_WM   true
#define USE_BLYNK_WM   false

#define USE_SSL     false

#if USE_BLYNK_WM
  #if USE_SSL
    #include <BlynkSimpleEsp8266_SSL_WM.h>        //https://github.com/khoih-prog/Blynk_WM
  #else
    #include <BlynkSimpleEsp8266_WM.h>            //https://github.com/khoih-prog/Blynk_WM
  #endif
#else
  #if USE_SSL
    #include <BlynkSimpleEsp8266_SSL.h>
    #define BLYNK_HARDWARE_PORT     9443
  #else
    #include <BlynkSimpleEsp8266.h>
    #define BLYNK_HARDWARE_PORT     8080   
  #endif
#endif

#if !USE_BLYNK_WM
  #define USE_LOCAL_SERVER    true
  
  // If local server
  #if USE_LOCAL_SERVER
    char blynk_server[]   = "yourname.duckdns.org";
    //char blynk_server[]   = "192.168.2.110";
  #else
    char blynk_server[]   = "";
  #endif

char auth[]     = "***";
char ssid[]     = "***";
char pass[]     = "***";

#endif

#include "ESP8266TimerInterrupt.h"
#include "ESP8266_ISR_Timer.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

#define HW_TIMER_INTERVAL_MS        50

#define WIFI_TIMEOUT      20000L

volatile uint32_t lastMillis = 0;

// Init ESP32 timer 0
ESP8266Timer ITimer;

// Init BlynkTimer
ESP8266_ISR_Timer ISR_Timer;

BlynkTimer blynkTimer;

void ICACHE_RAM_ATTR TimerHandler(void)
{
  static bool toggle = false;
  static bool started = false;

  static int timeRun      = 0;

  ISR_Timer.run();

  // Toggle LED every 50 x 100 = 5000ms = 5s
  if (++timeRun == 100)
  { 
    timeRun = 0;

    if (!started)
    {
      started = true;
      pinMode(LED_BUILTIN, OUTPUT);
    }
  
    #if (TIMER_INTERRUPT_DEBUG > 0)
    Serial.println("Delta ms = " + String(millis() - lastMillis));
    lastMillis = millis();
    #endif
    
    //timer interrupt toggles pin LED_BUILTIN
    digitalWrite(LED_BUILTIN, toggle);
    toggle = !toggle;
  }
}

void ICACHE_RAM_ATTR doingSomething2s()
{
  static unsigned long previousMillis = lastMillis;
  Serial.println("doingSomething2s: Delta ms = " + String(millis() - previousMillis));
  previousMillis = millis();
}

void ICACHE_RAM_ATTR doingSomething5s()
{
  static unsigned long previousMillis = lastMillis;
  Serial.println("doingSomething5s: Delta ms = " + String(millis() - previousMillis));
  previousMillis = millis();
}

void ICACHE_RAM_ATTR doingSomething10s()
{
  static unsigned long previousMillis = lastMillis;
  Serial.println("doingSomething10s: Delta ms = " + String(millis() - previousMillis));
  previousMillis = millis();
}

void ICACHE_RAM_ATTR doingSomething50s()
{
  static unsigned long previousMillis = lastMillis;
  Serial.println("doingSomething50s: Delta ms = " + String(millis() - previousMillis));
  previousMillis = millis();
}

#define BLYNK_TIMER_MS        2000L
  
void blynkDoingSomething2s()
{
  static unsigned long previousMillis = lastMillis;
  Serial.println("blynkDoingSomething2s: Delta programmed ms = " + String(BLYNK_TIMER_MS) + ", actual = " + String(millis() - previousMillis));
  previousMillis = millis();
}

void setup()
{
  Serial.begin(115200);
  Serial.println("\nStarting");
  
  // Interval in microsecs
  if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_MS * 1000, TimerHandler))
  {
    lastMillis = millis();
    Serial.println("Starting  ITimer OK, millis() = " + String(lastMillis));
  }
  else
    Serial.println("Can't set ITimer correctly. Select another freq. or interval");

  // Just to demonstrate, don't use too many ISR Timers if not absolutely necessary
  ISR_Timer.setInterval(2000L, doingSomething2s);  
  ISR_Timer.setInterval(5000L, doingSomething5s);  
  ISR_Timer.setInterval(10000L, doingSomething10s);  
  ISR_Timer.setInterval(50000L, doingSomething50s);

  // You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary.
  blynkTimer.setInterval(BLYNK_TIMER_MS, blynkDoingSomething2s);  


  #if USE_BLYNK_WM
    Blynk.begin();
  #else
    unsigned long startWiFi = millis();
    
    WiFi.begin(ssid, pass);
    
    do
    {
      delay(200);
      if ( (WiFi.status() == WL_CONNECTED) || (millis() > startWiFi + WIFI_TIMEOUT) )
        break;
    } while (WiFi.status() != WL_CONNECTED);
    
    Blynk.config(auth, blynk_server, BLYNK_HARDWARE_PORT);
    Blynk.connect();

    if (Blynk.connected())
      Serial.println("Blynk connected");
    else
      Serial.println("Blynk not connected yet");  
  #endif

}

#define BLOCKING_TIME_MS      3000L

void loop()
{
  static unsigned long previousMillis = lastMillis;
  
  Blynk.run();

  // 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.
  blynkTimer.run();
}

1 Like