- 协议栈串口在启动以后会打印一堆数据,是由于MT封装的
- 取消掉对LCD的支持(宏定义)不会打印出Energy以及Network ID等信息
- 在MT_TASK.c文件中可以注释掉
//HalUARTWrite(MT_UART_DEFAULT_PORT, msgPtr, dataLen + SPI_0DATA_MSG_LEN);
可以避免打印FE开头的数据
也可以在MT_TransportSend函数中将上述语句替换为自己的开机提示信息方便上位机判断zigbee模块是否重启
HalUARTWrite(MT_UART_DEFAULT_PORT, "Reset\r\n", 7);
- 在数据包头部加入了每个数据包有效数据的长度,后期会用到,暂时不用管头部的八个字节。只把有效数据发送给上位机的串口。
- QT的串口由于会出现粘包现象,也就是连续两次来到串口的数据有可能回一次性读取,或者一次来的数据会分成几次读取,利用
if(serial.bytesAvailable() >=5 )
检测缓冲区,如果有效可读数据大于等于5的话再去读取串口!!!
还有就是表示发送开始的信息发送出去以后,延时一会儿,这样QT那边就能将整个开始信息完整读取,方便新建文件,同样,在发送结束信息之前也要延时!!!
- 在使用QFile新建文件的时候,文件名不能包含冒号等特殊字符,否则会不能新建文件。
- 多利用Linux的grep命令在开源项目查找代码
- 需要解决的问题:
(1)如果协调器断电重启、终端重启
(2)QT串口如何处理包头信息已达到区分不同类型的节点数据
(3)美化上位机:添加设置选项,目前是写死了波特率等选项!还有进度条,所用时间等都需要设置!
串口读取的核心代码:
void MainWindow::serialRead()
{
static unsigned int length = 0;
if(serial.bytesAvailable() >=5 )
{
//读串口数据
QByteArray serial_data = serial.readAll();
length += serial_data.length();
char *str_tmp=serial_data.data();
//判断图片是否传送完毕--接收到的是否是end
if(strcmp("end\r\n",str_tmp) == 0)
{
dst.close();
qDebug()<<"end";
qDebug()<<length;
return ;
}
//判断是不是一张新的图片
//如果是beigin开头,新建文件
if(strcmp("begin\r\n",str_tmp) == 0)
{
QDateTime time = QDateTime::currentDateTime();//获取系统现在的时间
//设置显示格式,注意QFile的文件名不能有:(冒号)等特殊字符
QString str = time.toString("yyyy-MM-dd-hh-mm-ss");
str += ".jpg";
qDebug()<<str;
dst.setFileName(str);//将时间作为文件名
//QDir::currentPath()
bool isOK = dst.open(QIODevice::WriteOnly|QIODevice::Append);
if(isOK == false)
{
qDebug()<<"dst.open err";
this->close();
}
qDebug()<<length;
return ;
}
//如果不是begin开头,将数据插入原来打开文件的末尾
dst.write(serial_data);
//将数据格式化
QString str_display;
QString str = serial_data.toHex().data();
str = str.toUpper();
str_display.clear();
for(int i = 0;i < str.length();i+= 2)
{
QString st = str.mid(i,2);
str_display += st;
str_display += " ";
}
//显示数据
ui->textEdit->insertPlainText(str_display);
qDebug()<<length;
}
}
协调器核心代码
void SimonApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
uint8 id = 255;
if ( MY_ENDPOINT01 == pkt->endPoint )//7号端点
{
switch ( pkt->clusterId )
{
case 0x0001://1号员工--负责处理终端节点按键3按下发送过来的数据
//Uart_Send_String(pkt->cmd.Data,pkt->cmd.DataLength);
LS164_BYTE(pkt->cmd.Data[0]);
MYLED1 ^= 1;//LED翻转
break;
case 0x0002://2号员工--负责处理终端节点按键4按下发送过来的数据
//Uart_Send_String(pkt->cmd.Data,pkt->cmd.DataLength);
LS164_BYTE(pkt->cmd.Data[0]);
MYLED2 ^= 1;
break;
}
}
if ( MY_ENDPOINT02 == pkt->endPoint )//6号端点/房间
{
switch ( pkt->clusterId )
{
case CLUSTER_PHOTO_NODE://1号员工
//Uart_Send_String(pkt->cmd.Data,pkt->cmd.DataLength);
//LS164_BYTE(pkt->cmd.Data[0]);
//HalUARTWrite(MY_PORT_NUM,"Simon\r\n",7);
if(TYPE_PHOTO == pkt->cmd.Data[0])//如果是图像节点发送来的数据
{
MYLED3 ^= 1;//LED3翻转
id = pkt->cmd.Data[1];//获取节点在同类节点中的序号
gPhotoID = id;//缓存序号
//HalUARTWrite(MY_PORT_NUM,pkt->cmd.Data,pkt->cmd.DataLength);
//为了避免终端节点突然死机重新入网引起的数据中断错误需要判断新的网络地址和之前保存的是否一致
//此时在协调器PothoNode_AddrArr[id].exist非零,但终端节点的网络短地址可能发生改变
//如果不更新网络地址,则协调器反馈的信息将不能达到终端节点,从而引发错误
if((0 == PothoNode_AddrArr[id].exist) || (PothoNode_AddrArr[id].shortAddr != *((uint16 *)&(pkt->cmd.Data[4]))))
{
//((uint8 * )&(PothoNode_AddrArr[id].shortAddr))[0] = pkt->cmd.Data[5];//pkt->cmd.Data[4]<<8+ pkt->cmd.Data[5]);
//((uint8 * )&(PothoNode_AddrArr[id].shortAddr))[1] = pkt->cmd.Data[4];
/*每次数据到来都讲数据包里携带的网络短地址和端点缓存下来*/
PothoNode_AddrArr[id].shortAddr = *((uint16 *)&(pkt->cmd.Data[4]));//(pkt->cmd.Data[5]*256+ pkt->cmd.Data[4]);
PothoNode_AddrArr[id].endPoint = pkt->cmd.Data[6];
//表示已经缓存过该节点--现处于调试阶段--后期修改
//按理来说,这里的代码应该只在节点第一次发送数据给协调器的时候调用
PothoNode_AddrArr[id].exist = 1;
MYLED2 ^= 1;//协调器第一次收到某个节点发送的数据--翻转LED2
}
#if 0
//HalUARTWrite(MY_PORT_NUM,(uint8 *)PothoNode_AddrArr,PHOTO_ID_MAX*sizeof(AddrInfo_t));
//if((PothoNode_AddrArr[id].seqnb + 1) == (pkt->cmd.Data[2]<<8+ pkt->cmd.Data[3]))
//if( (((uint8 * )&(PothoNode_AddrArr[id].seqnb))[0] +1) == pkt->cmd.Data[3]
// && ((uint8 * )&(PothoNode_AddrArr[id].seqnb))[1] == pkt->cmd.Data[2] )
//if((PothoNode_AddrArr[id].seqnb + 1) == (pkt->cmd.Data[3]*256+ pkt->cmd.Data[2]))
if((PothoNode_AddrArr[id].seqnb + 1) == (*((uint16 *)&(pkt->cmd.Data[2]))))
{
HalUARTWrite(MY_PORT_NUM,pkt->cmd.Data,pkt->cmd.DataLength);
osal_set_event( SimonApp_TaskID, SimonApp_RESPONSE_PHOTO_EVT );
PothoNode_AddrArr[id].seqnb++;
}
#endif
//每次有数据到来,都直接将数据发送给上位机
HalUARTWrite(MY_PORT_NUM,pkt->cmd.Data+8,pkt->cmd.DataLength-8);
//每次有数据到来,都给节点反馈信息
osal_set_event( SimonApp_TaskID, SimonApp_RESPONSE_PHOTO_EVT );
//ResponsePhotoNode();
}
break;
}
}
}
终端模块核心代码
/*added by Simon*/
/*当串口接收到上位机的数据以后,打包-->发送给协调器*/
static void rxCB(uint8 port,uint8 event)
{
static uint8 tmp_len = 0;
//osal_memset(gUartBuf,0,BUF_MAX);
//在协议栈的回调函数里必须加上该条件判断才能正常使用回调功能
//回调函数默认是利用DMA的轮询方式工作
if ((event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT)) &&!tmp_len)
{
if(!tmp_len)//tmp_len必须是0
{
osal_memset(gUartBuf,0,BUF_MAX);//清空缓冲区
tmp_len=HalUARTRead(MY_PORT_NUM,gUartBuf+7, BUF_MAX);//读取串口内容,此时的长度tmp_len为有效数据长度
/*开头的八个字节是节点的相关信息*/
gUartBuf[0] = TYPE_PHOTO;//节点类型
gUartBuf[1] = MY_ID;//同类节点中的节点序号
/*2530是低地址对应低字节*/
/*在网络数据包里尽量吧低字节放在低地址(数据包的前面)*/
/*该数据包在已发送序列中的序号*/
mySeq++;
//HalUARTWrite(MY_PORT_NUM,(uint8 *)&mySeq,2);
gUartBuf[3] = (mySeq>>8);
gUartBuf[2] = (mySeq%256);
/*该节点的网络短地址*/
myAddr = NLME_GetShortAddr();
gUartBuf[5] = (myAddr>>8);
gUartBuf[4] = (myAddr%256);
/*该节点用哪个端点给协调器发送数据*/
gUartBuf[6] = MY_ENDPOINT;
//gUartBuf[7] = 0;//保留未使用-->填充为0
//HalUARTWrite(MY_PORT_NUM,gUartBuf,tmp_len+3);
}
if(tmp_len>2 )//该参数有待修改
{
// HalUARTWrite(MY_PORT_NUM,(uint8*)&tmp_len,1);
//HalUARTWrite(MY_PORT_NUM,gUartBuf,tmp_len+3);
tmp_len += 7;//长度修正为实际数据包大小
/*填充目标地址*/
SimonApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;//单播模式
SimonApp_DstAddr.addr.shortAddr = 0x0000;//目标是协调器
// Take the first endpoint, Can be changed to search through endpoints
SimonApp_DstAddr.endPoint = MY_ENDPOINT02;//发送给协调器的6号端点
AF_DataRequest( &SimonApp_DstAddr, &SimonApp_epDesc,//端点描述符用默认填充的就好
CLUSTER_PHOTO_NODE,//6号端点里面的CLUSTER_PHOTO_NODE员工
tmp_len,//实际长度
(byte *)&gUartBuf,//打包好的数据
&SimonApp_TransID,//协议栈自动维护的数据包序号
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );//后两个参数默认不修改
}
tmp_len=0; //清空长度
}
}
图片发送模块Linux开发板核心模块:
#include "uart.h"
#define MAX_BUF 64
#define HDR_LEN 1
#define OPEN_PIC_ERR -2
#define MAX_SEC 2
const char begin_str[] = "begin\r\n";
const char end_str[] = "end\r\n";
char buf[MAX_BUF+HDR_LEN]={0};
char rcv_buf[MAX_BUF]={0};
fd_set inset,tempset;
struct timeval tv;
int waitACK(int src_fd,int read_cnt)
{
int res = 0;
while(1)
{
memset(rcv_buf,0,MAX_BUF);
/*为了程序的健壮性,需要在主循环不断更新文件描述符集合以及等待时间*/
tempset = inset;
tv.tv_sec = MAX_SEC;
tv.tv_usec = 0;
res = select(src_fd + 1,&tempset,NULL,NULL,&tv);
/*3.3.1 规定时间内收到响应数据包继续往下执行*/
/*3.3.2 超时没有响应信息则重新发送当前缓冲区数据包*/
/*3.3.3 发生错误就退出程序给出提示信息*/
if(res == 0)
{
printf("time out \n");
write(src_fd,buf,read_cnt+HDR_LEN);
continue;
}
else if (res < 0)
{
printf("select error\n");
exit(-1);
}
else
{
if(FD_ISSET(src_fd, &inset))
{
read(src_fd,rcv_buf,MAX_BUF);
printf("from zigbee:%s",rcv_buf);
//if(strcmp(rcv_buf,"OK\r\n") == 0)
memset(rcv_buf,0,MAX_BUF);
return 0;
}
}
}
}
int main(int argc,char ** argv)
{
int uart_fd = 0;
int pic_fd = 0;
int read_cnt = 0;
FD_ZERO(&inset);
if(argc <2)
{
printf("too less args...\n");
exit(-1);
}
/*0. 打开串口*/
uart_fd = open_uart(1);
if(uart_fd < 0)
{
perror("open_uart");
return OPEN_UART_ERR;
}
FD_SET(uart_fd,&inset);
/*等待时间初始化*/
tv.tv_sec = MAX_SEC;
tv.tv_usec = 0;
/*1. 配置串口*/
if(set_uart_config(uart_fd,115200,8,'N',1) < 0)
{
perror("set_uart_config");
return SET_UART_ERR;
}
/*2. 向串口写入开始发送数据包*/
memset(buf,0,MAX_BUF+HDR_LEN);
memset(rcv_buf,0,MAX_BUF);
read_cnt = strlen(begin_str);
buf[0] = read_cnt;
strcpy(buf+HDR_LEN,begin_str);
write(uart_fd,buf,read_cnt+HDR_LEN);
while(waitACK(uart_fd,read_cnt) != 0);
sleep(MAX_SEC);
/*3. 传输图片*/
/*3.1. 打开图片*/
pic_fd = open((const char *)argv[1],O_RDONLY);
if(pic_fd < 0)
{
perror("open");
return OPEN_PIC_ERR;
}
/*3.2. 读取一部分数据到缓冲区*/
/*临时缓冲区清零*/
memset(buf,0,MAX_BUF+HDR_LEN);
while((read_cnt = read(pic_fd,buf+HDR_LEN,MAX_BUF)) > 0 )
{
buf[0] = read_cnt;
/*3.3. 将当前缓冲区数据包写入串口*/
write(uart_fd,buf,read_cnt+HDR_LEN);
/*3.3. 等待接收端响应*/
while(waitACK(uart_fd,read_cnt) != 0);
/*临时缓冲区清零*/
memset(buf,0,MAX_BUF+HDR_LEN);
}
/*3.4. 数据是否读取完毕*/
/*3.4.1 没有读完,重复3.2至此*/
memset(buf,0,MAX_BUF+HDR_LEN);
memset(rcv_buf,0,MAX_BUF);
/*3.4.2 读取完毕,向串口写入发送完毕数据包*/
sleep(MAX_SEC);
read_cnt = strlen(end_str);
buf[0] = read_cnt;
strcpy(buf+HDR_LEN,end_str);
write(uart_fd,buf,read_cnt+HDR_LEN);
while(waitACK(uart_fd,read_cnt) != 0);
close(pic_fd);
close(uart_fd);
return 0;
}