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

Attempting to Start Scanning after disconnecting as peripheral causes NRF_INVALID_STATE

We have bots (what I'm calling our device for this question) that play both the peripheral and central role depending on their context (not unlike the relay example; except these bots talk to each other, not other devices).

In our code, we do the following:

  • Scan

  • Advertise Non connectable

  • Bot1 indicates it wants to go connect (GAP PERIPHERAL)

  • Change to connectable advertising

  • Bot 2 connects (GAP CENTRAL)

  • Bot 1 and 2 do some things, and then each bot disconnects (no service discovery at this point).

  • at disconnect, each bot starts scanning and advertising again (non-connectable).

  • Bot 1 (the GAP Peripheral) disconnects, ble_conn_state_status_t is 1, for BLE_CONN_STATUS_DISCONNECTED.

  • When Bot 1 tries to start advertising again, it gets NRF_INVALID_STATE both when I try to stop and start scanning.

Relevant is that we're using flash data storage to store some data right before disconnect; both devices do it, but the central seems to record far fewer flash operations than the peripheral; which seems strange since they're doing the same thing.

Here's the code:

static void on_ble_peripheral_evt(ble_evt_t *p_ble_evt) {

  uint32_t err_code;
  switch (p_ble_evt->header.evt_id) {

  case BLE_GAP_EVT_CONNECTED: {
	m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
   
    const ble_gap_addr_t *peer_addr = &p_ble_evt->evt.gap_evt.params.connected.peer_addr;
    NRF_LOG("PERIPHERAL: CONNECTED\r\n");
    start_advertising(ADV_MODE_NONCONNECTABLE);
    break;
  }

  case BLE_GAP_EVT_DISCONNECTED:
    NRF_LOG("PERIPHERAL: DISCONNECTED\r\n");
    m_gap_role = BLE_GAP_ROLE_INVALID;
    start_advertising(ADV_MODE_NONCONNECTABLE);
    start_delayed_scanning();

    break;

  case BLE_GATTS_EVT_SYS_ATTR_MISSING:
    err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
    APP_ERROR_CHECK(err_code);
    break;
  case BLE_GAP_EVT_TIMEOUT:
    NRF_LOG("BLE_GAP_EVT_TIMEOUT\r\n");
    start_scanning();
    start_advertising(ADV_MODE_NONCONNECTABLE);
  default:
    // No implementation needed.
    break;
  }
}

void on_ble_central_evt(const ble_evt_t *const p_ble_evt) {
  const ble_gap_evt_t *p_gap_evt = &p_ble_evt->evt.gap_evt;
  switch (p_ble_evt->header.evt_id) {
  case BLE_GAP_EVT_CONNECTED: {
    const ble_gap_addr_t *peer_addr = &p_gap_evt->params.connected.peer_addr;
    m_conn_handle = p_gap_evt->conn_handle;
    NRF_LOG("CENTRAL: CONNECTED\r\n");
     if (m_conn_handle != BLE_CONN_HANDLE_INVALID && !friend_adding_mode()) {
        uint32_t err_code =
            ble_db_discovery_start(&m_ble_db_discovery_aas, m_conn_handle);
        NRF_LOG_PRINTF("STARTING DISCOVERY: %u\r\n", err_code);
        APP_ERROR_CHECK(err_code);
      }
      start_scanning();
	  start_advertising(ADV_MODE_NONCONNECTABLE);
    break; // BLE_GAP_EVT_CONNECTED
  }

  case BLE_GAP_EVT_DISCONNECTED: {
    NRF_LOG("CENTRAL: DISCONNECTED\r\n");
    m_gap_role = BLE_GAP_ROLE_INVALID;
    if (p_gap_evt->conn_handle == m_conn_handle) {
      m_conn_handle = BLE_CONN_HANDLE_INVALID;
    }
    if (m_conn_handle == BLE_CONN_HANDLE_INVALID) {
      start_delayed_scanning();
    }
    start_advertising(ADV_MODE_NONCONNECTABLE);
    break;
  } 
  case BLE_GAP_EVT_ADV_REPORT: {
    //snipp for our purposes. Irrelevant.        
    break;
  }

  case BLE_GAP_EVT_TIMEOUT: {
    if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) {
      NRF_LOG("[APPL]: Connection Request timed out.\r\n");
    }
    NRF_LOG("BLE_GAP_EVT_TIMEOUT\r\n");
    start_scanning();
    start_advertising(ADV_MODE_NONCONNECTABLE);
  } break; 

  case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: {
    // Accept parameters requested by peer.
    ret_code_t err_code;
    err_code = sd_ble_gap_conn_param_update(
        p_gap_evt->conn_handle,
        &p_gap_evt->params.conn_param_update_request.conn_params);
    NRF_LOG_PRINTF("BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: %u\r\n", err_code);
    APP_ERROR_CHECK(err_code);
  } break; // BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST

  default:
    // No implementation needed.
    break;
  }
}

