This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Recover from a sd_softdevice_disable()

When writing to flash it is MUCH easier to perform the write if SoftDevice is disabled. If it is not, one has to wait for an event to continue writing. ON the Nordic platforms, that is very complicated as one already has code for fragmented notifications handling asynchronous waits. The only 'wait' one can perform that is not a CPU busy loop is through sd_app_evt_wait().

Thus I simply disable the softdevice and the write is synchronous; it returns when done. However, now I need to recover so I can start advertising again.

My work flow is:

  • Advertise
  • Get connected
  • Get disconnected
  • disable soft device
  • write to flash
  • ? (what do I need to do here)
  • Advertise

My cop-out approach has been after the write to flash to simply call NVIC_SystemReset() and have the whole application start from scratch. That approach has worked fine on the DKs but as soon as I invoke that NVIC_SystemReset() on an nRF52840 dongle, it no longer works. The app simply dies and one needs to power cycle the dongle.

Since NVIC_SystemReset() does not work, how do I recover from an sd_softdevice_disable? What does that disable? The documentation suggests EVERYTHING. Have I lost my service tables? Do I have to re-initialize the BLE stack? How many of the startup 'init' methods do I need to call?

  • Yes it did. And I guess the answer to the actual posted question (should I ever need to do it) is that I basically have to start from scratch. In other words, the best option after the disabled soft device call is to do a system reset and reinitialize everything, the bt stack, the services and characteristics, advertisement, etc.

  • Hi

    All the SoftDevice init will need to be repeated, yes, but a full system reset is not really necessary. 

    I would recommend designing some kind of Bluetooth handler/manager module that takes care of all the SoftDevice init steps, allowing you to initialize the SoftDevice from a single call to this module in the rest of your code. 

    This will speed up the process of disabling, writing to flash, re-enabling the SoftDevice compared to doing a full system reset. 

    Best regards
    Torbjørn

  • I was thinking about doing that but basically realized that would be about 90% of my main function.

    int main(void)
    {
        ret_code_t err_code;
        unsigned char cccds[2];
        bool result;
        m_adv_handle = 0;
        queue = initializeQueue(10);
        if (queue == NULL)
        {
            NRF_LOG_DEBUG("Could not allocate memory for the message queue. Quitting");
            return 0;
        }
        ghs_abort = false;
        send_live_data = false;
        first_cont_sent = false;
        numberOfStoredMsmtGroups = 0;
        global_send.continuous_stage = CONT_NONE;
    
        // Initialize.
        err_code = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(err_code);
        NRF_LOG_DEFAULT_BACKENDS_INIT();
        NRF_LOG_DEBUG("Main start GHS");
    
        // Allocate memory for the security keys
        allocateMemoryForSecurityKeys(&keys);
        sec_params_init();
        timers_init();
    
        memset(&m_sccd_ghs_cp_handle, 0, sizeof(m_sccd_ghs_cp_handle));
        memset(&m_sccd_ghs_response_handle, 0, sizeof(m_sccd_ghs_response_handle));
    
        memset(cccds, 0, noOfCccds);
        loadKeysFromFlash(&keys, &saveDataBuffer, &saveDataLength, cccds, &noOfCccds);
        memcpy(cccdSet, cccds, noOfCccds);  // destination, source, length
    
    
        buttons_leds_init();
    
        gpiote_init_new();
    
    //================================= All stuff fof softdevice init
        // This also provides the callback method to receive events.
        ble_stack_init();
        sd_mutex_new(&p_mutex);
        sd_mutex_new(&q_mutex);
    
        // Set device address
        ble_gap_addr_t addrStruct;
        addrStruct.addr_type = BLE_GAP_ADDR_TYPE_PUBLIC;
        memcpy(addrStruct.addr, getBtAddress(), 6);  // destination, source, length
        err_code = sd_ble_gap_addr_set(&addrStruct);
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_DEBUG("Could not set the Bluetooth Address. Error code %d", err_code);
            APP_ERROR_CHECK(err_code);
        }
        configureSpecializations();
    
        // Sets max and min connection intervals, slave latency, and the GAP characteristic entries
        // for the friendly name and appearance.
        gap_params_init();
        // Creates the advertisement and scan response data arrays
        err_code = advertisement_ghs_set(&m_adv_handle);
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_DEBUG("Could not configure the advertisements");
            APP_ERROR_CHECK(err_code);
        }
        // ===================================== Create the GHS service
        err_code = createPrimaryService(&m_sccd_ghs_service_handle, BTLE_SCCD_GHS_SERVICE);
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_DEBUG("Could not create GHS service");
            APP_ERROR_CHECK(err_code);
        }
        // Create the GHS control point characteristic
        err_code = createStandardCharacteristic(m_sccd_ghs_service_handle,
            &m_sccd_ghs_cp_handle,
            BTLE_SCCD_GHS_CP_CHAR,
            true,   // Has CCCD
            true,   // This will cause indicate instead of notify if CCCD is set to true
            0,
            NULL,
            false,
            (ble_gap_conn_sec_mode_t) {1, 1},   // [CCCD write is open]
            (ble_gap_conn_sec_mode_t) {0, 0},   // Reading characteristic value is forbidden
            (ble_gap_conn_sec_mode_t) {1, 1});  // [Writing characteristic is open]
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_DEBUG("Could not create GHS control point characteristic");
            APP_ERROR_CHECK(err_code);
        }
        // ===================================== Create the GHS response characteristic
        err_code = createStandardCharacteristic(m_sccd_ghs_service_handle,
            &m_sccd_ghs_response_handle,
            BTLE_SCCD_GHS_RESPONSE_CHAR,
            true,   // Has CCCD
            false,  // This will cause indicate instead of notify if CCCD is set to true
            0,
            NULL,
            false,
            (ble_gap_conn_sec_mode_t)
            {
                1, 1
            },
            // CCCD write is open
            (ble_gap_conn_sec_mode_t)
            {
                0, 0
            },
                // Reading characteristic value is forbidden
            (ble_gap_conn_sec_mode_t)
            {
                1, 1
            });  // Writing characteristic is open
        if (err_code != NRF_SUCCESS)
        {
            NRF_LOG_DEBUG("Could not create GHS response characteristic");
            APP_ERROR_CHECK(err_code);
        }
        // Not sure about this yet. I think it allows one to put up a fight to argue 
        // over connection parameters and sets up responses to connection parameter
        // change requests.
        //conn_params_init();
    //=============================== End of SoftDevice init???
        // Start execution.
        err_code = app_timer_start(m_app_dummy_timer_id, CMD_SENSOR_TIME, NULL);
        APP_ERROR_CHECK(err_code);
        elapsedTimeStart = app_timer_cnt_get();
        #if (USES_TIMESTAMP == 1)
            if (sTimeInfo->timeFlagsSupport == 1)
            {
                sGhsTime->epoch = (unsigned long long)elapsedTimeStart;
            }
            else if(sTimeInfo->timeFlagsSupport > 1)
            {
                epoch = GHS_TIME_EPOCH_DEFAULT;
                sGhsTime->epoch = (unsigned long long)elapsedTimeStart + epoch;
            }
        #endif
        NRF_LOG_INFO("GHS Start at time %u!\r", getTicks());
        #if (USE_DK == 0)
            bring_up_adver();
        #endif
        // Since we don't have continuous power we cannot maintain a real time clock between power cycles
        // So each power cycle will be treated as a factory reset where we start a new wall clock time
        // of a facory reset value. We will then create stored measurements with a button push and use our
        // tick counter to add to this factory reset time
        //=============================================================================== Start GHS Pulse OX
        // Enter main loop.
        main_loop();
    }

    If I understand how much that would be it looks like all the code between my doft device init start comment and soft device init end comment. I suppose there is a lot of stuff that happens on a system reset that I don't see so even though 90 % of my main function would be the softdevice init and setup of all the BTLE stuff it would still be more efficient.

  • Hi

    As you say there is quite a lot that happens during a reset, and doing this unnecessarily will increase the runtime and power consumption of your application. For instance various startup code is run, a lot of RAM variables will be initialized, and if you are using some kind of bootloader you will also run through that. 

    Personally I think that the way many of our examples are set up, with hundreds of lines of BLE related code in main.c, is not the best way to set up a project. By abstracting this into its own c file you make the main file much more clean, and if you ever need to change anything BLE related in your project you immediately know where to go. 

    Some of our more complex software deliveries, such as the mouse/keyboard reference examples, are set up this way. Below I added the main function for the desktop mouse as an example:

    /**@brief Application main function.
     */
    int main(void)
    {   
        APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
        APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
        modules_init();
        gzll_keep_alive_init();
        buffer_init();
        misc_io_init();
        
        // Enter main loop
        for (;;)
        {   
            app_sched_execute();
            m_pwr_mgmt_run();
        }
    }
    

    Best regards
    Torbjørn

  • IN my case most of the main is dedicated to initializing the Bluetooth, and its not that long because I have one service with two characteristics, so a reset didn't look like much was happening. I could easily make main a 10 line program or something of that size by calling a setup Bluetooth method.

Related