Interrupts 101

Contents

Code

The guide uses several small projects to illustrate various aspects of interrupts.

  • Basics using a button.
  • Visualisation of Serial usage of interrupts.
  • A basic communications project that relies on interrupts for time critical operations.

Where relevant, the projects consist of two versions, specifically an interrupt driven version and a non-interrupt driven version. The idea is to illustrate how to convert a "regular" program to an interrupt driven version.

Basics using a button

Overview

This pair of programs is used to illustrate how to trigger an interrupt using a button.

It also is used to illustrate some don't do's when using interrupts. For example, implementing a delay to deal with debouncing a button and how that fails miserably.
This is explained in the
video at about 23:15,

These programs uses the first circuit based upon a single Arduino.

Button Basics - no interrupts

A program the checks for a button press - without using interrupts.

This program is covered in the video starting at about 08:15.

/*
 * Button - no Interrupts.
 *
 * This program responds to button presses to change a system state.
 * When the system state changes, a counter is incremented.
 *
 * We also look at a problem with buttons known as bounce and look at a (simple
 * version) of the solution which is to let the button settle before responding
 * to it. NB: the method used in this program is not the best method, but it
 * is good enough for this example.
 */
 
// Values to manage the heartbeat LED.
const int heartbeatLedPin =  7;// the number of the LED pin
unsigned long heartbeatPreviousMillis = 0;
const long heartbeatInterval = 200;

// Additional constants describing our circuit.
const int systemStatusLedPin = 4;
const int buttonPin = 2;

// Variables that track various aspects of the project
int systemState = LOW;
int counter = 0;

