Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Time slots denied for 6-8 ms after each connection event on central device

I am using the S140 softdevice on NRF52840.

I am using NRF 5 SDK 15.2

As part of evaluating NRF52840 for a product I want to see how well i can perform proprietary accurately timed regular data transmissions (would be audio streaming in the product we want to develop) while my NRF 52840 is connected to 2 peripheral devices via the softdevice.

To do this I am using the Time slot API.

For my evaluation I am requesting a 600 us time slot every 10 ms. When the time slot is denied, i request another time slot 20.1 ms later (The added .1 is to try and move my 10 ms time slots so they might not collide with softdevice traffic in the future).

I see a large amount of denied time slots, and my .1 ms time modifications does not help me find a timing sweet spot to avoid that the soft device denies many of my time slot requests.

I have observed the BLE transmissions and the time slot patterns by measuring the NFR52840 power with an oscilloscope.

I am seeing that after each connection event Tx to the pripherals, the softdevice will deny all time slot requests for about 7 ms.

This leaves me with the following question:

1. Why does the soft device deny time slots for about 7 ms after each connection event transmission? It seems like an excessive time, and it prevents me from regularly transmitting my proprietary data.

2. Can I change and minimize the amount of time after connection events that my time slots are denied?

I have attached screen shots of my power measurements. The large dips (it measures voltage over a resistor, so larger power draw is lower voltage) every 10 ms is my proprietary transmissions. The smaller double dips are connection events (I assume). It shows how the time slots are denied (gaps in the 10 ms pattern) for about 7 ms after each connection event.I have attached my Central device source code and .hex, .map and .out  files. (it runs on nRF52840-DK board)

I have replaced the 2 connected devices with the Blinky peripheral example code to ensure the issue is not related to the peripheral device I am connecting to, and to ease support.

My evaluation application is originally based on the Blinky central example, but has been modified quite a bit.

#include "TxBuffer.h"
#include "nrf_log.h"


void TxBufferInit(STxBuffer* pThis, tx_message_t* pStorage, uint32_t iCapacity)
{
    TxBufferReset(pThis);
    pThis->pStorage = pStorage;
    pThis->iCapacity = iCapacity;
}

void TxBufferReset(STxBuffer* pThis)
{
    pThis->iPushCount = 0;
    pThis->iPullCount = 0;
}

/**@brief Function for passing any pending request from the buffer to the stack.
 */
void TxBufferProcess(STxBuffer* pThis)
{
    if (pThis->iPushCount == pThis->iPullCount)     {   return;     }

    uint32_t err_code;
    uint32_t iIndex = pThis->iPullCount % pThis->iCapacity;
    tx_message_t* pMsg = &(pThis->pStorage[iIndex]);

    if(pMsg->type == READ_REQ)
    {
        err_code = sd_ble_gattc_read(pMsg->conn_handle,
                                    pMsg->req.read_handle,
                                    0);
    }
    else
    {
        err_code = sd_ble_gattc_write(pMsg->conn_handle,
                                        &(pMsg->req.write_req.gattc_params));
    }
    
    if (err_code == NRF_SUCCESS)
    {
        NRF_LOG_DEBUG("SD Read/Write API returns Success..");
        pThis->iPullCount++;
    }
    else
    {
        NRF_LOG_DEBUG("SD Read/Write API returns error %d. This message sending will be "
            "attempted again..", err_code);
    }
    
}

tx_message_t* TxBufferPush(STxBuffer* pThis)
{
    uint32_t iCount = pThis->iPushCount - pThis->iPullCount;
    if(iCount >= pThis->iCapacity)      {   return 0;   }

    uint32_t iIndex = pThis->iPushCount % pThis->iCapacity;
    tx_message_t* pMsg = &(pThis->pStorage[iIndex]);

    pThis->iPushCount++;

    return pMsg;
}
TxBuffer.h
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "TxBuffer.h"
#include "sdk_config.h"
#include "nordic_common.h"
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#include "app_timer.h"
#include "bsp_btn_ble.h"
#include "ble.h"
#include "ble_hci.h"
#include "ble_advertising.h"
#include "ble_conn_params.h"
#include "ble_db_discovery.h"
#include "nrf_ble_gatt.h"
#include "ble_conn_state.h"
#include "nrf_ble_gatt.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_ble_scan.h"
#include "peer_manager_types.h"
#include "peer_manager.h"
#include "peer_manager_handler.h"
#include "nrf_soc.h"
#include "nrf_sdh_soc.h"

#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"


#define APP_BLE_CONN_CFG_TAG        1                                   /**< Tag that refers to the BLE stack configuration that is set with @ref sd_ble_cfg_set. The default tag is @ref APP_BLE_CONN_CFG_TAG. */
#define APP_BLE_OBSERVER_PRIO       3                                   /**< BLE observer priority of the application. There is no need to modify this value. */

#define APP_SOC_OBSERVER_PRIO       1

#define CENTRAL_SCANNING_LED        BSP_BOARD_LED_0
#define CENTRAL_CONNECTED_LED       BSP_BOARD_LED_1
#define LEDHA0_LED                  BSP_BOARD_LED_2                     /**< LED to indicate if HA is in program 0. */
#define LEDHA1_LED                  BSP_BOARD_LED_3                     /**< LED to indicate if HA is in program 0. */

#define HA0VOLUP_BUTTON             BSP_BUTTON_0                        /**< Button performs volume up on first connected HA. */
#define HA0VOLDOWN_BUTTON           BSP_BUTTON_1                        /**< Button performs volume down on first connected HA. */
#define HA1VOLUP_BUTTON             BSP_BUTTON_2                        /**< Button performs volume up on second connected HA. */
#define HA1VOLDOWN_BUTTON           BSP_BUTTON_3                        /**< Button performs volume down on second connected HA. */
#define BUTTON_DETECTION_DELAY      APP_TIMER_TICKS(50)                 /**< Delay from a GPIOTE event until a button is reported as pushed (in number of timer ticks). */

NRF_BLE_GATT_DEF(m_gatt);                                               /**< GATT module instance. */
//NRF_BLE_GATTS_C_ARRAY_DEF(m_gatts_c, NRF_SDH_BLE_CENTRAL_LINK_COUNT);       /**< GATT service client instances. */
BLE_DB_DISCOVERY_ARRAY_DEF(m_db_disc, NRF_SDH_BLE_CENTRAL_LINK_COUNT);  /**< Database discovery module instances. */
NRF_BLE_SCAN_DEF(m_scan);                                               /**< Scanning Module instance. */

static char const m_target_periph_name[] = "Nordic_Blinky";             /**< Name of the device to try to connect to. This name is searched for in the scanning report data. */

#define LBS_UUID_BASE        {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, \
                              0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00}
#define LBS_UUID_SERVICE     0x1523
#define LBS_UUID_BUTTON_CHAR 0x1524
#define LBS_UUID_LED_CHAR    0x1525



/**@brief Structure containing the handles related to the LED Button Service found on the peer. */
typedef struct
{
    uint16_t button_cccd_handle;  /**< Handle of the CCCD of the Button characteristic. */
    uint16_t button_handle;       /**< Handle of the Button characteristic as provided by the SoftDevice. */
    uint16_t led_handle;          /**< Handle of the LED characteristic as provided by the SoftDevice. */
} lbs_db_t;

