Revert back to Coded PHY connection when 2M is not possible because of distance.

Hi,

I have 2 nrf52840 DK boards advertising on coded PHY mode. Once connected the connection is updated (using sd_ble_gap_phy_update()) to use 2M mode. No problem in doing so.

However, I have a situation or use case where after the successful update to 2M mode, but because of the distance between the 2 boards is too far for 2M mode to continue, I would like the connection to revert back down to coded PHY. Is that even possible in the first place? If it is possible, what are steps required to revert back to coded PHY.

In my testing,

I tried walking around to find the distance that is too far for 2M communication but still possible with Coded PHY. 

What I find is that they always connect on coded and then updated to 2M. When the distance is too far apart, there is no connection at all even on coded PHY. 

I can't find a distance/case where they connect on coded PHY but fail to update to 2M because of distance.

Some may say why not just connect on coded PHY and forget about 2M. My case is we want to use 2M whenever possilbe and auto revert down to coded PHY when physical distance does not allow 2M mode.

Your help is very much appreciated.

  • Also is there a disconnected reason status code (HCI_STATUS_CODE) associated with distance being out of range?

  • Hi

    What SDK version are you using here? I assume one of the nRF52840 DKs are acting as a central, and the other is advertising and connecting to it, correct? It sounds strange that you don't see a range difference between when connected in Coded PHY and the 2MBPS PHY I must say. Can you show me how you set up Coded PHY advertising in your application?

    There is no "automatic" way to switch the PHY when the connection starts having issues, so you'll have to find a way to do a MTU/PHY update at some point. Here are a few suggestions:

    • You can make the central device check the RSSI (Received Signal Strength Indicator) of the peripheral and when it's under a certain value you can trig a PHY update to switch to the Coded PHY.
    • If your central device starts losing packets from the peripheral and gets corrupt data, you can make that trig a PHY update to switch to the Coded PHY
    • Upon a disconnection caused by HCI_CONNECTION_TIMEOUT for example (indicating that the connection timed out due to poor reception or no activity) you can trig a reconnect that sets the connection to Coded PHY.

    I don't think there are any error codes caused by a device being out of range, as it would give the same behavior as a device with poor reception or a lot of interference.

    Best regards,

    Simon

  • Hi Simon,

    Thanks for coming back!

    The SDK version is nRF5_SDK_15.2.0

    Yes, one DK is acting as a central and the other is advertising and connecting to it.

    I am surprised too that I don't get the extra range Coded PHY provides. As mentioned, whenever I got connected on Coded PHY mode, the central will ask for PHY mode change to 2M via sd_ble_gap_phy_update().

    Correct me if I am wrong, but I think the "handshake" between central and peripheral is still taking place on Coded PHY when sd_ble_gap_phy_update() is taking place and when they are both updated to 2M, because of the distance is too far for 2M to support, the devices would disconnect after a short interval, say between 1 to 5 seconds? Shoudl this be the expected observation?

    I've inherited the code base from a contractor and is still understanding it. Advertising code is shown below.

    CBleAdvertiser::CBleAdvertiser()
    {
    	memset(&m_AdvData, 0, sizeof(m_AdvData));
        memset(&m_AdvParams, 0, sizeof(m_AdvParams));
    
    	m_AdvParams.p_peer_addr     = NULL;    // Undirected advertisement.
        m_AdvParams.filter_policy   = BLE_GAP_ADV_FP_ANY;
        m_AdvParams.duration        = 0;       // Never time out.
    	m_AdvParams.primary_phy 	= BLE_GAP_PHY_1MBPS;
    	m_AdvParams.secondary_phy 	= BLE_GAP_PHY_1MBPS;
    
        m_AdvData.name_type = BLE_ADVDATA_FULL_NAME;
        m_AdvData.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    }
    
    void CBleAdvertiser::SetPhyMode(AdvMode_t _mode)
    {
    	m_AdvMode = _mode;
    	switch (m_AdvMode) {
    		case ADV_1MBPS:
    			m_AdvParams.primary_phy 	= BLE_GAP_PHY_1MBPS;
    			m_AdvParams.secondary_phy 	= BLE_GAP_PHY_1MBPS;
    			break;
    		case ADV_2MBPS:
    			m_AdvParams.primary_phy 	= BLE_GAP_PHY_2MBPS;
    			m_AdvParams.secondary_phy 	= BLE_GAP_PHY_2MBPS;
    			break;
    		case ADV_CODED:
    			m_AdvParams.primary_phy 	= BLE_GAP_PHY_CODED;
    			m_AdvParams.secondary_phy 	= BLE_GAP_PHY_CODED;
    			break;
    		default:
    			m_AdvParams.primary_phy 	= BLE_GAP_PHY_1MBPS;
    			m_AdvParams.secondary_phy 	= BLE_GAP_PHY_1MBPS;
    			break;
    	}
    
    	if (m_AdvParams.properties.type == BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED
    		|| m_AdvParams.properties.type == BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED)
    	{  // Connectable
    		EnableConnectableAdvertisement(true);
    	}
    	else
    	{  // NonConnectable
    		EnableConnectableAdvertisement(false);
    	}
    }
    
    void CBleAdvertiser::EnableConnectableAdvertisement(bool _enable)
    {
    	if (m_AdvMode == ADV_CODED)
    	{
    		m_AdvParams.properties.type = _enable ? BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED : BLE_GAP_ADV_TYPE_EXTENDED_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
    	} 
    	else 
    	{
    		m_AdvParams.properties.type = _enable ? BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED : BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
    	}
    }
    
    void CBleAdvertiser::SetAdvertisementInterval(uint32_t _interval)
    {
    	m_AdvParams.interval = MSEC_TO_UNITS(_interval, UNIT_0_625_MS);
    }
    
    bool CBleAdvertiser::SetDeviceName(const char * _device_name)
    {
    	ble_gap_conn_sec_mode_t sec_mode;
    	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
        	
    	uint32_t err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *)_device_name, strlen(_device_name));
    	APP_ERROR_CHECK(err_code);
        
    	return err_code == NRF_SUCCESS;
    }
    
    void CBleAdvertiser::SetCompanyId(uint16_t _company_id)
    {
    	m_ManufData.company_identifier = _company_id;	
    }
    
    
    CBleAdvertiser::Me().SetPhyMode(CBleAdvertiser::ADV_CODED);
    CBleAdvertiser::Me().EnableConnectableAdvertisement(true);
    CBleAdvertiser::Me().SetAdvertisementInterval(300);
    CBleAdvertiser::Me().SetDeviceName(BLE_MESH_NODE_NAME);
    CBleAdvertiser::Me().SetCompanyId(0xFFFE);
    
    
    
    bool CBleAdvertiser::Configure()
    {
    	static uint8_t enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX];  // Buffer for storing an encoded advertising set.
    	static ble_gap_adv_data_t adv_data =
    	{
    	    .adv_data =
    	    {
    	        .p_data = enc_advdata,
    	        .len    = BLE_GAP_ADV_SET_DATA_SIZE_MAX
    	    },
    	    .scan_rsp_data =
    	    {
    	        .p_data = NULL,
    	        .len    = 0
    	    }
    	};
    
        if(m_AdvParams.interval == 0)
    	{
        	m_AdvParams.interval = MSEC_TO_UNITS(100, UNIT_0_625_MS);
    	}
    
    	uint32_t err_code = ble_advdata_encode((const ble_advdata_t *)&m_AdvData, adv_data.adv_data.p_data, &adv_data.adv_data.len);
        APP_ERROR_CHECK(err_code);
    
        err_code = sd_ble_gap_adv_set_configure(&m_Handle, &adv_data, &m_AdvParams);
    
        if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE) && (err_code != BLE_ERROR_INVALID_ADV_HANDLE))
        {
            APP_ERROR_CHECK(err_code);
        }
    
        return true;
    }
    
    bool CBleAdvertiser::StartAdvertisement()
    {
        ret_code_t err_code = sd_ble_gap_adv_start(m_Handle, APP_BLE_CONN_CFG_TAG);
        if(err_code != NRF_SUCCESS)
        {
        	LOG("Error start adv %u", err_code);
        }
    	return ( err_code == NRF_SUCCESS );
    }
    
    
    
    void UpdateAdvPacket()
    {
    	CBleAdvertiser::Me().StopAdvertisement();
    	CBleAdvertiser::Me().SetManufacturerData((const void *)&m_AdvManufactureData, sizeof(m_AdvManufactureData));
    	CBleAdvertiser::Me().Configure();
    	CBleAdvertiser::Me().SetTxPower(CBle::Txp8dBm);
    	bool ret = CBleAdvertiser::Me().StartAdvertisement();
    	ASSERT(ret);
    }
    

    Thank you for the suggestions on triggering the PHY update. I wouldn't have thought about them if you did not mention them!

  • Hi again

    What is the distance you're seeing between the two devices exactly? Are you able to see the MTU/PHY update in logging or something to make sure you actually move to the 2MBPS PHY? Your advertising parameters seem to be correct, so I would think it advertises on the Coded PHY at least.

    Best regards,

    Simon

  • Hi Simon,

    The distance is approximately 100 metres. Note that this is conducted on a noisy street with buildings on either sides of the road. One of the board is laying flat on the walk steps hence the reception might not be that great.

    Yes, I saw from the logging that PHY is updated to 2MBPS.

    Thanks for the confirmation on the code.

    Correct me if I am wrong, but I think the "handshake" between central and peripheral is still taking place on Coded PHY when sd_ble_gap_phy_update() is taking place and when they are both updated to 2M, because of the distance is too far for 2M to support, the devices would disconnect after a short interval, say between 1 to 5 seconds? Shoudl this be the expected observation?

    Can you help to confirm if this is the case?

    You can make the central device check the RSSI (Received Signal Strength Indicator) of the peripheral and when it's under a certain value you can trig a PHY update to switch to the Coded PHY.

    I think for my use case, it would be more of always connect in Coded PHY and if RSSI value is strong (which means devices are close to each other) trigger a PHY update to switch to 2M. Will that work?

Related