主控芯片:MM32F2377 (MB-039)

WiFi 适配器:ESP8266

开发环境:IAR 7.80.4

调试助手:ESP8266 调试工具V2.2

ESP8266 AT 指令烧录工具:flash_download_tool_3.9.2.exe

网络调试工具:hercules_3-2-8.exe (TCP)

从上周开始使用 ESP8266,磕磕绊绊终于实现了 TCP 和 MQTT 的通讯。

ESP8266 简介

ESP8266 由乐鑫公司开发,提供了一套高度集成的 Wi-Fi SoC 解决方案。

  • 通讯接口:UART
  • 用户配置:AT 指令集
  • 工作模式:
  1. Station:作为客户端连接路由器
  2. AP:ESP8266 自身作为热点供用户连接
  3. Station + AP:即能作为热点也能作为终端设备
  • AT 指令:
  1. AT 指令下载:乐鑫官方 AT 下载地址 / Ai-thinker AT 下载地址
  2. 指令详情可见 ESP-AT 用户指南
  • 烧录工具:ESP8266Flasher 或者 flash_download_tool

ESP8266 烧录 AT 指令集

这里使用的是 flash_download_tool.exe 工具,AT 包用的是乐鑫官方提供的 v2.2.1.0 ESP8266-IDF-AT_V2.2.1.0.zip

选择 factory_WROOM-02.bin ,地址选择 0x00

esp8266配置udp esp8266 tcp_#include

勾选正确后,点击 START,这里需要你按一下 RST 键,或将 RST 接口重新上电复位,之后就会开始烧录了。

ESP8266 连接 USB 转 TTL

烧录完 AT 指令后,先将 ESP8266 连接 USB 转 TTL 设备试一下,我这里用的是 YS-CH340,连接方式如下:

esp8266配置udp esp8266 tcp_#include_02

这里使用的调试工具是 ESP8266调试工具V2.2,一开始用的是山外多功能调试助手,发送 AT 指令没有应答,后来发现山外多功能调试助手的回车只发送’\n’,不发送’\r’,而 AT 指令需要 ‘\r\n’。

  1. 首先测试 AT 指令:发送 AT,应答 OK
  2. 配置 Station 模式:发送 AT+CWMODE=1,应答 OK

查询当前模式:AT+CWMODE?,应答 +CWMODE:1

  1. 连接 WiFi:发送 AT+CWJAP="xxx","xxx",第一个参数是热点名,第二个参数是密码,连接成功应答 WIFI CONNECTED WIFI GOT IP OK
  2. esp8266配置udp esp8266 tcp_esp8266配置udp_03

  3. 还可以扫描当前可用的 WiFi:AT+CWLAP,应答 +CWLAP:<ecn>,<ssid>,<rssi>,<mac>,<channel>,<freq_offset>,<freqcal_val>,<pairwise_cipher>,<group_cipher>,<bgn>,<wps> OK也可以获取 ESP8266 当前 IP 地址和 MAC 地址:AT+CIFSR,应答 +CIFSR:STAIP,"10.3.1.120" +CIFSR:STAMAC,"98:cd:ac:0b:cd:45" OK

MM32 连接 ESP8266 实现 TCP 传输

接下来我们就把 ESP8266 连接到主控 MM32F3277 上,这里连接的是 UART8

  1. 首先配置 MM32F3277 UART:
  • UART1: 用于 printf 打印错误报告
  • UART8: 用于 MM32 和 ESP8266 进行通讯
#define _UART_C_

#include <string.h>
#include <stdio.h>	
#include "mm32_types.h"

#include "common.h"
#include "uart.h"

#include "hal_uart.h"
#include "hal_gpio.h"
#include "hal_nvic.h"

#define BUFFERSIZE  516
GLOBAL char rxBuffer[BUFFERSIZE];
GLOBAL bool stringStart;


/// @brief  printf redirection function.
/// @param  ch: One character.
/// @param  f: File pointer.
/// @retval int32_t: One character.

#ifdef __GNUC__
#    define PUTCHAR_PROTOTYPE int32_t __io_putchar(int32_t ch)
#else
#    define PUTCHAR_PROTOTYPE int32_t fputc(int32_t ch, FILE * f)
#endif
PUTCHAR_PROTOTYPE
{
    UART_SendData(UART1, (uint8_t)ch);
    while (UART_GetFlagStatus(UART1, UART_CSR_TXC) == RESET);
    return ch;
}


/// @brief  UART GPIO Configuration.
/// @param  UARTx: Select the UART or the UART peripheral.
/// @retval None.

