Function only runs on powerup

I am trying to get this beep code of 3 short and one long to beep one time when output5 is pulled back to high. I am using this to keep a switched outlet hot and when its rebooted using an html toggle I want it to beep this code each time, one time and then stop because output5 is always high. Right now it will beep the code but only on startup and when I move the toggle and the countdown is done it does not beep anymore. Any help would be appreciated!



// Import required libraries
#ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>
#else
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "SSID";
const char* password = "PASS";

const char* PARAM_INPUT_1 = "state";
const char* PARAM_INPUT_2 = "value";

const int output5 = 5;   //GPIO-05-D1  //Green LED on status indicator
const int output4 = 4;   //GPIO-04-D2  //Null status indicator
const int output13 = 13; //GPIO-13-D7  //Red LED off status indicator
/*const int output14 = 14; //GPIO-14-D5  //PIEZO status beep indicator*/
/*const int buzzer = 14; //buzzer to arduino pin 14*/

/*const int output2 = 2; //GPIO-04-D2  //PIEZO status beep indicator*/

String timerSliderValue = "10";

  // Current time millis for LED Flasher
  unsigned long currentTime = millis();
  
  // Previous time millis for LED Flasher
  unsigned long previousTime = 0; 

/*//////////////////////////////*/

  // Current time millis for Piezo Beeper
  unsigned long currentMillis = millis();

  // Previous time millis for Piezo Beeper
  unsigned long previousMillis = 0;

  unsigned long runningTime = 2000;

#define buzzer 14
int code[] = {1, 1, 1, 10}; // array with 3 digit code to transmit
int n = 13;
int beeps = 0; // remember the number of beeps played for this digit

unsigned long beepDelay = 0UL;    // how long to delay between beeps /*0UL = Zero Unsigned Long*/
byte codeIdx = 0;                 // index into code
byte beepCount;                   // # of beeps in current code  
int runXTimes = 0;
bool doneFlag = false;

