Cerca codice

Progetto ESP32-S3 Matrice LED RGB 3 - Testo da telefono cellulare

Progetto ESP32-S3 Matrice LED RGB 3 - Testo da telefono cellulare

Progetto 3 - Testo della matrice di controllo dal tuo telefono (Testo HTTP)

In questo progetto il modulo ESP32-S3 RGB LED Matrix ospita una piccola pagina web in modo da poter modificare il testo scorrevole, il colore, la direzione e la velocità direttamente dal tuo telefono o computer. Non hai bisogno di un'app separata, solo di un browser web. Questo rende il modulo un piccolo display di testo Wi-Fi che puoi aggiornare in tempo reale.

Tutti e sei i progetti di questa serie sono spiegati e dimostrati in un video di YouTube. Lo stesso video è incorporato in questa pagina, quindi puoi vedere esattamente come appare l'interfaccia web e come il testo si aggiorna istantaneamente sulla matrice. Il codice sorgente completo per questo progetto viene caricato automaticamente sotto l'articolo, e puoi acquistare il modulo matrice LED RGB ESP32-S3 presso i negozi affiliati elencati nella sezione codice.

In questo articolo ci concentriamo su come funziona la logica di rete (Wi-Fi domestico vs punto di accesso) e su quali impostazioni puoi modificare nel codice per personalizzare il comportamento.

Panoramica del Modulo Matrice LED RGB ESP32-S3

L'hardware è lo stesso di tutti gli altri progetti di questa serie: una scheda microcontrollore ESP32-S3 con una matrice LED RGB 8×8 incorporata e un sensore di movimento QMI8658C sul retro. La porta USB-C è utilizzata per l'alimentazione e la programmazione, e i pin attorno ai bordi sono ancora disponibili per altre I/O.:contentReference[oaicite:0]{index=0}

  • ESP32-S3Microcontrollore con capacità Wi-Fi e Bluetooth.
  • matrice RGB 8×8- 64 LED RGB indirizzabili per testo e grafica.
  • accelerometro QMI8658C- utilizzato nei progetti di tilt e gioco.
  • porta USB- alimenta la scheda e carica gli sketch dall'IDE di Arduino.
  • Pin esposti- consentire sensori o attuatori aggiuntivi se necessario.
  • Pulsanti di avvio/reimpostazione- per il caricamento del firmware e il riavvio.

Per il Progetto 3, la caratteristica più importante è il Wi-Fi dell'ESP32, che consente alla scheda di funzionare come un piccolo server web per la pagina di controllo del testo.:contentReference[oaicite:1]{index=1}

Progetti trattati nel video (Timestamp)

Il video unico per questa serie copre tutti e sei i progetti. Per un rapido riferimento:

  • 00:00- Introduzione
  • 02:01- Installazione delle schede ESP32
  • 03:32- Installazione delle librerie
  • 05:32- Progetto 1: Punto in movimento
  • 11:11- Progetto 2: Scorrimento del Testo
  • 12:59-Progetto 3: HTTP Testo (questo progetto)
  • 16:41- Progetto 4: Tilt Dot
  • 18:55- Progetto 5: Freccia Su
  • 20:02- Progetto 6: Gioco del bersaglio

Si consiglia di guardare la sezione HTTP Text nel video mentre si lavora con questo articolo. Il video mostra come viene generata la pagina web dall'ESP32 e come le modifiche al testo, al colore e alla velocità vengano riflesse istantaneamente sulla matrice di LED.:contentReference[oaicite:2]{index=2}

Installazione delle schede ESP32 nell'Arduino IDE

Se hai già completato i Progetti 1 o 2, la configurazione della scheda è stata completata e puoi saltare questa sezione. Altrimenti, segui questi passaggi nell'IDE Arduino:

  1. ApriFile > Preferencese aggiungi l'URL delle schede ESP32 a "URL aggiuntivi del gestore schede".
  2. Vai aTools > Board > Boards Manager…, cerca diESP32e installa il pacchetto ufficiale ESP32.
  3. Seleziona la scheda matrice RGB ESP32-S3 corretta daTools > Board.
  4. Collega il modulo tramite USB e scegli la porta seriale corretta sottoTools > Port.

