High current at BLE Idle - aroudn 1.6mA

I will attach my main.c and skd_conifg, maybe i'm overseeing something in code. Also i will attach schematics and pcb. I'm aware of RGB led consumption, in reality it's around 0.4mA hence when i turn it of i'm getting the 1.6mA. The wireless charging ic was removed. 

dev_pcb.zip

/****************************************************************************************
 *  nRF52820 - S112 SoftDevice                                                          *
 *  Vibration - RGB - Voltage-Alert BLE Demo                                           *
 *                                                                                     *
 *  23-Apr-2025                                                                        *
 *  . Switched COMP from 3 ms polling to interrupt-driven edge detection.              *
 *  . Removed RTC sampling timer, m_do_sample flag and related code.                   *
 *  . COMP "UP" event restarts 800 ms latch watchdog; timeout clears the alert.        *
 *  . Saves 250 uA when idle.                                                         *
 ****************************************************************************************/

#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 "nrf_sdh.h"
#include "nrf_sdh_soc.h"
#include "nrf_sdh_ble.h"
#include "nrf_ble_gatt.h"

#include "nrf_delay.h"
#include "SEGGER_RTT.h"

#include "nrf_gpio.h"
#include "nrfx_comp.h"
#include "app_timer.h"
#include "nrf_error.h"
#include "nrfx_spim.h"

/* -------------------------------------------------------------------------- */
/*  Conditional debug output                                                  */
/* -------------------------------------------------------------------------- */
#if defined(NRF_LOG_ENABLED) && NRF_LOG_ENABLED
#   define DBG_PUTS(s)      SEGGER_RTT_WriteString(0, (s))
#   define DBG_PRINTF(...)  SEGGER_RTT_printf(0, __VA_ARGS__)
#else
#   define DBG_PUTS(s)      do { } while (0)
#   define DBG_PRINTF(...)  do { } while (0)
#endif

/* ---------- QUICK-FIX FOR OLD SDKs ---------------------------------------- */
#ifndef NRF_ERROR_ALREADY
#define NRF_ERROR_ALREADY  (NRF_ERROR_BASE_NUM + 0x018)
#endif
/* -------------------------------------------------------------------------- */

#define APP_BLE_CONN_CFG_TAG            1
#define DEVICE_NAME                     "Oulffa DB1/2025"

/* --- GPIO ----------------------------------------------------------------- */
#define VIBRATION_PIN                   NRF_GPIO_PIN_MAP(0,30)
#define WS2812B_PIN                     29

/* --- BLE timing ----------------------------------------------------------- */
#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(1000, UNIT_1_25_MS)
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(1800, UNIT_1_25_MS)
#define SLAVE_LATENCY                   4
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(24000, UNIT_10_MS)

/* --- Service / characteristic UUIDs -------------------------------------- */
#define GPIO_SERVICE_UUID_TYPE          BLE_UUID_TYPE_BLE
#define GPIO_SERVICE_UUID               0x1523
#define VIBRATION_CHAR_UUID             0x1524
#define RGB_CHAR_UUID                   0x1525
#define VOLTAGE_ALERT_CHAR_UUID         0x1526

/* --- Comparator threshold ------------------------------------------------- */
#define THRESHOLD_MV                    150.0f
#define INTERNAL_REF_MV                 1000.0f

/* --- Voltage-latch timing ------------------------------------------------- */
#define VOLTAGE_TIMEOUT_MS              800

/* --- BLE / advertising ---------------------------------------------------- */
NRF_BLE_GATT_DEF(m_gatt);
BLE_ADVERTISING_DEF(m_advertising);

/* --- Voltage-timeout timer ----------------------------------------------- */
APP_TIMER_DEF(m_voltage_timer_id);

/* -------------------------------------------------------------------------- */
/*  WS2812 via SPIM0                                                          */
/* -------------------------------------------------------------------------- */
#define WS_MOSI_PIN   29
#define WS_SCK_PIN    28

static const nrfx_spim_t WS_SPI = NRFX_SPIM_INSTANCE(0);

