当前位置:   article > 正文

ESP8266+STM32获取天气时间信息_stm32天气预报时钟esp8266

stm32天气预报时钟esp8266

       之前写过一篇
ESP8266+STM32获取苏宁时间
的博客,很多人反映说获取不到苏宁时间了,最近刚好有时间自己重新验证了一下,确实是获取不到时间了。发送的GET请求返回的是400 bad request,也不知道问题出在哪里,最近也在研究ESP8266WIFI模块的功能和AT指令集,于是开始写下这篇进阶文章。在文章末尾已经将源代码打包至资源包,内部还有教程使用。另外在工程文件可以获得1、时间戳转换函数。2、OLED驱动函数(支持画图、中文显示、图片显示)。3、Esp8266驱动函数以及简单的数据读取函数。如果觉得文章写的不错,请一定点赞留言支持一下,如有使用问题,请评论区留言,我会定时解答,谢谢!

     基本思路:

        下载我打包好的代码,打开先浏览一下代码,重点先看看ESP8266.c文件内的代码,了解哪些指令要发送给ESP8266、以及每条指令的含义。理解上一步之后,就可以进行简单的模拟了,将串口助手和ESP8266连接好,打开电脑的串口助手,手动发送AT指令,观察数据是否正常收发。其次就是数据的处理部分了,处理部分偏硬核,在keil5环境内我找不到使用JSON库的方式解析数据的方式(之前我在ARDUINO平台解析这类数据都可以直接生成JSON解析函数,真的巨方便,有懂得的大佬踢我一下=_=),就是接收并存储起受到的数据,然后使用字符串查找找到关键字,然后通过指针移位的方式取出想要的数据,我感觉效率很低。=_=

  • 弄清除AT指令控制ESP8266-01S的具体方法,也即熟悉以下的AT指令集。

AT指令集

功能

指令返回值

芯片测试

ATOK
芯片复位AT+RSTOK
波特率设置AT+CIOBAUD=BaudRate(默认115200)OK
设置工作模式AT+CWMODE=ModeOK
Mode=1Station模式(客户端)
Mode=2AP模式(服务器、热点)
Mode=3Station+AP模式(混合模式)
AP模式连接WIFIAT+CWSAP=“WIFI名”,“密码”OK
AP模式开启透传模式AT+CIPMODE=ModeOK
Mode=0非透传,缺省模式(默认)
Mode=1透传模式
Station模式设置服务器端口号AT+CIPSERVER=1,8080(默认) 
Station+AP模式设置AT+CIPMUX=MODEOK
MODE=0单连接模式
MODE=1多连接模式
退出AP模式AT+CWQAPOK

设置发送数据

单路连接

(AT+CIPMUX=0)

模块收到指令后先换行返回”>”,然后开始接收串口数据,当数据长度满Length时发送数据,如果未建立连接或连接被断开,返回ERROR;如果数据发送成功,返回SEND OK,ID为客户端编号。

多路连接

(AT+CIPMUX=1)

查询IP地址

AT+CIFSR当前IP地址
  • 连接好模块,进行单ESP8266-01S和串口助手结合AT指令获取天气数据、实践数据,这一步要结合代码的ESP8266.c文件一起看,更好的理解运行方式。

0458e03135824664b565c7c9a4073ab5.jpeg

ESP8266模块

f67e2a0de56c479a8d6d6f07a0770808.jpeg

ESP8266模块

        另外还需要在电脑端准备一个能进行串口通讯的软件,这里我使用的是XCOM,软件我已经放到文章末尾的百度网盘里了,需要的自取即可。求赞! 

        安装好XCOM之后,将模块插在电脑上,软件会自动找到插在了哪个端口,(需要安装CH340的串口驱动,这个不用多说吧,不懂得百度一下就行了)选择波特率115200,其余设置如下图一,最后点击打开串口助手即可开始用AT指令控制ESP8266了!

4ee8cf954752474bb6353f34d2a6d6fc.png

