欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

stm32zet6将温度数据上传至yeelnk服务器,电脑上位机查看

程序员文章站 2022-05-03 09:29:34
...

对于yeelink服务器的请求的最快的频率是11秒一次,请求时间间隔小于10秒的时候会丢包,不知道机智云的效果怎么样,有尝试过的朋友可以留言。

stm32将18b20的温度信息上传到yeelink服务器。上位机读取yeelink服务器的数据。同时可用stm32串口查看服务器的回传信息(usart1 波特率115200 无校验位 一个停止位)。


stm32zet6将温度数据上传至yeelnk服务器,电脑上位机查看

上位机显示界面,代码和上一篇的代码基本相同。不同的地方是定时器的时间间隔加长了,因为时间间隔过短会导致丢包。


下位机是stm32zet6+18b20.18b20的DQ引脚接PG11,至于w5500按照用户手册插线即可。用网线将路由器和w5500模块连接(因为路由器可以自动分配ip地址,使用起来很方便)。


下位机的难点是dns解析。


/**
  ******************************************************************************
  * @file    main.c
  * $Author: 飞鸿踏雪 $
  * $Revision: 17 $
  * $Date:: 2014-10-25 11:16:48 +0800 #$
  * @brief   主函数.
  ******************************************************************************
  * @attention
  *
  *<h3><center>&copy; Copyright 2009-2012, EmbedNet</center>
  *<center><a href="http:\\www.embed-net.com">http://www.embed-net.com</a></center>
  *<center>All Rights Reserved</center></h3>
  * 
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "delay.h"
#include "spi.h"
#include "socket.h"	// Just include one header for WIZCHIP
#include "Internet/DNS/dns.h"
#include "timer.h"
#include <string.h>
#include <math.h>
/* Private typedef -----------------------------------------------------------*/
#include "ds18b20.h"
/* Private define ------------------------------------------------------------*/
#define SOCK_TCPS        	0
#define SOCK_DNS					1
#define DATA_BUF_SIZE   2048
/* Private macro -------------------------------------------------------------*/
uint8_t gDATABUF[DATA_BUF_SIZE];
uint8_t domain_ip[4]={0};
uint8_t domain_name[]="yeelink.net";
// Default Network Configuration
wiz_NetInfo gWIZNETINFO = { .mac = {0x00, 0x08, 0xdc,0x00, 0xab, 0xcd},
                            .ip = {192, 168, 0, 123},//貌似瞎写就行
                            .sn = {255,255,255,0},
                            .gw = {192, 168, 0, 1},//这个也需要用ipconfig /all查看
                            .dns = {192, 168, 0, 1},//修改成自己网络的DNS,windows下需要用ipconfig /all查看dns服务器地址
                            .dhcp = NETINFO_STATIC };
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
void platform_init(void);								// initialize the dependent host peripheral
void network_init(void);								// Initialize Network information and display it
uint8_t yeelink_get(const char *device_id,const char *sensors_id,char *value);
/**
  * @brief  串口打印输出
  * @param  None
  * @retval None
  */
int main(void)
{
	uint8_t tmp;
	int32_t ret = 0,t=0;
	uint8_t memsize[2][8] = {{2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2}};
	char value[16]={0};
	delay_init();
//	DS18B20_Init();
	while(DS18B20_Init())	//DS18B20初始化	
	{
	}


	//Host dependent peripheral initialized
	platform_init();
	// First of all, Should register SPI callback functions implemented by user for accessing WIZCHIP 
	/* Critical section callback */
	reg_wizchip_cris_cbfunc(SPI_CrisEnter, SPI_CrisExit);	//注册临界区函数
	/* Chip selection call back */
#if   _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_
	reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);//注册SPI片选信号函数
#elif _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_FDM_
	reg_wizchip_cs_cbfunc(SPI_CS_Select, SPI_CS_Deselect);  // CS must be tried with LOW.
#else
   #if (_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SIP_) != _WIZCHIP_IO_MODE_SIP_
      #error "Unknown _WIZCHIP_IO_MODE_"
   #else
      reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect);
   #endif
