NHS31xx msg - Message Handler Protocol
msg: Message Handler

Detailed Description

The message handler is a module which provides a command - response message mechanism to help an upper layer to communicate with an external device. It is purely a software implementation without any direct dependency on hardware or transport layer. Using the message handler module, the upper layer can easily add or remove commands and responses, both at compile time and at runtime - by tweaking the diversity settings the module offers. At all times, the upper layer retains full control on what is received, what is parsed, what is generated and what is sent.

Definitions
A few definitions make the explanations below more clear:
Command
A sequence of bytes following a Protocol that is given to the message handler module that forms an instruction to perform an action.
When looking at system level, commands are typically sent by the tag reader via the NFC interface. On a few occasions, the application may generate commands itself, based on some events or interrupts, and then feed that to the message handler module.
Response
A sequence of bytes following a Protocol that is generated to partially or fully complete the action(s) requested by a previously received command and that is to be sent out via any transport layer.
When looking at system level, responses are always generated by some handler function, called by the message handler module. When generated, the message handler module will present it to the application, who then typically wraps them in an NDEF message and copied it to the NFC shared memory. On a few occasions, the application may decide to not forward the response, or to hold off forwarding the response to a later time.
Message
A command or a response. A more generic term when the directionality of the communication path is not important.
Command generator
A part of the upper layer that somehow gets hold of a command. That command may be originating from a phone, who has copied data to the NFC shared memory, which is then in turn copied by the firmware application to eventually feed it to the message handler module. The module does not know nor care: the part of the application that feeds the commands to the message handler module is designated as the 'command generator'.
Command handler
A function, either part of the message handler module or the upper layer, that interprets the content enclosed in a specific command, takes the appropriate actions and generates the corresponding responses. The output of the command handler, the response, is given back to the message handler module who is responsible for further distribution.
The message handler module has built-in support for handling commands that are deemed not specific for an application. For example MSG_ID_GETVERSION. Others can be enabled or disabled at compile time
Response handler
A part of the upper layer that receives responses via a designated callback, and that is responsible for sending out the generated responses to the originator of the corresponding command. The response handler is called by the message handler module.
Immediate response
The message handler module will ensure that for every command it receives, at least one response is generated and returned to the upper layer (*). This first guaranteed response will become available in a synchronous way, i.e. before the call has ended, and will be made available to the upper layer in the same CPU context as where the command has been given. This means that in case a specific command handler does not create a response - one may not be available or the designated message handler may reject generating any response - the message handler module will create one on that specific command handler's behalf. This first guaranteed response is called the immediate response. Additional responses may or may not be generated immediately or later - this is command and application specific.
Response generator
Either the message module itself, or the designated command handler. The block that will generate the immediate response.
Directionality
Each message requires the presence of a directionality field as part of the Protocol.
Overview
The message handler module does not run in its own thread and will not react to interrupts: it has to be explicitly triggered to do something.

When the upper layer delivers a sequence of bytes to the message handler module, i.e. the upper layer acts as a Command generator, the message handler module tries to interpret this as a command. Each sequence of bytes is supposed to constitute one command.

The message handler module maintains a list of known commands and tries to match the incoming command with this list. If a match is found, i.e. there is a Command handler for this specific command, the corresponding handler is called. At that point either the handler generates at least one response, i.e. it is also the response generator, after which the message handler module considers the command handling complete; or no response is generated, after which the message handler module will generate an immediate response itself, i.e. the message handler module will take over the role of Response generator.

Protocol
The message handler module uses the following communication protocol for commands and responses. The first byte shall indicate the message id. The second byte shall indicate the direction of transfer as mentioned below:
  • value of 0x0 for commands coming into message mod
  • value of 0x1 for responses going out from message mod
