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

sd_app_evt_wait() behavior

Hi

In my app i need to keep in sync client/server communication, which means no packet is sent in any direction until the sender got the confirmation from receiver for the previous packet.

I’m doing this using a cmd/resp protocol. Client(Android) is driving the communication sending commands and the GATT server (52832/s132) send messages either as response to commands or unsolicited messages if the client enable this feature.

Server messages are indications sent using sd_ble_gatts_hvx() and each message has to be confirmed by a BLE_GATTS_EVT_HVC event. If BLE_GATTS_EVT_HVC is not received within 500 msec the assumption is the BLE packet is lost and server will retry sending the message. If retry fails 5 times the server considers client is dead and disconnects itself.

To monitor the HVCs I have a global variable: wait_for_hvc and an app timer: hvc_timer

hvc_timer handler clears wait_for_hvc and increments hvc_unack variable.

Before calling sd_ble_gatts_hvx() I check wait_for_hvc and if ‘1’ I need to wait until it gets 0 either by BLE_GATTS_EVT_HVC or by hvc_timer handler.

To wait for this event I implemented my sleep function, based on sd_app_evt_wait() and an additional app timer: sleep_timer

Here is the code for the sleep function, timer functions and the part of the application using sleep function

APP_TIMER_DEF(sleep_timer_id);
APP_TIMER_DEF(m_hvc_timer_id);
static uint8_t sleep_timeout;
timer_func()
	{
	...
	err_code = app_timer_create(&sleep_timer_id, APP_TIMER_MODE_SINGLE_SHOT, sleep_timeout_handler);
	APP_ERROR_CHECK(err_code);
	sleep_timer = sleep_timer_id;
	...
	
	err_code = app_timer_create(&m_hvc_timer_id, APP_TIMER_MODE_SINGLE_SHOT, hvc_timeout_handler);
	APP_ERROR_CHECK(err_code);
	hvc_timer = m_hvc_timer_id;
	}

void sleep_timeout_handler(void *p_context)
  {
  sleep_timeout = 1;
  }

void hvc_timeout_handler(void *p_context)
  {
  wait_for_hvc = 0;
  hvc_unack++;
  NRF_LOG_INFO("hvc timeout: %x", hvc_unack);
  if(hvc_unack > HVC_UNACK_MAXCOUNT)
    sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
  }


void sleep(uint32_t msec2sleep)
  {
  uint32_t err_code;
  sleep_timeout = 0;
  err_code = app_timer_start(sleep_timer, APP_TIMER_TICKS(msec2sleep), NULL);
  APP_ERROR_CHECK(err_code);
  while(!sleep_timeout)
    {
    sd_app_evt_wait();
    nrf_sdh_evts_poll();
    } 
  }
  
/* code in the application */
while(wait_for_hvc) // wait until wait_for_hvc = 0
	{
    NRF_LOG_INFO("auth-request: wait_for_HVC still 1");
    sleep(10);
    }
err_code = sd_ble_gatts_hvx(m_service.conn_handle, &hvx_params);
if(err_code == NRF_SUCCESS)
	{
  // open HVC window
    APP_ERROR_CHECK(app_timer_start(hvc_timer, APP_TIMER_TICKS(HVC_TIMEOUT), NULL)); 
    wait_for_hvc = 1;
  //--------------------------------------------
    }

The issue comes with sleep function. The moment this function is called (once 20-30 messages) it loops for ever because sleep_timeout is never set by sleep_timeout_handler which is never invoked. If I force a break in the loop after some 500000 iteration then the breakpoint in the sleep_timeout)_handler is hit.

My expectation here is sd_app_evt_wait to return on app timer expiration which will invoke sleep_timer_handler, which will set sleep_timeout, causing the loop in sleep function to break… but this does not happen Frowning2

More, I measured the duration of a loop cycle, which is test sleep_timeout, call and wait for return sd_app_evt_wait, call and wait for return of nrf_sdh_evts_poll. This is roughly 22usec … Don’t know which event can cause sd_app_evt_wait to return so frequently, but this is another story.

According to the comments in this ticket https://devzone.nordicsemi.com/f/nordic-q-a/71852/wake-up-sd_app_evt_wait-from-application/302009#302009, it should work as expected but it doesn’t.

There is a note in sd_app_evt_wait documentation

@note If an application interrupt has happened since the last time sd_app_evt_wait was
* called this function will return immediately and not go to sleep. This is to avoid race
* conditions that can occur when a flag is updated in the interrupt handler and processed
* in the main loop.

which is pretty confusing to me

  • Sorry i could not make it more clear for you...

    In your original post you say that you are "stuck in the while loop". What while loop is that? Is it the while (!sleep_timeout)?
    • Yes is the while(!sleep_timeout)
    If so, where is the while(!sleep_timeout) loop? Is it in your main() function? Or is it called from an interrupt?
    • It is called from an Interrupt. This is the call stack

    void sleep(unsigned int msec2sleep=0x00000064)
    uint32_t timer_value_update(unsigned char val2update=0x04)
    void notification_timeout_handler(void* p_context = 0x00000000)
    _Boot timer_expire(app_timer_t* p_timer(0x20005568)
    void on_compare_evt(const drv_rtc_t* p_instance=0x2000228c)
    void rtc_irq(const drv_rtc_t* p_instance=0x2000228c)
    void RTC1_IRQHandler()

    sleep function is called in RTC1_IRQHandler context triggered by notification timer (see line 10 in timers initialization function)

    • current_int_priority_get() returns 6 in sleep function and same in notification_timeout_handler (line 31 in timers initialization function)

    Regarding my previous comment

    But in the first case there are already 2 timers running in parallel: notification_timer and hvc_timer., which i can confirm are running fine. Here is the full timers initialization function

    I need to withdraw it. I dont know if second timer is running. wait_for_hvc variable can be set to 0, either by hvc_timeout_handler, or by HVC_EVT received by the the soft device from client.

    Consider just uploading the entire main.c file if that is where this is happening.

    Its more than 1 file to upload, i need to give you my entire project. I can do it if you think is necessary.

    Will try to explain again the flow

    1. Notification is started in repeated mode with 700 msec timeout value
    2. in notification timeout handler app is sending a value to client
      1. first check if it received the HVC for previous value (wait_for_hvc = 0)
      2. if wait_for_hvc == 0, waits until it gets 0 using sleep function
    3. after wait_for_hvc = 0 sends the indication with the new value -> sd_ble_gatts_hvx()...
    4. starts hvc_timer

    Here at 2.b execution gets stuck

    Hopefully is more clear now. If not let me know and will send the project. 

  • typo in 2.b. Replace with

    if wait_for_hvc == 1, waits until it gets 0 using sleep function

  • ves said:

    sleep function is called in RTC1_IRQHandler context triggered by notification timer (see line 10 in timers initialization function)

    • current_int_priority_get() returns 6 in sleep function and same in notification_timeout_handler (line 31 in timers initialization function)

     There is the issue. You are already in an interrupt with the same priority, meaning the app_timer interrupt will not get access to the CPU until you leave your current interrupt. So the app_timer is not able to set the sleep_timeout to 1. 

    You need to re-write the logic of your application so that you do not wait inside the interrupt like this.

    BR,

    Edvin

  • Hi Edvin

    Yes you are right!

    i moved the sleep function in the main thread and its working fine!

    I did that using schedule handling library

    Thx for your support!

Related