Step 3c: ZWave - CC_SENSOR_MULTILEVEL

 

The code for this step is found here.

 

Adding ZWave access to the sensor will be pretty simple. We need to put the CC byte in the NIF, route the packet in the transport layer's application command handler, and implement the command class. Complicating things will be the need to transmit a reply, and we'll try to make our implementation general, using stubs that the application must provide for specific information like supported sensor types and scales. A diff of the application source code to Step 3b will also show many code clean-ups that we won't discuss.

 

SDS13812 defines sensor types. Air temperature sensors have value (ID) 0x01, with the supported bit mask being bit 0 of byte 1. We will provide two scales, Fahrenheit (ID 0x01) and Celsius (0x00). Humidity sensors have value 0x05, with the supported mask being bit 5 of byte 1. There are two possible scales, relative (ID 0x00) and absolute (ID 0x01), and we'll only support the first. Both sensor readings are in milli-percent/degrees, so the values reported will use 4 bytes with a precision of 3. This means that the combined mask for SENSOR_MULTILEVEL_SUPPORTED_SENSOR_REPORT is 0x11. SENSOR_MULTILEVEL_SUPPORTED_SCALE_REPORT will return 0x6C for temp (F) and 0x64 for humidity and temp (C).

 

Structures holding the contents of the frames we'll use are defined in ${SDK}/ZWave/API/ZW_classcmd.h. We will use the latest version of the command class, V11. The frames are ZW_SENSOR_MULTILEVEL_GET_V11_FRAME, ZW_SENSOR_MULTILEVEL_REPORT_[1234]BYTE_V11_FRAME, ZW_SENSOR_MULTILEVEL_SUPPORTED_GET_SENSOR_V11_FRAME, ZW_SENSOR_MULTILEVEL_SUPPORTED_SENSOR_REPORT_[1234]BYTE_V11_FRAME, ZW_SENSOR_MULTILEVEL_SUPPORTED_GET_SCALE_V11_FRAME, and ZW_SENSOR_MULTILEVEL_SUPPORTED_SCALE_REPORT_V11_FRAME. The reports have four different versions depending on the number of mask bytes that must be sent; we choose the appropriate version for the value.

 

Outgoing messages use three structures, TRANSMIT_OPTIONS_TYPE_SINGLE_EX defined in ${SDK}/ZAF/ApplicationUtilities/ZW_TransportEndpoint.h, ZAF_TRANSPORT_TX_BUFFER defined in ${SDK}/ZAF/ApplicationUtilities/ZAF_tx_mutex.h, and ZAF_APPLICATION_TX_BUFFER defined in ZW_classcmd.h. The first has the source and destination nodes, security flags, and transmission flags. The preferred way to fill it in is to use the RxToTxOptions() function to convert a corresponding structure on the incoming GET. If we send a reading in response to a button press or during logging, then for this demo we force the destination to be node 1, the controller (in a full product you would send it to the lifeline). The second structure contains multichannel endpoint information, a SUPERVISION_GET frame, and the actual transmission stored in the third structure. It is a union of structures of all possible frame contents, like those in the previous paragraph. We fill in the correct frame with the sensor value or supported bit mask and zero the supervision structure. Call Transport_SendResponseEP() with the options and transport buffer to send the report.

 

