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

SDK 15.3.0: Restart Non-Connectable Advertising after Timeout

The objective is to have a device which constantly advertises some status information - this must be always visible to any Central in range.

The device may connect to at most one Central - but must continue to advertise the status information while connected.

So the device has 2 states:

  1. Not connected; doing Connectable advertising;
  2. Connected; doing Non-connectable advertising.

The information in the advertising doesn't change rapidly, so I use the Advertising Timeout as the point to update it; On the timeout event:

  1. Update the advertising data;
  2. Re-start the advertising.

This works fine for Connectable advertising, but not for Non-connectable.

I have tried the solution here, but it doesn't work:

https://devzone.nordicsemi.com/f/nordic-q-a/45644/how-do-i-simply-do-a-non-connectable-advert-on-nrf52-using-sdk-15/187363#187363

I suspect the problem is related to the fiddling with priorities.

So I started again from scratch,

I have attached my modified main.c from the SDK 15.3.0 'Template' example.

Also attached is ble_advertising.c, as I've added some extra logging - but no functional changes.

Here is the trace of a connection:

<debug> app: ADV: GAP Conn
<info> app: GAP Conn
<info> app: Non-connectable, Non-scannable advertising; Handle 0
<debug> nrf_ble_gatt: Peer on connection 0x0 requested a data length of 27 bytes.
<debug> nrf_ble_gatt: Updating data length to 27 on connection 0x0.
<debug> app: ADV: evt 35
<debug> nrf_ble_gatt: Data length updated to 27 on connection 0x0.
<debug> nrf_ble_gatt: max_rx_octets: 27
<debug> nrf_ble_gatt: max_tx_octets: 27
<debug> nrf_ble_gatt: max_rx_time: 328
<debug> nrf_ble_gatt: max_tx_time: 2120
<debug> app: ADV: evt 36
<debug> app: ADV: evt 33
<debug> app: PHY upd req
<debug> app: ADV: evt 34
<debug> app: ADV: Conn Parm Upd
<debug> app: ADV: Conn Parm Upd
<debug> app: ADV: Conn Parm Upd

I get the GAP Connect event, and the Non-connectable advertising starts OK.

But, when the Non-connectable advertising times out:

<info> app: Adv Tmo: Conn Hdl 0000; Adv Hdl 0; Mnf07
<info> app: Non-connectable, Non-scannable advertising; Handle 0
<error> app: ERROR 7 [NRF_ERROR_INVALID_PARAM] at main.c:780
PC at: 0x00031387
<error> app: End of error report

I get NRF_ERROR_INVALID_PARAM at this point:

    err_code = sd_ble_gap_adv_set_configure(&m_advertising.adv_handle,
                                            &m_advertising.adv_data,
                                            &adv_params);
    APP_ERROR_CHECK(err_code);

This is in non_connectable_advertising_start() - it worked fine to initially start the Non-connectable advertising, so why does it fail on re-starting it?

None of the parameters should have changed!

It is an unhelpful Error code as it give no help in identifying which parameter was invalid, nor how it was invalid. And, because it's an SD call, there's no way to step in to see where it's going wrong.

.

/**
 * Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include  "sdk_common.h"
#if NRF_MODULE_ENABLED(BLE_ADVERTISING)
#include "ble_advdata.h"
#include "ble_advertising.h"
#include "nrf_soc.h"

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

#include "sdk_errors.h"
#include "nrf_sdh_ble.h"
#include "nrf_sdh_soc.h"

#define BLE_ADV_MODES (5) /**< Total number of possible advertising modes. */


/**@brief Function for checking if the whitelist is in use.
 *
 * @param[in] p_advertising Advertising module instance.
 */
static bool whitelist_has_entries(ble_advertising_t * const p_advertising)
{
    return p_advertising->whitelist_in_use;
}


/**@brief Function for checking if an address is valid.
 *
 * @param[in] p_addr Pointer to a bluetooth address.
 */
static bool addr_is_valid(uint8_t const * const p_addr)
{
    for (uint32_t i = 0; i < BLE_GAP_ADDR_LEN; i++)
    {
        if (p_addr[i] != 0)
        {
            return true;
        }
    }
    return false;
}


/**@brief Function for checking the next advertising mode.
 *
 * @param[in] adv_mode Current advertising mode.
 */
static ble_adv_mode_t adv_mode_next_get(ble_adv_mode_t adv_mode)
{
    return (ble_adv_mode_t)((adv_mode + 1) % BLE_ADV_MODES);
}


/**@brief Function for handling the Connected event.
 *
 * @param[in] p_ble_evt Event received from the BLE stack.
 */
static void on_connected(ble_advertising_t * const p_advertising, ble_evt_t const * p_ble_evt)
{
    if (p_ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_PERIPH)
    {
        p_advertising->current_slave_link_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
    }
}


/**@brief Function for handling the Disconnected event.
 *
 * @param[in] p_advertising Advertising module instance.
 * @param[in] p_ble_evt Event received from the BLE stack.
 */
static void on_disconnected(ble_advertising_t * const p_advertising, ble_evt_t const * p_ble_evt)
{
    uint32_t ret;

    p_advertising->whitelist_temporarily_disabled = false;

    if (p_ble_evt->evt.gap_evt.conn_handle == p_advertising->current_slave_link_conn_handle &&
        p_advertising->adv_modes_config.ble_adv_on_disconnect_disabled == false)
    {
       ret = ble_advertising_start(p_advertising, BLE_ADV_MODE_DIRECTED_HIGH_DUTY);
       if ((ret != NRF_SUCCESS) && (p_advertising->error_handler != NULL))
       {
           p_advertising->error_handler(ret);
       }
    }
}


/**@brief Function for handling the Timeout event.
 *
 * @param[in] p_advertising Advertising module instance.
 * @param[in] p_ble_evt Event received from the BLE stack.
 */
static void on_terminated(ble_advertising_t * const p_advertising, ble_evt_t const * p_ble_evt)
{
    ret_code_t ret;

    if (p_ble_evt->header.evt_id != BLE_GAP_EVT_ADV_SET_TERMINATED)
    {
        // Nothing to do.
        NRF_LOG_DEBUG( "!ADV_SET_TERMINATED" );
        return;
    }

    if (  p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_TIMEOUT
        ||p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_LIMIT_REACHED)
    {
        // Start advertising in the next mode.
        ret = ble_advertising_start(p_advertising, adv_mode_next_get(p_advertising->adv_mode_current));

        NRF_LOG_DEBUG( "ADV_SET_TERMINATED: start result %d", ret );

        if ((ret != NRF_SUCCESS) && (p_advertising->error_handler != NULL))
        {
            p_advertising->error_handler(ret);
        }
    }
}


/**@brief Get the next available advertising mode.
 *
 * @param[in] p_advertising Advertising module instance.
 * @param[in] adv_mode Requested advertising mode.
 *
 * @returns adv_mode if possible, or the best available mode if not.
 */
