Z-Wave HTSensor Project Code Snippets

Step 1: ApplicationInit()

#include "ZW_basis_api.h"

 

ZW_APPLICATION_STATUS ApplicationInit(EResetReason_t reset) {

return APPLICATION_RUNNING;

}

 

Step 1: config_app.h

#ifndef _CONFIG_APP_H

#define _CONFIG_APP_H

 

#define APP_VERSION                         1

#define APP_REVISION                        1

#define APP_PATCH                           0

 

#define APP_MANUFACTURER_ID            0x0000

#define APP_PRODUCT_ID                      1

 

/* This is from ZAF_ApplicationUtilities/ZW_product_id_enum.h. */

enum _PRODUCT_TYPE_ID_ENUM_ {

PRODUCT_TYPE_ID_ZWAVE_ZIP_GATEWAY = 1,

PRODUCT_TYPE_ID_ZWAVE,

PRODUCT_TYPE_ID_ZWAVE_PLUS,

PRODUCT_TYPE_ID_ZWAVE_PLUS_V2

};

 

#endif   /* _CONFIG_APP_H */

 

Step 2A: handle_event()

static void handle_event(void) {

enum zwevtIDs evt;                    /* an event in the queue */

 

while (xQueueReceive(evtqueue, (void *) &evt, 0)) {

DPRINTF("Event %d", evt);

/* Despite BTN_EVENT_SHORT_PRESS being a macro (see board.h), it calls a

  function so we can't use a switch statement here. */

if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB2) == (BUTTON_EVENT) evt) {

DPRINT("  - button 2\n");

if (0 < nsample) {

nsample = 0;

Board_SetLed(BOARD_LED2, LED_OFF);

} else {

nsample = 1;

Board_SetLed(BOARD_LED2, LED_ON);

}

} else if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB3) == (BUTTON_EVENT) evt) {

DPRINT("  - button 3 = measure temperature\n");

Board_SetLed(BOARD_LED4, LED_ON);

TimerStart(&simtimer, 1000);

} else if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB4) == (BUTTON_EVENT) evt) {

DPRINT("  - button 4 = measure humidity\n");

Board_SetLed(BOARD_LED4, LED_ON);

TimerStart(&simtimer, 1000);

} else /{

DPRINT("  - unknown button\n");

}

}

}

 

Step2A: simulate_ht()

static void simulate_ht(SSwTimer *timer) {

Board_SetLed(BOARD_LED4, LED_OFF);

}

 

Step 2A: ApplicationInit()

ZW_APPLICATION_STATUS ApplicationInit(EResetReason_t reset) {

int reg;                              /* true if task registered */

 

AppTimerInit(EVT_TIMER, NULL);

 

memset(&config, 0, sizeof(config));

 

/* Per ZW_application_transport_interface.h the pointers in the structure

  must not change although their contents can. */

 

nif.DeviceOptionsMask = DEVICE_OPTIONS_MASK;

nif.NodeType.generic = GENERIC_TYPE;

nif.NodeType.specific = SPECIFIC_TYPE;

nif.CommandClasses.UnSecureIncludedCC.iListLength = 0;

nif.CommandClasses.UnSecureIncludedCC.pCommandClasses = NULL;

nif.CommandClasses.SecureIncludedUnSecureCC.iListLength = 0;

nif.CommandClasses.SecureIncludedUnSecureCC.pCommandClasses = NULL;

nif.CommandClasses.SecureIncludedSecureCC.iListLength = 0;

nif.CommandClasses.SecureIncludedSecureCC.pCommandClasses = NULL;

 

radio.iListenBeforeTalkThreshold = ELISTENBEFORETALKTRESHOLD_DEFAULT;

radio.iTxPowerLevelMax = MAX_TXPWR;

radio.iTxPowerLevelAdjust = MEASURED_0DBM_TXPWR;

radio.eRegion = REGION_US;

 

s2keys = 0;

 

config.pVirtualSlaveNodeInfoTable = NULL;

config.pSecureKeysRequested = &s2keys;

config.pNodeInfo = &nif;

config.pRadioConfig = &radio;

 

nsample = 0;

Board_Init();

 

#ifdef DEBUGPRINT

ZAF_UART0_enable(115200, true, false);

DebugPrintConfig(dbgbuff, sizeof_array(dbgbuff), ZAF_UART0_tx_send);

#endif   /* DEBUGPRINT*/

 

reg = ZW_ApplicationRegisterTask(ApplicationTask, EVT_ZWRX, EVT_ZWCMD, &config);

ASSERT(reg);

 

return APPLICATION_RUNNING;

}

 

Step2A: ApplicationTask()

static void ApplicationTask(SApplicationHandles *zwsetup) {

SEventDistributor evtdistrib;         /* event router */

SQueueNotifying evtnotify;            /* event queue with notifications */

StaticQueue_t evtstatic;              /* stack-allocated queue structure */

enum zwevtIDs evtqueue_mem[MAXEVENT]; /* memory backing queue */

EventDistributorEventHandler handlers[] = {

AppTimerNotificationHandler,

handle_dummy,

handle_dummy,

handle_event

};                         /* event handlers (one per evtID) */

TaskHandle_t apptask;                 /* FreeRTOS handle for this task */

 

apptask = xTaskGetCurrentTaskHandle();

AppTimerSetReceiverTask(apptask);

ZAF_Init(apptask, zwsetup, NULL, NULL);

 

/* We never exit from this function so can use local variables rather than

  globals, because they won't be going away. */

evtqueue = xQueueCreateStatic(MAXEVENT, sizeof(evtqueue_mem[0]),

                             (uint8_t *) evtqueue_mem, &evtstatic);

QueueNotifyingInit(&evtnotify, evtqueue, apptask, EVT_APP);

ZAF_EventHelperInit(&evtnotify);

EventDistributorConfig(&evtdistrib, sizeof_array(handlers), handlers, NULL);

 

Board_EnableButton(BOARD_BUTTON_PB1);

Board_EnableButton(BOARD_BUTTON_PB2);

Board_EnableButton(BOARD_BUTTON_PB3);

Board_EnableButton(BOARD_BUTTON_PB4);

 

AppTimerRegister(&simtimer, true, simulate_ht);

 

Board_SetLed(BOARD_LED1, LED_OFF);

Board_SetLed(BOARD_LED2, LED_OFF);

Board_SetLed(BOARD_LED3, LED_OFF);

Board_SetLed(BOARD_LED4, LED_OFF);

 

/* The FreeRTOS task loop.  Do not exit without cleaning up. */

for (;;) {

EventDistributorDistribute(&evtdistrib, 10, 0);

}

}

 