/* 5-bit symbol encoding helpers (4 MHz clock -> 250 ns/bit)                   */
static void encode_byte(uint8_t byte, uint8_t *dst)
{
    uint8_t out[5] = {0};
    int bitpos = 0;
    for (int bit = 7; bit >= 0; --bit)
    {
        uint8_t pat = (byte & (1 << bit)) ? 0b11100 : 0b11000;   /* 1 or 0 */
        for (int k = 4; k >= 0; --k)
        {
            if (pat & (1 << k))
                out[bitpos >> 3] |= 1 << (7 - (bitpos & 7));
            bitpos++;
        }
    }
    memcpy(dst, out, 5);
}

static uint8_t txbuf[25];   /* 15 B data + 10 B reset */

static void ws2812_send_color(uint8_t r,
                              uint8_t g,
                              uint8_t b,
                              uint8_t br)          /* 0-255 brightness */
{
    /* ---------- 1.  Apply global brightness & build the frame -------- */
    r = (uint16_t) r * br / 255;
    g = (uint16_t) g * br / 255;
    b = (uint16_t) b * br / 255;

    encode_byte(g, &txbuf[0]);      /*  0- 4 */
    encode_byte(r, &txbuf[5]);      /*  5- 9 */
    encode_byte(b, &txbuf[10]);     /* 10-14 */
    memset(&txbuf[15], 0x00, 25);   /* >= 50 us "reset" idle */

    /* ---------- 2.  Dynamically init SPIM0 --------------------------- */
    static const nrfx_spim_t WS_SPI = NRFX_SPIM_INSTANCE(0);

    nrfx_spim_config_t cfg = NRFX_SPIM_DEFAULT_CONFIG;
    cfg.sck_pin      = WS_SCK_PIN;
    cfg.mosi_pin     = WS_MOSI_PIN;
    cfg.miso_pin     = NRFX_SPIM_PIN_NOT_USED;
    cfg.ss_pin       = NRFX_SPIM_PIN_NOT_USED;
    cfg.frequency    = NRF_SPIM_FREQ_4M;
    cfg.mode         = NRF_SPIM_MODE_1;
    cfg.bit_order    = NRF_SPIM_BIT_ORDER_MSB_FIRST;
    cfg.irq_priority = 6;
    APP_ERROR_CHECK(nrfx_spim_init(&WS_SPI, &cfg, NULL, NULL));

    /* ---------- 3.  Transmit buffer ---------------------------------- */
    nrfx_spim_xfer_desc_t x =
    {
        .p_tx_buffer = txbuf,
        .tx_length   = sizeof(txbuf)
    };
    APP_ERROR_CHECK(nrfx_spim_xfer(&WS_SPI, &x, 0));

    /* ---------- 4.  >= 60 us reset-latch ------------------------------ */
    nrf_delay_us(60);

    /* ---------- 5.  Power-down SPIM0 -------------------------------- */
    nrfx_spim_uninit(&WS_SPI);
}

/* -------------------------------------------------------------------------- */
/*  GATT service struct                                                       */
/* -------------------------------------------------------------------------- */
typedef struct
{
    uint16_t                 service_handle;
    ble_gatts_char_handles_t vibration_char_handles;
    ble_gatts_char_handles_t rgb_char_handles;
    ble_gatts_char_handles_t voltage_alert_char_handles;
} ble_gpio_service_t;

static ble_gpio_service_t  m_gpio_service;
static ble_uuid_t          m_adv_uuids[] = {{GPIO_SERVICE_UUID, GPIO_SERVICE_UUID_TYPE}};
static uint16_t            m_conn_handle = BLE_CONN_HANDLE_INVALID;
static bool                m_voltage_alert_enabled = false;
static bool                m_voltage_active        = false;

/* -------------------------------------------------------------------------- */
/*  Support / error handling                                                  */
/* -------------------------------------------------------------------------- */
void assert_nrf_callback(uint16_t line, const uint8_t *file)
{
    app_error_handler(0xDEADBEEF, line, file);
}

/* -------------------------------------------------------------------------- */
/*  Vibration helpers                                                         */
/* -------------------------------------------------------------------------- */
static void vibration_init(void)
{
    DBG_PUTS("Init vibration pin\r\n");
    nrf_gpio_cfg_output(VIBRATION_PIN);
    nrf_gpio_pin_clear(VIBRATION_PIN);
    nrf_gpio_pin_set(VIBRATION_PIN);
    nrf_delay_ms(500);
    nrf_gpio_pin_clear(VIBRATION_PIN);
}

