当前位置: 首页 > news >正文

利用ESP-01S中继实现STM32F103C8T6与MQTT服务器的串口双向通信

实现流程

STM32通过串口与ESP通信,ESP通过WiFi与MQTT服务器通信。

元件

STM32F103C8T6开发板:

STM32仿真器:

USB转TTL:

ESP-01S开发板:

ESP-01S烧录下载器:

烧录程序时的接线

STM32F103C8T6与仿真器的接线如下:

STM32

ST-LINK

3V3

3.3V

GND

GND

SWDIO

SWDIO

SWCLK

SWCLK

ESP-01S开发板直接倒扣在下载器上:

开发环境

ESP-01S开发板的开发环境为Arduino IDE,开发板类型为Generic ESP8266 Module。

STM32的开发环境为Keil5 IDE。

ESP-01S连接WiFi

代码

代码的第4、5行是WiFi名称与密码,大家根据自己实际的热点配置去更改:

const char* wifiname = "DOILMSBOIOT";
const char* password = "doilmsboiot";

#include <ESP8266WiFi.h>// 设置wifi接入信息
const char* wifiname = "DOILMSBOIOT";
const char* password = "doilmsboiot";void setup() 
{Serial.begin(9600);    // 启动串口通讯WiFi.mode(WIFI_STA);    //设置ESP8266工作模式为无线终端模式connectWifi();    // 连接WiFi
}void loop() 
{
}// ESP8266连接wifi
void connectWifi()  
{WiFi.begin(wifiname, password);Serial.println("Connecting to WiFi");while (WiFi.status() != WL_CONNECTED) //等待WiFi连接,当wifi未连接时,持续输出".";成功连接后输出连接成功信息{delay(1000);Serial.print(".");}Serial.println("");Serial.println("WiFi Connected!");  Serial.println(""); 
}

现象

Arduino IDE中的串口提示ESP-01S已连接WiFi:

手机热点管理界面会显示连接到的ESP-01S的mac地址:

ESP-01S接收来自MQTT服务器的消息

代码

代码的第7行是MQTT服务器的地址,大家根据自己使用的服务器去更改:
const char* mqttServer = "broker.emqx.io";

顺便一提,该服务器开源,下载地址:MQTTX Download


代码的第84行是订阅的主题名称,大家根据自己的主题去更改:

  String topicString = "deviceControl3";   // 订阅主题的名称 

#include <ESP8266WiFi.h>
#include <PubSubClient.h>// 设置wifi接入信息和MQTT服务器
const char* wifiname = "DOILMSBOIOT";
const char* password = "doilmsboiot";
const char* mqttServer = "broker.emqx.io";WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);void setup() 
{Serial.begin(9600);    // 启动串口通讯WiFi.mode(WIFI_STA);    //设置ESP8266工作模式为无线终端模式connectWifi();    // 连接WiFimqttClient.setServer(mqttServer, 1883);   // 设置MQTT服务器和端口号mqttClient.setCallback(receiveCallback);    // 设置MQTT订阅回调函数connectMQTTserver();    // 连接MQTT服务器
}void loop() 
{if (mqttClient.connected())   // 如果开发板成功连接服务器{ mqttClient.loop();          // 处理信息(收到信息后的回调函数)以及心跳} else                          // 如果开发板未能成功连接服务器{                      connectMQTTserver();        // 则尝试连接服务器并订阅主题}
}// 连接MQTT服务器并订阅主题
void connectMQTTserver()
{// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)String clientId = "esp8266-" + WiFi.macAddress();if (mqttClient.connect(clientId.c_str()))     //如果成功连接MQTT服务器{ Serial.print("MQTT Server Has Connected. ");Serial.print("Server Address: ");Serial.println(mqttServer);Serial.print("ClientId: ");Serial.println(clientId);subscribeTopic(); // 订阅指定主题} else {Serial.print("MQTT Server Connect Failed. Client State:");Serial.println(mqttClient.state());delay(3000);}   
}// 收到信息后的回调函数
void receiveCallback(char* topic, byte* payload, unsigned int length) 
{Serial.print("Message with the topic of [ ");Serial.print(topic);Serial.println(" ] has been received.");Serial.print("Content: ");for (int i = 0; i < length; i++) {Serial.print((char)payload[i]);}Serial.println("");Serial.print("Message Length (Bytes) :  ");Serial.println(length);Serial.println(" ");
}// 订阅指定主题
void subscribeTopic()
{String topicString = "deviceControl3";   // 订阅主题的名称char subTopic[topicString.length() + 1];  strcpy(subTopic, topicString.c_str());if(mqttClient.subscribe(subTopic))    //如果成功订阅主题{Serial.print("Subscrib Topic: ");Serial.println(subTopic);Serial.println("");} else {Serial.print("Subscribe Fail...");}  
}// ESP8266连接wifi
void connectWifi()  
{WiFi.begin(wifiname, password);Serial.println("Connecting to WiFi");while (WiFi.status() != WL_CONNECTED) //等待WiFi连接,当wifi未连接时,持续输出".";成功连接后输出连接成功信息{delay(1000);Serial.print(".");}Serial.println("");Serial.println("WiFi Connected!");  Serial.println(""); 
}