图一

      如图一所示,出现ready表示连接成功,接下来分别准备以下指令集,供控制所用

      以上步骤可以理解为:设置ESP8266-01S为AP模式(作为移动设备连接WIFI入网),启用单连接模式,和心知天气的服务器建立TCP连接,开启透传模式(顾名思义,一种简洁好用的传输协议。)最后通过GET的形式发送API请求(相当于是在手机浏览器上键入了api.seniverse.com/v3/weather/now.json?key=你的私钥&location=beijing&language=zh-Hans&unit=c

 这个网址,并且点击了访问。

        建议大家去看看心知天气的产品文档,看完你会知道如何对第七条指令更改以获取你想要的信息,或者是获取别的天气信息。链接在这。

        到这,就可以逐步的点击指令,验证是否可以获得天气数据和实践数据了!附上图二,正确验证的结果,最后一条便是心知天气发来的数据信息,实践戳数据类似,忘记截图补上我获取到的报文格式,一致的话就没问题({"server_time":1710587205160})。

bee0aba0783d4f1cbad1058597b94075.png

图二:正确获取的结果

        观察数据会发现有乱码的中文,其实这是显示问题,实际上数据是准确的。完成了理论验证,接下来就是换成是STM32来控制ESP8266获取天气数据了。 

串口模块介绍(usart.c):

        本次共用到了两个串口;分别是串口一和串口二。

        介绍一下STM32F103C8T6的串口资源,这里直接贴一下别人写的文章真的很不错。http://t.csdnimg.cn/yCPg7http://t.csdnimg.cn/yCPg7

         串口模块是初始化了之后,设置好波特率就可以开始使用了,波特率设置一定要对上,电脑对ESP8266或者ESP8266对STM32都是,否则就是乱码。

这里贴上两个串口的初始化代码函数:

  1. //==================================================================
  2. //函 数 名:uart1_init(u32 bound)
  3. //功能描述:串口一初始化函数
  4. //输入参数:串口要使用的波特率
  5. //==================================================================
  6. void uart1_init(u32 bound){
  7. //GPIO端口设置
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. USART_InitTypeDef USART_InitStructure;
  10. NVIC_InitTypeDef NVIC_InitStructure;
  11. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
  12. //USART1_TX GPIOA.9
  13. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  14. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  15. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  16. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
  17. //USART1_RX GPIOA.10初始化
  18. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  19. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  20. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
  21. //Usart1 NVIC 配置
  22. NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  23. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
  24. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
  25. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
  26. NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
  27. //USART 初始化设置
  28. USART_InitStructure.USART_BaudRate = bound;//串口波特率
  29. USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  30. USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
  31. USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
  32. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  33. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
  34. USART_Init(USART1, &USART_InitStructure); //初始化串口1
  35. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  36. USART_Cmd(USART1, ENABLE); //使能串口1
  37. }
  38. //==================================================================
  39. //函 数 名:uart2_Init(unsigned int baud)
  40. //功能描述:串口二初始化函数
  41. //输入参数:串口要使用的波特率
  42. //==================================================================
  43. void Usart2_Init(unsigned int baud)
  44. {
  45. GPIO_InitTypeDef gpioInitStruct;
  46. USART_InitTypeDef usartInitStruct;
  47. NVIC_InitTypeDef nvicInitStruct;
  48. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  49. RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  50. //PA2 TXD
  51. gpioInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
  52. gpioInitStruct.GPIO_Pin = GPIO_Pin_2;
  53. gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  54. GPIO_Init(GPIOA, &gpioInitStruct);
  55. //PA3 RXD
  56. gpioInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  57. gpioInitStruct.GPIO_Pin = GPIO_Pin_3;
  58. gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  59. GPIO_Init(GPIOA, &gpioInitStruct);
  60. usartInitStruct.USART_BaudRate = baud;
  61. usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
  62. usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送
  63. usartInitStruct.USART_Parity = USART_Parity_No; //无校验
  64. usartInitStruct.USART_StopBits = USART_StopBits_1; //1位停止位
  65. usartInitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
  66. USART_Init(USART2, &usartInitStruct);
  67. USART_Cmd(USART2, ENABLE); //使能串口
  68. USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能接收中断
  69. nvicInitStruct.NVIC_IRQChannel = USART2_IRQn;
  70. nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
  71. nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
  72. nvicInitStruct.NVIC_IRQChannelSubPriority = 0;
  73. NVIC_Init(&nvicInitStruct);
  74. }
  75. /******************************************************** 发送一个16位数 ************************************************/
  76. void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
  77. {
  78. uint8_t temp_h, temp_l;
  79. /* 取出高八位 */
  80. temp_h = (ch&0XFF00)>>8;
  81. /* 取出低八位 */
  82. temp_l = ch&0XFF;
  83. /* 发送高八位 */
  84. USART_SendData(pUSARTx,temp_h);
  85. while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
  86. /* 发送低八位 */
  87. USART_SendData(pUSARTx,temp_l);
  88. while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
  89. }
  90. /***************** 发送一个字符 **********************/
  91. void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
  92. {
  93. /* 发送一个字节数据到USART */
  94. USART_SendData(pUSARTx,ch);
  95. /* 等待发送数据寄存器为空 */
  96. while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
  97. }
  98. /***************** 发送字符串 **********************/
  99. void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
  100. {
  101. unsigned short count = 0;
  102. for(; count < len; count++)
  103. {
  104. USART_SendData(USARTx, *str++); //发送数据
  105. while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //等待发送完成
  106. }
  107. }
  108. /***************** 发送字符串 **********************/
  109. void Usart_SendString2( USART_TypeDef * pUSARTx, char *str)
  110. {
  111. unsigned int k=0;
  112. do
  113. {
  114. Usart_SendByte( pUSARTx, *(str + k) );
  115. k++;
  116. } while(*(str + k)!='\0');
  117. /* 等待发送完成 */
  118. while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  119. {}
  120. }
  121. /***************** 选择特定的串口发送打印数据 **********************/
  122. void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...)
  123. {
  124. unsigned char UsartPrintfBuf[296];
  125. va_list ap;
  126. unsigned char *pStr = UsartPrintfBuf;
  127. va_start(ap, fmt);
  128. vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化
  129. va_end(ap);
  130. while(*pStr != 0)
  131. {
  132. USART_SendData(USARTx, *pStr++);
  133. while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
  134. }
  135. }

        串口二中断处理函数:

  1. //==================================================================
  2. //函 数 名:USART2_IRQHandler(void)
  3. //功能描述:串口2中断控制函数
  4. //输入参数:无
  5. //介绍说明:将串口2接收到的数据转发出去
  6. //==================================================================
  7. void USART2_IRQHandler(void)
  8. {
  9. if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
  10. {
  11. if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0;
  12. esp8266_buf[esp8266_cnt++] = USART2->DR;
  13. USART_SendData(USART1,USART2->DR);
  14. USART_ClearFlag(USART2, USART_FLAG_RXNE);
  15. }
  16. }

        串口一的串口中断函数应该是被拆开了,直接对串口一的中断标志位寄存器和数据接收寄存器进行操作。

 定时器模块介绍(timer.c):

        也是用到了两个定时器,定时器二和定时器三。定时器二用来按键扫描。定时器三用来作为本地是时钟源,运行解析出来的时间。

        贴一下定时器的初始化代码,STM32的定时器模块还是比较复杂的,不懂的自己去学习一下。

  1. //==================================================================
  2. //函 数 名:TIM3_Int_Init(u32 arr,u16 psc)
  3. //功能描述:定时器3初始化函数
  4. //输入参数:arr-自动重装载值 pre-预分频值
  5. //定时长度:72000000/(arr+1)*(pre+1)
  6. //==================================================================
  7. void TIM3_Int_Init(u32 arr,u16 psc)
  8. {
  9. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  10. NVIC_InitTypeDef NVIC_InitStructure;
  11. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
  12. TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000500ms
  13. TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
  14. TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  15. TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
  16. TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  17. TIM_ITConfig( TIM3,TIM_IT_Update,ENABLE); //使能或者失能指定的TIM中断
  18. NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
  19. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0
  20. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3
  21. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  22. NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
  23. }
  24. //==================================================================
  25. //函 数 名:Timer2_Init(void)
  26. //功能描述:定时器2初始化函数
  27. //输入参数:无
  28. //==================================================================
  29. void Timer2_Init(void)
  30. {
  31. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
  32. TIM_InternalClockConfig(TIM2);
  33. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  34. TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
  35. TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
  36. TIM_TimeBaseInitStructure.TIM_Period = 999;
  37. TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
  38. TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
  39. TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
  40. TIM_ClearFlag(TIM2, TIM_FLAG_Update);
  41. TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  42. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  43. NVIC_InitTypeDef NVIC_InitStructure;
  44. NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
  45. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  46. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  47. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  48. NVIC_Init(&NVIC_InitStructure);
  49. }

