资讯详情

CH579 以太网 DHCP+DNS+MQTT

更新,目前已修复BUG(以下源码已更改)

  1. 添加订阅逻辑,找到一个BUG,参考以下修改 https://bbs.21ic.com/icview-3016440-1-2.html
  2. 若出现 SINT_STAT_DISCONNECT不能重新联网,改成Transport_Open即可
  3. 没有心跳包,导致脱线重连,这里定期调用MQTT_Pingreq可以(这里主循环时间不准,然后可以放定时器)
  4. 将MAC作为 Client ID ,因为这个CID必须是唯一的,否则会被提下线,username 我随便设置了密码和密码。
  5. 加入检查入网逻辑,如果平台在60秒内失败,重启
  6. 加入心跳回复检查,如果10秒内没有收到心跳回复,重启
  7. 添加看门狗逻辑
  8. 对某些路由器 DHCP失败,这里可能和MAC有关系,前三个字节是厂家代码,我们只需要把第一个字节改成0,比如383b260df369 改为 003b260df369
  9. 此外,还出现了第一次联网后偶尔重启的问题。这里需要增加heap 10.DHCP失败,和DNS失败,直接重启 ,dns的修改,在DNS.c的DnsQuery在里面,超时2000s改成5s,然后添加失败复位操作 在这里插入图片描述

一、前言

很久没写博客了,最近很忙,这次分享一个CH579的组合例子 其实感觉很奇怪。DHCP感觉是必须的,DNS感觉也是,应该和TCP应用组合在一起。。分开给例子,挺那个的。


二、为何选CH579

  1. 价格太香了,随便放个图片,不是广告
  2. 看看外设
  3. 总结一下,6鸡,白菜价,引脚多,外设强,就拿来搞以太网都比W5500香

三、数据获取

  1. 软件(官网地址):https://www.wch.cn/products/CH579.html
  2. 硬件(立创开源原厂开发板):https://oshwhub.com/staunchheart/ch579 这里需要注意的是太网外部32M;若不使用DC-DC,电感部分电路可以直接短接;注意使用两个网口灯EVK的引脚; 所以只使用以太网的最小系统:(注3.3V的10UF必须有电容,否则在获取电容时DHCP会不断复位,上电会复位几次,应该是电压降低造成的)

四、参考程序

  1. 先看看example对我们来说,目录通常是DHCP搭配DNS搭配使用,这里分开,所以我整合了
  2. 分别使用了 DHCP_ClientMQTTDNS 这3个例子

五、程序重点

  • 对于库的使用,可以参考CH57xNET协议栈库说明.pdf

  • main.c协议栈的主任务函数已经运行,使用了10个ms这里的例子有相应的注释

    /******************************************************************************* * Function Name : main * Description : 主函数 * Input : None * Output : None * Return : None *******************************************************************************/ int main(void)  { 
                   UINT8 ip[4],i = 0;     UINT8 dns_flag = 0;     SystemClock_UART1_init();  GetMacAddr(MACAddr);      i = CH57xNET_LibInit(IPAddr,GWIPAddr,IPMask,MACAddr);                       /* 库初始化 */     mStopIfError(i);                                                            /* 检查错误 */     PRINT("CH57xNETLibInit Success\r\n");    Timer0Init( 10000 );                                                  /*初始定时器:10ms*/  NVIC_EnableIRQ(ETH_IRQn
             
              )
              ; 
              while 
              ( CH57xInf
              .PHYStat 
              < 
              2 
              ) 
              { 
                
              DelayMs
              (
              50
              )
              ; 
              } 
              PRINT
              (
              "CH579 DNS client create!\r\n"
              )
              ; 
              while
              (
              1
              ) 
              { 
                
              CH57xNET_MainTask
              (
              )
              ; 
              /* CH57xNET库主任务函数,需要在主循环中不断调用 */ 
              if
              (
              CH57xNET_QueryGlobalInt
              (
              )
              )
              CH57xNET_HandleGlobalInt
              (
              )
              ; 
              /* 查询中断,如果有中断,则调用全局中断处理函数 */ 
              if
              ( dns_flag 
              == 
              0 
              )
              { 
                i 
              = 
              DnsQuery
              (SocketId
              ,url_dn3
              ,ip
              )
              ; 
              /* 查询 */ 
              if
              (i
              )
              { 
                dns_flag 
              = 
              1
              ; 
              if
              ( i 
              == 
              1 
              )
              { 
                
              PRINT
              (
              "Domain name:%s \n"
              ,url_dn3
              )
              ; 
              PRINT
              (
              " HTTPs_IP=%d.%d.%d.%d\n\n"
              ,ip
              [
              0
              ]
              ,ip
              [
              1
              ]
              ,ip
              [
              2
              ]
              ,ip
              [
              3
              ]
              )
              ; 
              } 
              } 
              } 
              } 
              } 
             
  • 上电后的逻辑:上电后建立DNS的UDP(只需调用一次)–>开启DHCP–>DNS拿到IP–>将此IP复制到TCP的Socket参数中–>建立TCP–>建立MQTT 这里比较需要注意的是:SOCKET建立后,其实并不需要自己重新关闭什么的,例子已经帮我们做好了重连操作了DNS也是,对于插入网口后,只需要使用一次DNS即可,所以在程序中,DNS的SOCKET和MQTT的SOCKET,都是建立了一次后,就不需要自己操作断开什么的了。

  • 插拔网口逻辑:拔网口–>GINT_STAT_PHY_CHANGE 01–>使用CH57xNET_DHCPStop()关闭DHCP–>插入网口–>GINT_STAT_PHY_CHANGE 02–>使用CH57xNET_DHCPStart(CH57xNET_DHCPCallBack)重新开启DHCP–>TCPtimeout或者disconnect–>DHCP完成–>TCPtimeout或者disconnect–>自动重连 没太理解的,可以看下这个LOG