The transport layer has a second queue for outgoing frames, accessed with Transport_SendRequestEP(). Since requests must be used for unsolicited messages, we need to use it for values sent from button presses. However, it seems to have no difference in the packet sent (you'll notice the frame type is REQ in the zpiffer traces) and in the transport layer code there are only a couple of differences: requests override the source node for multi-channel frames, and the callbacks after transmission have different signatures, a request getting a transmission result and a response the transmit status.

 

To implement the command class, create ZAF_CC/CC_MultiSensor.[ch]. We'll have two functions for our ZWave functionality, handleCCMultilevelSensor() for incoming frames and sendSensorValue() for outgoing reports. All other transmissions are REPORTs in response to GETs and are performed in the handler. The source code contains these two functions, plus an internal helper with the common send code for both report paths. It also invokes the REGISTER_CC macro to add the handler to the command publisher. The handler is called in the application by Transport_ApplicationCommandHandlerEx() (the command publisher is already doing the routing in the frame handler), and values are sent in response to button press events.

 

The command class will also prototype five functions that must be implemented in the application to supply device-specific information. This architecture is similar to that used by the binary switch and user code commands already included in ZAF. appSensorValidType() validates the sensor type passed in a GET. appSensorMask() generates the bit mask for the supported sensor types, and appSensorScaleMask() the scale specification for a sensor type. appSensorLevel() returns the precision/scale/size byte for a sensor reading. appSensorValue() returns the humidity or temperature after reading it from the sensor. It does the Fahrenheit conversion locally.

 

The differences in the application code to Step 3b, ignoring edits for code cleanup, are

object

status

role

description

appnif1 (global var)

edit

transport

add command class

sample_ht

edit

logger

send reading over ZWave

send_temperature|humidity

new

ZWave

send reading over ZWave

Transport_AppCmdHandlerEx

edit

transport

call command class handler

handle_cmdstatus

edit

ZAF

callback after successful Tx

am_included

new

ZWave

test if node included in a network

appSensorValidType

new

CC support

validate requested sensor

appSensorMask

new

CC support

get mask for supported sensors

appSensorScaleMask

new

CC support

get mask for supported scales

appSensorValue

new

CC support

read sensor

appSensorLevel

new

CC support

get size of sensor value

 

An edit to handle_cmdstatus() is needed to avoid filling up a limited queue of callbacks after frames are transmitted. Comments in ZW_TransportEndpoint.c indicate the queue is supposed to be a ring, but it doesn't look like it cycles correctly and the head eventually collides with the tail. Executing the callback on a EZWAVECOMMANDSTATUS_TX event solves the problem.

 

The project files now include the source for the new command class.

hw/

src/

ZAF_AppUtil/

ZAF_CC/

displayconfigapp.h

config_app.h

ZAF_TSE.c

CC_MultiSensor.c

displayfont16x20HT.h

config_rf.h

 

CC_MultiSensor.h

em_types.h

HTSensor1.c

 

CC_Version.c

graphdisplay.c

i2cspmconfig.h

 

 

graphdisplay.h

 

 

 

textdisplay.c

 

 

 

testdisplayconfig.h

 

 

 

textdisplay.h

 

 

 

linked within Simplicity Studio

display.c

 

application_properties.c

CC_Supervision.c

displayls013b7dh03.c

 

AppTimer.c

multichannel.c

displaypalemlib.c

 

board_BRD420x.c

 

em_i2c.c

 

board_indicator.c

 

em_letimer.c

 

board.c

 

em_prs.c

 

ZAF_command_class_utils.c

 

gpiointerrupts.c

 

zaf_event_helper.c

 

i2cspm.c

 

ZAF_network_learn.c

 

startup_zgm13.S

 

ZAF_uart_utils.c

 

system_zgm13.c

 

ZW_TransportEndpoint.c

 

 

 

ZW_TransportMulticast.c

 

 

 

ZW_TransportSecProtocol.c

 

linked on file system

 

 

 

CC_Common.h

 

 

 

CC_Supervision.h

 

 

 

CC_Version.h

 

 

 

multichannel.h

 

 

Testing, you'll find the application now responds to all CC_SENSOR_MULTILEVEL commands. Buttons 3 and 4 will send single sensor readings, and button 2 will start logging with periodic updates sent to the controller. The setup of the log is still hard-coded, and we need to change that next with the Configuration class.

 

GET and REPORT with humidity reading

zpiffer trace of GET (left/blue) and REPORT (right/red) for humidity reading. Value is in milli-percent.

 

GET and REPORT with temperature reading

zpiffer trace of GET (left/blue) and REPORT (right/red) for temperature reading. Value is in millidegrees.

 

humidity REPORT after button presstemperature REPORT after button press

Humidity (left) and temperature (right) readings after button presses.