打印
[STM32F4]

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

[复制链接]
3367|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
湛只为无双|  楼主 | 2015-2-27 16:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 湛只为无双 于 2015-2-27 16:37 编辑

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论坛飞鸿踏雪网友的字符串生成方法,个人感觉方法不错,并且操作起来灵活多变,尤其适合于多个传感器数据需要提交的情况。
函数的声明如下:
char* Yeelink_PostString(const char *device_id,const char*sensors_id,float value);
其中第一个输入形参为设备id的字符串指针,第二个输入形参为传感器id的字符串指针,最后一个输入形参为浮点型的传感器数据值,返回参数为生成后的字符串的指针,用于被调用。
具体的函数内容如下:
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发送字符串采用的是TCPClient方式,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用于指定当前设备的工作状态,可以取值如下面的宏定义:
#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这个函数来向服务器发送数据,接收服务器返回的数据,并解析数据。
函数声明为:
int32_t Yeelink_SendTCPC(uint8_t sn,char *pYeelinkStr,int16_t port);
第一个输入形参sn为所使用的W5500的SOCKET编号,取值范围为0~7,第二个输入形参为所需要发送的字符串的指针,第三个输入形参为第一个参数中SOCKET所需要对应的端口号,返回值为发送过程中的相关错误信息。
当程序上电后用于指定设备当前工作状态的变量初始值为Yeelink_STATUS_SEND,然后此时需要进行的处理如下:
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,进行重新初始化。经过事实也证明了此种举措是很有必要的,在更改完程序后,进行连续工作,每天平均测试八个小时,连续两天的时间,并偶尔拔掉设备的网线进行人为破坏,发现设备均可正常工作,不出现死机的现象。
具体的程序如下:
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);
}

沙发
湛只为无双|  楼主 | 2015-2-27 16:26 | 只看该作者
本帖最后由 湛只为无双 于 2015-2-27 16:43 编辑

五、实验现象和数据说明:
如图二所示,为上电后数据连接正常,串口向电脑打印出来的信息。由返回的信息可以知道在上电的过程中,首先是打印出配置好的MAC地址、本机IP、网关、子网掩码和DNS服务器,并标明网络状况良好。紧接着就是SOCKET0的开始、打开、连接,以及最后的响应OK。这就是一个完整的温度数据上传过程,剩下的就是循环进行SOCKET的开始、打开、连接和响应。
如果在上传的过程中拔掉网线,串口返回的信息如图三所示。可以看到,当断开网络后,对于SOCKET0而言,不断的开始、打开和连接,连接无效后再次进行开始、打开和连接,周而复始。
如果在断开网线后再次把网线插上,就可以看到服务器的响应是OK的,如图四所示。这样表明了数据的上传是完整的,重新恢复了原来的连接。
然后登陆Yeelink网页,查看上传的温度数据,在上传温度的过程中,同样用手给传感器进行加热,查看温度的变化,如图五和图六所示。
最后是连续时间测试,本次给大家分享一个下午的时间内,从两点到五点半的室内温度测试结果图,可以看出,室内的温度变化可真快呀。

1ping.png (39.63 KB )

图一通过ping来获取Yeelink的IP地址

图一通过ping来获取Yeelink的IP地址

2上电后串口返回的信息.png (36.08 KB )

图二 上电后串口返回的信息

图二 上电后串口返回的信息

3突然拔掉网线后设备进行重连.png (35.89 KB )

图三 连接过程中拔去网线

图三 连接过程中拔去网线

4插上网线后数据恢复上传.png (36.11 KB )

图四 重新插上网线后工作正常

图四 重新插上网线后工作正常

5Yeelink上查看到的数据.png (27.86 KB )

图五 Yeelink的温度显示一

图五 Yeelink的温度显示一

6Yeelink查看到的数据.png (28.76 KB )

图六 Yeelink的温度显示二

图六 Yeelink的温度显示二

使用特权

评论回复
板凳
湛只为无双|  楼主 | 2015-2-27 16:26 | 只看该作者
本帖最后由 湛只为无双 于 2015-2-27 16:59 编辑

老规矩这一层上传相关的源代码和资料
以及实物图

1.jpg (71.3 KB )

1.jpg

2.jpg (75.97 KB )

2.jpg

3.jpg (65.14 KB )

3.jpg

4.jpg (71.5 KB )

4.jpg

5.jpg (76.89 KB )

5.jpg

6.jpg (67.27 KB )

6.jpg

Nucleo411_W5500_TCPC_Yeelink.zip

506.18 KB

源代码

使用特权

评论回复
地板
湛只为无双|  楼主 | 2015-2-27 16:26 | 只看该作者
本帖最后由 湛只为无双 于 2015-2-27 16:53 编辑

这一层上传室内温度连续变化图,供希望看的网友,以压缩包的形式,以免手机党费流量。

pic.zip

230.02 KB

某天下午的室内温度变化

使用特权

评论回复
5
21ic-09| | 2015-2-27 18:26 | 只看该作者
:handshake

使用特权

评论回复
6
湛只为无双|  楼主 | 2015-2-27 22:30 | 只看该作者

使用特权

评论回复
7
zh113214| | 2015-2-28 15:17 | 只看该作者
这个看上去也得用到传感器吧

使用特权

评论回复
8
小浣熊| | 2015-2-28 15:46 | 只看该作者
不错,我也想知道如何设计。。。

使用特权

评论回复
9
湛只为无双|  楼主 | 2015-2-28 15:49 | 只看该作者
zh113214 发表于 2015-2-28 15:17
这个看上去也得用到传感器吧

是的,加了个温度传感器,你可以通过实物图看到,上面还有个小东西呢!

使用特权

评论回复
10
湛只为无双|  楼主 | 2015-2-28 15:50 | 只看该作者
小浣熊 发表于 2015-2-28 15:46
不错,我也想知道如何设计。。。

只要走了硬件就可以做出来的,硬件怎么连上面已经给出来了,然后软件代码也公布出来了,你可以照着做就是了。

使用特权

评论回复
11
zh113214| | 2015-2-28 21:44 | 只看该作者
湛只为无双 发表于 2015-2-28 15:49
是的,加了个温度传感器,你可以通过实物图看到,上面还有个小东西呢! ...

恩 和我想的差不多

使用特权

评论回复
12
zh113214| | 2015-3-5 21:11 | 只看该作者
湛只为无双 发表于 2015-2-28 15:49
是的,加了个温度传感器,你可以通过实物图看到,上面还有个小东西呢! ...

恩 是的啊

使用特权

评论回复
13
meselfly| | 2015-3-22 17:03 | 只看该作者
请问,设备id 和传感器id的实参在哪里

使用特权

评论回复
14
湛只为无双|  楼主 | 2015-3-22 19:54 | 只看该作者
你可以看下工程文件,里面有主函数,这个是在主函数里面被调用的。

使用特权

评论回复
15
沉默胜过白金| | 2015-4-5 15:49 | 只看该作者
好帖子,借鉴下。

使用特权

评论回复
16
ljl342301| | 2015-5-25 21:45 | 只看该作者
最近正需要,非常感谢

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

171

帖子

9

粉丝