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

Friendship Terminated reason 2, How to adjust the friendship setting to have a solid connection

Info:

Windows 10

Segger Embedded Studio V5.20a

SDK16.0.0 & Mesh SDK 4.1.0

nRF52840 & nRF52832

I'm setting up a friend node and a low power node. The friend node is just Nordic's Light Switch Server with the friendship capabilities turned on (which didn't work until the persistent storage was turned off, this might be related to having no bootloader, but it wasn't important to the test I needed to get going). I took my current project, added in the lpn files and added in the appropriate functions from the low power example so I have a low power module in my project. My project need to move in and out of low power mode depending upon it's needs. SO far the Mesh SDK supports this. The definition, MESH_FEATURE_LPN_ACT_AS_REGULAR_NODE_OUT_OF_FRIENDSHIP, allows it to trigger a search for a friend, find it and while it has a friend, it assumes a very lower power state, it drops from about 6mA down to 265uA. When it does not have a friend it will resume normal mesh operation and power consumption goes up.

The problem I am having is after a minute or two the Low Power Node terminates the friendship with reason 2. Here is the termination reason enum:

typedef enum
{
    /** The Low Power node actively terminated the friendship. */
    NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_LPN,
    /** There was no response from the LPN within the Poll Timeout. */
    NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_TIMEOUT,
    /** The Friend node did not reply to the (repeated) Friend Poll. */
    NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_NO_REPLY,
    /** The Low Power node was not able to send transport command due to internal fault. */
    NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_INTERNAL_TX_FAILED,
    /** A new Friend Request was received from an LPN. */
    NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_NEW_FRIEND_REQUEST,
    /** The friendship was terminated through the API. */
    NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_USER,
    /** The LPN established a friendship with a different Friend. */
    NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_NEW_FRIEND
} nrf_mesh_evt_friendship_terminated_reason_t;

A few seconds later the friend node gives a reason 1 for the reason it terminates, and that makes sense, since the low power node is no longer being a friend because it errored out with a reason 2 - no replay from the friend.

The first issue is I should be able to maintain a friendship for longer than a few minutes. I've tried changing some of the settings to establish the friendship, but they don't seem to cause the changes I want and some of the names seem a little unclear.

This struct setups the friendship (and all friendship settings are determined by the low power node as far as I understand it according to the spec):

static mesh_lpn_friend_request_t m_freq = {
	.friend_criteria.friend_queue_size_min_log = MESH_FRIENDSHIP_MIN_FRIEND_QUEUE_SIZE_16,
	.friend_criteria.receive_window_factor = MESH_FRIENDSHIP_RECEIVE_WINDOW_FACTOR_1_0,
	.friend_criteria.rssi_factor = MESH_FRIENDSHIP_RSSI_FACTOR_1_0,
	.poll_timeout_ms = POLL_TIMEOUT_MS,
	.receive_delay_ms = RECEIVE_DELAY_MS };
	
	~~~~~~
	
/* The maximum duration to scan for incoming Friend Offers. */
#define FRIEND_REQUEST_TIMEOUT_MS (MESH_LPN_FRIEND_REQUEST_TIMEOUT_MAX_MS)
/* The upper limit for two subsequent Friend Polls. */
#define POLL_TIMEOUT_MS (SEC_TO_MS(9))
/* The time between LPN sending a request and listening for a response. */
#define RECEIVE_DELAY_MS (100)

These should be mostly default settings from the LPN example. I've changed the POLL_TIMEOUT_MS a little too, but it seems to control the polling interval rather than the maximum polling interval. It seems like there should be a place where the actually polling interval should be selected and be less than POLL_TIMEOUT_MS, if that is the maximum. It appears to me that the error 2 is being caused by the actually polling time exceeding the maximum poling time, but I don't see a way to explicitly set that.

Now suppose that the friendship is much more stable, it still should be able to reestablish a friendship upon the termination of an old one. I wanted it to be automatic so I added code to just request a new friend ship when the friendship terminated event comes through. This does not work. The new friendship request function fails because the states haven't been reset. So I call a friendship termination, but that also fails because the state of the friendship hasn't fully been terminated. I would think that by the time the friendship terminated event makes it to my callback that it would be terminated. This could be an event order problem with my callback being called before some internal event callback actually changes the saved state of the friendship.

Here's my module code:

