首页 > 其他分享 >利用ESP-01S中继实现STM32F103C8T6与MQTT服务器的串口双向通信

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

时间:2024-12-13 23:57:51浏览次数:5  
标签:USART ESP 双向通信 WiFi MQTT 串口 println Serial void

最终现象

未完待续

实现流程

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

元件与接线

STM32相关

STM32F103C8T6开发板:

STM32仿真器:

烧录程序时,STM32F103C8T6与仿真器的接下如下:

STM32

ST-LINK

3V3

3.3V

GND

GND

SWDIO

SWDIO

SWCLK

SWCLK

USB转TTL:

未完待续

ESP相关

ESP-01S开发板:

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服务器的消息

代码

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

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


代码的第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();    // 连接WiFi
  
  mqttClient.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(""); 
}

现象

<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="jFawxogu-1734100158109" src="https://live.csdn.net/v/embed/438789"></iframe>

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

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

代码

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

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


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


代码的第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();    // 连接WiFi
  
  mqttClient.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(""); 
}

现象

<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="Xu5ZeJEh-1734101284642" src="https://live.csdn.net/v/embed/438792"></iframe>

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

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

接线

STM32

ESP-01S

USB-TTL

3.3

3V3

G

GND

PA10

TX

PA9RX

代码

ESP-01S代码

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

stm32代码

工程文件:未完待续

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));	//等待计数到0
	SysTick->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_H

void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);

#endif

现象

<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="dmaJBdT6-1734105174981" src="https://live.csdn.net/v/embed/438802"></iframe>

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

STM32F103C8T6向MQTT服务器发送消息

未完待续

参考

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

标签:USART,ESP,双向通信,WiFi,MQTT,串口,println,Serial,void
From: https://blog.csdn.net/qq_44955826/article/details/144459591

相关文章

  • 2024年12月GESPC++三级真题解析
    一、单选题(每题2分,共30分)题目123456789101112131415答案BDAADBCAADDCDCA1.下列二进制表示的十进制数值分别是()[10000011]原=()[10000011]补=()A.-125,-3B.-3,-125C.-3,-3D.-125,-125【答案】B【考纲知识点】原码和补码的计算及转换【......
  • 【保姆级】免踩坑,快速在Arduino IDE搭建esp8266/esp32开发环境
    1.安装ArduinoIDE首先安装好ArduinoIDE推荐下载一个1.8.x的经典稳定版本入手开发,再下载一个最新的2.x版本的zip版本体验跳转、调试、快速编译等全新体验下载ArduinoIDEArduino官网下载2.添加附加开发板地址打开ArduinoIDE的菜单>文件>首选项,在附加开发板管理......
  • 【经验分享】怎样在ESP32平台上实现高效刷图动画
    此篇文章在2024年11月5日被记录怎样在ESP32上流畅的播放动画最近有一个预研项目,在ESP32上播放动画,于是来测试一下ESP32刷动态图的极限性能1.硬件以及内容描述名称描述CPUESP32S3@240MhzRAM512KB(IRAM(192KB)+DRAM(328KB))LCDILI9341分辨率240......
  • 用Beetle ESP32 C6复现ncnn_on_esp32
    前言偶然发现nihui大佬的知乎文章在esp32c3用ncnn跑神经网络mnist-知乎该项目的项目地址:GitHub-nihui/ncnn_on_esp32于是我买了一块BeetleESP32C6,尝试复现该项目。该开发板小巧可爱,性能也是比较好的。本博客希望能够通过列举自己在复现过程中遇到的问题,帮助同......
  • 题解:P11380 [GESP202412 八级] 排队
    题目传送门题意概要有nnn个人排队,其中有mmm对人必须相邻且前......
  • 串口、Modbus通信协议
    1.串口一般来说就是指串行通讯口,常用的有RS485,RS232和RS422串口是一种硬件连接方式,可以看成是硬件2.Modbus通信协议Modbus通信协议分为MODBUSRTU,MODBUSASCII和MODBUSTCP三种模式。MODBUSRTU,MODBUSASCII所用的物理硬件接口都是串行(Serial)通讯(RS232,RS422,RS485)。而MODBUSTCP......
  • 请求响应(Request-Response)和事件响应(Event-Driven)
    请求响应(Request-Response)和事件响应(Event-Driven)是两种常见的软件和系统设计框架,它们在目的、设计和实现方式上存在明显的区别:请求响应框架目的和概念请求响应框架是一种同步通信模式,常见于客户端-服务器架构中。在这种框架下,客户端发送请求到服务器,服务器经过处理后返回......
  • 2024-12-12 Respeak 4mic 安装在Raspberry Debian 12 bookworm
    2024-12-12Respeak4mic安装在RaspberryDebian12bookworm官方驱动不支持后续的linux内核,旧版本不太好用,所以不能用官方的版本驱动大致步骤可以参照官网1.驱动安装先查看内核是6.6uname-r sudoapt-getupdate gitclonehttps://github.com/HinTak/seeed-vo......
  • 基于ESP32的环境监测系统设计
    最终效果环境监测项目介绍该项目是“物联网实验室监测控制系统设计(仿智能家居)”项目中的“环境监测系统设计”子项目,前者还包括“物联网设计”、“门禁设计”、“家电控制设计”和“小程序设计”等内容。本文只介绍“环境监测”部分。项目功能实现的大致思路为:单片机采......
  • C#中的DateTime、DateTimeOffset和TimeSpan(链接)
    下面的微软官方文档,介绍了C#中的DateTime:DateTimeStructSystem.DateTimestruct其中这里有提到,DateTime的精度为100纳秒:Timevaluesaremeasuredin100-nanosecondunitscalledticks.DateTime.TicksProperty属性可以返回DateTime代表的100纳秒数。而DateTime(Int64)......