六、源码

注意点:

  • dns.c和dns.h放到mqtt例子中
  • 下面的程序替换掉MQTT_Pub.c
  • 将dns.c里面的status替换成dns_status,因为dns.c里面的status和dhcp里面的status冲突了
/********************************** (C) COPYRIGHT ****************************** * File Name : MQTT_Pub.c * Author : WCH * Version : V1.0 * Date : 2018/12/01 * Description : 基于TCP/IP的MQTT协议通讯例程,实现以MQTT方式,通过百度、阿里云物联网服务器实现设备互通;使用前需设置帐号和密码 * (1)、CH579 Examples by KEIL; * (2)、串口1输出监控信息,115200bps,打开或者关闭调试信息输出在IDE宏定义中设置; * (3)、本程序用于演示基于TCP/IP的MQTT协议通讯, 此程序演示PUB的过程 *******************************************************************************/



/******************************************************************************/
/* 头文件包含*/
#include <stdio.h>
#include <string.h>
#include "CH57x_common.h"
#include "core_cm0.h"
#include "CH57xNET.H"
#include "MQTTPacket.H"
#include "dns.h"

#define KEEPLIVE_ENABLE 1 /* 开启KEEPLIVE功能 */

/* 下面的缓冲区和全局变量必须要定义,库中调用 */
__align(16)UINT8    CH57xMACRxDesBuf[(RX_QUEUE_ENTRIES )*16];                   /* MAC接收描述符缓冲区,16字节对齐 */
__align(4) UINT8    CH57xMACRxBuf[RX_QUEUE_ENTRIES*RX_BUF_SIZE];                /* MAC接收缓冲区,4字节对齐 */
__align(4) SOCK_INF SocketInf[CH57xNET_MAX_SOCKET_NUM];                         /* Socket信息表,4字节对齐 */

UINT16 MemNum[8] = { 
        CH57xNET_NUM_IPRAW,
                    CH57xNET_NUM_UDP,
                    CH57xNET_NUM_TCP,
                    CH57xNET_NUM_TCP_LISTEN,
                    CH57xNET_NUM_TCP_SEG,
                    CH57xNET_NUM_IP_REASSDATA,
                    CH57xNET_NUM_PBUF,
                    CH57xNET_NUM_POOL_BUF
                    };
UINT16 MemSize[8] = { 
        CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_IPRAW_PCB),
                    CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_UDP_PCB),
                    CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_TCP_PCB),
                    CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_TCP_PCB_LISTEN),
                    CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_TCP_SEG),
                    CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_IP_REASSDATA),
                    CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_PBUF) + CH57xNET_MEM_ALIGN_SIZE(0),
                    CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_PBUF) + CH57xNET_MEM_ALIGN_SIZE(CH57xNET_SIZE_POOL_BUF)
                    };