typedef struct
{
    uint16_t ConHandle;
    pm_peer_id_t PeerId;
    bool Secure;
    bool Discovered;
    lbs_db_t PeerDb;
//    ServiceCache_t PeerCache;
} LbsDevice_t;



static tx_message_t m_aTxBufferStorage[8];
static STxBuffer m_TxBuffer;

static uint8_t m_ble_uuid_type;
static LbsDevice_t m_aLbsDevices[NRF_SDH_BLE_CENTRAL_LINK_COUNT];


static void OnLbsDeviceDisconnect(LbsDevice_t* pDevice);
static void SendPacket(uint8_t * pPacket);


/**@brief Function for handling asserts in the SoftDevice.
 *
 * @details This function is called in case of an assert in the SoftDevice.
 *
 * @warning This handler is only an example and is not meant for the final product. You need to analyze
 *          how your product is supposed to react in case of an assert.
 * @warning On assert from the SoftDevice, the system can only recover on reset.
 *
 * @param[in] line_num     Line number of the failing assert call.
 * @param[in] p_file_name  File name of the failing assert call.
 */
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
    app_error_handler(0xDEADBEEF, line_num, p_file_name);
}


/**@brief Function for initializing the LEDs.
 *
 * @details Initializes all LEDs used by the application.
 */
static void leds_init(void)
{
    bsp_board_init(BSP_INIT_LEDS);
}



static uint32_t GetConHandleIndex(uint16_t ConHandle)
{
    for(uint32_t i = 0; i < ARRAY_SIZE(m_aLbsDevices); i++)
    {
        if( ConHandle == m_aLbsDevices[i].ConHandle)
        {
            return i;
        }
    }

    return 0xFFFFFFFF;  // Not found value
}

static LbsDevice_t* GetLbsDevice(uint16_t ConHandle)
{
    for(uint32_t i = 0; i < ARRAY_SIZE(m_aLbsDevices); i++)
    {
        if( ConHandle == m_aLbsDevices[i].ConHandle)
        {
            return &m_aLbsDevices[i];
        }
    }

    return 0;
}

static void LbsDeviceClear(LbsDevice_t* pDevice)
{
    pDevice->ConHandle = BLE_CONN_HANDLE_INVALID;
    pDevice->PeerId = PM_PEER_ID_INVALID;
    pDevice->Secure = false;
    pDevice->Discovered = false;
    memset(&pDevice->PeerDb, 0x00, sizeof(lbs_db_t));
}

static LbsDevice_t* LbsDeviceGetFreeSlot(void)
{
    uint32_t iIndex = GetConHandleIndex(BLE_CONN_HANDLE_INVALID);
    if(0xFFFFFFFF == iIndex)
    {
        return 0;
    }

    return &m_aLbsDevices[iIndex];
}


static void scan_evt_handler(scan_evt_t const * p_scan_evt)
{
    ret_code_t err_code;

    switch(p_scan_evt->scan_evt_id)
    {
        case NRF_BLE_SCAN_EVT_CONNECTING_ERROR:
        {
            err_code = p_scan_evt->params.connecting_err.err_code;
            APP_ERROR_CHECK(err_code);
        } break;

        default:
            break;
    }
}


/**@brief Function for initializing the scanning and setting the filters.
 */
static void scan_init(void)
{
    ret_code_t          err_code;
    nrf_ble_scan_init_t init_scan;

    memset(&init_scan, 0, sizeof(init_scan));

    init_scan.connect_if_match = true;
    init_scan.conn_cfg_tag     = APP_BLE_CONN_CFG_TAG;

    err_code = nrf_ble_scan_init(&m_scan, &init_scan, scan_evt_handler);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_ble_scan_filter_set(&m_scan, SCAN_NAME_FILTER, m_target_periph_name);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_ble_scan_filters_enable(&m_scan, NRF_BLE_SCAN_NAME_FILTER, false);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for starting scanning. */
static void scan_start(void)
{
    ret_code_t ret;

    NRF_LOG_INFO("Start scanning for device name %s.", (uint32_t)m_target_periph_name);
    ret = nrf_ble_scan_start(&m_scan);
    APP_ERROR_CHECK(ret);
    // Turn on the LED to signal scanning.
    bsp_board_led_on(CENTRAL_SCANNING_LED);
}

/*
static void ButtonChange(LbsDevice_t* pDevice, int8_t iButton)
{
    tx_message_t * p_msg;

    p_msg = TxBufferPush(&m_TxBuffer);
    if(!p_msg)
    {
        NRF_LOG_INFO("TxBufferFull");
        return;
    }

    NRF_LOG_DEBUG("Sending Button %d", NewButton);

    p_msg->req.write_req.gattc_params.handle   = pDevice->PeerDb.button_handle;
    p_msg->req.write_req.gattc_params.len      = 1;
    p_msg->req.write_req.gattc_params.p_value  = p_msg->req.write_req.gattc_value;
    p_msg->req.write_req.gattc_params.offset   = 0;
    p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ;
    p_msg->req.write_req.gattc_value[0]        = 0x01;
    p_msg->conn_handle                         = pDevice->ConHandle;
    p_msg->type                                = WRITE_REQ;

    TxBufferProcess(&m_TxBuffer);
}
*/

static void ButtonUpdate(LbsDevice_t* pDevice, const uint8_t* pButtonData)
{
    // Debug info - report button
    NRF_LOG_INFO("Conn %d, button = %d", GetConHandleIndex(pDevice->ConHandle), pButtonData[0]);

    // Update LEDs
    uint32_t iIndex = GetConHandleIndex(pDevice->ConHandle);
    uint32_t LedId = (0 == iIndex) ? LEDHA0_LED : LEDHA1_LED;
    uint32_t Button = pButtonData[0];

    if(!Button)
    {
        bsp_board_led_on(LedId);
    }
    else
    {
        bsp_board_led_off(LedId);
    }
}


/**@brief Function for configuring the CCCD.
 *
 * @param[in] conn_handle The connection handle on which to configure the CCCD.
 * @param[in] handle_cccd The handle of the CCCD to be configured.
 * @param[in] enable      Whether to enable or disable the CCCD.
 *
 * @return NRF_SUCCESS if the CCCD configure was successfully sent to the peer.
 */
static uint32_t cccd_configure(uint16_t conn_handle, uint16_t handle_cccd, bool enable)
{
    NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d",
        handle_cccd,conn_handle);

    tx_message_t * p_msg;
    uint16_t       cccd_val = enable ? BLE_GATT_HVX_NOTIFICATION : 0;

    p_msg = TxBufferPush(&m_TxBuffer);
    if(!p_msg)
    {
        NRF_LOG_INFO("TxBufferFull");
        return NRF_ERROR_BUSY;
    }

    p_msg->req.write_req.gattc_params.handle   = handle_cccd;
    p_msg->req.write_req.gattc_params.len      = BLE_CCCD_VALUE_LEN;
    p_msg->req.write_req.gattc_params.p_value  = p_msg->req.write_req.gattc_value;
    p_msg->req.write_req.gattc_params.offset   = 0;
    p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ;
    p_msg->req.write_req.gattc_value[0]        = LSB_16(cccd_val);
    p_msg->req.write_req.gattc_value[1]        = MSB_16(cccd_val);
    p_msg->conn_handle                         = conn_handle;
    p_msg->type                                = WRITE_REQ;

    TxBufferProcess(&m_TxBuffer);
    return NRF_SUCCESS;
}


