Bluetooth Low Energy (a.k.a Bluetooth LE, BTLE, Bluetooth Smart)

Low Power Credentials of the current BLE API

23 Nov 2014

Here's an update on the power consumption profile when using the BLE API. It's a topic of considerable importance for BLE; and we target the best that the radio controller has to offer. It is our goal that the power consumed by the BLE stack (together with BLE API) will be dwarfed by the overhead of driving the radio.

Measuring power can be tricky with BLE because of the large dynamic range involved; the baseline currents are around 6-10uAmps while the BLE stack is idling, but consumption jumps to 10-15mAmps when the radio is used. That's a variance of 3 orders of magnitude. Traditional methods such as measuring the voltage drop across a sense resistor don't have enough of a dynamic range, and so don't work very well. In some cases useful measurements can be obtained as averages (some multi-meters offer reasonably accurate estimates of average currents through the use of min-max-avg modes); else through the use of auto-ranging oscilloscopes.

Some of the dev boards (such as the mkit) pose a further challenge in the form of introducing the mbed interface-chip into the equation. Measuring power by observing what's fed into the USB input to the board gives misleading numbers due to the overheads of the supporting circuitry. In those cases, it becomes necessary to isolate the extraneous components through appropriate jumpers and schematic-study; and by switching to battery supply instead of USB. Measuring from a custom board with minimal components (or through the use of something like the smart-beacon kit) is another successful strategy.

Using the smart beacon kit (where the inteface chip is absent), the BLE_Beacon example on mbed consumes 35uA of average current. Here's a power profile for that use-case.

/media/uploads/rgrover1/advertisingpowerprofileonsmartbeacon.png

The vertical axis in this figure is the current consumption (in milli-amps), and the horizontal axis shows time. This picture captures nearly 1 second of activity. The major peak is an advertising event where a beacon is broadcast; the minor peak comes from periodic housekeeping activities of the bluetooth stack. Outside the peaks, current consumption is around 6uAmps (when measured using instruments with 0.1uA precision); as can be seen from the instantaneous current value of 0.01mA.

The primary contribution to power consumption comes from the use of the radio. Please note that the radio peaks at 12-15mA of current for around 2ms during every advertisement event (where advertising payload must be sent out over the three advertising channels and then the radio is run a little longer to look for scan-requests or connection requests). In the default beacon example, advertisements are sent out at 1Hz (once every second) and at a power level of 0dB; both of these factors are under the control of the developer. This should give you an idea of what's possible.

The average current, highlighted with a red underline on the left panel, is around 35uAmps; and with a coin cell battery the system may be expected to last around 7000 hours (which is nearly a year).

Here are some of the strategies we've employed on the Nordic nRF51822 to lower baseline power consumption:

- The system now starts with low-frequency clock by default (instead of using the external 16MHz clock). The high frequency clock is still kicked into action automatically by the soft-device when needed; but now it doesn't remain active all the time. This by itself lowers the baseline consumption significantly. It also means that if an application needs the high- frequency clock (such as for any high-speed serial communication), then it must enable the external clock source explicitly; and hopefully the external clock will be managed in a power-conscious manner, which means that clocks will be turned off when not needed.

- We make an effort to avoid using wait() API from mbed. This API is currently synchronous and wastes a lot of power.

- We've also re-implemented the Ticker APIs to use the RTC instead of a high- frequency timer. This makes a tradeoff between precision and power; and we believe power trumps over precision in the context of BLE.

It's worth re-emphasizing this. Users are free to enable high-frequency timers explicitly if needed. If an application requires the use of high-frequency clock (for instance to do serial communication at high baud rates), then it must enable the high-frequency clock by writing into the appropriate control registers (please refer to the datasheets of the nRF51822). It would help to turn off high-frequency clocks at the earliest opportunity.

Users may also experiment with reducing the transmit power of their radio; there's an API for this.

Connection intervals and connection latency are also available for modification, and play a very important role in power consumption.

Please try using the following API once a connection is made in order to request the central to update the settings:

BLEDevice::updateConnectionParams(Gap::Handle_t handle, const Gap::ConnectionParams_t *params)

It is ultimately up to the central/master to choose the connection parameters. Your requested settings may not be honored.

Please take notice of the following comment from ble_gap.h (under nrf- sdk/s110) and also pay attention to the constraint mentioned mentioned at the end:

