主控芯片: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 指令集
- 工作模式:
- Station:作为客户端连接路由器
- AP:ESP8266 自身作为热点供用户连接
- Station + AP:即能作为热点也能作为终端设备
- AT 指令:
- AT 指令下载:乐鑫官方 AT 下载地址 / Ai-thinker AT 下载地址
- 指令详情可见 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
勾选正确后,点击 START,这里需要你按一下 RST 键,或将 RST 接口重新上电复位,之后就会开始烧录了。
ESP8266 连接 USB 转 TTL
烧录完 AT 指令后,先将 ESP8266 连接 USB 转 TTL 设备试一下,我这里用的是 YS-CH340,连接方式如下:
这里使用的调试工具是 ESP8266调试工具V2.2,一开始用的是山外多功能调试助手,发送 AT 指令没有应答,后来发现山外多功能调试助手的回车只发送’\n’,不发送’\r’,而 AT 指令需要 ‘\r\n’。
-
首先测试 AT 指令:发送
AT
,应答OK
-
配置 Station 模式:发送
AT+CWMODE=1
,应答OK
查询当前模式:
AT+CWMODE?
,应答+CWMODE:1
-
连接 WiFi:发送
AT+CWJAP="xxx","xxx"
,第一个参数是热点名,第二个参数是密码,连接成功应答WIFI CONNECTED WIFI GOT IP OK
还可以扫描当前可用的 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
-
首先配置 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)); }
-
-
配置网络调试助手,模拟 TCP Server,这里用的是 hercules,不知道为什么我电脑上山外的网络调试助手不大行
点击 Listen,开始监听
-
通过 MM32F3277 发送相应的 AT 指令给 ESP8266,连接 WiFi,连接 TCP
AT 指令的顺序是:
- 退出透传:
+++
不要\r\n
,只要三个 +,这一步是防止设备处于透传模式,会讲 AT 指令当做数据进行发送 - 重启复位:
AT+RST\r\n
,检查应答OK
,如果之前连接过 WiFi,这一步之后会自动连接 WiFi - 配置 Station 模式:
AT+CWMODE=1\r\n
,检查应答OK
- 连接 WiFi:
AT+CWJAP=\"WiFi_Name\",\"password\"\r\n
,检查应答OK
,注意这里的引号需要添加转义符 - 连接 TCP:
AT+CIPSTART=\"TCP\",\"IP_Address\",Port_Num
,检查应答OK
- 配置透传模式:
AT+CIPMODE=1
,检查应答OK
- 开始发送数据:
AT+CIPSEND
,检查应答>
- 就可以和 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; }
- 退出透传:
-
贴一张结果图,
Congratulation!
是 MM32 发送给服务器的,123456
是服务器发送给 MM32 的
The End
踩着 4 月的尾巴写了一篇 优快云,MQTT 的部分下周再说吧。
好害怕,我们还没能独当一面,还没来得及多陪陪长辈,没能给他们更好的生活,他们却已老去。
林阿公拜托一定一定要挺住,虽然现在很疼很累,但再坚持一下,我们都爱你,请再等等我们。