#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
// WiFi
#define WIFI_SSID "Wokwi-GUEST"
#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: accept all certs for demo
tls.setInsecure();
tls.setTimeout(15000);
showPricesOnce();
}
void loop() {
static unsigned long tPrev = 0;
const unsigned long PERIOD_MS = 60000; // update every 60s
unsigned long now = millis();
if (now - tPrev >= PERIOD_MS) {
tPrev = now;
// Redraw header region occasionally to avoid artifacts
tftHeader("Crypto Prices");
showPricesOnce();
}
delay(50);
}Loading
esp32-s3-devkitc-1
esp32-s3-devkitc-1