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
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