Trying to call a function and making it loop within the void loop()

Hi, I have been working on this project where I am monitoring and controlling humidity/temperature for a humidor I am going to build. I am using an OLED screen and right now i’ve been designing the menu/ UI. I know that the code right now isnt the best but I have just been trying to make it work then finalise the workings and details once its fully working. (sorry im not a big commentor in my code). I am using a rotary encoder to navigate the menu.

    if (value == 0){ //Encoder Position
    Menu();
    display.setTextSize(1);
    display.setCursor(0,15);
    display.print(">"); 
    display.display();  
      if(buttonState== LOW){ //If encoder is pressed
        value = 6;
        while (value == 6){
          MainDisplay(t,h);
        }        
    }
  }

This is the problem I have, I want to call the MainDisplay() function which displays the temperature and humidity easily to read, I have tried to use a while loop while runs the function while the encoder value is equal to 6, but when I try to loop this function the OLED screen freezes and so does the serial readout in the IDE. This function calls fine when I don’t try to loop it, however that isn’t very useful because the screen will only display the temp & humidity from when the function is called and remains stationary.

I’m pretty new to arduino code and this is my first project, so any help or advice would be very much appreciated. Once again, I know the other parts of the code may not be the most efficient but I plan to fix this when I get the code to work and I will tidy it up.

I will attach an image of the main display showing the temperature and humidity, and an image of the options menu which hopefully will make more sense and visualisation of what the code is doing during the display() lines of code.

Here is the full code.

#include <ClickEncoder.h>
#include <TimerOne.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "DHT.h"
#define DHTPIN 12     // what pin we're connected to
#define DHTTYPE DHT22   // DHT 22  (AM2302)
DHT dht(DHTPIN, DHTTYPE);

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define Pa 2 //Encoder Pin A
#define Pb 3 //Encoder Pin B
#define SW A1 //Encoder Button

ClickEncoder *encoder;
int16_t last, value;
const int buttonPin = SW; //Encoder Button
int buttonState;    // variable for reading the pushbutton status
int standby = 15;
int leftnumber;

float t;
float h;
float highT;
float lowT = 50;
float highH;
float lowH = 100;

void timerIsr() {
  encoder->service();
}

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() {
  Serial.begin(9600);
    
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  encoder = new ClickEncoder(Pa, Pb, SW);
  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr); 
  last = -1;
    // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT_PULLUP);

 Menu();
}

void MainDisplay(float t, float h){

  //RESET
  //delay(100);
  display.clearDisplay();
 
  //TEMP1
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("Temperature: ");
  display.setTextSize(2);
  display.setCursor(0,10);
  display.print(t);
  display.print(" ");
  display.setTextSize(1);
  display.cp437(true);
  display.write(167);
  display.setTextSize(2);
  display.print("C");
  //HUMIDITY1
  display.setTextSize(1);
  display.setCursor(0, 35);
  display.print("Humidity: ");
  display.setTextSize(2);
  display.setCursor(0, 45);
  display.print(h);
  display.print(" %"); 
 
  //ICON-*Flashes*
  display.fillRoundRect(100, 10, 20, 44, 2, WHITE);
  display.setCursor(105,15);
  display.setTextColor(BLACK);
  display.print("J");
  display.setCursor(105,35);
  display.print("B");
  display.display();
  //delay(20);
  //display.fillRoundRect(100, 10, 20, 44, 2, WHITE); //FLASH (copy Temp1 & Humidity1)

}
void Menu(){

    //TITLE
    display.clearDisplay();
    delay(50);
    display.setTextColor(WHITE);
    display.setTextSize(2);
    display.setCursor(40,0);
    display.print("MENU");
    //MENU1 Temp    
    display.setTextSize(1);
    display.setCursor(10,25);
    display.print("Temperature Set");
    //MENU2 Humid
    display.setCursor(10,35);
    display.print("Humidity Set");
    //MENU3 Setting
    display.setCursor(10,45);
    display.print("Settings");
    //24HR Min/Max
    display.setCursor(10,55);
    display.print("Daily Min/Max");
    //RETURN
    display.setCursor(10,15);
    display.print("Main Display");

    display.setCursor(115,0);
    display.print(value);
    
    display.display();
            
}

void TempMenu(){ //Set Temperature
 value = 6;
 //ActualTEMP
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("Set Temperature =");
  display.setTextSize(1);
  display.cp437(true);
  display.write(167);
  display.setTextSize(2);
  display.print("C");
  display.display();
}
void HumMenu(){
  
}
void SetMenu(){
  
}
void MaxMenu(){ //Max/Min Temp&Hum
  value = 6;
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(2);
  display.setCursor(30,0);
  display.print("MIN/MAX");  
  display.setTextSize(1);
  display.setCursor(0,25);
  display.print("Max Temperature="); 
  display.print(highT); 
  display.setCursor(0,35);
  display.print("Min Temperature="); 
  display.print(lowT); 
  display.setCursor(0,45);
  display.print("Max Humidity ="); 
  display.print(highH); 
  display.setCursor(0,55);
  display.print("Min Humidity ="); 
  display.print(lowH); 
  display.display();
}