This is followed by the optional payload specific for each command/response. Details of the commands and responses are captured in the following sections.
Note
The NTAG SmartSensor family uses the NFC as a bi-directional communication path. But using the NFC technology it is not possible to flag the presence of new data to the tag reader: it is up to the tag reader to decide when to read. Waiting too long slows down communication, waiting a too short time results in reading out the previous data, likely the command bytes that the tag reader itself wrote in the NFC memory. Since it is possible that the exact same bytes constitute the response from the tag, the directionality byte is added as a means to distinguish commands from responses unambiguously. This byte is added as an aid for the tag reader: the tag itself has sufficient interrupt option at his disposal to know when a new message was written by the tag.
Commands
A command can be any sequence of bytes, that adheres to the Protocol.
Handlers
The message handler module has a default list of commands and corresponding command handlers - which can be enabled and disabled at compile time. The upper layer can at compile time add handlers for specific commands, or it can make use of a catch-all command handler to dynamically change the way it handles commands at runtime.
Apart from some minimal rules, no predefined formats exist, leaving the application with full freedom how to implement and handle its own specific commands.
Responses
Responses are typically generated in a command handler. Generated responses are given to the message handler module, which then attempts to have them sent by calling a designated callback. That callback can either immediately send the response over the transport layer of its choosing back to the originator of the command; or reject the response. When rejected, the message handler module will store the response in its internal buffer; a special command is available to retrieve them at a later time.
Note
The device(s), layer(s) or block(s) that creates commands and handles responses can be considered to be the counter-part of the message handler module. The limitations imposed by the message handler module and the precise format of each command and response must be known by respected by that counter-part. Changes made to this on one side must be made as well on the other side.

Note
All commands generate one immediate response. This includes a response for the MSG_ID_RESET command, but a subsequent reset will re-initialize all peripherals, including the NFC controller and it NFC shared memory. All commands may subsequently generate zero, one or multiple additional responses at a later time.
Some commands are asynchronous in nature: in that case a response still must be immediately returned, denoting only the acceptance of the command, and subsequent responses provide the requested result.



Sequence diagrams
The following example sequence diagrams provide a quick insight in the different interactions.
Command - response

The full communication flow between the tag reader and the tag largely follows a strict command - response sequence. Although the protocol can be used to communicate over any HW block, it is assumed that the NFC memory is used as transport layer.

  • Each time the NHS31xx IC starts, an initial NDEF message must be generated. This is necessary to correctly identify the IC as an NDEF tag (not just an NDEF-formatable tag) and to provide the application running on the tag reader with enough information to get started: think of firmware type indication through the MIME string, version information to unlock all possible functionality, and status information to display a report to the end-user as fast as possible.

    Note
    The generation of the initial NDEF message is a responsibility of the application: it must internally generate a command and feed that to the message handler module. The response can then be used to initialize the NFC shared memory appropriately.
    msc_inline_mscgraph_2
    Note
    It is the responsibility of the application to generate the initial message as soon as possible, to ensure that the desired information is present in the NFC memory at the moment that the tag reader starts reading out the NFC contents.



  • From then on, the application firmware waits for an incoming command: an NDEF message, written by the tag reader, overwriting the initial message. For each command that is written, one response will be created and stored in the same NFC shared memory, for the tag reader to read out.
    Also if a command is unknown, badly formatted, or not allowed (for instance, due to access restrictions), one response will be made available by the tag reader.
msc_inline_mscgraph_3
Note
It is not possible to notify the tag reader when a response is available in NFC memory. The tag reader must poll by continually reading the NFC memory until a memory change is detected.



  • Some commands take a long time to complete. Even then, one response will be made available immediately. This immediate response then only acknowledges the acceptance of the command. The requested response can then later be fetched using the command MSG_ID_GETRESPONSE. Also when commands trigger multiple responses, the subsequent responses can be fetched be repetitively issuing the command MSG_ID_GETRESPONSE.
msc_inline_mscgraph_4
Note
It requires precise knowledge of the exact command being sent, to know whether a delayed response or multiple responses will be made available, and to estimate when these responses are ready to be sent out.



Automatic responses