Step 2B: read_HT()

static int read_HT(uint32_t *rh, int32_t *tC) {

I2C_TransferSeq_TypeDef tx;           /* packet to transfer */

I2C_TransferReturn_TypeDef status;    /* message back from sensor */

uint32_t meas;                        /* measured value */

uint8_t txbuff[1];                    /* outgoing message */

uint8_t rxbuff[2];                    /* incoming buffer */

uint8_t ntry;                         /* try counter */

 

if ((NULL == rh) && (NULL == tC)) {

return 0;

}

 

tx.addr = TH_I2CADDR;

tx.flags = I2C_FLAG_WRITE_READ;

tx.buf[0].data = txbuff;

tx.buf[0].len = sizeof_array(txbuff);

tx.buf[1].data = rxbuff;

tx.buf[1].len = sizeof_array(rxbuff);

 

if (NULL != rh) {

txbuff[0] = READ_H_HOLD;

ntry = 2;

status = i2cTransferInProgress;

while ((0 < ntry) && (i2cTransferInProgress == status)) {

status = I2CSPM_Transfer(I2C0, &tx);

ntry -= 1;

}

 

if ((0 == ntry) && (i2cTransferInProgress == status)) {

*rh = 0;

return -1;

} else if (i2cTransferDone != status) {

*rh = 0;

return -1;

}

 

meas = ((uint32_t) rxbuff[0] << 8) + (rxbuff[1] & 0xfc);

*rh = (meas * 125000L) >> 16;

if (*rh < 6000) {

*rh = 0;

} else if (106000 < *rh) {

*rh = 100000;

} else {

*rh -= 6000;

}

}

 

if (NULL != tC) {

if (NULL != rh) {

txbuff[0] = READ_T_AFTER_H;

} else {

txbuff[0] = READ_T_HOLD;

}

 

ntry = 2;

status = i2cTransferInProgress;

while ((0 < ntry) && (i2cTransferInProgress == status)) {

status = I2CSPM_Transfer(I2C0, &tx);

ntry -= 1;

}

 

if ((0 == ntry) && (i2cTransferInProgress == status)) {

*tC = 0;

return -1;

} else if (i2cTransferDone != status) {

*tC = 0;

return -1;

}

 

if ((0 == ntry) && (i2cTransferInProgress == status)) {

return -1;

} else if (i2cTransferDone != status) {

return -1;

}

 

meas = ((uint32_t) rxbuff[0] << 8) + (rxbuff[1] & 0xfc);

/* The formula in the spec overflows at 32 bit, ie. meas * 175720.

  Scaling the fraction by 8 above and below. */

*tC = (meas * 21965) >> 13;

*tC -= 46850;

}

 

return 0;

}

 

Step 2B: verify_sensor()

static void verify_sensor(void) {

I2C_TransferSeq_TypeDef tx;           /* packet to transfer */

I2C_TransferReturn_TypeDef status;    /* message back from sensor */

uint8_t txbuff[2];                    /* buffer holding outgoing message */

uint8_t rxbuff[8];                    /* buffer holding incoming message */

uint8_t ntry;                         /* try counter */

int8_t found;                         /* true if we found the sensor */

 

txbuff[0] = READ_ID2_BYTE1;

txbuff[1] = READ_ID2_BYTE2;

 

/* From datasheet, section 5. */

tx.addr = TH_I2CADDR;

tx.flags = I2C_FLAG_WRITE_READ;

tx.buf[0].data = txbuff;

tx.buf[0].len = sizeof_array(txbuff);

tx.buf[1].data = rxbuff;

tx.buf[1].len = sizeof_array(rxbuff);

 

found = 0;

ntry = 2;

status = i2cTransferInProgress;

while ((0 < ntry) && (i2cTransferInProgress == status)) {

status = I2CSPM_Transfer(I2C0, &tx);

if (i2cTransferDone == status) {

/* Per spec, 0x0D is a Si7013, 0x14 a Si7020.  These aren't on our

  devkit board. */

if (0x15 != rxbuff[0]) {

DPRINTF("FAILED to find sensor: ID 0x%02x not 7021 ID 0x%02x\n",

       rxbuff[0], 0x15);

} else {

DPRINT("found sensor!\n");

found = 1;

}

break;

}

ntry -= 1;

}

if ((0 == ntry) && (i2cTransferInProgress == status)) {

DPRINT("FAILED to find sensor: out of tries\n");

} else if (i2cTransferDone != status) {

/* See em_i2c.h for error codes. */

DPRINTF("FAILED to find sensor: error %d (%s)\n",

       status, i2c_strerror(status));

}

 

ASSERT(found);

}

 

Step 2B: setup_i2c()