void loop() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit
  float f = dht.readTemperature(true);

    if (t > highT){
      highT = t; //Highest Temperature
  }
    if (t < lowT){ 
      lowT = t; //Lowest Temperature
  }
    if (h > highH){
      highH = h; //Highest Humidity
  }
    if (h < lowH){
      lowH = h; //Lowest Humidity
  }


  value += encoder->getValue();
  if (value != last) {
    last = value;
    Serial.print("Encoder Value: ");
    Serial.println(value);
 }
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
  // check if the pushbutton is pressed. If it is, the buttonState is LOW:

    if(value < 0){
      value = 0; // Doesnt allow encoder to go below 0
  }

    if (value == 0){ //Encoder Position
    Menu();
    display.setTextSize(1);
    display.setCursor(0,15);
    display.print(">"); 
    display.display();  
      if(buttonState== LOW){ //If encoder is pressed
        value = 6;
        while (value == 6){
          MainDisplay(t,h);
        }        
    }
  }   
    
    if (value == 1){ //Encoder Position
    Menu();
    display.setTextSize(1);
    display.setCursor(0,25);
    display.print(">"); 
    display.display();  
      if (buttonState == LOW){ //If encoder is pressed
        TempMenu();   
      }
  }
    if (value == 2){ //Encoder Position
    Menu();  
    display.setTextSize(1);
    display.setCursor(0,35);
    display.print(">");
    display.display(); 
      if (buttonState == LOW){ //If encoder is pressed
        HumMenu();   
      }     
  }
    if (value == 3){ //Encoder Position
    Menu();  
    display.setTextSize(1);
    display.setCursor(0,45);
    display.print(">");
    display.display();
      if (buttonState == LOW){ //If encoder is pressed
        SetMenu();  
      }      
  }
    if (value == 4){ //Encoder Position
    Menu();  
    display.setTextSize(1);
    display.setCursor(0,55);
    display.print(">"); 
    display.display(); 
          if (buttonState == LOW){ //If encoder is pressed
        MaxMenu();   
      }    
  }
}

Main Display - mainDisplay()

That while is "Do forever". You need Woodoo to change the value to something else than 6. MainDisplay is not doing it.
Maybe call the selector reading routine in that loop?

Hi, thanks for the reply. Im not sure what you mean exactly, the “value” variable is determined by the encoder position so wont the encoder still write to the variable while that loop is running ?

I want this loop to run for long peroids of time because the mainDisplay() function will be on the display when not operating. I have tried changing it so when the encoder button is pressed it will exit the loop but the same thing is happening. The loop doesn’t run with the code shown, the arduino just crashes and must be reset. For the other options in the menu they work fine to call the function when selected, however they aren’t being looped, and they are exited when the encoder position is lowered.

Could you show me an example using my code what exactly you mean ? Thanks for the reply anyways.

Settings menu attached below

The execution is locked up in Your while loop. Nothing is done to read that encoder. All that is done is rewriting the display for no good at all. No data will be changed as no sensor are being red.

Thanks, I understand now. I will try adding that into the function to read the encoder.

You're not the first member doing that mistake. If You lock Yourself up in Your hobbyroom You can't expect dinner to be made. You need to go to the kitchen and make that dinner at the same time as doing the hobby.

Im still confused as to why the loop wont initiate. I would understand if it started and didnt stop as the encoder would not be able to be read from inside the loop. The function wont start and the arduino crashes. I've added code to the function to read the encoder while the loop is running but that isnt changing anything as the function doesnt seem to run from the start. Still stuck with this.

I have tried using a for loop to prove that it isnt to do with the exit term and the function isnt starting. The same thing happens, the screen shows but the values do not change, and the arduino crashes and needs to be reset.

    if (value == 0){ //Encoder Position
    Menu();
    display.setTextSize(1);
    display.setCursor(0,15);
    display.print(">"); 
    display.display();  
      if(buttonState== LOW){ //If encoder is pressed
        value = 6;
        for(int i=0;i<900;i++){
          MainDisplay(t,h);
        }
              
    }
  }

