Updating data length on peripheral/server side

Hello,

I have followed 2.2 from this blog post to try and get my application to update its data length and MTU size when connecting to the nRF Connect for Mobile app. It's not clear from the blog post when to call the 'request_...' functions, but I implemented it such that request_data_len_update is called at the end of my on_connected() callback and request_mtu_exchange is called if the data length update was successful. I made sure to set the same configurations.

However, when I do connect, I see in my debug log "bt_hci_core: bt_hci_cmd_send_sync: opcode 0x2022 status 0x12" and 

bt_conn_le_data_len_update returns -EIO (-5). 
From this Q&A I found that the opcodes and status codes should be defined in the Bluetooth Spec, so I set out to try and find what was going on, I'll summarize it here in case someone else might find it useful.
From Vol 4, Part E, 5.4.1 it seems that HCI Command packet opcodes are a combination of a 6 bit Opcode Group Field (OGF) and a 10 bit Opcode Command Field (OCF).
From Vol 4, Part E, 7.8 it became clear that LE Controller commands have an OGF of 0x08 so the OCF would be 0x0022 which matches HCI_LE_Set_Data_Length, as expected from the thing I'm trying to achieve.
The status code 0x12 seems to indicate Invalid HCI Command Parameters.
So now I'm wondering, why are these invalidate parameters? Did I forget something in the configurations, or is trying to update the Data Length not possible in the on_connected callback and should it take place at a different moment?
Parents
  • Hi,

    Can you say more about which device you are using, how you are testing (perhaps show the code and configs) and which nRF Connect SDK version you are using?

  • Hi Einar,

    I am using nRF Connect SDK 2.5.0 on a custom board with a nRF52840-QFAA

    These are my relevant configurations

    custom board defconfig

    CONFIG_SOC_SERIES_NRF52X=y
    CONFIG_SOC_NRF52840_QFAA=y
    CONFIG_BOARD_CUSTOM=y
    
    # Enable MPU
    CONFIG_ARM_MPU=y
    
    # Enable hardware stack protection
    CONFIG_HW_STACK_PROTECTION=y
    
    # Enable RTT
    CONFIG_USE_SEGGER_RTT=y
    
    # enable GPIO
    CONFIG_GPIO=y
    
    # enable uart driver
    CONFIG_SERIAL=n
    
    # enable console
    CONFIG_CONSOLE=y
    CONFIG_UART_CONSOLE=n
    CONFIG_RTT_CONSOLE=y
    
    CONFIG_PINCTRL=y

    prj.conf

    CONFIG_BT=y
    CONFIG_BT_PERIPHERAL=y
    CONFIG_BT_DEVICE_NAME="DLE_sample"
    
    CONFIG_BT_PERIPHERAL_PREF_MIN_INT=160
    CONFIG_BT_PERIPHERAL_PREF_MAX_INT=160
    CONFIG_BT_PERIPHERAL_PREF_LATENCY=2
    CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=800
    CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=y
    
    # For requesting ATT_MTU update
    CONFIG_BT_GATT_CLIENT=y
    # For data length update
    CONFIG_BT_USER_DATA_LEN_UPDATE=y 
    # Max supported by Nordic Softdevice controller
    CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 
    CONFIG_BT_BUF_ACL_TX_SIZE=251
    CONFIG_BT_BUF_ACL_RX_SIZE=251
    # Maximum supported by Nordic Softdevice controller (Data length - 4)
    CONFIG_BT_L2CAP_TX_MTU=247
    
    CONFIG_POWEROFF=y # Allows going to complete power off
    CONFIG_PM_DEVICE=y
    
    CONFIG_FPU=y
    
    # Support for sensors
    CONFIG_GPIO=y
    CONFIG_I2C=y
    
    # Debugging
    CONFIG_DEBUG_OPTIMIZATIONS=y
    CONFIG_DEBUG_THREAD_INFO=y # Helps to identify stack size issues
    
    CONFIG_MPSL_WORK_STACK_SIZE=2048
    # CONFIG_BT_RX_STACK_SIZE=2048
    
    CONFIG_LOG=y
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_LOG_BACKEND_RTT=y
    CONFIG_LOG_PRINTK=n
    CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
    
    CONFIG_BT_DEBUG_LOG=y

    I created a ble.c file to take care of all BLE related activity

    #include <zephyr/bluetooth/addr.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/gap.h>
    #include <zephyr/bluetooth/gatt.h>
    
    #include "ble.h"
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(ble, CONFIG_APP_LOG_LEVEL);
    
    static struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
        (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_IDENTITY), // No special advertising options
        800,                                                     // Advertising interval set to about 500ms
        801,
        NULL // Peer address;
    );
    
    static const struct bt_data ad[] = {
        BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), // No classic Bluetooth is supported, generally connectable
        BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN)          // Include complete local name in advertising
    };
    
    static const struct bt_data sd[] = {}; // Can hold data for a scan response if applicable
    
    static void update_phy(struct bt_conn *conn)
    {
        int err;
        const struct bt_conn_le_phy_param preferred_phy = {
            .options = BT_CONN_LE_PHY_OPT_CODED_S8, // Each symbol is represented by 8 symbols, 125kbps datarate
            .pref_rx_phy = BT_GAP_LE_PHY_CODED,     
            .pref_tx_phy = BT_GAP_LE_PHY_CODED,
        };
        err = bt_conn_le_phy_update(conn, &preferred_phy);
        if (err)
        {
            LOG_ERR("bt_conn_le_phy_update() returned %d", err);
        }
    }
    
    void mtu_exchange_cb(
        struct bt_conn *conn, uint8_t att_err,
        struct bt_gatt_exchange_params *params)
    {
        if (att_err)
        {
            LOG_ERR("MTU exchange returned with error code %d", att_err);
        }
        else
        {
            LOG_INF("MTU sucessfully set to %d", CONFIG_BT_L2CAP_TX_MTU);
        }
    }
    
    static void request_mtu_exchange(struct bt_conn *conn)
    {
        int err;
        static struct bt_gatt_exchange_params exchange_params;
        exchange_params.func = mtu_exchange_cb;
    
        err = bt_gatt_exchange_mtu(conn, &exchange_params);
        if (err)
        {
            LOG_ERR("MTU exchange failed (err %d)", err);
        }
        else
        {
            LOG_INF("MTU exchange pending ...");
        }
    }
    
    static void request_data_len_update(struct bt_conn *conn)
    {
        int err;
    
        err = bt_conn_le_data_len_update(conn, CONFIG_BT_CTLR_DATA_LENGTH_MAX);
        if (err)
        {
            LOG_ERR("Data length update request failed: %d", err);
            return;
        }
    
        LOG_INF("Data length updated to %d", CONFIG_BT_CTLR_DATA_LENGTH_MAX);
        request_mtu_exchange(conn);
    }
    
    void on_connected(struct bt_conn *conn, uint8_t err)
    {
        if (err)
        {
            LOG_ERR("Connection error %d", err);
            return;
        }
        LOG_INF("Connected");
    
        struct bt_conn_info info;
        err = bt_conn_get_info(conn, &info);
        if (err)
        {
            LOG_ERR("bt_conn_get_info() returned %d", err);
            return;
        }
    
        double connection_interval = info.le.interval * 1.25; // in ms
        uint16_t supervision_timeout = info.le.timeout * 10;  // in ms
        LOG_INF("Connection parameters: interval %.2f ms, latency %d intervals, timeout %d ms", connection_interval, info.le.interval, supervision_timeout);
    
        // update_phy(conn);
        request_data_len_update(conn);
    }
    
    void on_disconnected(struct bt_conn *conn, uint8_t reason)
    {
        LOG_INF("Disconnected, reason %d", reason);
    }
    
    void on_le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout)
    {
        double connection_interval = interval * 1.25; // in ms
        uint16_t supervision_timeout = timeout * 10;  // in ms
        LOG_INF("Connection parameters updated: interval %.2f ms, latency %d intervals, timeout %d ms", connection_interval, latency, supervision_timeout);
    }
    
    void on_le_phy_updated(struct bt_conn *conn, struct bt_conn_le_phy_info *param)
    {
        if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_1M)
        {
            LOG_INF("PHY updated. New PHY: 1M");
        }
        else if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_2M)
        {
            LOG_INF("PHY updated. New PHY: 2M");
        }
        else if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_CODED_S2)
        {
            LOG_INF("PHY updated. New PHY: Long Range (500kbps)");
        }
        else if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_CODED_S8)
        {
            LOG_INF("PHY updated. New PHY: Long Range (125kbps)");
        }
    }
    
    struct bt_conn_cb connection_callbacks = {
        .connected = on_connected,
        .disconnected = on_disconnected,
        .le_param_updated = on_le_param_updated,
        .le_phy_updated = on_le_phy_updated,
    };
    
    /**
     * @brief Initialize the BLE functionality
     *
     * Register the callbacks and enable the BLE stack
     *
     * @return int Error code
     */
    int ble_init()
    {
        // Register connection callbacks
        bt_conn_cb_register(&connection_callbacks);
    
        int err = bt_enable(NULL);
        if (err)
        {
            LOG_ERR("Bluetooth init failed (err %d)\n", err);
            return err;
        }
        LOG_INF("Bluetooth initialized\n");
    
        return 0;
    }
    
    /**
     * @brief Start advertising
     *
     * Start advertising with preset parameters
     *
     * @return int Error code
     */
    int ble_adv_start()
    {
        int err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
        if (err)
        {
            LOG_ERR("Advertising failed to start\n");
            return err;
        }
    
        return 0;
    }

    The call to update_phy is commented out for now because I'm testing with the app on my phone which doesn't seem to support the coded PHY, but I intend to eventually connect to another device which does have a Nordic processor that will allow coded PHY as well.

    Hope this helps to clear things up.

