赞
踩
之前写过一篇
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 | OK | ||
芯片复位 | AT+RST | OK | ||
波特率设置 | AT+CIOBAUD=BaudRate(默认115200) | OK | ||
设置工作模式 | AT+CWMODE=Mode | OK | ||
Mode=1 | Station模式(客户端) | |||
Mode=2 | AP模式(服务器、热点) | |||
Mode=3 | Station+AP模式(混合模式) | |||
AP模式连接WIFI | AT+CWSAP=“WIFI名”,“密码” | OK | ||
AP模式开启透传模式 | AT+CIPMODE=Mode | OK | ||
Mode=0 | 非透传,缺省模式(默认) | |||
Mode=1 | 透传模式 | |||
Station模式设置服务器端口号 | AT+CIPSERVER=1,8080(默认) | |||
Station+AP模式设置 | AT+CIPMUX=MODE | OK | ||
MODE=0 | 单连接模式 | |||
MODE=1 | 多连接模式 | |||
退出AP模式 | AT+CWQAP | OK | ||
设置发送数据 | 单路连接 (AT+CIPMUX=0) |
| ||
多路连接 (AT+CIPMUX=1) | ||||
查询IP地址 | AT+CIFSR | 当前IP地址 |
连接好模块,进行单ESP8266-01S和串口助手结合AT指令获取天气数据、实践数据,这一步要结合代码的ESP8266.c文件一起看,更好的理解运行方式。
ESP8266模块
ESP8266模块
另外还需要在电脑端准备一个能进行串口通讯的软件,这里我使用的是XCOM,软件我已经放到文章末尾的百度网盘里了,需要的自取即可。求赞!
安装好XCOM之后,将模块插在电脑上,软件会自动找到插在了哪个端口,(需要安装CH340的串口驱动,这个不用多说吧,不懂得百度一下就行了)选择波特率115200,其余设置如下图一,最后点击打开串口助手即可开始用AT指令控制ESP8266了!
图一
如图一所示,出现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})。
图二:正确获取的结果
观察数据会发现有乱码的中文,其实这是显示问题,实际上数据是准确的。完成了理论验证,接下来就是换成是STM32来控制ESP8266获取天气数据了。
本次共用到了两个串口;分别是串口一和串口二。
介绍一下STM32F103C8T6的串口资源,这里直接贴一下别人写的文章真的很不错。http://t.csdnimg.cn/yCPg7http://t.csdnimg.cn/yCPg7
串口模块是初始化了之后,设置好波特率就可以开始使用了,波特率设置一定要对上,电脑对ESP8266或者ESP8266对STM32都是,否则就是乱码。
这里贴上两个串口的初始化代码函数:
- //==================================================================
- //函 数 名:uart1_init(u32 bound)
- //功能描述:串口一初始化函数
- //输入参数:串口要使用的波特率
- //==================================================================
- void uart1_init(u32 bound){
- //GPIO端口设置
- GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
-
- //USART1_TX GPIOA.9
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
-
- //USART1_RX GPIOA.10初始化
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
-
- //Usart1 NVIC 配置
- NVIC_InitStructure.NVIC_IRQChannel = USART1_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 = bound;//串口波特率
- 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(USART1, &USART_InitStructure); //初始化串口1
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
- USART_Cmd(USART1, ENABLE); //使能串口1
-
- }
-
-
- //==================================================================
- //函 数 名:uart2_Init(unsigned int baud)
- //功能描述:串口二初始化函数
- //输入参数:串口要使用的波特率
- //==================================================================
- void Usart2_Init(unsigned int baud)
- {
-
- GPIO_InitTypeDef gpioInitStruct;
- USART_InitTypeDef usartInitStruct;
- NVIC_InitTypeDef nvicInitStruct;
-
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
-
- //PA2 TXD
- gpioInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
- gpioInitStruct.GPIO_Pin = GPIO_Pin_2;
- gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &gpioInitStruct);
-
- //PA3 RXD
- gpioInitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- gpioInitStruct.GPIO_Pin = GPIO_Pin_3;
- gpioInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(GPIOA, &gpioInitStruct);
-
- usartInitStruct.USART_BaudRate = baud;
- usartInitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
- usartInitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送
- usartInitStruct.USART_Parity = USART_Parity_No; //无校验
- usartInitStruct.USART_StopBits = USART_StopBits_1; //1位停止位
- usartInitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
- USART_Init(USART2, &usartInitStruct);
-
- USART_Cmd(USART2, ENABLE); //使能串口
-
- USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能接收中断
-
- nvicInitStruct.NVIC_IRQChannel = USART2_IRQn;
- nvicInitStruct.NVIC_IRQChannelCmd = ENABLE;
- nvicInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
- nvicInitStruct.NVIC_IRQChannelSubPriority = 0;
- NVIC_Init(&nvicInitStruct);
-
- }
-
-
- /******************************************************** 发送一个16位数 ************************************************/
- void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
- {
- uint8_t temp_h, temp_l;
-
- /* 取出高八位 */
- temp_h = (ch&0XFF00)>>8;
- /* 取出低八位 */
- temp_l = ch&0XFF;
-
- /* 发送高八位 */
- USART_SendData(pUSARTx,temp_h);
- while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
-
- /* 发送低八位 */
- USART_SendData(pUSARTx,temp_l);
- while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
- }
-
- /***************** 发送一个字符 **********************/
- void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
- {
- /* 发送一个字节数据到USART */
- USART_SendData(pUSARTx,ch);
-
- /* 等待发送数据寄存器为空 */
- while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
- }
-
- /***************** 发送字符串 **********************/
- void Usart_SendString(USART_TypeDef *USARTx, unsigned char *str, unsigned short len)
- {
-
- unsigned short count = 0;
-
- for(; count < len; count++)
- {
- USART_SendData(USARTx, *str++); //发送数据
- while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); //等待发送完成
- }
-
- }
-
-
- /***************** 发送字符串 **********************/
- void Usart_SendString2( USART_TypeDef * pUSARTx, char *str)
- {
- unsigned int k=0;
- do
- {
- Usart_SendByte( pUSARTx, *(str + k) );
- k++;
- } while(*(str + k)!='\0');
-
- /* 等待发送完成 */
- while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
- {}
- }
-
- /***************** 选择特定的串口发送打印数据 **********************/
- void UsartPrintf(USART_TypeDef *USARTx, char *fmt,...)
- {
-
- unsigned char UsartPrintfBuf[296];
- va_list ap;
- unsigned char *pStr = UsartPrintfBuf;
-
- va_start(ap, fmt);
- vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap); //格式化
- va_end(ap);
-
- while(*pStr != 0)
- {
- USART_SendData(USARTx, *pStr++);
- while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
- }
- }
-
-
串口二中断处理函数:
- //==================================================================
- //函 数 名:USART2_IRQHandler(void)
- //功能描述:串口2中断控制函数
- //输入参数:无
- //介绍说明:将串口2接收到的数据转发出去
- //==================================================================
- void USART2_IRQHandler(void)
- {
- if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
- {
-
- if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0;
-
- esp8266_buf[esp8266_cnt++] = USART2->DR;
- USART_SendData(USART1,USART2->DR);
-
- USART_ClearFlag(USART2, USART_FLAG_RXNE);
- }
- }
串口一的串口中断函数应该是被拆开了,直接对串口一的中断标志位寄存器和数据接收寄存器进行操作。
也是用到了两个定时器,定时器二和定时器三。定时器二用来按键扫描。定时器三用来作为本地是时钟源,运行解析出来的时间。
贴一下定时器的初始化代码,STM32的定时器模块还是比较复杂的,不懂的自己去学习一下。
- //==================================================================
- //函 数 名:TIM3_Int_Init(u32 arr,u16 psc)
- //功能描述:定时器3初始化函数
- //输入参数:arr-自动重装载值 pre-预分频值
- //定时长度:72000000/(arr+1)*(pre+1)
- //==================================================================
- void TIM3_Int_Init(u32 arr,u16 psc)
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
-
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
- TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
-
- TIM_ITConfig( TIM3,TIM_IT_Update,ENABLE); //使能或者失能指定的TIM中断
- NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
- NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
- }
-
- //==================================================================
- //函 数 名:Timer2_Init(void)
- //功能描述:定时器2初始化函数
- //输入参数:无
- //==================================================================
- void Timer2_Init(void)
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
-
- TIM_InternalClockConfig(TIM2);
-
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
- TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
- TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInitStructure.TIM_Period = 999;
- TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
- TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
-
- TIM_ClearFlag(TIM2, TIM_FLAG_Update);
- TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
-
- NVIC_InitTypeDef NVIC_InitStructure;
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
- NVIC_Init(&NVIC_InitStructure);
- }
-
时间戳转换模块是我在网上摘抄下来的,我看csdn平台也有人写相关的,但我看的文章不是csdn的。但现在找关键词找不到了,这里就直接引用了。感谢原作者!
- #include <stdio.h>
- #include "OLED.h"
- #include "timer.h"
- #include "time.h"
-
- typedef unsigned short uint16_t;
- typedef unsigned int uint32_t;
- //全局变量,存储时间
- int year_1,month_1,day_1,hour_1,minter_1,sec_1,week_1;
- //暂时没用的,有时间改一下函数结构,去冗余
- const uint16_t month_days_table[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-
- /**
- * @brief 判断是否为闰年 注:原文的链接忘记复制了,感谢该作者
- * @param [in] year 年
- * @retval 1 为闰年
- * @retval 0 为平年
- */
- uint16_t fml_leap_year(uint16_t year)
- {
- return (((year % 4 == 0)&&(year % 100 != 0)) || (year % 400 == 0));
- }
-
-
- /**
- * @brief 日期转时间戳
- * @param [in] date 日期值
- * @retval =0 成功
- * @retval 非0 失败,以及失败原因
- */
- uint32_t fml_time_to_stamp(date_time_t date)
- {
- static uint32_t dax = 0;
- static uint32_t day_count = 0;
- uint16_t leap_year_count = 0;
- uint16_t i;
-
- // 计算闰年数
- for (i = 1970; i < date.year; i++)
- {
- if (fml_leap_year(i))
- {
- leap_year_count++;
- }
- }
-
- // 计算年的总天数
- day_count = leap_year_count * 366 + (date.year - 1970 - leap_year_count) * 365;
-
- // 累加计算当年所有月的天数
- for (i = 1; i < date.month; i++)
- {
- if ((2 == i) && (fml_leap_year(date.year)))
- {
- day_count += 29;
- }
- else
- {
- day_count += month_days_table[i];
- }
- }
-
- // 累加计算当月的天数
- day_count += (date.day - 1);
-
- dax = (uint32_t)(day_count * 86400) + (uint32_t)((uint32_t)date.hour * 3600) + (uint32_t)((uint32_t)date.min * 60) + (uint32_t)date.sec;
-
- /* 北京时间补偿 */
- dax = dax - 8*60*60;
-
- return dax;
- }
-
- //==================================================================
- //函 数 名:fml_stamp_to_time;
- //功能描述:时间戳转换年月日
- //输入参数:无
- //公式来源:蔡勒公式
- //==================================================================
- uint32_t fml_stamp_to_time( uint32_t timep, date_time_t *date)
- {
- uint32_t days = 0;
- uint32_t rem = 0;
-
- /* 北京时间补偿 */
- timep = timep + 8*60*60;
-
- // 计算天数
- days = (uint32_t)(timep / 86400);
- rem = (uint32_t)(timep % 86400);
-
-
- // 计算年份
- uint16_t year;
- for (year = 1970; ; ++year)
- {
- uint16_t leap = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
- uint16_t ydays = leap ? 366 : 365;
- if (days < ydays)
- {
- break;
- }
- days -= ydays;
- }
- date->year = year;
-
-
- // 计算月份
- static const uint16_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- uint16_t month;
- for (month = 0; month < 12; month++)
- {
- uint16_t mdays = days_in_month[month];
- if (month == 1 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
- {
- mdays = 29;
- }
- if (days < mdays)
- {
- break;
- }
- days -= mdays;
- }
- date->month = month;
- date->month += 1;
-
- // 计算日期
- date->day = days + 1;
-
- // 计算时间
- date->hour = rem / 3600;
- rem %= 3600;
- date->min = rem / 60;
- date->sec = rem % 60;
- year_1 = data1.year;
- month_1 = data1.month;
- day_1 = data1.day;
- hour_1 = data1.hour;
- minter_1 = data1.min;
- sec_1 = data1.min;
- return 0;
- }
- //==================================================================
- //函 数 名:get_week();
- //功能描述:计算出星期
- //输入参数:无
- //公式来源:蔡勒公式
- //==================================================================
- void get_week(void)
- {
- if (month_1 == 1 || month_1 == 2)//判断month是否为1或2
- year_1--, month_1 += 12;
- int c = year_1 / 100;
- int y = year_1 - c * 100;
- int week = y + y / 4 + c / 4 - 2 * c + 26 * (month_1 + 1) / 10 + day_1 - 1;
- while (week < 0)
- week += 7;
- week %= 7;
- week_1 = week;
- }
我这里修改了时间戳转换日期的函数、新增一个星期转换函数。传入10位数的时间戳就可得出对应的日期。
OLED的库函数我也是走了不少弯路才找到的,过程曲折,没想到答案远在天边近在眼前。感谢中景园电子!
这个库函数的功能是我见过比较丰富的了,还有更复杂的是GITHUB上的自制驱动,但是他们用的OLED屏幕和我用的不一样,试图移植过但失败了。贴上.h文件,给大伙看看这个驱动能实现的功能。
- /*初始化函数*/
- void OLED_Init(void);
-
- /*更新函数*/
- void OLED_Update(void);
- void OLED_UpdateArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
-
- /*显存控制函数*/
- void OLED_Clear(void);
- void OLED_ClearArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
- void OLED_Reverse(void);
- void OLED_ReverseArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
-
- /*显示函数*/
- void OLED_ShowChar(uint8_t X, uint8_t Y, char Char, uint8_t FontSize);
- void OLED_ShowString(uint8_t X, uint8_t Y, char *String, uint8_t FontSize);
- void OLED_ShowNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
- void OLED_ShowSignedNum(uint8_t X, uint8_t Y, int32_t Number, uint8_t Length, uint8_t FontSize);
- void OLED_ShowHexNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
- void OLED_ShowBinNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
- void OLED_ShowFloatNum(uint8_t X, uint8_t Y, double Number, uint8_t IntLength, uint8_t FraLength, uint8_t FontSize);
- void OLED_ShowChinese(uint8_t X, uint8_t Y, char *Chinese);
- void OLED_ShowImage(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height, const uint8_t *Image);
- void OLED_Printf(uint8_t X, uint8_t Y, uint8_t FontSize, char *format, ...);
-
- /*绘图函数*/
- void OLED_DrawPoint(uint8_t X, uint8_t Y);
- uint8_t OLED_GetPoint(uint8_t X, uint8_t Y);
- void OLED_DrawLine(uint8_t X0, uint8_t Y0, uint8_t X1, uint8_t Y1);
- void OLED_DrawRectangle(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height, uint8_t IsFilled);
- void OLED_DrawTriangle(uint8_t X0, uint8_t Y0, uint8_t X1, uint8_t Y1, uint8_t X2, uint8_t Y2, uint8_t IsFilled);
- void OLED_DrawCircle(uint8_t X, uint8_t Y, uint8_t Radius, uint8_t IsFilled);
- void OLED_DrawEllipse(uint8_t X, uint8_t Y, uint8_t A, uint8_t B, uint8_t IsFilled);
- void OLED_DrawArc(uint8_t X, uint8_t Y, uint8_t Radius, int16_t StartAngle, int16_t EndAngle, uint8_t IsFilled);
支持两个大小的汉字显示、数字字母显示、图像显示、简单的图形绘制、图片显示。
还是和第一版的一样,我会把取字模和图像的软件打包起来,直接用就可以。
这个就比较的复杂了,直接贴代码吧,大家看了有不懂的评论区留言我帮忙解答
- #include "stm32f10x.h"
- #include "sys.h"
- #include "string.h"
- #include "stdlib.h"
- #include "esp8266.h"
- #include "usart2.h"
- #include "delay.h"
- #include "init.h"
-
- //WIFI的SSID和密码
- #define ESP8266_WIFI_INFO "AT+CWJAP=\"TP-LINK_C568\",\"zz18835784774\"\r\n"
-
- //心知天气API
- #define Xinzhi_TCP "AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80\r\n"
- //拼多多API
- #define Time_TCP "AT+CIPSTART=\"TCP\",\"qapi.pinduoduo.com\",80\r\n"
-
-
- //获取当天天气
- #define Now_GET "GET https://api.seniverse.com/v3/weather/now.json?key=SEa9maHD3BOFLn8Xv&location=beijing&language=en&unit=c\r\n"
- //获取天气预报
- #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"
- //获取生活指数
- #define Life_GET "GET https://api.seniverse.com/v3/life/suggestion.json?key=SEa9maHD3BOFLn8Xv&location=shanghai&language=en&days=1\r\n"
- //获取拼多多时间戳
- #define Time_GET "GET https://api.pinduoduo.com/api/server/_stm\r\n"
-
- unsigned char esp8266_buf[1000] = {0};
- unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
- unsigned char Xinzhi_buff[1000];
- unsigned char Time_buff[50];
-
- //==================================================================
- //函 数 名:ESP8266_Init()
- //功能描述:ESP8266模块初始化
- //介绍说明:按照我写的第一篇文章的步骤,使用串口助手在电脑端通过串口
- //助手分别发送相应的AT指令验证是否能初始化
- //==================================================================
- void ESP8266_Init(void)
- {
- ESP8266_Clear();
- while(ESP8266_SendCmd("+++", ""));
- while(ESP8266_SendCmd("AT+RESTORE\r\n", "OK"));
- while(ESP8266_SendCmd("AT\r\n", "OK"));
- ESP8266_SendCmd("AT+RST\r\n", "");
- delay_ms(500);
- ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");
- delay_ms(500);
- while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"));
- while(ESP8266_SendCmd("AT+CIPMUX=0\r\n", "OK"));
- while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "WIFI GOT IP"));
- }
-
- //==================================================================
- //函 数 名:Get_current_time(unsigned char s)
- //功能描述:获取指定API的数据
- //介绍说明:按照我写的第一篇文章的步骤,使用串口助手在电脑端通过串口
- //助手分别发送相应的AT指令验证是否能获取到对应的数据报文
- //==================================================================
- void Get_current_time(unsigned char s)
- {
- ESP8266_Init();
- ESP8266_Clear();
- switch(s)
- {
- //case 0:获取时间戳报文
- case 0 :{while(ESP8266_SendCmd(Time_TCP, "CONNECT"));
- while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
- ESP8266_SendData((u8 *)Time_GET, sizeof(Time_GET));
- ESP8266_GetIPD_GET(50, Time_buff);
- } break;
- //case 1:获取当前天气报文
- case 1 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
- while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
- ESP8266_SendData((u8 *)Now_GET, sizeof(Now_GET));
- ESP8266_GetIPD_GET(200, Xinzhi_buff);
- } break;
- //case 2:获取天气预报报文
- case 2 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
- while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
- ESP8266_SendData((u8 *)Forcast_GET, sizeof(Forcast_GET));
- ESP8266_GetIPD_GET(1000, Xinzhi_buff);
- } break;
- //case 3:未完成部分-生活指数
- // case 3 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
- // while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
- // ESP8266_SendData((u8 *)Life_GET, sizeof(Life_GET));
- // ESP8266_GetIPD_GET(1000, Xinzhi_buff);
- // } break;
- }
- ESP8266_Clear();
- while(ESP8266_SendCmd("+++", ""));
- }
-
-
- #define NAME_ADD_DRES 7
- #define TEXT_ADD_DRES 7
- #define TEMP_ADD_DRES 14
-
- #define HIGH_TEMP_DRES 7
- #define LOW_TEMP_DRES 6
- #define RAINFALL_DRES 11
- #define WIND_DIRCTION 17
- #define WIND_SCAL 13
- #define HUMITIDITY 11
-
- //#define AIR_POLLU 25
-
- #define TIME_ADD_DRES 13
-
- int TEXT,TEMP,LOW_TEMP,HIGH_TEMP,RAINFALL,WIND_DIR,SCAL_WIND,HUMI;
-
- unsigned int stamp;
-
- //==================================================================
- //函 数 名:cJSON_Time_Parse(unsigned char s)
- //功能描述:通过标志位启动不同的数据解析方式
- //输入参数:0-解析时间、1-解析当前天气、2-解析天气预报
- //==================================================================
- void cJSON_Time_Parse(unsigned char s)
- {
- char *data_pt;
- char *text_string;
- char *temp_string;
- char *time;
- switch(s){
- case 0:{
- data_pt = strstr((const char *)Time_buff, (const char *)"server_time");
- time = data_pt + TIME_ADD_DRES;
- stamp = Get_Time(time);
- } break;
- case 1:{
- data_pt = strstr((const char *)Xinzhi_buff, (const char *)"code");
- text_string = data_pt + TEXT_ADD_DRES;
- data_pt = strstr((const char *)Xinzhi_buff, (const char *)"temperature");
- temp_string = data_pt + TEMP_ADD_DRES;
- TEXT = Get_Temp(text_string);
- TEMP = Get_Temp(temp_string);
- } break;
- case 2:{
- data_pt = strstr((const char *)Xinzhi_buff, (const char *)"high");
- time = data_pt + HIGH_TEMP_DRES;
- HIGH_TEMP = Get_Temp(time);
- data_pt = strstr((const char *)Xinzhi_buff, (const char *)"low");
- time = data_pt + LOW_TEMP_DRES;
- LOW_TEMP = Get_Temp(time);
- data_pt = strstr((const char *)Xinzhi_buff, (const char *)"rainfall");
- time = data_pt + RAINFALL_DRES;
- RAINFALL = Get_Temp(time);
- data_pt = strstr((const char *)Xinzhi_buff, (const char *)"wind_direction");
- time = data_pt + WIND_DIRCTION;
- WIND_DIR = Get_Wind(time);
- data_pt = strstr((const char *)Xinzhi_buff, (const char *)"wind_scale");
- time = data_pt + WIND_SCAL;
- SCAL_WIND = Get_Temp(time);
- data_pt = strstr((const char *)Xinzhi_buff, (const char *)"humidity");
- time = data_pt + HUMITIDITY;
- HUMI = Get_Temp(time);
- } break;
- //未完成部分-生活指数
- // case 3:{
- // data_pt = strstr((const char *)Xinzhi_buff, (const char *)"air_pollution");
- // text_string = data_pt + AIR_POLLU;
- // data_pt = strstr((const char *)Xinzhi_buff, (const char *)"comfort");
- // temp_string = data_pt + TEMP_ADD_DRES;
- // TEXT = Get_Temp(text_string);
- // TEMP = Get_Temp(temp_string);
- // } break;
- }
- }
-
- //==================================================================
- //函 数 名:Get_Wind(char *y)
- //功能描述:得到风向
- //输入参数:无
- //介绍说明:不同的返回值代表不同的风向
- //==================================================================
- int Get_Wind(char *y)
- {
- char dir[] = {'W','E','N','S','"'};// 0 1 2 3
- int flag , i , j = 1;
- for(i=0;i<4;i++)
- {
- if(*y == dir[i]){
- flag = i+4;
- }
- }
- for(i=0;i<2;i++)
- {
- for(j=0;j<2;j++)
- {
- if(*y == dir[i + 2] && *(y+1) == dir[j]){
- if(i==0){flag = i*2 + j*1;};
- if(i==1){flag = i*2 + j*1;};
- }
- }
- }
- return flag;
- }
-
- //==================================================================
- //函 数 名:Get_Temp(char *y)
- //功能描述:从字符串中取出时间戳-最长10位长度
- //输入参数:无
- //==================================================================
- int Get_Temp(char *y)
- {
- int temp_return;
- temp_return = atoi(y);
- return temp_return;
- }
-
- //==================================================================
- //函 数 名:Get_Time(char *d)
- //功能描述:从字符串中取出数字-最长7位长度
- //输入参数:无
- //==================================================================
- unsigned int Get_Time(char *d)
- {
- unsigned int u;
- char dest[10] = {0};
- strncpy(dest, d, 10);
- u = atoi(dest);
- return u;
- }
-
- //==================================================================
- //函 数 名:USART2_IRQHandler(void)
- //功能描述:串口2中断控制函数
- //输入参数:无
- //介绍说明:将串口2接收到的数据转发出去
- //==================================================================
- void USART2_IRQHandler(void)
- {
- if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
- {
-
- if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0;
-
- esp8266_buf[esp8266_cnt++] = USART2->DR;
- USART_SendData(USART1,USART2->DR);
-
- USART_ClearFlag(USART2, USART_FLAG_RXNE);
- }
- }
-
- /**下面的代码来源于:
- ************************************************************
- ************************************************************
- ************************************************************
- * 文件名: esp8266.c
- *
- * 作者: 张继瑞
- *
- * 日期: 2017-05-08
- *
- * 版本: V1.0
- *
- * 说明: ESP8266的简单驱动
- *
- * 修改记录:
- ************************************************************
- ************************************************************
- ************************************************************
- **/
- //==========================================================
- // 函数名称: ESP8266_Clear
- //
- // 函数功能: 清空缓存
- //
- // 入口参数: 无
- //
- // 返回参数: 无
- //
- // 说明:
- //==========================================================
- void ESP8266_Clear(void)
- {
-
- memset(esp8266_buf, 0, sizeof(esp8266_buf));
- esp8266_cnt = 0;
-
- }
- //==========================================================
- // 函数名称: ESP8266_SendData
- //
- // 函数功能: 发送数据
- //
- // 入口参数: data:数据
- // len:长度
- //
- // 返回参数: 无
- //
- // 说明:
- //==========================================================
- void ESP8266_SendData(unsigned char *data, unsigned short len)
- {
- char cmdBuf[32];
-
- ESP8266_Clear(); //清空接收缓存
- sprintf(cmdBuf, "AT+CIPSEND\r\n"); //发送命令
-
- if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’时可以发送数据
- {
- //printf("8.开始处于透传发送状态!\r\n");
- /*发送请求数据*/
- Usart_SendString(USART2, data, len); //发送设备连接请求数据
- }
- }
-
- //==========================================================
- // 函数名称: ESP8266_GetIPD
- //
- // 函数功能: copy天气数据到buff数组里面
- //
- // 返回参数: 平台返回的原始数据
- //
- // 说明: copy天气数据到buff
- //==========================================================
-
- unsigned char *ESP8266_GetIPD_GET(unsigned short timeOut, u8 *buff) //这里我用了一个全局变量将esp8266buf储存到这个全局变量里面
- {
- do
- {
- delay_ms(5);
- }
- while(timeOut--);
-
- strcpy((char*)buff, (char*)esp8266_buf);
- return buff;
- }
-
- //==========================================================
- // 函数名称: ESP8266_WaitRecive
- //
- // 函数功能: 等待接收完成
- //
- // 入口参数: 无
- //
- // 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成
- //
- // 说明: 循环调用检测是否接收完成
- //==========================================================
- _Bool ESP8266_WaitRecive(void)
- {
-
- if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
- return REV_WAIT;
-
- if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕
- {
- esp8266_cnt = 0; //清0接收计数
-
- return REV_OK; //返回接收完成标志
- }
-
- esp8266_cntPre = esp8266_cnt; //置为相同
-
- return REV_WAIT; //返回接收未完成标志
-
- }
-
-
- //==========================================================
- // 函数名称: ESP8266_SendCmd
- //
- // 函数功能: 发送命令
- //
- // 入口参数: cmd:命令
- // res:需要检查的返回指令
- //
- // 返回参数: 0-成功 1-失败
- //
- // 说明:
- //==========================================================
- _Bool ESP8266_SendCmd(char *cmd, char *res)
- {
-
- unsigned char timeOut = 250;
-
- Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
-
- while(timeOut--)
- {
- if(ESP8266_WaitRecive() == REV_OK) //如果收到数据
- {
- if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词
- {
- ESP8266_Clear(); //清空缓存
-
- return 0;
- }
- }
-
- delay_ms(10);
- }
- return 1;
- }
-
-
-
-
-
-
-
所有模块
时间显示
天气显示
其他显示
拥有以上模块就可以复现一下了。最后附上我制作的资源包。
链接:https://pan.baidu.com/s/1qJjVCp3twO0nC9jopgRv5w?pwd=0316
提取码:0316
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。