How to set transmit power for BLE in nRF Connect SDK?

How can I adjust the BLE transmit (Tx) power, up to and including +8 dBm? I'm using nRF Connect SDK (NCS) on an nRF52840-DK.

I modified the throughput sample to reverse the roles (GAP central becomes GATT server; GAP peripheral becomes GATT client), added timeslots and radio notifications.

I've tried 2 different approaches so far, neither of which fully works. I'm testing by running the code on one nRF52840-DK, pressing Button #2 to make it the peripheral (advertising), and scanning with an nRF52840-Dongle (with "nRF Connect for Desktop Bluetooth Low Energy Standalone v4.0.0") on my PC to see its Rx signal strength.

I see a possible 3rd approach, but the code doesn't make sense to me, and I'm not sure if it applies.

Approach #1a - mpsl_tx_power_channel_map_set(), NCS v2.0.2

This seems to work up to 0 dBm, i.e. I get higher Rx signal strength at the Dongle when DK is set to 0 dBm compared to -20 dBm. But there is no difference in Rx signal strength comparing Tx power set to 0 and 8 dBm.

Approach #1b - mpsl_tx_power_channel_map_set(), NCS v2.1.0-rc1

Doesn't work. No difference in Rx signal strength, regardless of Tx power setting.

Approach #2 - mpsl_tx_power_radio_supported_power_adjust(), NCS v2.1.0-rc1

Doesn't work. No difference in Rx signal strength, regardless of Tx power setting.

Note, this is the first tag where mpsl_tx_power_radio_supported_power_adjust() is provided.

Possible Approach #3 - Imitate HCI Power Control Sample

I haven't tried this. Does this fit my situation, since I'm not using the HCI in my application?

Also, the set_tx_power() function looks like it's setting values (members of the cp struct) as if to use them to control the tx power, but it never uses them. How does this even work?

throughput_mod__no_tx_pwr_ctrl.zip

throughput_mod__approach_1a.zip

throughput_mod__approach_1b.zip

throughput_mod__approach_2.zip

