Delay.... but not delay, so network aRest commands can come

I have seen the Blink without delay() example, and I am really trying to wrap my head around applying it to my code, but I am failing at it. Can someone help me out with this. I have a few sections of a largish arduino sketch that is running in an escape room. The exit code for that puzzle is done by flashing candles in a controlled manner. However as I have it presently designed, I am using the awful delay() command. This is causing problems with the back end telling the arduino to continue on after the Exit code has been entered.
This code is using aRest commands to trigger various timings in the puzzle. (puzzle ready, puzzle start, puzzle solved, puzzle done -- all of which is triggered via aRest commands "gameState?params=action"), each of these actions sends commands back to a Node-Red instance that is the master controller. When the puzzle is solved, the candles begin flashing a 4 digit sequence. This is then entered into another arduino, which sends that to Node-Red which verifies if the sequence entered matches the expected code. If it does, it sends back to the candles arduino to advance to the done step (which advances the game to the next puzzle.)

I am including the relevant coding sections as this is where the arduino is getting hung up on. Thank you so very much in advance, this has been stumping me for quite a while now.

void giveNextCode(String GameCodeReceived) {
  EthernetClient client = server.available();
  rest.handle(client);

  if (candleBlink == 0) {
    nextGameCode = GameCodeReceived;
    gameCodeSent = true;
    int GameCode = GameCodeReceived.toInt();
  #ifdef debug
    Serial.print("Game Code received: ");
    Serial.println(GameCodeReceived);
  #endif
  }

  if (currentMillis - previousMillis >= interval) {
    if (candleBlink == 0) {
      candleBlink = 1
      flashCandles(GameCode / 1000);
    }
  }
  
  flashCandles(GameCode / 1000);
  for (int x = 0; x < 500; x++) {
    rest.handle(client);
    delay(2);
  }
  flashCandles((GameCode / 100) % 10);
  for (int x = 0; x < 500; x++) {
    rest.handle(client);
    delay(2);
  }
  flashCandles((GameCode / 10) % 10);
  for (int x = 0; x < 500; x++) {
    rest.handle(client);
    delay(2);
  }
  flashCandles(GameCode % 10);
  for (int x = 0; x < 1500; x++) {
    rest.handle(client);
    delay(2);
  }
  rest.handle(client);
}
int flashCandles(int CodeNumber) {
#ifdef debug
  Serial.print("Candle Flash: ");
  Serial.println(CodeNumber);
#endif
  
  for (int n = 0; n < CodeNumber; n++) {
    for (int c = 0; c < numCandles; c++) {
      digitalWrite(ledPins[c], LOW);
    }
    delay(250);
    for (int c = 0; c < numCandles; c++) {
      digitalWrite(ledPins[c], HIGH);
    }
    delay(250);
    for (int c = 0; c < numCandles; c++) {
      digitalWrite(ledPins[c], LOW);
    }
    delay(250);
  }
  delay(1000);
}

int setGameState(String gameState) {
  if (gameState == "start") {
#ifdef debug
    Serial.println(F("Remotely Triggered: Game Begin."));
#endif

    NodeRedUpdate("?game=candle&action=start");
  } else if (gameState == "solved") {
#ifdef debug
    Serial.println(F("Remotely Triggered: Game Solved."));
#endif
    gameStatus = 3;
    gameSolved();
    NodeRedUpdate("?game=candle&action=solved");
  } else if (gameState == "done") {
#ifdef debug
    Serial.println(F("Remotely Triggered: Game Done."));
#endif
    gameStart = false;
    isGameSolved = false;
    gameCodeSent = false;
    nextGameCode = "";
    for (int i = 0; i < numCandles; i++) {
      digitalWrite(ledPins[i], LOW);
    }
    NodeRedUpdate("?game=candle&action=done");
  }
}

Please post the full sketch so that the problem can be seen in context

Well I managed to hack something together, and it is responding quite quickly now using the millis() method. For reference here is the updated code. I only changed (at the moment) the flashCandles portion.

