MM32F3277 + ESP8266 使用指南(1. 实现 TCP Client 透传)

2024-01-16 17:20

本文主要是介绍MM32F3277 + ESP8266 使用指南(1. 实现 TCP Client 透传),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

主控芯片: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

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

ESP8266 连接 USB 转 TTL

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

esp8266_ttl

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

  1. 首先测试 AT 指令:发送 AT,应答 OK

    AT

  2. 配置 Station 模式:发送 AT+CWMODE=1,应答 OK

    AT_STATION

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

  3. 连接 WiFi:发送 AT+CWJAP="xxx","xxx",第一个参数是热点名,第二个参数是密码,连接成功应答 WIFI CONNECTED WIFI GOT IP OK
    AT_WIFI

    还可以扫描当前可用的 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.9GPIO_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.10GPIO_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.1GPIO_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.0GPIO_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 UART1COMMON_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 UART8COMMON_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 NVICNVIC_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 NVICNVIC_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));
    }
    
  2. 配置网络调试助手,模拟 TCP Server,这里用的是 hercules,不知道为什么我电脑上山外的网络调试助手不大行

    点击 Listen,开始监听

    tcp_server

  3. 通过 MM32F3277 发送相应的 AT 指令给 ESP8266,连接 WiFi,连接 TCP

    AT 指令的顺序是:

    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;
    }
    
  4. 贴一张结果图,Congratulation! 是 MM32 发送给服务器的,123456 是服务器发送给 MM32 的

    result

The End

踩着 4 月的尾巴写了一篇 CSDN,MQTT 的部分下周再说吧。

好害怕,我们还没能独当一面,还没来得及多陪陪长辈,没能给他们更好的生活,他们却已老去。

林阿公拜托一定一定要挺住,虽然现在很疼很累,但再坚持一下,我们都爱你,请再等等我们。

这篇关于MM32F3277 + ESP8266 使用指南(1. 实现 TCP Client 透传)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/613340

相关文章

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

java如何分布式锁实现和选型

《java如何分布式锁实现和选型》文章介绍了分布式锁的重要性以及在分布式系统中常见的问题和需求,它详细阐述了如何使用分布式锁来确保数据的一致性和系统的高可用性,文章还提供了基于数据库、Redis和Zo... 目录引言:分布式锁的重要性与分布式系统中的常见问题和需求分布式锁的重要性分布式系统中常见的问题和需求

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文