时间解析模块介绍(time.c):

        时间戳转换模块是我在网上摘抄下来的,我看csdn平台也有人写相关的,但我看的文章不是csdn的。但现在找关键词找不到了,这里就直接引用了。感谢原作者!

  1. #include <stdio.h>
  2. #include "OLED.h"
  3. #include "timer.h"
  4. #include "time.h"
  5. typedef unsigned short uint16_t;
  6. typedef unsigned int uint32_t;
  7. //全局变量,存储时间
  8. int year_1,month_1,day_1,hour_1,minter_1,sec_1,week_1;
  9. //暂时没用的,有时间改一下函数结构,去冗余
  10. const uint16_t month_days_table[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  11. /**
  12. * @brief 判断是否为闰年 注:原文的链接忘记复制了,感谢该作者
  13. * @param [in] year 年
  14. * @retval 1 为闰年
  15. * @retval 0 为平年
  16. */
  17. uint16_t fml_leap_year(uint16_t year)
  18. {
  19. return (((year % 4 == 0)&&(year % 100 != 0)) || (year % 400 == 0));
  20. }
  21. /**
  22. * @brief 日期转时间戳
  23. * @param [in] date 日期值
  24. * @retval =0 成功
  25. * @retval 非0 失败,以及失败原因
  26. */
  27. uint32_t fml_time_to_stamp(date_time_t date)
  28. {
  29. static uint32_t dax = 0;
  30. static uint32_t day_count = 0;
  31. uint16_t leap_year_count = 0;
  32. uint16_t i;
  33. // 计算闰年数
  34. for (i = 1970; i < date.year; i++)
  35. {
  36. if (fml_leap_year(i))
  37. {
  38. leap_year_count++;
  39. }
  40. }
  41. // 计算年的总天数
  42. day_count = leap_year_count * 366 + (date.year - 1970 - leap_year_count) * 365;
  43. // 累加计算当年所有月的天数
  44. for (i = 1; i < date.month; i++)
  45. {
  46. if ((2 == i) && (fml_leap_year(date.year)))
  47. {
  48. day_count += 29;
  49. }
  50. else
  51. {
  52. day_count += month_days_table[i];
  53. }
  54. }
  55. // 累加计算当月的天数
  56. day_count += (date.day - 1);
  57. dax = (uint32_t)(day_count * 86400) + (uint32_t)((uint32_t)date.hour * 3600) + (uint32_t)((uint32_t)date.min * 60) + (uint32_t)date.sec;
  58. /* 北京时间补偿 */
  59. dax = dax - 8*60*60;
  60. return dax;
  61. }
  62. //==================================================================
  63. //函 数 名:fml_stamp_to_time
  64. //功能描述:时间戳转换年月日
  65. //输入参数:无
  66. //公式来源:蔡勒公式
  67. //==================================================================
  68. uint32_t fml_stamp_to_time( uint32_t timep, date_time_t *date)
  69. {
  70. uint32_t days = 0;
  71. uint32_t rem = 0;
  72. /* 北京时间补偿 */
  73. timep = timep + 8*60*60;
  74. // 计算天数
  75. days = (uint32_t)(timep / 86400);
  76. rem = (uint32_t)(timep % 86400);
  77. // 计算年份
  78. uint16_t year;
  79. for (year = 1970; ; ++year)
  80. {
  81. uint16_t leap = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
  82. uint16_t ydays = leap ? 366 : 365;
  83. if (days < ydays)
  84. {
  85. break;
  86. }
  87. days -= ydays;
  88. }
  89. date->year = year;
  90. // 计算月份
  91. static const uint16_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  92. uint16_t month;
  93. for (month = 0; month < 12; month++)
  94. {
  95. uint16_t mdays = days_in_month[month];
  96. if (month == 1 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
  97. {
  98. mdays = 29;
  99. }
  100. if (days < mdays)
  101. {
  102. break;
  103. }
  104. days -= mdays;
  105. }
  106. date->month = month;
  107. date->month += 1;
  108. // 计算日期
  109. date->day = days + 1;
  110. // 计算时间
  111. date->hour = rem / 3600;
  112. rem %= 3600;
  113. date->min = rem / 60;
  114. date->sec = rem % 60;
  115. year_1 = data1.year;
  116. month_1 = data1.month;
  117. day_1 = data1.day;
  118. hour_1 = data1.hour;
  119. minter_1 = data1.min;
  120. sec_1 = data1.min;
  121. return 0;
  122. }
  123. //==================================================================
  124. //函 数 名:get_week();
  125. //功能描述:计算出星期
  126. //输入参数:无
  127. //公式来源:蔡勒公式
  128. //==================================================================
  129. void get_week(void)
  130. {
  131. if (month_1 == 1 || month_1 == 2)//判断month是否为12 
  132. year_1--, month_1 += 12;
  133. int c = year_1 / 100;
  134. int y = year_1 - c * 100;
  135. int week = y + y / 4 + c / 4 - 2 * c + 26 * (month_1 + 1) / 10 + day_1 - 1;
  136. while (week < 0)
  137. week += 7;
  138. week %= 7;
  139. week_1 = week;
  140. }

         我这里修改了时间戳转换日期的函数、新增一个星期转换函数。传入10位数的时间戳就可得出对应的日期。

OLED模块介绍(oled.c):

        OLED的库函数我也是走了不少弯路才找到的,过程曲折,没想到答案远在天边近在眼前。感谢中景园电子!

        这个库函数的功能是我见过比较丰富的了,还有更复杂的是GITHUB上的自制驱动,但是他们用的OLED屏幕和我用的不一样,试图移植过但失败了。贴上.h文件,给大伙看看这个驱动能实现的功能。

  1. /*初始化函数*/
  2. void OLED_Init(void);
  3. /*更新函数*/
  4. void OLED_Update(void);
  5. void OLED_UpdateArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
  6. /*显存控制函数*/
  7. void OLED_Clear(void);
  8. void OLED_ClearArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
  9. void OLED_Reverse(void);
  10. void OLED_ReverseArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
  11. /*显示函数*/
  12. void OLED_ShowChar(uint8_t X, uint8_t Y, char Char, uint8_t FontSize);
  13. void OLED_ShowString(uint8_t X, uint8_t Y, char *String, uint8_t FontSize);
  14. void OLED_ShowNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
  15. void OLED_ShowSignedNum(uint8_t X, uint8_t Y, int32_t Number, uint8_t Length, uint8_t FontSize);
  16. void OLED_ShowHexNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
  17. void OLED_ShowBinNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
  18. void OLED_ShowFloatNum(uint8_t X, uint8_t Y, double Number, uint8_t IntLength, uint8_t FraLength, uint8_t FontSize);
  19. void OLED_ShowChinese(uint8_t X, uint8_t Y, char *Chinese);
  20. void OLED_ShowImage(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height, const uint8_t *Image);
  21. void OLED_Printf(uint8_t X, uint8_t Y, uint8_t FontSize, char *format, ...);
  22. /*绘图函数*/
  23. void OLED_DrawPoint(uint8_t X, uint8_t Y);
  24. uint8_t OLED_GetPoint(uint8_t X, uint8_t Y);
  25. void OLED_DrawLine(uint8_t X0, uint8_t Y0, uint8_t X1, uint8_t Y1);
  26. void OLED_DrawRectangle(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height, uint8_t IsFilled);
  27. void OLED_DrawTriangle(uint8_t X0, uint8_t Y0, uint8_t X1, uint8_t Y1, uint8_t X2, uint8_t Y2, uint8_t IsFilled);
  28. void OLED_DrawCircle(uint8_t X, uint8_t Y, uint8_t Radius, uint8_t IsFilled);
  29. void OLED_DrawEllipse(uint8_t X, uint8_t Y, uint8_t A, uint8_t B, uint8_t IsFilled);
  30. void OLED_DrawArc(uint8_t X, uint8_t Y, uint8_t Radius, int16_t StartAngle, int16_t EndAngle, uint8_t IsFilled);

        支持两个大小的汉字显示、数字字母显示、图像显示、简单的图形绘制、图片显示。

还是和第一版的一样,我会把取字模和图像的软件打包起来,直接用就可以。

ESP8266驱动函数(ESP8266.c):

        这个就比较的复杂了,直接贴代码吧,大家看了有不懂的评论区留言我帮忙解答

  1. #include "stm32f10x.h"
  2. #include "sys.h"
  3. #include "string.h"
  4. #include "stdlib.h"
  5. #include "esp8266.h"
  6. #include "usart2.h"
  7. #include "delay.h"
  8. #include "init.h"
  9. //WIFI的SSID和密码
  10. #define ESP8266_WIFI_INFO "AT+CWJAP=\"TP-LINK_C568\",\"zz18835784774\"\r\n"
  11. //心知天气API
  12. #define Xinzhi_TCP "AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80\r\n"
  13. //拼多多API
  14. #define Time_TCP "AT+CIPSTART=\"TCP\",\"qapi.pinduoduo.com\",80\r\n"
  15. //获取当天天气
  16. #define Now_GET "GET https://api.seniverse.com/v3/weather/now.json?key=SEa9maHD3BOFLn8Xv&location=beijing&language=en&unit=c\r\n"
  17. //获取天气预报
  18. #define Forcast_GET "GET https://api.seniverse.com/v3/weather/daily.json?key=SEa9maHD3BOFLn8Xv&location=beijing&language=en&unit=c&start=0&days=3\r\n"
  19. //获取生活指数
  20. #define Life_GET "GET https://api.seniverse.com/v3/life/suggestion.json?key=SEa9maHD3BOFLn8Xv&location=shanghai&language=en&days=1\r\n"
  21. //获取拼多多时间戳
  22. #define Time_GET "GET https://api.pinduoduo.com/api/server/_stm\r\n"
  23. unsigned char esp8266_buf[1000] = {0};
  24. unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
  25. unsigned char Xinzhi_buff[1000];
  26. unsigned char Time_buff[50];
  27. //==================================================================
  28. //函 数 名:ESP8266_Init()
  29. //功能描述:ESP8266模块初始化
  30. //介绍说明:按照我写的第一篇文章的步骤,使用串口助手在电脑端通过串口
  31. //助手分别发送相应的AT指令验证是否能初始化
  32. //==================================================================
  33. void ESP8266_Init(void)
  34. {
  35. ESP8266_Clear();
  36. while(ESP8266_SendCmd("+++", ""));
  37. while(ESP8266_SendCmd("AT+RESTORE\r\n", "OK"));
  38. while(ESP8266_SendCmd("AT\r\n", "OK"));
  39. ESP8266_SendCmd("AT+RST\r\n", "");
  40. delay_ms(500);
  41. ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");
  42. delay_ms(500);
  43. while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"));
  44. while(ESP8266_SendCmd("AT+CIPMUX=0\r\n", "OK"));
  45. while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "WIFI GOT IP"));
  46. }
  47. //==================================================================
  48. //函 数 名:Get_current_time(unsigned char s)
  49. //功能描述:获取指定API的数据
  50. //介绍说明:按照我写的第一篇文章的步骤,使用串口助手在电脑端通过串口
  51. //助手分别发送相应的AT指令验证是否能获取到对应的数据报文
  52. //==================================================================
  53. void Get_current_time(unsigned char s)
  54. {
  55. ESP8266_Init();
  56. ESP8266_Clear();
  57. switch(s)
  58. {
  59. //case 0:获取时间戳报文
  60. case 0 :{while(ESP8266_SendCmd(Time_TCP, "CONNECT"));
  61. while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
  62. ESP8266_SendData((u8 *)Time_GET, sizeof(Time_GET));
  63. ESP8266_GetIPD_GET(50, Time_buff);
  64. } break;
  65. //case 1:获取当前天气报文
  66. case 1 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
  67. while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
  68. ESP8266_SendData((u8 *)Now_GET, sizeof(Now_GET));
  69. ESP8266_GetIPD_GET(200, Xinzhi_buff);
  70. } break;
  71. //case 2:获取天气预报报文
  72. case 2 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
  73. while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
  74. ESP8266_SendData((u8 *)Forcast_GET, sizeof(Forcast_GET));
  75. ESP8266_GetIPD_GET(1000, Xinzhi_buff);
  76. } break;
  77. //case 3:未完成部分-生活指数
  78. // case 3 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
  79. // while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
  80. // ESP8266_SendData((u8 *)Life_GET, sizeof(Life_GET));
  81. // ESP8266_GetIPD_GET(1000, Xinzhi_buff);
  82. // } break;
  83. }
  84. ESP8266_Clear();
  85. while(ESP8266_SendCmd("+++", ""));
  86. }
  87. #define NAME_ADD_DRES 7
  88. #define TEXT_ADD_DRES 7
  89. #define TEMP_ADD_DRES 14
  90. #define HIGH_TEMP_DRES 7
  91. #define LOW_TEMP_DRES 6
  92. #define RAINFALL_DRES 11
  93. #define WIND_DIRCTION 17
  94. #define WIND_SCAL 13
  95. #define HUMITIDITY 11
  96. //#define AIR_POLLU 25
  97. #define TIME_ADD_DRES 13
  98. int TEXT,TEMP,LOW_TEMP,HIGH_TEMP,RAINFALL,WIND_DIR,SCAL_WIND,HUMI;
  99. unsigned int stamp;
  100. //==================================================================
  101. //函 数 名:cJSON_Time_Parse(unsigned char s)
  102. //功能描述:通过标志位启动不同的数据解析方式
  103. //输入参数:0-解析时间、1-解析当前天气、2-解析天气预报
  104. //==================================================================
  105. void cJSON_Time_Parse(unsigned char s)
  106. {
  107. char *data_pt;
  108. char *text_string;
  109. char *temp_string;
  110. char *time;
  111. switch(s){
  112. case 0:{
  113. data_pt = strstr((const char *)Time_buff, (const char *)"server_time");
  114. time = data_pt + TIME_ADD_DRES;
  115. stamp = Get_Time(time);
  116. } break;
  117. case 1:{
  118. data_pt = strstr((const char *)Xinzhi_buff, (const char *)"code");
  119. text_string = data_pt + TEXT_ADD_DRES;
  120. data_pt = strstr((const char *)Xinzhi_buff, (const char *)"temperature");
  121. temp_string = data_pt + TEMP_ADD_DRES;
  122. TEXT = Get_Temp(text_string);
  123. TEMP = Get_Temp(temp_string);
  124. } break;
  125. case 2:{
  126. data_pt = strstr((const char *)Xinzhi_buff, (const char *)"high");
  127. time = data_pt + HIGH_TEMP_DRES;
  128. HIGH_TEMP = Get_Temp(time);
  129. data_pt = strstr((const char *)Xinzhi_buff, (const char *)"low");
  130. time = data_pt + LOW_TEMP_DRES;
  131. LOW_TEMP = Get_Temp(time);
  132. data_pt = strstr((const char *)Xinzhi_buff, (const char *)"rainfall");
  133. time = data_pt + RAINFALL_DRES;
  134. RAINFALL = Get_Temp(time);
  135. data_pt = strstr((const char *)Xinzhi_buff, (const char *)"wind_direction");
  136. time = data_pt + WIND_DIRCTION;
  137. WIND_DIR = Get_Wind(time);
  138. data_pt = strstr((const char *)Xinzhi_buff, (const char *)"wind_scale");
  139. time = data_pt + WIND_SCAL;
  140. SCAL_WIND = Get_Temp(time);
  141. data_pt = strstr((const char *)Xinzhi_buff, (const char *)"humidity");
  142. time = data_pt + HUMITIDITY;
  143. HUMI = Get_Temp(time);
  144. } break;
  145. //未完成部分-生活指数
  146. // case 3:{
  147. // data_pt = strstr((const char *)Xinzhi_buff, (const char *)"air_pollution");
  148. // text_string = data_pt + AIR_POLLU;
  149. // data_pt = strstr((const char *)Xinzhi_buff, (const char *)"comfort");
  150. // temp_string = data_pt + TEMP_ADD_DRES;
  151. // TEXT = Get_Temp(text_string);
  152. // TEMP = Get_Temp(temp_string);
  153. // } break;
  154. }
  155. }
  156. //==================================================================
  157. //函 数 名:Get_Wind(char *y)
  158. //功能描述:得到风向
  159. //输入参数:无
  160. //介绍说明:不同的返回值代表不同的风向
  161. //==================================================================
  162. int Get_Wind(char *y)
  163. {
  164. char dir[] = {'W','E','N','S','"'};// 0 1 2 3
  165. int flag , i , j = 1;
  166. for(i=0;i<4;i++)
  167. {
  168. if(*y == dir[i]){
  169. flag = i+4;
  170. }
  171. }
  172. for(i=0;i<2;i++)
  173. {
  174. for(j=0;j<2;j++)
  175. {
  176. if(*y == dir[i + 2] && *(y+1) == dir[j]){
  177. if(i==0){flag = i*2 + j*1;};
  178. if(i==1){flag = i*2 + j*1;};
  179. }
  180. }
  181. }
  182. return flag;
  183. }
  184. //==================================================================
  185. //函 数 名:Get_Temp(char *y)
  186. //功能描述:从字符串中取出时间戳-最长10位长度
  187. //输入参数:无
  188. //==================================================================
  189. int Get_Temp(char *y)
  190. {
  191. int temp_return;
  192. temp_return = atoi(y);
  193. return temp_return;
  194. }
  195. //==================================================================
  196. //函 数 名:Get_Time(char *d)
  197. //功能描述:从字符串中取出数字-最长7位长度
  198. //输入参数:无
  199. //==================================================================
  200. unsigned int Get_Time(char *d)
  201. {
  202. unsigned int u;
  203. char dest[10] = {0};
  204. strncpy(dest, d, 10);
  205. u = atoi(dest);
  206. return u;
  207. }
  208. //==================================================================
  209. //函 数 名:USART2_IRQHandler(void)
  210. //功能描述:串口2中断控制函数
  211. //输入参数:无
  212. //介绍说明:将串口2接收到的数据转发出去
  213. //==================================================================
  214. void USART2_IRQHandler(void)
  215. {
  216. if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
  217. {
  218. if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0;
  219. esp8266_buf[esp8266_cnt++] = USART2->DR;
  220. USART_SendData(USART1,USART2->DR);
  221. USART_ClearFlag(USART2, USART_FLAG_RXNE);
  222. }
  223. }
  224. /**下面的代码来源于:
  225. ************************************************************
  226. ************************************************************
  227. ************************************************************
  228. * 文件名: esp8266.c
  229. *
  230. * 作者: 张继瑞
  231. *
  232. * 日期: 2017-05-08
  233. *
  234. * 版本: V1.0
  235. *
  236. * 说明: ESP8266的简单驱动
  237. *
  238. * 修改记录:
  239. ************************************************************
  240. ************************************************************
  241. ************************************************************
  242. **/
  243. //==========================================================
  244. // 函数名称: ESP8266_Clear
  245. //
  246. // 函数功能: 清空缓存
  247. //
  248. // 入口参数: 无
  249. //
  250. // 返回参数: 无
  251. //
  252. // 说明:
  253. //==========================================================
  254. void ESP8266_Clear(void)
  255. {
  256. memset(esp8266_buf, 0, sizeof(esp8266_buf));
  257. esp8266_cnt = 0;
  258. }
  259. //==========================================================
  260. // 函数名称: ESP8266_SendData
  261. //
  262. // 函数功能: 发送数据
  263. //
  264. // 入口参数: data:数据
  265. // len:长度
  266. //
  267. // 返回参数: 无
  268. //
  269. // 说明:
  270. //==========================================================
  271. void ESP8266_SendData(unsigned char *data, unsigned short len)
  272. {
  273. char cmdBuf[32];
  274. ESP8266_Clear(); //清空接收缓存
  275. sprintf(cmdBuf, "AT+CIPSEND\r\n"); //发送命令
  276. if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’时可以发送数据
  277. {
  278. //printf("8.开始处于透传发送状态!\r\n");
  279. /*发送请求数据*/
  280. Usart_SendString(USART2, data, len); //发送设备连接请求数据
  281. }
  282. }
  283. //==========================================================
  284. // 函数名称: ESP8266_GetIPD
  285. //
  286. // 函数功能: copy天气数据到buff数组里面
  287. //
  288. // 返回参数: 平台返回的原始数据
  289. //
  290. // 说明: copy天气数据到buff
  291. //==========================================================
  292. unsigned char *ESP8266_GetIPD_GET(unsigned short timeOut, u8 *buff) //这里我用了一个全局变量将esp8266buf储存到这个全局变量里面
  293. {
  294. do
  295. {
  296. delay_ms(5);
  297. }
  298. while(timeOut--);
  299. strcpy((char*)buff, (char*)esp8266_buf);
  300. return buff;
  301. }
  302. //==========================================================
  303. // 函数名称: ESP8266_WaitRecive
  304. //
  305. // 函数功能: 等待接收完成
  306. //
  307. // 入口参数: 无
  308. //
  309. // 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成
  310. //
  311. // 说明: 循环调用检测是否接收完成
  312. //==========================================================
  313. _Bool ESP8266_WaitRecive(void)
  314. {
  315. if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
  316. return REV_WAIT;
  317. if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕
  318. {
  319. esp8266_cnt = 0; //0接收计数
  320. return REV_OK; //返回接收完成标志
  321. }
  322. esp8266_cntPre = esp8266_cnt; //置为相同
  323. return REV_WAIT; //返回接收未完成标志
  324. }
  325. //==========================================================
  326. // 函数名称: ESP8266_SendCmd
  327. //
  328. // 函数功能: 发送命令
  329. //
  330. // 入口参数: cmd:命令
  331. // res:需要检查的返回指令
  332. //
  333. // 返回参数: 0-成功 1-失败
  334. //
  335. // 说明:
  336. //==========================================================
  337. _Bool ESP8266_SendCmd(char *cmd, char *res)
  338. {
  339. unsigned char timeOut = 250;
  340. Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
  341. while(timeOut--)
  342. {
  343. if(ESP8266_WaitRecive() == REV_OK) //如果收到数据
  344. {
  345. if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词
  346. {
  347. ESP8266_Clear(); //清空缓存
  348. return 0;
  349. }
  350. }
  351. delay_ms(10);
  352. }
  353. return 1;
  354. }

        最后展示: 

4e705ebfc16648a595b6c51d60cc499b.jpeg

所有模块

32c1d90ce9f04a17b10df9d1512436e8.jpeg

时间显示

bf7317700d9c4f44a6a9a9da19677f23.jpeg

天气显示

7856d2e6627b42099e8ee314195bcdbb.jpeg

其他显示

        拥有以上模块就可以复现一下了。最后附上我制作的资源包。

链接:https://pan.baidu.com/s/1qJjVCp3twO0nC9jopgRv5w?pwd=0316 
提取码:0316 

 

 

 

 

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/708694
推荐阅读
相关标签
  

闽ICP备14008679号