• 基于stm32控制的ESP8266在设备模式下通讯


    一、文章中要用的指令

    指令作用
    AT+UART=115200,8,1,0,0之前的51通讯是9600,这里的321用的是115200,需要改一下波特率
    AT+CWMODE=XX是1代表station(设备)模式 ,X是2代表AP(路由)模式 ,X是3.代表双模模式(工作模式在串口助手里已经设置为双模,掉电后自动保存,因此程序中并未体现配置工作模式,这个配置模式的指令写与不写均可以
    AT+CWJAP="PEI","jmgcyjs."第一个引号里的是wifi的名称,第二个是WiFi密码,要注意的是,使用的时候一定要确保你的电脑和wifi模块连接的是一个wifi,否则无法成功。
    AT+CIPSTART="TCP","192.168.1.112",8880

    连接服务器,TCP代表客户端模式,电脑连接无线网地址192.168.1.112,8880代表端口号

    AT+CIPMODE=1透传指令,不用受发送次数还有发送字节大小限制
    AT+CIPSEND开始发送
    AT+RST重启指令

    有关esp8266的烧录我在之前说过,有兴趣的可以看一看,http://t.csdnimg.cn/J3Sid

    二、代码部分

    1. #include "main.h"
    2. #include "usart.h"
    3. #include "gpio.h"
    4. #include
    5. #include
    6. uint8_t buf=0;//串口接收缓存(1字节)
    7. #define UART1_REC_LEN 200//定义最大接收字节数 200,可根据需求调整
    8. uint8_t UART1_RX_Buffer[UART1_REC_LEN];// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
    9. uint16_t UART1_RX_STA=0;// 接收状态
    10. // bit15, 接收完成标志
    11. // bit14, 接收到0x0d
    12. // bit13~0, 接收到的有效字节数目
    13. void SystemClock_Config(void);
    14. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    15. {
    16. // 判断中断是由哪个串口触发的
    17. if(huart->Instance == USART1)
    18. {
    19. // 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
    20. if((UART1_RX_STA & 0x8000) == 0)
    21. {
    22. // 如果已经收到了 0x0d (回车),
    23. if(UART1_RX_STA & 0x4000)
    24. {
    25. // 则接着判断是否收到 0x0a (换行)
    26. if(buf == 0x0a)
    27. { // 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
    28. UART1_RX_STA |= 0x8000;
    29. if(!strcmp((const char*)UART1_RX_Buffer,"WIFI GOT IP"))
    30. AT_Connect_Net_Flag = 1;
    31. if(!strcmp((const char*)UART1_RX_Buffer,"OK"))
    32. AT_OK_Flag = 1;
    33. if(!strcmp((const char*)UART1_RX_Buffer,"L1"))
    34. HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
    35. if(!strcmp((const char*)UART1_RX_Buffer,"L0"))
    36. HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
    37. if(!strcmp((const char*)UART1_RX_Buffer,"FALL"))//连接网络不成功就会给单片机返回FALL
    38. {
    39. int i=0;
    40. for(i=0;i<5;i++)
    41. {
    42. HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
    43. HAL_Delay(1000);
    44. }
    45. }
    46. memset(UART1_RX_Buffer,0,UART1_REC_LEN);
    47. UART1_RX_STA=0;
    48. }
    49. else
    50. // 否则认为接收错误,重新开始
    51. UART1_RX_STA = 0;
    52. }
    53. else // 如果没有收到了 0x0d (回车)
    54. {
    55. //则先判断收到的这个字符是否是 0x0d (回车)
    56. if(buf == 0x0d)
    57. {
    58. // 是的话则将 bit14 位置为1
    59. UART1_RX_STA |= 0x4000;
    60. }
    61. else
    62. {
    63. // 否则将接收到的数据保存在缓存数组里
    64. UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
    65. UART1_RX_STA++;
    66. // 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
    67. if(UART1_RX_STA > UART1_REC_LEN - 1)
    68. UART1_RX_STA = 0;
    69. }
    70. }
    71. }
    72. // 重新开启中断
    73. HAL_UART_Receive_IT(&huart1, &buf, 1);
    74. }
    75. }
    76. int fputc(int ch, FILE *f)
    77. {
    78. unsigned char temp[1]={ch};
    79. HAL_UART_Transmit(&huart1,temp,1,0xffff);
    80. return ch;
    81. }
    82. int main(void)
    83. {
    84. HAL_Init();
    85. SystemClock_Config();
    86. MX_GPIO_Init();
    87. MX_USART1_UART_Init();
    88. MX_USART2_UART_Init();
    89. /* USER CODE BEGIN 2 */
    90. HAL_NVIC_SetPriority(SysTick_IRQn,0,0);
    91. HAL_UART_Receive_IT(&huart1, &buf, 1);//系统滴答定时器,防止死机
    92. HAL_UART_Transmit(&huart2,"let's go\r\n",strlen("let's go\r\n"),100);
    93. printf("AT+CWJAP=\"PEI\",\"jmgcyjs.\"\r\n");//入网指令
    94. while(!AT_OK_Flag)
    95. HAL_Delay(50);
    96. HAL_UART_Transmit(&huart2,"333\r\n",strlen("333\r\n"),100);
    97. AT_OK_Flag = 0;
    98. //发送连服务器指令并等待成功
    99. printf("AT+CIPSTART=\"TCP\",\"192.168.1.105\",8880\r\n"); //连接服务器指令
    100. while(!AT_OK_Flag)
    101. HAL_Delay(100);
    102. HAL_UART_Transmit(&huart2,"433\r\n",strlen("433\r\n"),100);
    103. AT_OK_Flag = 0;
    104. //发送透传模式指令并等待成功
    105. printf("AT+CIPMODE=1\r\n"); //透传指令
    106. while(!AT_OK_Flag)
    107. HAL_Delay(50);
    108. HAL_UART_Transmit(&huart2,"533\r\n",strlen("533\r\n"),100);
    109. AT_OK_Flag = 0;
    110. //发送数据传输指令并等待成功
    111. printf("AT+CIPSEND\r\n");//数据传输开始指令
    112. while(!AT_OK_Flag)
    113. HAL_Delay(50);
    114. HAL_UART_Transmit(&huart2,"633\r\n",strlen("633\r\n"),100);
    115. /* USER CODE END 2 */
    116. /* Infinite loop */
    117. /* USER CODE BEGIN WHILE */
    118. while (1)
    119. {
    120. /* USER CODE END WHILE */
    121. /* USER CODE BEGIN 3 */
    122. HAL_UART_Transmit(&huart2,"666\r\n",strlen("666\r\n"),100);
    123. HAL_Delay(1000);
    124. }
    125. /* USER CODE END 3 */
    126. }

    三、思路分析

            这里的思路和51哪里的差不多。都是通过串口将单片机中的指令发给ESP8266.但是与51不同是,51在调试中用的是一个串口单向的调试,而32的是用的双向两个串口调试。

    51测试的思路

            32的具体的思路是这样的,基于上一篇文章的串口基础上,每当执行到UART1_RX_STA |= 0x8000时,代表着单片机的接收寄存器已经完全收到了来自单片机内部printf重映射的指令或是网络调试助手的指令。

            接着从头说起,上电之后首先要输入入网指令

            printf("AT+CWJAP=\"PEI\",\"jmgcyjs.\"\r\n");

             输入完成后进入中断回调函数,如果你的网络名称和密码都没问题,ESP8266会通过串口1给单片机回复对应的内容

    WIFI CONNECTED
    WIFI GOT IP

    OK

    如图所示

            UART1_RX_STA |= 0x8000;下面的第一个if将会判断成功,将WIFI GOT IP返回值的标志位AT_Connect_Net_Flag 变为1,接着是ESP8266返回OK指令,就可以让OK返回值的标志位变为1,同时在main函数里在printf("AT+CWJAP=\"PEI\",\"jmgcyjs.\"\r\n")下面有while循环标志位的判断来确保每一步的进行都是正确的。每当OK返回值的标志位使用完后,就把OK返回值的标志位AT_OK_Flag变为0。等到下一次ESP8266回复给单片机OK时,就进入中断将AT_OK_Flag变为1,程序可以继续正常运行。

          思路就是这这样一个思路。

    接着就是

     连接服务器指令是否成功,  透传指令是否成功 ,数据传输开始指令是否成功 ,如果成功ESP8266就都会返回OK.如果在哪一步不成功,就会卡到哪里一直死循环。

    当然了,这里还是要细说一下,失败也是分情况的。第一种失败是入网指令的失败,这个时候如果你失败,ESP8266会给单片机返回FALL,这时候我们的LED1会亮灭5次。如果你是入网成功后的失败,那你就是会一直陷入死循环。

    四、其它

        1.关于while后面加延时,你可以不加,只要不死机。每个while的条件后面不是直接跟;而是跟的延时函数,这样可以避免死机。

         2.HAL_NVIC_SetPriority(SysTick_IRQn,0,0);滴答定时器优先级提高,中断中加入了延时函数,如果不把滴答定时器优先级提高,也会死机。

          3.全部成功后,进入网络助手后拾取指令完成后,一定记得要带上回车再点击发送,因为我们的串口输入判断\r\n是一个重要的依据,不能不带上回车。

          4.按照51的写法,把指令写成数组

    char LJWL[]  = "AT+CWJAP=\"PEI\",\"jmgcyjs.\"\r\n"; //入网指令
    char LJFWQ[] = "AT+CIPSTART=\"TCP\",\" 192.168.10.206\",8880\r\n"; //连接服务器指令
    char TCMS[]  = "AT+CIPMODE=1\r\n";  //透传指令
    char SJCS[]  = "AT+CIPSEND\r\n";    //数据传输开始指令
    char CQMK[] = "AT+RST\r\n";        //重启模块指令

          你可以写成printf(LJWL)的形式,意思就是这个就是把数组名为LJWL的数组里的内容发出去,也就是发送入网指令。前提是你要把你的wifi模块的波特率改过来才行。你也可以像我一样使用printf("AT+CWJAP=\"PEI\",\"jmgcyjs.\"\r\n");,二选一。使用printf(LJWL)记得把这5个字符串写入数组。

       5.还有一点我要补充一下,不知道大家有没有注意到一个小细节

    在第一章里,入网指令写的是AT+CWJAP="PEI","jmgcyjs."但是到了printf里我写的是printf("AT+CWJAP=\"PEI\",\"jmgcyjs.\"\r\n");。\"PEI\",\"jmgcyjs.\"\r\n和"PEI","jmgcyjs."一样吗?确实不一样。使用的场景不一样AT+CWJAP="PEI","jmgcyjs."是在串口助手里使用。写完这条指令后,点击发送新行就相当于回车换行的/r/n,而"AT+CWJAP=\"PEI\",\"jmgcyjs.\"\r\n"是在单片机内部使用,\"使用的是转义字符,简单里说就是让“不要有别的意思,就让它是个引号。大家也可以自己查一下关于转义字符的使用,我就不多说了。    

  • 相关阅读:
    PCM会重塑汽车OTA格局吗(2)
    【C++杂货铺】一颗具有搜索功能的二叉树
    用 Plop 加快项目相似代码生成
    CAS机制的的解释和总结
    SpringMVC学习(三)SpringMVC原理
    一篇长文---深入理解synchronized
    【C++】C++中的qualified name和unqualified name
    python实现列表元素进行正交
    最新AI创作系统ChatGPT系统运营源码+支持GPT-4多模态模型
    CCL2022自然语言处理国际前沿动态综述——开放域对话生成前沿综述
  • 原文地址:https://blog.csdn.net/weixin_51374594/article/details/133954257