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);
}