Problem with KY040 AND LCD Display

Hello,
I'm developping a project and encounter an issue with a block of code I tested.

I have a character table whith 20 items (0-19)

I select the item with the encoder and must display his content.

It's OK if I only display on the monitor, but when I add the output to the LCD it seems to lock. The display is OK (tested at the beginning of the sketch)
Is it possible it's due by the fact I use interrupts to read the encoder.

Here are the code lines implied:

==========================================================================

#include <Wire.h>
#include <LiquidCrystal_I2C.h> 
#define pinArduinoRaccordementSignalSW  2 // La pin D2 recevra la ligne SW du module KY-040
#define pinArduinoRaccordementSignalCLK 3 // La pin D3 recevra la ligne CLK du module KY-040
#define pinArduinoRaccordementSignalDT  4 // La pin D4 la ligne DT du module KY-040

LiquidCrystal_I2C lcd(0x27,20,4); 

 void setup ()
 {
  lcd.init();
  lcd.backlight();



    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalCLK), changementDetecteSurLigneCLK, FALLING); // FALLING => détecte tout front descendant
    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalSW), changementDetecteSurLigneSW, CHANGE);    // CHANGE => détecte tout changement d'état

    lcd.setCursor(0, 3);
    lcd.print("test lcd");  =====> AFFICHAGE OK

void changementDetecteSurLigneCLK() {

    // Lecture de la ligne DT, issue du KY-040, et arrivant sur l'arduino
    int etatActuelDeLaLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);
    int etatActuelDeLaLigneCLK = LOW;

    if (etatActuelDeLaLigneCLK != etatActuelDeLaLigneDT) {
        compteur++;
        
               Serial.print(F("Sens = horaire | Valeur du compteur = "));
    }
      
    else {
        compteur--;
                
        Serial.print(F("Sens = antihoraire | Valeur du compteur = "));
    }



    if (compteur > (MAX_Process -1))  {
            compteur = 0;
        }
    if (compteur < 0 )  {
            compteur = MAX_Process-1;
    }  

POUR MON APPLICATION DOIT ETRE entre 0 et 19

     Serial.println(compteur);  
     Serial.println(description[compteur]);  ======> ELEMENT D'UN TABLEAU

OK JUSQUE LA, SI JE METS EN COMMENTAIRE LES LIGNES SUIVANTE
     lcd.setCursor(0,0);=======================================\ 
     lcd.print(description[index]);============================/ N'AFFICHE RIEN ET BLOQUE LE PROCESS 
}

The sketch or fragment that you posted looks like it would not doesn't even verify.

Please post a complete sketch that compiles and misbehaves in the manner you report.

     lcd.print(description[index]);============================/ N'AFFICHE RIEN ET BLOQUE LE PROCESS 

Since I have time to guess, I would guess that index is referencing elements outside of edscription[]

a7