/**@brief Update connection parameters.
 *
 * @details In the central role this will initiate a Link Layer connection parameter update procedure,
 *          otherwise in the peripheral role, this will send the corresponding L2CAP request and wait for
 *          the central to perform the procedure. In both cases, and regardless of success or failure, the application
 *          will be informed of the result with a @ref BLE_GAP_EVT_CONN_PARAM_UPDATE event.
 *
 * @note If both a connection supervision timeout and a maximum connection interval are specified, then the following constraint
 *       applies: (conn_sup_timeout * 8) >= (max_conn_interval * (slave_latency + 1))
...

Here's one user's attempt to reduce connection intervals for the heart-rate demo after a connection has been established:

#define MIN_CONN_INTERVAL               MSEC_TO_UNITS(379, UNIT_1_25_MS)              /**< Minimum connection interval (379 ms) */
#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(399, UNIT_1_25_MS)              /**< Maximum connection interval (399 ms). */
#define SLAVE_LATENCY                   4                                             /**< Slave latency. */
#define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(6000, UNIT_10_MS)               /**< Connection supervisory timeout (6 seconds). */

void onConnectionCallback(Gap::Handle_t handle, const Gap::ConnectionParams_t *p_conn_param)
{
    Gap::ConnectionParams_t gap_conn_params;
    gap_conn_params.minConnectionInterval = MIN_CONN_INTERVAL;
    gap_conn_params.maxConnectionInterval = MAX_CONN_INTERVAL;
    gap_conn_params.slaveLatency = SLAVE_LATENCY;
    gap_conn_params.connectionSupervisionTimeout = CONN_SUP_TIMEOUT;
    ble.updateConnectionParams(handle, &gap_conn_params);
}

...

The mbed team is also working towards asynchronous (non-blocking, interrupt- driven) APIs; automatic, smart management of clocks; and the use of DMA. Once these are in place, even non-trivial applications should be able to exhibit excellent power profiles.

13 Oct 2014

Quote:

- We've also re-implemented the Ticker APIs to use the RTC instead of a high- frequency timer. This makes a tradeoff between precision and power; and we believe power trumps over precision in the context of BLE.

Hi Rohit,

have you seen the reports that the 'new' Ticker is broken. You cant use more than 2 or the program crashes and you dont seem to be able to detach/re-attach. See http://developer.mbed.org/questions/4827/Ticker-problem-when-using-more-than-2-Ti/ and http://developer.mbed.org/questions/4461/nRF51822-Ticker/ and http://developer.mbed.org/forum/bugs-suggestions/topic/5136/ I am also seeing problems in iBeacon example. It gets stuck in ble.waitForEvent() unless I add a Ticker somewhere. Issues happen with mbed Rev 89 and dont show in Rev 88.

07 Nov 2014

The problems reported about the TIcker APIs have been fixed.

23 Nov 2014

Thank you for the great article. One remark & a question: Beacon and Heart Rate examples are nicely power optimized. Their consumption value is well in the range of micro amps. However the UART Loopback example consumes constantly over 1 mA when connected (of course without debug output, so the reason can not be the real UART HW peripheral). I could not find out the reason, but it seems to lie within the BLE UART service implementation, because it increases the consumption when I add it to any BLE project. Regards...

28 Nov 2014

I've updated the LoopbackUART to consume less power in the connected state. The problem had to do with the use of Stream() as a base class for UARTService. Instantiating a Stream() was causing an fopen() for stdin/out/err filehandles, and that was opening up the console uart. Initializing the UART caused a constant drain of 1mA.

Please update.

28 Nov 2014

Great! Thank you very much. I will try it very soon.

30 Nov 2014

Removing the stream class broke the UART console redirect. Is there any way to make this feature optional to allow the use of retargetStdout? This is a handy debug tool when power consumption is not critical.

01 Dec 2014

I tried hard to preserve Stream() as a base class. Unfortunately, Stream needs to use fopen(), and then the C library makes many assumptions about standard POSIX filehandles and causes an initialization of console uart.

You could still use the .write() method offered by UARTService. Is that a useful workaround?

02 Dec 2014

Yes, the .write() method is acceptable. It would be nice to have an example. The documentation for some of the services is a little light.

03 Dec 2014

Rohit Grover wrote:

I tried hard to preserve Stream() as a base class. Unfortunately, Stream needs to use fopen(), and then the C library makes many assumptions about standard POSIX filehandles and causes an initialization of console uart.

You could still use the .write() method offered by UARTService. Is that a useful workaround?

I would like to second what @Greg said. The docs are a bit all over the place right now, and the article you wrote (http://developer.mbed.org/forum/team-63-Bluetooth-Low-Energy-community/topic/5169/) still references using retargetStdout. Could you please update it with a few examples?

Thanks very much!