static ble_adv_mode_t adv_mode_next_avail_get(ble_advertising_t * const p_advertising,
                                              ble_adv_mode_t            adv_mode)
{
    bool peer_addr_is_valid = addr_is_valid(p_advertising->peer_address.addr);

    // If a mode is disabled, continue to the next mode.

    switch (adv_mode)
    {
        case BLE_ADV_MODE_DIRECTED_HIGH_DUTY:
            if (   (p_advertising->adv_modes_config.ble_adv_directed_high_duty_enabled)
                && (!p_advertising->adv_modes_config.ble_adv_extended_enabled)
                && (peer_addr_is_valid))
            {
                return BLE_ADV_MODE_DIRECTED_HIGH_DUTY;
            }
            // Fallthrough.

        case BLE_ADV_MODE_DIRECTED:
            if ((p_advertising->adv_modes_config.ble_adv_directed_enabled) && peer_addr_is_valid)
            {
                return BLE_ADV_MODE_DIRECTED;
            }
            // Fallthrough.

        case BLE_ADV_MODE_FAST:
            if (p_advertising->adv_modes_config.ble_adv_fast_enabled)
            {
                return BLE_ADV_MODE_FAST;
            }
            // Fallthrough.

        case BLE_ADV_MODE_SLOW:
            if (p_advertising->adv_modes_config.ble_adv_slow_enabled)
            {
                return BLE_ADV_MODE_SLOW;
            }
            // Fallthrough.

        default:
            return BLE_ADV_MODE_IDLE;
    }
}


/**@brief Function for starting high duty directed advertising.
 *
 * @param[in]  p_advertising Advertising instance.
 * @param[out] p_adv_params Advertising parameters.
 *
 * @return NRF_SUCCESS
 */
static ret_code_t set_adv_mode_directed_high_duty(ble_advertising_t * const p_advertising,
                                                  ble_gap_adv_params_t    * p_adv_params)
{
    p_advertising->adv_evt    = BLE_ADV_EVT_DIRECTED_HIGH_DUTY;
    p_advertising->p_adv_data = NULL;

    p_adv_params->p_peer_addr     = &(p_advertising->peer_address);
    p_adv_params->interval        = 0;
    p_adv_params->properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED_HIGH_DUTY_CYCLE;
    p_adv_params->duration        = BLE_GAP_ADV_TIMEOUT_HIGH_DUTY_MAX;

    return NRF_SUCCESS;
}


/**@brief Function for starting directed slow advertising.
 *
 * @param[in]  p_advertising Advertising module instance.
 * @param[out] p_adv_params Advertising parameters.
 *
 * @return NRF_SUCCESS
 */
static ret_code_t set_adv_mode_directed(ble_advertising_t * const p_advertising,
                                        ble_gap_adv_params_t    * p_adv_params)
{
    p_advertising->adv_evt = BLE_ADV_EVT_DIRECTED;
#if !defined (S112) && !defined(S312)
    if (p_advertising->adv_modes_config.ble_adv_extended_enabled)
    {
        p_adv_params->properties.type = BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_DIRECTED;
    }
    else
    {
#endif // !defined (S112) && !defined(S312)
        p_adv_params->properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_NONSCANNABLE_DIRECTED;
#if !defined (S112) && !defined(S312)
    }
#endif // !defined (S112) && !defined(S312)
    p_adv_params->duration = p_advertising->adv_modes_config.ble_adv_directed_timeout;

    p_advertising->p_adv_data = NULL;

    p_adv_params->p_peer_addr = &p_advertising->peer_address;
    p_adv_params->interval    = p_advertising->adv_modes_config.ble_adv_directed_interval;

    return NRF_SUCCESS;
}


/**@brief Function for indicating whether to use whitelist for advertising.
 *
 * @param[in]  p_advertising Advertising module instance.
 *
 * @return Whether to use whitelist.
 */
static bool use_whitelist(ble_advertising_t * const p_advertising)
{
    return((p_advertising->adv_modes_config.ble_adv_whitelist_enabled) &&
           (!p_advertising->whitelist_temporarily_disabled) &&
           (whitelist_has_entries(p_advertising)));
}


/**@brief Function for setting new advertising flags in the advertising parameters.
 *
 * @param[in]  p_advertising Advertising module instance.
 * @param[in]  flags         New flags.
 *
 * @return Any error from @ref sd_ble_gap_adv_set_configure.
 */
static ret_code_t flags_set(ble_advertising_t * const p_advertising, uint8_t flags)
{
    uint8_t * p_flags = ble_advdata_parse(p_advertising->adv_data.adv_data.p_data,
                                          p_advertising->adv_data.adv_data.len,
                                          BLE_GAP_AD_TYPE_FLAGS);

    if (p_flags != NULL)
    {
        *p_flags = flags;
    }

    return sd_ble_gap_adv_set_configure(&p_advertising->adv_handle, &p_advertising->adv_data, &p_advertising->adv_params);
}


/**@brief Function for starting fast advertising.
 *
 * @param[in]  p_advertising Advertising module instance.
 * @param[out] p_adv_params Advertising parameters.
 *
 * @return NRF_SUCCESS or an error from @ref flags_set().
 */
static ret_code_t set_adv_mode_fast(ble_advertising_t * const p_advertising,
                                    ble_gap_adv_params_t    * p_adv_params)
{
    ret_code_t ret;

    p_adv_params->interval = p_advertising->adv_modes_config.ble_adv_fast_interval;
    p_adv_params->duration = p_advertising->adv_modes_config.ble_adv_fast_timeout;

#if !defined (S112) && !defined(S312)
    if (p_advertising->adv_modes_config.ble_adv_extended_enabled)
    {
        p_advertising->adv_params.properties.type = BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED;
    }
    else
    {
#endif // !defined (S112) && !defined(S312)
        p_advertising->adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
#if !defined (S112) && !defined(S312)
    }
#endif // !defined (S112) && !defined(S312)
 
    if (use_whitelist(p_advertising))
    {
        p_adv_params->filter_policy = BLE_GAP_ADV_FP_FILTER_CONNREQ;

        // Set correct flags.
        ret = flags_set(p_advertising, BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED);
        VERIFY_SUCCESS(ret);

        p_advertising->adv_evt = BLE_ADV_EVT_FAST_WHITELIST;
    }
    else
    {
        p_advertising->adv_evt = BLE_ADV_EVT_FAST;
    }
    p_advertising->p_adv_data = &(p_advertising->adv_data);
    return NRF_SUCCESS;
}


/**@brief Function for starting slow advertising.
 *
 * @param[in]  p_advertising Advertising module instance.
 * @param[out] p_adv_params Advertising parameters.
 *
 * @return NRF_SUCCESS or an error from @ref flags_set().
 */