// Setup our environment.
void setup() {
  Serial.begin(115200);
  pinMode(heartbeatLedPin, OUTPUT);
  pinMode(systemStatusLedPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  Serial.println("Button - no ISR");
}


// An optional delay function that is used to let time pass when required.
void myDelay(unsigned long duration) {
  unsigned long startTime = millis();
  while (millis() - startTime <= duration) {
  }
}


// The main loop which constantly checks for button presses and when detected
// action them.
void loop() {
static int prevButtonState = HIGH;
static int prevSystemState = LOW;
  unsigned long currentMillis = millis();

  // Heartbeat logic
  if (currentMillis - heartbeatPreviousMillis >= heartbeatInterval) {
    heartbeatPreviousMillis = currentMillis;
    digitalWrite(heartbeatLedPin, !digitalRead(heartbeatLedPin));
  }

  // Button processing.
  int buttonState = digitalRead(buttonPin);
  if (buttonState != prevButtonState) {
    prevButtonState = buttonState;
    myDelay(50);                        // Let time pass to avoid button bounce.
    if (buttonState == LOW) {           // The button state will be LOW when th button is pressed.
      systemState = !systemState;       // toggle the system state in response to a button press.
    }
  }

  // System state change detection.
  // This could be placed in the button processing, but it is seperated out for 
  // modularisation - which will make the conversion to interrupts easier.
  if (systemState != prevSystemState) {
    prevSystemState = systemState;
    counter++;                          // Increment our counter and ouput the new status.
    digitalWrite(systemStatusLedPin, systemState);
    Serial.print("No ISR - New state: "); Serial.print(systemState);
    Serial.print(", count: "); Serial.println(counter);
  }
}


Jump to: Top | Button Basics | Serial Monitor | Senders | Receivers

Button Basics - Interrupt driven

A program that responds to button presses using an interrupt. This operates similarly to the previous program, but uses interrupts to process the button presses.

This program is covered in the video starting at about 17:46.

/*
 * Button - no Interrupts.
 *
 * This program responds to button presses to change a system state.
 * When the system state changes, a counter is incremented.
 *
 * We also look at a problem with buttons known as bounce and look at a (simple
 * version) of the solution which is to let the button settle before responding
 * to it. NB: the method used in this program is not the best method, but it
 * is good enough for this example.
 */
 
// Values to manage the heartbeat LED.
const int heartbeatLedPin =  7;// the number of the LED pin
unsigned long heartbeatPreviousMillis = 0;
const long heartbeatInterval = 200;

// Additional constants describing our circuit.
const int systemStatusLedPin = 4;
const int buttonPin = 2;

// Variables that track various aspects of the project
volatile int systemState = LOW;
int counter = 0;

// Setup our environment.
void setup() {
  Serial.begin(115200);
  pinMode(heartbeatLedPin, OUTPUT);
  pinMode(systemStatusLedPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  Serial.println("Button - ISR");

  /*
   * attachInterrupt is part of the Arduino HAL that makes it easy to use
   * "INT" style interrupts which are linked to GPIO pins.
   * Note that it does not work for other types of interrupts including PCINT
   * interrupt types which are also linked to GPIO pins.
   *
   * You can read about attachInterrupt in the online documentation:
   *   https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
   *
   * We use FALLING as the mode because we are only interested in firing an interrupt
   * when the GPIO/DIO pin changes from HIGH to LOW - which mirrors the logic of the
   * non interrupt driven version of this program.
   */
  attachInterrupt(digitalPinToInterrupt(buttonPin), buttonISR, FALLING);
}


// An optional delay function that is used to let time pass when required.
void myDelay(unsigned long duration) {
  unsigned long startTime = millis();
  Serial.println("In_myDelay");
  while (millis() - startTime <= duration) {
  }
}


/*
 * An ISR that is called when the button is pressed.
 */
void buttonISR () {
  // Perform the same function as the non-interrupt driven does when the
  // button is pressed.
  systemState = !systemState;
    // Caution: this delay will cause your Arduino to "freeze up".
//  myDelay(50);                  // A delay to manage button bounce
}


// The main loop which constantly checks for button presses and when detected
// action them.
void loop() {
static int prevButtonState = HIGH;
static int prevSystemState = LOW;
  unsigned long currentMillis = millis();

  // Heartbeat logic
  if (currentMillis - heartbeatPreviousMillis >= heartbeatInterval) {
    heartbeatPreviousMillis = currentMillis;
    digitalWrite(heartbeatLedPin, !digitalRead(heartbeatLedPin));
  }

  // Button processing not required in Interrupts version.

  // System state change detection.
  // This could be placed in the button processing, but it is seperated out for 
  // modularisation - which will make the conversion to interrupts easier.
  if (systemState != prevSystemState) {
    prevSystemState = systemState;
    counter++;
    digitalWrite(systemStatusLedPin, systemState);
    Serial.print("ISR - New state: "); Serial.print(systemState);
    Serial.print(", count: "); Serial.println(counter);
  }
}

Jump to: Top | Button Basics | Serial Monitor | Senders | Receivers

Arduino Serial - how it uses interrupts

This program gives us an insight into how the Arduino Serial object works and how it uses interrupts to simultaneously send output to the Serial device and allow your program to run.

Through a combination of memory buffers, the MCU's USART hardware and interrupts, the Serial object maintains a continuous feed of stored data into the USART as it is ready to receive it. The USART is the part of the MCU which manages the actual transmission of a piece of data to the outside world.

The main idea of this program is to show how "sometimes" a print request will take 0ms or close to 0ms to complete. This is because the content of the print is simply saved into a memory buffer for later transmission by the UART and associated interrupts.

Later as the buffer fills up, a message that previously might have taken 0ms start taking much more time. How much and how long before this occurs depends upon the speed Serial.begin();, the length of the message to be sent and although we won't change it, the size of the buffer.

Lastly, we will observe that our "big print" function actually returns having sent the final messages, even though they didn't appear on the Serial Monitor until "much" later.

This program uses the first circuit based upon a single Arduino.

This program is covered in the video starting at about 40:45.

/*
 * Serial Monitor
 *
 * This program provides an insight into how
 * the hardwareSerial module of the Arduino HAL
 * works using timers and LEDs to indicate the
 * operations of our program while interacting
 * with the Serial object.
 */
// Values defining our hardware environment
const int ledPin = 7;
const int buttonPin = 2;


// Setup our environment.
void setup() {
  /* Try different speeds to observe the how this affects:
   * - The time to output individual messages
   * - how long before messages take longer than 0 (or 1)ms to print.
   * - how long the "activity" LED turns off before all the messages
   *   appear on the Serial monitor.
   */
//  Serial.begin(115200);
//  Serial.begin(9600);
//  Serial.begin(2400);
  Serial.begin(1200);
  Serial.println("Hardware Serial Monitor");
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

/*
 * A simple function that generates alot of output.
 * 
 * It turns on an activty LED when it is entered and
 * turns it off again when complete. This allows us to
 * observe data being saved in a buffer allowing the
 * function to return - even though the messages haven't
 * been sent yet.
 * 
 * It also times how long a message takes to "print". This
 * time is output as part of the next message. Thus for example,
 * if message number 4 says the previous message took 28ms to
 * output, then that means message number 3 took 28ms. The time
 * that message 4 took will be displayed as part of message 5
 * and so on.
 * 
 * These characteristics can be altered by:
 * - changing the Serial speed
 * - adjusting the size of each message (gives insight into the size of the Serial buffer)
 * - running multiple times in quick succession or just once only followed by a brief delay.
 * 
 */
void runTest() {
unsigned long startTime, endTime;
unsigned long prevTime = 0;
static int testNum = 0;

  digitalWrite(ledPin, HIGH);     // Indicate Activty started.
  startTime = millis();           // Capture start time and output first message.
  Serial.print("0: Running test: "); Serial.println(testNum++);
  endTime = millis();             // Capture the end time.
  prevTime = endTime - startTime; // Calculate the elapsed time.

  // Now output some largish messages.
  // Each message includes the time required to output the previous message.
  for(int i = 0; i < 10; i++) {
    startTime = millis();
    Serial.print(i+1);
    Serial.print(": Prev msg: ");
    Serial.print(prevTime);
    Serial.println("ms elapsed");
    endTime = millis();
    prevTime = endTime - startTime;
  }
  digitalWrite(ledPin, LOW);      // Indicate Activity complete.
}


// The main loop. All we need to do is check if the button is currently
// pressed. If it is run our test.
void loop() {
  if (digitalRead(buttonPin) == LOW) {
    runTest();
  }
}

Jump to: Top | Button Basics | Serial Monitor | Senders | Receivers

Senders - timer interrupts

Overview

This pair of programs is used to illustrate how "other activity" can interfere with the generation of a regular signal.

This program is the first half of a larger "system" involving two Arduinos talking to each other. The ultimate idea is that we will be implementing a tamper resistant communications system (although the full implementation is left as an exercise for you).

The idea here is that we need to reliably generate a pulse that alternates between being high for 200ms and low for 200ms. However, we will see that a "big task" that also needs to run will interfere with this.
Obviously we will use interrupts to fix this.

These programs use the first circuit based upon a single Arduino. Later, when incorporating the receiver, we will migrate to the second - dual Arduino - circuit.

I have provided an alternative Big Task if you wish to experiment with a "big task" that outputs a large volume of data such as in a large status report.

Sender - no interrupts

This is the starting point, it tries to generate a regular signal, but runs into trouble when we activate the "big task" that also needs to run.

This program is covered in the video starting at about 48:47.

/*
 * Sender - No Interrupts
 *
 * This program attempts to generate a regular signal.
 * 
 * However, it runs into trouble when we try to do additional things.
 * This will be addressed in the next version.
 */
// Values defining our hardware environment
const int signalLedPin= 4;
const int heartbeatLedPin =  7;

// Establish values to manage our signal and
// "Big task" operations.
const long heartbeatInterval = 200;
const long reportInterval = 8000;

// Setup our environment.
void setup() {
  Serial.begin(115200);
  pinMode(heartbeatLedPin, OUTPUT);
  pinMode(signalLedPin, OUTPUT);
  Serial.println("Sender - no ISR");
}

/*
 * A "Big Task" that takes a "long time" to run.
 * The idea of this is to run it periodically to illustrate how
 * it interferes with our signal generation.
 * 
 * This task is possibly taking an excessive amount of time, but the
 * idea is that it takes enough time to see from a human perspective.
 * As such it is long enough so that we, as humans, can see what is going
 * on. 
 * At MCU speeds, a shorter task can interfere with our signal in
 * exactly the same way. So the important part here is the principle that
 * sometimes things can interfere with other activity and that that can be
 * problematic if one of those activities is critical in some way such as
 * our signal which we need to be regular and consistant.
 */
void bigTask() {
  Serial.println("Starting Big Task: ");
  for(int i = 0; i < 40; i++) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("\nBig Task Complete");
}

/* 
 * Generate our signal by toggling both the signal and heartbeat pins.
 * The heartbeat pin should be visible (via an LED) in all versions of 
 * the senders.
 * At some point the signal pin will be connected to another Arduino.
 */
void heartBeat() {
  int sysStatus = !digitalRead(heartbeatLedPin);
  digitalWrite(heartbeatLedPin, sysStatus);
  digitalWrite(signalLedPin, sysStatus);
}


/*
 * Our main loop. Checks for and acivates:
 * - The heartbeat
 * - The big Task.
 */
void loop() {
static unsigned long reportPreviousMillis = 0;
static unsigned long heartbeatPreviousMillis = 0;

  unsigned long currentMillis = millis();
  // Heartbeat logic
  if (currentMillis - heartbeatPreviousMillis >= heartbeatInterval) {
    heartbeatPreviousMillis = currentMillis;
    heartBeat();
  }

  // big task logic - comment this out in the initial version
  // to observe a regular signal.
  // Uncomment it to see the interference.
  if (currentMillis - reportPreviousMillis >= reportInterval) {
    reportPreviousMillis = currentMillis;
    bigTask();
  }  
}

Jump to: Top | Button Basics | Serial Monitor | Senders | Receivers

Sender - interrupt driven

This is the interrupt driven version of the sender. It uses a timer interrupt to ensure that the signal being sent is reliably generated despite other system activity.

This program is covered in the video starting at about 1:09:34.

For more details about the registers used in the setup() function, refer to this alternate listing which is fully commented.

/*
 * Sender - Interrupt driven
 *
 * This program resolves the problem with the non-interrupt driven
 * version of the program.
 * 
 * It resolves it by using Timer2 to generate an interrupt once every
 * millisecond. Then we count 200 invocations (= 200ms). Once we reach
 * our target interval (i.e. 200ms) we toggle the system status.
 * 
 * This works reliably - despite the regular execution of the "big task".
 * 
 */
// Values defining our hardware environment
const int signalLedPin= 4;
const int heartbeatLedPin =  7;// the number of the LED pin

// Establish values to manage our signal and
// "Big task" operations.
const long heartbeatInterval = 200;
const long reportInterval = 8000;

// Setup our environment.
void setup() {
  Serial.begin(115200);
  pinMode(heartbeatLedPin, OUTPUT);
  pinMode(signalLedPin, OUTPUT);
  Serial.println("Sender - ISR");

  noInterrupts();
  TCCR2A = 0;       // set timer 2 control registers 0 to turn off
  TCCR2B = 0;       // all functions.
  TCNT2  = 0;       // initialize the timer 2 counter value to 0

  OCR2A = 249;      // Count match value
  TCCR2B |= (1 << CS22);    // Set 64x prescaler
  TCCR2A |= (1 << WGM21);   // Clear the counter (TCNT2) when it reaches the match value (OCR2A).
  TIMSK2 |= (1 << OCIE2A);  // Enable the the Timer2 Compare Match A Interrupt.
  interrupts();
  
}

/*
 * A "Big Task" that takes a "long time" to run.
 * The idea of this is to run it periodically to illustrate how
 * it interferes with our signal generation.
 * 
 * This task is possibly taking an excessive amount of time, but the
 * idea is that it takes enough time to see from a human perspective.
 * As such it is long enough so that we, as humans, can see what is going
 * on. 
 * At MCU speeds, a shorter task can interfere with our signal in
 * exactly the same way. So the important part here is the principle that
 * sometimes things can interfere with other activity and that that can be
 * problematic if one of those activities is critical in some way such as
 * our signal which we need to be regular and consistant.
 */
 void bigTask() {
  Serial.println("Starting Big Task(ISR): ");
  for(int i = 0; i < 40; i++) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("\nBig Task Complete");
}

/*
 * This is our Timer ISR (Interrupt Service Routine).
 * It is triggered once ever millisecond (i.e. 1000
 * times per second).
 * Since we want our heartbeat to be 200 milliseconds
 * in duration (as specified by the heartbeatInterval),
 * we will count that many ticks before toggling the system
 * state and outputing it to the two DIO pins.
 */
//void heartBeat() {
SIGNAL(TIMER2_COMPA_vect) {
static int cnt = heartbeatInterval;

    // Count this clock tick.
    // We are counting down from heartbeatInterval.
    // If the counter is non-zero, we simply return (i.e. do nothing this time round).
  if (--cnt) {
    return;
  }
    // Otherwise, our counter has reached zero. This means the desired interval
    // has passed, so we reset the counter to our interval...
  cnt = heartbeatInterval;
    // Toggle the system state and output it.
  int sysStatus = !digitalRead(heartbeatLedPin);
  digitalWrite(heartbeatLedPin, sysStatus);
  digitalWrite(signalLedPin, sysStatus);
}

/*
 * Our main loop. Checks for and acivates:
 * - The big Task.
 * The non ISR also managed the heartbeat, but in this
 * version, the heartbeat (and thus the signal) is
 * managed by the timer ISR.
 */
void loop() {
static unsigned long reportPreviousMillis = 0;

  unsigned long currentMillis = millis();

  if (currentMillis - reportPreviousMillis >= reportInterval) {
    reportPreviousMillis = currentMillis;
    bigTask();
  }  
}

Jump to: Top | Button Basics | Serial Monitor | Senders | Receivers

Receivers - GPIO pin interrupts

Overview

These programs receive the signal sent by the sender programs. As with the other pairs of program, one program attempts to read the signal using a traditional polling method which will be disrupted when the big task is invoked.

The other program uses interrupts that are triggered when the signal is received from the sender program.

These programs uses the second circuit which involves two interconnected Arduinos.

Receiver - no interrupts

Receive an incoming signal by polling the DIO pin. This works fine until the big task is activated. At this time there is no opportunity to check for the incoming signal and thus several signal transitions will be missed.

This program is covered in the video starting at about 1:22:12.

/*
 * Receiver - No Interrupts
 *
 * This program receives a signal from an outside source.
 * For the purposes of the tutorial, this will be another
 * Arduino.
 * 
 * It will use an LED connected to pin 7 to visualise the signal
 * received via pin 2.
 * 
 * From time to time this program will run a "big task".
 * 
 * As with the non interrupt driven sender we will run into a bit
 * of a problem when the big task runs.
 * 
 * What we will see is that the LED will blink irregularly when the
 * big task is run.
 */

// Values defining our hardware environment
const int statusLedPin = 7;
const int signalPin = 2;

// Establish values to manage our "Big task" operations.
const unsigned long bigTaskInterval = 8000;

// Variables that will track the incoming signal.
int counter = 0;
int systemStatus = 0;

// Setup our environment.
void setup() {
  Serial.begin(115200);
  Serial.println("Receiver - no ISR");
  pinMode(statusLedPin, OUTPUT);
  pinMode(signalPin, INPUT);
}


/*
 * A "Big Task" that takes a "long time" to run.
 * The idea of this is to run it periodically to illustrate how
 * it interferes with our signal generation.
 * 
 * This task is possibly taking an excessive amount of time, but the
 * idea is that it takes enough time to see from a human perspective.
 * As such it is long enough so that we, as humans, can see what is going
 * on. 
 * At MCU speeds, a shorter task can interfere with our signal in
 * exactly the same way. So the important part here is the principle that
 * sometimes things can interfere with other activity and that that can be
 * problematic if one of those activities is critical in some way such as
 * our signal which we need to be regular and consistant.
 */
void bigTask() {
  Serial.println("RX Starting Big Task: ");
  for(int i = 0; i < 40; i++) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("\nRX Big Task Complete");
}

/*
 * Receive the signal using a digital read.
 * When the signal changes state (e.g. transisitions from HIGH to LOW
 * or LOW to HIGH) toggle the system state, count the event and output
 * our status.
 * 
 * Periodically run the big task - which will interfere with the
 * reception of the incoming signal.
 */
void loop() {
  static unsigned long prevBigTaskTime = 4000;
  static int prevSystemStatus = 0;
  systemStatus = digitalRead(signalPin);
  if (systemStatus != prevSystemStatus) {
    prevSystemStatus = systemStatus;
    counter++;
    digitalWrite(statusLedPin, systemStatus);
    Serial.print("RX Signal (no interupts): "); Serial.print(systemStatus);
    Serial.print(" cnt = "); Serial.println(counter);
  }

  unsigned long currTime = millis();
  if (currTime - prevBigTaskTime >= bigTaskInterval) {
    prevBigTaskTime = currTime;
    bigTask();
  }
}

Jump to: Top | Button Basics | Serial Monitor | Senders | Receivers

Receiver - Interrupt driven

This program receives the incoming signal via an interrupt which is fired whenever the DIO/GPIO pin's state changes.

This program is covered in the video starting at about 1:29:34.

/*
 * Receiver - Interrupt driven
 *
 * This program receives a signal from an outside source.
 * For the purposes of the tutorial, this will be another
 * Arduino.
 * 
 * It will use an LED connected to pin 7 to visualise the signal
 * received via pin 2.
 * 
 * From time to time this program will run a "big task".
 * 
 * Unlike the traditional polling version of this program, this
 * variant receives the incoming signal via an interrupt enabled
 * DIO/GPIO pin. As such, this version will reliably receive the
 * signal - unlike the non interrupt driven version - when the
 * big task is invoked.
 * 
 * What we will see is that the LED will continue to blink in a
 * regular and consistant manner - even when the big task is run.
 */
// Values defining our hardware environment
const int statusLedPin = 7;
const int signalPin = 2;

// Establish values to manage our "Big task" operations.
const unsigned long bigTaskInterval = 8000;

// Variables that will track the incoming signal.
volatile int counter = 0;
volatile int systemStatus = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("Receiver - ISR");
  pinMode(statusLedPin, OUTPUT);
  pinMode(signalPin, INPUT);

  // Connect our ISR (signalISR) to the GPIO pin (signalPin),
  // so that whenever the state of the pin CHANGEs fire the
  // interrupt - which calls the ISR function (signalISR).
  attachInterrupt(digitalPinToInterrupt(signalPin), signalISR, CHANGE);
}


/*
 * A "Big Task" that takes a "long time" to run.
 * The idea of this is to run it periodically to illustrate how
 * it interferes with our signal generation.
 * 
 * This task is possibly taking an excessive amount of time, but the
 * idea is that it takes enough time to see from a human perspective.
 * As such it is long enough so that we, as humans, can see what is going
 * on. 
 * At MCU speeds, a shorter task can interfere with our signal in
 * exactly the same way. So the important part here is the principle that
 * sometimes things can interfere with other activity and that that can be
 * problematic if one of those activities is critical in some way such as
 * our signal which we need to be regular and consistant.
 */
 void bigTask() {
  Serial.println("RX Starting Big Task: ");
  for(int i = 0; i < 40; i++) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("\nRX Big Task Complete");
}

/* This is our Interrupt Service Routine (ISR).
 * It is called according to the rules specified in the
 * attachInterrupt() function call.
 * When invoked, deal with the incoming signal which involves:
 * - incrementing the counter
 * - setting the LED to reflect the received signal (i.e. HIGH or LOW).
 */
void signalISR() {
  systemStatus = digitalRead(signalPin);
  counter++;
  digitalWrite(statusLedPin, systemStatus);
}

/* In our loop, we will continuously check for a change in the
 * system state. If there is a change observed, then a status
 * report is output.
 * 
 * Additionally, we will periodically invoke the big task.
 * 
 * Because this interrupt driven version will reliably receive
 * the signal, we will observe that the counter will have incremented
 * multiple times while the big task is running.
 * This skipping of counter values is revealed by gaps in the 
 * report messages sent to the Serial monitor.
 */
void loop() {
  static unsigned long prevBigTaskTime = 4000;
  static int prevSystemStatus = 0;
  
  if (systemStatus != prevSystemStatus) {
    prevSystemStatus = systemStatus;
    Serial.print("RX Signal (no interupts): "); Serial.print(systemStatus);
    Serial.print(" cnt = "); Serial.println(counter);
  }

  unsigned long currTime = millis();
  if (currTime - prevBigTaskTime >= bigTaskInterval) {
    prevBigTaskTime = currTime;
    bigTask();
  }
}

Jump to: Top | Button Basics | Serial Monitor | Senders | Receivers

Alternative Big Task

Following is an alternative big task. This can be used with the Sender or receiver programs.

This alternative outputs a multiplaction table - which represents a large amount of output such as a largish status report.

The big task that I did use in the above examples is not only shorter and thus easier to read, but also is a proxy for a potentially long running operation such as a network access, waiting for a user input or other lengthy operations.
Bear in mind that a "lengthy operation" is defined by how much error tolerance that your time critical task can tollerate. A "lengthy operation", in the context of this guide, is not the actual elapsed time that the operation requires. I am only using very lengthy operations so that we humans can see them.

To use this alternative big task, simply remove the big task from the above examples and replace it with all of the following code.
Additionally, to see the impact of this task, you will need to use a slower Serial Monitor speed such as 9600 or lower.

/* A supporting function for the bigTask multiplication table big task
 * This function will output a number, right justified in a field of the
 * specified size.
 */
void output(int n, int fieldWidth = 4, bool newLine = false) {
int numWidth = n <= 0 ? 1 : 0;

  // Work out how many digits the number contains.
  int wrk = abs(n);
  while (wrk) {
    numWidth += 1;
    wrk /= 10;
  }

  // Output leading spaces.
  for (int i = numWidth; i < fieldWidth; i++) {
    Serial.print(" ");
  }
  // Output the number
  Serial.print(n);

  // Output a newline if requested.
  if (newLine) {
    Serial.println();
  }
}


/* An alternative big task that produces a large amount of output in
 * the form of a multiplication table.
 */
void bigTask() {
static int tableCnt;
const int maxValue = 12;
  Serial.print("Multiplication table #"); Serial.println(++tableCnt);
  Serial.print("  --");
  for (int i = 0; i <= maxValue; i++) {
    output(i, 4);
  }
  Serial.println();
  Serial.print("    ");
  for (int i = 0; i < maxValue * 4 + 4; i++) {
    Serial.print("-");
  }
  Serial.println();

  for (int x = 0; x <= maxValue; x++) {
    output(x, 2);
    Serial.print(": ");
    for (int y = 0; y <= maxValue; y++) {
      output(x * y, 4);
    }
    Serial.println();
  }
  Serial.print("End of table "); Serial.println(tableCnt);
}

Jump to: Top | Button Basics | Serial Monitor | Senders | Receivers

Please support me if you can

Please help support these projects: Buy me a coffee - gm310509 Patreon - gm310509