Multi-Connection BLE Central on nRF52840DK (nRF Connect SDK v3.2.1)

Dear Nordic Semiconductor Support Team,
My name is Iwo, and I’m developing a BLE central application using the nRF52840 DK with nRF Connect SDK v3.2.1.
The application works reliably when connecting to a single custom BLE peripheral (a smart lamp with service 0xFFE5 and characteristic 0xFFE9), but I am unable to achieve stable connections to two identical peripherals simultaneously or sequentially in a reliable cycle. I ultimately want to achieve a setup with 10 lamps, so failure at just 2 is hard to swallow.
Here is a summary of my current situation:

Working Single-Lamp Configuration
The configuration and code I'll attach below successfully connects, discovers the characteristic (FFE9 at handle 32), performs MTU exchange to 65, and sends 22-byte write-without-response commands to toggle the lamp.

The Problem with Two Lamps
I have two identical lamps with known public MAC addresses:

  • Lamp 1: 57:60:07:A8:EA:EC
  • Lamp 2: 40:B2:C4:2B:A1:A0

I have tried various approaches to connect to both, including:

  • Concurrent connections using CONFIG_BT_MAX_CONN=2 + increased buffers (ACL/L2CAP/ATT)
  • Sequential cycling (connect → write → disconnect → next lamp)
  • Continuous scanning with auto-connect on filter match
  • Skipping MTU exchange and discovery (hardcoded handle 32)
  • Using the multi-link/multi-role example from https://github.com/NordicPlayground/nrf52-ble-multi-link-multi-role as reference

Typical behavior in logs:

  • Scan starts
  • Finds one lamp (randomly 1 or 2)
  • Connects successfully ("Connected")
  • MTU exchange often fails (err 14: BT_ATT_ERR_UNLIKELY) or succeeds to 65
  • Discovery finds FFE9 at handle 32
  • Writes fail with -12 (ENOMEM) or -128 (no ATT channel)
  • Disconnects with reason 62 (connection timeout)

The lamp does not show the BT connection icon on its display (even when logs say "Connected"), but it does respond to writes when single-lamp code works (lamp flashes).

Questions / Help Needed

  1. Is there a known limitation in nRF Connect SDK v3.2.1 with the SoftDevice Controller when acting as BLE central to multiple identical custom peripherals?
  2. What is the correct/recommended way to increase buffers and enable reliable 2 concurrent central connections on nRF52840DK? (I tried the buffer configs above but still get ENOMEM/-12 on writes)
  3. Is MTU exchange mandatory for 22-byte writes? Some lamps seem to reject it (err 14). Is there a workaround to use default MTU reliably?
  4. Why does the connection appear established in logs but the peripheral (lamp) does not show the BT icon? Does the lamp require a specific GATT action (e.g., write to FFE9) to consider the connection "active"?

Working Single-Lamp Configuration:

prj.conf (single lamp):
# Enable BLE stack as central with GATT client
CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_SMP=y
CONFIG_BT_GATT_CLIENT=y

# Enable scanning
CONFIG_BT_SCAN=y
CONFIG_BT_SCAN_FILTER_ENABLE=y
CONFIG_BT_SCAN_ADDRESS_CNT=1

# Enable GATT discovery manager
CONFIG_BT_GATT_DM=y

# Increase MTU and buffer sizes for 21-byte writes (default MTU 23 allows only ~20-byte payload)
CONFIG_BT_L2CAP_TX_MTU=65
CONFIG_BT_BUF_ACL_TX_SIZE=69
CONFIG_BT_BUF_ACL_RX_SIZE=69

# Logging and heap
CONFIG_LOG=y
CONFIG_HEAP_MEM_POOL_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

main.c (single lamp - works perfectly):

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <bluetooth/gatt_dm.h>
#include <bluetooth/scan.h>
#include <zephyr/bluetooth/att.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>

LOG_MODULE_REGISTER(central_lamp, CONFIG_LOG_DEFAULT_LEVEL);

static struct bt_conn *lamp_conn;
static uint16_t cmd_handle;
static bool discovered = false;

static struct bt_uuid_16 service_uuid = BT_UUID_INIT_16(0xFFE5);  // Lamp's service UUID from app screenshots
static struct bt_uuid_16 cmd_uuid = BT_UUID_INIT_16(0xFFE9);