static void setup_i2c(void) {

I2CSPM_Init_TypeDef i2c0;             /* I2C setup */

 

/* The values come from UG381.  Can't find a match in the hardware

  kits for our board. */

i2c0.port = I2C0;

i2c0.sclPort = gpioPortC;

i2c0.sclPin = 10;

i2c0.portLocationScl = 14;

i2c0.sdaPort = gpioPortC;

i2c0.sdaPin = 11;

i2c0.portLocationSda = 16;

i2c0.i2cRefFreq = 0;

i2c0.i2cMaxFreq = I2C_FREQ_STANDARD_MAX;

i2c0.i2cClhr = i2cClockHLRStandard;

I2CSPM_Init(&i2c0);

 

verify_sensor();

}

 

Step 2B: setup_lcd()

static void setup_lcd(void) {

/* Per the LCD spec SCS must be kept low while EXTCOMIN toggles for

  a static image. */

GPIO_PinModeSet(gpioPortD, 14, gpioModeWiredAndPullUp, 1);

GPIO_PinOutClear(gpioPortD, 14);

GPIO_PinModeSet(gpioPortD, 15, gpioModeWiredAndPullUp, 1);

GPIO_PinOutSet(gpioPortD, 15);

 

lcdon = 0;

GPIO_PinModeSet(gpioPortD, 13, gpioModeWiredAndPullUp, 1);

GPIO_PinOutClear(gpioPortD, 13);

AppTimerRegister(&lcdtimer, true, toggle_extcomin);

TimerStart(&lcdtimer, 250);

}

 

Step 2B: handle_event()

static void handle_event(void) {

enum zwevtIDs evt;                    /* an event in the queue */

uint32_t rh;                          /* relative humidity measurement */

int32_t tC;                           /* temperature measurement */

 

while (xQueueReceive(evtqueue, (void *) &evt, 0)) {

/* Despite BTN_EVENT_SHORT_PRESS being a macro (see board.h), it calls a

  function so we can't use a switch statement here. */

if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB2) == (BUTTON_EVENT) evt) {

/* The display EXTCOMIN timer will handle the sampling.  We multiple

  by 4 because the software timer lasts 250 ms. */

if (0 == nsample) {

Board_SetLed(BOARD_LED2, LED_ON);

nsample = NLOG * TSAMPLE * 4;

}

} else if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB3) == (BUTTON_EVENT) evt) {

Board_SetLed(BOARD_LED4, LED_ON);

read_HT(NULL, &tC);

Board_SetLed(BOARD_LED4, LED_OFF);

DPRINTF("  temperature  %3d.%03d C\n", tC/1000, ((tC<0) ? -tC : tC)%1000);

} else if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB4) == (BUTTON_EVENT) evt) {

Board_SetLed(BOARD_LED4, LED_ON);

read_HT(&rh, NULL);

Board_SetLed(BOARD_LED4, LED_OFF);

DPRINTF("  humidity     %3d.%03d %%\n", rh/1000, rh%1000);

}

}

}

 

Step 2C: textdisplay.c

#ifdef TEXTDISPLAY_NUMBER_FONT_16x20HT

#include "displayfont16x20HT.h"

#define FONT_ASCII_START  ('-')

#define FONT_CHARACTERS    (16)

#define FONT_BITS_MASK    (0xF)

#define FONT_BITS_LOG2      (4)

#define fontBits numbers_16x20_bits

#endif

 

Step 2C: displayconfigapp.h

/* Include textdisplay functionality using our custom font and with

  escape codes. */

#define INCLUDE_TEXTDISPLAY_SUPPORT

#define TEXTDISPLAY_NUMBER_FONT_16x20HT

#define INCLUDE_VIDEO_TERMINAL_ESCAPE_SEQUENCE_SUPPORT

 

Step 2C: display_humidity()

static EMSTATUS display_humidity(uint32_t rh) {

uint32_t pwr10;                       /* which digit to print */

EMSTATUS err;

 

/* Maximum width is 7 characters, but since we should never see 100%,

  the first digit will be a blank.  Therefore left-justify the

  string so it looks centered, usually. */

 

err = TEXTDISPLAY_WriteString(app.text, TEXTDISPLAY_ESC_SEQ_CURSOR_HOME_VT52);

RET_ON_ERR(err);

 

/* Round up the tenths digit. */

rh += 50;

 

pwr10 = 100000;

while (10 < pwr10) {

/* Don't print leading zeros before the unit's digit (pwr10==1000),

  put do print it and after the decimal point. */

if ((0 == (rh / pwr10)) && (1000 < pwr10)) {

err = TEXTDISPLAY_WriteChar(app.text, ' ');

RET_ON_ERR(err);

} else {

err = TEXTDISPLAY_WriteChar(app.text, '0' + (rh / pwr10));

RET_ON_ERR(err);

}

if (1000 == pwr10) {

err = TEXTDISPLAY_WriteChar(app.text, '.');

RET_ON_ERR(err);

}

 

rh %= pwr10;

pwr10 /= 10;

}

 

err = TEXTDISPLAY_WriteChar(app.text, ' ');

RET_ON_ERR(err);

 

err = TEXTDISPLAY_WriteChar(app.text, '/');

RET_ON_ERR(err);

 

return EMSTATUS_OK;

}

 

Step 2C: appconfig struct

struct appconfig {

DISPLAY_Device_t lcd;                 /* overall display setup */

TEXTDISPLAY_Handle_t text;            /* text half of LCD */

GRAPHDISPLAY_Handle_t graph;          /* graphical half of LCD */

};

 

/* setup/state of this application */

static struct appconfig app;

 

Step 2C: setup_display()

