Hướng dẫn này là một phần của: Ma trận LED RGB ESP32-S3
Đây là một dự án thú vị và hữu ích sử dụng module ma trận RGB ESP32-S3. Các liên kết đến các video khác được cung cấp bên dưới bài viết này.
Dự án ma trận LED RGB ESP32-S3 3 - Văn bản từ điện thoại di động
Dự án 3 – Điều khiển Ma trận Chữ từ Điện thoại của Bạn (HTTP Text)
Trong dự án này, ESP32-S3 RGB LED Matrix lưu trữ một trang web nhỏ để bạn có thể thay đổi văn bản cuộn, màu sắc, hướng và tốc độ trực tiếp từ điện thoại hoặc máy tính của mình. Bạn không cần một ứng dụng riêng – chỉ cần một trình duyệt web. Điều này biến mô-đun thành một màn hình hiển thị văn bản Wi-Fi nhỏ mà bạn có thể cập nhật theo thời gian thực.
Tất cả sáu dự án trong loạt bài này được giải thích và trình diễn trong một video YouTube. Video tương tự được nhúng trên trang này, vì vậy bạn có thể xem chính xác giao diện web trông như thế nào và cách văn bản cập nhật ngay lập tức trên ma trận. Mã nguồn đầy đủ cho dự án này được tự động tải bên dưới bài viết và bạn có thể mua mô-đun ESP32-S3 RGB LED Matrix từ các cửa hàng liên kết được liệt kê dưới phần mã.
Trong bài viết này, chúng tôi tập trung vào cách logic mạng hoạt động (Wi-Fi gia đình so với điểm truy cập) và những cài đặt nào bạn có thể thay đổi trong mã để tùy chỉnh hành vi.



Tổng quan về Mô-đun ESP32-S3 RGB LED Matrix
Phần cứng giống như tất cả các dự án khác trong loạt bài này: một bo mạch vi điều khiển ESP32-S3 với ma trận RGB LED 8×8 tích hợp và cảm biến chuyển động QMI8658C ở mặt sau. Cổng USB-C được sử dụng để cấp nguồn và lập trình, và các chân xung quanh các cạnh vẫn có sẵn cho các IO khác.:contentReference[oaicite:0]{index=0}

- ESP32-S3 – Vi điều khiển có khả năng Wi-Fi và Bluetooth.
- Ma trận RGB 8×8 – 64 đèn LED RGB có thể định địa chỉ cho văn bản và đồ họa.
- Gia tốc kế QMI8658C – được sử dụng trong các dự án nghiêng và trò chơi.
- Cổng USB – cấp nguồn cho bo mạch và tải lên các bản phác thảo từ Arduino IDE.
- Các chân tiếp xúc – cho phép thêm cảm biến hoặc bộ truyền động nếu cần.
- Các nút Boot/Reset – để tải lên chương trình cơ sở và khởi động lại.
Đối với Dự án 3, tính năng quan trọng nhất là Wi-Fi của ESP32, cho phép bo mạch hoạt động như một máy chủ web nhỏ cho trang điều khiển văn bản.:contentReference[oaicite:1]{index=1}

