Can you use Outboard DFU and card.aux GPIO mode

We use the AUX2 input connected to a momentarily contact push-button switch to wake up our device when we want on-demand readings. We don’t use AUX1, AUX3 or AUX4.

The Outboard DFU documentation states:
Notecard Outboard Firmware Update is only compatible with the following card.aux modes:

  • off
  • dfu
  • neo-monitor

Is this still correct and you can’t also use GPIO mode with Outboard DFU?

If so, any suggestions for implementing a wake button?

Thanks,
Karl

Hello @Karl_iWell!

To answer your question directly, “Yes, the documentation is still correct.”

I know that’s not the answer you were hoping for, so maybe we can explore some alternatives together.

Just to make sure I understand your scenario correctly…

  1. You are using "card.attn","mode":"auxgpio" to signal your Notecard to wake up the connected MCU via the ATTN pin.
  2. You would also like to take advantage of Notecard Outboard Firmware Update

Is my understanding correct?
Zak

Zak,

You have it exactly right.

It is slick that the Notecard can control/wake up the MCU with the ATTN pin, well in reality the ATTN pin controls the power to the MCU. One advantage of this is the MCU can be asleep while the Notecard finishes communication with the Notehub.

I realize I can use the pushbutton to wake up the MCU, but that way seems to have power management issues.

I would appreciate any thoughts you have and will try implementing them and posting my results on this board.

Karl

Hi Karl,

I haven’t forgotten about you! I’ve been iterating on some different approaches all afternoon. I keep thinking that “I’ll have it in 10 minutes”, so I’ve been waiting to reply.

I think I have worked out a good strategy, and as soon as I can prove it out, I’ll post the code!

Best,
Zak

Hi Karl,

I’ve tested the following code myself, and I think I’ve got it! At a high-level, here is how it works:

As you know, "card.aux","mode":"gpio" and "card.aux","mode":"dfu" are incompatible. To work around this limitation, I am polling the Notecard (each time the application wakes), to see if a binary has been downloaded from Notehub to the Notecard. If it has, then I am switching from "card.aux","mode":"gpio" to "card.aux","mode":"dfu", so the AUX pins can perform the DFU.

As far as I can see, the only drawback of this approach is that DFU doesn’t happen the instant the new firmware arrives, but is delayed until the application wakes.

It’s worth noting, I’m using "hub.set","mode":"continuous","sync":true for the sake of testing and performance. You will likely want to switch that to "mode":periodic (without "sync"), since you are presumably creating a low-power application.

You should be able to strip away the NotecardPseudoSensor library, and update the configureNotecard() and sampleSensors() methods with your own logic. Let me know if this solution works for you, or if you have any ideas/concerns about tweaking it.

Cheers,
Zak

Example_ReactiveDfu.ino

// Copyright 2023 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>
#include <NotecardPseudoSensor.h>

#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

using namespace blues;

Notecard notecard;
NotecardPseudoSensor sensor(notecard);

bool dfu_ready = false;

void configureButton () {
    // Configure auxiliary GPIO for input (interrupt trigger)
    if (J *req = notecard.newRequest("card.aux")) {
        JAddStringToObject(req, "mode", "gpio");
        if (J *usage = JAddArrayToObject(req, "usage")) {
            JAddItemToArray(usage, JCreateString(""));             // Aux 1
            JAddItemToArray(usage, JCreateString("count-pullup")); // Aux 2
            JAddItemToArray(usage, JCreateString(""));             // Aux 3
            JAddItemToArray(usage, JCreateString(""));             // Aux 4
            notecard.sendRequest(req);
        } else {
            notecard.deleteResponse(req);
        }
    }
}

void configureNotecard () {
    // Configure Notehub Interactions
    if (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
    }

    // Disable Notecard Flashing MCU, but allow
    // prepatory downloading from Notehub to Notecard
    if (J *req = notecard.newRequest("card.dfu")) {
        JAddStringToObject(req, "name", "stm32");
        JAddBoolToObject(req, "off", true);
        notecard.sendRequest(req);
    }

    // Setup Aux Mode for Button
    configureButton();
}

void performDFU () {
    // Place `card.aux` in ODFU compatible configuration
    if (J *req = notecard.newRequest("card.aux")) {
        JAddStringToObject(req, "mode", "off");
        notecard.sendRequest(req);
    }

    // Allow Notecard to flash MCU with downloaded binary
    if (J *req = notecard.newRequest("card.dfu")) {
        JAddStringToObject(req, "name", "stm32");
        JAddBoolToObject(req, "on", true);
        notecard.sendRequest(req);
    }

    // Sync to start DFU
    notecard.sendRequest(notecard.newRequest("hub.sync"));
}

bool queryDFUStatus () {
    bool result = false;

    // Check Notecard
    if (J *req = notecard.newRequest("dfu.status")) {
        JAddBoolToObject(req, "on", true);
        if (J *rsp = notecard.requestAndResponse(req)) {
            // Process response contents
            if (JContainsString(rsp,"mode","outboard-ready")) {
                result = true;
            } else {
                notecard.logDebug("INFO: No firmware available on Notecard.\n");
            }
        }
    }

    return result;
}

void sampleSensors() {
    float temperature = sensor.temp();
    float humidity = sensor.humidity();

    usbSerial.print("Temperature = ");
    usbSerial.print(temperature);
    usbSerial.println(" *C");
    usbSerial.print("Humidity = ");
    usbSerial.print(humidity);
    usbSerial.println(" %");

    if (J *req = notecard.newRequest("note.add"))
    {
        JAddStringToObject(req, "file", "sensors.qo");
        JAddBoolToObject(req, "sync", true);
        J *body = JAddObjectToObject(req, "body");
        if (body)
        {
            JAddNumberToObject(body, "temp", temperature);
            JAddNumberToObject(body, "humidity", humidity);
        }
        notecard.sendRequest(req);
    }
}

// One-time Arduino initialization
void setup()
{
    // Indicate active execution with LED
    ::pinMode(LED_BUILTIN, OUTPUT);
    ::digitalWrite(LED_BUILTIN, HIGH);

    // 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;)
        ;
    notecard.setDebugOutputStream(usbSerial);
#endif

    // Initialize the physical I/O channel to the Notecard
    notecard.begin();

    // Configure Notecard Settings
    configureNotecard();

    // Do some work
    sampleSensors();

    // Check for Firmware Update
    dfu_ready = queryDFUStatus();
    if (dfu_ready) {
        performDFU();
    }
}

void loop()
{
    if (!dfu_ready) {
        // Request sleep from loop to safeguard against tranmission failure, and
        // ensure sleep request is honored so power usage is minimized.
        if (J *req = notecard.newCommand("card.attn"))
        {
            JAddStringToObject(req, "mode", "rearm,auxgpio,sleep");
            JAddNumberToObject(req, "seconds", 60);
            notecard.sendRequest(req);
        }
    }
    ::delay(3000);
}

Thanks Zak,

I appreciate the testing and example code. In our application, we are in "hub.set","mode":"minimum"

After collecting our sensor data and doing note.add we then issue
"hub.sync","allow":true

We then monitor hub.status until the notecard connects to cell network or we timeout because the notecard never connected to the cell network. Once notecard connects, we monitor hub.sync.status until the sync is completed. During this sync is when I’m assuming the new firmware will download to the notecard.

Is there anything else I need to address in using "hub.set","mode":"minimum"?

That is how I will be testing, I’ll let you know how it works.

Thanks again,
Karl