Problem with while loop

Hi!

I have a problem with my code, specifically in the void loop (I posted the whole code at the end):

void loop() { 
  //Serial.println(unitState);
  if (i2c){
    display.print(i2cDecoder[buf[0]]);
    display.display();
    Serial.println(i2cDecoder[buf[0]]);
    i2c=false;
  }

  //Mostar en pantalla
  if (digitalRead(SW)==!lastStateSW){
    keyValueState= true;
    delay(botonPulse);
    Serial.println(keyValueState);
    while (keyValueState)
    {
      Serial.println("Enter while");
      if (digitalRead(SW)==!lastStateSW)
        {
        keyValueState= false;
        delay(botonPulse);
      }
      display.clearDisplay();
      display.setCursor(0, 35);
      display.print("Key Value:");
      display.print(keyValue);
      display.display();
      /*Serial.print("Key Value: ");
      Serial.println(keyValue);*/
  }
  Serial.println(keyValueState);
}
}

I have an enconder, the idea is that, when I press the button, I should enter the while loop, and it whould show me a variable in an OLed Screen, wich I can change until I press the button again, wich will take me out of the loop.

This works perfect, but just once, when I press the button for a second time, I’m not entering the while loop.

To debug I put some Serial.print to get where the problem should be and I realized the keyValueState variable changes from true to false before entering the loop for no reason. This is what I get in the terminal:

1 //First button press
Enter while
Enter while
Enter while
Enter while
Enter while
Enter while
Enter while
Enter while
0 //Second button press
1 //Third button press
0 //Change keyValueState for no reason, and skip the while loop

I wrote comments of what is going on. Does anyone know why it is just working once?

The whole code is here:

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//OLed variables
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)

//I2C comunication
float keyValue=0; //Key value we are looking to modify in the feeder
bool i2c;
int i2cRecieve1;
String i2cDecoder[] = {"Neumatic Extruder" ,"Stepper Extruder", "-1" ,"1"};
#define adress 99
int buf[200];

//Encoder variables
#define CLK 2
#define DT 3
#define SW 4
#define botonPulse 250

bool keyValueState = false;
int lastStateCLK;
int lastStateSW;
int currentStateCLK;


float power(float x, int y)
{ 
  float mult=1.0;
  if (y>0)
  {
    for (int i=0 ; i < y ; i++ )
    {
      mult=mult*x;
    }
  }
  else
  {
    for (int i=0 ; i < abs(y) ; i++ )
    {
      mult=mult/x;
      Serial.println(mult);
    }
  return mult;
}

  
  return mult;
}
void getKeyValue()
{
  if (i2cDecoder[buf[0]]=="Neumatic Extruder")
  {
    keyValue=  power(10,(buf[1]*i2cDecoder[buf[2]].toInt()))+255* buf[3]+buf[4]*i2cDecoder[buf[5]].toInt();
  }
  else
  {
    keyValue = power(10,(buf[1]*i2cDecoder[buf[2]].toInt()))*(255* buf[3]+buf[4]*i2cDecoder[buf[5]].toInt());
    //keyValue = power(10,(buf[1]*i2cDecoder[buf[2]].toInt()));
    Serial.println(keyValue);
  }
}

void receiveEvent(int howMany)
{
  unsigned int pos=0;
  while (Wire.available () > 0)
  {
   byte c = Wire.read ();
   //Serial.println(c);
   if (pos < sizeof buf)
     buf [pos++] = c;
  } 
  getKeyValue();
  i2c=true;
  return;
}


void flag ()
{
  if (keyValueState)
  {
    currentStateCLK = digitalRead(CLK);
    if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){
      if (digitalRead(DT) != currentStateCLK) {
			  keyValue --;
		  }  
      else {
			  keyValue ++;
		  }
	  }
    lastStateCLK = currentStateCLK;
  /*if (digitalRead(SW)==!lastStateSW)
    {
    unitState= false;
    delay(botonPulse);
    }*/
  }
  return;
}

void setup() {
  Serial.begin(250000);
  //I2C initialization
  Wire.begin(adress); 
  Wire.onReceive(receiveEvent); // register event

  //Oled initialization
  /*if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 10);
  display.display();
  display.println("Hello, world!");
  display.display();
  delay(100);
  display.clearDisplay();
  display.display();*/

  //Encoder configuration
  pinMode(CLK,INPUT);
	pinMode(DT,INPUT);
	pinMode(SW, INPUT_PULLUP);
	lastStateCLK = digitalRead(CLK);
  lastStateSW = digitalRead(SW);
  attachInterrupt(digitalPinToInterrupt(CLK),flag,RISING);
}