/**@brief Function for handling BLE events.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 * @param[in]   p_context   Unused.
 */
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    ret_code_t err_code;

    // For readability.
    //    ble_common_evt_t const *    p_common_evt    = &p_ble_evt->evt.common_evt; /**< Common Event, evt_id in BLE_EVT_* series. */
    ble_gap_evt_t const *       p_gap_evt       = &p_ble_evt->evt.gap_evt;    /**< GAP originated event, evt_id in BLE_GAP_EVT_* series. */
    ble_gattc_evt_t const *     p_gattc_evt     = &p_ble_evt->evt.gattc_evt;  /**< GATT client originated event, evt_id in BLE_GATTC_EVT* series. */

    if(p_ble_evt->header.evt_id == 29)    // I cant figure out what type of event this is, but its spamming like crazy
    {
        return;
    }

    NRF_LOG_DEBUG("BLE Event ID %d", p_ble_evt->header.evt_id);

    switch (p_ble_evt->header.evt_id)
    {
        // Upon connection, check which peripheral is connected, initiate DB
        // discovery, update LEDs status, and resume scanning, if necessary.
        case BLE_GAP_EVT_CONNECTED:
        {
            NRF_LOG_INFO("Connection 0x%x established", p_gap_evt->conn_handle);

            APP_ERROR_CHECK_BOOL(p_gap_evt->conn_handle < NRF_SDH_BLE_CENTRAL_LINK_COUNT);

            // Store the conHandle
            LbsDevice_t* pDevice = LbsDeviceGetFreeSlot();
            if(!pDevice)
            {
                NRF_LOG_INFO("Out of Device storage");
                break;
            }
            else
            {
                pDevice->ConHandle = p_gap_evt->conn_handle;
            }


            uint32_t iIndex = GetConHandleIndex(pDevice->ConHandle);
            err_code = ble_db_discovery_start(&m_db_disc[iIndex],
                                              pDevice->ConHandle);

            if (err_code != NRF_ERROR_BUSY)
            {
                APP_ERROR_CHECK(err_code);
            }

            // Update LEDs status and check whether it is needed to look for more
            // peripherals to connect to.
            bsp_board_led_on(CENTRAL_CONNECTED_LED);
            if (ble_conn_state_central_conn_count() == NRF_SDH_BLE_CENTRAL_LINK_COUNT)
            {
                bsp_board_led_off(CENTRAL_SCANNING_LED);
            }
            else
            {
                // Resume scanning.
                bsp_board_led_on(CENTRAL_SCANNING_LED);
                scan_start();
            }
        } break; // BLE_GAP_EVT_CONNECTED

        // Upon disconnection, reset the connection handle of the peer that disconnected, update
        // the LEDs status and start scanning again.
        case BLE_GAP_EVT_DISCONNECTED:
        {
            if(BLE_HCI_CONNECTION_TIMEOUT == p_gap_evt->params.disconnected.reason)
            {
                NRF_LOG_INFO("central link 0x%x disconnected (reason: Connection timeout)",
                    p_gap_evt->conn_handle);
            }
            else
            {
                NRF_LOG_INFO("central link 0x%x disconnected (reason: 0x%x)",
                    p_gap_evt->conn_handle,
                    p_gap_evt->params.disconnected.reason);
            }

            // Clean up
            LbsDevice_t* pDevice = GetLbsDevice(p_gap_evt->conn_handle);
            if(!pDevice)
            {
                NRF_LOG_INFO("Disconnected Con handle not recognized");
            }
            else
            {
                OnLbsDeviceDisconnect(pDevice);
            }


            if (ble_conn_state_central_conn_count() == 0)
            {
                // Turn off the LED that indicates the connection.
                bsp_board_led_off(CENTRAL_CONNECTED_LED);
            }

            // Start scanning.
            scan_start();

            // Turn on the LED for indicating scanning.
            bsp_board_led_on(CENTRAL_SCANNING_LED);

        } break;

        case BLE_GAP_EVT_TIMEOUT:
        {
            // Timeout for scanning is not specified, so only the connection requests can time out.
            if (p_gap_evt->params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN)
            {
                NRF_LOG_DEBUG("Connection request timed out.");
            }
        } break;

        case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST.");
            // Accept parameters requested by peer.
            err_code = sd_ble_gap_conn_param_update(p_gap_evt->conn_handle,
                                        &p_gap_evt->params.conn_param_update_request.conn_params);
            APP_ERROR_CHECK(err_code);
        } break;

        case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("PHY update request.");
            ble_gap_phys_t const phys =
            {
                .rx_phys = BLE_GAP_PHY_AUTO,
                .tx_phys = BLE_GAP_PHY_AUTO,
            };
            err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
            APP_ERROR_CHECK(err_code);
        } break;

        case BLE_GATTC_EVT_TIMEOUT:
        {
            // Disconnect on GATT client timeout event.
            NRF_LOG_DEBUG("GATT client timeout.");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
        } break;

        case BLE_GATTS_EVT_TIMEOUT:
        {
            // Disconnect on GATT server timeout event.
            NRF_LOG_DEBUG("GATT server timeout.");
            err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            APP_ERROR_CHECK(err_code);
        } break;

        // The next two cases are Gatt stuff
        // GATT Value Notification received from the SoftDevice
        case BLE_GATTC_EVT_HVX:
        {
            // Check if the event is on the link for this instance
            LbsDevice_t* pDevice = GetLbsDevice(p_gattc_evt->conn_handle);
            if(!pDevice)    {   return; }

            NRF_LOG_DEBUG("Notification received on Con %d", pDevice->ConHandle);

            // Check if this is a Program volume fine tuning notification.
            if (p_gattc_evt->params.hvx.handle == pDevice->PeerDb.button_handle)
            {
                // Ensure we got at least expected length
                if (p_gattc_evt->params.hvx.len >= 17)
                {
                    ButtonUpdate(pDevice, p_gattc_evt->params.hvx.data);
                }
            }
        }   break;

        // GATT Write response received from the SoftDevice
        case BLE_GATTC_EVT_WRITE_RSP:
        {
            // Check if this conn handle is interesting
            LbsDevice_t* pDevice = GetLbsDevice(p_ble_evt->evt.gattc_evt.conn_handle);
            if(!pDevice)    {   return; }

            //If it is, Process pending Tx in the Tx Buffer
            TxBufferProcess(&m_TxBuffer);
        }   break;

        // GATT read response received from the SoftDevice
        case BLE_GATTC_EVT_READ_RSP:
        {
            const ble_gattc_evt_read_rsp_t* pReadResp = &p_gattc_evt->params.read_rsp;

            if(pReadResp->len < 1)
            {
                TxBufferProcess(&m_TxBuffer);
                break;
            }

            LbsDevice_t* pDevice = GetLbsDevice(p_ble_evt->evt.gattc_evt.conn_handle);

            if(pReadResp->handle == pDevice->PeerDb.button_handle)
            {
                if(pReadResp->len >= 1)
                {
                    ButtonUpdate(pDevice, pReadResp->data);
                }
            }

        }   break;

        default:
            // Update GATT
            nrf_ble_gatt_on_ble_evt(p_ble_evt, p_context);  // FIXME is this needed?

            // No further implementation needed.
            break;
    }
}