int flashCandles(int CodeNumber) {
#ifdef debug
  Serial.print("Candle Flash: ");
  Serial.println(CodeNumber);
#endif
  bool digitDone = false;
  bool flashDone = false;
  int flashState = 0; // 0 - 1st action, 1 - 2nd action, 2 - 3rd action, 3 - exit
  int n = 0;

  previousMillis = millis();
  
  while (digitDone == false) {
    unsigned long currentMillis = millis();

    while (flashDone == false) {
      EthernetClient client = server.available();
      rest.handle(client);
      
      unsigned long currentMillis = millis();
      
      if (flashState == 0) {
        if (currentMillis - previousMillis >= 100) {
          for (int c = 0; c < numCandles; c++) {
            digitalWrite(ledPins[c], LOW);
          }
          previousMillis = currentMillis;
          flashState = 1;
        }
      }
      if (flashState == 1) {
        if (currentMillis - previousMillis >= 250) {
          for (int c = 0; c < numCandles; c++) {
            digitalWrite(ledPins[c], HIGH);
          }
          previousMillis = currentMillis;
          flashState = 2;
        }
      }
      if (flashState == 2) {
        if (currentMillis - previousMillis >= 250) {
          for (int c = 0; c < numCandles; c++) {
            digitalWrite(ledPins[c], LOW);
          }
          previousMillis = currentMillis;
          flashState = 3;
        }
      }
      if (flashState == 3) {
        flashState = 0;
        n = n + 1;
        if (n < CodeNumber) {
          digitDone = true;
        } else {
          flashDone = true;
        }
      }
    }

    if (currentMillis - previousMillis >= 1000) {
      previousMillis = currentMillis;
      digitDone = true;
    }
  }
}

First of all:
I will write bolded in bigger letters but the meaning of this

bolded big letters

is not meant as shouting - just as emphasising

All you users out there if you find it too much reading so many big bolded letters
question your habits!
You are in a similar situation as a newcomer with the habit
of slow-looping more sequential code-execution. uh ! what is this !?!?!?

And this is

normal

And I want to explain why:

This example-code of blink without delay() is an example
without emphasising on the fundamental difference how this code works.
And 95% of the difficulties come from

not

explaining this difference.
As long as you try to see a delay()-similar thing in the BWD-code
you almost

must

fail!

because it really really works different!

The fundamental difference in very short words (still not explaining it all) is
delay() is executed a single time by blocking all code-execution

non-blocking timing based on function millis() is a very often

repeated

compairing of how much time has passed by including
fully intended hundreds_of_thousand-times looping through the code

I wrote a tutorial about this with an everyday example that a lot of people are familiar with
Whatever questions you have about this tutorial I'm very interested in infinite improving this
tutorial to make it easier to understand for newcomers.

best regards Stefan

As requested.... but a lot of the code uses references to other external devices to the Arduino. This is currently taking into account my hacked together, void flashCandles, that does do millis. Not well.... but it does work.

#include <SPI.h>
#include <Ethernet.h>
#include <Wire.h>
#include <aREST.h>
#include <avr/wdt.h>

#define debug

// CONSTANTS
const byte numCandles = 4;
// Define the GPIO pins connected to the anode of each candle's LED
// To create flickering effect, this pin must support PWM (UNO/Nano pins 3, 5, 6, 9, 10 and 11)
const byte ledPins[] = {3, 4, 5, 6};
// Define the pins connected to the sound sensor inputs. These can be any GPIO pins.
const byte inputPins[] = {22, 23, 24, 25};
// interval at which to blink (milliseconds)
const long interval = 1000;
// Define the order in which candles should be blown out (Will be candles 1, 3, 2, 4
byte order[] = {0, 0, 0, 0};


// constants won't change:


// GLOBALS
// What step of the sequence is the player currently on?
int currentStep = 0;
unsigned long previousMillis = 0;  // will store last time LED was updated
bool isLit[numCandles];
int mistakesMade = 0;
bool gameStart = false;
bool isGameSolved = false;
bool gameCodeSent = false;
String nextGameCode = "";
int candleBlink = 0;
int gameStatus = 0; // 0=default, 1=startup. 2=start, 3=solved, 4=done, 5=shutdown

String ones;
String tens;
String hund;
String thou;

