This tutorial is part of: ESP32-S3 RGB LED matrica
Kul projekat za kreiranje zabave i praktičnih aplikacija korištenjem ESP32-S3 RGB Matrix modula. Linkovi do drugih videa nalaze se ispod ovog članka.
ESP32-S3 RGB LED Matrix projekat 3 - Tekst sa mobilnog telefona
Projekat 3 – Kontrola teksta na matrici sa telefona (HTTP tekst)
U ovom projektu ESP32-S3 RGB LED matrica hostuje malu web stranicu tako da možete mijenjati tekst koji se pomiče, boju, smjer i brzinu direktno sa svog telefona ili računara. Ne treba vam posebna aplikacija – samo web preglednik. Ovo čini modul malim Wi-Fi tekstualnim displejom koji možete ažurirati u realnom vremenu.
Svih šest projekata u ovoj seriji objašnjeno je i prikazano u jednom YouTube videu. Isti video je ugrađen na ovoj stranici, tako da možete tačno vidjeti kako web interfejs izgleda i kako se tekst trenutno ažurira na matrici. Kompletni izvorni kod za ovaj projekat automatski se učitava ispod članka, a modul ESP32-S3 RGB LED Matrix možete kupiti iz affiliate prodavnica navedenih ispod sekcije s kodom.
U ovom članku fokusiramo se na to kako mrežna logika funkcioniše (kućni Wi-Fi naspram pristupne tačke) i koje postavke možete mijenjati u kodu da biste prilagodili ponašanje.



Pregled ESP32-S3 RGB LED Matrix modula
Hardver je isti kao za sve ostale projekte u ovoj seriji: ESP32-S3 mikrokontrolerska ploča s ugrađenom 8×8 RGB LED matricom i QMI8658C senzorom pokreta na poleđini. USB-C priključak koristi se za napajanje i programiranje, a pinovi na rubovima i dalje su dostupni za druge ulazno-izlazne uređaje.:contentReference[oaicite:0]{index=0}

- ESP32-S3 – mikrokontroler s Wi-Fi i Bluetooth mogućnostima.
- 8×8 RGB matrica – 64 adresabilne RGB LED diode za tekst i grafiku.
- QMI8658C akcelerometar – koristi se u projektima s naginjanjem i igrama.
- USB priključak – napaja ploču i učitava skice iz Arduino IDE-a.
- Izloženi pinovi – omogućavaju dodatne senzore ili aktuatore ako je potrebno.
- Boot/Reset dugmad – za upload firmvera i ponovno pokretanje.
Za Projekat 3, najvažnija karakteristika je ESP32 Wi-Fi, koji omogućava ploči da radi kao mali web server za stranicu s kontrolom teksta.:contentReference[oaicite:1]{index=1}