Senza il supporto corretto per la scheda ESP32 e la porta giusta, lo sketch del server web non verrà caricato.

Installazione di NeoMatrix e librerie richieste

Questo progetto utilizza le stesse librerie del precedente progetto di scorrimento del testo:

  • Adafruit NeoMatrix
  • Adafruit NeoPixel
  • Adafruit GFX Library

Installa tramite il Gestore Librerie:

  1. ApriSketch > Include Library > Manage Libraries….
  2. Cerca perAdafruit NeoMatrixe fai clicInstalla.
  3. Accetta l'installazione delle dipendenzeAdafruit GFXeAdafruit NeoPixel).

Una volta installati, dovresti vedere gli esempi di sketch NeoMatrix e NeoPixel sottoFile > Examples.

Due modalità Wi-Fi nel Progetto 3

Il concetto più importante di questo progetto è che l'ESP32 può funzionare indue modalità diverse:

  1. Modalità Stazione (STA)- l'ESP32 si connette alla tua rete Wi-Fi domestica esistente.
  2. Modalità Access Point (AP)L'ESP32 crea la propria rete Wi-Fi se la rete Wi-Fi di casa non è disponibile.

Entrambi i modelli utilizzano la stessa interfaccia web: una pagina HTML servita direttamente dall'ESP32, dove puoi cambiare il testo, il colore, la direzione di scorrimento e la velocità.:contentReference[oaicite:3]{index=3}

Modalità 1 - Connessione al Wi-Fi di casa (Modalità Stazione)

In modalità Stazione, l'ESP32 si connette alla rete Wi-Fi di casa o dell'ufficio. Questa è la modalità preferita ogni volta che il tuo router è disponibile perché:

  • Il tuo telefono e computer sono già connessi alla stessa rete Wi-Fi.
  • Puoi puntare il tuo browser all'indirizzo IP dell'ESP32 e controllare il testo da qualsiasi dispositivo su quella rete.

Nella sezione delle impostazioni dello sketch, fornisci il tuo SSID Wi-Fi e la password:


// Home Wi-Fi credentials (Station mode)
const char* WIFI_SSID = "YourHomeWiFi";
const char* WIFI_PASS = "YourHomePassword";

Dopo che la scheda si avvia, tenta di connettersi aWIFI_SSIDSe ha successo, il codice stampa l'indirizzo IP assegnato al Monitor Serial, per esempio:


Connected to WiFi
IP address: 192.168.1.16

Per controllare il testo:

  1. Assicurati che il tuo telefono o PC sia connesso alla stessa rete Wi-Fi (ad esempio,YourHomeWiFi).
  2. Apri un browser e inserisci l'indirizzo IP stampato, comehttp://192.168.1.16/. :contentReference[oaicite:4]{index=4}
  3. La pagina di controllo apparirà, consentendoti di digitare testo, scegliere il colore, selezionare la direzione e regolare la velocità di scorrimento.

Modalità 2 - Punto di Accesso Standalone (Modalità AP)

Se l'ESP32 non riesce a connettersi al Wi-Fi di casa (password errata, rete non disponibile o se stai utilizzando il modulo all'esterno), lo sketch torna automaticamente alla modalità Punto di Accesso. In modalità AP, l'ESP32 diventa un hotspot Wi-Fi con il proprio nome di rete e password.

In questo progetto, le impostazioni AP sono fissate a:


// Access Point (AP) credentials (fallback mode)
const char* AP_SSID = "ESP32";
const char* AP_PASS = "password";

Quando la modalità Stazione fallisce, il modulo passa alla modalità AP e inizia a trasmettere una rete Wi-Fi chiamataESP32. Per controllare la matrice:

  1. Sul tuo telefono o computer, apri le impostazioni Wi-Fi e connettiti alla rete.ESP32.
  2. Inserisci la passwordpassword(come definito nel codice).
  3. Una volta connesso, apri un browser e vai ahttp://192.168.4.1/(l'indirizzo IP predefinito per la modalità AP dell'ESP32).
  4. La stessa pagina di controllo appare, permettendoti di cambiare testo, colore, velocità e direzione.

