Persistent Failure in ble_stack_init() with sd_softdevice_enable (Error 8194) on nRF52840 (PCA10056, S140)

Hello Nordic community,

I’m working on a project with the nRF52840 DK (PCA10056) using the nRF5 SDK and the S140 SoftDevice.

My goal is to:

1. Read two NTC thermistors using SAADC (P0.30/AIN6 and P0.31/AIN7) every 100 ms.
2. Control an NTC enable pin (NTC_EN) on P0.29 to power the NTC circuit.
3. Toggle an LED (P0.24) every 200 ms.
4. Set a reset pin (P0.13) high after 10 seconds and stop NTC sampling.
5. Advertise via BLE


Issue:

The ble_stack_init() function consistently fails at sd_softdevice_enable(), returning error code 8194 (0x2002). I’ve confirmed the SoftDevice is flashed.

Here’s the relevant log:

<info> app: NTC SAADC with LED and Reset Control
<error> app: SoftDevice enable failed: 8194 

#include <stdint.h>
#include "nrf.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
#include "nrf_drv_saadc.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "nrf_sdh.h"
#include "nrf_sdh_ble.h"
#include "ble.h"
#include "ble_advdata.h"
#include "ble_conn_params.h"
#include "app_timer.h"
#include "app_error.h"

// Pin definitions (nRF52840)
#define LED_PIN 24        // P0.24 for LED
#define SR_RESET_PIN 13   // P0.13 for reset (pin 33)
#define NTC_EN 29        // P0.20 for NTC enable
#define NTC1_AIN NRF_SAADC_INPUT_AIN6  // P0.30 (pin 10)
#define NTC2_AIN NRF_SAADC_INPUT_AIN7  // P0.31 (pin 9)

// SAADC configuration
#define SAADC_BUFFER_SIZE 2            // One sample per channel
#define SAADC_SAMPLE_INTERVAL_MS 100   // Sample every 100 ms (10 Hz)
#define LED_INTERVAL_MS 200            // LED toggle every 200 ms
#define RESET_TIMEOUT_MS 10000         // Reset/NTC disable after 10 s

// BLE configuration
#define DEVICE_NAME "ProximityDevice"
#define APP_ADV_INTERVAL 300           // Advertising interval (300 * 0.625 ms = 187.5 ms)
#define APP_ADV_TIMEOUT_IN_SECONDS 0   // No timeout (advertise forever)
#define MIN_CONN_INTERVAL MSEC_TO_UNITS(100, UNIT_1_25_MS)
#define MAX_CONN_INTERVAL MSEC_TO_UNITS(200, UNIT_1_25_MS)
#define SLAVE_LATENCY 0
#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS)
#define APP_BLE_CONN_CFG_TAG 1         // BLE connection configuration tag
#define APP_BLE_OBSERVER_PRIO 3        // BLE observer priority

// Global variables
static nrf_saadc_value_t saadc_buffer_1[SAADC_BUFFER_SIZE];  // Single buffer
static bool ntc_sampling_enabled = true;                     // SAADC sampling state
static uint32_t number_counter = 0;                          // ADC reading counter
static uint16_t m_conn_handle = BLE_CONN_HANDLE_INVALID;     // BLE connection handle
static uint8_t m_adv_handle = BLE_GAP_ADV_SET_HANDLE_NOT_SET; // Advertising handle
static ble_gap_adv_data_t m_adv_data;                        // Advertising data
static uint8_t m_enc_advdata[BLE_GAP_ADV_SET_DATA_SIZE_MAX]; // Encoded advertising data

/**@brief SAADC event handler.
 */
static void saadc_event_handler(nrf_drv_saadc_evt_t const * p_event)
{
    ret_code_t err_code;
    if (p_event->type == NRF_DRV_SAADC_EVT_DONE && ntc_sampling_enabled)
    {
        number_counter++;
        nrf_saadc_value_t ntc1_value = p_event->data.done.p_buffer[0];
        nrf_saadc_value_t ntc2_value = p_event->data.done.p_buffer[1];
        NRF_LOG_INFO("Reading #%lu: NTC1 (P0.30): %d, NTC2 (P0.31): %d",
                     number_counter, ntc1_value, ntc2_value);

        // Prepare the same buffer again
        nrf_saadc_buffer_init(saadc_buffer_1, SAADC_BUFFER_SIZE);
    }
    else
    {
        NRF_LOG_WARNING("Unexpected SAADC event: %d", p_event->type);
    }
}