// aREST to NodeRed
int HTTP_PORT = 1880;
String HTTP_METHOD = "GET";
char HOST_NAME[] = "172.16.40.100";
String PATH_NAME = "/api";
String queryString = "";

// Create aREST instance
aREST rest = aREST();

// The MAC Address and IP for the game
byte mac[] = {
  0xDE, 0xAD, 0x02, 0x00, 0x00, 0x09
};

IPAddress ip(172, 16, 40, 109);

EthernetServer server(80);
EthernetClient client;
EthernetClient client2;

// Create Reset function
void(* resetFunc) (void) = 0; //declare reset function @ address 0

void setup() {
  // Open serial communications and wait for port to open:
#ifdef debug
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
#endif

  // Initialize serial communication
  Serial.println(__FILE__);
  Serial.println("Compiled: " __DATE__ ", " __TIME__);

  // start the Ethernet connection:
  Ethernet.begin(mac);

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
#ifdef debug
    Serial.println(F("Ethernet shield was not found.  Sorry, can't run without hardware. :("));
#endif
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }

#ifdef debug
  Serial.println(F("Attempting to obtain IP from DHCP, stand by..."));
#endif

  delay(500);

  if (Ethernet.begin(mac) == 0) {
    // Failed to configure ethernet using DHCP, reverting to static IP
#ifdef debug
    Serial.print(F("Failed to obtain DHCP address, reverting to "));
    Serial.println(ip);
#endif
    Ethernet.begin(mac, ip);
  } else {
    // Obtained DHCP address
#ifdef debug
    Serial.print(F("Received IP: "));
    Serial.println(Ethernet.localIP());
#endif
  }

  while (Ethernet.linkStatus() == LinkOFF) {
#ifdef debug
    Serial.println(F("Ethernet cable is not connected."));
#endif
    delay(500);
  }

  if (client.connect(HOST_NAME, HTTP_PORT)) {
    NodeRedUpdate("?game=candle&action=startup");
    gameStatus = 1;
  } else {
#ifdef debug
    Serial.println(F("Failed connecting to NodeRed Server"));
#endif
  }

  rest.function("favicon.ico", favIcon);
  rest.function("gameState", setGameState);
  rest.function("gameReset", setGameReset);
  rest.function("gameOrder", apiGameOrder);
  rest.function("giveCode", giveNextCode);
  rest.function("flameOn", setFlameOn);
  rest.function("candles", setCandles);
  rest.function("api", returnAPI);
  rest.set_id("011");
  rest.set_name("Epicenter_Candle");

  delay(50);

  // Initialise the input pins that have switches attached
  for (int i = 0; i < numCandles; i++) {
    pinMode(inputPins[i], INPUT);
    pinMode(ledPins[i], OUTPUT);
  }

  // Reset the puzzle to its default state
  reset();

#ifdef debug
  Serial.println(F("Ready for REST API."));
#endif
}

void reset() {

  // (Re)light all the candles (only if gameStart is True)
  if (gameStatus  == 2) {
    for (int i = 0; i < numCandles; i++) {
      isLit[i] = true;
      digitalWrite(ledPins[i], HIGH);
      delay(100);
    }
  }
  // Reset the counters
  currentStep = 0;
  mistakesMade = 0;

  if (gameStatus == 2) {
#ifdef debug
    Serial.println(F("Puzzle Reset!"));
#endif
    delay(1000);
  }
  if (isGameSolved == true) {
#ifdef debug
    Serial.println(F("Puzzle Solved, triggered in reset command."));
#endif
  }
}