static uint32_t setup_display(void) {

TEXTDISPLAY_Config_t textcfg;         /* textdisplay configuration */

GRAPHDISPLAY_Config_t graphcfg;       /* graphdisplay configuration */

uint32_t err;

 

err = DISPLAY_Init();

RET_ON_ERR(err);

 

err = DISPLAY_DeviceGet(0, &app.lcd);

RET_ON_ERR(err);

 

/* Function only uses these members of cfg: displayDeviceNo,

  scrollEnable, lfToCrLf. */

textcfg.displayDeviceNo = 0;

textcfg.scrollEnable = 0;

textcfg.lfToCrLf = 0;

textcfg.maxlines = 2;

err = TEXTDISPLAY_New(&textcfg, &app.text);

RET_ON_ERR(err);

 

graphcfg.displayDeviceNo = 0;

graphcfg.y0 = GRAPH_TOP;

graphcfg.hdraw = GRAPH_HT;

graphcfg.ngraph = 2;

err = GRAPHDISPLAY_New(&graphcfg, &app.graph);

RET_ON_ERR(err);

 

return 0;

}

 

Step 2C: htsamples struct

struct htsamples {

uint8_t nsample;                      /* number samples to take */

uint8_t tsample;                      /* time in seconds between data logs */

uint8_t id;                           /* current sample index */

uint32_t rh[NLOG];                    /* humidity (milli-percent) */

uint32_t rhmin;                       /* lowest humidity in log */

uint32_t rhmax;                       /* largest humidity in log */

uint32_t g0min;                       /* lower humidity bound graph 0 */

uint32_t g0scl;                       /* scaling humidity graph 0 */

int32_t tC[NLOG];                     /* temperature (milli-degrees C) */

int32_t tCmin;                        /* lowest temperature in log */

int32_t tCmax;                        /* highest temperature in log */

int32_t g1min;                        /* lower temperature bound graph 1 */

int32_t g1scl;

};

 

/* humidity, temperature logs */

static struct htsamples htlog;

 

Step 3A: handle_cmdstatus()

static void handle_cmdstatus(void) {

SApplicationHandles *zwapp;           /* ZWave configuration and queues */

QueueHandle_t queue;                  /* command status queue */

SZwaveCommandStatusPackage status;    /* command status */

enum netstate learned;                /* learn mode outcome */

 

zwapp = ZAF_getAppHandle();

queue = zwapp->ZwCommandStatusQueue;

 

while (pdTRUE == xQueueReceive(queue, (void *) &status, 0)) {

switch (status.eStatusType) {

case EZWAVECOMMANDSTATUS_TX:

DPRINTF("command status %3d (TX)\n", status.eStatusType);

break;

case EZWAVECOMMANDSTATUS_GENERATE_RANDOM:

DPRINTF("command status %3d (GEN RND)\n", status.eStatusType);

break;

case EZWAVECOMMANDSTATUS_NODE_INFO:

DPRINTF("command status %3d (NIF)\n", status.eStatusType);

break;

case EZWAVECOMMANDSTATUS_LEARN_MODE_STATUS:

if (EINCLUSIONSTATE_EXCLUDED == zwapp->pNetworkInfo->eInclusionState) {

learned = LEARN_EXCLUDED;

} else {

learned = LEARN_INCLUDED;

}

switch (status.Content.LearnModeStatus.Status) {

case ELEARNSTATUS_ASSIGN_COMPLETE:

DPRINTF("command status %3d (LEARN STATUS) - complete\n",

       status.eStatusType);

app.learning = false;

break;

case ELEARNSTATUS_ASSIGN_NODEID_DONE:         /* fall through */

case ELEARNSTATUS_ASSIGN_RANGE_INFO_UPDATE:   /* fall through */

case ELEARNSTATUS_ASSIGN_INFO_PENDING:        /* fall through */

case ELEARNSTATUS_ASSIGN_WAITING_FOR_FIND:    /* fall through */

case ELEARNSTATUS_SMART_START_IN_PROGRESS:    /* fall through */

case ELEARNSTATUS_LEARN_IN_PROGRESS:

/* Source code has separate DPRINTF per case, for tracking. */

DPRINTF("command status %3d (LEARN STATUS) - in progress\n",

       status.eStatusType);

learned = LEARN_UNDERWAY;

break;

case ELEARNSTATUS_LEARN_MODE_COMPLETED_TIMEOUT:    /* fall through */

case ELEARNSTATUS_LEARN_MODE_COMPLETED_FAILED:

DPRINTF("command status %3d (LEARN STATUS) - failed\n",

       status.eStatusType);

learned = LEARN_FAILED;

app.learning = false;

break;

default:

DPRINTF("command status %3d (LEARN STATUS) - unknown status\n",

       status.eStatusType);

}

set_indicator(learned);

break;

/* Many other EZWAVECOMMANDSTATUS_* states omitted.  See source code. */

default:

DPRINTF("command status type %3d (???)\n", status.eStatusType);

}

}

}

 

Step 3A: handle_frame()

static void handle_frame(void) {

SApplicationHandles *zwapp;           /* ZWave configuration and queues */

QueueHandle_t queue;                  /* frame queue */

SZwaveReceivePackage rxpkg;           /* incoming frame and meta-data */

 

zwapp = ZAF_getAppHandle();

queue = zwapp->ZwRxQueue;

 

while (pdTRUE == xQueueReceive(queue, (void *) &rxpkg, 0)) {

switch (rxpkg.eReceiveType) {

case EZWAVERECEIVETYPE_SINGLE:

DPRINTF("received frame type %3d (SINGLE)\n", rxpkg.eReceiveType);

break;

case EZWAVERECEIVETYPE_MULTI:

DPRINTF("received frame type %3d (MULTI)\n", rxpkg.eReceiveType);

break;

case EZWAVERECEIVETYPE_NODE_UPDATE:

DPRINTF("received frame type %3d (UPDATE)\n", rxpkg.eReceiveType);

break;

case EZWAVERECEIVETYPE_SECURITY_EVENT:

DPRINTF("received frame type %3d (SECURITY)\n", rxpkg.eReceiveType);

break;

default:

DPRINTF("received frame type %3d (???)\n", rxpkg.eReceiveType);

}

}

}

 

Step 3A: handle_event()

