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); // 1秒ごとに更新
}
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
// コメント无,翻訳不要
WeatherIcons.cpp
#include "WeatherIcons.h"
const unsigned char WeatherIcons::icon_sunny[] PROGMEM = {
...
};
const unsigned char WeatherIcons::icon_cloudy[] PROGMEM = {
...
};
const unsigned char WeatherIcons::icon_rainy[] PROGMEM = {
...
};
const unsigned char WeatherIcons::icon_snowy[] PROGMEM = {
...
};
const unsigned char WeatherIcons::icon_foggy[] PROGMEM = {
...
};
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