智能家居的无线终端设备千差万别,未来也不知道有多少新玩意出现。必须有囊括一切的设备描述能力,否则通用性免谈!好在应阁物联网科技的设备接口设计做到了这点。下面设计一个终端设备(硬件已经实现了): 有1个LED指示灯(可控制),一个温度传感器、一个湿度传感器。每5秒采集一次温湿度数据报告给监控平台。但监控平台可以设置采样的周期(1--60秒)。设计流程与协调其类似。
1、设备初始化代码
void Initdevicestable(void) //1、硬件厂家必须描述子设备数量表:在应用程序初始化后调用该函数
{
deviceinfo = (devicetable_t *)osal_mem_alloc(sizeof(devicetable_t));//动态分配结构体
memset(deviceinfo, 0, sizeof(devicetable_t)); //首先全部清零
//对六类子设备填写数据
setdevicetypeandnumbers(&deviceinfo->subdevices[DO],DO,1); //有1个LED灯开关
setdevicetypeandnumbers(&deviceinfo->subdevices[AI],AI,2); ////有1个温度传感器和1个湿度传感器
setdevicetypeandnumbers(&deviceinfo->subdevices[AO],AO,1); //有1个AO设备:采样周期1--60秒
sprintf(deviceinfo->description,"应阁温湿度传感器");
deviceinfo->PanID=_NIB.nwkPanId;
InitDeviceDescription(); //始化子设备的描述
}
void InitDeviceDescription(void) //2、硬件厂家必须初始化子设备的描述
{
uint8 i;
uint8 devnumbers=getdevicenumbers(deviceinfo->subdevices[DO]); //DO子设备数量
i=sizeof(devicedescription_t);
DODevices = (devicedescription_t *) osal_mem_alloc( devnumbers * i);//动态分配结构体
for (i=0;i
memset((void *)(DODevices+i), 0, sizeof(devicedescription_t)); //首先全部清零
DODevices[i].devicenumber=(DO<<5)+i; //类型+序号
DODevices[i].subtype=OPEN;
strcpy(DODevices[i].unit,""); //计量单位:无
sprintf(DODevices[i].description,"第%d个LED灯",i+1);
sprintf(DODevices[i].operation,"点击DO切换开关");
}
devnumbers=getdevicenumbers(deviceinfo->subdevices[AI]); //AI子设备数量
i=sizeof(devicedescription_t);
AIDevices = (devicedescription_t *) osal_mem_alloc( devnumbers * i);//动态分配结构体
for (i=0;i
memset((void *)(AIDevices+i), 0, sizeof(devicedescription_t)); //首先全部清零
AIDevices[i].devicenumber=(AI<<5)+i; //类型+序号
AIDevices[i].subtype=Unknow;
if (i==0)
{
strcpy(AIDevices[i].unit,"℃"); //温度传感器:计量单位
sprintf(AIDevices[i].description,"温度传感器");
sprintf(AIDevices[i].operation,"");
}
else if (i==1)
{
strcpy(AIDevices[i].unit,"%"); //湿度传感器:计量单位
sprintf(AIDevices[i].description,"湿度传感器");
sprintf(AIDevices[i].operation,"");
}
else
{
strcpy(AIDevices[i].unit,""); //计量单位:无
sprintf(AIDevices[i].description,"未知设备");
sprintf(AIDevices[i].operation,"未知设备");
}
}
devnumbers=getdevicenumbers(deviceinfo->subdevices[AO]); //AO子设备数量
i=sizeof(devicedescription_t);
AODevices = (devicedescription_t *) osal_mem_alloc( devnumbers * i);//动态分配结构体
for (i=0;i
memset((void *)(AODevices+i), 0, sizeof(devicedescription_t)); //首先全部清零
AODevices[i].devicenumber=(AO<<5)+i; //类型+序号
AODevices[i].subtype=Unknow;
if (i==0)
{
strcpy(AODevices[i].unit,"毫秒"); //采样周期
sprintf(AODevices[i].description,"定时采样周期");
sprintf(AODevices[i].operation,"输入毫秒数");
}
else
{
strcpy(AODevices[i].unit,""); //计量单位:无
sprintf(AODevices[i].description,"未知设备");
sprintf(AODevices[i].operation,"未知设备");
}
}
//如果有其他类型的子设备,需要一一填写结构体
//.............
}
2、入网后报告设备信息
static void NotifyCoordinatorDevices(void) //报告设备信息给协调器
{
uint8 i,size;
uint8 cnt;
uint16 addr;
devicedescription_t* devdes;
//交互命令CMD协议定义,应用数据帧FF FX ADDR SIZE DATA
//数据内容标识头=0xFFF0+CMD; 第3,4个字节为设备ID号
//第5个字节SIZE为后面数据DATA的字节数
//#define SENDDATAHead 0xFFF0// 交互数据内容识头
//#define SENDDEVTABLE 0 // 发送设备信息表,E-->C
//#define SENDDEVDESCP 1 // 发送子设备描述记录,E-->C
//#define SENDDEVSTATE 2 // 发送子设备状态数据,E-->C
//#define SENDDEVCTRLL 3 // 控制子设备工作指令,C-->E
//1、发送设备数据表
uint8 table[80];
addr=NLME_GetShortAddr();
table[0]=SENDDATAHead>>8;
table[1]=(SENDDATAHead & 0x00F0)+SENDDEVTABLE;
table[2]=addr&0xFF;
table[3]=addr>>8;
table[4]=sizeof(devicetable_t);
memcpy((void*)(table+5), (void*)deviceinfo, table[4]);
while(true)
{
if(AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
5+table[4], //这个参数就是发送的数据的长度
(uint8 *)table,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) break;
else
{
HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE); //LED指示一下
DelayMS(500); //稍微延时后重发
}
}
//2、发送各个子设备的描述
/*****************DO**************************/
DelayMS(1000); //稍微延时
cnt=getdevicenumbers(deviceinfo->subdevices[DO]); //DO子设备数量
size=sizeof(devicedescription_t); //子设备信息描述结构体大小
for (i=0;i
//HalLedSet(HAL_LED_2, HAL_LED_MODE_BLINK);
//HalLedBlink(HAL_LED_2,3,50,500);
devdes=DODevices+i;
table[1]=(SENDDATAHead & 0x00F0)+SENDDEVDESCP; //发送子设备信息描述
table[4]=size;
memcpy((void*)(table+5), (void*)devdes, size);
while(true)
{
if (AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
5+size,//这个参数就是发送的数据的长度
(void *)table,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) break;
else
{
HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE); //LED指示一下
//DelayMS(500); //稍微验收后重发
HalLedBlink(HAL_LED_2,3,50,500);
}
}
DelayMS(1000); //稍微延时
}
/*****************AI**************************/
cnt=getdevicenumbers(deviceinfo->subdevices[AI]); //AI子设备数量
size=sizeof(devicedescription_t); //子设备信息描述结构体大小
for (i=0;i
//HalLedSet(HAL_LED_2, HAL_LED_MODE_BLINK);
//HalLedBlink(HAL_LED_2,3,50,500);
devdes=AIDevices+i;
table[1]=(SENDDATAHead & 0x00F0)+SENDDEVDESCP; //发送子设备信息描述
table[4]=size;
memcpy((void*)(table+5), (void*)devdes, size);
while(true)
{
if (AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
5+size,//这个参数就是发送的数据的长度
(void *)table,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) break;
else
{
HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE); //LED指示一下
//DelayMS(500); //稍微验收后重发
HalLedBlink(HAL_LED_2,3,50,500);
}
}
DelayMS(500); //稍微延时
}
/*****************AI**************************/
cnt=getdevicenumbers(deviceinfo->subdevices[AO]); //AO子设备数量
size=sizeof(devicedescription_t); //子设备信息描述结构体大小
for (i=0;i
devdes=AODevices+i;
table[1]=(SENDDATAHead & 0x00F0)+SENDDEVDESCP; //发送子设备信息描述
table[4]=size;
memcpy((void*)(table+5), (void*)devdes, size);
while(true)
{
if (AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
5+size,//这个参数就是发送的数据的长度
(void *)table,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ) break;
else
{
HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE); //LED指示一下
//DelayMS(500); //稍微验收后重发
HalLedBlink(HAL_LED_2,3,50,500);
}
}
DelayMS(500); //稍微延时
}
}
3、收到监控平台数据指令的处理
static void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )//接受处理函数
{
uint8 buffer[120];
uint16 addr;
uint8 cmd;
uint8 type;
uint8 number;
uint16 addrself;
addrself=NLME_GetShortAddr();
//需要根据具体硬件设计来修改该函数,有待完善....//
switch ( pkt->clusterId )
{
case GENERICAPP_CLUSTERID:
osal_memcpy(buffer,pkt->cmd.Data,pkt->cmd.DataLength); //获取应用数据
//交互命令CMD协议定义,应用数据帧FF FX ADDR SIZE DATA
//数据内容标识头=0xFFF0+CMD; 第3,4个字节为设备ID号
//第5个字节SIZE为后面数据DATA的字节数
//提取地址,验证地址码?可能是广播
if(pkt->cmd.DataLength<6) return; //协议规定了至少6个字节
if(buffer[0]!=0xFF || (buffer[1]>>4)!=0x0F) return;
addr=buffer[2]+(buffer[3]<<8); //验证地址码?
if (addrself!=addr && addr!=0xFFFF) return; //不是发给自己的,或者广播地址
ProcessTempHumiData(buffer);
break;
}
}
void ProcessTempHumiData(uint8* buffer)
{
uint16 addr;
uint8 cmd;
uint8 type;
uint8 number;
uint8 *data;
//需要根据具体硬件设计来修改该函数,有待完善....//
//交互命令CMD协议定义,应用数据帧FF FX ADDR SIZE DATA
//数据内容标识头=0xFFF0+CMD; 第3,4个字节为设备ID号
//第5个字节SIZE为后面数据DATA的字节数
//提取地址,验证地址码?可能是广播
cmd=buffer[1] & 0x0F;
if (cmd==SENDDEVCTRLL) // 3:控制子设备工作指令,P-->C-->E
{ //高3位:子设备类型,低五位:子设备编号0--15
type=buffer[5]>>5;
number=buffer[5]&0x1F;
if (type==DO) //DO子设备
{
if(number==0)
{
//HalLedSet(HAL_LED_1,HAL_LED_MODE_TOGGLE);
P1_0=buffer[6];
buffer[1]=0xF0+SENDDEVSTATE;// 负责返回实际状态SENDDEVSTATE 2:发送子设备状态数据,E<-->C<-->P
buffer[6]=P1_0; //查看接线图
buffer[4]=2; //两个字节的数据
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
7,
buffer,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS );
}
else if (number==1)
{
//HalLedSet(HAL_LED_2,HAL_LED_MODE_TOGGLE);
P1_1=buffer[6];
buffer[1]=0xF0+SENDDEVSTATE;// 负责返回实际状态SENDDEVSTATE 2:发送子设备状态数据,E<-->C<-->P
buffer[6]=P1_1; //查看接线图
buffer[4]=2; //两个字节的数据
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
7,
buffer,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS );
}
}
if (type==AO) //AO子设备
{
if(number==0) //调节采样周期
{
data= (uint8 *)osal_mem_alloc(buffer[4]);//动态分配数据
memset(data,0,buffer[4]);
memcpy(data,&buffer[6],buffer[4]-1);
addr=atoi(data);
if (addr>=1000 && addr<=60000)
{
My_SEND_MSG_TIMEOUT=addr;
osal_start_timerEx( GenericApp_TaskID,
GENERICAPP_SEND_MSG_EVT,
My_SEND_MSG_TIMEOUT);
buffer[1]=0xF0+SENDDEVSTATE;// 负责返回实际状态SENDDEVSTATE 2:发送子设备状态数据,E<-->C<-->P
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
buffer[4]+5,
buffer,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS );
}
osal_mem_free(data);
}
}
}
else if (cmd==SENDDEVSTATE)// SENDDEVSTATE 2:发送子设备状态数据,E<-->C<-->P
{ //高3位:子设备类型,低五位:子设备编号0--15
type=buffer[5]>>5;
number=buffer[5]&0x1F;
if (type==DO) //获取DO子设备状态
{
if(number==0)
{
buffer[6]=P1_0; //查看接线图
buffer[4]=2; //两个字节的数据
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
7,
buffer,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS );
}
else if (number==1)
{
buffer[6]=P1_1; //查看接线图
buffer[4]=2; //两个字节的数据
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
7,
buffer,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS );
}
else if (number==0x1F) //所有DO子设备状态
{
buffer[6]=P1_0; //查看接线图
buffer[7]=P1_1; //查看接线图
buffer[4]=3; //3个字节的数据
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
8,
buffer,
&GenericApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS );
}
}
else if (type==AI) //获取AI子设备状态
{
if(number==0)
{
//GenericApp_Send_TempSensor_Message();
}
else if (number==1)
{
//GenericApp_Send_LightSensor_Message();
}
else if (number==0x1F) //所有AI子设备状态
{
GenericApp_Send_TempHumitureSensor_Message();
}
}
else if (type==AO) //获取AO 子设备状态:采样周期
{
if(number==0)
{
GenericApp_Send_Sample_Time();
}
else if (number==1)
{
//GenericApp_Send_LightSensor_Message();
}
else if (number==0x1F) //所有A0子设备状态
{
GenericApp_Send_Sample_Time();
}
}
}
}
只要程序设计者,合理规划设备的功能,任何复杂的设备都可以统一监控。是不是很爽?就像上面例子的AO子设备,其实是一项功能,并没有实际的硬件电路支持(定时器除外)。需要详细工程项目案例的代码,请到http://www.ionfox.com.cn/ZigBeeDevice.zip下载查看运行。开发环境:IAR8.10.1
祝你的设备哪里都可以用。