现象

ESP-01S接收MQTT服务器传来的消息

ESP-01S向MQTT服务器发送消息

发送固定消息

发送固定消息指的是要发送的消息内容在代码中写死了,运行后不可更改。

代码

代码的第66行是发布的主题名称,大家根据自己的主题去更改:

  String topicString = "environmentalMonitoring1";     //发布信息的主题


代码的第70行是向MQTT服务器发送的消息内容,大家根据自己的实际需求去更改:

  String messageString = "{\"temperature\":26, \"humidity\":55, \"illumination\":2345, \"smog\":\"high\"}";      //发布信息的内容

顺便一提,消息内容采用JSON格式是为了后续(STM32)的数据处理。

#include <ESP8266WiFi.h>
#include <PubSubClient.h>// 设置wifi接入信息和MQTT服务器
const char* wifiname = "DOILMSBOIOT";
const char* password = "doilmsboiot";
const char* mqttServer = "broker.emqx.io";WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);void setup() 
{Serial.begin(9600);   // 启动串口通讯WiFi.mode(WIFI_STA);    //设置ESP8266工作模式为无线终端模式connectWifi();    // 连接WiFimqttClient.setServer(mqttServer, 1883);   // 设置MQTT服务器和端口号connectMQTTserver();    // 连接MQTT服务器
}void loop() 
{ if (mqttClient.connected())   // 如果开发板成功连接服务器{ pubRetMQTTmsg();               // 发布信息mqttClient.loop();          // 保持心跳   }else                          // 如果开发板未能成功连接服务器{                      connectMQTTserver();        // 则尝试连接MQTT服务器}
}// 连接MQTT服务器
void connectMQTTserver()    
{// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)String clientId = "esp8266-" + WiFi.macAddress();if (mqttClient.connect(clientId.c_str()))   //如果成功连接MQTT服务器{ Serial.println("MQTT Server Connected.");Serial.print("Server Address: ");Serial.println(mqttServer);Serial.print("ClientId: ");Serial.println(clientId);} else{Serial.print("MQTT Server Connect Failed. Client State:");Serial.println(mqttClient.state());delay(3000);}   Serial.println("");
}// 发布信息
void pubRetMQTTmsg()  
{String topicString = "environmentalMonitoring1";     //发布信息的主题char publishTopic[topicString.length() + 1];  strcpy(publishTopic, topicString.c_str());String messageString = "{\"temperature\":26, \"humidity\":55, \"illumination\":2345, \"smog\":\"high\"}";      //发布信息的内容char publishMsg[messageString.length() + 1];   strcpy(publishMsg, messageString.c_str());if(mqttClient.publish(publishTopic, publishMsg, true))  //如果成功发布信息;publish函数第三个参数用于设置保留信息{Serial.print("Publish Topic: ");Serial.println(publishTopic);Serial.print("Publish Retained message: ");Serial.println(publishMsg); Serial.println("");   } else  //如果未能成功发布信息{Serial.println("Message Publish Failed."); }delay(3000);
}// 连接wifi
void connectWifi()  
{WiFi.begin(wifiname, password);Serial.println("Connecting to WiFi");while (WiFi.status() != WL_CONNECTED) //等待WiFi连接,当wifi未连接时,持续输出".";成功连接后输出连接成功信息{delay(1000);Serial.print(".");}Serial.println("");Serial.println("WiFi Connected!");  Serial.println(""); 
}

现象

ESP-01S向MQTT服务器发送消息

发送任意消息

发送任意消息指的是将串口输入的内容发送至MQTT服务器。

代码

与发送固定消息的区别是在loop()和pubRetMQTTmsg()函数中多了串口内容的接收和处理。