static void mesh_evt_handler(const nrf_mesh_evt_t * p_evt)
{
	/* USER_NOTE: User can insert mesh core event proceesing here */
	switch (p_evt->type)
	{
		case NRF_MESH_EVT_LPN_FRIEND_OFFER:
		{
			const nrf_mesh_evt_lpn_friend_offer_t *p_offer = &p_evt->params.friend_offer;

			NRF_LOG_DEBUG("Received friend offer from 0x%04X",
				  p_offer->src);

			uint32_t status = mesh_lpn_friend_accept(p_offer);
			switch (status)
			{
				case NRF_SUCCESS:
					break;

				case NRF_ERROR_INVALID_STATE:
				case NRF_ERROR_INVALID_PARAM:
				case NRF_ERROR_NULL:
					NRF_LOG_DEBUG("Cannot accept friendship: %d", status);
					break;

				default:
					APP_ERROR_CHECK(status);
					break;
			}

			break;
		}

		case NRF_MESH_EVT_LPN_FRIEND_POLL_COMPLETE:
			NRF_LOG_DEBUG("Friend poll procedure complete");
			break;

		case NRF_MESH_EVT_LPN_FRIEND_REQUEST_TIMEOUT:
			NRF_LOG_DEBUG("Friend Request timed out");
			break;

		case NRF_MESH_EVT_FRIENDSHIP_ESTABLISHED:
		{
			const nrf_mesh_evt_friendship_established_t *p_est =
					&p_evt->params.friendship_established;
			(void) p_est;

			NRF_LOG_DEBUG("Friendship established with: 0x%04X", p_est->friend_src);
			break;
		}

		case NRF_MESH_EVT_FRIENDSHIP_TERMINATED:
		{
			const nrf_mesh_evt_friendship_terminated_t *p_term = &p_evt->params.friendship_terminated;
			UNUSED_VARIABLE(p_term);

			NRF_LOG_DEBUG("Friendship with 0x%04X terminated. Reason: %d",
				p_term->friend_src, p_term->reason);
			if(p_term->reason == NRF_MESH_EVT_FRIENDSHIP_TERMINATED_REASON_NO_REPLY)
			{
				terminate_friendship(); // Just for demostrating that it's not fully terminated.
				initiate_friendship();
				terminate_friendship();
				initiate_friendship();
			}
			break;
		}

		default:
			break;
	}
}

void initiate_friendship(void)
{
	NRF_LOG_DEBUG("Initiating the friendship establishment procedure.");

	uint32_t status = mesh_lpn_friend_request(m_freq, FRIEND_REQUEST_TIMEOUT_MS);
	switch (status)
	{
		case NRF_SUCCESS:
			break;

		case NRF_ERROR_INVALID_STATE:
			NRF_LOG_DEBUG("Already in an active friendship");
			break;

		case NRF_ERROR_INVALID_PARAM:
			NRF_LOG_DEBUG("Friend request parameters outside of valid ranges.");
			break;

		default:
			APP_ERROR_CHECK(status);
			break;
	}
}

void terminate_friendship(void)
{
	uint32_t status = mesh_lpn_friendship_terminate();
	switch (status)
	{
		case NRF_SUCCESS:
			NRF_LOG_DEBUG("Terminating the active friendship");
			break;

		case NRF_ERROR_INVALID_STATE:
			NRF_LOG_DEBUG("Not in an active friendship");
			break;

		default:
			APP_ERROR_CHECK(status);
			break;
	}
}

So, what method can I use to fine tune the friendship and besides managing the friend restart externally what options do I have to restart it in the event callback?

  • Hi.

    I'm sorry about the delay on this ticket.

    Have you tried changing the POLL_TIMEOUT_MS to a lower value, and check if that is able to extend the time of the friendship?

    BR,
    Joakim

  • Yes, I have tried that. POLL_TIMEOUT_MS seems to set the polling interval. If I set it to 15, it polls every 15 seconds. If I set it to 6, it polls every 6 seconds. The notes in the source code makes it sound like a maximum interval, rather than the actual interval, but it seems to set the actual interval. A larger or smaller number still fails and ends the friendship in a minute or so. I haven't figured out a work around for restarting a search for a friend unless I make some external timer to try again, which may be the only solution to restart it. I'm just surprised with how quickly it fails and ends the friendship.

    I've been on vacation too, so the delay fairly well coincided with my vacation.

  • Are there any other suggestions or a primer on how the different settings can affect the connection? I've tried some different combinations and I feel like I'm missing something.

  • I found a way to make the connection much more stable, but it seems like it might not still be the preferred solution. Hopefully this may help someone else who is trying to use the low power node.

    MESH_LPN_POLL_RETRY_COUNT was set to 5, I boosted it up until I could get more than an hour of friendship polls that do not cause a friendship termination. 15 tries to get an update from a friend seems like a lot, but since my device can actually stay in a low power mode that power savings from not having to find a friend every 2 minutes is very good. I have my own code that will seek a new friend after a short interval when the friendship was terminated, but at a frequency of about every 2 minutes or less, I was increasing my power consumption quite a bit when this is supposed to be helping to conserve power.

    I increased MESH_LPN_POLL_RETRY_COUNT to 15 in the end and that helped it last more than an hour, but any other ideas or methods for making a friendship more stable are welcome.

Related