static ret_code_t set_adv_mode_slow(ble_advertising_t * const p_advertising,
                                    ble_gap_adv_params_t    * p_adv_params)
{
    ret_code_t ret;

    p_adv_params->interval = p_advertising->adv_modes_config.ble_adv_slow_interval;
    p_adv_params->duration = p_advertising->adv_modes_config.ble_adv_slow_timeout;

#if !defined (S112) && !defined(S312)
    if (p_advertising->adv_modes_config.ble_adv_extended_enabled)
    {
        p_advertising->adv_params.properties.type = BLE_GAP_ADV_TYPE_EXTENDED_CONNECTABLE_NONSCANNABLE_UNDIRECTED;
    }
    else
    {
#endif // !defined (S112) && !defined(S312)
        p_advertising->adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
#if !defined (S112) && !defined(S312)
    }
#endif // !defined (S112) && !defined(S312)

    if (use_whitelist(p_advertising))
    {
        p_adv_params->filter_policy = BLE_GAP_ADV_FP_FILTER_CONNREQ;

        // Set correct flags.
        ret = flags_set(p_advertising, BLE_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED);
        VERIFY_SUCCESS(ret);

        p_advertising->adv_evt = BLE_ADV_EVT_SLOW_WHITELIST;
    }
    else
    {
        p_advertising->adv_evt = BLE_ADV_EVT_SLOW;
    }
    p_advertising->p_adv_data = &(p_advertising->adv_data);
    return NRF_SUCCESS;
}


/**@brief Function for checking if an advertising module configuration is legal.
 *
 * @details Advertising module can not be initialized if high duty directed advertising is used
 *          together with extended advertising.
 *
 * @param[in] p_config Pointer to the configuration.
 *
 * @return True  If the configuration is valid.
 * @return False If the configuration is invalid.
 */
static bool config_is_valid(ble_adv_modes_config_t const * const p_config)
{
    if ((p_config->ble_adv_directed_high_duty_enabled == true) &&
        (p_config->ble_adv_extended_enabled == true))
    {
        return false;
    }
#if !defined (S140)
    else if ( p_config->ble_adv_primary_phy == BLE_GAP_PHY_CODED ||
              p_config->ble_adv_secondary_phy == BLE_GAP_PHY_CODED)
    {
        return false;
    }
#endif // !defined (S140)
    else
    {
        return true;
    }
}


void ble_advertising_conn_cfg_tag_set(ble_advertising_t * const p_advertising,
                                      uint8_t                   ble_cfg_tag)
{
    p_advertising->conn_cfg_tag = ble_cfg_tag;
}


uint32_t ble_advertising_init(ble_advertising_t            * const p_advertising,   // Constant pointer
                              ble_advertising_init_t const * const p_init)          // Constant pointer to constant data
{
    uint32_t ret;
    if ((p_init == NULL) || (p_advertising == NULL))
    {
        return NRF_ERROR_NULL;
    }
    if (!config_is_valid(&p_init->config))
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    p_advertising->adv_mode_current               = BLE_ADV_MODE_IDLE;
    p_advertising->adv_modes_config               = p_init->config;
    p_advertising->conn_cfg_tag                   = BLE_CONN_CFG_TAG_DEFAULT;
    p_advertising->evt_handler                    = p_init->evt_handler;
    p_advertising->error_handler                  = p_init->error_handler;
    p_advertising->current_slave_link_conn_handle = BLE_CONN_HANDLE_INVALID;
    p_advertising->p_adv_data                     = &p_advertising->adv_data;

    memset(&p_advertising->peer_address, 0, sizeof(p_advertising->peer_address));

    // Copy advertising data.
    if (!p_advertising->initialized)
    {
        p_advertising->adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET;
    }
    p_advertising->adv_data.adv_data.p_data = p_advertising->enc_advdata;

    if (p_advertising->adv_modes_config.ble_adv_extended_enabled == true)
    {
#ifdef BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
        p_advertising->adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED;
#else
    p_advertising->adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
#endif // BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
    }
    else
    {
        p_advertising->adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
    }

    ret = ble_advdata_encode(&p_init->advdata, p_advertising->enc_advdata, &p_advertising->adv_data.adv_data.len);
    VERIFY_SUCCESS(ret);

    p_advertising->adv_data.scan_rsp_data.p_data = p_advertising->enc_scan_rsp_data;
    if (p_advertising->adv_modes_config.ble_adv_extended_enabled == true)
    {
#ifdef BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
        p_advertising->adv_data.scan_rsp_data.len = BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED;
#else
        p_advertising->adv_data.scan_rsp_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
#endif // BLE_GAP_ADV_SET_DATA_SIZE_EXTENDED_CONNECTABLE_MAX_SUPPORTED
    }
    else
    {
        p_advertising->adv_data.scan_rsp_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;
    }
    ret = ble_advdata_encode(&p_init->srdata,
                              p_advertising->adv_data.scan_rsp_data.p_data,
                             &p_advertising->adv_data.scan_rsp_data.len);
    VERIFY_SUCCESS(ret);

    // Configure a initial advertising configuration. The advertising data and and advertising
    // parameters will be changed later when we call @ref ble_advertising_start, but must be set
    // to legal values here to define an advertising handle.
    p_advertising->adv_params.primary_phy     = BLE_GAP_PHY_1MBPS;
    p_advertising->adv_params.duration        = p_advertising->adv_modes_config.ble_adv_fast_timeout;
    p_advertising->adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
    p_advertising->adv_params.p_peer_addr     = NULL;
    p_advertising->adv_params.filter_policy   = BLE_GAP_ADV_FP_ANY;
    p_advertising->adv_params.interval        = p_advertising->adv_modes_config.ble_adv_fast_interval;

    ret = sd_ble_gap_adv_set_configure(&p_advertising->adv_handle, NULL, &p_advertising->adv_params);
    VERIFY_SUCCESS(ret);

    p_advertising->initialized = true;
    return ret;
}


/**@brief Function for checking that a phy define value matches one of the valid phys from the SD.
 *
 * @param[in]  PHY to be validated.
 *
 * @retval true  If the PHY value is valid (1mbit, 2mbit, coded).
 * @retval false If the PHY value is invalid.
 */
static bool phy_is_valid(uint32_t const * const p_phy)
{
    if ((*p_phy) == BLE_GAP_PHY_1MBPS ||
        (*p_phy) == BLE_GAP_PHY_2MBPS
#if defined (S140)
     || (*p_phy) == BLE_GAP_PHY_CODED
#endif // !defined (S140)
        )
    {
        return true;
    }
    else
    {
        return false;
    }
}