/**@brief Initialize GPIO pins.
 */
static void gpio_init(void)
{
    nrf_gpio_cfg(LED_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT,
                 NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    nrf_gpio_cfg(SR_RESET_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT,
                 NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    nrf_gpio_cfg(NTC_EN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT,
                 NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    nrf_gpio_pin_clear(SR_RESET_PIN);
    nrf_gpio_pin_set(NTC_EN);
    nrf_delay_ms(10); // Allow pin state to settle
    NRF_LOG_INFO("NTC_EN pin state after set: %d", nrf_gpio_pin_read(NTC_EN));
    if (!nrf_gpio_pin_read(NTC_EN)) {
        NRF_LOG_WARNING("NTC_EN failed to set high, possible hardware issue");
        nrf_gpio_pin_set(NTC_EN); // Retry setting
        nrf_delay_ms(10);
        NRF_LOG_INFO("NTC_EN pin state after retry: %d", nrf_gpio_pin_read(NTC_EN));
    }
    NRF_LOG_INFO("GPIO PORT OUT: 0x%08x", NRF_GPIO->OUT);
}

/**@brief Initialize SAADC.
 */
static void saadc_init(void)
{
    ret_code_t err_code;

    // Initialize logging
    err_code = NRF_LOG_INIT(NULL);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_DEFAULT_BACKENDS_INIT();
    NRF_LOG_INFO("NTC SAADC with LED and Reset Control");

    // Initialize SAADC
    nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG;
    saadc_config.resolution = NRF_SAADC_RESOLUTION_10BIT;
    saadc_config.oversample = NRF_SAADC_OVERSAMPLE_DISABLED;
    NRF_LOG_INFO("Initializing SAADC with interrupt priority: %d", saadc_config.interrupt_priority);
    err_code = nrf_drv_saadc_init(&saadc_config, saadc_event_handler);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_INFO("SAADC initialized successfully");

    // Configure two channels
    nrf_saadc_channel_config_t channel_config_1 = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NTC1_AIN);
    nrf_saadc_channel_config_t channel_config_2 = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NTC2_AIN);
    NRF_LOG_INFO("Initializing SAADC channel 0");
    err_code = nrf_drv_saadc_channel_init(0, &channel_config_1);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_INFO("Initializing SAADC channel 1");
    err_code = nrf_drv_saadc_channel_init(1, &channel_config_2);
    APP_ERROR_CHECK(err_code);

    // Set up a single buffer
    NRF_LOG_INFO("Setting up SAADC buffer 1 at address: 0x%08x", (uint32_t)saadc_buffer_1);
    nrf_saadc_buffer_init(saadc_buffer_1, SAADC_BUFFER_SIZE);
}

/**@brief BLE event handler.
 */
static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            NRF_LOG_INFO("BLE connected");
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            NRF_LOG_INFO("BLE disconnected");
            m_conn_handle = BLE_CONN_HANDLE_INVALID;
            break;

        default:
            break;
    }
}

/**@brief Initialize BLE stack.
 */
static void ble_stack_init(void)
{
    ret_code_t err_code;

    // Initialize the SoftDevice
    err_code = nrf_sdh_enable_request();
    APP_ERROR_CHECK(err_code);

    // Configure the BLE stack
    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 BLE event handler
    NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
}

/**@brief Initialize advertising.
 */
static void advertising_init(void)
{
    ret_code_t err_code;
    ble_advdata_t advdata = {0};
    ble_uuid_t adv_uuids[] = {{0xFFFF, BLE_UUID_TYPE_BLE}};

    // Initialize advertising buffer
    m_adv_data.adv_data.p_data = m_enc_advdata;
    m_adv_data.adv_data.len = BLE_GAP_ADV_SET_DATA_SIZE_MAX;

    // Build advertising data
    advdata.name_type = BLE_ADVDATA_FULL_NAME;
    advdata.include_appearance = false;
    advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    advdata.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
    advdata.uuids_complete.p_uuids = adv_uuids;

    err_code = ble_advdata_encode(&advdata, m_adv_data.adv_data.p_data, &m_adv_data.adv_data.len);
    APP_ERROR_CHECK(err_code);

    ble_gap_adv_params_t adv_params = {0};
    adv_params.properties.type = BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED;
    adv_params.interval = APP_ADV_INTERVAL;
    adv_params.duration = APP_ADV_TIMEOUT_IN_SECONDS;
    adv_params.p_peer_addr = NULL;
    adv_params.filter_policy = BLE_GAP_ADV_FP_ANY;
    adv_params.primary_phy = BLE_GAP_PHY_1MBPS;

    err_code = sd_ble_gap_adv_set_configure(&m_adv_handle, &m_adv_data, &adv_params);
    APP_ERROR_CHECK(err_code);
}