/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupts.
 */
static void ble_stack_init(void)
{
    ret_code_t err_code;

    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack using the default settings.
    // Fetch the start address of the application RAM.
    uint32_t ram_start = 0;
    err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
    APP_ERROR_CHECK(err_code);

    // Enable BLE stack.
    err_code = nrf_sdh_ble_enable(&ram_start);
    APP_ERROR_CHECK(err_code);

    // Register a handler for BLE events.
    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}


static void ClearBondings(void)
{
    // Abort if there is no peers to clear
    if(0 == pm_peer_count())                            {   return;     }
    
    // Abort if we have any active BLE connections
    if(0 != ble_conn_state_central_conn_count())        {   return;     }

    nrf_ble_scan_stop();

    ret_code_t err_code;
    //err_code = softdevice_handler_sd_disable();
    err_code = pm_peers_delete();

    if(NRF_SUCCESS == err_code)
    {
        NRF_LOG_INFO("Starting bondings clearing");
    }
    else
    {
        NRF_LOG_INFO("Bondings clear failed, code %d", err_code);
    }
}



/**@brief Function for handling events from the button handler module.
 *
 * @param[in] pin_no        The pin that the event applies to.
 * @param[in] button_action The button action (press or release).
 */
static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
    static uint8_t ButtonStatus;    // Keeps an overview of the state of all buttons

    uint8_t iIndex = 0;
//    int8_t  iDeltaVolume = 0;
    uint8_t Mask;

    switch (pin_no)
    {
        case HA0VOLUP_BUTTON:
            iIndex = 0;
//            iDeltaVolume = 1;
            Mask = 1;
            break;

        case HA0VOLDOWN_BUTTON:
            iIndex = 0;
//            iDeltaVolume = -1;
            Mask = 2;
            break;

        case HA1VOLUP_BUTTON:
            iIndex = 1;
//            iDeltaVolume = 1;
            Mask = 4;
            break;

        case HA1VOLDOWN_BUTTON:
            iIndex = 1;
//            iDeltaVolume = -1;
            Mask = 8;
            break;

        default:
            APP_ERROR_HANDLER(pin_no);
            return;
            break;
    }

    // Only adjust volume on button pressed, ignore releases
    if(button_action)
    {
        if(m_aLbsDevices[iIndex].Secure && m_aLbsDevices[iIndex].Discovered)
        {
            //ButtonChange(&m_aLbsDevices[iIndex], iButton);
        }
    }


    // Update button status overview
    if(button_action)
    {
        ButtonStatus |= Mask;
    }
    else
    {
        ButtonStatus &= ~Mask;
    }


    // Check for clear bondings
    if( 
        (button_action) &&              // if event is button pressed
        (ButtonStatus == (1 | 4))       // And only the Clear bonding buttons are currently pressed
    )                                   // Its because the second of the two Clear bonding buttons was just pressed
    {
        ClearBondings();
    }
}


/**@brief Function for initializing the button handler module.
 */
static void buttons_init(void)
{
    ret_code_t err_code;

   // The array must be static because a pointer to it is saved in the button handler module.
    static app_button_cfg_t buttons[] =
    {
        {HA0VOLUP_BUTTON,   false, BUTTON_PULL, button_event_handler},
        {HA0VOLDOWN_BUTTON, false, BUTTON_PULL, button_event_handler},
        {HA1VOLUP_BUTTON,   false, BUTTON_PULL, button_event_handler},
        {HA1VOLDOWN_BUTTON, false, BUTTON_PULL, button_event_handler}
    };

    err_code = app_button_init(buttons, ARRAY_SIZE(buttons), BUTTON_DETECTION_DELAY);
    APP_ERROR_CHECK(err_code);

    err_code = app_button_enable();
    APP_ERROR_CHECK(err_code);
}


static void EnableLbsNotifications(const LbsDevice_t* pDevice)
{
    // Enable button notifications
    uint32_t Status = cccd_configure(pDevice->ConHandle, pDevice->PeerDb.button_cccd_handle, true);
    if(Status != NRF_SUCCESS)
    {
        NRF_LOG_INFO("cccd_configure error %d", Status);
        NRF_LOG_INFO("Handle Button %d, CCCd %d", pDevice->PeerDb.button_handle, pDevice->PeerDb.button_cccd_handle);
    }
}

static void ReadCharacteristic(uint16_t ConHandle, uint16_t CharHandle)
{
    tx_message_t * p_msg = TxBufferPush(&m_TxBuffer);

    p_msg->req.read_handle                     = CharHandle;
    p_msg->conn_handle                         = ConHandle;
    p_msg->type                                = READ_REQ;
    TxBufferProcess(&m_TxBuffer);

    NRF_LOG_DEBUG("Reading characteristic Char %d on con %d", CharHandle, ConHandle);
}

static void OnLbsDeviceReady(const LbsDevice_t* pDevice)
{
    EnableLbsNotifications(pDevice);
    ReadCharacteristic(pDevice->ConHandle, pDevice->PeerDb.button_handle);
}


static void OnLbsDeviceDisconnect(LbsDevice_t* pDevice)
{
    uint32_t iIndex = GetConHandleIndex(pDevice->ConHandle);
    uint32_t LedId = (0 == iIndex) ? LEDHA0_LED : LEDHA1_LED;
    bsp_board_led_off(LedId);
    LbsDeviceClear(pDevice);
}


/**@brief Function for handling database discovery events.
 *
 * @details This function is a callback function to handle events from the database discovery module.
 *          Depending on the UUIDs that are discovered, this function forwards the events
 *          to their respective services.
 *
 * @param[in] p_event  Pointer to the database discovery event.
 */
