Aiuto con creazione / debugging menu on 4x20 I2C LCD Display

Ciao a tutti!
Sto progettando un banco automatico, utilizzando 3 NEMA 17.
Al momento mi trovo in questa situazione:
Ho scritto il codice per regolare i primi due parametri "Fence" e "Tool".
Con questo codice tutto sembra funzionare:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

// Pin definitions for Motor 1
#define STEP_PIN_1 5
#define DIR_PIN_1 6
#define ENABLE_PIN_1 7

// Pin definitions for Motor 2
#define STEP_PIN_2 8
#define DIR_PIN_2 9
#define ENABLE_PIN_2 10

// Pin definitions for Motor 3
#define STEP_PIN_3 11
#define DIR_PIN_3 12
#define ENABLE_PIN_3 13

// Encoder and Button Pins
#define ENCODER_PIN_A 2
#define ENCODER_PIN_B 3
#define ENCODER_BUTTON A0

// Endstop Pins
#define ENDSTOP_1 A1
#define ENDSTOP_2 A2
#define ENDSTOP_3 A3

// Motor and movement parameters
const float steps_per_mm = 100.0; // 200 steps = 2 mm
const int motor_steps_per_encoder = 10;
float mm_per_encoder_step = (float)motor_steps_per_encoder / steps_per_mm; // 0.1 mm per encoder step

// Display settings
LiquidCrystal_I2C lcd(0x27, 20, 4);
Encoder encoder(ENCODER_PIN_A, ENCODER_PIN_B);

long encoder_position = 0;
long previous_encoder_position = 0;
float mm_position = 0.0;
float previous_mm_position = 0.0;
int cursor_position = 0; // Cursor for post-movement menu

// State Variables
enum State { MAIN_MENU, SELECT_MM, MOVEMENT, POST_MOVEMENT };
State currentState = MAIN_MENU;

bool isFenceSelected = true; // Default selection

void setupMotor(int stepPin, int dirPin, int enablePin) {
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, LOW); // Enable motor driver
}

void moveMotor(int stepPin, int dirPin, float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  digitalWrite(dirPin, delta_mm > 0 ? HIGH : LOW);
  for (int i = 0; i < steps; i++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(1000);
  }
}

void moveSynchronousMotors(float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  bool direction = delta_mm > 0 ? HIGH : LOW;
  digitalWrite(DIR_PIN_1, direction);
  digitalWrite(DIR_PIN_2, direction);
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN_1, HIGH);
    digitalWrite(STEP_PIN_2, HIGH);
    delayMicroseconds(1000);
    digitalWrite(STEP_PIN_1, LOW);
    digitalWrite(STEP_PIN_2, LOW);
    delayMicroseconds(1000);
  }
}

void displayMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Select Mode:");
  lcd.setCursor(0, 1);
  lcd.print(isFenceSelected ? "> Fence" : "  Fence");
  lcd.setCursor(0, 2);
  lcd.print(isFenceSelected ? "  Tool" : "> Tool");
}

void displayMMSelection(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set MM - ");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print("Press to Confirm");
}

void displayPostMovement(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode: ");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 0 ? "> Change Value" : "  Change Value");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 1 ? "> Back to Menu" : "  Back to Menu");
}

void setup() {
  setupMotor(STEP_PIN_1, DIR_PIN_1, ENABLE_PIN_1);
  setupMotor(STEP_PIN_2, DIR_PIN_2, ENABLE_PIN_2);
  setupMotor(STEP_PIN_3, DIR_PIN_3, ENABLE_PIN_3);

  pinMode(ENCODER_BUTTON, INPUT_PULLUP);

  lcd.init();
  lcd.backlight();
  displayMenu();
}