uint32_t ble_advertising_start(ble_advertising_t * const p_advertising,
                               ble_adv_mode_t            advertising_mode)
{
    uint32_t ret;

    if (p_advertising->initialized == false)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    p_advertising->adv_mode_current = advertising_mode;

    memset(&p_advertising->peer_address, 0, sizeof(p_advertising->peer_address));

    if (  ((p_advertising->adv_modes_config.ble_adv_directed_high_duty_enabled) && (p_advertising->adv_mode_current == BLE_ADV_MODE_DIRECTED_HIGH_DUTY))
        ||((p_advertising->adv_modes_config.ble_adv_directed_enabled)           && (p_advertising->adv_mode_current == BLE_ADV_MODE_DIRECTED_HIGH_DUTY))
        ||((p_advertising->adv_modes_config.ble_adv_directed_enabled)           && (p_advertising->adv_mode_current == BLE_ADV_MODE_DIRECTED))
       )
    {
        if (p_advertising->evt_handler != NULL)
        {
            p_advertising->peer_addr_reply_expected = true;
            p_advertising->evt_handler(BLE_ADV_EVT_PEER_ADDR_REQUEST);
        }
        else
        {
            p_advertising->peer_addr_reply_expected = false;
        }
    }

    p_advertising->adv_mode_current = adv_mode_next_avail_get(p_advertising, advertising_mode);

    // Fetch the whitelist.
    if ((p_advertising->evt_handler != NULL) &&
        (p_advertising->adv_mode_current == BLE_ADV_MODE_FAST || p_advertising->adv_mode_current == BLE_ADV_MODE_SLOW) &&
        (p_advertising->adv_modes_config.ble_adv_whitelist_enabled) &&
        (!p_advertising->whitelist_temporarily_disabled))
    {
        p_advertising->whitelist_in_use         = false;
        p_advertising->whitelist_reply_expected = true;
        p_advertising->evt_handler(BLE_ADV_EVT_WHITELIST_REQUEST);
    }
    else
    {
        p_advertising->whitelist_reply_expected = false;
    }

    // Initialize advertising parameters with default values.
    memset(&p_advertising->adv_params, 0, sizeof(p_advertising->adv_params));

    p_advertising->adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;

    // Use 1MBIT as primary phy if no phy was selected.
    if (phy_is_valid(&p_advertising->adv_modes_config.ble_adv_primary_phy))
    {
        p_advertising->adv_params.primary_phy = p_advertising->adv_modes_config.ble_adv_primary_phy;
    }
    else
    {
        p_advertising->adv_params.primary_phy = BLE_GAP_PHY_1MBPS;
    }

    if (p_advertising->adv_modes_config.ble_adv_extended_enabled)
    {
        // Use 1MBIT as secondary phy if no phy was selected.
        if (phy_is_valid(&p_advertising->adv_modes_config.ble_adv_primary_phy))
        {
            p_advertising->adv_params.secondary_phy = p_advertising->adv_modes_config.ble_adv_secondary_phy;
        }
        else
        {
            p_advertising->adv_params.secondary_phy = BLE_GAP_PHY_1MBPS;
        }
    }
    p_advertising->adv_params.filter_policy = BLE_GAP_ADV_FP_ANY;

    // Set advertising parameters and events according to selected advertising mode.
    switch (p_advertising->adv_mode_current)
    {
        case BLE_ADV_MODE_DIRECTED_HIGH_DUTY:
            ret = set_adv_mode_directed_high_duty(p_advertising, &p_advertising->adv_params);
            break;

        case BLE_ADV_MODE_DIRECTED:
            ret = set_adv_mode_directed(p_advertising, &p_advertising->adv_params);
            break;

        case BLE_ADV_MODE_FAST:
            ret = set_adv_mode_fast(p_advertising, &p_advertising->adv_params);
            break;

        case BLE_ADV_MODE_SLOW:
            ret = set_adv_mode_slow(p_advertising, &p_advertising->adv_params);
            break;

        case BLE_ADV_MODE_IDLE:
            p_advertising->adv_evt = BLE_ADV_EVT_IDLE;
            break;

        default:
            break;
    }

    if (p_advertising->adv_mode_current != BLE_ADV_MODE_IDLE)
    {

        ret = sd_ble_gap_adv_set_configure(&p_advertising->adv_handle, p_advertising->p_adv_data, &p_advertising->adv_params);
        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
        ret = sd_ble_gap_adv_start(p_advertising->adv_handle, p_advertising->conn_cfg_tag);

        if (ret != NRF_SUCCESS)
        {
            return ret;
        }
    }

    if (p_advertising->evt_handler != NULL)
    {
        p_advertising->evt_handler(p_advertising->adv_evt);
    }

    return NRF_SUCCESS;
}


void ble_advertising_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    ble_advertising_t * p_advertising = (ble_advertising_t *)p_context;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_DEBUG( "ADV: GAP Conn" );
            //NRF_LOG_FLUSH();
            on_connected(p_advertising, p_ble_evt);
            break;

        // Upon disconnection, whitelist will be activated and direct advertising is started.
        case BLE_GAP_EVT_DISCONNECTED:
            NRF_LOG_DEBUG( "ADV: GAP Disc" );
            //NRF_LOG_FLUSH();
            on_disconnected(p_advertising, p_ble_evt);
            break;

        // Upon terminated advertising (time-out), the next advertising mode is started.
        case BLE_GAP_EVT_ADV_SET_TERMINATED:
            NRF_LOG_DEBUG( "ADV: Term" );
            //NRF_LOG_FLUSH();
            on_terminated(p_advertising, p_ble_evt);
            break;

        case BLE_GAP_EVT_CONN_PARAM_UPDATE:
            NRF_LOG_DEBUG( "ADV: Conn Parm Upd" );
            break;

        default:
            NRF_LOG_DEBUG( "ADV: evt %d", p_ble_evt->header.evt_id );
            //NRF_LOG_FLUSH();
            break;
    }
}


void ble_advertising_on_sys_evt(uint32_t evt_id, void * p_context)
{
    // Changes to advertising and fds components have made this function obsolete.
    // This function will be removed in a future major release.
}


uint32_t ble_advertising_peer_addr_reply(ble_advertising_t * const p_advertising,
                                         ble_gap_addr_t          * p_peer_address)
{
    if (!p_advertising->peer_addr_reply_expected)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    p_advertising->peer_addr_reply_expected = false;

    memcpy(&p_advertising->peer_address, p_peer_address, sizeof(p_advertising->peer_address));

    return NRF_SUCCESS;
}


uint32_t ble_advertising_whitelist_reply(ble_advertising_t * const p_advertising,
                                         ble_gap_addr_t    const * p_gap_addrs,
                                         uint32_t               addr_cnt,
                                         ble_gap_irk_t  const * p_gap_irks,
                                         uint32_t               irk_cnt)
{
    if (!p_advertising->whitelist_reply_expected)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    p_advertising->whitelist_reply_expected = false;
    p_advertising->whitelist_in_use         = ((addr_cnt > 0) || (irk_cnt > 0));

    return NRF_SUCCESS;
}


