物联网实战之基于OneNET的智能农业系统

Noya ·
更新时间:2024-05-16
· 942 次阅读

想玩物联网的时候物色了很多国内的云平台,最后选了中移云去实现。其实阿里云华为云机智云都不错,机智云网页界面不行但是带APP,阿里云也有个贝壳物联APP功能更强大,华为云不太了解不过好像也还行。中移云感觉更加的纯粹,没有那么多花里胡哨的功能(其实是我用不到而已)。这里记录一下基于4个stm32+lora建立组网,上传数据到云平台这个简单的项目。

硬件环境

在这里插入图片描述

在这里插入图片描述

硬件环境即一个主控板(照片找不到了)+三个采集板,基于LORA构建组网,采集3个不同地点的环境数据,通过WIFI模块上发数据到云平台。外加一个OpenMV3做人脸识别。采集板挂载了温湿度+光照强度+LORA模块。

软件环境

IDE:Keil 5.15
OS:Ucos-III

ESP8266 WIFI模块基本知识详解

市面上的WIFI模块大多数都是乐鑫的,通过串口+AT指令,即可简单的进行操作。
硬件连接:
在这里插入图片描述

AT指令基本结构:
在这里插入图片描述
WIFI模块和服务器建立连接有两种方式:
在这里插入图片描述
TCP连接通信比较常用,再该模式下,相应的设备必须处于同一网络中,即处于同一个局域网中!!!AP模式下ESP8266做服务端时就是,手机和电脑必须连在ESP8266的WiFi,才能用网络调试助手等软件与ESP8266通信。

WIFI 模块的AT指令配置大家看官方文档即可,整体了来说还是比较简单的。WIFI一般有WIFI-AP模式,WIFI-STA模式,WIFI-AP兼STA模式三种,这里只说常用的前两种:
①、WIFI-AP模式:首先,我们可以把WIFI模块当成一个热点,开启热点以后别的设备可以连接这个热点,从而我们就可以向连接热点的设备
!!发送数据!!
ESP8266建立TCP服务器时,步骤为:
1.AT+CWMODE=2//配置ESP8266为AP模式,(路由器模式,发出WIFI)。
在这里插入图片描述
2.AT+RST // 重启模块使刚刚配置模式生效
3.AT+CWSAP=“wifi账号”,“WIFI密码”,1,4
在这里插入图片描述
4.AT+CIPMUX = 1 // 启动多连接
5.AT+CIPSERVER = 1,8080 // 模块开启服务器模式,端口号8080
这一步一定要保证设备连接到了ESP8266的WIFI,不然会连接失败
6.AT+CIPSEND=X,X // 第一个X是对应的ID号,第二个X是对应的字符个数,调用这个AT指令就可以向对应的客户端发送数据,注意对应的字符个数问题,如果个数少了就不发,个数多了就只发前对应的个数。
以上,便是ESP8266做服务器,PC端(移动端)做客户端的情况。

ESP8266做客户端,PC端(移动端)做服务器时(单路连接),具体的步骤简要为:
在这里插入图片描述完成连接后,调用AT+CIPSEND这个指令即可与服务器收发数据。这里我们只介绍单路连接,这种模式下是可以做到最多5路连接,感兴趣的话可以查一下相关资料。
在这里插入图片描述
②.WIFI-STA模式:这个模式与AP模式相反,是ESP8266去连接别的路由器(WIFI),只要保证与通信方处于同一局域网下即可,可以理解为一个基站去接收信号。
ESP8266做服务器,PC做客户端,具体步骤为:
在这里插入图片描述
到这里,我们发送AT+CWLAP指令可以查询周围的WIFI信号(去酒店之类的可以用这招预防那种WIFI摄像头,但是SD卡那种还是防不了的。。)
发送AT+CWJAP?可以查看WIFI是否真的连接成功。
4.AT+CIPMUX=1启动多连接
5.AT+CIPSERVER=1,8080 // 打开服务器端口为8080
6.AT+CIFSR // 看一下你模块现在分配的ip是多少,只要模块联网就会重新分配ip地址,这也是网络调试助手里面远程主机栏要填写的ip地址。
在这里插入图片描述

ESP8266做客户端,PC端(移动端)做服务器,具体步骤如下:
1.AT+CWMODE=1,设置为STA模式
2.AT+RST , 重启生效
3.AT+CWJAP=“WIFI账号”,“WIFI密码”
4.AT+CIPMUX=0 ,单连接
5.AT+CIPSTART=“TCP”,“192.168.1.101”,8080
下面用AT+CIPSEND发送数据即可

