所用开发板:OneNET NB-IoT 开发板 V2.1
需要ESP8266
需要注册心知天气账号(自行百度)
1、头文件
#ifndef __SHOW_WEATHER_H
#define __SHOW_WEATHER_H
#include "OLED.h"
#include <string.h>
#include <stdlib.h>
#include "usart.h"
#include "delay.h"
#include "led.h"
extern char usart2Buf[512];
extern int usart2Len;
extern char usart1Buf[512];
extern int usart1Len;
//映射汉字及其编号
struct cn_and_num{
char cn[4];
int num;
};
void init_cnnum(struct cn_and_num *cnnum,int len);//初始化汉字代号映射表
//将汉字转化其在字库中的位置
void change_cn_num(int *cn_num,char *cn_buf,int cn_buf_len,
struct cn_and_num *cnnum,int cnnum_len);
void show_wether(int *cn_num,int temp,char *time);//输出格式化后的数据到oled
void data_parsing(char *tmp,int len,char *time_buf,
char *cn_buf,int *wendu);//对获得的天气数据进行解析
void get_city_weather(int type);//初始化wifi模块、根据编号获取对应城市的天气
//若连接好wifi以后仅需调用这个
void show_city_weather(int type,int *check);//根据编号打印获取城市天气并输出到oled
/*
type :
0 初始化
1 成都
2 南充
3 拉萨
4 石家庄
5 乌鲁木齐
*/
#endif
2、具体实现
2.1、说明
- 解析步骤
获取原始数据 --> 解析提取 --> 获取汉字在字库的位置 --> 显示
显示核心函数:OLED_ShowCN(uint8_t x,uint8_t y,uint8_t index)
即根据汉字在字库中的位置 index来显示汉字
- 原始数据
- 解析后的数据
=================
南充abc阴abc
34
2022-08-08 10:46
=================
心知天气返回的数据为UTF-8编码(每个汉字3个字节)
把城市和天气放一起是为了方便后面显示 abc为分割标志
2.2、让keil能够编译UTF-8的数据
–locale=english
2.3、未解决的问题
使用AT+RST命令后必须重置开发板否则会卡死
也就是AT+RST命令和天气查询不能共存
2.4、代码
#include "show_weather.h"
static int loop_i;
static int loop_j;
static int loop_b;
/* 初始化汉字与其代号的映射表
*/
void init_cnnum(struct cn_and_num *cnnum,int len){
loop_i = 0;
loop_j = 18;
strncpy(cnnum[loop_i].cn,"成",3);
cnnum[loop_i].cn[3] = 0;//收尾处理
cnnum[loop_i].num = loop_j;//成 在字库的位置为18
loop_i++;
loop_j++;
strncpy(cnnum[loop_i].cn,"都",3);
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"拉");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"萨");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"石");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"家");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"庄");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"南");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"充");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"乌");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"鲁");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"木");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"齐");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"晴");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"阴");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"多");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"云");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"转");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"大");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"小");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"到");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"暴");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"雨");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"中");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"获");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"取");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"失");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
loop_j++;
strcpy(cnnum[loop_i].cn,"败");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = loop_j;
loop_i++;
strcpy(cnnum[loop_i].cn,"abc");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = -1;
loop_i++;
strcpy(cnnum[loop_i].cn,"abc");
cnnum[loop_i].cn[3] = 0;
cnnum[loop_i].num = -1;
loop_i = 0;
loop_j = 0;
}
/*
*汉字转化:将汉字转化其在字库中的位置
*cn_num 保存有 城市名和天气 的数字代号 以-1隔开
*如:18 19 -1 30 -1 代表: 成 都 晴
*
*cn_buf 需要转化的汉字集合 格式:城市名abc天气abc
*cn_buf_len:cn_buf大小
*
*cnnum 保存汉字及其数字代号
*cnnum_len cnnum大小
*/
void change_cn_num(int *cn_num,char *cn_buf,int cn_buf_len,
struct cn_and_num *cnnum,int cnnum_len){
int head,tail;//动态改变遍历位置,减小时间开销
head = 0;
for(loop_i=0;loop_i<cn_buf_len/3;loop_i++){
tail = cnnum_len+1;
for(loop_j = head;loop_j<tail;loop_j++){
if(cnnum[loop_j].num == -1){//映射表结尾
loop_j = 0;//从头开始找
tail = head;//到上一个字结束
}
//一个汉字占3个字节
else if(strncmp(cn_buf+loop_i*3,"abc",3) == 0){//分割位
cn_num[loop_i] = -1;
head = loop_j;
break;
}
else if(strncmp(cnnum[loop_j].cn,cn_buf+loop_i*3,3) == 0){
cn_num[loop_i] = cnnum[loop_j].num;
head = loop_j;//从这个字后面开始找
break;
}
}
}
}
/*
*天气显示
*cn_num 保存有 城市名和天气 的数字代号 以-1隔开
*如:18 18 -1 20 -1 代表: 成 都 晴
*
*temp 温度
*time 更新时间
*/
void show_wether(int *cn_num,int temp,char *time){
//城市
OLED_Clear();
OLED_ShowCN(0,0,13);
OLED_ShowCN(16,0,14);
OLED_ShowChar(1,5,':');
for(loop_i = 0;loop_i<30;loop_i++){
if(cn_num[loop_i] == -1){
loop_j = loop_i +1;
break;
}
OLED_ShowCN(42+loop_i*16,0,cn_num[loop_i]);
}
//天气
OLED_ShowCN(0,2,15);
OLED_ShowCN(16,2,16);
OLED_ShowChar(2,5,':');
for(loop_i = loop_j;loop_i<30;loop_i++){
if(cn_num[loop_i] == -1){
break;
}
OLED_ShowCN(42+(loop_i-loop_j)*16,2,cn_num[loop_i]);
}
//温度
OLED_ShowCN(0,4,11);
OLED_ShowCN(16,4,17);
OLED_ShowChar(3,5,':');
OLED_ShowNum(3,6,temp,2);
//更新时间;
OLED_ShowString(4,1,time);
}
/* 数据解析
* tmp 需要解析的数据 len tmp的数据长度
* time_buf 更新天气时的时间
* cn_buf 城市+天气 示例 成都abc晴abc
* wendu 温度
*/
void data_parsing(char *tmp,int len,char *time_buf,
char *cn_buf,int *wendu){
char temp_buf[3];
int temp;//温度
memset(temp_buf,0,3);
OLED_weather_loading(30,0);
//从name 前开始
for(loop_i = 46;loop_i< len - 10;loop_i++){
//获取城市名
//strncmp(tmp+loop_i,"name",4);
if(tmp[loop_i] == 'n' && tmp[loop_i+1] == 'a' &&
tmp[loop_i+2] == 'm' && tmp[loop_i+3] == 'e'){
loop_j = loop_i + 7;
loop_b = 0;
while ( tmp[loop_j] != '\"' && loop_b<20){
cn_buf[loop_b] = tmp[loop_j];
loop_b++;
loop_j++;
OLED_weather_loading(31,loop_b%99);
}
//添加分割标记
cn_buf[loop_b] = 'a';
loop_b++;
cn_buf[loop_b] = 'b';
loop_b++;
cn_buf[loop_b] = 'c';
loop_b++;
cn_buf[loop_b] = 0;//收尾处理
}
//获取天气
else if(tmp[loop_i] == 't' && tmp[loop_i+1] == 'e' &&
tmp[loop_i+2] == 'x' && tmp[loop_i+3] == 't'){
loop_j = loop_i + 7;
while ( tmp[loop_j] != '\"'&& loop_b<40){
cn_buf[loop_b] = tmp[loop_j];
loop_b++;
loop_j++;
OLED_weather_loading(32,loop_b%99);
}
cn_buf[loop_b] = 'a';
loop_b++;
cn_buf[loop_b] = 'b';
loop_b++;
cn_buf[loop_b] = 'c';
loop_b++;
cn_buf[loop_b] = 0;
UsartPrintf(USART1, cn_buf);//向串口1发解析后的数据
}
//获取温度
else if(tmp[loop_i] == 't' && tmp[loop_i+1] == 'u' &&
tmp[loop_i+2] == 'r' && tmp[loop_i+3] == 'e'){
loop_j = loop_i + 7;
loop_b = 0;
while ( tmp[loop_j] != '\"'&& loop_b<3){
temp_buf[loop_b] = tmp[loop_j];
loop_b++;
loop_j++;
OLED_weather_loading(33,loop_b%99);
}
temp_buf[loop_b] = '\0';//收尾处理
//这里用atoi会出错
//0的ASCII码是 48
temp = (temp_buf[0] - 48)*10 + temp_buf[1] - 48;
*wendu = temp;
UsartPrintf(USART1, temp_buf);
}
//获取时间
else if(tmp[loop_i] == 'd' && tmp[loop_i+1] == 'a' &&
tmp[loop_i+2] == 't' && tmp[loop_i+3] == 'e') {
loop_j = loop_i + 12;
loop_b = 0;
while ( tmp[loop_j] != '+'&& tmp[loop_j] != '}' && loop_b<16){
time_buf[loop_b] = tmp[loop_j];
loop_b++;
loop_j++;
OLED_weather_loading(34,loop_b%99);
}
time_buf [5] = ' ';//把横线转为空格
time_buf [loop_b] = 0;
UsartPrintf(USART1, time_buf);
OLED_weather_loading(34,loop_b);
break;
}
OLED_weather_loading(30,loop_i%99);
}
}
/* 获取对应城市的天气 */
void get_city_weather(int city){
char conn_server[] = "AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80\r\n";//连接服务器
switch(city){
case 0://初始化
{
unsigned char query_weather[] = "GET https://api.seniverse.com/v3/weather/now.json?key=SpKydYpilyEAHrt6C&location=nanchong&language=zh-Hans&unit=c\r\n";//南充
//配置wifi 从串口2
OLED_weather_loading(2,1);
Usart_SendString(USART2,(unsigned char *)"+++",3);//退出透传
Delay_ms(1000);
OLED_weather_loading(2,2);
Usart_SendString(USART2,(unsigned char *)"+++",3);//退出透传
Delay_ms(1000);
OLED_weather_loading(2,3);
Usart_SendString(USART2,(unsigned char *)"+++",3);//退3次就能成功
Delay_ms(500);
// 重置会出错
// usart2Len = 0;
// OLED_weather_loading(2,41);
// UsartPrintf(USART2,"AT+RST\r\n");//重置
// Delay_ms(5000);
// usart2Len = 0;
OLED_weather_loading(2,42);
Usart_SendString(USART2, (unsigned char *)"AT+CIPMUX=0\r\n",sizeof("AT+CIPMUX=0\r\n"));//禁止多连接
Delay_ms(500);
OLED_weather_loading(2,5);
Usart_SendString(USART2,(unsigned char *)conn_server,strlen(conn_server));//连接心知天气服务器
Delay_ms(500);
OLED_weather_loading(2,6);
Usart_SendString(USART2, (unsigned char *)"AT+CIPMODE=1\r\n",sizeof("AT+CIPMODE=1\r\n"));//使能穿透模式
Delay_ms(500);
OLED_weather_loading(2,7);
Usart_SendString(USART2, (unsigned char *)"AT+CIPSEND\r\n",sizeof("AT+CIPSEND\r\n"));//给服务器发消息测试
Delay_ms(500);
usart2Len = 0;//保证接收的是天气原始数据
Usart_SendString(USART2,(unsigned char *)query_weather,sizeof(query_weather));//查询天气数据
OLED_weather_loading(25,0);
break;
}
case 1:
{
unsigned char query_weather[] = "GET https://api.seniverse.com/v3/weather/now.json?key=SpKydYpilyEAHrt6C&location=chengdu&language=zh-Hans&unit=c\r\n";//查询成都天气
usart2Len = 0;
Usart_SendString(USART2,(unsigned char *)query_weather,sizeof(query_weather));//查询天气数据
OLED_weather_loading(25,1);
break;
}
case 2:
{
unsigned char query_weather[] = "GET https://api.seniverse.com/v3/weather/now.json?key=SpKydYpilyEAHrt6C&location=nanchong&language=zh-Hans&unit=c\r\n";//南充
usart2Len = 0;
Usart_SendString(USART2,query_weather,sizeof(query_weather));//查询天气数据
OLED_weather_loading(25,2);
break;
}
case 3:
{
unsigned char query_weather[] = "GET https://api.seniverse.com/v3/weather/now.json?key=SpKydYpilyEAHrt6C&location=lasa&language=zh-Hans&unit=c\r\n";//拉萨
usart2Len = 0;
Usart_SendString(USART2,query_weather,sizeof(query_weather));//查询天气数据
OLED_weather_loading(25,3);
break;
}
case 4:
{
unsigned char query_weather[] = "GET https://api.seniverse.com/v3/weather/now.json?key=SpKydYpilyEAHrt6C&location=shijiazhuang&language=zh-Hans&unit=c\r\n";//石家庄
usart2Len = 0;
Usart_SendString(USART2,query_weather,sizeof(query_weather));//查询天气数据
OLED_weather_loading(25,4);
break;
}
case 5:
{
unsigned char query_weather[] = "GET https://api.seniverse.com/v3/weather/now.json?key=SpKydYpilyEAHrt6C&location=wulumuqi&language=zh-Hans&unit=c\r\n";//乌鲁木齐
usart2Len = 0;
Usart_SendString(USART2,query_weather,sizeof(query_weather));//查询天气数据
OLED_weather_loading(25,5);
break;
}
default:
break;
}
Delay_ms(500);
}
/* 获取并打印对应城市的天气 */
void show_city_weather(int type,int *check){
//给个默认值
char data_tmp[300] = "{\"results\":[{\"location\":{\"id\":\"WM6N2PM3WY2K\",\"name\":\"获取失败\",\"country\":\"CN\",\"path\":\"xx,xx,xx,xx\",\"timezone\":\"Asia/Shanghai\",\"timezone_offset\":\"+08:00\"},\"now\":{\"text\":\"获取失败\",\"code\":\"0\",\"temperature\":\"0\"},\"last_update\":\"0000-00-00T00:00:00+00:00\"}]}";
int temp;//温度
char time_buf[20];//时间
char cn_buf[50];//需要转换的汉字集合
int cn_num[40];//保存汉字编号
struct cn_and_num cnnum[128];//映射汉字及其编号
OLED_weather_loading(1,0);
memset(cnnum,0,sizeof(cnnum));//初始化汉字代码映射表
memset(cn_num,-1,sizeof(cn_num));//初始化汉字编号
memset(cn_buf,0,sizeof(cn_buf));//初始化解析后的汉字集合
init_cnnum(cnnum,128);
Delay_ms(500);
get_city_weather(type);
OLED_weather_loading(1,1);
Delay_ms(500);
if(strncmp(usart2Buf,"{\"results\":",10) == 0){
memset(data_tmp,0,sizeof(data_tmp));
strncpy(data_tmp,usart2Buf,strlen(usart2Buf));
OLED_weather_loading(1,2);
usart2Len = 0;//及时清零
}
//UsartPrintf(USART1, data_tmp);
//天气数据解析
data_parsing(data_tmp,strlen(data_tmp),time_buf,cn_buf,&temp);
OLED_weather_loading(1,3);
//汉字转代号
change_cn_num(cn_num,cn_buf,sizeof(cn_buf),cnnum,30);
OLED_weather_loading(1,4);
//打印
show_wether(cn_num,temp,time_buf);
*check = 0;
// GPIO_SetBits(GPIOA,GPIO_Pin_5);
return ;
}