소스 검색

Display Updates

Benjamin Harris 2 주 전
부모
커밋
bc37599079
8개의 변경된 파일141개의 추가작업 그리고 94개의 파일을 삭제
  1. 1 1
      CLAUDE.md
  2. 9 10
      README.md
  3. 8 12
      main/User_Setup.h
  4. 6 7
      main/config.h
  5. 66 42
      main/display.cpp
  6. 2 0
      main/display.h
  7. 30 13
      main/logo.h
  8. 19 9
      main/main.ino

+ 1 - 1
CLAUDE.md

@@ -74,7 +74,7 @@ The display should update immediately if the input state changes.
 
 ## File Structure
 
-src/
+main/
 ├── main.ino
 ├── display.cpp
 ├── display.h

+ 9 - 10
README.md

@@ -40,23 +40,22 @@ System selection is determined by the voltage level present on a dedicated ESP32
 
 ## GPIO
 
-| Function            | ESP32-S2 Pin |
+| Function            | ESP32-DEVKIT V1 Pin |
 | ------------------- | ------------ |
-| System Select Input | TBD          |
-| TFT MOSI            | TBD          |
-| TFT SCLK            | TBD          |
-| TFT CS              | TBD          |
-| TFT DC              | TBD          |
-| TFT RST             | TBD          |
-| TFT BL              | TBD          |
+| System Select Input | GPIO32       |
+| TFT MOSI            | GPIO23       |
+| TFT SCLK            | GPIO18       |
+| TFT CS              | GPIO26       |
+| TFT DC              | GPIO25       |
+| TFT RST             | GPIO27       |
 
 ## Build
 
 1. Install Arduino IDE.
-2. Install ESP32 board support package.
+2. Install ESP32 DEVKIT V1 board support package.
 3. Install TFT_eSPI library.
 4. Configure display settings for GC9A01.
-5. Compile and upload to the ESP32-S2.
+5. Compile and upload to the ESP32-DEVKIT V1.
 
 ## Future Improvements
 

+ 8 - 12
User_Setup.h → main/User_Setup.h

@@ -1,12 +1,12 @@
 // ---------------------------------------------------------------------------
-// TFT_eSPI User_Setup.h for ESP32-S2 + GC9A01 240x240 round TFT
+// TFT_eSPI User_Setup.h for ESP32-DEVKIT V1 + GC9A01 240x240 round TFT
 //
 // INSTRUCTIONS:
 //   Copy this file to your TFT_eSPI library folder, replacing the existing
 //   User_Setup.h, then recompile. Alternatively, place it in the sketch
 //   folder if your TFT_eSPI version supports sketch-local setup files.
 //
-//   Pin numbers MUST match the wiring in src/config.h.
+//   Pin numbers MUST match the wiring in config.h.
 // ---------------------------------------------------------------------------
 
 // Driver selection
@@ -16,16 +16,12 @@
 #define TFT_WIDTH   240
 #define TFT_HEIGHT  240
 
-// SPI pins — update to match your hardware wiring
-#define TFT_MOSI    35
-#define TFT_SCLK    36
-#define TFT_CS      34
-#define TFT_DC      33
-#define TFT_RST     38
-// Backlight is controlled in firmware (PIN_TFT_BL in config.h)
-
-// SPI bus (ESP32-S2 FSPI = SPI2)
-#define USE_FSPI_PORT
+// SPI pins — ESP32-DEVKIT V1 VSPI bus
+#define TFT_MOSI    23
+#define TFT_SCLK    18
+#define TFT_CS      26
+#define TFT_DC      25
+#define TFT_RST     27
 
 // SPI clock frequency
 #define SPI_FREQUENCY       27000000

+ 6 - 7
main/config.h

@@ -7,10 +7,7 @@
 // System select GPIO
 // HIGH = System A active
 // LOW  = System B active
-#define PIN_SYS_SELECT      1
-
-// TFT backlight (active high)
-#define PIN_TFT_BL          21
+#define PIN_SYS_SELECT      32
 
 // ---------------------------------------------------------------------------
 // Display geometry
@@ -27,16 +24,18 @@
 #define LOGO_FADE_STEPS     100         // animation steps per fade phase
 #define LOGO_FADE_STEP_MS   10          // ms per step (phase duration = 1 s each)
 #define LOGO_STATIC_MS      8000UL      // ms logo holds after both fades complete
+#define SPLASH_MS           3000UL      // ms splash screen holds before system screen
+#define BLANK_TIMEOUT_MS    300000UL    // ms on System A before screen blanks (5 min)
 #define POLL_INTERVAL_MS    50UL
 
 // ---------------------------------------------------------------------------
 // Colours (RGB565)
 // ---------------------------------------------------------------------------
 #define COL_BG_SYS_A        0x1600  // dark green
