Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info
Excerpt

We show how the CLIB issues commands to your SmartMesh device.


It uses https://github.com/dustcloud/sm_clib/blob/REL-1.0.1.4 to illustrate the concepts described.

Tip

This post is targeted at enthusiasts who want to understand the internals of the CLIB. You do not need this level of understanding if you just want to use it.

Table of Contents

The CLIB allows your micro-controller to issue commands to your SmartMesh device.

To illustrate, imagine you want to know the network id your SmartMesh IP manager is using, and therefore issue the getNetworkConfig command.

Gliffy
imageAttachmentIdatt110310986
nameclib_setup
diagramAttachmentIdatt110310982

Gliffy
imageAttachmentIdatt110311188
nameclib_layers2
diagramAttachmentIdatt110311184

Issuing the request

If you use the CLIB, all you need to do is call dn_ipmg_getNetworkConfig(), and pass a pointer to hold the reply.

Code Block
languagecpp

The body of the dn_ipmg_getNetworkConfig() is:

Code Block
languagecpp
titledn_ipmg_getNetworkConfig() (dn_ipmg.c)
linenumberstrue
collapsetrue

After a number of checks, dn_ipmg_getNetworkConfig() calls dn_serial_mg_sendRequest(), which calls dn_serial_sendRequestNoCheck():

Code Block
languagecpp
titledn_serial_sendRequestNoCheck() (dn_serial_mg.c)
linenumberstrue
collapsetrue
Tip

The HDLC module of the CLIB implements "on-the-fly" HDLC framing, which is particularly interesting for constrained devices. Rather than having a buffer which holds the packet to be sent and calculating the CRC in one go, the HDLC module escapes characters and calculates the CRC as it is receiving bytes from the caller, and sends those onto the serial port.

To output a HDLC frame, use:

Code Block

The function your code initially called, dn_ipmg_getNetworkConfig(), now returns.

Note

This function returns as soon as the request has left the serial port. It does not mean you have received a response from the device.

Internally, the CLIB is now waiting for a reply to that function, and keeps the state in the table below:

variablevaluemeaning
dn_ipmg_vars.busyTxTRUE

The CLIB is busy transmitting. Any calls to issue new commands are rejected as long as:

  • the response for that command isn't received, or
  • the application calls dn_ipmg_cancelTx(), typically when timing out
dn_ipmg_vars.cmdId

integer

The identifier of the request just transmitted. Used to match the response.
dn_ipmg_vars.replyContentspointerA pointer to where to write the response.
variablevaluemeaning
dn_serial_mg_vars.replyCmdIdintegerThe identifier of the reply to wait for (same value as dn_ipmg_varscmdId).
dn_serial_mg_vars.replyCbpointerA function pointer to the function to call when a reply is received. For example, dn_ipmg_getNetworkConfig_reply().

Handling the response

Each time a serial byte is received from the device, the dn_hdlc_rxByte() function is called.

Code Block
languagecpp
titledn_hdlc_rxByte() (dn_hdlc.c)
linenumberstrue
collapsetrue

Similar to the transmission case, the HDLC module implements "on-the-fly" HDLC de-framing using the following functions:

  • dn_hdlc_inputOpen()
  • dn_hdlc_inputWrite()
  • dn_hdlc_inputClose()

When done receiving a correctly-formatting HDLC frame, the dn_hdlc_vars.inputBuf buffer contains the un-framed response. It is handed to the "upper-layer" through the callback function dn_hdlc_vars.rxFrame_cb().

Info

Note that that this callback function was installed when the HDLC module was initialized:

Code Block
languagecpp
titledn_hdlc_init() (dn_hdlc.c)
linenumberstrue
collapsetrue

Which itself is installed by the dn_serial_mg module, and points to dn_serial_mg_rxHdlcFrame().

When the received frame is handed to dn_serial_mg_rxHdlcFrame(), it has the following format:

controlcmdIdseqNumlengthpayload
1B1B1B1Bvariable
Tip

The flags in the control byte allow the dn_serial_mg module to distinguish a response from a notification. This allows notifications to be handled even when the module is waiting for a response.

The payload is handed to the dn_serial_mg_dispatch_response() function:

Code Block
languagecpp
titledn_serial_mg_dispatch_response (dn_serial_mg.c)
linenumberstrue
collapsetrue

At this point, the payload contains:

rcpayload
1Bvariable

Which only continues handling this packet if this response corresponds to the request sent earlier. The dn_serial_mg_dispatch_response() function strips the return code, and hands the payload to the reply handler, in our case dn_ipmg_getNetworkConfig_reply().

Finally, dn_ipmg_getNetworkConfig_reply() verifies the length of the payload, and populates the fields of the reply buffer dn_ipmg_vars.replyContents.

Code Block
languagecpp
titledn_ipmg_getNetworkConfig_reply() (dn_ipmg.c)
linenumberstrue
collapsetrue

At this point, the dn_ipmg module indicates a response was received by calling the dn_ipmg_vars.replyCb() callback function. The application can now read the response in the dn_ipmg_getNetworkConfig_rpt buffer it passed initially when calling dn_ipmg_getNetworkConfig().