Reply
  • Hi Einar,

    I am using nRF Connect SDK 2.5.0 on a custom board with a nRF52840-QFAA

    These are my relevant configurations

    custom board defconfig

    CONFIG_SOC_SERIES_NRF52X=y
    CONFIG_SOC_NRF52840_QFAA=y
    CONFIG_BOARD_CUSTOM=y
    
    # Enable MPU
    CONFIG_ARM_MPU=y
    
    # Enable hardware stack protection
    CONFIG_HW_STACK_PROTECTION=y
    
    # Enable RTT
    CONFIG_USE_SEGGER_RTT=y
    
    # enable GPIO
    CONFIG_GPIO=y
    
    # enable uart driver
    CONFIG_SERIAL=n
    
    # enable console
    CONFIG_CONSOLE=y
    CONFIG_UART_CONSOLE=n
    CONFIG_RTT_CONSOLE=y
    
    CONFIG_PINCTRL=y

    prj.conf

    CONFIG_BT=y
    CONFIG_BT_PERIPHERAL=y
    CONFIG_BT_DEVICE_NAME="DLE_sample"
    
    CONFIG_BT_PERIPHERAL_PREF_MIN_INT=160
    CONFIG_BT_PERIPHERAL_PREF_MAX_INT=160
    CONFIG_BT_PERIPHERAL_PREF_LATENCY=2
    CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=800
    CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=y
    
    # For requesting ATT_MTU update
    CONFIG_BT_GATT_CLIENT=y
    # For data length update
    CONFIG_BT_USER_DATA_LEN_UPDATE=y 
    # Max supported by Nordic Softdevice controller
    CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 
    CONFIG_BT_BUF_ACL_TX_SIZE=251
    CONFIG_BT_BUF_ACL_RX_SIZE=251
    # Maximum supported by Nordic Softdevice controller (Data length - 4)
    CONFIG_BT_L2CAP_TX_MTU=247
    
    CONFIG_POWEROFF=y # Allows going to complete power off
    CONFIG_PM_DEVICE=y
    
    CONFIG_FPU=y
    
    # Support for sensors
    CONFIG_GPIO=y
    CONFIG_I2C=y
    
    # Debugging
    CONFIG_DEBUG_OPTIMIZATIONS=y
    CONFIG_DEBUG_THREAD_INFO=y # Helps to identify stack size issues
    
    CONFIG_MPSL_WORK_STACK_SIZE=2048
    # CONFIG_BT_RX_STACK_SIZE=2048
    
    CONFIG_LOG=y
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_LOG_BACKEND_RTT=y
    CONFIG_LOG_PRINTK=n
    CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
    
    CONFIG_BT_DEBUG_LOG=y

    I created a ble.c file to take care of all BLE related activity

    #include <zephyr/bluetooth/addr.h>
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/conn.h>
    #include <zephyr/bluetooth/gap.h>
    #include <zephyr/bluetooth/gatt.h>
    
    #include "ble.h"
    
    #include <zephyr/logging/log.h>
    LOG_MODULE_REGISTER(ble, CONFIG_APP_LOG_LEVEL);
    
    static struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
        (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_USE_IDENTITY), // No special advertising options
        800,                                                     // Advertising interval set to about 500ms
        801,
        NULL // Peer address;
    );
    
    static const struct bt_data ad[] = {
        BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), // No classic Bluetooth is supported, generally connectable
        BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN)          // Include complete local name in advertising
    };
    
    static const struct bt_data sd[] = {}; // Can hold data for a scan response if applicable
    
    static void update_phy(struct bt_conn *conn)
    {
        int err;
        const struct bt_conn_le_phy_param preferred_phy = {
            .options = BT_CONN_LE_PHY_OPT_CODED_S8, // Each symbol is represented by 8 symbols, 125kbps datarate
            .pref_rx_phy = BT_GAP_LE_PHY_CODED,     
            .pref_tx_phy = BT_GAP_LE_PHY_CODED,
        };
        err = bt_conn_le_phy_update(conn, &preferred_phy);
        if (err)
        {
            LOG_ERR("bt_conn_le_phy_update() returned %d", err);
        }
    }
    
    void mtu_exchange_cb(
        struct bt_conn *conn, uint8_t att_err,
        struct bt_gatt_exchange_params *params)
    {
        if (att_err)
        {
            LOG_ERR("MTU exchange returned with error code %d", att_err);
        }
        else
        {
            LOG_INF("MTU sucessfully set to %d", CONFIG_BT_L2CAP_TX_MTU);
        }
    }
    
    static void request_mtu_exchange(struct bt_conn *conn)
    {
        int err;
        static struct bt_gatt_exchange_params exchange_params;
        exchange_params.func = mtu_exchange_cb;
    
        err = bt_gatt_exchange_mtu(conn, &exchange_params);
        if (err)
        {
            LOG_ERR("MTU exchange failed (err %d)", err);
        }
        else
        {
            LOG_INF("MTU exchange pending ...");
        }
    }
    
    static void request_data_len_update(struct bt_conn *conn)
    {
        int err;
    
        err = bt_conn_le_data_len_update(conn, CONFIG_BT_CTLR_DATA_LENGTH_MAX);
        if (err)
        {
            LOG_ERR("Data length update request failed: %d", err);
            return;
        }
    
        LOG_INF("Data length updated to %d", CONFIG_BT_CTLR_DATA_LENGTH_MAX);
        request_mtu_exchange(conn);
    }
    
    void on_connected(struct bt_conn *conn, uint8_t err)
    {
        if (err)
        {
            LOG_ERR("Connection error %d", err);
            return;
        }
        LOG_INF("Connected");
    
        struct bt_conn_info info;
        err = bt_conn_get_info(conn, &info);
        if (err)
        {
            LOG_ERR("bt_conn_get_info() returned %d", err);
            return;
        }
    
        double connection_interval = info.le.interval * 1.25; // in ms
        uint16_t supervision_timeout = info.le.timeout * 10;  // in ms
        LOG_INF("Connection parameters: interval %.2f ms, latency %d intervals, timeout %d ms", connection_interval, info.le.interval, supervision_timeout);
    
        // update_phy(conn);
        request_data_len_update(conn);
    }
    
    void on_disconnected(struct bt_conn *conn, uint8_t reason)
    {
        LOG_INF("Disconnected, reason %d", reason);
    }
    
    void on_le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout)
    {
        double connection_interval = interval * 1.25; // in ms
        uint16_t supervision_timeout = timeout * 10;  // in ms
        LOG_INF("Connection parameters updated: interval %.2f ms, latency %d intervals, timeout %d ms", connection_interval, latency, supervision_timeout);
    }
    
    void on_le_phy_updated(struct bt_conn *conn, struct bt_conn_le_phy_info *param)
    {
        if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_1M)
        {
            LOG_INF("PHY updated. New PHY: 1M");
        }
        else if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_2M)
        {
            LOG_INF("PHY updated. New PHY: 2M");
        }
        else if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_CODED_S2)
        {
            LOG_INF("PHY updated. New PHY: Long Range (500kbps)");
        }
        else if (param->tx_phy == BT_CONN_LE_TX_POWER_PHY_CODED_S8)
        {
            LOG_INF("PHY updated. New PHY: Long Range (125kbps)");
        }
    }
    
    struct bt_conn_cb connection_callbacks = {
        .connected = on_connected,
        .disconnected = on_disconnected,
        .le_param_updated = on_le_param_updated,
        .le_phy_updated = on_le_phy_updated,
    };
    
    /**
     * @brief Initialize the BLE functionality
     *
     * Register the callbacks and enable the BLE stack
     *
     * @return int Error code
     */
    int ble_init()
    {
        // Register connection callbacks
        bt_conn_cb_register(&connection_callbacks);
    
        int err = bt_enable(NULL);
        if (err)
        {
            LOG_ERR("Bluetooth init failed (err %d)\n", err);
            return err;
        }
        LOG_INF("Bluetooth initialized\n");
    
        return 0;
    }
    
    /**
     * @brief Start advertising
     *
     * Start advertising with preset parameters
     *
     * @return int Error code
     */
    int ble_adv_start()
    {
        int err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
        if (err)
        {
            LOG_ERR("Advertising failed to start\n");
            return err;
        }
    
        return 0;
    }

    The call to update_phy is commented out for now because I'm testing with the app on my phone which doesn't seem to support the coded PHY, but I intend to eventually connect to another device which does have a Nordic processor that will allow coded PHY as well.

    Hope this helps to clear things up.

Children
Related