static void db_disc_handler(ble_db_discovery_evt_t * p_evt)
{
    NRF_LOG_INFO("Call to db_disc_handler for link 0x%x!", p_evt->conn_handle);

    // Check if the Led Button Service was discovered.
    if (
        p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
        p_evt->params.discovered_db.srv_uuid.uuid == LBS_UUID_SERVICE &&
        p_evt->params.discovered_db.srv_uuid.type == m_ble_uuid_type
       )
    {
        NRF_LOG_INFO("LSB service discovered");

        uint16_t ConHandle = p_evt->conn_handle;
        lbs_db_t DiscoveredDb;

        for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
        {
            const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
            NRF_LOG_DEBUG("UUID %x", p_char->characteristic.uuid.uuid);
            NRF_LOG_DEBUG("notify %d, read %d, write %d, write_command %d",
                        p_char->characteristic.char_props.notify,
                        p_char->characteristic.char_props.read,
                        p_char->characteristic.char_props.write,
                        p_char->characteristic.char_props.write_wo_resp
            );


            switch (p_char->characteristic.uuid.uuid)
            {
                case LBS_UUID_LED_CHAR:
                    DiscoveredDb.led_handle                     = p_char->characteristic.handle_value;
                    break;
                case LBS_UUID_BUTTON_CHAR:
                    DiscoveredDb.button_handle      = p_char->characteristic.handle_value;
                    DiscoveredDb.button_cccd_handle = p_char->cccd_handle;
                    break;

                default:
                    NRF_LOG_INFO("Unused characteristic");
                    break;
            }
        }

        LbsDevice_t* pDevice = GetLbsDevice(ConHandle);
        if(!pDevice)
        {
            NRF_LOG_INFO("Discovery before saved device");
            return;
        }

        // Store the discovered service parameters if entry is clear
        if (
            (pDevice->PeerDb.led_handle         == BLE_GATT_HANDLE_INVALID)&&
            (pDevice->PeerDb.button_handle      == BLE_GATT_HANDLE_INVALID)&&
            (pDevice->PeerDb.button_cccd_handle == BLE_GATT_HANDLE_INVALID)
            )
        {
            pDevice->PeerDb = DiscoveredDb;
            pDevice->Discovered = true;

            // See if we are already bonded
            if(!pDevice->Secure)
            {
                pm_peer_id_get(pDevice->ConHandle, &pDevice->PeerId);

                if(PM_PEER_ID_INVALID == pDevice->PeerId)
                {
                    // If not start bonding
                    NRF_LOG_INFO("New LBS Device - Securing connection and bonding");
                    ret_code_t Status = pm_conn_secure(pDevice->ConHandle, false);
                    if( NRF_SUCCESS != Status)
                    {
                        NRF_LOG_INFO("Securing connection failed with code %d", Status);
                    }
                }
            }
            else
            {
                OnLbsDeviceReady(pDevice);
            }
        }
        else
        {
            NRF_LOG_INFO("Discovery info rejected due to peer info already set");
        }
    }
    else
    {
        if(p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE)
        {
            NRF_LOG_INFO("Discovery complete");
            NRF_LOG_INFO("Failed to discover Button LED Service");
            NRF_LOG_INFO("Found    UUID %d, type %d", p_evt->params.discovered_db.srv_uuid.uuid, p_evt->params.discovered_db.srv_uuid.type);
            NRF_LOG_INFO("Expected UUID %d, type %d", LBS_UUID_SERVICE, m_ble_uuid_type);
        }
        else
        {
            if(p_evt->evt_type == BLE_DB_DISCOVERY_SRV_NOT_FOUND)
            {
                NRF_LOG_INFO("DISC: Service not found");
            }

            NRF_LOG_INFO("Disc event type %d", p_evt->evt_type);
        }
    }
}


/** @brief Database discovery initialization.
 */
static void db_discovery_init(void)
{
    ret_code_t err_code = ble_db_discovery_init(db_disc_handler);
    APP_ERROR_CHECK(err_code);
}


static void PeerManagerEventHandler(pm_evt_t const * p_event)
{
    NRF_LOG_DEBUG("PeerManagerEvent %d", p_event->evt_id);
    pm_handler_on_pm_evt(p_event);
    pm_handler_flash_clean(p_event);

    switch(p_event->evt_id)
    {
        case PM_EVT_CONN_SEC_SUCCEEDED:
        {
            LbsDevice_t* pDevice = GetLbsDevice(p_event->conn_handle);
            if(!pDevice)
            {
                NRF_LOG_INFO("Secured connection to non LBS device!!")
                return;
            }

            pDevice->PeerId = p_event->peer_id;
            pDevice->Secure = true;

            if(pDevice->Discovered)
            {
                OnLbsDeviceReady(pDevice);
            }

        }   break;

        case PM_EVT_PEERS_DELETE_SUCCEEDED:
        {
            NRF_LOG_INFO("Bondings cleared");
            scan_start();
        }   break;

        case PM_EVT_PEERS_DELETE_FAILED:
        {
            NRF_LOG_INFO("Failed to clear Bondings");
        }   break;

        default:
            break;
    }

}


/**@brief Function for initializing power management.
 */
static void power_management_init(void)
{
    ret_code_t err_code;
    err_code = nrf_pwr_mgmt_init();
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling the idle state (main loop).
 *
 * @details This function handles any pending log operations, then sleeps until the next event occurs.
 */
static void idle_state_handle(void)
{
    if (NRF_LOG_PROCESS() == false)
    {
        //nrf_pwr_mgmt_run();   // Disabled, as the Time slot events does not cause main process to exit this sleep
    }
}


/** @brief Function for initializing the log module.
 */
static void log_init(void)
{
    ret_code_t err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}


/** @brief Function for initializing the timer.
 */
static void timer_init(void)
{
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);
}


static void gatt_event_handler(nrf_ble_gatt_t *p_gatt, nrf_ble_gatt_evt_t const *p_evt)
{
    // FIXME this is called when a parameter value is changed
    NRF_LOG_INFO("Gatt event handler triggered.");
}

/**@brief Function for initializing the GATT module.
 */
static void gatt_init(void)
{
    ret_code_t err_code = nrf_ble_gatt_init(&m_gatt, gatt_event_handler);
    APP_ERROR_CHECK(err_code);
}

static void PeerManagerInit(void)
{
    ret_code_t err_code;

    err_code = pm_init();
    APP_ERROR_CHECK(err_code);

    // Configure peer manager
    static ble_gap_sec_params_t sec_params =
    {
        .bond           = true,
        .mitm           = false,
        .lesc           = 1,
        .keypress       = 0,
        .io_caps        = BLE_GAP_IO_CAPS_NONE,
        .oob            = false,
        .min_key_size   = 7,
        .max_key_size   = 16,
        .kdist_own      =
        {
            .enc            = 1,
            .id             = 1,
            .sign           = 0,
            .link           = 0
        },
        .kdist_peer     =
        {
            .enc            = 1,
            .id             = 1,
            .sign           = 0,
            .link           = 0
        }
    };

    err_code = pm_sec_params_set(&sec_params);
    APP_ERROR_CHECK(err_code);
}

ret_code_t ble_MyStuff_client_init(void)
{
    ret_code_t    err_code;
    ble_uuid_t    lbs_uuid;
    ble_uuid128_t lbs_base_uuid = {LBS_UUID_BASE};

    TxBufferInit(&m_TxBuffer, m_aTxBufferStorage, ARRAY_SIZE(m_aTxBufferStorage));

    // Initialize con handle and peer db storage
    for(uint32_t i = 0; i < NRF_SDH_BLE_CENTRAL_LINK_COUNT; i++)
    {
        LbsDeviceClear(&m_aLbsDevices[i]);
    }

    // This function sets p_ble_lbs_c->uuid_type to a suitable value
    err_code = sd_ble_uuid_vs_add(&lbs_base_uuid, &m_ble_uuid_type);
    VERIFY_SUCCESS(err_code);

    lbs_uuid.type = m_ble_uuid_type;
    lbs_uuid.uuid = LBS_UUID_SERVICE;

    // Register discovery event handler
    err_code = ble_db_discovery_evt_register(&lbs_uuid);
    VERIFY_SUCCESS(err_code);

    // Register Peer Manager event handler
    err_code = pm_register(PeerManagerEventHandler);
    VERIFY_SUCCESS(err_code);

    return NRF_SUCCESS;
}