void loop() {
  encoder_position = encoder.read() / 4;

  switch (currentState) {
    case MAIN_MENU:
      if (encoder_position != previous_encoder_position) {
        isFenceSelected = !isFenceSelected;
        displayMenu();
        previous_encoder_position = encoder_position;
      }
      if (digitalRead(ENCODER_BUTTON) == LOW) {
        currentState = SELECT_MM;
        displayMMSelection(isFenceSelected ? "Fence" : "Tool");
        delay(300);
      }
      break;

    case SELECT_MM:
      if (encoder_position != previous_encoder_position) {
        mm_position += (encoder_position - previous_encoder_position) * mm_per_encoder_step;
        if (mm_position < 0) mm_position = 0;
        displayMMSelection(isFenceSelected ? "Fence" : "Tool");
        previous_encoder_position = encoder_position;
      }
      if (digitalRead(ENCODER_BUTTON) == LOW) {
        currentState = MOVEMENT;
        delay(300);
      }
      break;

    case MOVEMENT:
      if (isFenceSelected) {
        moveSynchronousMotors(mm_position);
      } else {
        moveMotor(STEP_PIN_3, DIR_PIN_3, mm_position);
      }
      currentState = POST_MOVEMENT;
      displayPostMovement(isFenceSelected ? "Fence" : "Tool");
      break;

    case POST_MOVEMENT:
      if (encoder_position != previous_encoder_position) {
        cursor_position = (encoder_position % 2 + 2) % 2;
        displayPostMovement(isFenceSelected ? "Fence" : "Tool");
        previous_encoder_position = encoder_position;
      }
      if (digitalRead(ENCODER_BUTTON) == LOW) {
        if (cursor_position == 0) {
          currentState = SELECT_MM;
          displayMMSelection(isFenceSelected ? "Fence" : "Tool");
        } else {
          currentState = MAIN_MENU;
          displayMenu();
        }
        delay(300);
      }
      break;
  }
}

Il display mostra correttamente una scelta, tra fence e tool.

Fatta la selezione consente di modificare lo spostamento in decimi di mm.

Fatto lo spostamento permette di tornare al menu principale o di modificare nuovamente la posizione.

Ho provato a questo punto ad "espandere" il menù:
Ora mostra correttamente

Tuttavia ogni volta che faccio una selezione, il tutto va in blocco, continua a lampeggiare il display come se facesse aggiornamenti continui e non mi consente di fare alcuna selezione.
Example-ezgif.com-video-to-gif-converter

In aggiunta le funzioni che prima funzionavano e mi consentivano di fare gli aggiustamenti di posizione non sono più disponibili.
L'idea era quella di espandere il menu per aggiungere pian piano funzioni, ma ora non riesco a venirne fuori...
Questo il codice che non riesco a far funzionare:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

// Pin definitions for Motor 1
#define STEP_PIN_1 5
#define DIR_PIN_1 6
#define ENABLE_PIN_1 7

// Pin definitions for Motor 2
#define STEP_PIN_2 8
#define DIR_PIN_2 9
#define ENABLE_PIN_2 10

// Pin definitions for Motor 3
#define STEP_PIN_3 11
#define DIR_PIN_3 12
#define ENABLE_PIN_3 13

// Encoder and Button Pins
#define ENCODER_PIN_A 2
#define ENCODER_PIN_B 3
#define ENCODER_BUTTON A0

// Endstop Pins
#define ENDSTOP_1 A1
#define ENDSTOP_2 A2
#define ENDSTOP_3 A3

// Motor and movement parameters
const float steps_per_mm = 100.0; // 200 steps = 2 mm
const int motor_steps_per_encoder = 10;
float mm_per_encoder_step = (float)motor_steps_per_encoder / steps_per_mm; // 0.1 mm per encoder step

// Display settings
LiquidCrystal_I2C lcd(0x27, 20, 4);
Encoder encoder(ENCODER_PIN_A, ENCODER_PIN_B);

long encoder_position = 0;
long previous_encoder_position = 0;
float mm_position = 0.0;
float previous_mm_position = 0.0;
int cursor_position = 0; // Cursor for post-movement menu

// State Variables
enum State { MAIN_MENU, ZERO_CALIBRATION, FENCE_TOOL_MENU, SELECT_MM, MOVEMENT, POST_MOVEMENT };
State currentState = MAIN_MENU;

