If you need a reliable, full-color display for an Arduino project, buy a 2.8-inch 240x320 ILI9341 SPI TFT module. While the market is flooded with cheap parallel-interface shields and tiny I2C OLEDs, the ILI9341 SPI variant hits the exact sweet spot for hobbyists and prototypers: it requires only 5 logic pins, updates fast enough for smooth UI animations, and has rock-solid library support via Adafruit_GFX.

This guide cuts through the datasheet noise. We will cover the exact hardware you need, the mandatory 5V-to-3.3V logic shifting that most tutorials ignore (and why skipping it will fry your display), complete compilable code with hardware fault detection, and a decision tree for when things inevitably go wrong on the bench.

The Verdict: Which Arduino LCD TFT Should You Buy?

Not all TFTs are created equal. The controller chip on the back of the glass dictates your wiring complexity, refresh rate, and library support. Use this decision matrix to select the right module for your specific constraints.

Controller / InterfaceResolutionPins RequiredRefresh SpeedBest Use Case
ILI9341 (SPI)240x3205 (CS, DC, MOSI, SCK, RST)High (~30fps)Default Pick: Sensor dashboards, UI menus, general prototyping.
ST7789 (SPI)240x240 or 135x2404 or 5HighWearables, compact enclosures where 2.8" is too large.
ILI9488 (8-bit Parallel)320x48012+Low (CPU bottleneck)High-res static images where pin count is not an issue.
MCUFRIEND (Uno Shield)240x320All digital pinsMediumQuick plug-and-play testing, but blocks all Uno I/O for sensors.
Bench Tip: Avoid the "MCUFRIEND" shields for any project that also requires an SD card, I2C sensors, or serial communication. They hijack the Uno's hardware SPI and I2C pins, forcing you into painful software-emulation workarounds. Stick to the standalone SPI ILI9341 module.

Parts List and Pin Mapping for the ILI9341 SPI Build

The most common point of failure in Arduino LCD TFT builds is ignoring voltage domains. The Arduino Uno R3 operates at 5V logic. The ILI9341 controller is strictly a 3.3V device. Feeding 5V into the TFT's MISO or DC pins will degrade the silicon and eventually result in a dead display. You must use a logic level converter.

Exact Parts List

  • Microcontroller: Arduino Uno R3 (ATmega328P variant) or genuine Arduino Uno R4 Minima.
  • Display: 2.8" ILI9341 SPI TFT LCD Module (Generic or Adafruit Product ID 1651). Expect to pay $12-$18.
  • Logic Shifter: 4-channel BSS138 bidirectional logic level converter (SparkFun BOB-12009 or generic equivalent). Do not use resistor dividers; they ruin SPI signal rise times at high clock speeds.
  • Power: Dedicated 3.3V LDO regulator (like the AMS1117-3.3) if your module lacks one, though most red generic boards include a 3.3V LDO for the backlight and VCC.
  • Wiring: 22 AWG solid core jumper wires, half-size breadboard.

Spec-Sheet Pin Mapping

Wire the display through the BSS138 level shifter as follows. The level shifter has a low-voltage side (LV) connected to 3.3V, and a high-voltage side (HV) connected to 5V.

Arduino Uno R3 PinLevel Shifter HVLevel Shifter LVILI9341 TFT PinFunction
5VHV--High-side logic reference
3.3V-LVVCC (if no onboard LDO)Low-side logic reference
GNDGNDGNDGNDCommon ground
Pin 13 (SCK)HV1LV1SCK / SCLSPI Clock
Pin 11 (MOSI)HV2LV2SDI / MOSISPI Master Out Slave In
Pin 12 (MISO)HV3LV3SDO / MISOSPI Master In Slave Out
Pin 10HV4LV4CSChip Select (Active Low)
Pin 9--DC / RSData/Command (Direct 3.3V safe if using PWM)
Pin 8--RESETHardware Reset (Active Low)
Safety & Hardware Warning: The DC and RESET pins on some generic ILI9341 boards are not 5V tolerant. If your level shifter only has 4 channels, use a simple 2kΩ/3.3kΩ resistor divider for the DC and RESET lines, or run the Arduino on a 3.3V board (like an Arduino Nano 33 IoT) to eliminate the shifter entirely.

Compilable Code: Booting the Dashboard with Error Handling

The following code targets the Arduino Uno R3 (AVR ATmega328P). It uses the industry-standard Adafruit GFX and Adafruit ILI9341 libraries. Unlike basic tutorials that blindly push pixels, this sketch includes a hardware verification step that reads the display's ID register over SPI to confirm the wiring is correct before attempting to draw.

Prerequisite: Install "Adafruit GFX Library" and "Adafruit ILI9341" via the Arduino Library Manager.

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// --- PIN DEFINITIONS ---
#define TFT_CS   10  // Chip Select
#define TFT_DC    9  // Data/Command
#define TFT_RST   8  // Reset
#define TFT_MOSI 11  // Hardware SPI MOSI
#define TFT_MISO 12  // Hardware SPI MISO
#define TFT_SCK  13  // Hardware SPI Clock

// Initialize hardware SPI instance for maximum speed
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);

