- Cos'è questo progetto
- Come funziona il software originale
- Architettura del dispositivo hardware
- Come funziona il programma Flutter
- Struttura dei file
- Cosa rimane da fare per farlo funzionare
- Troubleshooting
Skin Analyzer Pro è una app Flutter per Windows Desktop che si collega all'analizzatore cutaneo fisico Skin Observed System (distribuito da varie marche in Cina/Asia) e visualizza i risultati dell'analisi della pelle in modo professionale, con:
- Grafici circolari animati per ognuno degli 8 parametri
- Radar chart a 8 assi
- Gestione pazienti (anagrafica, storico scansioni)
- Export PDF del referto (formato A4)
- Interfaccia medica pulita in Material 3
Il progetto è un reverse engineering + reimplementazione del software originale Skin_Plus.exe — software Windows Forms scritto in C# che veniva fornito col dispositivo.
Il software originale (C:\Program Files (x86)\Skin Observed System\Skin_Plus.exe) è un'applicazione .NET Framework 4.0 (Windows Forms, 32-bit) che usa le seguenti DLL:
| DLL | Ruolo |
|---|---|
Skin_Plus.exe |
Frontend Windows Forms C# |
SkinDetector.dll |
Motore di analisi — contiene gli 8 algoritmi detector |
Geb.Image.dll |
Libreria di computer vision per elaborazione immagini |
DataBase.mdb |
Database MS Access (Jet OLEDB 4.0) per pazienti e risultati |
Il dispositivo si identifica sul bus USB con:
- VendorID:
0x0555 - ProductID:
0x0160
Identificato tramite diagnostica HID (il vecchio 0x0AC8 / 0x5678 trovato nel binario non corrisponde all'hardware reale).
Il dispositivo è fisicamente una webcam USB con un tasto/sensore touch integrato. Viene enumerato dal sistema operativo come dispositivo HID (Human Interface Device), esattamente come un mouse o tastiera, quindi non richiede driver particolari.
L'analisi è basata su immagini, NON su segnali elettrici raw.
Questo è il punto più importante da capire: il dispositivo non misura direttamente i parametri cutanei con sensori. Funziona così:
Dispositivo fisico
│
├─ Webcam USB ──────→ Cattura frame video della pelle
│ (illuminate da LED del dispositivo)
│
└─ HID Interface ──→ Segnale "tasto premuto" per avviare scan
La DLL SkinDetector.dll riceve l'immagine dalla webcam e la elabora tramite Geb.Image.dll. Per ogni parametro esiste una classe *Detector con un metodo Detect(image) che restituisce un punteggio da 0.0 a 9.9.
| # | Parametro | Cosa misura visivamente |
|---|---|---|
| 1 | Umidità | Riflettanza superficiale — pelle secca appare diversamente |
| 2 | Olio/Sebo | Zone brillanti/lucide nell'immagine |
| 3 | Texture | Irregolarità della superficie cutanea |
| 4 | Fibra Collagene | Analisi cromatica dello strato dermico visibile |
| 5 | Rughe | Rilevamento bordi/solchi nell'immagine |
| 6 | Pigmentazione | Macchie, discromie, variazioni di colore |
| 7 | Sensibilità | Rossori, eritemi, variazioni vascolari |
| 8 | Pori | Dimensione e densità dei pori nella texture |
File: C:\Program Files (x86)\Skin Observed System\DataBase\DataBase.mdb
Password Jet OLEDB: 33560
Driver richiesto: Jet OLEDB 4.0 (32-bit) — solo accessibile da process a 32-bit.
Tabelle principali:
| Tabella | Contenuto |
|---|---|
Examer |
Anagrafica pazienti (nome, età, sesso) |
MeasData |
Misurazioni grezze |
Result |
Risultati analisi con 8 score |
SetValue |
Configurazione soglie |
ResultMark |
Range di giudizio (Eccellente/Buono/Medio/Scarso/Critico) |
Le foto della pelle vengono salvate in:
C:\Program Files (x86)\Skin Observed System\DataBase\Picture\<ID_paziente>\
1. Avvio app → connessione al dispositivo USB (VID 0AC8, PID 5678)
2. Operatore seleziona paziente nel database
3. Operatore preme il tasto touch sull'analizzatore (o UI)
4. Il firmware del dispositivo invia segnale HID al PC
5. Skin_Plus.exe riceve il segnale e avvia acquisizione video
6. La webcam cattura 1 o più frame della zona cutanea
7. I frame vengono passati a SkinDetector.dll
8. Ogni Detector elabora l'immagine e produce uno score 0.0-9.9
9. I risultati vengono salvati nel database MDB
10. L'interfaccia mostra grafici e suggerimenti
┌─────────────────────────────────────────┐
│ ANALIZZATORE CUTANEO │
│ │
│ ┌─────────┐ ┌─────────────────────┐ │
│ │ LED │ │ Sensore / Webcam │ │
│ │ bianchi │ │ CMOS (Z-Star) │ │
│ │ + UV │ │ VID:0AC8 PID:5678 │ │
│ └────┬────┘ └──────────┬──────────┘ │
│ │ illumina │ acquisisce │
│ └─────────→ PELLE ←─┘ │
│ │
│ ┌─────────────────────────────────────┐│
│ │ Tasto Touch / Trigger ││
│ │ → segnale HID sul bus USB ││
│ └─────────────────────────────────────┘│
└────────────────────┬────────────────────┘
│ USB 2.0
▼
PC Windows
(driver HID standard,
nessun driver extra)
Il dispositivo si connette tramite un solo cavo USB che trasporta:
- Il flusso video (UVC — USB Video Class)
- Il segnale HID del tasto
lib/
├── main.dart → Entry point, MultiProvider setup
├── app.dart → MaterialApp + tema
├── theme/app_theme.dart → Colori, font Inter, ThemeData
│
├── models/
│ ├── scan_result.dart → SkinParam enum, SkinResult, range giudizi
│ └── patient.dart → Patient model
│
├── providers/ → State management (Provider pattern)
│ ├── device_provider.dart → Gestisce connessione USB + polling
│ ├── scan_provider.dart → Storico scansioni, paziente selezionato
│ └── patient_provider.dart → CRUD pazienti
│
├── services/
│ ├── hid_service.dart → Comunicazione USB HID via FFI + win32
│ └── report_service.dart → Generazione PDF (libreria pdf/printing)
│
├── screens/
│ ├── home_screen.dart → NavigationRail principale
│ ├── scan_screen.dart → Schermata scansione
│ ├── report_screen.dart → Visualizzazione risultati + export
│ └── patients_screen.dart → Gestione anagrafica pazienti
│
└── widgets/
├── skin_gauge.dart → Gauge circolare animato (CustomPainter)
├── radar_chart.dart → Radar chart a 8 assi (CustomPainter)
├── scan_animation.dart → Animazione durante acquisizione
├── device_status_badge.dart → Indicatore connessione dispositivo
└── parameter_card.dart → Card singolo parametro
Utente preme "Avvia Scansione"
│
▼
DeviceProvider.triggerScan()
│
├─ [SIMULATION MODE = true]
│ └─ dopo 3 secondi genera 8 score casuali (1.5–8.5)
│
└─ [SIMULATION MODE = false — da implementare]
└─ HidService.instance.readReport()
│ legge 65 byte HID
└─ HidService.parseScores() → 8 double
│
▼
onScanComplete(List<double> scores)
│
▼
ScanProvider._onScanData()
│ crea SkinResult con score + resultNos
│ inserisce in cima alla lista storico
└─ notifyListeners()
│
▼
HomeScreen naviga automaticamente → ReportScreen
│ mostra gauge, radar chart, suggerimenti
└─ tasto "Esporta PDF" → ReportService.generateAndPrint()
Il file lib/services/hid_service.dart usa Win32 FFI (Foreign Function Interface) per comunicare direttamente con Windows senza driver aggiuntivi:
hid_service.dart
│
├─ hid.dll → HidD_GetHidGuid(), HidD_GetAttributes()
│ (caricata direttamente con DynamicLibrary.open)
│
└─ setupapi.dll → SetupDiGetClassDevs(), SetupDiEnumDeviceInterfaces()
(tramite win32 package)
Algoritmo di ricerca dispositivo:
HidD_GetHidGuid→ ottieni GUID della classe HIDSetupDiGetClassDevs→ lista tutti i dispositivi HID connessi- Per ognuno:
SetupDiGetDeviceInterfaceDetail→ path del device CreateFiletemporaneo →HidD_GetAttributes→ legge VID/PID- Se VID=
0x0AC8e PID=0x5678→ apre conFILE_FLAG_OVERLAPPED ReadFileasincrona +WaitForSingleObjectcon timeout 3 secondi
Attualmente _simulationMode = true in device_provider.dart.
In questa modalità:
- Il dispositivo appare sempre connesso (senza hardware reale)
- La scansione genera 8 score casuali dopo 3 secondi
- Tutte le funzionalità UI sono testabili senza l'analizzatore fisico
C:\SkinAnalyzer\
├── lib/ → Codice sorgente Dart
├── windows/ → Codice platform Windows (C++, CMake)
│ └── runner/ → Entry point Win32 nativo
├── assets/ → Risorse (immagini, font — attualmente vuote)
├── test/ → Test (placeholder)
├── pubspec.yaml → Dipendenze Flutter
├── analysis_options.yaml → Regole linter
├── setup.ps1 → Script PowerShell per setup ambiente
└── PROGETTO.md → Questo file
| Pacchetto | Versione | Uso |
|---|---|---|
win32 |
^5.5.1 | Win32 API (HID, SetupDI, File I/O) |
ffi |
^2.1.3 | FFI Dart ↔ C/C++ |
provider |
^6.1.2 | State management |
fl_chart |
^0.69.0 | Grafici (non usato direttamente, CustomPainter) |
pdf |
^3.11.1 | Generazione PDF |
printing |
^5.13.1 | Stampa/anteprima PDF |
google_fonts |
^6.2.1 | Font Inter |
intl |
^0.19.0 | Formattazione date |
path_provider |
^2.1.4 | Directory documenti (salvataggio PDF) |
uuid |
^4.4.2 | ID univoci per pazienti/scansioni |
shared_preferences |
^2.3.2 | Persistenza impostazioni |
Flutter Windows compila il runner nativo con MSVC. VS 2022 è installato ma manca il workload "Desktop development with C++".
Soluzione A — tramite VS Installer (raccomandato):
- Apri Visual Studio Installer dal menu Start
- Clicca Modifica su "Visual Studio Community 2022"
- Seleziona "Sviluppo di applicazioni desktop con C++"
- Assicurati che siano selezionati:
- MSVC v143 (o superiore)
- Windows 10 SDK (10.0.19041.0 o superiore)
- Clicca Modifica e aspetta il download (~3-5 GB)
Soluzione B — da PowerShell (Amministratore):
& "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" modify `
--installPath "C:\Program Files\Microsoft Visual Studio\2022\Community" `
--add Microsoft.VisualStudio.Workload.NativeDesktop `
--includeRecommended --quiet --norestartVerifica:
flutter doctor
# Deve mostrare: [✓] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.x)Dopo aver installato il workload C++:
cd C:\SkinAnalyzer
flutter pub get
flutter run -d windowsL'app si avvierà in simulation mode — il dispositivo appare connesso e le scansioni generano dati casuali. Testa che tutto funzioni:
- Navigazione tra le tre sezioni (Scansione / Pazienti / Referti)
- Aggiunta di un paziente
- Esecuzione di una scansione (tasto "Avvia Scansione")
- Visualizzazione gauge e radar chart
- Export PDF
Questo è il passo più critico per il funzionamento reale.
In lib/providers/device_provider.dart, riga ~22:
// PRIMA (simulation):
bool _simulationMode = true;
// DOPO (reale):
bool _simulationMode = false;Poi decommentare il blocco _checkDevice() reale (~riga 62) che usa HidService.
Il problema: Non sappiamo esattamente il formato dei dati HID che il dispositivo manda.
Ci sono due scenari possibili:
Scenario A — Il dispositivo manda SOLO il segnale del tasto (più probabile) Il dispositivo è una webcam. La maggior parte dei frame dati passa attraverso il protocollo video (UVC), NON HID. Il report HID contiene solo il segnale del pulsante touch (1 byte = 0x01 quando premuto).
In questo caso parseScores() non è il posto giusto — gli score devono venire dall'analisi del frame video della webcam, non dai byte HID.
Scenario B — Il dispositivo manda i punteggi preprocessati via HID Alcuni dispositivi di questo tipo hanno un microcontrollore interno che elabora il segnale e invia direttamente i valori numerici.
Installa Wireshark + USBPcap:
- Scarica USBPcap e installalo
- Apri Wireshark → seleziona interfaccia
USBPcap1(o simile) - Avvia il software originale
Skin_Plus.exe - Esegui una scansione reale
- Filtra in Wireshark:
usb.idVendor == 0x0ac8 - Cattura i pacchetti durante e dopo la scansione
Cosa cercare nei pacchetti:
- Se vedi pacchetti HID con payload di 8+ byte con valori 0-255 → Scenario B → aggiorna
parseScores() - Se i pacchetti HID hanno solo 1-2 byte → Scenario A → serve accesso webcam
Aggiungere al progetto:
# pubspec.yaml
dependencies:
camera_windows: ^0.2.1+6
# oppure usare direttamente MediaFoundation via FFIIl flusso diventa:
Tasto HID premuto → apri stream webcam → cattura frame →
analisi immagine → 8 score
Il database MS Access originale contiene i pazienti già inseriti.
Per importarli nell'app Flutter, creare uno script di migrazione:
# Connessione al DB originale (PowerShell 32-bit)
$conn = New-Object System.Data.OleDb.OleDbConnection
$conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source='C:\Program Files (x86)\Skin Observed System\DataBase\DataBase.mdb';Jet OLEDB:Database Password=33560"Esportare i pazienti da Examer e i risultati da Result in JSON, poi importarli nell'app tramite PatientProvider.addPatient().
Quando tutto funziona:
cd C:\SkinAnalyzer
flutter build windows --releaseL'eseguibile finale si trova in:
build\windows\x64\runner\Release\skin_analyzer.exe
Per distribuirlo, copia tutta la cartella Release\ (include le DLL necessarie).
→ Installare il workload VS C++ (vedi Passo 1)
// Aggiungi log temporaneo in hid_service.dart findAndOpen():
debugPrint('Device path: $devicePath');
debugPrint('VID: ${attributes.ref.VendorID.toRadixString(16)}');→ Verifica che VID/PID corrispondano
→ Prova ad aprire il device con accesso GENERIC_READ invece di 0 nella chiamata temporanea
→ Il device non sta mandando dati HID spontaneamente. Conferma che il tasto fisico viene premuto, oppure invia un report di output per richiedere dati.
→ Assicurati che path_provider abbia permessi di scrittura. Su Windows, i documenti vanno in %USERPROFILE%\Documents\.
| Componente | Stato |
|---|---|
| UI completa (gauge, radar, pazienti, PDF) | ✅ Funzionante |
| Simulation mode | ✅ Funzionante |
| Compilazione Windows | ⛔ Bloccata (manca VS C++ workload) |
| Connessione HID reale | |
| Byte mapping HID | |
| Import dati da DB originale | ⬜ Non implementato |
Priorità assoluta: installare il workload VS C++ e fare flutter run -d windows.