uint32_t ble_advertising_restart_without_whitelist(ble_advertising_t * const p_advertising)
{
    ret_code_t ret;

    (void) sd_ble_gap_adv_stop(p_advertising->adv_handle);

    p_advertising->whitelist_temporarily_disabled = true;
    p_advertising->whitelist_in_use               = false;
    p_advertising->adv_params.filter_policy       = BLE_GAP_ADV_FP_ANY;
    // Set correct flags.
    ret = flags_set(p_advertising, BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
    VERIFY_SUCCESS(ret);

    ret = ble_advertising_start(p_advertising, p_advertising->adv_mode_current);
    if ((ret != NRF_SUCCESS) && (p_advertising->error_handler != NULL))
    {
        p_advertising->error_handler(ret);
    }

    return NRF_SUCCESS;
}


void ble_advertising_modes_config_set(ble_advertising_t            * const p_advertising,
                                      ble_adv_modes_config_t const * const p_adv_modes_config)
{
    p_advertising->adv_modes_config = *p_adv_modes_config;
}


ret_code_t ble_advertising_advdata_update(ble_advertising_t  * const p_advertising,
                                          ble_gap_adv_data_t * const p_new_advdata_buf,
                                          bool                       permanent)
{
    if (permanent)
    {
        memcpy(&p_advertising->adv_data, p_new_advdata_buf, sizeof(p_advertising->adv_data));
        p_advertising->p_adv_data = &p_advertising->adv_data;
    }
    else
    {
        p_advertising->p_adv_data = p_new_advdata_buf;
    }

    return sd_ble_gap_adv_set_configure(&p_advertising->adv_handle,
                                        p_advertising->p_adv_data,
                                        NULL);
}


#endif // NRF_MODULE_ENABLED(BLE_ADVERTISING)

/**
 * Copyright (c) 2014 - 2019, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, must reproduce the above copyright notice, this list of
 *    conditions and the following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
/** @file
 *
 * @defgroup ble_sdk_app_template_main main.c
 * @{
 * @ingroup ble_sdk_app_template
 * @brief Template project main file.
 *
 * This file contains a template for creating a new application. It has the code necessary to wakeup
 * from button, advertise, get a connection restart advertising on disconnect and if no new
 * connection created go back to system-off mode.
 * It can easily be used as a starting point for creating a new application, the comments identified
 * with 'YOUR_JOB' indicates where and how you can customize.
 */

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

#include "nordic_common.h"
#include "nrf.h"
#include "app_error.h"
#include "ble.h"
#include "ble_hci.h"
#include "ble_srv_common.h"
#include "ble_advdata.h"
#include "ble_advertising.h"
#include "ble_conn_params.h"
#include "nrf_sdh.h"
#include "nrf_sdh_soc.h"
#include "nrf_sdh_ble.h"
#include "app_timer.h"
#include "fds.h"
#include "peer_manager.h"
#include "peer_manager_handler.h"
#include "bsp_btn_ble.h"
#include "sensorsim.h"
#include "ble_conn_state.h"
#include "nrf_ble_gatt.h"
#include "nrf_ble_qwr.h"
#include "nrf_pwr_mgmt.h"

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


#define DEVICE_NAME                     "Hacked Template"                       /**< Name of device. Will be included in the advertising data. */
#define MANUFACTURER_NAME               "HackedSemiconductor"                   /**< Manufacturer. Will be passed to Device Information Service. */
#define BT_COMPANY_ID                   0xFFFF                                  /**< BT SIG Company ID; FFFF is for test purposes only (0059 is Nordic's ID) */
#define APP_ADV_INTERVAL                300                                     /**< The advertising interval (in units of 0.625 ms. This value corresponds to 187.5 ms). */

#define APP_ADV_DURATION                3000                                   /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
#define APP_BLE_OBSERVER_PRIO           3                                       /**< Application's BLE observer priority. You shouldn't need to modify this value. */
#define APP_BLE_CONN_CFG_TAG            1                                       /**< A tag identifying the SoftDevice BLE configuration. */

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(100, UNIT_1_25_MS)        /**< Minimum acceptable connection interval (0.1 seconds). */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(200, UNIT_1_25_MS)        /**< Maximum acceptable connection interval (0.2 second). */
#define SLAVE_LATENCY                   0                                       /**< Slave latency. */
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)         /**< Connection supervisory timeout (4 seconds). */

#define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                   /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                  /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT    3                                       /**< Number of attempts before giving up the connection parameter negotiation. */

#define SEC_PARAM_BOND                  1                                       /**< Perform bonding. */
#define SEC_PARAM_MITM                  0                                       /**< Man In The Middle protection not required. */
#define SEC_PARAM_LESC                  0                                       /**< LE Secure Connections not enabled. */
#define SEC_PARAM_KEYPRESS              0                                       /**< Keypress notifications not enabled. */
#define SEC_PARAM_IO_CAPABILITIES       BLE_GAP_IO_CAPS_NONE                    /**< No I/O capabilities. */
#define SEC_PARAM_OOB                   0                                       /**< Out Of Band data not available. */
#define SEC_PARAM_MIN_KEY_SIZE          7                                       /**< Minimum encryption key size. */
#define SEC_PARAM_MAX_KEY_SIZE          16                                      /**< Maximum encryption key size. */

#define DEAD_BEEF                       0xDEADBEEF                              /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */


NRF_BLE_GATT_DEF(m_gatt);                                                       /**< GATT module instance. */
NRF_BLE_QWR_DEF(m_qwr);                                                         /**< Context for the Queued Write module.*/
BLE_ADVERTISING_DEF(m_advertising);                                             /**< Advertising module instance. */

static  ble_advdata_t            m_advdata;                                    /**< Advertising Data.    */

static  ble_advdata_manuf_data_t m_manuf_metadata;                             /**< Metadata describing the Manufacturer-Specific data (for Advertising).    */
#define MANUF_DATA_LEN           4
static  uint8_t                  m_manuf_data[MANUF_DATA_LEN] = "Mnf";         /**< The actual Manufacturer-Specific data.                                   */

static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;                        /**< Handle of the current connection. */

/* YOUR_JOB: Declare all services structure your application is using
 *  BLE_XYZ_DEF(m_xyz);
 */

// YOUR_JOB: Use UUIDs for service(s) used in your application.
static ble_uuid_t m_adv_uuids[] =                                               /**< Universally unique service identifiers. */
{
    {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}
};


static void advertising_start(bool erase_bonds);
static void non_connectable_advertising_start(void);


/**@brief Callback function for asserts in the SoftDevice.
 *
 * @details This function will be called in case of an assert in the SoftDevice.
 *
 * @warning This handler is an example only and does not fit a final product. You need to analyze
 *          how your product is supposed to react in case of 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] 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(DEAD_BEEF, line_num, p_file_name);
}


/**@brief Function for handling Peer Manager events.
 *
 * @param[in] p_evt  Peer Manager event.
 */
static void pm_evt_handler(pm_evt_t const * p_evt)
{
    pm_handler_on_pm_evt(p_evt);
    pm_handler_flash_clean(p_evt);

    switch (p_evt->evt_id)
    {
        case PM_EVT_PEERS_DELETE_SUCCEEDED:
            advertising_start(false);
            break;

        default:
            break;
    }
}


/**@brief Function for the Timer initialization.
 *
 * @details Initializes the timer module. This creates and starts application timers.
 */