static void handle_event(void) {

SApplicationHandles *zwapp;           /* ZWave configuration and queues */

enum zwevtIDs evt;                    /* an event in the queue */

uint32_t rh;                          /* relative humidity measurement */

int32_t tC;                           /* temperature measurement */

uint8_t wgraph;                       /* width drawing area = max samples */

uint32_t err;

 

while (xQueueReceive(evtqueue, (void *) &evt, 0)) {

/* Despite BTN_EVENT_SHORT_PRESS being a macro (see board.h), it calls a

  function so we can't use a switch statement here. */

if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB1) == (BUTTON_EVENT) evt) {

if (app.learning) {

/* You could cancel the learn ... */

continue;

}

zwapp = ZAF_getAppHandle();

if (EINCLUSIONSTATE_EXCLUDED == zwapp->pNetworkInfo->eInclusionState) {

ZAF_setNetworkLearnMode(E_NETWORK_LEARN_MODE_INCLUSION,

                       ERESETREASON_OTHER);

} else {

ZAF_setNetworkLearnMode(E_NETWORK_LEARN_MODE_EXCLUSION_NWE,

                       ERESETREASON_OTHER);

}

} else if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB2) == (BUTTON_EVENT) evt) {

/* Deleted for snippet - no change from Step2C. */

} else if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB3) == (BUTTON_EVENT) evt) {

/* Deleted for snippet - no change from Step2C. */

} else if (BTN_EVENT_SHORT_PRESS(BOARD_BUTTON_PB4) == (BUTTON_EVENT) evt) {

/* Deleted for snippet - no change from Step2C. */

}

}

}

 

Step 3A: set_indicator()

static void set_indicator(enum netstate state) {

 

switch (state) {

case LEARN_INCLUDED:

Board_IndicatorControl(0, 0, 0, false);

Board_SetLed(BOARD_LED1, LED_ON);

break;

case LEARN_EXCLUDED:

Board_IndicatorControl(0, 0, 0, false);

Board_SetLed(BOARD_LED1, LED_OFF);

break;

case LEARN_UNDERWAY:

Board_IndicatorControl(100, 900, 0, false);

break;

case LEARN_FAILED:

Board_IndicatorControl(100, 100, 75, false);

break;

default:

DPRINTF("unknown network state %d\n", state);

Board_IndicatorControl(0, 0, 0, false);

}

}

 

Step 3B: handle_frame()

static void handle_frame(void) {

SApplicationHandles *zwapp;           /* ZWave configuration and queues */

QueueHandle_t queue;                  /* frame queue */

SZwaveReceivePackage rxpkg;           /* incoming frame and meta-data */

uint8_t *frame;                       /* raw payload */

int i;

 

zwapp = ZAF_getAppHandle();

queue = zwapp->ZwRxQueue;

 

while (pdTRUE == xQueueReceive(queue, (void *) &rxpkg, 0)) {

switch (rxpkg.eReceiveType) {

case EZWAVERECEIVETYPE_SINGLE:

DPRINTF("single RX frame len %d\n", rxpkg.uReceiveParams.Rx.iLength);

ZAF_CP_CommandPublish(ZAF_getCPHandle(), &rxpkg);

break;

/* Other cases are unchanged from Step 3A ... */

}

}

}

 

Step 3B: Transport_ApplicationCommandHandlerEx()

received_frame_status_t

Transport_ApplicationCommandHandlerEx(RECEIVE_OPTIONS_TYPE_EX *rxopt,

                                     ZW_APPLICATION_TX_BUFFER *frame,

                                     uint8_t len) {

received_frame_status_t status;       /* handler result */

 

DPRINTF("  handle command class 0x%02x\n", frame->ZW_Common.cmdClass);

 

switch (frame->ZW_Common.cmdClass) {

case COMMAND_CLASS_VERSION:

status = CC_Version_handler(rxopt, frame, len);

break;

default:

status = RECEIVED_FRAME_STATUS_NO_SUPPORT;

break;

}

 

return status;

}

 

Step 3B: NIF/command class globals

/* unsecured command classes */

static uint8_t cc_unsecure[] = {

COMMAND_CLASS_VERSION

};

 

/* first node info setup (must be same info as appnif2 - use copy_nif() */

static app_node_information_t appnif1 = {

cc_unsecure, sizeof_array(cc_unsecure),

NULL, 0, NULL, 0,

DEVICE_OPTIONS_MASK, {GENERIC_TYPE, SPECIFIC_TYPE}

};

 

Step 3B: ApplicationInit()

ZW_APPLICATION_STATUS ApplicationInit(EResetReason_t reset) {

uint32_t err;

 

AppTimerInit(EVT_TIMER, NULL);

 

memset(dbgbuff, 0, sizeof_array(dbgbuff));

memset(&zwcfg, 0, sizeof(zwcfg));

 

/* Per ZW_application_transport_interface.h the pointers in the structure

  must not change although their contents can. */

 

copy_nif(&appnif1, &appnif2);

 

radio.iListenBeforeTalkThreshold = ELISTENBEFORETALKTRESHOLD_DEFAULT;

/* These values come from config_rf.h. */

radio.iTxPowerLevelMax = MAX_TXPWR;

radio.iTxPowerLevelAdjust = MEASURED_0DBM_TXPWR;

radio.eRegion = RADIO_REGION;

 

s2keys = 0;

 

zwcfg.pVirtualSlaveNodeInfoTable = NULL;

zwcfg.pSecureKeysRequested = &s2keys;

zwcfg.pNodeInfo = &appnif2;

zwcfg.pRadioConfig = &radio;

 

clear_log();

 

err = Board_Init();

ASSERT(0 == err);

 

#ifdef DEBUGPRINT

ZAF_UART0_enable(115200, true, false);

DebugPrintConfig(dbgbuff, sizeof_array(dbgbuff), ZAF_UART0_tx_send);

DPRINT("\n");

#endif   /* DEBUGPRINT*/

 

err = BRD420xBoardInit(radio.eRegion);

ASSERT(0 != err);

err = setup_display();

ASSERT(0 == err);

 

CC_Version_SetApplicationVersionInfo(APP_VERSION, APP_REVISION, APP_PATCH, 0);

 

err = ZW_ApplicationRegisterTask(ApplicationTask, EVT_ZWRX, EVT_ZWCMD, &zwcfg);

ASSERT(err);

 

return APPLICATION_RUNNING;

}

 

