How do I use commissioning mode to setup a network?
The purpose of commissioning mode is to setup a network without using the standard association process.
The general method would be to load up all your attributes while in one "dummy" network with all of the desired network information, then you could join a new network using the commissioning mode.
For a deeper breakdown, you will need to use emberJoinNetwork() for both your coordinator and your routers. You cannot actually use the commissioning joining method with your end devices as they need parents. When you join this way, you will need to set the emberJoinNetwork with EmberNetworkParams.joinMethod set to "EMBER_USE_NWK_COMMISSIONING" as described in stack/include/ember-types.h. When doing this, you are able to setup your device by filling out the remaining struct members in the EmberNetworkParameters struct (nwkManagerId, nwkUpdateId, and channels). You will also have to set the initial security state before joining. The initial security state struct is also in stack/include/ember-types.h and requires you to set EMBER_HAVE_TRUST_CENTER_EUI64, EMBER_HAVE_PRECONFIGURED_KEY and EMBER_HAVE_NETWORK_KEY in the EmberInitialSecurityBitmask, and you will need to provide corresponding EUI64 and key data for these items in the EmberInitialSecurityState struct.
Since you are unable to use this method with end devices, the best method is likely to "force" the end device to join a particular network based on previously known parameters. Using the EMBER_USE_NWK_REJOIN_HAVE_NWK_KEY joinMethod, an end device can do the equivalent to a secure rejoin with any available parent device in an already-joined network.
For information on emberJoinNetwork() please review stack/include/network-formation.h.
EmberNetworkParameters struct :
int8u extendedPanId  - Extended PAN ID of the network
int16u panId - PAN ID of the network
int8s radioTxPower - Transmissiong power setting in dBm
int8u radioChannel - A radio channel. Be sure to specify a channel supported by the radio.
EmberJoinMethod joinMethod - Join method: The protocol messages used to establish an initial parent. It is ignored when forming a ZigBee network, or when querying the stack for its network parameters. (Use EMBER_USE_NWK_COMMISSIONING)
EmberNodeId nwkManagerId - The ID of the network manager in the current network. This may only be set at joining when using EMBER_USE_NWK_COMMISSIONING as the join method. Most likely in your case the ID will be 0x0000 as the network manager is usually the coordinator. You should also be able to use EMBER_TRUST_CENTER_NODE_ID.
int8u nwkUpdateId - The value of the ZigBee nwkUpdateId known by the stack. This is used to determine the newest instance of the network after a PAN ID or channel change. This may only be set at joining when using EMBER_USE_NWK_COMMISSIONING as the join method. This value starts at 0x00 and increments by 1 every time it changes for a single network.
int32u channels - NWK channel mask. The list of preferred channels that the NWK manager has told this device to use when searching for the network. This may only be set at joining when using EMBER_USE_NWK_COMMISSIONING as the join method.
How do I get a route from a concentrator to a router?
Note: The suggestions in thie KBA are implemented in the Concentrator Support plugin in stack version EmberZNet5.1.2 and later, as well as the NCP firmware. If you are using a stack version EmberZNet5.1.2 and later this is mearly and informative article. However, this can be used as a help guide for earlier stack version.
When using a concentrator in your network, every time a Many-to-one-route-request (MTORR) is sent out by the concentrator, routes are created for each device back to that concentrator. A common issue arises at this point. How do you get a route from the concentrator to the other devices?
One solution would be to set up some custom logic in order to have your concentrator send broadcasts to specific routers (with some built in jitter to reduce flooding the network with messages) to cause a unicast response to the concentrator. The easiest way to do this method would be to send a message from the coordinator that forces an application layer response to be generated automatically as a unicast. A good example of this would be to send a ZDO IEEE address request addressed to a broadcast address mask (so that you don't need a route to send it) but with a deviceOfInterest address matching the target's node ID (so you only get one response). However, you can't use emberAfFindIeeeAddress() to do this because that routine wants to send the address request as a unicast to the target node ID (deviceOfInterest == targetAddress), since that's the typical and most efficient scenario. You'd have to replicate what emberIeeeAddressRequest() (in app/util/zigbee-framework/zigbee-device-host.c) is doing but with a broadcast destination address for the transmission instead of using the target node ID as both destination and deviceOfInterest. This will result in a unicast response and the creation of a route record. If you can do this in some logical fashion where you send about 1 per second (aimed at different routers), then you won't flood your network with broadcasts, and will stay under the broadcast limit. This method can also be adopted for getting a route to only one device without the need of setting up a state machine with jitter.
Another thing to try, if you have a system design where you can control the application behavior of all the devices, you can use a callback for when the MTORR is received. This will allow you to schedule a unicast to the concentrator over some random jitter interval. However, this callback is not listed in AppBuilder, so you would need to define EMBER_APPLICATION_HAS_INCOMING_MANY_TO_ONE_ROUTE_REQUEST_HANDLER in your Macros list. Then you can implement your own logic in emberIncomingManyToOneRouteRequestHandler (see stack/include/message.h for the full description of this API).
Common mistakes for creating a custom plugin.
There is often a unique issue that comes up with a custom stack status callback for cluster related custom plugins. In the plugin.properties file you can implement a client stack status callback. However, you have to make sure that the name of this callback relates to the name of the plugin. For instance, if the plugin folder is called "custom-plugin", the stack status callback must be named "emberAfPluginCustomPluginStackStatusCallback".
In addition, if your custom plugin includes an init callback, the name of the init callback must relate to the name of the plugin. For example, if the plugin folder is called "custom-plugin", the plugin init callback function must be named "emberAfPluginCustomPluginInitCallback".
Some information on dependsOnClusterClient and dependsOnClusterServer:
* dependsOnClusterClient - comma separated list of client clusters required for this plugin to be enabled; if more than one cluster is listed, all of the clusters must be present for the plugin to be enabled; if server clusters are also defined, all client and server clusters must be included
* dependsOnClusterServer - comma separated list of server clusters required for this plugin to be enabled; if more than one cluster is listed, all of the clusters must be present for the plugin to be enabled; if client clusters are also defined, all client and server clusters must be included
When including non-custom callbacks for a custom plugin based off an already defined cluster you must use "emberAf" prefix for these callbacks in the callback.xml.
Will not work: myPrefixCustomPluginTunnelingClusterServerInitCallback, myPrefixCustomPluginTunnelingClusterServerTickCallback, myPrefixCustomPluginTunnelingClusterServerAttributeChangedCallback
Will work: emberAfCustomPluginTunnelingClusterServerInitCallback, emberAfCustomPluginTunnelingClusterServerTickCallback, emberAfCustomPluginTunnelingClusterServerAttributeChangedCallback
How are different tables managed in EmberZNet?
Each table has a slightly different protocol for when the table is full.
(volatile) The routing table and neighbor table (volatile) are similar in that the table entries stay in RAM until it completely fills. When this happens the new entry will overwrite any "stale" entries. "Stale" is different for the routing table and the neighbor table. For routing tables it means 64 seconds of the route being actively used without another route discovery. For neighbor tables it means 64 seconds of no successful neighbor table exchanges. If there are no "stale" entries, then it will look to overwrite entries that have the most failures or worst neighbor quality. However, if the new potential neighbor has a lower LQI than the worst current neighbor, the new neighbor will not be registered into the table.
(non-volatile) When binding tables get full and you try to add to them they will give you "Binding Table Full" error. You will have to manually remove an entry if you want to add a new one. The binding table is not needed for network behavior but can be used by the application for multiple uses such as reporting, multicast and other uses. While typical ZigBee convention is to have one binding per-cluster, per-endpoint, our stack doesn’t enforce this limitation and will allow you to send via a binding that has a different source endpoint or cluster ID from the APS frame of the outgoing message. However, in multi-network operation, the stack does enforce the per-network binding requirement.
(volatile) Address Tables will overwrite the oldest entry once it is full and you are trying to add a new entry.
*Note about the address table: Both emberAfAddAddressTableEntry and emberAfPluginAddressTableAddEntry will add a table entry to the address table. The difference between the two is that one is only meant to be used if you are using the plugin (Address Table plugin). If you are using the plugin you will want to use emberAfPluginAddresTableAddEntry. Otherwise you will want to use emberAfAddAddressTableEntry. If you use the latter, you will also want to use emberAfRemoveAddressTableEntry to remove the entries. The reason for this is because they do keep track of the reference count. The reference count itself is used to stop you from setting an entry to a used position. However, the plugin add will check to make sure it is empty before adding to it. In fact, it uses the emberSetAddressTableRemoteNodeId to set it and emberSetAddressTableRemoteNodeId(index, EMBER_TABLE_ENTRY_UNUSED_NODE_ID)) to clear it. To summarize, if you are using the plugin use emberAfPluginAddresTableAddEntry/emberAfPluginAddressTableRemoveEntry and if you want emberSetAddressTableRemoteNodeId. Otherwise use emberAfAddAddressTableEntry/emberAfRemoveAddressTableEntry and emberAfSetAddressTableEntry. For hosts only, ezspReplaceAddressTableEntry can be used as a more convienent method to replace an address table entry.
(non-volatile) Child Tables will not let any new entries join once it is full. There is also no concept of "stale" children. A child entry can time out if it does not poll its parent after a certain time (usually ~5 minutes). However you can use EmberStatus emberRemoveChild(EmberEUI64 childEui64) to manually remove an entry.
(volatile) The broadcast table is used because the only specific limit that ZigBee (and therefore our stack) places on sending of messages is in regards to broadcasts. The ZigBee Networking specification requires that in-progress broadcasts (at the Network layer) be tracked by each router (via a broadcast table) to prevent repeating duplicates of an already-circulating broadcast. Since the space in the table is constrained (by RAM), only a limited number of entries exist; and since the duration of each broadcast must be considered in the worst-case scenario (large networks where broadcasts may take many seconds to circulate through the network), limits are placed on how many broadcasts can be sent in a given timeframe. For ZigBee Pro, this limit is effectively 8 broadcasts over any 9-second period.
So if the application tries to queue an APS Broadcast or an APS Multicast (which relies on the broadcast mechanism at the NWK layer) after this broadcast table is full (due to other NWK layer broadcast activity created by APS Broadcasts, APS Multicasts, Route Discoveries, Address Discoveries, Device Announcements, etc.), the stack will refuse to queue the message and instead will return EMBER_NETWORK_BUSY status. The application must then wait until the broadcast table has room to track a new broadcast. (How long to wait depends on how quickly the table filled up relative to the 9-second table entry timeout.)
Note that this broadcast table behavior is a ZigBee NWK layer compliance requirement and therefore can't be altered by the user at run-time or compile-time. It is fixed in the stack to ensure compliant behavior (and prevent flooding the network with too much broadcast activity).
How can I use link keys in Home Automation?
There are two types of link keys that you can use in a HA network after your device has joined the network. These link keys are intended to allow for secure messaging within a network where only the two devices messaging each other can decrypt the message. These link keys are called the trust center link key and the application link key. Every device that joins a HA network already has a well-known trust center link key. However, once the device joins the network it can request a new trust center link key for secure messaging between the trust center and itself. The application link key is a link key between two non-trust center devices.
On EZSP-based NCP platforms, the application will not be asked directly to make a decision about whether or not to provide link keys. So in order to allow trust center link keys, EzspPolicyId EZSP_TC_KEY_REQUEST_POLICY must be set to allow EZSP_ALLOW_TC_KEY_REQUESTS. For Application link keys use EZSP_APP_KEY_REQUEST_POLICY.
Next, it is important to make sure all the devices that plan to use a unique link key have key table space available for each unique key it wants to use. This does not include the initial trust center link key.
Finally, as long as your TC is setup with all the policies and all of the devices have link key tables large enough to accommodate all of your requests, once any node joins the network and calls emberRequestLinkKey, the trust center should setup a unique trust center link key for that node. Also, you can use emberRequestLinkKey(EmberEUI64 partner) from one device with a non-trust center "partner" you want to have a private link key between. The drawback with having only one device request a key is that the other device may be asleep, offline, or have insufficient capacity to hold another key.
There is another method that allows you to have two devices request the key while the trust center stores the request until both requests are heard. The problem with this method is that it is not standardized in ZigBee and is non-interoperable with other vendors’ devices. This also uses emberRequestLinkKey(EmberEUI64 partner), except both devices need to request it. Since this method is not standardized in ZigBee, a workaround used in our Partner Link Key Exchange plugin, in the application framework, is to send a Bind Request on the Key Establishment cluster to a remote node in advance of asking the TC to negotiate a mutual key with that partner. This serves as evidence that the node is reachable, and capable of receiving the key. If the plugin is also enabled on the target of the bind request (which is also the partner for the key request), the plugin will verify that there is sufficient space in the key table on the local device (as it already does on the initiating device). If this verification fails, a negative response is sent on the binding request. If this verification fails, a negative response is sent on the binding request. The drawback to this approach, though, is that the plugin requires the Key Establishment client & server support enabled on at least one endpoint, which is not typical for HA devices. Also, since this exchange method is Silicon Labs-specific, devices not using our framework (and that plugin or a similar method) won’t do the target-side check for available key table space, so the bind request will likely succeed (if the device is reachable/awake) even if the remote node doesn't have sufficient link key table entries to handle the impending key delivery.
In order to setup the TC to either wait for the a second device to request an application link key the define EMBER_REQUEST_KEY_TIMEOUT must be used. By default, the framework is setup to use #define EMBER_REQUEST_KEY_TIMEOUT 0 which is the ZigBee Pro Compliant value. However, this value can be set to any value less than or equal to 10 which corresponds to minutes. A value of 0 does not wait for a second request, but instead responds immediately.