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);
}