static const uint8_t on_cmd[] = {0x3a, 0x26, 0xa3, 0x0d, 0x40, 0xb2, 0x01, 0x00, 0xff, 0x2d, 0x64, 0x19, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x78, 0x07, 0x0d, 0x0a};
static const uint8_t off_cmd[] = {0x3a, 0x26, 0xa3, 0x0d, 0x40, 0xb2, 0x00, 0x00, 0xff, 0x2d, 0x64, 0x19, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x77, 0x07, 0x0d, 0x0a};

static bt_addr_le_t target_addr = {
    .type = BT_ADDR_LE_PUBLIC,
    .a.val = {0xEC, 0xEA, 0xA8, 0x07, 0x60, 0x57}  // Reversed byte order for 57:60:07:A8:EA:EC
};

/* LED setup using devicetree alias led0 (LED1 on nRF52840 DK) */
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);

/* Debug: Log all found devices */
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
                         struct net_buf_simple *ad)
{
    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
    LOG_INF("Found device: %s (RSSI %d, type %u)", addr_str, rssi, type);
}

static void scan_filter_match(struct bt_scan_device_info *device_info,
                              struct bt_scan_filter_match *filter_match,
                              bool connectable)
{
    char addr[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
    LOG_INF("Filters matched. Address: %s connectable: %d", addr, connectable);
}

static void scan_connecting_error(struct bt_scan_device_info *device_info)
{
    LOG_WRN("Connecting failed");
}

static void scan_connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn)
{
    lamp_conn = bt_conn_ref(conn);
}

BT_SCAN_CB_INIT(scan_cb, scan_filter_match, device_found, scan_connecting_error, scan_connecting);

static void start_scan(void)
{
    int err;

    struct bt_le_scan_param scan_param = {
        .type = BT_LE_SCAN_TYPE_ACTIVE,
        .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
        .interval = BT_GAP_SCAN_FAST_INTERVAL,
        .window = BT_GAP_SCAN_FAST_WINDOW,
    };

    struct bt_scan_init_param scan_init = {
        .connect_if_match = 1,
        .scan_param = &scan_param,
        .conn_param = BT_LE_CONN_PARAM_DEFAULT,
    };

    bt_scan_init(&scan_init);
    bt_scan_cb_register(&scan_cb);

    err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_ADDR, &target_addr);
    if (err) {
        LOG_ERR("Scanning filters cannot be set (err %d)", err);
        return;
    }

    err = bt_scan_filter_enable(BT_SCAN_ADDR_FILTER, false);
    if (err) {
        LOG_ERR("Filters cannot be enabled (err %d)", err);
        return;
    }

    err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
    if (err) {
        LOG_ERR("Scanning failed to start (err %d)", err);
    } else {
        LOG_INF("Scan started");
    }
}

static void discovery_complete(struct bt_gatt_dm *dm, void *ctx)
{
    LOG_INF("Service discovery completed");

    const struct bt_gatt_dm_attr *attr = bt_gatt_dm_char_by_uuid(dm, &cmd_uuid.uuid);
    if (attr) {
        struct bt_gatt_chrc *chrc = bt_gatt_dm_attr_chrc_val(attr);
        if (chrc) {
            cmd_handle = chrc->value_handle;
            LOG_INF("Found FFE9 characteristic, handle: %u", cmd_handle);
            discovered = true;
        } else {
            LOG_WRN("FFE9 value not found");
        }
    } else {
        LOG_WRN("FFE9 characteristic not found");
    }

    bt_gatt_dm_data_release(dm);
}

static void discovery_service_not_found(struct bt_conn *conn, void *ctx)
{
    LOG_ERR("Service not found");
}

static void discovery_error(struct bt_conn *conn, int err, void *ctx)
{
    LOG_ERR("Error discovering (err %d)", err);
}

static const struct bt_gatt_dm_cb discovery_cb = {
    .completed = discovery_complete,
    .service_not_found = discovery_service_not_found,
    .error_found = discovery_error,
};

static void exchange_func(struct bt_conn *conn, uint8_t att_err, struct bt_gatt_exchange_params *params)
{
    if (att_err) {
        LOG_ERR("MTU exchange failed (err %u)", att_err);
        return;
    }

    uint16_t mtu = bt_gatt_get_mtu(conn);
    LOG_INF("MTU exchange done, new MTU: %u", mtu);

    int err = bt_gatt_dm_start(conn, &service_uuid.uuid, &discovery_cb, NULL);
    if (err) {
        LOG_ERR("bt_gatt_dm_start failed (err %d)", err);
    }
}

