"static uint16_t" blocked after lcd.[] commands [SOLVED]

Dear Forum,

I've never encountered a problem like this before. Therefore I'm here to seek your wisdom. Please don't be too harsh on me, it's my first post and I'll give it my best, I promise. :slight_smile:

What do I want to do?
I want to use a rotary encoder, the KY040 to be precise (with all Pullup Resistors in place), to change a counter. This counter will then be used to see in which direction the encoder was turned and after knowing that this information will lead to a number added to float m_material (so you can change how much material is needed).
m_material is then divided by my calculated mass flow d_m_gear (float) to give me the time needed to transport the material.
This time and the mass of the material (m_material) shall be shown on a LCD Display with an I2C adapter soldered on it.
But right now, only the counter and the LCD are important. (rest is already done, tested and working!)

Sidenote: The Output of the Rotary Encoder is extremely bouncy. Therefore I'm using a digital filter I found on another forum to debounce it. Link is in the code.

How do I want to do it?
This is a part of my code. I have checked the rest and found the mistake, but not the solution. I have cut all unnecessary stuff away and only left the lines that create the problem. More to the problem under the code:

#include <Wire.h>                       //insert Wire library for I2C
#include <LiquidCrystal_I2C.h>          //insert LiquidCrystal_I2C library
LiquidCrystal_I2C lcd(0x27, 16, 2);     //define LCD type (it's a I2C LCD btw) and address

const int DT = 5;                       //Encoder Data PIN
const int CLK = 6;                      //Encoder Clock PIN

float d_m_gear = 0.48;                  //mass flow of the gear
float m_material = 3.0;                 //mass to be transported (later adjustable by turning the rotary encoder)
float t_ms = 0;                         //time to transport all material from A to B

static uint16_t state = 0;              //static unsigned portable 16 Bit Integer
                                        //used for Digital Debouncing of the Rotary Encoders Feedback
static uint16_t counter = 0;            //static unsigned portable 16 Bit Integer for Counting


////////////////////////////////////////
void setup() 
{
  //make LCD ready
  lcd.init();                               //start LCD 
  lcd.backlight();                          //LCD backlight on

  //PINs of the KY040 (left SW PIN out)
  pinMode(CLK,    INPUT);                   //read PIN Clock
  pinMode(DT,     INPUT);                   //read PIN Data

  //feedback for monitor
  Serial.begin(9600);                       //start Serial
  Serial.println("Rotary Encoder KY-040");  //print Encoder Type
  Serial.println(counter);                  //print start value of counter
}


////////////////////////////////////////
void loop() 
{
//// filter for the rotary encoder
  delayMicroseconds(100);              //simulate action
  
  state=(state<<1) | digitalRead(CLK) | 0xe000;

  if (state==0xf000)  //filter is explained here https://www.best-microcontroller-projects.com/rotary-encoder.html
   {
     state=0x0000;
     if(digitalRead(DT))
       counter++;                      //static uint16_t counter +1
     else
       counter--;                      //static uint16_t counter -1
     Serial.println(counter);          //print new counter value into the monitor
   }


  t_ms = m_material/d_m_gear;          //calculate time in ms


// if the following sequence till the end is uncommented the counter does not work anymore
// no matter how much you turn or what you do if uncommented, no new value will be printed 
// into the monitor

////// standard display
//  lcd.setCursor(0,0);                //LCD textposition
//  lcd.print("some string text");     //LCD text
//    
//  lcd.setCursor(2,1);                //LCD textposition
//  lcd.print(m_material);             //LCD text
//  lcd.print("g");                    //LCD text
//                                        
//  lcd.setCursor(9,1);                //LCD textposition
//  lcd.print(t_ms);                   //LCD text
//  lcd.print("s");                    //LCD text

}

What and where is the problem?
If you load the code like this and have the KY040 and the LCD I2C display connected at the right PINs (schematics of the complete project are attached) the serial monitor will work like a charm and show the changing counter.
Buuuuut if you uncomment the area with the lcd.print and lcd.setCursor commands the filter for the KY040 and the counter won't work anymore.

I have no idea how. This is why I'm here. Maybe it has something to do with the static portable 16 bit unsigned integer, but I just don't know. What I know is that the problem is not generated by the "delayMicroseconds();" line. Already tested uncommenting it. :slight_smile:

Until now I was always able to debug by myself, but this time I'm just not smart enough. I'm sorry for taking your time and hope you may be able to help me.
Thank you all very much.

Those LCD operations take a lot of time and by the time you make 12 iterations (which is how the filter works) you have probably totally missed the clock edge.

That sounds like a logic explanation.

Any idea how I could solve it? Maybe put them in seperate and yet connected loops somehow?
Unfortunately I can not put the lcd.[] area in a bool-one-time loop because I need the two floats to "refresh"

Maybe I could put the lcd stuff in a Timer Loop, so the Display will only refresh every 500ms. That would be enough time for the clock edge don't you think so?

longcircuit91:
That sounds like a logic explanation.

