In ZigBee networks, power consumption is often a major concern as many device types such as remote controllers, wall sensors, switches and others are expected to be battery-powered.
Independently of its networking status (joined to a network or not), a device can be either in active mode or sleep mode. Based on ZigBee PRO spec, only ZigBee end devices can be put into sleep mode because indirect data delivery via buffering on a parent is defined for end devices only (see Section 5.9).
After being powered up, a node always starts in active mode, with its MCU fully turned on.
In sleep mode, the RF chip and the MCU are put in special low power states and only the functionality required for MCU wake ups remains active. Thus, the application cannot perform any radio Tx/Rx operations, communicate with external periphery, in sleep mode.
This section describes mechanisms implemented in BitCloud for power management.
7.1
Sleep-when-idle
The simplest and the most efficient way to control switching between sleep and active modes is using sleep-when-idle feature implemented in BitCloud.
With this feature BitCloud task scheduler will automatically attempt to put an end device into sleep every time when it has no pending tasks to do (see Section 7.1.1). Stack will also automatically wake up the device to process callbacks for any running timers as described in Section 9.7.
By default sleep-when-idle mode is not started and MCU stays in active state after power up. By calling
SYS_EnableSleepWhenIdle() function application will immediately turn on sleep-when-idle operation mode. To stop it completely SYS_DisableSleepWhenIdle() shall be called. Both functions can be used at any time.
7.1.1 Going into Sleep
If sleep-when-idle mode is running then BitCloud stack will automatically put a device into sleep when there are no pending tasks within the stack or application. Such check is performed in following steps:
•
The first step checks for any posted tasks in the task manager.This is verified during execution of SYS_RunTask() in the infinite loop in main() function (see Section 2.2.1 for more information on task manager operation). If there any posted tasks the stack will keep the device in active mode. As soon as all posted tasks are handled the stack will proceed to step 2.
•
During the second step the stack checks whether BitCloud components and the application are actually ready to go into sleep mode.The stack does this by posting a special BC_EVENT_BUSY_REQUEST event and collecting responses.
Depending on their states and ongoing transactions the stack components and application may not allow going into sleep mode (for example if busy with RF communication or doing some encryption/decryption work, etc.) even when there is no actual task posted for immediate handling.
If an application wants to have additional control on when the device is put into sleep it shall subscribe to the
BC_EVENT_BUSY_REQUEST event and set the value of the data argument accordingly. Here is a code example on how this can be done:
//define event callback function, event receiver and a status flag
static void isAppReadyToSleep(SYS_EventId_t eventId, SYS_EventData_t data); static SYS_EventReceiver_t appBusyEventReceiver = { .func = isAppReadyToSleep}; static bool readyToSleepFlag; // 1 means is ready to sleep and 0 means is not …
// subscribe to the event
SYS_SubscribeToEvent(BC_EVENT_BUSY_REQUEST, &appBusyEventReceiver); …
static void isAppReadyToSleep(SYS_EventId_t eventId, SYS_EventData_t data) {
bool *check = (bool *)data;
if (BC_EVENT_BUSY_REQUEST == eventId) *check &= readyToSleepFlag;
}
Caution: Same as for any other BitCloud events same memory is used for the data argument by all
BC_EVENT_BUSY_REQUEST event receivers. Hence bitwise and operation (&) shall be used in event callback handler to ensure that if a single component is not ready for sleep then the data value stays as false independent on values set in other event callback functions.
•
Finally during the third step the stack components, MCU and RF transceiver are put into sleep mode. Right before going into sleep mode stack calculates sleep duration. The sleep time is set as the time interval left until expiration of the next HAL timer. If there’re no running timers then sleep interval will be set to the value of CS_END_DEVICE_SLEEP_PERIOD parameter. If this parameter is set to 0 then MCU is put into power down mode and will wake up only on external interrupt.7.1.2 Wake up Procedure
There are two ways to wake up a node (that is, to switch from sleep mode to active mode): scheduled and IRQ triggered as described in subsections below.
7.1.2.1 Wake up on a Scheduled Time
In the scheduled approach, a node wakes up automatically after specific time interval. As described in Section 7.1.1 BitCloud stack automatically calculates sleep interval based on the next expiring timer started with
HAL_StartAppTimer() function inside the stack or application. On end devices the BitCloud stack uses timers in a very limited cases, mainly for tracking time of over-the-air transactions that require some response, for periodic parent polling (Section 5.9) and for automatic ZCL attribute reporting (Section 5.5.3.2).
For the application there is no visible difference between cases when a HAL timer expires while device is in sleep state or when it expires during awake state. BitCloud stack will execute the function registered as callback for corresponding timer and no additional steps are expected from the application. The stack is fully operation and ready to perform data exchange if needed.
The application is notified about the scheduled wake up via the ZDO_WakeUpInd() function that is executed prior to the timer callback function that has triggered device wake up. There is no need to perform any additional actions in
ZDO_WakeUpInd() to wake up the stack. It is called for notification purpose as the callback function for the timer might be present inside the stack.
After handling timer callback and related activity the stack will automatically attempt to put device back into sleep following the common procedure described in Section 7.1.1.
While the device is sleeping, the MCU is found in the power save mode, consuming much less power than in the idle state.
7.1.3 Wakeup on an External Hardware Interrupt
In the IRQ triggered approach, the MCU is switched to active mode upon a registered IRQ event (see Section 9.6 for more details on IRQ handling in BitCloud).
Wakeup on an external hardware interrupt is handled in a different way comparing to a wakeup on scheduled time described in Section 7.1.2.1, where BitCloud stack is fully operational when a timer callback is called. The registered external interrupt callback function is executed as part of the interrupt service routine (ISR) and the networking components of the stack are not automatically awaked by that. In order to bring the whole stack back to active
operation, the application must call the ZDO_WakeUpReq() function. After the ZDO_WakeUpConf callback registered for this request returns ZDO_SUCCESS_STATUS, the stack, the RF chip, and the MCU are fully awake and ready for data exchange. Figure 7-1 shows the control flow for an IRQ triggered wakeup.
Figure 7-1. IRQ Triggered Wakeup
Application Stack
* Function registered as a callback in the ZDO_WakeUpReq() argument. Sleep mode
ZDO_WakeUpConf()*
ZDO_SUCCESS_STATUS
ZDO_WakeUpReq() IRQ callback function
...
Here is a code example how this can be done.
//define event callback function and event receiver
static void wakeUpEventHandler (SYS_EventId_t eventId, SYS_EventData_t data); static SYS_EventReceiver_t wakeUpEventListener = {.func = wakeUpEventHandler}; //define stack wake up request and callback function
static ZDO_WakeUpReq_t zdoWakeUpReq;
static void ZDO_WakeUpConf(ZDO_WakeUpConf_t *conf); …
// subscribe to the event
SYS_SubscribeToEvent(HAL_EVENT_WAKING_UP, & wakeUpEventListener); …
//event callback handler
static void wakeUpEventHandler (SYS_EventId_t eventId, SYS_EventData_t data) {
HAL_SleepControl_t *sleepControl = (HAL_SleepControl_t *)data; // wake up the stack if device is awaked on the external interrupt if (HAL_WAKEUP_SOURCE_EXT_IRQ == sleepControl->wakeupSource) { zdoWakeUpReq.ZDO_WakeUpConf = ZDO_WakeUpConf; ZDO_WakeUpReq(&zdoWakeUpReq); } } …
static void ZDO_WakeUpConf(ZDO_WakeUpConf_t *conf) {
if(ZDO_SUCCESS_STATUS == conf->status) …
An IRQ callback must not perform any long operations, such as issuing asynchronous requests. It is also not recommended to call other API functions, since the stack is not fully awakened at that moment.
7.2
Sleep Control without Sleep-when-idle
It is possible for the application to fully control the sleep behavior on the device. In such case sleep-when-idle modes shall be kept disabled (in default or using SYS_DisableSleepWhenIdle() function).
In order to put a device into sleep mode when sleep-when-idle is disabled, an application shall call the
ZDO_SleepReq() function with an argument of the ZDO_SleepReq_t type. After that, the confirmation callback provided with the argument will indicate the execution status, and if ZDO_SUCCESS_STATUS is returned, the node will enter sleep mode after executing the callback. The device will be put into sleep for CS_END_DEVICE_SLEEP_PERIOD interval. After this interval expires the device will wake up and indicate this to the application via ZDO_WakeUpInd() function. All application timers that have expired during the sleep will be triggered upon wake up. If
CS_END_DEVICE_SLEEP_PERIOD is set to 0 then the MCU will be put into power down mode and can wake up on external interrupt only.
Wake up on external interrupt is handled in the same way as when sleep-when-idle mode is enabled (see Section 7.1.3).
7.3
Synchronizing Sleeping End Devices and their Parent Devices
The user application is fully responsible for synchronizing sleeping end devices with sleeping routers and coordinator. Note that the polling mechanism is only implemented for end devices. Data sent to a router or the coordinator while it is sleeping is not cached on its parent automatically like it is for end devices.
Values for sleep periods on end devices and fully functional devices should be chosen carefully. A parent node must be awakened when its sleeping child wakes up, otherwise network connections may become broken.
Special attention should be given to configuring the CS_END_DEVICE_SLEEP_PERIOD parameter on routers and the coordinator, which use it to calculate time estimates for child device wake ups, as described in Section5.9.2.