-#define COL_BG_SYS_B        0x0010  // dark navy
+#define COL_BG_SYS_B        0xe861  // dark red
 #define COL_BG_LOGO         TFT_BLACK
 #define COL_RING            TFT_WHITE
 #define COL_LABEL           TFT_WHITE
-#define COL_LETTER_A        0x07E0  // bright green
-#define COL_LETTER_B        0x5C1F  // cornflower blue
+#define COL_LETTER_A        TFT_WHITE
+#define COL_LETTER_B        TFT_WHITE
 #define COL_LOGO_ACCENT     0xFEA0  // gold

+ 66 - 42
main/display.cpp

@@ -8,62 +8,86 @@
 // ---------------------------------------------------------------------------
 
 static void drawStatusScreen(TFT_eSPI &tft,
-                              uint16_t bgColour,
-                              uint16_t letterColour,
-                              const char *letter) {
-    TFT_eSprite spr = TFT_eSprite(&tft);
-    spr.createSprite(DISPLAY_W, DISPLAY_H);
-    spr.fillSprite(bgColour);
-
-    // Outer ring
-    spr.drawCircle(DISPLAY_CX, DISPLAY_CY, DISPLAY_RADIUS,     COL_RING);
-    spr.drawCircle(DISPLAY_CX, DISPLAY_CY, DISPLAY_RADIUS - 1, COL_RING);
-
-    // "SYSTEM" label
-    spr.setTextDatum(MC_DATUM);
-    spr.setTextFont(2);
-    spr.setTextColor(COL_LABEL, bgColour);
-    spr.drawString("SYSTEM", DISPLAY_CX, 72);
-
-    // Separator line under label
-    spr.drawFastHLine(DISPLAY_CX - 55, 88, 110, COL_LABEL);
-
-    // Large system letter (A or B)
-    spr.setTextFont(8);   // large built-in font
-    spr.setTextColor(letterColour, bgColour);
-    spr.setTextDatum(MC_DATUM);
-    spr.drawString(letter, DISPLAY_CX, 138);
-
-    // Separator line above ACTIVE
-    spr.drawFastHLine(DISPLAY_CX - 55, 164, 110, COL_LABEL);
-
-    // "ACTIVE" label
-    spr.setTextFont(2);
-    spr.setTextColor(COL_LABEL, bgColour);
-    spr.drawString("ACTIVE", DISPLAY_CX, 180);
-
-    spr.pushSprite(0, 0);
-    spr.deleteSprite();
+                             uint16_t bgColour,
+                             uint16_t letterColour,
+                             const char *letter) {
+  tft.fillScreen(bgColour);
+
+  // Outer ring
+  tft.drawCircle(DISPLAY_CX, DISPLAY_CY, DISPLAY_RADIUS, COL_RING);
+  tft.drawCircle(DISPLAY_CX, DISPLAY_CY, DISPLAY_RADIUS - 1, COL_RING);
+  tft.drawCircle(DISPLAY_CX, DISPLAY_CY, DISPLAY_RADIUS - 2, COL_RING);
+  tft.drawCircle(DISPLAY_CX, DISPLAY_CY, DISPLAY_RADIUS - 3, COL_RING);
+
+  // "SYSTEM" label
+  tft.setTextDatum(MC_DATUM);
+  tft.setTextFont(4);
+  tft.setTextSize(1);
+  tft.setTextColor(COL_LABEL, bgColour);
+  tft.drawString("SYSTEM", DISPLAY_CX, 72);
+
+  // Separator line under label
+  tft.drawFastHLine(DISPLAY_CX - 55, 88, 110, COL_LABEL);
+
+  // Large system letter (A or B)
+  tft.setTextFont(4);
+  tft.setTextSize(3);
+  tft.setTextColor(letterColour, bgColour);
+  tft.setTextDatum(MC_DATUM);
+  tft.drawString(letter, DISPLAY_CX, 135);
+
+  // Separator line above ACTIVE
+  tft.drawFastHLine(DISPLAY_CX - 55, 164, 110, COL_LABEL);
+
+  // "ACTIVE" label
+  tft.setTextFont(4);
+  tft.setTextSize(1);
+  tft.setTextColor(COL_LABEL, bgColour);
+  tft.drawString("ACTIVE", DISPLAY_CX, 185);
 }
 
 // ---------------------------------------------------------------------------
 // Public API
 // ---------------------------------------------------------------------------
 
