Cirkit Designer LogoCirkit DesignerYour all-in-one circuit design IDE Launch Cirkit Designer App

ESP32-S3 Wi-Fi Controlled ILI9341 TFT Display for Cryptocurrency Prices

View and Edit Project In Cirkit DesignerImage of ESP32-S3 Wi-Fi Controlled ILI9341 TFT Display for Cryptocurrency Prices

Circuit Documentation

Summary

This circuit integrates an ESP32-S3 microcontroller with an ILI9341 TFT display. The ESP32-S3 is responsible for fetching cryptocurrency prices from an API and displaying them on the ILI9341 screen. The circuit is designed to connect to WiFi and periodically update the displayed prices.


Component List

1. ILI9341

2. ESP32-S3 (beta)


Wiring Details

ILI9341


ESP32-S3 (beta)


Documented Code

Main Code (sketch.ino)

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

// WiFi
#define WIFI_SSID "CirkitWifi"
#define WIFI_PASS ""

// TFT pins (match your wiring)
#define TFT_CS    10
#define TFT_DC     9
#define TFT_RST   -1   // ILI9341 RST tied to ESP32 RST (no GPIO control)
#define TFT_SCLK  12
#define TFT_MOSI  11
#define TFT_MISO  13

// Route public API calls through Cirkit's data gateway so shared demos
// use cached responses and don't hammer third-party APIs directly.
// This is the avoid rate-limiting in public demos.
String cachedUrl(const char* url) {                                                 
    String encoded = String(url);
    encoded.replace("&", "%26");                                                     
    return "https://app.cirkitdesigner.com/api/data-gateway/fetch?url=" + encoded;
}

// CoinGecko (one request for multiple coins)
String priceUrl = cachedUrl("https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum,solana&vs_currencies=usd"); 

Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
WiFiClientSecure tls;

// Simple text writer
static int gY = 34;
inline void tftResetCursor() { gY = 34; }

void tftHeader(const char* title) {
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextWrap(false);
  tft.setTextSize(2);
  tft.setTextColor(ILI9341_CYAN, ILI9341_BLACK);
  tft.setCursor(4, 4);
  tft.print(title);
  tft.drawLine(0, 26, tft.width(), 26, ILI9341_DARKGREY);
  tftResetCursor();
}

void tftLine(const String& s, uint16_t color = ILI9341_WHITE) {
  const int lineH = 20;
  if (gY > (tft.height() - lineH)) gY = 34;
  tft.fillRect(0, gY, tft.width(), lineH, ILI9341_BLACK);
  tft.setCursor(4, gY);
  tft.setTextSize(2);
  tft.setTextColor(color, ILI9341_BLACK);
  tft.print(s);
  gY += lineH;
}

static String bodyBuf;

static int httpsGet(const char* url, String& bodyOut) {
  HTTPClient https;
  https.setUserAgent("ESP32-CRYPTO/1.0");
  https.setTimeout(15000);
  if (!https.begin(tls, url)) return -1000;
  int code = https.GET();
  if (code > 0) bodyOut = https.getString();
  https.end();
  return code;
}

// Minimal JSON extractor: finds "<coin>":{"usd":<number>}
static String getUsd(const String& json, const char* coin) {
  String key = String("\"") + coin + String("\"");
  int k = json.indexOf(key);
  if (k < 0) return "";
  int u = json.indexOf("\"usd\":", k);
  if (u < 0) return "";
  int i = u + 6;
  while (i < (int)json.length() && isspace((unsigned char)json[i])) i++;
  int j = i;
  while (j < (int)json.length()) {
    char c = json[j];
    if (!(isDigit((unsigned char)c) || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-')) break;
    j++;
  }
  if (j > i) return json.substring(i, j);
  return "";
}

static void connectWiFiOnce() {
  WiFi.mode(WIFI_STA);
  WiFi.setSleep(false);
  WiFi.begin(WIFI_SSID, WIFI_PASS);

  unsigned long t0 = millis();
  const unsigned long TIMEOUT = 20000;

  tftLine("Connecting WiFi...");
  while (WiFi.status() != WL_CONNECTED && millis() - t0 < TIMEOUT) {
    delay(300);
  }

  if (WiFi.status() == WL_CONNECTED) {
    tftLine("WiFi: connected", ILI9341_GREEN);
    tftLine("IP: " + WiFi.localIP().toString(), ILI9341_DARKGREY);
  } else {
    tftLine("WiFi: FAILED", ILI9341_RED);
  }
}

static void showPricesOnce() {
  if (WiFi.status() != WL_CONNECTED) {
    tftLine("No WiFi", ILI9341_RED);
    return;
  }

  bodyBuf = "";
  int code = httpsGet(priceUrl.c_str(), bodyBuf);
  if (code != 200 || bodyBuf.length() == 0) {
      tftLine("Fail code:" + String(code), ILI9341_RED);                                                                                                                 
      return;                                                                                                                                                            
    }  

  String btc = getUsd(bodyBuf, "bitcoin");
  String eth = getUsd(bodyBuf, "ethereum");
  String sol = getUsd(bodyBuf, "solana");

  if (!btc.length() && !eth.length() && !sol.length()) {
    tftLine("Parse failed", ILI9341_RED);
    return;
  }

  // Draw a price block
  const int barH = 26;
  if (gY > (tft.height() - 3 * barH)) gY = 34;

  auto drawRow = [&](const char* sym, const String& v, uint16_t fg, uint16_t bg) {
    tft.fillRect(0, gY, tft.width(), barH, bg);
    tft.setCursor(4, gY + 4);
    tft.setTextSize(2);
    tft.setTextColor(fg, bg);
    tft.print(sym);
    tft.print(": $");
    tft.print(v.length() ? v : String("-"));
    gY += barH;
  };

  drawRow("BTC", btc, ILI9341_YELLOW, ILI9341_DARKGREY);
  drawRow("ETH", eth, ILI9341_CYAN,   ILI9341_BLACK);
  drawRow("SOL", sol, ILI9341_GREEN,  ILI9341_DARKGREY);
}

void setup() {
  // Prepare SPI before TFT
  pinMode(TFT_CS, OUTPUT); digitalWrite(TFT_CS, HIGH);
  pinMode(TFT_DC, OUTPUT);
  if (TFT_RST >= 0) { pinMode(TFT_RST, OUTPUT); digitalWrite(TFT_RST, HIGH); }

  SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, TFT_CS);

  tft.begin(8000000);
  tft.setRotation(1);

  tftHeader("Crypto Prices");

  connectWiFiOnce();

  // TLS