Search Code

ESP32-S3 RGB LED Matris Projesi 3 - Cep telefonundan metin

ESP32-S3 RGB LED Matris Projesi 3 - Cep telefonundan metin

Proje 3 – Matris Metnini Telefonunuzdan Kontrol Edin (HTTP Metni)

Bu projede ESP32-S3 RGB LED Matris, küçük bir web sayfası barındırır; böylece kayan metni, rengi, yönü ve hızı doğrudan telefonunuzdan veya bilgisayarınızdan değiştirebilirsiniz. Ayrı bir uygulamaya ihtiyacınız yok – sadece bir web tarayıcısı yeterli. Bu, modülü gerçek zamanlı olarak güncelleyebileceğiniz küçük bir Wi-Fi metin ekranı haline getirir.

Bu serideki altı projenin tamamı tek bir YouTube videosunda açıklanmakta ve gösterilmektedir. Aynı video bu sayfaya gömülüdür; böylece web arayüzünün nasıl göründüğünü ve metnin matris üzerinde anında nasıl güncellendiğini tam olarak görebilirsiniz. Bu projenin tam kaynak kodu makalenin altında otomatik olarak yüklenir ve ESP32-S3 RGB LED Matris modülünü kod bölümünün altında listelenen bağlı kuruluş mağazalarından satın alabilirsiniz.