#endif
	/* SPI Read & Write callback function */
	reg_wizchip_spi_cbfunc(SPI_ReadByte, SPI_WriteByte);	//注册读写函数

	/* WIZCHIP SOCKET Buffer initialize */
	if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -1){
		 printf("WIZCHIP Initialized fail.\r\n");
		 while(1);
	}

	/* PHY link status check */
	do{
		 if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1){
				printf("Unknown PHY Link stauts.\r\n");
		 }
	}while(tmp == PHY_LINK_OFF);

	/* Network initialization */
	network_init();
	/* DNS client initialization */
	DNS_init(SOCK_DNS, gDATABUF);
	Timer_Start();
	/* DNS procssing */
	if ((ret = DNS_run(gWIZNETINFO.dns, domain_name, domain_ip)) > 0){ // try to 1st DNS
		printf("> 1st DNS Reponsed\r\n");
	}else if(ret == -1){
		printf("> MAX_DOMAIN_NAME is too small. Should be redefined it.\r\n");
		Timer_Stop();
		while(1);
	}else{
		printf("> DNS Failed\r\n");
		Timer_Stop();
		while(1);
	}
	//DNS解析成功
	if(ret > 0){
		printf("> Translated %s to %d.%d.%d.%d\r\n",domain_name,domain_ip[0],domain_ip[1],domain_ip[2],domain_ip[3]);
	}
	Timer_Stop();
	while(1)
	{
		yeelink_get("358143","406963",value);//获取开关变量
//		printf("%s\n\r",value);
//		delay_ms(1000);
		for(t=0;t<11;t++){
			delay_ms(1000);
		}
	}
}

/**
  * @brief  Intialize the network information to be used in WIZCHIP
  * @retval None
  */
void network_init(void)
{
	uint8_t tmpstr[6];
	ctlnetwork(CN_SET_NETINFO, (void*)&gWIZNETINFO);
	ctlnetwork(CN_GET_NETINFO, (void*)&gWIZNETINFO);

	// Display Network Information
	ctlwizchip(CW_GET_ID,(void*)tmpstr);
	printf("\r\n=== %s NET CONF ===\r\n",(char*)tmpstr);
	printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n",gWIZNETINFO.mac[0],gWIZNETINFO.mac[1],gWIZNETINFO.mac[2],
		  gWIZNETINFO.mac[3],gWIZNETINFO.mac[4],gWIZNETINFO.mac[5]);
	printf("SIP: %d.%d.%d.%d\r\n", gWIZNETINFO.ip[0],gWIZNETINFO.ip[1],gWIZNETINFO.ip[2],gWIZNETINFO.ip[3]);
	printf("GAR: %d.%d.%d.%d\r\n", gWIZNETINFO.gw[0],gWIZNETINFO.gw[1],gWIZNETINFO.gw[2],gWIZNETINFO.gw[3]);
	printf("SUB: %d.%d.%d.%d\r\n", gWIZNETINFO.sn[0],gWIZNETINFO.sn[1],gWIZNETINFO.sn[2],gWIZNETINFO.sn[3]);
	printf("DNS: %d.%d.%d.%d\r\n", gWIZNETINFO.dns[0],gWIZNETINFO.dns[1],gWIZNETINFO.dns[2],gWIZNETINFO.dns[3]);
	printf("======================\r\n");
}

/**
  * @brief  Loopback Test Example Code using ioLibrary_BSD	
  * @retval None
  */
void platform_init(void)
{
	SystemInit();//系统时钟初始化
	USART_Configuration();//串口1初始化
	printf("\x0c");printf("\x0c");//超级终端清屏
	printf("\033[1;40;32m");//设置超级终端背景为黑色,字符为绿色
	printf("\r\n*******************************************************************************");
	printf("\r\n************************ Copyright 2009-2014, EmbedNet ************************");
	printf("\r\n*************************** http://www.embed-net.com **************************");
	printf("\r\n***************************** All Rights Reserved *****************************");
	printf("\r\n*******************************************************************************");
	printf("\r\n");
	//Config SPI
	SPI_Configuration();
	//延时初始化
	delay_init();
	//初始化DNS所需要的定时器
	Timer_Config();
}