bool isFenceSelected = true; // Default selection

void setupMotor(int stepPin, int dirPin, int enablePin) {
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, LOW); // Enable motor driver
}

void moveMotor(int stepPin, int dirPin, float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  digitalWrite(dirPin, delta_mm > 0 ? HIGH : LOW);
  for (int i = 0; i < steps; i++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(1000);
  }
}

void moveSynchronousMotors(float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  bool direction = delta_mm > 0 ? HIGH : LOW;
  digitalWrite(DIR_PIN_1, direction);
  digitalWrite(DIR_PIN_2, direction);
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN_1, HIGH);
    digitalWrite(STEP_PIN_2, HIGH);
    delayMicroseconds(1000);
    digitalWrite(STEP_PIN_1, LOW);
    digitalWrite(STEP_PIN_2, LOW);
    delayMicroseconds(1000);
  }
}

void displayMainMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Welcome!");
  lcd.setCursor(0, 1);
  lcd.print(cursor_position == 0 ? "> Zero Calibration" : "  Zero Calibration");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 1 ? "> Fence & Tool" : "  Fence & Tool");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 2 ? "> Apps" : "  Apps");
}

void displayFenceToolMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Fence & Tool Menu");
  lcd.setCursor(0, 1);
  lcd.print(isFenceSelected ? "> Fence" : "  Fence");
  lcd.setCursor(0, 2);
  lcd.print(isFenceSelected ? "  Tool" : "> Tool");
}

void displayMMSelection(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set MM - Mode:");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print("Press to Confirm");
}

void displayPostMovement(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode: ");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 0 ? "> Change Value" : "  Change Value");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 1 ? "> Back to Menu" : "  Back to Menu");
}

void setup() {
  setupMotor(STEP_PIN_1, DIR_PIN_1, ENABLE_PIN_1);
  setupMotor(STEP_PIN_2, DIR_PIN_2, ENABLE_PIN_2);
  setupMotor(STEP_PIN_3, DIR_PIN_3, ENABLE_PIN_3);

  pinMode(ENCODER_BUTTON, INPUT_PULLUP);

  lcd.init();
  lcd.backlight();
  displayMainMenu();
}

void loop() {
  encoder_position = encoder.read() / 4;

  switch (currentState) {
    case MAIN_MENU:
      if (encoder_position != previous_encoder_position) {
        cursor_position = (encoder_position % 3 + 3) % 3;
        displayMainMenu();
        previous_encoder_position = encoder_position;
      }
      if (digitalRead(ENCODER_BUTTON) == LOW) {
        if (cursor_position == 0) currentState = ZERO_CALIBRATION;
        else if (cursor_position == 1) currentState = FENCE_TOOL_MENU;
        else if (cursor_position == 2) currentState = MAIN_MENU;
        delay(300);
      }
      break;

    case ZERO_CALIBRATION:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Zero Calibration");
      lcd.setCursor(0, 1);
      lcd.print("> Back to Menu");
      if (digitalRead(ENCODER_BUTTON) == LOW) {
        currentState = MAIN_MENU;
        delay(300);
      }
      break;

    case FENCE_TOOL_MENU:
      displayFenceToolMenu();
      if (digitalRead(ENCODER_BUTTON) == LOW) {
        currentState = SELECT_MM;
        delay(300);
      }
      break;
  }
}

Sto utilizzando un display 4x20 ed un encoder ky-040 per effettuare le selezioni e spostarmi nel menu. Nel codice precedente, correttamente permetteva di aumentare il valore in decimi di mm dove richiesto, una volta effettuato il movimento, nuovamente veniva utilizzato per spostarsi tra le opzioni disponibili.

Spero in un vostro aiuto :pray:

Grazie mille