Not all tag readers are capable of writing to a tag but can only read from one. For example, an iPhone 7 running iOS 11 will not be able to write configuration data to the tag and start a demonstration session around the temperature logging use case. A true command - response sequence is in this case not possible.
However, the full communication layer present in the SDK can still be used. The communication module is fully agnostic of the physical transport layer, the firmware application can thus feed it with self-generated commands each time the NFC HW block reports that the tag reader has read out the NFC memory. The firmware application can then follow a script, where a sequence of commands is listed that is to be followed in case the tag reader is incapable of writing and controlling the data flow itself.

This approach is implemented in the NHS3100 temperature logger demo: the firmware application can switch between an 'automatic' mode, where it generates the commands himself, and the normal 'response' mode, where it waits for commands written by the tag reader, only reacting on them.

  • The Android APP - available in the Google Play Store - will take control, and will write commands to fetch the data it wants to know.
  • The iOS APP - available in the Apple APP store - will repetitively read the NFC memory until no more new data is being fed by the application firmware. Although the functionality to configure and start a temperature monitoring session is lacking, it can still do a full data readout of tens of thousands of measurements points, together with the current status and the conclusion as calculated on the NTAG SmartSensor

A sequence diagram of such an application, where the application firmware implements an 'automatic' mode, can look as follows:

msc_inline_mscgraph_5
Note
Synchronization of the read and write actions in the communication flow is not guaranteed anymore:
  • if the tag reader reads too fast during a time consuming response generation, the tag reader may erroneously decide the script execution has finished.
  • Vice versa, when reading has finished, but a reading error has occurred causing a few bytes to have reversed bits, the application firmware cannot be informed to pause its script execution, needed to give the necessary time to the tag reader to read the response a second time.
This means that some redundancy and leniency must be implemented on both sides to still ensure a full correct readout of all the data provided by the implemented use case.



Internal messages / custom messages
A number of commands and corresponding responses have been predefined by the message handler module. They can be found in the enumeration MSG_ID_T. By default only one of them is compiled in to save code space: MSG_ID_GETVERSION.

Each command has a corresponding command structure MSG_CMD_xxxx_T that describes the fields which are packed into the command payload, and one or possibly two response structures MSG_RESPONSE_xxxx_T that describe which fields to expect in return, as part of the response payload.

By default, there exist no custom messages: it is up to the application to define and implement them, and to connect them at compile time.

Data Structures

struct  MSG_CMD_READREGISTER_T
 
struct  MSG_CMD_WRITEREGISTER_T
 
struct  MSG_CMD_READMEMORY_T
 
struct  MSG_CMD_WRITEMEMORY_T
 
struct  MSG_RESPONSE_RESULTONLY_T
 
struct  MSG_RESPONSE_GETVERSION_T
 
struct  MSG_RESPONSE_READREGISTER_T
 
struct  MSG_RESPONSE_READMEMORY_T
 
struct  MSG_RESPONSE_GETUID_T
 
struct  MSG_RESPONSE_GETNFCUID_T
 
struct  MSG_RESPONSE_CHECKBATTERY_T
 

Macros

#define MSG_API_MAJOR_VERSION   (0x6)
 
#define MSG_API_MINOR_VERSION   (0x1)
 

Enumerations

enum  MSG_ID_T {
  MSG_ID_GETRESPONSE = 0x01,
  MSG_ID_GETVERSION = 0x02,
  MSG_ID_RESET = 0x03,
  MSG_ID_READREGISTER = 0x04,
  MSG_ID_WRITEREGISTER = 0x05,
  MSG_ID_READMEMORY = 0x06,
  MSG_ID_WRITEMEMORY = 0x07,
  MSG_ID_PREPAREDEBUG = 0x08,
  MSG_ID_GETUID = 0x09,
  MSG_ID_GETNFCUID = 0x0a,
  MSG_ID_CHECKBATTERY = 0x0b,
  MSG_ID_GETCALIBRATIONTIMESTAMP = 0x0c,
  MSG_ID_GETDIAGDATA = 0x3E,
  MSG_ID_LASTRESERVED = 0x3F
}
 