Questo comportamento di fallback rende il progetto utile ovunque: a casa, in laboratorio o in un ambiente dimostrativo dove non è disponibile alcun router.

Progetto 3 - Impostazioni principali nel codice

Il completo schema del testo HTTP è caricato sotto questo articolo dal sito web. Qui documentiamo solo le opzioni di configurazione più importanti che è probabile tu debba modificare.

Impostazioni Wi-Fi e Punto di Accesso

In cima del disegno troverai la sezione di configurazione Wi-Fi. Cambia solo le credenziali della Stazione (Wi-Fi di casa); le impostazioni AP sono normalmente mantenute come predefinite:


// ---------- Wi-Fi SETTINGS ----------

// Home Wi-Fi (Station mode)
const char* WIFI_SSID = "YourHomeWiFi";      // put your router SSID here
const char* WIFI_PASS = "YourHomePassword";  // put your router password here

// Fallback Access Point (AP mode)
const char* AP_SSID = "ESP32";               // fixed AP name
const char* AP_PASS = "password";            // fixed AP password

Comportamento:

  • SeWIFI_SSIDeWIFI_PASSsono corretti e la rete è disponibile → ESP32 si connette come un normale dispositivo Wi-Fi (modalità Stazione).
  • Se la connessione fallisce dopo un timeout → ESP32 avvia il proprio AP utilizzandoAP_SSIDeAP_PASS.

Pin della matrice, dimensione e luminosità

Queste impostazioni sono le stesse dei progetti precedenti:


// Matrix configuration
const int MATRIX_PIN    = 14;   // RGB matrix data pin
const int MATRIX_WIDTH  = 8;
const int MATRIX_HEIGHT = 8;

// Overall display brightness (0–255)
uint8_t matrixBrightness = 40;  // adjust for your environment

MantieniMATRIX_PINat14per questa scheda. Puoi aumentarematrixBrightnessse hai bisogno di più luce, ma valori più bassi sono più comodi per la visualizzazione da vicino.

Impostazioni predefinite del testo e di scorrimento

Quando la scheda si avvia, visualizza un messaggio iniziale fino a quando non apri la pagina web e non ne digiti uno nuovo. Puoi cambiare il testo predefinito nella configurazione:


// Default message shown at startup
String currentText = "Robojax";   // overwrite from web UI later

Il resto del comportamento dello scroll è controllato da un insieme di variabili che vengono aggiornate dall'interfaccia web:


// Scroll delay in milliseconds (lower = faster)
int scrollDelayMs = 50;

// Scroll direction: 0=left, 1=right, 2=up, 3=down
int scrollDirection = 0;   // default: scroll left

La pagina web invia nuovi valori basati sulle selezioni dello slider e dei pulsanti. Dal lato di Arduino, devi solo sapere che:

  • DecrescentescrollDelayMsrende il testo più veloce.
  • In aumentoscrollDelayMslo fa muovere più lentamente.
  • CambiandoscrollDirectionpassa tra modalità di scorrimento a sinistra, a destra, in alto o in basso.

Colore del testo (Controllato dalla pagina web)

Il colore del testo è controllato da tre valori compresi tra 0 e 255 (rosso, verde, blu). Vengono aggiornati ogni volta che scegli un nuovo colore sulla pagina web:


// Current text color (R, G, B)
uint8_t textRed   = 255;
uint8_t textGreen = 255;
uint8_t textBlue  = 255;

Quando scegli un colore nel browser e clicchi su Applica, l'ESP32 analizza i valori RGB e aggiorna queste tre variabili; il testo cambia immediatamente colore sulla matrice. Nel video, questo comportamento è dimostrato con molteplici variazioni di colore, inclusi esempi di rosso, verde e blu.:contentReference[oaicite:5]{index=5}

Riepilogo

Il Progetto 3 trasforma il tuo matrice LED RGB ESP32-S3 in un display testuale completamente wireless che puoi controllare utilizzando qualsiasi dispositivo con un browser web. Lo sketch è progettato per essere robusto:

  • Prima tenta di connettersi al tuo Wi-Fi domestico utilizzando l'SSID e la password che hai configurato.
  • Se ciò fallisce, diventa automaticamente un punto di accesso con il nomeESP32e passwordpassword.
  • In entrambe le modalità, l'apertura dell'indirizzo IP corretto in un browser visualizza la stessa pagina di controllo per testo, colore, direzione e velocità.

