first commit
This commit is contained in:
5
TFT-ESP32/.gitignore
vendored
Normal file
5
TFT-ESP32/.gitignore
vendored
Normal 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
10
TFT-ESP32/.vscode/extensions.json
vendored
Normal 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
BIN
TFT-ESP32/data/san28.bin
Normal file
Binary file not shown.
39
TFT-ESP32/include/README
Normal file
39
TFT-ESP32/include/README
Normal 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
46
TFT-ESP32/lib/README
Normal 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
42
TFT-ESP32/platformio.ini
Normal 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
768
TFT-ESP32/src/main.cpp
Normal 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 (发送):GPIO16(UART2_TXD)
|
||||
// RX (接收):GPIO17(UART2_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
11
TFT-ESP32/test/README
Normal 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
82
TFT-ESP32/test/test.cpp
Normal 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
111
TFT-ESP32/test/testmain.cpp
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user