#include <ESP8266WiFi.h>
#include <PubSubClient.h>// 设置wifi接入信息和MQTT服务器
const char* wifiname = "DOILMSBOIOT";
const char* password = "doilmsboiot";
const char* mqttServer = "broker.emqx.io";WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);String serial_information = "";    //从串口获得的信息void setup() 
{Serial.begin(9600);   // 启动串口通讯WiFi.mode(WIFI_STA);    //设置ESP8266工作模式为无线终端模式connectWifi();    // 连接WiFimqttClient.setServer(mqttServer, 1883);   // 设置MQTT服务器和端口号connectMQTTserver();    // 连接MQTT服务器
}void loop() 
{ if (mqttClient.connected())   // 如果开发板成功连接服务器{ pubRetMQTTmsg();               // 发布信息mqttClient.loop();          // 保持心跳   }else                          // 如果开发板未能成功连接服务器{                      connectMQTTserver();        // 则尝试连接MQTT服务器}while (Serial.available() > 0)  {serial_information += char(Serial.read());delay(2); //延时一会,让串口缓存准备好下一个字符,不延时会导致数据丢失,}
}// 连接MQTT服务器
void connectMQTTserver()    
{// 根据ESP8266的MAC地址生成客户端ID(避免与其它ESP8266的客户端ID重名)String clientId = "esp8266-" + WiFi.macAddress();if (mqttClient.connect(clientId.c_str()))   //如果成功连接MQTT服务器{ Serial.println("MQTT Server Connected.");Serial.print("Server Address: ");Serial.println(mqttServer);Serial.print("ClientId: ");Serial.println(clientId);} else{Serial.print("MQTT Server Connect Failed. Client State:");Serial.println(mqttClient.state());delay(3000);}   Serial.println("");
}// 发布信息
void pubRetMQTTmsg()  
{String topicString = "environmentalMonitoring1";     //发布信息的主题char publishTopic[topicString.length() + 1];  strcpy(publishTopic, topicString.c_str());char publishMsg[50] = {'\0'};    //发布信息的内容if(serial_information.length() > 0){  strcpy(publishMsg, serial_information.c_str());serial_information = "";}if(publishMsg[0] != '\0') {if(mqttClient.publish(publishTopic, publishMsg, true))  //如果成功发布信息;publish函数第三个参数用于设置保留信息{Serial.print("Publish Topic: ");Serial.println(publishTopic);Serial.print("Publish Retained message: ");Serial.println(publishMsg); Serial.println(""); publishMsg[0] = '\0';} else  //如果未能成功发布信息{Serial.println("Message Publish Failed."); }}delay(1000);
}// 连接wifi
void connectWifi()  
{WiFi.begin(wifiname, password);Serial.println("Connecting to WiFi");while (WiFi.status() != WL_CONNECTED) //等待WiFi连接,当wifi未连接时,持续输出".";成功连接后输出连接成功信息{delay(1000);Serial.print(".");}Serial.println("");Serial.println("WiFi Connected!");  Serial.println(""); 
}

现象

通过串口输入使ESP-01S向MQTT发送任意的消息

STM32F103C8T6接收来自MQTT服务器的消息

数据流向

MQTT->ESP-01S-->STM32-->USB-TTL-->电脑

接线

STM32

ESP-01S

USB-TTL

3.3

3V3

G

GND

PA10

TX

PA9RX

代码

ESP-01S代码

同上文“ESP-01S接收来自MQTT服务器的消息”。

STM32代码

工程文件:https://download.csdn.net/download/qq_44955826/90130634?spm=1001.2014.3001.5501

  • main.c
#include "stm32f10x.h"                  
#include "serial.h"
#include "Delay.h"int main(void)
{Serial_Init();printf("串口已启用\r\n\r\n");	while(1){printf("正在等待接收MQTT数据\r\n");if (Serial_RxFlag == 1){printf("收到的MQTT数据:%s\r\n\r\n", Serial_RxData);Serial_RxFlag = 0;			}Delay_s(3);}	
}
  • serial.c
//串口1打印和输入#include "stm32f10x.h"                  
#include <stdio.h>
#include <stdarg.h>void Serial_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_WordLength = USART_WordLength_8b;USART_Init(USART1, &USART_InitStructure);USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);USART_Cmd(USART1, ENABLE);
}void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte);while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//当其置1(SET) 标志着串口1接收完成
}//重定向c库函数printf到串口
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);return ch;
}/*
uint8_t Serial_ReceiveByte(void)
{uint8_t Byte;Byte = USART_ReceiveData(USART1);while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);	//当其置1(SET) 标志着串口1发送完成return Byte;
}
*/
/*
//重定向c库函数scanf到串口,重写后可使用scanf、getchar等函数
int fgetc(FILE *f)
{uint8_t ch;ch = Serial_ReceiveByte();return ch;
}
*/
char Serial_RxData[100];		//全局变量,用于储存USART1获得的文本数据,最多100个字符
uint8_t Serial_RxFlag;		//全局变量,串口接收标志位/*
下面的中断服务函数只会向Serial_RxData[]数组中储存第1对{}之间的内容
如  fdgh{"lamp": 77,"curtain": 3},"airConditioner": {2}frgtr  发送至USART1后,Serial_RxData[]数组中的内容为  {"lamp": 77,"curtain": 3}
如果只有 { 而没有 } ,Serial_RxData[]数组的'\0'结束符的位置就未被重置,后续处理就会出错
*/void USART1_IRQHandler(void)
{static uint8_t RxState = 0;static uint8_t count = 0;	//Serial_RxData[]数组的下标if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET){uint8_t RxData = USART_ReceiveData(USART1);if (RxState == 0){if (RxData == '{' && Serial_RxFlag == 0){RxState = 1;count = 0;}}if (RxState == 1){if (RxData == '}'){Serial_RxData[count] = '}';Serial_RxData[count + 1] = '\0';	//标志数组的结束,方便后续处理RxState = 0;Serial_RxFlag = 1;count = 0;}else{Serial_RxData[count] = RxData;count ++;}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);}
}
  • serial.h