uint8_t yeelink_get(const char *device_id,const char *sensors_id,char *value)
{
	int ret;
	int temperature=256;
	char* presult;
	char remote_server[] = "api.yeelink.net";
	char str_tmp[128] = {0};
	// 请求缓冲区和响应缓冲区
	static char http_request[DATA_BUF_SIZE] = {0};	//声明为静态变量,防止堆栈溢出
	static char http_response[DATA_BUF_SIZE] = {0};	//声明为静态变量,防止堆栈溢出
	static char test_request[DATA_BUF_SIZE] = {0};	//声明为静态变量,防止堆栈溢出
	static char http_content[64] = { 0, };
	
	temperature=DS18B20_Get_Temp();	
	sprintf(http_content, "{\"value\":%d}", temperature);
	sprintf(test_request,"POST /v1.0/device/写自己的/sensor/写自己的/datapoints HTTP/1.1\r\nHost:api.yeelink.net\r\nU-ApiKey:写自己的\r\nContent-Length:%d\r\nContent-Type:application/x-www-form-urlencoded\r\n\r\n{\"value\":%d}",strlen(http_content),temperature);
	sprintf(str_tmp,"/v1.0/device/%s/sensor/%s/datapoints",device_id,sensors_id);
	// 确定 HTTP请求首部
	// 例如POST /v1.0/device/98d19569e0474e9abf6f075b8b5876b9/1/1/datapoints/add HTTP/1.1\r\n
	sprintf( http_request , "GET %s HTTP/1.1\r\n",str_tmp);
	// 增加属性 例如 Host: api.machtalk.net\r\n
	sprintf( str_tmp , "Host:%s\r\n" , remote_server);
	strcat( http_request , str_tmp);

	// 增加密码 例如 APIKey: d8a605daa5f4c8a3ad086151686dce64
	sprintf( str_tmp , "U-ApiKey:%s\r\n" , "写自己的");//需要替换为自己的APIKey
	strcat( http_request , str_tmp);
	//
	strcat( http_request , "Accept: */*\r\n");
	// 增加表单编码格式 Content-Type:application/x-www-form-urlencoded\r\n
	strcat( http_request , "Content-Type: application/x-www-form-urlencoded\r\n");
	strcat( http_request , "Connection: close\r\n");
	// HTTP首部和HTTP内容 分隔部分
	strcat( http_request , "\r\n");
	
	//将数据通过TCP发送出去
	//新建一个Socket并绑定本地端口5000
	ret = socket(SOCK_TCPS,Sn_MR_TCP,5000,0x00);
	if(ret != SOCK_TCPS){
		printf("%d:Socket Error\r\n",SOCK_TCPS);
		while(1);
	}
	//连接TCP服务器
	ret = connect(SOCK_TCPS,domain_ip,80);
	if(ret != SOCK_OK){
		printf("%d:Socket Connect Error\r\n",SOCK_TCPS);
		while(1);
	}	
	//发送请求
//	ret = send(SOCK_TCPS,(unsigned char *)http_request,strlen(http_request));
	ret = send(SOCK_TCPS,(unsigned char *)test_request,strlen(test_request));
	if(ret != strlen(test_request)){
		printf("%d:Socket Send Error\r\n",SOCK_TCPS);
		while(1);
	}

	// 获得响应
	ret = recv(SOCK_TCPS,(unsigned char *)http_response,DATA_BUF_SIZE);
	if(ret <= 0){
		printf("%d:Socket Get Error\r\n",SOCK_TCPS);
		while(1);
	}
	http_response[ret] = '\0';
	//判断是否收到HTTP OK
	printf("%s",http_response);
//	presult = strstr( (const char *)http_response , (const char *)"200 OK\r\n");
//	if( presult != NULL ){
//		static char strTmp[DATA_BUF_SIZE]={0};//声明为静态变量,防止堆栈溢出
//		sscanf(http_response,"%*[^{]{%[^}]",strTmp);
//		//提取返回信息
//		char timestamp[64]={0};
//		char timestampTmp[64]={0};
//		char valueTmp[64]={0};
//		sscanf(strTmp,"%[^,],%[^,]",timestampTmp,valueTmp);
//		strncpy(timestamp,strstr(timestampTmp,":")+2,strlen(strstr(timestampTmp,":"))-3);
//		strncpy(value,strstr(valueTmp,":")+1,strlen(strstr(valueTmp,":"))-1);
//	}else{
//		printf("Http Response Error\r\n");
//		printf("%s",http_response);
//	}
	close(SOCK_TCPS);
	return 0;
}
/*********************************END OF FILE**********************************/