Any idea how I could solve it? Maybe put them in seperate and yet connected loops somehow?
Unfortunately I can not put the lcd.[] area in a bool-one-time loop because I need the two floats to "refresh"

Maybe I could put the lcd stuff in a Timer Loop, so the Display will only refresh every 500ms. That would be enough time for the clock edge don't you think so?

Yes but then you might miss edges every half second when someone is actually turning it. You could try it.

You may have to make the encoder interrupt-driven but I don't know much about rotary encoders except the switches bounce. Maybe you can find some code that handles the bounce with interrupts.

By far, the best technique for handling rotary encoders that I've ever seen is the State Table Approach described here: Buxtronix: Rotary encoders, done properly.

As part of its operation, this approach automatically handles switch contact bounce without the need for "filters", delays, etc.

As @ToddL1962 mentioned, interrupts can be used to get around the long, blocking code of LCD updates.

Here's an interrupt-driven implementation of the State Table Approach that I developed: GitHub - gfvalvo/NewEncoder: Rotary Encoder Library. Feel free to use it or just study the technique and implement it in your own way.

I have seen the buxtronix implementation but didn't quite understand it at the time.

But with your example I will give it another try and test when I come home after work. (it's about 9AM in Germany right now :slight_smile: )

I don't know all the rules of the forum, like I said it is my first post, but is it ok to leave the subject open until I know that I have found a working solution?

longcircuit91:
I don't know all the rules of the forum, like I said it is my first post, but is it ok to leave the subject open until I know that I have found a working solution?

Yes please do. Some might be interested when you find a working solution.

Working solution was found!
Thanks to the library from gfvalvo I was able to understand the library from Buxtronix. (wasn't quite able to completely understand gfvalvo's library tho :cold_sweat: )

After understanding it, I started implementing and it seems to be working just fine!

You can find the finished working code at the bottom of this message, I tried to explain it well for future readers with a similar problem.

I want to thank all the helpers for their time and finally making it possible to solve my problem!

#include <Rotary.h>                     //insert Rotary library
                                        //http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
Rotary rotary = Rotary(2, 3);           //define Rotary Encoder connected at interrupt PINs (UNO)

#include <Wire.h>                       //insert Wire library for I2C
#include <LiquidCrystal_I2C.h>          //insert LiquidCrystal_I2C library
LiquidCrystal_I2C lcd(0x27, 16, 2);     //define LCD type (it's a I2C LCD btw) and address

const int DT = 2;                       //Encoder Data PIN
const int CLK = 3;                      //Encoder Clock PIN

float d_m_gear = 0.48;                  //mass flow of the gear
float m_material = 3.0;                 //mass to be transported (later adjustable by turning the rotary encoder)
float t_ms = 0;                         //time to transport all material from A to B

int counter = 0;                        //set a counter at value 0


////////////////////////////////////////
void setup() 
{
  //make LCD ready
  lcd.init();                               //start LCD 
  lcd.backlight();                          //LCD backlight on

  //PINs of the KY040 (left SW PIN out)
  //not needed, are included in Rotary library
  pinMode(CLK,    INPUT);                   //read PIN Clock
  pinMode(DT,     INPUT);                   //read PIN Data

  //feedback for monitor
  Serial.begin(57600);                      //start Serial Build Rate
  Serial.println("Rotary Encoder KY-040");  //print Encoder Type
  Serial.println(counter);                  //print start value of counter

  attachInterrupt(0, rotate, CHANGE);       //activate interrupt at interrupt PIN 0 (UNO PIN 2)
  attachInterrupt(1, rotate, CHANGE);       //activate interrupt at interrupt PIN 1 (UNO PIN 3)
}


////////////////////////////////////////
void loop() 
{
  t_ms = m_material/d_m_gear;               //calculate time in ms

//// standard display
  lcd.setCursor(0,0);                       //LCD textposition
  lcd.print("some string text");            //LCD text
    
  lcd.setCursor(2,1);                       //LCD textposition
  lcd.print(m_material);                    //LCD text
  lcd.print("g");                           //LCD text
                                        
  lcd.setCursor(9,1);                       //LCD textposition
  lcd.print(t_ms);                          //LCD text
  lcd.print("s");                           //LCD text
}

////this function will start if encoder clicks (state changes)
void rotate()
{
  unsigned char result = rotary.process();
  if (result == DIR_CW)                     //check for state change forward
  {
    counter++;                              //counter integer +1
    Serial.println(counter);                //print counter into monitor to check function
  }
  else if (result == DIR_CCW)               //check for state change backward
  {
    counter--;                              //counter integer -1
    Serial.println(counter);                //print counter into monitor to check function
  }
}

I don't know how it works, do I have to close the subject?

longcircuit91:
I don't know how it works, do I have to close the subject?

Just change the title to include the text [SOLVED].

I just want to point out you should never print from an interrupt. Other interrupts are disabled during an interrupt (usually) and serial prints take quite a bit of time which can (again) mess up your timing.