这次工程是把WIFI模块配成STA模式下的客户端,网站就当成服务器,方便操作。

OneNET建立自己的物联网应用

首先来个官网网址中移云网址,进去以后注册一个自己的账号
在这里插入图片描述
点击开发者中心,然后创建自己的产品。这里跟着官方文档走就好了,不多赘述创建产品
接入协议这里选择http协议,因为我们只需要POST上报数据,GET获得数据即可。有能力的话建议搞一下MQTT协议,近两年最火的物联网协议。然后建一个设备,这里跟着官方文档走就可以了。最后我们会得到这些必要信息:
在这里插入图片描述
这四个ID/key是上报数据必须的东西,要做好记录。至此平台端我们已经解决了大半。

收发协议

因为涉及了不同设备多个数据的采集,这里制定了一个简单的协议,只有一个帧头采集板发过来的帧头到主控板会被识别为ASCII码,所以主控接收从机帧头时一定要以ASCII码形式判断。其实出来实习以后,发现这些协议走16进制的话会更方便,便于不同平台之间交互。下面上代码:

u8 a = 0; u8 b = 0; u8 c = 0; u8 RX1_Buff[3]; u8 RX2_Buff[3]; u8 RX3_Buff[3]; int i = 1; int j = 1; int k = 1; char one = '1'; char two = '2'; char three = '3'; void USART2_IRQHandler(void) { u8 res; { res=USART_ReceiveData(USART2); //printf("%c",res); if(res == one) { a = 1; two = '7'; three = '8'; } if(a == 1) { switch(i) { case 1:i++;break; case 2:i++;RX1_Buff[0] = res;break; case 3:i++;RX1_Buff[1] = res;break; case 4:i++;RX1_Buff[2] = res;i=1;a=0;two = '2';three = '3';break; default:break; } } if(res == two) { b=1; one = '6'; three = '8'; } if(b == 1) { switch(j) { case 1:j++;break; case 2:j++;RX2_Buff[0]=res;;break; case 3:j++;RX2_Buff[1]=res;;break; case 4:j++;RX2_Buff[2]=res;j=1;b=0;one = '1';three = '3';break; default:break; } } if(res == three) { c = 1; one = '6'; two = '7'; } if(c == 1) { switch(k) { case 1:k++;break; case 2:k++;RX3_Buff[0]=res;break; case 3:k++;RX3_Buff[1]=res;break; case 4:k++;RX3_Buff[2]=res;k=1;c=0;one = '1';two = '2';break; default:break; } } LED0 = ~LED0; printf("从机1%d,%d,%d\r\n",RX1_Buff[0],RX1_Buff[1],RX1_Buff[2]); printf("从机2%d,%d,%d\r\n",RX2_Buff[0],RX2_Buff[1],RX2_Buff[2]); printf("从机3%d,%d,%d\r\n",RX3_Buff[0],RX3_Buff[1],RX3_Buff[2]); USART_ClearITPendingBit(USART2,USART_IT_RXNE); } }

其实思想就是每个从机都有自己的一个帧头,后面分别跟着温度湿度光照强度等环境数据。

与OneNET云平台的交互

这里我们把WIFI模块设置为STA工作模式,就是把WIFI模块连接我们手机的热点,然后与OenNET云平台建立TCP连接,开启透传模式。数据通过POST发送,打包成JSON格式。现在知道了官方有个cJSON,经过简单的移植就可以自动打包JSON数据,但是当时年少无知,就直接手打JSON格式就好了。datastreams这个数据后面跟着传感器的名称和数据流的名称即可。api-key从前面我们说的地方获取即可,Host则直接用这个网址就好,Content-Length的值一定要和datastreams后面的大小对应,不然会报格式错误。

#include "common.h" #include "stdlib.h" #include "string.h" #include "dht11.h" #include "adc.h" #include "lsens.h" #include "led.h" //ATK-ESP8266 WIFI STA测试 //用于测试TCP/UDP连接 //返回值:0,正常 // 其他,错误代码 extern u8 RX1_Buff[3]; extern u8 RX2_Buff[3]; extern u8 RX3_Buff[3]; u8 atk_8266_wifista_test(void) { u8 key; u8 timex=0; u8 b; char RX_Buff[200]; u8 strData[2]; u8 *p; u8 *q; u8 *m; u16 t=9999; //加速第一次获取链接状态 u8 res=0; u16 rlen=0; u8 constate=0; //连接状态 HC_Init();//LORA数据收发协议初始化 atk_8266_send_cmd("AT+CWMODE=1","OK",500); //设置WIFI STA模式 atk_8266_send_cmd("AT+RST","OK",200); //DHCP服务器关闭(仅AP模式有效) delay_ms(1000); //延时3S等待重启成功 delay_ms(1000); //设置连接到的WIFI网络名称/加密方式/密码,这几个参数需要根据自己的路由器设置进行修改!! //sprintf((char*)p,"AT+CWJAP=\"%s\",\"%s\"",wifista_ssid,wifista_password);//设置无线参数:ssid,密码 p = "AT+CWJAP=\"mayday123\",\"sunshine998\""; while(atk_8266_send_cmd(p,"WIFI GOT IP",3000)); //连接目标路由器,并且获得IP { atk_8266_send_cmd("AT+CIPMUX=0","OK",200); //0:单连接,1:多连接 q = "AT+CIPSTART=\"TCP\",\"183.230.40.33\",80"; //sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",%s",(u8*)ipbuf,(u8*)portnum); //配置目标TCP服务器 while(atk_8266_send_cmd(q,"OK",2000)); atk_8266_send_cmd("AT+CIPMODE=1","OK",2000); //传输模式为:透传 } USART3_RX_STA=0; atk_8266_quit_trans(); atk_8266_send_cmd("AT+CIPSEND","OK",200); //开始透传 while(1) { /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ ///***********************************发送第一从机温度数据**************************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:63\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"temp_num\",\"datapoints\":[{\"value\":%d}]}]}",RX1_Buff[0]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } ///******************************************************************************************/ delay_ms(500); ///***********************************发送第一从机湿度数据**************************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:63\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"humi_num\",\"datapoints\":[{\"value\":%d}]}]}",RX1_Buff[1]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } ///******************************************************************************************/ delay_ms(500); ///***********************************发送第一从机光照强度数据************************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:63\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"illu_num\",\"datapoints\":[{\"value\":%d}]}]}",RX1_Buff[2]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } ///******************************************************************************************/ delay_ms(500); /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /***********************************发送第二从机温度数据**************************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:64\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"temp1_num\",\"datapoints\":[{\"value\":%d}]}]}",RX2_Buff[0]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } /*********************************************************************************************/ delay_ms(500); ///***********************************发送第二从机湿度数据**************************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:64\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"humi1_num\",\"datapoints\":[{\"value\":%d}]}]}",RX2_Buff[1]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } ///******************************************************************************************/ delay_ms(500); ///***********************************发送第二从机光照强度数据************************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:64\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"illu1_num\",\"datapoints\":[{\"value\":%d}]}]}",RX2_Buff[2]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } /******************************************************************************************/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /***********************************发送第三从机温度数据**************************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:64\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"temp2_num\",\"datapoints\":[{\"value\":%d}]}]}",RX3_Buff[0]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } /*********************************************************************************************/ delay_ms(500); /***********************************发送第三从机湿度数据*************************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:64\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"humi2_num\",\"datapoints\":[{\"value\":%d}]}]}",RX3_Buff[1]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } ///******************************************************************************************/ delay_ms(500); ///***********************************发送第三从机光照强度数据*********************************/ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:64\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"illu2_num\",\"datapoints\":[{\"value\":%d}]}]}",RX3_Buff[2]); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } /******************************************************************************************/ delay_ms(500); if(LED2 == 1) { sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:63\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"open_see\",\"datapoints\":[{\"value\":11}]}]}"); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } }else{ sprintf(RX_Buff,"POST /devices/526305478/datapoints HTTP/1.1\r\n\ api-key:AXFPHI6MWoj6oTvz6MQVyEGANR0=\r\n\ Host:api.heclouds.com\r\n\ Content-Length:63\r\n\ \r\n\ {\"datastreams\":[{\"id\":\"open_see\",\"datapoints\":[{\"value\":22}]}]}"); u3_printf("%s",RX_Buff); // delay_ms(500); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } } /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ /*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/ atk_8266_at_response(1); for(b = 0;b < sizeof(RX_Buff);b++) { RX_Buff[b] = 0 ; } } return res; }

AT指令的发送直接用了原子的程序,在串口中断里根据协议接收数据然后上发到云平台即可。简要来说就是:
通过将WIFI连接上网络,解析出OneNET云平台的IP和端口号,在云平台新建设备,每个设备会分配一个独有的序列号(用于WIFI与设备连接),然后通过AT指令,将WIFI模块连接上云平台,此时用WIFI发送在云平台新建好的设备的序列号,即可连接设备,与设备进行通信。然后定时发送从采集板收到的数据,当数据超过云平台的报警值之后,云平台会自动报警,并将报警信息通过用户绑定邮箱发送给用户,实现实时监测的效果。

把每个步骤都打印出来:
在这里插入图片描述

当执行到指令AT+CIPSEND时,将数据打包成JSON格式发送即可。

反馈的信息为:
在这里插入图片描述
这个项目比较简单不需要解析反馈的JSON格式的数据,当error后面为0时,则代表数据上传正确。

下面介绍两个比较重要的点。
如何截取整段反馈的JSON报文

//串口接收缓存区 u8 USART3_RX_BUF[USART3_MAX_RECV_LEN]; //接收缓冲,最大USART3_MAX_RECV_LEN个字节. u8 USART3_TX_BUF[USART3_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节 //通过判断接收连续2个字符之间的时间差不大于10ms来决定是不是一次连续的数据. //如果2个字符接收间隔超过10ms,则认为不是1次连续数据.也就是超过10ms没有接收到 //任何数据,则表示此次接收完毕. //接收到的数据状态 //[15]:0,没有接收到数据;1,接收到了一批数据. //[14:0]:接收到的数据长度 vu16 USART3_RX_STA=0; void USART3_IRQHandler(void) { u8 res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收到数据 { res =USART_ReceiveData(USART3); if((USART3_RX_STA&(1<<15))==0)//接收完的一批数据,还没有被处理,则不再接收其他数据 { if(USART3_RX_STA<USART3_MAX_RECV_LEN) //还可以接收数据 { TIM_SetCounter(TIM7,0);//计数器清空 //计数器清空 if(USART3_RX_STA==0) //使能定时器7的中断 { TIM_Cmd(TIM7,ENABLE);//使能定时器7 } USART3_RX_BUF[USART3_RX_STA++]=res; //记录接收到的值 }else { USART3_RX_STA|=1<<15; //强制标记接收完成 } } } } //初始化IO 串口3 //pclk1:PCLK1时钟频率(Mhz) //bound:波特率 void usart3_init(u32 bound) { NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //串口3时钟使能 USART_DeInit(USART3); //复位串口3 //USART3_TX PB10 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB10 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB10 //USART3_RX PB11 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化PB11 USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART3, &USART_InitStructure); //初始化串口 3 USART_Cmd(USART3, ENABLE); //使能串口 //使能接收中断 USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断 //设置中断优先级 NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 TIM7_Int_Init(10000-1,7200-1); //100ms中断 USART3_RX_STA=0; //清零 TIM_Cmd(TIM7,DISABLE); //关闭定时器7 }

这里也是用原子的程序,其实思想很简单,第一次进入串口中断时打开定时器,一定时间(10ms)没有接收到数据就判定为一帧数据接收完毕。其实如果现在做这个的话直接用IDEL更方便,不需要这么麻烦。

串口3的重定向

//va_list 是C语言定义的宏,解决变量参数的一个宏,可以传入一个参数或多个参数,且对类型无要求,用法: //首先在函数中,定义一个具有va_list类型的参数ap //然后在va_start宏初始化变量刚定义的va_list类型的变量,执行:ap = (va_list)&fmt + _INTSIZEOF(fmt),此时ap指向第一个可变参数在堆栈的地址。 //函数声明:int vsprintf(char *str, const char *format, va_list arg) //str -- str指向一个字符数组的指针,该数组存储了C字符串。 //format -- 这是字符串,包含了要被写入到字符串str的文本。它可以包含嵌入的format标签,format标签可被随后的附加参数中指定的值替换 //并按需求进行格式化。format标签属性是%[flags][width][.precision][length]specifier。 //va_end(ap)的作用是清空va_list ap。 //下面是事例,一看就懂。 void u3_printf(char* fmt,...) { u16 i,j; va_list ap; va_start(ap,fmt); vsprintf((char*)USART3_TX_BUF,fmt,ap); va_end(ap); i=strlen((const char*)USART3_TX_BUF); //此次发送数据的长度 for(j=0;j<i;j++) //循环发送数据 { while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); //循环发送,直到发送完毕 USART_SendData(USART3,USART3_TX_BUF[j]); } }

这个其实没啥讲的,用的东西是非开源的,只要直到怎么用就好了,注释的很清楚。这样做就可以把串口3以printf的形式调用,非常方便。

效果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在我们的云平台是可以搭建界面的,这里显示了3地温湿度,光照强度的曲线比较,并且也可以单独显示某一个地点的环境数据,并且还有异常报警机制,就是发邮箱了。。


作者:奔驰的战猪



onenet 实战 农业 物联网 系统

需要 登录 后方可回复, 如果你还没有账号请 注册新账号