enum  MSG_ERR_T {
  MSG_OK = 0,
  MSG_ERR_UNKNOWN_COMMAND = 0x10007,
  MSG_ERR_NO_RESPONSE = 0x1000B,
  MSG_ERR_INVALID_COMMAND_SIZE = 0x1000D,
  MSG_ERR_INVALID_PARAMETER = 0x1000E,
  MSG_ERR_INVALID_PRECONDITION = 0x1000F,
  MSG_ERR_INVALID_NYI = 0x10010,
  MSG_ERR_LASTRESERVED = 0x1003F
}
 

Data Structure Documentation

◆ MSG_CMD_READREGISTER_T

struct MSG_CMD_READREGISTER_T
Data Fields
uint32_t address

Register address to read from.

◆ MSG_CMD_WRITEREGISTER_T

struct MSG_CMD_WRITEREGISTER_T
Data Fields
uint32_t address

Register address to write to.

uint32_t data

Data to write to the ARM register.

◆ MSG_CMD_READMEMORY_T

struct MSG_CMD_READMEMORY_T
Data Fields
uint32_t address

Byte offset in the ARM RAM to start reading from.

uint8_t length

The number of consecutive bytes to read with a maximum of 32.

◆ MSG_CMD_WRITEMEMORY_T

struct MSG_CMD_WRITEMEMORY_T
Data Fields
uint32_t address

Byte offset in the ARM RAM to start writing to.

uint8_t length

The number of consecutive bytes to write with a maximum of 32.

uint8_t data[32]

A container for the data to write.

◆ MSG_RESPONSE_RESULTONLY_T

struct MSG_RESPONSE_RESULTONLY_T

Used whenever no other information is required to be returned except the outcome of a command.

Data Fields
uint32_t result

The command result.

Note
If this structure is used to return an immediate result, the interpretation of the result value is different, depending on the type of command: synchronous or asynchronous:
  • For synchronous commands, the result indicates the result of executing the command. The command has been fully executed. MSG_OK means that the command was successfully executed, any other value indicates an error code and signifies that the command has been partially executed or not at all.
  • For asynchronous commands the result only indicates the result of receiving the command and checking its pre-conditions. MSG_OK means that the commands was successfully received and execution of the command has started, any other value indicates an error code and signifies that the command will be partially executed or not at all. The final result of the execution of the command will be part of the response that will be queued later.
If this structure is used to respond to the command MSG_ID_GETRESPONSE, it signifies the final result of the execution of an asynchronous command.

◆ MSG_RESPONSE_GETVERSION_T

struct MSG_RESPONSE_GETVERSION_T
Data Fields
uint16_t reserved2

Reserved for future use. Must be 0. Does not bear any significance.

uint16_t swMajorVersion

The software major version

uint16_t swMinorVersion

The software minor version

uint16_t apiMajorVersion

Equal to MSG_API_MAJOR_VERSION

uint16_t apiMinorVersion

Equal to MSG_API_MINOR_VERSION

uint32_t deviceId

This value will be equal to:

  • 0x4E310020 for NHS3100 devices
  • 0x4E315220 for NHS3152 devices

◆ MSG_RESPONSE_READREGISTER_T

struct MSG_RESPONSE_READREGISTER_T
Data Fields
uint32_t result

The command result. Only when result equals MSG_OK, the contents of data is valid.

uint32_t data

The value read from the ARM Register

◆ MSG_RESPONSE_READMEMORY_T

struct MSG_RESPONSE_READMEMORY_T
Data Fields
uint32_t result

The command result. Only when result equals MSG_OK, the contents of data is valid.

uint8_t length

The number of consecutive bytes read.

uint8_t data[32]

The values read from the ARM memory. The extraneous array elements - where the index is greater than length - are set to 0.

◆ MSG_RESPONSE_GETUID_T

struct MSG_RESPONSE_GETUID_T
See also
MSG_ID_GETUID
Data Fields
uint32_t uid[4]

