STM32第十七课:连接云平台进行数据传输

目录

  • 需求
  • 一、云平台项目创建
  • 二、代码编写
    • 1.导入MQTT包
    • 2.连接阿里云
    • 3.发布数据
  • 三、关键代码
  • 总结


需求

1.通过生活物联网平台设计一个空气质量检测仪app。
2.连接阿里云平台将硬件数据传输到云端,使手机端能够实时收到。
在这里插入图片描述


一、云平台项目创建

先进入阿里云生活服务平台创建一个新项目
在这里插入图片描述
接下来根据步骤一步一步做就行在这里插入图片描述
在定义功能时,记得进行使变量类型和之前代码中定义类型保持一致
标识符要尽可能简洁明了,后续代码中要一一对应。
在这里插入图片描述

最后点击发布完,扫描二维码下载一个app就能看到设备了。(云智能)

二、代码编写

1.导入MQTT包

本次数据通信模式为MQTT协议,需要按照要求拼接指令。
该指令拼接有大佬在github上发布过做好的函数,所以我们先把人家写好的包加入进来,方便我们后续的快速开发。githubMQTT协议包
在这里插入图片描述

在这里插入图片描述
要使用只需把src里的所有文件加入到工程中即可。

2.连接阿里云

连接阿里云就是先拼接报文,再将拼接好的数组通过wifi模块发送给云端阿里云。
整个包的头文件:#include "MQTTPacket.h"
拼接连接报文函数:MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)。
参数1:存放连接报文的数组地址。
参数2:存放连接报文数组的大小。
参数3:MQTTPacket_connectData options结构体的数组。
主要就是填一下clientID,password和username。
该部分均可以在阿里云上获取。
返回值:连接报文的长度。

代码如下:

//发送连接报文,链接阿里云
void Ali_SendConnect()
{
	uint8_t buf[300]={0};//存放连接报文
	uint16_t buflen=0;//报文长度
	MQTTPacket_connectData options = MQTTPacket_connectData_initializer;
	options.clientID.cstring = clientId;
	options.password.cstring = passwd;
	options.username.cstring = Username;
	
	//拼接连接报文,返回值是;连接报文的长度
	buflen = MQTTSerialize_connect(buf,300,&options);
	if(buflen==0)
	{
		printf("连接报文拼接失败\r\n");
		return ;
	}
	printf("连接报文拼接成功\r\n");
		//发送连接
	U3_Sendarr(buf,buflen);	
}

3.发布数据

发布数据就是将数据传输到云端。
主要就是用到MQTTSerialize_publish()函数

参数1:buf
类型:unsigned char*
描述:指向存储序列化后消息的缓冲区的指针。调用函数时,需要提供足够大小的缓冲区来存储序列化后的消息。

参数2:buflen
类型:int
描述:指定缓冲区的大小,即buf指向的内存块的最大长度。在调用函数之前,需要确保该缓冲区足够大,以防止消息序列化时溢出。

参数3:dup
类型:unsigned char
描述:指示消息是否是一个重复发布的标志。
值:0表示这是第一次发布,1表示这是一个重复发布。

参数4:qos
类型:int
描述:指定发布消息的服务质量等级(QoS级别)。
值:可以是0、1或2,分别代表不同的QoS级别,详情如下:
0:最多一次(At most once)
1:至少一次(At least once)
2:恰好一次(Exactly once)

参数5:retained
类型:unsigned char
描述:指示消息是否应保留在代理(broker)上。
值:0表示不保留,1表示保留。

参数6:packetid
类型:unsigned short
描述:消息标识符(Packet Identifier),用于标识QoS级别为1或2的消息。
值:如果QoS为0,则忽略该字段;如果QoS为1或2,则这个字段用来标识消息。

参数7:topicName
类型:char*
描述:指向发布主题名的C字符串指针。
注意:topicName必须在调用MQTTSerialize_publish函数时已经设置好。

参数8:payload
类型:unsigned char*
描述:指向要发布的消息内容的字节流的指针。
注意:payload必须在调用MQTTSerialize_publish函数时已经设置好。

参数9:payloadlen
类型:int
描述:指定payload的长度,即消息内容的字节数。

注意:payloadlen必须在调用MQTTSerialize_publish函数时已经设置好,并且与实际消息内容长度相匹配。

