ESP32S3 遥控器可用代码第一版
·
#include <Arduino.h>
#include <TFT_eSPI.h>
// ================= 屏幕基础配置 =================
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
#define TFT_MOSI 11
#define TFT_SCLK 12
#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 8
#define TFT_BL 7
// ================= 系统配置 =================
#define REPORT_INTERVAL 1000
#define TEST_PIN 42
#define BEEP_DURATION 200
#define UI_REFRESH_INTERVAL 25
// ================= 引脚定义 =================
#define JOY_X1 14
#define JOY_Y1 15
#define JOY_X2 1
#define JOY_Y2 2
#define KEY1 19
#define KEY2 20
// ================= 需隔离的旧引脚 =================
#define OLD_PIN_35 35
#define OLD_PIN_36 36
#define OLD_PIN_37 37
#define OLD_PIN_38 38
// ================= ADC配置 =================
#define ADC_MIN 0
#define ADC_MAX 4095
#define ADC_DEAD_ZONE 200
// ================= 颜色定义 =================
#define COLOR_BG 0x1042
#define COLOR_JOY_BG 0x18C3
#define COLOR_JOY_FRAME 0x3186
#define COLOR_JOY_CROSS 0x2945
#define COLOR_JOY_DOT 0x07E0
#define COLOR_JOY_DOT_BORDER 0xFFFF
#define COLOR_KEY_IDLE 0x6000
#define COLOR_KEY_ACT 0xF800
#define COLOR_SYS_FRAME 0x39E7
#define COLOR_SYS_TEXT 0xC618
#define COLOR_WHITE 0xFFFF
// ================= UI区域定义 (已上移,无标题栏) =================
typedef struct {
int16_t x;
int16_t y;
int16_t w;
int16_t h;
const char* title;
int16_t joy_inner_x;
int16_t joy_inner_y;
int16_t joy_inner_w;
int16_t joy_inner_h;
int16_t key_x;
int16_t key_y;
int16_t key_w;
int16_t key_h;
} JoyUI_Config;
// 左摇杆 (整体上移15像素)
JoyUI_Config leftJoy = {
.x = 10, .y = 10, .w = 105, .h = 140, .title = "LEFT",
.joy_inner_x = 15, .joy_inner_y = 30, .joy_inner_w = 95, .joy_inner_h = 60,
.key_x = 32, .key_y = 100, .key_w = 60, .key_h = 18
};
// 右摇杆
JoyUI_Config rightJoy = {
.x = 125, .y = 10, .w = 105, .h = 140, .title = "RIGHT",
.joy_inner_x = 130, .joy_inner_y = 30, .joy_inner_w = 95, .joy_inner_h = 60,
.key_x = 147, .key_y = 100, .key_w = 60, .key_h = 18
};
// 系统信息区域 (上移)
#define SYS_INFO_X 10
#define SYS_INFO_Y 160
#define SYS_INFO_W 220
#define SYS_INFO_H 150
TFT_eSPI tft = TFT_eSPI();
int adcLX = 2048, adcLY = 2048;
int adcRX = 2048, adcRY = 2048;
bool key1State = false, key2State = false;
uint32_t lastScreenUpdate = 0;
void initTFT();
void drawBootAnimation();
void drawMainUIFrame();
void readAllInputs();
void updateJoyDisplay(JoyUI_Config config, int adcX, int adcY, bool keyState);
void updateSysInfo();
void testBeep();
void drawCenteredText(const char* text, int y, uint16_t color, uint8_t size);
void drawRightAlignedText(const char* text, int x, int y, uint16_t color, uint8_t size);
void setup() {
Serial.begin(115200);
delay(500);
pinMode(OLD_PIN_35, INPUT);
pinMode(OLD_PIN_36, INPUT);
pinMode(OLD_PIN_37, INPUT);
pinMode(OLD_PIN_38, INPUT);
pinMode(JOY_X1, INPUT);
pinMode(JOY_Y1, INPUT);
pinMode(JOY_X2, INPUT);
pinMode(JOY_Y2, INPUT);
pinMode(KEY1, INPUT_PULLUP);
pinMode(KEY2, INPUT_PULLUP);
pinMode(TEST_PIN, OUTPUT);
digitalWrite(TEST_PIN, LOW);
initTFT();
drawBootAnimation();
drawMainUIFrame();
testBeep();
}
void loop() {
readAllInputs();
if (millis() - lastScreenUpdate >= UI_REFRESH_INTERVAL) {
lastScreenUpdate = millis();
updateJoyDisplay(leftJoy, adcLX, adcLY, key1State);
updateJoyDisplay(rightJoy, adcRX, adcRY, key2State);
}
static uint32_t lastSysReport = 0;
if (millis() - lastSysReport >= REPORT_INTERVAL) {
lastSysReport = millis();
updateSysInfo();
}
}
void initTFT() {
pinMode(TFT_RST, OUTPUT);
digitalWrite(TFT_RST, LOW);
delay(100);
digitalWrite(TFT_RST, HIGH);
delay(200);
tft.init();
tft.setRotation(2);
tft.fillScreen(COLOR_BG);
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
}
void drawBootAnimation() {
for (int y = 0; y < SCREEN_HEIGHT; y++) {
uint8_t blue = map(y, 0, SCREEN_HEIGHT, 10, 70);
tft.drawFastHLine(0, y, SCREEN_WIDTH, tft.color565(0, 0, blue));
}
drawCenteredText("ESP32-S3", 80, COLOR_WHITE, 4);
delay(300);
drawCenteredText("Joystick Monitor", 130, COLOR_JOY_DOT, 2);
delay(500);
tft.fillRoundRect(30, 180, 180, 20, 10, COLOR_JOY_FRAME);
for (int i = 0; i <= 176; i += 4) {
tft.fillRoundRect(32, 182, i, 16, 8, COLOR_JOY_DOT);
delay(12);
}
drawCenteredText("System Ready!", 220, COLOR_WHITE, 2);
delay(800);
tft.fillScreen(COLOR_BG);
}
// ================= 主界面:已移除顶部标题栏 =================
void drawMainUIFrame() {
// 左摇杆
tft.fillRoundRect(leftJoy.x, leftJoy.y, leftJoy.w, leftJoy.h, 5, COLOR_JOY_BG);
tft.drawRoundRect(leftJoy.x, leftJoy.y, leftJoy.w, leftJoy.h, 5, COLOR_JOY_FRAME);
tft.setTextColor(COLOR_WHITE);
tft.setTextSize(1);
tft.setCursor(leftJoy.x + 8, leftJoy.y + 8);
tft.println(leftJoy.title);
// 右摇杆
tft.fillRoundRect(rightJoy.x, rightJoy.y, rightJoy.w, rightJoy.h, 5, COLOR_JOY_BG);
tft.drawRoundRect(rightJoy.x, rightJoy.y, rightJoy.w, rightJoy.h, 5, COLOR_JOY_FRAME);
tft.setCursor(rightJoy.x + 8, rightJoy.y + 8);
tft.println(rightJoy.title);
// 系统信息
tft.drawRoundRect(SYS_INFO_X, SYS_INFO_Y, SYS_INFO_W, SYS_INFO_H, 5, COLOR_SYS_FRAME);
tft.setTextColor(COLOR_SYS_TEXT);
tft.setTextSize(1);
tft.setCursor(SYS_INFO_X + 10, SYS_INFO_Y + 8);
tft.println("System Information");
}
void readAllInputs() {
adcLX = analogRead(JOY_X1);
adcLY = analogRead(JOY_Y1);
adcRX = analogRead(JOY_X2);
adcRY = analogRead(JOY_Y2);
key1State = (digitalRead(KEY1) == LOW);
key2State = (digitalRead(KEY2) == LOW);
}
// ================= 核心:优化版摇杆显示 =================
void updateJoyDisplay(JoyUI_Config config, int adcX, int adcY, bool keyState) {
// 1. 安全清除整个内部区域 (稍微扩大一点,防止边缘残留)
tft.fillRect(config.x + 1, config.y + 18, config.w - 2, config.h - 22, COLOR_JOY_BG);
// 2. 定义活动区域
int innerX = config.x + 10;
int innerY = config.y + 28; // 稍微下移,给标题留空间
int innerW = config.w - 20;
int innerH = config.h - 55;
// =========================================================
// 关键修复1:左右方向反转
// 如果你觉得左右反了,就把下面这行的 map 参数调换顺序
// =========================================================
// 原逻辑:int dotX = map(adcY, ADC_MIN, ADC_MAX, innerX + 8, innerX + innerW - 8);
// 反转后:(把 innerX + innerW - 8 和 innerX + 8 换位置)
int dotX = map(adcY, ADC_MIN, ADC_MAX, innerX + innerW - 8, innerX + 8);
// 上下方向 (保持原样,如果你觉得上下也反了,同理调换)
int dotY = map(adcX, ADC_MIN, ADC_MAX, innerY + innerH - 8, innerY + 8);
// 死区处理
if (abs(adcX - 2048) < ADC_DEAD_ZONE) dotY = innerY + innerH/2;
if (abs(adcY - 2048) < ADC_DEAD_ZONE) dotX = innerX + innerW/2;
// 严格限制范围
dotX = constrain(dotX, innerX + 8, innerX + innerW - 8);
dotY = constrain(dotY, innerY + 8, innerY + innerH - 8);
// 3. 绘制十字准星
int centerX = innerX + innerW/2;
int centerY = innerY + innerH/2;
tft.drawFastVLine(centerX, innerY, innerH, COLOR_JOY_CROSS);
tft.drawFastHLine(innerX, centerY, innerW, COLOR_JOY_CROSS);
tft.fillCircle(centerX, centerY, 3, COLOR_JOY_CROSS);
// 4. 绘制摇杆圆点
tft.fillCircle(dotX, dotY, 7, COLOR_JOY_DOT);
tft.drawCircle(dotX, dotY, 7, COLOR_JOY_DOT_BORDER);
// =========================================================
// 关键修复2:按键文字绝对居中
// =========================================================
int keyBoxX = config.x + (config.w - 50) / 2; // 按键框居中
int keyBoxY = config.y + config.h - 22;
// 先清除按键区域背景
tft.fillRect(keyBoxX - 2, keyBoxY - 2, 54, 20, COLOR_JOY_BG);
// 绘制按键框
tft.fillRoundRect(keyBoxX, keyBoxY, 50, 16, 3, keyState ? COLOR_KEY_ACT : COLOR_KEY_IDLE);
tft.drawRoundRect(keyBoxX, keyBoxY, 50, 16, 3, COLOR_WHITE);
// 绘制文字 "SW" (居中算法)
tft.setTextColor(COLOR_WHITE);
tft.setTextSize(1);
const char* keyLabel = "SW";
int textWidth = strlen(keyLabel) * 6; // 6 pixels per char
int textX = keyBoxX + (50 - textWidth) / 2;
int textY = keyBoxY + 4;
tft.setCursor(textX, textY);
tft.print(keyLabel);
// =========================================================
// 关键修复3:ADC数值坐标居中显示
// =========================================================
char coordStr[16];
sprintf(coordStr, "%d,%d", adcX, adcY);
int coordWidth = strlen(coordStr) * 6;
int coordX = config.x + (config.w - coordWidth) / 2;
tft.setTextSize(1);
tft.setCursor(coordX, config.y + config.h - 35);
tft.print(coordStr);
}
void updateSysInfo() {
tft.fillRect(SYS_INFO_X + 10, SYS_INFO_Y + 20, SYS_INFO_W - 20, SYS_INFO_H - 30, COLOR_BG);
tft.setTextColor(COLOR_SYS_TEXT);
tft.setTextSize(1);
char buffer[48];
sprintf(buffer, "CPU Frequency");
tft.setCursor(SYS_INFO_X + 15, SYS_INFO_Y + 25);
tft.print(buffer);
sprintf(buffer, "%d MHz", ESP.getCpuFreqMHz());
drawRightAlignedText(buffer, SYS_INFO_X + SYS_INFO_W - 15, SYS_INFO_Y + 25, COLOR_WHITE, 1);
sprintf(buffer, "Memory Usage");
tft.setCursor(SYS_INFO_X + 15, SYS_INFO_Y + 45);
tft.print(buffer);
uint32_t heapUsed = (ESP.getHeapSize() - ESP.getFreeHeap()) * 100 / ESP.getHeapSize();
sprintf(buffer, "%d %%", heapUsed);
drawRightAlignedText(buffer, SYS_INFO_X + SYS_INFO_W - 15, SYS_INFO_Y + 45, COLOR_WHITE, 1);
sprintf(buffer, "Free Memory");
tft.setCursor(SYS_INFO_X + 15, SYS_INFO_Y + 65);
tft.print(buffer);
sprintf(buffer, "%d KB", ESP.getFreeHeap() / 1024);
drawRightAlignedText(buffer, SYS_INFO_X + SYS_INFO_W - 15, SYS_INFO_Y + 65, COLOR_WHITE, 1);
sprintf(buffer, "System Uptime");
tft.setCursor(SYS_INFO_X + 15, SYS_INFO_Y + 85);
tft.print(buffer);
sprintf(buffer, "%lu s", millis() / 1000);
drawRightAlignedText(buffer, SYS_INFO_X + SYS_INFO_W - 15, SYS_INFO_Y + 85, COLOR_WHITE, 1);
}
void drawCenteredText(const char* text, int y, uint16_t color, uint8_t size) {
tft.setTextSize(size);
tft.setTextColor(color);
int16_t x = (SCREEN_WIDTH - strlen(text) * 6 * size) / 2;
tft.setCursor(x, y);
tft.print(text);
}
void drawRightAlignedText(const char* text, int x, int y, uint16_t color, uint8_t size) {
tft.setTextSize(size);
tft.setTextColor(color);
int16_t textWidth = strlen(text) * 6 * size;
tft.setCursor(x - textWidth, y);
tft.print(text);
}
void testBeep() {
uint32_t end = millis() + BEEP_DURATION;
while (millis() < end) {
digitalWrite(TEST_PIN, HIGH);
delayMicroseconds(250);
digitalWrite(TEST_PIN, LOW);
delayMicroseconds(250);
}
digitalWrite(TEST_PIN, LOW);
}
更多推荐


所有评论(0)