ciao...se non ho visto male non verifichi il "rilascio" del pulsante dell'encoder...quindi quando cambi di case cambi subito di stato...devi crearti un "qualche cosa" che ritorna il comando invito da pressione pulsante encoder e non semplicemente leggerne lo stato dentro allo switch.
spero di essermi spiegato.

Perdonami ma non ho capito :sweat_smile:

ciao...ho aggiunto una funzione che si chiama "nuovoComando()"
dimmi se funziona.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

// Pin definitions for Motor 1
#define STEP_PIN_1 5
#define DIR_PIN_1 6
#define ENABLE_PIN_1 7

// Pin definitions for Motor 2
#define STEP_PIN_2 8
#define DIR_PIN_2 9
#define ENABLE_PIN_2 10

// Pin definitions for Motor 3
#define STEP_PIN_3 11
#define DIR_PIN_3 12
#define ENABLE_PIN_3 13

// Encoder and Button Pins
#define ENCODER_PIN_A 2
#define ENCODER_PIN_B 3
#define ENCODER_BUTTON A0

// Endstop Pins
#define ENDSTOP_1 A1
#define ENDSTOP_2 A2
#define ENDSTOP_3 A3

// Motor and movement parameters
const float steps_per_mm = 100.0; // 200 steps = 2 mm
const int motor_steps_per_encoder = 10;
float mm_per_encoder_step = (float)motor_steps_per_encoder / steps_per_mm; // 0.1 mm per encoder step

// Display settings
LiquidCrystal_I2C lcd(0x27, 20, 4);
Encoder encoder(ENCODER_PIN_A, ENCODER_PIN_B);

long encoder_position = 0;
long previous_encoder_position = 0;
float mm_position = 0.0;
float previous_mm_position = 0.0;
int cursor_position = 0; // Cursor for post-movement menu

// State Variables
enum State { MAIN_MENU, ZERO_CALIBRATION, FENCE_TOOL_MENU, SELECT_MM, MOVEMENT, POST_MOVEMENT };
State currentState = MAIN_MENU;

bool isFenceSelected = true; // Default selection

void setupMotor(int stepPin, int dirPin, int enablePin) {
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, LOW); // Enable motor driver
}

void moveMotor(int stepPin, int dirPin, float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  digitalWrite(dirPin, delta_mm > 0 ? HIGH : LOW);
  for (int i = 0; i < steps; i++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(1000);
  }
}

void moveSynchronousMotors(float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  bool direction = delta_mm > 0 ? HIGH : LOW;
  digitalWrite(DIR_PIN_1, direction);
  digitalWrite(DIR_PIN_2, direction);
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN_1, HIGH);
    digitalWrite(STEP_PIN_2, HIGH);
    delayMicroseconds(1000);
    digitalWrite(STEP_PIN_1, LOW);
    digitalWrite(STEP_PIN_2, LOW);
    delayMicroseconds(1000);
  }
}

void displayMainMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Welcome!");
  lcd.setCursor(0, 1);
  lcd.print(cursor_position == 0 ? "> Zero Calibration" : "  Zero Calibration");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 1 ? "> Fence & Tool" : "  Fence & Tool");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 2 ? "> Apps" : "  Apps");
}

void displayFenceToolMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Fence & Tool Menu");
  lcd.setCursor(0, 1);
  lcd.print(isFenceSelected ? "> Fence" : "  Fence");
  lcd.setCursor(0, 2);
  lcd.print(isFenceSelected ? "  Tool" : "> Tool");
}

void displayMMSelection(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set MM - Mode:");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print("Press to Confirm");
}

void displayPostMovement(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode: ");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 0 ? "> Change Value" : "  Change Value");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 1 ? "> Back to Menu" : "  Back to Menu");
}

bool nuovoComando() {
  static bool locPrevStatus = HIGH;
  bool locStatoPulsante = digitalRead(ENCODER_BUTTON); //leggo pulsante encoder
  bool locNuovoComando = false;
  
  if (locStatoPulsante == LOW && locPrevStatus == HIGH) { // pulsante premuto
    locPrevStatus = LOW;
    locNuovoComando = true;
  }
  if (locStatoPulsante == HIGH && locPrevStatus == LOW) { // pulsante rilaciato
    locPrevStatus = HIGH;
  }
  return locNuovoComando;
}

