This commit is contained in:
feie9456 2025-11-13 20:54:45 +08:00
commit 2b5cfc1538
13 changed files with 479 additions and 0 deletions

5
.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
.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"
]
}

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"files.associations": {
"*.dbclient-js": "javascript",
"string": "cpp"
}
}

6
data/settings.json Normal file
View File

@ -0,0 +1,6 @@
{
"wifi": {
"ssid": "Ti",
"password": "94544549"
}
}

37
include/README Normal file
View File

@ -0,0 +1,37 @@
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 convention is to give header files names that end with `.h'.
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

25
include/init.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef INIT_H
#define INIT_H
#include <Adafruit_ST7735.h>
#include <Arduino.h>
namespace esp32 {
namespace init {
// 在屏幕上打印日志
void tftLog(const char* msg, uint16_t color);
// 读取并解析配置文件
bool loadSettings();
// 连接 WiFi
void connectWiFi();
// 初始化系统
void initSystem(Adafruit_ST7735& tft);
} // namespace init
} // namespace esp32
#endif // INIT_H

48
include/utils.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef UTILS_H
#define UTILS_H
#include <stdint.h>
namespace esp32 {
namespace utils {
// 三次贝塞尔曲线计算
float cubicBezier(float t, float p0, float p1, float p2, float p3);
float solveCubicBezierX(float xTarget,
float x1,
float x2,
float epsilon = 0.00001f);
float linear2CubicBezier(float source,
float x1 = 0.42f,
float y1 = 0.0f,
float x2 = 0.58f,
float y2 = 1.0f);
// RGB 颜色转换
uint16_t rgbTo565(uint32_t rgbColor);
} // namespace utils
// 颜色常量 (全局命名空间)
namespace colors {
inline uint16_t BLACK() { return utils::rgbTo565(0x000000); }
inline uint16_t WHITE() { return utils::rgbTo565(0xFFFFFF); }
inline uint16_t RED() { return utils::rgbTo565(0xFF0000); }
inline uint16_t GREEN() { return utils::rgbTo565(0x00FF00); }
inline uint16_t BLUE() { return utils::rgbTo565(0x0000FF); }
inline uint16_t YELLOW() { return utils::rgbTo565(0xFFFF00); }
} // namespace colors
} // namespace esp32
// 为了兼容性,保留宏定义
#define BLACK esp32::colors::BLACK()
#define WHITE esp32::colors::WHITE()
#define RED esp32::colors::RED()
#define GREEN esp32::colors::GREEN()
#define BLUE esp32::colors::BLUE()
#define YELLOW esp32::colors::YELLOW()
#endif // UTILS_H

46
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 the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for 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
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

29
platformio.ini Normal file
View File

@ -0,0 +1,29 @@
; 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:esp32-s3-n16r8]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
board_build.arduino.memory_type = qio_opi
board_build.flash_mode = qio
board_build.psram_type = opi
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_build.extra_flags =
-DBOARD_HAS_PSRAM
lib_deps =
adafruit/Adafruit ST7735 and ST7789 Library
adafruit/Adafruit GFX Library
bblanchon/ArduinoJson@^7.4.2
upload_speed = 921600
monitor_speed = 115200
build_flags = -std=gnu++2a
board_build.filesystem = littlefs

107
src/init.cpp Normal file
View File

@ -0,0 +1,107 @@
#include "init.h"
#include <ArduinoJson.h>
#include <LittleFS.h>
#include <WiFi.h>
#include "utils.h"
namespace esp32 {
namespace init {
// WiFi 配置变量
static String wifiSSID = "";
static String wifiPassword = "";
// TFT 显示器引用 (在 initSystem 中设置)
static Adafruit_ST7735* tftPtr = nullptr;
// 在屏幕上打印日志
void tftLog(const char* msg, uint16_t color) {
if (tftPtr == nullptr)
return;
tftPtr->setTextColor(color);
tftPtr->println(msg);
tftPtr->setCursor(tftPtr->getCursorX(),
tftPtr->getCursorY() + 2); // 增加2像素行距
}
// 读取并解析配置文件
bool loadSettings() {
if (!LittleFS.begin(true)) {
tftLog("LittleFS mount failed!", RED);
return false;
}
tftLog("LittleFS mounted", GREEN);
File file = LittleFS.open("/settings.json", "r");
if (!file) {
tftLog("Can't open settings", RED);
return false;
}
tftLog("Settings file opened", GREEN);
// 使用 ArduinoJson 7.x 解析
JsonDocument doc;
DeserializationError error = deserializeJson(doc, file);
file.close();
if (error) {
tftLog("JSON parse failed!", RED);
return false;
}
// 读取 WiFi 配置
wifiSSID = doc["wifi"]["ssid"].as<String>();
wifiPassword = doc["wifi"]["password"].as<String>();
tftLog("Config loaded:", GREEN);
tftPtr->print(" SSID: ");
tftLog(wifiSSID.c_str(), WHITE);
return true;
}
// 连接 WiFi
void connectWiFi() {
if (wifiSSID.isEmpty()) {
tftLog("WiFi SSID empty", RED);
return;
}
tftPtr->print("Connecting: ");
tftLog(wifiSSID.c_str(), WHITE);
WiFi.mode(WIFI_STA);
WiFi.begin(wifiSSID.c_str(), wifiPassword.c_str());
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
tftPtr->print(".");
attempts++;
}
tftPtr->println();
if (WiFi.status() == WL_CONNECTED) {
tftLog("WiFi connected!", GREEN);
tftPtr->print("IP: ");
tftLog(WiFi.localIP().toString().c_str(), WHITE);
} else {
tftLog("WiFi failed!", RED);
}
}
// 初始化系统
void initSystem(Adafruit_ST7735& tft) {
tftPtr = &tft;
tftLog("=== ESP32-S3 ===", YELLOW);
tft.println();
// 读取配置文件并连接 WiFi
if (loadSettings()) {
connectWiFi();
}
}
} // namespace init
} // namespace esp32

78
src/main.cpp Normal file
View File

@ -0,0 +1,78 @@
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
#include "utils.h"
#include "init.h"
using namespace esp32;
#define TFT_MOSI 16
#define TFT_SCLK 15
#define TFT_CS 18
#define TFT_DC 17
#define TFT_RST -1
// 屏幕尺寸为 128x160
Adafruit_ST7735 tft(TFT_CS, TFT_DC, TFT_RST);
#define function auto
#define let auto
function rgbTo565(uint32_t rgbColor) -> uint16_t {
uint8_t r = (rgbColor >> 16) & 0xFF;
uint8_t g = (rgbColor >> 8) & 0xFF;
uint8_t b = rgbColor & 0xFF;
// 交换 r 和 b
uint8_t tmp = r;
r = b;
b = tmp;
uint16_t r5 = (r * 31 / 255) << 11;
uint16_t g6 = (g * 63 / 255) << 5;
uint16_t b5 = (b * 31 / 255);
return r5 | g6 | b5;
}
const int BTN_LEFT = 12; // 左按钮
const int BTN_RIGHT = 11; // 右按钮
const int BTN_UP = 10; // 上按钮
#define BLACK rgbTo565(0x000000)
#define WHITE rgbTo565(0xFFFFFF)
#define RED rgbTo565(0xFF0000)
#define GREEN rgbTo565(0x00FF00)
#define BLUE rgbTo565(0x0000FF)
#define YELLOW rgbTo565(0xFFFF00)
void setup() {
// 按钮:内部下拉
pinMode(BTN_LEFT, INPUT_PULLDOWN);
pinMode(BTN_RIGHT, INPUT_PULLDOWN);
pinMode(BTN_UP, INPUT_PULLDOWN);
// 1. 把硬件 SPI 绑定到你现有的引脚
SPI.begin(TFT_SCLK, -1 /*MISO 不用*/, TFT_MOSI, TFT_CS);
SPI.setFrequency(27000000); // ST7735 通常 27MHz 左右比较稳
// 2. 初始化屏幕
tft.initR(INITR_GREENTAB);
tft.setRotation(0);
tft.fillScreen(BLACK);
tft.setCursor(0, 0);
tft.setTextColor(WHITE);
tft.setTextSize(1);
// 初始化系统(读取配置并连接 WiFi)
init::initSystem(tft);
}
void loop() {
bool leftPressed = digitalRead(BTN_LEFT) == HIGH; // 按下=HIGH
bool rightPressed = digitalRead(BTN_RIGHT) == HIGH;
bool upPressed = digitalRead(BTN_UP) == HIGH;
tft.fillRect(0, 30, 20, 20, leftPressed ? RED : BLACK);
tft.fillRect(108, 30, 20, 20, rightPressed ? RED : BLACK);
tft.fillRect(54, 0, 20, 20, upPressed ? RED : BLACK);
}

71
src/utils.cpp Normal file
View File

@ -0,0 +1,71 @@
#include "utils.h"
#include <Arduino.h>
#define function auto
#define let auto
namespace esp32 {
namespace utils {
// 三次贝塞尔曲线计算
function cubicBezier(float t, float p0, float p1, float p2, float p3) -> float {
let u = 1.0f - t;
return (u * u * u * p0) + (3.0f * u * u * t * p1) +
(3.0f * u * t * t * p2) + (t * t * t * p3);
}
function solveCubicBezierX(float xTarget,
float x1,
float x2,
float epsilon) -> float {
let t = xTarget;
let x = cubicBezier(t, 0.0f, x1, x2, 1.0f);
int iteration = 0;
while (abs(x - xTarget) > epsilon && iteration < 100) {
let d = 3.0f * (1.0f - t) * (1.0f - t) * (x1 - 0.0f) +
6.0f * (1.0f - t) * t * (x2 - x1) + 3.0f * t * t * (1.0f - x2);
if (d == 0.0f)
break;
t -= (x - xTarget) / d;
x = cubicBezier(t, 0.0f, x1, x2, 1.0f);
iteration++;
}
return t;
}
function linear2CubicBezier(float source,
float x1,
float y1,
float x2,
float y2) -> float {
if (source < 0.0f || source > 1.0f) {
return source; // 返回原值而不是抛出异常
}
let t = solveCubicBezierX(source, x1, x2);
let y = cubicBezier(t, 0.0f, y1, y2, 1.0f);
return y;
}
function rgbTo565(uint32_t rgbColor) -> uint16_t {
uint8_t r = (rgbColor >> 16) & 0xFF;
uint8_t g = (rgbColor >> 8) & 0xFF;
uint8_t b = rgbColor & 0xFF;
// 交换 r 和 b
uint8_t tmp = r;
r = b;
b = tmp;
uint16_t r5 = (r * 31 / 255) << 11;
uint16_t g6 = (g * 63 / 255) << 5;
uint16_t b5 = (b * 31 / 255);
return r5 | g6 | b5;
}
} // namespace utils
} // namespace esp32

11
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