Step 3B: CC_Version.c

#include "CC_Version.h"

 

uint8_t CC_Version_getNumberOfFirmwareTargets_handler(void) {

return 1;

}

 

void CC_Version_GetFirmwareVersion_handler(uint8_t fwID,

                                          VG_VERSION_REPORT_V2_VG *fwver) {

fwver->firmwareVersion = 2;

fwver->firmwareSubVersion = 1;

}

 

uint8_t CC_Version_GetHardwareVersion_handler(void) {

return 3;

}

 

Step 3C: handleCCMultlevelSensor()

received_frame_status_t

handleCCMultilevelSensor(RECEIVE_OPTIONS_TYPE_EX *rxopt,

                        ZW_APPLICATION_TX_BUFFER *rxcmd, uint8_t rxlen) {

ZAF_TRANSPORT_TX_BUFFER txbuff;       /* transmit buffer */

ZW_APPLICATION_TX_BUFFER *txcmd;      /* application data within txbuff */

TRANSMIT_OPTIONS_TYPE_SINGLE_EX *txopt;   /* outgoing options */

enum EQueueNotifyingStatus status;    /* transmit result */

uint8_t masks[16];                    /* bit masks for supported sensors */

uint8_t nmask;                        /* number masks used */

uint8_t type;                         /* sensor type to handle */

uint8_t scale;                        /* scale of sensor to pick */

uint8_t level;                        /* precision/scale/size of value */

int32_t val;                          /* value from sensor */

uint8_t txlen;                        /* length of transmit buffer */

 

txcmd = &(txbuff.appTxBuf);

memset(txcmd, 0, sizeof(*txcmd));

RxToTxOptions(rxopt, &txopt);

 

switch (rxcmd->ZW_Common.cmd) {

case SENSOR_MULTILEVEL_GET_V11:

if (Check_not_legal_response_job(rxopt)) {

DPRINTF("  SENSOR_MULTILEVEL_GET not legal response!\n");

return RECEIVED_FRAME_STATUS_FAIL;

}

 

/* Per spec, first two commands verify the validity of the type and

  scale, substituting default values if not. */

type = rxcmd->ZW_SensorMultilevelGetV11Frame.sensorType;

scale = (rxcmd->ZW_SensorMultilevelGetV11Frame.properties1 >> 3) & 0x03;

val = appSensorValue(type, scale);

level = appSensorLevel(type, scale);

 

return send_value(txcmd, txopt, appSensorValidType(type), val, level, 1);

case SENSOR_MULTILEVEL_SUPPORTED_GET_SENSOR_V11:

if (Check_not_legal_response_job(rxopt)) {

DPRINTF("  SENSOR_MULTILEVEL_SUPPORTED_GET_SENSOR illegal response!\n");

return RECEIVED_FRAME_STATUS_FAIL;

}

 

appSensorMask(masks, &nmask);

switch (nmask) {

/* Cases 1, 3, and 4 are similar and found in the source. */

case 2:

txcmd->ZW_SensorMultilevelSupportedSensorReport2byteV11Frame.cmdClass =

COMMAND_CLASS_SENSOR_MULTILEVEL_V11;

txcmd->ZW_SensorMultilevelSupportedSensorReport2byteV11Frame.cmd =

SENSOR_MULTILEVEL_SUPPORTED_SENSOR_REPORT_V11;

txcmd->ZW_SensorMultilevelSupportedSensorReport2byteV11Frame.bitMask1 =

masks[0];

txcmd->ZW_SensorMultilevelSupportedSensorReport2byteV11Frame.bitMask2 =

masks[1];

txlen = sizeof(ZW_SENSOR_MULTILEVEL_SUPPORTED_SENSOR_REPORT_2BYTE_V11_FRAME);

break;

}

break;

case SENSOR_MULTILEVEL_SUPPORTED_GET_SCALE_V11:

if (Check_not_legal_response_job(rxopt)) {

DPRINTF("  SENSOR_MULTILEVEL_SUPPORTED_GET_SCALE illegal response!\n");

return RECEIVED_FRAME_STATUS_FAIL;

}

 

type = rxcmd->ZW_SensorMultilevelSupportedGetScaleV11Frame.sensorType;

 

txcmd->ZW_SensorMultilevelSupportedScaleReportV11Frame.cmdClass =

COMMAND_CLASS_SENSOR_MULTILEVEL_V11;

txcmd->ZW_SensorMultilevelSupportedScaleReportV11Frame.cmd =

SENSOR_MULTILEVEL_SUPPORTED_SCALE_REPORT_V11;

txcmd->ZW_SensorMultilevelSupportedScaleReportV11Frame.sensorType = type;

txcmd->ZW_SensorMultilevelSupportedScaleReportV11Frame.properties1 =

appSensorScaleMask(type);

txlen = sizeof(ZW_SENSOR_MULTILEVEL_SUPPORTED_SCALE_REPORT_V11_FRAME);

break;

/* Placeholders for REPORT, SUPPORTED_SENSOR_REPORT, SUPPORT_SCALE_REPORT

  are found in source. */

}

 

status = Transport_SendResponseEP((uint8_t *) txcmd, txlen, txopt, NULL);

if (EQUEUENOTIFYING_STATUS_SUCCESS == status) {

return RECEIVED_FRAME_STATUS_SUCCESS;

} else {

return RECEIVED_FRAME_STATUS_FAIL;

}

}

 