void MQTT_Send_Publish()
{
		char baowen[900]={0};
		int baowenlen;
		MQTTString topicName;
		topicName.cstring =TOPICNAME;
		char Load_Massage[500]={0};
		sprintf(Load_Massage,"{\
		\"method\":\"thing.event.property.post\",\
   		\"id\":\"1820044024\",\
    	\"params\":{\
       		\"TVOC\":%.2f,\
	    	\"HCHO\":%.2f,\
			\"co2\":%.2f,\
			\"Humidity\":%.2f,\
			\"temperature\":%.2f,\
			\"Smoke\":%d,\
        	\"lightp\":1\
   		},\
   		\"version\":\"1.0.0\"\
		}",voc,ch2o,co2,hum,tem,adcData.mq2);
		baowenlen=MQTTSerialize_publish(baowen,900,0,0,0,0,topicName,Load_Massage,strlen(Load_Massage));
		U3_Sendarr(baowen,baowenlen);
}

payload格式:
在这里插入图片描述
版本和id无所谓,主要就是method和参数。

三、关键代码

main.c

#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"
#include "delay.h"
#include "string.h"
#include "pwm.h"
#include "adc.h"
#include "su03t.h"
#include "dht11.h"
#include "kqm.h"
#include "key.h"
#include "RTC.h"
#include "bsp_lcd.h"
#include "wifi.h"
#include "aliot.h"
uint8_t Send_wifidata[102];
char D_wen[20];
char D_shi[20];
char D_time[20];

extern float voc;
extern float ch2o;
extern float co2;
extern float hum;
extern float tem;
extern ADCARR adcData;
extern const unsigned char gImage_hengliu[153600];
uint8_t key3flag,cntt;
uint32_t sec=0;

int main()
{
	  NVIC_SetPriorityGrouping(5);//两位抢占两位次级
      Usart1_Config(); 
	  SysTick_Config(72000);
	  Esp8266_Config();
	  strcpy((char*)Send_wifidata, "hello world");
	  Kqm_U4Config();
	  Su03t_U5Config();
	  DHT11_Config();	 
   	  Adc_Config();
	  RTC_Configuration();
	  Wifi_ConnectIP();
	  Ali_SendConnect();
    while(1)
    {		 
		if(ledcnt[0]>=ledcnt[1]){//过去500ms
			ledcnt[0]=0;
	  	Get_Smoke_Light_MidValue();//烟雾光照中位数
			DHT11_ReadData();//温湿度
			KQM_DealData();//空气质量
			Get_Smoke_Light_MidValue();
			Delay_ms(5000);
			MQTT_Send_Publish();
		}
    }
		return 0;
}


aliot.c

#include "aliot.h"


//发送连接报文,链接阿里云
void Ali_SendConnect()
{
	uint8_t buf[300]={0};//存放连接报文
	uint16_t buflen=0;//报文长度
	MQTTPacket_connectData options = MQTTPacket_connectData_initializer;
	options.clientID.cstring = clientId;
	options.password.cstring = passwd;
	options.username.cstring = Username;
	
	//拼接连接报文,返回值是;连接报文的长度
	buflen = MQTTSerialize_connect(buf,300,&options);
	if(buflen==0)
	{
		printf("连接报文拼接失败\r\n");
		return ;
	}
	printf("连接报文拼接成功\r\n");
		//发送连接
	U3_Sendarr(buf,buflen);	
}

void MQTT_Send_Publish()
{
		char baowen[900]={0};
		int baowenlen;
		MQTTString topicName;
		topicName.cstring =TOPICNAME;
		char Load_Massage[500]={0};
		sprintf(Load_Massage,"{\
		\"method\":\"thing.event.property.post\",\
    \"id\":\"1820044024\",\
    \"params\":{\
        \"TVOC\":%.2f,\
	    	\"HCHO\":%.2f,\
				\"co2\":%.2f,\
				\"Humidity\":%.2f,\
				\"temperature\":%.2f,\
				\"Smoke\":%d,\
        \"lightp\":1\
    },\
    \"version\":\"1.0.0\"\
		}",voc,ch2o,co2,hum,tem,adcData.mq2);
	baowenlen=MQTTSerialize_publish(baowen,900,0,0,0,0,topicName,Load_Massage,strlen(Load_Massage));
	U3_Sendarr(baowen,baowenlen);
}

aliot.h

#ifndef _ALIOT_H_
#define _ALIOT_H_
#include "stm32f10x.h"
#include "stdio.h"
#include "wifi.h"
#include "MQTTPacket.h"
#include "string.h"
#include "su03t.h"
#include "adc.h"

