天气预报

借鉴代码  菜鸟 https://blog.csdn.net/dpjcn1990/article/details/92830087

/**
* Demo:
* 天气信息,并输出
* @author lsq
* @date 2019/12/30
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
//以下三个定义为调试定义
#define DebugBegin(baud_rate) Serial.begin(baud_rate)
#define DebugPrintln(message) Serial.println(message)
#define DebugPrint(message) Serial.print(message)

//显示屏信息
// For the breakout, you can use any 2 or 3 pins
// These pins will also work for the 1.8" TFT shield
#define TFT_CS D1
#define TFT_RST D2 // you can also connect this to the Arduino reset
// in which case, set this #define pin to 0!
#define TFT_DC D3

// Option 1 (recommended): must use the hardware SPI pins
// (for UNO thats sclk = 13 and sid = 11) and pin 10 must be
// an output. This is much faster - also required if you want
// to use the microSD card (see the image drawing example)
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);

// Option 2: use any pins but a little slower!
#define TFT_SCLK 13 // set these to be whatever pins you like!
#define TFT_MOSI 11 // set these to be whatever pins you like!
//Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

#define WEATHER_CODE_DAY_SUN "0" //晴(国内城市白天晴)
#define WEATHER_CODE_NIGHT_SUN "1" //晴(国内城市夜晚晴)
#define WEATHER_CODE_DAY_SUN1 "2" //晴(国外城市白天晴)
#define WEATHER_CODE_NIGHT_SUN2 "3" //晴(国外城市夜晚晴)
#define WEATHER_CODE_CLOUDY "4" //多云
#define WEATHER_CODE_DAY_PARTLY_CLOUDY "5" //白天晴间多云
#define WEATHER_CODE_NIGHT_PARTLY_CLOUDY "6" //夜晚晴间多云
#define WEATHER_CODE_DAY_MOSTLY_CLOUDY "7" //白天大部多云
#define WEATHER_CODE_NIGHT_MOSTLY_CLOUDY "8" //夜晚大部多云
#define WEATHER_CODE_OVERCAST "9" //阴
#define WEATHER_CODE_SHOWER "10" //阵雨
#define WEATHER_CODE_THUNDERSHOWER "11" //雷阵雨
#define WEATHER_CODE_THUNDERSHOWER_WITH_HAIL "12" //雷阵雨伴有冰雹
#define WEATHER_CODE_LIGHT_RAIN "13" //小雨
#define WEATHER_CODE_MODERATE_RAIN "14" //中雨
#define WEATHER_CODE_HEAVY_RAIN "15" //大雨
#define WEATHER_CODE_STORM "16" //暴雨
#define WEATHER_CODE_HEAVY_STORM "17" //大暴雨
#define WEATHER_CODE_SEVERE_STORM "18" //特大暴雨
#define WEATHER_CODE_ICE_RAIN "19" //冻雨
#define WEATHER_CODE_SLEET "20" //雨夹雪
#define WEATHER_CODE_SNOW_FLURRY "21" //阵雪
#define WEATHER_CODE_LIGHT_SNOW "22" //小雪
#define WEATHER_CODE_MODERATE_SNOW "23" //中雪
#define WEATHER_CODE_HEAVY_SNOW "24" //大雪
#define WEATHER_CODE_SNOW_STORM "25" //暴雪


unsigned char quan[] = {0x01,0x00,0x02,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,0x01,0x04,0x7D,0x88,0x09,0x50,0x11,0x20,0x21,0x18,0xC5,0x06,0x02,0x00};
unsigned char zhou[] = {0x10,0x04,0x10,0x84,0x10,0x84,0x10,0x84,0x10,0x84,0x54,0xA4,0x52,0x94,0x52,0x94,0x90,0x84,0x10,0x84,0x10,0x84,0x10,0x84,0x20,0x84,0x20,0x84,0x40,0x04,0x80,0x04};
unsigned char wen[] = {0x00,0x00,0x23,0xF8,0x12,0x08,0x12,0x08,0x83,0xF8,0x42,0x08,0x42,0x08,0x13,0xF8,0x10,0x00,0x27,0xFC,0xE4,0xA4,0x24,0xA4,0x24,0xA4,0x24,0xA4,0x2F,0xFE,0x00,0x00};
unsigned char du[] = {0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x3F,0xFC,0x22,0x20,0x22,0x20,0x23,0xE0,0x20,0x00,0x2F,0xF0,0x24,0x10,0x42,0x20,0x41,0xC0,0x86,0x30,0x38,0x0E};
unsigned char qing[] = {0x00,0x20,0x00,0x20,0x7B,0xFE,0x48,0x20,0x49,0xFC,0x48,0x20,0x4B,0xFE,0x78,0x00,0x49,0xFC,0x49,0x04,0x49,0xFC,0x49,0x04,0x79,0xFC,0x49,0x04,0x01,0x14,0x01,0x08};

const char* ssid = "kangtine"; // XXXXXX -- 使用时请修改为当前你的 wifi ssid
const char* password = "87602261"; // XXXXXX -- 使用时请修改为当前你的 wifi 密码
const char* host = "api.seniverse.com";
const char* APIKEY = "wcmquevztdy1jpca"; //API KEY
const char* city = "quanzhou";
const char* language = "zh-Hans";//zh-Hans 简体中文 会显示乱码
const char* city2 = "qingdao";
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 = 10000; // max size of the HTTP response

// 我们要从此网页中提取的数据的类型
struct WeatherData {
char city[16];//城市名称
char weather[32];//天气介绍(多云...)
char temp[16];//温度
char udate[32];//更新时间
};

WiFiClient client;
char response[MAX_CONTENT_SIZE];
char endOfHeaders[] = " ";

void setup() {
// put your setup code here, to run once:
WiFi.mode(WIFI_STA); //设置esp8266 工作模式
DebugBegin(BAUD_RATE);
DebugPrint("Connecting to ");//写几句提示,哈哈
DebugPrintln(ssid);
WiFi.begin(ssid, password); //连接wifi
WiFi.setAutoConnect(true);
while (WiFi.status() != WL_CONNECTED) {
//这个函数是wifi连接状态,返回wifi链接状态
delay(500);
DebugPrint(".");
}
DebugPrintln("");
DebugPrintln("WiFi connected");
delay(500);
DebugPrintln("IP address: ");
DebugPrintln(WiFi.localIP());//WiFi.localIP()返回8266获得的ip地址
client.setTimeout(HTTP_TIMEOUT);


// Use this initializer (uncomment) if you're using a 1.44" TFT
tft.initR(INITR_144GREENTAB); // initialize a ST7735S chip, black tab

DebugPrintln("init");


DebugPrintln("This is a test of the rotation capabilities of the TFT library!");
DebugPrintln("Press <SEND> (or type a character) to advance");
//使能软件看门狗的触发间隔
ESP.wdtEnable(5000);
}

void loop() {
// put your main code here, to run repeatedly:
//判断tcp client是否处于连接状态,不是就建立连接
while (!client.connected()){
if (!client.connect(host, 80)){
DebugPrintln("connection....");
delay(500);
ESP.wdtFeed();
}
}
//发送http请求 并且跳过响应头 直接获取响应body
if (sendRequest(host, city2, APIKEY) && skipResponseHeaders()) {
//清除缓冲
clrEsp8266ResponseBuffer();
//读取响应数据
readReponseContent(response, sizeof(response));
WeatherData weatherData;
if (parseUserData(response, &weatherData)) {
printUserData(&weatherData);
}
else
{
tft.fillScreen(ST7735_BLACK);
tft.setCursor(0, 10);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(2);
tft.println("can't find weather ");
delay(1000);
}
}

delay(50000);//每50s调用一次
ESP.wdtFeed();
}

/**
* @发送http请求指令
*/
bool sendRequest(const char* host, const char* cityid, const char* apiKey) {
// We now create a URI for the request
//心知天气 发送http请求
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 " +
"Host: " + host + " " +
"Connection: close ");
DebugPrintln("create a request:");
DebugPrintln(String("GET ") + GetUrl + " HTTP/1.1 " +
"Host: " + host + " " +
"Connection: close ");
delay(1000);
return true;
}

