Community project
Ho Un Esp32-2432s028 Vorrei Fare Un Dispositivo

This ESP32-2432S028 project transforms the development board into a file synchronization device with a touchscreen interface. It connects to WiFi networks, communicates with WebDAV servers, and manages file transfers from a microSD card, all controlled through the integrated 2.4-inch display and touch input.
The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions. Builders will configure network settings, upload the provided firmware, insert a microSD card, and use the touchscreen to monitor and control synchronization between local storage and remote servers.
Wiring diagram
Interactive · read-onlyPan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Assembly
4 stepsInserisci la microSD
A scheda spenta, inserisci una microSD formattata FAT32 nello slot integrato. Per il primo test, copia uno o due piccoli file direttamente nella cartella principale della scheda.
- Tip: Usa nomi file semplici, senza caratteri speciali, per il primo test.
- Tip: Il firmware sincronizza i file nella cartella principale; non trasferisce le sottocartelle.
- ⚠ Non rimuovere la microSD mentre una sincronizzazione è in corso.
Alimenta la scheda
Collega l'ESP32-2432S028R al computer o a un alimentatore USB stabile tramite la sua porta USB.
- Tip: Non sono necessari moduli esterni: display, touchscreen e slot microSD sono integrati nella scheda.
- ⚠ Usa esclusivamente alimentazione USB a 5 V.
Configura la rete e il server
Dopo il deploy, se la rete Wi-Fi non è ancora configurata, collega telefono o computer alla rete Wi-Fi temporanea "SD-WebDAV-Setup". Apri http://192.168.4.1, quindi inserisci la rete Wi-Fi e l'URL completo della cartella WebDAV del NAS, con utente e password.
- Tip: L'URL deve indicare una cartella WebDAV esistente e terminare preferibilmente con /.
- Tip: Dopo la connessione Wi-Fi, l'indirizzo della pagina web appare sul display.
- ⚠ La pagina web non usa HTTPS locale: usala soltanto sulla tua rete fidata. Le credenziali WebDAV vengono memorizzate nella memoria interna della scheda.
Avvia e controlla la sincronizzazione
Premi il pulsante "Sincronizza" dalla pagina web oppure tocca il grande pulsante centrale sul display. Il display mostra il file in invio e il risultato finale.
- Tip: Un file con lo stesso nome già presente nella cartella WebDAV viene sostituito.
- Tip: Per file grandi, mantieni la scheda alimentata e con Wi-Fi stabile fino alla fine.
- ⚠ Non spegnere la scheda durante il caricamento di un file.
Firmware
ESP32#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <SD.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>
#include <Preferences.h>
// Forward declarations
String esc(String value);
String encodePath(const String &value);
void drawScreen();
bool beginSD();
void beginTouch();
void loadSettings();
void saveSettings();
void connectWifi();
void startAccessPoint();
bool webDavPut(File &file, const String &name);
void synchronize();
String page();
void startServer();
void handleTouch();
constexpr int SD_CS = 5;
constexpr int SD_SCK = 18;
constexpr int SD_MISO = 19;
constexpr int SD_MOSI = 23;
constexpr int TOUCH_CS = 33;
constexpr int TOUCH_IRQ = 36;
constexpr int TOUCH_SCK = 25;
constexpr int TOUCH_MISO = 39;
constexpr int TOUCH_MOSI = 32;
TFT_eSPI tft;
SPIClass vspi(VSPI);
XPT2046_Touchscreen touch(TOUCH_CS, TOUCH_IRQ);
WebServer server(80);
Preferences prefs;
String ssid, wifiPassword, davUrl, davUser, davPassword;
String statusLine = "Avvio";
String lastResult = "Nessuna sincronizzazione";
bool sdReady = false;
bool syncing = false;
unsigned long lastTouch = 0;
String esc(String value) {
value.replace("&", "&"); value.replace("<", "<");
value.replace(">", ">"); value.replace("\"", """);
return value;
}
String encodePath(const String &value) {
const char *hex = "0123456789ABCDEF";
String result;
for (size_t i = 0; i < value.length(); ++i) {
const char c = value[i];
if (isalnum(c) || c == '/' || c == '.' || c == '-' || c == '_') result += c;
else { result += '%'; result += hex[(c >> 4) & 15]; result += hex[c & 15]; }
}
return result;
}
void drawScreen() {
tft.fillScreen(TFT_NAVY);
tft.setTextColor(TFT_WHITE, TFT_NAVY);
tft.setTextSize(2);
tft.drawString("SD WebDAV Sync", 10, 10);
tft.drawFastHLine(10, 35, 220, TFT_CYAN);
tft.setTextSize(1);
String network = WiFi.status() == WL_CONNECTED ? "Wi-Fi: " + WiFi.SSID() : "Wi-Fi: da configurare";
tft.drawString(network, 10, 52);
tft.drawString(sdReady ? "microSD: pronta" : "microSD: non trovata", 10, 70);
tft.drawString("Stato:", 10, 96);
tft.setTextColor(TFT_YELLOW, TFT_NAVY);
tft.drawString(statusLine.substring(0, 36), 10, 112);
uint16_t colour = syncing ? TFT_DARKGREY : TFT_GREEN;
tft.fillRoundRect(12, 148, 216, 50, 6, colour);
tft.setTextColor(TFT_BLACK, colour);
tft.setTextDatum(MC_DATUM);
tft.setTextSize(2);
tft.drawString(syncing ? "ATTENDI" : "TOCCA: SINCRONIZZA", 120, 173);
tft.setTextDatum(TL_DATUM);
tft.setTextColor(TFT_WHITE, TFT_NAVY);
tft.setTextSize(1);
String address = WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : "192.168.4.1";
tft.drawString("Pagina web: http://" + address, 10, 222);
tft.drawString("Configura Wi-Fi e Nextcloud dal browser", 10, 240);
}
bool beginSD() {
vspi.end();
vspi.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
sdReady = SD.begin(SD_CS, vspi, 20000000);
return sdReady;
}
void beginTouch() {
vspi.end();
vspi.begin(TOUCH_SCK, TOUCH_MISO, TOUCH_MOSI, TOUCH_CS);
touch.begin(vspi);
touch.setRotation(1);
}
void loadSettings() {
prefs.begin("webdavsync", true);
ssid = prefs.getString("ssid", ""); wifiPassword = prefs.getString("wp", "");
davUrl = prefs.getString("url", ""); davUser = prefs.getString("usr", ""); davPassword = prefs.getString("pwd", "");
prefs.end();
}
void saveSettings() {
prefs.begin("webdavsync", false);
prefs.putString("ssid", ssid); prefs.putString("wp", wifiPassword);
prefs.putString("url", davUrl); prefs.putString("usr", davUser); prefs.putString("pwd", davPassword);
prefs.end();
}
void connectWifi() {
if (ssid.isEmpty()) return;
statusLine = "Connessione Wi-Fi..."; drawScreen();
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), wifiPassword.c_str());
unsigned long started = millis();
while (WiFi.status() != WL_CONNECTED && millis() - started < 15000) delay(250);
statusLine = WiFi.status() == WL_CONNECTED ? "Wi-Fi connesso" : "Wi-Fi non raggiungibile";
}
void startAccessPoint() {
WiFi.mode(WIFI_AP_STA);
WiFi.softAP("SD-WebDAV-Setup");
statusLine = "Rete setup: SD-WebDAV-Setup";
}
bool webDavPut(File &file, const String &name) {
String url = davUrl;
if (!url.endsWith("/")) url += "/";
HTTPClient request;
request.setTimeout(30000);
if (!request.begin(url + encodePath(name))) return false;
if (!davUser.isEmpty()) request.setAuthorization(davUser.c_str(), davPassword.c_str());
request.addHeader("Content-Type", "application/octet-stream");
int response = request.sendRequest("PUT", &file, file.size());
request.end();
return response >= 200 && response < 300;
}
void synchronize() {
if (syncing) return;
syncing = true; statusLine = "Sincronizzazione..."; drawScreen();
if (WiFi.status() != WL_CONNECTED || davUrl.isEmpty() || !beginSD()) {
lastResult = "Configura Wi-Fi, WebDAV e microSD";
} else {
File root = SD.open("/");
int done = 0, errors = 0;
File item;
while ((item = root.openNextFile())) {
if (!item.isDirectory()) {
statusLine = "Invio " + String(item.name()); drawScreen();
if (webDavPut(item, String(item.name()))) ++done; else ++errors;
}
item.close();
delay(5);
}
root.close();
lastResult = "Inviati " + String(done) + ", errori " + String(errors);
}
syncing = false; statusLine = lastResult;
beginTouch();
drawScreen();
}
String page() {
String ip = WiFi.status() == WL_CONNECTED ? WiFi.localIP().toString() : WiFi.softAPIP().toString();
String result = "<!doctype html><html><head><meta name='viewport' content='width=device-width,initial-scale=1'><style>body{font-family:Arial;max-width:650px;margin:20px auto;padding:0 14px;background:#f4f6f8}.card{background:#fff;padding:16px;margin:12px 0;border-radius:8px}input{width:100%;box-sizing:border-box;padding:9px;margin:5px 0 12px}button{background:#087ea4;color:#fff;border:0;border-radius:5px;padding:11px 15px;font-weight:bold}</style></head><body>";
result += "<h1>SD WebDAV Sync</h1><div class='card'><b>Indirizzo:</b> " + ip + "<br><b>Stato:</b> " + esc(statusLine) + "<br><b>Risultato:</b> " + esc(lastResult) + "</div>";
result += "<div class='card'><form method='post' action='/save'><h2>Wi-Fi e cartella Nextcloud</h2>Nome rete Wi-Fi<input name='ssid' value='" + esc(ssid) + "'>Password Wi-Fi<input type='password' name='wp'>URL WebDAV della cartella Nextcloud<input name='url' value='" + esc(davUrl) + "' placeholder='https://cloud.example.it/remote.php/dav/files/NOME_UTENTE/Cartella/'>Utente Nextcloud<input name='usr' value='" + esc(davUser) + "'>Password o password per app Nextcloud<input type='password' name='pwd'><button>Salva e connetti</button></form><p>Per una cartella condivisa, usa il suo percorso dentro ai tuoi File Nextcloud. L'utente deve avere permesso di creare e modificare file nella condivisione.</p></div>";
result += "<div class='card'><form method='post' action='/sync'><button>Carica ora microSD su Nextcloud</button></form><p>Carica i file presenti nella cartella principale della microSD nella cartella Nextcloud scelta. Un file remoto con lo stesso nome viene sostituito; le sottocartelle non sono incluse.</p></div></body></html>";
return result;
}
void startServer() {
server.on("/", HTTP_GET, []() { server.send(200, "text/html; charset=utf-8", page()); });
server.on("/save", HTTP_POST, []() {
ssid = server.arg("ssid"); davUrl = server.arg("url"); davUser = server.arg("usr");
if (server.arg("wp").length()) wifiPassword = server.arg("wp");
if (server.arg("pwd").length()) davPassword = server.arg("pwd");
saveSettings(); connectWifi(); if (WiFi.status() != WL_CONNECTED) startAccessPoint();
drawScreen(); server.sendHeader("Location", "/"); server.send(303);
});
server.on("/sync", HTTP_POST, []() { server.send(200, "text/html", "Sincronizzazione avviata. <a href='/'>Torna allo stato</a>"); synchronize(); });
server.begin();
}
void handleTouch() {
if (syncing || millis() - lastTouch < 600 || !touch.touched()) return;
TS_Point point = touch.getPoint();
if (point.x > 500 && point.x < 3600 && point.y > 500 && point.y < 3600) {
lastTouch = millis();
synchronize();
}
}
void setup() {
Serial.begin(115200);
tft.init(); tft.setRotation(1);
loadSettings();
beginSD(); beginTouch();
connectWifi();
if (WiFi.status() != WL_CONNECTED) startAccessPoint();
startServer();
drawScreen();
}
void loop() {
server.handleClient();
handleTouch();
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.