void initGPIO_UART(UART_TypeDef *UARTx)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	if(UARTx == UART1){
   		COMMON_EnableIpClock(emCLOCK_GPIOA);

    	// UART1_TX   GPIOA.9
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_7);

    	// UART1_RX   GPIOA.10
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_7);
	}else if(UARTx == UART8){
    	COMMON_EnableIpClock(emCLOCK_GPIOE);

    	// UART8_TX   GPIOE.1
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_Init(GPIOE, &GPIO_InitStructure);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource1, GPIO_AF_8);

    	// UART8_RX   GPIOE.0
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    	GPIO_Init(GPIOE, &GPIO_InitStructure);
    	GPIO_PinAFConfig(GPIOE, GPIO_PinSource0, GPIO_AF_8);
	}
}


/// @brief  Initializes the UARTx peripheral according to the specified
///         parameters in the UART_InitStruct.
/// @param  UARTx: Select the UART or the UART peripheral.
/// @param  baudrate: UART communication baudrate.
/// @retval None.

void initUART(UART_TypeDef *UARTx, uint32_t baudrate)
{
	UART_InitTypeDef UART_InitStructure;

	if(UARTx == UART1){
    	// Init UART1
    	COMMON_EnableIpClock(emCLOCK_UART1);
    	UART_InitStructure.BaudRate      = baudrate;
    	UART_InitStructure.WordLength    = UART_WordLength_8b;
    	UART_InitStructure.StopBits      = UART_StopBits_1;
    	UART_InitStructure.Parity        = UART_Parity_No;
    	UART_InitStructure.Mode          = UART_GCR_RX | UART_GCR_TX;
    	UART_InitStructure.HWFlowControl = UART_HWFlowControl_None;

    	UART_Init(UART1, &UART_InitStructure);
    	UART_ITConfig(UART1, UART_IER_RX, ENABLE);
   		UART_Cmd(UART1, ENABLE);
	}else if(UARTx == UART8){
    	// Init UART8
    	COMMON_EnableIpClock(emCLOCK_UART8);
    	UART_InitStructure.BaudRate      = baudrate;
    	UART_InitStructure.WordLength    = UART_WordLength_8b;
    	UART_InitStructure.StopBits      = UART_StopBits_1;
    	UART_InitStructure.Parity        = UART_Parity_No;
    	UART_InitStructure.Mode          = UART_GCR_RX | UART_GCR_TX;
    	UART_InitStructure.HWFlowControl = UART_HWFlowControl_None;

    	UART_Init(UART8, &UART_InitStructure);
    	UART_ITConfig(UART8, UART_IER_RX, ENABLE);
    	UART_Cmd(UART8, ENABLE);
	}
}


/// @brief  Configure UART NVIC.
/// @param  UARTx: Select the UART or the UART peripheral.
/// @retval None.

void NVIC_UART(UART_TypeDef *UARTx)
{
	NVIC_InitTypeDef NVIC_InitStructure;

	if(UARTx == UART1){
   		// UART1 NVIC
    	NVIC_InitStructure.NVIC_IRQChannel = UART1_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStructure);
	}else if(UARTx == UART8){
    	// UART8 NVIC
    	NVIC_InitStructure.NVIC_IRQChannel = UART8_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStructure);
	}
}


/// @brief  UART8 Receive data by interrupt.
/// @param  None.
/// @retval None.

void UART8_IRQHandler(void)
{
	if(UART_GetITStatus(UART8, UART_ISR_RX) != RESET) {
    	UART_ClearITPendingBit(UART8, UART_ISR_RX);
    	static u16 rCnt = 0;
    	if(stringStart){
        	rCnt = 0;
        	stringStart = 0;
    	}
    	*(rxBuffer + rCnt) = UART_ReceiveData(UART8);
    	rCnt++;
    	if (rCnt >= BUFFERSIZE){
        	rCnt = 0;
    	}
	}
}


/// @brief  UART send package data.
/// @param  UARTx: Select the UART or the UART peripheral.
/// @param  ptr: Data sent by UART.
/// @param  len: Length of data.
/// @retval None.

void UART_SendPackage(UART_TypeDef *UARTx, u8* ptr, u16 len)
{
	while(len--){
    	UART_SendData(UARTx, *(u16*)ptr);
    	ptr++;
    	while (UART_GetFlagStatus(UARTx, UART_CSR_TXC) == RESET);
	}
}


/// @brief  Clear Rx Buffer.
/// @param  None.
/// @retval None.

void UART_ClearRxBuffer()
{
	memset(rxBuffer, 0, sizeof(rxBuffer));
	stringStart = 1;
}


/// @brief  Initialize UART on board MB-039.
/// @param  None.
/// @retval None.