Parents
  • Hi Brendan, 

    Do you use MPSL (Multi Protocol Service Layer) in your application ? You only need MPSL when you have other protocol than BLE and want to run it concurrently with the main protocol then you should use MPSL. 

    If you only use BLE, then actually using HCI command to control TX power is the preferred way. 

    You can find my example here:  How to set TX power to maximum +8dbm for advertising as well as when connected 

    Actually any BLE example would use HCI. It's the communication between the host and the controller in BLE. 

    The issue here is that there isn't an API from the application to the host to control TX power and the application has to access the controller directly to set the TX power. 

  • Do you use MPSL (Multi Protocol Service Layer) in your application ? You only need MPSL when you have other protocol than BLE and want to run it concurrently with the main protocol then you should use MPSL. 

    I am using MPSL for timeslots, to ensure that the radio is not in use for some small periods of time. So controlling the radio power for just BLE, or for all protocols, should be fine for my purposes. As long as it covers BLE in both connected and non-connected (i.e. advertising/scan) states.

    ... I see that your function set_tx_power() is identical to the one in the HCI Power Control sample. (So to tie this back to my original post, you're suggesting my proposed option #3.)

    I tried adding those set_tx_power() and get_tx_power() functions to my code, and calling them like this:

    static int tx_pwr_cmd(const struct shell *shell, size_t argc,
    			char **argv)
    {
    	if (argc == 1) {
    		shell_help(shell);
    		return SHELL_CMD_HELP_PRINTED;
    	}
    
    	if (argc > 2) {
    		shell_error(shell, "%s: bad parameters count", argv[0]);
    		return -EINVAL;
    	}
    
    	int8_t tx_pwr = (int8_t)strtol(argv[1], NULL, 10);
    	int8_t txp_get = 0;
    	set_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV, 0, tx_pwr); //TODO: also set power when connected...
    	get_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV, 0, &txp_get);
    
    	shell_print(shell, "Tx Power set to: %d", (int)txp_get);
    
    	return 0;
    }

    but it doesn't work. I get this output on the console:

    Why isn't this working, or what am I doing wrong?

    Also, should my approaches #1 and #2 work? If so, why aren't they? If not, why not?

    BTW, I forgot to include the boards/nrf52840dk_nrf52840.overlay file in my previous attachments:

    0407.nrf52840dk_nrf52840.overlay

  • You should add build configuration and select the nRF52840 DK like this: 

    That allowed me to build your example, thanks.

    I modified your example's main for loop to set_tx_power() with all values repeatedly. It works for BT_HCI_VS_LL_HANDLE_TYPE_ADV and BT_HCI_VS_LL_HANDLE_TYPE_CONN, but not for BT_HCI_VS_LL_HANDLE_TYPE_SCAN.

    My modification of your example's loop:

    	printk("Advertising successfully started\n");
    
    	for (;;) {
    		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    
    		if ((blink_status % 4) == 0)
    		{
    			printk("\nset_tx_power():\n");
    			printk("\n - BT_HCI_VS_LL_HANDLE_TYPE_ADV...)\n");
    			set_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_ADV,
    							0,
    							8);
    
    			printk("\n - BT_HCI_VS_LL_HANDLE_TYPE_SCAN...)\n");
    			set_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_SCAN,
    							0,
    							8);
    
    			printk("\n - BT_HCI_VS_LL_HANDLE_TYPE_CONN...)\n");
    			set_tx_power(BT_HCI_VS_LL_HANDLE_TYPE_CONN,
    							0,
    							8);
    
    			printk("\n");
    		}
    
    		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    	}

    Console output (same whether connected or not):

    set_tx_power():
    
     - BT_HCI_VS_LL_HANDLE_TYPE_ADV...)
    Actual Tx Power: 8
    
     - BT_HCI_VS_LL_HANDLE_TYPE_SCAN...)
    W: opcode 0xfc0e status 0x12
    Set Tx power err: -5 reason 0x00
    
     - BT_HCI_VS_LL_HANDLE_TYPE_CONN...)
    Actual Tx Power: 8

    That is similar to how in my previous reply ("approach 3.1"), set_tx_power() works for 2/3 of the handle_types (but it fails for BT_HCI_VS_LL_HANDLE_TYPE_CONN, not BT_HCI_VS_LL_HANDLE_TYPE_SCAN like here).

    Why are these not working for all handle_types?

  • Hi Brendan, 

    What do you expect when control the BT_HCI_VS_LL_HANDLE_TYPE_SCAN ? Which activity you are looking at ? 
    As far as know BT_HCI_VS_LL_HANDLE_TYPE_SCAN only applied if you are doing active scanning in observer (scanner) role. 

  • What do you expect when control the BT_HCI_VS_LL_HANDLE_TYPE_SCAN ?

    I expect to be able to set the power for any given mode before I enter that mode, so that specified power level is always used in that mode, unless/until it is commanded otherwise. Is this not how it works?

    Which activity you are looking at ?

    I'm not sure what you mean. The device is a GAP peripheral and GATT client, if that's relevant.

    ... Zooming out, going back to my original post: will I be able to use this set_tx_power() function for dynamic power control, and also use the MPSL for timeslots? If these will not work together, then shouldn't we be working on getting my "Approach 1 or 2" working instead of this (which I called "Approach 3")?

  • Hi Brendan, 

    BT_HCI_VS_LL_HANDLE_TYPE_SCAN  only applied for when you do scanning (send scan request), not when you do advertising. If you want that mode you need to enable Observer role. 

    Please test and verify that you can change the txpower when you advertise and when you are connected. 

    bmcdonnell-npm said:
    will I be able to use this set_tx_power() function for dynamic power control, and also use the MPSL for timeslots?

    Yes.

  • Please test and verify that you can change the txpower when you advertise and when you are connected. 

    Already verified using your example, but it does not work when connected in my attached code "approach 3.1". What is wrong there?

Reply Children
No Data
Related