// Struct for requesting first time slot
static const nrf_radio_request_t RadioRequestFirst =
{
    .request_type = NRF_RADIO_REQ_TYPE_EARLIEST,
    .params = 
    {
        .earliest =
        {
            .hfclk = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED,
            .priority = NRF_RADIO_PRIORITY_NORMAL,
            .length_us = 600,               // Time slot duration of 600 us
            .timeout_us = 1000000           // Error if time slot not aquired within 1 second
        }
    }
};

//Struct for periodic time slot requesting
static const nrf_radio_request_t RadioRequestPeriodic =
{
    .request_type = NRF_RADIO_REQ_TYPE_NORMAL,
    .params =
    {
        .normal =
        {
            .hfclk = NRF_RADIO_HFCLK_CFG_XTAL_GUARANTEED,
            .priority = NRF_RADIO_PRIORITY_HIGH,
            .length_us = 600,               // Time slot duration of 600 us
            .distance_us = 10000            // Request periodic time slot every 10 ms
//            .distance_us = 1000000            // Request periodic time slot every 1 s
        }
    }
};


static uint8_t aTestPacket[80];

/**@brief Function for configuring radio for raw transmission of BLE like packets.
 *
 * @details Never use function outside of a timeslot given by the BLE stack, or when BLE stack is disabled
 *
 * @param[in] iFrequency    Frequency in MHz.
 * @param[in] bUse2Mbit     true: 2 Mbit mode, false: 1 Mbit mode.
 * @param[in] iPower        Power in db(note that not all values are legal, consult NRF radio peripheral manual).
 * @param[in] Address       Address is not implemented!!
 * @param[in] CrcSeed       24 bit CRC seed.
 */
static void RadioConfigure(uint32_t iFrequency, bool bUse2Mbit, int8_t iPower, uint32_t Address, uint32_t CrcSeed)
{
    NRF_RADIO->POWER = 0x01;    // Power on the radio, no side effect if already powered on
    NRF_RADIO->INTENSET = 0x200001; // DEBUG
    NRF_RADIO->INTENSET = 0xFFFFFFFF; // DEBUG

    NRF_RADIO->SHORTS = 0x40002;  // END->Disable and TxReady->Start 
    NRF_RADIO->FREQUENCY = iFrequency - 2400;
    NRF_RADIO->TXPOWER = iPower;

    if(bUse2Mbit)
    {
        NRF_RADIO->MODE = 0x04; // 2 mbit BLE
        NRF_RADIO->PCNF0 = 0x1000008;   // 2 bytes pre-amble (2 mbit BLE), Length field on air is 8 bits
    }
    else
    {
        NRF_RADIO->MODE = 0x03; // 1 mbit BLE
        NRF_RADIO->PCNF0 = 0x08;   // 1 byte1 pre-amble (non 2 mbit BLE), Length field on air is 8 bits
    }

    NRF_RADIO->PCNF1 = 0x2030000;   // FIXME
    //NRF_RADIO->BASE0           - Radio base addr, controls something about the address, cant quite figure it out
    //NRF_RADIO->BASE1
    //NRF_RADIO->PREFIX0         - Radio prefix addr, controls something about addr. 
    //NRF_RADIO->PREFIX1         -
    NRF_RADIO->TXADDRESS = 0;

    NRF_RADIO->CRCCNF = 0x0103;
    NRF_RADIO->CRCPOLY = 0x80032C;  // Polynomium config for BLE
    NRF_RADIO->CRCINIT = CrcSeed;  // 0x555555 used for advertising, something agreed on connection for non advertising. Defined in WICL advertising for WICL

    uint32_t WhiteningSeed = (iFrequency + 1);              // BLE whitening seed is ((channel no * 2) + 1)
    NRF_RADIO->DATAWHITEIV = (__RBIT(WhiteningSeed)) >> 25; // Reverse and move to bits 0-6, as thats how NRF radio HW wants it
}


static nrf_radio_signal_callback_return_param_t TimeslotCbReturn;
static uint32_t iLostSlots = 0;
static uint32_t aStates[10];

/**@brief Function for requesting a timeslot with the periodic configuration, when a timeslot has been denied.
 *
 * @param[in] iFrequency    Number of consecutively denied time slots, so it requests a time slot at the correct time.
 */
static void RequestPeriodic(uint32_t iIntervalMultiplier)
{
     static nrf_radio_request_t RadioRequest;
    memcpy(&RadioRequest, &RadioRequestPeriodic, sizeof(RadioRequest));
    //RadioRequest.params.normal.distance_us *= iIntervalMultiplier;
    // Debug - try and adjust timing, by adding an extra 0,1 ms. to get out of sync with connection events
    RadioRequest.params.normal.distance_us *= iIntervalMultiplier;
    RadioRequest.params.normal.distance_us += 100;

    // Turn on LED, Time slot denied (function is only called when being denied a time slot)
    bsp_board_led_on(LEDHA1_LED);
    // debug end

    sd_radio_request(&RadioRequest);
}


/**@brief Function called by stack on time slot start and for events during time slot.
 * 
 * @details Note this function is called in high priority IRQ, blocking even the stack.
 *
 * @param[in] Signal    Reason for function being called
 */
nrf_radio_signal_callback_return_param_t* RadioTimeslotCb(uint8_t Signal)
{
    static nrf_radio_request_t RadioRequest;
    // NRF_RADIO_SIGNAL_CALLBACK_ACTION_NONE
    // NRF_RADIO_SIGNAL_CALLBACK_ACTION_EXTEND
    // NRF_RADIO_SIGNAL_CALLBACK_ACTION_END
    // NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END

    // Time stamp radio timeslot start
    NRF_TIMER0->TASKS_CAPTURE[0] = 1U;
    aStates[0] = NRF_TIMER0->CC[0];

    switch(Signal)
    {
        case NRF_RADIO_CALLBACK_SIGNAL_TYPE_START:
        {
            SendPacket(aTestPacket);

            // Turn off LED, as we got the time slot
            bsp_board_led_off(LEDHA1_LED);

            iLostSlots = 0;

            // Request next timeslot
            memcpy(&RadioRequest, &RadioRequestPeriodic, sizeof(RadioRequest)); // Use copy as return arg is not const
            TimeslotCbReturn.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
            TimeslotCbReturn.params.request.p_next = &RadioRequest;
        }   break;

        case NRF_RADIO_CALLBACK_SIGNAL_TYPE_TIMER0:
        {
            NRF_LOG_INFO("A");
            APP_ERROR_HANDLER(Signal);
            return 0;
        }   break;

        case NRF_RADIO_CALLBACK_SIGNAL_TYPE_RADIO:
        {
            NRF_LOG_INFO("B");
            APP_ERROR_HANDLER(Signal);
            return 0;
        }   break;

        case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_FAILED:
        {
            NRF_LOG_INFO("C");
            APP_ERROR_HANDLER(Signal);
            return 0;
        }   break;

        case NRF_RADIO_CALLBACK_SIGNAL_TYPE_EXTEND_SUCCEEDED:
        {
            NRF_LOG_INFO("D");
            APP_ERROR_HANDLER(Signal);
            return 0;
        }   break;

        default:
        NRF_LOG_INFO("E");
            APP_ERROR_HANDLER(Signal);
            return 0;
            break;
    }

    // Time stamp radio timeslot end
    NRF_TIMER0->TASKS_CAPTURE[0] = 1U;
    aStates[6] = NRF_TIMER0->CC[0];
    return &TimeslotCbReturn;
}

