构建基于 ESP8266 和 ILI9341 的 Wi-Fi 可控多行文本显示屏
本文将深入探讨如何使用 ESP8266 微控制器、ILI9341 TFT LCD 显示屏(GBR 颜色格式)以及 Adafruit_GFX 和 Adafruit_ILI9341 库,构建一个功能完备、可通过 Wi-Fi 网络远程控制的多行文本显示系统。我们将详细讲解代码的各个组成部分、工作原理、配置方法、使用说明,并提供一些扩展思路。
1. 项目概述
本项目实现了一个运行在 ESP8266 上的小型 Web 服务器。用户可以通过任何支持 HTTP POST 请求的设备(例如,计算机、智能手机、平板电脑)向 ESP8266 发送特定格式的数据,从而动态控制 ILI9341 显示屏上显示的文本内容、颜色(支持十六进制代码和预定义颜色名称)以及字体大小。
主要功能和特点:
- Wi-Fi 远程控制: 通过 Wi-Fi 网络,从任何连接到同一网络的设备控制显示屏。
- 多行文本显示: 支持同时显示多行文本,行数可动态调整。
- GBR 颜色格式: 针对特定批次或型号的 ILI9341 显示屏,这些显示屏使用 GBR 而非标准的 RGB565 颜色格式。代码中提供了切换颜色格式的选项。
- 颜色定制: 可以为每行文本独立设置颜色。
- 字体大小可调: 每行文本的字体大小可以在 1-7 之间调整。
- 左对齐: 文本内容左对齐显示。
- 可调行间距: 可以调整行与行之间的垂直间距(当前设置为 15 像素)。
- 动态更新: 显示内容实时更新,无需重启 ESP8266。
- 易于使用: 通过简单的 HTTP POST 请求即可控制显示屏,无需编写复杂的客户端程序。
- 可扩展性: 代码结构清晰,易于添加新功能,例如:
- 支持更多显示效果(居中、右对齐、滚动等)。
- 集成传感器数据读取。
- 添加更友好的 Web 用户界面。
2. 硬件准备
- ESP8266 开发板: 任何基于 ESP8266 的开发板均可,例如 NodeMCU、WeMos D1 mini 等。
- ILI9341 TFT LCD 显示屏: 2.2 英寸、2.4 英寸、2.8 英寸等尺寸的 ILI9341 显示屏均可。重要提示: 请务必确认您的 ILI9341 显示屏使用的是 GBR 颜色格式还是标准的 RGB565 格式。本文提供的代码默认支持 GBR 格式,但可以通过修改
useGBR
变量来切换到 RGB565 格式。 - 杜邦线: 用于连接 ESP8266 和 ILI9341 显示屏。
接线图:
ESP8266 引脚 | ILI9341 引脚 | 说明 |
---|---|---|
D8 | CS | 片选信号 (Chip Select) |
D1 | DC (或 A0) | 数据/命令选择 |
D2 | RST | 复位信号 (Reset) |
D7 (MOSI) | SDA | 数据输入 (Serial Data) |
D6 (MISO) | (通常不连接) | 数据输出 (通常不需要) |
D5 (SCK) | SCL | 时钟信号 (Serial Clock) |
3V3 | VCC | 电源正极 (3.3V) |
GND | GND | 电源负极 (Ground) |
3v3 | LED/BLK | 背光控制 (可选) |
注意:
- 不同型号的 ESP8266 开发板和 ILI9341 显示屏的引脚名称可能略有差异,请参考您使用的具体硬件的数据手册。
- ILI9341 的 MOSI、MISO 和 SCK 引脚通常与 ESP8266 的硬件 SPI 引脚(D5-D7)相连,以实现高速通信。
- 背光控制引脚(LED/BLK)可以连接到 ESP8266 的 3.3V 引脚(常亮背光),也可以连接到 ESP8266 的 PWM 引脚,以实现背光亮度调节。
3. 软件准备
- Arduino IDE: 用于编写、编译和上传代码到 ESP8266。
- ESP8266 开发板支持包: 在 Arduino IDE 中安装 ESP8266 开发板支持包。
- 库:
- Adafruit_GFX: Adafruit 提供的通用图形库。
- Adafruit_ILI9341: Adafruit 提供的 ILI9341 显示屏驱动库。
- ESP8266WiFi: ESP8266 Wi-Fi 功能库。
- ESP8266WebServer: ESP8266 Web 服务器库。
- map: C++标准库
在 Arduino IDE 中,通过“工具”->“库管理器”搜索并安装这些库。
4. 代码详解
4.1 代码结构
代码主要由以下几个部分组成:
- 引入库: 包含项目所需的库文件。
- 全局常量和变量: 定义 Wi-Fi SSID 和密码、引脚定义、显示屏对象、Web 服务器对象、数据结构等。
- 函数声明: 声明项目中使用到的函数。
setup()
函数: 初始化硬件、连接 Wi-Fi、启动 Web 服务器。loop()
函数: 循环处理客户端请求。- 辅助函数:
connectToWiFi()
: 连接到 Wi-Fi 网络。handleSet()
: 处理客户端发送的设置显示内容的请求。handleNotFound()
: 处理未找到的请求(404 错误)。updateDisplay()
: 将数据更新到 ILI9341 显示屏上。parseColor()
: 解析颜色字符串(十六进制或预定义名称)。
4.2 关键代码段解析
1. 全局变量:
struct LineData {
String text;
uint16_t color;
uint8_t size;
int16_t y;
};
std::map<int, LineData> lines;
bool useGBR = true; // 关键:控制颜色格式
LineData
结构体:用于存储每一行文本的数据(文本内容、颜色、字体大小、y 坐标)。lines
:std::map
类型,用于存储所有行的数据。 使用map
可以方便地根据行号(键)来访问和更新每一行的数据,并且map
会自动按键(行号)排序。useGBR
: 关键变量。true
表示使用 GBR 颜色格式,false
表示使用 RGB565 格式。 请根据您的 ILI9341 显示屏的实际格式进行设置。
2. handleSet()
函数:
void handleSet() {
lines.clear();
for (int i = 0; i < 100; i++) {
String lineKey = "line" + String(i);
if (server.hasArg(lineKey)) {
LineData line;
line.text = server.arg(lineKey);
String colorKey = "color" + String(i);
if (server.hasArg(colorKey)) {
line.color = parseColor(server.arg(colorKey));
} else {
line.color = ILI9341_WHITE;
}
String sizeKey = "size" + String(i);
if (server.hasArg(sizeKey)) {
int size = server.arg(sizeKey).toInt();
line.size = (size >= 1 && size <= 7) ? size : 2;
} else {
line.size = 2;
}
lines[i] = line;
}
}
updateDisplay();
server.send(200, "text/plain", "OK");
}
- 核心逻辑:
- 循环遍历可能的行号(0-99)。
- 只处理存在
lineN
参数的行。 - 对于每一行,获取文本内容(
lineN
)、颜色(colorN
,如果存在)和字体大小(sizeN
,如果存在)。 - 如果
colorN
或sizeN
不存在,则使用默认值(白色、大小 2)。 - 将每一行的数据存储到
lines
map 中。
- 优点:
- 灵活:支持任意行数的设置,支持跳行设置(例如,直接设置
line0
和line3
,而没有line1
和line2
)。 - 高效:只处理提供了数据的行,避免了不必要的计算。
- 健壮:对
sizeN
参数进行了范围检查,防止无效的字体大小。
- 灵活:支持任意行数的设置,支持跳行设置(例如,直接设置
3. parseColor()
函数:
uint16_t parseColor(String colorString) {
// ... (预定义颜色名称处理) ...
if (colorString.startsWith("#") && colorString.length() == 7) {
long number = strtol(colorString.substring(1).c_str(), NULL, 16);
uint8_t r = (number >> 16) & 0xFF;
uint8_t g = (number >> 8) & 0xFF;
uint8_t b = number & 0xFF;
uint16_t color565;
if (useGBR) {
// GBR 转换: G(5) B(6) R(5)
color565 = ((g & 0xF8) << 8) | ((b & 0xFC) << 3) | (r >> 3);
Serial.println("Using GBR conversion");
} else {
// RGB565 转换 (默认)
color565 = tft.color565(r, g, b);
Serial.println("Using RGB conversion");
}
// ... (串口调试输出) ...
return color565;
}
return ILI9341_WHITE;
}
- GBR 转换 (关键):
color565 = ((g & 0xF8) << 8) | ((b & 0xFC) << 3) | (r >> 3);
- 将 8 位的 G、B、R 值转换为 16 位的 GBR565 格式。
- RGB565 转换 (备用):
color565 = tft.color565(r, g, b);
如果useGBR
为false
,则执行此代码。 - 调试输出: 便于调试颜色问题
4. updateDisplay()
函数:
void updateDisplay() {
tft.fillScreen(ILI9341_BLACK);
int16_t yPos = 20;
for (auto& [lineNum, line] : lines) {
line.y = yPos;
int16_t xPos = 20; // 左对齐
tft.setCursor(xPos, line.y);
tft.setTextColor(line.color);
tft.setTextSize(line.size);
tft.println(line.text);
yPos += line.size * 6 * (tft.getRotation() % 2 == 0 ? 1 : 2) + 15; // 可调行间距
}
}
- 左对齐:
xPos
固定为 20 像素。 - 可调行间距:
yPos
的计算公式中,+ 15
控制行间距。
5. 使用说明
-
配置 Wi-Fi:
- 在代码中找到
ssid
和password
变量,将它们修改为您自己的 Wi-Fi 网络的名称和密码。
- 在代码中找到
-
设置颜色格式:
- 如果您确定您的 ILI9341 显示屏使用 GBR 颜色格式,请确保
useGBR
变量设置为true
。 - 如果您确定您的 ILI9341 显示屏使用标准的 RGB565 颜色格式,请将
useGBR
变量设置为false
。
- 如果您确定您的 ILI9341 显示屏使用 GBR 颜色格式,请确保
-
调整显示参数(可选):
- 屏幕方向: 您可以通过修改
setup()
函数中的tft.setRotation(0)
来更改屏幕方向。0
表示竖屏,1
表示横屏,2
表示竖屏反向,3
表示横屏反向。 - 左边距: 您可以通过修改
updateDisplay()
函数中的xPos
变量来调整文本的左边距。 - 行间距: 您可以通过修改
updateDisplay()
函数中的yPos
计算公式中的+ 15
来调整行间距。
- 屏幕方向: 您可以通过修改
-
上传代码:
- 将 ESP8266 开发板连接到计算机。
- 在 Arduino IDE 中选择正确的开发板和端口。
- 点击“上传”按钮,将代码上传到 ESP8266。
-
获取 ESP8266 的 IP 地址:
- 打开 Arduino IDE 的串口监视器(波特率设置为 115200)。
- ESP8266 启动后,会自动连接到 Wi-Fi 网络。
- 连接成功后,串口监视器会显示 ESP8266 的 IP 地址。
-
发送控制命令:
- 使用提供的 Python 脚本示例(或其他任何可以发送 HTTP POST 请求的工具,例如 Postman、curl)。
- 修改 Python 脚本中的
esp8266_ip
变量,将其替换为您在上一步中获取到的 ESP8266 的 IP 地址。 - 根据需要修改 Python 脚本中的
data
字典,设置要显示的文本、颜色和字体大小。lineN
: 行号(从 0 开始)。colorN
: 颜色(十六进制颜色代码,如#FF0000
表示红色;或预定义颜色名称,如red
)。sizeN
: 字体大小(1-7)。
- 运行 Python 脚本。
Python 脚本示例 (GBR 格式下):
import requests
esp8266_ip = "192.168.31.131" # 将此处的 IP 地址替换为您的 ESP8266 的实际 IP 地址
url = f"http://{esp8266_ip}/set"
# 要发送的数据 (GBR颜色)
data = {
"line0": "Red (GBR)",
"color0": "#00FF00", # 在 GBR 格式下,这将显示为红色
"size0": "2",
"line1": "Green (GBR)",
"color1": "#FF0000", # 在 GBR 格式下,这将显示为绿色
"size1": "3",
"line2": "Blue (GBR)",
"color2": "#0000FF", # 蓝色
"size2": "4",
"line3": "Yellow (GBR)",
"color3": "#00FFFF", # GBR格式下的黄色
"size3": "1",
}
try:
response = requests.post(url, data=data)
response.raise_for_status() # 如果请求失败,会引发异常
print("请求成功!")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
RGB565格式的Python脚本(useGBR = false):
import requests
esp8266_ip = "192.168.31.131" # 将此处的 IP 地址替换为您的 ESP8266 的实际 IP 地址
url = f"http://{esp8266_ip}/set"
# 要发送的数据 (RGB565)
data = {
"line0": "Red (RGB)",
"color0": "#FF0000", # 红色
"size0": "2",
"line1": "Green (RGB)",
"color1": "#00FF00", # 绿色
"size1": "3",
"line2": "Blue (RGB)",
"color2": "#0000FF", # 蓝色
"size2": "4",
"line3": "Yellow (RGB)",
"color3": "#FFFF00", # 黄色
"size3": "1",
}
try:
response = requests.post(url, data=data)
response.raise_for_status() # 如果请求失败,会引发异常
print("请求成功!")
print(f"响应状态码: {response.status_code}")
print(f"响应内容: {response.text}")
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
重要提示:
- GBR 颜色格式: 如果您的显示屏使用 GBR 格式,您需要使用 GBR 格式对应的十六进制颜色代码。例如,要显示红色,您应该使用
#00FF00
(在 RGB 格式中,这是绿色)。 - RGB565 颜色格式: 如果您将代码中的
useGBR
设置为false
,则应使用标准的 RGB565 十六进制颜色代码。 - 跳行设置: 您可以跳过某些行号。例如,您可以只设置
line0
、line2
和line5
,而跳过line1
、line3
和line4
。 - 参数名唯一性: 确保
data
字典中的每个键(lineN
、colorN
、sizeN
)都是唯一的。
6. 扩展思路
- 更多对齐方式: 除了左对齐,还可以实现居中对齐和右对齐。
- 滚动文本: 实现单行或多行文本的水平或垂直滚动。
- 显示图片: 使用更高级的图形库(如 LVGL),可以在显示屏上显示图片。
- 传感器数据: 将 ESP8266 与各种传感器(如温度、湿度、光照传感器)连接,并将传感器数据实时显示在屏幕上。
- Web 用户界面: 使用 HTML、CSS 和 JavaScript 构建一个更友好的 Web 用户界面,方便用户通过浏览器控制显示屏。
- MQTT 集成: 将显示屏集成到智能家居系统中,通过 MQTT 协议与其他设备通信。
- OTA 更新: 添加 OTA(Over-The-Air)无线更新功能,可以通过 Wi-Fi 更新 ESP8266 上的固件,无需连接 USB 线。
7. 总结
本项目提供了一个功能完备、易于使用且可扩展的基于 ESP8266 和 ILI9341 的 Wi-Fi 可控多行文本显示解决方案。通过灵活的参数设置和 GBR/RGB565 颜色格式的支持,用户可以轻松地定制显示内容。希望这篇文章能帮助您理解并构建自己的 Wi-Fi 控制显示屏!
完整的 Arduino 代码
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <map>
const char* ssid = "yang1234";
const char* password = "y123456789";
#define TFT_CS D8
#define TFT_DC D1
#define TFT_RST D2
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
ESP8266WebServer server(80);
struct LineData {
String text;
uint16_t color;
uint8_t size;
int16_t y;
};
std::map<int, LineData> lines;
// 全局变量,控制是否使用 GBR 格式
bool useGBR = true; // GBR 格式
void handleSet();
void handleNotFound();
void updateDisplay();
void connectToWiFi();
uint16_t parseColor(String colorString);
void setup() {
Serial.begin(115200);
tft.begin();
SPI.setFrequency(1000000);
tft.setRotation(0);
uint8_t caset_data[] = {0x00, 0x00, 0x00, 0xEF};
tft.sendCommand(ILI9341_CASET, caset_data, 4);
uint8_t raset_data[] = {0x00, 0x00, 0x01, 0x3F};
tft.sendCommand(ILI9341_PASET, raset_data, 4);
tft.writeCommand(ILI9341_RAMWR);
uint8_t madctl_data = 0xE8;
tft.sendCommand(ILI9341_MADCTL, &madctl_data, 1);
tft.fillScreen(ILI9341_BLACK);
connectToWiFi();
server.on("/set", HTTP_POST, handleSet);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
updateDisplay();
}
void loop() {
server.handleClient();
}
void connectToWiFi() {
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected! IP address:");
Serial.println(WiFi.localIP());
}
void handleSet() {
lines.clear();
for (int i = 0; i < 100; i++) {
String lineKey = "line" + String(i);
if (server.hasArg(lineKey)) {
LineData line;
line.text = server.arg(lineKey);
String colorKey = "color" + String(i);
if (server.hasArg(colorKey)) {
line.color = parseColor(server.arg(colorKey));
} else {
line.color = ILI9341_WHITE;
}
String sizeKey = "size" + String(i);
if (server.hasArg(sizeKey)) {
int size = server.arg(sizeKey).toInt();
line.size = (size >= 1 && size <= 7) ? size : 2;
} else {
line.size = 2;
}
lines[i] = line;
}
}
updateDisplay();
server.send(200, "text/plain", "OK");
}
void handleNotFound() {
server.send(404, "text/plain", "404 Not Found");
}
void updateDisplay() {
tft.fillScreen(ILI9341_BLACK);
int16_t yPos = 20;
for (auto& [lineNum, line] : lines) {
line.y = yPos;
// --- 左对齐 ---
int16_t xPos = 20; // 左边距为 20 像素
tft.setCursor(xPos, line.y);
tft.setTextColor(line.color);
tft.setTextSize(line.size);
tft.println(line.text);
yPos += line.size * 6 * (tft.getRotation() % 2 == 0 ? 1 : 2) + 15; // 15 像素行间距
}
}
uint16_t parseColor(String colorString) {
if (colorString == "red") return ILI9341_RED;
if (colorString == "green") return ILI9341_GREEN;
if (colorString == "blue") return ILI9341_BLUE;
if (colorString == "yellow") return ILI9341_YELLOW;
if (colorString == "cyan") return ILI9341_CYAN;
if (colorString == "magenta") return ILI9341_MAGENTA;
if (colorString == "white") return ILI9341_WHITE;
if (colorString == "black") return ILI9341_BLACK;
if (colorString == "orange") return ILI9341_ORANGE;
if (colorString.startsWith("#") && colorString.length() == 7) {
long number = strtol(colorString.substring(1).c_str(), NULL, 16);
uint8_t r = (number >> 16) & 0xFF;
uint8_t g = (number >> 8) & 0xFF;
uint8_t b = number & 0xFF;
uint16_t color565;
if (useGBR) {
// GBR 转换: G(5) B(6) R(5)
color565 = ((g & 0xF8) << 8) | ((b & 0xFC) << 3) | (r >> 3);
Serial.println("Using GBR conversion");
} else {
// RGB565 转换 (默认)
color565 = tft.color565(r, g, b);
Serial.println("Using RGB conversion");
}
// --- 串口调试输出 ---
Serial.print("Color String: ");
Serial.println(colorString);
Serial.print("R: "); Serial.println(r);
Serial.print("G: "); Serial.println(g);
Serial.print("B: "); Serial.println(b);
Serial.print("565 Color: "); Serial.println(color565);
return color565;
}
return ILI9341_WHITE;
}
最终的方案
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <map>
#include <vector>
const char* ssid = "yang1234"; // 请替换为你的 Wi-Fi SSID
const char* password = "y123456789"; // 请替换为你的 Wi-Fi 密码
#define TFT_CS D8
#define TFT_DC D1
#define TFT_RST D2
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
ESP8266WebServer server(80);
struct LineData {
String text;
uint16_t color;
uint8_t size;
int16_t y;
};
std::map<int, LineData> lines;
// --- 图片数据结构 ---
struct ImageData {
std::vector<uint16_t> pixels;
int16_t width;
int16_t height;
int16_t y_offset; // 图片的 Y 轴偏移量
};
ImageData image;
// 全局变量,控制是否使用 GBR 格式
bool useGBR = true; // GBR 格式
void handleSet();
void handleImage(); // 新增图片处理函数
void handleNotFound();
void updateDisplay();
void connectToWiFi();
uint16_t parseColor(String colorString);
void setup() {
Serial.begin(115200);
tft.begin();
SPI.setFrequency(1000000);
tft.setRotation(0);
uint8_t caset_data[] = {0x00, 0x00, 0x00, 0xEF};
tft.sendCommand(ILI9341_CASET, caset_data, 4);
uint8_t raset_data[] = {0x00, 0x00, 0x01, 0x3F};
tft.sendCommand(ILI9341_PASET, raset_data, 4);
tft.writeCommand(ILI9341_RAMWR);
uint8_t madctl_data = 0xE8;
tft.sendCommand(ILI9341_MADCTL, &madctl_data, 1);
tft.fillScreen(ILI9341_BLACK);
connectToWiFi();
server.on("/set", HTTP_POST, handleSet);
server.on("/image", HTTP_POST, handleImage); // 处理图片请求
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
updateDisplay();
}
void loop() {
server.handleClient();
}
void connectToWiFi() {
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected! IP address:");
Serial.println(WiFi.localIP());
Serial.print("请在Python脚本中将 esp8266_ip 替换为: ");
Serial.println(WiFi.localIP());
}
void handleSet() {
lines.clear();
for (int i = 0; i < 100; i++) {
String lineKey = "line" + String(i);
if (server.hasArg(lineKey)) {
LineData line;
line.text = server.arg(lineKey);
String colorKey = "color" + String(i);
if (server.hasArg(colorKey)) {
line.color = parseColor(server.arg(colorKey));
} else {
line.color = ILI9341_WHITE;
}
String sizeKey = "size" + String(i);
if (server.hasArg(sizeKey)) {
int size = server.arg(sizeKey).toInt();
line.size = (size >= 1 && size <= 7) ? size : 2;
} else {
line.size = 2;
}
lines[i] = line;
}
}
updateDisplay();
server.send(200, "text/plain", "OK");
}
// --- 处理图片上传 ---
void handleImage() {
if (server.hasArg("width") && server.hasArg("height") && server.hasArg("data")) {
int width = server.arg("width").toInt();
int height = server.arg("height").toInt();
String data = server.arg("data");
if (width > 0 && height > 0 && data.length() > 0) {
image.width = width;
image.height = height;
image.pixels.clear();
image.pixels.reserve(width * height); // 预留空间
// 将接收到的字符串数据转换为像素数据
for (int i = 0; i < data.length(); i += 4) { // 假设每像素 4 字符 (16进制)
if (i + 4 <= data.length()) {
String hexColor = data.substring(i, i + 4);
unsigned long colorValue = strtoul(hexColor.c_str(), NULL, 16);
image.pixels.push_back(colorValue);
}
}
image.y_offset = 0; // 默认图片 Y 轴偏移量为 0
if (server.hasArg("y_offset")) {
image.y_offset = server.arg("y_offset").toInt();
}
updateDisplay(); // 更新显示
server.send(200, "text/plain", "Image OK");
} else {
server.send(400, "text/plain", "Invalid image parameters");
}
} else {
server.send(400, "text/plain", "Image parameters missing");
}
}
void handleNotFound() {
server.send(404, "text/plain", "404 Not Found");
}
void updateDisplay() {
tft.fillScreen(ILI9341_BLACK);
int16_t yPos = 20;
for (auto& [lineNum, line] : lines) {
line.y = yPos;
// --- 左对齐 ---
int16_t xPos = 20; // 左边距为 20 像素
tft.setCursor(xPos, line.y);
tft.setTextColor(line.color);
tft.setTextSize(line.size);
tft.println(line.text);
yPos += line.size * 6 * (tft.getRotation() % 2 == 0 ? 1 : 2) + 15; // 15 像素行间距
}
// --- 显示图片 ---
if (!image.pixels.empty()) {
int16_t startY = yPos + image.y_offset; // 图片的起始 Y 坐标,在文字下方
for (int y = 0; y < image.height; y++) {
for (int x = 0; x < image.width; x++) {
if (x < tft.width() && (startY + y) < tft.height()) { // 边界检查
tft.drawPixel(x, startY + y, image.pixels[y * image.width + x]);
}
}
}
}
}
uint16_t parseColor(String colorString) {
if (colorString == "red") return ILI9341_RED;
if (colorString == "green") return ILI9341_GREEN;
if (colorString == "blue") return ILI9341_BLUE;
if (colorString == "yellow") return ILI9341_YELLOW;
if (colorString == "cyan") return ILI9341_CYAN;
if (colorString == "magenta") return ILI9341_MAGENTA;
if (colorString == "white") return ILI9341_WHITE;
if (colorString == "black") return ILI9341_BLACK;
if (colorString == "orange") return ILI9341_ORANGE;
if (colorString.startsWith("#") && colorString.length() == 7) {
long number = strtol(colorString.substring(1).c_str(), NULL, 16);
uint8_t r = (number >> 16) & 0xFF;
uint8_t g = (number >> 8) & 0xFF;
uint8_t b = number & 0xFF;
uint16_t color565;
if (useGBR) {
// GBR 转换: G(5) B(6) R(5)
color565 = ((g & 0xF8) << 8) | ((b & 0xFC) << 3) | (r >> 3);
Serial.println("Using GBR conversion");
} else {
// RGB565 转换 (默认)
color565 = tft.color565(r, g, b);
Serial.println("Using RGB conversion");
}
// --- 串口调试输出 ---
Serial.print("Color String: ");
Serial.println(colorString);
Serial.print("R: "); Serial.println(r);
Serial.print("G: "); Serial.println(g);
Serial.print("B: "); Serial.println(b);
Serial.print("565 Color: "); Serial.println(color565);
return color565;
}
return ILI9341_WHITE;
}
本文作者: 永生
本文链接: https://yys.zone/detail/?id=384
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
发表评论
评论列表 (0 条评论)
暂无评论,快来抢沙发吧!