+void displayShowSplash(TFT_eSPI &tft) {
+  tft.fillScreen(TFT_BLACK);
+  tft.setTextDatum(MC_DATUM);
+
+  // "MMC" — large
+  tft.setTextFont(4);
+  tft.setTextSize(1);
+  tft.setTextColor(TFT_WHITE, TFT_BLACK);
+  tft.drawString("MMC", DISPLAY_CX, 80);
+
+  // "SCOTTSDALE"
+  tft.drawString("SCOTTSDALE", DISPLAY_CX, 120);
+
+  // "2026"
+  tft.drawString("2026", DISPLAY_CX, 160);
+
+  tft.setTextSize(1);
+  delay(SPLASH_MS);
+}
+
 void displayInit(TFT_eSPI &tft) {
-    tft.init();
-    tft.setRotation(0);
-    tft.fillScreen(TFT_BLACK);
+  tft.init();
+  tft.setRotation(0);
+  tft.fillScreen(TFT_BLACK);
 }
 
 void displayShowLogo(TFT_eSPI &tft) {
-    drawLogo(tft);
+  drawLogo(tft);
+}
+
+void displayBlank(TFT_eSPI &tft) {
+  tft.fillScreen(TFT_BLACK);
 }
 
 void displayShowSystemA(TFT_eSPI &tft) {
-    drawStatusScreen(tft, COL_BG_SYS_A, COL_LETTER_A, "A");
+  drawStatusScreen(tft, COL_BG_SYS_A, COL_LETTER_A, "A");
 }
 
 void displayShowSystemB(TFT_eSPI &tft) {
-    drawStatusScreen(tft, COL_BG_SYS_B, COL_LETTER_B, "B");
+  drawStatusScreen(tft, COL_BG_SYS_B, COL_LETTER_B, "B");
 }

+ 2 - 0
main/display.h

@@ -3,5 +3,7 @@
 
 void displayInit(TFT_eSPI &tft);
 void displayShowLogo(TFT_eSPI &tft);
+void displayShowSplash(TFT_eSPI &tft);
 void displayShowSystemA(TFT_eSPI &tft);
 void displayShowSystemB(TFT_eSPI &tft);
+void displayBlank(TFT_eSPI &tft);

+ 30 - 13
main/logo.h

@@ -6,19 +6,26 @@
 // Logo polygon data — design canvas 107 × 125 units
 // ---------------------------------------------------------------------------
 static const int kGreenPts[][2] = {
-    {19, 92}, {19, 39}, {54, 66}, {107, 24},
-    {107,  1}, {54, 42}, {  1,  2}, {  1, 107}
+    { 20,  92},   // left leg inner, mid-height
+    { 20,  39},   // left leg inner, upper
+    { 54,  66},   // centre V, bottom
+    {107,  24},   // right leg, upper
+    {107,   1},   // right leg, top
+    { 54,  42},   // centre V, top peak
+    {  1,   2},   // left leg, top-left
+    {  1, 107},   // left leg, bottom-left
+                  // Z closes back to {20, 92}
 };
 
 static const int kBluePts[][2] = {
-    {  1, 125}, {31, 125}, {89,  79}, { 89, 107},
-    { 76, 107}, {76, 125}, {107, 125}, {107,  42}
+    {  1, 125}, { 31, 125}, { 89,  79}, { 89, 107},
+    { 76, 107}, { 76, 125}, {107, 125}, {107,  42}
 };
 
 static constexpr int   LOGO_N      = 8;
-static constexpr float LOGO_SCALE  = 1.6f;
-static constexpr int   LOGO_X_OFF  = 34;
-static constexpr int   LOGO_Y_OFF  = 20;
+static constexpr float LOGO_SCALE  = 1.2f;
+static constexpr int   LOGO_X_OFF  = 55;
+static constexpr int   LOGO_Y_OFF  = 45;
 
 static inline int lsx(int x) { return LOGO_X_OFF + (int)(x * LOGO_SCALE); }
 static inline int lsy(int y) { return LOGO_Y_OFF + (int)(y * LOGO_SCALE); }