Il codice HTTP Text completo è disponibile sotto questo articolo (caricato automaticamente sul sito web). Per una guida passo passo completa e una dimostrazione dal vivo su come il testo si aggiorna in tempo reale, assicurati di guardare la sezione Progetto 3 del video. Se desideri costruire il progetto da solo, puoi anche acquistare il modulo LED Matrix RGB ESP32-S3 utilizzando i link affiliati elencati sotto il codice.

Immagini

ESP32 S3 Matrix
ESP32 S3 Matrix
ESP32 S3 Matrix  pin out
ESP32 S3 Matrix pin out
ESP32-S3_RGB_8x8_matrix-3
ESP32-S3_RGB_8x8_matrix-3
ESP32 S3 Matrix displaying rainbow heart
ESP32 S3 Matrix displaying rainbow heart
ESP32-S3_RGB_8x8_matrix1
ESP32-S3_RGB_8x8_matrix1
ESP32-S3_RGB_8x8_matrix-2
ESP32-S3_RGB_8x8_matrix-2
ESP32-S3 RGB Matrix- Mobile Phone Text
ESP32-S3 RGB Matrix- Mobile Phone Text
801-ESP32-S3 RGB LED Matrix Project 3 - Text from mobile phone
Lingua: C++
/*
 * Progetto 4: Scorrimento Testo HTTP - Matrice LED RGB ESP32-S3 (Waveshare)
 * 
 * - Si connette al tuo WiFi domestico (modalità stazione, con fallback AP).
 * - Serve una pagina web dove puoi impostare:
 * Testo
 * Colore
 * Display ON/OFF
 * Direzione di scorrimento (Sinistra / Destra / Su / Giù)
 * Ritardo di scorrimento (velocità)
 * - Supporta matrice LED RGB 8×8 utilizzando Adafruit_NeoMatrix.
 * 
 * ▶️ Tutorial Video:
 * https://youtu.be/JKLuYrRcLMI
 * 
 * 📄 Risorse & Pagina Codice:
 * 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>

 // ======================= IMPOSTAZIONI WIFI =========================
 // WiFi domestico (cambia queste con le credenziali del tuo router)
const char* WIFI_SSID = "Biseem";
const char* WIFI_PASS = "wan9&Jang~";

 // Punto di accesso di emergenza
const char* AP_SSID = "ESP32";
const char* AP_PASS = "password";

 // ======================= IMPOSTAZIONI MATRICE =======================

#define MATRIX_PIN    14
#define MATRIX_WIDTH  8
#define MATRIX_HEIGHT 8

 // 0, 1, 2 o 3 - regola se l'orientamento del testo è sbagliato con USB in alto
#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
);

 // ======================= STATO DELLA SCORRIMENTO ==========================

enum ScrollDir {
  DIR_LEFT = 0,
  DIR_RIGHT,
  DIR_UP,
  DIR_DOWN
};

String   scrollText  = "Robojax";
uint16_t textColor   = 0xFFFF; // bianco predefinito
bool     displayOn   = true;
ScrollDir currentDir = DIR_LEFT;

 // 1 passo ogni questo numero di ms (minore = più veloce)
unsigned long scrollInterval = 80;
unsigned long lastScrollTime = 0;

 // Posizione di scorrimento orizzontale
int scrollX = MATRIX_WIDTH;
int scrollY = 0; // fila superiore

 // Stato per lo scorrimento verticale per lettera
int vertCharIndex = 0; // quale carattere della stringa
int vertY         = MATRIX_HEIGHT; // posizione verticale di quel carattere

 // ======================= SERVER WEB ============================

WebServer server(80);

 // ======================= PAGINA HTML ============================

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')">&#9650;</button>
        <div></div>

        <button class="arrow-btn" id="btnLeft" onclick="setDir('left')">&#9664;</button>
        <div></div>
        <button class="arrow-btn" id="btnRight" onclick="setDir('right')">&#9654;</button>

        <div></div>
        <button class="arrow-btn" id="btnDown" onclick="setDir('down')">&#9660;</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';
      });
  }

 // Imposta attivo predefinito
  setDir('left');
</script>
</body>
</html>
)rawliteral";

 // ======================= AIUTANTI ===============================

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) {
 // Aspettati "#RRGGBB" o "RRGGBB"
  String c = hex;
  if (c.startsWith("#")) c.remove(0, 1);
  if (c.length() != 6) {
 // bianco predefinito
    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; // carattere predefinito ~6 px per carattere
  int textHeight = 8; // Il carattere 5x7 si adatta in 8

  switch (currentDir) {
    case DIR_LEFT:
 // Inizia appena all'esterno del bordo destro.
      scrollX = MATRIX_WIDTH;
      scrollY = 0;
      break;

    case DIR_RIGHT:
 // Inizia appena al di fuori del bordo sinistro.
      scrollX = -textWidth;
      scrollY = 0;
      break;

    case DIR_UP:
 // Scorrimento verticale per lettera, partendo dal primo carattere sotto la matrice
      vertCharIndex = 0;
      vertY = MATRIX_HEIGHT; // 8 → entra da basso
      break;

    case DIR_DOWN:
 // Scorrimento verticale per lettera, iniziando con il primo carattere sopra la matrice
      vertCharIndex = 0;
      vertY = -textHeight; // -8 → entra dall'alto
      break;
  }
}

 // ======================= GESTORI HTTP =========================

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();

 // morsetto di sicurezza semplice
    if (val < 10)   val = 10; // molto veloce
    if (val > 1000) val = 1000; // 1 secondo max

    scrollInterval = (unsigned long)val;
 // Serial.print("Intervallo di scorrimento impostato su ");
 // Serial.print(scrollInterval);
 // Serial.println(" ms");
  }

  resetScrollPosition();
  server.send(200, "text/plain", "Updated");
}

 // ======================= LOGICA SCORRIMENTO ==========================

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);

 // -------- SCORRIMENTO ORIZZONTALE (SINISTRA / DESTRA) --------
  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_DESTRA
      scrollX++;
      if (scrollX > MATRIX_WIDTH) {
        scrollX = -textWidth;
      }
    }
    return;
  }

 // -------- SCORRIMENTO VERTICALE (SU / GIÙ) - PER LETTERA --------

  if (scrollText.length() == 0) {
    matrix.show();
    return;
  }

 // Personaggio attuale
  char c = scrollText[vertCharIndex];

  int charWidth  = 6;
  int baseX = (MATRIX_WIDTH - charWidth) / 2; // centrare orizzontalmente

  matrix.setCursor(baseX, vertY);
  matrix.print(c);
  matrix.show();

  if (currentDir == DIR_UP) {
 // Sposta il personaggio verso l'alto
    vertY--;
    if (vertY < -textHeight) {
 // Questo personaggio è completamente superato, vai al successivo.
      vertY = MATRIX_HEIGHT;
      vertCharIndex++;
      if (vertCharIndex >= scrollText.length()) {
        vertCharIndex = 0; // torna al primo carattere
      }
    }
  } else if (currentDir == DIR_DOWN) {
 // Sposta il personaggio verso il basso
    vertY++;
    if (vertY > MATRIX_HEIGHT) {
 // Questo personaggio è completamente superato, vai al successivo.
      vertY = -textHeight;
      vertCharIndex++;
      if (vertCharIndex >= scrollText.length()) {
        vertCharIndex = 0; // ritorno al ciclo
      }
    }
  }
}

 // ======================= INIZIO WIFI =============================

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();
  }
}

 // ======================= CONFIGURAZIONE E CICLO ==========================

void setup() {
  Serial.begin(115200);
  delay(500);

 // Inizializzazione della matrice
  matrix.begin();
  matrix.setRotation(MATRIX_ROTATION);
  matrix.setBrightness(BRIGHTNESS);
  matrix.fillScreen(0);
  matrix.show();

 // WiFi
  connectWiFi();

 // Percorsi del server web
  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();
  }
}

Cose di cui potresti avere bisogno

Risorse e riferimenti

File📁

File Fritzing