Hi,
I am trying to fix a crash in the dfu service. I am using a custom board based on the NR52840, SDK 15.0 and softdevice 6.0.
I am trying to add some new characteristics to the DFU service that are populated from values in FDS. This causes the DFU service to crash while initialising the bluetooth transport. The crash started when I added in the calling of the characteristic code.
The relevant code is:
FDS header
#ifndef DFU_CONF_H
#define DFU_CONF_H
#include "fds.h"
/** @file dfu_conf.h
** @brief configuration storage api
**
*/
/** @ingroup config
** @{
*/
/** @enum record_handles
*** @brief list of handles for flash stored records
*/
enum record_handles {
CONF_BEGIN = 0x100, ///< valid ids start at 0x0001 so the next item will be valid
CONF_MOVEMENT_THRES, ///< movement threshold handle @see sample.c
CONF_TIME_THRES, ///< movement time threshold @see sample.c
CONF_IDLE_TIMEOUT, ///< activity timeout @see standby.c
CONF_BAR_THRES, ///< average value when off bar
CONF_BAR_RANGE, ///< variation for off bar value
CONF_BAR_ENABLED, ///< switch on and off bar
CONF_SAMPLE_MODE, ///< 1, 3 or 5 tof sensors in use
CONF_SENSOR_ID, ///< handle for the sensor id
CONF_X_OFFSET, ///< handle for the mems x_offset calib value
CONF_Y_OFFSET, ///< handle for the mems y_offset calib value
CONF_Z_OFFSET, ///< handle for the mems z_offset calib value
CONF_X_SCALE, ///< handle for the mems x_scale calib value
CONF_Y_SCALE, ///< handle for the mems y_scale calib value
CONF_Z_SCALE, ///< handle for the mems z_scale calib value
CONF_HW_MAJ, ///< handle for the hardware revision
CONF_HW_MIN, ///< handle for the hardware revision
CONF_FW_MAJ, ///< handle for firmware revision based on release numbers from version.h
CONF_FW_MIN, ///< handle for firmware revision based on git numbers from version.h
CONF_SW_MAJ, ///< handle for gale protocol version
CONF_SW_MIN, ///< handle for gale protocol version
CONF_LAST, ///< max for last is 0xC000, peer manager uses this and above
};
#define CONF_FILEHANDLE 0xF010 ///< conf file handle - range 0x0000 to 0xbfff
/** @brief resets configuration to default
*** @returns any error that occurred
*/
ret_code_t conf_reset(void);
/** @brief initialise the configuration storage
*** @returns any error that occured
*/
ret_code_t conf_init(void);
/** @brief configuration storage event handler
*** @param[in] p_evt the fds event structure
*/
void conf_on_event(fds_evt_t const *p_evt);
/** @brief get the specified record
*** @param[in] id is the record id @see record_handles
*** @param[out] p_data receives the value in the specified record
*** @returns any error that occurs
*/
ret_code_t conf_get(uint32_t id, uint32_t *p_data);
/** @brief sets the specified record to the data value
*** @param[in] id is the record id @see record_handles
*** @param[in] data is the 32 bit value to write to the record
*** @returns any error that occurs
*/
ret_code_t conf_set(uint32_t id, uint32_t data);
/** @brief checks if a value is there and if not sets it to the default
*** @param[in] handle_ record handle to check and set
*** @returns any error that occured
*/
ret_code_t conf_check_and_set(uint16_t handle_);
/** @brief dumps out all records in the file
*/
void conf_dump(void);
/// @}
#endif
FDS code
/** @file config.c
** @brief configuration storage api
**
*/
/** @defgroup config configuration storage in flash
*** @{
*** @addtogroup Drivers
*** @{
*/
#include "fds.h"
#include "string.h"
#include "nrf_mtx.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "app_scheduler.h"
#include "dfu_conf.h"
bool conf_is_started = false; ///< status of init: true and we have successfully inited
bool conf_write_done = false; ///< flag to indicate write is finished
static nrf_mtx_t m_conf_mutex;
/** @brief dumps out all records in the file
*/
void conf_dump(void) {
fds_record_desc_t desc;
fds_find_token_t tok;
uint32_t value;
fds_flash_record_t flash_record;
ret_code_t err_code;
NRF_LOG_INFO("DUMPING...");
memset(&tok, 0x00, sizeof(fds_find_token_t));
memset(&desc, 0x00, sizeof(fds_record_desc_t ));
while(fds_record_iterate(&desc, &tok) != FDS_ERR_NOT_FOUND) {
memset(&flash_record, 0x00, sizeof(fds_flash_record_t));
err_code = fds_record_open(&desc, &flash_record);
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf get: failed open: 0x%x", err_code);
return;
}
memcpy(&value, flash_record.p_data, sizeof(uint32_t));
NRF_LOG_INFO("id: 0x%x val: %d", desc.record_id, value);
NRF_LOG_FLUSH();
err_code = fds_record_close(&desc);
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf get: failed close: 0x%x", err_code);
return;
}
}
NRF_LOG_INFO("DUMPED.");
}
/** @brief initialise the configuration storage
*** @returns any error that occured
*/
ret_code_t conf_init(void) {
ret_code_t err_code;
fds_stat_t m_stat;
int i = 0;
// register a handler
err_code = fds_register(conf_on_event);
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf init: failed to register event handler0x%x", err_code);
return err_code;
}
// init the fds api
err_code = fds_init();
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf init: failed to init the api 0x%x", err_code);
return err_code;
}
// busy wait for started
while ((!conf_is_started) && (i < 10000)) {
i = i + 1;
}
if (!conf_is_started) {
NRF_LOG_INFO("conf init: failed to init the api - no inited event");
return err_code;
}
}
/** @brief configuration storage event handler
*** @param[in] p_evt the fds event structure
*/
void conf_on_event(fds_evt_t const *p_evt) {
switch (p_evt->id) {
case FDS_EVT_INIT:
if (p_evt->result == FDS_SUCCESS) {
conf_is_started = true;
}
break;
case FDS_EVT_WRITE:
conf_write_done = true;
if (p_evt->result != FDS_SUCCESS) {
NRF_LOG_INFO("conf evt: failed write 0x%x", p_evt->result);
}
break;
case FDS_EVT_UPDATE:
conf_write_done = true;
if (p_evt->result != FDS_SUCCESS) {
NRF_LOG_INFO("conf evt: failed update 0x%x", p_evt->result);
}
break;
case FDS_EVT_DEL_RECORD:
conf_write_done = true;
if (p_evt->result != FDS_SUCCESS) {
NRF_LOG_INFO("conf evt: failed del rec 0x%x", p_evt->result);
}
break;
case FDS_EVT_DEL_FILE:
conf_write_done = true;
if (p_evt->result != FDS_SUCCESS) {
NRF_LOG_INFO("conf evt: failed del file 0x%x", p_evt->result);
}
break;
case FDS_EVT_GC:
if (p_evt->result != FDS_SUCCESS) {
NRF_LOG_INFO("conf evt: failed GC 0x%x", p_evt->result);
}
break;
default:
break;
}
}
/** @brief get the specified record
*** @param[in] id is the record id @see record_handles
*** @param[out] p_data receives the value in the specified record
*** @returns any error that occurs
*/
ret_code_t conf_get(uint32_t id, uint32_t *p_data) {
fds_record_desc_t record_desc;
fds_find_token_t ftok;
fds_flash_record_t flash_record;
ret_code_t err_code;
// find the record
// assert only one record per key otherwise only finds the first
// assert always a valid record because we make sure of it in the init function
memset(&ftok, 0x00, sizeof(fds_find_token_t));
memset(&record_desc, 0x00, sizeof(fds_record_desc_t));
err_code = fds_record_find(CONF_FILEHANDLE, id, &record_desc, &ftok);
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf get: failed find: 0x%x", err_code);
return err_code;
}
// open the record
memset(&flash_record, 0x00, sizeof(fds_flash_record_t));
err_code = fds_record_open(&record_desc, &flash_record);
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf get: failed open: 0x%x", err_code);
return err_code;
}
// read the data
memcpy(p_data, flash_record.p_data, sizeof(uint32_t));
// close the record
err_code = fds_record_close(&record_desc);
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf get: failed close: 0x%x", err_code);
return err_code;
}
return FDS_SUCCESS;
}
/** @brief sets the specified record to the data value
*** @param[in] id is the record id @see record_handles
*** @param[in] data is the 32 bit value to write to the record
*** @returns any error that occurs
*/
ret_code_t conf_set(uint32_t id, uint32_t data) {
fds_record_desc_t record_desc;
fds_find_token_t ftok;
fds_record_t record;
ret_code_t err_code;
static uint32_t val;
// find the record
// assert only one record per key otherwise only finds the first
// assert always a valid record because we make sure of it in the init function
memset(&ftok, 0x00, sizeof(fds_find_token_t));
memset(&record_desc, 0x00, sizeof(fds_record_desc_t));
err_code = fds_record_find(CONF_FILEHANDLE, id, &record_desc, &ftok);
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf set: failed find: 0x%x", err_code);
return err_code;
}
// update the record
val = data;
record.file_id = CONF_FILEHANDLE;
record.key = id;
record.data.p_data = &val;
record.data.length_words = 1; //32bit width words
conf_write_done = false;
err_code = fds_record_update(&record_desc, &record);
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf set: failed update: 0x%x", err_code);
return err_code;
}
while (!conf_write_done)
;
// garbage collect
err_code = fds_gc();
if (err_code != FDS_SUCCESS) {
NRF_LOG_INFO("conf set: failed garbage collect: 0x%x", err_code);
return err_code;
}
return NRF_SUCCESS;
}
/// @}
/// @}
Characteristic code
#include <stdlib.h>
#include "string.h"
#include "nrf_dfu_ble.h"
#include "dfu_conf.h"
#include "ble_gatts.h"
// this is just about ensuring we dont collide
#define FW_UUID CONF_FW_MAJ + BLE_DFU_PKT_CHAR_UUID
#define HW_UUID CONF_HW_MAJ + BLE_DFU_PKT_CHAR_UUID
#define SW_UUID CONF_SW_MAJ + BLE_DFU_PKT_CHAR_UUID
#define MAXCHARICSIZE 20 ///< amount of data in a characteristic in bytes
/** @brief add a characteristic to DFU service
*
* @param[in] svc_h service handle
* @param[in] charic_uuid 16 bit uuid of this charic.
* @param[in] char_h our charic handle
* @param[in] charic_flags charic flags cf Galebluetoothuuids.h
* @param[in] p_desc pointer to user description string
* @param[in] size - initial/max size of the characteristic
* @returns any errors
*/
uint32_t charic_add(uint16_t *svc_h,
uint8_t *svc_type,
uint16_t charic_uuid,
ble_gatts_char_handles_t *char_h,
uint8_t *p_desc,
uint8_t size) {
uint32_t err_code;
ble_uuid_t char_uuid;
ble_uuid_t base_uuid;
ble_gatts_attr_md_t attr_md;
ble_gatts_attr_t attr_char_value;
ble_gatts_char_md_t char_md;
ble_gatts_attr_md_t cccd_md;
uint8_t value[20];
base_uuid.uuid = BLE_DFU_SERVICE_UUID;
base_uuid.type = (*svc_type);
char_uuid.uuid = charic_uuid;
// charic type is set by sd_ble_uuid_vs_add
//err_code = sd_ble_uuid_vs_add(&base_uuid, &char_uuid.type);
// APP_ERROR_CHECK(err_code);
memset(value, 0, sizeof(value));
memset(&attr_md, 0, sizeof(attr_md));
memset(&char_md, 0, sizeof(char_md));
memset(&cccd_md, 0, sizeof(cccd_md));
char_md.char_props.read = 1;
attr_md.rd_auth = 1; // force the stack to event when charic is read
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
attr_md.vlen = 1;
attr_md.vloc = BLE_GATTS_VLOC_STACK;
cccd_md.vloc = BLE_GATTS_VLOC_STACK;
char_md.p_cccd_md = NULL;
char_md.p_char_user_desc = p_desc;
char_md.char_user_desc_size = strlen(p_desc);
char_md.char_user_desc_max_size = strlen(p_desc);
memset(&attr_char_value, 0, sizeof(attr_char_value));
attr_char_value.p_uuid = &char_uuid;
attr_char_value.p_attr_md = &attr_md;
attr_char_value.init_len = size * sizeof(uint8_t);
attr_char_value.max_len = MAXCHARICSIZE;
attr_char_value.p_value = (uint8_t *) &value;
err_code = sd_ble_gatts_characteristic_add((*svc_h),
&char_md,
&attr_char_value,
char_h);
APP_ERROR_CHECK(err_code);
return NRF_SUCCESS;
}
/** @brief generic function to update a characteristic
** @param[in] charic_index - index into the charic array in the service control structure
** @param[in] value - pointer to the value to set
** @param[in] len - lenght of the value buffer
*/
void dfu_update_charic(uint16_t s_handle, ble_gatts_char_handles_t *p_c_handle, uint8_t *value, uint8_t len) {
ble_gatts_value_t charic_value;
uint32_t err_code;
charic_value.len = len;
charic_value.offset = 0;
charic_value.p_value = value;
err_code = sd_ble_gatts_value_set(s_handle,
p_c_handle->value_handle,
&charic_value);
APP_ERROR_CHECK(err_code);
}
void charic_add_all(ble_dfu_t *p_dfu) {
char buffer_[20];
ble_gatts_char_handles_t char_h[3];
uint32_t tmp;
// hardware rev
strcpy(buffer_, "Hardware Revision");
charic_add(&p_dfu->service_handle,
&p_dfu->uuid_type,
HW_UUID,
&char_h[0],
buffer_,
strlen(buffer_));
conf_get(CONF_HW_MAJ, &tmp);
itoa(tmp, &buffer_[0], 10);
strcat(&buffer_[0], ".");
conf_get(CONF_HW_MIN, &tmp);
itoa(tmp, &buffer_[strlen(&buffer_[0])], 10);
dfu_update_charic(p_dfu->service_handle,
&char_h[0],
(uint8_t *)buffer_,
strlen(buffer_));
// firmware rev
strcpy(buffer_, "Firmware Revision");
charic_add(&p_dfu->service_handle,
&p_dfu->uuid_type,
FW_UUID,
&char_h[1],
buffer_,
strlen(buffer_));
conf_get(CONF_FW_MAJ, &tmp);
itoa(tmp, &buffer_[0], 16);
strcat(&buffer_[0], ".");
conf_get(CONF_FW_MIN, &tmp);
itoa(tmp, &buffer_[strlen(&buffer_[0])], 16);
dfu_update_charic(p_dfu->service_handle,
&char_h[1],
(uint8_t *)buffer_,
strlen(buffer_));
// gale protocol revision
strcpy(buffer_, "Gale Revision");
charic_add(&p_dfu->service_handle,
&p_dfu->uuid_type,
SW_UUID,
&char_h[2],
buffer_,
strlen(buffer_));
conf_get(CONF_SW_MAJ, &tmp);
itoa(tmp, &buffer_[0], 10);
strcat(&buffer_[0], ".");
conf_get(CONF_SW_MAJ, &tmp);
itoa(tmp, &buffer_[strlen(&buffer_[0])], 10);
dfu_update_charic(p_dfu->service_handle,
&char_h[2],
(uint8_t *)buffer_,
strlen(buffer_));
}
Main
/**
* Copyright (c) 2016 - 2018, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/** @file
*
* @defgroup bootloader_secure_ble main.c
* @{
* @ingroup dfu_bootloader_api
* @brief Bootloader project main file for secure DFU.
*
*/
#include <stdint.h>
#include "boards.h"
#include "nrf_mbr.h"
#include "nrf_bootloader.h"
#include "nrf_bootloader_app_start.h"
#include "nrf_dfu.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "app_error.h"
#include "app_error_weak.h"
#include "nrf_bootloader_info.h"
#include "app_timer.h"
#include "nrf_delay.h"
#include "dfu_conf.h"
static void on_error(void)
{
NRF_LOG_FINAL_FLUSH();
#if NRF_MODULE_ENABLED(NRF_LOG_BACKEND_RTT)
// To allow the buffer to be flushed by the host.
nrf_delay_ms(100);
#endif
#ifdef NRF_DFU_DEBUG_VERSION
NRF_BREAKPOINT_COND;
#endif
NVIC_SystemReset();
}
void app_error_handler(uint32_t error_code, uint32_t line_num, const uint8_t * p_file_name)
{
NRF_LOG_ERROR("%s:%d", p_file_name, line_num);
on_error();
}
void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info)
{
NRF_LOG_ERROR("Received a fault! id: 0x%08x, pc: 0x%08x, info: 0x%08x", id, pc, info);
on_error();
}
void app_error_handler_bare(uint32_t error_code)
{
NRF_LOG_ERROR("Received an error: 0x%08x!", error_code);
on_error();
}
/**
* @brief Function notifies certain events in DFU process.
*/
static void dfu_observer(nrf_dfu_evt_type_t evt_type)
{
switch (evt_type)
{
case NRF_DFU_EVT_DFU_FAILED:
case NRF_DFU_EVT_DFU_ABORTED:
case NRF_DFU_EVT_DFU_INITIALIZED:
bsp_board_init(BSP_INIT_LEDS);
bsp_board_led_on(BSP_BOARD_LED_0);
bsp_board_led_on(BSP_BOARD_LED_1);
bsp_board_led_off(BSP_BOARD_LED_2);
break;
case NRF_DFU_EVT_TRANSPORT_ACTIVATED:
bsp_board_led_off(BSP_BOARD_LED_1);
bsp_board_led_on(BSP_BOARD_LED_2);
break;
case NRF_DFU_EVT_DFU_STARTED:
break;
default:
break;
}
}
/**@brief Function for application main entry. */
int main(void)
{
uint32_t ret_val;
(void) NRF_LOG_INIT(app_timer_cnt_get);
NRF_LOG_DEFAULT_BACKENDS_INIT();
conf_init();
conf_dump();
// Protect MBR and bootloader code from being overwritten.
ret_val = nrf_bootloader_flash_protect(0, MBR_SIZE, false);
APP_ERROR_CHECK(ret_val);
ret_val = nrf_bootloader_flash_protect(BOOTLOADER_START_ADDR, BOOTLOADER_SIZE, false);
APP_ERROR_CHECK(ret_val);
NRF_LOG_INFO("Inside main");
NRF_LOG_FLUSH();
ret_val = nrf_bootloader_init(dfu_observer);
APP_ERROR_CHECK(ret_val);
// Either there was no DFU functionality enabled in this project or the DFU module detected
// no ongoing DFU operation and found a valid main application.
// Boot the main application.
nrf_bootloader_app_start();
// Should never be reached.
NRF_LOG_INFO("After main");
NRF_LOG_FLUSH();
}
DFU init from nrf_dfu_ble.c
void charic_add_all(ble_dfu_t *p_dfu);
/**@brief Function for checking if the CCCD of DFU Control point is configured for Notification.
*
* @details This function checks if the CCCD of DFU Control Point characteristic is configured
* for Notification by the DFU Controller.
*
* @param[in] p_dfu DFU Service structure.
*
* @return True if the CCCD of DFU Control Point characteristic is configured for Notification.
* False otherwise.
*/
uint32_t ble_dfu_init(ble_dfu_t * p_dfu)
{
ASSERT(p_dfu != NULL);
ble_uuid_t service_uuid;
uint32_t err_code;
m_conn_handle = BLE_CONN_HANDLE_INVALID;
BLE_UUID_BLE_ASSIGN(service_uuid, BLE_DFU_SERVICE_UUID);
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
&service_uuid,
&(p_dfu->service_handle));
VERIFY_SUCCESS(err_code);
ble_uuid128_t const base_uuid128 =
{
{
0x50, 0xEA, 0xDA, 0x30, 0x88, 0x83, 0xB8, 0x9F,
0x60, 0x4F, 0x15, 0xF3, 0x00, 0x00, 0xC9, 0x8E
}
};
err_code = sd_ble_uuid_vs_add(&base_uuid128, &p_dfu->uuid_type);
VERIFY_SUCCESS(err_code);
err_code = dfu_pkt_char_add(p_dfu);
VERIFY_SUCCESS(err_code);
err_code = dfu_ctrl_pt_add(p_dfu);
VERIFY_SUCCESS(err_code);
charic_add_all(p_dfu);
return NRF_SUCCESS;
}
listing from a run:
<info> app: DUMPING... <info> app: id: 0x22E val: 300 <info> app: id: 0x260 val: 128 <info> app: id: 0x261 val: 0 <info> app: id: 0x262 val: 65534 <info> app: id: 0x263 val: 47181<info> app: DUMPING... <info> app: id: 0x22E val: 300 <info> app: id: 0x267 val: 128 <info> app: id: 0x268 val: 0 <info> app: id: 0x269 val: 65534 <info> app: id: 0x26A val: 47181 <info> app: id: 0x26B val: 2 <info> app: id: 0x26C val: 2 <info> app: id: 0x26D val: 7111 <info> app: id: 0x8D val: 30 <info> app: id: 0x8E val: 10 <info> app: id: 0x92 val: 1 <info> app: id: 0x93 val: 1 <info> app: id: 0x95 val: 0 <info> app: id: 0x96 val: 0 <info> app: id: 0x97 val: 0 <info> app: id: 0xD3 val: 56 <info> app: DUMPED. <info> app: Inside main <debug> app: In nrf_bootloader_init <debug> nrf_dfu_settings: Calling nrf_dfu_settings_init()... <debug> nrf_dfu_flash: Initializing nrf_fstorage_nvmc backend. <info> app: No firmware to activate. <debug> app: Enter nrf_dfu_app_is_valid <debug> app: Return true. App was valid <debug> app: DFU mode requested via GPREGRET. <info> nrf_bootloader_wdt: WDT is not enabled <debug> app: in weak nrf_dfu_init_user <info> app_timer: RTC: initialized. <info> app: Entering DFU mode. <info> app: 1
The listing on different runs stops at different points around starting the ble transport for dfu. The error is cause by the on_error handler in main.
Many thanks in advance
Paul