极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 37211|回复: 12

《博哥ESP8266系列2》- ESP8266 获取天气情况

[复制链接]
发表于 2017-9-15 08:27:21 | 显示全部楼层 |阅读模式
一、前言                          

         接着[url=《博哥ESP8266系列1》- Arduino IDE for esp8266 http://www.geek-workshop.com/thread-30836-1-1.html (出处: 极客工坊)]上一篇[/url],这一次我们在配置好的环境下撸代码。之前说了,8266既然作为wifi模块,那么我们就可以进行一些网络请求。在我刚开始学Android的时候,最喜欢找一些免费的第三方接口去做一些小东西,这里也一样,我们直接用第三方API 心知天气 。具体的细节请到官网去参考文档,我只介绍其中一个API。我们用第三方接口的时候,免不了要注册以及使用到API_KEY,不想注册的同学直接使用楼主的密钥(wcmquevztdy1jpca),仅供学习参考用。

二、获取指定城市的实况天气

       请求地址: https://api.thinkpage.cn/v3/weat ... guage=en&unit=c
       请求方式:Get
       1.首先,我们可以在浏览器直接打开以上网址,不出意外,应该有以下信息,也就是我们需要的信息。
      [kenrobot_code]{
    "results": [
        {
            "location": {
                "id": "WS0E9D8WN298",
                "name": "Guangzhou",
                "country": "CN",
                "path": "Guangzhou,Guangzhou,Guangdong,China",
                "timezone": "Asia/Shanghai",
                "timezone_offset": "+08:00"
            },
            "now": {
                "text": "Sunny",
                "code": "1",
                "temperature": "26"
            },
            "last_update": "2017-09-13T23:05:00+08:00"
        }
    ]
}[/kenrobot_code]

       2.直接撸上我们的代码:
       [kenrobot_code]/**
* 日期:2017/09/13
* 功能:在线获取天气数据情况 心知天气API
* 作者:单片机菜鸟
**/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
  
const char* ssid     = "360wifilulu";         // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* password = "6206908you11011010";         // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const char* host = "api.seniverse.com";
const char* APIKEY = "wcmquevztdy1jpca";        //API KEY
const char* city = "guangzhou";
const char* language = "en";//zh-Hans 简体中文  会显示乱码
  
const unsigned long BAUD_RATE = 115200;                   // serial connection speed
const unsigned long HTTP_TIMEOUT = 5000;               // max respone time from server
const size_t MAX_CONTENT_SIZE = 1000;                   // max size of the HTTP response
  
// 我们要从此网页中提取的数据的类型
struct UserData {
  char city[16];//城市名称
  char weather[32];//天气介绍(多云...)
  char temp[16];//温度
  char udate[32];//更新时间
};
  
WiFiClient client;
char response[MAX_CONTENT_SIZE];
char endOfHeaders[] = "\r\n\r\n";
  
/**
* @Desc 初始化操作
*/
void setup() {
  WiFi.mode(WIFI_STA);     //设置esp8266 工作模式
  Serial.begin(BAUD_RATE);
  delay(10);
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");//写几句提示,哈哈
  Serial.println(ssid);
  WiFi.begin(ssid, password);   //连接wifi
  while (WiFi.status() != WL_CONNECTED) {
    //这个函数是wifi连接状态,返回wifi链接状态
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  delay(500);
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());//WiFi.localIP()返回8266获得的ip地址
  client.setTimeout(HTTP_TIMEOUT);
}
  
/**
* @Desc  主函数
*/
void loop() {
  while (!client.connected()){
     if (!client.connect(host, 80)){
         Serial.println("connection....");
         delay(500);
     }
  }
  if (sendRequest(host, city, APIKEY) && skipResponseHeaders()) {
    clrEsp8266ResponseBuffer();
    readReponseContent(response, sizeof(response));
    UserData userData;
    if (parseUserData(response, &userData)) {
      printUserData(&userData);
    }
  }
  delay(5000);//每5s调用一次
}
  
/**
* @发送请求指令
*/
bool sendRequest(const char* host, const char* cityid, const char* apiKey) {
  // We now create a URI for the request
  //心知天气
  String GetUrl = "/v3/weather/now.json?key=";
  GetUrl += apiKey;
  GetUrl += "&location=";
  GetUrl += city;
  GetUrl += "&language=";
  GetUrl += language;
  // This will send the request to the server
  client.print(String("GET ") + GetUrl + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n\r\n");
  Serial.println("create a request:");
  Serial.println(String("GET ") + GetUrl + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "Connection: close\r\n");
  delay(1000);
  return true;
}
  
/**
* @Desc 跳过 HTTP 头,使我们在响应正文的开头
*/
bool skipResponseHeaders() {
  // HTTP headers end with an empty line
  bool ok = client.find(endOfHeaders);
  if (!ok) {
    Serial.println("No response or invalid response!");
  }
  return ok;
}
  
/**
* @Desc 从HTTP服务器响应中读取正文
*/
void readReponseContent(char* content, size_t maxSize) {
  size_t length = client.peekBytes(content, maxSize);
  delay(100);
  Serial.println("Get the data from Internet!");
  content[length] = 0;
  Serial.println(content);
  Serial.println("Read data Over!");
  client.flush();//这句代码需要加上  不然会发现每隔一次client.find会失败
}
  
/**
* @Desc 解析数据
* 数据格式如下:
* {
*    "results": [
*        {
*            "location": {
*                "id": "WX4FBXXFKE4F",
*                "name": "北京",
*                "country": "CN",
*                "path": "北京,北京,中国",
*                "timezone": "Asia/Shanghai",
*                "timezone_offset": "+08:00"
*            },
*            "now": {
*                "text": "多云",
*                "code": "4",
*                "temperature": "23"
*            },
*            "last_update": "2017-09-13T09:51:00+08:00"
*        }
*    ]
*}
*/
bool parseUserData(char* content, struct UserData* userData) {
//    -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
//   如果你使用StaticJsonBuffer时才需要
//    const size_t BUFFER_SIZE = 1024;
//   在堆栈上分配一个临时内存池
//    StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
//    -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
  DynamicJsonBuffer jsonBuffer;
   
  JsonObject& root = jsonBuffer.parseObject(content);
   
  if (!root.success()) {
    Serial.println("JSON parsing failed!");
    return false;
  }
   
  //复制我们感兴趣的字符串
  strcpy(userData->city, root["results"][0]["location"]["name"]);
  strcpy(userData->weather, root["results"][0]["now"]["text"]);
  strcpy(userData->temp, root["results"][0]["now"]["temperature"]);
  strcpy(userData->udate, root["results"][0]["last_update"]);
  //  -- 这不是强制复制,你可以使用指针,因为他们是指向“内容”缓冲区内,所以你需要确保
  //   当你读取字符串时它仍在内存中
  return true;
}
   
// 打印从JSON中提取的数据
void printUserData(const struct UserData* userData) {
  Serial.println("Print parsed data :");
  Serial.print("City : ");
  Serial.print(userData->city);
  Serial.print(", \t");
  Serial.print("Weather : ");
  Serial.print(userData->weather);
  Serial.print(",\t");
  Serial.print("Temp : ");
  Serial.print(userData->temp);
  Serial.print(" C");
  Serial.print(",\t");
  Serial.print("Last Updata : ");
  Serial.print(userData->udate);
  Serial.println("\r\n");
}
   
// 关闭与HTTP服务器连接
void stopConnect() {
  Serial.println("Disconnect");
  client.stop();
}
  
void clrEsp8266ResponseBuffer(void){
    memset(response, 0, MAX_CONTENT_SIZE);      //清空
}[/kenrobot_code]

       代码很熟悉有木有?是不是跟直接烧录Arduino一样。
      这里我们用到了ArduinoJson库,不清楚的同学可以参考我这篇帖子 ArduinoJson库
      然后把上面的代码烧录进你的Esp8266(楼主的是01)。没有意外的话,Esp8266处于工作模式时会打印出信息:
      
      
     那么有人问了,该怎么连线呢?
     硬件平台搭建:
    1、硬件:可以选择以下的任一种
        (1)ESP8266-01 ~13 + USB 转 TTL串口模块(如PL2303、CH340)
        (2)ESP8266开发板或NodeMCU开发板 + USB数据线
         硬件连接如下
                ESP8266-01 ~13 + USB 转 TTL串口模块(如PL2303、CH340)
        (1)如果wifi模块是ESP8266-01的按以下接线:(按烧写模式接线)
                运行模式接线方法:(用USB转TTL串口连接模块与PC)
                esp8266-01         u转串
                VCC-----------3.3
                GND----------GND
                CH_PD--------3.3
                RX-------------TX
                TX-------------Rx
                其余引脚为空。
               烧写模式接线方法:烧写模式时需要将gpio0接地,工作模式时gpio0悬空
               (注意有时8266需要独立供电,不直接在ttl取电,很多出现问题都在供电上,别对自己的电源太自信)
                  

     楼主的连接图(参考跑龙套帖子-http://www.arduino.cn/forum.php? ... =1&extra=#pid148026):
        
     楼主这里用了一个Esp测试座,本质上还是上面的操作方式。附上一个测试座的讲解图:
      
    硬件连接好了,就可以下载了,配置参考以下图。
     
    点击上传,稍等一会下载完成。默认出现以下页面就是正常下载。
      
    下载时可能会出现以下问题:
     1.下载出错,可能没有进入到烧录模式
           
     2.卡着不动,需重启一下8266,烧写模式时GPIO0接地,烧写完毕重启模块,GPIO0置空。
     3.正确的下载的情况是ide通知栏是一串小点在增加,最后显示下载成功

三、总结

     研究过程总会遇到各种问题,最重要淡定。

     通过以上代码,我们就可以在线获取天气情况,既然能拿到天气信息,那么我们就可以在这基础上加入点显示功能,比如什么OLED 点阵屏等等。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2017-9-15 09:09:28 | 显示全部楼层
好文章,通俗易懂~~~感谢分享
回复 支持 反对

使用道具 举报

发表于 2017-9-15 21:02:44 | 显示全部楼层
好文章,多谢


回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-9-15 22:07:44 | 显示全部楼层
lala5 发表于 2017-9-15 09:09
好文章,通俗易懂~~~感谢分享

谢谢支持哈哈哈
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-9-15 22:07:58 | 显示全部楼层

谢谢支持 继续更新
回复 支持 反对

使用道具 举报

发表于 2017-9-18 10:46:23 | 显示全部楼层
先收藏下次再請教問題,
3Q for sharing.
回复 支持 反对

使用道具 举报

发表于 2017-10-1 20:12:33 | 显示全部楼层
想根据楼主的代码做一个天气瓶子,把处理后的数据显示在oled上,并通过判断code的值来做不同的反馈。现在oled已经能显示成功,但判断code的值的时候出了问题,“userData->code”是什么格式的?怎么用if或case语句判断数值?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-10-3 09:56:41 | 显示全部楼层
monster.yang 发表于 2017-10-1 20:12
想根据楼主的代码做一个天气瓶子,把处理后的数据显示在oled上,并通过判断code的值来做不同的反馈。现在ol ...

这是一个结构体
回复 支持 反对

使用道具 举报

发表于 2017-10-15 15:22:54 | 显示全部楼层
请问中文乱码怎么解决????
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-10-15 18:41:20 | 显示全部楼层
lbq691477940 发表于 2017-10-15 15:22
请问中文乱码怎么解决????

中文乱码应该是编码问题
回复 支持 反对

使用道具 举报

发表于 2017-11-28 19:16:47 | 显示全部楼层
好文章,通俗易懂~~~感谢分享,多谢
回复 支持 反对

使用道具 举报

发表于 2019-9-16 22:19:26 | 显示全部楼层
请问博哥  代码和你一样 库也下了 为啥一运行就jeson解析错误


Get the data from Internet!
{"results":[{"location":{"id":"WS0E9D8WN298","name":"Guangzhou","country":"CN","path":"Guangzhou,Guangzhou,Guangdong,China","timezone":"Asi
Read data Over!
JSON parsing failed!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-17 03:53 , Processed in 0.046547 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表