STM32F103C8T6+ESP-01S+MQTT服务器实现数据上传和接收(二)


文章目录
STM32F103C8T6+ESP-01S+MQTT服务器实现数据上传和接收(一)

前言

因程序需要接收并识别JSON数据,需要引入cJSON库。
cJSON的使用需要占用堆内存空间,STM32F103C8T6默认设置的堆空间为0x00000200,不足以达到cJSON的使用要求。
因此需要修改startup_stm32f10x_md.s文件中第44行。将堆内存改为0x00001000

修改完成后如下所示:

修改堆内存大小

串口配置

初始化

常规的串口初始化代码,最后需要注意开启接收中断和总线空闲中断

这里简单接收一下两种中断
串口每次接收到一个字符就会进入一下接收中断。
串口接收到一个完整的字符串会进入一次总线空闲中断。
例如:设备向单片机发送了一个字符串"lcheng"。
就会进入接收中断6次,当字符串最后一个’g’接收完毕后,就会进入总线空闲中断1次

void usart_init(uint32_t bound)				//115200
{
    
    
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;

        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
        //USART1_TX   PA.9
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);   
        
        USART_InitStructure.USART_BaudRate = bound;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        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(USART1, &USART_InitStructure);


        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启接收中断
		USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);			//开启空闲中断
				
        USART_Cmd(USART1, ENABLE);                   
}

printf输出重定向

如果需要重定向到USART2,请替换下面所有的USART1为USART2

#include <stdio.h>							//重定向需要引入该头文件
int fputc(int ch,FILE *f)					
{
    
      
	USART_ClearFlag(USART1, USART_FLAG_TC);
    USART_SendData(USART1,(unsigned char)ch); 
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);  
    return ch;  
}

串口中断函数

在串口接收中断中,将接收到的每一个字符配合Rec_i存入全局变量USART_ReceiveString中。

uint16_t Rec_i;
void USART1_IRQHandler(void)
{
    
    
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   
    {
    
    
			USART_ReceiveString[Rec_i++]=USART_ReceiveData(USART1);				//将接收到的字符存入USART_ReceiveString中
    }
		if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET){
    
    					//表示接收到了一个完整的字符串
			uint16_t clear;
			USART_ReceiveString[Rec_i]='\0';
			if(USART_ReceiveString[0]!='\0'){
    
    
				baseACK(USART_ReceiveString);				//处理AT指令基本回复  OK  ERROR FALT
				msg_handle(USART_ReceiveString);			//真正的数据处理
				clear_ReceiveString();						//清除串口的接收数据。
			}
			clear = USART1->SR;								//清除寄存器
			clear = USART1->DR;
		}
}
void clear_ReceiveString(){
    
    	
	USART_ReceiveString[0]='\0';
	Rec_i=0;
}

对串口接收到的数据进行处理

baseAck()

该函数用于对8266的基本应答做出修改程序中对应的应答标志位的处理。

void baseACK(char USART_ReceiveString[]){
    
    
	if(checkOK(USART_ReceiveString,OK)){
    
    
		back_flag=SUCC;
	}else if(checkOK(USART_ReceiveString,ERROR)||checkOK(USART_ReceiveString,FAIL)){
    
    
		back_flag=FAULT;
	}else{
    
    
		clearBackFlag();
	}
	if (checkOK(USART_ReceiveString,WIFI_GOT_IP)||checkOK(USART_ReceiveString,WIFI_CONNECTED)){
    
    
			ATBack_KeyWords=WIFI_GOT;
		}
}

获取MQTT主题内容get_mqttval()

该函数用于获取MQTT订阅的主题的内容,需要配合info_type()使用

void get_mqttval(char USART_ReceiveString[],char *val){
    
    
	unsigned short int i,j,count=0;  //count为逗号数量
	for(i=0;USART_ReceiveString[i]!='\0';i++){
    
    
		if(USART_ReceiveString[i]==','&&count!=3){
    
    
			if(count<3){
    
    
				j=0;
				count+=1;
			}
		}else{
    
    
			if(count==3){
    
    
				val[j++]=USART_ReceiveString[i];
				if(USART_ReceiveString[i+1]=='\r'){
    
    
					val[j++]='\0';
				}
			}
		}
	}
}

msg_handle()

根据串口接收到的数据进行相应的处理

void msg_handle(char *USART_ReceiveString){
    
    

	if(info_type(USART_ReceiveString)){
    
    	//是mqtt订阅的话题题消息,就根据mqtt协议得到最终数据进行处理
		char val[300],*type,*msg;
		cJSON* val_json=NULL;
		get_mqttval(USART_ReceiveString,val);		//去掉MQTT协议壳得到原始数据(json字符串)
		val_json=cJSON_Parse(val);					//将json字符串转化成cJSON结构体数据
		if(val_json!=NULL){
    
    
			type=cJSON_GetObjectItem(val_json,"type")->valuestring;		//得到json中键为type的值
			msg=cJSON_GetObjectItem(val_json,"msg")->valuestring;       //得到json中键为msg的值
			if(!strcmp(type,"dj")){
    
    				//对键值进行判断
				if(!strcmp(msg,"ON")|| !strcmp(msg,"on")){
    
      
					//如果匹配进行相应处理
				}else{
    
    
					//其他处理........
				}
			}
			cJSON_Delete(val_json);				//JSON结构体使用完毕需要释放掉,不然会造成堆内存溢出导致程序卡死
		}
	}else{
    
    		//非MQTT话题消息
		/*非MQTT话题消息处理
		例如baseACK()函数可以放在此处性能会更好,
		但是为了代码可读性更好,选择放在了与msg_handle()同级的地方。*/
	}
}

总结

发送MQTT消息时没有选择使用cJSON是因为MQTT发送的json数据中的’,‘需要转义,cJSON构建的字符串中’,'并没有转义,这个地方不太和谐,另外使用cJSON构建json字符串比起解析json字符串来说需要占用更大的堆内存,因此没有使用cJSON,后续可继续改进这个地方。另外由于引入了cJSON会导致生成的hex文件偏大一点,如果没有使用JSON的需求可以选择不引入cJSON,会节省一些flash容量,

猜你喜欢

转载自blog.csdn.net/weixin_46144773/article/details/122813763
今日推荐