void loop() { 
  //Serial.println(unitState);
  if (i2c){
    display.print(i2cDecoder[buf[0]]);
    display.display();
    Serial.println(i2cDecoder[buf[0]]);
    i2c=false;
  }

  //Mostar en pantalla
  if (digitalRead(SW)==!lastStateSW){
    keyValueState= true;
    delay(botonPulse);
    Serial.println(keyValueState);
    while (keyValueState)
    {
      Serial.println("Enter while");
      if (digitalRead(SW)==!lastStateSW)
        {
        keyValueState= false;
        delay(botonPulse);
      }
      display.clearDisplay();
      display.setCursor(0, 35);
      display.print("Key Value:");
      display.print(keyValue);
      display.display();
      /*Serial.print("Key Value: ");
      Serial.println(keyValue);*/
  }
  Serial.println(keyValueState);
}
}

lastStateSW is never updated in loop()

you never update variable lastStateSW

so it might be that you not even enter the if-condition.

as a general hint: add more debug-output to get a more detailed picture of what is going on

  //Mostar en pantalla
  if (digitalRead(SW)==!lastStateSW){
    keyValueState= true;
    delay(botonPulse);
    Serial.println(keyValueState);
    while (keyValueState)
    {
      Serial.println("Enter while");
      if (digitalRead(SW)==!lastStateSW)
        {

best regards Stefan

I make it work, by adding by mistake the command Serial.println(digitalRead(true));

I wanted to Serial.println(true); and write it wrong, now that lines makes everything work and I can't modify it.

this is how the void loop looks:

void loop() { 
  //Serial.println(unitState);
  if (i2c){
    display.print(i2cDecoder[buf[0]]);
    display.display();
    Serial.println(i2cDecoder[buf[0]]);
    i2c=false;
  }

  //Mostar en pantalla
  Serial.println(digitalRead(true)); //This line makes everythin work, I don't even now what it does, I wroted by mistake

  if (digitalRead(SW)==!stateSW){
    keyValueState= true;
    delay(botonPulse);
    Serial.println("Enter the first if");
    /*Serial.print("keyValueState: ");
    Serial.println(keyValueState);*/
    while (keyValueState)
    {
      Serial.println("Enter while");
      if (digitalRead(SW)==!stateSW)
        {
        keyValueState= false;
        delay(botonPulse);
        Serial.println("Enter the second if");
      }
      display.clearDisplay();
      display.setCursor(0, 35);
      display.print("Key Value:");
      display.print(keyValue);
      display.display();
      /*Serial.print("Key Value: ");
      Serial.println(keyValueState);*/
  }
  /*Serial.print("keyValueState: ");
  Serial.println(keyValueState);*/
  Serial.println("out of the first if");
}
}

Does someone now why does this happen?

PD: I didn't need to update the lastStateSW, that name came from old code, I change it to stateSW.

Thx!

I make it work, by adding by mistake the command Serial.println(digitalRead(true));

Why are you printing the value of the state of pin 1, which is what that statement does

I wrote that by mistake, I was looking to print the value of state in the pin where the button is attached.
But that line makes everything work, and if I delete it, things go back to the problem I had at the beginning

my recommendation add a lot of serial output to get a detailed picture about the values of each variable and which conditions are true which one are false.

You can go on scratching your head trying this thying that by guessing and using a lot of time with it.
Or you can add serial output and find out what is really happening. Sure adding the debug-output needs time.
But it is well invested because you will learn from it and you will find the bug.

best regards Stefan

Stefan, I put a lot of serial-output as you recomended.

This is what I get:

Enter the first if
keyValueState: 1
Enter while
Key Value: 1
Enter while
Key Value: 1
Enter while
Key Value: 1
Enter while
Key Value: 1
Enter the second if
Key Value: 0
keyValueState: 0
out of the first if
Enter the first if
keyValueState: 0
keyValueState: 0
out of the first if

Here is the code:

  if (digitalRead(SW)==!stateSW){
    delay(botonPulse);
    Serial.println("Enter the first if");
    keyValueState= true;
    Serial.print("keyValueState: ");
    Serial.println(keyValueState);
    while (keyValueState)
    {
      Serial.println("Enter while");
      if (digitalRead(SW)==!stateSW)
        {
        keyValueState= false;
        delay(botonPulse);
        Serial.println("Enter the second if");
      }
      display.clearDisplay();
      display.setCursor(0, 35);
      display.print("Key Value:");
      display.print(keyValue);
      display.display();
      Serial.print("Key Value: ");
      Serial.println(keyValueState);
  }
  Serial.print("keyValueState: ");
  Serial.println(keyValueState);
  Serial.println("out of the first if");
}

What I don't understand is why everything works when I press the button the first time, but when I enter again the first if, the variable keyValueState doesn't change to true, as it is assigned (you can see that on the serial output keyValueState: 0), after entering the first if for the second time. The variable is 0 even though the function keyValueState = true; is just above the serial output.

Please post your complete sketch as it is now

Have you taken note of replies #1 and #2 and corrected the problem ?

There you can see the full code. I didn’t update the variable lastStateSW, as I wasn’t looking to compare the digitalRead(SW); to the last state, I was looking to compare them to the state of the button unpressed. I changed the name as I saw it produced confusion.

Here you can see the whole code:

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//OLed variables
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)

//I2C comunication
float keyValue=0; //Key value we are looking to modify in the feeder
bool i2c;
int i2cRecieve1;
String i2cDecoder[] = {"Neumatic Extruder" ,"Stepper Extruder", "-1" ,"1"};
int buf[200];
#define adress 99

//Encoder variables
#define CLK 2
#define DT 3
#define SW 4
#define botonPulse 250

bool keyValueState = false;
int lastStateCLK;
int stateSW;
int currentStateCLK;


float power(float x, int y)
{ 
  float mult=1.0;
  if (y>0)
  {
    for (int i=0 ; i < y ; i++ )
    {
      mult=mult*x;
    }
  }
  else
  {
    for (int i=0 ; i < abs(y) ; i++ )
    {
      mult=mult/x;
      Serial.println(mult);
    }
  return mult;
}

  
  return mult;
}
void getKeyValue()
{
  if (i2cDecoder[buf[0]]=="Neumatic Extruder")
  {
    keyValue=  power(10,(buf[1]*i2cDecoder[buf[2]].toInt()))+255* buf[3]+buf[4]*i2cDecoder[buf[5]].toInt();
  }
  else
  {
    keyValue = power(10,(buf[1]*i2cDecoder[buf[2]].toInt()))*(255* buf[3]+buf[4]*i2cDecoder[buf[5]].toInt());
    //keyValue = power(10,(buf[1]*i2cDecoder[buf[2]].toInt()));
    Serial.println(keyValue);
  }
}

void receiveEvent(int howMany)
{
  unsigned int pos=0;
  while (Wire.available () > 0)
  {
   byte c = Wire.read ();
   //Serial.println(c);
   if (pos < sizeof buf)
     buf [pos++] = c;
  } 
  getKeyValue();
  i2c=true;
  return;
}


void flag ()
{
  if (keyValueState)
  {
    currentStateCLK = digitalRead(CLK);
    if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){
      if (digitalRead(DT) != currentStateCLK) {
			  keyValue --;
		  }  
      else {
			  keyValue ++;
		  }
	  }
    lastStateCLK = currentStateCLK;
  /*if (digitalRead(SW)==!stateSW)
    {
    unitState= false;
    delay(botonPulse);
    }*/
  }
  return;
}

void setup() {
  Serial.begin(250000);
  //I2C initialization
  Wire.begin(adress); 
  Wire.onReceive(receiveEvent); // register event

  //Oled initialization
  /*if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  delay(2000);
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 10);
  display.display();
  display.println("Hello, world!");
  display.display();
  delay(100);
  display.clearDisplay();
  display.display();*/

  //Encoder configuration
  pinMode(CLK,INPUT);
	pinMode(DT,INPUT);
	pinMode(SW, INPUT_PULLUP);
	lastStateCLK = digitalRead(CLK);
  stateSW = digitalRead(SW);
  Serial.print("stateSW: ");
  Serial.println(stateSW);
  attachInterrupt(digitalPinToInterrupt(CLK),flag,RISING);
}

void loop() { 
  //Serial.println(unitState);
  if (i2c){
    display.print(i2cDecoder[buf[0]]);
    display.display();
    Serial.println(i2cDecoder[buf[0]]);
    i2c=false;
  }

  //Mostar en pantalla
  Serial.println(digitalRead(true)); //This line makes everythin work, I don't even now what it does, I wroted by mistake

  if (digitalRead(SW)==!stateSW){
    delay(botonPulse);
    Serial.println("Enter the first if");
    keyValueState= true;
    Serial.print("keyValueState: ");
    Serial.println(keyValueState);
    while (keyValueState)
    {
      Serial.println("Enter while");
      if (digitalRead(SW)==!stateSW)
        {
        keyValueState= false;
        delay(botonPulse);
        Serial.println("Enter the second if");
      }
      display.clearDisplay();
      display.setCursor(0, 35);
      display.print("Key Value:");
      display.print(keyValue);
      display.display();
      Serial.print("Key Value: ");
      Serial.println(keyValueState);
  }
  Serial.print("keyValueState: ");
  Serial.println(keyValueState);
  Serial.println("out of the first if");
}
}

I haven't gone through all lines of code with all details.

You should name every debug-out different
you have mutliple output

  Serial.print("keyValueState: ");
  Serial.println(keyValueState);

they all should be different for example like this

  Serial.print("keyValueState 1: ");
  Serial.println(keyValueState);

  Serial.print("keyValueState 2: ");
  Serial.println(keyValueState);

Somewhere there is an oberlooked logical error and the different naming will lead you to the place where it is.

Without the different naming it is much harder to follow through exactly what is going on.
There are two famous wording about programming:

1.) Your program does always what you have programmed (0,1% exceptions)
Though if the program does something different than you expect, you don't really understand (yet) what you have programmed
:slight_smile:

