I am using the cdc_acm example and have added a command to set and read tx power. I am trying to use the following commands for this.
I am using the cdc_acm example and have added a command to set and read tx power. I am trying to use the following commands for this.
Hi,
You can refer to the HCI Power Control sample for how to set the Tx power via HCI.
I have referred to this and get the same issue when using the method from this sample
I see. The same should work and does so om my end. Can you start by copy-pasting in the exact implementation from the sample and see that it works before you start modifyin things? Does it still fail? If so, can you share a simple but complete project I can test on my end that fails so that I can have a look? (as source code/text, not screenshots)
I see. The same should work and does so om my end. Can you start by copy-pasting in the exact implementation from the sample and see that it works before you start modifyin things? Does it still fail? If so, can you share a simple but complete project I can test on my end that fails so that I can have a look? (as source code/text, not screenshots)
Hello Einar,
I have tried to copy and paste the implementation directly from the hci power control but still failing. I have the function outside of the shell command and then call it in the shell command with the value for tx power as the argument. I've attached my source code, the main.c has the implementation for the tx_power at the start of the code
/* * Copyright (c) 2019 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Sample echo app for CDC ACM class with BLE advertising and TX power control * * Sample app for USB CDC ACM class driver. The received data is echoed back * to the serial port. Additionally, it supports BLE advertising and TX power control * via shell commands. */ #include <stdio.h> #include <string.h> #include <stdint.h> #include <zephyr/device.h> #include <zephyr/drivers/uart.h> #include <zephyr/kernel.h> #include <zephyr/sys/printk.h> #include <zephyr/sys/ring_buffer.h> #include <zephyr/usb/usb_device.h> #include <zephyr/usb/usbd.h> #include <zephyr/logging/log.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/hci.h> #include <zephyr/shell/shell.h> #include <C:\ncs\v2.6.1\modules\hal\nordic\nrfx\hal\nrf_radio.h> #include <zephyr/bluetooth/hci_vs.h> #include <zephyr/bluetooth/conn.h> LOG_MODULE_REGISTER(cdc_acm_echo, LOG_LEVEL_INF); #define RING_BUF_SIZE 1024 uint8_t ring_buffer[RING_BUF_SIZE]; struct ring_buf ringbuf; static bool rx_throttled; static struct bt_conn *default_conn; static uint16_t default_conn_handle; #if defined(CONFIG_USB_DEVICE_STACK_NEXT) USBD_CONFIGURATION_DEFINE(config_1, USB_SCD_SELF_POWERED, 200); USBD_DESC_LANG_DEFINE(sample_lang); USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, "ZEPHYR"); USBD_DESC_PRODUCT_DEFINE(sample_product, "Zephyr USBD CDC ACM"); USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn, "0123456789ABCDEF"); USBD_DEVICE_DEFINE(sample_usbd, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), 0x2fe3, 0x0001); static int enable_usb_device_next(void) { int err; err = usbd_add_descriptor(&sample_usbd, &sample_lang); if (err) { LOG_ERR("Failed to initialize language descriptor (%d)", err); return err; } err = usbd_add_descriptor(&sample_usbd, &sample_mfr); if (err) { LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err); return err; } err = usbd_add_descriptor(&sample_usbd, &sample_product); if (err) { LOG_ERR("Failed to initialize product descriptor (%d)", err); return err; } err = usbd_add_descriptor(&sample_usbd, &sample_sn); if (err) { LOG_ERR("Failed to initialize SN descriptor (%d)", err); return err; } err = usbd_add_configuration(&sample_usbd, &config_1); if (err) { LOG_ERR("Failed to add configuration (%d)", err); return err; } err = usbd_register_class(&sample_usbd, "cdc_acm_0", 1); if (err) { LOG_ERR("Failed to register CDC ACM class (%d)", err); return err; } err = usbd_init(&sample_usbd); if (err) { LOG_ERR("Failed to initialize device support"); return err; } err = usbd_enable(&sample_usbd); if (err) { LOG_ERR("Failed to enable device support"); return err; } LOG_DBG("USB device support enabled"); return 0; } #endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK_NEXT) */ static int set_tx_power(uint8_t handle_type, uint16_t handle, int8_t tx_pwr_lvl) { struct bt_hci_cp_vs_write_tx_power_level *cp; struct bt_hci_rp_vs_write_tx_power_level *rp; struct net_buf *buf, *rsp = NULL; int err; buf = bt_hci_cmd_create(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL, sizeof(*cp)); if (!buf) { printk("Unable to allocate command buffer\n"); return; } cp = net_buf_add(buf, sizeof(*cp)); cp->handle = sys_cpu_to_le16(handle); cp->handle_type = handle_type; cp->tx_power_level = tx_pwr_lvl; err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL, buf, &rsp); if (err) { uint8_t reason = rsp ? ((struct bt_hci_rp_vs_write_tx_power_level *) rsp->data)->status : 0; printk("Set Tx power err: %d reason 0x%02x\n", err, reason); return; } rp = (void *)rsp->data; printk("Actual Tx Power: %d\n", rp->selected_tx_power); net_buf_unref(rsp); } static void get_tx_power(uint8_t handle_type, uint16_t handle, int8_t *tx_pwr_lvl) { struct bt_hci_cp_vs_read_tx_power_level *cp; struct bt_hci_rp_vs_read_tx_power_level *rp; struct net_buf *buf, *rsp = NULL; int err; *tx_pwr_lvl = 0xFF; buf = bt_hci_cmd_create(BT_HCI_OP_VS_READ_TX_POWER_LEVEL, sizeof(*cp)); if (!buf) { printk("Unable to allocate command buffer\n"); return; } cp = net_buf_add(buf, sizeof(*cp)); cp->handle = sys_cpu_to_le16(handle); cp->handle_type = handle_type; err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_TX_POWER_LEVEL, buf, &rsp); if (err) { uint8_t reason = rsp ? ((struct bt_hci_rp_vs_read_tx_power_level *) rsp->data)->status : 0; printk("Read Tx power err: %d reason 0x%02x\n", err, reason); return; } rp = (void *)rsp->data; *tx_pwr_lvl = rp->tx_power_level; net_buf_unref(rsp); } static int cmd_set_tx_pwr(const struct shell *shell, size_t argc, char **argv) { int8_t tx_power; struct net_buf *buf, *rsp; int err; if (argc < 2) { shell_error(shell, "Usage: tx_power <value>"); return -EINVAL; } // Print the received argument shell_print(shell, "Received argument: %s", argv[1]); // Convert the argument to an integer tx_power = atoi(argv[1]); // Print the parsed value shell_print(shell, "Parsed TX power value: %d", tx_power); // Check if tx_power is within the acceptable range if (tx_power < -40 || tx_power > 8) { // Adjust range based on your chip shell_error(shell, "TX power must be between -40 and +8 dBm"); return -EINVAL; } LOG_INF("Setting TX power to %d dBm", tx_power); // Send the command and wait for a response set_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_CONN, default_conn_handle, tx_power); if (err) { shell_error(shell, "Failed to set TX power, error: %d", err); return -EIO; } shell_print(shell, "TX power set to %d dBm", tx_power); return 0; } static void nrf_radio_ook_configure(bool enable) { if (enable) { // Configure the radio to transmit continuously (OOK "on") NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm << RADIO_TXPOWER_TXPOWER_Pos; NRF_RADIO->MODE = RADIO_MODE_MODE_Nrf_1Mbit << RADIO_MODE_MODE_Pos; // Set packet configuration for continuous transmission NRF_RADIO->PCNF0 = (8 << RADIO_PCNF0_LFLEN_Pos); // 8-bit length field NRF_RADIO->PCNF1 = (1 << RADIO_PCNF1_MAXLEN_Pos); // Max length 1 byte NRF_RADIO->PACKETPTR = (uint32_t)&(uint8_t){0xFF}; // Send 0xFF for continuous "on" // Start transmitting NRF_RADIO->TASKS_TXEN = 1; } else { // Disable radio to stop transmission (OOK "off") NRF_RADIO->TASKS_DISABLE = 1; } } /* Shell command to toggle OOK (On-Off Keying) */ static int cmd_toggle_ook(const struct shell *shell, size_t argc, char **argv) { static bool ook_enabled = false; // Parse the command argument if (argc > 1) { if (strcmp(argv[1], "on") == 0) { ook_enabled = true; } else if (strcmp(argv[1], "off") == 0) { ook_enabled = false; } else { shell_error(shell, "Invalid argument. Use 'on' or 'off'."); return -EINVAL; } } else { shell_error(shell, "Usage: ook <on|off>"); return -EINVAL; } if (ook_enabled) { LOG_INF("Enabling OOK"); nrf_radio_ook_configure(true); // Enable OOK (start transmission) } else { LOG_INF("Disabling OOK"); nrf_radio_ook_configure(false); // Disable OOK (stop transmission) } shell_print(shell, "OOK %s", ook_enabled ? "enabled" : "disabled"); return 0; } /* Shell command to start advertising */ static int cmd_start_adv(const struct shell *shell, size_t argc, char **argv) { LOG_INF("Starting BLE advertising"); int err = bt_enable(NULL); if (err) { printk("Bluetooth init failed (err %d)\n", err); return; } printk("Bluetooth initialized\n"); struct bt_le_adv_param adv_param = { .options = BT_LE_ADV_OPT_CONNECTABLE, .interval_min = BT_GAP_ADV_SLOW_INT_MIN, .interval_max = BT_GAP_ADV_SLOW_INT_MAX, .peer = NULL }; err = bt_le_adv_start(&adv_param, NULL, 0, NULL, 0); if (err) { shell_error(shell, "Failed to start advertising: %d", err); return err; } shell_print(shell, "Advertising started"); return 0; } /* Shell command to stop advertising */ static int cmd_stop_adv(const struct shell *shell, size_t argc, char **argv) { int err = bt_le_adv_stop(); if (err) { shell_error(shell, "Failed to stop advertising: %d", err); return err; } shell_print(shell, "Advertising stopped"); return 0; } /* Shell commands */ // Define a static subcommand set for Bluetooth commands SHELL_STATIC_SUBCMD_SET_CREATE(sub_ble); // Register the tx_power command SHELL_CMD_ARG_REGISTER(tx_power, NULL, "Set TX power <value>", cmd_set_tx_pwr, 2, 0); // Register the read_power command //SHELL_CMD_ARG_REGISTER(rd_power, NULL, "Read Current Tx power", cmd_get_tx_power, 1, 0); // Register the start_adv command SHELL_CMD_ARG_REGISTER(start_adv, NULL, "Start BLE advertising", cmd_start_adv, 1, 0); // Register the stop_adv command SHELL_CMD_ARG_REGISTER(stop_adv, NULL, "Stop BLE advertising", cmd_stop_adv, 1, 0); SHELL_CMD_ARG_REGISTER(ook, NULL, "Toggle OOK <on|off>", cmd_toggle_ook, 2, 0); // Register the main Bluetooth command with the subcommands SHELL_CMD_REGISTER(ble, &sub_ble, "Bluetooth commands", NULL); static void interrupt_handler(const struct device *dev, void *user_data) { ARG_UNUSED(user_data); while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { if (!rx_throttled && uart_irq_rx_ready(dev)) { int recv_len, rb_len; uint8_t buffer[64]; size_t len = MIN(ring_buf_space_get(&ringbuf), sizeof(buffer)); if (len == 0) { /* Throttle because ring buffer is full */ uart_irq_rx_disable(dev); rx_throttled = true; continue; } recv_len = uart_fifo_read(dev, buffer, len); if (recv_len < 0) { LOG_ERR("Failed to read UART FIFO"); recv_len = 0; }; rb_len = ring_buf_put(&ringbuf, buffer, recv_len); if (rb_len < recv_len) { LOG_ERR("Drop %u bytes", recv_len - rb_len); } LOG_DBG("tty fifo -> ringbuf %d bytes", rb_len); if (rb_len) { uart_irq_tx_enable(dev); } } if (uart_irq_tx_ready(dev)) { uint8_t buffer[64]; int rb_len, send_len; rb_len = ring_buf_get(&ringbuf, buffer, sizeof(buffer)); if (!rb_len) { LOG_DBG("Ring buffer empty, disable TX IRQ"); uart_irq_tx_disable(dev); continue; } if (rx_throttled) { uart_irq_rx_enable(dev); rx_throttled = false; } send_len = uart_fifo_fill(dev, buffer, rb_len); if (send_len < rb_len) { LOG_ERR("Drop %d bytes", rb_len - send_len); } LOG_DBG("ringbuf -> tty fifo %d bytes", send_len); } } } void main(void) { const struct device *dev; uint32_t baudrate, dtr = 0U; int ret; dev = DEVICE_DT_GET_ONE(zephyr_cdc_acm_uart); if (!device_is_ready(dev)) { LOG_ERR("CDC ACM device not ready"); return; } #if defined(CONFIG_USB_DEVICE_STACK_NEXT) ret = enable_usb_device_next(); #else ret = usb_enable(NULL); #endif if (ret != 0) { LOG_ERR("Failed to enable USB"); return; } ring_buf_init(&ringbuf, sizeof(ring_buffer), ring_buffer); LOG_INF("Wait for DTR"); while (true) { uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr); if (dtr) { break; } else { /* Give CPU resources to low priority threads. */ k_sleep(K_MSEC(100)); } } LOG_INF("DTR set"); /* They are optional, we use them to test the interrupt endpoint */ ret = uart_line_ctrl_set(dev, UART_LINE_CTRL_DCD, 1); if (ret) { LOG_WRN("Failed to set DCD, ret code %d", ret); } ret = uart_line_ctrl_set(dev, UART_LINE_CTRL_DSR, 1); if (ret) { LOG_WRN("Failed to set DSR, ret code %d", ret); } /* Wait 100ms for the host to do all settings */ k_msleep(100); ret = uart_line_ctrl_get(dev, UART_LINE_CTRL_BAUD_RATE, &baudrate); if (ret) { LOG_WRN("Failed to get baudrate, ret code %d", ret); } else { LOG_INF("Baudrate detected: %d", baudrate); } /* Initialize Bluetooth */ if (bt_enable(NULL)) { LOG_ERR("Failed to enable Bluetooth"); return; } uart_irq_callback_set(dev, interrupt_handler); /* Enable rx interrupts */ uart_irq_rx_enable(dev); }
# Enable USB device stack CONFIG_USB_DEVICE_STACK=y CONFIG_USB_DEVICE_PRODUCT="nRF52840 Dongle" # Enable USB CDC ACM (UART over USB) CONFIG_UART_LINE_CTRL=y CONFIG_UART_CONSOLE=y CONFIG_USB_CDC_ACM=y CONFIG_UART_INTERRUPT_DRIVEN=y # Enable Bluetooth functionality CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_DEVICE_NAME="nRF52840 Dongle" CONFIG_BT_SHELL=y CONFIG_BT_HCI_VS_EXT=y # Enable TX power control CONFIG_BT_HCI=y CONFIG_BT_HCI_VS=y CONFIG_BT_CTLR=y CONFIG_BT_BROADCASTER=y CONFIG_BT_CENTRAL=y CONFIG_BT_LL_SOFTDEVICE=y CONFIG_BT_LL_SW_SPLIT=y CONFIG_BT_CTLR_TX_PWR_DYNAMIC_CONTROL=y CONFIG_BT_CTLR_ADVANCED_FEATURES=y CONFIG_BT_CTLR_CONN_RSSI=y # Shell configuration CONFIG_SHELL=y # Enable console CONFIG_CONSOLE=y # Power management settings CONFIG_PM_DEVICE=y #Enable logging CONFIG_LOG=y
Hi,
There is one thing sticking out here. You are setting BT_HCI_VS_LL_HANDLE_TYPE_CONN and passing a handle that is never set. To set the power for a connection, you need to provide the handle to a active connection. Perhaps you should have set the advertising Tx power? That is done by BT_HCI_VS_LL_HANDLE_TYPE_ADV (and any subsequent resulting connection will inherit that Tx power). This is also demonstrated in the hci_pwer_ctrl sample.