ESP32小屏幕玩出花:用TFT_eSPI库在1.3寸ST7789上显示动态天气和传感器数据
ESP32小屏幕玩出花用TFT_eSPI库在1.3寸ST7789上显示动态天气和传感器数据当一块1.3寸的ST7789屏幕遇上ESP32能碰撞出怎样的火花这不仅仅是点亮几个像素点那么简单。想象一下你的桌面上摆放着一个精致的小设备实时显示着室内温湿度、室外天气状况甚至还有动态的温度计效果——这就是我们今天要实现的创意项目。对于已经熟悉ESP32基础开发的创客来说如何将硬件能力转化为实际应用才是真正的挑战。本文将带你从零开始构建一个完整的动态信息仪表盘系统。这个系统不仅会读取本地传感器数据还能通过WiFi获取网络天气信息最终在一块240×240像素的小屏幕上呈现专业级的可视化效果。1. 硬件准备与环境搭建1.1 所需硬件清单要完成这个项目你需要准备以下硬件组件ESP32开发板推荐使用ESP32-WROOM-32它具备足够的处理能力和内存1.3寸ST7789 SPI屏幕240×240分辨率带IPS面板的版本显示效果更佳温湿度传感器DHT22AM2302精度更高DHT11更经济实惠面包板与连接线用于快速原型搭建Micro USB数据线用于供电和程序烧录硬件连接时特别注意SPI接口的引脚分配。以下是推荐接线方式屏幕引脚ESP32引脚备注VCC3.3V电源正极GNDGND电源地SCLGPIO18SPI时钟线SDAGPIO23SPI数据线RESGPIO17复位引脚(低电平有效)DCGPIO16数据/命令选择引脚CSGPIO5片选引脚1.2 软件环境配置开发环境我们选择PlatformIO VSCode组合它比传统的Arduino IDE更适合管理复杂项目。首先安装必要的库# 在PlatformIO项目的lib_deps中添加以下库 lib_deps bodmer/TFT_eSPI^2.4.79 adafruit/DHT sensor library^1.4.4 bblanchon/ArduinoJson^6.19.4TFT_eSPI库需要正确配置才能驱动ST7789屏幕。找到库目录下的User_Setup.h文件进行如下修改#define ST7789_DRIVER // 指定驱动芯片类型 #define TFT_WIDTH 240 // 屏幕宽度 #define TFT_HEIGHT 240 // 屏幕高度 #define TFT_MOSI 23 // SPI数据引脚 #define TFT_SCLK 18 // SPI时钟引脚 #define TFT_CS 5 // 片选引脚 #define TFT_DC 16 // 数据/命令选择引脚 #define TFT_RST 17 // 复位引脚 #define LOAD_GLCD // 启用默认字体 #define SPI_FREQUENCY 40000000 // SPI通信频率提示如果屏幕显示颜色异常尝试调整TFT_RGB_ORDER定义ST7789通常需要设置为TFT_RGB_ORDER TFT_RGB2. 传感器数据采集与网络连接2.1 读取DHT传感器数据DHT系列传感器虽然使用简单但在实际应用中需要注意读取时机和错误处理。以下是经过优化的读取代码#include DHT.h #define DHTPIN 4 // 传感器数据引脚 #define DHTTYPE DHT22 // DHT 22 (AM2302) DHT dht(DHTPIN, DHTTYPE); void setup() { Serial.begin(115200); dht.begin(); } float readTemperature() { float t dht.readTemperature(); if (isnan(t)) { Serial.println(Failed to read temperature!); return -999.0; // 特殊值表示读取失败 } return t; } float readHumidity() { float h dht.readHumidity(); if (isnan(h)) { Serial.println(Failed to read humidity!); return -999.0; } return h; }2.2 连接WiFi并获取天气数据要让ESP32接入网络获取天气信息我们需要完成WiFi连接和HTTP请求处理。这里使用免费的OpenWeatherMap API#include WiFi.h #include HTTPClient.h #include ArduinoJson.h const char* ssid your_SSID; const char* password your_PASSWORD; const String apiKey your_API_KEY; const String city Beijing; void connectWiFi() { WiFi.begin(ssid, password); Serial.print(Connecting to WiFi); int attempts 0; while (WiFi.status() ! WL_CONNECTED attempts 20) { delay(500); Serial.print(.); attempts; } if (WiFi.status() WL_CONNECTED) { Serial.println(\nConnected!); } else { Serial.println(\nConnection failed!); } } String getWeatherData() { if (WiFi.status() ! WL_CONNECTED) return ; HTTPClient http; String url http://api.openweathermap.org/data/2.5/weather?q city appid apiKey unitsmetric; http.begin(url); int httpCode http.GET(); if (httpCode HTTP_CODE_OK) { String payload http.getString(); http.end(); return payload; } else { http.end(); return ; } } void parseWeatherData(String jsonStr, float temp, float humidity, String conditions) { DynamicJsonDocument doc(1024); deserializeJson(doc, jsonStr); temp doc[main][temp]; humidity doc[main][humidity]; conditions doc[weather][0][main].asString(); }注意实际项目中应该将API密钥等敏感信息存储在单独的配置文件中不要硬编码在代码里3. 屏幕UI设计与动态效果实现3.1 基础UI框架搭建在240×240的小屏幕上设计UI需要精打细算每个像素。我们先规划几个显示区域顶部区域高度40px显示城市名称和当前天气状况中间左侧120×160室外温度显示与趋势图中间右侧120×160室内传感器数据显示底部区域高度40px状态栏WiFi信号、更新时间创建基础绘制函数#include TFT_eSPI.h TFT_eSPI tft; void initDisplay() { tft.init(); tft.setRotation(1); // 根据屏幕实际方向调整 tft.fillScreen(TFT_BLACK); } void drawHeader(String city, String weather) { tft.fillRect(0, 0, 240, 40, TFT_NAVY); tft.setTextColor(TFT_WHITE, TFT_NAVY); tft.setTextSize(2); tft.drawString(city, 10, 10); tft.drawString(weather, 150, 10); } void drawFooter(bool wifiStatus) { tft.fillRect(0, 200, 240, 40, TFT_DARKGREY); // 绘制WiFi状态图标 if (wifiStatus) { tft.fillCircle(220, 220, 8, TFT_GREEN); } else { tft.fillCircle(220, 220, 8, TFT_RED); } }3.2 温度计动态效果静态显示温度值不够直观我们实现一个动态的温度计柱状图void drawThermometer(float temp, float minTemp, float maxTemp, int x, int y) { // 温度计边框 tft.drawRoundRect(x, y, 40, 120, 10, TFT_WHITE); tft.fillRoundRect(x15, y120, 10, 20, 5, TFT_WHITE); // 计算温度柱高度 (0-100范围) int tempHeight map(constrain(temp, minTemp, maxTemp), minTemp, maxTemp, 0, 100); // 使用渐变色 - 低温蓝色到高温红色 uint16_t color; if (temp 0) { color TFT_BLUE; } else if (temp 15) { color tft.color565(0, 0, 255 - (temp * 17)); } else if (temp 30) { color tft.color565((temp-15)*17, 0, 255 - (temp-15)*17); } else { color tft.color565(255, (temp-30)*8, 0); } // 绘制温度柱 tft.fillRoundRect(x5, y110-tempHeight, 30, tempHeight, 5, color); // 显示温度值 tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.setTextSize(1); tft.drawNumber((int)temp, x50, y50); tft.drawString(C, x70, y50); }3.3 数据刷新策略频繁刷新整个屏幕会导致闪烁我们需要智能更新策略unsigned long lastUpdate 0; float lastOutTemp -100; float lastInTemp -100; void updateDisplay(float outTemp, float inTemp, float humidity, String conditions, bool wifiStatus) { unsigned long now millis(); // 每5秒全量刷新一次 if (now - lastUpdate 5000) { tft.fillScreen(TFT_BLACK); drawHeader(Beijing, conditions); drawFooter(wifiStatus); lastUpdate now; } // 温度变化超过0.5度才更新 if (abs(outTemp - lastOutTemp) 0.5) { drawThermometer(outTemp, -10, 40, 30, 50); lastOutTemp outTemp; } if (abs(inTemp - lastInTemp) 0.5) { drawThermometer(inTemp, 10, 40, 150, 50); lastInTemp inTemp; } // 湿度显示 static int lastHumidity -1; if ((int)humidity ! lastHumidity) { tft.fillRect(150, 180, 80, 20, TFT_BLACK); tft.setTextColor(TFT_CYAN, TFT_BLACK); tft.setTextSize(2); tft.drawString(Hum: String((int)humidity) %, 150, 180); lastHumidity (int)humidity; } }4. 系统整合与优化4.1 主程序逻辑将所有功能模块整合到一个协调的工作流程中void setup() { Serial.begin(115200); initDisplay(); dht.begin(); connectWiFi(); // 初始显示 tft.fillScreen(TFT_BLACK); drawHeader(Loading..., ); drawFooter(false); } void loop() { // 读取传感器数据 float inTemp readTemperature(); float inHumidity readHumidity(); // 获取天气数据 String weatherJson getWeatherData(); float outTemp 0, outHumidity 0; String conditions ; if (weatherJson ! ) { parseWeatherData(weatherJson, outTemp, outHumidity, conditions); } // 更新显示 updateDisplay(outTemp, inTemp, inHumidity, conditions, WiFi.status() WL_CONNECTED); // 节能延迟 delay(2000); }4.2 性能优化技巧经过实际测试我们发现以下几个优化点能显著提升用户体验SPI优化将SPI频率提升到最大稳定值ST7789通常支持40MHz局部刷新只更新变化的部分区域减少闪烁内存管理使用PROGMEM存储静态资源预渲染复杂图形到内存缓冲区电源管理在不活跃时降低屏幕亮度使用ESP32的深度睡眠模式// 示例使用双缓冲减少闪烁 TFT_eSprite spr TFT_eSprite(tft); void setup() { // ...其他初始化... spr.createSprite(240, 240); // 创建与屏幕同尺寸的缓冲区 } void drawToBuffer() { spr.fillScreen(TFT_BLACK); // 所有绘制操作改为对spr进行... spr.pushSprite(0, 0); // 一次性输出到屏幕 }4.3 扩展思路这个基础项目可以进一步扩展为多数据源整合加入空气质量指数、天气预报等交互功能通过按钮切换显示模式云端同步将传感器数据上传到物联网平台低功耗设计使用电池供电优化续航在实现这些扩展功能时建议采用模块化设计每个功能独立成单独的代码文件通过清晰的接口进行交互。例如可以创建WeatherService类处理所有天气相关逻辑DisplayManager类管理屏幕输出等。