extern float voc;
extern float ch2o;
extern float co2;
extern float hum;
extern float tem;
extern ADCARR adcData;

void MQTT_Send_Publish();
void Ali_SendConnect();
#define clientId "a1QAarSERXA.20240704zzz|securemode=2,signmethod=hmacsha256,timestamp=1720146161015|"
#define Username "20240704zzz&a1QAarSERXA"
#define passwd  "8b5d1527f058aeddf2b9606bd13bf68b425fc658fba79d1f8064fddf455ca368"
#define IP "a1QAarSERXA.iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define port "1883"
#define TOPICNAME "/sys/a1QAarSERXA/20240704zzz/thing/event/property/post"

#endif
		

wifi.c

#include "wifi.h"

WIFIDATA wifidata={0};

//配置串口3  8数据位,0校验位,1停止位,波特率115200
//PB10(TX) PB11(RX)
void Esp8266_Config()
{
	 //开时钟:GPIOB,USART3
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
	  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
	
	  //配置对应的IO口 PB10(tx):复用推挽 PB11(RX):浮空输入
	  GPIO_InitTypeDef GPIO_InitStruct = {0};
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOB,&GPIO_InitStruct);
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
		GPIO_Init(GPIOB,&GPIO_InitStruct);
		//PE6
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOE,&GPIO_InitStruct);
		
		
	  //配置串口3  8数据位,0校验位,1停止位,波特率115200
		USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转
		USART_InitStruct.USART_BaudRate = 115200;//波特率
		USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开
		USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//打开发送和接收
		USART_InitStruct.USART_Parity = USART_Parity_No;
		USART_InitStruct.USART_StopBits = USART_StopBits_1;
		USART_InitStruct.USART_WordLength = USART_WordLength_8b;
		USART_Init(USART3,&USART_InitStruct);
		USART_Cmd(USART3,ENABLE);
    //配置串口3的中断
		USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);//USART1->CR1 |= 0x1<<5;//使能串口1的接收非空中断
		NVIC_SetPriority(USART3_IRQn,7);//设置优先级0~15
		NVIC_EnableIRQ(USART3_IRQn);//使能中断通道
		GPIO_SetBits(GPIOE,GPIO_Pin_6);
	  Delay_nms(500);
}

void USART3_IRQHandler(void)
{
	
	uint8_t data=0;
	if((USART3->SR&0x1<<5)!=0)
	{//执行该中断函数的原因有很多,所以判断一下是不是接收导致的
		data = USART_ReceiveData(USART3);//读操作,同时也是清空中断标志位
		wifidata.recvbuf[wifidata.recvcnt] = data;
		wifidata.recvcnt++;
		wifidata.recvcnt%=1024;
		USART_SendData(USART1, data); 
	}
}

//串口3发送单字节函数
void Usart3Senddata(uint8_t data)
{
	//等待发送完成
	while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==0);
	//如果上次发送完成,就发送
	USART_SendData(USART3,data);
}

//串口3发送数组函数
void U3_Sendarr(uint8_t * data,uint32_t len)
{
	uint32_t i=0;
	for(i=0;i<len;i++){
		Usart3Senddata(*data);
		data++;
	}
}

void U3_SendStr(uint8_t * data)
{
	uint32_t i=0;
	while(*data!='\0')
	{
		Usart3Senddata(*data);
		data++;
	}
}

uint8_t Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout)
{
	uint32_t timecnt=0;
	memset(&wifidata,0,sizeof(wifidata));
	U3_SendStr((uint8_t *)cmd);
	while(strstr((char *)wifidata.recvbuf,recv)==NULL){
	timecnt++;
	Delay_nms(1);
		if(timecnt>=timeout){
		printf("发送超时失败%s",cmd);
		return 1;
	 }
	}
	printf(" 发送成功 ");
	return 0;
}

uint8_t Wifi_ConnectIP(void)
{
	
	uint8_t buf[100] = {0};
	
	if(Wifi_Send_Cmd("AT\r\n","OK",1000) != 0){//测试
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CWMODE=1\r\n","OK",2000) != 0){//设置为STA
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CWJAP=\"LEGION-5169\",\"88888888\"\r\n","OK",10000)!= 0){//连接热点
		return 1;
	}
	sprintf((char*)buf,"AT+CIPSTART=\"TCP\",\"%s\",%s\r\n",IP,port);
	if(Wifi_Send_Cmd((char*)buf,"OK",10000)!= 0){//连接服务器
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CIPMODE=1\r\n","OK",1000)!= 0){//开启透传
		return 1;
	}
	if(Wifi_Send_Cmd("AT+CIPSEND\r\n","OK",1000)!= 0){//启动发送功能
		return 1;
	}
	return 0;	
}