/**@brief Function called by stack on SOC events.
 * 
 * @details Among other things, called by stack with new of time slots being cancelled.
 *
 * @param[in] sys_evt   Reason for function being called
 * @param[in] pContext  Unused.
 */
static void soc_evt_handler(uint32_t sys_evt, void * pContext)
{
    switch(sys_evt)
    {
        case NRF_EVT_RADIO_SIGNAL_CALLBACK_INVALID_RETURN:
        {
            NRF_LOG_INFO("Timeslot Invalid");
        }   break;

        case NRF_EVT_RADIO_SESSION_IDLE:
        {
            NRF_LOG_INFO("Timeslot idle");
        }   break;

        case NRF_EVT_RADIO_SESSION_CLOSED:
        {
            NRF_LOG_INFO("Timeslot closed");
        }   break;

        case NRF_EVT_RADIO_BLOCKED:
        {
            NRF_LOG_INFO("Timeslot blocked");

            iLostSlots++;

            // Request next timeslot
            RequestPeriodic(iLostSlots + 1);
        }   break;

        case NRF_EVT_RADIO_CANCELED:
        {
            NRF_LOG_INFO("Timeslot canceled");

            iLostSlots++;

            // Request next timeslot
            RequestPeriodic(iLostSlots + 1);
        }   break;

        case NRF_EVT_FLASH_OPERATION_SUCCESS:
        {
            NRF_LOG_INFO("Flash Wr OK");
            break;
        }

        default:
            NRF_LOG_INFO("Unknown soc event %d", sys_evt);
            APP_ERROR_HANDLER(sys_evt);
            break;
    }
}

static bool bTxing = false;

/**@brief Sends pPacket as 2Mbit BLE on 2404 MHz with advertising CRC and some undefined address
 * 
 * @details Sends BLE like 2 Mbit package and saves a time stamp for each radio state during the activity.
 *          NOTE: Only call when BEL stack does not control the radio, e.g. in time slots or when stack is disabled.
 *
 * @param[in] pPacket   First byte of Packet is length of the packet. The following bytes is the payload.
 */
static void SendPacket(uint8_t * pPacket)
{
    //Power : int8_t(8), int8_t(4), int8_t(0), int8_t(-40)
    uint32_t iFrequency = 2404;
    bool bUse2Mbit = true;
    int8_t iPower = (int8_t)4;
    uint32_t Address = 0;   // FIXME
    uint32_t CrcSeed = 0x555555;    // 0x555555 used for advertising, something agreed on connection for non advertising. Defined in WICL advertising for WICL
    RadioConfigure(iFrequency, bUse2Mbit, iPower, Address, CrcSeed);
    NRF_RADIO->PACKETPTR = (uint32_t)pPacket;

    // send the packet:
    NRF_RADIO->TASKS_TXEN   = 1;

    // Time stamp the radio states, for debug information
    // Time stamp TX start
    NRF_TIMER0->TASKS_CAPTURE[0] = 1U;
    aStates[1] = NRF_TIMER0->CC[0];

    // Wait for radio to exit disabled
    while(0x00 == NRF_RADIO->STATE);

    // Time stamp disabled exited
    NRF_TIMER0->TASKS_CAPTURE[0] = 1U;
    aStates[2] = NRF_TIMER0->CC[0];

    // Wait for radio to exit Tx ramp up
    while(0x09 == NRF_RADIO->STATE);

    // Time stamp TX ramp up exited
    NRF_TIMER0->TASKS_CAPTURE[0] = 1U;
    aStates[3] = NRF_TIMER0->CC[0];


    // Wait for radio to exit TX
    while(0x0B == NRF_RADIO->STATE);

    // Time stamp radio TX exited
    NRF_TIMER0->TASKS_CAPTURE[0] = 1U;
    aStates[4] = NRF_TIMER0->CC[0];


    // Wait for radio to exit Tx disable state
    while(0x0C == NRF_RADIO->STATE);

    // Time stamp radio Tx disable exit
    NRF_TIMER0->TASKS_CAPTURE[0] = 1U;
    aStates[5] = NRF_TIMER0->CC[0];

    bTxing = true;
}

int main(void)
{
    NRF_NVMC->ICACHECNF |= 0x0100;
    // Initialize.
    log_init();
    timer_init();
    leds_init();
    buttons_init();
    power_management_init();
    ble_stack_init();
    gatt_init();
    db_discovery_init();
    PeerManagerInit();

    uint32_t InitStatus = ble_MyStuff_client_init();
    if(NRF_SUCCESS != InitStatus)
    {
        NRF_LOG_INFO("INIT FAILED!!");
        APP_ERROR_CHECK(InitStatus);
    }

    NRF_SDH_SOC_OBSERVER(m_soc_observer, APP_SOC_OBSERVER_PRIO, soc_evt_handler, NULL);

    ble_conn_state_init();
    scan_init();


    // Start execution.
    NRF_LOG_INFO("Time slot Tx example.");

    scan_start();

    memset(aTestPacket, 0xDEADBEEF, 80);
    aTestPacket[0] = 79;

    ret_code_t Status = sd_radio_session_open(RadioTimeslotCb);
    APP_ERROR_CHECK(Status);

    Status = sd_radio_request(&RadioRequestFirst);
    APP_ERROR_CHECK(Status);

    for (;;)
    {
        if(bTxing)
        {
            bTxing = false;
            // Debug
            // Print debug info
            // NRF_LOG_INFO("States0 %X, %X, %X, %X", aStates[0], aStates[1], aStates[2], aStates[3]);
            // NRF_LOG_INFO("States1 %X, %X, %X, %X", aStates[4], aStates[5], aStates[6], aStates[7]);
            // Debug end
        }
        else
        {
            idle_state_handle();
        }
    }
}
sdk_config.h

nrf52840_xxaa.hexnrf52840_xxaa.mapnrf52840_xxaa.out