static void vibrate(uint8_t intensity, uint32_t ms)
{
    if (!intensity) { nrf_gpio_pin_clear(VIBRATION_PIN); return; }
    if (intensity >= 100)
    {
        nrf_gpio_pin_set(VIBRATION_PIN);
        nrf_delay_ms(ms);
        nrf_gpio_pin_clear(VIBRATION_PIN);
        return;
    }
    uint32_t cyc = 10000, on = (cyc * intensity) / 100, off = cyc - on,
             rep = ms / (cyc / 1000);
    for (uint32_t i = 0; i < rep; i++)
    {
        nrf_gpio_pin_set(VIBRATION_PIN);
        nrf_delay_us(on);
        nrf_gpio_pin_clear(VIBRATION_PIN);
        nrf_delay_us(off);
    }
}
static void vibrate_pattern_single(uint8_t i) { vibrate(i, 200); }
static void vibrate_pattern_double(uint8_t i)
{
    vibrate(i, 100); nrf_delay_ms(100); vibrate(i, 100);
}
static void vibrate_pattern_long(uint8_t i)   { vibrate(i, 500); }
static void vibrate_pattern_escalating(void)
{
    for (uint8_t i = 20; i <= 100; i += 20)
    { vibrate(i, 200); nrf_delay_ms(50); }
}

/* -------------------------------------------------------------------------- */
/*  Comparator                                                                */
/* -------------------------------------------------------------------------- */
static void send_voltage_alert(bool high);

static void voltage_timeout_handler(void *p_context)
{
    (void)p_context;
    if (m_voltage_active)
    {
        m_voltage_active = false;
        send_voltage_alert(false);
    }
}

static void comp_evt_handler(nrf_comp_event_t event)
{
    if (event == NRF_COMP_EVENT_UP)
    {
        if (!m_voltage_active)
        {
            m_voltage_active = true;
            send_voltage_alert(true);
        }

        ret_code_t err = app_timer_start(m_voltage_timer_id,
                                         APP_TIMER_TICKS(VOLTAGE_TIMEOUT_MS),
                                         NULL);
        if (err != NRF_SUCCESS &&
            err != NRF_ERROR_INVALID_STATE &&
            err != NRF_ERROR_ALREADY)
        {
            APP_ERROR_CHECK(err);
        }
    }
}


static void comp_init(void)
{
    nrfx_comp_config_t config = NRFX_COMP_DEFAULT_CONFIG(NRF_COMP_INPUT_0);
    config.threshold.th_up = config.threshold.th_down =
        (uint8_t)((THRESHOLD_MV / INTERNAL_REF_MV) * 63.0f);
    config.input = NRF_COMP_INPUT_0;



    ret_code_t err = nrfx_comp_init(&config, comp_evt_handler);
    APP_ERROR_CHECK(err);

    nrfx_comp_start(COMP_INTENSET_UP_Msk, 0);  // enable UP interrupt only
}


/* -------------------------------------------------------------------------- */
/*  Voltage alert helpers                                                     */
/* -------------------------------------------------------------------------- */
static void send_voltage_alert(bool high)
{
    if (!m_voltage_alert_enabled || m_conn_handle == BLE_CONN_HANDLE_INVALID)
        return;

    uint8_t d = high ? 1 : 0;
    ble_gatts_hvx_params_t hvx =
    {
        .handle = m_gpio_service.voltage_alert_char_handles.value_handle,
        .type   = BLE_GATT_HVX_NOTIFICATION,
        .p_data = &d,
        .p_len  = (uint16_t[]){1},
    };
    ret_code_t err = sd_ble_gatts_hvx(m_conn_handle, &hvx);
    if (err != NRF_SUCCESS &&
        err != NRF_ERROR_BUSY &&
        err != NRF_ERROR_RESOURCES)
    {
        APP_ERROR_CHECK(err);
    }
}

/* -------------------------------------------------------------------------- */
/*  BLE event handling                                                        */
/* -------------------------------------------------------------------------- */
static void on_write(ble_evt_t const *e)
{
    const ble_gatts_evt_write_t *w = &e->evt.gatts_evt.params.write;

    if (w->handle == m_gpio_service.vibration_char_handles.value_handle && w->len == 2)
    {
        uint8_t p = w->data[0], i = w->data[1];
        switch (p)
        {
            case 0: vibrate(0, 0);               break;
            case 1: vibrate_pattern_single(i);   break;
            case 2: vibrate_pattern_double(i);   break;
            case 3: vibrate_pattern_long(i);     break;
            case 4: vibrate_pattern_escalating();break;
        }
    }
    else if (w->handle == m_gpio_service.rgb_char_handles.value_handle && w->len == 4)
    {
        ws2812_send_color(w->data[0], w->data[1], w->data[2], w->data[3]);
    }
    else if (w->handle == m_gpio_service.voltage_alert_char_handles.cccd_handle && w->len == 2)
    {
        m_voltage_alert_enabled = (w->data[0] & 1);
    }
}