void setup() {
  setupMotor(STEP_PIN_1, DIR_PIN_1, ENABLE_PIN_1);
  setupMotor(STEP_PIN_2, DIR_PIN_2, ENABLE_PIN_2);
  setupMotor(STEP_PIN_3, DIR_PIN_3, ENABLE_PIN_3);

  pinMode(ENCODER_BUTTON, INPUT_PULLUP);

  lcd.init();
  lcd.backlight();
  displayMainMenu();
}

void loop() {
  encoder_position = encoder.read() / 4;

  switch (currentState) {
    case MAIN_MENU:
      if (encoder_position != previous_encoder_position) {
        cursor_position = (encoder_position % 3 + 3) % 3;
        displayMainMenu();
        previous_encoder_position = encoder_position;
      }
      if (nuovoComando()) {
        if (cursor_position == 0) currentState = ZERO_CALIBRATION;
        else if (cursor_position == 1) currentState = FENCE_TOOL_MENU;
        else if (cursor_position == 2) currentState = MAIN_MENU;
        delay(300);
      }
      break;

    case ZERO_CALIBRATION:
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Zero Calibration");
      lcd.setCursor(0, 1);
      lcd.print("> Back to Menu");
      if (nuovoComando()) {
        currentState = MAIN_MENU;
        delay(300);
      }
      break;

    case FENCE_TOOL_MENU:
      displayFenceToolMenu();
      if (nuovoComando()) {
        currentState = SELECT_MM;
        delay(300);
      }
      break;
  }
}

Purtroppo non è cambiato nulla :face_with_diagonal_mouth:
Continua a lampeggiare come prima e non consente alcuna selezione :frowning:

Leggendo sul forum ho trovato questo articolo:
https://forum.arduino.cc/t/due-to-millis-my-4x20-lcd-flickering-how-can-i-prevent-it/684636/8

Sembra che il problema sia dovuto ai delay...
Purtroppo ho provato diverse modifiche, spostato ed eliminato i delay, ma senza successo...

chiaro...oltre al pulante devi dire che quel case è stato già eseguito...prova quest...c'è una variabile "eseguito":

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

// Pin definitions for Motor 1
#define STEP_PIN_1 5
#define DIR_PIN_1 6
#define ENABLE_PIN_1 7

// Pin definitions for Motor 2
#define STEP_PIN_2 8
#define DIR_PIN_2 9
#define ENABLE_PIN_2 10

// Pin definitions for Motor 3
#define STEP_PIN_3 11
#define DIR_PIN_3 12
#define ENABLE_PIN_3 13

// Encoder and Button Pins
#define ENCODER_PIN_A 2
#define ENCODER_PIN_B 3
#define ENCODER_BUTTON A0

// Endstop Pins
#define ENDSTOP_1 A1
#define ENDSTOP_2 A2
#define ENDSTOP_3 A3

// Motor and movement parameters
const float steps_per_mm = 100.0; // 200 steps = 2 mm
const int motor_steps_per_encoder = 10;
float mm_per_encoder_step = (float)motor_steps_per_encoder / steps_per_mm; // 0.1 mm per encoder step

// Display settings
LiquidCrystal_I2C lcd(0x27, 20, 4);
Encoder encoder(ENCODER_PIN_A, ENCODER_PIN_B);

long encoder_position = 0;
long previous_encoder_position = 0;
float mm_position = 0.0;
float previous_mm_position = 0.0;
int cursor_position = 0; // Cursor for post-movement menu

bool eseguito = false;

// State Variables
enum State { MAIN_MENU, ZERO_CALIBRATION, FENCE_TOOL_MENU, SELECT_MM, MOVEMENT, POST_MOVEMENT };
State currentState = MAIN_MENU;

bool isFenceSelected = true; // Default selection