Parents
  • Hi, sorry for the late response. I think the problem is that the event length is too long. I'm pretty sure that 6 is default value in the examples (6x1.25ms = 7.5ms). This is basically the time the softdevice sets aside for radio transmission each connection event in case it needs to send more packets per connection interval. You can read more here: http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s140.sds/dita/softdevices/s130/multilink_scheduling/extend_connection_event.html?cp=2_3_2_0_14_6 and here: http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s140.sds/dita/softdevices/s130/ble_data_throughput/ble_data_throughput.html?cp=2_3_2_0_16

    Try setting event length to 3, which is the minimum. This setting can be found in the sdk_config.h as NRF_SDH_BLE_GAP_EVENT_LENGTH

  • Hi

    Thank you for the helpful reply.

    NRF_SDH_BLE_GAP_EVENT_LENGTH was indeed set to 6 in my code, and changing it to 3 lowers the time after connection events where time slots are denied by 3.75 ms as you describe.This gives a great improvement in the performance of my evaluation application.

    In the SoftDevice API reference I found that the minimum value is defined as 2 ( BLE_GAP_EVENT_LENGTH_MIN   (2) ). Testing with the value 2 gave the expected result of even better performance. 

    My application transfers very little data over the BLE connections, so I am not worried about bandwidth, but as I use the time slots for audio streaming data it is very important that I can be granted the time slots as reliable as possible.

    To optimize towards the reliable granting of time slots, I would like to disable Connection Event Length Extensions in the SoftDevice. When reading the SoftDevice Specification I get the clear impression that Connection Event Length Extension can be disabled, but I have not been able to find out how.

    Is it possible to disable Connection Event Length Extension in the SoftDevice? If so how do I disable it?

  • Yes, you're right, 2 is the minimum.

    Connection Event Length Extension is enabled/disabled by using the sd_ble_opt_set() SD call. You need to set the enable bit to 0 in the ble_common_opt_conn_evt_ext_t struct.

    Something like this:

    void conn_evt_len_ext_set(bool status){
        ret_code_t err_code;
        ble_opt_t  opt;

        memset(&opt, 0x00, sizeof(opt));
        opt.common_opt.conn_evt_ext.enable = status ? 1 : 0;

        err_code = sd_ble_opt_set(BLE_COMMON_OPT_CONN_EVT_EXT, &opt);
        APP_ERROR_CHECK(err_code);
    }

    You can call this function in main after softdevice init, before advertising start.

  • That sounds like just what I need.

    Unfortunately clearing this bit does not seem to have any effect.

    I tried verifying it with sd_ble_opt_get(BLE_COMMON_OPT_CONN_EVT_EXT, &opt), but the getter always returns NRF_ERROR_NOT_SUPPORTED.

    I also tried calling sd_ble_opt_set in different parts of the init code, but it always return NRF_SUCCESS but does not appear to have any effect. I also tried setting it to 1 (enabling it), which also did not have any effect.

    Here is the ble_stack_init method where I added the call to sb_ble_opt_set:
    /**@brief Function for initializing the BLE stack.
     *
     * @details Initializes the SoftDevice and the BLE event interrupts.
     */
    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Register a handler for BLE events.
        NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
    
        // Disable Connection Event Length Extension - to minimize the time that stack reserves the radio
        ble_opt_t BleOptions;
    
    /*
        // First read back existing option
        err_code = sd_ble_opt_get(BLE_COMMON_OPT_CONN_EVT_EXT, &BleOptions);
        NRF_LOG_INFO("Existing BLE option : %d", BleOptions.common_opt.conn_evt_ext.enable);
        NRF_LOG_INFO("Error code %d", err_code);
        APP_ERROR_CHECK(err_code);
    */
    
        // Disable stuff
        memset(&BleOptions, 0x00, sizeof(BleOptions));
        BleOptions.common_opt.conn_evt_ext.enable = 0;
        err_code = sd_ble_opt_set(BLE_COMMON_OPT_CONN_EVT_EXT, &BleOptions);
        if(NRF_SUCCESS != err_code)
        {
            NRF_LOG_INFO("Failed to disable connection event length extension");
            NRF_LOG_INFO("Error code %d", err_code);
        }
    
        APP_ERROR_CHECK(err_code);
    
    /*
        // Read back again
        err_code = sd_ble_opt_get(BLE_COMMON_OPT_CONN_EVT_EXT, &BleOptions);
        NRF_LOG_INFO("After config BLE option : %d", BleOptions.common_opt.conn_evt_ext.enable);
        NRF_LOG_INFO("Error code %d", err_code);
        APP_ERROR_CHECK(err_code);
    */
    }
    ble_stack_init is called from main during initialization, just as in the Blinky peripheral sample app, as well as in the code attached to this support case.
  • sd_ble_opt_get is not supported for that parameter. Usually the event length ext. is disabled by default, so I'm not sure if you are looking for the right thing here.

    The event length ext. makes it possible for the soft device to dynamically extend the connection event if it has more data to send. If it does not have more data to send, the connection event will not be extended and the length will be NRF_SDH_BLE_GAP_EVENT_LENGTH. So, in other words the event length parameter will be the minimum length set aside, and you can choose to dynamically extend it or not.

    You can also try keep the hfclk running all the time. If you request the time slot with NRF_RADIO_HFCLK_CFG set to NRF_RADIO_HFCLK_CFG_NO_GUARANTEE. The softdevice will not start up and stop the clock in every time slot, instead you can start the clock with sd_clk_hfclk_request and just let it run as long as you need.

  • I see your point, the Connection Event Length Extension is not the problem, as there is no data traffic to speak of. I thought the NRF_SDH_BLE_GAP_EVENT_LENGTH would become irrellevant if I disabled Connection Event Length Extension, and that the SoftDevice would just minimize the reserved radio time. That was a misunderstanding.

    As I want to optimize towards reliable granting of time slots, I really want to get NRF_SDH_BLE_GAP_EVENT_LENGTH much lower than the minimum setting of 2.5 ms.

    From what I measure, the connection events only use the radio for about 600 us (measuring the power consumption, see the plots attached to this support case), so why does the SoftDevice need to reserve the radio for minimum 2.5 ms for every connection event?

    If I understand it correctly, The SoftDevice is hogging the Radio for almost 2 ms (minimum 2.5 ms reserved, but only uses 0.6 ms) extra for every connection event, just in case it might need it to send extra data. Can i somehow disable that?

    Low data rate with the connections is not a problem in my application, its much more important that I get as many of my requested time slots as possible.

Reply
  • I see your point, the Connection Event Length Extension is not the problem, as there is no data traffic to speak of. I thought the NRF_SDH_BLE_GAP_EVENT_LENGTH would become irrellevant if I disabled Connection Event Length Extension, and that the SoftDevice would just minimize the reserved radio time. That was a misunderstanding.

    As I want to optimize towards reliable granting of time slots, I really want to get NRF_SDH_BLE_GAP_EVENT_LENGTH much lower than the minimum setting of 2.5 ms.

    From what I measure, the connection events only use the radio for about 600 us (measuring the power consumption, see the plots attached to this support case), so why does the SoftDevice need to reserve the radio for minimum 2.5 ms for every connection event?

    If I understand it correctly, The SoftDevice is hogging the Radio for almost 2 ms (minimum 2.5 ms reserved, but only uses 0.6 ms) extra for every connection event, just in case it might need it to send extra data. Can i somehow disable that?

    Low data rate with the connections is not a problem in my application, its much more important that I get as many of my requested time slots as possible.

Children
No Data
Related