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

Runtime restart advertising with new MAC and services

Hi

I have a requirement to be able to re-start advertising with new parameters when told to do so over a Slave SPI interface. From other posts, it appears that there is no way to 'de-init' the soft-device - eg to remove and re-add services. Rather, the SD must be disabled and then re-initialised.

SDK 15.2, Soft device 132 V6.1

My new parameters consist of a new MAC address and a new service UUID (this is generated randomly, and used to uniquely identify an advertising device. The mobile learns the device MAC and GUID via NFC. Also in my advertising data is a mask detailing which of my three apps may connect at any time. The device is a medical product with both patient and clinician apps. I need to prevent the use of the clinician app in certain runtime modes.

My StartAdvertising(), and ble_stack_init() is as follows. The crash happens in  err_code = nrf_sdh_enable_request(); which returns NRF_ERROR_INVALID_STATE 0x08

Hoping someone has an idea of how to resolve the crash, my backup plan is to have the Nordic MCU reset (via SPI command from its master) and then await new config from the master, but that's a right pain. Restarting the SD without rebooting the MCU should work somehow.

NB - the ability to stop advertising, remove services, change manuf specific data and then restart advertising WITHOUT killing off and re-initialising the soft device would be a nice addition for a future SDK.

void StartAdvertising(void)
{
		ret_code_t err_code = sd_softdevice_disable();
		APP_ERROR_CHECK(err_code);
		
		nrf_delay_ms(500);  //tried a delay here - did not work
		
		ble_stack_init();  //  <-- crash is within this method, see below
		gap_params_init();
		gatt_init();  				
		services_init();
		advertising_init();
	    conn_params_init();
        peer_manager_init();  
		ConfigureTagForPatientAppNfcPairing();
		
		start_advertising(true);
}

void ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();     //<-- The Crash Happens here
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    // Register a handler for BLE events.
    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}