上述代码是网站上的大神编写的。不过已经被我按照自己的需求更改过了。下面的链接是这个大神的博客,里面有他的源码。

http://www.embed-net.com/forum.php?mod=viewthread&tid=69

这是文章链接。他写了很多关于yeelink的博客,都很有用。

http://www.embed-net.com/thread-57-1-1.html

这是用stm32实现域名解析的链接。




stm32zet6将温度数据上传至yeelnk服务器,电脑上位机查看

上图显示了工程所引用的文件。个人感觉dns.c dhcp.c的文件嘴刁。他们可以将域名解析成ip地址。前提是要在程序里面书写正确的dns服务器地址。


stm32zet6将温度数据上传至yeelnk服务器,电脑上位机查看


stm32zet6将温度数据上传至yeelnk服务器,电脑上位机查看


stm32zet6将温度数据上传至yeelnk服务器,电脑上位机查看


因为我的电脑里面安装了虚拟机,所以可能和大多数人不一样。上面三张命令行的图片中的第一张图片显示了dns服务器的ip地址,网关地址等重要信息。在stm32的程序中把自己的dns服务器的地址写上,网关也写上。就可以解析域名了。





uint8_t yeelink_get(const char *device_id,const char *sensors_id,char *value)
{
	int ret;
	int temperature=256;
	char* presult;
	char remote_server[] = "api.yeelink.net";
	char str_tmp[128] = {0};
	// 请求缓冲区和响应缓冲区
	static char http_request[DATA_BUF_SIZE] = {0};	//声明为静态变量,防止堆栈溢出
	static char http_response[DATA_BUF_SIZE] = {0};	//声明为静态变量,防止堆栈溢出
	static char test_request[DATA_BUF_SIZE] = {0};	//声明为静态变量,防止堆栈溢出
	static char http_content[64] = { 0, };
	
	temperature=DS18B20_Get_Temp();	
	sprintf(http_content, "{\"value\":%d}", temperature);
	sprintf(test_request,"POST /v1.0/device/写自己的/sensor/写自己的/datapoints HTTP/1.1\r\nHost:api.yeelink.net\r\nU-ApiKey:写自己的\r\nContent-Length:%d\r\nContent-Type:application/x-www-form-urlencoded\r\n\r\n{\"value\":%d}",strlen(http_content),temperature);
	sprintf(str_tmp,"/v1.0/device/%s/sensor/%s/datapoints",device_id,sensors_id);
	// 确定 HTTP请求首部
	// 例如POST /v1.0/device/98d19569e0474e9abf6f075b8b5876b9/1/1/datapoints/add HTTP/1.1\r\n
	sprintf( http_request , "GET %s HTTP/1.1\r\n",str_tmp);
	// 增加属性 例如 Host: api.machtalk.net\r\n
	sprintf( str_tmp , "Host:%s\r\n" , remote_server);
	strcat( http_request , str_tmp);

	// 增加密码 例如 APIKey: d8a605daa5f4c8a3ad086151686dce64
	sprintf( str_tmp , "U-ApiKey:%s\r\n" , "写自己的");//需要替换为自己的APIKey
	strcat( http_request , str_tmp);
	//
	strcat( http_request , "Accept: */*\r\n");
	// 增加表单编码格式 Content-Type:application/x-www-form-urlencoded\r\n
	strcat( http_request , "Content-Type: application/x-www-form-urlencoded\r\n");
	strcat( http_request , "Connection: close\r\n");
	// HTTP首部和HTTP内容 分隔部分
	strcat( http_request , "\r\n");
	
	//将数据通过TCP发送出去
	//新建一个Socket并绑定本地端口5000
	ret = socket(SOCK_TCPS,Sn_MR_TCP,5000,0x00);
	if(ret != SOCK_TCPS){
		printf("%d:Socket Error\r\n",SOCK_TCPS);
		while(1);
	}
	//连接TCP服务器
	ret = connect(SOCK_TCPS,domain_ip,80);
	if(ret != SOCK_OK){
		printf("%d:Socket Connect Error\r\n",SOCK_TCPS);
		while(1);
	}	
	//发送请求
//	ret = send(SOCK_TCPS,(unsigned char *)http_request,strlen(http_request));
	ret = send(SOCK_TCPS,(unsigned char *)test_request,strlen(test_request));
	if(ret != strlen(test_request)){
		printf("%d:Socket Send Error\r\n",SOCK_TCPS);
		while(1);
	}

	// 获得响应
	ret = recv(SOCK_TCPS,(unsigned char *)http_response,DATA_BUF_SIZE);
	if(ret <= 0){
		printf("%d:Socket Get Error\r\n",SOCK_TCPS);
		while(1);
	}
	http_response[ret] = '\0';
	//判断是否收到HTTP OK
	printf("%s",http_response);
//	presult = strstr( (const char *)http_response , (const char *)"200 OK\r\n");
//	if( presult != NULL ){
//		static char strTmp[DATA_BUF_SIZE]={0};//声明为静态变量,防止堆栈溢出
//		sscanf(http_response,"%*[^{]{%[^}]",strTmp);
//		//提取返回信息
//		char timestamp[64]={0};
//		char timestampTmp[64]={0};
//		char valueTmp[64]={0};
//		sscanf(strTmp,"%[^,],%[^,]",timestampTmp,valueTmp);
//		strncpy(timestamp,strstr(timestampTmp,":")+2,strlen(strstr(timestampTmp,":"))-3);
//		strncpy(value,strstr(valueTmp,":")+1,strlen(strstr(valueTmp,":"))-1);
//	}else{
//		printf("Http Response Error\r\n");
//		printf("%s",http_response);
//	}
	close(SOCK_TCPS);
	return 0;
}
用上面这段程序向服务器发送指令。但是一定要记住,不管是上位机发送指令还是下位机发送指令,指令之间的时间间隔必须大于10秒。否则会出错。