__align(4)UINT8 Memp_Memory[CH57xNET_MEMP_SIZE];
__align(4)UINT8 Mem_Heap_Memory[CH57xNET_RAM_HEAP_SIZE];
__align(4)UINT8 Mem_ArpTable[CH57xNET_RAM_ARP_TABLE_SIZE];
/******************************************************************************/
/* 本演示程序的相关宏 */
#define RECE_BUF_LEN 536 /* 接收缓冲区的大小 */
/* CH57xNET库TCP的MSS长度为536字节,即一个TCP包里的数据部分最长为536字节 */
/* TCP协议栈采用滑动窗口进行流控,窗口最大值为socket的接收缓冲区长度。在设定 */
/* RX_QUEUE_ENTRIES时要考虑MSS和窗口之间的关系,例如窗口值为4*MSS,则远端一次会发送 */
/* 4个TCP包,如果RX_QUEUE_ENTRIES小于4,则必然会导致数据包丢失,从而导致通讯效率降低 */
/* 建议RX_QUEUE_ENTRIES要大于( 窗口/MSS ),如果多个socket同时进行大批量发送数据,则 */ 
/* 建议RX_QUEUE_ENTRIES要大于(( 窗口/MSS )*socket个数) 在多个socket同时进行大批数据收发时 */
/* 为了节约RAM,请将接收缓冲区的长度设置为MSS */

/* CH579MQTT相关定义,需要根据实际情况修改 */
UINT8 MACAddr[6] = { 
        0x84,0xc2,0xe4,0x02,0x03,0x04};                              /* CH579MAC地址 */
UINT8 MACAddr_Str[20];
UINT8 IPAddr[4]  = { 
        192,168,1,200};                                              /* CH579IP地址 */
UINT8 GWIPAddr[4]= { 
        192,168,1,1};                                                /* CH579网关 */
UINT8 IPMask[4]  = { 
        255,255,255,0};                                              /* CH579子网掩码 */
UINT8 DESIP[4]   = { 
        58,213,74,190};                                              /* 目的IP地址 */
UINT16 aport=1000;											                     /* CH579源端口 */

/* 网口灯定义 PB口低十六位有效 */
UINT16 CH57xNET_LEDCONN=0x0010;                                                 /* 连接指示灯 PB4 */
UINT16 CH57xNET_LEDDATA=0x0080;                                                 /* 通讯指示灯 PB7 */ 

char *username =  "123";							                                 /* 设备名,每个设备唯一,可用”/“做分级 */
char *password =  "123";								                             /* 服务器登陆密码 */
char *sub_topic = "tt456";								                             /* 订阅的会话名,为了自发自收,应与发布的会话名相同 */
char *pub_topic = "tt123";									                         /* 发布的会话*/

UINT8 SocketId;                                                                  /* 保存socket索引,可以不用定义 */
UINT8 SocketRecvBuf[RECE_BUF_LEN];                                               /* socket接收缓冲区 */
UINT8 MyBuf[RECE_BUF_LEN];                                                       /* 定义一个临时缓冲区 */

UINT8 con_flag=0;										                         /* 已连接MQTT服务器标志位 */
UINT8 pub_flag=1;											                     /* 已发布会话消息标志位 */
UINT8 sub_flag=0;											                     /* 已订阅会话标志位 */
UINT8 tout_flag=0;											                     /* 超时标志位 */
UINT16 packetid=0;											                     /* 包ID */

UINT8 dns_status;
extern UINT8 dns_buf[512];
UINT8 dhcp_ok = 1;
UINT8 dns_flag = 0;
#define MAX_URL_SIZE 128
UINT8  url_dn3[MAX_URL_SIZE] = "test.mosquitto.org";  
UINT16 check_heart;//检查心跳回复
UINT16 check_conn;//检查入网

/******************************************************************************* * Function Name : IRQ_Handler * Description : IRQ中断服务函数 * Input : None * Output : None * Return : None *******************************************************************************/
void ETH_IRQHandler( void )						                             	/* 以太网中断 */
{ 
        
	CH57xNET_ETHIsr();								                            /* 以太网中断中断服务函数 */
}

void TMR0_IRQHandler( void ) 						                            /* 定时器中断 */
{ 
        
	CH57xNET_TimeIsr(CH57xNETTIMEPERIOD);                                       /* 定时器中断服务函数 */
	R8_TMR0_INT_FLAG |= 0xff;					                             	/* 清除定时器中断标志 */
}



