first commit

This commit is contained in:
2025-01-10 15:20:33 +08:00
commit 4f5d2aa650
66 changed files with 15921 additions and 0 deletions

5
TFT-ESP32/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

10
TFT-ESP32/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

BIN
TFT-ESP32/data/san28.bin Normal file

Binary file not shown.

39
TFT-ESP32/include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
TFT-ESP32/lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

42
TFT-ESP32/platformio.ini Normal file
View File

@ -0,0 +1,42 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps =
bodmer/TFT_eSPI@^2.5.43
bblanchon/ArduinoJson@^7.2.1
bodmer/JPEGDecoder@^2.0.0
build_flags =
-Os
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
-DUSER_SETUP_LOADED=1
-DTFT_INVERSION_ON=1
-DILI9341_DRIVER=1
-DTFT_WIDTH=320
-DTFT_HEIGHT=240
-DTFT_MISO=19
-DTFT_MOSI=23
-DTFT_SCLK=18
-DTFT_CS=15
-DTFT_DC=2
-DTFT_RST=4
-DTOUCH_CS=33
-DLOAD_GLCD=1
-DLOAD_FONT2=1
-DLOAD_FONT4=1
-DLOAD_FONT6=1
-DLOAD_FONT7=1
-DLOAD_FONT8=1
-DLOAD_GFXFF=1
-DSMOOTH_FONT=1
-DSPI_FREQUENCY=27000000

768
TFT-ESP32/src/main.cpp Normal file
View File