/*/////////////////////////////*/

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>ESP Web Server</title>
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 2.4rem;}
    p {font-size: 2.2rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px} 
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 34px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 68px}
    input:checked+.slider {background-color: #2196F3}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); -moz-transform: translateX(52px); transform: translateX(52px)}
    .slider2 { -webkit-appearance: none; -moz-appearance: none; appearance: none; margin: 14px; width: 300px; height: 20px; background: #ccc; outline: none; -webkit-transition: .2s; -moz-transition: opacity .2s; transition: opacity .2s;}
    .slider2::-webkit-slider-thumb {-webkit-appearance: none; -moz-appearance: none; appearance: none; width: 30px; height: 30px; background: #2f4468; cursor: pointer;}
    .slider2::-moz-range-thumb { width: 30px; height: 30px; background: #2f4468; cursor: pointer; } 
  </style>
</head>
<body>
  <h2>Reboot ISP Radio</h2>
  <p><span id="timerValue">%TIMERVALUE%</span> s</p>
  <p><input type="range" onchange="updateSliderTimer(this)" id="timerSlider" min="1" max="20" value="%TIMERVALUE%" step="1" class="slider2"></p>
  %BUTTONPLACEHOLDER%
<script>
function toggleCheckbox(element) {
  var sliderValue = document.getElementById("timerSlider").value; /*This function gets the current value of the slider label:*/
  var xhr = new XMLHttpRequest();
  if(element.checked === false){ xhr.open("GET", "/update?state=0", true); xhr.send(); /*Makes a request on the /update?state=1 URL so that the ESP knows it needs to set the output to LOW*/

    /*The following lines (from var count to 1000);) decrease the slider label value every second creating the countdown timer.*/
    var count = sliderValue, timer = setInterval(function() {
      count--; document.getElementById("timerValue").innerHTML = count;
      if(count == 0){ clearInterval(timer); document.getElementById("timerValue").innerHTML = document.getElementById("timerSlider").value; }
    }, 1000);

    /*When the timer hits zero, the label value gets back to its original value and a request is made on the /update?state=0 URL, so that the ESP knows it is time to set the output to LOW. The button on the web server gets back to the off state.*/
    sliderValue = sliderValue*1000;
    setTimeout(function(){ xhr.open("GET", "/update?state=1", true); 
    document.getElementById(element.id).checked = true; xhr.send(); }, sliderValue);
  }
}
function updateSliderTimer(element) {
  var sliderValue = document.getElementById("timerSlider").value;
  document.getElementById("timerValue").innerHTML = sliderValue;
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider?value="+sliderValue, true);
  xhr.send();
}
</script>
</body>
</html>
)rawliteral";

// Replaces placeholder with button section in your web page
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons = "";
    String outputStateValue = outputState();
    buttons+= "<p><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"output5\" " + outputStateValue + " checked ><span class=\"slider\"></span></label></p>";
    return buttons;
  }
  else if(var == "TIMERVALUE"){
    return timerSliderValue;
  }
  return String();
}

String outputState(){
  if(digitalRead(output5)){
    return "checked";
  }
  else {
    return "";
  }
  return "";
}

// Variables will change:
  int ledState = LOW;      // ledState used to set the LED

  /////////////////
  class Flasher
{
    // Class Member Variables
    // These are initialized at startup
    int ledPin;      // the number of the LED pin
    /*long timeoutTime;*/  //Length of pause before blinking starts

    long OnTime;     // milliseconds of on-time
    long OffTime;    // milliseconds of off-time

    int blinkCount;  // how many times to blink LED
    int blinks;      // set to 0 to start blink counting

    // These maintain the current state
    int ledState;                 // ledState used to set the LED
    unsigned long previousTime;   // will store last time LED was updated
    
     
  // Constructor - creates a Flasher 
  // and initializes the member variables and state
  public:
  Flasher(int pin, long on, long off, int count)
  {
    ledPin = pin;
    pinMode(ledPin, OUTPUT);    
        
    OnTime = on;
    OffTime = off;
    blinkCount = count;
    
    ledState = LOW; 
    previousTime = 0;
    blinks = 0;
  }

  void Update()
  {

    // Check to see if it's time to change the state of the LED
    unsigned long currentTime = millis();
         
    if (currentTime - previousTime >= OnTime)
    if (blinks <= blinkCount)
    {
      previousTime = currentTime;  // Remember the time
      
      // If the LED is off turn it on and vice-versa
      if (ledState == LOW) {
      ledState = HIGH;
      blinks++;
    } else {
      ledState = LOW;
      blinks++;
    }
      
      //Set the LED with the ledState of the variable
      //Set the PIEZO with the piezoState of the variable
      digitalWrite(ledPin, ledState);  // Update the actual LED 
    } 
    else 
    {
      unsigned long currentTime = millis();
      ledState = LOW;

    if (currentTime - previousTime >= OffTime) {
      previousTime = currentTime;   // Remember the time
      blinks = 0;

      
    }
    
   }
   
  }

};  
     

//Set the following for the LED blink indicator
//   LED pin#s/onTime/offTime/#of blinks
Flasher led3(13, 300, 300, 1);


/*///////////////////////////////////////*/

  
  
  
/*///////////////////////////////////////*/

void setup(){

  pinMode(buzzer, OUTPUT);
  analogWrite(buzzer, LOW);
  pinMode(output4, OUTPUT);
  digitalWrite(output4, LOW);
  pinMode(output5, OUTPUT);
  digitalWrite(output5, HIGH);
  
  // initialize count for first code
  beepCount = code[0];

  // Serial port for debugging purposes
  Serial.begin(115200);

  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  
  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Send a GET request to <ESP_IP>/update?state=<inputMessage>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    // GET input1 value on <ESP_IP>/update?state=<inputMessage>
    if (request->hasParam(PARAM_INPUT_1)) {
      inputMessage = request->getParam(PARAM_INPUT_1)->value();
      digitalWrite(output5, inputMessage.toInt());

    }
    else {
      inputMessage = "No message sent";
    }
    /*Serial.println(inputMessage); /*Prints the 0 or 1 after checkbox checked or unchecked */
    request->send(200, "text/plain", "OK");
  });
  
  // Send a GET request to <ESP_IP>/slider?value=<inputMessage>
  server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    // GET input1 value on <ESP_IP>/slider?value=<inputMessage>
    if (request->hasParam(PARAM_INPUT_2)) {
      inputMessage = request->getParam(PARAM_INPUT_2)->value();
      timerSliderValue = inputMessage;
    }
    else {
      inputMessage = "No message sent";
    }
    Serial.println(inputMessage);
    request->send(200, "text/plain", "OK");
  });

    // Start server
    server.begin();

}

