The 2.4- and 2.8-inch ILI9341 panels are the step up from small TFTs: 320×240 pixels, often with a touch layer and an SD slot on the back, around ₹450–700 in India.
The ESP32 DevKit V1 suits them well. The panel’s signals are 3.3 V, the same as the ESP32, and the VSPI bus (D18, D19, D23) runs it at 40 MHz. Only VCC wants 5 V, from VIN. The sketch first reads the panel’s status registers back over MISO — a quick proof that every SPI wire is right — then times a full-screen fill and shows a flicker-free clock.
Wiring
| ESP32 DevKit V1 | ILI9341 TFT 2.4″ / 2.8″ (240×320, SPI, touch) | Wire | Notes |
|---|---|---|---|
| VIN | VCC | red | 5 V with jumper J1 open (the board’s regulator makes 3.3 V). Only with J1 bridged may VCC go to 3V3. |
| GND | GND | black | |
| 3V3 | LED | red | Backlight. Left open, the screen stays dark. |
| D18 | SCK | yellow | VSPI clock |
| D23 | SDI(MOSI) | green | VSPI MOSI |
| D19 | SDO(MISO) | blue | Only needed to read the panel back; the sketch uses it to confirm the wiring. |
| D4 | CS | purple | Not D5 or D15 — both are strapping pins. |
| D26 | DC | orange | |
| D27 | RESET | white |
5 V with J1 open (on-board regulator) or 3.3 V with J1 bridged part, 3.3 V board. VCC can be 5 V, but every signal pin is 3.3 V only. ESP32 and Pico connect directly. On an Uno use a level shifter (74LVC245 / CD4050) or 1 kΩ / 2 kΩ dividers on CS, RESET, DC, SDI and SCK — 5 V signals give a white screen or a dead panel.
Board quirks
- VCC may be 5 V, the signals may not. The ESP32’s 3.3 V signals are exactly right, which is why this panel is so much easier on an ESP32 than on an Uno (no level shifter needed).
- The ESP32’s VSPI runs the panel at 40 MHz. A full-screen fill takes a few tens of milliseconds — fast enough for gauges and menus. If you see garbage, shorten the wires or lower
tft.begin()to 27 MHz. - The touch controller (T_CLK, T_CS, T_DIN, T_DO, T_IRQ) is a separate SPI chip. It can share D18/D23/D19 with its own chip-select on a free pin such as D32; it isn’t used in this sketch.
- An ILI9341 with backlight draws 80–100 mA. From a USB port that is fine; if you add Wi-Fi, a servo or LEDs, use a proper 5 V 1 A supply on VIN.
- Bodmer’s TFT_eSPI library is faster again on the ESP32, but it is configured by editing a file inside the library. Adafruit_ILI9341 keeps the pins in your sketch.
Code
// 2.4"/2.8" ILI9341 SPI TFT (240×320) on an ESP32 DevKit V1.
// VCC → VIN (5 V, J1 open), GND → GND, LED → 3V3, SCK → D18, SDI(MOSI) → D23,
// SDO(MISO) → D19, CS → D4, DC → D26, RESET → D27. Touch pins (T_*) not used here.
// Reads the panel's status registers back (proves MISO works), times a full-screen
// fill at 40 MHz SPI, then shows a live clock.
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
constexpr int8_t TFT_CS = 4;
constexpr int8_t TFT_DC = 26;
constexpr int8_t TFT_RST = 27;
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
void setup() {
Serial.begin(115200);
tft.begin(40000000); // 40 MHz SPI; drop to 27 MHz if long wires give garbage
uint8_t mode = tft.readcommand8(ILI9341_RDMODE);
uint8_t diag = tft.readcommand8(ILI9341_RDSELFDIAG);
Serial.printf("Power mode 0x%02X, self-diagnostic 0x%02X%s\n", mode, diag,
(mode == 0x00 || mode == 0xFF) ? " (no reply — MISO not wired? drawing still works)" : "");
uint32_t t0 = millis();
tft.fillScreen(ILI9341_BLUE);
Serial.printf("Full-screen fill: %lu ms\n", (unsigned long)(millis() - t0));
tft.setRotation(1); // landscape, 320×240
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(3);
tft.setCursor(10, 10);
tft.print("FlashReady");
tft.setTextSize(2);
tft.setCursor(10, 50);
tft.print("ILI9341 on ESP32 DevKit V1");
tft.drawRect(0, 0, tft.width(), tft.height(), ILI9341_YELLOW);
}
void loop() {
static uint32_t last = 0;
if (millis() - last < 1000) return;
last = millis();
uint32_t s = millis() / 1000;
tft.setTextSize(5);
tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK); // background colour: overwrite, no flicker
tft.setCursor(40, 120);
tft.printf("%02lu:%02lu:%02lu", (unsigned long)(s / 3600), (unsigned long)(s / 60 % 60), (unsigned long)(s % 60));
}// 2.4"/2.8" ILI9341 SPI TFT (240×320) on an ESP32 DevKit V1.
// VCC → VIN (5 V, J1 open), GND → GND, LED → 3V3, SCK → D18, SDI(MOSI) → D23,
// SDO(MISO) → D19, CS → D4, DC → D26, RESET → D27. Touch pins (T_*) not used here.
// Reads the panel's status registers back (proves MISO works), times a full-screen
// fill at 40 MHz SPI, then shows a live clock.
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
constexpr int8_t TFT_CS = 4;
constexpr int8_t TFT_DC = 26;
constexpr int8_t TFT_RST = 27;
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
void setup() {
Serial.begin(115200);
tft.begin(40000000); // 40 MHz SPI; drop to 27 MHz if long wires give garbage
uint8_t mode = tft.readcommand8(ILI9341_RDMODE);
uint8_t diag = tft.readcommand8(ILI9341_RDSELFDIAG);
Serial.printf("Power mode 0x%02X, self-diagnostic 0x%02X%s\n", mode, diag,
(mode == 0x00 || mode == 0xFF) ? " (no reply — MISO not wired? drawing still works)" : "");
uint32_t t0 = millis();
tft.fillScreen(ILI9341_BLUE);
Serial.printf("Full-screen fill: %lu ms\n", (unsigned long)(millis() - t0));
tft.setRotation(1); // landscape, 320×240
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(3);
tft.setCursor(10, 10);
tft.print("FlashReady");
tft.setTextSize(2);
tft.setCursor(10, 50);
tft.print("ILI9341 on ESP32 DevKit V1");
tft.drawRect(0, 0, tft.width(), tft.height(), ILI9341_YELLOW);
}
void loop() {
static uint32_t last = 0;
if (millis() - last < 1000) return;
last = millis();
uint32_t s = millis() / 1000;
tft.setTextSize(5);
tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK); // background colour: overwrite, no flicker
tft.setCursor(40, 120);
tft.printf("%02lu:%02lu:%02lu", (unsigned long)(s / 3600), (unsigned long)(s / 60 % 60), (unsigned long)(s % 60));
}Library: Adafruit ILI9341 (Adafruit (also install Adafruit GFX Library; on ESP32, TFT_eSPI by Bodmer is faster)) — compiled with Adafruit ILI9341 1.7.0.
Expected serial output
Power mode 0x9C, self-diagnostic 0xC0
Full-screen fill: 37 msPower mode 0x9C, self-diagnostic 0xC0 Full-screen fill: 37 ms
Common errors
White screen
Power is on but no data arrives. Check SDI(MOSI) on D23, SCK on D18, CS on D4, DC on D26, RESET on D27. Make sure VCC isn’t on 3V3 with J1 open (too little voltage for the regulator).
“no reply — MISO not wired?” but the screen works
Only the read-back failed. Connect SDO(MISO) to D19, or ignore it: drawing doesn’t need MISO. Some cheap panels never answer reads.
Screen flickers when Wi-Fi or a motor starts
Supply dips. Use a 5 V 1–2 A supply on VIN and a 100 µF capacitor across VCC–GND at the display.