static void ble_evt_handler(ble_evt_t const *e, void *p_ctx)
{
    (void)p_ctx;
    switch (e->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            m_conn_handle = e->evt.gap_evt.conn_handle;
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            m_conn_handle           = BLE_CONN_HANDLE_INVALID;
            m_voltage_alert_enabled = false;
            m_voltage_active        = false;
            nrf_gpio_pin_clear(VIBRATION_PIN);
            ws2812_send_color(0, 0, 0, 0);
            ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
            break;

        case BLE_GATTS_EVT_WRITE:
            on_write(e);
            break;

        default:
            break;
    }
}

/* -------------------------------------------------------------------------- */
/*  GATT service / characteristics                                            */
/* -------------------------------------------------------------------------- */
static void gpio_service_init(void)
{
    ret_code_t err;
    ble_uuid_t svc = { .type = GPIO_SERVICE_UUID_TYPE, .uuid = GPIO_SERVICE_UUID };
    APP_ERROR_CHECK(sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
                                             &svc,
                                             &m_gpio_service.service_handle));

    /* --- Vibration characteristic ------------------------------------- */
    ble_gatts_char_md_t cmd = {0};
    cmd.char_props.read  = 1;
    cmd.char_props.write = 1;
    static char vib_desc[] = "Vibration Pattern";
    cmd.p_char_user_desc   = (uint8_t*)vib_desc;
    cmd.char_user_desc_max_size = cmd.char_user_desc_size = strlen(vib_desc);

    ble_uuid_t cuuid = { .type = GPIO_SERVICE_UUID_TYPE, .uuid = VIBRATION_CHAR_UUID };
    ble_gatts_attr_md_t attr_md = {0};
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);
    attr_md.vloc = BLE_GATTS_VLOC_STACK;

    uint8_t vib_init[2] = {0, 0};
    ble_gatts_attr_t attr =
    {
        .p_uuid    = &cuuid,
        .p_attr_md = &attr_md,
        .init_len  = 2,
        .max_len   = 2,
        .p_value   = vib_init,
    };
    err = sd_ble_gatts_characteristic_add(m_gpio_service.service_handle,
                                          &cmd,
                                          &attr,
                                          &m_gpio_service.vibration_char_handles);
    APP_ERROR_CHECK(err);

    /* --- RGB characteristic ------------------------------------------ */
    memset(&cmd, 0, sizeof(cmd));
    cmd.char_props.read  = 1;
    cmd.char_props.write = 1;
    static char rgb_desc[] = "RGB Control";
    cmd.p_char_user_desc   = (uint8_t*)rgb_desc;
    cmd.char_user_desc_max_size = cmd.char_user_desc_size = strlen(rgb_desc);

    cuuid.uuid = RGB_CHAR_UUID;
    uint8_t rgb_init[4] = {0, 0, 0, 0};
    attr.p_uuid   = &cuuid;
    attr.init_len = 4;
    attr.max_len  = 4;
    attr.p_value  = rgb_init;
    err = sd_ble_gatts_characteristic_add(m_gpio_service.service_handle,
                                          &cmd,
                                          &attr,
                                          &m_gpio_service.rgb_char_handles);
    APP_ERROR_CHECK(err);

    /* --- Voltage alert characteristic -------------------------------- */
    memset(&cmd, 0, sizeof(cmd));
    cmd.char_props.read   = 1;
    cmd.char_props.notify = 1;
    static char volt_desc[] = "Voltage Alert";
    cmd.p_char_user_desc   = (uint8_t*)volt_desc;
    cmd.char_user_desc_max_size = cmd.char_user_desc_size = strlen(volt_desc);

    ble_gatts_attr_md_t cccd_md = {0};
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;
    cmd.p_cccd_md = &cccd_md;

    cuuid.uuid = VOLTAGE_ALERT_CHAR_UUID;
    uint8_t volt_init = 0;
    attr.p_uuid   = &cuuid;
    attr.init_len = 1;
    attr.max_len  = 1;
    attr.p_value  = &volt_init;
    err = sd_ble_gatts_characteristic_add(m_gpio_service.service_handle,
                                          &cmd,
                                          &attr,
                                          &m_gpio_service.voltage_alert_char_handles);
    APP_ERROR_CHECK(err);
}