Here is the full code:`

/*  
 *   minuterie exposition tubes uv pour procédés photographique
*/

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


#define pinArduinoRaccordementSignalSW  2       // La pin D2 de l'Arduino recevra la ligne SW du module KY-040
#define pinArduinoRaccordementSignalCLK 3       // La pin D3 de l'Arduino recevra la ligne CLK du module KY-040
#define pinArduinoRaccordementSignalDT  4       // La pin D4 de l'Arduino recevra la ligne D

#define MAX_Process  20                        // nombre de procédés différents


int Program_index = 0 ;

 
char description [20][35] = {"Cyanotype", "Van Dijck Brown",
          "Gomme bichromatée", "Puretch trame", "Puretch image","Tok solar trame",
          "Tok solar image","Film ..... trame", "Film ...... image",
          "Plaque verte trame", "Plaque verte image","12","13","14","15","16","17","18","19","20"
};     // nom du procédé
int exposition6 [20] = {101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119};     //exposition led 625nm  en secondes
int exposition8 [20] = {21,22,3,14,125,16,37,18,109,10,21,32,43,74,65,26,77,48,29};             //exposition led 815nm  en secondes


// Variables
int etatPrecedentLigneSW;           // Cette variable nous permettra de stocker le dernier état de la ligne SW, afin de le comparer à l'actuel

int compteur = 0;                   // Cette variable nous permettra de compter combien de crans ont été parcourus, sur l'encodeur
                                    // (sachant que nous compterons dans le sens horaire, et décompterons dans le sens antihoraire)




LiquidCrystal_I2C lcd(0x27,20,4); 

 void setup ()
 {
  lcd.init();
  lcd.backlight();

 pinMode(pinArduinoRaccordementSignalSW, INPUT);
 pinMode(pinArduinoRaccordementSignalDT, INPUT);
 pinMode(pinArduinoRaccordementSignalCLK, INPUT);
 
    Serial.begin(9600);    // test à supprimer
    Serial.println(F("=========================================================================="));  //test à supprimer
    Serial.println(F("           interruptions Arduino INT0 et INT1, avec affichage du nombre de"));  //test à supprimer
    Serial.println(F("           crans parcourus sur l'encodeur, ainsi que le sens de rotation)"));  //test à supprimer
    Serial.println(F("========================================================================="));  //test à supprimer
    Serial.println(F("========================================================================="));  //test à supprimer
    Serial.println(F("========================================================================="));  //test à supprimer
    Serial.println("");  //test à supprimer
    delay(200);

    // Mémorisation de la valeur initiale de la ligne SW, au démarrage du programme
    etatPrecedentLigneSW  = digitalRead(pinArduinoRaccordementSignalSW);

    // Affichage de la valeur initiale du compteur, sur le moniteur série
    Serial.print(F("Valeur initiale du compteur = "));  //test à supprimer
    Serial.println(compteur);  //test à supprimer


    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalCLK), changementDetecteSurLigneCLK, FALLING); // FALLING => détecte tout front descendant
    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalSW), changementDetecteSurLigneSW, CHANGE);    // CHANGE => détecte tout changement d'état

    lcd.setCursor(0, 3);
    lcd.print("test lcd");
  

}

void loop() {
  // put your main code here, to run repeatedly:


//  POUR PREMIERS TESTS
 
}

void eff_ligne (int ligne)
{
   lcd.setCursor(0,ligne-1);
   lcd.print( "                   ");

}

void affiche_lcd(int index) {
   LiquidCrystal_I2C lcd(0x27,20,4); 
   lcd.init();
   lcd.backlight();
   eff_ligne(1);
   eff_ligne(2);
   eff_ligne(3); 
   lcd.setCursor(0,0);
   lcd.print(description[index]);
}
void changementDetecteSurLigneCLK() {

    // Lecture de la ligne DT, issue du KY-040, et arrivant sur l'arduino
    int etatActuelDeLaLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);
    int etatActuelDeLaLigneCLK = LOW;
        // Nota : ici, la ligne CLK est forcément au niveau bas (0V), du fait qu'on entre dans cette routine
        //        que sur front descendant de CLK (donc passage de 1 à 0)
        

    // On compare ensuite l'état des lignes CLK et DT
    // ----------------------------------------------
    // Nota : - si CLK est différent de DT, alors cela signifie que nous avons tourné l'encodeur dans le sens horaire
    //        - si CLK est égal à DT, alors cela signifie que nous avons tourné l'encodeur dans le sens antihoraire

    if(etatActuelDeLaLigneCLK != etatActuelDeLaLigneDT) {
        // CLK différent de DT => cela veut dire que nous tournons dans le sens horaire
        // Alors on incrémente le compteur
        compteur++;
        
        // Et on affiche ces infos sur le moniteur série
        Serial.print(F("Sens = horaire | Valeur du compteur = "));
        // Serial.println(compteur);
    }
    else {
        // CLK est identique à DT => cela veut dire que nous tournons dans le sens antihoraire
        // Alors on décrémente le compteur
        compteur--;
        
        
        // Et on affiche ces infos sur le moniteur série
        Serial.print(F("Sens = antihoraire | Valeur du compteur = "));
       
    }
    if (compteur > (MAX_Process -1))  {
            compteur = 0;
        }
    if (compteur < 0 )  {
            compteur = MAX_Process-1;
    }  
     Serial.println(compteur); 
     Serial.println(description[compteur]);
   //  affiche_lcd(compteur);

 
}


// ====================================================
// Routine d'interruption : changementDetecteSurLigneSW
// ====================================================
void changementDetecteSurLigneSW() {

    // On lit le nouvel état de la ligne SW
    int etatActuelDeLaLigneSW = digitalRead(pinArduinoRaccordementSignalSW);

    // On mémorise le nouvel état de la ligne SW, puisqu'il vient de changer (sans quoi nous ne serions pas dans cette routine d'interruption)
    etatPrecedentLigneSW = etatActuelDeLaLigneSW;

    // Puis on affiche le nouvel état de SW sur le moniteur série de l'IDE Arduino
    if(etatActuelDeLaLigneSW == LOW)
        Serial.println(F("Bouton SW appuyé"));
    else
        Serial.println(F("Bouton SW relâché"));

}
// Nota : en pratique, sans "filtre anti-rebond", vous noterez qu'il y a parfois pas mal de comptes/décomptes non souhaités, en l'état

P.S.: I tried different version moving the lcd.print in a function, but it's the same.

this version work, but if I uncomment like that:

   Serial.println(compteur); 
     Serial.println(description[compteur]);
     affiche_lcd(compteur);

(before the section testing the SW)

It lock

Part of this should be in the setup().

Yes. These lines stricken

void affiche_lcd(int index) {
///  no need   LiquidCrystal_I2C lcd(0x27,20,4); 
/// done in setup once   lcd.init();

The other problem seems to have something to do with using the LCD within the ISR maybe.

// I added a global variable
volatile signed char kludgeIndex = -1;




// ... and put these service lines in your loop

void loop() {
  if (kludgeIndex >= 0) {
    affiche_lcd(kludgeIndex);
    kludgeIndex = -1;
  }
}




//... then where you called affiche_lcd() just set the kludgeIndex so it would be handled later in the loop:


     Serial.println(compteur); 
     Serial.println(description[compteur]);
    
    kludgeIndex = compteur;

It is genearlly advised not to do too much work in the ISR. Printing was once problematic. Usually we recommend the ISR do nothing but set flags to be noticed and acted upon in the main code.

Of course its different for a rotary encoder ISR. Buty it should just increment or decrement some counter we all look at to decide what or whether do. anything.

HTH

a7

Thanks, I will try to just increment/decrement counter and active a flag

The other solution, which is unorthodox, is to enable the interrupts inside the ISR, as long as you keep it short enough :

/*
     minuterie exposition tubes uv pour procédés photographique
*/

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


#define pinArduinoRaccordementSignalSW  2       // La pin D2 de l'Arduino recevra la ligne SW du module KY-040
#define pinArduinoRaccordementSignalCLK 3       // La pin D3 de l'Arduino recevra la ligne CLK du module KY-040
#define pinArduinoRaccordementSignalDT  4       // La pin D4 de l'Arduino recevra la ligne D

#define MAX_Process  20                        // nombre de procédés différents


int Program_index = 0 ;


char description [20][35] = {"Cyanotype", "Van Dijck Brown",
                             "Gomme bichromatée", "Puretch trame", "Puretch image", "Tok solar trame",
                             "Tok solar image", "Film ..... trame", "Film ...... image",
                             "Plaque verte trame", "Plaque verte image", "12", "13", "14", "15", "16", "17", "18", "19", "20"
                            };     // nom du procédé
int exposition6 [20] = {101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119}; //exposition led 625nm  en secondes
int exposition8 [20] = {21, 22, 3, 14, 125, 16, 37, 18, 109, 10, 21, 32, 43, 74, 65, 26, 77, 48, 29}; //exposition led 815nm  en secondes


// Variables
int etatPrecedentLigneSW;           // Cette variable nous permettra de stocker le dernier état de la ligne SW, afin de le comparer à l'actuel

int compteur = 0;                   // Cette variable nous permettra de compter combien de crans ont été parcourus, sur l'encodeur
// (sachant que nous compterons dans le sens horaire, et décompterons dans le sens antihoraire)




LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup ()
{
  lcd.init();
  lcd.backlight();

  pinMode(pinArduinoRaccordementSignalSW, INPUT);
  pinMode(pinArduinoRaccordementSignalDT, INPUT);
  pinMode(pinArduinoRaccordementSignalCLK, INPUT);

  Serial.begin(115200);    // test à supprimer
  Serial.println(F("=========================================================================="));  //test à supprimer
  Serial.println(F("           interruptions Arduino INT0 et INT1, avec affichage du nombre de"));  //test à supprimer
  Serial.println(F("           crans parcourus sur l'encodeur, ainsi que le sens de rotation)"));  //test à supprimer
  Serial.println(F("========================================================================="));  //test à supprimer
  Serial.println(F("========================================================================="));  //test à supprimer
  Serial.println(F("========================================================================="));  //test à supprimer
  Serial.println("");  //test à supprimer
  delay(200);

  // Mémorisation de la valeur initiale de la ligne SW, au démarrage du programme
  etatPrecedentLigneSW  = digitalRead(pinArduinoRaccordementSignalSW);

  // Affichage de la valeur initiale du compteur, sur le moniteur série
  Serial.print(F("Valeur initiale du compteur = "));  //test à supprimer
  Serial.println(compteur);  //test à supprimer


  attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalCLK), changementDetecteSurLigneCLK, FALLING); // FALLING => détecte tout front descendant
  attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalSW), changementDetecteSurLigneSW, CHANGE);    // CHANGE => détecte tout changement d'état

  lcd.setCursor(0, 3);
  lcd.print("test lcd");


}

void loop() {
  // put your main code here, to run repeatedly:


  //  POUR PREMIERS TESTS

}

void eff_ligne (int ligne)
{
  lcd.setCursor(0, ligne - 1);
  lcd.print( "                   ");

}

void affiche_lcd(int index) {
  eff_ligne(1);
  eff_ligne(2);
  eff_ligne(3);
  lcd.setCursor(0, 0);
  lcd.print(description[index]);
}


void changementDetecteSurLigneCLK() {

  // Lecture de la ligne DT, issue du KY-040, et arrivant sur l'arduino
  int etatActuelDeLaLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);
  int etatActuelDeLaLigneCLK = LOW;
  // Nota : ici, la ligne CLK est forcément au niveau bas (0V), du fait qu'on entre dans cette routine
  //        que sur front descendant de CLK (donc passage de 1 à 0)


  // On compare ensuite l'état des lignes CLK et DT
  // ----------------------------------------------
  // Nota : - si CLK est différent de DT, alors cela signifie que nous avons tourné l'encodeur dans le sens horaire
  //        - si CLK est égal à DT, alors cela signifie que nous avons tourné l'encodeur dans le sens antihoraire

  if (etatActuelDeLaLigneCLK != etatActuelDeLaLigneDT) {
    // CLK différent de DT => cela veut dire que nous tournons dans le sens horaire
    // Alors on incrémente le compteur
    compteur++;
    if (compteur > (MAX_Process - 1))  {
      compteur = 0;
    }

    // Et on affiche ces infos sur le moniteur série
    interrupts();
    Serial.print(F("Sens = horaire | Valeur du compteur = "));
  }
  else {
    // CLK est identique à DT => cela veut dire que nous tournons dans le sens antihoraire
    // Alors on décrémente le compteur
    compteur--;
    if (compteur < 0 )  {
      compteur = MAX_Process - 1;
    }

    // Et on affiche ces infos sur le moniteur série
    interrupts();
    Serial.print(F("Sens = antihoraire | Valeur du compteur = "));

  }
  Serial.println(compteur);
  Serial.println(description[compteur]);
  affiche_lcd(compteur);
}


// ====================================================
// Routine d'interruption : changementDetecteSurLigneSW
// ====================================================
void changementDetecteSurLigneSW() {

  // On lit le nouvel état de la ligne SW
  int etatActuelDeLaLigneSW = digitalRead(pinArduinoRaccordementSignalSW);

  // On mémorise le nouvel état de la ligne SW, puisqu'il vient de changer (sans quoi nous ne serions pas dans cette routine d'interruption)
  etatPrecedentLigneSW = etatActuelDeLaLigneSW;

  // Puis on affiche le nouvel état de SW sur le moniteur série de l'IDE Arduino
  interrupts();
  if (etatActuelDeLaLigneSW == LOW)
    Serial.println(F("Bouton SW appuyé"));
  else
    Serial.println(F("Bouton SW relâché"));

}
// Nota : en pratique, sans "filtre anti-rebond", vous noterez qu'il y a parfois pas mal de comptes/décomptes non souhaités, en l'état

Here is the result in a simulator :

I would use a good library to read your encoder, like this one:-
Rotary Library
from Encoder Library, for Measuring Quadarature Encoded Position or Rotation Signals

It comes with examples.

Some other libraries written do not use the correct state machine to read the encoder correctly.

Yes! The problem is exactly that interrupts are off in the ISR. I realized that when I was brushing my cat's teeth.

The solution of turning them on briefly is unorthodox, and although it appears to work is def not the recommended manner to deal with a this kind of thing.

The "set flags in the interrupt" and "do stuff because flags in the main loop" is a good pattern to follow for ISRs.

Some ISRs are naturally where the work should be done. In general, printing and by extension publishing to an LCD is prolly stuff anyone should figure out a better way to handle. Again in general those ISRs are coded to take a minimal amount of time.

Note that using interrupts and flags may mean you need to keep your loop running free. I had a blank loop to play with, my service lines were being execute thousands of ties a second usually doing nothing.

a7

There are ways to make this work, but often it is a recipe for disaster.
Another big issue with re-enabling interrupts in an ISR is reentrancy.
Normally and ISR is not re-entered, but if you re-enable interrupts in the ISR there is the possibility that the same interrupt occurs and the ISR is re-entered.
This can cause all kinds of issues that can be difficult to resolve.

About the LCD, no you can't ever print to the LCD in an ISR since it uses the WIRE library and the WIRE library needs interrupts to function. If interrupts are disabled when you try to send something over the i2c interface, the code in WIRE will hang.

I also would suggest not printing out the serial port from the ISR either.
While it "works" it can cause interrupts to be masked for along period.
This is because HardwareSerial detects when interrupts are blocked and starts polling the h/w vs buffering it for interrupts to drain.
Depending on the baud rate and the length of the message this can keep the processor in the ISR for quite some time, which means that interrupts are masked for quite some time.

--- bill

Thanks to all.

I just set a flag and increment/decrement counter.

Now, it works just fine

After all modifications, I see a problem (not noticed before ). I have to push 2 times on the switch to start.
Here is the complete sketch:

/*  
 *   minuterie exposition tubes uv pour procédés photographique
*/

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


#define pinArduinoRaccordementSignalSW  2       // La pin D2 de l'Arduino recevra la ligne SW du module KY-040
#define pinArduinoRaccordementSignalCLK 3       // La pin D3 de l'Arduino recevra la ligne CLK du module KY-040
#define pinArduinoRaccordementSignalDT  4       // La pin D4 de l'Arduino recevra la ligne D
#define uvc61 5      // relais pour les leds UV 650 nm
#define uvc62 6
#define uvc63 7
#define uvc64 8
#define uvc81 9     // relais pour les leds UV 810 nm
#define uvc82 10
#define uvc83 11
#define uvc84 12


#define MAX_Process  20                        // nombre de procédés différents

char description [20][35] = {
          "Cyanotype", "Van Dijck Brown", "Gomme bichromatee", "Puretch trame",
          "Puretch image","Tok solar trame","Tok solar image", "Film ..... trame",
          "Film ...... image", "Plaque verte trame", "Plaque verte image", "essai_6  5 sec",
          "essai_6 10 sec","essai_6 20 sec","essai_6 40 sec", "essai_6 80 sec",
          "essai_8  5 sec","essai_8 10 sec","essai_8 20 sec","essai_8 40 sec"
          };     // nom du procédé
int exposition6 [20] = {
           101,102,103,104,
           105,106,107,108,
           109,110,15,5,
           10,20,40,80,
           0,0,0,0
           };     //exposition led 625nm  en secondes
int exposition8 [20] = {
            21,22,3,14,
            125,16,37,18,
            109,10,8,0,
            0,0,0,0,
            5,10,20,40
            };             //exposition led 815nm  en secondes

int Program_index = 0 ;
bool changeindex = 0;
int Expose=0;
int etatPrecedentLigneSW;           // Cette variable nous permettra de stocker le dernier état de la ligne SW, afin de le comparer à l'actuel
int compteur = 0;                   // Cette variable nous permettra de compter combien de crans ont été parcourus, sur l'encodeur
int Exp6 = 0;      //temps exposition 650 nm
int Exp8 = 0;      //temps exposition 810 nm
bool Stop6 = 0;    //fin exposition 650 nm
bool Stop8 = 0;    //fin exposition 810 nm                                     // (sachant que nous compterons dans le sens horaire, et décompterons dans le sens antihoraire)

LiquidCrystal_I2C lcd(0x27,20,4); 

 void setup ()
 {
  lcd.init();
  lcd.backlight();

  pinMode(pinArduinoRaccordementSignalSW, INPUT);
  pinMode(pinArduinoRaccordementSignalDT, INPUT);
  pinMode(pinArduinoRaccordementSignalCLK, INPUT);
  pinMode(uvc61, OUTPUT);
  pinMode(uvc62, OUTPUT);
  pinMode(uvc63, OUTPUT);
  pinMode(uvc64, OUTPUT);
  pinMode(uvc81, OUTPUT);
  pinMode(uvc82, OUTPUT);
  pinMode(uvc83, OUTPUT);
  pinMode(uvc84, OUTPUT);
  digitalWrite(uvc61,LOW);
  digitalWrite(uvc62,LOW);
  digitalWrite(uvc63,LOW);
  digitalWrite(uvc64,LOW);
  digitalWrite(uvc81,LOW);
  digitalWrite(uvc82,LOW);
  digitalWrite(uvc83,LOW);
  digitalWrite(uvc84,LOW);
  delay (250);
  Serial.begin(9600); 
  // Mémorisation de la valeur initiale de la ligne SW, au démarrage du programme
  etatPrecedentLigneSW  = digitalRead(pinArduinoRaccordementSignalSW);
    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalCLK), changementDetecteSurLigneCLK, FALLING); // FALLING => détecte tout front descendant
    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalSW), changementDetecteSurLigneSW, CHANGE);    // CHANGE => détecte tout changement d'état

    lcd.setCursor(0,0);
    lcd.print(" BOX EXPOSITION UV");
    affiche_lcd(0);
}

void loop() 
{
if (changeindex == 1)
  { 
  affiche_lcd(compteur);
  changeindex = 0;
  }
if (Expose == 1)
   {
   if (Exp6 > 0)
      {
      Allume650();
      Exp6 --;
      }  
    else
      Eteint650();
    if (Exp8 > 0)
      {
      Allume810();
      Exp8 --;
      }
    else
      Eteint810();
  
    Serial.print(F("Exp6 "));
    Serial.print(Exp6);
    Serial.print(F("   Exp8 "));
    Serial.println(Exp8);
    delay(100);
    }
  if (Exp6 == 0 && Exp8 == 0)
    {
      Eteint650();
      Eteint810();
      Expose=0;
  
    } 
 }

void eff_ligne (int ligne)
{
   lcd.setCursor(0,ligne-1);
   lcd.print( "                   ");

}

void affiche_lcd(int index) {
   eff_ligne(2);
   eff_ligne(3);
   eff_ligne(4); 
   lcd.setCursor(0,1);
   lcd.print(description[index]);
   lcd.setCursor (0,2);
   lcd.print(exposition6[index]);
   lcd.setCursor (0,3);
   lcd.print(exposition8[index]);  
}
void changementDetecteSurLigneCLK() {
    // Lecture de la ligne DT, issue du KY-040, et arrivant sur l'arduino
    int etatActuelDeLaLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);
    int etatActuelDeLaLigneCLK = LOW;
        // Nota : ici, la ligne CLK est forcément au niveau bas (0V), du fait qu'on entre dans cette routine
        //        que sur front descendant de CLK (donc passage de 1 à 0)
     
    // On compare ensuite l'état des lignes CLK et DT
    // ----------------------------------------------
    // Nota : - si CLK est différent de DT, alors cela signifie que nous avons tourné l'encodeur dans le sens horaire
    //        - si CLK est égal à DT, alors cela signifie que nous avons tourné l'encodeur dans le sens antihoraire

    if(etatActuelDeLaLigneCLK != etatActuelDeLaLigneDT) {
        // CLK différent de DT => cela veut dire que nous tournons dans le sens horaire
        // Alors on incrémente le compteur
        compteur++;
         }
    else {
        // CLK est identique à DT => cela veut dire que nous tournons dans le sens antihoraire
        // Alors on décrémente le compteur
        compteur--;
             
            }
    if (compteur > (MAX_Process -1))  {
            compteur = 0;
        }
    if (compteur < 0 )  {
            compteur = MAX_Process-1;
    }  
    changeindex=true;

}


// ====================================================
// Routine d'interruption : changementDetecteSurLigneSW
// ====================================================
void changementDetecteSurLigneSW() {

    // On lit le nouvel état de la ligne SW
    int etatActuelDeLaLigneSW = digitalRead(pinArduinoRaccordementSignalSW);

    // On mémorise le nouvel état de la ligne SW, puisqu'il vient de changer (sans quoi nous ne serions pas dans cette routine d'interruption)
    etatPrecedentLigneSW = etatActuelDeLaLigneSW;

    // Puis on affiche le nouvel état de SW sur le moniteur série de l'IDE Arduino
    if(etatActuelDeLaLigneSW == LOW)
       Expose = 1;
       Exp6 = exposition6[compteur];
       Exp8 = exposition8[compteur];

}
// Nota : en pratique, sans "filtre anti-rebond", vous noterez qu'il y a parfois pas mal de comptes/décomptes non souhaités, en l'état
void Allume650()
{
  digitalWrite(uvc61,HIGH);      
  digitalWrite(uvc62,HIGH);
  digitalWrite(uvc63,HIGH);
  digitalWrite(uvc64,HIGH);
}

void Eteint650()
{
  
  digitalWrite(uvc61,LOW);
  digitalWrite(uvc62,LOW);
  digitalWrite(uvc63,LOW);
  digitalWrite(uvc64,LOW);
}
void Allume810()
{
  digitalWrite(uvc81,HIGH);
  digitalWrite(uvc82,HIGH);
  digitalWrite(uvc83,HIGH);
  digitalWrite(uvc84,HIGH);
}

void Eteint810()
{
  
  digitalWrite(uvc81,LOW);
  digitalWrite(uvc82,LOW);
  digitalWrite(uvc83,LOW);
  digitalWrite(uvc84,LOW);
}

delay in loop must be 1000 (1sec), but for test, I lower it to 100, so I don't have to wait to much

Why do you trigger an interrupt on CHANGE for pinArduinoRaccordementSignalSW ? FALLING (if pulled-up) seems more appropriate.
etatPrecedentLigneSW is not used...

The indentation makes you think all three instructions will be executed only when etatActuelDeLaLigneSW == LOW but if you ask your IDE to format the code (ctrl+T with Arduino IDE 2), you'll notice the mistake even though the ISR works well...

But the culprit could be pinArduinoRaccordementSignalSW which is not pulled-up (internally at least).
Here are the modifications I made:

/*
     minuterie exposition tubes uv pour procédés photographique
*/

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


#define pinArduinoRaccordementSignalSW  2       // La pin D2 de l'Arduino recevra la ligne SW du module KY-040
#define pinArduinoRaccordementSignalCLK 3       // La pin D3 de l'Arduino recevra la ligne CLK du module KY-040
#define pinArduinoRaccordementSignalDT  4       // La pin D4 de l'Arduino recevra la ligne D
#define uvc61 5      // relais pour les leds UV 650 nm
#define uvc62 6
#define uvc63 7
#define uvc64 8
#define uvc81 9     // relais pour les leds UV 810 nm
#define uvc82 10
#define uvc83 11
#define uvc84 12


#define MAX_Process  20                        // nombre de procédés différents

char description [20][35] = {
  "Cyanotype", "Van Dijck Brown", "Gomme bichromatee", "Puretch trame",
  "Puretch image", "Tok solar trame", "Tok solar image", "Film ..... trame",
  "Film ...... image", "Plaque verte trame", "Plaque verte image", "essai_6  5 sec",
  "essai_6 10 sec", "essai_6 20 sec", "essai_6 40 sec", "essai_6 80 sec",
  "essai_8  5 sec", "essai_8 10 sec", "essai_8 20 sec", "essai_8 40 sec"
};     // nom du procédé
int exposition6 [20] = {
  101, 102, 103, 104,
  105, 106, 107, 108,
  109, 110, 15, 5,
  10, 20, 40, 80,
  0, 0, 0, 0
};     //exposition led 625nm  en secondes
int exposition8 [20] = {
  21, 22, 3, 14,
  125, 16, 37, 18,
  109, 10, 8, 0,
  0, 0, 0, 0,
  5, 10, 20, 40
};             //exposition led 815nm  en secondes

int Program_index = 0 ;
bool changeindex = 0;
int Expose = 0;
// int etatPrecedentLigneSW;           // Cette variable nous permettra de stocker le dernier état de la ligne SW, afin de le comparer à l'actuel
int typeDexposition = 0;                   // Cette variable nous permettra de compter combien de crans ont été parcourus, sur l'encodeur
int Exp6 = 0;      //temps exposition 650 nm
int Exp8 = 0;      //temps exposition 810 nm
bool Stop6 = 0;    //fin exposition 650 nm
bool Stop8 = 0;    //fin exposition 810 nm                                     // (sachant que nous compterons dans le sens horaire, et décompterons dans le sens antihoraire)

LiquidCrystal_I2C lcd(0x27, 20, 4);

void setup ()
{
  Serial.begin(115200);
  lcd.init();
  lcd.backlight();

  pinMode(pinArduinoRaccordementSignalSW, INPUT_PULLUP);
  pinMode(pinArduinoRaccordementSignalDT, INPUT);
  pinMode(pinArduinoRaccordementSignalCLK, INPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(uvc61, OUTPUT);
  pinMode(uvc62, OUTPUT);
  pinMode(uvc63, OUTPUT);
  pinMode(uvc64, OUTPUT);
  pinMode(uvc81, OUTPUT);
  pinMode(uvc82, OUTPUT);
  pinMode(uvc83, OUTPUT);
  pinMode(uvc84, OUTPUT);
  digitalWrite(uvc61, LOW);
  digitalWrite(uvc62, LOW);
  digitalWrite(uvc63, LOW);
  digitalWrite(uvc64, LOW);
  digitalWrite(uvc81, LOW);
  digitalWrite(uvc82, LOW);
  digitalWrite(uvc83, LOW);
  digitalWrite(uvc84, LOW);
  delay (250);
  // Mémorisation de la valeur initiale de la ligne SW, au démarrage du programme
  // etatPrecedentLigneSW  = digitalRead(pinArduinoRaccordementSignalSW);
  attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalCLK), changementDetecteSurLigneCLK, FALLING); // FALLING => détecte tout front descendant
  attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalSW), changementDetecteSurLigneSW, FALLING);    // CHANGE => détecte tout changement d'état

  lcd.setCursor(0, 0);
  lcd.print(" BOX EXPOSITION UV");
  affiche_lcd(0);
}

void loop()
{
  if (changeindex == 1)
  {
    affiche_lcd(typeDexposition);
    changeindex = 0;
  }

  if (Expose == 1)
  {
    if (Exp6 > 0)
    {
      Allume650();
      Exp6 --;
    }
    else
      Eteint650();
    if (Exp8 > 0)
    {
      Allume810();
      Exp8 --;
    }
    else
      Eteint810();

    Serial.print(F("Exp6 "));
    Serial.print(Exp6);
    Serial.print(F("   Exp8 "));
    Serial.println(Exp8);
    delay(100);
  }

  if (Exp6 == 0 && Exp8 == 0)
  {
    // Eteint650();
    // Eteint810();
    Expose = 0;
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void eff_ligne (int ligne)
{
  lcd.setCursor(0, ligne - 1);
  lcd.print( "                   ");
}

void affiche_lcd(int index) {
  eff_ligne(2);
  eff_ligne(3);
  eff_ligne(4);
  lcd.setCursor(0, 1);
  lcd.print(description[index]);
  lcd.setCursor (0, 2);
  lcd.print(exposition6[index]);
  lcd.setCursor (0, 3);
  lcd.print(exposition8[index]);
}

void changementDetecteSurLigneCLK() {
  // Lecture de la ligne DT, issue du KY-040, et arrivant sur l'arduino
  int etatActuelDeLaLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);
  int etatActuelDeLaLigneCLK = LOW;
  // Nota : ici, la ligne CLK est forcément au niveau bas (0V), du fait qu'on entre dans cette routine
  //        que sur front descendant de CLK (donc passage de 1 à 0)

  // On compare ensuite l'état des lignes CLK et DT
  // ----------------------------------------------
  // Nota : - si CLK est différent de DT, alors cela signifie que nous avons tourné l'encodeur dans le sens horaire
  //        - si CLK est égal à DT, alors cela signifie que nous avons tourné l'encodeur dans le sens antihoraire

  if (etatActuelDeLaLigneCLK != etatActuelDeLaLigneDT) {
    // CLK différent de DT => cela veut dire que nous tournons dans le sens horaire
    // Alors on incrémente le typeDexposition
    typeDexposition++;
    if (typeDexposition > (MAX_Process - 1))  {
      typeDexposition = 0;
    }
  }
  else {
    // CLK est identique à DT => cela veut dire que nous tournons dans le sens antihoraire
    // Alors on décrémente le typeDexposition
    typeDexposition--;
    if (typeDexposition < 0 )  {
      typeDexposition = MAX_Process - 1;
    }

  }
  changeindex = true;

}


// ====================================================
// Routine d'interruption : changementDetecteSurLigneSW
// ====================================================
void changementDetecteSurLigneSW() {

  // On lit le nouvel état de la ligne SW
  // int etatActuelDeLaLigneSW = digitalRead(pinArduinoRaccordementSignalSW);

  // On mémorise le nouvel état de la ligne SW, puisqu'il vient de changer (sans quoi nous ne serions pas dans cette routine d'interruption)
  // etatPrecedentLigneSW = etatActuelDeLaLigneSW;

  // Puis on affiche le nouvel état de SW sur le moniteur série de l'IDE Arduino
  // if (etatActuelDeLaLigneSW == LOW)
  Expose = 1;
  Exp6 = exposition6[typeDexposition];
  Exp8 = exposition8[typeDexposition];
  digitalWrite(LED_BUILTIN, HIGH);
}

// Nota : en pratique, sans "filtre anti-rebond", vous noterez qu'il y a parfois pas mal de comptes/décomptes non souhaités, en l'état
void Allume650()
{
  digitalWrite(uvc61, HIGH);
  digitalWrite(uvc62, HIGH);
  digitalWrite(uvc63, HIGH);
  digitalWrite(uvc64, HIGH);
}

void Eteint650()
{
  digitalWrite(uvc61, LOW);
  digitalWrite(uvc62, LOW);
  digitalWrite(uvc63, LOW);
  digitalWrite(uvc64, LOW);
}
void Allume810()
{
  digitalWrite(uvc81, HIGH);
  digitalWrite(uvc82, HIGH);
  digitalWrite(uvc83, HIGH);
  digitalWrite(uvc84, HIGH);
}

void Eteint810()
{
  digitalWrite(uvc81, LOW);
  digitalWrite(uvc82, LOW);
  digitalWrite(uvc83, LOW);
  digitalWrite(uvc84, LOW);
}

and the result in a simulator:

Thanks,
the encoder has pullup resistors integrated

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