void startBeep() {

if (runXTimes < n)
  {

  // if it's time to beep
  if (millis() - previousMillis >= beepDelay)
  {
    tone(buzzer, 50, 200);

    // if no more beeps for this code
    if ( !--beepCount )
    {

      // delay between codes
      beepDelay = 800;

      // increment index, wrap to first code
      codeIdx = codeIdx == 3 ? 0 : codeIdx + 1;

      // set # of beeps for this code
      beepCount = code[codeIdx];

    } else {

      // delay between beeps in code
      beepDelay = 200;

    } // else

    // mark time of this beep
    previousMillis = millis();
    
    runXTimes++;


  } // if

 }

};

void loop() {

    if (digitalRead(output5) == HIGH){
    startBeep();
    
    /*Serial.println("output5 is HIGH");*/
    }
    if (digitalRead(output5) == LOW) {
    led3.Update(); /* Start Red LED Flasher */
    }

 };

why do you configure output5 as an OUTPUT if you want to read it as an input?

It is in the section of code for the timerSlider and output5 would keep relay opened until countdown is set and html toggle is switched off. After countdown the html toggle automatically goes back to the on position and relay is pulled in. Does that make sense?

I also forgot to mention that the led3.Update function works every time reading output5.

i think your telling me what you think is does

but what do you expect digitalRead() to return when the pin is configured as an output?

why don't you read a different pin, one configured as an INPUT or INPUT_PULLUP?

I'm pretty sure I'm telling you how the code was written. It's an output because it's turning something off and back on.
I tried using another pin set as an input but this did not work either. I'm confused as to why this part works:

if (digitalRead(output5) == LOW) {
    led3.Update(); /* Start Red LED Flasher */
    }

why do you think this works?

At this point I would only venture to guess. I appreciate the teachable moments and if I were a code guru I wouldn't be asking questions on this forum.

Hi John,

This means your code has to store if the beeping has been done one time since last output5 switched to low. This is done by a variable. In coding such variables are called flag-variable.

The switching to low resets the flag-variable

boolean beepingOnceDone = false;

if (digitalRead(output5) == LOW) {
  beepingOnceDone = false; // if output goes low AGAIN reset flag-variable to do a NEW beeping
}

if (beepingOnceDone == false && digitalRead(output5) == HIGH ) {
  // only in case beepingOnceDone is false
  startBeep();
  beepingOnceDone = true; // set flag-variable true to lock against repeated beeping
}
/*Serial.println("output5 is HIGH");*/

best regards Stefan

Thanks Stefan, this was very helpful. I tried this before your suggestion but wasn't using it correctly in the code. After adding this back in the correct way I am only getting the one beep and not the 3 short/1 long beep that I wanted. I tried adding a delay between startBeep(); and doneFlag but this did not work. I am getting closer!

if your function startBeep() shall be called multiple times the if-condition to set the doneFlag to true has to be modified to that condition that must be fullfilled
You seem to count up variable runXTimes
so the condition to set the doneFlag is

if (runXTimes == 4 ) {
  doneFlag = true
}

and you have to reset variable runXTimes to zero too
otherwise it is incrementing infinietly

Stefan, Success!!!
I really appreciate your help. I spent countless hours on this and was reluctant to ask for help but now, I can finally move on. And I learned something that I can use later on :slightly_smiling_face:
I know when you look at runXTimes and see it is set at "13", it's because I am reading the beep code index as: 1, 1, 1, 10 which is essentially 13 beeps (3 short and 1 long).

void loop() {

    if (digitalRead(output5) == LOW) {
    doneFlag = false; // if output goes low AGAIN reset flag-variable to do a NEW beeping
    led3.Update(); /* Start Red LED Flasher */
    }
    
    if (doneFlag == false && digitalRead(output5) == HIGH ) {
    // only in case beepingOnceDone is false
    startBeep();
    
    }
    if (runXTimes == 13) {
    doneFlag = true; // set flag-variable true to lock against repeated beeping
    runXTimes = 0;
    }
    
    
 };

When you are checking bounds, it's always safer to use "equal to or greater than", just in case the equals value gets skipped, then it would increment forever. So use:

    if (runXTimes >= 13) {

Other than being safer, there is no effect on the logic when used in this way.

why is there a ")" after the "}"?
does this code compile?

Thanks for that tip aarg, I made this change and still works as expected and makes perfect sense.

Greg, yes it does compile. That is the closing bracket for the get request/s.

Thank you everyone for all of your help!

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