Millis in switch case

Ciao a tutti,

ho un pò di problemi con l'usare switch case e millis insieme. Quello che sto cercando di fare è di far accendere un Led secondo la posizione in cui si trova un rotary encoder. Quindi quando l'encoder si trova in posizione 1 lo switch case 1 è eseguito ed il led in esso si accende per 5 secondi e poi si spegne automaticamente. Non voglio usare delay perché blocca l'esecuzione di tutto il programma e ho bisogno che resti attivo, nel senso che se muovo l'encoder durante "quei 5 secondi di led acceso" posso passare allo switch case precedente o successivo senza dover aspettare la fine del conteggio (come avviene con il delay).

In poche parole quello che voglio fare è una specie di timer, dove case1 è 5 secondi, case2 10 secondi e così via.

Questo è il mio codice ad ora ma non funziona, da quando ho aggiunto la funzione millis. Senza millis funziona una favola, nel senso che se muovo l'encoder il rispettivo led si accende ma poi resta acceso!!!

Non so davvero dove sto sbagliando e cosa non va! Qualcuno può aiutarmi?!?

#include <Adafruit_NeoPixel.h>
#include <avr/power.h>

const int  buttonPin = 4;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

int state = 0;

int encoder0PinA = 12;
int encoder0PinB = 11;
int encoder0Pos = -1;
int encoder0PinALast = LOW;
//int n = LOW;
int currentValue=LOW;

unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 5000;           // interval at which to blink (milliseconds)

//int i=interval;

//LED
#define PIN 6
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      5
int sine[5] = {0, 1, 2, 3, 4}; //these are the pixels in order of animation


// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);

  pinMode (encoder0PinA, INPUT);
  pinMode (encoder0PinB, INPUT);

  pixels.begin();
  pixels.setBrightness(40); //adjust brightness here
  pixels.show(); // Initialize all pixels to 'off'

  // initialize serial communication:
  Serial.begin(9600);
}

void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);
  currentValue = digitalRead(encoder0PinA);

  unsigned long currentMillis = millis();
  
  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button
      // wend from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
    }
    else {
      // if the current state is LOW then the button
      // wend from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;

  // turns ON the LED every two button pushes by checking the modulo of the button push counter.
  //if (buttonPushCounter % 2 == 0) {
    if (buttonState == HIGH) {
      for (int i = 0; i < NUMPIXELS; i++) {
        pixels.setPixelColor(i, pixels.Color(75, 250, 100)); //change RGB color value here
        pixels.show();

        for (int j = 0; j < 5; j++) {
          pixels.setPixelColor(j, pixels.Color(0, 0, 0));
        }

        pixels.show();
      }

      digitalWrite(ledPin, HIGH);

      encoder0Pos = 0;

    //}
  }

  if ((encoder0PinALast == LOW) && (currentValue == HIGH)) {
    if (digitalRead(encoder0PinB) == LOW) {
      encoder0Pos--;

//encoder0Pos = currentValue;

if(currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;  
      switch (encoder0Pos) {

        case 0:    // your hand is on the sensor
          for (int i = 0; i <= NUMPIXELS; i++) {
            // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
            pixels.setPixelColor(i, pixels.Color(0, 0, 0)); // Moderately bright green color.
            pixels.show();
          }
          Serial.println("OFF");
          break;

        case 1:    // your hand is on the sensor
          pixels.setPixelColor(sine[1], pixels.Color(0, 0, 0)); // Moderately bright green color.
          pixels.show();
          Serial.println("LED 1");
          break;

        case 2:    // your hand is close to the sensor
          pixels.setPixelColor(sine[2], pixels.Color(0, 0, 0)); // Moderately bright green color.
          pixels.show();
          Serial.println("LED 2");
          break;

        case 3:    // your hand is a few inches from the sensor
          pixels.setPixelColor(sine[3], pixels.Color(0, 0, 0)); // Moderately bright green color.
          pixels.show();
          Serial.println("LED 3");
          break;

        case 4:    // your hand is nowhere near the sensor
          pixels.setPixelColor(sine[4], pixels.Color(0, 0, 0)); // Moderately bright green color.
          pixels.show();
          Serial.println("LED 4");
          break;
      }

      if (encoder0Pos <= 0) {
        encoder0Pos = 0;
      }
    }


    else {
      encoder0Pos++;

      switch (encoder0Pos) {

        case 0:    // your hand is on the sensor
          for (int i = 0; i <= NUMPIXELS; i++) {
            // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
            pixels.setPixelColor(i, pixels.Color(0, 0, 0)); // Moderately bright green color.
            pixels.show();
          }
          Serial.println("OFF");
          break;

        case 1:    // your hand is on the sensor
          pixels.setPixelColor(sine[0], pixels.Color(60, 0, 0)); // Moderately bright green color.
          pixels.show();
          Serial.println("LED 1");
          break;

        case 2:    // your hand is close to the sensor
          pixels.setPixelColor(sine[1], pixels.Color(60, 0, 0)); // Moderately bright green color.
          pixels.show();
          Serial.println("LED 2");
          break;

        case 3:    // your hand is a few inches from the sensor
          pixels.setPixelColor(sine[2], pixels.Color(60, 0, 0)); // Moderately bright green color.
          pixels.show();
          Serial.println("LED 3");
          break;

        case 4:    // your hand is nowhere near the sensor
          pixels.setPixelColor(sine[3], pixels.Color(60, 0, 0)); // Moderately bright green color.
          pixels.show();
          Serial.println("LED 4");
          break;
      }
      //Maximum value steps 24 that means 2 hrs
      if (encoder0Pos >= 24) {
        encoder0Pos = 24;
      }
    }
    Serial.print (encoder0Pos); //Display steps
    Serial.println (" ");

  }
  encoder0PinALast = currentValue; //take count of encoder steps
  }
}

