#include <AccelStepper.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <OneButton.h> // Includi la libreria OneButton
// --- Definizione Pin e Oggetti ---
const int rs = 9, en = 10, d4 = 11, d5 = 12, d6 = 13, d7 = A0;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
const int stepPin = 4;
const int dirPin = 3;
AccelStepper stepper(AccelStepper::DRIVER, stepPin, dirPin);
const int btnOrario = 5;
const int btnAntiorario = 6;
const int btnSalva = 7;
const int btnVaiA = 8;
// Oggetti OneButton per ogni pulsante
// OneButton(pin, activeLow, pullup)
OneButton btnOrario_obj(btnOrario, true, true);
OneButton btnAntiorario_obj(btnAntiorario, true, true);
OneButton btnSalva_obj(btnSalva, true, true);
OneButton btnVaiA_obj(btnVaiA, true, true);
// --- Costanti e Variabili di Sistema ---
const float stepPerMM = 100.0;
const int stepIncrement = 100;
const int maxPosizioni = 10;
long posizioni[maxPosizioni];
int posSalvate = 0;
int indiceCorrente = 0; // Indice della *prossima* posizione nel ciclo "Vai"
int indiceVisualizzato = 0; // Indice della posizione *attualmente* mostrata all'utente
long posizioneAttuale = 0; // Posizione attuale del motore (in steps)
long offsetPosizione = 3000; // Offset iniziale o posizione di riferimento
const int eepromStart = 0; // Indirizzo di partenza in EEPROM per i dati
unsigned long lastUpdateDisplay; // Ultimo aggiornamento del display
// --- Prototipi delle Funzioni (Callback) ---
// Pulsanti Orario/Antiorario
void onOrarioHold();
void onAntiorarioHold();
void onOrarioReleased(); // Chiamata al rilascio di Orario
void onAntiorarioReleased(); // Chiamata al rilascio di Antiorario
// Pulsante Salva
void onSalvaClick(); // Potrebbe non servire, dato che usiamo il long press
void onSalvaLongPressStart(); // Inizio del long press
void onSalvaLongPressEnd(); // Fine del long press (rilascio dopo long press)
// Pulsante Vai
void onVaiClick();
// Funzioni di utilità
void updateDisplay();
void resetListaEEPROM();
// --- Setup ---
void setup() {
Serial.begin(9600);
lcd.begin(16, 4);
lcd.print("Sistema pronto");
delay(1000);
stepper.setMaxSpeed(2500);
stepper.setAcceleration(1000);
// Non serve più pinMode per i pulsanti se usi OneButton con pullup=true
// I pin vengono configurati dalla libreria
// --- Collegamento dei callback ai pulsanti ---
// Orario: movimento continuo se tenuto premuto, e rilascio
btnOrario_obj.attachDuringLongPress(onOrarioHold); // Movimento continuo
btnOrario_obj.attachRelease(onOrarioReleased); // Al rilascio
// Antiorario: movimento continuo se tenuto premuto, e rilascio
btnAntiorario_obj.attachDuringLongPress(onAntiorarioHold); // Movimento continuo
btnAntiorario_obj.attachRelease(onAntiorarioReleased); // Al rilascio
// Salva: Long press per salvare, e rilascio (per pulizia stato)
btnSalva_obj.setPressTicks(1000); // Imposta il tempo per il long press (1 secondo)
btnSalva_obj.attachLongPressStart(onSalvaLongPressStart); // Quando inizia il long press
btnSalva_obj.attachLongPressStop(onSalvaLongPressEnd); // Quando il long press termina (rilascio)
// Puoi anche usare attachClick se vuoi una breve pressione per altre azioni
// btnSalva_obj.attachClick(onSalvaClick);
// Vai: Click singolo per scorrere le posizioni
btnVaiA_obj.attachClick(onVaiClick);
// Se vuoi il reset con Salva+Vai, servirà una logica combinata nel loop o un approccio più avanzato
// per OneButton che rileva due pulsanti contemporaneamente (non è integrato di default).
// Per ora, manterremo la logica Salva+Vai nel loop per il reset.
// --- Caricamento EEPROM e Inizializzazione Posizioni ---
EEPROM.get(eepromStart, posSalvate);
if (posSalvate < 0 || posSalvate > maxPosizioni) {
posSalvate = 0;
}
for (int i = 0; i < posSalvate; i++) {
EEPROM.get(eepromStart + sizeof(int) + (i * sizeof(long)), posizioni[i]);
}
if (posSalvate == 0) {
posizioneAttuale = offsetPosizione;
posizioni[0] = offsetPosizione;
} else {
posizioneAttuale = posizioni[0];
}
stepper.setCurrentPosition(posizioneAttuale);
targetPosition = posizioneAttuale;
indiceCorrente = 0;
indiceVisualizzato = 0;
lcd.clear();
updateDisplay();
}
// --- Loop Principale ---
void loop() {
// --- Aggiornamento dello stato dei pulsanti (Tick) ---
btnOrario_obj.tick();
btnAntiorario_obj.tick();
btnSalva_obj.tick();
btnVaiA_obj.tick();
stepper.run();
// --- Reset della lista (Salva + Vai premuti insieme) ---
// Questa logica rimane qui perché OneButton non gestisce combinazioni dirette di pulsanti.
// Puoi leggere lo stato RAW dei pin per questa combinazione.
if (digitalRead(btnSalva) == LOW && digitalRead(btnVaiA) == LOW) {
resetListaEEPROM();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("EEPROM RESET!");
delay(1000); // Bloccante, ma ok per un evento di reset raro
updateDisplay();
// Potrebbe essere necessario un piccolo delay per evitare doppi reset
// o per impedire che i callback di OneButton vengano attivati subito dopo.
// E.g., while(digitalRead(btnSalva) == LOW || digitalRead(btnVaiA) == LOW); // Attesa rilascio
return;
}
// --- Aggiornamento Display per Movimento Manuale / A Riposo ---
if (millis() - lastUpdateDisplay >= 500 && !stepper.isRunning()) {
lastUpdateDisplay = millis();
updateDisplay();
}
// Le azioni specifiche dei pulsanti sono ora gestite dai callback.
// Il loop principale rimane più pulito.
}
// --- Funzioni di Callback ---
// --- Movimento Orario ---
void onOrarioHold() {
targetPosition -= stepIncrement;
indiceVisualizzato = -1; // Indica movimento manuale
// updateDisplay() non qui per evitare flickering eccessivo
}
void onOrarioReleased() {
// Azioni al rilascio del pulsante Orario (ad esempio, un suono, o reset di una modalità)
// Puoi anche forzare un updateDisplay qui se vuoi l'aggiornamento finale appena rilasciato.
// updateDisplay();
}
// --- Movimento Antiorario ---
void onAntiorarioHold() {
targetPosition += stepIncrement;
indiceVisualizzato = -1; // Indica movimento manuale
// updateDisplay() non qui
}
void onAntiorarioReleased() {
// updateDisplay();
}
// --- Pulsante Salva ---
void onSalvaLongPressStart() {
// Quando il long press del Salva inizia
Serial.println("Salva: Long Press START");
// Prepara l'interfaccia utente per il salvataggio
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Salva...");
lcd.setCursor(0, 1);
lcd.print("Attendi 1 sec");
}
void onSalvaLongPressEnd() {
// Quando il long press termina (pulsante rilasciato dopo 1 secondo)
Serial.println("Salva: Long Press END (Salvataggio)");
if (posSalvate < maxPosizioni) {
posizioni[posSalvate] = stepper.currentPosition();
EEPROM.put(eepromStart + sizeof(int) + (posSalvate * sizeof(long)), posizioni[posSalvate]); // Salva la singola posizione
posSalvate++;
EEPROM.put(eepromStart, posSalvate); // Aggiorna il conteggio
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Salvato piega:");
lcd.print(posSalvate);
lcd.setCursor(0, 1);
lcd.print(stepper.currentPosition() / stepPerMM, 2);
lcd.print(" mm");
delay(500); // Breve pausa per visualizzare il messaggio
indiceVisualizzato = posSalvate - 1; // Mostra l'ultima piega salvata
indiceCorrente = 0; // Il prossimo "Vai" partirà dalla prima piega
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("LISTA PIENA!");
delay(1000);
}
updateDisplay(); // Aggiorna il display allo stato finale
}
void onSalvaClick() {
// Questa funzione verrebbe chiamata per un click breve (se l'hai attaccata)
// Non fa nulla per ora, dato che il long press è per salvare.
// Potrebbe essere usata per mostrare un messaggio "Tieni premuto per salvare".
Serial.println("Salva: Click Breve");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Tieni SALVA");
lcd.setCursor(0, 1);
lcd.print("per 1 sec");
delay(1000); // Bloccante per un messaggio, ma ok per breve feedback
updateDisplay();
}
// --- Pulsante Vai ---
void onVaiClick() {
Serial.println("Vai: Click");
if (posSalvate > 0 && !stepper.isRunning()) {
if (indiceCorrente >= posSalvate) {
indiceCorrente = 0;
}
// Aggiorna l'indice che verrà visualizzato con la posizione a cui stiamo andando
indiceVisualizzato = indiceCorrente;
targetPosition = posizioni[indiceCorrente];
stepper.moveTo(targetPosition);
updateDisplay(); // Aggiorna il display SUBITO
// Prepara indiceCorrente per la *prossima* pressione di "Vai"
indiceCorrente = (indiceCorrente + 1) % posSalvate;
// Nota: Il delay di 300ms che avevi prima qui non è più necessario né desiderabile
// perché OneButton gestisce il debounce in modo asincrono.
// Mettere un delay qui bloccherebbe il loop e renderebbe il sistema meno reattivo.
} else if (posSalvate == 0) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Lista vuota!");
delay(1000);
updateDisplay();
}
}
// --- Funzioni di Utilità ---
void updateDisplay() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Piega N:");
if (posSalvate == 0) {
lcd.print("0 di 0");
} else {
if (indiceVisualizzato == -1) {
lcd.print("Manuale");
} else {
lcd.print(indiceVisualizzato + 1);
lcd.print(" di ");
lcd.print(posSalvate);
}
}
lcd.setCursor(0, 1);
lcd.print("Pos: ");
lcd.print(stepper.currentPosition() / stepPerMM, 2);
lcd.print(" mm");
}
void resetListaEEPROM() {
posSalvate = 0;
indiceCorrente = 0;
indiceVisualizzato = 0;
for (int i = 0; i < maxPosizioni; i++) {
posizioni[i] = 0;
}
EEPROM.put(eepromStart, posSalvate);
}