/**@brief Start advertising.
 */
static void advertising_start(void)
{
    ret_code_t err_code = sd_ble_gap_adv_start(m_adv_handle, APP_BLE_CONN_CFG_TAG);
    APP_ERROR_CHECK(err_code);
    NRF_LOG_INFO("Advertising started");
}

/**@brief Connection parameters event handler.
 */
static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
{
    if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
    {
        ret_code_t err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
        APP_ERROR_CHECK(err_code);
    }
}

/**@brief Connection parameters error handler.
 */
static void conn_params_error_handler(uint32_t nrf_error)
{
    APP_ERROR_HANDLER(nrf_error);
}

/**@brief Initialize connection parameters.
 */
static void conn_params_init(void)
{
    ret_code_t err_code;
    ble_conn_params_init_t cp_init = {0};

    cp_init.p_conn_params = NULL;
    cp_init.first_conn_params_update_delay = APP_TIMER_TICKS(5000);
    cp_init.next_conn_params_update_delay = APP_TIMER_TICKS(30000);
    cp_init.max_conn_params_update_count = 3;
    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 Main function.
 */
int main(void)
{
    // Initialize GPIO
    gpio_init();
        saadc_init();
    // Initialize BLE stack
    ble_stack_init();

    // Initialize app timer
    ret_code_t err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    // Initialize BLE advertising and connection parameters
    advertising_init();
    conn_params_init();

    // Start advertising
    advertising_start();

    // Initialize SAADC


    // Initialize counters
    uint32_t reset_counter = 0;
    uint32_t led_counter = 0;
    uint32_t saadc_counter = 0;

    const uint32_t reset_max_count = RESET_TIMEOUT_MS / 100; // 10 s / 100 ms
    const uint32_t led_interval = LED_INTERVAL_MS / 100;     // 200 ms / 100 ms
    const uint32_t saadc_interval = SAADC_SAMPLE_INTERVAL_MS / 100; // 100 ms / 100 ms

    while (1)
    {
        // Increment counters
        reset_counter++;
        led_counter++;
        saadc_counter++;

        // Toggle LED every 200 ms
        if (led_counter >= led_interval)
        {
            nrf_gpio_pin_toggle(LED_PIN);
            led_counter = 0;
        }

        // Sample ADC every 100 ms if enabled
        if (ntc_sampling_enabled && saadc_counter >= saadc_interval)
        {
            NRF_LOG_INFO("Sampling condition met: saadc_counter=%d, saadc_interval=%d", saadc_counter, saadc_interval);
            NRF_LOG_INFO("SAADC enable state before sampling: %d", NRF_SAADC->ENABLE);
            NRF_LOG_INFO("SAADC status: 0x%08x", NRF_SAADC->STATUS);
            NRF_LOG_INFO("SAADC events - START: %d, END: %d", NRF_SAADC->EVENTS_STARTED, NRF_SAADC->EVENTS_END);
            // Clear events
            NRF_SAADC->EVENTS_END = 0;
            NRF_SAADC->EVENTS_STARTED = 0;
            // Manually start SAADC
            NRF_SAADC->TASKS_START = 1;
            ret_code_t err_code = nrf_drv_saadc_sample();
            if (err_code != NRF_SUCCESS)
            {
                NRF_LOG_ERROR("SAADC sample failed: %d", err_code);
            }
            saadc_counter = 0;
        }

        // After 10 seconds, update pins and stop sampling
        if (reset_counter >= reset_max_count)
        {
            nrf_gpio_pin_set(SR_RESET_PIN);
            nrf_gpio_pin_clear(NTC_EN);
            ntc_sampling_enabled = false;
            reset_counter = reset_max_count; // Stop counter
        }

        // Process logs
        while (NRF_LOG_PROCESS() != NRF_SUCCESS);

        // Wait for 100 ms (SoftDevice compatible)
        nrf_delay_ms(100);
    }
}

Related