void setupMotor(int stepPin, int dirPin, int enablePin) {
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, LOW); // Enable motor driver
}

void moveMotor(int stepPin, int dirPin, float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  digitalWrite(dirPin, delta_mm > 0 ? HIGH : LOW);
  for (int i = 0; i < steps; i++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(1000);
  }
}

void moveSynchronousMotors(float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  bool direction = delta_mm > 0 ? HIGH : LOW;
  digitalWrite(DIR_PIN_1, direction);
  digitalWrite(DIR_PIN_2, direction);
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN_1, HIGH);
    digitalWrite(STEP_PIN_2, HIGH);
    delayMicroseconds(1000);
    digitalWrite(STEP_PIN_1, LOW);
    digitalWrite(STEP_PIN_2, LOW);
    delayMicroseconds(1000);
  }
}

void displayMainMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Welcome!");
  lcd.setCursor(0, 1);
  lcd.print(cursor_position == 0 ? "> Zero Calibration" : "  Zero Calibration");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 1 ? "> Fence & Tool" : "  Fence & Tool");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 2 ? "> Apps" : "  Apps");
}

void displayFenceToolMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Fence & Tool Menu");
  lcd.setCursor(0, 1);
  lcd.print(isFenceSelected ? "> Fence" : "  Fence");
  lcd.setCursor(0, 2);
  lcd.print(isFenceSelected ? "  Tool" : "> Tool");
}

void displayMMSelection(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set MM - Mode:");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print("Press to Confirm");
}

void displayPostMovement(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode: ");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 0 ? "> Change Value" : "  Change Value");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 1 ? "> Back to Menu" : "  Back to Menu");
}

bool nuovoComando() {
  static bool locPrevStatus = HIGH;
  bool locStatoPulsante = digitalRead(ENCODER_BUTTON); //leggo pulsante encoder
  bool locNuovoComando = false;

  if (locStatoPulsante == LOW && locPrevStatus == HIGH) { // pulsante premuto
    locPrevStatus = LOW;
    locNuovoComando = true;
  }
  if (locStatoPulsante == HIGH && locPrevStatus == LOW) { // pulsante rilaciato
    locPrevStatus = HIGH;
  }
  return locNuovoComando;
}

void setup() {
  setupMotor(STEP_PIN_1, DIR_PIN_1, ENABLE_PIN_1);
  setupMotor(STEP_PIN_2, DIR_PIN_2, ENABLE_PIN_2);
  setupMotor(STEP_PIN_3, DIR_PIN_3, ENABLE_PIN_3);

  pinMode(ENCODER_BUTTON, INPUT_PULLUP);

  lcd.init();
  lcd.backlight();
  displayMainMenu();
}

void loop() {
  encoder_position = encoder.read() / 4;

  switch (currentState) {
    case MAIN_MENU:
      if (encoder_position != previous_encoder_position) {
        cursor_position = (encoder_position % 3 + 3) % 3;
        if(!eseguito){
        displayMainMenu();
        eseguito = true;
        }
        previous_encoder_position = encoder_position;
      }
      if (nuovoComando()) {
        if (cursor_position == 0) currentState = ZERO_CALIBRATION;
        else if (cursor_position == 1) currentState = FENCE_TOOL_MENU;
        else if (cursor_position == 2) currentState = MAIN_MENU;
        delay(300);
      }
      break;

    case ZERO_CALIBRATION:
    if(!eseguito){
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Zero Calibration");
      lcd.setCursor(0, 1);
      lcd.print("> Back to Menu");
      eseguito = true;
    }
      if (nuovoComando()) {
        currentState = MAIN_MENU;
        delay(300);
        eseguito = false;
      }
      break;

    case FENCE_TOOL_MENU:
      displayFenceToolMenu();
      if (nuovoComando()) {
        currentState = SELECT_MM;
        delay(300);
      }
      break;
  }
}

Compila ma non riesco a testare se flickera ancora perché non consente più la selezione all’interno del menu :see_no_evil:

