NOTICE: The Processors Wiki will End-of-Life on January 15, 2021. It is recommended to download any files or other content you may need that are hosted on processors.wiki.ti.com. The site is now set to read only.
Occupancy Sensor
This sample demonstrates how to implement an occupancy sensor device and how the sensor communicates with other devices using the standard ZigBee Home Automation profile. The example is written for ease of use first, performance second. There is most likely a faster way to do everything, but we wanted to ensure that the examples are easy to understand.
The installer that will add the sample application to an existing Z-Stack Home installation can be downloaded here.
Contents
Sample Devices[edit]
In this sample, two devices are implemented. One is an occupancy sensor device and the other is a general on/off light with occupancy sensing client.
- Occupancy Sensor Device
- OnOff Light Device
Occupancy Sensor Device Detail[edit]
- Cluster Implemented. (In HA spec, the mandatory cluster of occupancy sensor is Occupancy Sensing Cluster)
- Server: Occupancy Sensing Cluster
- Client: None
<syntaxhighlight lang='c'> // zcl_SampleOcc_data.c // This is the Cluster ID List and should be filled with Application // specific cluster IDs.
- define ZCLSampleOcc_MAX_INCLUSTERS 2
const cId_t zclSampleOcc_InClusterList[ZCLSampleOcc_MAX_INCLUSTERS] = {
ZCL_CLUSTER_ID_GEN_BASIC, ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING
};
SimpleDescriptionFormat_t zclSampleOcc_SimpleDesc =
{
SampleOcc_ENDPOINT, // int Endpoint; ZCL_HA_PROFILE_ID, // uint16 AppProfId[2]; ZCL_HA_DEVICEID_OCCUPANCY_SENSOR, // uint16 AppDeviceId[2]; SampleOcc_DEVICE_VERSION, // int AppDevVer:4; SampleOcc_FLAGS, // int AppFlags:4; ZCLSampleOcc_MAX_INCLUSTERS, // byte AppNumInClusters; (cId_t *)zclSampleOcc_InClusterList, // byte *pAppInClusterList; 0, // byte AppNumInClusters; NULL // byte *pAppInClusterList;
}; </syntaxhighlight>
- Attribute Implemented
- 0x0000: Occupancy
- 0x0001: OccupancySensorType
- 0x0010: PIROccupiedToUnoccupiedDelay
- 0x0011: PIRUnoccupiedToOccupiedDelay
<syntaxhighlight lang='c'> // zcl_SampleOcc_data.c // Occupancy Cluster uint8 zclSampleOcc_Occupied = 0; /* Set default to Not be occupied */ uint8 zclSampleOcc_OccType = MS_OCCUPANCY_SENSOR_TYPE_PIR; uint8 zclSampleOcc_PirOccupiedToUnoccupiedDelay = 5; uint8 zclSampleOcc_PirUnoccupiedToOccupiedDelay = 5;
/*********************************************************************
* ATTRIBUTE DEFINITIONS - Uses REAL cluster IDs */
CONST zclAttrRec_t zclSampleOcc_Attrs[SampleOcc_MAX_ATTRIBUTES] = {
// *** Occupancy Cluster Attributes *** { ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING, { // Attribute record ATTRID_MS_OCCUPANCY_SENSING_CONFIG_OCCUPANCY, ZCL_DATATYPE_BITMAP8, ACCESS_CONTROL_READ, (void *)&zclSampleOcc_Occupied } },
// *** Occupancy Cluster Attributes *** { ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING, { // Attribute record ATTRID_MS_OCCUPANCY_SENSING_CONFIG_OCCUPANCY_SENSOR_TYPE, ZCL_DATATYPE_ENUM8, ACCESS_CONTROL_READ, (void *)&zclSampleOcc_OccType } },
// *** Occupancy Cluster Attribute *** { ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING, { // Attribute record ATTRID_MS_OCCUPANCY_SENSING_CONFIG_PIR_O_TO_U_DELAY, ZCL_DATATYPE_UINT16, (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE), (void *)&zclSampleOcc_PirOccupiedToUnoccupiedDelay } },
// *** Occupancy Cluster Attribute *** { ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING, { // Attribute record ATTRID_MS_OCCUPANCY_SENSING_CONFIG_PIR_U_TO_O_DELAY, ZCL_DATATYPE_UINT16, (ACCESS_CONTROL_READ | ACCESS_CONTROL_WRITE), (void *)&zclSampleOcc_PirUnoccupiedToOccupiedDelay } },
}; </syntaxhighlight>
- Commands Implemented
- ZCL Report Attribute Command
Lighting Device Detail[edit]
The occupancy light device is the successor of Switch/Light sample, so it contains the feature of OnOff. Besides, it also has the ability to handle the report command of occupancy state.
- Cluster Implemented. (In HA Spec, the mandatory cluster of OnOff Light is OnOff, Scene, Group cluster. Occupancy sensing cluster is optional)
- Server: Basic, Scene, Group, OnOff, Level Control
- Client: Occupancy Sensing
<syntaxhighlight lang='c'> //zcl_samplelight_data.c
- define ZCLSAMPLELIGHT_MAX_INCLUSTERS 5
const cId_t zclSampleLight_InClusterList[ZCLSAMPLELIGHT_MAX_INCLUSTERS] = {
ZCL_CLUSTER_ID_GEN_BASIC, ZCL_CLUSTER_ID_GEN_SCENES, ZCL_CLUSTER_ID_GEN_GROUPS, ZCL_CLUSTER_ID_GEN_ON_OFF, ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL
};
- define ZCLSAMPLELIGHT_MAX_OUTCLUSTERS 2
const cId_t zclSampleLight_OutClusterList[ZCLSAMPLELIGHT_MAX_OUTCLUSTERS] = {
ZCL_CLUSTER_ID_GEN_BASIC, ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING
};
SimpleDescriptionFormat_t zclSampleLight_SimpleDesc = {
SAMPLELIGHT_ENDPOINT, // int Endpoint; ZCL_HA_PROFILE_ID, // uint16 AppProfId[2]; ZCL_HA_DEVICEID_DIMMABLE_LIGHT, // uint16 AppDeviceId[2]; SAMPLELIGHT_DEVICE_VERSION, // int AppDevVer:4; SAMPLELIGHT_FLAGS, // int AppFlags:4; ZCLSAMPLELIGHT_MAX_INCLUSTERS, // byte AppNumInClusters; (cId_t *)zclSampleLight_InClusterList, // byte *pAppInClusterList; ZCLSAMPLELIGHT_MAX_OUTCLUSTERS, // byte AppNumInClusters; (cId_t *)zclSampleLight_OutClusterList // byte *pAppInClusterList;
}; </syntaxhighlight>
- Attribute Implemented
- 0x0000: OnOff
<syntaxhighlight lang='c'> // zcl_samplelight_data.c // On/Off Cluster uint8 zclSampleLight_OnOff = LIGHT_OFF; CONST zclAttrRec_t zclSampleLight_Attrs[SAMPLELIGHT_MAX_ATTRIBUTES] = {
// *** On/Off Cluster Attributes *** { ZCL_CLUSTER_ID_GEN_ON_OFF, { // Attribute record ATTRID_ON_OFF, ZCL_DATATYPE_UINT8, ACCESS_CONTROL_READ, (void *)&zclSampleLight_OnOff } },
}; </syntaxhighlight>
- Commands Implemented
- None
Basic Operation[edit]
- Network Start: In this sample, we run the Light Device as the Coordinator and the Occupancy Sensor Device as Router. Of course they also can be configured to other types, it depends on the different use case. So here Light form the network and Occupancy Sensor join it accordingly.
- Service Discovery / End device binding: After the network starts successfully, there are two possible methods for the nodes to set up communication at the application level. One is the Match Descriptor Request/Response mechanism and another is End Device Binding. In this example, we use the former one in which the user must press LEFT (push joystick left on SmartRF05, left button on SmartRF06) on the Router to send a Match Descriptor Request which the Coordinator responds.
<syntaxhighlight lang='c'> // zcl_SampleOcc.c /*********************************************************************
- @fn zclSampleSw_ProcessZDOMsgs()
- @brief Process response messages
- @param none
- @return none
- /
void zclSampleOcc_ProcessZDOMsgs( zdoIncomingMsg_t *inMsg ) {
switch ( inMsg->clusterID ) { case Match_Desc_rsp:
ZDO_ActiveEndpointRsp_t *pRsp = ZDO_ParseEPListRsp( inMsg ); if ( pRsp ) { if ( pRsp->status == ZSuccess && pRsp->cnt ) { zclSampleOcc_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; zclSampleOcc_DstAddr.addr.shortAddr = pRsp->nwkAddr; // Take the first endpoint, Can be changed to search through endpoints zclSampleOcc_DstAddr.endPoint = pRsp->epList[0]; // Light LED HalLedSet( HAL_LED_4, HAL_LED_MODE_ON ); } osal_mem_free( pRsp ); } } break; }
} </syntaxhighlight>
- Occupancy sensor simulating. The hardware platform we used for this example is SmartRF05/SmartRF06 board, which does not have an occupancy sensor in it. So we use a Joystick/Button to simulate it. Push DOWN the joystick / press DOWN button once to make the occupancy state be OCCUPIED and push / press DOWN again to change the status to UNOCCUPIED.
<syntaxhighlight lang='c'> // zcl_SampleOcc.c static void zclSampleOcc_HandleKeys( byte shift, byte keys ) {
if ( keys & HAL_KEY_SW_3 ) { osal_stop_timerEx (zclSampleOcc_TaskID, SampleOcc_STATUS_CHANGE_EVT);
// change the occupancy status zclSampleOcc_Input = ~zclSampleOcc_Input;
- ifdef LCD_SUPPORTED
HalLcdWriteScreen ((zclSampleOcc_Input)? "Occupied" : "Unoccupied", "");
- endif
osal_start_timerEx( zclSampleOcc_TaskID, SampleOcc_STATUS_CHANGE_EVT, (zclSampleOcc_Input)? zclSampleOcc_PirOccupiedToUnoccupiedDelay * 1000 : zclSampleOcc_PirUnoccupiedToOccupiedDelay * 1000); }
} </syntaxhighlight>
- Reporting Attribute. Once the occupancy state changed, it won't report until zclSampleOcc_PirOccupiedToUnoccupiedDelay/zclSampleOcc_PirUnoccupiedToOccupiedDelay elapse.
<syntaxhighlight lang='c'> // zcl_SampleOcc.c
- ifdef ZCL_REPORT
/*********************************************************************
* @fn zclSampleOcc_ProcessStatusChange * * @brief Called to send report command when occupancy status really change. * * @param none * * @return none */
static void zclSampleOcc_ProcessStatusChange( void ) {
zclReportCmd_t reportCmd;
zclSampleOcc_Occupied = zclSampleOcc_Input ? 1 : 0; reportCmd.numAttr = 1; reportCmd.attrList[0].attrID = ATTRID_MS_OCCUPANCY_SENSING_CONFIG_OCCUPANCY; reportCmd.attrList[0].dataType = ZCL_DATATYPE_UINT8; reportCmd.attrList[0].attrData = &zclSampleOcc_Occupied;
zcl_SendReportCmd(SampleOcc_ENDPOINT, &zclSampleOcc_DstAddr, ZCL_CLUSTER_ID_MS_OCCUPANCY_SENSING, &reportCmd, 1,0,1);
}
- endif
</syntaxhighlight>
- Light Operation. When receiving the report command, the light device will set it's OnOff attribute accordingly.
<syntaxhighlight lang='c'> // zcl_samplelight.c
- ifdef ZCL_REPORT
/*********************************************************************
* @fn zclSampleLight_ProcessInReportCmd * * @brief Process the "Profile" Report Command * * @param pInMsg - incoming message to process * * @return none */
static void zclSampleLight_ProcessInReportCmd( zclIncomingMsg_t *pInMsg ) {
zclReportCmd_t *pReportCmd; pReportCmd = (zclReportCmd_t *)pInMsg->attrCmd;
- if defined( LCD_SUPPORTED )
HalLcdWriteScreen( "Occ Report Rcvd", "" );
- endif
if (pReportCmd->attrList[0].attrID == ATTRID_MS_OCCUPANCY_SENSING_CONFIG_OCCUPANCY) { if (*pReportCmd->attrList[0].attrData == OCC_STATE_UNOCCUPIED) { HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF ); zclSampleLight_OnOff = LIGHT_OFF;
- if defined( LCD_SUPPORTED )
HalLcdWriteScreen( "Occ Report Rcvd", "value = 0" );
- endif
} else { HalLedSet ( HAL_LED_4, HAL_LED_MODE_ON ); zclSampleLight_OnOff = LIGHT_ON;
- if defined( LCD_SUPPORTED )
HalLcdWriteScreen( "Occ Report Rcvd", "value = 1" );
- endif
} }
}
- endif /* ZCL_REPORT */
</syntaxhighlight>