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 |
---|
|
dn_err_t dn_ipmg_getNetworkConfig(dn_ipmg_getNetworkConfig_rpt* reply); |
The body of the dn_ipmg_getNetworkConfig()
is:
Code Block |
---|
language | cpp |
---|
title | dn_ipmg_getNetworkConfig() (dn_ipmg.c) |
---|
linenumbers | true |
---|
collapse | true |
---|
|
/**
The getNetworkConfig command returns general network configuration parameters,
including the Network ID, bandwidth parameters and number of motes.
*/
dn_err_t dn_ipmg_getNetworkConfig(dn_ipmg_getNetworkConfig_rpt* reply) {
uint8_t extraFlags;
dn_err_t rc;
// lock the module
dn_lock();
// verify no ongoing transmissions
if (dn_ipmg_vars.busyTx) {
// unlock the module
dn_unlock();
// return
return DN_ERR_BUSY;
}
// store callback information
dn_ipmg_vars.cmdId = CMDID_GETNETWORKCONFIG;
dn_ipmg_vars.replyContents = (uint8_t*)reply;
// extraFlags
extraFlags = 0x00;
// build outputBuf
// send outputBuf
rc = dn_serial_mg_sendRequest(
CMDID_GETNETWORKCONFIG, // cmdId
extraFlags, // extraFlags
dn_ipmg_vars.outputBuf, // payload
DN_GETNETWORKCONFIG_REQ_LEN, // length
dn_ipmg_getNetworkConfig_reply // replyCb
);
if (rc==DN_ERR_NONE) {
// I'm not busy transmitting
dn_ipmg_vars.busyTx = TRUE;
}
// unlock the module
dn_unlock();
return rc;
} |
After a number of checks, dn_ipmg_getNetworkConfig()
calls dn_serial_mg_sendRequest()
, which calls dn_serial_sendRequestNoCheck()
:
Code Block |
---|
language | cpp |
---|
title | dn_serial_sendRequestNoCheck() (dn_serial_mg.c) |
---|
linenumbers | true |
---|
collapse | true |
---|
|
dn_err_t dn_serial_sendRequestNoCheck(uint8_t cmdId, bool isAck, bool shouldBeAcked, uint8_t* payload, uint8_t length, dn_serial_reply_cbt replyCb) {
uint8_t i;
uint8_t control;
// register reply callback
dn_serial_mg_vars.replyCmdId = cmdId;
dn_serial_mg_vars.replyCb = replyCb;
// create the control byte
control = 0;
if (isAck==1) {
control |= DN_SERIAL_FLAG_ACK;
} else {
control |= DN_SERIAL_FLAG_DATA;
}
if (shouldBeAcked==1) {
control |= DN_SERIAL_FLAG_ACKNOWLEDGED;
} else {
control |= DN_SERIAL_FLAG_UNACKNOWLEDGED;
}
// send the frame over serial
dn_hdlc_outputOpen();
dn_hdlc_outputWrite(control); // Control
dn_hdlc_outputWrite(cmdId); // Packet Type
dn_hdlc_outputWrite(dn_serial_mg_vars.txPacketId); // Seq. Number
dn_hdlc_outputWrite(length); // Payload Length
for (i=0; i<length; i++) { // Payload
dn_hdlc_outputWrite(payload[i]);
}
dn_hdlc_outputClose();
// increment the txPacketId
dn_serial_mg_vars.txPacketId++;
return DN_ERR_NONE;
} |
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 |
---|
dn_hdlc_outputOpen(); // prepares the HDLC module for outputting a new frame
dn_hdlc_outputWrite(byte1); // call for each character
...
dn_hdlc_outputWrite(byten);
dn_hdlc_outputClose(); // wraps up the calculation of the CRC |
|
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:
variable | value | meaning |
---|
dn_ipmg_vars.busyTx | TRUE | 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.replyContents | pointer | A pointer to where to write the response. |
variable | value | meaning |
---|
dn_serial_mg_vars.replyCmdId | integer | The identifier of the reply to wait for (same value as dn_ipmg_varscmdId ). |
dn_serial_mg_vars.replyCb | pointer | A 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 |
---|
language | cpp |
---|
title | dn_hdlc_rxByte() (dn_hdlc.c) |
---|
linenumbers | true |
---|
collapse | true |
---|
|
/**
\brief Function which getted called each time a byte is received over UART.
\param[in] rxbyte The received byte.
*/
void dn_hdlc_rxByte(uint8_t rxbyte) {
// lock the module
dn_lock();
if (
dn_hdlc_vars.busyReceiving==FALSE &&
dn_hdlc_vars.lastRxByte==DN_HDLC_FLAG &&
rxbyte!=DN_HDLC_FLAG
) {
// start of frame
// I'm now receiving
dn_hdlc_vars.busyReceiving = TRUE;
// create the HDLC frame
dn_hdlc_inputOpen();
// add the byte just received
dn_hdlc_inputWrite(rxbyte);
} else if (
dn_hdlc_vars.busyReceiving==TRUE &&
rxbyte!=DN_HDLC_FLAG
){
// middle of frame
// add the byte just received
dn_hdlc_inputWrite(rxbyte);
if (dn_hdlc_vars.inputBufFill+1>DN_HDLC_INPUT_BUFFER_SIZE) {
// input buffer overflow
dn_hdlc_vars.inputBufFill = 0;
dn_hdlc_vars.busyReceiving = FALSE;
}
} else if (
dn_hdlc_vars.busyReceiving==TRUE &&
rxbyte==DN_HDLC_FLAG
) {
// end of frame
// finalize the HDLC frame
dn_hdlc_inputClose();
if (dn_hdlc_vars.inputBufFill==0) {
// invalid HDLC frame
} else {
// hand over frame to upper layer
dn_hdlc_vars.rxFrame_cb(&dn_hdlc_vars.inputBuf[0],dn_hdlc_vars.inputBufFill);
// clear inputBuffer
dn_hdlc_vars.inputBufFill=0;
}
dn_hdlc_vars.busyReceiving = FALSE;
}
dn_hdlc_vars.lastRxByte = rxbyte;
// unlock the module
dn_unlock();
} |
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 |
---|
language | cpp |
---|
title | dn_hdlc_init() (dn_hdlc.c) |
---|
linenumbers | true |
---|
collapse | true |
---|
| /**
\brief Setting up the instance.
*/
void dn_hdlc_init(dn_hdlc_rxFrame_cbt rxFrame_cb) {
// reset local variables
memset(&dn_hdlc_vars, 0, sizeof(dn_hdlc_vars));
// store params
dn_hdlc_vars.rxFrame_cb = rxFrame_cb;
// initialize UART
dn_uart_init(dn_hdlc_rxByte);
} |
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:
control | cmdId | seqNum | length | payload |
1B | 1B | 1B | 1B | variable |
---|
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 |
---|
language | cpp |
---|
title | dn_serial_mg_dispatch_response (dn_serial_mg.c) |
---|
linenumbers | true |
---|
collapse | true |
---|
|
void dn_serial_mg_dispatch_response(uint8_t cmdId, uint8_t* payload, uint8_t length) {
uint8_t rc;
rc = payload[0];
if (cmdId==dn_serial_mg_vars.replyCmdId && dn_serial_mg_vars.replyCb!=NULL) {
// call the callback
(dn_serial_mg_vars.replyCb)(cmdId,rc,&payload[1],length-1);
// reset
dn_serial_mg_vars.replyCmdId = 0x00;
dn_serial_mg_vars.replyCb = NULL;
}
} |
At this point, the payload contains:
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 |
---|
language | cpp |
---|
title | dn_ipmg_getNetworkConfig_reply() (dn_ipmg.c) |
---|
linenumbers | true |
---|
collapse | true |
---|
|
void dn_ipmg_getNetworkConfig_reply(uint8_t cmdId, uint8_t rc, uint8_t* payload, uint8_t len) {
dn_ipmg_getNetworkConfig_rpt* reply;
// verify I'm expecting this answer
if (dn_ipmg_vars.busyTx==FALSE || dn_ipmg_vars.cmdId!=cmdId) {
return;
}
// verify length
if (rc==DN_SERIAL_RC_OK && len<DN_GETNETWORKCONFIG_REPLY_LEN) {
return;
}
// cast the replyContent
reply = (dn_ipmg_getNetworkConfig_rpt*)dn_ipmg_vars.replyContents;
// store RC
reply->RC = rc;
// parse returned value (iff RC==0)
if (rc==DN_SERIAL_RC_OK) {
dn_read_uint16_t(&reply->networkId,&payload[DN_GETNETWORKCONFIG_REPLY_OFFS_NETWORKID]);
reply->apTxPower = (int8_t)payload[DN_GETNETWORKCONFIG_REPLY_OFFS_APTXPOWER];
reply->frameProfile = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_FRAMEPROFILE];
dn_read_uint16_t(&reply->maxMotes,&payload[DN_GETNETWORKCONFIG_REPLY_OFFS_MAXMOTES]);
dn_read_uint16_t(&reply->baseBandwidth,&payload[DN_GETNETWORKCONFIG_REPLY_OFFS_BASEBANDWIDTH]);
reply->downFrameMultVal = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_DOWNFRAMEMULTVAL];
reply->numParents = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_NUMPARENTS];
reply->ccaMode = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_CCAMODE];
dn_read_uint16_t(&reply->channelList,&payload[DN_GETNETWORKCONFIG_REPLY_OFFS_CHANNELLIST]);
reply->autoStartNetwork = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_AUTOSTARTNETWORK];
reply->locMode = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_LOCMODE];
reply->bbMode = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_BBMODE];
reply->bbSize = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_BBSIZE];
reply->isRadioTest = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_ISRADIOTEST];
dn_read_uint16_t(&reply->bwMult,&payload[DN_GETNETWORKCONFIG_REPLY_OFFS_BWMULT]);
reply->oneChannel = payload[DN_GETNETWORKCONFIG_REPLY_OFFS_ONECHANNEL];
}
// call the callback
dn_ipmg_vars.replyCb(cmdId);
// I'm not busy transmitting anymore
dn_ipmg_vars.busyTx=FALSE;
} |
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()
.