上文我们讲到如何搭建本地MQTT服务器,现在介绍如何通过stm32连接MQTT
一.首先我们初始化esp8266这里我们使用的是USART4与其通信代码如下
1. void UART4_Init(uint32_t bound) 2. { 3. GPIO_InitTypeDef GPIO_InitStructure; 4. USART_InitTypeDef USART_InitStructure; 5. 6. 7. RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE); //使能UART4,GPIOA时钟 8. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); 9. 10. USART_DeInit(UART4); //复位串口4 11. 12. //UART4_TX PC.10 13. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; 14. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出 16. GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化PC10 17. 18. //UART4_RX PC.11 19. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; 20. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入 21. GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化PC11 22. 23. //USART 初始化设置 24. USART_InitStructure.USART_BaudRate = bound; //设置波特率,一般设置为9600; 25. USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式 26. USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位 27. USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位 28. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制 29. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 30. 31. USART_Init(UART4, &USART_InitStructure); //初始化串口 32. USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);//开启中断 33. USART_Cmd(UART4, ENABLE); //使能串口 34. 35. 36. TIM7_Int_Init(100-1,7200-1); //10ms 为了判断是不是一串数据后文有解释 37. 38. TIM_Cmd(TIM7,ENABLE); // 39. }
二.通过数据间隔时间来判断是否是一串数据如果间隔时间大于30ms则判定为不是一串数据,接收缓冲器清零。 代码如下!
1. //定时器7中断服务程序 2. void TIM7_IRQHandler(void) 3. { 4. if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断 5. { 6. 7. TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志 8. if(F_UART4_RX_RECEIVING)//正在接收串口数据 9. { 10. UART4_RX_TIMEOUT_COUNT++;//串口超时计数 11. if(UART4_RX_TIMEOUT_COUNT>3)//数据接收间隔超过30ms 12. {//串口接收完成或结束 13. F_UART4_RX_RECEIVING=0; 14. UART4_RX_TIMEOUT_COUNT=0; 15. F_UART4_RX_FINISH=1; 16. } 17. } 18. } 19. } 20. 21. 22. //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us. 23. //Ft=定时器工作频率,单位:Mhz 24. //通用定时器中断初始化 25. //这里始终选择为APB1的2倍,而APB1为36M 26. //arr:自动重装值。 27. //psc:时钟预分频数 28. void TIM7_Int_Init(u16 arr,u16 psc) 29. { 30. NVIC_InitTypeDef NVIC_InitStructure; 31. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; 32. 33. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能 34. 35. //定时器TIM7初始化 36. TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 37. TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 38. TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim 39. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 40. TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 41. 42. TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断 43. 44. TIM_Cmd(TIM7,ENABLE);//开启定时器7 45. 46. NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; 47. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0 48. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2 49. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 50. NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 51. 52. }
三.初始化esp8266后我们就可以连接WiFi及mqtt了
首先我们将esp8266设置为station模式,如果返回ok则设置成功
代码如下:
1. #define AT_CWMODE "AT+CWMODE=1" //设置为“station”模式 2. int8_t ESP8266_SetStation(void) 3. { 4. ClrAtRxBuf();//清空缓存 5. SendAtCmd((uint8_t *)AT_CWMODE,strlen(AT_CWMODE)); 6. delay_ms(100); 7. if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL) 8. { 9. return -1; 10. } 11. return 0; 12. }
四.设置成功之后就可以连接WiFi(可以是路由器也可以是手机或电脑开热点)
代码如下:
1. /******************************************************************* 2. *函数:int8_t ESP8266_SetAP(void) 3. *功能:设置ESP8266要连接的热点名称和密码 4. *输入:char *wifi-热点名称 char *pwd-热点密码 5. *输出: 6. return = 0 ,sucess 7. return < 0 ,error 8. *特殊说明: 9. *******************************************************************/ 10. #define WIFI_AP "dmx"//WiFi热点名称 11. #define WIFI_PWD "88888888" //WiFi密码 12. 13. int8_t ESP8266_SetAP(void) 14. { 15. uint8_t AtCwjap[MAX_AT_TX_LEN]; 16. memset(AtCwjap, 0x00, MAX_AT_TX_LEN);//清空缓存 17. ClrAtRxBuf();//清空缓存 18. sprintf((char *)AtCwjap,"AT+CWJAP=\"%s\",\"%s\"",WIFI_AP, WIFI_PWD ); 19. SendAtCmd((uint8_t *)AtCwjap,strlen((const char *)AtCwjap)); 20. delay_ms(5500); 21. if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL) 22. { 23. return -1; 24. } 25. return 0; 26. }
五.现在就已经成功连接WiFi了下一步连接本地mqtt服务器(上篇博客我教大家如何搭建的)
1. #define SERVER_PC_IP "172.16.40.36" //服务器IP地址 2. #define SERVER_PC_PORT 1883 //服务器端口号 3. int8_t ESP8266_IpStart(void) 4. { 5. uint8_t IpStart[MAX_AT_TX_LEN]; 6. memset(IpStart, 0x00, MAX_AT_TX_LEN);//清空缓存 7. ClrAtRxBuf();//清空缓存 8. 9. /**************配置MQTT_USER信息***************/ 10. //AT+MQTTUSERCFG=0,1,"用户ID","账号","密码",0,0,"" 11. //AT+MQTTUSERCFG=0,1,\"M3\",\"\",\"\",0,0,\"\" 12. sprintf((char *)IpStart,"AT+MQTTUSERCFG=0,1,\"M3\",\"\",\"\",0,0,\"\""); 13. SendAtCmd((uint8_t *)IpStart,strlen((const char *)IpStart)); 14. delay_ms(1500); 15. printf("IpStart-USER_Config: %s\r\n", (char *)AT_RX_BUF); 16. if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL) 17. { 18. /******断开连接*****/ 19. memset(IpStart, 0x00, MAX_AT_TX_LEN);//清空缓存 20. ClrAtRxBuf();//清空缓存 21. 22. //AT+MQTTCLEAN=0 23. sprintf((char *)IpStart,"AT+MQTTCLEAN=0"); 24. 25. SendAtCmd((uint8_t *)IpStart,strlen((const char *)IpStart)); 26. 27. return -1; 28. } 29. 30. /**************建立MQTT连接***************/ 31. memset(IpStart, 0x00, MAX_AT_TX_LEN);//清空缓存 32. ClrAtRxBuf();//清空缓存 33. 34. //AT+MQTTCONN=0,"服务器IP",1883,0 35. //AT+MQTTCONN=0,"172.16.40.36",1883,0 36. sprintf((char *)IpStart,"AT+MQTTCONN=0,\"%s\",%d,0",SERVER_PC_IP, SERVER_PC_PORT ); 37. 38. SendAtCmd((uint8_t *)IpStart,strlen((const char *)IpStart)); 39. delay_ms(1500); 40. printf("IpStart-MQTT_Connect: %s\r\n", (char *)AT_RX_BUF); 41. if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL) 42. { 43. return -2; 44. } 45. 46. return 0; 47. }
六.经过以上五步就已经成功连接mqtt服务器了。现在通过命令设置自己为订阅者
1. int8_t ESP8266_MQTT_Sub(char *topic) //主题自己设置 2. { 3. int8_t error = 0; 4. uint8_t IpSend[MAX_AT_TX_LEN]; 5. memset(IpSend, 0x00, MAX_AT_TX_LEN);//清空缓存 6. ClrAtRxBuf();//清空缓存 7. 8. 9. //AT+MQTTSUB=0,M3",0 10. sprintf((char *)IpSend,"AT+MQTTSUB=0,\"%s\",1", topic); 11. SendAtCmd((uint8_t *)IpSend,strlen((const char *)IpSend)); 12. delay_ms(1500); 13. printf("MQTT_Sub_Ack: %s\r\n",AT_RX_BUF); 14. if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL) 15. { 16. return -4; 17. } 18. 19. return error; 20. }
在这里我们通过mqtt.fx同样连接本地搭建的服务器,作为一个发布者发送消息stm32就能收到了。不过此处还没写收到信息的代码
七.stm32作为发布者者代码,这边你可以开一个mqtt.fx订阅相同的主题就能收到消息了
1. int8_t ESP8266_MQTT_Pub(char *IpBuf, uint8_t len, uint8_t qos)//信息 消息长度 以及qos(百度) 2. { 3. uint8_t TryGo = 0; 4. int8_t error = 0; 5. uint8_t IpSend[MAX_AT_TX_LEN]; 6. memset(IpSend, 0x00, MAX_AT_TX_LEN);//清空缓存 7. ClrAtRxBuf();//清空缓存 8. /************设置为MQTT_Binary发送模式************/ 9. /* 注意----------dmx为发布的主题-----------*/ 10. sprintf((char *)IpSend,"AT+MQTTPUBRAW=0,\"dmx\",%d,%d,0", len, qos); 11. //printf("%s\r\n",IpSend); 12. SendAtCmd((uint8_t *)IpSend,strlen((const char *)IpSend)); 13. delay_ms(100); 14. //注意--如果断开连接或者怎么样,这一步ESP8266就会返回ERROR 15. if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL) 16. { 17. printf("MQTT_Pub Fail:%s\r\n", AT_RX_BUF); 18. MQTTSendErrorTimes++; 19. return -1; 20. } 21. 22. ClrAtRxBuf();//清空缓存 23. 24. //以二进制数据形式发送字符串 25. SendAtCmd((uint8_t *)IpBuf, len); 26. printf("MQTT_Pub: %s\r\n", IpBuf); 27. if(qos == 0){ 28. return 0; 29. } 30. 31. //等待qos回执处理 32. delay_ms(100); 33. if(strstr((const char *)AT_RX_BUF, (const char *)"OK") == NULL) 34. { 35. MQTTSendErrorTimes++; 36. return -2; 37. } 38. return error; 39. }
八.到这里,完成的步骤已经完成,由于篇幅有限,很多细节也讲不完,如果有什么问题可以评论区留言,最后我附一个搭建好的mqtt的一个简易智能家居!可以读取各种传感器器数据,如温湿度,光照,烟雾溶度等上传给服务器,风扇 PWM灯也可以被服务器传来的数据控制。