static void connected(struct bt_conn *conn, uint8_t conn_err)
{
    if (conn_err) {
        LOG_ERR("Connection failed (err %u)", conn_err);
        if (lamp_conn) {
            bt_conn_unref(lamp_conn);
            lamp_conn = NULL;
        }
        start_scan();
        return;
    }

    LOG_INF("Connected");

    static struct bt_gatt_exchange_params exchange_params;
    exchange_params.func = exchange_func;
    int err = bt_gatt_exchange_mtu(conn, &exchange_params);
    if (err) {
        LOG_ERR("MTU exchange failed (err %d)", err);
        exchange_func(conn, 0, NULL);  // Fallback
    }
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
    LOG_INF("Disconnected (reason %u)", reason);
    discovered = false;
    if (lamp_conn) {
        bt_conn_unref(lamp_conn);
        lamp_conn = NULL;
    }
    start_scan();
}

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

int main(void)
{
    int err;

    /* Initialize LED */
    if (!gpio_is_ready_dt(&led)) {
        LOG_ERR("LED device not ready");
        return -1;
    }
    err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    if (err < 0) {
        LOG_ERR("LED configure failed (err %d)", err);
        return -1;
    }
    LOG_INF("LED initialized (should start blinking soon)");

    /* Initialize Bluetooth */
    LOG_INF("Initializing Bluetooth...");
    err = bt_enable(NULL);
    if (err) {
        LOG_ERR("Bluetooth init failed (err %d)", err);
        return -1;
    }
    LOG_INF("Bluetooth initialized");

    start_scan();

    uint32_t blink_counter = 0;

    while (1) {
        /* Blink LED every 500 ms to confirm code is running */
        gpio_pin_toggle_dt(&led);
        blink_counter++;
        if (blink_counter % 10 == 0) {  // Log every 5 seconds
            LOG_INF("Alive - LED toggled %u times", blink_counter);
        }

        /* BLE logic */
        if (discovered && lamp_conn) {
            err = bt_gatt_write_without_response(lamp_conn, cmd_handle, on_cmd, sizeof(on_cmd), false);
            if (err) {
                LOG_ERR("Write ON failed (err %d)", err);
            } else {
                LOG_INF("Sent ON command successfully");
            }
            k_sleep(K_SECONDS(1));

            err = bt_gatt_write_without_response(lamp_conn, cmd_handle, off_cmd, sizeof(off_cmd), false);
            if (err) {
                LOG_ERR("Write OFF failed (err %d)", err);
            } else {
                LOG_INF("Sent OFF command successfully");
            }
            k_sleep(K_SECONDS(1));
        } else {
            k_sleep(K_MSEC(100));
        }
    }

    return 0;
}

