Timing and delays

millis() and micros() return the number of milliseconds and microseconds elapsed after reset, respectively.

delay(ms) pauses the sketch for a given number of milliseconds and allows WiFi and TCP/IP tasks to run. delayMicroseconds(us) pauses for a given number of microseconds.

Remember that there is a lot of code that needs to run on the chip besides the sketch to keep an existing WiFi connection alive. WiFi and TCP/IP libraries get a chance to handle any pending events each time the loop() function completes, OR when delay() is called. If you have a loop somewhere in your sketch that takes a lot of time (>50ms) without calling delay(), you might consider adding a call to delay function to keep the WiFi stack running smoothly.

There is also a yield() function which is equivalent to delay(0). The delayMicroseconds function, on the other hand, does not yield to other tasks, so using it for delays more than 20 milliseconds is not recommended.

  

Interrupts

Interrupts are events or conditions that cause the microprocessor or microcontroller to stop the execution of the task that it is performing, work in a different task temporarily and come back to the initial task. Interrupts are very useful because they allow us to be able to receive data from sensors in a microcontroller without constantly asking the sensor if it has new data. In other words, interrupts allow us to avoid polling.

Naturally, when an interrupt occurs, we need to handle it in a so-called interrupt service routine (ISR), which is the function that will be executed, when the interrupt happens.

It is a good practice to design interrupt service routines as small as possible (e.g. setting a flag), so the processor gets back to the execution of the main program. So, we should not do blocking calls or handle communication, for example, inside an ISR. The best approach is to signal the main code, using for example a flag or a counter, to indicate that the interrupt has happened. Then, the main code should have the logic necessary to handle it.

Pin interrupts are supported through attachInterrupt(), detachInterrupt() functions. Interrupts may be attached to any GPIO pin except GPIO16, but since GPIO6-GPIO11 are typically used to interface with the flash memory ICs on most esp8266 modules, applying interrupts to these pins are likely to cause problems. Standard Arduino interrupt types are supported: CHANGE, RISING, FALLING.

Interrupt Example Code

/********************interrupt.ino***********************/
const byte interruptPin = 13;
int numberOfInterrupts = 0;

//variable interruptCounter will be used in the interrupt service routine. Thus, it needs to be declared as "volatile"
volatile boolean interruptFlag = 0;

void setup() {

  Serial.begin(115200);          

//sets interruptPin (pin 13) as input with pull-up
  pinMode(interruptPin, INPUT_PULLUP);

//sets interruptPin (pin13) as interrupt that is triggered on a falling edge. 
//when interrupt is triggered function "myInterruptServiceRoutine" is called.
  attachInterrupt(digitalPinToInterrupt(interruptPin), myInterruptServiceRoutine, FALLING);

}

void myInterruptServiceRoutine() {
  interruptFlag=1;
}

void loop() {

  if(interruptFlag==1){

      interruptFlag=0;
      numberOfInterrupts++;

      Serial.print("An interrupt has occurred. Total: ");
      Serial.println(numberOfInterrupts);
  }

}

After uploading the code to the ESP8266, just open the serial console. Then, the easiest way to generate interrupts without any additional hardware is to connect and disconnect the ground pin of the ESP8266 to the GPIO with the interrupt routine configured. Be carefull to avoid connecting the ground pin to the wrong GPIO.

With this method, the voltage will change between ground and VCC and, when the falling edge occurs, the interrupt will trigger the execution of interrupt function and the console log can be seen in the serial monitor.

  

Timer Interrupts

Timers, like external interrupts, run independently from your main program. Rather than running a loop or repeatedly calling millis(), you can let the hardware (here: a timer) do that work for you while your code does other things.

So suppose you have a device that needs to do something –like blink an LED every 5 seconds. If you are not using timers but just conventional code techniques, you’d have to set a variable with the next time the LED should blink, then check constantly to see if that time had arrived. However, with a timer interrupt, you can set up the interrupt, then turn on the timer. The LED will blink perfectly on time, regardless of what your main program was just doing.

A typical example is the PWM functionality. It uses hardware timers to create a PWM signal of a desired duty cycle and frequency. However, manipulating hardware timers requires manipulation on register level, such that the resulting code is not easily portable to other devices using the Arduino IDE.

(Visited 186 times, 7 visits today)