STM32第十七课:连接云平台进行数据传输
如何使用USB接口进行数据传输 #生活技巧# #数码产品使用技巧# #电子设备保养知识#
目录 需求一、云平台项目创建二、代码编写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); } 123456789101112131415161718192021
3.发布数据
MQTT是一种一种轻量级的消息传输协议,基于订阅和发布的,建立在TCP/IP之上的应用层协议。
MQTT 的通信机制基于发布/订阅模式,主要包括以下几个关键部分:
客户端(Client):包括发布者(Publisher)和订阅者(Subscriber)。发布者负责产生消息并将其发布到特定的主题(Topic),订阅者则向代理服务器(Broker)订阅感兴趣的主题以接收相应的消息。
代理服务器(Broker):作为消息的中转枢纽,接收来自发布者的消息,并根据订阅者的订阅情况将消息分发给相应的订阅者。
主题(Topic):用于对消息进行分类和标识的字符串,也类似于消息的路径。主题采用分层结构,以斜杠“/”分隔不同的层级,例如“sensor/temperature”。
QoS(Quality of Service,服务质量)级别:MQTT 定义了三种 QoS 级别:
QoS 0:最多发送一次,消息可能会丢失或重复。
QoS 1:至少发送一次,确保消息至少到达一次,但可能会重复。
QoS 2:恰好发送一次,保证消息准确到达且不会重复。
MQTT 主题的主要作用包括:
(1)消息分类与组织
主题就像文件夹一样,对不同类型的消息进行分类。例如,“sensor/temperature”和“sensor/humidity”可以分别用于区分温度和湿度相关的消息。
(2)消息路由
代理服务器根据主题来确定将消息发送给哪些订阅了该主题的客户端。这确保了消息能够准确地到达对其感兴趣的接收方。
(3)解耦发布者和订阅者
发布者不需要知道具体有哪些订阅者,只需将消息发布到特定主题。订阅者也不需要关心消息的来源,只需订阅感兴趣的主题即可接收消息。
(4)权限控制
可以基于主题设置访问权限,限制某些客户端对特定主题的发布或订阅操作,以保障系统的安全性和数据的合理性使用。
(5)系统架构的灵活性
允许轻松添加新的主题来支持新的消息类型或功能,而不需要对整个系统的架构进行重大更改。
发布数据就是将数据传输到云端。
主要就是用到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); } 12345678910111213141516171819202122232425
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]){//过去500msledcnt[0]=0;Get_Smoke_Light_MidValue();//烟雾光照中位数DHT11_ReadData();//温湿度KQM_DealData();//空气质量Get_Smoke_Light_MidValue();Delay_ms(5000);MQTT_Send_Publish();} }return 0; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
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); } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
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 12345678910111213141516171819202122232425262728
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);//PE6GPIO_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停止位,波特率115200USART_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~15NVIC_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);}if(USART_GetITStatus(USART3,USART_IT_IDLE) == SET){rxflag = 1;data = USART3->SR;data = USART3->DR;} } //串口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(" 发送成功 ");rxflag=0;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){//设置为STAreturn 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; } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
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 1234567891011121314151617181920
总结
1.当代码将报文发送给串口3时,由于串口3连接的是wifi模块,此时就相当于将报文通过wifi模块传送到云端。
2.将数据传输到串口1时,由于串口1连接的是电脑上,所以相当于将数据打印。
网址:STM32第十七课:连接云平台进行数据传输 https://www.yuejiaxmz.com/news/view/705335
相关内容
数据传输与IoT:设备连接与数据传输智能环境监测:STM32+机智云温湿度与空气质量数据上传系统
单片机数据上传到阿里云物联网平台后,如何在手机端和网页端获取获取数据?
(二)stm32单片机连接阿里云生活物联网平台/物联网平台(附代码)
深度解析:STM32对接米家平台,打造WiFi智能插座(ESP8266、电流检测)
通用ESP8266连接阿里云物联网平台
十大云课堂平台
踏入物联网第一篇——STM32F103开发板接入阿里云IOT平台
基于STM32单片机ESP8266物联网阿里云的远程控制LED系统
基于STM32的家庭健康监测系统