What do You mean by this: the loop wont initiate.

  value += encoder->getValue();
  if (value != last) {
    last = value;
    Serial.print("Encoder Value: ");
    Serial.println(value);

What printout comes up in Serial.monitor?

If you want a responsive program you should not use FOR or WHILE as they block the Arduino until they complete. Just use IF and allow loop() to do the repetition. You should not be designing any part of the program to run for long periods. Each function should return to loop() as quickly as possible so other things can happen - even if it is only to check the same thing repeatedly.

If you want to navigate between menus then have a variable that keeps track of which menu you are at. Suppose the menus are numbered 0 to 3 then your code might be something like this

void loop() {
   // code to update menuSelected with encoder

   if (menuSelected == 0) {
       doMainMenu();
   }
   else if menuSelected == 1) {
       doMenu1();
   }
   // etc
}

void doMainMenu() {
   // code to display the menu
   // code to check for user input
}
// etc

...R

Hi, thanks for the reply. That is what I have done with my static menus, I have simply called the function for the menu. (Below)

    if (value == 1){ //Encoder Position
    Menu();
    display.setTextSize(1);
    display.setCursor(0,25);
    display.print(">"); 
    display.display();  
      if (buttonState == LOW){ //If encoder is pressed
        TempMenu();   
      }
  }
    if (value == 2){ //Encoder Position
    Menu();  
    display.setTextSize(1);
    display.setCursor(0,35);
    display.print(">");
    display.display(); 
      if (buttonState == LOW){ //If encoder is pressed
        HumMenu();   
      }     
  }
    if (value == 3){ //Encoder Position
    Menu();  
    display.setTextSize(1);
    display.setCursor(0,45);
    display.print(">");
    display.display();
      if (buttonState == LOW){ //If encoder is pressed
        SetMenu();  
      }      
  }
    if (value == 4){ //Encoder Position
    Menu();  
    display.setTextSize(1);
    display.setCursor(0,55);
    display.print(">"); 
    display.display(); 
          if (buttonState == LOW){ //If encoder is pressed
        MaxMenu();   
      }    
  }

However, the menu I am trying to run must be looped as it actively reads values from the temperature/ humidity sensor

void loop() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit
  float f = dht.readTemperature(true);

I have tried adding two arguments to the function:

void MainDisplay(float t, float h)

But this just displays the values from the time the function was called. I am trying to call this function but have the values update live which is why I am trying to loop the function.

What about reply #9?

Railroader:
What do You mean by this:

the loop wont initiate.
  value += encoder->getValue();

if (value != last) {
   last = value;
   Serial.print("Encoder Value: ");
   Serial.println(value);




What printout comes up in Serial.monitor?

Hi, sorry was just about to reply to you too...

The serial monitor stops showing values as the arduino crashes when I try to loop the menu function. Before the menu function gets called in the while loop the serial monitor works fine in showing the encoder position. EG:

19:39:22.286 -> Encoder Value: 5
19:39:22.574 -> Encoder Value: 4
19:39:22.718 -> Encoder Value: 3
19:39:22.862 -> Encoder Value: 2
19:39:23.868 -> Encoder Value: 1
19:39:25.546 -> Encoder Value: 6
19:39:27.895 -> Encoder Value: 5
19:39:28.087 -> Encoder Value: 6
19:39:28.470 -> Encoder Value: 7
19:39:28.470 -> Encoder Value: 6
19:39:28.518 -> Encoder Value: 7

Okey. Good. Then we know that the encoder reading works.

Question about loop the menu function
Do You mean when calling

    if (buttonState == LOW) {
      value = 6;
      MainDisplay(t, h);
      loop();

How does that code look now? Don't ever try calling loop(). If the compiler supports that it will quickly go wrong. That would be recursive calls and memory quickly runs to an end.
The for loop should not crash or hang the execution.

What does "crash" show up looking like?

Railroader:
Okey. Good. Then we know that the encoder reading works.

Question about loop the menu function
Do You mean when calling

    if (buttonState == LOW) {

value = 6;
      MainDisplay(t, h);
      loop();




How does that code look now? Don't ever try calling loop(). If the compiler supports that it will quickly go wrong. That would be recursive calls and memory quickly runs to an end.
The for loop should not crash or hang the execution.

What does "crash" show up looking like?

By crash, I meant the serial monitor stopped showing the encoder values, and the arduino LED wasnt flashing as I turned the encoder, and I obviously wasn't able to exit the menu to go back to the settings menu.

I have changed the code to what you stated above with the "loop()". The menu now shows up, but the values are still fixed and not updating, but I am able to go backwards to the settings menu so the code is still working, the values on the screen are just not updating and I need to go back to the menu and select the main display again which will re-run the "MainDisplay()" function and update the values.

Good!
Can You attach the entire code You just use? Before I didn't look deeper than the first issue. We'll se what can be done in the large perspective.

jburke424:
However, the menu I am trying to run must be looped as it actively reads values from the temperature/ humidity sensor

That's thinking about the problem the wrong way.

The code in loop() should call the function to get the temperature values and call the function to deal with the menu. Keep them separate except that they are both called from loop()

Have a look at how the code is organized in Several Things at a Time

Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

...R