The sequence of 4 32-bit words (LSByte first) is guaranteed unique among all NHS31xx ICs.

◆ MSG_RESPONSE_GETNFCUID_T

struct MSG_RESPONSE_GETNFCUID_T
Data Fields
uint8_t nfcuid[8]

The sequence of bytes as assigned to the NFC controller.

◆ MSG_RESPONSE_CHECKBATTERY_T

struct MSG_RESPONSE_CHECKBATTERY_T
Data Fields
uint32_t result

The command result. Only when result equals MSG_OK, the contents of data is valid.

int32_t threshold

Extra current consumption is enabled in progressive steps. The extra current consumption which triggered the BOD is returned here. A negative value indicates BOD could not be triggered.

Macro Definition Documentation

◆ MSG_API_MAJOR_VERSION

#define MSG_API_MAJOR_VERSION   (0x6)

Defines the major API version. This should be incremented each time the API changes.

◆ MSG_API_MINOR_VERSION

#define MSG_API_MINOR_VERSION   (0x1)

Defines the minor API version. This should be reset each time the API changes, and incremented each time the API doesn't change but the implementation or documentation changes.

Enumeration Type Documentation

◆ MSG_ID_T

enum MSG_ID_T

Supported messages.

Note
Each message comprises of a command and a response. For each command and response a corresponding struct exists which explains the parameters, their sizes and their use.
Enumerator
MSG_ID_GETRESPONSE 

0x01
The message handler module holds a queue of responses. Each time a response is created which cannot be sent back immediately (in response to a command), it is queued. It's the task of the host to use this command to retrieve the queued responses.

Note
When the host doesn't retrieve the responses fast enough, the buffer gets filled up. When a new response can not be stored any more, the oldest responses are discarded until sufficient room is available to store the newest response.
Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
The response is returned immediately.
Note
synchronous command
MSG_ID_GETVERSION 

0x02
This message id allows the host to determine the version of the firmware.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
MSG_RESPONSE_GETVERSION_T
Note
synchronous command
This command is always available.
MSG_ID_RESET 

0x03
Generate a SW reset of the digital part of the system.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
MSG_RESPONSE_RESULTONLY_T. This response will be available for 500 milliseconds.
Note
synchronous command
MSG_ID_READREGISTER 

0x04
Read and return the value from the selected ARM register address.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: MSG_CMD_READREGISTER_T
Returns
MSG_RESPONSE_READREGISTER_T
Note
synchronous command
MSG_ID_WRITEREGISTER 

0x05
Write the data supplied by the host to the selected ARM register address.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: MSG_CMD_WRITEREGISTER_T
Returns
MSG_RESPONSE_RESULTONLY_T.
Note
synchronous command
MSG_ID_READMEMORY 

0x06
Read and return the value from the selected address.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: MSG_CMD_READMEMORY_T
Returns
MSG_RESPONSE_READMEMORY_T.
Note
synchronous command
The address range may be part of SRAM, Flash or EEPROM.
Warning
It is assumed the address region is accessible and can be read. This is not checked for. e.g. This implies it is the application's responsibility to ensure the EEPROM is initialized when reading EEPROM data, and that it is the caller's responsibility to ensure the range can be mapped to valid addresses.
MSG_ID_WRITEMEMORY 

0x07
Write the data supplied by the host to the selected ARM memory address.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: MSG_CMD_WRITEMEMORY_T
Returns
MSG_RESPONSE_RESULTONLY_T.
Note
synchronous command
Warning
Only SRAM can be written to. It is assumed the address region which is referred to is fully part of SRAM. This is not checked for.
MSG_ID_PREPAREDEBUG 

0x08
Configures and enables the SWD lines, then waits in an endless while loop. This allows a developer to attach a debugger to the running device.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
MSG_RESPONSE_RESULTONLY_T.
Note
synchronous command
When a debugging session has been established, set the boolean gMsgWaitForDebugConnection to false to return from the command handler.
MSG_ID_GETUID 