@ -0,0 +1,768 @@
// Font file is stored on SD card
#include <SD.h>
#include <HardwareSerial.h>
// Graphics and font library
#include <TFT_eSPI.h>
#include <SPI.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <esp_sleep.h>
#include <JPEGDecoder.h>
#define SD_CS 22 // Set GPIO 5 as chip select for SD card
#define LED_PIN 14 // Set GPIO 14 as LED output
#define HUMIDIFIER_PIN 13 // Set GPIO 13 as humidifier output
// TX (发送)GPIO16UART2_TXD
// RX (接收)GPIO17UART2_RXD
#define MAX_IMAGE_WIDTH 240
int16_t xpos = 0;
int16_t ypos = 0;
const char *ssid = "Xiaomi_3A40";
const char *password = "112233ww";
const char *jsonUrl = "http://monjack.cn/weatherInfo.php";
const char *postUrl = "http://192.168.131.51/postLocalData.php";
const char *postStatusUrl = "http://192.168.131.51/postStatus.php";
int dataIndex = 0;
char receivedData0[100]; // 用于存储接收到的数据
HardwareSerial serial2(2); // UART1 实例化
float local_humidity = 0.0;
float temp_local_humidity = 0.0;
float local_temperature = 0.0;
float temp_local_temperature = 0.0;
u8_t standby = 0;
u8_t reboot = 0;
u8_t humidifier = 1;
u8_t screen = 1;
bool boot = true;
String weather = "";
String temp_weather = "";
String temperature = "25";
String temp_temperature = "";
String humidity = "60";
String temp_humidity = "";
String wind_direction = "东北";
String temp_wind_direction = "";
String comfort_clothing = "穿短裙、短裤、短袖、短外套";
SemaphoreHandle_t xMutex;
TFT_eSPI tft = TFT_eSPI(); // Invoke library
void showTime(uint32_t msTime)
{
// tft.setCursor(0, 0);
// tft.setTextFont(1);
// tft.setTextSize(2);
// tft.setTextColor(TFT_WHITE, TFT_BLACK);
// tft.print(F(" JPEG drawn in "));
// tft.print(msTime);
// tft.println(F(" ms "));
Serial.print(F(" JPEG drawn in "));
Serial.print(msTime);
Serial.println(F(" ms "));
}
// ####################################################################################################
// Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit
// ####################################################################################################
// This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not
// fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders.
void jpegRender(int xpos, int ypos)
{
// jpegInfo(); // Print information from the JPEG file (could comment this line out)
uint16_t *pImg;
uint16_t mcu_w = JpegDec.MCUWidth;
uint16_t mcu_h = JpegDec.MCUHeight;
uint32_t max_x = JpegDec.width;
uint32_t max_y = JpegDec.height;
bool swapBytes = tft.getSwapBytes();
tft.setSwapBytes(true);
// Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
// Typically these MCUs are 16x16 pixel blocks
// Determine the width and height of the right and bottom edge image blocks
uint32_t min_w = jpg_min(mcu_w, max_x % mcu_w);
uint32_t min_h = jpg_min(mcu_h, max_y % mcu_h);
// save the current image block size
uint32_t win_w = mcu_w;
uint32_t win_h = mcu_h;
// record the current time so we can measure how long it takes to draw an image
uint32_t drawTime = millis();
// save the coordinate of the right and bottom edges to assist image cropping
// to the screen size
max_x += xpos;
max_y += ypos;
// Fetch data from the file, decode and display
while (JpegDec.read())
{ // While there is more data in the file
pImg = JpegDec.pImage; // Decode a MCU (Minimum Coding Unit, typically a 8x8 or 16x16 pixel block)
// Calculate coordinates of top left corner of current MCU
int mcu_x = JpegDec.MCUx * mcu_w + xpos;
int mcu_y = JpegDec.MCUy * mcu_h + ypos;
// check if the image block size needs to be changed for the right edge
if (mcu_x + mcu_w <= max_x)
win_w = mcu_w;
else
win_w = min_w;
// check if the image block size needs to be changed for the bottom edge
if (mcu_y + mcu_h <= max_y)
win_h = mcu_h;
else
win_h = min_h;
// copy pixels into a contiguous block
if (win_w != mcu_w)
{
uint16_t *cImg;
int p = 0;
cImg = pImg + win_w;
for (int h = 1; h < win_h; h++)
{
p += mcu_w;
for (int w = 0; w < win_w; w++)
{
*cImg = *(pImg + w + p);
cImg++;
}
}
}
// calculate how many pixels must be drawn
uint32_t mcu_pixels = win_w * win_h;
// draw image MCU block only if it will fit on the screen
if ((mcu_x + win_w) <= tft.width() && (mcu_y + win_h) <= tft.height())
tft.pushImage(mcu_x, mcu_y, win_w, win_h, pImg);
else if ((mcu_y + win_h) >= tft.height())
JpegDec.abort(); // Image has run off bottom of screen so abort decoding
}
tft.setSwapBytes(swapBytes);
showTime(millis() - drawTime); // These lines are for sketch testing only
}
// ####################################################################################################
// Print image information to the serial port (optional)
// ####################################################################################################
// JpegDec.decodeFile(...) or JpegDec.decodeArray(...) must be called before this info is available!
void jpegInfo()
{
// Print information extracted from the JPEG file
Serial.println("JPEG image info");
Serial.println("===============");
Serial.print("Width :");
Serial.println(JpegDec.width);
Serial.print("Height :");
Serial.println(JpegDec.height);
Serial.print("Components :");
Serial.println(JpegDec.comps);
Serial.print("MCU / row :");
Serial.println(JpegDec.MCUSPerRow);
Serial.print("MCU / col :");
Serial.println(JpegDec.MCUSPerCol);
Serial.print("Scan type :");
Serial.println(JpegDec.scanType);
Serial.print("MCU width :");
Serial.println(JpegDec.MCUWidth);
Serial.print("MCU height :");
Serial.println(JpegDec.MCUHeight);
Serial.println("===============");
Serial.println("");
}
// ####################################################################################################
// Show the execution time (optional)
// ####################################################################################################
// WARNING: for UNO/AVR legacy reasons printing text to the screen with the Mega might not work for
// sketch sizes greater than ~70KBytes because 16-bit address pointers are used in some libraries.
// The Due will work fine with the HX8357_Due library.
void listDir(fs::FS &fs, const char *dirname, uint8_t levels);
void serialEvent();
void ParseData(char *data);
void displayTask(void *parameter);
void fetchDataTask(void *parameters);
void parseJson(String jsonData);
void getWeatherInfo();
void postData();
void postStatus();
void parseJson2(String jsonData);
void checkStatusTask(void *parameters);
void drawSdJpeg(const char *filename, int xpos, int ypos);
// -------------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------------
void setup(void)
{
Serial.begin(115200); // Used for messages
serial2.begin(115200); // Used for messages
Serial.println("UART2 initialized");
// Connect to WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
// 设置SPI频率为40MHz
SPISettings spiSettings(40000000, MSBFIRST, SPI_MODE0);
// Initialise the SD library before the TFT so the chip select is defined
if (!SD.begin(SD_CS))
{
Serial.println("Card Mount Failed");
return;
}
// Initialise the TFT after the SD card!
tft.init();
tft.setRotation(3);
tft.fillScreen(TFT_BLACK);
listDir(SD, "/", 0);
pinMode(LED_PIN, OUTPUT); // Set LED pin as output
pinMode(HUMIDIFIER_PIN, OUTPUT); // Set humidifier pin as output
digitalWrite(LED_PIN, HIGH); // Turn on LED
digitalWrite(HUMIDIFIER_PIN, HIGH); // Turn on humidifier
Serial.println("SD and TFT initialisation done.");
Serial.println("👌👌👌👌");
postStatus();
int x = 0;
int y = 0;
drawSdJpeg("/Photos/background.jpg", x, y); // This draws a jpeg pulled off the SD Card
// 配置 GPIO 触发唤醒
esp_sleep_enable_ext0_wakeup(GPIO_NUM_35, 0); // GPIO_NUM_35 为低电平唤醒
// 创建互斥锁
xMutex = xSemaphoreCreateMutex();
// 创建任务
xTaskCreate(displayTask, "Display Task", 4096, NULL, 1, NULL);
xTaskCreate(fetchDataTask, "Fetch Data Task", 4096, NULL, 2, NULL);
xTaskCreate(checkStatusTask, "Check Status Task", 4096, NULL, 3, NULL);
}
// -------------------------------------------------------------------------
// Main loop
// -------------------------------------------------------------------------
void loop()
{
}
void fetchDataTask(void *parameters){
while (true)
{
getWeatherInfo();
postData();
vTaskDelay(500 / portTICK_PERIOD_MS); // 每0.5秒获取一次JSON数据
}
}
void getWeatherInfo(){
if (WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
// 配置HTTP请求
http.begin(jsonUrl);
// 发送HTTP GET请求
int httpCode = http.GET();
if (httpCode > 0)
{
// 如果请求成功
if (httpCode == HTTP_CODE_OK)
{
String payload = http.getString(); // 获取响应字符串
Serial.println("Received JSON data:");
Serial.println(payload);
// 解析JSON数据
parseJson(payload);
}
}
else
{
// 如果请求失败
Serial.print("Error on sending GET: ");
Serial.println(http.errorToString(httpCode).c_str());
}
http.end(); // 结束HTTP请求
}
else
{
Serial.println("Error in WiFi connection");
}
}
void postStatus()
{
if (WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
// 配置HTTP请求
http.begin(postStatusUrl);
// 设置HTTP头部
http.addHeader("Content-Type", "application/json");
StaticJsonDocument<100> doc;
doc["standby"] = standby;
doc["reboot"] = reboot;
doc["humidifier"] = humidifier;
doc["screen"] = screen;
// 序列化JSON数据
String jsonPayload;
serializeJson(doc, jsonPayload);
Serial.print("JSON Payload: ");
Serial.println(jsonPayload);
// 发送HTTP POST请求
int httpCode = http.POST(jsonPayload);
if (httpCode > 0)
{
// 如果请求成功
if (httpCode == HTTP_CODE_OK)
{
String payload = http.getString(); // 获取响应字符串
Serial.println("poststatus状态信息");
Serial.println(payload);
// 解析JSON数据
parseJson2(payload);
}
else
{
Serial.print("Unexpected HTTP code: ");
Serial.println(httpCode);
}
}
else
{
// 如果请求失败
Serial.print("Error on sending POST: ");
Serial.println(http.errorToString(httpCode).c_str());
}
http.end(); // 结束HTTP请求
}
else
{
Serial.println("WiFi is not connected.");
}
}
void postData()
{
if (WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
// 配置HTTP请求
http.begin(postUrl);
// 设置HTTP头部
http.addHeader("Content-Type", "application/json");
StaticJsonDocument<200> doc;
doc["Humidity"] = local_humidity;
doc["Temperature"] = local_temperature;
// 序列化JSON数据
String jsonPayload;
serializeJson(doc, jsonPayload);
Serial.print("JSON Payload: ");
Serial.println(jsonPayload);
// 发送HTTP POST请求
int httpCode = http.POST(jsonPayload);
if (httpCode > 0)
{
// 如果请求成功
if (httpCode == HTTP_CODE_OK)
{
String payload = http.getString(); // 获取响应字符串
Serial.println("状态信息:");
Serial.println(payload);
// 解析JSON数据
parseJson2(payload);
}
}
else
{
// 如果请求失败
Serial.print("Error on sending POST: ");
Serial.println(http.errorToString(httpCode).c_str());
}
http.end(); // 结束HTTP请求
}
else
{
Serial.println("Error in WiFi connection");
}
}
//解析天气情况
void parseJson(String jsonData){
// 定义JSON文档的大小根据实际情况调整
const size_t capacity = JSON_OBJECT_SIZE(2) + 300;
StaticJsonDocument<capacity> doc;
// 解析JSON数据
DeserializationError error = deserializeJson(doc, jsonData);
if (error)
{
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
return;
}
// 获取JSON中的值
String newHumidity = doc["lives"][0]["humidity"] | "0.0";
String newTemperature = doc["lives"][0]["temperature"] | "0.0";
String newWeather = doc["lives"][0]["weather"] | "";
String newWindDirection = doc["lives"][0]["winddirection"] | "东北";
if(newTemperature.toInt()<4){
comfort_clothing = "棉衣或羽绒服";
}else if(newTemperature.toInt()<10){
comfort_clothing = "厚外套";
}else if(newTemperature.toInt()<20){
comfort_clothing = "薄外套、长袖";
}else{
comfort_clothing = "短外套、短裤、短裙";
}
Serial.print("newHumidity: ");
Serial.println(newHumidity);
Serial.print("newTemperature: ");
Serial.println(newTemperature);
Serial.print("Weather: ");
Serial.println(newWeather);
Serial.print("WindDirection: ");
Serial.println(newWindDirection);
// 获取互斥锁
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE)
{
humidity = newHumidity;
temperature = newTemperature;
weather = newWeather;
wind_direction = newWindDirection;
// 释放互斥锁
xSemaphoreGive(xMutex);
}
}
//解析状态信息
void parseJson2(String jsonData){
// 定义JSON文档的大小根据实际情况调整
const size_t capacity = JSON_OBJECT_SIZE(2) + 300;
StaticJsonDocument<capacity> doc;
// 解析JSON数据
DeserializationError error = deserializeJson(doc, jsonData);
if (error)
{
Serial.print("deserializeJson() failed: ");
Serial.println(error.c_str());
return;
}
// 获取JSON中的值
u8_t newStandby = doc["standby"].as<u8_t>();
u8_t newReboot = doc["reboot"].as<u8_t>();
u8_t newHumidifier = doc["humidifier"].as<u8_t>();
u8_t newScreen = doc["screen"].as<u8_t>();
Serial.print("😊😊screen::😊😊");
Serial.println(newScreen);
Serial.print("😊😊standby::😊😊");
Serial.println(newStandby);
Serial.print("😊😊reboot::😊😊");
Serial.println(newReboot);
Serial.print("😊😊humidifier::😊😊");
Serial.println(newHumidifier);
// 获取互斥锁
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE)
{
standby = newStandby;
reboot = newReboot;
humidifier = newHumidifier;
screen = newScreen;
// 释放互斥锁
xSemaphoreGive(xMutex);
}
}
void displayTask(void *parameter)
{
while (true)
{
// Check for incoming serial data
serialEvent();
// Name of font file (library adds leading / and .vlw)
String fileName = "MiSan20Chinese";
if(boot){
tft.loadFont(fileName, SD);
tft.setTextColor(0x5ACB, TFT_WHITE, true);
tft.setCursor(30, 135);
tft.print("病房空气指标");
boot = false;
tft.loadFont("BigFonts45", SD);
tft.setCursor(50, 160);
tft.print("良好");
}
tft.loadFont(fileName, SD); // Use font stored on SD
if (temp_local_temperature != local_temperature)
{
tft.loadFont("MiddleNumFonts25", SD);
tft.setTextColor(0x5ACB, TFT_WHITE, true);
tft.fillRect(220, 195, 70, 20, TFT_WHITE); // Clear screen
tft.setCursor(220, 195);
tft.print(local_temperature);
tft.println("");
temp_local_temperature = local_temperature;
}
if (temp_local_humidity != local_humidity)
{
tft.loadFont("MiddleNumFonts25",SD);
tft.setTextColor(0x5ACB, TFT_WHITE, true);
tft.fillRect(220, 140, 70, 20, TFT_WHITE); // Clear screen
tft.setCursor(220, 140);
tft.print(local_humidity);
tft.println("%");
temp_local_humidity = local_humidity;
}
tft.setTextColor(TFT_WHITE, TFT_BLACK);
if(temp_weather != weather)
{
tft.loadFont("BigFonts45", SD);
tft.setTextColor(TFT_WHITE, 0x5873FF, true);
drawSdJpeg("/Photos/cloudy.jpg",23,20);
tft.setCursor(112, 20);
tft.print(weather);
temp_weather = weather;
}
if(temp_temperature != temperature){
tft.loadFont(fileName, SD);
tft.setTextColor(TFT_WHITE, 0x5873FF,true);
tft.setCursor(240, 20);
tft.print(temperature);
tft.print("");
temp_temperature = temperature;
}
if(temp_humidity != humidity){
tft.setTextColor(TFT_WHITE, 0x5873FF,true);
tft.setCursor(240, 60);
tft.print(humidity);
tft.print("%");
temp_humidity = humidity;
}
// if(temp_wind_direction != wind_direction){
// tft.fillRect(80,130,60,20,TFT_BLACK);
// tft.print(" ");
// tft.print(wind_direction);
// tft.print("风");
// temp_wind_direction = wind_direction;
// }
vTaskDelay(1000 / portTICK_PERIOD_MS); // 每1秒刷新一次屏幕
}
}
void checkStatusTask(void *parameters){
while (true)
{
if(screen == 1){
digitalWrite(LED_PIN, HIGH); // Turn on LED
}else{
digitalWrite(LED_PIN, LOW); // Turn off LED
}
if(reboot == 1){
if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE){
reboot = 0;
xSemaphoreGive(xMutex);
}
postStatus();
delay(1000);
ESP.restart();
}
if(standby == 1){
Serial.println("进入待机模式");
delay(1000);
esp_light_sleep_start(); // 进入低功耗模式
}
if(humidifier == 1){
digitalWrite(HUMIDIFIER_PIN, HIGH); // Turn on humidifier
}else{
digitalWrite(HUMIDIFIER_PIN, LOW); // Turn off humidifier
}
vTaskDelay(500 / portTICK_PERIOD_MS); // 每0.秒检查一次数据
}
}
void listDir(fs::FS & fs, const char *dirname, uint8_t levels)
{
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if (!root)
{
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory())
{
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file)
{
if (file.isDirectory())
{
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels)
{
listDir(fs, file.name(), levels - 1);
}
}
else
{
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void serialEvent()
{
// 检查是否有数据传入
while (serial2.available())
{
// 从串口读取一个字符
char incomingByte = serial2.read();
// 如果接收到换行符或最大长度限制,则显示数据
if (incomingByte == '\n' || dataIndex >= 100)
{
receivedData0[dataIndex] = '\0'; // 结束字符串
ParseData(receivedData0); // 显示数据
// Serial.println(receivedData0);
dataIndex = 0; // 重置接收指针
}
else
{
receivedData0[dataIndex++] = incomingByte; // 将字符存入缓冲区
}
}
}
void ParseData(char *data) // 解析数据
{
char *token = strtok(data, ":");
while (token != NULL)
{
char *value = strtok(NULL, "C\r\n");
if (value != NULL)
{
if (strcmp(token, "Humidity") == 0)
{
local_humidity = atof(value);
Serial.print("Humidity: ");
Serial.println(local_humidity);
}
else if (strcmp(token, "Temperature") == 0)
{
local_temperature = atof(value);
Serial.print("Temperature: ");
Serial.println(local_temperature);
}
}
token = strtok(NULL, ":");
}
}
void drawSdJpeg(const char *filename, int xpos, int ypos)
{
// Open the named file (the Jpeg decoder library will close it)
File jpegFile = SD.open(filename, FILE_READ); // or, file handle reference for SD library
if (!jpegFile)
{
Serial.print("ERROR: File \"");
Serial.print(filename);
Serial.println("\" not found!");
return;
}
Serial.println("===========================");
Serial.print("Drawing file: ");
Serial.println(filename);
Serial.println("===========================");
// Use one of the following methods to initialise the decoder:
bool decoded = JpegDec.decodeSdFile(jpegFile); // Pass the SD file handle to the decoder,
// bool decoded = JpegDec.decodeSdFile(filename); // or pass the filename (String or character array)
if (decoded)
{
// print information about the image to the serial port
jpegInfo();
// render the image onto the screen at given coordinates
jpegRender(xpos, ypos);
}
else
{
Serial.println("Jpeg file format not supported!");
}
}

11
TFT-ESP32/test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html

82
TFT-ESP32/test/test.cpp Normal file
View File

@ -0,0 +1,82 @@
#include <TFT_eSPI.h> // TFT 显示屏库
TFT_eSPI tft = TFT_eSPI(); // 初始化 TFT 实例
const int screenWidth = 240; // 根据你的屏幕分辨率设置
const int screenHeight = 320;
const int byteWidth = (screenWidth + 7) / 8; // 每行需要的字节数
// 数组用于存储二值化后的屏幕数据
uint8_t binaryImage[screenHeight][byteWidth];
// 将屏幕数据读取并二值化,存储到数组中
void binaryScreenToArray()
{
for (int y = 0; y < screenHeight; y++)
{
uint8_t byteValue = 0;
for (int x = 0; x < screenWidth; x++)
{
// 读取像素颜色
uint16_t color = tft.readPixel(x, y);
// 将颜色二值化假设TFT_WHITE为白色其它为黑色
if (color == TFT_WHITE)
{
byteValue |= (1 << (7 - (x % 8))); // 设置当前位为1白色
}
// 每8个像素存储为一个字节
if (x % 8 == 7 || x == screenWidth - 1)
{
binaryImage[y][x / 8] = byteValue;
byteValue = 0;
}
}
}
}
// 将二值化后的屏幕数据通过串口发送到电脑,值之间用逗号隔开
void sendArrayToPC()
{
Serial.println("Start sending binary screen data:");
for (int y = 0; y < screenHeight; y++)
{
for (int x = 0; x < byteWidth; x++)
{
Serial.print(binaryImage[y][x], HEX); // 以十六进制格式发送每个字节
// 如果不是最后一个字节,则添加逗号
if (x < byteWidth - 1)
{
Serial.print(","); // 用逗号分隔
}
}
Serial.println(); // 每行结束后换行
}
Serial.println("Binary screen data sent.");
}
void setup()
{
Serial.begin(115200); // 初始化串口通信
tft.init(); // 初始化屏幕
tft.setRotation(1); // 根据需要设置屏幕的旋转
tft.fillScreen(TFT_BLACK); // 将屏幕设置为黑色
// 绘制一些内容(测试用)
tft.fillCircle(120, 160, 50, TFT_WHITE); // 在屏幕中间绘制一个白色的圆圈
// 读取并二值化屏幕内容
binaryScreenToArray();
// 将二值化后的数组通过串口传输到电脑
sendArrayToPC();
}
void loop()
{
// 主要功能在 setup 中执行loop 中不做任何操作
}

111
TFT-ESP32/test/testmain.cpp Normal file
View File

@ -0,0 +1,111 @@
//<App !Start!>
// FILE: [main.cpp]
// Created by GUIslice Builder version: [0.17.b34]
//
// GUIslice Builder Generated File
//
// For the latest guides, updates and support view:
// https://github.com/ImpulseAdventure/GUIslice
//
//<App !End!>
// ------------------------------------------------
// Headers to include
// ------------------------------------------------
#include "GUIsliceProjects_GSLC.h"
// ------------------------------------------------
// Program Globals
// ------------------------------------------------
// Save some element references for direct access
//<Save_References !Start!>
//<Save_References !End!>
// Define debug message function
static int16_t DebugOut(char ch)
{
if (ch == (char)'\n')
Serial.println("");
else
Serial.write(ch);
return 0;
}
// ------------------------------------------------
// Callback Methods
// ------------------------------------------------
// Common Button callback
bool CbBtnCommon(void *pvGui, void *pvElemRef, gslc_teTouch eTouch, int16_t nX, int16_t nY)
{
// Typecast the parameters to match the GUI and element types
gslc_tsGui *pGui = (gslc_tsGui *)(pvGui);
gslc_tsElemRef *pElemRef = (gslc_tsElemRef *)(pvElemRef);
gslc_tsElem *pElem = gslc_GetElemFromRef(pGui, pElemRef);
if (eTouch == GSLC_TOUCH_UP_IN)
{
// From the element's ID we can determine which button was pressed.
switch (pElem->nId)
{
//<Button Enums !Start!>
case E_ELEM_BTN1:
break;
case E_ELEM_BTN2:
break;
//<Button Enums !End!>
default:
break;
}
}
return true;
}
//<Checkbox Callback !Start!>
//<Checkbox Callback !End!>
//<Keypad Callback !Start!>
//<Keypad Callback !End!>
//<Spinner Callback !Start!>
//<Spinner Callback !End!>
//<Listbox Callback !Start!>
//<Listbox Callback !End!>
//<Draw Callback !Start!>
//<Draw Callback !End!>
//<Slider Callback !Start!>
//<Slider Callback !End!>
//<Tick Callback !Start!>
//<Tick Callback !End!>
void setup()
{
// ------------------------------------------------
// Initialize
// ------------------------------------------------
Serial.begin(9600);
// Wait for USB Serial
// delay(1000); // NOTE: Some devices require a delay after Serial.begin() before serial port can be used
gslc_InitDebug(&DebugOut);
// ------------------------------------------------
// Create graphic elements
// ------------------------------------------------
InitGUIslice_gen();
}
// -----------------------------------
// Main event loop
// -----------------------------------
void loop()
{
// ------------------------------------------------
// Update GUI Elements
// ------------------------------------------------
// TODO - Add update code for any text, gauges, or sliders
// ------------------------------------------------
// Periodically call GUIslice update function
// ------------------------------------------------
gslc_Update(&m_gui);
}