进行这个之前首选要知道几个理解知识和工具。
下面是当时找的相关文档和资料连接:
ESP8266笔记 ---- 常用AT指令_esp8266 at-CSDN博客
esp8266WIFI模块教程:正点原子ATK-ESP8266进行网络通信,单片机与电脑,单片机与手机发送数据_正点原子esp8266-CSDN博客
【常用模块】ESP8266 WIFI串口通信模块使用详解(实例:附STM32详细代码)_正点原子wifiesp8266提供代码-CSDN博客
最有用的是乐鑫官方给的AT指令操作示例:
TCP-IP AT 示例 - ESP32 - — ESP-AT 用户指南 latest 文档 (espressif.com)
首先说说AT指令:
AT指令,也称为Attention指令,是应用于终端设备与PC应用之间的连接与通信的指令。这些指令是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter,TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。AT指令主要用于控制移动台(Mobile Station,MS)的功能,与GSM网络业务进行交互。
工具:串口工具(调试助手),网络调试助手。
下面了解一下当时使用的WIFI模块:
https://docs.ai-thinker.com/_media/esp8266/docs/esp-12f_product_specification_zh_v1.0.pdf
型号:安信可ESP-12F。
模块的定义与尺寸图:
引脚定义:
模组启动模式:
推荐的电路连接方式(正常工作模式)
下载步骤:
下载模式需要把只需要把GPIO0接到地,rst先浮空,配置好flash下载软件之后,先点击擦除,之后会等待上电复位,将rst用杜邦线触碰一下地,即可开始烧录,此时等待烧录完毕,将GPIO接到高电平,重新上电即可正常使用AT指令集。
连接调试示意图:
使用串口助手操作时的相关信息显示:
当前程序具备收发功能,请注意,由于我的编程习惯可能不够规范,代码可能显得有些凌乱,建议仔细阅读和理解代码逻辑。
本程序在开发过程中参考了正点原子的代码,在使用串口助手进行调试时,务必确保所有发送的指令后都加上换行符。这是AT指令集的规定,如果不加换行符,指令将无法正确执行,并返回错误提示。
网络调试助手需设置为TCP服务器模式。在选择服务器的IP地址时,请确保它与wifi芯片所分配的IP地址处于同一网段。例如,如果通过查询得到的IP地址是192.168.137.89,掩码是255.255.255.0,那么服务器的IP地址前三个数字也应为192.168.137。设置好IP地址后,您可以在网络调试助手中选择相应的IP号,并使用默认的通道号进行通信。
TCP/IP是网络通信的基础协议,对于理解和使用网络通信至关重要。虽然在这里只需简要了解,但建议深入学习TCP/IP协议的相关知识,以便更好地进行网络编程和调试。
在完成上述注意点的检查和调整后,您可以重新生成程序,确保所有设置和配置都已正确无误,然后进行测试和验证。
当时测试时使用的开发板与wifi串口芯片:
连接参考图:
可以正常运行的代码:
void mywifi_uart_init(void) { // GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能USART2,GPIOA时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // USART1_TX GPIOA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; // PA.2 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.2 // USART1_RX GPIOA.10初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; // PA3 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.3 // Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能 NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化VIC寄存器 // USART 初始化设置 USART_InitStructure.USART_BaudRate = 115200; // 串口波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发模式 USART_Init(USART2, &USART_InitStructure); // 初始化串口2 USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); // 开启串口接受中断 USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); // 开启串口空闲中断 USART_Cmd(USART2, ENABLE); // 使能串口1 } /** * @description: 给wifi芯片传输指定长度数据 * @param {char} *data 传输数据 * @param {u8} len 传输数据长度 * @return {*} */ void mywifi_send_nbytes(char *data, u8 len) { for (int i = 0; i < len; i++) { USART_SendData(USART2, data[i]); // 发送测试 while (!USART_GetFlagStatus(USART2, USART_FLAG_TC)) ; // 等待发送完成 } } /** * @description: wifi发送数据 * @param {char} *cmd数据 * @param {char} *ack期待的应答 * @param {u32} timeout超时 * @param {u8} len数据长度 * @return {*} */ u8 mywifi_send_at_cmd(char *cmd, char *ack, u32 timeout, u8 len) { char *ret = NULL; // 用于接收数据 // atk_mw8266d_uart_rx_restart(); // 正点原子有一个硬件复位的过程,这里没设置了, mywifi_send_nbytes(cmd, len); if ((ack == NULL) || (timeout == 0)) { return MYWIFI_OK; } else { while (timeout > 0) { if (mywifi_recv_data(ret)) { if (strstr((const char *)mywifi_recv_buf, ack) != NULL) // ack 作为子串去查询 { // mywifi_send_nbytes(mywifi_recv_buf, sizeof(mywifi_recv_buf)); return MYWIFI_OK; } else { // 不同则复位 // atk_mw8266d_uart_rx_restart(); } } timeout--; delay_ms(1); } return MYWIFI_ERROR; // 这里代表超时 } } /** * @description: 接收数据 * @param {char} *data * @return {*} 返回当前是否完成了数据接收 */ u8 mywifi_recv_data(char *data) { char temp[MAX_LENGTH] = {0}; // 测试使用 if (rev_finish_flag == 1) { // 当前接收完成 data = mywifi_recv_buf; // 先预留的,目前我没有用到 strncpy(temp, mywifi_recv_buf, receive_number); rev_finish_flag = 0; receive_number = 0; // printf("the rec data : \r\n"); // printf("%s", temp); return 1; } else return 0; } /** * @description: wifi自检测 * @return {*} 自检成功返回1 */ u8 mywifi_self_test(void) { char test[] = {"AT\r\n"}; char ack[] = {"OK"}; u8 flag = 0; for (u8 i = 0; i < 50; i++) { flag = mywifi_send_at_cmd(test, (char *)ack, 20, sizeof(test) / sizeof(test[0])); char temp[MAX_LENGTH] = {0}; while (strstr((const char *)temp, "busy") != NULL) // 等待忙完成 { // 等待不忙,才执行后面的,这里写的有点问题,没有达到预期的效果 memset(temp, 0, MAX_LENGTH); strncpy(temp, mywifi_recv_buf, temp_rec_num); flag = mywifi_send_at_cmd(test, (char *)ack, 20, sizeof(test) / sizeof(test[0])); rev_finish_flag = 0; temp_rec_num = 0; // 开机都忙等待的话直接给它硬件复位 } if (flag) return flag; } return flag; // 自检测5次,最后一次成功表示成功 } /** * @description: wifi软件重启,这个操作在自检测成功之后 * @return {*} */ char soft_rst[] = {"AT+RST\r\n"}; void mywifi_soft_rst(void) { mywifi_send_at_cmd(soft_rst, NULL, 20, sizeof(soft_rst) / sizeof(soft_rst[0])); } /** * @description: WiFi回显开关 * @param {u8} flag * @return {*} */ char polldispaly0[] = {"ATE0\r\n"}; char polldispaly1[] = {"ATE1\r\n"}; void mywifi_polldispaly(u8 flag) { mywifi_send_at_cmd(flag ? polldispaly0 : polldispaly1, "OK", 20, sizeof(polldispaly0) / sizeof(polldispaly0[0])); } /** * @description: wifi设置工作模式,站点模式 Or AP模式 * @param {u8} mode 1表示设置为站点模式 * @return {*} */ char set_mode0[] = {"AT+CWMODE=0\r\n"}; char set_mode1[] = {"AT+CWMODE=1\r\n"}; void mywifi_set_mode(u8 mode) { mywifi_send_at_cmd(mode ? set_mode1 : set_mode0, "OK", 20, sizeof(set_mode0) / sizeof(set_mode0[0])); } /** * @description: 芯片连接到热点,这里我默认是我电脑的热点 * @return {*} */ char connecet_host[] = {"AT+CWJAP=\"mldesk\",\"12345671\"\r\n"}; bool mywifi_connect_host(void) { mywifi_send_at_cmd(connecet_host, "OK", 4000, sizeof(connecet_host) / sizeof(connecet_host[0])); delay_ms(4000); return true; } /** * @description: 获取wifi连接获取的ip地址 * @return {*} */ char get_ip[] = {"AT+CIPSTA?\r\n"}; void mywifi_get_ip(void) { char temp[MAX_LENGTH] = {0}; while (strstr((const char *)temp, "OK") == NULL) { // 等待不忙,才执行后面的 memset(temp, 0, MAX_LENGTH); strncpy(temp, mywifi_recv_buf, temp_rec_num); rev_finish_flag = 0; temp_rec_num = 0; } mywifi_send_at_cmd(get_ip, NULL, 2000, sizeof(get_ip) / sizeof(get_ip[0])); delay_ms(1000); // 获取ip号先保存起来之后使用,这个打来之后是会调硬件错误的,可能是数组超了 // if (rev_finish_flag == 1) // { // // 当前接收完成 // u8 i = 0; // rev_finish_flag = 0; // for (; i < temp_rec_num; i++) // { // if ((mywifi_recv_buf[i] == 0x69) && (mywifi_recv_buf[i + 1] == 0x70)) // break; // } // // 找到了ip地址的位置 // memset(temp, 0, MAX_LENGTH); // strncpy(temp, mywifi_recv_buf + i + 4, 15); // temp_rec_num = 0; // //printf("%s",temp); // } } /** * @description: 判断热点是否连接 * @return {*} */ bool mywifi_judge_host_connect(void) { if ((strstr((const char *)mywifi_recv_buf, "CONNCETED") != NULL) || (strstr((const char *)mywifi_recv_buf, "GOT") != NULL)) { printf("\r\nconnecet_host ok\r\n"); // 成功之后设置相应的标志位,比如可以用来连接tcp服务器 wifi_connect_flag = 1; // wifi 连接成功 return true; } else if ((strstr((const char *)mywifi_recv_buf, "DISCONNECT") != NULL)) { printf("\r\nconnecet_host error\r\n"); // 失败时候需要将相应标志位清除,显示连接丢失 wifi_connect_flag = 0; // wifi 连接失败 return false; } return false; } /** * @description: wifi芯片作为client 连接到服务器 * @return {*} */ char connecet_sever[] = {"AT+CIPSTART=\"TCP\",\"192.168.137.1\",8080\r\n"}; void mywifi_connect_sever(void) { if (wifi_connect_flag) // 需要保证wifi连接成功之后设置tcpip单路连接 { char temp[MAX_LENGTH] = {0}; while (strstr((const char *)temp, "OK") == NULL) { // 等待上一次指令完成,才执行后面的 memset(temp, 0, MAX_LENGTH); strncpy(temp, mywifi_recv_buf, temp_rec_num); rev_finish_flag = 0; temp_rec_num = 0; } mywifi_send_at_cmd(connecet_sever, "OK", 2000, sizeof(connecet_sever) / sizeof(connecet_sever[0])); delay_ms(1000); } } /** * @description: 设置透传模式 * @param {u8} flag 1 表示透传 0 表示关闭透传发送+++ * @return {*} */ char open_transparent[] = {"AT+CIPMODE=1\r\n"}; char close_transparent[] = {"AT+CIPMODE=0\r\n"}; char send_data_cmd[] = {"AT+CIPSEND\r\n"}; char fade_transparent[] = {"+++"}; void mywifi_set_transparent(u8 flag) { if (flag) { mywifi_send_at_cmd(open_transparent, "OK", 200, sizeof(open_transparent) / sizeof(open_transparent[0])); delay_ms(1000); mywifi_send_at_cmd(send_data_cmd, ">", 200, sizeof(send_data_cmd) / sizeof(send_data_cmd[0])); } else { delay_ms(2000); // 需要等待一秒,结束透传 mywifi_char_printf("+++"); // 发送结束标志 delay_ms(4000); // 需要等待一秒,结束透传 mywifi_send_at_cmd("\r\n", NULL, 200, 0); mywifi_send_at_cmd(close_transparent, "OK", 200, sizeof(close_transparent) / sizeof(close_transparent[0])); mywifi_close_tcp_connection(); // 断开tcp连接 } } /** * @description: 通过透传模式进行输出传输 * @param {u8} *data * @param {u8} len * @return {*} */ void mywifi_data_send(u8 *data, u8 len) { if (wifi_connect_flag) // 需要保证wifi连接成功,服务器连接成功,才能进行输出传输 { mywifi_send_at_cmd(data, NULL, 0, len); // 数据发送,需要等待 } } /** * @description: 断开tcp连接 * @return {*} */ char close_tcp[] = {"AT+CIPCLOSE\r\n"}; void mywifi_close_tcp_connection(void) { mywifi_send_at_cmd(close_tcp, "OK", 200, sizeof(close_tcp) / sizeof(close_tcp[0])); } /** * @description: 判断是否结束透传,需要通过服务器发送+++,判断 * @return {*} */ void mywifi_judge_tranfer_end(void) { } /** * @description: 用于打印+++,推出透传模式 * @param {char} *fmt * @return {*} */ void mywifi_char_printf(char *fmt, ...) { va_list ap; uint16_t len; char cmd[MAX_LENGTH] = {0}; va_start(ap, fmt); vsprintf((char *)cmd, fmt, ap); va_end(ap); len = strlen((const char *)cmd); mywifi_send_nbytes(cmd, len); } /** * @description: 串口2中断回调函数 * @return {*} */ void USART2_IRQHandler() { static char res; // 静态变量防止堆栈溢出 static u32 temp; char temp1[MAX_LENGTH] = {0}; // 静态变量防止堆栈溢出 if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) { res = USART_ReceiveData(USART2); // 读取接收到的数据 if (receive_number >= MAX_LENGTH) // 防止刷爆 receive_number = MAX_LENGTH - 1; mywifi_recv_buf[receive_number] = res; receive_number++; USART_ClearITPendingBit(USART2, USART_IT_RXNE); } if (USART_GetFlagStatus(USART2, USART_FLAG_IDLE) != RESET) // 命中空闲中断 { /*---------------使用串口1打印从wifi芯片接收到的数据--------*/ strncpy(temp1, mywifi_recv_buf, receive_number); temp_rec_num = receive_number; printf("%s", temp1); /*----------------end-------------------------------------*/ rev_finish_flag = 1; // 判断wifi是否断开连接 mywifi_judge_host_connect(); // 判断服务器是否断开连接 // ------还没实现,可以通过各个指令不同的返回值去判定 // 下面这个操作是必要的,清除空闲中断标志位 temp = USART2->SR; temp = USART2->DR; USART_ClearITPendingBit(USART2, USART_IT_IDLE); // 清除空闲中断标志位 receive_number = 0; } }