@@ -43,6 +50,13 @@ static inline uint16_t logoBlueAt(float t) {
 // ---------------------------------------------------------------------------
 // Scanline polygon fill — operates in screen coordinates
 // ---------------------------------------------------------------------------
+static void outlinePolygon(TFT_eSPI &tft, int pts[][2], int n, uint16_t colour) {
+    for (int i = 0; i < n; i++) {
+        int j = (i + 1) % n;
+        tft.drawLine(pts[i][0], pts[i][1], pts[j][0], pts[j][1], colour);
+    }
+}
+
 static void fillPolygon(TFT_eSPI &tft, int pts[][2], int n, uint16_t colour) {
     int yMin = pts[0][1], yMax = pts[0][1];
     for (int i = 1; i < n; i++) {
@@ -50,16 +64,19 @@ static void fillPolygon(TFT_eSPI &tft, int pts[][2], int n, uint16_t colour) {
         if (pts[i][1] > yMax) yMax = pts[i][1];
     }
 
-    int nodeX[16]; // 8-point polygon produces at most 8 intersections per line
+    int nodeX[16];
 
     for (int y = yMin; y <= yMax; y++) {
         int nodes = 0;
         int j = n - 1;
         for (int i = 0; i < n; i++) {
             int yi = pts[i][1], yj = pts[j][1];
-            if ((yi < y && yj >= y) || (yj < y && yi >= y)) {
-                int dy = yj - yi;
-                nodeX[nodes++] = pts[i][0] + (y - yi) * (pts[j][0] - pts[i][0]) / dy;
+
+            // Key fix: use strictly-one-side rule to avoid double-counting vertices
+            if ((yi <= y && yj > y) || (yj <= y && yi > y)) {
+                long num = (long)(y - yi) * (pts[j][0] - pts[i][0]);
+                long dy  = yj - yi;
+                nodeX[nodes++] = pts[i][0] + (int)(num / dy);
             }
             j = i;
         }
@@ -105,13 +122,13 @@ inline void drawLogo(TFT_eSPI &tft) {
 
     // Phase 1: green fade-in
     for (int i = 0; i <= LOGO_FADE_STEPS; i++) {
-        fillPolygon(tft, sGreen, LOGO_N, logoGreenAt(i / (float)LOGO_FADE_STEPS));
+        outlinePolygon(tft, sGreen, LOGO_N, logoGreenAt(i / (float)LOGO_FADE_STEPS));
         delay(LOGO_FADE_STEP_MS);
     }
 
     // Phase 2: blue fade-in (green redrawn solid to prevent bleed-through)
     for (int i = 0; i <= LOGO_FADE_STEPS; i++) {
-        fillPolygon(tft, sGreen, LOGO_N, logoGreenAt(1.0f));
+        outlinePolygon(tft, sGreen, LOGO_N, logoGreenAt(1.0f));
         fillPolygon(tft, sBlue,  LOGO_N, logoBlueAt(i / (float)LOGO_FADE_STEPS));
         delay(LOGO_FADE_STEP_MS);
     }

+ 19 - 9
main/main.ino

@@ -4,31 +4,31 @@
 
 static TFT_eSPI tft = TFT_eSPI();
 static bool lastSelectState = false;
+static bool screenBlanked = false;
+static unsigned long sysAStartMs = 0;
 
 // ---------------------------------------------------------------------------
 // Show the correct screen for the current input state
 // ---------------------------------------------------------------------------
 static void applyState(bool sysSelect) {
-    // HIGH = System A
-    // LOW  = System B
+    screenBlanked = false;
     if (sysSelect) {
+        // System A — show screen and start the blank timer
         displayShowSystemA(tft);
+        sysAStartMs = millis();
     } else {
+        // System B — show screen, no blank timer
         displayShowSystemB(tft);
     }
 }
 
 void setup() {
-    // System select input — internal pull-up so the pin has a defined level
-    // when nothing is connected (defaults to HIGH = System A)
+    // Internal pull-up: pin floating defaults to HIGH = System A
     pinMode(PIN_SYS_SELECT, INPUT_PULLUP);
 
-    // Backlight on
-    pinMode(PIN_TFT_BL, OUTPUT);
-    digitalWrite(PIN_TFT_BL, HIGH);
-
     displayInit(tft);
-    displayShowLogo(tft); // animation handles its own 10 s timing
+    displayShowLogo(tft);
+    displayShowSplash(tft);
 
     lastSelectState = digitalRead(PIN_SYS_SELECT);
     applyState(lastSelectState);
@@ -36,9 +36,19 @@ void setup() {
 
 void loop() {
     bool state = digitalRead(PIN_SYS_SELECT);
+
     if (state != lastSelectState) {
         lastSelectState = state;
         applyState(state);
     }
+
+    // Blank screen after timeout when sitting on System A
+    if (lastSelectState && !screenBlanked) {
+        if (millis() - sysAStartMs >= BLANK_TIMEOUT_MS) {
+            displayBlank(tft);
+            screenBlanked = true;
+        }
+    }
+
     delay(POLL_INTERVAL_MS);
 }