参考资料
- STM32F103参考手册:rm0008-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx
- STM32103ZE数据手册:STM32F103xC, STM32F103xD, STM32F103xE
ESP32-C3
AT固件烧录成功后,重启,串口输出日志如下:
这里要注意以下AT固件的串口波特率配置,稍后STM32和与之通信时,需要配置相同的波特率
硬件连接
- STM32F103ZET6
- ESP32-C3FN4
测试AT指令
Cube配置
串口1用来打印日志
串口2用来和ESP32通信
波特率需要和ESP32 AT固件的保持一致:115200
示例代码
esp32.h
//
// Created by 86157 on 2024/12/15.
//
#ifndef INC_40_ESP32_HAL_ESP32_H
#define INC_40_ESP32_HAL_ESP32_H
#include "stm32f1xx.h"
void ESP32_init(void);
void ESP32_SendAtCmd(char *atCmd);
#endif //INC_40_ESP32_HAL_ESP32_H
esp32.c
要特别注意复用缓冲区
responseBuf
时,如果上一次事务留存的数据影响下一次事务处理过程中的判断,需要在每次事务的开头将其清零。
//
// Created by 86157 on 2024/12/15.
//
#include "ESP32.h"
#include "usart.h"
#include <string.h>
#include "logger.h"
#define RXBUF_MAX_SIZE 256
#define RESPONSE_BUF_MAX_SIZE 512
#define AT_OK "OK"
#define AT_ERROR "ERROR"
#define AT_SUFFIX "\r\n"
#define AT_RST ("AT+RST" AT_SUFFIX)
#define AT_TEST ("AT" AT_SUFFIX)
#define AT_GMR ("AT+GMR" AT_SUFFIX)
uint8_t rxBuf[RXBUF_MAX_SIZE] = {0};
__IO uint8_t rxSize = 0;
uint8_t responseBuf[RESPONSE_BUF_MAX_SIZE] = {0};
__IO uint8_t responseSize = 0;
void ESP32_init(void) {
MX_USART2_UART_Init();
// 接收定长/空闲检测+中断 非阻塞接收数据到rxBuf中
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rxBuf, RXBUF_MAX_SIZE);
// 发送复位指令
ESP32_SendAtCmd(AT_RST);
HAL_Delay(2000); // 等待芯片复位完成
// 测试AT指令
ESP32_SendAtCmd(AT_TEST);
ESP32_SendAtCmd(AT_GMR);
}
void ESP32_SendAtCmd(char *atCmd) {
responseSize = 0; // 记录发送指令后,收到的响应字节数量
rxSize = 0; // 记录接收中断时收到的字节数量
memset(responseBuf, 0, sizeof(responseBuf)); // 必须清零,避免残留数据影响对后续流程的判断
HAL_UART_Transmit(&huart2, (uint8_t *) atCmd, strlen(atCmd), 1000);
// 最多接收三次响应
uint8_t rxCount = 3;
do {
// 轮询等待AT响应
uint32_t timeout = 0xFFFFFF;
while (rxSize == 0 && timeout--)
;
// 收到一次响应,将接收缓冲区rxBuf收到的数据拷贝到响应结果中暂存
memcpy(responseBuf + responseSize, rxBuf, rxSize);
responseSize += rxSize;
rxSize = 0; // 继续接收响应
} while (strstr((char *) responseBuf, AT_OK) == NULL &&
strstr((char *) responseBuf, AT_ERROR) == NULL && rxCount--);
LOG_DEBUG("rxCount = %d, responseSize = %d, responseBuf = %s", rxCount, responseSize, (char *)responseBuf)
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart == &huart2) {
rxSize = Size;
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rxBuf, RXBUF_MAX_SIZE);
}
}
main.c
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
LOG_DEBUG("Hello, World!\n");
ESP32_init();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
测试
通过ESP连接热点
创建热点
使用手机创建一个2.4G的热点,设置热点名称和密码,以备后续esp32来连接该热点
示例代码
esp32.c
#define AT_CWMODE_STATION ("AT+CWMODE=1" AT_SUFFIX)
#define AT_CWJAP ("AT+CWJAP=\"" WIFI_NAME "\",\"" WIFI_PASSWORD "\"" AT_SUFFIX)
#define AT_CWSTATE ("AT+CWSTATE?" AT_SUFFIX)
// 热点的名称和密码
#define WIFI_NAME "HONOR 90"
#define WIFI_PASSWORD "Zawalliswell1998"
void ESP32_ModeStation(void) {
// 设置WIFI工作模式:station(作为终端连接热点)
ESP32_SendAtCmd(AT_CWMODE_STATION);
// 设置热点的名称和密码
ESP32_SendAtCmd(AT_CWJAP);
// 查看WIFI状态(是否连接成功)
ESP32_SendAtCmd(AT_CWSTATE);
}
main.c
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
LOG_DEBUG("Hello, World!\n");
ESP32_init();
ESP32_ModeStation();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
完整代码
esp32.h
//
// Created by 86157 on 2024/12/15.
//
#ifndef INC_40_ESP32_HAL_ESP32_H
#define INC_40_ESP32_HAL_ESP32_H
#include "stm32f1xx.h"
void ESP32_init(void);
void ESP32_SendAtCmd(char *atCmd);
void ESP32_ModeStation(void);
#endif//INC_40_ESP32_HAL_ESP32_H
esp32.c
//
// Created by 86157 on 2024/12/15.
//
#include "esp32.h"
#include "logger.h"
#include "usart.h"
#include <string.h>
#define RXBUF_MAX_SIZE 256
#define RESPONSE_BUF_MAX_SIZE 512
#define AT_OK "OK"
#define AT_ERROR "ERROR"
#define AT_SUFFIX "\r\n"
#define AT_RST ("AT+RST" AT_SUFFIX)
#define AT_TEST ("AT" AT_SUFFIX)
#define AT_GMR ("AT+GMR" AT_SUFFIX)
#define AT_CWMODE_STATION ("AT+CWMODE=1" AT_SUFFIX)
#define AT_CWJAP ("AT+CWJAP=\"" WIFI_NAME "\",\"" WIFI_PASSWORD "\"" AT_SUFFIX)
#define AT_CWSTATE ("AT+CWSTATE?" AT_SUFFIX)
// 热点的名称和密码
#define WIFI_NAME "HONOR 90"
#define WIFI_PASSWORD "Zawalliswell1998"
uint8_t rxBuf[RXBUF_MAX_SIZE] = {0};
__IO uint8_t rxSize = 0;
uint8_t responseBuf[RESPONSE_BUF_MAX_SIZE] = {0};
__IO uint8_t responseSize = 0;
void ESP32_init(void) {
MX_USART2_UART_Init();
// 接收定长/空闲检测+中断 非阻塞接收数据到rxBuf中
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rxBuf, RXBUF_MAX_SIZE);
// 发送复位指令
ESP32_SendAtCmd(AT_RST);
HAL_Delay(2000);// 等待芯片复位完成
// 测试AT指令
ESP32_SendAtCmd(AT_TEST);
ESP32_SendAtCmd(AT_GMR);
}
void ESP32_ModeStation(void) {
// 设置WIFI工作模式:station(作为终端连接热点)
ESP32_SendAtCmd(AT_CWMODE_STATION);
// 设置热点的名称和密码
ESP32_SendAtCmd(AT_CWJAP);
// 查看WIFI状态(是否连接成功)
ESP32_SendAtCmd(AT_CWSTATE);
}
void ESP32_SendAtCmd(char *atCmd) {
responseSize = 0;// 记录发送指令后,收到的响应字节数量
rxSize = 0; // 记录接收中断时收到的字节数量
memset(responseBuf, 0,
sizeof(responseBuf));// 必须清零,避免残留数据影响对后续流程的判断
HAL_UART_Transmit(&huart2, (uint8_t *) atCmd, strlen(atCmd), 1000);
// 最多接收五次响应
uint8_t rxCount = 5;
do {
// 轮询等待AT响应
uint32_t timeout = 0xFFFFFF;
while (rxSize == 0 && timeout--)
;
// 收到一次响应,将接收缓冲区rxBuf收到的数据拷贝到响应结果中暂存
memcpy(responseBuf + responseSize, rxBuf, rxSize);
responseSize += rxSize;
rxSize = 0;// 继续接收响应
} while (strstr((char *) responseBuf, AT_OK) == NULL &&
strstr((char *) responseBuf, AT_ERROR) == NULL && rxCount--);
LOG_DEBUG("rxCount = %d, responseSize = %d, responseBuf = %s", rxCount, responseSize,
(char *) responseBuf)
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart == &huart2) {
rxSize = Size;
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rxBuf, RXBUF_MAX_SIZE);
}
}
main.c
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
LOG_DEBUG("Hello, World!\n");
ESP32_init();
ESP32_ModeStation();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
测试
通过ESP搭建TCPServer
启动TCP服务
AT指令启动TCP服务
#define AT_CIPMUX_MULTI ("AT+CIPMUX=1" AT_SUFFIX)
#define AT_CREATE_TCP_SERVER ("AT+CIPSERVER=1," TCP_SERVER_PORT AT_SUFFIX)
#define AT_IPD_MODE1 ("AT+CIPDINFO=1" AT_SUFFIX)
// TCP服务端口
#define TCP_SERVER_PORT "8080"
void ESP32_TCP_StartServer(void) {
// 启用复用连接
ESP32_SendAtCmd(AT_CIPMUX_MULTI);
// 启动TCP服务,指定端口
ESP32_SendAtCmd(AT_CREATE_TCP_SERVER);
/*
* 设置入站数据包格式(incoming package data)
* 0: does not show the remote host and port in “+IPD” and “+CIPRECVDATA” messages.
* 1: show the remote host and port in “+IPD” and “+CIPRECVDATA” messages.
* */
ESP32_SendAtCmd(AT_IPD_MODE1);
}
轮询接收TCP入站数据包
void ESP32_TCP_HandleReceive(void) {
if (rxSize) {
LOG_DEBUG("%.*s", rxSize, rxBuf);
rxSize = 0;
}
}
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
LOG_DEBUG("Hello, World!\n");
ESP32_init();
ESP32_ModeStation();
ESP32_TCP_StartServer();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
ESP32_TCP_HandleReceive();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
测试TCP连接
注意:
- 在手机热点上查看分配给esp、电脑的IP地址,本例中为
192.168.201.181
、192.168.201.38
- 远程端口要和AT指令
AT+CIPSERVER
中指定的端口号一致,本地端口填0会自动分配
连接后,我们的 ESP32_TCP_HandleReceive
会打印ESP发给我们的TCP入站数据包
入站数据包解析
esp32.c
void ESP32_TCP_HandleReceive(uint16_t *connectionId, uint8_t *ip, uint16_t *port,
uint16_t *dataSize, uint8_t *data) {
if (rxSize) {
// 解析esp抄送的入站数据包,示例:\r\n+IPD,0,5,"192.168.201.38",51105:hello\r\n
if (strstr((char *) rxBuf, "+IPD")) {
LOG_DEBUG("<%s>", rxBuf)
sscanf((char *) rxBuf, "%*[\r\n]+IPD,%hu,%hu,\"%[^\"]\",%hu", connectionId,
dataSize, ip, port);
// 数据中可能包含\r\n,scanf遇到后会停止匹配,因此需要单独解析
strtok((char *) rxBuf, ":");
memcpy(data, strtok(NULL, ":"), *dataSize);
LOG_DEBUG("connectionId = %hu, ip = %s, port = %hu, dataSize = %hu, data = "
"%.*s",
*connectionId, ip, *port, *dataSize, *dataSize, data);
}
memset(rxBuf, 0, sizeof(rxBuf));
rxSize = 0;
}
}
main.c
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
LOG_DEBUG("Hello, World!\n");
ESP32_init();
ESP32_ModeStation();
ESP32_TCP_StartServer();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint16_t connectionId = 0;
uint8_t ip[16] = {0};
uint16_t port = 0;
uint16_t dataSize = 0;
uint8_t data[256] = {0};
while (1)
{
ESP32_TCP_HandleReceive(&connectionId, ip, &port, &dataSize, data);
memset(ip, 0, sizeof(ip));
memset(data, 0, sizeof(data));
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
测试
发送数据给客户端
esp32.c
#define AT_CIPMUX_MULTI ("AT+CIPMUX=1" AT_SUFFIX)
#define AT_CREATE_TCP_SERVER ("AT+CIPSERVER=1," TCP_SERVER_PORT AT_SUFFIX)
#define AT_IPD_MODE1 ("AT+CIPDINFO=1" AT_SUFFIX)
#define AT_CIPSEND ("AT+CIPSEND=%hu,%hu" AT_SUFFIX)
char sendBuf[64] = {0};
void ESP32_TCP_HandleTransmit(uint16_t connectionId, uint8_t *data, uint16_t dataSize) {
if (dataSize <= 0) {
return;
}
memset(sendBuf, 0, sizeof(sendBuf) / sizeof(sendBuf[0]));
sprintf(sendBuf, AT_CIPSEND, connectionId, dataSize);
ESP32_SendAtCmd(sendBuf);
HAL_UART_Transmit(&huart2, data, dataSize, 1000);
ESP32_WaitResponse();
}
static void ESP32_WaitResponse(void) {
responseSize = 0;// 记录发送指令后,收到的响应字节数量
rxSize = 0; // 记录每次接收中断收到的字节数量
// 必须清零,避免残留数据影响对后续流程的判断
memset(responseBuf, 0, sizeof(responseBuf));
// 最多接收五次响应
uint8_t rxCount = 5;
do {
// 轮询等待AT响应
uint32_t timeout = 0xFFFFFF;
while (rxSize == 0 && timeout--)
;
// 收到一次响应,将接收缓冲区rxBuf收到的数据拷贝到响应结果中暂存
memcpy(responseBuf + responseSize, rxBuf, rxSize);
responseSize += rxSize;
rxSize = 0;// 继续接收响应
} while (strstr((char *) responseBuf, AT_OK) == NULL &&
strstr((char *) responseBuf, AT_ERROR) == NULL && rxCount--);
LOG_DEBUG("rxCount = %d, responseSize = %d, responseBuf = %s", rxCount, responseSize,
(char *) responseBuf)
}
main.c
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
LOG_DEBUG("Hello, World!\n");
ESP32_init();
ESP32_ModeStation();
ESP32_TCP_StartServer();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint16_t connectionId = 0;
uint8_t ip[16] = {0};
uint16_t port = 0;
uint16_t dataSize = 0;
uint8_t data[256] = {0};
while (1)
{
memset(ip, 0, sizeof(ip));
memset(data, 0, sizeof(data));
dataSize = 0;
ESP32_TCP_HandleReceive(&connectionId, ip, &port, &dataSize, data);
ESP32_TCP_HandleTransmit(connectionId, data, dataSize);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
测试
完整代码
esp32.h
//
// Created by 86157 on 2024/12/15.
//
#ifndef INC_40_ESP32_HAL_ESP32_H
#define INC_40_ESP32_HAL_ESP32_H
#include "stm32f1xx.h"
void ESP32_init(void);
void ESP32_SendAtCmd(char *atCmd);
void ESP32_ModeStation(void);
void ESP32_TCP_StartServer(void);
void ESP32_TCP_HandleReceive(uint16_t *connectionId, uint8_t *ip, uint16_t *port,
uint16_t *dataSize, uint8_t *data);
void ESP32_TCP_HandleTransmit(uint16_t connectionId, uint8_t *data, uint16_t dataSize);
#endif//INC_40_ESP32_HAL_ESP32_H
esp32.c
//
// Created by 86157 on 2024/12/15.
//
#include "esp32.h"
#include "logger.h"
#include "usart.h"
#include <string.h>
#define RXBUF_MAX_SIZE 256
#define RESPONSE_BUF_MAX_SIZE 512
#define AT_OK "OK"
#define AT_ERROR "ERROR"
#define AT_SUFFIX "\r\n"
#define AT_RST ("AT+RST" AT_SUFFIX)
#define AT_TEST ("AT" AT_SUFFIX)
#define AT_GMR ("AT+GMR" AT_SUFFIX)
#define AT_CWMODE_STATION ("AT+CWMODE=1" AT_SUFFIX)
#define AT_CWJAP ("AT+CWJAP=\"" WIFI_NAME "\",\"" WIFI_PASSWORD "\"" AT_SUFFIX)
#define AT_CWSTATE ("AT+CWSTATE?" AT_SUFFIX)
#define AT_CIPMUX_MULTI ("AT+CIPMUX=1" AT_SUFFIX)
#define AT_CREATE_TCP_SERVER ("AT+CIPSERVER=1," TCP_SERVER_PORT AT_SUFFIX)
#define AT_IPD_MODE1 ("AT+CIPDINFO=1" AT_SUFFIX)
#define AT_CIPSEND ("AT+CIPSEND=%hu,%hu" AT_SUFFIX)
// TCP服务端口
#define TCP_SERVER_PORT "8080"
// 热点的名称和密码
#define WIFI_NAME "HONOR 90"
#define WIFI_PASSWORD "Zawalliswell1998"
uint8_t rxBuf[RXBUF_MAX_SIZE] = {0};
__IO uint8_t rxSize = 0;
uint8_t responseBuf[RESPONSE_BUF_MAX_SIZE] = {0};
__IO uint8_t responseSize = 0;
static void ESP32_WaitResponse(void);
void ESP32_init(void) {
MX_USART2_UART_Init();
// 接收定长/空闲检测+中断 非阻塞接收数据到rxBuf中
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rxBuf, RXBUF_MAX_SIZE);
// 发送复位指令
ESP32_SendAtCmd(AT_RST);
HAL_Delay(2000);// 等待芯片复位完成
// 测试AT指令
ESP32_SendAtCmd(AT_TEST);
ESP32_SendAtCmd(AT_GMR);
}
void ESP32_ModeStation(void) {
// 设置WIFI工作模式:station(作为终端连接热点)
ESP32_SendAtCmd(AT_CWMODE_STATION);
// 设置热点的名称和密码
ESP32_SendAtCmd(AT_CWJAP);
// 查看WIFI状态(是否连接成功)
ESP32_SendAtCmd(AT_CWSTATE);
}
void ESP32_TCP_StartServer(void) {
// 启用复用连接
ESP32_SendAtCmd(AT_CIPMUX_MULTI);
// 启动TCP服务,指定端口
ESP32_SendAtCmd(AT_CREATE_TCP_SERVER);
/*
* 设置入站数据包格式(incoming package data)
* 0: does not show the remote host and port in “+IPD” and “+CIPRECVDATA” messages.
* 1: show the remote host and port in “+IPD” and “+CIPRECVDATA” messages.
* */
ESP32_SendAtCmd(AT_IPD_MODE1);
}
void ESP32_TCP_HandleReceive(uint16_t *connectionId, uint8_t *ip, uint16_t *port,
uint16_t *dataSize, uint8_t *data) {
if (rxSize) {
// 解析esp抄送的入站数据包,示例:\r\n+IPD,0,5,"192.168.201.38",51105:hello\r\n
if (strstr((char *) rxBuf, "+IPD")) {
//LOG_DEBUG("<%s>", rxBuf)
sscanf((char *) rxBuf, "%*[\r\n]+IPD,%hu,%hu,\"%[^\"]\",%hu", connectionId,
dataSize, ip, port);
// 数据中可能包含\r\n,scanf遇到后会停止匹配,因此需要单独解析
strtok((char *) rxBuf, ":");
memcpy(data, strtok(NULL, ":"), *dataSize);
LOG_DEBUG("connectionId = %hu, ip = %s, port = %hu, dataSize = %hu, data = "
"%.*s",
*connectionId, ip, *port, *dataSize, *dataSize, data);
}
memset(rxBuf, 0, sizeof(rxBuf));
rxSize = 0;
}
}
char sendBuf[64] = {0};
void ESP32_TCP_HandleTransmit(uint16_t connectionId, uint8_t *data, uint16_t dataSize) {
if (dataSize <= 0) {
return;
}
memset(sendBuf, 0, sizeof(sendBuf) / sizeof(sendBuf[0]));
sprintf(sendBuf, AT_CIPSEND, connectionId, dataSize);
ESP32_SendAtCmd(sendBuf);
HAL_UART_Transmit(&huart2, data, dataSize, 1000);
ESP32_WaitResponse();
}
void ESP32_SendAtCmd(char *atCmd) {
HAL_UART_Transmit(&huart2, (uint8_t *) atCmd, strlen(atCmd), 1000);
ESP32_WaitResponse();
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart == &huart2) {
rxSize = Size;
HAL_UARTEx_ReceiveToIdle_IT(&huart2, rxBuf, RXBUF_MAX_SIZE);
}
}
static void ESP32_WaitResponse(void) {
responseSize = 0;// 记录发送指令后,收到的响应字节数量
rxSize = 0; // 记录每次接收中断收到的字节数量
// 必须清零,避免残留数据影响对后续流程的判断
memset(responseBuf, 0, sizeof(responseBuf));
// 最多接收五次响应
uint8_t rxCount = 5;
do {
// 轮询等待AT响应
uint32_t timeout = 0xFFFFFF;
while (rxSize == 0 && timeout--)
;
// 收到一次响应,将接收缓冲区rxBuf收到的数据拷贝到响应结果中暂存
memcpy(responseBuf + responseSize, rxBuf, rxSize);
responseSize += rxSize;
rxSize = 0;// 继续接收响应
} while (strstr((char *) responseBuf, AT_OK) == NULL &&
strstr((char *) responseBuf, AT_ERROR) == NULL && rxCount--);
LOG_DEBUG("rxCount = %d, responseSize = %d, responseBuf = %s", rxCount, responseSize,
(char *) responseBuf)
}
main.c
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
LOG_DEBUG("Hello, World!\n");
ESP32_init();
ESP32_ModeStation();
ESP32_TCP_StartServer();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint16_t connectionId = 0;
uint8_t ip[16] = {0};
uint16_t port = 0;
uint16_t dataSize = 0;
uint8_t data[256] = {0};
while (1)
{
memset(ip, 0, sizeof(ip));
memset(data, 0, sizeof(data));
dataSize = 0;
ESP32_TCP_HandleReceive(&connectionId, ip, &port, &dataSize, data);
ESP32_TCP_HandleTransmit(connectionId, data, dataSize);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
实验-ESP32蓝牙透传
官方示例
在ESP32和手机之间建立SPP(Serial Port Profile, UART-Bluetooth LE passthrough mode)连接后,能够实现MCU串口和手机之间经过透传通道(对两者之间BLE的存在无感知)收发原始数据。
透传的好处(MCU对于BLE无感知):
- 与之前的WIFI案例对比,MCU接收数据不需要解析
+IPD
格式的数据包了,通过串口接收到的就是手机发送的原始数据- MCU发送数据直接发到串口,不需要先指定AT指令了(对比之前案例中的
AT+CIPSEND
)
蓝牙广播&连接
void ESP32_Bluetooth_Init(void) {
ESP32_init();
// 示例:https://docs.espressif.com/projects/esp-at/en/latest/esp32/AT_Command_Examples/bluetooth_le_at_examples.html#establish-spp-connection-between-esp32-and-mobile-phone-and-transmit-data-in-uart-bluetooth-le-passthrough-mode
// 初始化蓝牙,模式为服务端(等待被连接)
ESP32_SendAtCmd(AT_BLEINIT_SERVER);
// 服务端创建GATT服务
ESP32_SendAtCmd(AT_BLEGATTSSRVCRE);
// 服务端启动服务
ESP32_SendAtCmd(AT_BLEGATTSSRVSTART);
// 服务端设置广播参数,例如广播时间间隔
ESP32_SendAtCmd(AT_BLEADVPARAM);
// 服务端设置广播数据,例如蓝牙名称
ESP32_SendAtCmd(AT_BLEADVDATAEX);
// 服务端开始广播
ESP32_SendAtCmd(AT_BLEADVSTART);
}
启动日志
连接蓝牙
连接建立/断开日志
WIFI连接/断开日志
此前的案例对WIFI的配置被保存到了NVS(Non-Volatile Storage非易失性存储)中。
见AT+CWJAP中的note
手机发送数据
设置连接为透传模式
初始化时增加透传相关参数的提前配置
void ESP32_Bluetooth_Init(void) {
ESP32_init();
// 示例:https://docs.espressif.com/projects/esp-at/en/latest/esp32/AT_Command_Examples/bluetooth_le_at_examples.html#establish-spp-connection-between-esp32-and-mobile-phone-and-transmit-data-in-uart-bluetooth-le-passthrough-mode
// 初始化蓝牙,模式为服务端(等待被连接)
ESP32_SendAtCmd(AT_BLEINIT_SERVER);
// 服务端创建GATT服务
ESP32_SendAtCmd(AT_BLEGATTSSRVCRE);
// 服务端启动服务
ESP32_SendAtCmd(AT_BLEGATTSSRVSTART);
// 服务端设置广播参数,例如广播时间间隔
ESP32_SendAtCmd(AT_BLEADVPARAM);
// 服务端设置广播数据,例如蓝牙名称
ESP32_SendAtCmd(AT_BLEADVDATAEX);
// 服务端开始广播
ESP32_SendAtCmd(AT_BLEADVSTART);
// 配置透传参数
ESP32_SendAtCmd(AT_BLESPPCFG);
// 透传模式下,Wi-Fi、socket、Bluetooth LE 或 Bluetooth 状态改变时会打印提示信息
ESP32_SendAtCmd(AT_SYSMSG);
}
启动蓝牙后根据ESP的连接状态提示信息进入或退出透传
esp进入透传模式后,就不能给esp发送AT指令了,因为通过串口发给esp的,esp会原样通过蓝牙发送给客户端。详见 note
void ESP32_Bluetooth_HandleReceive(void) {
if (rxSize) {
LOG_DEBUG("%.*s", rxSize, rxBuf)
if (ESP32_Bluetooth_HandleConnChange() == BLE_DATA) {
LOG_DEBUG("收到客户端数据 = %s", rxBuf);
}
rxSize = 0;
memset(rxBuf, 0, sizeof(rxBuf));
}
}
/*
* 蓝牙连接/断开
* [DEBUG esp32.c:86] +BLECONN:0,"60:8f:c2:b1:9a:22"
* [DEBUG esp32.c:86] +BLEDISCONN:0,"60:8f:c2:b1:9a:22"
*
* [DEBUG esp32.c:86] +BLECONNPARAM:0,0,0,6,0,500
* [DEBUG esp32.c:86] +BLESETPHY:"60:8f:c2:b1:9a:22",2,2
* [DEBUG esp32.c:86] +BLECONNPARAM:0,0,0,24,0,500
*
* WIFI连接/断开
* [DEBUG esp32.c:86] WIFI DISCONNECT
* [DEBUG esp32.c:86] WIFI CONNECTED
* [DEBUG esp32.c:86] WIFI GOT IP
*
* 客户端(如手机蓝牙调试助手)发来的数据
* [DEBUG esp32.c:86] +WRITE:0,1,3,,4,1234
* */
ESP32_BLE_STATE ESP32_Bluetooth_HandleConnChange(void) {
if (strstr((char *)rxBuf, "+BLECONN:")) {
// 连接建立,开启透传(蓝牙收到的数据原样发给串口,串口发给蓝牙的数据原样发给客户端)
LOG_DEBUG("连接建立成功,准备使能透传")
ESP32_SendAtCmd(AT_BLESPP); // 使能透传
LOG_DEBUG("连接建立成功,使能透传完成")
return BLE_CONNECT;
}
if (strstr((char *)rxBuf, "+BLEDISCONN:")) {
LOG_DEBUG("连接断开,关闭透传模式")
// 连接断开,关闭透传,进入正常命令模式。要特别注意透传模式下,直接发送“+++”才能返回正常命令模式
HAL_UART_Transmit(&huart2, (uint8_t *)"+++", 3, 10);
HAL_Delay(1000); // 至少等待1s以确保esp返回命令模式
LOG_DEBUG("进入正常命令模式")
// 继续广播,等待连接
LOG_DEBUG("开始广播")
ESP32_SendAtCmd(AT_BLEADVSTART);
return BLE_DISCONNECT;
}
if(strstr((char *)rxBuf, "WIFI")) {
LOG_DEBUG("wifi状态变化")
return BLE_WIFI_CHANGE;
}
if (strstr((char *)rxBuf, "+BLECONNPARAM:") || strstr((char *)rxBuf, "+BLESETPHY:")) {
return BLE_OTHER;
}
return BLE_DATA;
}
手机连接蓝牙并发送数据
手机断开连接 & 重连
蓝牙向手机发送数据
通过串口透传SPP可以直接将数据发送到客户端