# Conflict of TFT display and SD card SPI communication

**URL:** <https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969>\
**Category:** Nano ESP32\
**Created:** [June 27, 2024, 7:04am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969 "2024-06-27T07:04:32Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![kavindaelzian](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/kavindaelzian/32/974938_2.png) [@kavindaelzian](https://forum.arduino.cc/u/kavindaelzian)\
**Post date:** [June 27, 2024, 7:04am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/1 "2024-06-27T07:04:32Z")

</div>

This code sensor data display and store to SD card. below code is working. but there is problem. after uncomment the if condition inside the go\_to\_menu\_mode() function. It shows SD card attached but Error opening file in Serial monitor. Is it possible? What is the reason for that?

```arduino
#include <SD.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include <HardwareSerial.h>
#include "logo.h"

#define RE 2
#define DE 2
#define RO 13 // Modbus -> RX on GPIO13
#define DI 15 // Modbus -> TX on GPIO15
#define EN 25
#define PWR_EN 12
#define CS_SD 5 // Chip select pin for SD card
#define CS_TFT 4 // Chip select pin for display
#define BTN_1 32 // S2
#define BACK_BTN 33 // S3
#define DWN_BTN 35 // S4
#define OK_BTN 34 // S5
#define WIFI_BTN 39 // S6
#define PWR_AND_UP_BTN 36 // S7

#define powerOnTimeOut 1000
#define powerOffTimeOut 2000

bool btnPressed = 0;
unsigned long currentMillis = 0;
int btnDebounceDelay = 200;

#define TFT_GRAY 0x5AEB // Define background colour

TFT_eSPI tft = TFT_eSPI();

HardwareSerial modSerial(2); // Use UART2 which allows you to set custom pins to serial

// Define mod addresses
const byte nitro[] = { 0x01, 0x03, 0x00, 0x1e, 0x00, 0x01, 0xe4, 0x0c };
const byte phos[] = { 0x01, 0x03, 0x00, 0x1f, 0x00, 0x01, 0xb5, 0xcc };
const byte pota[] = { 0x01, 0x03, 0x00, 0x20, 0x00, 0x01, 0x85, 0xc0 };
const byte soil_ph[] = { 0x01, 0x03, 0x00, 0x06, 0x00, 0x01, 0x64, 0x0b };
const byte soil_moist[] = { 0x01, 0x03, 0x00, 0x12, 0x00, 0x01, 0x24, 0x0f };
const byte elec_conduct[] = { 0x01, 0x03, 0x00, 0x15, 0x00, 0x01, 0x95, 0xce };
const byte soil_temp[] = { 0x01, 0x03, 0x00, 0x13, 0x00, 0x01, 0x75, 0xcf };

byte values[11];

void setup() {
  pinMode(RE, OUTPUT);
  pinMode(DE, OUTPUT);
  pinMode(EN, OUTPUT);
  pinMode(PWR_AND_UP_BTN, INPUT);
  pinMode(BTN_1, INPUT);
  pinMode(BACK_BTN, INPUT);
  pinMode(DWN_BTN, INPUT);
  pinMode(OK_BTN, INPUT);
  pinMode(WIFI_BTN, INPUT);
  pinMode(PWR_EN, OUTPUT);
  pinMode(CS_TFT, OUTPUT);
  pinMode(CS_SD, OUTPUT);

  digitalWrite(EN, HIGH);
  digitalWrite(CS_TFT, HIGH);
  digitalWrite(CS_SD, HIGH);

  delay(powerOnTimeOut);
  digitalWrite(PWR_EN, HIGH);
  while (digitalRead(PWR_AND_UP_BTN))
    ;

  Serial.begin(115200);
  modSerial.begin(9600, SERIAL_8N1, RO, DI);

  SPI.begin();

  // Initialize TFT
  digitalWrite(CS_TFT, LOW);
  delay(100);
  tft.init();
  tft.setRotation(3);

  tft.fillScreen(TFT_WHITE);
  tft.setSwapBytes(true);
  tft.pushImage(60, 0, 200, 200, logo);
  delay(200);

  tft.setCursor(50, 220, 4);
  tft.setTextColor(TFT_YELLOW);
  tft.setFreeFont(&FreeSansBold12pt7b);
  auto_typing_text("ELZIAN AGRO EYE");
  tft.setFreeFont(NULL);
  delay(1000);
  digitalWrite(CS_TFT, HIGH);
  delay(100);

  // Initialize SD caed
  digitalWrite(CS_SD, LOW);
  delay(100);
  while (!SD.begin(CS_SD)) {
    Serial.println("SD card mount failed, retrying...");
    delay(1000);
  }
  digitalWrite(CS_SD, HIGH);
  delay(100);
}

void loop() {
  go_to_menu_modes();
}

// Handle menu
void go_to_menu_modes() {
  String menuOptions[] = { "Measuring", "Records", "Upload to Internet", "Wifi Settings" }; // Can add more options
  int currentMode = 0;
  int maxModes = sizeof(menuOptions) / sizeof(menuOptions[0]);

  while (true) {
    digitalWrite(CS_TFT, LOW);
    delay(100);

    clear_screen();
    print_header();

    tft.setCursor(130, 40, 4);
    tft.setTextColor(TFT_GREEN);
    tft.setFreeFont(&FreeSansBold12pt7b);
    tft.println("Menu");
    tft.setFreeFont(NULL);

    tft.setTextColor(TFT_BLUE);
    tft.setTextFont(4);

    // for (int i = 0; i < maxModes; i++) {
    // if (i == currentMode) {
    // tft.setTextColor(TFT_YELLOW); // Highlight current mode
    // } else {
    // tft.setTextColor(TFT_BLUE);
    // }
    // tft.println(" " + String(i + 1) + ". " + menuOptions[i]);
    // }

    digitalWrite(CS_TFT, HIGH);
    delay(100);

    digitalWrite(CS_SD, LOW);
    delay(100);

    uint8_t cardType = SD.cardType();
    if (cardType == CARD_NONE) {
      Serial.println("No SD card attached");
    } else {
      Serial.println("SD card attached");
      if (SD.exists("/data.txt")) {
        Serial.println("File exists");
      } else {
        Serial.println("File does not exist, creating file...");
      }
      File file = SD.open("/data.txt", FILE_WRITE);
      if (file) {
        file.println("kavinda");
        file.close();
        Serial.println("data written to file");
      } else {
        Serial.println("Error opening file for writing");
      }
    }

    digitalWrite(CS_SD, HIGH);
    delay(100);

    delay(2000);
  }
}

```

---

<div class="post-metadata">

**Author:** ![PaulRB](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/paulrb/32/216330_2.png) [@PaulRB](https://forum.arduino.cc/u/PaulRB)\
**Post date:** [June 27, 2024, 7:13am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/2 "2024-06-27T07:13:17Z")

</div>

Is the SD card full? Faulty? Have you checked it in PC/laptop?

---

<div class="post-metadata">

**Author:** ![david\_2018](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/david_2018/32/1013044_2.png) [@david\_2018](https://forum.arduino.cc/u/david_2018)\
**Post date:** [June 27, 2024, 8:18am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/3 "2024-06-27T08:18:27Z")

</div>

Is this the full code? This seems like a problem where you are writing outside the bounds of an array somewhere else in the code, since there is no obvious problem with the if statement that is commented out.

---

<div class="post-metadata">

**Author:** ![SurferTim](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/surfertim/32/1072341_2.png) [@SurferTim](https://forum.arduino.cc/u/SurferTim)\
**Post date:** [June 27, 2024, 8:38am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/4 "2024-06-27T08:38:42Z")

</div>

The respective libraries take care of the CS\_SD and CS\_TFT lines. Only need to be set HIGH in setup.

Try the filename without the leading slash.

---

<div class="post-metadata">

**Author:** ![kavindaelzian](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/kavindaelzian/32/974938_2.png) [@kavindaelzian](https://forum.arduino.cc/u/kavindaelzian)\
**Post date:** [June 27, 2024, 9:09am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/5 "2024-06-27T09:09:47Z")

</div>

Yes, There is no any data in SD card.

---

<div class="post-metadata">

**Author:** ![SurferTim](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/surfertim/32/1072341_2.png) [@SurferTim](https://forum.arduino.cc/u/SurferTim)\
**Post date:** [June 27, 2024, 9:12am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/6 "2024-06-27T09:12:52Z")

</div>

What type SD card is it? How big? Format?

---

<div class="post-metadata">

**Author:** ![kavindaelzian](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/kavindaelzian/32/974938_2.png) [@kavindaelzian](https://forum.arduino.cc/u/kavindaelzian)\
**Post date:** [June 27, 2024, 10:19am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/7 "2024-06-27T10:19:45Z")

</div>

It is not full code. I will send it by another sample code. In here also same issue. after uncomment the if condition, It show "Error opening file".

```arduino
#include <SD.h>
#include <SPI.h>
#include <TFT_eSPI.h>

#define CS_SD 5
#define CS_TFT 4

TFT_eSPI tft = TFT_eSPI();

void setup() {
  pinMode(CS_TFT, OUTPUT);
  pinMode(CS_SD, OUTPUT);

  digitalWrite(CS_TFT, HIGH);
  digitalWrite(CS_SD, HIGH);

  Serial.begin(115200);

  SPI.begin();

  // Initialize TFT
  digitalWrite(CS_TFT, LOW);
  delay(50);
  tft.init();
  tft.setRotation(3);
  digitalWrite(CS_TFT, HIGH);
  delay(50);

  // Initialize SD card
  digitalWrite(CS_SD, LOW);
  delay(50);
  while (!SD.begin(CS_SD)) {
    Serial.println("SD card mount failed, retrying...");
    delay(1000); // Wait for 1 second before retrying
  }
  digitalWrite(CS_SD, HIGH);
  delay(50);
}

void loop() {
  digitalWrite(CS_TFT, LOW);
  delay(50);
  tft.fillScreen(TFT_GREEN);
  delay(1000);

  // if (true) {
  // tft.setCursor(130, 40, 4);
  // tft.setTextColor(TFT_GREEN);
  // tft.println("Menu");
  // }

  tft.fillScreen(TFT_BLUE);
  digitalWrite(CS_TFT, HIGH);
  delay(50);

  digitalWrite(CS_SD, LOW);
  delay(50);

  uint8_t cardType = SD.cardType();
  if (cardType == CARD_NONE) {
    Serial.println("No SD card attached");
  } else {
    Serial.println("SD card attached");
    File file = SD.open("/data.txt", FILE_APPEND);
    if (file) {
      file.print("kavinda");
      file.print(", ");
      file.println("shammi");
      file.close();
      Serial.println("data written to file");
    } else {
      Serial.println("Error opening file for writing");
    }
  }

  digitalWrite(CS_SD, HIGH);
  delay(50);

  delay(2000);
}

```

---

<div class="post-metadata">

**Author:** ![kavindaelzian](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/kavindaelzian/32/974938_2.png) [@kavindaelzian](https://forum.arduino.cc/u/kavindaelzian)\
**Post date:** [June 27, 2024, 10:20am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/8 "2024-06-27T10:20:51Z")

</div>

It is empty card

---

<div class="post-metadata">

**Author:** ![SurferTim](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/surfertim/32/1072341_2.png) [@SurferTim](https://forum.arduino.cc/u/SurferTim)\
**Post date:** [June 27, 2024, 10:32am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/9 "2024-06-27T10:32:38Z")

</div>

OK, empty.  
Size? 32 GB or smaller?  
Format? FAT16 or FAT32?

---

<div class="post-metadata">

**Author:** ![kavindaelzian](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/kavindaelzian/32/974938_2.png) [@kavindaelzian](https://forum.arduino.cc/u/kavindaelzian)\
**Post date:** [June 27, 2024, 10:25am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/10 "2024-06-27T10:25:48Z")

</div>

This code working properly. But there is a problem. After uncomment the if condition in line 46, the serial monitor show "Error opening file". What is this happen.

```arduino
#include <SD.h>
#include <SPI.h>
#include <TFT_eSPI.h>

#define CS_SD 5
#define CS_TFT 4

TFT_eSPI tft = TFT_eSPI();

void setup() {
  pinMode(CS_TFT, OUTPUT);
  pinMode(CS_SD, OUTPUT);

  digitalWrite(CS_TFT, HIGH);
  digitalWrite(CS_SD, HIGH);

  Serial.begin(115200);

  SPI.begin();

  // Initialize TFT
  digitalWrite(CS_TFT, LOW);
  delay(50);
  tft.init();
  tft.setRotation(3);
  digitalWrite(CS_TFT, HIGH);
  delay(50);

  // Initialize SD card
  digitalWrite(CS_SD, LOW);
  delay(50);
  while (!SD.begin(CS_SD)) {
    Serial.println("SD card mount failed, retrying...");
    delay(1000); // Wait for 1 second before retrying
  }
  digitalWrite(CS_SD, HIGH);
  delay(50);
}

void loop() {
  digitalWrite(CS_TFT, LOW);
  delay(50);
  tft.fillScreen(TFT_GREEN);
  delay(1000);

  // if (true) {
  // tft.setCursor(130, 40, 4);
  // tft.setTextColor(TFT_GREEN);
  // tft.println("Menu");
  // }

  tft.fillScreen(TFT_BLUE);
  digitalWrite(CS_TFT, HIGH);
  delay(50);

  digitalWrite(CS_SD, LOW);
  delay(50);

  uint8_t cardType = SD.cardType();
  if (cardType == CARD_NONE) {
    Serial.println("No SD card attached");
  } else {
    Serial.println("SD card attached");
    File file = SD.open("/data.txt", FILE_APPEND);
    if (file) {
      file.print("kavinda");
      file.print(", ");
      file.println("shammi");
      file.close();
      Serial.println("data written to file");
    } else {
      Serial.println("Error opening file for writing");
    }
  }

  digitalWrite(CS_SD, HIGH);
  delay(50);

  delay(2000);
}

```

---

<div class="post-metadata">

**Author:** ![J-M-L](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/j-m-l/32/766245_2.png) [@J-M-L](https://forum.arduino.cc/u/J-M-L)\
**Post date:** [June 27, 2024, 11:20am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/11 "2024-06-27T11:20:53Z")

</div>

the SD library manages the Chip Select (CS) pin for the SD card ➜ try not messing around yourself with the pin state. (you don't even need to set the CS pin as an OUTPUT).

---

<div class="post-metadata">

**Author:** ![SurferTim](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/surfertim/32/1072341_2.png) [@SurferTim](https://forum.arduino.cc/u/SurferTim)\
**Post date:** [June 27, 2024, 11:25am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/12 "2024-06-27T11:25:50Z")

</div>

If there is more than one SPI device on the bus. I suggest at least setting all SPI CS pins as INPUT\_PULLUP. I use OUTPUT and HIGH.

---

<div class="post-metadata">

**Author:** ![pert](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/pert/32/217111_2.png) [@pert](https://forum.arduino.cc/u/pert)\
**Post date:** [June 27, 2024, 11:38am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/13 "2024-06-27T11:38:36Z")

</div>

I have merged your cross-posts @kavindaelzian.

Cross-posting is against the Arduino forum [rules](https://forum.arduino.cc/faq#keep-tidy). The reason is that duplicate posts can waste the time of the people trying to help. Someone might spend a lot of time investigating and writing a detailed answer on one topic, without knowing that someone else already did the same in the other topic.

Repeated cross-posting can result in a suspension from the forum.

In the future, please only create one topic for each distinct subject matter. This is basic forum etiquette, as explained in [the " **How to get the best out of this forum**" guide](https://forum.arduino.cc/t/how-to-get-the-best-out-of-this-forum/679966). It contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

---

<div class="post-metadata">

**Author:** ![david\_2018](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/david_2018/32/1013044_2.png) [@david\_2018](https://forum.arduino.cc/u/david_2018)\
**Post date:** [June 27, 2024, 11:54am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/14 "2024-06-27T11:54:49Z")

</div>

Can you identify if there is a particular line within the if statement that is causing the problem?

In the User\_Setup.h file of the TFT\_eSPI library, is the line #define USE\_HSPI\_PORT commented out? There seem to be a lot of issues about SD card usage involving that on the TFT\_eSPI github page.

---

<div class="post-metadata">

**Author:** ![sudogrep](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/sudogrep/32/1126423_2.png) [@sudogrep](https://forum.arduino.cc/u/sudogrep)\
**Post date:** [March 27, 2026, 11:03pm UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/15 "2026-03-27T23:03:53Z")

</div>

# Forum Post Draft — Adafruit Forums

**Title:** SOLVED: ESP32-S3 Reverse TFT Feather + Adalogger FeatherWing — SD card writes fail silently (hardware fix)

**Category:** Adafruit Products / Feather

* * *

## The Problem

If you stack an **Adalogger FeatherWing** (RTC + SD) on an **ESP32-S3 Reverse TFT Feather** (or the w.FL Antenna variant), SD card writes fail silently on Arduino core 3.x. Reads may work, but FILE\_WRITE and FILE\_APPEND produce zero-byte files or fail entirely. No error is reported — SD.begin() returns true, SD.open() returns a valid File object, but nothing is written.

The TFT display works fine. Sensors work fine. Everything looks normal except your data isn't being saved.

## Root Cause

The ESP32-S3 Reverse TFT has its ST7789 TFT display **hardwired internally** to GPIO 35/36/37 (MOSI/SCK/MISO). These same GPIOs are exposed on the Feather stacking headers. When you stack the Adalogger, its SD card connects to the same pins through the headers.

On ESP32 Arduino core 2.0.x, both devices could share the SPI bus with CS arbitration. On **core 3.x** , something changed in the SPI peripheral GPIO routing that breaks this. The TFT driver takes ownership of the SPI GPIO routing in a way that prevents SD writes, even with proper CS management.

## What Does NOT Work (save yourself days of debugging)

I tried all of these. None of them fix the problem:

- **Shared SPI with CS arbitration** (deassert TFT\_CS before SD access) — FAILS
- **Separate HSPI peripheral on the same physical pins** (GPIO 35/36/37) — FAILS
- **sdReinit/sdRelease cycling** (tear down and rebuild HSPI before/after each SD access) — SD works but TFT display hangs
- **Init order changes** (SD.begin before tft.init, or vice versa) — FAILS
- **Lowering SPI clock speed** — FAILS

The problem is hardware-level GPIO routing, not software timing or CS management.

## The Fix: Hardware Mod (10 minutes, fully reversible)

Give the SD card its own SPI pins that the TFT doesn't touch. The ESP32-S3 GPIO matrix allows HSPI to be assigned to any available pins.

### What You Need

- 3 short jumper wires (any thin insulated wire)
- Soldering iron
- Pliers (to pull/bend header pins)

### Steps

1. **Unstack** the Adalogger from the ESP32-S3.

2. **Disconnect three SPI pins.** On the ESP32-S3 board, bend or pull the male header pins for **MO, MI, and SCK** so they no longer enter the Adalogger's female sockets when stacked. These pins must NOT make contact. (This is reversible — straighten them back anytime.)

3. **Solder three jumper wires on the Adalogger's top nubs:**

```arduino
Adalogger MO top nub --wire--> Adalogger A4 top nub (routes to ESP32 GPIO 14)
Adalogger MI top nub --wire--> Adalogger A5 top nub (routes to ESP32 GPIO 8)
Adalogger SCK top nub --wire--> Adalogger A3 top nub (routes to ESP32 GPIO 15)

```

1. **Restack** the boards. CS (GPIO 10), RTC (I2C), power, and GND all pass through the stacking headers normally.

### Important: Pin Mapping Warning

On the ESP32-S3 Reverse TFT Feather, the Arduino analog pin labels do **NOT** match GPIO numbers:

| Board Label | Actual GPIO |
| --- | --- |
| A0 | GPIO 18 |
| A1 | GPIO 17 |
| A2 | GPIO 16 |
| A3 | GPIO 15 |
| A4 | GPIO 14 |
| A5 | GPIO 8 |

This is defined in `pins_arduino.h` for the `adafruit_feather_esp32s3_reversetft` variant. Always use GPIO numbers in code, never the A0-A5 aliases.

## Working Code

```cpp
#include <SPI.h>
#include <SD.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>

// SD card on HSPI — separate bus from TFT
#define SD_CS 10
#define SD_SCK 15 // A3 = GPIO 15
#define SD_MOSI 14 // A4 = GPIO 14
#define SD_MISO 8 // A5 = GPIO 8

SPIClass sdSPI(HSPI);
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  Serial.begin(115200);
  delay(2000);

  // Power up I2C + TFT
  pinMode(TFT_I2C_POWER, OUTPUT);
  digitalWrite(TFT_I2C_POWER, HIGH);
  delay(100);
  pinMode(TFT_BACKLITE, OUTPUT);
  digitalWrite(TFT_BACKLITE, HIGH);

  // TFT on default FSPI (internal GPIO 35/36/37)
  tft.init(135, 240);
  tft.setRotation(3);
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_WHITE);
  tft.setCursor(0, 10);
  tft.println("SD+TFT Test");
  Serial.println("TFT init OK");

  // SD on HSPI (rewired GPIO 14/8/15) — completely separate bus
  sdSPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, sdSPI, 4000000)) {
    Serial.println("SD FAIL");
    tft.setTextColor(ST77XX_RED);
    tft.println("SD FAIL");
    while(1);
  }
  Serial.println("SD OK");

  // Write test
  File f = SD.open("/test.txt", FILE_WRITE); // NOTE: "/" prefix required!
  if (f) { f.println("Hello from SD!"); f.close(); Serial.println("WRITE OK"); }

  // Append test
  File f2 = SD.open("/test.txt", FILE_APPEND);
  if (f2) { f2.println("Append works too"); f2.close(); Serial.println("APPEND OK"); }

  // TFT update THEN SD write — the sequence that always failed before
  tft.fillScreen(ST77XX_BLACK);
  tft.setCursor(0, 10);
  tft.setTextColor(ST77XX_YELLOW);
  tft.println("Post-TFT write...");

  File f3 = SD.open("/test.txt", FILE_APPEND);
  if (f3) { f3.println("After TFT update"); f3.close(); Serial.println("POST-TFT WRITE OK"); }

  // Read back
  File f4 = SD.open("/test.txt", FILE_READ);
  if (f4) { while (f4.available()) Serial.write(f4.read()); f4.close(); }

  SD.remove("/test.txt");

  tft.fillScreen(ST77XX_BLACK);
  tft.setCursor(0, 10);
  tft.setTextColor(ST77XX_GREEN);
  tft.setTextSize(3);
  tft.println("ALL OK!");
}

void loop() {
  // TFT keeps updating — proves both buses work simultaneously
  static unsigned long last = 0;
  if (millis() - last > 1000) {
    last = millis();
    tft.setCursor(0, 60);
    tft.setTextSize(2);
    tft.setTextColor(ST77XX_CYAN, ST77XX_BLACK);
    tft.print("Up: "); tft.print(millis()/1000); tft.println("s ");
  }
}

```

## Additional ESP32 SD Gotchas

A few more things that bit me and aren't well documented:

1. **"/" prefix required for file creation.** `SD.open("test.txt", FILE_WRITE)` silently fails. `SD.open("/test.txt", FILE_WRITE)` works. This is ESP32-specific — other Arduino platforms don't require it.

2. **FILE\_WRITE truncates on ESP32.** Unlike RP2040/AVR where FILE\_WRITE appends, on ESP32 it truncates the file to zero and starts fresh. Use `FILE_APPEND` to add data to existing files.

3. **FILE\_APPEND cannot create new files.** If the file doesn't exist, FILE\_APPEND fails silently. Create with FILE\_WRITE first, then use FILE\_APPEND for subsequent writes.

4. **Delete Arduino build cache after changing pin definitions.** The IDE caches compiled headers. If you change `#define` values in a .h file, the old values may persist. Delete `C:\Users\<you>\AppData\Local\Temp\arduino\sketches\` or use Sketch \> Clean Build Folder.

## Environment

- Adafruit ESP32-S3 Reverse TFT Feather (w.FL Antenna variant, product #6303)
- Adalogger FeatherWing (product #2922)
- Arduino IDE 2.x
- esp32 by Espressif board support package v3.3.7
- Board selected: "Adafruit Feather ESP32-S3 Reverse TFT"

Hope this saves someone else a few days of debugging. Happy to answer questions.

---

<div class="post-metadata">

**Author:** ![srnet](https://dub1.discourse-cdn.com/arduino/user_avatar/forum.arduino.cc/srnet/32/238868_2.png) [@srnet](https://forum.arduino.cc/u/srnet)\
**Post date:** [March 28, 2026, 7:54am UTC](https://forum.arduino.cc/t/conflict-of-tft-display-and-sd-card-spi-communication/1275969/16 "2026-03-28T07:54:52Z")

</div>

Some SD cards are just not happy sharing the SPI bus.

On the ESP32S3 you can drive the SD card in MMC mode, that only needs 3 GPIOs.
