Handling Reboot Requests

Your MCU code will often use the attrEventCallback() callback to take action when an attribute value has been changed by a call to af_lib_set_attribute(). Your primary concern will be changes to attributes you define, but your code must also watch for changes to the system attribute AF_SYSTEM_ASR_STATE. This attribute can have one of several values, including:

  • 0 = Rebooted
  • 1 = Linked
  • 2 = Updating
  • 3 = Update Ready to Apply (Reboot Requested)
  • 4 = Initialized
  • 5 = Relinked

AF_SYSTEM_ASR_STATE will have value 3 (Reboot Requested) whenever ASR receives an Over-the-Air (OTA) firmware update. When the OTA is complete, ASR sends an update message with that attribute ID and value, and your MCU code is responsible for recognizing this case and triggering a reboot. You code should do this calling af_lib_set_attribute() for the attribute AF_SYSTEM_COMMAND, with value 1. Here’s a snippet:

bool initializationPending = true;  // If true, we're waiting on AF_MODULE_STATE_INITIALIZED
bool rebootPending = false;          // If true, a reboot is needed (e.g. if we received an OTA firmware update.)

// SNIP //

void attrEventCallback(const af_lib_event_type_t eventType,
                       const af_lib_error_t error,
                       const uint16_t attributeId,
                       const uint16_t valueLen,
                       const uint8_t* value) {

  switch (attributeId) {
// SNIP //

    case AF_SYSTEM_ASR_STATE:
      switch (value[0]) {

    case AF_MODULE_STATE_REBOOTED:
      // ASR has been rebooted
      initializationPending = true;
      break;

    case AF_MODULE_STATE_LINKED:
      break;

    case AF_MODULE_STATE_UPDATING:
      break;

    case AF_MODULE_STATE_UPDATE_READY:
      // ASR signals that an update has been received and a reboot is required to use it
      rebootPending = true;
      break;

    case AF_MODULE_STATE_INITIALIZED:
      // ASR signals that it's ready
      initializationPending = false;
      break;

    default:
      break;
    }
    break;

  default:
    break;
  }
}

void loop() {
  af_lib_loop(af_lib);    // Give the afLib state machine some time.

  if (initializationPending) {
    // If we're awaiting initialization, don't bother checking/setting attributes
  } else {
    
    if (rebootPending) {  // Someone wants us to reboot
      int retVal = af_lib_set_attribute_32(af_lib, AF_SYSTEM_COMMAND, AF_MODULE_COMMAND_REBOOT);
      rebootPending = (retVal != AF_SUCCESS);

      // Check for success; if not successful, we'll re-try next time around loop()
      if (!rebootPending) {
        // Reboot command sent; now awaiting AF_MODULE_STATE_INITIALIZED
        initializationPending = true;
      }

    }

  // Your application code here //
  
  }
}

Our attrEventCallback() checks the supplied attribute ID, looking for AF_SYSTEM_ASR_STATE. If we have received an update message for that attribute ID, we check the attribute value. If the value is 3 (AF_MODULE_STATE_UPDATE_READY), then we trigger a reboot by setting a global, rebootPending. In our main loop() function, any time rebootPending is true, we'll call af_lib_set_attribute_32() for the AF_SYSTEM_COMMAND attribute, with value 1 (which is the value that signals a reboot). Our code resets rebootPending to false if it succeeds; if the set_attribute() call fails (for instance if the request queue is full), we'll try again next time around the loop().