static void ble_evt_dispatch(ble_evt_t *p_ble_evt) {

  ble_conn_state_on_ble_evt(p_ble_evt);
  notify_dfu_ble_evt(p_ble_evt);
  m_gap_role = ble_conn_state_role(p_ble_evt->evt.gap_evt.conn_handle);
  if (m_gap_role == BLE_GAP_ROLE_PERIPH) {
    on_ble_peripheral_evt(p_ble_evt);
    ble_conn_params_on_ble_evt(p_ble_evt);
    //custom service 
  } 
  else if ((m_gap_role == BLE_GAP_ROLE_CENTRAL) || (p_ble_evt->header.evt_id == BLE_GAP_EVT_ADV_REPORT)) {
    if(p_ble_evt->header.evt_id != BLE_GAP_EVT_ADV_REPORT) {
      NRF_LOG_PRINTF("GATT CLIENT, GAP CENTRAL EVENT: %04x\r\n", p_ble_evt->header.evt_id);
    }
    //snip
    if (p_ble_evt->header.evt_id != BLE_GAP_EVT_DISCONNECTED) {
      on_ble_central_evt(p_ble_evt);
    }
    if (p_ble_evt->evt.gap_evt.conn_handle == m_conn_handle) {
      //leaving discovery out for this question 
     }
    if (p_ble_evt->header.evt_id == BLE_GAP_EVT_DISCONNECTED) {
      on_ble_central_evt(p_ble_evt);
    }
  }
}

static void sys_evt_dispatch(uint32_t sys_evt) {
  fs_sys_event_handler(sys_evt);
  advertising_on_sys_evt(sys_evt);
  scan_on_sys_evt(sys_evt);
  power_management_on_sys_evt(sys_evt);
}

###scan.c

#include <stdbool.h>
#include <stdint.h>
#include <string.h>


#include "app_error.h"
#include "app_timer.h"
#include "ble.h"
#include "nordic_common.h"
#include "nrf.h"
#include "nrf_delay.h"
#include "nrf_soc.h"
#include "sdk_config.h"
#include "sdk_errors.h"
#include "ble_central_event_handler.h"
#include "fstorage.h"


#include "scan.h"
#include "utils.h"
//#include "SEGGER_RTT.h"

#define SCAN_INTERVAL 0x0168
#define SCAN_WINDOW 0x00E0

APP_TIMER_DEF(scan_timer_id);
#define SCAN_RESTART_TIMEOUT APP_TIMER_TICKS(20000, APP_TIMER_PRESCALER)


static ble_opt_t m_ble_opt;
static bool m_scan_start_pending = false;
static bool m_scanning = false;
static const ble_gap_scan_params_t m_scan_param = {
    1,    // Active scanning set.
    0,    // Selective scanning not set.
    NULL, // No whitelist provided.
    SCAN_INTERVAL, SCAN_WINDOW,
    0x0000 // No timeout.
};
const ble_gap_scan_params_t *get_scan_params(void) { return &m_scan_param; }

static void scan_timer_handler(void * p_context) {
  UNUSED_PARAMETER(p_context);
  uint32_t err_code = app_timer_stop(scan_timer_id);
  APP_ERROR_CHECK(err_code);
  //disconnect();
  //stop_scanning();
  start_scanning();
}

void scan_init() {
  app_timer_create(&scan_timer_id, APP_TIMER_MODE_SINGLE_SHOT, scan_timer_handler);
}

void scan_on_sys_evt(uint32_t sys_evt) {
  switch (sys_evt) {
  case NRF_EVT_FLASH_OPERATION_SUCCESS:
  case NRF_EVT_FLASH_OPERATION_ERROR:
    if (m_scan_start_pending) {
      start_scanning();
      //NRF_LOG_PRINTF("FLASH OP SCAN PENDING: %u\r\n", m_scan_start_pending);
    }

    break;

  default:
    if (m_scan_start_pending) {
      start_scanning();
    }
    break;
  }
}
void set_scan_report(void) {
  m_ble_opt.gap_opt.scan_req_report.enable = 1;
  uint32_t err_code = sd_ble_opt_set(BLE_GAP_OPT_SCAN_REQ_REPORT, &m_ble_opt);
  APP_ERROR_CHECK(err_code);
}

void stop_scanning(void) {
  uint32_t err_code = sd_ble_gap_scan_stop();
  if (err_code == NRF_SUCCESS || err_code == NRF_ERROR_INVALID_STATE) {
    m_scanning = false;
  }
  NRF_LOG_PRINTF("SCAN STOP, %u\r\n", err_code);
}

void start_delayed_scanning() {
 NRF_LOG("DELAYED SCANNING\r\n");
 app_timer_start(scan_timer_id, SCAN_RESTART_TIMEOUT, NULL);
}

void start_scanning(void) {
  uint32_t count = 0;
  uint32_t err_code = fs_queued_op_count_get(&count);
  if (count != 0) {
    NRF_LOG_PRINTF("SCAN_START: FP err: %u, count: %u \r\n", err_code, count);
    m_scan_start_pending = true;
    return;
  }
  APP_ERROR_CHECK(err_code);
  stop_scanning();
  err_code = sd_ble_gap_scan_start(&m_scan_param);
  if (err_code == NRF_SUCCESS) { 
    m_scanning = true;
    //NRF_LOG("SCAN START\r\n");
    m_scan_start_pending = false;
  }
  if (err_code != NRF_SUCCESS) {
    //NRF_LOG_PRINTF("ERROR ON START SCANNING: %u\r\n", err_code);
    APP_ERROR_CHECK(err_code);
    m_scan_start_pending = true;
    app_timer_start(scan_timer_id, SCAN_RESTART_TIMEOUT, NULL);
  }

}
bool is_scanning() {
  return m_scanning;
}

###advertising.c

can be found in this answer.

Screenshots of logging on failure (only happens to peripheral):

image description

Why is this only happening to the peripheral bot, and why does it only happen when the Central disconnects from the peripheral before the peripheral is able to disconnect?

Edit: It fails due to the fact that there is a NRF_EVT_FLASH_OPERATION_ERROR being emanated by the sys_evt_dispatch. The advertising is able to keep trying to get around that; but the scanner is not.

Related