本教程是的一部分: ESP32-S3 RGB LED 点阵
这是一个很棒的项目,可以使用 ESP32-S3 RGB 矩阵模块进行创作,兼具趣味性和实用性。本文下方有其他相关视频的链接。
ESP32-S3 RGB LED矩阵项目3 - 手机文本
项目3 - 从您的手机控制矩阵文本 (HTTP文本)
在这个项目中,ESP32-S3 RGB LED矩阵托管了一个小网页,以便您可以直接通过手机或计算机更改滚动文本、颜色、方向和速度。您不需要单独的应用程序 - 仅需使用网络浏览器。这使得该模块成为一个可以实时更新的微型Wi-Fi文本显示器。
本系列的六个项目在一个YouTube视频中进行了说明和演示。该视频嵌入在此页面上,因此您可以清楚地看到网页界面的外观,以及文本如何在矩阵上即时更新。该项目的完整源代码会在文章下方自动加载,您可以从代码部分列出的附属商店购买ESP32-S3 RGB LED矩阵模块。
在本文中,我们重点关注网络逻辑是如何工作的(家庭Wi-Fi与接入点),以及您可以在代码中更改哪些设置以自定义行为。



ESP32-S3 RGB LED矩阵模块概览
硬件与此系列中所有其他项目相同:一块ESP32-S3微控制器板,配有内置的8×8 RGB LED矩阵和背面的QMI8658C运动传感器。USB-C端口用于供电和编程,边缘周围的引脚仍可用于其他IO。:contentReference[oaicite:0]{index=0}

- ESP32-S3具备Wi-Fi和蓝牙功能的微控制器。
- 8×8 RGB矩阵64 个可寻址的 RGB LED 用于文本和图形。
- QMI8658C 加速度计- 用于倾斜和游戏项目。
- USB端口- 为电路板供电并从 Arduino IDE 上传草图。
- 暴露的引脚- 如有需要,可以允许额外的传感器或执行器。
- 启动/重置按钮- 用于固件上传和重启。
对于项目 3,最重要的功能是 ESP32 的 Wi-Fi,它使得开发板可以充当文本控制页面的微型 web 服务器。:contentReference[oaicite:1]{index=1}

