Attention Pin (ATTN
) Guide
There are several situations where you may need your embedded application to be alerted the instant the Notecard receives new information. This guide is designed to aid you in creating an interactive example to demonstrate how to leverage this feature of the Notecard. The goal of this example is to solidify and confirm your understanding of the Notecard's attention interrupt and its behavior.
At a high-level, your program will:
- Respond to a button click.
- Author a request to the Notecard; instructing it to fire after a few seconds.
- Respond to interrupts and update an LED to visualize the behavior of the
ATTN
interrupt.
During the course of this example, you will learn:
- How to configure the Notecard's attention interrupt.
- How to use the Notecarrier AF's built-in button from your program.
- How to use the Huzzah32's built-in LED from your program.
- How to write and handle an interrupt service routine (ISR) on the ESP32.
Although this guide uses an Adafruit Huzzah32 microcontroller and a Notecarrier AF to show how the Notecard's attention pin works, you can use any combination of ESP32 microcontroller and Notecarrier to complete this guide. Just note you may need to map the instructions to work with your specific hardware configuration.
You can view or download the full source of this project from GitHub.
Notecard Interrupt
Originally designed for low-power use cases, the Notecard attention interrupt is a latching interrupt. Meaning, once it fires, it stays in the fired position, until it has been manually reset.
The latching behavior enables you to leverage the interrupt in myriad ways:
- The Notecard may idle or delay while waiting for communication from a cellular tower, while simultaneously disabling the host microcontroller with the enable pin.
- When used in a powered setting and connected to an interrupt capable pin on the host MCU, the host MCU can receive and respond to network communication as quickly as possible.
- The host MCU may optimize polling, by querying the logic value of the pin, as opposed to transacting with the Notecard to look for new data.
To learn more about configuring the Notecard ATTN
interrupts, read the
Handling Notecard Interrupts
section of the Notecard guide.
Hardware Setup
Ensure you have access to the following hardware:
- Notecard
- ESP32 microcontroller
- Notecarrier
- Male/male jumper wire
- USB A/micro cable
- momentary tactile push button (built-in to Notecarrier AF)
- LED (built-in to Adafruit Huzzah32 ESP32 Feather)
The attention, or ATTN
, pin is exposed on the Notecarrier AF, however it is
not wired to any pins that are exposed from the Feather socket. To utilize the
ATTN
pin you must first decide how you would like for it be used (as described
above), and then you must wire it the corresponding pin.
- You must connect the
ATTN
pin of the Notecard to an interrupt capable GPIO pin on the Huzzah32. Place the male/male jumper wire between theATTN
pin on the Notecarrier AF 13-Pin Header and pin5
on the Adafruit Feather 24-Pin Breakout Header.
That's it! To complete the project, you will use the Huzzah32's built-in LED,
LED_BUILTIN
, and the Notecarrier AF's built-in button, B0
.
The female headers pictured above are non-standard hardware. In order to connect
your jumper wire between ATTN
and 5
, either soldering or alligator clips
will be required.
Firmware Breakdown
Definitions and Declarations
-
First things first, you will need to include the Notecard library.
#include <Notecard.h>
-
Next, you will want to create some defines to make things easier. Take care to note
B0
andD5
. The pin labels on the Huzzah32 and Notecarrier AF do not line up 100%, and these defines make it easier to identify the interrupt pins to which you will be programmatically attaching.#ifdef B0 #undef B0 #endif #define B0 21 #ifdef D5 #undef D5 #endif #define D5 14 #define LOOP_HZ 20 #define LOOP_DELAY_MS (1000/LOOP_HZ) #define serialDebug Serial
-
You will need to instantiate the Notecard class globally, which enables you to configure and interact with your Notecard device in both the
setup
andloop
functions.Notecard notecard;
-
In order to optimize the interrupt execution (explained here), you need to declare a
volatile bool
flag. This allows the interrupt and main loop to share state, which enables the interrupt to offload processing onto the main loop.volatile bool notecard_request_to_arm = false;
-
Declare an interrupt to handle the button press event. This ISR will notify the main loop of the request by setting the flag to
true
, after checking if theATTN
pin is already armed.void IRAM_ATTR armInterrupt() { // Take no action when already armed if (digitalRead(D5)) { notecard_request_to_arm = true; } }
-
Declare an interrupt to handle the Notecard
ATTN
pin interrupt. Use the following code to set the Huzzah32's built-in LED to follow the state of theATTN
pin:void IRAM_ATTR attention() { // Visualize the attention pin state digitalWrite(LED_BUILTIN, digitalRead(D5)); }
setup
Function
-
First, you will start by enabling debug messages for the application.
// Initialize Debug Output serialDebug.begin(115200); while (!serialDebug) { ; // wait for serial port to connect. Needed for native USB }
-
Next, you configure and initialize the Notecard.
// Initialize Notecard notecard.begin(); notecard.setDebugOutputStream(serialDebug);
-
You have placed a jumper wire betwen pin
5
andATTN
. To register theattention
ISR to activity on pin5
, you must use theattachInterrupt
API.// Attach Notecard Interrupt pinMode(D5, INPUT); attachInterrupt(digitalPinToInterrupt(D5), attention, RISING);
-
Button
B0
on the Notecarrier AF connects through the Feather socket to pin21
of the Huzzah32. To register thearmInterrupt
ISR to a button press event, you must use theattachInterrupt
API.// Attach Button Interrupt pinMode(B0, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(B0), armInterrupt, RISING);
-
The Notecard can be powered seperately, and operates independently, of the Huzzah32. As a result, the Huzzah32 built-in LED and the Notecard's
ATTN
pin can get out of sync. To ensure alignment, you must initialize the state of the LED to match the state of theATTN
pin.// Debug LED (mirrors `ATTN`) pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, digitalRead(D5));
loop
Function
Due the fact most of the programs logic is executed through event driven code,
the loop
function is dedicated to servicing the button press event.
When signalled by the interrupt driven flag, notecard_request_to_arm
, the MCU
will construct a JSON request and send it to the Notecard. If the message is
sent successfully, then the Huzzah32's built-in LED will be updated to reflect
the state of the armed interrupt. Finally, loop
will delay at the frequency
specified in LOOP_HZ
.
void loop() {
// Process arming request
if (notecard_request_to_arm) {
notecard_request_to_arm = false;
// Arm ATTN Interrupt
J *req = NoteNewRequest("card.attn");
if (req) {
JAddStringToObject(req, "mode", "arm");
JAddNumberToObject(req, "seconds", 3);
if (notecard.sendRequest(req)) {
// Visualize the attention pin state
digitalWrite(LED_BUILTIN, digitalRead(D5));
} else {
notecard.logDebug("ERROR: Failed to arm ATTN interrupt!\n");
}
}
}
// Loop at `LOOP_HZ` Hz
delay(LOOP_DELAY_MS);
}
More on ESP32 Interrupt Handling
All GPIO pins on the ESP32 are interrupt capable. This is an amazing feature of the ESP32, and is not true of most microcontrollers. However, the ESP32's hardware interrupts require special handling, especially when using the Arduino board support package. Those details, and more, are discussed in this section.
The Notecarrier AF built-in button B0
is denoted by the orange line (bottom),
and the Notecard's ATTN
interrupt is shown in white (top). As illustrated by
the yellow marker, the rising edge of the button is the trigger. Once the button
is released, the program generates and sends a request to arm the attention
interrupt (depicted by the red area). Lastly, the Notecard arms the ATTN
interrupt, observed as the white line being pulled LOW
.
What exactly happens after the button press? Here is the break down:
- The MCU awaits the remaining frequency delay.
- The MCU sees the flag indicating the user has made a request to arm.
- The MCU forms and sends a JSON request to the Notecard to arm the
ATTN
pin. - The Notecard arms the
ATTN
pin by pulling the lineLOW
.
In the timing graph, you can see it takes ~80ms to service the request to arm. While 80ms may seem fast, it is quite slow for an MCU, and is precisely why the operation needs to be moved out of the interrupt and into the main loop.
ESP32 Interrupt Service Routines should be decorated with IRAM_ATTR
(1).
What is IRAM_ATTR?
By flagging a piece of code with the
IRAM_ATTR
attribute we are declaring that the compiled code will be placed in the Internal RAM (IRAM) of the ESP32.Otherwise the code is placed in the Flash. And flash on the ESP32 is much slower than internal RAM.
If the code we want to run is an interrupt service routine (ISR), we generally want to execute it as quickly as possible. If we had to "wait" for an ISR to load from flash, things would go horribly wrong.
Example:
void IRAM_ATTR attention() {
// Visualize the interrupt
digitalWrite(LED_BUILTIN, HIGH);
}
Due to a shortcoming in Espressif System's esp32
Arduino Board Package,
all interrupts must be configured to fire on the same edge. To successfully
observe the Notecard attention pin interrupt, you must monitor the
RISING
edge. As a result, any other interrupts in your project will also
need to fire on the RISING
edge.