void loop() {
  EthernetClient client = server.available();
  rest.handle(client);

  unsigned long currentMillis = millis();

  // check if isGameSolved is true
  if (nextGameCode != "") {
#ifdef debug
    Serial.println(F("Game Solved, triggered in loop."));
#endif
    giveNextCode(nextGameCode);

  }

  // check to see if gameCode has been sent, if so, then skip immediately there.
  if (gameCodeSent == true) {

  }
  if (gameStatus == 2) {
    // Loop through all the inputs
    for (int i = 0; i < numCandles; i++) {

      // Is this candle currently still alight?
      if (isLit[i]) {

        // Make the LED flicker
        analogWrite(ledPins[i], random(128, 255));

        // Is this candle being blown?
        if (digitalRead(inputPins[i]) == LOW) {

          // Simple debounce to guard against false readings
          // Wait for short amount of time
          delay(20);
          // And check whether the candle is *still* being blown
          if (digitalRead(inputPins[i]) == LOW) {

            // Extinguish the flame
            isLit[i] = false;
            digitalWrite(ledPins[i], LOW);

            // Was this the correct candle to blow out next in the sequence?
            if (order[currentStep] == i) {
#ifdef debug
              Serial.print(F("Candle #"));
              Serial.print((i + 1));
              Serial.println(F(" correctly extinguished!"));
#endif
              delay(250);
            }
            else {
#ifdef debug
              Serial.print(F("Candle #"));
              Serial.print((i + 1));
              Serial.print(F(" incorrectly extinguished!"));
              Serial.print(F(" (should have been "));
              Serial.print(order[(currentStep + 1)]);
              Serial.println(")");
#endif

              mistakesMade++;
              // Uncomment the following line if you'd like the puzzle to reset as
              // soon as one mistake is made
              // reset();
            }

            // Advance to the next step
            currentStep++;
          }
        }
      }
    }
    // If all the candles have been blown out
    if (currentStep == numCandles) {

      // If any candles were blown out in the wrong order, reset the puzzle
      if (mistakesMade > 0) {

        // send NodeRed command to play ERROR tone
        delay(5000);

        // reset Candles
        reset();
      }
      else {
        // Otherwise, the puzzle has been solved!
        gameSolved();
      }
    }
  }
}

// aREST COMMANDS

int returnAPI(String apiString) {
#ifdef debug
  Serial.println(F("API received."));
#endif
}

int favIcon() { // fixes browser double call of functions
}

void apiGameOrder(String receivedOrder) {
#ifdef debug
  Serial.print("Solve Order received: ");
  Serial.println(receivedOrder);
#endif
  ones = receivedOrder.substring(0, 1);
  tens = receivedOrder.substring(1, 2);
  hund = receivedOrder.substring(2, 3);
  thou = receivedOrder.substring(3, 4);
  order[0] = ones.toInt();
  order[1] = tens.toInt();
  order[2] = hund.toInt();
  order[3] = thou.toInt();
}

int setFlameOn() {
  gameStart = true;
  gameStatus = 2;

  reset();
}

int setGameState(String gameState) {
  if (gameState == "start") {
#ifdef debug
    Serial.println(F("Remotely Triggered: Game Begin."));
#endif

    NodeRedUpdate("?game=candle&action=start");
  } else if (gameState == "solved") {
#ifdef debug
    Serial.println(F("Remotely Triggered: Game Solved."));
#endif
    gameStatus = 3;
    gameSolved();
    NodeRedUpdate("?game=candle&action=solved");
  } else if (gameState == "done") {
#ifdef debug
    Serial.println(F("Remotely Triggered: Game Done."));
#endif
    gameStart = false;
    isGameSolved = false;
    gameCodeSent = false;
    nextGameCode = "";
    for (int i = 0; i < numCandles; i++) {
      digitalWrite(ledPins[i], LOW);
    }
    NodeRedUpdate("?game=candle&action=done");
  }
}

void setCandles(bool OnOffManual) {
  if (OnOffManual == true) {
    for (int i = 0; i < numCandles; i++) {
      digitalWrite(ledPins[i], HIGH);
    }
  } else if (OnOffManual == false) {
    for (int i = 0; i < numCandles; i++) {
      digitalWrite(ledPins[i], LOW);
    }
  }
}

void setGameReset() {
#ifdef debug
  Serial.println(F("Remotely Triggered: Game Reset."));
#endif

  gameStart = false;
  isGameSolved = false;
  gameCodeSent = false;
  nextGameCode = "";
  for (int i = 0; i < numCandles; i++) {
    digitalWrite(ledPins[i], LOW);
  }
  resetFunc(); // software reset button
}

