03-STM32-ESP8266-MQTT通信
2026/1/21大约 6 分钟
03-STM32-ESP8266-MQTT通信
上个视频讲得是如何使用esp8266进行MQTT通信,不过是用的电脑给esp8266发送的串口信息;
这期视频讲一下如何使用stm32给esp8266发送信息来实现串口通信。
stm32与esp8266接线图
OLED用来显示调试信息
代码思路
上期视频用电脑给esp8266发送串口信息,我们可以清晰看到电脑向esp8266发送了什么,返回了什么。但是现在让stm32去发送,stm32不知道要发送多快,太快esp8266来不及解析会发送busy...,太慢用户体验不好。而且只有stm32知道从esp8266接收了什么,我们是不知道的,所以
- 第一步就是控制stm32向esp8266发送信息,不能太快,也不能太慢,太快esp8266来不及解析会发送
busy...,太慢用户体验不好。 - 第二步是要知道在发送指令后esp8266返回了什么,因此需要用到串口1(PA9,PA10)将从串口2(PA2,PA3)接受的信息转发到串口助手上以便我们自己知道。
第一步
简单版
我们可以通过stm32的hal库函数进行发送,像下面这样:
void uart_print(UART_HandleTypeDef *huart,char* format,...) {
char buf[128]={0};
va_list ap;
va_start(ap,format);
vsprintf(buf,format,ap);
va_end(ap);
HAL_UART_Transmit(huart,(uint8_t*)buf,strlen(buf),HAL_MAX_DELAY);
}
uart_print(&huart2,"AT+MQTTCLEAN=0\r\n");
HAL_Delay(5000);
uart_print(&huart2,"AT+CWJAP=\"aaa\",\"aaaaaaaa\"\r\n");
HAL_Delay(5000);
uart_print(&huart2,"AT+MQTTUSERCFG=0,1,\"fs\",\"111111\",\"272935657\",0,0,\"\"\r\n");
HAL_Delay(5000);
uart_print(&huart2,"AT+MQTTCONN=0,\"45.192.110.61\",1883,1\r\n");
HAL_Delay(5000);
uart_print(&huart2,"AT+MQTTSUB=0,\"topic1\",1\r\n");进阶版
上面的简单版没法控制时间,每次发送都需要等5s给esp8266处理信息,太浪费时间,而且难以调试,写代码完全是凭感觉,接下来将会使用串口环形区来处理串口接收以及使用发送后等待接受处理发送。
串口缓冲区的原理:
- 定义一个缓冲区数组
- 定义一个读标志,写标志
- 通过DMA向缓冲区写入数据,并更新写标志(往前加)
- 提取数据时,更新读标志(往前加),如果读标志和写标志相等就说明没数据
- 系统主循环与初始化流程 (
mycode_loop)
- 底层数据抽离与解析流程 (
Process_ESP8266_Data)
- 非阻塞 AT 指令发送机制 (
ESP8266_Send_Cmd)
volatile uint8_t uart_rx_byte; // 存放接受到的字符
char uart_rx_buf[512]; // 存放已接受的数据
volatile uint16_t uart_rx_len = 0; // 已接收的数据长度
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if (huart->Instance == USART1){
if (uart_rx_len < (sizeof(uart_rx_buf) - 1)){
uart_rx_buf[uart_rx_len++] = (char)uart_rx_byte;
uart_rx_buf[uart_rx_len] = '\0';
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&uart_rx_byte, 1);
}
}- 串口格式化打印依然是需要滴
void uart_print(UART_HandleTypeDef *huart,char* format,...) {
char buf[128]={0};
va_list ap;
va_start(ap,format);
vsprintf(buf,format,ap);
va_end(ap);
HAL_UART_Transmit(huart,(uint8_t*)buf,strlen(buf),HAL_MAX_DELAY);
}- 定义函数
Send_ESP8266(UART_HandleTypeDef *huart,uint8_t timeout_ms,uint8_t max_retries,char* expected_resp,char* cmd,...){
uint8_t retry_count = 0;
uint32_t start_time = 0;
while (retry_count < max_retries)
{
memset(uart_rx_buf, 0, sizeof(uart_rx_buf));
uart_rx_len = 0;
uart_print(huart, "%s", cmd);
start_time = HAL_GetTick();
while ((HAL_GetTick() - start_time) < timeout_ms)
{
if (uart_rx_len > 0 && strstr(uart_rx_buf, expected_resp) != NULL)
{
uart_print(&huart2, "%s", uart_rx_buf);
memset(uart_rx_buf, 0, sizeof(uart_rx_buf));
uart_rx_len = 0;
return 0;
}
HAL_Delay(10);
}
if (uart_rx_len > 0) {
uart_print(&huart2, "%s", uart_rx_buf);
}
retry_count++;
HAL_Delay(500);
}
return -1;
}- 示例
代码详情
串口格式化打印
void uart_print(UART_HandleTypeDef *huart,char* format,...) {
char buf[128]={0};
va_list ap;
va_start(ap,format);
vsprintf(buf,format,ap);
va_end(ap);
HAL_UART_Transmit(huart,(uint8_t*)buf,strlen(buf),HAL_MAX_DELAY);
}中断不定长接收
// 初始化串口接收
HAL_UART_Receive_IT(&huart2,uart_buffer,1);
// 中断接收函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart==&huart2){
uart_time = uwTick;
uart_index++;
HAL_UART_Receive_IT(&huart2,(uint8_t *)&uart_buffer[uart_index],1);
}
}
void uart_process(void){
if(uart_index==0)return;
if(uwTick>uart_time+100){
memset(&uart_buffer,0,uart_index);
uart_index=0;
huart2.pRxBuffPtr = uart_buffer;
}
}MQTT消息解析
mqtt接收消息形式如下:
AT+MQTTPUB=0,"aaa","Hello from ESP8266",0,0
OK
+MQTTSUBRECV:0,"aaa",18,Hello from ESP8266+MQTTSUBRECV:0,"aaa",18,Hello from ESP8266即为我们要解析的数据
根据该数据的特点,aaa由双引号包裹,数据长度为第2个和第3个逗号之间,数据在第3个逗号后进行针对性解析
void extract_mqtt_info(const char *str, char *info1, char *info_b) {
const char *p1 = strchr(str, '"'); // 第一个引号
if (!p1) return;
const char *p2 = strchr(p1 + 1, '"'); // 第二个引号
if (!p2) return;
// 提取 info1
size_t len1 = p2 - p1 - 1;
strncpy(info1, p1 + 1, len1);
info1[len1] = '\0';
// 向后找逗号,找到数字部分
const char *p_comma = strchr(p2 + 1, ',');
if (!p_comma) return;
int length = atoi(p_comma + 1); // 提取长度数值
// 再找一个逗号,找到消息体开始处
const char *msg_start = strchr(p_comma + 1, ',');
if (!msg_start) return;
msg_start++; // 跳过逗号
// 提取信息b
strncpy(info_b, msg_start, length);
info_b[length] = '\0';
}MQTT连接
uart_print(&huart2,"AT+MQTTCLEAN=0\r\n");
uart_print(&huart2,"AT+CWJAP=\"aaa\",\"aaaaaaaa\"\r\n");
uart_print(&huart2,"AT+MQTTUSERCFG=0,1,\"fs\",\"111111\",\"272935657\",0,0,\"\"\r\n");
uart_print(&huart2,"AT+MQTTCONN=0,\"45.192.110.61\",1883,1\r\n");
uart_print(&huart2,"AT+MQTTSUB=0,\"topic1\",1\r\n");MQTT发送信息
uart_print(&huart2,"AT+MQTTPUB=0,\"topic1\",\"Ha\",0,0\r\n");主要思路
- 先初始化,连接MQTT服务器
- 完成串口不定长接收
- 接收消息后解析并打印