Các Dự án Được Trình Bày trong Video (Dấu thời gian)
Một video cho loạt bài này bao gồm tất cả sáu dự án. Để tham khảo nhanh:
- 00:00 – Giới thiệu
- 02:01 – Cài đặt bo mạch ESP32
- 03:32 – Cài đặt thư viện
- 05:32 – Dự án 1: Chấm di chuyển
- 11:11 – Dự án 2: Cuộn văn bản
- 12:59 – Dự án 3: HTTP Text (dự án này)
- 16:41 – Dự án 4: Chấm nghiêng
- 18:55 – Dự án 5: Mũi tên lên
- 20:02 – Dự án 6: Trò chơi mục tiêu
Bạn nên xem phần HTTP Text trong video trong khi làm việc với bài viết này. Video cho thấy cách trang web được tạo bởi ESP32 và cách thay đổi văn bản, màu sắc và tốc độ được phản ánh ngay lập tức trên ma trận LED.:contentReference[oaicite:2]{index=2}
Cài đặt Bo mạch ESP32 trong Arduino IDE
Nếu bạn đã hoàn thành Dự án 1 hoặc 2, việc thiết lập bo mạch đã xong và bạn có thể bỏ qua phần này. Nếu không, hãy làm theo các bước sau trong Arduino IDE:
- Mở
File > Preferencesvà thêm URL bo mạch ESP32 vào “Additional Boards Manager URLs”. - Đi tới
Tools > Board > Boards Manager…, tìm kiếmESP32và cài đặt gói ESP32 chính thức. - Chọn bo mạch ESP32-S3 RGB Matrix chính xác từ
Tools > Board. - Kết nối mô-đun qua USB và chọn cổng nối tiếp chính xác dưới
Tools > Port.
Nếu không có hỗ trợ bo mạch ESP32 phù hợp và cổng chính xác, bản phác thảo máy chủ web sẽ không tải lên được.
Cài đặt NeoMatrix và các Thư viện Cần thiết
Dự án này sử dụng các thư viện giống như dự án cuộn văn bản trước đó:
Adafruit NeoMatrixAdafruit NeoPixelAdafruit GFX Library
Cài đặt qua Library Manager:
- Mở
Sketch > Include Library > Manage Libraries…. - Tìm kiếm
Adafruit NeoMatrixvà nhấp vào Install. - Chấp nhận cài đặt các phụ thuộc (
Adafruit GFXvàAdafruit NeoPixel).
Sau khi cài đặt, bạn sẽ thấy các bản phác thảo ví dụ NeoMatrix và NeoPixel dưới File > Examples.
Hai Chế độ Wi-Fi trong Dự án 3
Khái niệm quan trọng nhất trong dự án này là ESP32 có thể hoạt động ở hai chế độ khác nhau:
- Chế độ Station (STA) – ESP32 kết nối với mạng Wi-Fi gia đình hiện có của bạn.
- Chế độ Access Point (AP) – ESP32 tạo mạng Wi-Fi riêng của nó nếu Wi-Fi gia đình không khả dụng.
Cả hai chế độ đều sử dụng cùng một giao diện web: một trang HTML được phục vụ từ chính ESP32, nơi bạn có thể thay đổi văn bản, màu sắc, hướng cuộn và tốc độ.:contentReference[oaicite:3]{index=3}
Chế độ 1 – Kết nối với Wi-Fi gia đình (Chế độ Station)
Trong chế độ Station, ESP32 tham gia mạng Wi-Fi gia đình hoặc văn phòng của bạn. Đây là chế độ ưa thích bất cứ khi nào bộ định tuyến của bạn khả dụng vì:
- Điện thoại và máy tính của bạn đã được kết nối với cùng một mạng Wi-Fi.
- Bạn có thể trỏ trình duyệt của mình đến địa chỉ IP của ESP32 và điều khiển văn bản từ bất kỳ thiết bị nào trên mạng đó.
Trong phần cài đặt của bản phác thảo, bạn cung cấp SSID và mật khẩu Wi-Fi của mình:
// Thông tin Wi-Fi tại nhà (Chế độ Station)
const char* WIFI_SSID = "YourHomeWiFi";
const char* WIFI_PASS = "YourHomePassword";
Sau khi bo mạch khởi động, nó cố gắng kết nối với WIFI_SSID. Nếu thành công, mã sẽ in địa chỉ IP được cấp ra Serial Monitor, ví dụ:
Connected to WiFi
IP address: 192.168.1.16
Để điều khiển chữ:
- Đảm bảo điện thoại hoặc PC của bạn được kết nối với cùng mạng Wi-Fi (ví dụ:
YourHomeWiFi). - Mở trình duyệt và nhập địa chỉ IP đã in, chẳng hạn như
http://192.168.1.16/. - Trang điều khiển sẽ xuất hiện, cho phép bạn nhập chữ, chọn màu, chọn hướng và điều chỉnh tốc độ cuộn.
Chế độ 2 – Điểm truy cập độc lập (Chế độ AP)
Nếu ESP32 không thể kết nối với Wi-Fi tại nhà của bạn (sai mật khẩu, mạng không khả dụng hoặc bạn đang sử dụng mô-đun ở ngoài trời), sketch sẽ tự động chuyển sang Chế độ Điểm truy cập. Trong chế độ AP, bản thân ESP32 trở thành một điểm phát Wi-Fi với tên mạng và mật khẩu riêng của nó.
Trong dự án này, cài đặt AP được cố định như sau:
// Thông tin Điểm truy cập (AP) (chế độ dự phòng)
const char* AP_SSID = "ESP32";
const char* AP_PASS = "password";
Khi Chế độ Station thất bại, mô-đun chuyển sang chế độ AP và bắt đầu phát sóng một mạng Wi-Fi có tên là ESP32. Để điều khiển ma trận:
- Trên điện thoại hoặc máy tính, mở cài đặt Wi-Fi và kết nối với mạng ESP32.
- Nhập mật khẩu password (như được định nghĩa trong mã).
- Sau khi kết nối, mở trình duyệt và truy cập
http://192.168.4.1/(địa chỉ IP mặc định cho chế độ AP của ESP32). - Trang điều khiển tương tự sẽ xuất hiện, cho phép bạn thay đổi chữ, màu sắc, tốc độ và hướng.
Hành vi dự phòng này làm cho dự án hữu ích ở mọi nơi: tại nhà, trong phòng thí nghiệm hoặc trong môi trường demo không có bộ định tuyến.
Dự án 3 – Cài đặt chính trong mã
Sketch HTTP Text đầy đủ được tải bên dưới bài viết này bởi trang web. Ở đây chúng tôi chỉ ghi lại các tùy chọn cấu hình quan trọng nhất mà bạn có thể sẽ chỉnh sửa.
Cài đặt Wi-Fi và Điểm truy cập
Ở đầu sketch, bạn sẽ tìm thấy phần cấu hình Wi-Fi. Chỉ thay đổi thông tin đăng nhập Station (Wi-Fi tại nhà); cài đặt AP thường được giữ ở mặc định:
// ---------- CÀI ĐẶT WI-FI ----------
// Wi-Fi tại nhà (Chế độ Station)
const char* WIFI_SSID = "YourHomeWiFi"; // đặt SSID bộ định tuyến của bạn ở đây
const char* WIFI_PASS = "YourHomePassword"; // đặt mật khẩu bộ định tuyến của bạn ở đây
// Điểm truy cập dự phòng (Chế độ AP)
const char* AP_SSID = "ESP32"; // tên AP cố định
const char* AP_PASS = "password"; // mật khẩu AP cố định
Hành vi:
- Nếu
WIFI_SSIDvàWIFI_PASSđúng và mạng khả dụng → ESP32 kết nối như một thiết bị Wi-Fi bình thường (Chế độ Station). - Nếu kết nối thất bại sau một khoảng thời gian chờ → ESP32 tự khởi động AP của riêng mình bằng cách sử dụng
AP_SSIDvàAP_PASS.
Chân, Kích thước và Độ sáng Ma trận
Các cài đặt này giống như các dự án trước:
// Cấu hình ma trận
const int MATRIX_PIN = 14; // chân dữ liệu ma trận RGB
const int MATRIX_WIDTH = 8;
const int MATRIX_HEIGHT = 8;
// Độ sáng hiển thị tổng thể (0–255)
uint8_t matrixBrightness = 40; // điều chỉnh cho môi trường của bạn
Giữ MATRIX_PIN ở 14 cho bo mạch này. Bạn có thể tăng matrixBrightness nếu cần nhiều ánh sáng hơn, nhưng giá trị thấp hơn sẽ thoải mái hơn khi xem ở khoảng cách gần.
Cài đặt Chữ Mặc định và Cuộn
Khi bo mạch khởi động, nó hiển thị một thông điệp ban đầu cho đến khi bạn mở trang web và nhập một thông điệp mới. Bạn có thể thay đổi chữ mặc định trong cấu hình:
// Thông điệp mặc định hiển thị khi khởi động
String currentText = "Robojax"; // sẽ được ghi đè từ giao diện web sau
Phần còn lại của hành vi cuộn được điều khiển bởi một tập hợp các biến được cập nhật bởi giao diện web:
// Độ trễ cuộn tính bằng mili giây (thấp hơn = nhanh hơn)
int scrollDelayMs = 50;
// Hướng cuộn: 0=trái, 1=phải, 2=lên, 3=xuống
int scrollDirection = 0; // mặc định: cuộn sang trái
Trang web gửi các giá trị mới dựa trên lựa chọn thanh trượt và nút. Từ phía Arduino, bạn chỉ cần biết rằng:
- Giảm
scrollDelayMslàm cho chữ di chuyển nhanh hơn. - Tăng
scrollDelayMslàm cho nó di chuyển chậm hơn. - Thay đổi
scrollDirectionchuyển đổi giữa các chế độ cuộn trái, phải, lên hoặc xuống.
Màu Chữ (Được điều khiển từ Trang Web)
Màu của chữ được điều khiển bởi ba giá trị 0–255 (đỏ, lục, lam). Chúng được cập nhật bất cứ khi nào bạn chọn một màu mới trên trang web:
// Màu chữ hiện tại (R, G, B)
uint8_t textRed = 255;
uint8_t textGreen = 255;
uint8_t textBlue = 255;
Khi bạn chọn một màu trong trình duyệt và nhấp vào Áp dụng, ESP32 phân tích các giá trị RGB và cập nhật ba biến này; chữ ngay lập tức đổi màu trên ma trận. Trong video, hành vi này được minh họa bằng nhiều lần đổi màu, bao gồm các ví dụ màu đỏ, lục và lam.
Tóm tắt
Dự án 3 biến ESP32-S3 RGB LED Matrix của bạn thành một màn hình chữ hoàn toàn không dây mà bạn có thể điều khiển bằng bất kỳ thiết bị nào có trình duyệt web. Sketch được thiết kế để mạnh mẽ:
- Trước tiên, nó cố gắng kết nối với Wi-Fi tại nhà của bạn bằng SSID và mật khẩu mà bạn đã cấu hình.
- Nếu kết nối đó thất bại, nó sẽ tự động trở thành một điểm truy cập có tên
ESP32và mật khẩupassword. - Trong cả hai chế độ, việc mở đúng địa chỉ IP trong trình duyệt sẽ hiển thị cùng một trang điều khiển cho văn bản, màu sắc, hướng và tốc độ.
Mã HTTP Text hoàn chỉnh có sẵn bên dưới bài viết này (được tải tự động trên trang web). Để có hướng dẫn chi tiết từng bước và trình diễn trực tiếp về cách văn bản cập nhật theo thời gian thực, hãy chắc chắn xem phần Dự án 3 của video. Nếu bạn muốn tự xây dựng dự án này, bạn cũng có thể mua mô-đun ESP32-S3 RGB LED Matrix bằng các liên kết liên kết được liệt kê bên dưới mã.
Hướng dẫn này là một phần của: Ma trận LED RGB ESP32-S3
- Dự án Ma trận LED RGB ESP32-S3 1- Chấm cơ bản
- Dự án Ma trận LED RGB ESP32-S3 2 - Văn bản cuộn
- Dự án Ma trận LED RGB ESP32-S3 4 - Chấm nghiêng
- Dự án Ma trận LED RGB ESP32-S3 5 - Mũi tên luôn hướng lên
- Dự án Ma trận LED RGB ESP32-S3 6 - Trò chơi Cible
- Dự án Đồng hồ thời gian Wi-Fi + NTP sử dụng ma trận LED RGB ESP32-S3 - 1 Đồng hồ cơ bản
- Dự án Đồng hồ Internet Ma trận LED RGB ESP32-S3 - Hiển thị Thời gian & Ngày tháng 2 Đồng hồ đa màu sắc
- Dự án Đồng hồ Internet Ma trận LED RGB ESP32-S3 - 3 Màu Ban Đêm kèm Ngày
- Dự án Đồng hồ Internet Ma trận LED RGB ESP32-S3 - 5 Màu cầu vồng
- Dự án Đồng hồ Internet Ma trận LED RGB ESP32-S3 - 4 màu ngẫu nhiên
- 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();
}
}
Nghĩ bạn có thể cần
-
Amazon
-
eBay
-
AliExpressPurchase ESP32-S3 RGB matrix from AliExpresss.click.aliexpress.com
-
AliExpressPurchase ESP32-S3 RGB matrix from AliExpress (2)s.click.aliexpress.com
Tài nguyên & tài liệu tham khảo
-
Nội bộ🎨 Color picker Toolrobojax.com
Tập tin📁
Tập tin Fritzing
-
esp32-S3-supermini-tht fritzing part
esp32-S3-supermini-tht.fzpz0.02 MB