void gameSolved() {
#ifdef debug
  Serial.println(F("Puzzle Solved!"));
#endif
  NodeRedUpdate("?game=candle&action=solved");
  gameStart = false;
  isGameSolved = true;
  gameStatus = 3;
  delay(5000);

  //Trigger flashing of Candles for codes
  for (int i = 0; i < numCandles; i++) {
    digitalWrite(ledPins[i], HIGH);
  }
#ifdef debug
  Serial.println(F("Start Flicker"));
#endif
  for (int f = 0; f < 250; f++) {
    for (int i = 0; i < numCandles; i++) {
      analogWrite(ledPins[i], random(128, 255));
      delay(10);
    }
  }
#ifdef debug
  Serial.println(F("Stop Flicker"));
#endif

  for (int i = 0; i < numCandles; i++) {
    digitalWrite(ledPins[i], LOW);
  }
  delay(100);

#ifdef debug
  Serial.println(F("Waiting for Game Code."));
#endif
  delay(500);
  reset();
}

void giveNextCode(String GameCodeReceived) {
  EthernetClient client = server.available();
  rest.handle(client);

  nextGameCode = GameCodeReceived;
  gameCodeSent = true;
  int GameCode = GameCodeReceived.toInt();
#ifdef debug
  Serial.print("Game Code received: ");
  Serial.println(GameCodeReceived);
#endif

  flashCandles(GameCode / 1000);
  for (int x = 0; x < 500; x++) {
    delay(2);
    rest.handle(client);
  }
  flashCandles((GameCode / 100) % 10);
  for (int x = 0; x < 500; x++) {
    delay(2);
    rest.handle(client);
  }
  flashCandles((GameCode / 10) % 10);
  for (int x = 0; x < 500; x++) {
    delay(2);
    rest.handle(client);
  }
  flashCandles(GameCode % 10);
  for (int x = 0; x < 1500; x++) {
    delay(2);
    rest.handle(client);
  }
}

int flashCandles(int CodeNumber) {
#ifdef debug
  Serial.print("Candle Flash: ");
  Serial.println(CodeNumber);
#endif
  bool digitDone = false;
  bool flashDone = false;
  int flashState = 0; // 0 - 1st action, 1 - 2nd action, 2 - 3rd action, 3 - exit
  int n = 0;

  previousMillis = millis();
  
  while (digitDone == false) {
    unsigned long currentMillis = millis();

    while (flashDone == false) {
      EthernetClient client = server.available();
      rest.handle(client);
      
      unsigned long currentMillis = millis();
      
      if (flashState == 0) {
        if (currentMillis - previousMillis >= 100) {
          for (int c = 0; c < numCandles; c++) {
            digitalWrite(ledPins[c], LOW);
          }
          previousMillis = currentMillis;
          flashState = 1;
        }
      }
      if (flashState == 1) {
        if (currentMillis - previousMillis >= 250) {
          for (int c = 0; c < numCandles; c++) {
            digitalWrite(ledPins[c], HIGH);
          }
          previousMillis = currentMillis;
          flashState = 2;
        }
      }
      if (flashState == 2) {
        if (currentMillis - previousMillis >= 250) {
          for (int c = 0; c < numCandles; c++) {
            digitalWrite(ledPins[c], LOW);
          }
          previousMillis = currentMillis;
          flashState = 3;
        }
      }
      if (flashState == 3) {
        flashState = 0;
        n = n + 1;
        if (n < CodeNumber) {
          digitDone = true;
        } else {
          flashDone = true;
        }
      }
    }

    if (currentMillis - previousMillis >= 1000) {
      previousMillis = currentMillis;
      digitDone = true;
    }
  }
}

int NodeRedUpdate(String query) {
  if (client.connect(HOST_NAME, HTTP_PORT)) {
    client.println(HTTP_METHOD + " " + PATH_NAME + query + " HTTP/1.1");
    client.println("Host: " + String(HOST_NAME));
    client.println(F("Connection: close"));
    client.println();

    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        //Serial.print(c);
      }
    }

    client.stop();
    //Serial.println(F("disconnected"));
  } else {
    //Serial.println(F("connection failed"));
  }
}

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