Your Business
Your Business
  • Home
  • Arduino Projects
    • List of projects
    • Traffic lights
    • Ir Car
    • LCD
    • Led remote control
    • Ultrasonic Detector
  • ESP 32 Projects
    • ESP32 & 1.28 display
  • More
    • Home
    • Arduino Projects
      • List of projects
      • Traffic lights
      • Ir Car
      • LCD
      • Led remote control
      • Ultrasonic Detector
    • ESP 32 Projects
      • ESP32 & 1.28 display
  • Home
  • Arduino Projects
    • List of projects
    • Traffic lights
    • Ir Car
    • LCD
    • Led remote control
    • Ultrasonic Detector
  • ESP 32 Projects
    • ESP32 & 1.28 display

Wi‑Fi Text Badge with ESP32 & 1.28″ Round GC9A01 TFT

Turn your GC9A01 round display into a wireless, three‑line b

 

  • Build time: ~15 min wiring  |  ~3 min flashed sketch
  • Cost: under €15 (ESP32 DevKit + TFT)  |  no level‑shifters required
  • What you get: an ESP32 hosts its own WPA2 access‑point, serves a simple textarea form, and auto‑wraps up to 96 chars onto the 240 × 240 round screen in yellow text.

Bill of Materials

  •  1 ESP32 DevKit v1Any WROOM‑32, 3 V3 logic. 
  •  1 1.28″ GC9A01 round TFT240 × 240 px, SPI interface. 
  • 7/ 8 Jumper wires F‑F / M‑MColour‑code for filming! 

Wiring (ESP32 ⇄ GC9A01)

     ESP 32                  DISPLAY

       3V                           VCC

     GND                        GND

     P18                           SCL

    P23                           SDA

    P16                            DC

    P22                            CS

     P8                            RST


Firmware

Library & board setup

  1. Open Arduino IDE ≥2.3 → Board Manager → search esp32 → install Espressif 32.
  2. Library Manager → install TFT_eSPI by Bodmer.
  3. Documents/Arduino/libraries/TFT_eSPI/User_Setup_Select.h → un‑comment:#include <User_Setups/Setup200_GC9A01.h>
  4. Edit Setup200_GC9A01.h pin‑map & SPI speed:

 

#include <WiFi.h>
#include <WebServer.h>
#include <TFT_eSPI.h>

/*──────────  Access-Point config  ─────────*/
const char* AP_SSID     = "GC9A01_Display";
const char* AP_PASS     = "GC9A01_2025";
const IPAddress AP_IP (192,168,4,1);
const IPAddress AP_MASK (255,255,255,0);
/*──────────────────────────────────────────*/

TFT_eSPI  tft = TFT_eSPI();
WebServer server(80);

String lastText = "Hello!";

/*──────────── HTML page (PROGMEM) ─────────*/
const char INDEX_HTML[] PROGMEM = R"rawliteral(
<!DOCTYPE html><html lang="it"><meta charset="utf-8">
<title>CircleCast Badge</title>
<style>
 body{background:#111;color:#eee;font-family:sans-serif;text-align:center;padding-top:60px}
 textarea{width:72%;max-width:400px;font-size:1.2rem;padding:10px;border-radius:6px;border:none;resize:vertical}
 button{padding:10px 18px;font-size:1.1rem;border:none;border-radius:6px;color:#fff;background:#03a9f4;margin-top:8px}
 .card{background:#222;padding:30px;border-radius:12px;width:80%;max-width:500px;margin:auto;box-shadow:0 0 20px #000}
</style>
<div class="card">
 <h2>Send your message</h2>
 <form action="/send" method="POST">
  <textarea name="msg" rows="3" maxlength="96" placeholder="Write Here..." autofocus required></textarea><br>
  <button>SEND</button>
 </form>
 <p>Previous Text:</p><pre style="white-space:pre-wrap;">%TEXT%</pre>
</div></html>)rawliteral";

/*──────── Text auto-wrap & draw ───────*/
void drawWrappedText(const String &raw)
{
tft.fillScreen(TFT_BLACK);                     // clear screen
tft.setTextDatum(MC_DATUM);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
const int16_t lineH = tft.fontHeight(4) + 4;

  String lines[3];
int line = 0;  String word, acc;
for (uint16_t i = 0; i <= raw.length(); ++i) {
char c = (i < raw.length()) ? raw[i] : ' ';
if (c == ' ' || c == '\n') {
if (acc.length()==0 && c==' ') continue;
      word = acc;  acc = "";
if (tft.textWidth(lines[line] + word + ' ', 4) > 220) {
if (++line == 3) break;                 // max 3 lines
}
lines[line] += word + ' ';
} else acc += c;
}

int16_t startY = 120 - (line * lineH) / 2;     // vertical centring
for (int i = 0; i <= line; ++i)
tft.drawString(lines[i], 120, startY + i * lineH, 4);
}

/*──────── HTTP handlers ───────────────*/
void handleRoot() {
static char html[950];
strncpy_P(html, INDEX_HTML, sizeof(html));
html[sizeof(html)-1] = '\0';

char* p = strstr(html, "%TEXT%");
if (p) {
size_t tail = strlen(p + 6);
size_t need = p - html + lastText.length() + tail;
if (need < sizeof(html)-1) {
memmove(p + lastText.length(), p + 6, tail + 1);
memcpy(p, lastText.c_str(), lastText.length());
}
}
server.send(200, "text/html", html);
}

void handleSend() {
if (server.hasArg("msg")) {
    lastText = server.arg("msg").substring(0, 96);
lastText.replace("\r", "");
drawWrappedText(lastText);
}
server.sendHeader("Location", "/");
server.send(303);
}

/*────────────  SETUP  ─────────────*/
void setup() {
Serial.begin(115200);

tft.init();
tft.setRotation(0);
drawWrappedText(lastText);

WiFi.mode(WIFI_AP);
WiFi.softAPConfig(AP_IP, AP_IP, AP_MASK);
WiFi.softAP(AP_SSID, AP_PASS);
Serial.printf("\nAP \"%s\" pronto  →  http://%s/\n",
                AP_SSID, WiFi.softAPIP().toString().c_str());

server.on("/",     HTTP_GET,  handleRoot);
server.on("/send", HTTP_POST, handleSend);
server.begin();
}

/*────────────  LOOP  ──────────────*/
void loop() {
server.handleClient();
}

Using the badge

 

  1. Connect your phone to GC9A01_Display (password GC9A01_2025).
  2. Browse to 192.168.4.1.
  3. Type a message (max 96 chars) → SEND → watch it appear, word‑wrapped, in yellow.

Hardening checklist

 

  • Change the default SSID & password before field‑use.
  • Disable the AP (WiFi.mode(WIFI_STA)) and push text over HTTPS when integrating in a larger project.
  • Consider OTA via arduino‑esp32 v3.0 but always flash over USB first.

Resources

 

  • GitHub repo: https://github.com/Alex3dWorks/gc9a01-wifi-badge
  • TFT_eSPI docs: https://github.com/Bodmer/TFT_eSPI


Copyright © 2025 Alex3dworks - All rights reserved

Powered by

  • Politica sulla privacy

This website uses cookies.

We use cookies to analyze website traffic and optimize your website experience. By accepting our use of cookies, your data will be aggregated with all other user data.

DeclineAccept