Day3 : zigbee无线传感器网络管理之星型网络

Day3 : zigbee无线传感器网络管理之星型网络

  • 基于广播和单播的星状网络
    • 网络拓扑结构
    • 功能要求
    • 实现过程
  • 组播通信
    • 功能要求
    • 实现过程
  • 串口无线透传实验
    • 基于 Z-Stack 的串口通信
      • 功能要求
      • 实现过程
    • 基于 Z-Stack 的串口透传
      • 功能要求
      • 实现过程

基于广播和单播的星状网络

网络拓扑结构

Zigbee 网络支持星状、树(簇)状,网状三种网络拓扑结构;

  • 星型拓扑
    星状拓扑是最简单的一种拓扑形式,包含一个协调器(Coordinator)和一系列的终端节点(End Device)。每一个终端节点只能和协调器通信。如果需要在两个节点之间进行通信则必须通过协调器进行消息的转发。
    • 缺点
      节点之间的数据路由只有唯一的一个路径。协调器有可能成为真个网络的瓶颈。实现星型网络拓扑不需要使用Zigbee的网络层协议,因为本身 IEEE 802.15.4 的协议层就已经实现了星型拓扑形式,但是这需要开发者在应用层做更多的工作,包括自己处理消息的转发。
  • 树(猝)型拓扑
    树型拓扑包含一个协调器以及一系列的路由器和终端节点。协调器连接一系列的路由器和终端节点,其子节点的路由器也可以接一些列的路由器和终端节点,这样可以重复多层级。
    • 注意
      1. 协调器和路由器可以包含自己的子节点。
      2. 终端节点不能有自己的子节点。
      3. 有同一个父节点的节点称为兄弟节点
      4. 有同一个祖父节点的节点称为堂兄弟节点
    • 通信规则
      1. 每一个节点都只能与其父节点和子节点进行通信。
      2. 如果需要从一个节点向另一个节点发送数据,那么信息将沿着树的路径向上传递到最近的祖先节点,然后再向下传递到目标节点。
    • 缺点
      信息只有唯一的路由通路。另外,信息的路由是由协议栈层处理的,整个路由过程对于应用层是完全透明的
  • 网状拓扑(Mesh 拓扑)
    网状拓扑包括一个协调器和一系列的路由器和终端节点。这种网络拓扑形式和树型拓扑相同。但是,网状拓扑具有更加灵活的信息路由规则,在可能的情况下,路由节点之间可以直接通信。这种路由机制使得信息的通信变得更有效率,而且意味着一旦一个路由路径出现了问题,信息可以自动地沿着其他的路由路径进行传输。
    • 通常在支持网状网络的实现上,网络层提供相应的路由探索功能,这一特性使得网络层可以找到信息传输的最优化路径。需要注意的是,以上所提到的特性都由网络层来实现,应用层不需要进行任何参与。
    • 网状拓扑具有强大的功能,可以通过“多级跳”的方式来通信。该拓扑结构还可以组成极为复杂的网络,这种网络具备自组织和自愈功能。

星型和树型网络适合点对点、距离相对较近的应用。
Day3 : zigbee无线传感器网络管理之星型网络_第1张图片

功能要求

  • 协调器周期性以广播的形式向终端节点发送数据“我是协调器!\r\n”
  • 加入其网路的终端节点都会收到数据,终端节点分别单播给协调器“我是终端1或2!\r\n”