/* -------------------------------------------------------------------------- */
/*  GAP / GATT / SoftDevice                                                   */
/* -------------------------------------------------------------------------- */
static void gap_params_init(void)
{
    ble_gap_conn_sec_mode_t sec;
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec);
    APP_ERROR_CHECK(sd_ble_gap_device_name_set(&sec,
                     (const uint8_t*)DEVICE_NAME,
                     strlen(DEVICE_NAME)));

    ble_gap_conn_params_t cp = {0};
    cp.min_conn_interval = MIN_CONN_INTERVAL;
    cp.max_conn_interval = MAX_CONN_INTERVAL;
    cp.slave_latency     = SLAVE_LATENCY;
    cp.conn_sup_timeout  = CONN_SUP_TIMEOUT;
    APP_ERROR_CHECK(sd_ble_gap_ppcp_set(&cp));
}

static void gatt_init(void)
{
    APP_ERROR_CHECK(nrf_ble_gatt_init(&m_gatt, NULL));
}

static void ble_stack_init(void)
{
    APP_ERROR_CHECK(nrf_sdh_enable_request());
    uint32_t ram_start = 0;
    APP_ERROR_CHECK(nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start));
    APP_ERROR_CHECK(nrf_sdh_ble_enable(&ram_start));
    NRF_SDH_BLE_OBSERVER(m_observer, 3, ble_evt_handler, NULL);
}

/* -------------------------------------------------------------------------- */
/*  Advertising                                                               */
/* -------------------------------------------------------------------------- */
static void advertising_init(void)
{
    ble_advdata_t adv = {0};
    adv.name_type          = BLE_ADVDATA_FULL_NAME;
    adv.include_appearance = true;
    adv.flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    adv.uuids_complete.p_uuids   = m_adv_uuids;
    adv.uuids_complete.uuid_cnt  = ARRAY_SIZE(m_adv_uuids);

    ble_advertising_init_t init = {0};
    init.advdata      = adv;
    init.config.ble_adv_fast_enabled  = true;
    init.config.ble_adv_fast_interval = 1600;
    init.config.ble_adv_fast_timeout  = 0;

    APP_ERROR_CHECK(ble_advertising_init(&m_advertising, &init));
    ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}

static void advertising_start(void)
{
    APP_ERROR_CHECK(ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST));
}

/* -------------------------------------------------------------------------- */
/*  MAIN                                                                      */
/* -------------------------------------------------------------------------- */
int main(void)
{
    //SEGGER_RTT_Init();
    //DBG_PUTS("Demo start\r\n");

    for (uint32_t pin = 0; pin < 32; pin++) {
    if (pin != VIBRATION_PIN && pin != WS2812B_PIN) {
        nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_PULLDOWN);
    }
    }

    vibration_init();
    comp_init();

    ble_stack_init();
    APP_ERROR_CHECK(app_timer_init());
    APP_ERROR_CHECK(app_timer_create(&m_voltage_timer_id,
                                     APP_TIMER_MODE_SINGLE_SHOT,
                                     voltage_timeout_handler));

    gap_params_init();
    gatt_init();
    gpio_service_init();
    advertising_init();
    advertising_start();
    APP_ERROR_CHECK(sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_advertising.adv_handle, -20)); // Sets TX power to -20 dBm

    ws2812_send_color(255, 0, 0, 255); nrf_delay_ms(500);
    ws2812_send_color(0, 0, 0, 0);

    // Example test sections:
nrfx_comp_uninit();  // Disable comparator
// Measure current

nrf_gpio_cfg_default(WS2812B_PIN); // Disable LED
// Measure current

// Disable BLE (temporarily):
sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);

    /* ---------- Main loop ---------- */
    for (;;)
    {
        /* Let SoftDevice wake us on BLE, COMP, or timer IRQs */
        (void) sd_app_evt_wait();
    }
}






 

 








222050.sdk_config.h

Related