Step 3C: sendSensorValue()

received_frame_status_t sendSensorValue(uint8_t type, int32_t val,

                                       uint8_t level) {

ZAF_TRANSPORT_TX_BUFFER txbuff;       /* transmit buffer */

ZW_APPLICATION_TX_BUFFER *txcmd;      /* application data within txbuff */

TRANSMIT_OPTIONS_TYPE_SINGLE_EX *txopt;   /* outgoing options */

RECEIVE_OPTIONS_TYPE_EX rxopt;            /* dummy incoming options */

 

txcmd = &(txbuff.appTxBuf);

memset(txcmd, 0, sizeof(*txcmd));

 

memset(&rxopt, 0, sizeof(rxopt));

rxopt.sourceNode.nodeId = 1;

RxToTxOptions(&rxopt, &txopt);

 

return send_value(txcmd, txopt, type, val, level, 0);

}

 

received_frame_status_t send_value(ZW_APPLICATION_TX_BUFFER *txcmd,

                                  TRANSMIT_OPTIONS_TYPE_SINGLE_EX *txopt,

                                  uint8_t type, int32_t val, uint8_t level,

                                  uint8_t isRES) {

enum EQueueNotifyingStatus status;    /* transmit result */

int16_t sval;                         /* 2 byte value */

int8_t cval;                          /* 1 byte value */

uint8_t txlen;                        /* length of transmit buffer */

 

switch (level & 0x07) {

/* Cases 1 and 4 are similar and found in the source. */

case 2:

sval = CLIP(val, -32768, 32767);

txcmd->ZW_SensorMultilevelReport2byteV11Frame.cmdClass =

COMMAND_CLASS_SENSOR_MULTILEVEL_V11;

txcmd->ZW_SensorMultilevelReport2byteV11Frame.cmd =

SENSOR_MULTILEVEL_REPORT_V11;

txcmd->ZW_SensorMultilevelReport2byteV11Frame.sensorType = type;

txcmd->ZW_SensorMultilevelReport2byteV11Frame.level = level;

txcmd->ZW_SensorMultilevelReport2byteV11Frame.sensorValue1 =

(sval >> 8) & 0xff;

txcmd->ZW_SensorMultilevelReport2byteV11Frame.sensorValue2 = sval & 0xff;

txlen = sizeof(ZW_SENSOR_MULTILEVEL_REPORT_2BYTE_V11_FRAME);

break;

}

 

if (isRES) {

status = Transport_SendResponseEP((uint8_t *) txcmd, txlen, txopt, NULL);

} else {

status = Transport_SendRequestEP((uint8_t *) txcmd, txlen, txopt, NULL);

}

if (EQUEUENOTIFYING_STATUS_SUCCESS == status) {

return RECEIVED_FRAME_STATUS_SUCCESS;

} else {

return RECEIVED_FRAME_STATUS_FAIL;

}

}

 

Step 3C: appSensorValidType()

uint8_t appSensorValidType(uint8_t type) {

switch (type) {

case SENSOR_TEMP:     /* fall through */

case SENSOR_RH:

return type;

default:

return SENSOR_RH;

}

}

 

Step 3C: appSensorMask()

void appSensorMask(uint8_t *masks, uint8_t *nmask) {

masks[0] = 0x11;

*nmask = 1;

}

 

Step 3C: appSensorScaleMask()

uint8_t appSensorScaleMask(uint8_t type) {

switch (type) {

case SENSOR_TEMP:

/* 0x01 for Celcius, 0x02 for Fahrenheit. */

return 0x03;

case SENSOR_RH:

/* 0x01 for relative humidity. */

return 0x01;

default:

/* Recommended behavior by spec. */

return 0x00;

}

}

 

Step 3C: appSensorLevel()

uint8_t appSensorLevel(uint8_t type, uint8_t scale) {

switch (type) {

case SENSOR_TEMP:

switch (scale) {

case SCALE_DEGF:

return 0x6C;

case SCALE_DEGC:     /* fall through */

default:

return 0x64;

}

case SENSOR_RH:        /* fall through */

default:

switch (scale) {

case SCALE_PCT:      /* fall through */

default:

return 0x64;

}

}

}

 

Step 3C: appSensorValue()

int32_t appSensorValue(uint8_t type, uint8_t scale) {

uint32_t rh;                          /* humidity measurement */

int32_t tC;                           /* temperature measurement */

uint32_t err;

 

switch (type) {

case SENSOR_TEMP:

err = read_HT(NULL, &tC);

if (0 != err) {

return 0;

}

switch (scale) {

case SCALE_DEGF:

/* + 2 for round-off. */

return (32000 + (((9 * tC) + 2) / 5));

case SCALE_DEGC:     /* fall through */

default:

return tC;

}

case SENSOR_RH:        /* fall through */

default:

err = read_HT(&rh, NULL);

if (0 != err) {

return 0;

}

switch (scale) {

case SCALE_PCT:       /* fall through */

default:

return rh;

}

}

}

 

Step 3C: handle_cmdstatus()

