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容量,