我使用的协议栈版本及例子信息:
ZigBee2006\ZStack-1.4.3-1.2.1个人注释版_5.24\Projects\zstack\Samples\SimpleApp\CC2430DB
1、传感器节点sensor采集数据发往收集节点collector
什么时候传感器节点开始读取温度电池电量信息的?
从绑定成功后开始的,看下zb_BindConfirm()这个函数:
*********************************
void zb_BindConfirm( uint16 commandId, uint8 status )
{
if ( ( status == ZB_SUCCESS ) && ( myAppState == APP_START ) )
{
myAppState = APP_BOUND;
//Start reporting sensor values
myApp_StartReporting();
}
else
{
// Continue to discover a collector
osal_start_timerEx( sapi_TaskID, MY_FIND_COLLECTOR_EVT, myBindRetryDelay );
}
}
*********************************
可以看到如果绑定成功则调用 myApp_StartReporting()开始报告传感器的数据值.
看下myApp_StartReporting()这个函数:
*********************************
//周期性地读取传感器采集的数据
void myApp_StartReporting( void )
{
//温度
osal_start_timerEx( sapi_TaskID, MY_REPORT_TEMP_EVT, myTempReportPeriod );
//电池能量
osal_start_timerEx( sapi_TaskID, MY_REPORT_BATT_EVT, myBatteryCheckPeriod );
HalLedSet( HAL_LED_1, HAL_LED_MODE_ON );
}
*********************************
可以看到这里设定两个软定时器定时触发MY_REPORT_TEMP_EVT事件和MY_REPORT_BATT_EVT事件,即报告温度值事件和报告电池电量事件.注意这些事件属于ZB_USER_EVENTS,用户应用事件ZB_USER_EVENTS事件有以下这些:
// This must be the last event to be processed
/*
// Application osal event identifiers
// Bit mask of events ( from 0x0000 to 0x00FF )
#define MY_START_EVT 0x0001
#define MY_REPORT_TEMP_EVT 0x0002
#define MY_REPORT_BATT_EVT 0x0004
#define MY_FIND_COLLECTOR_EVT 0x0008
*/
//而ZB_USER_EVENTS = 0x00FF;以上应用事件和ZB_USER_EVENTS相与都不为0,因此会执行
SAPI_ProcessEvent()中以下程序:
*********************************
if ( events & ( ZB_USER_EVENTS ) ) /*用户应用事件*/
{
// User events are passed to the application
zb_HandleOsalEvent( events );
// Do not return here, return 0 later
}
*********************************
调用zb_HandleOsalEvent():
*********************************
//SENSOR_REPORT_CMD_ID命令信息带有两个字节的负载:第一个字节指示读取的类型(温度或
//电池电压);第二个字节为传感器指示值(温度或电压指示).
void zb_HandleOsalEvent( uint16 event )
{
uint8 pData[2];
if ( event & MY_START_EVT )
{
zb_StartRequest();
}
if ( event & MY_REPORT_TEMP_EVT )//温度报告
{
// Read and report temperature value
pData[0] = TEMP_REPORT; //0x01(用来指示这是温度数据)
pData[1] = myApp_ReadTemperature();//温度值
//0xFFFE = INVALID_NODE_ADDR = ZB_BINDING_ADDR
zb_SendDataRequest( 0xFFFE, SENSOR_REPORT_CMD_ID, 2, pData, 0, AF_ACK_REQUEST, 0 );
/*因为是周期性地读取温度电池值,则每次事件处理完后要为下一次读取而重新开启一个定时器*/
osal_start_timerEx( sapi_TaskID, MY_REPORT_TEMP_EVT, myTempReportPeriod );
}
if ( event & MY_REPORT_BATT_EVT )//电池电量报告
{
// Read battery value
// If battery level low, report battery value
pData[0] = BATTERY_REPORT; //0x02(用来指示这是电池能量数据)
pData[1] = myApp_ReadBattery();
zb_SendDataRequest( 0xFFFE, SENSOR_REPORT_CMD_ID, 2, pData, 0, AF_ACK_REQUEST, 0 );
osal_start_timerEx( sapi_TaskID, MY_REPORT_BATT_EVT, myBatteryCheckPeriod );
}
if ( event & MY_FIND_COLLECTOR_EVT )
{
// Find and bind to a collector device
zb_BindDevice( TRUE, SENSOR_REPORT_CMD_ID, (uint8 *)NULL );
}
}
*********************************
主要涉及三个函数:myApp_ReadTemperature();myApp_ReadBattery()和zb_SendDataRequest();
myApp_ReadTemperature():读取温度值
myApp_ReadBattery:读取电池电量值
zb_SendDataRequest():把所读取数据发往绑定设备collector.
2、收集节点collector接收到数据进行处理
数据信息是从APS发往AF的,因此涉及afIncomingData(),afIncomingData()调用afBuildMSGIncoming(),
afBuildMSGIncoming()调用osal_msg_send()触发事件,这里具体流程参见以前的记录. SAPI_ProcessEvent()任务事件处理函数对AF_INCOMING_MSG_CMD的处理是调用SAPI_ReceiveDataIndication(),来看下:
*********************************
* @fn SAPI_ReceiveDataIndication
*
* @brief The SAPI_ReceiveDataIndication callback function is called
* asynchronously by the ZigBee stack to notify the application
* when data is received from a peer device.
*
* @param source - The short address of the peer device that sent the data
* command - The commandId associated with the data
* len - The number of bytes in the pData parameter
* pData - The data sent by the peer device
*
* @return none
*/
void SAPI_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData )
{
#if defined ( MT_SAPI_CB_FUNC )
//确认下有没有宏定义MT_SAPI_CB_FUNC
/* First check if MT has subscribed for this callback. If so , pass it as
a event to MonitorTest and return control to calling function after that */
if ( SAPICB_CHECK( SPI_CB_SAPI_RCV_DATA_IND ) )
{
//接收数据指示函数,通知串口,调用回调函数
zb_MTCallbackReceiveDataIndication( source, command, len, pData );
}
else
#endif //MT_SAPI_CB_FUNC
{
//这个函数,对于不同类型的节点它定义了不同的功能,具体参见各类型节点此函数的定义
zb_ReceiveDataIndication( source, command, len, pData );
}
}
*********************************
可以看到如果定义了MT_SAPI_CB_FUNC就调用zb_MTCallbackReceiveDataIndication();然后再调用zb_ReceiveDataIndication().
这俩函数有什么区别?
zb_MTCallbackReceiveDataIndication:Process the callback subscription for zb_ReceiveDataIndication
zb_ReceiveDataIndication: The zb_ReceiveDataIndication callback function is called asynchronously by the ZigBee stack to notify the application when data is received from a peer device.
这里我个人认为,zb_MTCallbackReceiveDataIndication就是接收到数据通知串口,然后调用相应回调函数进行处理;zb_ReceiveDataIndication就是接收到数据通知应用,然后调用相应处理函数进行处理;貌似在这个收集节点中这俩函数功能有点重复,具体最后都是把接收到的数据发串口送PC显示;不过这里没有用到zb_MTCallbackReceiveDataIndication(),因为project->options->c/c++compiler->preprocessor下的Defined symbols中可以看到编译选项是:xMT_SAPI_CB_FUNC.因此直接执行 zb_ReceiveDataIndication().
看下这个函数:
*********************************
CONST uint8 strDevice[ ] = "Device:0x";
CONST uint8 strTemp[ ] = "Temp: ";
CONST uint8 strBattery[ ] = "Battery: ";
void zb_ReceiveDataIndication( uint16 source, uint16 command, uint16 len, uint8 *pData )
{
uint8 buf[32];
uint8 *pBuf;
uint8 tmpLen;
uint8 sensorReading;
if (command == SENSOR_REPORT_CMD_ID)
{
// Received report from a sensor 读取数据值
sensorReading = pData[1];
// If tool available, write to serial port 写到串口
tmpLen = (uint8)osal_strlen( (char*)strDevice );
//把字符串赋给buf[],返回的是指向目的内存起始地址的指针,因此这里
//pBuf是指向buf[]的指针。
pBuf = osal_memcpy( buf, strDevice, tmpLen);
_ltoa( source, pBuf, 16 ); //把无符号长整形转成字符串
pBuf += 4;
*pBuf++ = ' ';
if ( pData[0] == BATTERY_REPORT )//电池电压
{
tmpLen = (uint8)osal_strlen( (char*)strBattery );
pBuf = osal_memcpy( pBuf, strBattery, tmpLen );
*pBuf++ = (sensorReading / 10 ) + '0'; // convent msb to ascii转换MSB为ASCII
*pBuf++ = '.'; // decimal point 小数点( battery reading is in
units of 0.1 V
*pBuf++ = (sensorReading % 10 ) + '0'; // convert lsb to ascii转换LSB为ASCII
*pBuf++ = ' ';
*pBuf++ = 'V';
}
else //温度
{
tmpLen = (uint8)osal_strlen( (char*)strTemp );
pBuf = osal_memcpy( pBuf, strTemp, tmpLen );
*pBuf++ = (sensorReading / 10 ) + '0'; // convent msb to ascii 转换MSB为ASCII
*pBuf++ = (sensorReading % 10 ) + '0'; // convert lsb to ascii 转换LSB为ASCII
*pBuf++ = ' ';
*pBuf++ = 'C';
}
*pBuf++ = '\r';
*pBuf++ = '\n';
*pBuf = '\0';
#if defined( MT_TASK )
debug_str( (uint8 *)buf ); //通过这里来触发MT层任务事件,再传送数据
#endif
// can also write directly to uart
//为了方便,这里也可以调用串口驱动函数,直接把接收到的数据写到串口,HalUARTWrite()!
}
}
*********************************
当然,这里MT_TASK是编译过的,因此最后调用debug_str()这个函数.
*********************************
void debug_str( byte *str_ptr )
{
…………
if ( msg )
{
// Message type, length
msg->hdr.event = CMD_DEBUG_STR; //CMD_DEBUG_STR
msg->sln = sln;
…………
osal_msg_send( MT_TaskID, (uint8 *)msg );
}
} // debug_str()
*********************************
可以看到最后触发任务MT_TaskID的CMD_DEBUG_STR事件,调用MT_ProcessEvent()处理,MT_ProcessEvent()处理SYS_EVENT_MSG事件是调用了MT_ProcessCommand()函数,MT_ProcessCommand()函数中对事件CMD_DEBUG_STR的处理是调用MT_ProcessDebugStr()函数,MT_ProcessDebugStr()最终调用HalUARTWrite()把数据写到串口送PC上.流程结束.
再回到刚才的zb_MTCallbackReceiveDataIndication(),其调用MT_BuildAndSendZToolCB(),
*********************************
void MT_BuildAndSendZToolCB( uint16 callbackID, byte len, byte *pData )
{
…………
if ( msgPtr )
{
msgPtr->hdr.event = CB_FUNC; //CB_FUNC
…………
osal_msg_send( MT_TaskID, (uint8 *)msgPtr );
}
}
*********************************
可以看到最终会触发MT_TaskID任务的CB_FUNC事件.调用MT_ProcessEvent()处理,MT_ProcessEvent()处理SYS_EVENT_MSG事件是调用了MT_ProcessCommand()函数,MT_ProcessCommand()函数中对事件CB_FUNC的处理如下:
*********************************
void MT_ProcessCommand( mtOSALSerialData_t *msg )
{
…………
case CB_FUNC:
/*
Build SPI message here instead of redundantly(多余地) calling MT_BuildSPIMsg
because we have copied data already in the allocated message
*/
/* msg_ptr is the beginning of the intended SPI message */
len = SPI_0DATA_MSG_LEN + msg_ptr[DATALEN_FIELD];
/*
FCS goes to the last byte in the message and is calculated over all
the bytes except FCS and SOP
*/
msg_ptr[len-1] = SPIMgr_CalcFCS( msg_ptr + 1 , (byte)(len-2) );
#ifdef SPI_MGR_DEFAULT_PORT
HalUARTWrite ( SPI_MGR_DEFAULT_PORT, msg_ptr, len );
#endif
break;
}
*********************************
可以看到最终也是通过HalUARTWrite()把数据写串口发往PC.
这两个……暂时认为是这样的吧!
*********************************
注:sensor节点不需要按键就可以以终端启动,因为在对ZB_ENTRY_EVENT的处理中:
***********************************
if ( events & ZB_ENTRY_EVENT )
{
uint8 startOptions;
// Give indication to application of device startup
zb_HandleOsalEvent( ZB_ENTRY_EVENT );
// LED off cancels HOLD_AUTO_START blink set in the stack
HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
if ( startOptions & ZCD_STARTOPT_AUTO_START )
{
zb_StartRequest();
}
else
{
// blink leds and wait for external input to config and restart
HalLedBlink(HAL_LED_2, 10, 50, 500);//闪烁10次,使现象明显些
#if defined SENSOR
UINT8 logicalType;
logicalType = ZG_DEVICETYPE_ENDDEVICE;
zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType);
// Do more configuration if necessary and then restart device with auto-start bit set
zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
startOptions = ZCD_STARTOPT_AUTO_START;
zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions );
zb_SystemReset();
#endif
}
return (events ^ ZB_ENTRY_EVENT );
}
***********************************
因为sensor节点预编译了SENSOR,因此这里会直接把ZG_DEVICETYPE_ENDDEVICE和ZCD_STARTOPT_AUTO_START写入NV然后以终端形式自动启动。而其它类型的节点则闪烁10次以后表示需要通过外界控制来启动。
*********************************
(1) 对于外接集成传感器器件,相关引脚与CC2430相连,根据其配置时序及命令读取数据,那这里就应该是类似于myApp_ReadTemperature()和myApp_ReadBattery()这两个函数了,再根据上面流程发送与接收.
(2)对于水环境那个OO,变送器+MSP430,采集一堆数据后通过串口发送给CC2430终端节点,CC2430终端节点接收到数据后再发送到基站.上面的实验是软定时周期性触发终端节点采集数据并发送,个人觉得这里可以是串口接收到数据触发终端节点发送接收到的数据事件,如果是要根据上位机的命令来读取,是否可以把430采集的数据通过串口存在2430里,上位机命令没有发到2430读取采集数据则覆盖,命令发来则读取数据发送?或者把上位机通过网络发送到2430的命令,再通过串口发送到430,什么时候采集,采集何种数据,这些由430来完成,采集好后再送2430发送?暂时两种思路,暑假再来看…!