wifi.h

#ifndef _WIFI_H_
#define _WIFI_H_
#include "stm32f10x.h"
#include "aliot.h"
#include "delay.h"
#include "stdio.h"
#include "string.h"
typedef struct{
	uint8_t recvbuf[1024];
	uint16_t recvcnt;
}WIFIDATA;

void Esp8266_Config();
void U3_SendStr(uint8_t * data);
uint8_t Wifi_Send_Cmd(char * cmd,char * recv,uint32_t timeout);
uint8_t Wifi_ConnectIP(void);
void U3_SendStr(uint8_t * data);
void U3_Sendarr(uint8_t * data,uint32_t len);
#endif
		

总结

1.当代码将报文发送给串口3时,由于串口3连接的是wifi模块,此时就相当于将报文通过wifi模块传送到云端。
2.将数据传输到串口1时,由于串口1连接的是电脑上,所以相当于将数据打印。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/782072.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

cs231n 作业3

使用普通RNN进行图像标注 单个RNN神经元行为 前向传播&#xff1a; 反向传播&#xff1a; def rnn_step_backward(dnext_h, cache):dx, dprev_h, dWx, dWh, db None, None, None, None, Nonex, Wx, Wh, prev_h, next_h cachedtanh 1 - next_h**2dx (dnext_h*dtanh).dot(…

打造属于你的私人云盘:在 OrangePi AIpro 上搭建个人云盘

随着数字化时代的到来&#xff0c;数据的存储和管理变得愈发重要。相比于公共云存储服务&#xff0c;搭建一个属于自己的个人云盘不仅能够更好地保护隐私&#xff0c;还可以更灵活地管理数据。 近期刚好收到了一个 香橙派 AIpro 的开发板&#xff0c;借此机会用来搭建一个属于…

人工智能项目论文复现

文章目录 &#xff08;一&#xff09;技术学习任务Ⅰ、机器学习之聚类1、基本介绍概念2、聚类分析基本介绍3、K均值聚类4、K近邻分类模型(KNN)5、均值漂移聚类6、代码实现7、上述三种算法总结 Ⅱ、机器学习其他常用技术1、决策树基本知识2、异常检测概念3、主成分分析4、决策树…

落日余晖映晚霞

落日余晖映晚霞&#xff0c;立于海滨&#xff0c;望夕阳余晖洒于波光粼粼之上&#xff0c;金光跳跃&#xff0c;若繁星闪烁&#xff0c;耀人心目。 海风轻拂&#xff0c;心境宁静&#xff0c;凡尘俗务皆于此刹那消散&#xff0c;思绪万干&#xff0c;或忆往昔点滴&#xff0c;或…

SQL 对一个经常有数据更新和删除操作的表,怎样优化以减少磁盘空间的占用?

文章目录 一、定期清理不再需要的数据二、使用合适的数据类型三、压缩数据四、删除重复数据五、分区表六、索引优化七、碎片整理八、归档历史数据九、监控和评估 在数据库管理中&#xff0c;当面对一个经常进行数据更新和删除操作的表时&#xff0c;磁盘空间的有效利用是一个重…

PIP换源的全面指南

##概述 在Python的世界里&#xff0c;pip是不可或缺的包管理工具&#xff0c;它帮助开发者安装和管理Python软件包。然而&#xff0c;由于网络条件或服务器位置等因素&#xff0c;直接使用默认的pip源有时会遇到下载速度慢或者连接不稳定的问题。这时&#xff0c;更换pip源到一…

赋值运算符重载和const成员函数和 const函数

文章目录 1.运算符重载(1)(2)运算符重载的语法&#xff1a;(3)运算符重载的注意事项&#xff1a;(4)前置和后置重载区别 2.const成员函数3.取地址及const取地址操作符重载4.总结 1.运算符重载 (1) 我们知道内置类型(整形&#xff0c;字符型&#xff0c;浮点型…)可以进行一系…

利用docker搭建漏洞环境,使用SSRF+Redis写入centos以及ubuntu的公钥,实现免密登录