/**
* @Desc 跳过 HTTP 头,使我们在响应正文的开头
*/
bool skipResponseHeaders() {
// HTTP headers end with an empty line
bool ok = client.find(endOfHeaders);
if (!ok) {
DebugPrintln("No response or invalid response!");
}
return ok;
}

/**
* @Desc 从HTTP服务器响应中读取正文
*/
void readReponseContent(char* content, size_t maxSize) {

size_t length = client.readBytes(content, maxSize);
delay(1000);
DebugPrintln("MaxSize");
DebugPrint(client.available());
DebugPrintln("readSize");
DebugPrint(length);
DebugPrintln("Get the data from Internet!");
content[length] = 0;
DebugPrintln(content);
DebugPrintln("Read data Over!");
client.flush();//清除一下缓冲
}

/**
* @Desc 解析数据 Json解析
* 数据格式如下:
* {
* "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 WeatherData* weatherData) {
// -- 根据我们需要解析的数据来计算JSON缓冲区最佳大小
// 如果你使用StaticJsonBuffer时才需要
// const size_t BUFFER_SIZE = 1024;
// 在堆栈上分配一个临时内存池
// StaticJsonBuffer<BUFFER_SIZE> jsonBuffer;
// -- 如果堆栈的内存池太大,使用 DynamicJsonBuffer jsonBuffer 代替
DynamicJsonBuffer jsonBuffer;

JsonObject& root = jsonBuffer.parseObject(content);

if (!root.success()) {
DebugPrintln("JSON parsing failed!");
return false;
}
if(root.containsKey("status"))
{
DebugPrintln("Time out");
return false;
}
else
{
//复制我们感兴趣的字符串
strcpy(weatherData->city, root["results"][0]["location"]["name"]);
strcpy(weatherData->weather, root["results"][0]["now"]["text"]);
strcpy(weatherData->temp, root["results"][0]["now"]["temperature"]);
strcpy(weatherData->udate, root["results"][0]["last_update"]);
// -- 这不是强制复制,你可以使用指针,因为他们是指向“内容”缓冲区内,所以你需要确保
// 当你读取字符串时它仍在内存中
}

return true;
}
//输出中文
void printChinese(uint x,uint y,unsigned char *Hz_code)
{
int tbl_size=sizeof(Hz_code)/sizeof(char);
short int temp_code=0;
int i,j,k;

for(i=0;i<16;i++)
for(j=0;j<2;j++)
for(k=0;k<8;k++)
if(((Hz_code[i*2+j]>>(7-k))&0x1)!=NULL)
tft.drawPixel(x+8*j+k,y+i, ST7735_WHITE);
}

// 打印从JSON中提取的数据
void printUserData(const struct WeatherData* weatherData) {

tft.fillScreen(ST7735_BLACK);
// Serial.println(tft.getRotation(), DEC);

if(strcmp(weatherData->city,"泉州")==0)
{
//tft.WindowMax(0,30,16,46);
tft.setCursor(0, 0);
printChinese(4,0,quan);
printChinese(20,0,zhou);

tft.setCursor(44, 0);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(2);
tft.print(weatherData->temp);
printChinese(70,0,du);

if(strcmp(weatherData->weather,"晴")==0)
printChinese(96,0,qing);
}
}

// 关闭与HTTP服务器连接
void stopConnect() {
DebugPrintln("Disconnect");
client.stop();
}

void clrEsp8266ResponseBuffer(void){
memset(response, 0, MAX_CONTENT_SIZE); //清空
}

原文地址:https://www.cnblogs.com/Lonelychampion/p/12120487.html