Logo
DSR GreenBOSS  1.1.0
GreenBOSS Development Guide

Table of Contents

GreenBOSS Application Execution

It is supposed that GPD is a power-limited device running on MCU. If it is battery-less, it can do only limited set of actions until power shortage.

If it is battery-powered, its battery resource is supposed to be low, so the device should be in a deep sleep state generally, consuming minimal energy and usually not retaining RAM. GPD can be waked from the deep sleep by button press, soft reset etc. Anyway it starts from the very beginning of the function main() each time. It means that GPD application starts multiple times from the very beginning of function main (in GreenBOSS it is declared as MAIN).

GPD shall be commissioned to Zigbee network before it can be used.

The commissioning state is stored in NVM in the flash memory. GreenBOSS commissioning API can be used to check the commissioning state and start/continue commissioning.

Application execution flow for Unidirectional operational

Therefore the normal application execution flow is the following:

Application execution flow for Bidirectional operational

GPD implementing Bidirectional in Operational mode can be only battery-powered. In contrast to GPD which is Unidirectional in the Operational mode, it first loops until Commissioning completed then loops infinitely to handle incoming packets from GPS.

So the execution flow is:

NVM Structure

GreenBOSS is designed to use internal MCU flash to store GPD information. It is supposed that flash is erased to -1 (all ones) while write operation clears some bits. Depending on a HW, the number of writes into the same word can vary from 1 (some bits can be set to 0 only in a single write) to 32 (every bit in a word can be set to 0 independently). That setting is platform-dependent and build-configurable.

There are two types of information:

GreenBOSS uses a single eraseable page to store commissioning info. It consists of a header storing commissioning state, channel, key etc, and a bitmap of channel indexes to implement channel cycling. To do decommissioning GreenBOSS erases the commissioning page. Since page erasing is power-consuming operation, at battery-less device page erasing uses power budget of the entire commissioning step.

See also
zb_comm_page_header_t.

Packet numbers are stored as a bitmap using more than one page.

GreenBOSS Application Structure

DSR provides number of application samples (samples/ folder in GreenBOSS core git repository). DSR recommends to base real devices development on that samples. In summary, the application structure is as follows:

Header Files

Any GreenBOSS application shall include header file "gboss_api.h".

Initialization

Application shall define set of constant data structures describing GPD capabilities, OOB key, TX channels set etc.

See the example:

static const zb_uint8_t gs_commands[] = {
GBOSS_APP_CMD_ID_OFF,
GBOSS_APP_CMD_ID_ON,
GBOSS_APP_CMD_ID_TOGGLE
};
static const zb_uint16_t gs_clusters_cli[] = {
};
static const zb_uint16_t gs_clusters_srv[] = {
};
static const gboss_device_config_t g_device_config = {
.manufacturer = 0x1234, /* DSR */
.model = 0x0001,
.src_addr = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88},
.src_id = 0xabacadac,
.application_id = GBOSS_APP_ID_0,
.endpoint = 0x55,
.comm_options = {
.mac_sequence_number_capability = GBOSS_MAC_SEQ_NUM_INCREMENTAL,
.rx_on_capability = ZB_FALSE,
.application_info_present = ZB_TRUE,
.pan_id_request = ZB_FALSE,
.gp_security_key_request = ZB_FALSE,
.fixed_location = ZB_FALSE,
.extended_options_present = ZB_TRUE},
.comm_extended_options = {
.security_level_capabilities = GBOSS_SECURITY_KEY_CAP_L3,
.gpd_key_present = ZB_TRUE,
.gpd_key_encryption = ZB_TRUE,
.gpd_outgoing_counter_present = ZB_TRUE},
.comm_app_info = {
.manuf_id_present = ZB_TRUE,
.model_id_present = ZB_TRUE,
.gpd_commands_present = ZB_TRUE,
.cluster_list_present = ZB_TRUE,
.switch_info_present = ZB_FALSE,
.gpd_app_descr_command_follows = ZB_FALSE},
.cmd_list_count = ZB_ARRAY_SIZE(gs_commands),
.cmd_list = gs_commands,
.cluster_srv_cnt = ZB_ARRAY_SIZE(gs_clusters_srv),
.cluster_list_srv = gs_clusters_srv,
.cluster_cli_cnt = ZB_ARRAY_SIZE(gs_clusters_cli),
.cluster_list_cli = gs_clusters_cli
};

