Datalogger industriale su ESP32-S3 (ESP-IDF + FreeRTOS). Acquisisce temperature da 8 termocoppie (MAX31855), 5 ingressi digitali e 5 ingressi analogici, registra su flash interna (FAT, wear-levelling) ed espone i log al PC via USB Mass Storage. Ha un display TFT locale con GUI LVGL per visualizzare canali, grafici e impostazioni senza bisogno di un host.
- MCU: ESP32-S3 (WROOM-1-N8R2, 8 MB flash, 2 MB PSRAM Quad SPI)
- Display: pannello SPI ST7796 480×320, GUI LVGL via
esp_lvgl_port - Nessun RTC hardware: l'orologio è impostato manualmente dall'operatore a ogni accensione
(componente
timekeeping), UTC0 fisso, senza persistenza dello stato "valido" tra i boot
Termocouple bus (MAX31855, SPI3_HOST, no DMA, CS software)
| Segnale | GPIO |
|---|---|
| SCLK | 36 |
| MISO | 37 |
| CS canale 0–7 | 1, 2, 42, 41, 40, 39, 38, 35 |
Ingressi digitali (GPIO 15, 16, 17, 18, 3)
Ingressi analogici IN2–IN6 (GPIO 9, 7, 6, 5, 4) — IN1 (GPIO13) non fa parte di questo gruppo: è riassegnato a ingresso digitale "record enable" (livello alto = logging attivo, vedi sotto).
Canali — nomi e unità sono in components/data_model/data_model_channels.c, condivisi da
display e log (modificarli richiede un nuovo firmware):
| Ingresso | Sigla | Grandezza |
|---|---|---|
| Tc1–Tc4 | Cil 1–Cil 4 |
temperatura cilindro 1–4 |
| Tc5 / Tc6 | IC in / IC out |
temperatura ingresso / uscita intercooler |
| Tc7 / Tc8 | Olio / Acqua |
temperatura olio / acqua |
| IN2 / IN3 | P IC in / P IC out |
pressione turbo ingresso / uscita intercooler |
| IN4 | P scar |
pressione gas di scarico |
| IN5 / IN6 | P benz / P olio |
pressione benzina / olio |
Le pressioni sono in volt finché non è nota la scala dei sensori. IN7 (contagiri) non è ancora gestito.
Display (SPI2_HOST)
| Segnale | GPIO |
|---|---|
| MOSI | 11 |
| SCLK | 12 |
| CS | 10 |
| DC | 8 |
| RST | 14 |
| Backlight (LEDC) | 21 |
USB nativo — D+/D- sugli IO19/IO20 dell'ESP32-S3, stessa porta USB-C usata per alimentazione 5 V da PC e per l'esposizione USB MSC.
app_core— orchestratore, avvia i servizi daapp_mainboard— HAL con l'assegnazione pin reale (nessun placeholder)max31855— driver SPI3_HOST, 8 canali, CS manuali via GPIOdigital_inputs/analog_inputs— driver GPIO/ADC oneshot con calibrazioneacquisition— task FreeRTOS periodico (periodo configurabile, vedisettings) che campiona tutti i driver in unkdl_sensor_sample_t(data_model)logger— scrive i campioni in CSV su FAT (/data/logs), flush periodico con fflush+fsyncstorage— partizione FAT con wear-levelling, arbitraggio di accesso FIRMWARE/USB_HOSTusb_msc— espone la partizionestoragecome Mass Storage Device (TinyUSB)timekeeping— orologio software impostato dall'operatore, nessuna batteria tamponesettings— parametri persistenti in NVS (periodo di acquisizione, luminosità display)display/gui— driver ST7796 + pagine LVGL (main, grafico, impostazioni, data/ora, USB, splash), gestione pulsanti con debounce
Il logging non parte automaticamente all'accensione: segue il livello dell'ingresso "record enable" (IN1 / GPIO13) — alto = logging ON, basso = OFF. Serve un comando esterno (interruttore o segnale) per avviare/fermare la sessione senza passare dalla GUI.
Ogni sessione di registrazione crea /data/logs/log_NNNN.csv. Il file è pensato per essere aperto
con doppio click in Excel italiano: separatore ;, decimale ,, UTF-8 con BOM.
Spazio: la partizione dati è di circa 5 MB. Il logger cancella da solo i log più vecchi quando lo
spazio libero scende sotto 256 KB (controllo all'avvio della sessione e a ogni flush, ogni 2 s) o i
file superano 500: la registrazione non si ferma mai per disco pieno. Se un file non si apre o una
scrittura fallisce, la barra di stato mostra ERRORE LOG finché la sessione successiva non parte
correttamente. Nota macOS: i file cancellati dal Finder finiscono in .Trashes sul disco del
datalogger e occupano spazio finché non si svuota il Cestino con il disco collegato.
Data;Ora;Tempo [s];Cil 1 [°C];…;Acqua [°C];P IC in [V];…;P olio [V]
23/09/2026;14:32:05;0,00;85,25;…;;1,234;…
Data/Orasono vuote se l'orologio non è stato impostato dopo l'accensione.Tempo [s]parte da 0 al primo campione della sessione: è l'asse X per i grafici.- Un canale non valido (termocoppia aperta/in corto, errore ADC) è una cella vuota.
- IN1 non compare: è l'ingresso record-enable. Gli analogici sono in volt, senza scala.
La partizione storage non può essere scritta contemporaneamente da firmware e host USB:
- Fuori dalla modalità USB il dispositivo è staccato dal bus (
tud_disconnect()): anche col cavo collegato il PC non lo vede, quindi non può scrivere sulla partizione né riprendersela mentre il logger registra. - Il pulsante USB sull'HMI (con registrazione ferma) collega il dispositivo al PC, che vede la partizione come disco rimovibile; acquisizione e logging sono fermi.
- Si esce espellendo il disco dal PC: il firmware riceve l'eject e torna da solo in modalità normale. "Esci" sull'HMI senza eject mostra un avviso e richiede una seconda pressione entro 5 s: è la via d'uscita se il PC si è bloccato o il cavo è stato staccato (la scheda non rileva VBUS, quindi lo scollegamento non sempre si vede).
idf.py build produce, oltre a build/KDatalogger.bin, la copia build/KDatalogger_v<versione>.bin
(versione da version.txt, da incrementare a ogni rilascio). Per aggiornare:
- Modalità USB dall'HMI, copiare il file
.binnella root del disco, espellere il disco dal PC. - Al rientro dalla modalità USB (e a ogni accensione) il datalogger cerca il file, lo installa nello slot OTA inattivo mostrando l'avanzamento, lo cancella e si riavvia.
- La versione in uso è sullo splash di avvio (in basso a destra) e in fondo alla pagina Impostazioni: dopo il riavvio mostra già quella nuova. Viene confermata dopo 15 s continuativi di funzionamento normale (volume leggibile, acquisizione che produce campioni); se nel frattempo si blocca, si resetta o fallisce il controllo, il bootloader torna alla versione precedente e il display lo segnala fino a "OK". Un aggiornamento riuscito non mostra messaggi.
Regole (components/fw_update):
- Il file si riconosce dal contenuto, non dal nome: header ESP32-S3 e project name
KDatalogger. Qualsiasi altro.binresta dov'è, ignorato, come i file._*che macOS scrive sul disco. - File troncato o danneggiato (anche per un disco scollegato senza espellerlo): rinominato in
.bad, firmware invariato. File identico al firmware in uso: cancellato. Più file validi: nessuna installazione finché non ne resta uno. - Mai durante una registrazione: se IN1 è attivo, l'aggiornamento aspetta la fine della sessione.
- Il confronto è sull'immagine (SHA dell'ELF), non sul numero di versione: una build diversa con la stessa versione viene installata comunque, e si può anche tornare a una versione precedente.
- Nessuna firma: il controllo evita file sbagliati o incompleti, non protegge da manomissioni. Un firmware che supera l'autotest ma non sa più aggiornarsi va ripristinato via seriale.
Richiede ESP-IDF (v6.x) con target esp32s3 configurato. Il progetto usa una partition table
custom (partitions.csv) e PSRAM Quad — entrambi già impostati in sdkconfig.defaults.
idf.py set-target esp32s3
idf.py buildIl dispositivo va programmato via USB-C nativo dell'ESP32-S3 — non ha (ne' serve) un connettore JTAG/UART dedicato. Il chip entra in bootloader mode automaticamente quando il tool di flashing lo richiede (reset via USB-Serial-JTAG nativo, nessun pulsante BOOT da tenere premuto in condizioni normali). Se il collegamento resta bloccato su "Connecting...", verificare che GPIO0 non sia vincolato da altro hardware sulla scheda.
Partizionamento (partitions.csv, custom): bootloader a 0x0, partition table a 0x8000,
otadata a 0xd000, due slot app OTA da 1,5 MB (ota_0 a 0x10000, ota_1 a 0x190000), dati
FAT (storage) da 0x310000 a fine flash (8 MB).
Una scheda programmata con la tabella precedente (factory da 2 MB) va cancellata per intero
una volta (idf.py -p PORT erase-flash flash): lo storage cambia offset e va riformattato, quindi
i log presenti vanno salvati prima.
idf.py -p PORT flash monitorCompila e flasha in un unico passaggio. E' il metodo da usare durante lo sviluppo.
Installazione (richiede Python 3.8+):
pip install esptool
esptool.py version # verifica installazioneServono comunque i binari compilati (idf.py build, oppure quelli gia' presenti in build/ da una
build precedente). Flash manuale specificando i quattro binari con i relativi offset:
esptool.py --chip esp32s3 -p PORT -b 460800 write_flash \
0x0 build/bootloader/bootloader.bin \
0x8000 build/partition_table/partition-table.bin \
0xd000 build/ota_data_initial.bin \
0x10000 build/KDatalogger.binSostituire PORT con la porta seriale (/dev/cu.usbmodem* su macOS, /dev/ttyACM0 su Linux,
COMx su Windows).
Monitor seriale dopo il flash:
esptool.py --chip esp32s3 -p PORT monitorRichiede un browser Chromium (Chrome o Edge) — Firefox e Safari non supportano la Web Serial API.
- Collegare il dispositivo via USB-C.
- Aprire https://espressif.github.io/esptool-js/
- "Connect" e selezionare la porta seriale.
- Nella sezione "Program", compilare le righe file + offset (pulsante "Add File" per aggiungerne
altre):
bootloader/bootloader.bin→0x0partition_table/partition-table.bin→0x8000ota_data_initial.bin→0xd000KDatalogger.bin→0x10000
- "Program" e attendere il completamento.
Utile per riprogrammare un'unita' sul campo senza toolchain ESP-IDF installata.