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"));
}
}