Hi all,
I'm looking for someone that can push me a bit in the right direction. My goal is to make a game with falling objects at random time and order so that my kids can catch them. The idea is to control 10 electromagnets trough a relais. With a push button, the game difficulty can be chosen. This part is working.
Next to that, I would like to use some NeoPixel rings to energize the game a bit.
I'm struggeling to replace the "delay" function to the millis() logic that I find everywhere so that the game and the NeoPixel can both run at the same time.
I'm especially struggeling with the "RunGame" void. It is possible that there is some dead code in my sketch. Still going trough it with trail and error and trying a lot of ideas from the forum.
Below my current code. Any help is appreciated!
//Sketch to control falling pipes game, including 3 buttons; easy (long time), hard (short time) and reset. Including some NeoPixel rings to generate some attraction.
//used examples;
// Random pipe order from https://forum.arduino.cc/t/shuffle-numbers-when-button-is-pressed/675503/7
//NeoPixel; https://forum.arduino.cc/t/replacing-delay-with-millis-in-rainbowcycle-function/634554/7
//variabelen nodig voor timers
unsigned long prevTimeT1 = millis(); // Deze is voor de knop LED te laten knipperen. Voor iedere actie met een tijdsfactor moet deze regel herhaald worden
unsigned long CurrentTime = millis(); // Deze is voor de timer van de buizen vallen (current time)
unsigned long prevTimeT2 = millis(); // Deze is voor de timer van de buizen vallen (previous time)
unsigned long patternInterval = 20; // time between steps in the pattern
unsigned long lastUpdate = 0; // for millis() when last update occurred
unsigned long intervals[] = { 20, 20, 50, 100, 30 }; // speed for each pattern - add here when adding more cases
int fadeStep = 0; // state variable for fade function
int numberOfCases = 4; // how many case statements or patterns you have
//variabelen voor random generators
long RandTime; //delay value number
long randPipe; //Pipenummer uit random generator
//array buildup for pipe randomizer
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
const int arraySize = sizeof array / sizeof array[0];
int remainingNumbers = 0;
int Level = 0; //moeilijkheidsgraad
//Input pins
#define Btn_Easy 20
#define Btn_Hard 21
//Resetbutton on Resetpin
//Output to relais
#define Pipe_1 2
#define Pipe_2 3
#define Pipe_3 4
#define Pipe_4 5
#define Pipe_5 6
#define Pipe_6 7
#define Pipe_7 8
#define Pipe_8 9
#define Pipe_9 10
#define Pipe_10 11
#define Btn_LED_Easy 14
#define Btn_LED_Hard 15
#define Btn_LED_Reset 16
int ledStateBtn = LOW; // ledState used to set the LED van drukknop
int ledStateRst = LOW; // ledState used to set the LED van resetknop
//NeoPixel setup
#define PINforControl 12 //Neopixel pin with PWM
#define NUMPIXELS1 12 // number of LEDs on strip
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip(NUMPIXELS1, PINforControl, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
int getRandomEntry() {
if (remainingNumbers == 0)
remainingNumbers = arraySize; // Start over with a full array
randomSeed(analogRead(A0)); // Pick a number from those remaining
int index = random(remainingNumbers); // 0 to remainingNumbers - 1
int returnValue = array[index];
if (index < remainingNumbers - 1) // Swap the chosen number with the number at the end of the current array (unless the chosen entry is already at the end)
{
array[index] = array[remainingNumbers - 1]; // Move the last entry into the chosen index
array[remainingNumbers - 1] = returnValue; // Move the value from the chosen index into the last entry
}
remainingNumbers--; // Shorten the list so the picked number is not picked again
return returnValue;
}
void setup() {
Serial.begin(9600);
//Neopixel startup;
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
wipe();
strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
pinMode(LED_BUILTIN, OUTPUT); //onboard LED
pinMode(20, INPUT_PULLUP);
pinMode(21, INPUT_PULLUP);
pinMode(Pipe_1, OUTPUT);
pinMode(Pipe_2, OUTPUT);
pinMode(Pipe_3, OUTPUT);
pinMode(Pipe_4, OUTPUT);
pinMode(Pipe_5, OUTPUT);
pinMode(Pipe_6, OUTPUT);
pinMode(Pipe_7, OUTPUT);
pinMode(Pipe_8, OUTPUT);
pinMode(Pipe_9, OUTPUT);
pinMode(Pipe_10, OUTPUT);
//fade(0, 255, 0, 64, 0, 0, 400); // fade from black to orange and back
colorWipeDelay(strip.Color(255, 50, 0), 100); // Orange
AllMagnetsOn(); //Alle magneten inschakelen
//fade(0, 0, 0, 255, 0, 0, 400); // fade from black to green and back
colorWipeDelay(strip.Color(0, 255, 0), 100); // Green
delay(2000);
wipe(); // off
Serial.println("Ready");
}
void loop() {
patternInterval = 20;
if (millis() - lastUpdate > patternInterval)
;
rainbow(); // Flowing rainbow cycle along the whole strip; This blocks the Button read loop because there is a delay within the function
if (digitalRead(Btn_Easy) == LOW) { // Als knop "Gemakkelijk" wordt ingedrukt; set moeilijkheid naar gemakkelijk en start spel
Level = 10;
Serial.println("Button Easy is pressed");
BtnLedEasyPuls();
rainbowCycle(); // Rainbow-enhanced theaterChase variant; this one blocks the rest of the programm until finished. I would like this one to continue running during the game.
RunGame();
AllMagnetsOn();
colorWipeDelay(strip.Color(0, 255, 0), 100); // Green
colorWipeDelay(strip.Color(0, 0, 0), 100); // Off
}
if (digitalRead(Btn_Hard) == LOW) { //als knop "Moeilijk" wordt ingedrukt; set moeilijkheid naar Moeilijk en start spel
Level = 3;
Serial.println("Button Hard is pressed");
BtnLedHardPuls();
theaterChaseRainbow();
RunGame();
AllMagnetsOn();
colorWipe(strip.Color(0, 255, 0)); // Green
colorWipe(strip.Color(0, 0, 0)); // Off
}
}
void AllMagnetsOn() { //insert a small delay to give relay unit some time to initialize all relays one at a time to prevent current peak.
digitalWrite(Pipe_1, LOW);
delay(50);
digitalWrite(Pipe_2, LOW);
delay(50);
digitalWrite(Pipe_3, LOW);
delay(50);
digitalWrite(Pipe_4, LOW);
delay(50);
digitalWrite(Pipe_5, LOW);
delay(50);
digitalWrite(Pipe_6, LOW);
delay(50);
digitalWrite(Pipe_7, LOW);
delay(50);
digitalWrite(Pipe_8, LOW);
delay(50);
digitalWrite(Pipe_9, LOW);
delay(50);
digitalWrite(Pipe_10, LOW);
delay(500);
}
void RunGameWithSerialAndDelay() { //works, but not together with NeoPixel routine, this void is now not in use, but remains in the code until the millis() void works
for (int i = 0; i < arraySize; i++) {
randomSeed(analogRead(A0));
RandTime = random(100, 1000);
randPipe = getRandomEntry();
delay(RandTime * Level);
Serial.print("RandTime: ");
Serial.println(RandTime);
Serial.print("Delay value: ");
Serial.println(RandTime * Level);
Serial.print("Pipe nr: ");
Serial.println(randPipe);
Serial.print("Pin nr: ");
Serial.println(randPipe + 2);
Serial.println();
digitalWrite(randPipe + 2, HIGH);
}
Serial.println("Finished");
}
void RunGame() {
for (int i = 0; i < arraySize; i++) {
CurrentTime = millis();
randomSeed(analogRead(A0));
RandTime = random(100, 1000);
randPipe = getRandomEntry();
if (CurrentTime - prevTimeT2 > RandTime) {
digitalWrite(randPipe + 2, HIGH);
prevTimeT2 = CurrentTime;
}
}
}
void BtnLedEasyPuls() {
unsigned long currentTime = millis();
if (currentTime - prevTimeT1 > 200) {
prevTimeT1 = currentTime;
// if the LED is off turn it on and vice-versa:
if (ledStateBtn == LOW) {
ledStateBtn = HIGH;
ledStateRst = LOW;
} else {
ledStateBtn = LOW;
ledStateRst = HIGH;
}
// set the LED with the ledState of the variable:
digitalWrite(Btn_LED_Easy, ledStateBtn);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
}
void BtnLedHardPuls() {
unsigned long currentTime = millis();
if (currentTime - prevTimeT1 > 200) {
prevTimeT1 = currentTime;
// if the LED is off turn it on and vice-versa:
if (ledStateBtn == LOW) {
ledStateBtn = HIGH;
ledStateRst = LOW;
} else {
ledStateBtn = LOW;
ledStateRst = HIGH;
}
// set the LED with the ledState of the variable:
digitalWrite(Btn_LED_Easy, ledStateBtn);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
}
void fade(int redStartValue, int redEndValue, int greenStartValue, int greenEndValue, int blueStartValue, int blueEndValue, int totalSteps) {
static float redIncrement, greenIncrement, blueIncrement;
static float red, green, blue;
static boolean fadeUp = false;
if (fadeStep == 0) { // first step is to initialise the initial colour and increments
red = redStartValue;
green = greenStartValue;
blue = blueStartValue;
fadeUp = false;
redIncrement = (float)(redEndValue - redStartValue) / (float)totalSteps;
greenIncrement = (float)(greenEndValue - greenStartValue) / (float)totalSteps;
blueIncrement = (float)(blueEndValue - blueStartValue) / (float)totalSteps;
fadeStep = 1; // next time the function is called start the fade
} else { // all other steps make a new colour and display it
// make new colour
red += redIncrement;
green += greenIncrement;
blue += blueIncrement;
// set up the pixel buffer
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, strip.Color((int)red, (int)green, (int)blue));
}
// now display it
strip.show();
fadeStep += 1; // go on to next step
if (fadeStep >= totalSteps) { // finished fade
if (fadeUp) { // finished fade up and back
fadeStep = 0;
return; // so next call re-calabrates the increments
}
// now fade back
fadeUp = true;
redIncrement = -redIncrement;
greenIncrement = -greenIncrement;
blueIncrement = -blueIncrement;
fadeStep = 1; // don't calculate the increments again but start at first change
}
}
}
void rainbow() { // modified from Adafruit example to make it a state machine
static uint16_t j = 0;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i + j) & 255));
}
strip.show();
j++;
if (j >= 256) j = 0;
lastUpdate = millis(); // time for next change to the display
}
void rainbowCycle() { // modified from Adafruit example to make it a state machine
static uint16_t j = 0;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
}
strip.show();
j++;
if (j >= 256 * 5) j = 0;
lastUpdate = millis(); // time for next change to the display
}
void theaterChaseRainbow() { // modified from Adafruit example to make it a state machine
static int j = 0, q = 0;
static boolean on = true;
if (on) {
for (int i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, Wheel((i + j) % 255)); //turn every third pixel on
}
} else {
for (int i = 0; i < strip.numPixels(); i = i + 3) {
strip.setPixelColor(i + q, 0); //turn every third pixel off
}
}
on = !on; // toggel pixelse on or off for next time
strip.show(); // display
q++; // update the q variable
if (q >= 3) { // if it overflows reset it and update the J variable
q = 0;
j++;
if (j >= 256) j = 0;
}
lastUpdate = millis(); // time for next change to the display
}
void colorWipe(uint32_t color) { // modified from Adafruit example to make it a state machine
static int i = 0;
strip.setPixelColor(i, color);
strip.show();
i++;
if (i >= strip.numPixels()) {
i = 0;
wipe(); // blank out strip
}
lastUpdate = millis(); // time for next change to the display
}
void colorWipeDelay(uint32_t c, uint8_t wait) { // Fill the dots one after the other with a color
for (uint16_t i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
void wipe() { // clear all LEDs
for (int k = 0; k < strip.numPixels(); k++) {
strip.setPixelColor(k, strip.Color(0, 0, 0));
}
}
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}