最终现象
未完待续
实现流程
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 | |
PA9 | RX |
代码
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