[Solved] Using button as toggle switch to enter and exit out of a loop

So, I have been looking for a tutorial that can help me with my build, but I just can't find anything.
What I'm trying to do is: When I push a button once, a loop where I'm measuring temperature every second should start working and continue to do so even after I release the button. If I click the button one more time, the code should stop working. I've tried to make it work for a week, but I can't get it. I tried by using a state machine but when I enter the loop I can't make it stop.

I'm pretty new to all of this so any help is appreciated. Thanks in advance.

First, figure out how to debounce a button.

Then just poll the button, toggle a flag if the button is pressed and loop your measurement code if the flag is set.

PushButton button = { 2 };  // Create a new PushButton object on pin 2

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);  // Set the built-in LED (pin 13) to output mode
}

void loop() {
    static bool measuring = false;
    if (button.isPressed()) {  // If the button is pressed
        measuring = ! measuring;  // Toggle the state of the flag
        digitalWrite (LED_BUILTIN, measuring); // Light the LED to show if you are currently measuring or not
    }
    if (measuring)
        measureTemperature();
}

void measureTemperature() {
    // Your implementation
}

Pieter

I'm not really familiar with the terms poll and flag. As I said i'm still a beginner. I know some C++ from school from a few years ago but I'm not that good.

Here is the code I'm using to measure temperature, turn on an LED when the temperature reaches 26 degrees Celsius and to turn it off when the temperature goes down to 24 degrees Celsius. Now I want to make this code run when I press a button.

float temp;
int sensor = 0;
int const startTemp = 26;
float onTemp = 26;
float offTemp = 24; 
int ledPin = 13;

void setup() 
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(sensor, INPUT);
}
 
void loop() 
{
  temp = analogRead(sensor);
  temp = temp * 0.48828125;

  Serial.print(temp);
  Serial.print("°C");
  Serial.print("\n");

  delay(1000);
  
  if (temp < onTemp)
  {
    onTemp = startTemp;
    digitalWrite(ledPin, HIGH);
  }
  
  else if(temp >= onTemp)
  {
    onTemp = offTemp;
    digitalWrite(ledPin, LOW);
  }
}

Or actually let me say my full intentions.

I ordered an arduino ethernet shield. I want to make this completely wireless over WAN. I will be making an android application to control the arduino through it's ip address. Basically I'm making some home automation stuff. I want to turn on a few devices even if i'm out of the country. Check the temperature (let's say a water heater). I'm just testing with the physical button to see if everything works like planned or is it simpler to control it with the app with a virtual on/off switch?

LanternMG:
I'm not really familiar with the terms poll and flag.

flag defined

Forget about the temperature reading for a moment, keep it simple.
Try to understand the example I posted.
Then remove the delay from your code, look up Blink Without Delay, and combine the two.

Polling the button just means that you keep on calling button.isPressed() in a fast loop to check if it is pressed.

If you want to combine the timer (every second) with the button code above:

PushButton button = { 2 };  // Create a new PushButton object on pin 2

const unsigned long interval = 1000; // 1 second

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);  // Set the built-in LED (pin 13) to output mode
}

void loop() {
    static bool measuring = false;
    static unsigned long timer = millis();
    if (button.isPressed()) {  // If the button is pressed
        measuring = ! measuring;  // Toggle the state of the flag
        digitalWrite (LED_BUILTIN, measuring); // Light the LED to show if you are currently measuring or not
        timer = millis();
    }
    if (measuring && (millis() - timer >= interval)) {
        measureTemperature();
        timer += interval;
    }
}

void measureTemperature() {
    // Your implementation
}

I finally got home and tried the code you provided. In this case, the button isn't used as a toggle switch, it's used as a regular button. When you press it, the LED is off and immediately after releasing it, it turns on. I need the button to act as a switch. If you press it once, the LED should stay off even after releasing the button. If I press it again, it should stay on, again, even after releasing the button.

I'm sorry, I know it's pretty easy for you, but this is literally my second arduino project. I'm still a newbie.

“I need the button to act as a switch.”

Then modify the code to do so.
Use change in state detection.

Edit:
“I ordered an arduino ethernet shield. I want to make this completely wireless over WAN.l”
If this is your 2nd project, you need to stop and spend more time on the basics before trying advanced stuff, but you know this already.

Please post the exact code you tried.

I finally got it to work. Thanks to everyone. I just modified the code you supplied just a bit and it works like a charm. Thank you once again.

When you get your code working it is normal to show your work so new people can see the solution.

Oh yeah, sorry. Here it is:

#include <Pushbutton.h>
Pushbutton button = { 2 };  // Create a new PushButton object on pin 2
float temp;
int sensor = 0;
int const startTemp = 26;
float onTemp = 26;
float offTemp = 24;
int ledPin = 13;

const unsigned long interval = 1000; // 1 second

void setup()
{
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  pinMode(sensor, INPUT);
}

void loop()
{
  
  static bool measuring = false;
  static unsigned long timer = millis();
  if (button.getSingleDebouncedPress())
  { // If the button is pressed
    measuring = ! measuring;  // Toggle the state of the flag
    digitalWrite (ledPin, measuring); // Light the LED to show if you are currently measuring or not
    timer = millis();
  }
  if (button.getSingleDebouncedRelease())
  {
    digitalWrite(ledPin, HIGH);
  }
  if (measuring && (timer - millis() >= interval))
  {
    measureTemperature();
      timer += interval;
    }
}