一、实验环境 kali:在kali中搭建docker容器环境&#xff0c;这里我主要是使用第一个&#xff1b; redis作为一种数据库&#xff0c;它可以将数据写入内存中去&#xff0c;我们通过利用ssrf请求&#xff0c;实现服务器对自己的公钥写入&#xff0c;从而实验免密登录&#xff1b;…

异步调用 - 初识

目录 1、引入 2、同步调用 2.1、例子&#xff1a;支付功能 2.2、同步调用的好处 2.3、同步调用的缺点 3、异步调用 3.1、异步调用的方式 3.2、异步调用的优势 3.3、异步调用的缺点 3.4、什么场景下使用异步调用 3.5、MQ技术选型 1、引入 为什么想要异步通信呢&…

LeetCode 算法:二叉树中的最大路径和 c++

原题链接&#x1f517;&#xff1a;二叉树中的最大路径和 难度&#xff1a;困难⭐️⭐️⭐️ 题目 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;…

Spring cloud 中使用 OpenFeign:让 http 调用更优雅

注意&#xff1a;本文演示所使用的 Spring Cloud、Spring Cloud Alibaba 的版本分为为 2023.0.0 和 2023.0.1.0。不兼容的版本可能会导致配置不生效等问题。 1、什么是 OpenFeign Feign 是一个声明式的 Web service 客户端。 它使编写 Web service 客户端更加容易。只需使用 F…

[数据结构] --- 线性数据结构(数组/链表/栈/队列)

1 线性结构和非线性结构的理解 1.1 线性结构 线性结构是什么&#xff1f; 数据结构中线性结构指的是数据元素之间存在着“一对一”的线性关系的数据结构。线性结构是一个有序数据元素的集合。 线性结构特点&#xff1a; 线性结构有唯一的首元素&#xff08;第一个元素&#…

13.SQL注入-宽字节

SQL注入-宽字节 含义&#xff1a; MySQL是用的PHP语言&#xff0c;然后PHP有addslashes()等函数&#xff0c;这类函数会自动过滤 ’ ‘’ null 等这些敏感字符&#xff0c;将它们转义成’ ‘’ \null&#xff1b;然后宽字节字符集比如GBK它会自动把两个字节的字符识别为一个汉…

Jmeter实现接口自动化

自动化测试理论知识 什么是自动化测试&#xff1f; 让程序或工具代替人为执行测试用例什么样的项目适合做自动化&#xff1f; 1、项目周期长 --多长算长&#xff1f;&#xff08;自己公司运营项目&#xff09; 2、需求稳定&#xff08;更多具体功能/模块&#xff09; 3、需要…

[数据结构] 归并排序快速排序 及非递归实现

&#xff08;&#xff09;标题&#xff1a;[数据结构] 归并排序&&快速排序 及非递归实现 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 (一)快速排序 类比递归谋划非递归 快速排序的非递归实现&#xff1a; &#xff08;二&#xff09;归并排序 归…

通过scp命令进行本地和远程服务器之间的文件传输

打开本地终端&#xff08;Windonws系统按下WinR键&#xff0c;输入cmd回车&#xff0c;即可打开终端&#xff09; 1、从本地向远程服务器传输文件 scp 本地文件路径文件名 用户名远程服务器IP地址:远程服务器存放文件的路径 例如&#xff1a; scp /Users/HP/Desktop/test/1.p…

【flutter问题记录】 无效的源发行版:17

问题描述 在看开源项目的时候&#xff0c;clone下来后一直编译失败&#xff0c;提示&#xff1a;无效的源发行版:17&#xff0c;看描述大概是jdk的版本问题&#xff0c;但是在Android studio各种指定都无用&#xff0c;网上资料也没有flutter项目的解决方案&#xff0c;最后在…

数据库(表)

要求如下&#xff1a; 一&#xff1a;数据库 1&#xff0c;登录数据库 mysql -uroot -p123123 2&#xff0c;创建数据库zoo create database zoo; Query OK, 1 row affected (0.01 sec) 3&#xff0c;修改字符集 mysql> use zoo;---先进入数据库zoo Database changed …

集成测试技术栈

前端 浏览器操作&#xff1a;playwright、selenium 后端 testcontainercucumbervitestcypressmsw

HTTP模块(一)

HTTP服务 本小节主要讲解HTTP服务如何创建服务&#xff0c;查看HTTP请求&响应报文&#xff0c;还有注意事项说明&#xff0c;另外讲解本地环境&Node环境&浏览器之间的链路图示&#xff0c;如何提取HTTP报文字符串&#xff0c;及报错信息查询。 创建HTTP服务端 c…