static void handle_cmdstatus(void) {

SApplicationHandles *zwapp;           /* ZWave configuration and queues */

QueueHandle_t queue;                  /* command status queue */

SZwaveCommandStatusPackage cmdstatus; /* general command status */

SZWaveTransmitStatus *txstatus;       /* specific status for queued item */

void (*txcb)(uint8_t, TX_STATUS_TYPE *);  /* callback fn after transmit */

enum netstate learned;                /* learn mode outcome */

 

zwapp = ZAF_getAppHandle();

queue = zwapp->ZwCommandStatusQueue;

 

while (pdTRUE == xQueueReceive(queue, (void *) &cmdstatus, 0)) {

switch (cmdstatus.eStatusType) {

case EZWAVECOMMANDSTATUS_TX:

/* Make sure the callback is run or the queue will fill up quickly. */

txstatus = &cmdstatus.Content.TxStatus;

if (txstatus->bIsTxFrameLegal && (NULL != txstatus->Handle)) {

if (ZAF_TSE_TXCallback == txstatus->Handle) {

ZAF_TSE_TXCallback(NULL);

} else {

txcb = txstatus->Handle;

txcb(txstatus->TxStatus, &txstatus->ExtendedTxStatus);

}

}

break;

/* Rest of cases unchanged from Step 3B. */

}

}

}

 

Step 3D: appConfigValidParam()

uint8_t appConfigValidParam(uint8_t paramID) {

switch (paramID) {

case CONFIG_TSAMPLE:     /* fall through */

case CONFIG_NSAMPLE:     /* fall through */

case CONFIG_RUN:

return paramID;

default:

return CONFIG_TSAMPLE;

}

}

 

Step 3D: appConfigValue()

int32_t appConfigValue(uint8_t paramID) {

int32_t val;                          /* value to return */

 

END_ON_LOCKERR(htlog_lock);

switch (paramID) {

case CONFIG_NSAMPLE:

val = htlog.nsample;

break;

case CONFIG_RUN:

val = htlog.running;

break;

case CONFIG_TSAMPLE:

val = htlog.tsample;

break;

default:

val = 0;

break;

}

END_ON_UNLOCKERR(htlog_lock);

 

return val;

 

onerror:

return 0;

}

 

Step 3D: appConfigLevel()

uint8_t appConfigLevel(uint8_t paramID) {

switch (paramID) {

case CONFIG_NSAMPLE:     /* fall through */

case CONFIG_RUN:

return 1;

case CONFIG_TSAMPLE:     /* fall through */

default:

return 4;

}

}

 

Step 3D: appConfigSet()

bool appConfigSet(uint8_t paramID, int32_t val, uint8_t dflt) {

uint32_t err;

 

END_ON_LOCKERR(htlog_lock);

 

switch (paramID) {

case CONFIG_NSAMPLE:

if (htlog.running) {

break;

}

if (dflt || (val < 0)) {

htlog.nsample = 0;

} else {

htlog.nsample = val;

}

err = bound_nsample(&(htlog.nsample));

if (EMSTATUS_OK != err) {

UNLOCK(htlog_lock);

return false;

}

DPRINTF(" set logger parameter nsample to %d\n", htlog.nsample);

break;

case CONFIG_TSAMPLE:

if (htlog.running) {

break;

}

if (dflt) {

htlog.tsample = TSAMPLE;

} else if (val <= 0) {

htlog.tsample = 1;

} else {

htlog.tsample = val;

}

DPRINTF(" set logger parameter tsample to %d\n", htlog.tsample);

break;

case CONFIG_RUN:

if (htlog.running) {

if (!val) {

DPRINTF(" stop logging\n");

htlog.nsample = 0;

} else {

DPRINTF("ignoring log start because already running\n");

}

} else {

if (0 == htlog.nsample) {

DPRINTF(" must set number of samples before starting log\n");

} else if (val) {

DPRINTF(" start logging %d samples every %d sec\n",

       htlog.nsample, htlog.tsample);

END_ON_UNLOCKERR(htlog_lock);

start_sampling();

return true;

} else {

DPRINTF("ignoring log stop because already stopped\n");

}

}

break;

default:

UNLOCK(htlog_lock);

return false;

}

 

END_ON_UNLOCKERR(htlog_lock);

return true;

 

onerror:

return false;

}

 

Step 3E: NIF/command class globals

/* unsecured command classes */

static uint8_t cc_unsecure[] = {

COMMAND_CLASS_VERSION,

COMMAND_CLASS_SECURITY

};

 

/* secured command classes available unsecured */

static uint8_t cc_open_secure[] = {

COMMAND_CLASS_SECURITY

};

 

/* secured command classes */

static uint8_t cc_secure[] = {

COMMAND_CLASS_SENSOR_MULTILEVEL,

COMMAND_CLASS_CONFIGURATION,

COMMAND_CLASS_SECURITY

};

 

/* first node info setup (must be same info as appnif2 - use copy_nif() */

static app_node_information_t appnif1 = {

cc_unsecure, sizeof_array(cc_unsecure),

cc_open_secure, sizeof_array(cc_open_secure),

cc_secure, sizeof_array(cc_secure),

DEVICE_OPTIONS_MASK, {GENERIC_TYPE, SPECIFIC_TYPE}

};

 

Step 3E: sendSensorValue()

received_frame_status_t sendSensorValue(uint8_t type, int32_t val,

                                       uint8_t level) {

ZAF_TRANSPORT_TX_BUFFER txbuff;       /* transmit buffer */

ZW_APPLICATION_TX_BUFFER *txcmd;      /* application data within txbuff */

TRANSMIT_OPTIONS_TYPE_SINGLE_EX *txopt;   /* outgoing options */

RECEIVE_OPTIONS_TYPE_EX rxopt;            /* dummy incoming options */

 

txcmd = &(txbuff.appTxBuf);

memset(txcmd, 0, sizeof(*txcmd));

 

memset(&rxopt, 0, sizeof(rxopt));

/* Fake an incoming packet from the host, assuming its encrypted if

  we have an S0 security key.  This would eventually go to the

  lifeline. */

rxopt.sourceNode.nodeId = 1;

if (ZAF_GetSecurityKeys() & SECURITY_KEY_S0_BIT) {

rxopt.securityKey = SECURITY_KEY_S0;

}

RxToTxOptions(&rxopt, &txopt);

 

return send_value(txcmd, txopt, type, val, level, 0);

}