if ((ret = DNS_run(gWIZNETINFO.dns, domain_name, domain_ip)) > 0){ // try to 1st DNS
		printf("> 1st DNS Reponsed\r\n");
	}else if(ret == -1){
		printf("> MAX_DOMAIN_NAME is too small. Should be redefined it.\r\n");
		Timer_Stop();
		while(1);
	}else{
		printf("> DNS Failed\r\n");
		Timer_Stop();
		while(1);
	}
初始化中(while死循环外部)有域名解析



一下代码是yeelink_get函数里面的


//新建一个Socket并绑定本地端口5000
	ret = socket(SOCK_TCPS,Sn_MR_TCP,5000,0x00);
	if(ret != SOCK_TCPS){
		printf("%d:Socket Error\r\n",SOCK_TCPS);
		while(1);
	}

创建端口


//连接TCP服务器
	ret = connect(SOCK_TCPS,domain_ip,80);
	if(ret != SOCK_OK){
		printf("%d:Socket Connect Error\r\n",SOCK_TCPS);
		while(1);
	}

与服务器连接



	//发送请求
//	ret = send(SOCK_TCPS,(unsigned char *)http_request,strlen(http_request));
	ret = send(SOCK_TCPS,(unsigned char *)test_request,strlen(test_request));
	if(ret != strlen(test_request)){
		printf("%d:Socket Send Error\r\n",SOCK_TCPS);
		while(1);
	}

向服务器发送请求,再次强调,任何想yeelink服务器发送请求的时间间隔必须大于10秒


	// 获得响应
	ret = recv(SOCK_TCPS,(unsigned char *)http_response,DATA_BUF_SIZE);
	if(ret <= 0){
		printf("%d:Socket Get Error\r\n",SOCK_TCPS);
		while(1);
	}
	http_response[ret] = '\0';
	//判断是否收到HTTP OK
	printf("%s",http_response);

获得服务器的回传数据,并将其数据通过串口打印到串口调试住手。


	close(SOCK_TCPS);

关闭套接字


主要步骤为:域名解析 建立套接字 连接服务器 向服务器发送请求 服务器回传信息 关闭套接字(第一次)


                                     建立套接字 连接服务器 向服务器发送请求 服务器回传信息 关闭套接字 (域名解析一次就行了)


http://download.csdn.net/detail/m1f2c3/9849558

YIXIN_W5500模块用户手册

http://download.csdn.net/detail/m1f2c3/9849573

JXW_W5500+模块配套资料

http://download.csdn.net/detail/m1f2c3/9849630

yeelink+stm32+18b20下位机