ESP32 + SSD1306 OLED 显示中文天气与网络时间(U8g2 + WiFi + NTP 完整实战)

一、简介

本文基于 ESP32 开发板 + SSD1306 128×64 OLED 显示屏,实现一个完整的物联网小项目,功能包括:

  • ESP32 通过 I2C 驱动 OLED
  • 使用 U8g2 库显示中文(无外置字库)
  • 通过 HTTP 请求天气 API 获取实时天气
  • 使用 NTP 网络时间协议自动校时
  • OLED 实时显示:日期、时间、城市、温度、天气情况
  • 自动刷新时间,天气数据动态更新

适合作为 ESP32 + OLED + 网络通信 的综合入门示例。


二、硬件连接说明

1. 接线方式(SSD1306 I2C)

OLED 引脚 ESP32 引脚
VCC 3V3
GND GND
SDA GPIO 21
SCL GPIO 22

ESP32 默认 I2C 引脚:

SDA → GPIO21,SCL → GPIO22


三、软件环境与依赖库

1. 开发环境

  • Arduino IDE(或 PlatformIO)
  • ESP32 Board Support Package

2. 需要安装的库

text 复制代码
U8g2
ArduinoJson
NTPClient

WiFi.hHTTPClient.h 为 ESP32 自带库。


四、天气 API 说明

本文使用的天气接口:

text 复制代码
http://t.weather.itboy.net/api/weather/city/101010100

特点:

  • 免费
  • 无需 Key
  • 返回 JSON,支持中文
  • 城市通过城市代码区分

五、完整示例代码(已添加中文注释)

⚠️ 以下代码为 最终整合版本

  • 中文显示
  • 天气获取
  • NTP 自动更新时间
  • OLED 实时刷新
cpp 复制代码
#include <Wire.h>
#include <U8g2lib.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

/* ================= WiFi 配置 ================= */
const char* ssid = "wifi帐号";
const char* password = "Wifi密码";

/* ================= 天气 API ================= */
String apiUrl = "http://t.weather.itboy.net/api/weather/city/101010100";

/* ================= OLED 初始化 =================
   使用硬件 I2C,ESP32 默认:
   SDA -> GPIO21
   SCL -> GPIO22
*/
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(
  U8G2_R0,
  U8X8_PIN_NONE,
  SCL,
  SDA
);

/* ================= NTP 时间配置 =================
   时区偏移:28800 秒(UTC+8,中国时间)
   更新时间间隔:60000 ms
*/
WiFiUDP udp;
NTPClient timeClient(udp, "pool.ntp.org", 28800, 60000);

/* ================= 刷新控制 ================= */
unsigned long lastUpdateTime = 0;
unsigned long interval = 1000;  // 每秒刷新一次显示

/* ================= 天气数据结构体 ================= */
struct WeatherData {
  String city;          // 城市
  String temperature;   // 温度
  String weatherType;   // 天气类型
  String timedate;      // 日期时间
};

void setup() {
  Serial.begin(115200);

  /* -------- 连接 WiFi -------- */
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }

  Serial.println("\nWiFi Connected");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  /* -------- OLED 初始化 -------- */
  u8g2.begin();
  u8g2.enableUTF8Print();  // 必须开启 UTF-8,否则中文乱码

  /* -------- 启动 NTP -------- */
  timeClient.begin();
}

void loop() {
  unsigned long currentMillis = millis();

  /* 每秒更新一次显示 */
  if (currentMillis - lastUpdateTime >= interval) {
    lastUpdateTime = currentMillis;

    // 获取天气数据
    WeatherData weatherData = getWeatherData();

    if (weatherData.city != "") {
      // 更新时间
      timeClient.update();
      String currentTime = timeClient.getFormattedTime();

      // OLED 显示
      u8g2.clearBuffer();
      u8g2.setFont(u8g2_font_wqy12_t_gb2312);  // 中文字体

      u8g2.setCursor(0, 15);
      u8g2.print("日期: " + extractDate(weatherData.timedate) + " " + currentTime);

      u8g2.setCursor(0, 30);
      u8g2.print("城市: " + weatherData.city);

      u8g2.setCursor(0, 45);
      u8g2.print("温度: " + weatherData.temperature);

      u8g2.setCursor(0, 60);
      u8g2.print("天气: " + weatherData.weatherType);

      u8g2.sendBuffer();
    }
  }
}

/* ================= 获取天气数据 ================= */
WeatherData getWeatherData() {
  HTTPClient http;
  WeatherData weatherData;

  http.begin(apiUrl);
  int httpCode = http.GET();

  if (httpCode == 200) {
    String payload = http.getString();
    Serial.println(payload);

    DynamicJsonDocument doc(1024);
    DeserializationError error = deserializeJson(doc, payload);

    if (!error) {
      weatherData.timedate = doc["time"].as<String>();
      weatherData.city = doc["cityInfo"]["city"].as<String>();
      weatherData.temperature = doc["data"]["wendu"].as<String>();
      weatherData.weatherType = doc["data"]["forecast"][0]["type"].as<String>();
    }
  }

  http.end();
  return weatherData;
}

/* ================= 日期格式处理 =================
   输入:YYYY-MM-DD HH:MM:SS
   输出:MM-DD
*/
String extractDate(String fullDate) {
  int spaceIndex = fullDate.indexOf(' ');
  String datePart = fullDate.substring(0, spaceIndex);
  return datePart.substring(5);
}

六、关键技术点总结

  1. U8g2 显示中文

    • 必须使用 u8g2_font_wqy12_t_gb2312
    • 必须调用 enableUTF8Print()
  2. ESP32 网络请求

    • 使用 HTTPClient
    • JSON 解析推荐 ArduinoJson
  3. 时间自动更新

    • NTPClient 负责时间
    • millis() 控制刷新频率,避免阻塞
  4. 性能注意

    • 不建议每秒请求天气 API
    • 可后续改为:天气 10 分钟更新一次,时间每秒更新
相关推荐
盈创力和20074 小时前
可延长探头以太网温湿度传感器:高精度环境感知如何赋能工业物联网?
物联网·以太网温湿度传感器·延长探头温湿度传感器·高精度温湿度传感器·狭窄空间适用工业温湿度传感器
北京耐用通信5 小时前
协议转换“黑科技”:耐达讯自动化CANopen转Profibus 网关破解电机控制通信难题
网络·人工智能·科技·物联网·自动化·信息与通信
三万棵雪松6 小时前
【AI小智硬件程序(八)】
c++·人工智能·嵌入式·esp32·ai小智
智慧化智能化数字化方案6 小时前
智慧水务——55页工业园区智慧水务物联网平台建设方案【附全文阅读】
物联网·智慧水务物联网平台·智慧水务大数据平台·智慧水务可行性研究报告
赋创小助手7 小时前
超微 SYS-E403-14B-FRN2T 深度解析:面向边缘与 IoT 场景的高扩展紧凑型服务器
运维·服务器·人工智能·科技·物联网·ai·边缘计算
北京耐用通信8 小时前
如何用耐达讯自动化Profibus总线光纤中继器解决变频器长距离通信干扰问题?
人工智能·物联网·网络协议·自动化·信息与通信
立昂8 小时前
人才社区中需要每个公寓水、电都开户吗
大数据·物联网
龙亘川9 小时前
城管住建领域丨市政设施监测功能详解——桥梁运行监测系统(2)、管廊运维监测系统(3)
大数据·运维·人工智能·物联网·政务
星纵物联9 小时前
中建八局低碳技术实验室建设与办公大楼智能化改造
人工智能·物联网·lorawan·传感器·绿色建筑