void advertising_start(bool erase_bonds)
{
    if (erase_bonds == true)
    {
        delete_bonds();
        // Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED event
    }
    else
    {
		whitelist_init();
        APP_ERROR_CHECK(ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST));
    }
}


		

  • NB: When the above is called the 1st time it works just fine. I then call sd_ble_gap_adv_stop(m_advertising.adv_handle); via another SPI command, then I call the above again. When called the second time (eg when SD is active) it crashes with the  NRF_ERROR_INVALID_STATE

  • I appear to have managed to get past that but by calling the following to shutdown the SD:

    void ble_stack_teardown(void)
    {
         ret_code_t err_code;

         if (!nrf_sdh_is_enabled())
              return;

         err_code = nrf_sdh_disable_request();
         APP_ERROR_CHECK(err_code);

        //Wait until soft device reports 'disabled' state. 
        while (nrf_sdh_is_enabled()) {}
    }

    Unfortunately, its now dying elsewhere when I attempt to restart advertising ble_advertising.c line 667


    if (p_advertising->evt_handler != NULL)
    {
         p_advertising->evt_handler(p_advertising->adv_evt);
    }

    I assume that this is because the advertising module has been re-initialised - I don't find a teardown function for this module, but it looks to me that this is what is required.

    Nigel

  • Hi
    You are correct in calling a  nrf_sdh_disable_request() before you disable the softdevice. The correct way of de initializing the softdevice is shown in this chart.
     
    veletron said:
    Unfortunately, its now dying elsewhere when I attempt to restart advertising ble_advertising.c line 667

     How are you restarting the advertising? Are you calling StartAdvertising()? What is the error code that is thrown? 

    Jared 

  • Hi

    I got this working in the end, mostly by tracking the current state of the comms: idle, advertising, advertising-paired, connected etc, and doing whats necessary:

    void StartAdvertising(uint8_t bAllowedApps)
    {
    		MurataStatus_e eMurataStatus = GS_GetMurataStatus();
    		if (eMurataStatus != eMurataStatus_Idle && eMurataStatus != eMurataStatus_BleDisabled)
    		{
    				NRF_LOG_INFO("Can't Advertise While MurataStatus: %s ",GS_GetTexturalMurataStatus(eMurataStatus));
    				return;
    		}
    	
    		GC_SetAllowedApps(bAllowedApps);
    		GC_SetRandomMacAddr();
    	    GC_SetRandomTestConUuid();
    			
    		//To reset the SD you need to disable it, and then re-enable it...
    		if (GS_GetMurataStatus() != eMurataStatus_BleDisabled)
    				ble_stack_teardown();
    		
    		ble_stack_init();
    		gap_params_init();
    		services_init();
    		advertising_init();
    	    conn_params_init();
            ConfigureTagForPatientAppNfcPairing(bAllowedApps);
    		AdvertiseOnDisconnect(true);
    		advertising_start(true);
    	
    }
    
    
    void ProcessTheIncommingMessage(p_str_ProteusMsgWithPayload_t psIncommingProteusMessage)
    {
    		if (!MessageSrcIsValid(&psIncommingProteusMessage->sProteusMsg) || !MessageDstIsValid(&psIncommingProteusMessage->sProteusMsg))
    		{
    				//todo NAK the message
    				return;
    		}
    	
    	
    		if(!MessageIsForMe(&psIncommingProteusMessage->sProteusMsg))
    		{
    				AddMessageToQueue(psIncommingProteusMessage);
    				return;
    		}
    				
    		//If we get here then this Message is for the Murata, so process it...
    			
    			
    		//We will need the current Murata status in the calls below
    		MurataStatus_e eCurrentMurataStatus = GS_GetMurataStatus();
    		NRF_LOG_INFO("Recieved Command: %s",GetTexuralBleMcuCommand((BleMcuCommand_e)psIncommingProteusMessage->sProteusMsg.bCommandId));
    		
    		switch ((BleMcuCommand_e)psIncommingProteusMessage->sProteusMsg.bCommandId)
    		{
    				case eBleMcuCommand_GlobalSetup:      				//Passes the struct: BleMcu_GlobalSetup_t
    					
    						if (!TestMurataStatus(eCurrentMurataStatus,eMurataStatus_Idle,true))
    								break;
    				
    						//So a size test on the expected length of the payload data - just ignore if not valid
    						if (psIncommingProteusMessage->sProteusMsg.wPayloadSize == sizeof(str_BleMcu_GlobalSetup_t))
    								DoGlobalSetup((p_str_BleMcu_GlobalSetup_t)psIncommingProteusMessage->psPayloadBuffer->PayloadBuffer);
    							
    						break;
    					
    				case eBleMcuCommand_Disconnect:
    											
    						if (!TestMurataStatus(eCurrentMurataStatus,eMurataStatus_Connected,true))
    								break;
    						
    						AdvertiseOnDisconnect(false);
    						ble_disconnect();
    						break;
    
    				case eBleMcuCommand_StopAdvertising:
    
    						if (!TestMurataStatus(eCurrentMurataStatus,eMurataStatus_Advertising,true))
    								break;
    																					
    						advertising_stop();
    						break;
    						
    						
    				case eBleMcuCommand_StartAdvertising:
    					
    						if (!TestMurataStatus(eCurrentMurataStatus,eMurataStatus_Idle,true))
    								break;
    						
    						if (!GC_AreMacAddrAndTestConIdValid())
    						{
    								NRF_LOG_INFO("ERROR: MAC and/or TestConId not yet set");
    								break;
    						}
    						
    						AdvertiseOnDisconnect(true);
    						advertising_start(false);
    						break;
    										
    				case eBleMcuCommand_BleOnAllApps:            //Sets the Murata System Settings struct. Allows the connection of any Mobile App (starts advertising)
    					
    					StartAdvertising(eAllowedApps_PatientApp | eAllowedApps_TechnicianApp | eAllowedApps_ProductionApp);
    					break;
    				
    				case eBleMcuCommand_BleOnPatientTechApp:     //Sets the Murata System Settings struct. Allows the connection Patient and Tech Apps (starts advertising)
    					
    					StartAdvertising(eAllowedApps_PatientApp | eAllowedApps_TechnicianApp);
    					break;
    	
    				case eBleMcuCommand_BleOnPatientApp:         //Sets the Murata System Settings struct. Allows the connection Patient App Only (starts advertising)
    					
    					StartAdvertising(eAllowedApps_PatientApp);
    					break;
    	
    				case eBleMcuCommand_BleOnTechApp:         //Sets the Murata System Settings struct. Allows the connection Tech App Only (starts advertising)
    					
    					StartAdvertising(eAllowedApps_TechnicianApp);
    					break;
    	
    				case eBleMcuCommand_BleOff:         //Tells any connected App that BLE is turning off, Disconnects App and turns off BLE. If no connected App, stops advertising, turns BLE off
    					
    					if (TestMurataStatus(eCurrentMurataStatus,eMurataStatus_Connected,false))
    					{
    							//todo - send message to app to tell it we are disconnecting from it
    							AdvertiseOnDisconnect(false);
    							ble_disconnect();
    					}
    					
    					if (TestMurataStatus(eCurrentMurataStatus,eMurataStatus_Advertising,false))
    							advertising_stop();
    										
    					if (WaitForStatus(eMurataStatus_Idle,100))
    							eCurrentMurataStatus = GS_GetMurataStatus();
    					else
    							break;
    					
    					DeleteNfcTagData();
    					ble_stack_teardown();   //Tear down the BLE stack - one of the start advertising commands will restart it
    					//delete_bonds();       //<-- Dont do this here as deleting the bonds restarts advertising via a callback - instead, bond will be deleted when we re-start BLE and call advertising_start(true)
    					GC_InvalidateMacAddrAndTestConId();
    										
    					break;
    					
    				case eBleMcuCommand_GetCurrentState:         //Murata will respond by sending the current state to the Src endpoint identified by the protocol (using a separate command)
    					
    					NRF_LOG_INFO("Murata State: %s",GS_GetTexturalMurataStatus(eCurrentMurataStatus));
    					//todo - implement response
    					break;
    	
    				case eBleMcuCommand_GetFwVersion:            //Requests that the Murata sends its FW Version
    				
    					//Todo - implement
    					break;
    	
    				case eBleMcuCommand_CacheFile:               //Requests that the Murata Caches the data that will be sent in the payload associated with this command
    					
    					//todo implement
    					break;
    	
    				case eBleMcuCommand_AccelerometerPacket:     //For Production App only - format is in 
    					
    					//Todo - implement
    					break;
    					
    				default:
    				
    					//Not expecting this message - ignore
    					break;
    		}
    		
    		//If there was a payload associated with the data we just processed then we need to 'free' it
    		if (psIncommingProteusMessage->sProteusMsg.wPayloadSize > 0)
    				FreePayloadBuffer(psIncommingProteusMessage->psPayloadBuffer);
    				
    }
    
    void ble_stack_teardown(void)
    {
    		ret_code_t err_code;
    	
    		if (!nrf_sdh_is_enabled())
    				return;
    
        err_code = nrf_sdh_disable_request();
        APP_ERROR_CHECK(err_code);
    	
    		//Wait until soft device reports 'disabled' state.
    		while (nrf_sdh_is_enabled()) 
    		{
    		    //todo - wrap with timer
    		}
    		
    			
    		GS_SetMurataStatus(eMurataStatus_BleDisabled);
    	
    }
    

Related