Detecting Button Press

DSR samples use gboss_test_get_button_mask() to check for buttons pressed on application start time. Customers are free to use another method or do not use buttons at all.

See the example:

zb_uint32_t button_mask = 0;
/* Get mask of buttons pressed at power ON time - if multiple are pressed
simultaneously, only the higher index is considered */
button_mask = gboss_test_get_button_mask();
button_id = 31 - MAGIC_CLZ(button_mask);
switch (button_id)
{
case BUTTON_COMMISSIONING:
/* Commissioning button pressed when alredy commissioned - let's decommission this device */
{
gboss_bidir_decommission(&g_device_config, &g_tx_cfg);
break;
}
/* In contrast to batteryless bidir commissioning, here we can do multiple
* iterations until commissioning complete. */
{
gboss_bidir_commissioning_step(&g_device_config, &g_tx_cfg);
/* If can't commission, do not loop there forever */
comm_steps++;
if (comm_steps > COMM_STEPS_LIMIT)
{
break;
}
}
comm_steps = 0;
{
/* If could not commission, go to deep sleep to save the battery */
continue;
}
break;
case BUTTON_ON:
/* Button 1 pressed - send ON */
GBOSS_CMD_ON(&g_device_config, &g_tx_cfg);

Details of Application Run at Network Simulator Platform

When using Network Simulator, each start of GPD is imitated by the next run of the example application process.

When GreenBOSS runs on Network Simulator platform there are no buttons physically present. Buttons press is imitated by the command line (-b N, where N - button number).

When osif_deep_sleep() is called, the application process exits.

DSR provides scripts to run test at Network Simulator named "runng.sh".

Unidirectional Commissioning

The device supporting unidirectional commissioning is supposed to be battery-less. So the device can either do a single commissioning step or a single GPD command TX. GPD enters commissioning mode or performs decommissioning by button press in DSR samples. The real GPD can use another methods. The commissioning step can involve either flash page erasing (in such case GreenBOSS has no power for transmit) or transmitting on number of channels and writing step status into NVM.

See the example of unidirectional commissioning step:

switch (button_id)
{
case BUTTON_COMMISSIONING:
/* Button 0 pressed - commissioning */
gboss_unidir_commissioning_step(&g_device_config, &g_tx_cfg);
break;

GreenBOSS marks unidirectional commissioning as finished when application GPD command TX run after series of commissioning steps.

When the commissioning step is called after one or more application GPD command TX calls, GreenBOSS starts unidirectional commissioning again.

Security Materials

GreenBOSS supports OOB key defined in the application configuration data.

If receiving a key from GPCB is supported (which is configured by the flag in the application configuration data), GPD can receive a key from GPCB and store it in NVM during commissioning. Receiving a key is supported in bidirectional commissioning mode only.

Bidirectional Commissioning at Battery-Powered Device

At battery-powered device GreenBOSS supposes all commissioning steps shall be done in a single run of GPD till completeness. To configure GPD as bidirectional battery-powered device user shall call gboss_bidir_battery_mode(). That call reads NVM, so it shall be placed before gboss_bidir_is_commissionned() call.

gboss_bidir_is_commissionned() can be used to check that GPD is commissioned.

To do bidirectional commissioning GPD shall run gboss_bidir_commissioning_step() in a loop.

See an example:

/* In contrast to batteryless bidir commissioning, here we can do multiple
* iterations until commissioning complete. */
{
gboss_bidir_commissioning_step(&g_device_config, &g_tx_cfg);
/* If can't commission, do not loop there forever */
comm_steps++;
if (comm_steps > COMM_STEPS_LIMIT)
{
break;
}
}
comm_steps = 0;

Bidirectional Commissioning at Battery-Less Device

That type of commissioning is similar to battery-powered bidirectional commissioning, but GPD energy budget is enough only to do a single commissioning step. To configure GPD as bidirectional battery-less device user shall call gboss_bidir_batteryless_mode(). The intermediate commissioning stage is stored in MVM. GPD can check the fact that it is already commissioned. If it is not commissioned, it may run single commissioning step.

See an example:

/* Inform platform layer about buttons number */
/* Get mask of buttons pressed at power ON time - if multiple are pressed
simultaneously, only the higher index is considered */
button_mask = gboss_test_get_button_mask();
button_id = 31 - MAGIC_CLZ(button_mask);
switch (button_id)
{
case BUTTON_COMMISSIONING:
/* Button 0 pressed - commissioning */
{
/* In bidir batteryless we can do only one commissioning iteration a time, then die */
gboss_bidir_commissioning_step(&g_device_config, &g_tx_cfg);
}
break;

Transmitting GPD Commands

Once GPD is started and known to be commissioned, it defines which command is to be set (DSR samples check for buttons, the real application can use another methods). GreenBOSS provides API to send commands, like GBOSS_CMD_ON().

See the example:

case BUTTON_ON:
/* Button 1 pressed - send ON */
GBOSS_CMD_ON(&g_device_config, &g_tx_cfg);
break;

Going to Deep Sleep

It is supposed that battery-powered device finally goes to deep sleep by calling osif_deep_sleep(). That call is not mandatory, the implementer of the real GPD may do something else.

Specifics of Bidirectional in Operational mode

Additional steps for Bidirectional in Operational application

In additional to the actions described in GreenBOSS Application Structure following steps are required:

while(1)
{
TRACE_MSG(TRACE_APP1, "Test loop", (FMT__0));
// Send command with Rx_On=1
TRACE_MSG(TRACE_APP1, "Send success", (FMT__0));
zgpd_timer_set(TX_TIMEOUT);
button_mask = gboss_test_get_button_mask();
button_id = 31 - MAGIC_CLZ(button_mask);
if (button_id == BUTTON_REQUEST_ATTR)
{
TRACE_MSG(TRACE_APP1, "Request attribute", (FMT__0));
ret = GBOSS_CMD_ATTR_REQUEST(&g_device_config, &g_tx_cfg,
ZB_ZCL_ATTRIB_LIST_GET_PTR(power_config_battery_voltage_attrs) + 1);
}
else
{
/* Send a report for POLL Control cluster as a "poll" to GPS. See its
* handling in ZBOSS sample app application/zgp_attr/zgp_attr.c */
GBOSS_CMD_ID_ATTR_REPORT_RX(&g_device_config, &g_tx_cfg,
ZB_ZCL_CLUSTER_ID_POLL_CONTROL, 1, &attr_poll, 1);
}
TRACE_MSG(TRACE_APP1, "Sleep tmo 20", (FMT__0));
zgpd_sleep_tmo(WAIT_TIME);
TRACE_MSG(TRACE_APP1, "Open RX", (FMT__0));
ret = gboss_wait_rx(RX_TIMEOUT, &cmd_id, &payload, &length);
TRACE_MSG(TRACE_APP1, "Opened RX = %d, cmd_id = %d len = %d", (FMT__H_H_H, ret, cmd_id, length));
if (ret == RET_OK)
{
switch(cmd_id)
{
case GBOSS_APP_CMD_ID_WRITE_ATTRIBUTES:
write_attr_handler(payload, length);
break;
case GBOSS_APP_CMD_ID_READ_ATTRIBUTES:
read_attr_handler(payload, length);
break;
default:
break;
}
}
zgpd_sleep_tmo(gpPeriod_msec - (WAIT_TIME + RX_TIMEOUT));
}

Using Attribute Reporting as a "poll"

Green Power specification does not define any command to be used as a replacement of MAC Poll (DATA REQUEST) in the classical Zigbee. Still, it can be necessary for GPD to periodically send some GPDF with RxAfterTx flag 1 to be able to receive some data from GPS. GreenBOSS currently sends Attribute Reporting command on Poll Control cluster as a logical "poll". That command is understood by application/zgp_attr/zgp_attr.c sample in ZBOSS which is used to test Bidirectional in Operational.

/* Send a report for POLL Control cluster as a "poll" to GPS. See its
* handling in ZBOSS sample app application/zgp_attr/zgp_attr.c */
GBOSS_CMD_ID_ATTR_REPORT_RX(&g_device_config, &g_tx_cfg,
ZB_ZCL_CLUSTER_ID_POLL_CONTROL, 1, &attr_poll, 1);

Handling Bidirectional in Operational command in the application

Since GreenBOSS, unlike ZBOSS, does not provide rich framework for the application writer, the application itself must handle commands received from GPS. GreenBOSS provides helper functions for such handling.

The example of getting received cmd:

ret = gboss_wait_rx(RX_TIMEOUT, &cmd_id, &payload, &length);
TRACE_MSG(TRACE_APP1, "Opened RX = %d, cmd_id = %d len = %d", (FMT__H_H_H, ret, cmd_id, length));
if (ret == RET_OK)
{
switch(cmd_id)
{
case GBOSS_APP_CMD_ID_WRITE_ATTRIBUTES:
write_attr_handler(payload, length);
break;
case GBOSS_APP_CMD_ID_READ_ATTRIBUTES:
read_attr_handler(payload, length);
break;
default:
break;
}
}

The example of Read Attribute handling:

static void read_attr_handler(zb_uint8_t *payload, zb_size_t length)
{
zb_uint8_t option;
zb_uint16_t manufactured_id;
zb_size_t hdr_len;
zb_uint16_t cluster_id;
zb_ret_t ret;
if (RET_OK == gboss_handle_header_attr_cmd(payload, length,
&option, &manufactured_id, &hdr_len))
{
payload += hdr_len;
length -= hdr_len;
while (length > 0)
{
hdr_len = gboss_handle_cluster_attr_cmd(payload, length, &cluster_id, &len);
payload += hdr_len;
length -= hdr_len;
ret = RET_INVALID_PARAMETER;
if (cluster_id == ZB_ZCL_CLUSTER_ID_IAS_ZONE)
{
ret = gboss_cmd_attr_read_response(ZB_ZCL_CLUSTER_ID_IAS_ZONE, payload, len,
ZB_ZCL_ATTRIB_LIST_GET_COUNT(ias_zone_zonestatus_attrs),
ZB_ZCL_ATTRIB_LIST_GET_PTR(ias_zone_zonestatus_attrs),
&g_device_config, &g_tx_cfg);
}
else if (cluster_id == ZB_ZCL_CLUSTER_ID_POWER_CONFIG)
{
ret = gboss_cmd_attr_read_response(ZB_ZCL_CLUSTER_ID_POWER_CONFIG, payload, len,
ZB_ZCL_ATTRIB_LIST_GET_COUNT(power_config_battery_voltage_attrs),
ZB_ZCL_ATTRIB_LIST_GET_PTR(power_config_battery_voltage_attrs),
&g_device_config, &g_tx_cfg);
}
if (ret != RET_OK)
{
break;
}
payload += len;
length -= len;
}
}
}

The example of Write Attribute handling:

static void write_attr_handler(zb_uint8_t *payload, zb_size_t length)
{
zb_uint8_t option;
zb_uint16_t manufactured_id;
zb_size_t hdr_len;
zb_uint16_t cluster_id;
zb_ret_t ret;
if (RET_OK == gboss_handle_header_attr_cmd(payload, length,
&option, &manufactured_id, &hdr_len))
{
payload += hdr_len;
length -= hdr_len;
while (length > 0)
{
hdr_len = gboss_handle_cluster_attr_cmd(payload, length, &cluster_id, &len);
payload += hdr_len;
length -= hdr_len;
ret = RET_INVALID_PARAMETER;
if (cluster_id == ZB_ZCL_CLUSTER_ID_IAS_ZONE)
{
ret = gboss_handle_cluster_attr_write(payload, len,
ZB_ZCL_ATTRIB_LIST_GET_COUNT(ias_zone_zonestatus_attrs),
ZB_ZCL_ATTRIB_LIST_GET_PTR(ias_zone_zonestatus_attrs));
}
else if (cluster_id == ZB_ZCL_CLUSTER_ID_POWER_CONFIG)
{
ret = gboss_handle_cluster_attr_write(payload, len,
ZB_ZCL_ATTRIB_LIST_GET_COUNT(power_config_battery_voltage_attrs),
ZB_ZCL_ATTRIB_LIST_GET_PTR(power_config_battery_voltage_attrs));
}
if (ret != RET_OK)
{
break;
}
payload += len;
length -= len;
}
}
}

Going to Sleep with RAM retention

Call zgpd_sleep_tmo() to go into sleep for predefined amount of time with radio off, RAM retention ON. That call is usually used by GPD which implements Bidirectional communication in Operational mode.