//串口1打印和输入#ifndef __SERIAL_H
#define __SERIAL_H#include <stdio.h>void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
uint8_t Serial_ReceiveByte(void);extern char Serial_RxData[100];
extern uint8_t Serial_RxFlag;#endif
  • Delay.c
#include "stm32f10x.h"/*** @brief  微秒级延时* @param  xus 延时时长,范围:0~233015* @retval 无*/
void Delay_us(uint32_t xus)
{SysTick->LOAD = 72 * xus;				//设置定时器重装值SysTick->VAL = 0x00;					//清空当前计数值SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器while(!(SysTick->CTRL & 0x00010000));	//等待计数到0SysTick->CTRL = 0x00000004;				//关闭定时器
}/*** @brief  毫秒级延时* @param  xms 延时时长,范围:0~4294967295* @retval 无*/
void Delay_ms(uint32_t xms)
{while(xms--){Delay_us(1000);}
}/*** @brief  秒级延时* @param  xs 延时时长,范围:0~4294967295* @retval 无*/
void Delay_s(uint32_t xs)
{while(xs--){Delay_ms(1000);}
} 
  • Delay.h
#ifndef __DELAY_H
#define __DELAY_Hvoid Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);#endif

现象

STM32通过ESP-01S接收来自MQTT服务器的消息

STM32F103C8T6向MQTT服务器发送任意消息

数据流向

电脑-->USB-TTL-->STM32-->ESP-01S-->MQTT

接线

STM32

ESP-01S

USB-TTL

3.3

3V3

G

GND

PA10

TX
PA9RX

代码

ESP-01S代码

同上文“ESP-01S向MQTT服务器发送消息 - 发送任意消息”。

STM32代码

工程文件:https://download.csdn.net/download/qq_44955826/90130634?spm=1001.2014.3001.5501

main.c

#include "stm32f10x.h"                  
#include "serial.h"
#include "Delay.h"int main(void)
{Serial_Init();while(1){	if (Serial_RxFlag == 1){printf("%s", Serial_RxData);Serial_RxFlag = 0;			}Delay_s(1);}	
}

其余代码与“STM32F103C8T6接收来自MQTT服务器的消息”一样。

现象

STM32通过ESP-01S向MQTT服务器发送任意消息

参考

零基础入门学用物联网 – MQTT应用篇 – 目录 – 太极创客


http://www.mrgr.cn/news/80142.html

相关文章:

  • Linux:Git
  • canal安装使用
  • [C++]多态
  • ORB-SLAM3源码学习:G2oTypes.cc: void EdgeInertial::computeError 计算预积分残差
  • 代码随想录算法训练营第一天 | 数组理论基础,977.有序数组平方结果再排序
  • 玛哈特精密矫平机:制造业的隐秘
  • C语言数组和字符串笔记
  • 51c大模型~合集89
  • 考研数学【线性代数基础box(数二)】
  • ubuntu20.04+ROS Noetic 安装PX4+Mavros
  • Vue基础记录
  • 海康萤石摄像机接入EasyNVR流程:开启RTSP-》萤石视频添加到EasyNVR-》未来支持海康SDK协议添加到EasyNVR
  • 代码随想录算法训练营第三天 | 链表理论基础 | 707.设计链表
  • 从整个SecurityFilterChain的角度,一个请求经过Spring Security的流程
  • L23.【LeetCode笔记】验证回文串(剖析几种解法)
  • Spring 如何解决循环依赖?
  • Linux下socket广播通讯的实现
  • 图像融合算法笔记2024 CDTNet
  • pytorch_fid 安装笔记
  • 【AIGC】ChatGPT保护指令:高效提升GPTs提示词与知识库文件的安全性