static void timers_init(void)
{
    // Initialize timer module.
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    // Create timers.

    /* YOUR_JOB: Create any timers to be used by the application.
                 Below is an example of how to create a timer.
                 For every new timer needed, increase the value of the macro APP_TIMER_MAX_TIMERS by
                 one.
       ret_code_t err_code;
       err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
       APP_ERROR_CHECK(err_code); */
}


/**@brief Function for the GAP initialization.
 *
 * @details This function sets up all the necessary GAP (Generic Access Profile) parameters of the
 *          device including the device name, appearance, and the preferred connection parameters.
 */
static void gap_params_init(void)
{
    ret_code_t              err_code;
    ble_gap_conn_params_t   gap_conn_params;
    ble_gap_conn_sec_mode_t sec_mode;

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);

    err_code = sd_ble_gap_device_name_set(&sec_mode,
                                          (const uint8_t *)DEVICE_NAME,
                                          strlen(DEVICE_NAME));
    APP_ERROR_CHECK(err_code);

    /* YOUR_JOB: Use an appearance value matching the application's use case.
       err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_);
       APP_ERROR_CHECK(err_code); */

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

    gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
    gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
    gap_conn_params.slave_latency     = SLAVE_LATENCY;
    gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;

    err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
    APP_ERROR_CHECK(err_code);
}


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


/**@brief Function for handling Queued Write Module errors.
 *
 * @details A pointer to this function will be passed to each service which may need to inform the
 *          application about an error.
 *
 * @param[in]   nrf_error   Error code containing information about what went wrong.
 */
static void nrf_qwr_error_handler(uint32_t nrf_error)
{
    APP_ERROR_HANDLER(nrf_error);
}


/**@brief Function for handling the YYY Service events.
 * YOUR_JOB implement a service handler function depending on the event the service you are using can generate
 *
 * @details This function will be called for all YY Service events which are passed to
 *          the application.
 *
 * @param[in]   p_yy_service   YY Service structure.
 * @param[in]   p_evt          Event received from the YY Service.
 *
 *
static void on_yys_evt(ble_yy_service_t     * p_yy_service,
                       ble_yy_service_evt_t * p_evt)
{
    switch (p_evt->evt_type)
    {
        case BLE_YY_NAME_EVT_WRITE:
            APPL_LOG("[APPL]: charact written with value %s. ", p_evt->params.char_xx.value.p_str);
            break;

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

/**@brief Function for initializing services that will be used by the application.
 */
static void services_init(void)
{
    ret_code_t         err_code;
    nrf_ble_qwr_init_t qwr_init = {0};

    // Initialize Queued Write Module.
    qwr_init.error_handler = nrf_qwr_error_handler;

    err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
    APP_ERROR_CHECK(err_code);

    /* YOUR_JOB: Add code to initialize the services used by the application.
       ble_xxs_init_t                     xxs_init;
       ble_yys_init_t                     yys_init;

       // Initialize XXX Service.
       memset(&xxs_init, 0, sizeof(xxs_init));

       xxs_init.evt_handler                = NULL;
       xxs_init.is_xxx_notify_supported    = true;
       xxs_init.ble_xx_initial_value.level = 100;

       err_code = ble_bas_init(&m_xxs, &xxs_init);
       APP_ERROR_CHECK(err_code);

       // Initialize YYY Service.
       memset(&yys_init, 0, sizeof(yys_init));
       yys_init.evt_handler                  = on_yys_evt;
       yys_init.ble_yy_initial_value.counter = 0;

       err_code = ble_yy_service_init(&yys_init, &yy_init);
       APP_ERROR_CHECK(err_code);
     */
}


/**@brief Function for handling the Connection Parameters Module.
 *
 * @details This function will be called for all events in the Connection Parameters Module which
 *          are passed to the application.
 *          @note All this function does is to disconnect. This could have been done by simply
 *                setting the disconnect_on_fail config parameter, but instead we use the event
 *                handler mechanism to demonstrate its use.
 *
 * @param[in] p_evt  Event received from the Connection Parameters Module.
 */
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
    ret_code_t err_code;

    if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
    {
        err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
        APP_ERROR_CHECK(err_code);
    }
}


/**@brief Function for handling a Connection Parameters error.
 *
 * @param[in] nrf_error  Error code containing information about what went wrong.
 */
static void conn_params_error_handler(uint32_t nrf_error)
{
    APP_ERROR_HANDLER(nrf_error);
}


/**@brief Function for initializing the Connection Parameters module.
 */
static void conn_params_init(void)
{
    ret_code_t             err_code;
    ble_conn_params_init_t cp_init;

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

    cp_init.p_conn_params                  = NULL;
    cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
    cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
    cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
    cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
    cp_init.disconnect_on_fail             = false;
    cp_init.evt_handler                    = on_conn_params_evt;
    cp_init.error_handler                  = conn_params_error_handler;

    err_code = ble_conn_params_init(&cp_init);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for starting timers.
 */
static void application_timers_start(void)
{
    /* YOUR_JOB: Start your timers. below is an example of how to start a timer.
       ret_code_t err_code;
       err_code = app_timer_start(m_app_timer_id, TIMER_INTERVAL, NULL);
       APP_ERROR_CHECK(err_code); */

}


/**@brief Function for putting the chip into sleep mode.
 *
 * @note This function will not return.
 */
static void sleep_mode_enter(void)
{
    ret_code_t err_code;

    err_code = bsp_indication_set(BSP_INDICATE_IDLE);
    APP_ERROR_CHECK(err_code);

    // Prepare wakeup buttons.
    err_code = bsp_btn_ble_sleep_mode_prepare();
    APP_ERROR_CHECK(err_code);

    // Go to system-off mode (this function will not return; wakeup will cause a reset).
    err_code = sd_power_system_off();
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling advertising events.
 *
 * @details This function will be called for advertising events which are passed to the application.
 *
 * @param[in] ble_adv_evt  Advertising event.
 */
static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
    ret_code_t err_code;

    switch (ble_adv_evt)
    {
        case BLE_ADV_EVT_FAST:
            NRF_LOG_INFO("Fast advertising.");
            err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
            APP_ERROR_CHECK(err_code);
            break;

        case BLE_ADV_EVT_IDLE:
            m_manuf_data[3]++;
            NRF_LOG_INFO("Adv Tmo: Conn Hdl %04X; Adv Hdl %d; %c%c%c%02X", m_conn_handle, m_advertising.adv_handle, m_manuf_data[0], m_manuf_data[1], m_manuf_data[2], m_manuf_data[3] );
            //sleep_mode_enter();

            // Update the Advertising Data to use the latest Manufacturer-Specific Data
            err_code = ble_advdata_encode( &m_advdata, m_advertising.enc_advdata, &m_advertising.adv_data.adv_data.len );
            APP_ERROR_CHECK(err_code);

            if( m_conn_handle == BLE_CONN_HANDLE_INVALID )
            {   // No connection
                advertising_start(false);
            }
            else
            {   // Connected
                non_connectable_advertising_start();
            }
            break;

        default:
            NRF_LOG_INFO("Adv Ev %d", ble_adv_evt);
            break;
    }
}


/**@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 = NRF_SUCCESS;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_DISCONNECTED:
            NRF_LOG_INFO("GAP Disc");
            // LED indication will be changed when advertising starts.

            m_conn_handle = BLE_CONN_HANDLE_INVALID; 

            // Stop the Non-connectable advertsing
            err_code = sd_ble_gap_adv_stop(m_advertising.adv_handle);
            APP_ERROR_CHECK(err_code);

            // Restart the Connectable advertising
            advertising_start(false);
            break;

        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_INFO("GAP Conn");
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
            APP_ERROR_CHECK(err_code);

            non_connectable_advertising_start();
            break;

        case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
        {
            NRF_LOG_DEBUG("PHY upd req");
            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_GAP_EVT_ADV_SET_TERMINATED:
            NRF_LOG_INFO("ADV_SET_TERM.");
            break;
                    
        case BLE_GATTC_EVT_TIMEOUT:
            // Disconnect on GATT Client timeout event.
            NRF_LOG_DEBUG("GATT C Tmo");
            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 S Tmo");
            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;

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


/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupt.
 */
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);
}