/******************************************************************************* * Function Name : mStopIfError * Description : 调试使用,显示错误代码 * Input : iError 错误代码 * Output : None * Return : None *******************************************************************************/
void mStopIfError(UINT8 iError)
{ 
        
    if (iError == CH57xNET_ERR_SUCCESS) return;                                /* 操作成功 */
    PRINT("mStopIfError: %02X\r\n", (UINT16)iError);                           /* 显示错误 */
}

/******************************************************************************* * Function Name : Transport_Open * Description : 创建TCP连接 * Input : None * Output : None * Return : None *******************************************************************************/
UINT8 Transport_Open()
{ 
        
	UINT8 i;																														 
	SOCK_INF TmpSocketInf;								                 	/* 创建临时socket变量 */

	memset((void *)&TmpSocketInf,0,sizeof(SOCK_INF));	                 	/* 库内部会将此变量复制,所以最好将临时变量先全部清零 */
	memcpy((void *)TmpSocketInf.IPAddr,DESIP,4);		                   	/* 设置目的IP地址 */
	TmpSocketInf.DesPort = 1883;							                /* 设置目的端口 */
	TmpSocketInf.SourPort = aport++;						                /* 设置源端口 */
	TmpSocketInf.ProtoType = PROTO_TYPE_TCP;			                	/* 设置socekt类型 */
	TmpSocketInf.RecvStartPoint = (UINT32)SocketRecvBuf;	                /* 设置接收缓冲区的接收缓冲区 */
	TmpSocketInf.RecvBufLen = RECE_BUF_LEN ;			                 	/* 设置接收缓冲区的接收长度 */
	i = CH57xNET_SocketCreat(&SocketId,&TmpSocketInf);		                /* 创建socket,将返回的socket索引保存在SocketId中 */
	mStopIfError(i);										                /* 检查错误 */
	
	i = CH57xNET_SocketConnect(SocketId);				                	/* TCP连接 */
	mStopIfError(i);										                /* 检查错误 */
	return SocketId;
}


/******************************************************************************* * Function Name : Transport_Close * Description : 关闭TCP连接 * Input : None * Output : None * Return : None *******************************************************************************/
UINT8 Transport_Close()
{ 
        
	UINT8 i;
	i=CH57xNET_SocketClose(SocketId,TCP_CLOSE_NORMAL);
	mStopIfError(i);
	return i;
}



/******************************************************************************* * Function Name : UDPSocketParamInit * Description : 创建UDP socket * Input : socket索引值,源端口,目的端口及目的IP * Output : None * Return : None *******************************************************************************/
void UDPSocketParamInit(UINT8 S,UINT8 *addr,UINT16 SourPort,UINT16 DesPort )
{ 
        
   UINT8 i;                                                             
   SOCK_INF TmpSocketInf;                                                       /* 创建临时socket变量 */

   memset((void *)&TmpSocketInf,0,sizeof(SOCK_INF));                            /* 库内部会将此变量复制,所以最好将临时变量先全部清零 */
   memcpy((void *)TmpSocketInf.IPAddr,addr,4);                                  /* 设置目的IP地址 */
 
   TmpSocketInf.DesPort = DesPort;                                              /* 设置目的端口 */
   TmpSocketInf.SourPort = SourPort;                                            /* 设置源端口 */
   TmpSocketInf.ProtoType = PROTO_TYPE_UDP;                                     /* 设置socekt类型 */
   TmpSocketInf.RecvStartPoint = (UINT32)SocketRecvBuf;                         /* 设置接收缓冲区的接收缓冲区 */
   TmpSocketInf.RecvBufLen = RECE_BUF_LEN ;                                     /* 设置接收缓冲区的接收长度 */
   i = CH57xNET_SocketCreat(&S,&TmpSocketInf);                           /* 创建socket,将返回的socket索引保存在SocketId中 */
   mStopIfError(i);                                                             /* 检查错误 */
}

/******************************************************************************* * Function Name : Transport_SendPacket * Description : 以太网发送数据 * Input : UINT8 *buf 发送数据的首字节地址 UINT32 len 发送数据的长度 * Output : None * Return : None *******************************************************************************/
void Transport_SendPacket(UINT8 *buf,UINT32 len)
{ 
        
	UINT32 totallen;
	UINT8 *p=buf;
	
	totallen=len;
	while(1)
	{ 
        
		len = totallen;
		CH57xNET_SocketSend(SocketId,p,&len);				              	/* 将MyBuf中的数据发送 */
		totallen -= len;					 					            /* 将总长度减去以及发送完毕的长度 */
		p += len;											             	/* 将缓冲区指针偏移*/
		if(totallen)continue;									            /* 如果数据未发送完毕,则继续发送*/
		break;													            /* 发送完毕,退出 */
	}
}