Projekti obuhvaćeni u videu (vremenske oznake)
Jedan video za ovu seriju pokriva svih šest projekata. Za brzu referencu:
- 00:00 – Uvod
- 02:01 – Instaliranje ESP32 ploča
- 03:32 – Instaliranje biblioteka
- 05:32 – Projekat 1: Pokretna tačka
- 11:11 – Projekat 2: Pomicanje teksta
- 12:59 – Projekat 3: HTTP tekst (ovaj projekat)
- 16:41 – Projekat 4: Tačka s naginjanjem
- 18:55 – Projekat 5: Strelica gore
- 20:02 – Projekat 6: Igra ciljanja
Preporučujemo da pogledate sekciju o HTTP tekstu u videu dok radite s ovim člankom. Video prikazuje kako ESP32 generiše web stranicu i kako se promjena teksta, boje i brzine odmah odražava na LED matrici.:contentReference[oaicite:2]{index=2}
Instaliranje ESP32 ploča u Arduino IDE
Ako ste već završili Projekte 1 ili 2, podešavanje ploče je gotovo i možete preskočiti ovu sekciju. U suprotnom, slijedite ove korake u Arduino IDE-u:
- Otvori
File > Preferencesi dodaj URL za ESP32 ploče u “Additional Boards Manager URLs”. - Idi na
Tools > Board > Boards Manager…, pretražiESP32i instaliraj zvanični ESP32 paket. - Odaberi ispravnu ESP32-S3 RGB Matrix ploču iz
Tools > Board. - Poveži modul putem USB-a i odaberi ispravan serijski port pod
Tools > Port.
Bez odgovarajuće podrške za ESP32 ploče i ispravnog porta, web-server skica se neće učitati.
Instaliranje NeoMatrix i potrebnih biblioteka
Ovaj projekat koristi iste biblioteke kao prethodni projekat s pomicanjem teksta:
Adafruit NeoMatrixAdafruit NeoPixelAdafruit GFX Library
Instalirajte putem Library Manager-a:
- Otvori
Sketch > Include Library > Manage Libraries…. - Pretraži
Adafruit NeoMatrixi klikni Install. - Prihvati instalaciju zavisnosti (
Adafruit GFXiAdafruit NeoPixel).
Nakon instalacije, trebali biste vidjeti NeoMatrix i NeoPixel primjere skica pod File > Examples.
Dva Wi-Fi načina rada u Projektu 3
Najvažniji koncept u ovom projektu je da ESP32 može raditi u dva različita načina:
- Station (STA) način – ESP32 se povezuje na vašu postojeću kućnu Wi-Fi mrežu.
- Access Point (AP) način – ESP32 kreira vlastitu Wi-Fi mrežu ako kućni Wi-Fi nije dostupan.
Oba načina koriste isti web interfejs: HTML stranicu koju servira sam ESP32, gdje možete mijenjati tekst, boju, smjer pomicanja i brzinu.:contentReference[oaicite:3]{index=3}
Način 1 – Povezivanje na kućni Wi-Fi (Station način)
U Station načinu, ESP32 se pridružuje vašoj kućnoj ili uredskoj Wi-Fi mreži. Ovo je poželjni način kad god je vaš ruter dostupan jer:
- Vaš telefon i računar su već povezani na istu Wi-Fi mrežu.
- Možete upisati IP adresu ESP32-ja u preglednik i kontrolisati tekst s bilo kojeg uređaja na toj mreži.
U sekciji s postavkama skice, unosite svoj Wi-Fi SSID i lozinku:
// Kućni Wi-Fi kredencijali (Station način rada) const char* WIFI_SSID = "YourHomeWiFi"; const char* WIFI_PASS = "YourHomePassword";Nakon što se ploča pokrene, pokušava se povezati na WIFI_SSID. Ako je uspješno, kod ispisuje dodijeljenu IP adresu na Serial Monitor, na primjer:
Connected to WiFi
IP address: 192.168.1.16
Za kontrolu teksta:
- Provjerite da su vaš telefon ili računalo povezani na istu Wi-Fi mrežu (na primjer,
YourHomeWiFi). - Otvorite preglednik i unesite ispisanu IP adresu, kao što je
http://192.168.1.16/. :contentReference[oaicite:4]{index=4} - Pojavit će se kontrolna stranica koja vam omogućava unos teksta, odabir boje, smjera i podešavanje brzine pomicanja.
Način 2 – Samostalna pristupna točka (AP način)
Ako se ESP32 ne može povezati na vašu kućnu Wi-Fi mrežu (pogrešna lozinka, mreža nije dostupna ili koristite modul vani), skica se automatski prebacuje na način pristupne točke. U AP načinu, ESP32 sam postaje Wi-Fi hotspot sa svojim imenom mreže i lozinkom.
U ovom projektu, AP postavke su fiksne:
// Access Point (AP) kredencijali (rezervni način)
const char* AP_SSID = "ESP32";
const char* AP_PASS = "password";
Kada Station način ne uspije, modul se prebacuje na AP način i počinje emitirati Wi-Fi mrežu pod nazivom ESP32. Za kontrolu matrice:
- Na telefonu ili računalu otvorite Wi-Fi postavke i povežite se na mrežu ESP32.
- Unesite lozinku password (kako je definirano u kodu).
- Nakon povezivanja, otvorite preglednik i idite na
http://192.168.4.1/(zadana IP adresa za ESP32 AP način). - Pojavit će se ista kontrolna stranica koja vam omogućava promjenu teksta, boje, brzine i smjera.
Ovo rezervno ponašanje čini projekt korisnim bilo gdje: kod kuće, u laboratoriju ili u demo okruženju gdje nije dostupan ruter.
Projekat 3 – Glavne postavke u kodu
Puna HTTP Text skica je učitana ispod ovog članka od strane web stranice. Ovdje dokumentujemo samo najvažnije opcije konfiguracije koje ćete vjerovatno mijenjati.
Wi-Fi i postavke pristupne točke
Na vrhu skice naći ćete sekciju za Wi-Fi konfiguraciju. Mijenjajte samo Station (kućni Wi-Fi) kredencijale; AP postavke obično ostaju kao zadane:
// ---------- WI-FI POSTAVKE ----------
// Kućni Wi-Fi (Station način)
const char* WIFI_SSID = "YourHomeWiFi"; // ovdje unesite SSID vašeg rutera
const char* WIFI_PASS = "YourHomePassword"; // ovdje unesite lozinku vašeg rutera
// Rezervna pristupna točka (AP način)
const char* AP_SSID = "ESP32"; // fiksno ime AP-a
const char* AP_PASS = "password"; // fiksna lozinka AP-a
Ponašanje:
- Ako su
WIFI_SSIDiWIFI_PASStačni i mreža je dostupna → ESP32 se povezuje kao normalan Wi-Fi uređaj (Station način). - Ako veza ne uspije nakon vremenskog ograničenja → ESP32 pokreće vlastiti AP koristeći
AP_SSIDiAP_PASS.
Pin matrice, veličina i svjetlina
Ove postavke su iste kao u prethodnim projektima:
// Konfiguracija matrice
const int MATRIX_PIN = 14; // pin za podatke RGB matrice
const int MATRIX_WIDTH = 8;
const int MATRIX_HEIGHT = 8;
// Ukupna svjetlina ekrana (0–255)
uint8_t matrixBrightness = 40; // prilagodite vašem okruženju
Zadržite MATRIX_PIN na 14 za ovu ploču. Možete povećati matrixBrightness ako vam treba više svjetla, ali niže vrijednosti su ugodnije za gledanje izbliza.
Zadani tekst i postavke pomicanja
Kada se ploča pokrene, prikazuje početnu poruku dok ne otvorite web stranicu i unesete novu. Možete promijeniti zadani tekst u konfiguraciji:
// Zadana poruka prikazana pri pokretanju
String currentText = "Robojax"; // kasnije se prepisuje iz web sučelja
Ostatak ponašanja pomicanja kontrolira skup varijabli koje ažurira web sučelje:
// Kašnjenje pomicanja u milisekundama (niže = brže)
int scrollDelayMs = 50;
// Smjer pomicanja: 0=lijevo, 1=desno, 2=gore, 3=dole
int scrollDirection = 0; // zadano: pomicanje lijevo
Web stranica šalje nove vrijednosti na osnovu odabira klizača i dugmadi. Sa Arduino strane, samo trebate znati da:
- Smanjenje
scrollDelayMsčini da se tekst kreće brže. - Povećanje
scrollDelayMsčini da se kreće sporije. - Promjena
scrollDirectionprebacuje između načina pomicanja lijevo, desno, gore ili dole.
Boja teksta (kontrolisana sa web stranice)
Boja teksta je kontrolisana sa tri vrijednosti 0–255 (crvena, zelena, plava). One se ažuriraju svaki put kada odaberete novu boju na web stranici:
// Trenutna boja teksta (R, G, B)
uint8_t textRed = 255;
uint8_t textGreen = 255;
uint8_t textBlue = 255;
Kada odaberete boju u pregledniku i kliknete Apply, ESP32 parsira RGB vrijednosti i ažurira ove tri varijable; tekst odmah mijenja boju na matrici. U videu, ovo ponašanje je demonstrirano sa više promjena boja, uključujući primjere crvene, zelene i plave.:contentReference[oaicite:5]{index=5}
Sažetak
Projekat 3 pretvara vašu ESP32-S3 RGB LED matricu u potpuno bežični tekstualni ekran koji možete kontrolisati bilo kojim uređajem sa web preglednikom. Skica je dizajnirana da bude robusna:
- Prvo pokušava da se poveže na vaš kućni Wi-Fi koristeći SSID i lozinku koje ste konfigurisali.
- Ako to ne uspije, automatski postaje pristupna tačka s imenom
ESP32i lozinkompassword. - U oba načina rada, otvaranje ispravne IP adrese u pregledniku prikazuje istu kontrolnu stranicu za tekst, boju, smjer i brzinu.
Kompletan HTTP Text kod dostupan je ispod ovog članka (automatski se učitava na web stranici). Za detaljan vodič korak po korak i uživo demonstraciju kako se tekst ažurira u stvarnom vremenu, obavezno pogledajte odjeljak Project 3 u videu. Ako želite sami izgraditi projekat, možete kupiti ESP32-S3 RGB LED Matrix modul koristeći affiliate linkove navedene ispod koda.
This tutorial is part of: ESP32-S3 RGB LED matrica
- ESP32-S3 RGB LED Matrix projekat 1 - Osnovna tačka
- ESP32-S3 RGB LED Matrix projekat 2 - Tekst koji se pomiče
- ESP32-S3 RGB LED Matrix projekat 4 - Tilt tačka
- ESP32-S3 RGB LED Matrica Projekat 5 - Strelica uvijek gore
- ESP32-S3 RGB LED Matrix projekat 6 - Cible igra
- ESP32-S3 RGB LED matrica Wi-Fi + NTP sat vremena projekat -1 Osnovni sat
- ESP32-S3 RGB LED matrica Internet sat projekat - 2 sata višebojni prikaz vremena i datuma
- ESP32-S3 RGB LED matrica internet sat projekat - 3 noćne boje sa datumom
- ESP32-S3 RGB LED matrica internet sat projekat - 5 duginih boja
- ESP32-S3 RGB LED matrica internet sat projekat - 4 nasumične boje
- ESP32-S3 RGB LED Matrix test for RGB, GRB setting
/*
Project 4: HTTP Text Scroll – ESP32-S3 RGB LED Matrix (Waveshare)
- Connects to your home WiFi (station mode, with AP fallback).
- Serves a web page where you can set:
* Text
* Color
* Display ON/OFF
* Scroll direction (Left / Right / Up / Down)
* Scroll delay (speed)
- Supports 8×8 RGB LED matrix using Adafruit_NeoMatrix.
▶️ Video Tutorial:
https://youtu.be/JKLuYrRcLMI
📄 Resources & Code Page:
https://robojax.com/your-resources-page-here
HTTP_Text_Scroll
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
// ======================= WIFI SETTINGS =========================
// Home WiFi (change these to your router credentials)
const char* WIFI_SSID = "Biseem";
const char* WIFI_PASS = "wan9&Jang~";
// Fallback Access Point
const char* AP_SSID = "ESP32";
const char* AP_PASS = "password";
// ======================= MATRIX SETTINGS =======================
#define MATRIX_PIN 14
#define MATRIX_WIDTH 8
#define MATRIX_HEIGHT 8
// 0, 1, 2, or 3 – adjust if text orientation is wrong with USB up
#define MATRIX_ROTATION 0
#define BRIGHTNESS 15
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(
MATRIX_WIDTH, MATRIX_HEIGHT, MATRIX_PIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE,
NEO_RGB + NEO_KHZ800
);
// ======================= SCROLL STATE ==========================
enum ScrollDir {
DIR_LEFT = 0,
DIR_RIGHT,
DIR_UP,
DIR_DOWN
};
String scrollText = "Robojax";
uint16_t textColor = 0xFFFF; // default white
bool displayOn = true;
ScrollDir currentDir = DIR_LEFT;
// 1 step every this many ms (lower = faster)
unsigned long scrollInterval = 80;
unsigned long lastScrollTime = 0;
// Horizontal scroll position
int scrollX = MATRIX_WIDTH;
int scrollY = 0; // top row
// State for vertical per-letter scroll
int vertCharIndex = 0; // which character of the string
int vertY = MATRIX_HEIGHT; // vertical position of that character
// ======================= WEB SERVER ============================
WebServer server(80);
// ======================= HTML PAGE ============================
const char MAIN_page[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ESP32 Text Scroll</title>
<style>
body {
font-family: Arial, sans-serif;
background: #111;
color: #eee;
text-align: center;
margin: 0;
padding: 10px;
}
.container {
max-width: 360px;
width: 100%%;
margin: 0 auto;
background: #222;
padding: 12px;
border-radius: 10px;
box-sizing: border-box;
}
input[type="text"] {
width: 100%%;
padding: 8px;
box-sizing: border-box;
border-radius: 5px;
border: 1px solid #444;
background: #111;
color: #eee;
margin-bottom: 10px;
}
.row {
margin: 10px 0;
}
.label {
display: block;
margin-bottom: 5px;
text-align: left;
}
.toggle {
display: inline-flex;
align-items: center;
gap: 8px;
}
.arrow-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-rows: repeat(3, 50px);
gap: 8px;
justify-content: center;
margin-top: 10px;
}
.arrow-btn {
background: #333;
border: 1px solid #555;
color: #eee;
font-size: 18px;
border-radius: 10px;
cursor: pointer;
width: 100%%;
height: 100%%;
}
.arrow-btn.active {
background: #0a84ff;
}
.arrow-btn:disabled {
opacity: 0.5;
cursor: default;
}
button#applyBtn {
margin-top: 15px;
padding: 10px 20px;
border-radius: 8px;
border: none;
background: #0a84ff;
color: #fff;
font-size: 16px;
cursor: pointer;
}
button#applyBtn:active {
transform: scale(0.97);
}
</style>
</head>
<body>
<div class="container">
<h2>Robojax ESP32-S3 Matrix Text Scroll</h2>
<div class="row">
<span class="label">Text to scroll:</span>
<input type="text" id="text" value="Hello" />
</div>
<div class="row">
<span class="label">Color:</span>
<input type="color" id="color" value="#ffffff" />
</div>
<div class="row">
<span class="label">Scroll Delay (fast → slow):</span>
<input
type="range"
id="speed"
min="20"
max="300"
value="80"
oninput="document.getElementById('speedVal').innerText = this.value + ' ms';"
/>
<div id="speedVal" style="text-align:right;font-size:12px;margin-top:4px;">
80 ms
</div>
</div>
<div class="row">
<label class="toggle">
<input type="checkbox" id="power" checked />
<span>Display ON</span>
</label>
</div>
<div class="row">
<span class="label">Scroll Direction:</span>
<div class="arrow-grid">
<div></div>
<button class="arrow-btn" id="btnUp" onclick="setDir('up')">▲</button>
<div></div>
<button class="arrow-btn" id="btnLeft" onclick="setDir('left')">◀</button>
<div></div>
<button class="arrow-btn" id="btnRight" onclick="setDir('right')">▶</button>
<div></div>
<button class="arrow-btn" id="btnDown" onclick="setDir('down')">▼</button>
<div></div>
</div>
</div>
<button id="applyBtn" onclick="sendUpdate()">Apply</button>
<p id="status"></p>
</div>
<script>
let currentDir = 'left';
function setDir(dir) {
currentDir = dir;
document.querySelectorAll('.arrow-btn').forEach(b => b.classList.remove('active'));
if (dir === 'up') document.getElementById('btnUp').classList.add('active');
if (dir === 'down') document.getElementById('btnDown').classList.add('active');
if (dir === 'left') document.getElementById('btnLeft').classList.add('active');
if (dir === 'right') document.getElementById('btnRight').classList.add('active');
sendUpdate();
}
function sendUpdate() {
const text = document.getElementById('text').value;
const color = document.getElementById('color').value;
const power = document.getElementById('power').checked ? '1' : '0';
const speed = document.getElementById('speed').value;
const url = `/update?text=${encodeURIComponent(text)}&color=${encodeURIComponent(color)}&power=${power}&dir=${currentDir}&speed=${speed}`;
fetch(url)
.then(r => r.text())
.then(t => {
document.getElementById('status').innerText = t;
})
.catch(err => {
document.getElementById('status').innerText = 'Error sending update';
});
}
// Set default active
setDir('left');
</script>
</body>
</html>
)rawliteral";
// ======================= HELPERS ===============================
bool isHexChar(char c) {
return (c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F');
}
String urlDecode(const String &src) {
String result;
result.reserve(src.length());
for (size_t i = 0; i < src.length(); i++) {
char c = src[i];
if (c == '+') {
result += ' ';
} else if (c == '%' && i + 2 < src.length()) {
char h1 = src[i + 1];
char h2 = src[i + 2];
if (isHexChar(h1) && isHexChar(h2)) {
char hex[3] = {h1, h2, 0};
int val = (int)strtol(hex, nullptr, 16);
result += (char)val;
i += 2;
} else {
result += c;
}
} else {
result += c;
}
}
return result;
}
uint16_t colorFromHex(const String &hex) {
// Expect "#RRGGBB" or "RRGGBB"
String c = hex;
if (c.startsWith("#")) c.remove(0, 1);
if (c.length() != 6) {
// default white
return matrix.Color(255, 255, 255);
}
long value = strtol(c.c_str(), NULL, 16);
uint8_t r = (value >> 16) & 0xFF;
uint8_t g = (value >> 8) & 0xFF;
uint8_t b = (value) & 0xFF;
return matrix.Color(r, g, b);
}
void resetScrollPosition() {
int textWidth = scrollText.length() * 6; // default font ~6 px per char
int textHeight = 8; // 5x7 font fits in 8
switch (currentDir) {
case DIR_LEFT:
// Start from just outside the right edge
scrollX = MATRIX_WIDTH;
scrollY = 0;
break;
case DIR_RIGHT:
// Start from just outside the left edge
scrollX = -textWidth;
scrollY = 0;
break;
case DIR_UP:
// Per-letter vertical scroll, starting with first character below matrix
vertCharIndex = 0;
vertY = MATRIX_HEIGHT; // 8 → enters from bottom
break;
case DIR_DOWN:
// Per-letter vertical scroll, starting with first character above matrix
vertCharIndex = 0;
vertY = -textHeight; // -8 → enters from top
break;
}
}
// ======================= HTTP HANDLERS =========================
void handleRoot() {
server.send_P(200, "text/html", MAIN_page);
}
void handleUpdate() {
if (server.hasArg("text")) {
String txt = urlDecode(server.arg("text"));
if (txt.length() == 0) {
scrollText = " ";
} else {
scrollText = txt;
}
}
if (server.hasArg("color")) {
String hex = server.arg("color");
textColor = colorFromHex(hex);
}
if (server.hasArg("power")) {
String p = server.arg("power");
displayOn = (p == "1");
}
if (server.hasArg("dir")) {
String d = server.arg("dir");
if (d == "left") currentDir = DIR_LEFT;
else if (d == "right") currentDir = DIR_RIGHT;
else if (d == "up") currentDir = DIR_UP;
else if (d == "down") currentDir = DIR_DOWN;
}
if (server.hasArg("speed")) {
String s = server.arg("speed");
int val = s.toInt();
// simple safety clamp
if (val < 10) val = 10; // very fast
if (val > 1000) val = 1000; // 1 second max
scrollInterval = (unsigned long)val;
// Serial.print("Scroll interval set to ");
// Serial.print(scrollInterval);
// Serial.println(" ms");
}
resetScrollPosition();
server.send(200, "text/plain", "Updated");
}
// ======================= SCROLL LOGIC ==========================
void drawScroll() {
if (!displayOn) {
matrix.fillScreen(0);
matrix.show();
return;
}
int textWidth = scrollText.length() * 6;
int textHeight = 8;
matrix.fillScreen(0);
matrix.setTextSize(1);
matrix.setTextWrap(false);
matrix.setTextColor(textColor);
// -------- HORIZONTAL SCROLL (LEFT / RIGHT) --------
if (currentDir == DIR_LEFT || currentDir == DIR_RIGHT) {
matrix.setCursor(scrollX, 0);
matrix.print(scrollText);
matrix.show();
if (currentDir == DIR_LEFT) {
scrollX--;
if (scrollX < -textWidth) {
scrollX = MATRIX_WIDTH;
}
} else { // DIR_RIGHT
scrollX++;
if (scrollX > MATRIX_WIDTH) {
scrollX = -textWidth;
}
}
return;
}
// -------- VERTICAL SCROLL (UP / DOWN) – PER LETTER --------
if (scrollText.length() == 0) {
matrix.show();
return;
}
// Current character
char c = scrollText[vertCharIndex];
int charWidth = 6;
int baseX = (MATRIX_WIDTH - charWidth) / 2; // center horizontally
matrix.setCursor(baseX, vertY);
matrix.print(c);
matrix.show();
if (currentDir == DIR_UP) {
// Move character upwards
vertY--;
if (vertY < -textHeight) {
// This character is fully passed, go to next
vertY = MATRIX_HEIGHT;
vertCharIndex++;
if (vertCharIndex >= scrollText.length()) {
vertCharIndex = 0; // loop back to first character
}
}
} else if (currentDir == DIR_DOWN) {
// Move character downwards
vertY++;
if (vertY > MATRIX_HEIGHT) {
// This character is fully passed, go to next
vertY = -textHeight;
vertCharIndex++;
if (vertCharIndex >= scrollText.length()) {
vertCharIndex = 0; // loop back
}
}
}
}
// ======================= WIFI INIT =============================
void startAPFallback() {
Serial.println("Starting AP fallback...");
WiFi.mode(WIFI_AP);
bool apOk = WiFi.softAP(AP_SSID, AP_PASS);
if (apOk) {
Serial.print("AP started. SSID: ");
Serial.print(AP_SSID);
Serial.print(" IP: ");
Serial.println(WiFi.softAPIP());
} else {
Serial.println("Failed to start AP.");
}
}
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
Serial.print("Connecting to WiFi ");
Serial.print(WIFI_SSID);
unsigned long startAttempt = millis();
const unsigned long timeout = 10000; // 10s
while (WiFi.status() != WL_CONNECTED && (millis() - startAttempt) < timeout) {
Serial.print(".");
delay(500);
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.print("Connected. IP address: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("WiFi connect failed, starting AP fallback.");
startAPFallback();
}
}
// ======================= SETUP & LOOP ==========================
void setup() {
Serial.begin(115200);
delay(500);
// Matrix init
matrix.begin();
matrix.setRotation(MATRIX_ROTATION);
matrix.setBrightness(BRIGHTNESS);
matrix.fillScreen(0);
matrix.show();
// WiFi
connectWiFi();
// Web server routes
server.on("/", handleRoot);
server.on("/update", handleUpdate);
server.onNotFound([]() {
server.send(404, "text/plain", "Not found");
});
server.begin();
Serial.println("HTTP server started.");
resetScrollPosition();
}
void loop() {
server.handleClient();
unsigned long now = millis();
if (now - lastScrollTime >= scrollInterval) {
lastScrollTime = now;
drawScroll();
}
}
Things you might need
-
Amazon
-
eBay
-
AliExpressPurchase ESP32-S3 RGB Matrix from AliExpresss.click.aliexpress.com
-
AliExpressPurchase ESP32-S3 RGB Matrix from AliExpress (2)s.click.aliexpress.com
Resources & references
-
Internal🎨 Color picker Toolrobojax.com
Files📁
Fritzing File
-
esp32-S3-supermini-tht fritzing part
esp32-S3-supermini-tht.fzpz0.02 MB