实现过程

  1. 复制SampleApp,改名为 基于广播和单播的星状网络
  2. 设置 Zigbee 网络的拓扑结构为星状
    在 Zigbee 协议栈的 NWK 目录中的 nwk_globals.h 文件中,找到 NWK_MODE 的设置模式
    Day3 : zigbee无线传感器网络管理之星型网络_第2张图片
    将 NWK_MODE_MESH 改成 NWK_MODE_STAR(NWK_MODE_MESH 为网状网、NWK_MODE_STAR 为星状网、NWK_MODE_TREE 为树状网,这里设置最简单、最稳定的星状网)。
  3. 设置信道 Chanel
    Zigbee 无线传感网络中,文件 f8wConfg.cfg 中 -DDEFAULT_CHANLIST 参数设置为 0X0B(默认值)~ 0X1A,共16个信道。-DDEFAULT_CHANLIST 选择的信道将作为唯一的信道通信。
    Day3 : zigbee无线传感器网络管理之星型网络_第3张图片
  4. 设置 PANID
    PANID 是指网络编号,用于区分不同的 Zigbee网络。
    文件 f8wConfg.cfg 中的 ZDO_CONFIG_PAN_ID 参数设置为 0XFFFF,协调器将根据自身的 IEEE 地址建立一个随机的 PAN ID 分配,节点的PAN ID 为 0x0001 ~ 0x3FFF。
    如果协调器的 ZDO_CONFIG_PAN_ID 参数设置不为 0xFFFF ,那么网络的 PAN ID 将由 ZDAPP_CONFIG_PAN_ID 确定。
    如果路由器或终端节点的 ZDO_CONFIG_PANID 的参数设置为 0xFFFF 那么他会随机加入一个网络。
    Day3 : zigbee无线传感器网络管理之星型网络_第4张图片
  5. 配置串口波特率为 115200、关闭流控制。
    Day3 : zigbee无线传感器网络管理之星型网络_第5张图片
  6. 设置协调器广播消息地址类型 为 广播模式(AddrBroadcast)
    在这里插入图片描述
  7. 设置广播消息、周期(5s)
    Day3 : zigbee无线传感器网络管理之星型网络_第6张图片
  8. 设置当无线消息到达时打印到串口
    Day3 : zigbee无线传感器网络管理之星型网络_第7张图片
  9. 配置工程并烧写到协调器
  10. 设置终端地址类型为 单点传送(Addr16Bit) 给 协调器(0x0000)
    在这里插入图片描述
  11. 配置工程并烧写到终端设备
  12. 试验现象
    协调器收到的数据
    Day3 : zigbee无线传感器网络管理之星型网络_第8张图片
    终端收到的数据
    Day3 : zigbee无线传感器网络管理之星型网络_第9张图片

组播通信

功能要求

  • 协调器创建网络,并加入一个组,并向组内成员组播数据 “我是协调器!\r\n”。
  • 终端1 加入网络,并加入与协调器相同的组,收到协调器发送而来的数据。
  • 终端2 加入网络,并加入另外一个组,不能收到协调器发来的数据。

实现过程

  1. 复制 SampleApp ,重命名为 组播通信
  2. 配置串口(波特率115200,关闭流控制)。
  3. 协调器默认在 Group - 1 中启动。
    在这里插入图片描述
  4. 修改协调器,周期事件地址类型为组播。
    地址模式设置为 afAddrGroup 并且 shortAddr 设置为组 ID。
    Day3 : zigbee无线传感器网络管理之星型网络_第10张图片
  5. 协调器周期发送无线数据,其他类型节点关闭定时器。
    Day3 : zigbee无线传感器网络管理之星型网络_第11张图片
    Day3 : zigbee无线传感器网络管理之星型网络_第12张图片
  6. 配置工程并烧写
  7. 默认Group 1 烧写到终端 1
  8. 修改组号为 Group 2 烧写到终端 2
    在这里插入图片描述
  • 试验现象
    终端1可以收到协调器发来的数据,而终端2却收不到。
    Day3 : zigbee无线传感器网络管理之星型网络_第13张图片
    刚才又做了路由器扩展网络覆盖范围的实验。
    嗯,路由器确实可以扩展网络范围。原来楼道收不到协调器信号,在厨房加了一个路由节点之后,再楼道就可以收到协调器信号了。2020年5月20日 22:28:46

串口无线透传实验

基于 Z-Stack 的串口通信

功能要求

  • Zigbee 节点每隔5s向串口发送“Hello world!\r\n”,并在PC上的串口调试软件上实时显示相应信息。
  • 另外,增加一个应用层新任务(没必要) —— 由PC端发送字符串 “1” 和 “2”,进而控制Zigbee模块中的LED2灯的开与关。

实现过程

  1. 拷贝 SampleApp,重命名为 基于Z-Stack 的串口通信。
  2. 配置串口(波特率,关闭流控制),注册任务
    Day3 : zigbee无线传感器网络管理之星型网络_第14张图片
    注意:只有任务注册之后,串口消息才会转发到!
  3. 设置每隔5s向串口发送“Hello world!\r\n”
  4. 分析回调函数 MT_UartProcessZToolData