/**@brief Function for the Peer Manager initialization.
 */
static void peer_manager_init(void)
{
    ble_gap_sec_params_t sec_param;
    ret_code_t           err_code;

    err_code = pm_init();
    APP_ERROR_CHECK(err_code);

    memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));

    // Security parameters to be used for all security procedures.
    sec_param.bond           = SEC_PARAM_BOND;
    sec_param.mitm           = SEC_PARAM_MITM;
    sec_param.lesc           = SEC_PARAM_LESC;
    sec_param.keypress       = SEC_PARAM_KEYPRESS;
    sec_param.io_caps        = SEC_PARAM_IO_CAPABILITIES;
    sec_param.oob            = SEC_PARAM_OOB;
    sec_param.min_key_size   = SEC_PARAM_MIN_KEY_SIZE;
    sec_param.max_key_size   = SEC_PARAM_MAX_KEY_SIZE;
    sec_param.kdist_own.enc  = 1;
    sec_param.kdist_own.id   = 1;
    sec_param.kdist_peer.enc = 1;
    sec_param.kdist_peer.id  = 1;

    err_code = pm_sec_params_set(&sec_param);
    APP_ERROR_CHECK(err_code);

    err_code = pm_register(pm_evt_handler);
    APP_ERROR_CHECK(err_code);
}


/**@brief Clear bond information from persistent storage.
 */