2.) The "bug" has its hands on the keyboard and is staring at the screen
:-)))

best regards Stefan

So, I changed the Serial output as you recommended, and this is what I receive:

Enter the first if
keyValueState 1: 1
Enter while
Key Value: 1
Enter while
Key Value: 1
Enter while
Enter the second if
Key Value: 0
keyValueState 2: 0
out of the first if
Enter the first if
keyValueState 1: 0
keyValueState 2: 0
out of the first if

As you can see in the void loop:

void loop() { 
  //Serial.println(unitState);
  if (i2c){
    display.print(i2cDecoder[buf[0]]);
    display.display();
    Serial.println(i2cDecoder[buf[0]]);
    i2c=false;
  }

  //Mostar en pantalla
  //Serial.println(digitalRead(true)); //This line makes everythin work, I don't even now what it does, I wroted by mistake

  if (digitalRead(SW)==!stateSW){
    delay(botonPulse);
    Serial.println("Enter the first if");
    keyValueState= true;
    Serial.print("keyValueState 1: ");
    Serial.println(keyValueState);
    while (keyValueState)
    {
      Serial.println("Enter while");
      if (digitalRead(SW)==!stateSW)
        {
        keyValueState= false;
        delay(botonPulse);
        Serial.println("Enter the second if");
      }
      display.clearDisplay();
      display.setCursor(0, 35);
      display.print("Key Value:");
      display.print(keyValue);
      display.display();
      Serial.print("Key Value: ");
      Serial.println(keyValueState);
  }
  Serial.print("keyValueState 2: ");
  Serial.println(keyValueState);
  Serial.println("out of the first if");
}
}

As you can see, if it enters the first if, it should change the value of the variable keyValueState to true, but when I serial output that variable, I receive a 0 instead of a 1. That is what I don’t understand.

 if (digitalRead(SW)==!stateSW){
    delay(botonPulse);
    Serial.println("Enter the first if");
    keyValueState= true;
    Serial.print("keyValueState 1: ");
    Serial.println(keyValueState);
    while (keyValueState)
    {
      Serial.println("Enter while");
      if (digitalRead(SW)==!stateSW)
        {
        keyValueState= false;

When you enter the while in the code snippet above digitalRead(SW) is guaranteed not to equal stateSW so keyValueState will always be set to zero. Is that what is intended to occur ?

Yes, when I press a button, it is suposed to enter the first if. In there, the variable keyStateValue will change into true, and the code should enter the while loop. Then when I press it again, it should enter the second if, where the keyValueState varibale will change, and it will go out of the while.