STM32F103C8T6+ESP-01S+MQTTサーバーでデータのアップロードと受信を実現 (2)


記事ディレクトリ
STM32F103C8T6+ESP-01S+MQTTサーバーでデータのアップロードと受信を実現 (1)

序文

プログラムは JSON データを受信して​​認識する必要があるため、cJSON ライブラリを導入する必要があります。
cJSON を使用するには、ヒープ メモリ スペースが必要です。STM32F103C8T6 のデフォルトのヒープ スペースは 0x00000200 であり、cJSON の要件を満たすには十分ではありません。
したがって、startup_stm32f10x_md.s ファイルの 44 行目を変更する必要があります。ヒープ メモリを 0x00001000 に変更します。

変更が完了すると、次のようになります。

ヒープ メモリ サイズを変更する

シリアルポート構成

初期化

従来のシリアルポートの初期化コード、最後に注意する必要があります受信割り込みとバスアイドル割り込みを有効にする

ここでは、単純に 2 種類の割り込みを受け取ります.
シリアル ポートが文字を受信するたびに、受信割り込みに入ります。
シリアル ポートが完全な文字列を受信すると、バス アイドル割り込みに入ります。
例: デバイスは文字列「lchen」を MCU に送信します。
受信割り込みに 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 を導入しないことを選択できます。これにより、フラッシュ容量が節約されます。

おすすめ

転載: blog.csdn.net/weixin_46144773/article/details/122813763