/******************************************************************************* * Function Name : MQTT_Connect * Description : 创建MQTT连接 * Input : char *username 设备名 char *password 服务器连接密码 * Output : None * Return : None *******************************************************************************/
void MQTT_Connect(char *username,char *password)
{ 
        
	MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
	UINT32 len;
	UINT8 buf[200];

	data.clientID.cstring = MACAddr_Str;
	PRINT("MQTT_Connect clientID:%s\r\n",data.clientID.cstring);
	data.keepAliveInterval = 20;
	data.cleansession = 1;
	data.username.cstring = username;																		
	data.password.cstring = password;
	 
	len=MQTTSerialize_connect(buf,sizeof(buf),&data);											
	Transport_SendPacket(buf,len);						
}


/******************************************************************************* * Function Name : MQTT_Subscribe * Description : MQTT订阅一个主题 * Input : char *topic 订阅的主题名 * Output : None * Return : None *******************************************************************************/
void MQTT_Subscribe( char *topic)
{ 
        
	MQTTString topicString = MQTTString_initializer;
	int req_qos=0;
	UINT32 len;
	UINT32 msgid=1;
	UINT8 buf[200];
	
	topicString.cstring=topic;
	len=MQTTSerialize_subscribe(buf,sizeof(buf),0,msgid,1,&topicString,&req_qos);
	Transport_SendPacket(buf,len);
}


/******************************************************************************* * Function Name : MQTT_Unsubscribe * Description : MQTT取消订阅一个主题 * Input : char *topic 取消订阅的主题名 * Output : None * Return : None *******************************************************************************/
void MQTT_Unsubscribe(char *topic)
{ 
        
	MQTTString topicString = MQTTString_initializer;
	UINT32 len;
	UINT32 msgid=1;
	UINT8 buf[200];
	
	topicString.cstring=topic;
	len=MQTTSerialize_unsubscribe(buf,sizeof(buf),0,msgid,1,&topicString);
	Transport_SendPacket(buf,len);
}


/******************************************************************************* * Function Name : MQTT_Subscribe * Description : MQTT订阅一个主题 * Input : char *topic 订阅的主题名 * Output : None * Return : None *******************************************************************************/
void MQTT_Publish(char *topic,char *payload)
{ 
        
	MQTTString topicString = MQTTString_initializer;
	UINT32 payloadlen;
	UINT32 len;
	UINT8 buf[1024];
	
	topicString.cstring=topic; 
	payloadlen=strlen(payload);
	len= MQTTSerialize_publish(buf,sizeof(buf),0,0,0,packetid++,topicString,payload,payloadlen);	
	Transport_SendPacket(buf,len);
}


/******************************************************************************* * Function Name : MQTT_Pingreq * Description : MQTT发送心跳包 * Input : None * Output : None * Return : None *******************************************************************************/
void MQTT_Pingreq()
{ 
        
	UINT32 len;
	UINT8 buf[200];
	
	len=MQTTSerialize_pingreq(buf,sizeof(buf));
	Transport_SendPacket(buf,len);
}


/******************************************************************************* * Function Name : MQTT_Disconnect * Description : 断开MQTT连接 * Input : None * Output : None * Return : None *******************************************************************************/
void MQTT_Disconnect()
{ 
        
	UINT32 len;
	UINT8 buf[50];
	len=MQTTSerialize_disconnect(buf,sizeof(buf));
	Transport_SendPacket(buf,len);
}



/******************************************************************************* * Function Name : CH57xNET_CreatTcpSocket * Description : 创建TCP Client socket * Input : None * Output : None * Return : None *******************************************************************************/
void CH57xNET_CreatTcpSocket(void)
{ 
        
   UINT8 i;                                                             
   SOCK_INF TmpSocketInf;                                                       /* 创建临时socket变量 */

   memset((void *)&TmpSocketInf,0,sizeof(SOCK_INF));                            /* 库内部会将此变量复制,所以最好将临时变量先全部清零 */
   memcpy((void *)TmpSocketInf.IPAddr,DESIP,4);                                 /* 设置目的IP地址 */
   TmpSocketInf.DesPort = 1000;                     
   TmpSocketInf.SourPort = 2000;                                                /* 设置源端口 */
   TmpSocketInf.ProtoType = PROTO_TYPE_TCP;                                     
        标签: 电容能替2uf1r8电容

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台