il mio era un esempio...premi subito e vedi e flickera...dopo va sistemato di conseguenza

Purtroppo se premo ora non succede nulla…

io non ho un encoder ma solo un pulsante...ho emulato il variare dell'encoder eliminando la lettura...quindi prova a premere solo il pulsante con questo ti passa tra i primi 2 menù tornando ogni volta al principale...

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Encoder.h>

// Pin definitions for Motor 1
#define STEP_PIN_1 5
#define DIR_PIN_1 6
#define ENABLE_PIN_1 7

// Pin definitions for Motor 2
#define STEP_PIN_2 8
#define DIR_PIN_2 9
#define ENABLE_PIN_2 10

// Pin definitions for Motor 3
#define STEP_PIN_3 11
#define DIR_PIN_3 12
#define ENABLE_PIN_3 13

// Encoder and Button Pins
#define ENCODER_PIN_A 2
#define ENCODER_PIN_B 3
#define ENCODER_BUTTON A0

// Endstop Pins
#define ENDSTOP_1 A1
#define ENDSTOP_2 A2
#define ENDSTOP_3 A3

// Motor and movement parameters
const float steps_per_mm = 100.0; // 200 steps = 2 mm
const int motor_steps_per_encoder = 10;
float mm_per_encoder_step = (float)motor_steps_per_encoder / steps_per_mm; // 0.1 mm per encoder step

// Display settings
LiquidCrystal_I2C lcd(0x27, 20, 4);
Encoder encoder(ENCODER_PIN_A, ENCODER_PIN_B);

long encoder_position = 0;
long previous_encoder_position = 0;
float mm_position = 0.0;
long previous_mm_position = -1;
int cursor_position = 0; // Cursor for post-movement menu

bool eseguito = false;

// State Variables
enum State { MAIN_MENU, ZERO_CALIBRATION, FENCE_TOOL_MENU, SELECT_MM, MOVEMENT, POST_MOVEMENT };
State currentState = MAIN_MENU;

bool isFenceSelected = true; // Default selection

void setupMotor(int stepPin, int dirPin, int enablePin) {
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, LOW); // Enable motor driver
}

void moveMotor(int stepPin, int dirPin, float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  digitalWrite(dirPin, delta_mm > 0 ? HIGH : LOW);
  for (int i = 0; i < steps; i++) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(1000);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(1000);
  }
}

void moveSynchronousMotors(float delta_mm) {
  int steps = abs(delta_mm) * steps_per_mm;
  bool direction = delta_mm > 0 ? HIGH : LOW;
  digitalWrite(DIR_PIN_1, direction);
  digitalWrite(DIR_PIN_2, direction);
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PIN_1, HIGH);
    digitalWrite(STEP_PIN_2, HIGH);
    delayMicroseconds(1000);
    digitalWrite(STEP_PIN_1, LOW);
    digitalWrite(STEP_PIN_2, LOW);
    delayMicroseconds(1000);
  }
}

void displayMainMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Welcome!");
  lcd.setCursor(0, 1);
  lcd.print(cursor_position == 0 ? "> Zero Calibration" : "  Zero Calibration");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 1 ? "> Fence & Tool" : "  Fence & Tool");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 2 ? "> Apps" : "  Apps");
}

void displayFenceToolMenu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Fence & Tool Menu");
  lcd.setCursor(0, 1);
  lcd.print(isFenceSelected ? "> Fence" : "  Fence");
  lcd.setCursor(0, 2);
  lcd.print(isFenceSelected ? "  Tool" : "> Tool");
}

void displayMMSelection(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Set MM - Mode:");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print("Press to Confirm");
}

void displayPostMovement(const char* mode) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Mode: ");
  lcd.print(mode);
  lcd.setCursor(0, 1);
  lcd.print("Value: ");
  lcd.print(mm_position);
  lcd.print(" mm");
  lcd.setCursor(0, 2);
  lcd.print(cursor_position == 0 ? "> Change Value" : "  Change Value");
  lcd.setCursor(0, 3);
  lcd.print(cursor_position == 1 ? "> Back to Menu" : "  Back to Menu");
}

