---
title: "[STM32F4] 【Nucleo设计分享】411RET6的W5500采集温度上传至Yeelink"
url: "https://maker.wiznet.io/chen/projects/%5Bstm32f4%5D-%E3%80%90nucleo%E8%AE%BE%E8%AE%A1%E5%88%86%E4%BA%AB%E3%80%91411ret6%E7%9A%84w5500%E9%87%87%E9%9B%86%E6%B8%A9%E5%BA%A6%E4%B8%8A%E4%BC%A0%E8%87%B3yeelink/"
markdown_url: "https://maker.wiznet.io/chen/projects/%5Bstm32f4%5D-%E3%80%90nucleo%E8%AE%BE%E8%AE%A1%E5%88%86%E4%BA%AB%E3%80%91411ret6%E7%9A%84w5500%E9%87%87%E9%9B%86%E6%B8%A9%E5%BA%A6%E4%B8%8A%E4%BC%A0%E8%87%B3yeelink/md"
type: "UCC: User Created Content"
author: "湛只为无双"
author_url: "https://bbs.21ic.com/icview-884081-1-1.html"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "湛只为无双"
original_url: "https://bbs.21ic.com/icview-884081-1-1.html"
published: "2022-09-01"
language: "en"
hardware: ["WIZnet W5500"]
likes: 7
views: 1113
comments: 1
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# [STM32F4] 【Nucleo设计分享】411RET6的W5500采集温度上传至Yeelink

> A Cloud application example of W5500 using STM32F4 Nucleo. A good tutourial for W5500 users.

