Trying to do ADV_DIRECT_IND - Connectable not scannable

Trying to do ADV_DIRECT_IND advertisement.

We are trying to do the following advertisements

// ADV_IND Scannable and Connectable
// ADV_DIRECT_IND - Connectable not scannable
// ADV_NONCONN_IND - neither scanning nor connection is allowed
// ADV_SCAN_IND - allows scanning but not connection

ADV_IND  is working

adv_param.options |= (BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_SCANNABLE);
adv_param.id = BT_ID_DEFAULT;
adv_param.peer = NULL;

err = bt_le_adv_start(&adv_param, adv_data, ARRAY_SIZE(adv_data), scan_url_data, ARRAY_SIZE(scan_url_data));

Device found MAC: FF:EE:DD:CC:BB:AA (random) SNIF: E7:09:68:43:C6:2C (RSSI: -53), type 0x00 = ADV_IND (Connect+Scan)
    Flags: 0x06 [LE General Discoverable Mode, BR/EDR Not Supported (LE Only)]
    Name: nrf54
    Manufacturer: 0x0059 (Nordic Semiconductor ASA), (data length: 2)
    Data: 00 00



ADV_NONCONN_IND is working

adv_param.options = 0;
adv_param.peer = NULL;

err = bt_le_adv_start(&adv_param, adv_data, ARRAY_SIZE(adv_data), NULL, 0);

Device found MAC: 24:E0:11:A4:89:7D (random) SNIF: E7:09:68:43:C6:2C (RSSI: -51), type 0x03 = ADV_NONCONN_IND
    Flags: 0x06 [LE General Discoverable Mode, BR/EDR Not Supported (LE Only)]
    Name: nrf54
    Manufacturer: 0x0059 (Nordic Semiconductor ASA), (data length: 2)
    Data: 00 00

ADV_SCAN_IND is working

adv_param.options = BT_LE_ADV_OPT_SCANNABLE;
adv_param.peer = NULL; // needs to be not null

err = bt_le_adv_start(&adv_param, adv_data , ARRAY_SIZE(adv_data), scan_uuid_data, ARRAY_SIZE(scan_uuid_data));

Device found MAC: 1F:4E:E4:14:18:75 (random) SNIF: E7:09:68:43:C6:2C (RSSI: -49), type 0x02 = ADV_SCAN_IND
    Flags: 0x06 [LE General Discoverable Mode, BR/EDR Not Supported (LE Only)]
    Name: nrf54
    Manufacturer: 0x0059 (Nordic Semiconductor ASA), (data length: 2)
    Data: 00 00

ADV_DIRECT_IND trying to debug

have tried:

low_duty

static bt_addr_le_t peer_addr_2 = {
.type = BT_ADDR_LE_RANDOM, // or BT_ADDR_LE_RANDOM BT_ADDR_LE_PUBLIC if the peer uses random address
.a.val = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 } // Example: FF:EE:DD:CC:BB:AA (reverse byte order!)
};
// For low-duty cycle directed (add DIR_MODE_LOW_DUTY)
static const struct bt_le_adv_param adv_param_low = {
.id = BT_ID_DEFAULT,
.sid = 0,
.options = (BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_USE_IDENTITY),
.interval_min = BT_GAP_ADV_SLOW_INT_MIN,
.interval_max = BT_GAP_ADV_SLOW_INT_MAX,
.peer = &peer_addr_2,
};

int err = bt_le_adv_start(&adv_param_low, NULL, 0, NULL, 0);

Have also tried these options

adv_param.options = BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_DIR_ADDR_RPA | BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY;

hi duty

static bt_addr_le_t peer_addr_2 = {
.type = BT_ADDR_LE_RANDOM, // or BT_ADDR_LE_RANDOM BT_ADDR_LE_PUBLIC if the peer uses random address
.a.val = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 } // Example: FF:EE:DD:CC:BB:AA (reverse byte order!)
};

// For high-duty cycle directed (ADV_DIRECT_IND high duty)
static const struct bt_le_adv_param adv_param_high = {
.id = BT_ID_DEFAULT,
.sid = 0, // Advertising set ID (0 for legacy)
.options = (BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_USE_IDENTITY),
.interval_min = 20, // 0 for high duty
.interval_max = 20,
.peer = &peer_addr_2,
};