I would greatly appreciate any guidance, sample project, or Kconfig recommendations for stable multi-central operation with custom GATT services on nRF52840DK.Thank you very much for your time and support.
I’m happy to provide full project files, complete logs, or any additional information.
Best regards,
Iwo

  • Hi Iwo, 

    It should be quite straight forward to support multiple central links. I'm not so sure why you have those problems. But please try: 
    - Increase CONFIG_BT_MAX_CONN to 3 for example, there is a chance that a connection is not terminated before you create a new connection.

    - Make sure you set CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=0 if you don't plan to have any connection to another central. 

    - I assume you are testing with a 3rd party lamp ? Please try to test with a set up with a peripheral you have full control of. Would suggest to start the test with 3 DK, 2 DK act as peripheral and one as central

    - Please use nRF Sniffer to get more insight on what could be wrong. 

    - It's a bit old but maybe still useful, please take a look at this post  Enter the Multi-NUS: A Simple Wireless UART Network At the end of the post there is a github repo that you can test.  

  • Thank you for your previous response and suggestions. I've implemented all of them as best as I could with the available hardware, but I'm still facing issues with connecting to the second lamp reliably. The application works perfectly for a single lamp (connects, discovers FFE9, sends commands), but for two lamps, it either connects to only one or fails with MTU exchange err 14 and disconnect reason 62. The lamps work fine with an ESP-32D (both sequentially and concurrently), so environmental factors like RF noise are unlikely the cause. Also the DK connects and sends commands to both of the lamp correctly via Nordic Bluetooth Low Energy app.


    Analysis of What I've Tried Since Your Response:
    • Increased CONFIG_BT_MAX_CONN to 3: Set in prj.conf (attached) — no change, still only one connection succeeds.
    • Set CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=0: Set in prj.conf — no improvement in multi-central stability.
    • Test with DKs as peripherals: I don't have three DKs, but tested with the available hardware (nRF52840 DK as central, lamps as peripherals). The lamps are third-party, but their ads are visible in sniffer captures (both MACs advertise normally).
    • nRF Sniffer: Set up on a dongle/Feather, captured traffic — shows both lamps advertising (ADV_IND with service UUID FFE5), but the DK only sends CONNECT_REQ to one. No CONNECT_REQ to the second, even though ads are received. MTU exchange fails on the first with "Unlikely Error" (0x0E).
    • Multi-NUS repo/post: Reviewed the multi-NUS GitHub repo and post — implemented similar concurrent central logic with separate conn objects, increased buffers (L2CAP, ACL, CONN_TX_MAX), but still MTU failure and only one connection.

    • Additional steps I tried:
      • Sequential cycling (connect1 → write → disconnect → connect2 → write → disconnect → repeat) — connects to one random lamp per reset, sends commands, but doesn't cycle to the second.
      • Skipped MTU exchange — writes fail with -12 (ENOMEM) or no ATT channel.
      • Dummy write to FFE9 for BT icon — works for one lamp.
      • Swapped MAC types (public/random) — no change.
      • Increased scan interval/window — catches ads, but connect only to one.

    Could you review the code/prj.conf and suggest fixes? Or provide a minimal multi-central sample for nRF Connect SDK v3.2.1 with two peripherals?

    Best regards,
    Iwo



    Main.c:

    • #include <zephyr/kernel.h>
      #include <zephyr/logging/log.h>
      #include <zephyr/bluetooth/bluetooth.h>
      #include <zephyr/bluetooth/conn.h>
      #include <zephyr/bluetooth/uuid.h>
      #include <zephyr/bluetooth/gatt.h>
      #include <bluetooth/gatt_dm.h>
      #include <bluetooth/scan.h>
      #include <zephyr/bluetooth/att.h>
      #include <zephyr/device.h>
      #include <zephyr/drivers/gpio.h>
      #include <zephyr/bluetooth/addr.h>
      
      LOG_MODULE_REGISTER(central_lamp, CONFIG_LOG_DEFAULT_LEVEL);
      
      #define MAX_LAMPS 2
      
      static struct bt_conn *current_conn = NULL;
      static uint16_t cmd_handle = 0;
      static bool discovered = false;
      static int current_lamp = 0;
      
      static struct bt_uuid_16 service_uuid = BT_UUID_INIT_16(0xFFE5);
      static struct bt_uuid_16 cmd_uuid = BT_UUID_INIT_16(0xFFE9);
      
      static const uint8_t on_cmd[] = {0x3a, 0x26, 0xa3, 0x0d, 0x40, 0xb2, 0x01, 0x00, 0xff, 0x2d, 0x64, 0x19, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x78, 0x07, 0x0d, 0x0a};
      static const uint8_t off_cmd[] = {0x3a, 0x26, 0xa3, 0x0d, 0x40, 0xb2, 0x00, 0x00, 0xff, 0x2d, 0x64, 0x19, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x77, 0x07, 0x0d, 0x0a};
      
      static bt_addr_le_t target_addrs[MAX_LAMPS] = {
          {.type = BT_ADDR_LE_PUBLIC, .a.val = {0xEC, 0xEA, 0xA8, 0x07, 0x60, 0x57}},  // Lamp 1
          {.type = BT_ADDR_LE_PUBLIC, .a.val = {0xA0, 0xA1, 0x2B, 0xC4, 0xB2, 0x40}}   // Lamp 2
      };
      
      /* LED setup using devicetree alias led0 (LED1 on nRF52840 DK) */
      #define LED0_NODE DT_ALIAS(led0)
      static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
      
      /* Debug: Log all found devices */
      static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
                               struct net_buf_simple *ad)
      {
          char addr_str[BT_ADDR_LE_STR_LEN];
          bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
          LOG_INF("Found device: %s (RSSI %d, type %u)", addr_str, rssi, type);
      }
      
      static void scan_filter_match(struct bt_scan_device_info *device_info,
                                    struct bt_scan_filter_match *filter_match,
                                    bool connectable)
      {
          char addr[BT_ADDR_LE_STR_LEN];
          bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
          LOG_INF("Filters matched. Address: %s connectable: %d", addr, connectable);
      }
      
      static void scan_connecting_error(struct bt_scan_device_info *device_info)
      {
          LOG_WRN("Connecting failed");
      }
      
      static void scan_connecting(struct bt_scan_device_info *device_info, struct bt_conn *conn)
      {
          current_conn = bt_conn_ref(conn);
      }
      
      BT_SCAN_CB_INIT(scan_cb, scan_filter_match, device_found, scan_connecting_error, scan_connecting);
      
      static void start_scan_for_lamp(const bt_addr_le_t *target)
      {
          int err;
      
          struct bt_le_scan_param scan_param = {
              .type = BT_LE_SCAN_TYPE_ACTIVE,
              .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
              .interval = BT_GAP_SCAN_FAST_INTERVAL,
              .window = BT_GAP_SCAN_FAST_WINDOW,
          };
      
          struct bt_scan_init_param scan_init = {
              .connect_if_match = 1,
              .scan_param = &scan_param,
              .conn_param = BT_LE_CONN_PARAM_DEFAULT,
          };
      
          bt_scan_init(&scan_init);
          bt_scan_cb_register(&scan_cb);
      
          err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_ADDR, target);
          if (err) {
              LOG_ERR("Scanning filters cannot be set (err %d)", err);
              return;
          }
      
          err = bt_scan_filter_enable(BT_SCAN_ADDR_FILTER, false);
          if (err) {
              LOG_ERR("Filters cannot be enabled (err %d)", err);
              return;
          }
      
          err = bt_scan_start(BT_SCAN_TYPE_SCAN_ACTIVE);
          if (err) {
              LOG_ERR("Scanning failed to start (err %d)", err);
          } else {
              LOG_INF("Scan started");
          }
      }
      
      static void discovery_complete(struct bt_gatt_dm *dm, void *ctx)
      {
          LOG_INF("Service discovery completed");
      
          const struct bt_gatt_dm_attr *attr = bt_gatt_dm_char_by_uuid(dm, &cmd_uuid.uuid);
          if (attr) {
              struct bt_gatt_chrc *chrc = bt_gatt_dm_attr_chrc_val(attr);
              if (chrc) {
                  cmd_handle = chrc->value_handle;
                  LOG_INF("Found FFE9 characteristic, handle: %u", cmd_handle);
                  discovered = true;
              } else {
                  LOG_WRN("FFE9 value not found");
              }
          } else {
              LOG_WRN("FFE9 characteristic not found");
          }
      
          bt_gatt_dm_data_release(dm);
      }
      
      static void discovery_service_not_found(struct bt_conn *conn, void *ctx)
      {
          LOG_ERR("Service not found");
      }
      
      static void discovery_error(struct bt_conn *conn, int err, void *ctx)
      {
          LOG_ERR("Error discovering (err %d)", err);
      }
      
      static const struct bt_gatt_dm_cb discovery_cb = {
          .completed = discovery_complete,
          .service_not_found = discovery_service_not_found,
          .error_found = discovery_error,
      };
      
      static void exchange_func(struct bt_conn *conn, uint8_t att_err, struct bt_gatt_exchange_params *params)
      {
          if (att_err) {
              LOG_ERR("MTU exchange failed (err %u)", att_err);
              return;
          }
      
          uint16_t mtu = bt_gatt_get_mtu(conn);
          LOG_INF("MTU exchange done, new MTU: %u", mtu);
      
          int err = bt_gatt_dm_start(conn, &service_uuid.uuid, &discovery_cb, NULL);
          if (err) {
              LOG_ERR("bt_gatt_dm_start failed (err %d)", err);
          }
      }
      
      static void connected(struct bt_conn *conn, uint8_t conn_err)
      {
          if (conn_err) {
              LOG_ERR("Connection failed (err %u)", conn_err);
              if (current_conn) {
                  bt_conn_unref(current_conn);
                  current_conn = NULL;
              }
              return;
          }
      
          LOG_INF("Connected");
      
          static struct bt_gatt_exchange_params exchange_params;
          exchange_params.func = exchange_func;
          int err = bt_gatt_exchange_mtu(conn, &exchange_params);
          if (err) {
              LOG_ERR("MTU exchange failed (err %d)", err);
              exchange_func(conn, 0, NULL);  // Fallback
          }
      }
      
      static void disconnected(struct bt_conn *conn, uint8_t reason)
      {
          LOG_INF("Disconnected (reason %u)", reason);
          discovered = false;
          if (current_conn) {
              bt_conn_unref(current_conn);
              current_conn = NULL;
          }
      }
      
      BT_CONN_CB_DEFINE(conn_callbacks) = {
          .connected = connected,
          .disconnected = disconnected,
      };
      
      int main(void)
      {
          int err;
      
          /* Initialize LED */
          if (!gpio_is_ready_dt(&led)) {
              LOG_ERR("LED device not ready");
              return -1;
          }
          err = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
          if (err < 0) {
              LOG_ERR("LED configure failed (err %d)", err);
              return -1;
          }
          LOG_INF("LED initialized (should start blinking soon)");
      
          /* Initialize Bluetooth */
          LOG_INF("Initializing Bluetooth...");
          err = bt_enable(NULL);
          if (err) {
              LOG_ERR("Bluetooth init failed (err %d)", err);
              return -1;
          }
          LOG_INF("Bluetooth initialized");
      
          int current_lamp = 0;
      
          while (1) {
              LOG_INF("Starting cycle for lamp %d", current_lamp + 1);
      
              discovered = false;
              start_scan_for_lamp(&target_addrs[current_lamp]);
      
              // Wait for connection and discovery (with timeout)
              int timeout = 30;
              while (!discovered && timeout > 0) {
                  k_sleep(K_SECONDS(1));
                  timeout--;
              }
      
              if (discovered) {
                  err = bt_gatt_write_without_response(current_conn, cmd_handle, on_cmd, sizeof(on_cmd), false);
                  if (err) {
                      LOG_ERR("Write ON failed (err %d)", err);
                  } else {
                      LOG_INF("Sent ON command successfully");
                  }
                  k_sleep(K_SECONDS(1));
      
                  err = bt_gatt_write_without_response(current_conn, cmd_handle, off_cmd, sizeof(off_cmd), false);
                  if (err) {
                      LOG_ERR("Write OFF failed (err %d)", err);
                  } else {
                      LOG_INF("Sent OFF command successfully");
                  }
                  k_sleep(K_SECONDS(1));
      
                  // Disconnect
                  err = bt_conn_disconnect(current_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
                  if (err) {
                      LOG_ERR("Disconnect failed (err %d)", err);
                  } else {
                      LOG_INF("Disconnected");
                  }
                  k_sleep(K_SECONDS(1));  // Wait for disconnect to complete
              } else {
                  LOG_ERR("Failed to connect/discover lamp %d - retrying", current_lamp + 1);
              }
      
              current_lamp = (current_lamp + 1) % MAX_LAMPS;
      
              gpio_pin_toggle_dt(&led);
          }
      
          return 0;
      }

      Prj.conf:
      # Enable BLE stack as central with GATT client
      CONFIG_BT=y
      CONFIG_BT_CENTRAL=y
      CONFIG_BT_SMP=y
      CONFIG_BT_GATT_CLIENT=y

      # Enable scanning
      CONFIG_BT_SCAN=y
      CONFIG_BT_SCAN_FILTER_ENABLE=y
      CONFIG_BT_SCAN_ADDRESS_CNT=2

      # Enable GATT discovery manager
      CONFIG_BT_GATT_DM=y

      # Increase MTU and buffer sizes for 21-byte writes
      CONFIG_BT_L2CAP_TX_MTU=65
      CONFIG_BT_BUF_ACL_TX_SIZE=69
      CONFIG_BT_BUF_ACL_RX_SIZE=69

      # Logging and heap
      CONFIG_LOG=y
      CONFIG_HEAP_MEM_POOL_SIZE=2048
      CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

      # Multi-connection (Nordic suggestions)
      CONFIG_BT_MAX_CONN=2
      CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=0
      CONFIG_BT_CONN_TX_MAX=5
      CONFIG_BT_L2CAP_TX_BUF_COUNT=5
  • Alright, I managed to get it working, case closed : ) 

  • Hi Iwo, 
    Glad that you managed to get it work. Would be great if you can share what you found that solved your issue. 

Related