void BSP_UART_Configure()
{
	initGPIO_UART(UART1);
	initUART(UART1, 115200);

	initGPIO_UART(UART8);
	initUART(UART8, 115200);
	NVIC_UART(UART8);

	memset(rxBuffer, 0x00, sizeof(rxBuffer));
}
  1. 配置网络调试助手,模拟 TCP Server,这里用的是 hercules,不知道为什么我电脑上山外的网络调试助手不大行点击 Listen,开始监听
  2. 通过 MM32F3277 发送相应的 AT 指令给 ESP8266,连接 WiFi,连接 TCPAT 指令的顺序是:
  1. 退出透传:+++ 不要 \r\n,只要三个 +,这一步是防止设备处于透传模式,会讲 AT 指令当做数据进行发送
  2. 重启复位:AT+RST\r\n,检查应答 OK,如果之前连接过 WiFi,这一步之后会自动连接 WiFi
  3. 配置 Station 模式:AT+CWMODE=1\r\n,检查应答 OK
  4. 连接 WiFi:AT+CWJAP=\"WiFi_Name\",\"password\"\r\n,检查应答 OK,注意这里的引号需要添加转义符
  5. 连接 TCP:AT+CIPSTART=\"TCP\",\"IP_Address\",Port_Num,检查应答 OK
  6. 配置透传模式:AT+CIPMODE=1,检查应答 OK
  7. 开始发送数据:AT+CIPSEND,检查应答 >
  8. 就可以和 TCP 收发数据啦

下面是指令发送的几个函数:

/// @brief  Send commands to ESP8266 by UART.
/// @param  cmd: Commands sent to ESP8266.
/// @retval None.

void ESP8266_UART_SendCmd(char *cmd)
{
    UART_ClearRxBuffer();
    char *wholeCmd = stringEndJoint(cmd);
    UART_SendPackage(ESP8266_UART, (u8*)wholeCmd, strlen(wholeCmd));
}


/// @brief  Send AT commands to ESP8266 and check ack.
/// @param  cmd: Commands sent to ESP8266.
/// @param  ack: Ack from ESP8266 after receiving AT commands.
/// @param  longestWaitTime: Longest time waitting for right ack.
/// @retval ESP8266 OK or ERROR.

ESP8266_Error_Typedef ESP8266_Send_AT_Command(char *cmd, char *ack, u32 longestWaitTime)
{
    u32 timeOut = 0;
    ESP8266_UART_SendCmd(cmd);
    while(strstr(rxBuffer, ack) == NULL){
        timeOut++;
        if(timeOut == longestWaitTime) break;
    }

    if(timeOut < longestWaitTime){
        printf("Send AT Command:%s   Get Ack:%s \n", cmd, ack);
        return ESP8266_OK;
    }else{
        printf("Send AT Command:%s   No Right Ack:%s \n", cmd, ack);
        return ESP8266_ERROR;
    }
}


/// @brief  Sending AT commands to ESP8266 for maxTimes.
/// @param  cmd: Commands sent to ESP8266.
/// @param  ack: Ack from ESP8266 after receiving AT commands.
/// @param  longestWaitTime: Longest time waitting for right ack.
/// @param  maxTimes: Send commands for times. Return ERROR if no right ack.
/// @retval ESP8266 OK or ERROR.

ESP8266_Error_Typedef ESP8266_Send_AT_Command_Times(char *cmd, char *ack, u32 longestWaitTime, u8 maxTimes)
{
    u8 times = 0;
    while(ESP8266_Send_AT_Command(cmd, ack, longestWaitTime)){
        if(++times >= maxTimes) break;
    }
    if(times < maxTimes){
        return ESP8266_OK;
    }else{
        return ESP8266_ERROR;
    }
}


/// @brief  Joint string cmd1 and cmd2.
/// @param  cmd1: First string.
/// @param  cmd2: Second string.
/// @retval The result string.

char* stringJoint(char *cmd1, char *cmd2)
{
    char *wholeCmd = (char *)malloc(strlen(cmd1) + strlen(cmd2) + 1);
    sprintf(wholeCmd, "%s%s", cmd1, cmd2);
    return wholeCmd;
}


/// @brief  Joint string cmd and comma.
/// @param  cmd: First string.
/// @retval The result string.

char* stringCommaJoint(char *cmd)
{
    char *sign = ",";
    char *wholeCmd = stringJoint(sign, cmd);
    return wholeCmd;
}


/// @brief  Joint string cmd and quotation.
/// @param  cmd: First string.
/// @retval The result string.

char* stringQutJoint(char *cmd)
{
    char *sign = "\"";
    char *wholeCmd = stringJoint(sign, stringJoint(cmd, sign));
    return wholeCmd;
}


/// @brief  Joint string cmd and \r\n.
/// @param  cmd: First string.
/// @retval The result string.

char* stringEndJoint(char *cmd)
{
    char *sign = "\r\n";
    char *wholeCmd = stringJoint(cmd, sign);
    return wholeCmd;
}
  1. 贴一张结果图,Congratulation! 是 MM32 发送给服务器的,123456 是服务器发送给 MM32 的