Internals of the embeNET demo for RC-CC1312R-868 modules

Table of contents Table of contents

This document describes the internals of the embeNET demo application running on RC-CC1312R-868 modules and provides information on how to modify and extend the demo.

How does the demo application work

The demo application uses the embeNET Node C API library to join the network and run two network services:

  • ENMS: embeNET Network Management service that allows to gather information about the network operation from the nodes
  • custom exemplary service, that sends out a simple message every 5 seconds and also reacts to some simple messages

NoRTOS operation

For simplicity, the demo is prepared without using any operating system. It uses TI's NoRTOS framework module.

Logging is available only in nodes

You will find several places in the code using printf that print out diagnostic messages. The SmartRF06 has one UARTs routed to COM port when the board gets connected to a PC. In root node this port is used for communication with the border router application running on PC. This means that for root node there is no easy way of printing the log output and so this output is not enabled. By default the log through this UART is only configured in nodes (when IS_ROOT=0).

Initialization

First we initialize the board and its basic peripherals:

Board_initGeneral();
GPIO_init();

Next we configure UART2 for logging (but only for nodes, as the root uses it to communicate with host). Then we start the NoRTOS framework:

#if 1 != IS_ROOT
// enable logging over UART2, but only for nodes
enable_logging();
#endif
NoRTOS_start();

Next the embeNET stack is initialized:

// Initialize structure to provide Stack with user-defined event handlers
EMBENET_NODE_EventHandlers handlers = {
.onJoined = onJoined,
.onLeft = onLeft,
.onJoinAttempt = onJoinAttempt,
.onDataOnUnregisteredPort = dataOnUregisteredPort,
.onQuickJoinCredentialsObsolete = onQuickJoinCredentialsObsolete
};
// Initialize network stack
if (EMBENET_RESULT_OK == EMBENET_NODE_Init(&handlers)) {
printf("embeNET Node initialized\n");
} else {
printf("Failed to initialize embeNET Node\n");
}

After that the ENMS service is initialized:

// Construct 128-bit hardware ID using 64-bit UID (here actually 802.15.4 MAC Address)
uint8_t hardwareId[16] = {0x00};
uint64_t uid = EMBENET_EUI64_Get();
memcpy(hardwareId+8, &uid, sizeof(uid));
// Initialize ENMS service on its default port. You may specify custom Hardware Identifier and firmware version
if (ENMS_NODE_RESULT_OK == ENMS_NODE_Init(&enmsNode, ENMS_DEFAULT_PORT, hardwareId, 1, 0, 0)) {
printf("ENMS service initialized\n");
} else {
printf("Failed to initialize ENMS service!\n");
}

In case of root we just start it:

EMBENET_NODE_RootStart(NULL, 0);

In case of nodes we start the custom demo service and MQTT-SN service:

// Initialize exemplary, user-defined custom service
custom_service_init();
// Initialize MQTT-SN service
mqttsn_client_service_init();
// Additionally tell the ENMS what services are running
(void) ENMS_NODE_RegisterService(&enmsNode, "custom", 1);
(void) ENMS_NODE_RegisterService(&enmsNode, "mqttsn", 1);

Joining the network

In order to join the network the node has to setup the required network configuration:

// embeNET network configuration:
// K1 key, used to authenticate the network node should join and
// PSK - Node's secret key.
// Note that the psk value should be preferably stored in secure memory, or be preloaded using custom bootloader.
EMBENET_NODE_Config config = {
.k1.val = { 0xc0, 0x8b, 0x76, 0x62, 0x77, 0x09, 0x9e, 0x7d, 0x7e, 0x9c, 0x02, 0x22, 0xf1, 0x68, 0xcc, 0x9e },
.psk.val = { 0x46, 0xd7, 0xdc, 0x94, 0xe8, 0xee, 0x74, 0x96, 0xce, 0xaf, 0x54, 0xa3, 0xab, 0x64, 0xcb, 0xeb },
};
// Make the node join the network
EMBENET_NODE_Join(&config);

The main loop

The application's main loop consists of a call to:

  • embeNET Node processing function
  • MQTT-SN service process (only needed for polling button status)
/* Infinite loop */
while (1) {
// Periodically call embeNET Node process function.
EMBENET_NODE_Proc();
// Periodically call MQTT-SN service process.
mqttsn_client_service_proc();
}

Network join event

Once the node joins the network an event callback will be called. In our demo application this callback simply starts the network services:

static void onJoined(EMBENET_PANID panId, const EMBENET_NODE_QuickJoinCredentials *quickJoinCredentials) {
printf("Joined network with PANID: x%04" PRIx16 "\n", panId);
printf("Starting ENMS service\n");
// Start ENMS Service that provides network-wide telemetry information
EnmsNodeResult enmsStartStatus = ENMS_NODE_Start(&enmsNode);
if (ENMS_NODE_RESULT_OK != enmsStartStatus) {
printf("ENMS service failed to start with status %d\n", (int) enmsStartStatus);
}
// Start exemplary, user-defined custom service
custom_service_start();
// Start MQTT-SN demo service
mqttsn_client_service_start();
}

Network leave event

In case when node gets disconnected from the network, another callback is called. In our demo application this callback stops the network services:

static void onLeft(void) {
printf("Node has left the network\n");
// Stop ENMS service
EnmsNodeResult enmsStopStatus = ENMS_NODE_Stop(&enmsNode);
if (ENMS_NODE_RESULT_OK == enmsStopStatus) {
printf("ENMS service stopped\n");
} else {
printf("ENMS service failed to stop with status %d\n", (int)enmsStopStatus);
}
// Stop exemplary, user-defined custom service
custom_service_stop();
// Stop MQTT-SN demo service
mqttsn_client_service_stop();
}

Handling critical errors

In cases when the embeNET stack detects a critical condition, the following handler is called by the stack, giving a chance to react:

__attribute__((noreturn)) void EXPECT_OnAbortHandler(char const *why, char const *file, int line) {
printf("Program aborted: %s %s:%i\n", why, file, line);
while(1) { ; }
__builtin_unreachable();
}

Customizing and extending the demo application

The structure of the firmware project is mostly generated by Code Composer IDE. This allows you to easily customize the peripherals for your demo project as needed.

>The only requirement is NOT to change configuration of peripherals used by the port implementation, i.e. RADIO, CRYP, TIM2.

Writing or extending a custom network service.

The best starting point is to look at implementation of the existing custom_service.

Disabling "zero rule"

In order to disable the "zero rule", delete the rule with "uid": 0, from your config.json file, and add appropriate rules for your nodes, for example:

"join_rules": [
{
"uid": 2,
"psk": "0x1ccd738f70a35be9574738ad190f86f2"
},
{
"uid": 3,
"psk": "0xdc2eafe4016303ebb1aa1b4b2ed4fda0"
}
]

This will allow only nodes with uid = 2 and 3, to join the network, provided that their PSKs match those from config.json file.

Using custom name for network interface

You may specify the name of desired network interface in .json file, by adding on root level:

"interface_name": "your-name"

Limitations of embeNET demo application

The demo version of the embeNET stack allows to connect up to 10 nodes only, with network depth of max. 3 hops.