static void delete_bonds(void)
{
    ret_code_t err_code;

    NRF_LOG_INFO("Erase bonds!");

    err_code = pm_peers_delete();
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for handling events from the BSP module.
 *
 * @param[in]   event   Event generated when button is pressed.
 */
static void bsp_event_handler(bsp_event_t event)
{
    ret_code_t err_code;

    switch (event)
    {
        case BSP_EVENT_SLEEP:
            sleep_mode_enter();
            break; // BSP_EVENT_SLEEP

        case BSP_EVENT_DISCONNECT:
            err_code = sd_ble_gap_disconnect(m_conn_handle,
                                             BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break; // BSP_EVENT_DISCONNECT

        case BSP_EVENT_WHITELIST_OFF:
            if (m_conn_handle == BLE_CONN_HANDLE_INVALID)
            {
                err_code = ble_advertising_restart_without_whitelist(&m_advertising);
                if (err_code != NRF_ERROR_INVALID_STATE)
                {
                    APP_ERROR_CHECK(err_code);
                }
            }
            break; // BSP_EVENT_KEY_0

        default:
            break;
    }
}


/**@brief Function for initializing the Advertising functionality.
 */
static void advertising_init(void)
{
    ret_code_t             err_code;
    ble_advertising_init_t init;

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

    // Set up the Manufacturer-Specific metadata
    m_manuf_metadata.company_identifier = BT_COMPANY_ID;
    m_manuf_metadata.data.p_data        = m_manuf_data;
    m_manuf_metadata.data.size          = MANUF_DATA_LEN;

    // Set up the Advertising Data
    m_advdata.name_type               = BLE_ADVDATA_FULL_NAME;

    m_advdata.include_appearance      = false;   // Don't want this - we have no standard "appearance"

    m_advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    m_advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
    m_advdata.uuids_complete.p_uuids  = m_adv_uuids;

    m_advdata.p_manuf_specific_data   =  &m_manuf_metadata;

    // Copy it into the initialisation structure
    init.advdata = m_advdata;

    // Set up the rest of the Advertising configuration
    init.config.ble_adv_fast_enabled  = true;
    init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
    init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;

    init.config.ble_adv_on_disconnect_disabled = true;  // Do NOT have the advertising module autonomously restart advertising on disconnect

    init.evt_handler = on_adv_evt;

    err_code = ble_advertising_init(&m_advertising, &init);
    APP_ERROR_CHECK(err_code);

    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}


/**@brief Function for initializing buttons and leds.
 *
 * @param[out] p_erase_bonds  Will be true if the clear bonding button was pressed to wake the application up.
 */
static void buttons_leds_init(bool * p_erase_bonds)
{
    ret_code_t err_code;
    bsp_event_t startup_event;

    err_code = bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
    APP_ERROR_CHECK(err_code);

    err_code = bsp_btn_ble_init(NULL, &startup_event);
    APP_ERROR_CHECK(err_code);

    *p_erase_bonds = (startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
}


/**@brief Function for initializing the nrf 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 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 If there is no pending log operation, then sleep until next the next event occurs.
 */
static void idle_state_handle(void)
{
    if (NRF_LOG_PROCESS() == false)
    {
        nrf_pwr_mgmt_run();
    }
}


/**@brief Function for starting connectable advertising.
 */
static void advertising_start(bool erase_bonds)
{
    NRF_LOG_INFO( "Connectable advertising" );

    if (erase_bonds == true)
    {
        delete_bonds();
        // Advertising is started by PM_EVT_PEERS_DELETED_SUCEEDED event
    }
    else
    {
        ret_code_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);

        APP_ERROR_CHECK(err_code);
    }
}


/**@brief Function for starting Non-connectable advertising.
 */
static void non_connectable_advertising_start(void)
{
    uint32_t    err_code;
    ble_gap_adv_params_t  adv_params;

    memcpy(&adv_params, &m_advertising.adv_params, sizeof(adv_params));

#if 0
    NRF_LOG_INFO( "Non-connectable, Scannable advertising" );
    adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED;
#else
    NRF_LOG_INFO( "Non-connectable, Non-scannable advertising; Handle %d", m_advertising.adv_handle );
    adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
#endif

    err_code = sd_ble_gap_adv_set_configure(&m_advertising.adv_handle,
                                            &m_advertising.adv_data,
                                            &adv_params);
    APP_ERROR_CHECK(err_code);


    err_code = sd_ble_gap_adv_start(m_advertising.adv_handle, m_advertising.conn_cfg_tag);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for application main entry.
 */
int main(void)
{
    bool erase_bonds;

    // Initialize.
    log_init();
    timers_init();
    buttons_leds_init(&erase_bonds);
    power_management_init();
    ble_stack_init();
    gap_params_init();
    gatt_init();
    advertising_init();
    services_init();
    conn_params_init();
    peer_manager_init();

    // Start execution.
    NRF_LOG_INFO("Hacked Template example started.");
    application_timers_start();

    advertising_start(erase_bonds);

    // Enter main loop.
    for (;;)
    {
        idle_state_handle();
    }
}


/**
 * @}
 */

  • None of the parameters should have changed

    Oh - if only it were that simple!

    But no - the Advertising module has to mess with the settings!

    Thanks to  - who did a write-up on some of this:

    https://devzone.nordicsemi.com/f/nordic-q-a/37233/ble_advertising_init-returning-nrf_error_invalid_param-for-parameters-set-by-the-function-itself/145212#145212

    This pointed me to the fact that the Advertising Interval gets trashed:

    <info> app: Connectable advertising
    <info> app: Fast advertising.
    <debug> app: ADV: GAP Conn
    <info> app: GAP Conn
    <info> app: Non-connectable, Non-scannable advertising; Handle 0; interval 300
    <debug> app: ADV: Conn Parm Upd
    <debug> app: ADV: Term
    <info> app: Adv Tmo: Conn Hdl 0000; Adv Hdl 0; Mnf01
    <info> app: Non-connectable, Non-scannable advertising; Handle 0; interval 0
    <error> app: ERROR 7 [NRF_ERROR_INVALID_PARAM] at main.c:790
    PC at: 0x000313A1
    <error> app: End of error report
    

    Fixing that, the Non-connectable advertising would re-start once - but then never time out.

    Which suggested that the Advertising Duration was also getting trashed:

    <info> app: Connectable advertising
    <info> app: Fast advertising.
    <debug> app: ADV: GAP Conn
    <info> app: GAP Conn
    <info> app: Non-connectable, Non-scannable advertising: Hdl 0; Int 300; Dur 3000
    <debug> app: ADV: Conn Parm Upd
    <debug> app: ADV: Term
    <info> app: Adv Tmo: Conn Hdl 0000; Adv Hdl 0; Mnf01
    <info> app: Non-connectable, Non-scannable advertising: Hdl 0; Int 0; Dur 0
    <error> app: ERROR 7 [NRF_ERROR_INVALID_PARAM] at main.c:781
    PC at: 0x00031397
    <error> app: End of error report
    

    But fixing that just led to a different error:

    <info> app: Connectable advertising
    <info> app: Fast advertising.
    <debug> app: ADV: GAP Conn
    <info> app: GAP Conn
    <info> app: Non-connectable, Non-scannable advertising: Hdl 0; Int 300; Dur 3000
    <debug> app: ADV: Conn Parm Upd
    <debug> app: ADV: Term
    <info> app: Adv Tmo: Conn Hdl 0000; Adv Hdl 0; Mnf01
    <info> app: Non-connectable, Non-scannable advertising: Hdl 0; Int 0; Dur 0
    <debug> app: ADV_SET_TERMINATED: start result 0
    <info> app: ADV_SET_TERM.
    <debug> app: ADV: Term
    <debug> app: ADV_SET_TERMINATED: start result 18
    <info> app: ADV_SET_TERM.
    

    The "ADV_SET_TERMINATED: start result 18" comes from on_terminated() in ble_advertising.c:

    static void on_terminated(ble_advertising_t * const p_advertising, ble_evt_t const * p_ble_evt)
    {
        ret_code_t ret;
    
        if (p_ble_evt->header.evt_id != BLE_GAP_EVT_ADV_SET_TERMINATED)
        {
            // Nothing to do.
            NRF_LOG_DEBUG( "!ADV_SET_TERMINATED" );
            return;
        }
    
        if (  p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_TIMEOUT
            ||p_ble_evt->evt.gap_evt.params.adv_set_terminated.reason == BLE_GAP_EVT_ADV_SET_TERMINATED_REASON_LIMIT_REACHED)
        {
            // Start advertising in the next mode.
            ret = ble_advertising_start(p_advertising, adv_mode_next_get(p_advertising->adv_mode_current));
    
            NRF_LOG_DEBUG( "ADV_SET_TERMINATED: start result %d", ret );   // <<<< HERE !!
    
            if ((ret != NRF_SUCCESS) && (p_advertising->error_handler != NULL))
            {
                p_advertising->error_handler(ret);
            }
        }
    }

    The 18 is NRF_ERROR_CONN_COUNT: so the Advertising module has trashed the setting to do Non-connectable advertising - and, instead, is trying to start Connectable advertising!!

    So my final solution is:

    static void non_connectable_advertising_start(void)
    {
        uint32_t    err_code;
        ble_gap_adv_params_t  adv_params;
    
        memcpy(&adv_params, &m_advertising.adv_params, sizeof(adv_params));
    
    #if 0
        NRF_LOG_INFO( "Non-connectable, Scannable advertising" );
        adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED;
    #else
        NRF_LOG_INFO( "Non-connectable, Non-scannable advertising: Hdl %d; Int %d; Dur %d", m_advertising.adv_handle, m_advertising.adv_params.interval, m_advertising.adv_params.duration );
        adv_params.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_NONSCANNABLE_UNDIRECTED;
    #endif
    
        // Restore parameters which get trashed by the Advertising module
        adv_params.duration = APP_ADV_DURATION;
        adv_params.interval = APP_ADV_INTERVAL;
    
        // Don't let the Advertising module think it's got somewhere else in its advertising sequence
        m_advertising.adv_mode_current = BLE_ADV_MODE_FAST;
    
        err_code = sd_ble_gap_adv_set_configure(&m_advertising.adv_handle,
                                                &m_advertising.adv_data,
                                                &adv_params);
        APP_ERROR_CHECK(err_code);
    
        err_code = sd_ble_gap_adv_start(m_advertising.adv_handle, m_advertising.conn_cfg_tag);
        APP_ERROR_CHECK(err_code);
    }

  • my final solution

    I guess the real solution is to just not use the Advertising module at all.

    It's fine when it works but, when you want to do something a little different, it's an absolute pain in the behind!

    Disappointed

    The trouble is, there are now no examples of how to do advertising "manually", and the Advertising module obfuscates it all so much that it's hard to pull the relevant details out.

    Disappointed

  • Hi,

    Thank you for the feedback.

    The trouble is, there are now no examples of how to do advertising "manually", and the Advertising module obfuscates it all so much that it's hard to pull the relevant details out.

    The ble_app_beacon and ble_app_blinky examples does not use the advertising module.

  • Hi Awneil,
    I'm also getting NRF_ERROR_INVALID_PARAM after using your fix. Did you end up making any changes to ble_advertising.c? What advertising interval APP_ADV_INTERVAL did you use?

Related