Grazie mille in anticipo :smiley:

Perché usi switch case?

In 4 dei 5 casi metti come indice il valore della variabile switch.

È meglio con if determinare il caso 0 e fare il fade (non bloccante perchße quello che hai scritto é bloccante) e nei altri 4 casi scrivere una funzione che prende come parametro il numero del LED.

Ciao Uwe

Lo switch case può tornare utile per implementare una macchina a stati finiti. Solitamente ci si accontenta di implementare gli stati più evidenti tralasciando la vera macchina a stati finiti.

Uno stato è composto da transizione in ingresso, dallo stato in permanenza e dalla transizione di uscita.

Lo stato di transizione può eseguire codice al fine di valutare se è necessario o meno effettuare la transizione in stato di permanenza, oppure non fare nulla.

L'applicazione dell'encoder mi sembra necessiti di più stati, cioè tre stati per ogni posizione dell'encoder.

Stato 0 == Stato di transizione verso stato 1 (0->1)
Stato 1 == Stato di permanenza -> transizione verso stato 2 (1>2)
Stato 2 == Stato di transizione in uscita da stato 2 (2>??)

?? Può essere uno stato predefinito o uno stato tra quelli disponibili e il valore può essere deciso durante lo stato 1 o dallo stato 2.

Riguardo a millis io credo che si fa confusione anche quando la si sa usare tanto che preferisco introdurla quando
ho già codice funzionante.

Io procederei per gradi, analizzando e implementato pezzi di codice al fine di testarne il comportamento.

Altre osservazioni utili
Quando assegni alla variabile "state" un valore, in realtà stai prenotando lo stato da eseguire al prossimo ciclo di clock.

Usando i tre stati per ogni posizione dell'encoder non si può più incrementare la variabile di una unità, ma di 3.
Se si prevede ci posizioni di encoder il numero di "case" necessari è 5 x 3 = 15.

Prendiamo come esempio la posizione 0.
Switch Case 0: // Transizione verso 1
if, for, altri switch e/o millis ecc
STATE = 1; // prenota lo stato 1
break;
Switch Case 1: // Stato di permanenza
Finché non muovi l'encoder si rimane in questo stato
Muovendo l'encoder si deve cedere il controllo allo stato di uscita da 1 e quindi
STATO = 2;
break;
Switch Case 2: // Stato di uscita
Qui si esegue codice per preparare l'uscita da stato 1
STATE = newState;
break;

Ovviamente in Case 1 dobbiamo controllare se l'encoder è stato mosso, per evitare di controllarlo in tutti gli stati
possiamo usare lo stato di default il quale assegna "true" alla variabile encoderChanged.
In Case 1 allora ci limitiamo a controlla re se encoderChanged == true, se lo è encoderChanged = false.

Non ti ho dato la soluzione anche perché non la ho e non mi sono mai ritrovato ad usare encoder, ma spero
di averti dato qualche spunto su cui discutere e implementare il codice pezzetto su pezzetto.

Ciao.