#include <WiFi.h>
#include <WebServer.h>
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>
// ====== Display configuration ======
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define CS_PIN 5
#define MATRICI_PER_RIGA 15
#define RIGHE 13
#define MAX_DEVICES (MATRICI_PER_RIGA * RIGHE)
MD_Parola P(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
// ====== Wi-Fi credentials ======
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
// ====== Web server ======
WebServer server(80);
// ====== Train display data ======
String zoneText[RIGHE] = {
"16:00 MILANO","16:10 TORINO","16:20 ROMA",
"16:30 NAPOLI","16:40 FIRENZE","16:50 BOLOGNA",
"17:00 VENEZIA","17:10 GENOVA","17:20 BARI",
"17:30 PALERMO","17:40 CATANIA","17:50 PISA","18:00 TRENTO"
};
// ====== HTML page with live preview ======
String generateHTML() {
String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'>";
html += "<style>";
html += "body{font-family:Arial; margin:20px;} ";
html += ".row{font-family:monospace; background:#000; color:#0F0; padding:4px; margin:2px;} ";
html += "input{width:250px; margin-bottom:4px;}";
html += "</style></head><body>";
html += "<h2>ESP32 Train Display</h2>";
html += "<form id='trainForm'>";
for (int i = 0; i < RIGHE; i++) {
html += "Row " + String(i) + ": <input type='text' name='row" + String(i) + "' id='input" + String(i) + "' value='" + zoneText[i] + "'><br>";
}
html += "<button type='button' onclick='updateDisplay()'>Update Display</button></form><hr>";
html += "<h3>Live Preview</h3>";
for (int i = 0; i < RIGHE; i++) {
html += "<div class='row' id='preview" + String(i) + "'>" + zoneText[i] + "</div>";
}
html += "<script>";
// Live preview
for (int i = 0; i < RIGHE; i++) {
html += "document.getElementById('input" + String(i) + "').addEventListener('input', function(){";
html += "document.getElementById('preview" + String(i) + "').innerText = this.value;";
html += "});";
}
// AJAX update function
html += "function updateDisplay(){";
html += "var xhr = new XMLHttpRequest();";
html += "var params = '';";
for (int i = 0; i < RIGHE; i++) {
html += "params += 'row" + String(i) + "=' + encodeURIComponent(document.getElementById('input" + String(i) + "').value) + '&';";
}
html += "xhr.open('POST','/update',true);";
html += "xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');";
html += "xhr.send(params);";
html += "}";
html += "</script>";
html += "</body></html>";
return html;
}
// ====== Setup ======
void setup() {
Serial.begin(115200);
// Initialize display
P.begin(RIGHE);
P.setIntensity(8);
for (uint8_t i = 0; i < RIGHE; i++) {
P.setZone(i, i * MATRICI_PER_RIGA, i * MATRICI_PER_RIGA + MATRICI_PER_RIGA - 1);
P.displayZoneText(i, zoneText[i].c_str(), PA_LEFT, 0, 0, PA_PRINT, PA_NO_EFFECT);
}
// Connect to Wi-Fi
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected! IP: " + WiFi.localIP().toString());
// ====== Web server routes ======
server.on("/", HTTP_GET, [](){
server.send(200, "text/html", generateHTML());
});
server.on("/update", HTTP_POST, [](){
for (int i = 0; i < RIGHE; i++) {
if (server.hasArg("row" + String(i))) {
zoneText[i] = server.arg("row" + String(i));
P.displayZoneText(i, zoneText[i].c_str(), PA_LEFT, 0, 0, PA_PRINT, PA_NO_EFFECT);
}
}
server.send(200, "text/plain", "OK");
});
server.begin();
Serial.println("Web server started");
}
// ====== Loop ======
void loop() {
server.handleClient(); // Handle HTTP requests
P.displayAnimate(); // Animate all zones
}