void setup() {
  Serial.begin(115200);
  while (!Serial); // Wait for serial monitor on native USB boards

  Serial.println(F("ILI9341 TFT Initialization..."));

  // Standard initialization
  tft.begin();

  // ERROR HANDLING: Read the display ID to verify SPI communication
  uint8_t x = tft.readcommand8(ILI9341_RDMODE);
  Serial.print(F("Display Power Mode: 0x")); Serial.println(x, HEX);
  
  uint8_t id = tft.readcommand8(ILI9341_RDID4);
  Serial.print(F("Display ID: 0x")); Serial.println(id, HEX);

  if (id == 0x00 || id == 0xFF) {
    Serial.println(F("FATAL: Unknown LCD driver chip or SPI failure."));
    Serial.println(F("Check MISO wiring, logic levels, and CS pin."));
    // Halt execution to prevent drawing to a dead bus
    while (1) {
      delay(1000);
    }
  }

  // Screen is verified, proceed to draw UI
  tft.setRotation(1); // Landscape mode
  tft.fillScreen(ILI9341_BLACK);
  
  // Draw a sample dashboard header
  tft.setTextColor(ILI9341_CYAN);
  tft.setTextSize(2);
  tft.setCursor(10, 10);
  tft.println(F("SYSTEM ONLINE"));
  
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(1);
  tft.setCursor(10, 40);
  tft.println(F("Sensor 1: 24.5 C"));
  tft.println(F("Sensor 2: 1013 hPa"));
}

void loop() {
  // Main application loop
  // Update dynamic variables here using tft.setCursor() and tft.print()
  delay(100);
}

Debugging the "White Screen of Death" and Common TFT Errors

When an Arduino LCD TFT project fails, it almost always results in a brightly illuminated, completely white screen. This happens because the backlight LEDs turn on (they are wired directly to 3.3V/GND), but the ILI9341 controller never receives the initialization sequence to turn on the pixel matrix.

The First Three Things to Check

  1. Logic Level Voltages: Put a multimeter on the LV side of your level shifter. It must read exactly 3.3V. If it reads 5V, your shifter is wired backward or unpowered, and you are currently cooking the TFT's input registers.
  2. The MISO Line: The Adafruit library relies on reading the MISO line during tft.begin() to auto-detect the chip. If MISO is disconnected, the library assumes failure or defaults to incorrect timing.
  3. Backlight (LED/BLK) Pin: On generic modules, the backlight pin is sometimes labeled "LED". If it is not internally tied to 3.3V, you must jumper it to 3.3V. Never connect this pin to 5V—you will burn out the onboard backlight resistor or the LEDs themselves.

Decoding Exact Error Strings

If you are monitoring the Serial output, you will encounter specific error states. Here is how to resolve them.

Exact Serial Error StringRanked Causes (Most Likely First)Fix / Measurement Threshold
Unknown LCD driver chip: 0 1. MISO line floating or broken.
2. CS pin stuck HIGH.
3. SPI clock too fast for wire length.
Measure CS pin with scope/meter; it must drop to <0.5V during init. If wires are >6 inches, add tft.setSPISpeed(8000000); before begin().
Display Power Mode: 0xFF 1. 5V logic destroying MISO buffer.
2. TFT reset pin held LOW.
Verify Reset pin is pulled HIGH (3.3V) after init. Check level shifter directionality.
Screen is white, no serial output 1. Code hanging in tft.begin().
2. Insufficient 3.3V current.
The ILI9341 draws ~120mA with backlight. The Uno's onboard 3.3V regulator maxes out at ~150mA and overheats. Use an external AMS1117 3.3V LDO.
Pro Debugging Trick: If you have a cheap $15 logic analyzer (like a Saleae clone), hook it up to SCK, MOSI, and CS. If you see the Uno clocking out data but the CS line never dips below 2.0V, your logic level shifter's MOSFET gate capacitance is too high for the SPI speed. Switch to a dedicated IC like the TXB0104 or lower the SPI speed to 4MHz.

Extending the Build: Adding Touch and Simplifying for Production

Once the basic display is rendering, you will likely want to add interactivity or move the project out of the prototype phase.

How to Extend: Adding Capacitive/Resistive Touch

Many 2.8" ILI9341 modules come with an XPT2046 resistive touch controller on a separate SPI bus. Do not share the CS pin. Wire the touch controller's T_CLK, T_MOSI, and T_MISO to the same SPI pins as the display, but use a dedicated GPIO (like Pin 7) for T_CS. Use the XPT2046_Touchscreen library by Paul Stoffregen. Poll the touch controller in your loop() no faster than 50Hz to prevent SPI bus contention with the display rendering.

How to Simplify: Moving to a 3.3V Native Architecture

If you are designing a custom PCB or moving to a production enclosure, drop the Arduino Uno and the BSS138 level shifter entirely. Switch to an ESP32-DevKitC V4 or an Arduino Nano 33 IoT. Because these microcontrollers operate natively at 3.3V logic, you can wire the ILI9341 directly to the GPIO pins. This eliminates the level shifter, reduces BOM cost by $3, cuts wiring complexity in half, and allows you to push the SPI clock to 40MHz (on the ESP32), resulting in buttery-smooth 60fps UI rendering when paired with the Arduino SPI reference libraries or LVGL.

By standardizing on the ILI9341 SPI module and respecting the 3.3V logic domain, you bypass the most common hardware traps and get straight to building functional, vibrant embedded interfaces.