bool nuovoComando() {
  static bool locPrevStatus = HIGH;
  bool locStatoPulsante = digitalRead(ENCODER_BUTTON); //leggo pulsante encoder
  bool locNuovoComando = false;

  if (locStatoPulsante == LOW && locPrevStatus == HIGH) { // pulsante premuto
    locPrevStatus = LOW;
    locNuovoComando = true;
  }
  if (locStatoPulsante == HIGH && locPrevStatus == LOW) { // pulsante rilaciato
    locPrevStatus = HIGH;
  }
  return locNuovoComando;
}

void setup() {
  setupMotor(STEP_PIN_1, DIR_PIN_1, ENABLE_PIN_1);
  setupMotor(STEP_PIN_2, DIR_PIN_2, ENABLE_PIN_2);
  setupMotor(STEP_PIN_3, DIR_PIN_3, ENABLE_PIN_3);

  pinMode(ENCODER_BUTTON, INPUT_PULLUP);

  lcd.init();
  lcd.backlight();
  displayMainMenu();
}

void loop() {
  //encoder_position = encoder.read() / 4;

  switch (currentState) {
    case MAIN_MENU:
    //Serial.println(encoder_position);
      if (encoder_position != previous_encoder_position) {
        cursor_position = (encoder_position % 3 + 3) % 3;
        eseguito = false;
        if (!eseguito) {
          displayMainMenu();
          eseguito = true;
        }
        previous_encoder_position = encoder_position;
      }
      if (nuovoComando()) {
        if (cursor_position == 0) currentState = ZERO_CALIBRATION;
        else if (cursor_position == 1) currentState = FENCE_TOOL_MENU;
        else if (cursor_position == 2) currentState = MAIN_MENU;
        eseguito = false;
        delay(300);
      }
      break;

    case ZERO_CALIBRATION:
      if (!eseguito) {
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Zero Calibration");
        lcd.setCursor(0, 1);
        lcd.print("> Back to Menu");
        eseguito = true;
      }
      if (nuovoComando()) {
        currentState = MAIN_MENU;
        encoder_position+=4;
        delay(300);
        eseguito = false;
      }
      break;

    case FENCE_TOOL_MENU:
    if(!eseguito){
      displayFenceToolMenu();
      eseguito = true;
    }
      if (nuovoComando()) {
        currentState = MAIN_MENU;
        encoder_position+=4;
        eseguito = false;
        delay(300);
      }
      break;
  }
}

ps:...non sono un esperto di encoder ma avevi messo il previous_mm_position come float

Ora dopo la prima selezione no flickera più! :partying_face:
Tuttavia se entro dentro un sotto-menu, non accade più nulla, se seleziono "Back to main menu", non torna indietro e lo stesso per Fence & Tool, entro nel primo sottomenu ma una volta fatta la selezione, non riesco a scendere nelle funzioni previste :confused:

ciao...a me con solo pulsante, quindi non "giro" ma premo, fa le seguenti cose:

appare il main menu
entra nello zeroing menu
ritorna al main menu
entra nel fence and tool
ritorna al main menu

ad ogni ritorno dovresti vedere la "freccia" che punta un menù diverso.

Non ho capito cosa volevi fare con questa istruzione

Secondo me ti conviene separare "concettualmente" le diverse funzionalità anche usando più "sotto-macchine" a stati, altrimenti è troppo facile confondersi e perdere la strada.

La gestione del display è una funzionalità ma deve essere svincolata dal resto.
La gestione dell'encoder e della posizione selezionata un'altra cosa ancora, ma che ovviamente influenza lo stato della prima, etc etc.

Ad esempio qui ho preso il tuo sketch e l'ho modificato qua e la...
Non mi sono soffermato troppo sui vari sotto-menu, ma il funzionamento di base di quello che intendevo prima c'è.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.