ESP8266实现时钟+天气状况表示
功能
可同步NTP服务器的时钟
外部天气实况API调用
文末有实现后的效果视频
主程序
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <ArduinoJson.h>
#include <U8g2lib.h>
#include "WeatherIcons.h"
// OLED 初始化(D1 -> SCL, D2 -> SDA)
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, D1, D2, U8X8_PIN_NONE);
// WiFi 信息
const char* ssid = "SSID";
const char* password = "PASSWORD";
// 天气信息
String weatherInfo = "";
WeatherIcons weatherIcons;
// NTP 时间客户端
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "ntp.nict.jp", 9 * 3600, 60000); // 日本时间 UTC+9
// 更新时间和天气的定时器
unsigned long lastWeatherUpdate = 0;
const unsigned long weatherInterval = 10 * 60 * 1000; // 每10分钟更新天气
void setup() {
Serial.begin(115200);
u8g2.begin();
WiFi.begin(ssid, password);
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB08_tr);
u8g2.drawStr(0, 15, "Connecting WiFi...");
u8g2.sendBuffer();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
timeClient.begin();
getWeather(); // 开始先拉一次天气
}
void loop() {
timeClient.update();
unsigned long now = millis();
if (now - lastWeatherUpdate > weatherInterval) {
getWeather();
lastWeatherUpdate = now;
}
// 获取当前时间
String formattedTime = timeClient.getFormattedTime();
time_t rawTime = timeClient.getEpochTime();
struct tm* timeinfo = localtime(&rawTime);
char dateBuffer[16];
strftime(dateBuffer, sizeof(dateBuffer), "%Y-%m-%d", timeinfo);
// 显示内容
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_wqy16_t_gb2312b);
u8g2.drawStr(10, 12, formattedTime.c_str()); // 时间
u8g2.drawStr(10, 27, dateBuffer); // 日期
displayWeather(weatherInfo); // 只在这里调用一次天气显示(包括图标和文字)
u8g2.sendBuffer();
delay(1000); // 每秒刷新一次时间
}
void getWeather() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
WiFiClient client;
String url = "http://api.weatherapi.com/v1/current.json?key=**********APIKEY**********&q=Tokyo&lang=zh";
http.begin(client, url);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, payload);
if (!error) {
String location = doc["location"]["name"].as();
String condition = doc["current"]["condition"]["text"].as();
float temp = doc["current"]["temp_c"].as();
weatherInfo = location + " " + condition + " " + String(temp, 1) + "°C";
} else {
weatherInfo = "天气解析错误";
}
} else {
weatherInfo = "天气请求失败";
}
http.end();
} else {
weatherInfo = "WiFi未连接";
}
}
void displayWeather(const String &weatherInfo) {
u8g2.setFont(u8g2_font_wqy12_t_gb2312b); // 设置中文字体
u8g2.drawUTF8(10, 60, weatherInfo.c_str()); // 正确显示中文
weatherIcons.draw(u8g2, weatherInfo, 100, 20); // 图标
}
WeatherIcons.h
#ifndef WEATHER_ICONS_H
#define WEATHER_ICONS_H
#include <Arduino.h>
#include <U8g2lib.h>
class WeatherIcons {
public:
void draw(U8G2 &u8g2, const String &weatherDesc, int x = 0, int y = 32);
private:
static const unsigned char icon_sunny[];
static const unsigned char icon_cloudy[];
static const unsigned char icon_rainy[];
static const unsigned char icon_snowy[];
static const unsigned char icon_foggy[];
};
#endif
WeatherIcons.cpp
#include "WeatherIcons.h"
const unsigned char WeatherIcons::icon_sunny[] PROGMEM = {
0x10, 0x80, 0x00, 0x00, 0x10, 0x80, 0x00, 0x00,
0x38, 0xE0, 0x7C, 0xF0, 0xFE, 0xF8, 0xFF, 0xF8,
0xFF, 0xF8, 0xFE, 0xF8, 0x7C, 0xF0, 0x38, 0xE0,
0x00, 0x00, 0x10, 0x80, 0x00, 0x00, 0x10, 0x80
};
const unsigned char WeatherIcons::icon_cloudy[] PROGMEM = {
0x00, 0x00, 0x0C, 0x00, 0x1E, 0x00, 0x3F, 0x00,
0x7F, 0x80, 0xFF, 0xC0, 0x7F, 0x80, 0x3F, 0x00,
0x1E, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3C, 0x00,
0x7E, 0x00, 0xFF, 0x00, 0x7E, 0x00, 0x3C, 0x00
};
const unsigned char WeatherIcons::icon_rainy[] PROGMEM = {
0x00, 0x00, 0x0E, 0x00, 0x1F, 0x00, 0x3F, 0x80,
0x7F, 0xC0, 0x3F, 0x80, 0x1F, 0x00, 0x0E, 0x00,
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00,
0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00
};
const unsigned char WeatherIcons::icon_snowy[] PROGMEM = {
0x00, 0x00, 0x1C, 0x00, 0x3E, 0x00, 0x7F, 0x00,
0xFF, 0x80, 0x7F, 0x00, 0x3E, 0x00, 0x1C, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
0x2A, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00
};
const unsigned char WeatherIcons::icon_foggy[] PROGMEM = {
0x00, 0x00, 0x7E, 0x00, 0x81, 0x00, 0xBD, 0x00,
0x81, 0x00, 0xBD, 0x00, 0x81, 0x00, 0x7E, 0x00,
0x18, 0x00, 0x18, 0x00, 0x7E, 0x00, 0x81, 0x00,
0x99, 0x00, 0x81, 0x00, 0x7E, 0x00, 0x00, 0x00
};
void WeatherIcons::draw(U8G2 &u8g2, const String &weatherDesc, int x, int y) {
if (weatherDesc.indexOf("晴") != -1) {
u8g2.drawXBMP(x, y, 16, 16, icon_sunny);
} else if (weatherDesc.indexOf("雨") != -1) {
u8g2.drawXBMP(x, y, 16, 16, icon_rainy);
} else if (weatherDesc.indexOf("雪") != -1) {
u8g2.drawXBMP(x, y, 16, 16, icon_snowy);
} else if (weatherDesc.indexOf("云") != -1 || weatherDesc.indexOf("阴") != -1) {
u8g2.drawXBMP(x, y, 16, 16, icon_cloudy);
} else if (weatherDesc.indexOf("雾") != -1) {
u8g2.drawXBMP(x, y, 16, 16, icon_foggy);
}
}
oxxo