Original author: 湛只为无双 (source: https://bbs.21ic.com/icview-884081-1-1.html)

## Components

- **WIZnet W5500** x 1 ([docs](https://docs.wiznet.io/Product/Chip/Ethernet/W5500))

WIZnet parts: W5500 ([Datasheet](https://docs.wiznet.io/Product/Chip/Ethernet/W5500/datasheet?utm_source=maker&utm_medium=project&utm_campaign=w5500), [product hub](https://maker.wiznet.io/products/w5500/))

## Article

| \| W5500_上传温度至Yeelink 本次设计的目的是在之前的基础上将获取到的DS18B20温度值上传到Yeelink用于在线监测，通过Yeelink这个平台，可以很方便的在在线查看温度值，以及历史记录。同样，Yeelink平台的移动客户端也有很好的支持，这样当人们在出行的时候可以随时查看数据，实现了物联网的这样一种设计方案。 具体的操作如下： **一、硬件连接简述：** 本次设计中需要用到的硬件连接如下所示： PA5作为SPI1的时钟SCK，PA6作为SPI1的主机输入从机输出MISO，PA7作为SPI1的主机输出从机输入MOSI，然后PB5用作SD卡接口的片选信号（本次设计保留，不做使用），PB6用作W5500网络接口的片选。以上是本次设计中用于网络通信的硬件连接。 DS18B20使用了PB9作为信号线，用于获取温度值。另外使用了串口2作为本次设计的调试信息的输出，使用的端口为PA2（UART2_TX）和PA3（UART2_RX）。 （在下面可以添加上述硬件的连接图，可以使用Visio进行硬件连接图的设计，并添加至此。） **二、软件工作过程简述：** 首先对所需要用的端口和外设进行初始化，包括了GPIO端口，SPI1外设，UART串口。然后是对W5500进行设置和DS18B20的检测，对于W5500和DS18B20的具体操作在上一篇**中已经详细介绍了，在此就不做赘述，有需要的可以参考上一篇的帖子。 同样在初始阶段中需要对W5500中8个SOCKET的缓冲区进行初始化操作，等待PHY的连接状态打开，对W5500的MAC地址、本机IP、网关、子网掩码、DMS服务器和DHCP状态进行初始化，并设置好溢出时间值和最大重发次数（可不设置）。 根据Yeelink的API相关文档，将从DS18B20所获取到的温度值，转换为所需要发送的字符串数据，然后通过TCP的Client工作模式向Yeelink上传数据，上传数据成功后等待十秒，然后进行下一次数据的发送（Yeelink的相关平台要求了每次数据的发送间隔应该大于十秒，否则会收到错误信息）。 **三、Yeelink字符串的生成过程：** 本次设计中的此部分引用了EmbedNet论坛飞鸿踏雪网友的字符串生成方法，个人感觉方法不错，并且操作起来灵活多变，尤其适合于多个传感器数据需要提交的情况。 函数的声明如下： 复制 ```plaintext
char* Yeelink_PostString(const char *device_id,const char*sensors_id,float value);
``` 其中第一个输入形参为设备id的字符串指针，第二个输入形参为传感器id的字符串指针，最后一个输入形参为浮点型的传感器数据值，返回参数为生成后的字符串的指针，用于被调用。 具体的函数内容如下： 复制 ```plaintext
char* Yeelink_PostString(const char *device_id,const char *sensors_id,float value)

{

        char remote_server[] = "api.yeelink.net";

        char str_tmp[128] = {0};

        // Http内容，表单内容

        char http_content[32] = {0};

        

        //表头的内容

        sprintf(str_tmp,"/v1.0/device/%s/sensor/%s/datapoints",device_id,sensors_id);

        // 确定提交内容的值 例如 {"value":20。1}

        sprintf( http_content , "{\"value\":%.1f}" , value);

        // 确定 HTTP请求首部

        // 例如POST /v1.0/device/dbeacfc4943e593ebe2bcf46a684f794/1/1/datapoints/add HTTP/1.1\r\n

        sprintf( http_request , "POST %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: dbeacfc4943e593ebe2bcf46a684f794

        sprintf( str_tmp , "U-ApiKey:%s\r\n" , "dbeacfc4943e593ebe2bcf46a684f794");//需要替换为自己的APIKey

        strcat( http_request , str_tmp);

        //

        strcat( http_request , "Accept: */*\r\n");

        // 增加提交表单内容的长度 例如 Content-Length:12\r\n

        sprintf( str_tmp , "Content-Length:%d\r\n" ,strlen(http_content) );

        strcat( http_request , str_tmp);

        // 增加表单编码格式 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");

        // HTTP负载内容

        strcat( http_request , http_content);

        

        return http_request;//返回生成的所需要发送的字符串指针

}
``` 需要注意的是此函数中需要定义好自己的APIkey，这个的获取方法见Yeelink平台的入门指导教程，里面包含了怎样申请账号，怎样创建设备，创建传感器，以及查看APIkey的具体操作步骤。 对于上面的函数，调用的过程举例： pString=Yeelink_PostString("14597","24869",DS18B20_Get_Temp()); 最后生成的pString字符型指针所指向的内容如下（示例）： POST /v1.0/device/14597/sensor/24869/datapoints HTTP/1.1 Host:api.yeelink.net Accept:*/* U-ApiKey:dbeacfc4943e593ebe2bcf46a684f794 Content-Length: 14 Content-Type: application/x-www-form-urlencoded Connection: close {“value”:12.1} **四、与Yeelink进行数据交互：** 向Yeelink发送字符串采用的是TCP的Client方式，Yeelink作为一个服务器Service，当需要发送数据时就和服务器进行连接，发送完数据后服务器返回发送的结果，然后服务器主动断开连接。因此，通过对Yeelink.net进行Ping操作，获取对应的ip地址，如图一所示。 由图一可以得知，Yeelink的ip地址为42.96.64.52。 根据Yeelink的API相关文档可以得到TCP的Service服务器端口为80端口，所以设计中需要将之前生成的字符串通过TCP的Client方式向42.96.64.52:80端口发送过去，并对返回的数据进行分析，查看是否发送成功。 通过TCP的Client方式连接服务器的具体操作过程如下： 首先定义的一个变量Yeelink_Status用于指定当前设备的工作状态，可以取值如下面的宏定义： 复制 ```plaintext
#define Yeelink_STATUS_SEND 0     //初始状态用于初始化网络

#define Yeelink_STATUS_RECV 1     //当发送成功后进入此状态

#define Yeelink_STATUS_OVER 2     //发送成功后如果接收到数据进入此状态

#define Yeelink_POSTOK           3       //当接收的内容含有OK则为此状态

#define Yeelink_POSTERR        4       //当接收的内容含有ERROR进入此状态
``` 然后是通过Yeelink_SendTCPC这个函数来向服务器发送数据，接收服务器返回的数据，并解析数据。 函数声明为： 复制 ```plaintext
int32_t Yeelink_SendTCPC(uint8_t sn,char *pYeelinkStr,int16_t port);
``` 第一个输入形参sn为所使用的W5500的SOCKET编号，取值范围为0~7，第二个输入形参为所需要发送的字符串的指针，第三个输入形参为第一个参数中SOCKET所需要对应的端口号，返回值为发送过程中的相关错误信息。 当程序上电后用于指定设备当前工作状态的变量初始值为Yeelink_STATUS_SEND，然后此时需要进行的处理如下： 复制 ```plaintext
if(Yeelink_Status == Yeelink_STATUS_SEND)

{

        switch(getSn_SR(sn))//获取SOCKET的状态

        {

        case SOCK_CLOSED://SOCKET最初的状态为关闭 需要配置完成后进入SOCK_INIT

                printf(“%d:Start!\r\n”,sn);

                if((ret=socket(sn,Sn_MR_TCP,port,Sn_MR_ND)) != sn)

                        return ret;

                printf(“%d:Opened!\r\n”,sn);

                break;

        case SOCK_INIT://SOCKET进入初始化

                printf(“%d:Connect,port[%d]\r\n”,sn,port);

                if( (ret=connect(sn,yeelink_ip,80)) != SOCK_OK)

                        return ret;

                break;

        case SOCK_ESTABLISHED://SOCKET连接成功

                if(getSn_IR(sn))

                {

                        setSn_IR(sn,Sn_IR_CON);

                }

                

                ret = send(sn,(unsigned char *)pYeelinkStr,strlen(pYeelinkStr));//发送数据包

                if(ret != strlen(pYeelinkStr))//如果发送不成功

                {

                        printf(“%d:Socket Send Error\r\n”,sn);

                        close(sn);//SOCKET关闭 然后重新初始化

                        return ret;

                }

                else//如果发送成功了

                {

                        Yeelink_Status=Yeelink_STATUS_RECV;//进入下一个状态

                }//end of ret==strlen(pYeelinkStr)

                break;

        case SOCK_CLOSE_WAIT://SOCKET等待关闭

                printf(“%d,CloseWait\r\n”,sn);

                if( (ret=disconnect(sn)) != SOCK_OK ) return ret;

                printf(“%d,Closed\r\n”,sn);

                break;

        }

}
``` 上述程序中SOCKET的初始状态为SOCK_CLOSED，此时需要对SOCKET的工作模式进行配置，配置为TCP模式，端口号为函数给出的端口。如果配置成功，再次获取SOCKET状态将会变为SOCK_INIT初始化模式，在这个模式下就需要设定目标服务器的IP和端口号，并尝试连接，调用的函数为connect。如果连接成功，获取到的SOCKET状态将会是SOCK_ESTABLISHED，表明已经与目标服务器连接成功了，接着就是需要发送之前生成带有数据的字符串，调用的函数为send，并检查是否发送成功。如果发送失败，关闭SOCKET，进行重新连接并发送；如果发送成功，这时候用于指定当前工作状态的变量将会变为Yeelink_STATUS_RECV，进入到了接收服务器返回数据的状态。在接收状态下，循环检测SOCKET的接收状态寄存器，当有数据到来后，就调用recv函数读取SOCKET缓冲区中接收到的数据，并将用于指定当前状态的变量值设定为Yeelink_STATUS_OVER。在接收完成后，需要解析数据中是否带有OK字符，根据是否带有OK来指定当前状态变量为Yeelink_POSTOK还是Yeelink_POSTERR，如果为Yeelink_POSTERR会把接收到的数据打印出来，查看错误发送的原因，最后关闭SOCKET。 需要注意的是，如果按照以上的工作过程，在一切连接良好的过程中，工作起来是没有问题的，但是实际的工作网络中，或多或少的存在一些干扰，导致正常的工作流程被打断，这样就有可能导致系统一直处于某一种状态无法进行后续的工作，也就是导致了设备的死机。例如，由于网络干扰的原因，在接收服务器返回的数据的过程中，没能到达设备，那么设备将会一直处于接收数据的状态，一直循环等待数据的带来，这样很显然是不科学的。在实际测试的过程中，设备工作大约半个小时后就由于某种原因停止了数据的采集，因此，思考良久，添加了防止处于某一状态循环的程序。 添加的程序的本质在于如果连续处于某一状态超过100次后，每次大约10ms，总共一秒的时间，那么自动退出循环，并强制设置当前工作状态为Yeelink_STATUS_SEND，进行重新初始化。经过事实也证明了此种举措是很有必要的，在更改完程序后，进行连续工作，每天平均测试八个小时，连续两天的时间，并偶尔拔掉设备的网线进行人为破坏，发现设备均可正常工作，不出现死机的现象。 具体的程序如下： 复制 ```plaintext
while(1)

{

        retry=0;//retry的功能是为了防止持续停留在某一状态 假如停留的时间过长 则应当退出当前状态 重新进行初始化

        /*注：在起初是没有retry的相关代码的 但是发现当运行半个小时后就死机了 加上后测试了8个小时工作良好*/

        pString=Yeelink_PostString(“14597”,”24869”,DS18B20_Get_Temp());

        do

        {

                Yeelink_SendTCPC(0,pString,5000);

                delay_ms(10);

                retry++;

        }while( (Yeelink_GetStatus()<Yeelink_POSTOK) && (retry<100) );

        Yeelink_SetStatus(Yeelink_STATUS_SEND);

        delay_ms(10000);

}
``` \| \| --- \| 使用特权 [**评论**](https://bbs.21ic.com/forum.php?mod=misc&action=comment&tid=884081&pid=5210771&extra=page%3D1&page=1)[**回复**](https://bbs.21ic.com/forum.php?mod=post&action=reply&fid=49&tid=884081&reppost=5210771&extra=page%3D1&page=1)**赏** [点赞](https://bbs.21ic.com/plugin.php?id=ilike:send&ac=showpopup&tid=884081&pid=5210771&hash=6ac5efce) |
| --- |
|  |

| [**沙发**](https://bbs.21ic.com/forum.php?mod=redirect&goto=findpost&ptid=884081&pid=5210773) [![](https://uc2.21ic.com/avatar.php?uid=868550&size=middle)](https://bbs.21ic.com/u/%E6%B9%9B%E5%8F%AA%E4%B8%BA%E6%97%A0%E5%8F%8C)[**湛只为无双**](https://bbs.21ic.com/u/%E6%B9%9B%E5%8F%AA%E4%B8%BA%E6%97%A0%E5%8F%8C)\|![](https://bbs.21ic.com/static/image/common/online_member2.gif) ![](https://bbs.21ic.com/static/image/common/ico_lz.png) 楼主 \| **2015-2-27 16:26** \| [只看该作者](https://bbs.21ic.com/icview-884081-1-1.html?author=%E6%B9%9B%E5%8F%AA%E4%B8%BA%E6%97%A0%E5%8F%8C) \| *本帖最后由 湛只为无双 于 2015-2-27 16:43 编辑* **五、实验现象和数据说明：** 如图二所示，为上电后数据连接正常，串口向电脑打印出来的信息。由返回的信息可以知道在上电的过程中，首先是打印出配置好的MAC地址、本机IP、网关、子网掩码和DNS服务器，并标明网络状况良好。紧接着就是SOCKET0的开始、打开、连接，以及最后的响应OK。这就是一个完整的温度数据上传过程，剩下的就是循环进行SOCKET的开始、打开、连接和响应。 如果在上传的过程中拔掉网线，串口返回的信息如图三所示。可以看到，当断开网络后，对于SOCKET0而言，不断的开始、打开和连接，连接无效后再次进行开始、打开和连接，周而复始。 如果在断开网线后再次把网线插上，就可以看到服务器的响应是OK的，如图四所示。这样表明了数据的上传是完整的，重新恢复了原来的连接。 然后登陆Yeelink网页，查看上传的温度数据，在上传温度的过程中，同样用手给传感器进行加热，查看温度的变化，如图五和图六所示。 最后是连续时间测试，本次给大家分享一个下午的时间内，从两点到五点半的室内温度测试结果图，可以看出，室内的温度变化可真快呀。 \| \| --- \| : [1ping.png](https://bbs.21ic.com/forum.php?mod=attachment&aid=NDEwMTM1fDU1OTMzMmQ3fDE2NjIwMTUxOTJ8MHw4ODQwODE%3D&nothumb=yes) *(39.63 KB )* 图一通过ping来获取Yeelink的IP地址 ![图一通过ping来获取Yeelink的IP地址](https://bbs.21ic.com/data/attachment/forum/201502/27/164107c48xbx8dfbt4wtp6.png.thumb.jpg) : [2上电后串口返回的信息.png](https://bbs.21ic.com/forum.php?mod=attachment&aid=NDEwMTM3fDZiOTc0ZDNhfDE2NjIwMTUxOTJ8MHw4ODQwODE%3D&nothumb=yes) *(36.08 KB )* 图二 上电后串口返回的信息 ![图二 上电后串口返回的信息](https://bbs.21ic.com/data/attachment/forum/201502/27/164109ldoszwnzf3uknn6s.png.thumb.jpg) : [3突然拔掉网线后设备进行重连.png](https://bbs.21ic.com/forum.php?mod=attachment&aid=NDEwMTQxfDlhYjQxMjdifDE2NjIwMTUxOTJ8MHw4ODQwODE%3D&nothumb=yes) *(35.89 KB )* 图三 连接过程中拔去网线 ![图三 连接过程中拔去网线](https://bbs.21ic.com/data/attachment/forum/201502/27/164111muruewz7n9skiwa1.png.thumb.jpg) : [4插上网线后数据恢复上传.png](https://bbs.21ic.com/forum.php?mod=attachment&aid=NDEwMTQ1fDJiN2FhNDRjfDE2NjIwMTUxOTJ8MHw4ODQwODE%3D&nothumb=yes) *(36.11 KB )* 图四 重新插上网线后工作正常 ![图四 重新插上网线后工作正常](https://bbs.21ic.com/data/attachment/forum/201502/27/164112iaqi6oizo68jayia.png.thumb.jpg) : [5Yeelink上查看到的数据.png](https://bbs.21ic.com/forum.php?mod=attachment&aid=NDEwMTQ5fDg0ZDMwMGY4fDE2NjIwMTUxOTJ8MHw4ODQwODE%3D&nothumb=yes) *(27.86 KB )* 图五 Yeelink的温度显示一 ![图五 Yeelink的温度显示一](https://bbs.21ic.com/data/attachment/forum/201502/27/164114qvvjwsx7h3swvkzz.png.thumb.jpg) : [6Yeelink查看到的数据.png](https://bbs.21ic.com/forum.php?mod=attachment&aid=NDEwMTUzfDJkZjhjZTAyfDE2NjIwMTUxOTJ8MHw4ODQwODE%3D&nothumb=yes) *(28.76 KB )* 图六 Yeelink的温度显示二 ![图六 Yeelink的温度显示二](https://bbs.21ic.com/data/attachment/forum/201502/27/164116iffvpfvg1gxp0p8z.png.thumb.jpg) |
| --- |

---

Source: https://maker.wiznet.io/chen/projects/%5Bstm32f4%5D-%E3%80%90nucleo%E8%AE%BE%E8%AE%A1%E5%88%86%E4%BA%AB%E3%80%91411ret6%E7%9A%84w5500%E9%87%87%E9%9B%86%E6%B8%A9%E5%BA%A6%E4%B8%8A%E4%BC%A0%E8%87%B3yeelink/
