---
title: Interrupt-Driven IoT with the Notecard ATTN Pin
description: Unnecessary polling is where battery-powered IoT quietly bleeds power. Here's how Notecard's ATTN pin lets a host MCU remain in deep sleep at µA levels and wake only when something actually happens.
source_url: https://dev.blues.io/blog/interrupt-driven-iot-notecard-attn-pin/
canonical_url: https://dev.blues.io/blog/interrupt-driven-iot-notecard-attn-pin/
markdown_url: https://dev.blues.io/blog/interrupt-driven-iot-notecard-attn-pin.md
---

# Interrupt-Driven IoT with the Notecard ATTN Pin

![Interrupt-Driven IoT with the Notecard ATTN Pin banner](https://dev.blues.io/images/blog/posts/interrupt-driven-iot-notecard-attn-pin/banner.png?v=c0d3a4c3)

July 14, 2026

## Unnecessary polling is where battery-powered IoT quietly bleeds power. Here's how Notecard's ATTN pin lets a host MCU remain in deep sleep at µA levels and wake only when something actually happens.

- [Low Power](https://dev.blues.io/blog/tag/low-power)
- [Notecard](https://dev.blues.io/blog/tag/notecard)
- [Firmware](https://dev.blues.io/blog/tag/firmware)

[![Rob Lauer](https://dev.blues.io/images/blog/authors/rob-lauer.jpg?v=57fb21a7)](https://dev.blues.io/blog/author/rob-lauer/)

[Rob LauerSenior Director of Developer Relations](https://dev.blues.io/blog/author/rob-lauer/)

Picture a battery-powered device out in a remote field. It reads a sensor a few times a day, occasionally gets a command from the cloud, and otherwise does nothing. Plenty of devices like this are programmed to wake every few seconds, ask "is there anything for me?", find nothing, and go back to waiting. Multiply that across a fleet (and months in the field), and you've spent a surprising amount of battery producing the answer: "nope, still nothing!".

That polling loop is one of the quietest ways to drain a battery, and much of that waste is avoidable. The good news is the [Blues Notecard](https://dev.blues.io/datasheets/notecard-datasheet.md) has a pin built for exactly this: `ATTN`. Instead of waking on a timer to check, **your host can wake only when there's actually something to do**. This article covers how this functionality works and when to use it.

![Event sources — an inbound Note, motion, or a timer — feed into a Notecard, which raises its ATTN pin to wake a Host MCU that is otherwise asleep.](https://dev.blues.io/images/blog/posts/interrupt-driven-iot-notecard-attn-pin/attn-concept-flow.svg?v=807dabf0)

## Why Polling is the Hidden Power Drain

It's easy to assume the radio is where all your battery power goes. However, in a well-designed Notecard product, it usually isn't. **Notecard itself idles at roughly 8–18 µA @ 5V** depending on the model (Cellular, Satellite, LoRa, or WiFi), and the [Low-Power Firmware Design guide](https://dev.blues.io/notecard/notecard-walkthrough/low-power-firmware-design.md) is blunt about where the real draw lives: in most battery-powered designs, the host MCU, not Notecard, is the largest contributor to idle current.

So one of the most effective things you can do for battery life is keep the host in true deep sleep as much as possible (e.g. STM32 `STOP`/`STANDBY`, ESP32 deep sleep, Nordic nRF52 `System OFF`), drawing microamps or less. A polling host can sleep between checks, but every poll is still a wake, a little I/O, and a return to sleep. Those unnecessary wakeups drive up its average current even when there's nothing to report.

The fix is to flip the relationship. Instead of the host repeatedly asking Notecard for news, let Notecard tell the host *when* there's news to act on. That's exactly what `ATTN` is for.

## Notecard's Hardware "Tap on the Shoulder"

`ATTN` is a [dedicated output pin on Notecard](https://dev.blues.io/datasheets/notecard-datasheet/note-wbglwt.md#pin-information) that you configure through the [card.attn API](https://dev.blues.io/api-reference/notecard-api/card-requests/latest.md#card-attn). Its job is to physically signal your host that an event of interest happened, so the host can sleep deeply the rest of the time. You can use it two ways:

1. **Interrupt-driven wake.** Notecard fires the pin when an asynchronous event happens, and your host (already asleep in its own deep-sleep mode) wakes on that notification.
2. **Power-off sleep.** Notecard drives the pin `LOW` for a set interval. If that pin is wired to the host's enable input, it cuts host power entirely until the pin goes `HIGH` again.

These map to two `card.attn` mechanisms: `arm` mode and `sleep` mode. They solve related problems very differently, and choosing between them is most of the design decision.

### The arm/fire Cycle

When you arm `ATTN`, Notecard drives the pin `LOW`, and when a monitored event occurs (or an optional timeout elapses), the pin goes `HIGH`. That low-to-high transition is your wake signal: wire `ATTN` to a host GPIO and configure the MCU to wake on it. On some MCUs that's a rising-edge interrupt; on others it's a level-based deep-sleep wake source (more on that below). Either way, your host can stay asleep until Notecard has something worth waking up for.

The events you can arm on are what make this more interesting than a plain timer:

- `files` — a Notefile has changed, usually in the form of an inbound Note (`.qi`) arriving from the cloud. This is your "the server has a command for you!" wake.
- `motion` and `motionchange` — the onboard accelerometer has detected movement, or the motion state flipped between "moving" and "stopped."
- `location` — the GPS module got a position fix.
- `env` — an [environment variable](https://dev.blues.io/notecard/notecard-walkthrough/essential-requests.md#working-with-environment-variables) changed.
- `connected` and `signal` — Notecard connected to the network, or received an inbound [signal](https://dev.blues.io/api-reference/glossary.md#signal).

> **Note:**
>
> Once the pin fires, it stays `HIGH` until you re-arm it. Your host code follows a simple loop: wake up on the edge, do the work, re-arm the pin, then go back to sleep. However, be sure to not skip that re-arm step! If you do, the pin never fires again, which is the most common reason an interrupt-driven design works only once.

![Wiring the ATTN pin: a Notecard connects to a Host MCU over one ATTN line, wired either to a GPIO to wake on interrupt or to EN to cut power. Below, the ATTN signal stays low while armed, steps high when it fires, and stays high until re-armed.](https://dev.blues.io/images/blog/posts/interrupt-driven-iot-notecard-attn-pin/attn-wiring-timeline.svg?v=9aa005de)

## An Implementation Walkthrough

These two mechanisms lead to two different paths: **Path 1** keeps the host powered and wakes it on an event (`arm` mode) while **Path 2** cuts the host's power entirely between wakes (`sleep` mode).

### Path 1: Interrupt-Driven Wake (arm mode)

Use this when a device mostly sleeps but must react promptly to an event, like a command arriving from the cloud. The host powers itself down into its own deep-sleep mode and wakes on the `ATTN` change.

#### Wire ATTN to a host GPIO

Connect Notecard's `ATTN` pin to an interrupt-capable GPIO on your host. That's the only connection this path needs as the host keeps its own power and manages its own sleep mode.

#### Configure Notecard

Associate Notecard with your [Notehub](https://blues.com/notehub/) project and set a sync mode. For inbound events, sync mode matters more than people expect. In periodic mode, Notecard only pulls inbound data on its `inbound` schedule, so a `files` interrupt on an inbound Note can't fire until the *next* inbound sync. If you need a command to reach the device as fast as possible, use `continuous` mode with `sync` set to `true`:

```json
{
  "req": "hub.set",
  "product": "com.your-company.your-name:your_project",
  "mode": "continuous",
  "outbound": 30,
  "sync": true
}
```

> **Note:**
>
> That responsiveness isn't free: a continuously-connected radio draws far more than one that wakes on a schedule. So weigh the whole power budget, not just the host. Sleeping the host is a big win when the device is mostly idle, but if you switch to continuous mode purely to catch fast inbound commands, the added radio draw can outweigh what you saved by sleeping the host.

#### Arm the pin

Next, tell Notecard to watch a specified inbound Notefile and fire `ATTN` when it changes:

```json
{
  "req": "card.attn",
  "mode": "arm,files",
  "files": ["data.qi"]
}
```

FYI you can arm on more than one trigger at once. An [asset tracker](https://dev.blues.io/guides-and-tutorials/notecard-guides/asset-tracking-with-gps.md), for example, might want to wake on either an inbound command or physical movement:

```json
{
  "req": "card.attn",
  "mode": "arm,files,motion",
  "files": ["data.qi"]
}
```

The `arm` mode also accepts an optional `seconds` timeout. If no monitored event occurs within that window, the pin fires anyway, giving you a periodic fallback so the host still checks in during a long quiet stretch.

#### Sleep, Wake, and Re-Arm on the Host

The host firmware side is relatively straightforward. Point the MCU's wake source at the `ATTN` pin, then drop into deep sleep:

```c
// NOTE: The deep-sleep entry itself is MCU-specific!
attachInterrupt(digitalPinToInterrupt(ATTN_PIN), onAttn, RISING);
// ... enter STOP / deep sleep here ...
```

When `ATTN` fires, the host wakes, works out what happened, acts on it, and re-arms before going back to sleep. Since you were watching a Notefile, that "what happened" step is usually a [note.get](https://dev.blues.io/api-reference/notecard-api/note-requests/latest.md#note-get) request against `data.qi` to read whatever the cloud sent. Then you send another `card.attn/mode:arm` request and go back to sleep. That's the whole loop.

How you "drop into deep sleep" (and what you can assume when you wake) varies by host MCU. Two things change from chip to chip: **how you arm the pin as a wake source**, and **whether your RAM survives the nap**:

| Host MCU                          | Deepest practical mode                   | RAM retained on wake?               | Arming `ATTN` as a wake source                                                                                                  |
| --------------------------------- | ---------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| STM32L4 (e.g. Blues Swan, Cygnet) | `STOP`, via `LowPower.deepSleep()`       | Yes                                 | Any EXTI-capable GPIO. `LowPower.attachInterruptWakeup(pin, cb, RISING, DEEP_SLEEP_MODE)`. `shutdown()` goes lower but reboots. |
| ESP32 / ESP32-S3                  | Deep sleep, via `esp_deep_sleep_start()` | No, but RTC memory persists         | `esp_sleep_enable_ext0_wakeup(gpio, 1)` on an RTC-capable GPIO (level-triggered), or `ext1` for several.                        |
| Nordic nRF52                      | `System OFF`                             | No, but RAM sections are retainable | GPIO `SENSE` (a latched, level-based port event). `System ON` idle keeps all RAM.                                               |

> **Warning:**
>
> This ESP32 detail often trips people up: a plain `attachInterrupt()` does *not* wake the chip from deep sleep. You have to register `ATTN` as an RTC wakeup source before you sleep, and the pin has to be one of the RTC-capable GPIOs. This works precisely because `ATTN` stays `HIGH` after it fires.

It's important to note that the "RAM retained" column determines how you write your firmware to safely recover from sleep mode. On **STM32** `STOP`, execution resumes on the line right after your sleep call with your variables intact, so the wake handler stays tiny. On **ESP32** deep sleep or **nRF52** `System OFF`, a wake is almost like a reboot: `setup()` runs from the top and ordinary globals are gone. You can still carry state across the gap, but you have to be deliberate about it, using ESP32 RTC slow memory (`RTC_NOINIT_ATTR`), an nRF52 [retained-RAM region](https://docs.nordicsemi.com/r/bundle/sdk_nrf5_v16.0.0/page/ram_retention_example.html), an RTC backup register, or flash.

That storage doesn't have to be on the MCU, though! Notecard can hold it for you if write the state to a local-only `.dbx` [database Notefile](https://dev.blues.io/notecard/notecard-walkthrough/inbound-requests-and-shared-data.md#using-database-files-for-replicated-state) with `note.add`, then read it back on the next boot with `note.get`. A `.dbx` file never syncs, so it costs no cellular data. (The base64 `payload` helper in Path 2 below does the same job, but it's tied to `card.attn` `sleep` mode, where a `LOW` `ATTN` pin gates the host's power off, so it belongs there, not here where the host sleeps itself.)

### Path 2: Power-Off Sleep (sleep mode)

Use this path for the lowest possible power draw, or for a purely periodic device. Instead of the host sleeping itself, Notecard drives the `ATTN` pin `LOW` for a set interval and cuts the host's power outright, then restores it when the interval ends.

> This is the pattern from the [Putting a Host to Sleep Between Sensor Readings](https://dev.blues.io/example-apps/samples/putting-a-host-to-sleep-between-sensor-readings.md) sample app.

#### Wire ATTN to the host's EN pin

Connect `ATTN` to the host's enable (`EN`) pin instead of a GPIO. When Notecard pulls `ATTN` `LOW`, the host loses power, and when it goes `HIGH` again the host boots back up. If you're prototyping on a [Notecarrier F](https://shop.blues.com/products/notecarrier-f?utm_source=dev-blues\&utm_medium=web\&utm_campaign=store-link), you can skip the jumper wire and set the `FEATHER_EN` DIP switch to the `N_ATTN` position, which routes `ATTN` to the Feather host's enable pin for you.

#### Put the Host to Sleep

The host asks Notecard to power it down with a request like this:

```json
{
  "req": "card.attn",
  "mode": "sleep",
  "seconds": 3600
}
```

Because the host loses power and reboots on wake, any state you care about has to survive the outage. Notecard can hold it as a base64 `payload` (one option among MCU flash and external storage), and the [note-arduino](https://github.com/blues/note-arduino) library wraps the encoding in convenience helpers:

```c
void loop()
{
  globalState.cycles++;

  // Hand our state to Notecard and power down for an hour.
  NotePayloadDesc payload = {0};
  NotePayloadAddSegment(&payload, globalSegmentID, &globalState, sizeof(globalState));
  NotePayloadSaveAndSleep(&payload, 3600, NULL);
}
```

On the next boot, the host gets its state back with `{"req":"card.attn","start":true}` (or the `NotePayloadRetrieveAfterSleep` helper) and picks up where it left off. Since every cycle is a cold start, that restore runs on every wake.

![Current draw over time. A polling host shows many repeated spikes and a large shaded energy area; an interrupt-driven host stays near zero and draws a single spike only when an event wakes it, for far less total energy.](https://dev.blues.io/images/blog/posts/interrupt-driven-iot-notecard-attn-pin/attn-current-comparison.svg?v=65539c0c)

## Summary

Interrupt-driven wake through `ATTN` earns its keep when a device's real work is sparse and hard to predict, like a control valve waiting on a command, a tracker that only matters when it moves, or a sensor whose alarm line trips a Notecard AUX GPIO the moment a threshold is crossed. For strictly periodic work, like an hourly sensor reading, `sleep` mode's timer is simpler and lower-power.

To see the full pattern end to end, the [Putting a Host to Sleep Between Sensor Readings](https://dev.blues.io/example-apps/samples/putting-a-host-to-sleep-between-sensor-readings.md) sample has complete host firmware for the full sleep mode path, and the [card.attn API reference](https://dev.blues.io/api-reference/notecard-api/card-requests/latest.md#card-attn) documents every trigger available to you.

Grab a [Blues Starter Kit](https://shop.blues.com/products/blues-starter-kits-cell-wifi?utm_source=dev-blues\&utm_medium=web\&utm_campaign=store-link), wire up that pin, and put a current meter on your host. The numbers tend to be convincing!

Happy hacking! 💙

## Frequently Asked Questions

**What is the Notecard ATTN pin?**

`ATTN` is a dedicated output pin on Notecard that can physically signal a host microcontroller when something happens, such as an inbound Note arriving, a Notefile changing, motion being detected, or a timer elapsing. Wired to a host GPIO or enable pin, it lets the host stay in deep sleep and wake only on a real event instead of polling on a timer.

**What events can wake a host through the ATTN pin?**

Depending on the Notecard model, `ATTN` can fire on inbound Notes or Notefile changes, accelerometer motion, a change in motion state, a GPS position fix, an environment variable update, a successful cellular connection, an inbound signal, an AUX GPIO change, USB power events, and an elapsed timer. Notecard for LoRa supports a subset of these triggers.

**What is the difference between card.attn arm mode and sleep mode?**

In `arm` mode Notecard signals a host that manages its own sleep state, so whether execution and RAM survive a wake depends on that state (STM32 `STOP` keeps RAM; ESP32 deep sleep and nRF52 `System OFF` reset the host). In `sleep` mode Notecard holds the `ATTN` pin `LOW` for a set time; if you've wired `ATTN` to the host's enable or regulator input, that cuts host power entirely, so the host reboots on wake and must restore any state it needs from MCU flash, RTC-backed memory, or Notecard itself.

**Do I need continuous mode to wake on an inbound Note?**

For near-real-time inbound delivery, effectively yes. In periodic mode Notecard only pulls inbound data on its inbound sync schedule, so a file-based `ATTN` interrupt only fires after that sync. Continuous mode delivers inbound Notes sooner at the cost of higher radio power, so the right choice depends on how quickly you need to react.