第1个字节 第2个字节 第3个字节 第4个字节 第5~Len+4字节 第 n+1 字节
数据枕头SOP 数据长度(Len) 命令低字节(CMD) 命令高字节(CMD) 数据(Len字节) 校验码
0XFE 按位XOR
void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
  uint8  ch;
  uint8  bytesInRxBuffer;
  
  (void)event;  // 有意未引用的参数

  while (Hal_UART_RxBufLen(port))				// 计算Rx缓冲区长度-缓冲区中的字节数。
  {
    HalUARTRead (port, &ch, 1);					// 读取一个数据

    switch (state)								// 串口接收状态机
    {
      case SOP_STATE:							// SOP_STATE - 数据针头 0x00
        if (ch == MT_UART_SOF)					// 收到第一个数据,MT_UART_SOF 为 0xFE
          state = LEN_STATE;					// 确认后跳转状态 LEN_STATE - 数据长度
        break;

      case LEN_STATE:							// LEN_STATE - 数据长度 0x03
        LEN_Token = ch;							// 接收数据长度

        tempDataLen = 0;						// 临时数据长度

        /* 为数据分配内存空间 */
        pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
                                                        MT_RPC_FRAME_HDR_SZ + LEN_Token );

        if (pMsg)								// 如果分配成功
        {
          /* 尽我们所能 */
          pMsg->hdr.event = CMD_SERIAL_MSG;		// 注册事件号 CMD_SERA_MSG
          pMsg->msg = (uint8*)(pMsg+1);			// 把数据定位到结构体数据部分
          pMsg->msg[MT_RPC_POS_LEN] = LEN_Token;// 存数据长度第一位数据
          state = CMD_STATE1;					// 跳转状态 命令低字节
        }
        else
        {
          state = SOP_STATE;
          return;
        }
        break;

      case CMD_STATE1:							// 接收第三个数据
        pMsg->msg[MT_RPC_POS_CMD0] = ch;		// 
        state = CMD_STATE2;						// 命令高字节
        break;

      case CMD_STATE2:
        pMsg->msg[MT_RPC_POS_CMD1] = ch;		// 命令高字节
		
        /* 如果没有数据,跳到FCS状态 */
        if (LEN_Token)
        {
          state = DATA_STATE;
        }
        else
        {
          state = FCS_STATE;
        }
        break;

      case DATA_STATE:

        /* 在缓冲区中填充数据的第一个字节 */
        pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen++] = ch;

        /* 检查Rx缓冲区中剩余的字节数 */
        bytesInRxBuffer = Hal_UART_RxBufLen(port);

        /* 如果剩下的数据在那里,把它们全部读出来,否则,只要读够就行了 */
        if (bytesInRxBuffer <= LEN_Token - tempDataLen)
        {
          HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], bytesInRxBuffer);
          tempDataLen += bytesInRxBuffer;
        }
        else
        {
          HalUARTRead (port, &pMsg->msg[MT_RPC_FRAME_HDR_SZ + tempDataLen], LEN_Token - tempDataLen);
          tempDataLen += (LEN_Token - tempDataLen);
        }

        /* 如果读取的字节数等于数据长度,则跳转到FCS的状态 */
        if ( tempDataLen == LEN_Token )
            state = FCS_STATE;

        break;

      case FCS_STATE:

        FSC_Token = ch;

        /* 确保它是正确的 */
        if ((MT_UartCalcFCS ((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ + LEN_Token) == FSC_Token))
        {
          osal_msg_send( App_TaskID, (byte *)pMsg );
        }
        else
        {
          /* 释放消息内存 */
          osal_msg_deallocate ( (uint8 *)pMsg );
        }

        /* 重置状态,此时发送或放弃缓冲区 */
        state = SOP_STATE;

        break;

      default:
       break;
    }
  }
}

MT_UartCalcFCS - 通过对每个字节进行异或运算来计算消息缓冲区的FCS。记住不要包含SOP和FCS字段,从CMD字段开始。

  1. 接收串口数据 并 打印
    MT.h 下 CMD_SERIAL_MSG 消息。
    将 (afIncomingMSGPacket_t *) 强制转换为 (mtOSALSerialData_t *)。
    同理,按键消息 (afIncomingMSGPacket_t * )被强制转换为 (keyChange_t *)
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
void SampleApp_SerialMSG( mtOSALSerialData_t *SeMsg );

MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
while ( MSGpkt )
{
	switch ( MSGpkt->hdr.event )
	{
		/* 收到串口数据 */
		case CMD_SERIAL_MSG:
		SampleApp_SerialMSG( (mtOSALSerialData_t *)MSGpkt );
		break;
		...
	}
	...
}
/*********************************************************************
* @fn      SampleApp_SerialMSG
*
* @brief   处理串口数据
*
* @param   mtOSALSerialData_t *SeMsg - 串口数据
*
* @return  none
*/
void SampleApp_SerialMSG( mtOSALSerialData_t *SeMsg )
{
	# HalUARTWrite ( 0, "有消息!\r\n", osal_strlen( "有消息!\r\n" ) );
	HalUARTWrite ( 0, SeMsg->msg + 1, *(SeMsg->msg) + 2 );	// 加上2个命令字节
	HalUARTWrite ( 0, "\r\n", 3 );
}

Day3 : zigbee无线传感器网络管理之星型网络_第15张图片
5. 控制 LED

void SampleApp_SerialMSG( mtOSALSerialData_t *SeMsg )
{
	/* 接收到串口消息 LED2 闪烁 */
	HalLedBlink (HAL_LED_2, 3, HAL_LED_DEFAULT_DUTY_CYCLE, HAL_LED_DEFAULT_FLASH_TIME);
	
	HalUARTWrite ( 0, "接收串口消息!\t", osal_strlen( "接收串口消息!\t" ) );
	HalUARTWrite ( 0, SeMsg->msg + 1, *(SeMsg->msg) + 2 );	// 加上2个命令字节
	HalUARTWrite ( 0, "\r\n", osal_strlen("\r\n") );
	
	/* MT传输协议中的CMD */
	uint16 cmd;
	cmd  = *(SeMsg->msg + 1);
	cmd |= *(SeMsg->msg + 2) << 8;
	switch ( *(SeMsg->msg + 1) )
	{
		/* TOGGLE */
		case 0x0030:
		HalLedSet( HAL_LED_1,HAL_LED_MODE_TOGGLE );
		break;
		
		/* 开灯 */
		case 0x0031:
		HalLedSet( HAL_LED_1,HAL_LED_MODE_ON );
		break;
		
		/* 关灯 */
		case 0x0032:
		HalLedSet( HAL_LED_1,HAL_LED_MODE_OFF );
		break;		

		default:
		
		break;
	}
}

我修改了协议栈的 LED4
在 hal_board_cfg.h,总添加代码:

/* 4 - NewLand */
#define LED4_BV           BV(3)
#define LED4_SBIT         P1_3
#define LED4_DDR          P1DIR
#define LED4_POLARITY     ACTIVE_HIGH
  HAL_TURN_OFF_LED4();                                           \
  LED4_DDR |= LED4_BV;											 \
#define HAL_LED_DEFAULT_MAX_LEDS      4

LED4 宏定义与其他 LED 的一致即可。

-试验现象
发送字符串控制 LED 灯。
Day3 : zigbee无线传感器网络管理之星型网络_第16张图片
Day3 : zigbee无线传感器网络管理之星型网络_第17张图片

基于 Z-Stack 的串口透传

功能要求

晚安,情人节快乐……

  • 实现无线透传

实现过程

修改串口回调函数

void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
  uint8 ch, len = 0;
  uint8 uartData[128];//定义一个数组暂存数据
  uint8 i;
  (void)event;//Intentionally unreferenced parameter
  while(Hal_UART_RxBufLen(port))//只要检测到串口缓存区有数据
  {
    HalUARTRead (port,&ch,1);//就把数据一个一个的传给ch变量
    uartData[len+1] = ch;//再把ch的数据传给数组
    len ++;
  }
  if(len)
  {
    uartData[0] = len;//第一个元素呢设为数据长度
    pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof(mtOSALSerialData_t ) + len + 1 );//为串口数据包分配内存
    if (pMsg)
    {
      /* Fill up what we can */
      pMsg->hdr.event = CMD_SERIAL_MSG;//设置串口数据到达事件
      pMsg->msg = (uint8*)(pMsg+1);//把数据定位到结构体数据部分
      for(i=0; i<=len; i++)
      {
        pMsg->msg[i] = uartData[i];//再把暂存区数据放入串口数据包中
      }
      osal_msg_send( App_TaskID, (byte *)pMsg );//将数据包发往消息队列
    }
    osal_msg_deallocate ((uint8 *)pMsg );//分起的内存用完后记得开除,以免内存溢出。
  }
}

你可能感兴趣的:(ZIGBEE技术)