int err = bt_le_adv_start(&adv_param_high, NULL, 0, NULL, 0);

Any help on getting the ADV_DIRECT_IND working would be helpful.  

Parents Reply Children
  • Thank-you,  (this is a mixture of code from various sources and some AI questions answered)  For nRF52840 - DK

    Thank-you ,  the scanner I  was using was not  picking up the ADV_DIRECT_IND .  It would show up as unknown . 

    extended-direct-scannable-advertising  it was built in vscode nRF Connection extension and the nRF Connect SDK toolchain v3.0.2

    It does a generic advertizement and what it connects to will then do directed advertizements.  Used nRF Connect on android for connection and disconnections,  als used LightBlue on android device for connection and disconnection

    directory structure

    extended-direct-scannable-advertising
    |__CMakelists.txt
    |__prj.conf
    |__src
             |__main.c

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.13.1)

    set(BOARD nrf52833dk_nrf52833)

    find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
    project(test-adv)

    target_sources(app PRIVATE
        src/main.c
    )

    zephyr_include_directories(
        src
    )

    prj.conf

    CONFIG_BT=y
    CONFIG_BT_PERIPHERAL=y
    CONFIG_BT_SMP=y
    CONFIG_BT_BONDABLE=y
    CONFIG_BT_PRIVACY=y
    CONFIG_BT_SETTINGS=y
    CONFIG_SETTINGS=y
    CONFIG_BT_MAX_PAIRED=3
    CONFIG_BT_KEYS_OVERWRITE_OLDEST=y
    CONFIG_REBOOT=y
    CONFIG_FLASH=y
    CONFIG_FLASH_PAGE_LAYOUT=y
    CONFIG_FLASH_MAP=y
    CONFIG_NVS=y
    CONFIG_SETTINGS_NVS=y
    CONFIG_BT_DEVICE_NAME="Dir_Adv"
    CONFIG_HEAP_MEM_POOL_SIZE=6144

    main.c

    /*
    * Directed Advertising using last connected address (workaround)
    */

    #include <zephyr/kernel.h>
    #include <zephyr/types.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/sys/reboot.h>
    #include <zephyr/settings/settings.h>

    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/uuid.h>
    #include <zephyr/bluetooth/gatt.h>

    /* ==================== Custom Service ==================== */
    #define BT_UUID_CUSTOM_SERVICE_VAL \
    BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)

    static const struct bt_uuid_128 primary_service_uuid = BT_UUID_INIT_128(BT_UUID_CUSTOM_SERVICE_VAL);
    static const struct bt_uuid_128 read_char_uuid = BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));
    static const struct bt_uuid_128 write_char_uuid = BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef2));

    static int stored_value = 0;

    static ssize_t read_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
    void *buf, uint16_t len, uint16_t offset)
    {
    return bt_gatt_attr_read(conn, attr, buf, len, offset, &stored_value, sizeof(stored_value));
    }

    static ssize_t write_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
    const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
    {
    if (offset + len > sizeof(stored_value)) {
    return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
    }
    memcpy((uint8_t *)&stored_value + offset, buf, len);
    printk("Value written: %d\n", stored_value);
    return len;
    }

    BT_GATT_SERVICE_DEFINE(primary_service,
    BT_GATT_PRIMARY_SERVICE(&primary_service_uuid),
    BT_GATT_CHARACTERISTIC(&read_char_uuid.uuid, BT_GATT_CHRC_READ,
    BT_GATT_PERM_READ, read_cb, NULL, NULL),
    BT_GATT_CHARACTERISTIC(&write_char_uuid.uuid, BT_GATT_CHRC_WRITE,
    BT_GATT_PERM_WRITE_ENCRYPT, NULL, write_cb, NULL),
    );

    /* ==================== Last Connected Address ==================== */
    static bt_addr_le_t last_connected_addr = { .a.val = {0} }; // Will be invalid initially

    // static const uint8_t target_addr[6] = {0x2C, 0x46, 0x43, 0x68, 0x09, 0xE7}; // Note: reversed byte order!
    //
    // static bt_addr_le_t last_connected_addr = {
    // .type = BT_ADDR_LE_RANDOM, // Most common for modern devices
    // .a.val = { target_addr[0], target_addr[1], target_addr[2],
    // target_addr[3], target_addr[4], target_addr[5] }
    // };

    /* ==================== Advertising Data ==================== */
    static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_CUSTOM_SERVICE_VAL),
    BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
    };

    static struct bt_le_adv_param adv_param;
    static struct k_work_delayable adv_work;

    /* ==================== Callbacks ==================== */
    static void connected(struct bt_conn *conn, uint8_t err)
    {
    char addr[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    if (err) {
    printk("Connection failed to %s (err 0x%02x)\n", addr, err);
    return;
    }

    printk("Connected to %s\n", addr);

    /* Save this address for next directed advertising */
    bt_addr_le_copy(&last_connected_addr, bt_conn_get_dst(conn));

    bt_conn_set_security(conn, BT_SECURITY_L2);
    }

    static void disconnected(struct bt_conn *conn, uint8_t reason)
    {
    char addr[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
    printk("Disconnected from %s (reason 0x%02x)\n", addr, reason);

    k_work_reschedule(&adv_work, K_MSEC(800));
    }

    static void security_changed(struct bt_conn *conn, bt_security_t level,
    enum bt_security_err err)
    {
    char addr[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));

    if (err) {
    printk("Security failed: %s err %d\n", addr, err);
    } else {
    printk("Security changed: %s level %u\n", addr, level);
    }
    }

    /* ==================== Advertising Restart ==================== */
    static void advertising_restart(struct k_work *work)
    {
    int err;
    char addr_str[BT_ADDR_LE_STR_LEN];

    /* Check if we have a previously connected address */
    if (last_connected_addr.a.val[0] != 0 || last_connected_addr.a.val[5] != 0) {
    bt_addr_le_to_str(&last_connected_addr, addr_str, sizeof(addr_str));
    printk("→ DIRECTED advertising to last connected device: %s\n", addr_str);

    adv_param = *BT_LE_ADV_CONN_DIR_LOW_DUTY(&last_connected_addr);
    adv_param.options |= BT_LE_ADV_OPT_DIR_ADDR_RPA;

    err = bt_le_adv_start(&adv_param, NULL, 0, NULL, 0);
    } else {
    printk("No previous connection → Starting normal advertising\n");
    err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
    }

    if (err) {
    printk("Advertising start failed (err %d)\n", err);
    }
    }

    /* ==================== Main ==================== */
    int main(void)
    {
    int err;

    printk("Starting Directed Advertising (Last Connected Address mode)\n");

    char init_addr[BT_ADDR_LE_STR_LEN];

    printk("Starting Directed Advertising (Initial MAC: E7:09:68:43:46:2C)\n");

    /* Print initial target address */
    bt_addr_le_to_str(&last_connected_addr, init_addr, sizeof(init_addr));
    printk("Initial directed target: %s\n", init_addr);

    k_work_init_delayable(&adv_work, advertising_restart);

    err = bt_enable(NULL);
    if (err) {
    printk("Bluetooth init failed (err %d)\n", err);
    return 0;
    }

    if (IS_ENABLED(CONFIG_SETTINGS)) {
    settings_load();
    printk("Settings loaded\n");
    }

    /* Start first advertising */
    k_work_schedule(&adv_work, K_NO_WAIT);

    BT_CONN_CB_DEFINE(conn_callbacks) = {
    .connected = connected,
    .disconnected = disconnected,
    .security_changed = security_changed,
    };

    while (1) {
    k_sleep(K_FOREVER);
    }

    return 0;
    }

    -------------------------------------------------------

    Sample output,  it tries to connect and due to security issue (no encrypted security keys that is was the logs say).  Yet this works enough for my purposes.

    Starting Directed Advertising (Last Connected Address mode)
    Starting Directed Advertising (Initial MAC: E7:09:68:43:46:2C)
    Initial directed target: 00:00:00:00:00:00 (public)
    Settings loaded
    No previous connection → Starting normal advertising
    Connected to 77:2A:89:3B:83:7C (random)
    Security failed: 77:2A:89:3B:83:7C (random) err 2
    Disconnected from 77:2A:89:3B:83:7C (random) (reason 0x13)
    → DIRECTED advertising to last connected device: 77:2A:89:3B:83:7C (random)

Related