Bu makalede ağ mantığının nasıl çalıştığına (ev Wi-Fi'i ile erişim noktası) ve davranışı özelleştirmek için kodda hangi ayarları değiştirebileceğinize odaklanıyoruz.

ESP32-S3 RGB LED Matris Modülü Genel Bakış

Donanım, bu serideki diğer tüm projelerle aynıdır: dahili 8×8 RGB LED matrisli ve arkasında QMI8658C hareket sensörlü bir ESP32-S3 mikrodenetleyici kartı. USB-C bağlantı noktası güç ve programlama için kullanılır ve kenarlardaki pinler diğer G/Ç işlemleri için hâlâ kullanılabilir.:contentReference[oaicite:0]{index=0}

  • ESP32-S3 – Wi-Fi ve Bluetooth destekli mikrodenetleyici.
  • 8×8 RGB matris – metin ve grafikler için 64 adreslenebilir RGB LED.
  • QMI8658C ivmeölçer – eğim ve oyun projelerinde kullanılır.
  • USB bağlantı noktası – karta güç sağlar ve Arduino IDE'den taslakları yükler.
  • Açık pinler – gerekirse ek sensörler veya aktüatörler bağlamaya olanak tanır.
  • Boot/Reset düğmeleri – donanım yazılımı yükleme ve yeniden başlatma için.

Proje 3 için en önemli özellik, kartın metin kontrol sayfası için küçük bir web sunucusu gibi davranmasını sağlayan ESP32'nin Wi-Fi özelliğidir.:contentReference[oaicite:1]{index=1}

Videoda Kapsanan Projeler (Zaman Damgaları)

Bu serinin tek videosu altı projenin tamamını kapsar. Hızlı başvuru için:

  • 00:00 – Giriş
  • 02:01 – ESP32 kartlarını yükleme
  • 03:32 – Kütüphaneleri yükleme
  • 05:32 – Proje 1: Hareket Eden Nokta
  • 11:11 – Proje 2: Metin Kaydırma
  • 12:59Proje 3: HTTP Metni (bu proje)
  • 16:41 – Proje 4: Eğim Noktası
  • 18:55 – Proje 5: Yukarı Ok
  • 20:02 – Proje 6: Hedef Oyunu

Bu makaleyle çalışırken videodaki HTTP Metni bölümünü izlemeniz önerilir. Video, web sayfasının ESP32 tarafından nasıl oluşturulduğunu ve metin, renk ve hız değişikliklerinin LED matris üzerinde anında nasıl yansıdığını gösterir.:contentReference[oaicite:2]{index=2}

Arduino IDE'de ESP32 Kartlarını Yükleme

Proje 1 veya 2'yi zaten tamamladıysanız kart kurulumu yapılmıştır ve bu bölümü atlayabilirsiniz. Aksi takdirde Arduino IDE'de şu adımları izleyin:

  1. Dosya > Tercihler bölümünü açın ve “Ek Kart Yöneticisi URL'leri”ne ESP32 kartları URL'sini ekleyin.
  2. Araçlar > Kart > Kart Yöneticisi… bölümüne gidin, ESP32 arayın ve resmi ESP32 paketini yükleyin.
  3. Araçlar > Kart bölümünden doğru ESP32-S3 RGB Matris kartını seçin.
  4. Modülü USB ile bağlayın ve Araçlar > Port altında doğru seri bağlantı noktasını seçin.

Uygun ESP32 kart desteği ve doğru bağlantı noktası olmadan web sunucusu taslağı yüklenmez.

NeoMatrix ve Gerekli Kütüphaneleri Yükleme

Bu proje, önceki metin kaydırma projesiyle aynı kütüphaneleri kullanır:

  • Adafruit NeoMatrix
  • Adafruit NeoPixel
  • Adafruit GFX Library

Kütüphane Yöneticisi üzerinden yükleyin:

  1. Eskiz > Kütüphane Ekle > Kütüphaneleri Yönet… bölümünü açın.
  2. Adafruit NeoMatrix arayın ve Yükle düğmesine tıklayın.
  3. Bağımlılıkların (Adafruit GFX ve Adafruit NeoPixel) yüklenmesini kabul edin.

Yüklendikten sonra Dosya > Örnekler altında NeoMatrix ve NeoPixel örnek taslaklarını görmelisiniz.

Proje 3'te İki Wi-Fi Modu

Bu projedeki en önemli kavram, ESP32'nin iki farklı modda çalışabilmesidir:

  1. İstasyon (STA) modu – ESP32 mevcut ev Wi-Fi ağınıza bağlanır.
  2. Erişim Noktası (AP) modu – ev Wi-Fi'i yoksa ESP32 kendi Wi-Fi ağını oluşturur.

Her iki mod da aynı web arayüzünü kullanır: ESP32'nin kendisinden sunulan ve metni, rengi, kaydırma yönünü ve hızı değiştirebileceğiniz bir HTML sayfası.:contentReference[oaicite:3]{index=3}

Mod 1 – Ev Wi-Fi'ine Bağlanma (İstasyon Modu)

İstasyon modunda ESP32, evinizin veya ofisinizin Wi-Fi ağına katılır. Yönlendiriciniz mevcut olduğunda tercih edilen mod budur çünkü:

  • Telefonunuz ve bilgisayarınız zaten aynı Wi-Fi ağına bağlıdır.
  • Tarayıcınızı ESP32'nin IP adresine yönlendirebilir ve bu ağdaki herhangi bir cihazdan metni kontrol edebilirsiniz.

Taslağın ayarlar bölümünde Wi-Fi SSID'nizi ve şifrenizi sağlarsınız:

// Ev Wi-Fi kimlik bilgileri (İstasyon modu) const char* WIFI_SSID = "YourHomeWiFi"; const char* WIFI_PASS = "YourHomePassword";

Kart açıldıktan sonra WIFI_SSID bağlantısını kurmayı dener. Başarılı olursa, kod atanan IP adresini Seri Monitöre yazdırır, örneğin:


Connected to WiFi
IP address: 192.168.1.16

Metni kontrol etmek için:

  1. Telefonunuzun veya bilgisayarınızın aynı Wi-Fi ağına bağlı olduğundan emin olun (örneğin, YourHomeWiFi).
  2. Bir tarayıcı açın ve yazdırılan IP adresini girin, örneğin http://192.168.1.16/. :contentReference[oaicite:4]{index=4}
  3. Kontrol sayfası görünecek ve metin yazmanıza, renk seçmenize, yön belirlemenize ve kaydırma hızını ayarlamanıza olanak tanır.

Mod 2 – Bağımsız Erişim Noktası (AP Modu)

ESP32 ev Wi-Fi'nize bağlanamazsa (yanlış şifre, ağ mevcut değil veya modülü dışarıda kullanıyorsanız), taslak otomatik olarak Erişim Noktası moduna geçer. AP modunda, ESP32'nin kendisi kendi ağ adı ve şifresiyle bir Wi-Fi erişim noktası haline gelir.

Bu projede, AP ayarları sabittir:


// Erişim Noktası (AP) kimlik bilgileri (yedek mod)
const char* AP_SSID = "ESP32";
const char* AP_PASS = "password";

İstasyon modu başarısız olduğunda, modül AP moduna geçer ve ESP32 adında bir Wi-Fi ağı yayınlamaya başlar. Matrisi kontrol etmek için:

  1. Telefonunuzda veya bilgisayarınızda Wi-Fi ayarlarını açın ve ESP32 ağına bağlanın.
  2. password şifresini girin (kodda tanımlandığı gibi).
  3. Bağlandıktan sonra bir tarayıcı açın ve http://192.168.4.1/ adresine gidin (ESP32 AP modu için varsayılan IP).
  4. Aynı kontrol sayfası görünür ve metin, renk, hız ve yönü değiştirmenize olanak tanır.

Bu yedek davranış, projeyi her yerde kullanışlı hale getirir: evde, laboratuvarda veya yönlendirici bulunmayan bir demo ortamında.

Proje 3 – Kodda Ana Ayarlar

Tam HTTP Metin taslağı, bu makalenin altında web sitesi tarafından yüklenir. Burada yalnızca düzenlemeniz muhtemel en önemli yapılandırma seçeneklerini belgeliyoruz.

Wi-Fi ve Erişim Noktası Ayarları

Taslağın üst kısmında Wi-Fi yapılandırma bölümünü bulacaksınız. Yalnızca İstasyon (ev Wi-Fi) kimlik bilgilerini değiştirin; AP ayarları normalde varsayılan olarak korunur:


// ---------- Wi-Fi AYARLARI ----------

// Ev Wi-Fi (İstasyon modu)
const char* WIFI_SSID = "YourHomeWiFi";      // yönlendirici SSID'nizi buraya yazın
const char* WIFI_PASS = "YourHomePassword";  // yönlendirici şifrenizi buraya yazın

// Yedek Erişim Noktası (AP modu)
const char* AP_SSID = "ESP32";               // sabit AP adı
const char* AP_PASS = "password";            // sabit AP şifresi

Davranış:

  • WIFI_SSID ve WIFI_PASS doğruysa ve ağ mevcutsa → ESP32 normal bir Wi-Fi cihazı olarak bağlanır (İstasyon modu).
  • Bağlantı bir zaman aşımından sonra başarısız olursa → ESP32, AP_SSID ve AP_PASS kullanarak kendi AP'sini başlatır.

Matris Pini, Boyutu ve Parlaklık

Bu ayarlar önceki projelerle aynıdır:


// Matris yapılandırması
const int MATRIX_PIN    = 14;   // RGB matris veri pini
const int MATRIX_WIDTH  = 8;
const int MATRIX_HEIGHT = 8;

// Genel ekran parlaklığı (0–255)
uint8_t matrixBrightness = 40;  // ortamınıza göre ayarlayın

Bu kart için MATRIX_PIN değerini 14 olarak tutun. Daha fazla ışık gerekiyorsa matrixBrightness değerini artırabilirsiniz, ancak daha düşük değerler yakından izleme için daha rahattır.

Varsayılan Metin ve Kaydırma Ayarları

Kart başladığında, web sayfasını açıp yeni bir metin yazana kadar bir başlangıç mesajı görüntüler. Varsayılan metni yapılandırmada değiştirebilirsiniz:


// Başlangıçta gösterilen varsayılan mesaj
String currentText = "Robojax";   // daha sonra web arayüzünden üzerine yazılır

Kaydırma davranışının geri kalanı, web arayüzü tarafından güncellenen bir dizi değişken tarafından kontrol edilir:


// Kaydırma gecikmesi milisaniye cinsinden (düşük = daha hızlı)
int scrollDelayMs = 50;

// Kaydırma yönü: 0=sol, 1=sağ, 2=yukarı, 3=aşağı
int scrollDirection = 0;   // varsayılan: sola kaydır

Web sayfası, kaydırıcı ve düğme seçimlerine göre yeni değerler gönderir. Arduino tarafından yalnızca şunu bilmeniz gerekir:

  • scrollDelayMs değerini azaltmak metnin daha hızlı hareket etmesini sağlar.
  • scrollDelayMs değerini artırmak daha yavaş hareket etmesini sağlar.
  • scrollDirection değerini değiştirmek sol, sağ, yukarı veya aşağı kaydırma modları arasında geçiş yapar.

Metin Rengi (Web Sayfasından Kontrol Edilir)

Metnin rengi, üç 0–255 değeri (kırmızı, yeşil, mavi) tarafından kontrol edilir. Web sayfasında yeni bir renk seçtiğinizde bunlar güncellenir:


// Geçerli metin rengi (R, G, B)
uint8_t textRed   = 255;
uint8_t textGreen = 255;
uint8_t textBlue  = 255;

Tarayıcıda bir renk seçip Uygula'ya tıkladığınızda, ESP32 RGB değerlerini ayrıştırır ve bu üç değişkeni günceller; metin matris üzerinde anında renk değiştirir. Videoda bu davranış, kırmızı, yeşil ve mavi örnekleri dahil birden fazla renk değişikliğiyle gösterilmiştir.:contentReference[oaicite:5]{index=5}

Özet

Proje 3, ESP32-S3 RGB LED Matrisinizi, web tarayıcısı olan herhangi bir cihazla kontrol edebileceğiniz tamamen kablosuz bir metin ekranına dönüştürür. Taslak sağlam olacak şekilde tasarlanmıştır:

  • Önce, yapılandırdığınız SSID ve şifre ile ev Wi-Fi ağınıza bağlanmayı dener.
  • Bu başarısız olursa, otomatik olarak ESP32 adında ve password şifresine sahip bir erişim noktası haline gelir.
  • Her iki modda da tarayıcıda doğru IP adresini açmak, metin, renk, yön ve hız için aynı kontrol sayfasını görüntüler.

Tam HTTP Metin kodu bu makalenin altında mevcuttur (web sitesinde otomatik olarak yüklenir). Metnin gerçek zamanlı olarak nasıl güncellendiğine dair adım adım tam bir anlatım ve canlı bir gösterim için videonun Proje 3 bölümünü izlemeyi unutmayın. Projeyi kendiniz oluşturmak isterseniz, kodun altında listelenen bağlı kuruluş bağlantılarını kullanarak ESP32-S3 RGB LED Matris modülünü de satın alabilirsiniz.

Görseller

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
Dil: C++
/*
  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')">&#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';
      });
  }

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

İhtiyacın olabileceğini düşünüyor

Kaynaklar ve referanslar

Dosyalar📁

Fritzing Dosyası