0x09
Retrieve the unique identifier of the IC. This is the device serial number guaranteed unique among all NHS31xx ICs.

Note
This is not equal to the NFC ID, which is a randomly assigned number.
Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
MSG_RESPONSE_GETUID_T
Note
synchronous command
MSG_ID_GETNFCUID 

0x0a
Retrieve the NFC identifier of the IC. This is the serial number assigned to the NFC controller and stored in the first two pages of the NFC shared memory.

Note
This is a random number assigned during production of the IC. It is not guaranteed unique among all NHS31xx ICs.
This is not equal to the device UID.
Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
MSG_RESPONSE_GETUID_T
Note
synchronous command
MSG_ID_CHECKBATTERY 

0x0b
Generates whether the BOD is triggered under extra load. Use this to check battery impedance.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
MSG_RESPONSE_RESULTONLY_T immediately;
If result was equal to MSG_OK, MSG_RESPONSE_CHECKBATTERY_T thereafter. This may take several 100 ms. This second response must be fetched by issuing a command with MSG_ID_GETRESPONSE
Note
asynchronous command
For this command to become available, define MSG_ENABLE_CHECKBATTERY
Warning
This check has a (small) negative impact on the battery. And, this check potentially can cause a reset. An application may be prudent by blocking this message while a reset is not desired, e.g. during a monitoring session in a logger application. This can be done by implementing MSG_COMMAND_ACCEPT_CB
MSG_ID_GETCALIBRATIONTIMESTAMP 

0x0b
Retrieves the timestamps of when the temperature calibration took place (at 30C and 85C).

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
MSG_RESPONSE_GETDIAGDATA_T
Note
synchronous command
MSG_ID_GETDIAGDATA 

0x3E
Retrieve diagnostics information as gathered by the diag module.

Parameters
Header: Sequence of bytes as per the Protocol.
Payload: No payload.
Returns
MSG_RESPONSE_GETDIAGDATA_T
Note
synchronous command
For this command to become available, define ENABLE_DIAG_MODULE.
MSG_ID_LASTRESERVED 

0x3F
This message id does not encompass a command or a response. It is used to signify the highest id that is reserved for use by the message handler itself. All application specific commands - which are to be handled by setting a specific handler - must use id's greater than this value.

Note
Only to be used as an offset for your own application specific commands and responses.
See also
Cmd_SetApplicationHandler

◆ MSG_ERR_T

enum MSG_ERR_T

Lists all possible error codes that may be returned.

Enumerator
MSG_OK 

0x00000000
No error was found.

MSG_ERR_UNKNOWN_COMMAND 

0x00010007 or [07h 00h 01h 00h]
No suitable command handler could be found for this id

MSG_ERR_NO_RESPONSE 

0x0001000B or [0Bh 00h 01h 00h]
Only used in the response to a command with id MSG_ID_GETRESPONSE, to indicate no stored responses are available in the buffer.

MSG_ERR_INVALID_COMMAND_SIZE 

0x0001000D or [0Dh 00h 01h 00h]
A number of parameters are lacking or were given in excess.

MSG_ERR_INVALID_PARAMETER 

0x0001000E or [0Eh 00h 01h 00h]
At least one parameter was missing or had an invalid value.

MSG_ERR_INVALID_PRECONDITION 

0x0001000F or [0Fh 00h 01h 00h]
The command can now not be handled. Check the documentation for a correct command sequence.

MSG_ERR_INVALID_NYI 

0x00010010 or [10h 00h 01h 00h]
The handler of the command is a stub and still needs to be implemented; or the implementation to handle the given combination of parameters is incomplete and a work in progress. Check with the developer to retrieve updated firmware.

MSG_ERR_LASTRESERVED 

0x0001003F
This error code does not encompass an error. It is used to signify the highest id that is reserved for use by the message handler itself. All application specific error codes must use id's greater than this value.

Note
Only to be used as an offset for your own application specific error codes.