ATTN pin fires for inbound notes, but not for environment changes or async web.* requests

Summary

I’ve discovered that the ATTN pin does not fire when environment variables are changed in Notehub, even when configured with `mode: “arm,env,files”`. The ATTN pin works correctly for inbound commands (`command.qi`), showing that the hardware and interrupt configuration are correct.

This behavior has been present since at least firmware 7.x in my experience, but wasn’t a blocker earlier. It’s now critical for us as async `web.put` requests are a key part of our current design, and these also don’t trigger ATTN when results are ready.

Environment

- Notecard: NOTE-WBNAW (Wi-Fi + Cell)

- Firmware: notecard-10.1.1.17497

- Notecard Connection: Wi-Fi

- Host: ESP32-S3 via UART

ATTN Configuration

{“req”:“card.attn”,“mode”:“arm,env,files”,“on”:true,“files”:[“command.qi”,“webreq.dbx”]}

Verification confirms `modified` is being monitored (via `env` mode).

Observed Behavior

Trigger Expected Actual
Environment variable change ATTN fires :cross_mark: ATTN silent
Async web.get result ATTN fires :cross_mark: ATTN silent
Inbound note ATTN fires :white_check_mark: ATTN fires

Key Finding

When an inbound command triggers ATTN, the response includes *both* `command.qi` AND `modified`:

{“files”:[“command.qi”,“modified”],“set”:true}

I believe this means Notecard knows environment variables changed, but does not trigger ATTN for them independently. env.modified also shows that Notecard sees the changes to the environment variables.

Reproduction

I’ve created a diagnostic tool to reproduce this issue:

The tool includes an automated test (press `M`) that monitors for 2 minutes and reports whether ATTN fired when environment variables change, as well as various other tests for async/sync web.* requests and inbound queues.

Hello @devElert,

Thanks for reporting! I’m trying to reproduce your issue, and I’ll have more information to follow shortly.

Cheers,

Zak

1 Like

Hi @devElert ,

I grabbed on of our examples, and tried to reduce it to the simplest possible repro of the issue you found.

Using the example below, I am UNABLE to reproduce the issue you’ve reported. Here, the ATTN pin is firing when environment variables are updated as we would expect.

// Copyright 2026 Blues Inc.  All rights reserved.
//
// Use of this source code is governed by licenses granted by the
// copyright holder including that found in the LICENSE file.

// Include the Arduino library for the Notecard
#include <Notecard.h>

// GPIO pin definitions
#define ATTN_INPUT_PIN 5 // Any digital GPIO pin on your board

// #define txRxPinsSerial Serial1
#define usbSerial Serial

// This is the unique Product Identifier for your device
#ifndef PRODUCT_UID
#define PRODUCT_UID "" // "com.my-company.my-name:my-project"
#pragma message "PRODUCT_UID is not defined in this example. Please ensure your Notecard has a product identifier set before running this example or define it in code here. More details at https://dev.blues.io/tools-and-sdks/samples/product-uid"
#endif

#define myProductID PRODUCT_UID

Notecard notecard;

// Set to true whenever ATTN interrupt occurs
static bool attnInterruptOccurred = false;

// Interrupt Service Routine for ATTN_INPUT_PIN transitions rising from LOW
// to HIGH
void attnISR()
{
    attnInterruptOccurred = true;
}

// Re-arm the interrupt
void attnArm()
{
    // Make sure that we pick up the next RISING edge of the interrupt
    attnInterruptOccurred = false;

    // Set the ATTN pin low, and wait for the earlier of file modification or
    // a timeout
    J *req = notecard.newRequest("card.attn");
    JAddStringToObject(req, "mode", "rearm,env");
    notecard.sendRequest(req);
}

// One-time Arduino initialization
void setup()
{
    // Set up for debug output (if available).
#ifdef usbSerial
    // If you open Arduino's serial terminal window, you'll be able to watch
    // JSON objects being transferred to and from the Notecard for each request.
    usbSerial.begin(115200);
    const size_t usb_timeout_ms = 3000;
    for (const size_t start_ms = millis(); !usbSerial && (millis() - start_ms) < usb_timeout_ms;)
        ;

    // For low-memory platforms, don't turn on internal Notecard logs.
#ifndef NOTE_C_LOW_MEM
    notecard.setDebugOutputStream(usbSerial);
#else
#pragma message("INFO: Notecard debug logs disabled. (non-fatal)")
#endif // !NOTE_C_LOW_MEM
#endif // usbSerial

    // Initialize the physical I/O channel to the Notecard
#ifdef txRxPinsSerial
    notecard.begin(txRxPinsSerial, 9600);
#else
    notecard.begin();
#endif

    J *req = notecard.newRequest("hub.set");
    if (myProductID[0])
    {
        JAddStringToObject(req, "product", myProductID);
    }
    JAddStringToObject(req, "mode", "continuous");
    JAddBoolToObject(req, "sync", true);
    notecard.sendRequestWithRetry(req, 5); // 5 seconds

    // Attach an interrupt pin
    pinMode(ATTN_INPUT_PIN, INPUT);
    attachInterrupt(digitalPinToInterrupt(ATTN_INPUT_PIN), attnISR, RISING);
    attnArm();

    usbSerial.println("[APP] Awaiting environment variable events...");
}

// In the Arduino main loop which is called repeatedly, add outbound data every 15 seconds
void loop()
{
    if (attnInterruptOccurred) {
        usbSerial.println("[APP] ATTN pin fired! Environment variables have changed.");
        attnArm();
    }
}

I would appreciate if you could help me modify the example to reproduce your issue. This would ensure that we have a simple way to isolate, test and fix your issue.

Best,

Zak

I don’t understand how this can work, unless you have some existing card.attn settings on the notecard. The ATTN pin is never armed as is only rearmed after the interrupt.

Lol. :person_facepalming:

I must have manually triggered the interrupt when I set up the hardware. I bet I flashed the firmware before I plugged in the ATTN jumper.

Let’s add a call to attnArm() at the end of setup() and that should solve the chicken/egg problem.

I’ve updated the code above to reflect the change.

~Zak