void measureTemperature()
{
  temp = analogRead(sensor);
  temp = temp * 0.48828125;

  Serial.print(temp);
  Serial.print("°C\n");

  static unsigned long timer = millis();

  if (timer - millis() >= interval)
  {
    if (temp < onTemp)
    {
      onTemp = startTemp;
      digitalWrite(ledPin, HIGH);
    }

    else if (temp >= onTemp)
    {
      onTemp = offTemp;
      digitalWrite(ledPin, LOW);
    }
  }
}

And if anyone is wondering, I'm using the LM35 temperature sensor.

I should have made it more clear that you had to use the class from the link in the first reply I posted, i.e.

class PushButton
{
  public:
    PushButton(uint8_t pin) // Constructor (executes when a PushButton object is created)
      : pin(pin) { // remember the push button pin
      pinMode(pin, INPUT_PULLUP); // enable the internal pull-up resistor
    };
    bool isPressed() // read the button state check if the button has been pressed, debounce the button as well
    {
      bool pressed = false;
      bool state = digitalRead(pin);               // read the button's state
      int8_t stateChange = state - previousState;  // calculate the state change since last time

      if (stateChange == falling) { // If the button is pressed (went from high to low)
        if (millis() - previousBounceTime > debounceTime) { // check if the time since the last bounce is higher than the threshold
          pressed = true; // the button is pressed
        }
      }
      if (stateChange == rising) { // if the button is released or bounces
        previousBounceTime = millis(); // remember when this happened
      }

      previousState = state; // remember the current state
      return pressed; // return true if the button was pressed and didn't bounce
    };
  private:
    uint8_t pin;
    bool previousState = HIGH;
    unsigned long previousBounceTime = 0;

    const static unsigned long debounceTime = 25;
    const static int8_t rising = HIGH - LOW;
    const static int8_t falling = LOW - HIGH;
};

PushButton button = { 2 };  // Create a new PushButton object on pin 2

const unsigned long interval = 1000; // 1 second

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);  // Set the built-in LED (pin 13) to output mode
}

void loop() {
    static bool measuring = false;
    static unsigned long timer = millis();
    if (button.isPressed()) {  // If the button is pressed
        measuring = ! measuring;  // Toggle the state of the flag
        digitalWrite (LED_BUILTIN, measuring); // Light the LED to show if you are currently measuring or not
        timer = millis();
    }
    if (measuring && (millis() - timer >= interval)) {
        measureTemperature();
        timer += interval;
    }
}

void measureTemperature() {
    // Your implementation
}

Why are you using two separate timers?

Well... I had no idea that I was using two different timers. I don't know how to use millis(), but I copied some stuff and it works, I don't know how, but it works and that's good enough for me. If you know how to make it more efficient, do so. I tried like this and it suits my needs.

This would be my approach

class PushButton
{
  public:
    PushButton(uint8_t pin) // Constructor (executes when a PushButton object is created)
      : pin(pin) { // remember the push button pin
      pinMode(pin, INPUT_PULLUP); // enable the internal pull-up resistor
    };
    bool isPressed() // read the button state check if the button has been pressed, debounce the button as well
    {
      bool pressed = false;
      bool state = digitalRead(pin);               // read the button's state
      int8_t stateChange = state - previousState;  // calculate the state change since last time

      if (stateChange == falling) { // If the button is pressed (went from high to low)
        if (millis() - previousBounceTime > debounceTime) { // check if the time since the last bounce is higher than the threshold
          pressed = true; // the button is pressed
        }
      }
      if (stateChange == rising) { // if the button is released or bounces
        previousBounceTime = millis(); // remember when this happened
      }

      previousState = state; // remember the current state
      return pressed; // return true if the button was pressed and didn't bounce
    };
  private:
    uint8_t pin;
    bool previousState = HIGH;
    unsigned long previousBounceTime = 0;

    const static unsigned long debounceTime = 25;
    const static int8_t rising = HIGH - LOW;
    const static int8_t falling = LOW - HIGH;
};

/* ----------------------------------------------------------------------------------------------------------------------- */

PushButton button = { 2 };  // Create a new PushButton object on pin 2

const unsigned long interval = 1000; // 1 second

const uint8_t ledPin = LED_BUILTIN;
const uint8_t sensorPin = A0;

const float highThreshold= 26;
const float lowThreshold = 24;

const float ADC_to_Celsius = 500/1023;  

void setup() {
    Serial.begin(9600);
    pinMode(ledPin, OUTPUT);  // Set the built-in LED (pin 13) to output mode
}

void loop() {
    static bool measuring = false;
    static unsigned long timer = millis();
    if (button.isPressed()) {  // If the button is pressed
        measuring = ! measuring;  // Toggle the state of the flag
        timer = millis();
    }
    if (measuring && (millis() - timer >= interval)) {
        measureTemperature();
        timer += interval;
    }
}

void measureTemperature() {
    float temp = analogRead(sensorPin) * ADC_to_Celsius;
    Serial.print(temp);
    Serial.println("°C");
    static float threshold = highThreshold;
    if (temp < threshold) {
        threshold = highThreshold;
        digitalWrite(ledPin, HIGH);
    } else if (temp >= threshold) {
        threshold = lowThreshold;
        digitalWrite(ledPin, LOW);
    }
}

Edit: I flipped millis() - timer.