视频中涵盖的项目(时间戳)
该系列的一个视频涵盖了所有六个项目。快速参考:
- 00:00- 介绍
- 02:01- 安装 ESP32 开发板
- 03:32安装库
- 05:32- 项目 1:移动点
- 11:11- 项目 2:文本滚动
- 12:59-项目 3:HTTP 文本(该项目)
- 16:41- 项目 4:倾斜点
- 18:55- 项目5:箭头向上
- 20:02- 项目 6:目标游戏
建议您在阅读本文时观看视频中的 HTTP 文本部分。视频展示了如何通过 ESP32 生成网页,以及如何即时反映文本、颜色和速度的变化在 LED 矩阵上。:contentReference[oaicite:2]{index=2}
在Arduino IDE中安装ESP32板
如果您已经完成了项目 1 或 2,则电路板设置已完成,您可以跳过此部分。否则,请在 Arduino IDE 中按照以下步骤操作:
- 打开
File > Preferences并将ESP32板的URL添加到“附加板管理器URL”。 - 去
Tools > Board > Boards Manager…搜索ESP32并安装官方的ESP32软件包。 - 选择正确的ESP32-S3 RGB矩阵板从
Tools > Board. - 通过USB连接模块,并在下方选择正确的串口
Tools > Port.
如果没有正确的ESP32板支持和正确的端口,网络服务器示例将无法上传。
安装NeoMatrix和所需库
该项目使用与先前的文本滚动项目相同的库:
Adafruit NeoMatrixAdafruit NeoPixelAdafruit GFX Library
通过库管理器安装:
- 打开
Sketch > Include Library > Manage Libraries…. - 搜索
Adafruit NeoMatrix和点击安装. - 接受依赖项的安装(
Adafruit GFX和Adafruit NeoPixel).
安装后,您应该在下方看到 NeoMatrix 和 NeoPixel 示例草图。File > Examples.
项目3中的两种Wi-Fi模式
这个项目中最重要的概念是 ESP32 可以工作于两种不同模式:
- 站点(STA)模式ESP32 连接到您现有的家庭 Wi-Fi 网络。
- 接入点(AP)模式ESP32会在家庭Wi-Fi不可用时创建自己的Wi-Fi网络。
两种模式使用相同的网页界面:一个从ESP32本身提供的HTML页面,您可以在其中更改文本、颜色、滚动方向和速度。:contentReference[oaicite:3]{index=3}
模式1 - 连接到家庭Wi-Fi(站点模式)
在站点模式下,ESP32连接到您的家庭或办公室Wi-Fi网络。这是您路由器可用时的首选模式,因为:
- 您的手机和计算机已经连接到同一个Wi-Fi网络。
- 您可以将浏览器指向ESP32的IP地址,并从该网络上的任何设备控制文本。
在草图的设置部分,您需要提供您的 Wi-Fi SSID 和密码:
// Home Wi-Fi credentials (Station mode)
const char* WIFI_SSID = "YourHomeWiFi";
const char* WIFI_PASS = "YourHomePassword";
主板启动后,它尝试连接到WIFI_SSID如果成功,代码将在串行监视器上打印分配的IP地址,例如:
Connected to WiFi
IP address: 192.168.1.16
控制文本:
- 确保您的手机或电脑连接到同一 Wi-Fi 网络(例如,
YourHomeWiFi). - 打开浏览器并输入打印的IP地址,例如
http://192.168.1.16/. :contentReference[oaicite:4]{index=4} - 控制页面将出现,允许您输入文本、选择颜色、选择方向以及调整滚动速度。
模式 2 - 独立接入点 (AP 模式)
如果ESP32无法连接到您的家庭Wi-Fi(密码错误、网络不可用或您在室外使用模块),草图将自动退回到接入点模式。在接入点模式下,ESP32本身成为一个具有自己网络名称和密码的Wi-Fi热点。
在这个项目中,AP设置固定为:
// Access Point (AP) credentials (fallback mode)
const char* AP_SSID = "ESP32";
const char* AP_PASS = "password";
当站点模式失败时,模块切换到AP模式并开始广播一个名为的Wi-Fi网络ESP32. 控制矩阵:
- 在你的手机或电脑上,打开Wi-Fi设置并连接到网络。ESP32.
- 输入密码密码(如代码中所定义的)。
- 连接后,打开浏览器并访问
http://192.168.4.1/(ESP32 AP模式的默认IP)。 - 相同的控制页面出现,可以让您更改文本、颜色、速度和方向。
这种后备行为使得该项目在任何地方都能使用:在家中、实验室或没有路由器的演示环境中。
项目 3 - 代码中的主要设置
此文章下方的网站加载了完整的HTTP文本草图。这里我们只记录您可能会编辑的最重要的配置选项。
Wi-Fi 和接入点设置
在草图的顶部,您将找到Wi-Fi配置部分。只需更改站点(家庭Wi-Fi)凭据;AP设置通常保持为默认值:
// ---------- 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
行为:
- 如果
WIFI_SSID和WIFI_PASS是正确的,网络可用 → ESP32 作为普通 Wi-Fi 设备(站点模式)连接。 - 如果连接在超时后失败 → ESP32 会使用自身的AP
AP_SSID和AP_PASS.
矩阵引脚、尺寸和亮度
这些设置与之前的项目相同:
// 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
保持MATRIX_PIN在14对于这个板子。你可以增加matrixBrightness如果您需要更多光线,但较低的值在近距离观看时更为舒适。
默认文本和滚动设置
当电路板启动时,它会显示一条初始消息,直到您打开网页并输入新的消息。您可以在配置中更改默认文本:
// Default message shown at startup
String currentText = "Robojax"; // overwrite from web UI later
滚动行为的其余部分由一组通过网页接口更新的变量控制:
// 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
网页根据滑块和按钮的选择发送新值。从Arduino方面来说,您只需知道:
- 减少
scrollDelayMs使文本移动得更快。 - 增加
scrollDelayMs使其移动得更慢。 - 改变
scrollDirection在左、右、上或下滚动模式之间切换。
文本颜色(从网页控制)
文本的颜色由三个 0-255 的值(红色、绿色、蓝色)控制。每当您在网页上选择一种新颜色时,它们会被更新:
// Current text color (R, G, B)
uint8_t textRed = 255;
uint8_t textGreen = 255;
uint8_t textBlue = 255;
当您在浏览器中选择一种颜色并点击应用时,ESP32 解析 RGB 值并更新这三个变量;文本在矩阵上立即改变颜色。在视频中,这种行为通过多种颜色变化进行演示,包括红色、绿色和蓝色示例。:contentReference[oaicite:5]{index=5}
摘要
项目3将您的ESP32-S3 RGB LED矩阵变成一个完全无线的文本显示器,您可以使用任何带有网页浏览器的设备进行控制。该草图设计得非常稳健:
- 它首先尝试使用您配置的SSID和密码连接到您的家庭Wi-Fi。
- 如果失败,它会自动变成一个名为的接入点。
ESP32和密码password. - 在两种模式下,在浏览器中打开正确的IP地址会显示相同的控制页面,用于文本、颜色、方向和速度。
完整的 HTTP 文本代码可在本文下方获得(在网站上自动加载)。要获取逐步指导和实时演示文本如何更新,请务必观看视频的项目 3 部分。如果您想自己构建该项目,还可以通过代码下方列出的附属链接购买 ESP32-S3 RGB LED 矩阵模块。
本教程是……的一部分: ESP32-S3 RGB LED 点阵
- ESP32-S3 RGB LED矩阵项目 1- 基本点阵
- ESP32-S3 RGB LED矩阵项目2 - 滚动文字
- ESP32-S3 RGB LED矩阵项目4 - 倾斜点
- ESP32-S3 RGB LED矩阵项目5 - 箭头始终向上
- ESP32-S3 RGB LED矩阵项目6 - Cible游戏
- ESP32-S3 RGB LED 矩阵 Wi-Fi + NTP 时钟项目 - 1 基本时钟
- ESP32-S3 RGB LED矩阵网络时钟项目 - 2个时钟多彩时间和日期显示
- ESP32-S3 RGB LED矩阵互联网时钟项目 - 带日期的3种夜间颜色
- ESP32-S3 RGB LED 矩阵网络时钟项目 - 5 种彩虹色
- ESP32-S3 RGB LED矩阵互联网时钟项目 - 4种随机颜色
- ESP32-S3 RGB LED矩阵测试,RGB,GRB设置
/*
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();
}
}
|||您可能需要的东西
-
亚马逊从亚马逊购买ESP32-S3 RGB矩阵amzn.to
-
易趣从eBay购买ESP32-S3 RGB矩阵ebay.us
-
全球速卖通从AliExpress购买ESP32-S3 RGB矩阵 (2)s.click.aliexpress.com
-
全球速卖通在AliExpress上购买ESP32-S3 RGB矩阵s.click.aliexpress.com
资源与参考
-
内部颜色选择器工具robojax.com
文件📁
Fritzing 文件
-
esp32-S3-supermini-tht Fritzing 部件
esp32-S3-supermini-tht.fzpz0.02 MB