Given
I should have said
I only noticed because changing the define did not... change the pin. It's just gonna use 20 no matter, it was misleading.
a7
Given
I should have said
I only noticed because changing the define did not... change the pin. It's just gonna use 20 no matter, it was misleading.
a7
I have posted the complete sketch. Please refer to the below link for the Arduino simulator.
Thank you! I have reviewed your solution, and below is some code and a link to it nuder simulation.
I increased the min and max times because I thought it would finally make the hammers drop, slowly one by one and so forth.
I also had to remove
// timeDelay = 500000UL;
the default time delay setting, as it bumped off any new delay time you set earlier.
We finally saw how your RunGame() functions. It does not block, rather every time it is called, it scans all N number of timers to see which, if any, have expired, and drops a hammer if it has. It then reschedules that hammer index.
Sry, that was so strange a solution to the OP's problem that I c0uld not read it as anything but a failed attemtp to capture the processor in RunGame() for the duration of one. Game.
I credit a member of the Umbrella Academy for figuring that out - we hooked up some instruments and found RunGame() being called many times a second, performing the entire for loop every time, usually doing nothing N times.
Your code does not block. Neither does it do anything like what the OP required:
Rather you are dropping hammer X every so often, and all the others every so often. As a result, a given hammer might be dropped again before all the others have been dropped. As a result, there may be a very short elapsed time during which several hammers are dropped.
Different game entirely. Different kind of fun.
Which is why the real RunGame() needs but one timer. How long to wait before dropping the next hammer. And why it has a mechanism for randomizing N hammers and doling them all out until it finishes. Which is another thing your sketch does not ever do. Finich.
This
4116 dropping 7 delay 3246 to next
4884 dropping 4 delay 2520 to next
5488 dropping 2 delay 1449 to next
6031 dropping 1 delay 4802 to next
6325 dropping 6 delay 1832 to next
6438 dropping 0 delay 4869 to next
6604 dropping 5 delay 2173 to next
6938 dropping 2 delay 5739 to next
7362 dropping 7 delay 4089 to next
shows hammer 7 being dropped, and scheduling its next drop, so at 4116 milliseconds into the run it drops, then again at 4116 + 3246 = 7362 into the run. No sign of hammer at indices 3 or 8 in between. But plenty of hammers dropping.
See it multitasking here in the wokwi simulator.
/*
Solution For:
Topics: Combine multiple millis() to control relais and neopixel simultanious
Category: Programming Questions
Link: https://forum.arduino.cc/t/combine-multiple-millis-to-control-relais-and-neopixel-simultanious/1099091
Sketch: AuCP_ RandomPipes.ino
Created: 11-Mar-2023
MicroBeaut (μB)
changed for changes 10-Mar-2023?! @alto777
https://wokwi.com/projects/358876197860297729
*/
# include <Adafruit_NeoPixel.h>
# define PIN 8 //Neopixel pin with PWM
# define NUMPIXELS 12 // number of LEDs on strip
Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
class TimerOn {
private:
bool prevInput;
bool state;
unsigned long startTime;
unsigned long timeDelay;
public:
TimerOn() {
prevInput = false;
state = false;
// timeDelay = 500000UL;
}
// Convert milliseconds to microseconds
void setTimeDelay(long msTimeDelay) {
timeDelay = msTimeDelay * 1000UL;
}
// Timer ON Condition
bool timerOn(bool input = true) {
if (input) {
if (!prevInput) {
startTime = micros();
} else if (!state) {
unsigned long elapsedTime = micros() - startTime;
if (elapsedTime >= timeDelay) {
elapsedTime = timeDelay;
state = true;
}
}
} else {
state = false;
}
prevInput = input;
return state;
}
bool done() {
return state;
}
};
#define RANDOM_MIN 1000L // Random Min x milliseconds
#define RANDOM_MAX 7000L // Random Mzx y milliseconds
const uint8_t numberOfPipes = 8; // Number of Pipes
TimerOn pipeTimerOns[numberOfPipes]; // Timer On objects
TimerOn myTO;
void setup() {
Serial.begin(115200);
Serial.println(" timer focus \n");
randomSeed(analogRead(0));
strip.begin();
// strip check - are we talking or what?
if (1) {
strip.setPixelColor(7, 0xffffff);
strip.show();
delay(777);
}
for (uint8_t index = 0; index < numberOfPipes; index++) {
long newDelay = random(RANDOM_MIN, RANDOM_MAX); //Initial Random Time n milliseconds
pipeTimerOns[index].setTimeDelay(newDelay); // Set delay time to object
}
}
unsigned long now;
void loop() {
now = millis();
RunGame();
rainbow();
}
void RunGame() {
for (uint8_t index = 0; index < numberOfPipes; index++) {
pipeTimerOns[index].timerOn(true); // True = Enable or Start, False = Disable or Stop or Reset
if (pipeTimerOns[index].done()) { // Timer On Done?
pipeTimerOns[index].timerOn(false); // Reset Timer On
// randomSeed(analogRead(0)); // Use an unconnected pin for randomSeed()
long newDelay = random(RANDOM_MIN, RANDOM_MAX); // New Random Time n milliseconds
pipeTimerOns[index].setTimeDelay(newDelay); // Set delay time to object
Serial.print(millis()); Serial.print(" dropping ");
Serial.print(index); Serial.print(" delay ");
Serial.print(newDelay); Serial.print(" to next");
Serial.println("");
}
}
}
// modified from Adafruit example to make it a state machine
void rainbow()
{
static uint16_t j = 0;
static unsigned long lastUpdate;
if (now - lastUpdate < 40)
return;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i + j) & 255));
}
strip.show();
j++;
if (j >= 256) j = 0;
lastUpdate = now; // time for next change to the display
}
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);
}
The TimerOn object seems like a long way to go for implementing a basic if-now-millis() paradigm, but hey! objects are fun, and yours works.
I do think, however, that anyone using it should know how it works, and that anyone who knows how it works probably wouldn't use it.
Thnks again for posting a complete sketch that compiles and functions. Sorry again for not seeing the cleverness which is your version, never mind it solves a different problem.
a7
Please do not change code and wokwis that you have posted here.
When you post a wokwi, lock that project and move on. Or at least move on, that is to say stop modifying it.
You could also post the code here, cheap and easy. That gives it the best chances of being meaningful to anyone who arrives here.
It is very odd to see code changing in front of my eyes!
Are you sticking with your game idea, or is it your plan to address that? Why did you jettison the Neopixel Ring? The OP came here specifically to get "two things at once", I added that so we can all "see" that happening. Fun!
Antway, new post new wokwi and sketches when you think it is moving slow enough. Please do not edit your upthread contributions!
I hope I have a version of your old code before I changed it.
I like the TimerOn object, the way it turns on / initialiases. It wasn't immediately obvious, and did baffle at first in the original demo.
a7
I cleaned up the code a bit and put 3 NeoPixel rings in the code (maybe not in the most efficient way, but it works).
Below the code that I'm going to put into the game. I will start construction soon and put the project when it's finished on instructables. At that time, I will post a link here so you can see the end-result.
A lot of thanks from me and my kids!
Danny
//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
//with many thanks to the help from the arduino forum and especially to alto777
//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 now; // Deze is voor de timer van de buizen vallen (current time)
int fadeStep = 0; // state variable for fade function
int counter;
//variabelen voor random generators
long nextTime; //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 PINforControl1 12 //Neopixel pin with PWM
#define PINforControl2 13 //Neopixel pin with PWM
#define PINforControl3 17 //Neopixel pin with PWM
#define NUMPIXELS1 12 // number of LEDs on strip
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip1(NUMPIXELS1, PINforControl1, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip1 object
Adafruit_NeoPixel strip2(NUMPIXELS1, PINforControl2, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip2 object
Adafruit_NeoPixel strip3(NUMPIXELS1, PINforControl3, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip3 object
int getRandomEntry() {
if (remainingNumbers == 0)
remainingNumbers = arraySize; // Start over with a full array
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);
randomSeed(analogRead(A0));
//Neopixel startup;
strip1.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip2.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip3.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
wipe();
//strip1.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);
//prepare for the game
colorWipeDelay(strip1.Color(255, 50, 0), 100); // Orange
colorWipeDelay(strip2.Color(255, 50, 0), 100); // Orange
colorWipeDelay(strip3.Color(255, 50, 0), 100); // Orange
AllMagnetsOn(); //Alle magneten inschakelen
colorWipeDelay(strip1.Color(0, 255, 0), 100); // Green
colorWipeDelay(strip2.Color(0, 255, 0), 100); // Green
colorWipeDelay(strip3.Color(0, 255, 0), 100); // Green
delay(2000);
colorWipeDelay(strip1.Color(0, 0, 0), 100); // Off
colorWipeDelay(strip2.Color(0, 0, 0), 100); // Off
colorWipeDelay(strip3.Color(0, 0, 0), 100); // Off
wipe(); // off
int counter; // Used to count how many pipes have dropped, when all pipes have dropped, game is finished.
Serial.println("Ready for first run");
}
void loop() {
now = millis();
rainbow(); // Flowing rainbow cycle along the whole strip with NeoPixel; 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
delay(50); //debounce button
Level = 10;
Serial.println("Button Easy is pressed");
counter = 0;
while (counter < arraySize) // when counter = arraySize; exit loop
{
BtnLedEasyPuls();
rainbowCycle();
RunGame();
}
AllMagnetsOn(); // game is finished, reset for the next run
delay(100);
colorWipeDelay(strip1.Color(0, 255, 0), 100); // Neopixel Green ring to show that pipes can be put back in place
colorWipeDelay(strip2.Color(0, 255, 0), 100); // Neopixel Green ring to show that pipes can be put back in place
colorWipeDelay(strip3.Color(0, 255, 0), 100); // Neopixel Green ring to show that pipes can be put back in place
delay(500);
colorWipeDelay(strip1.Color(0, 0, 0), 100); // NeoPixel Off
colorWipeDelay(strip2.Color(0, 0, 0), 100); // NeoPixel Off
colorWipeDelay(strip3.Color(0, 0, 0), 100); // NeoPixel Off
}
if (digitalRead(Btn_Hard) == LOW) { //als knop "Moeilijk" wordt ingedrukt; set moeilijkheid naar Moeilijk en start spel //this code must yet be corrected based on the Btn_Easy code
delay(50); //debounce button
Level = 3;
Serial.println("Button Hard is pressed");
counter = 0;
while (counter < arraySize) // when counter = arraySize; exit loop
{
BtnLedHardPuls();
theaterChaseRainbow();
RunGame();
}
AllMagnetsOn(); // game is finished, reset for the next run
delay(100);
colorWipeDelay(strip1.Color(0, 255, 0), 100); // Neopixel Green ring to show that pipes can be put back in place
colorWipeDelay(strip2.Color(0, 255, 0), 100); // Neopixel Green ring to show that pipes can be put back in place
colorWipeDelay(strip3.Color(0, 255, 0), 100); // Neopixel Green ring to show that pipes can be put back in place
delay(500);
colorWipeDelay(strip1.Color(0, 0, 0), 100); // NeoPixel Off
colorWipeDelay(strip2.Color(0, 0, 0), 100); // NeoPixel Off
colorWipeDelay(strip3.Color(0, 0, 0), 100); // NeoPixel 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 RunGame() {
static unsigned long lastTime = 3000;
static int nextTime = random(100, 1000);
now = millis();
if (now - lastTime < (nextTime*Level))
return;
// select and drop random
randPipe = getRandomEntry();
digitalWrite(randPipe + 2, HIGH); // laat de honden los!
Serial.print("Random delay value: ");
Serial.println(nextTime * Level);
Serial.print("Pipe nr: ");
Serial.print(randPipe);
Serial.print(", Pin nr: ");
Serial.println(randPipe + 2);
Serial.print("Pipe counter value; ");
Serial.print(counter);
Serial.print(", array size; ");
Serial.println(arraySize);
Serial.println();
// schedule
nextTime = random(100, 1000) ;
lastTime = now;
counter++; //counts every time a pipe has dropped
}
void BtnLedEasyPuls() {
unsigned long now = millis();
if (now - prevTimeT1 > 200) {
prevTimeT1 = now;
// 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 now = millis();
if (now - prevTimeT1 > 200) {
prevTimeT1 = now;
// 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 rainbow() { // modified from Adafruit example to make it a state machine
static uint16_t j = 0;
static unsigned long lastUpdate;
if (now - lastUpdate < 15)
return;
for (int i = 0; i < strip1.numPixels(); i++) {
strip1.setPixelColor(i, Wheel((i + j) & 255));
strip2.setPixelColor(i, Wheel((i + j) & 255));
strip3.setPixelColor(i, Wheel((i + j) & 255));
}
strip1.show();
strip2.show();
strip3.show();
j++;
if (j >= 256) j = 0;
lastUpdate = now; // time for next change to the display
}
void rainbowCycle() { // modified from Adafruit example to make it a state machine
static uint16_t j = 0;
static unsigned long lastUpdate2;
if (now - lastUpdate2 < 20)
return;
for (int i = 0; i < strip1.numPixels(); i++) { //moet dit ook nog worden opgebouwd voor strip 2 en 3?
strip1.setPixelColor(i, Wheel(((i * 256 / strip1.numPixels()) + j) & 255));
strip2.setPixelColor(i, Wheel(((i * 256 / strip2.numPixels()) + j) & 255));
strip3.setPixelColor(i, Wheel(((i * 256 / strip3.numPixels()) + j) & 255));
}
strip1.show();
strip2.show();
strip3.show();
j++;
if (j >= 256 * 5) j = 0;
lastUpdate2 = now; // 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;
static unsigned long lastUpdate3;
if (now - lastUpdate3 < 30)
return;
if (on) {
for (int i = 0; i < strip1.numPixels(); i = i + 3) { //moet dit ook nog worden opgebouwd voor strip 2 en 3?
strip1.setPixelColor(i + q, Wheel((i + j) % 255)); //turn every third pixel on
strip2.setPixelColor(i + q, Wheel((i + j) % 255)); //turn every third pixel on
strip3.setPixelColor(i + q, Wheel((i + j) % 255)); //turn every third pixel on
}
} else {
for (int i = 0; i < strip1.numPixels(); i = i + 3) {
strip1.setPixelColor(i + q, 0); //turn every third pixel off
strip2.setPixelColor(i + q, 0); //turn every third pixel off
strip3.setPixelColor(i + q, 0); //turn every third pixel off
}
}
on = !on; // toggel pixelse on or off for next time
strip1.show(); // display
strip2.show(); // display
strip3.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;
}
lastUpdate3 = now; // 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 < strip1.numPixels(); i++) {
strip1.setPixelColor(i, c);
strip2.setPixelColor(i, c);
strip3.setPixelColor(i, c);
strip1.show();
strip2.show();
strip3.show();
delay(wait);
}
}
void wipe() { // clear all LEDs
for (int k = 0; k < strip1.numPixels(); k++) {
strip1.setPixelColor(k, strip1.Color(0, 0, 0));
strip2.setPixelColor(k, strip2.Color(0, 0, 0));
strip3.setPixelColor(k, strip3.Color(0, 0, 0));
}
}
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip1.Color(255 - WheelPos * 3, 0, WheelPos * 3);
return strip2.Color(255 - WheelPos * 3, 0, WheelPos * 3);
return strip3.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip1.Color(0, WheelPos * 3, 255 - WheelPos * 3);
return strip2.Color(0, WheelPos * 3, 255 - WheelPos * 3);
return strip3.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip1.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
return strip2.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
return strip3.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
If you bother with making constants manifest
Then you should use them, not like this
But like this
pinMode(Btn_Easy, INPUT_PULLUP);
pinMode(Btn_Hard, INPUT_PULLUP);
The idea being that moving these buttons to alternate I/O pins we have one place to make one change.
Same with any number used all over the place. I only noticed when changing the constants at the top did not fix the problem - I was running the cod on a different board...
I look forward to seeing the results. Did you ever describe how this game works, how it is played, scored and is there a winner?
a7
There's some parts of your code that doesn't makes sense.
This part is redundant:
void wipe() { // clear all LEDs
for (int k = 0; k < strip1.numPixels(); k++) {
strip1.setPixelColor(k, strip1.Color(0, 0, 0));
strip2.setPixelColor(k, strip2.Color(0, 0, 0));
strip3.setPixelColor(k, strip3.Color(0, 0, 0));
}
}
Can be replaced by:
strip1.clear();
strip2.clear();
strip3.clear();
strip1.show();
strip2.show();
strip3.show();
I think that this function has some errors.
void BtnLedHardPuls() {
digitalWrite(Btn_LED_Easy, ledStateBtn);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
You are checking the status of hard button but is changing the state of easy LED.
It should be:
void BtnLedHardPuls() {
digitalWrite(Btn_LED_Hard, ledStateBtn);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
I don't know why you are changing the status of reset LED since reset button is NOT checked in any part of your code.
void BtnLedEasyPuls() {
digitalWrite(Btn_LED_Easy, ledStateBtn);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
void BtnLedHardPuls() {
digitalWrite(Btn_LED_Easy, ledStateEasy);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
I believe that your functions should be like this:
void BtnLedEasyPuls() {
digitalWrite(Btn_LED_Easy, ledStateEasy);
digitalWrite(Btn_LED_Hard, ledStateHard);
}
void BtnLedHardPuls() {
digitalWrite(Btn_LED_Easy, ledStateEasy);
digitalWrite(Btn_LED_Hard, ledStateHard);
}
Also I can't see on code above the part where the state of the magnets is changed while playing the game.
Moreover the buttons doesn't change the difficult of the game just change the LED status.
Here your code with some improvements.
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <Bounce2.h>
#define CONTROL_BUTTONS 3
#define NUM_PIPES 10
#define INITIAL_LED_STATE LOW
#define INITIAL_PIPE_STATE LOW
Bounce2::Button button[CONTROL_BUTTONS] = {Bounce2::Button()};
struct INFOS
{
const byte buttonPin;
bool ledState;
const byte ledPin;
};
INFOS info[CONTROL_BUTTONS] =
{
{20, INITIAL_LED_STATE, 14},
{21, INITIAL_LED_STATE, 15},
{18, INITIAL_LED_STATE, 16},
};
const byte pipePins[NUM_PIPES] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
// NeoPixel setup
#define PINforControl1 12
#define PINforControl2 13
#define PINforControl3 17
#define NUMPIXELS1 16 // number of LEDs on strip
Adafruit_NeoPixel strip1(NUMPIXELS1, PINforControl1, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip1 object
Adafruit_NeoPixel strip2(NUMPIXELS1, PINforControl2, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip2 object
Adafruit_NeoPixel strip3(NUMPIXELS1, PINforControl3, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip3 object
unsigned long prevTimeT1 = millis(); // Deze is voor de knop LED te laten knipperen. Voor iedere actie met een tijdsfactor moet deze regel herhaald worden // Deze is voor de timer van de buizen vallen (current time)
int fadeStep = 0; // state variable for fade function
int counter;
byte mode = 0;
// variabelen voor random generators
long nextTime; // 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
int getRandomEntry();
uint32_t Wheel(byte WheelPos);
void colorWipeDelay(uint32_t c, uint8_t wait);
void rainbowCycle();
void rainbow();
void RunGame();
void AllMagnetsOff();
void AllMagnetsOn() ;
void theaterChaseRainbow();
void prepareGame();
int getRandomEntry()
{
random(micros());
if (remainingNumbers == 0)
{
remainingNumbers = arraySize; // Start over with a full array
}
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;
strip1.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip2.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip3.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip1.show();
strip2.show();
strip3.show();
// strip1.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
for (byte i = 0; i < NUM_PIPES; i++)
{
pinMode(pipePins[i], OUTPUT);
digitalWrite(pipePins[i], INITIAL_PIPE_STATE);
}
for (byte i = 0; i < CONTROL_BUTTONS; i++)
{
button[i].attach(info[i].buttonPin, INPUT_PULLUP);
button[i].setPressedState(LOW);
button[i].interval(5);
pinMode(info[i].ledPin, OUTPUT);
digitalWrite(info[i].ledPin, info[i].ledState);
}
prepareGame();
Serial.println("Ready for first run");
}
void loop()
{
rainbow(); // Flowing rainbow cycle along the whole strip with NeoPixel; This blocks the Button read loop because there is a delay within the function
for (byte i = 0; i < CONTROL_BUTTONS; i++)
{
button[i].update();
if (button[0].pressed()) // als knop "Gemakkelijk" wordt ingedrukt; set moeilijkheid naar gemakkelijk en start spel
{
Level = 10;
Serial.println("Button Easy is pressed");
counter = 0;
while (counter < arraySize) // when counter = arraySize; exit loop
{
button[0].update();
if (button[0].pressed())
{
Serial.println("Easy mode selected");
mode = 1;
info[0].ledState = HIGH;
info[1].ledState = LOW;
digitalWrite(info[0].ledPin, info[0].ledState);
digitalWrite(info[1].ledPin, info[1].ledState);
}
rainbowCycle();
RunGame();
}
prepareGame();
}
else if (button[1].pressed()) // als knop "Moeilijk" wordt ingedrukt; set moeilijkheid naar Moeilijk en start spel //this code must yet be corrected based on the Btn_Easy code
{
Level = 3;
Serial.println("Button Hard is pressed");
counter = 0;
while (counter < arraySize) // when counter = arraySize; exit loop
{
button[1].update();
if (button[1].pressed())
{
Serial.println("Hard mode selected");
mode = 2;
info[1].ledState = HIGH;
info[0].ledState = LOW;
digitalWrite(info[1].ledPin, info[1].ledState);
digitalWrite(info[0].ledPin, info[0].ledState);
}
theaterChaseRainbow();
RunGame();
}
prepareGame();
}
else if (button[2].pressed()) // Reset button
{
Serial.println("Reset button pressed");
info[2].ledState = !info[2].ledState;
digitalWrite(info[2].ledPin, info[2].ledState);
}
}
}
void AllMagnetsOn() // insert a small delay to give relay unit some time to initialize all relays one at a time to prevent current peak.
{
for (byte i = 0; i < NUM_PIPES; i++)
{
digitalWrite(pipePins[i], !INITIAL_PIPE_STATE);
delay(50);
}
}
void AllMagnetsOff()
{
for (byte i = 0; i < NUM_PIPES; i++)
{
digitalWrite(pipePins[i], INITIAL_PIPE_STATE);
}
}
void RunGame()
{
random(micros());
static unsigned long lastTime = 3000;
static unsigned int nextTime = random(100, 1000);
if ((millis() - lastTime) < (nextTime * Level))
{
return;
}
// select and drop random
randPipe = getRandomEntry();
digitalWrite(randPipe + 2, HIGH); // laat de honden los!
Serial.print("Random delay value: ");
Serial.println(nextTime * Level);
Serial.print("Pipe nr: ");
Serial.print(randPipe);
Serial.print(", Pin nr: ");
Serial.println(randPipe + 2);
Serial.print("Pipe counter value; ");
Serial.print(counter);
Serial.print(", array size; ");
Serial.println(arraySize);
Serial.println();
// schedule
nextTime = random(100, 1000);
lastTime = millis();
counter++; // counts every time a pipe has dropped
}
void prepareGame()
{
// prepare for the game
colorWipeDelay(strip1.Color(255, 50, 0), 100); // Orange
colorWipeDelay(strip2.Color(255, 50, 0), 100); // Orange
colorWipeDelay(strip3.Color(255, 50, 0), 100); // Orange
AllMagnetsOn(); // Alle magneten inschakelen
colorWipeDelay(strip1.Color(0, 255, 0), 100); // Green
colorWipeDelay(strip2.Color(0, 255, 0), 100); // Green
colorWipeDelay(strip3.Color(0, 255, 0), 100); // Green
delay(2000);
colorWipeDelay(strip1.Color(0, 0, 0), 100); // Off
colorWipeDelay(strip2.Color(0, 0, 0), 100); // Off
colorWipeDelay(strip3.Color(0, 0, 0), 100); // Off
}
void rainbow() // modified from Adafruit example to make it a state machine
{
static uint16_t j = 0;
static unsigned long lastUpdate;
if (millis() - lastUpdate < 15)
{
return;
}
for (unsigned int i = 0; i < strip1.numPixels(); i++)
{
strip1.setPixelColor(i, Wheel((i + j) & 255));
strip2.setPixelColor(i, Wheel((i + j) & 255));
strip3.setPixelColor(i, Wheel((i + j) & 255));
}
strip1.show();
strip2.show();
strip3.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;
static unsigned long lastUpdate2;
if (millis() - lastUpdate2 < 20)
{
return;
}
for (unsigned int i = 0; i < strip1.numPixels(); i++) // moet dit ook nog worden opgebouwd voor strip 2 en 3?
{
strip1.setPixelColor(i, Wheel(((i * 256 / strip1.numPixels()) + j) & 255));
strip2.setPixelColor(i, Wheel(((i * 256 / strip2.numPixels()) + j) & 255));
strip3.setPixelColor(i, Wheel(((i * 256 / strip3.numPixels()) + j) & 255));
}
strip1.show();
strip2.show();
strip3.show();
j++;
if (j >= 256 * 5)
{
j = 0;
}
lastUpdate2 = 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;
static unsigned long lastUpdate3;
if (millis() - lastUpdate3 < 30)
{
return;
}
if (on)
{
for (unsigned int i = 0; i < strip1.numPixels(); i = i + 3) // moet dit ook nog worden opgebouwd voor strip 2 en 3?
{
strip1.setPixelColor(i + q, Wheel((i + j) % 255)); // turn every third pixel on
strip2.setPixelColor(i + q, Wheel((i + j) % 255)); // turn every third pixel on
strip3.setPixelColor(i + q, Wheel((i + j) % 255)); // turn every third pixel on
}
}
else
{
for (unsigned int i = 0; i < strip1.numPixels(); i = i + 3)
{
strip1.setPixelColor(i + q, 0); // turn every third pixel off
strip2.setPixelColor(i + q, 0); // turn every third pixel off
strip3.setPixelColor(i + q, 0); // turn every third pixel off
}
}
on = !on; // toggel pixelse on or off for next time
strip1.show(); // display
strip2.show(); // display
strip3.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;
}
}
lastUpdate3 = 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 < strip1.numPixels(); i++)
{
strip1.setPixelColor(i, c);
strip2.setPixelColor(i, c);
strip3.setPixelColor(i, c);
strip1.show();
strip2.show();
strip3.show();
delay(wait);
}
}
uint32_t Wheel(byte WheelPos)
{
WheelPos = 255 - WheelPos;
if (WheelPos < 85)
{
return strip1.Color(255 - WheelPos * 3, 0, WheelPos * 3);
return strip2.Color(255 - WheelPos * 3, 0, WheelPos * 3);
return strip3.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170)
{
WheelPos -= 85;
return strip1.Color(0, WheelPos * 3, 255 - WheelPos * 3);
return strip2.Color(0, WheelPos * 3, 255 - WheelPos * 3);
return strip3.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip1.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
return strip2.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
return strip3.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
There's so much job to be done.
But like this
pinMode(Btn_Easy, INPUT_PULLUP);
pinMode(Btn_Hard, INPUT_PULLUP);
I understand and changed this part of the code. I can imagine that you run into issues when using another board.
Did you ever describe how this game works, how it is played, scored and is there a winner?
My thought at this moment is to number the pipes (1 - 10) and sum the numbers from the pipes that are cought and put into a bucket. Pipes that touch the ground are discarded. I'm thinking of some variation in the pipe length (pipe with 10 points is shorter than the pipe for 1 point). Pipes will be PVC with something like a squashball in the bottom and a cap with a metal ring on top to attach to the magnets.
Maybe do 3 runs and add the score up. This also helps the kids to have some practice with math ;-).
Something like that, but maybe we change the rules when there is a more fun way of playing.
Hi FernandoGarcia,
Thank you for you in-depth check of the code.
This part is redundant:
void wipe() { // clear all LEDs
for (int k = 0; k < strip1.numPixels(); k++) {
strip1.setPixelColor(k, strip1.Color(0, 0, 0));
strip2.setPixelColor(k, strip2.Color(0, 0, 0));
strip3.setPixelColor(k, strip3.Color(0, 0, 0));
}
}
Can be replaced by:
strip1.clear();
strip2.clear();
strip3.clear();
strip1.show();
strip2.show();
strip3.show();
That will give another visual; wipe shuts off the LED's 1 at a time, clear just shuts all LED's off at the same time. I prefer the wipe function visually.
I think that this function has some errors.
void BtnLedHardPuls() {
digitalWrite(Btn_LED_Easy, ledStateBtn);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
You are correct. That's a copy-paste error. That one is fixed now. I didn't notice it yet because I have not yet installed all electronics.
I don't know why you are changing the status of reset LED since reset button is NOT checked in any part of your code.
void BtnLedEasyPuls() {
digitalWrite(Btn_LED_Easy, ledStateBtn);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
void BtnLedHardPuls() {
digitalWrite(Btn_LED_Easy, ledStateEasy);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
My idea here is that the LED's in the push buttons are flashing during the game; with the easy game, easy and reset flash alternating. With the hard game, the LED in the hard and reset will be flashing. This part of the code might change. Maybe I remove the "prepare for next game" from the code and led the Reset button LED flash after the game is finished. The reset button is connected to the reset pin, so the code ensures that everything is set for start. And kids love to push flashing buttons
Also I can't see on code above the part where the state of the magnets is changed while playing the game.
Moreover the buttons doesn't change the difficult of the game just change the LED status.
This part takes place at the loop function;
void loop() {
now = millis();
rainbow(); // Flowing rainbow cycle along the whole strip with NeoPixel; 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
delay(50); //debounce button
Level = 10;
Serial.println("Button Easy is pressed");
counter = 0;
while (counter < arraySize) // when counter = arraySize; exit loop
{
BtnLedEasyPuls();
rainbowCycle();
RunGame();
}
The first void in the wile loop (BtnLedEasyPuls) makes the LED from the pushbutton flash,
The 2nd void in the while loop (rainbowCycle) makes the NeoPixel give a light show
The 3rd void (RunGame) lets the magnets randomly shutt off.
The loop keeps running over these lines and checking a few conditions until all pipes have dropped.
I do like your simulator on wokwi. I wasn't aware that it is possible to build with other components. I think your last code is doing something else than I would like it to do, but I'm planning to also build a simulation of the game in there for testing.
I am a newbie to Arduino and may need time to learn a new device like Neopixel Ring .
Now, I understand how this game works. I have revised the code by,
/*
Solution For:
Topics: Combine multiple millis() to control relais and neopixel simultanious
Category: Programming Questions
Link: https://forum.arduino.cc/t/combine-multiple-millis-to-control-relais-and-neopixel-simultanious/1099091
Sketch: AuCP_RandomPipes.ino (https://wokwi.com/projects/358817800677315585)
Created: 11-Mar-2023 (GMT+7)
MicroBeaut (μB)
10-Mar-2023: changed for changes 10-Mar-2023?!
https://wokwi.com/projects/358876197860297729
By: @alto777
12-Mar-2023: Changed,
- Modified TimerOn by adding getTimeDelay() function and renaming "done" to "isDone"
- Defined Pipe color status
- Added RandomCollector() subroutine for pipes
- Added Dropping() subroutine
https://wokwi.com/projects/359013222335749121
By: @MicroBeaut
*/
# include <Adafruit_NeoPixel.h>
# define PIN 8 // Neopixel pin with PWM
# define PIPESTS_PIN 9 // NeoPixel Pipe Status pin
# define NUMPIXELS 12 // number of LEDs on strip
Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
Adafruit_NeoPixel pipeStatus(NUMPIXELS, PIPESTS_PIN, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
# define PIPE_GREEN pipeStatus.Color(0, 255, 0) // Pipe Available
# define PIPE_RED pipeStatus.Color(255, 0, 0) // Pipe Dropping
# define PIPE_BLACK pipeStatus.Color(0, 0, 0) // Pipe Droped
class TimerOn {
private:
bool prevInput;
bool state;
unsigned long startTime;
unsigned long timeDelay;
public:
TimerOn() {
prevInput = false;
state = false;
// timeDelay = 500000UL; 500 microseconds
}
// Convert milliseconds to microseconds
void setTimeDelay(long msTimeDelay) {
timeDelay = msTimeDelay * 1000UL;
}
// Timer ON Condition
bool timerOn(bool input = true) {
if (input) {
if (!prevInput) {
startTime = micros();
} else if (!state) {
unsigned long elapsedTime = micros() - startTime;
if (elapsedTime >= timeDelay) {
// elapsedTime = timeDelay;
state = true;
}
}
} else {
state = false;
}
prevInput = input;
return state;
}
uint16_t getTimeDelay() {
return timeDelay / 1000UL;
}
bool isDone() {
return state;
}
};
#define RANDOM_MIN 1000L // Random Min x milliseconds
#define RANDOM_MAX 7000L // Random Max y milliseconds
#define BLINK_DELAY 500 // 500 microseconds
const uint8_t numberOfPipes = 12; // Number of Pipes
// TimerOn pipeTimerOns[numberOfPipes]; // Timer On objects
TimerOn pipeTON; // Pipe Drop Timer On
uint8_t pipeCollector[numberOfPipes]; // Pipe Index Collector
uint8_t pipeIndex; // Pipe Index
TimerOn blinkTON; // Blinking Timer On
bool toggleState;
//TimerOn myTO;
void RunGame();
void rainbow();
uint32_t Wheel(byte WheelPos);
void RandomCollector();
void Dropping();
void PrintPipeInfo();
void setup() {
Serial.begin(115200);
Serial.println(" timer focus \n");
randomSeed(analogRead(0));
strip.begin();
pipeStatus.begin();
// strip check - are we talking or what?
if (1) {
strip.setPixelColor(7, 0xffffff);
strip.show();
delay(777);
}
for (uint8_t index = 0; index < numberOfPipes; index++) {
// long newDelay = random(RANDOM_MIN, RANDOM_MAX); // Initial Random Time n milliseconds
// pipeTimerOns[index].setTimeDelay(newDelay); // Set delay time to object
pipeCollector[index] = index;
}
uint16_t newDelay = random(RANDOM_MIN, RANDOM_MAX); // Initial Random Time n milliseconds
blinkTON.setTimeDelay(BLINK_DELAY); // Set Blink Time Delay
pipeTON.setTimeDelay (newDelay); // Initial time delay for first pipe
RandomCollector(); // Randomize Pipe
pipeIndex = 0; // Initil Pipe Index
PrintPipeInfo(); // Print dropping info. for first pipe
toggleState = true;
}
unsigned long now;
void loop() {
now = millis();
Dropping();
RunGame();
rainbow();
}
void RunGame() {
if (pipeTON.timerOn(true)) {
pipeTON.timerOn(false); // Reset timer
uint16_t newDelay = random(RANDOM_MIN, RANDOM_MAX); // New Random Time n milliseconds
pipeTON.setTimeDelay(newDelay); // Set new Delay Time
pipeStatus.setPixelColor(pipeCollector[pipeIndex], PIPE_BLACK); // Pipe dropped
pipeIndex = (pipeIndex + 1) % numberOfPipes; // Increase Pipe Index modular with numberOfPipes
if (pipeIndex == 0) RandomCollector(); // Random for new game
pipeStatus.setPixelColor(pipeCollector[pipeIndex], PIPE_RED); // Set status for pipe
pipeStatus.show(); // Update NeoPixel
// for (uint8_t index = 0; index < numberOfPipes; index++) {
// pipeTimerOns[index].timerOn(true); // True = Enable or Start, False = Disable or Stop or Reset
// if (pipeTimerOns[index].isDone()) { // Timer On Done?
// pipeTimerOns[index].timerOn(false); // Reset Timer On
// randomSeed(analogRead(0)); // Use an unconnected pin for randomSeed()
// long newDelay = random(RANDOM_MIN, RANDOM_MAX); // New Random Time n milliseconds
// pipeTimerOns[index].setTimeDelay(newDelay); // Set delay time to object
PrintPipeInfo(); // Print dropping info.
// }
}
}
// modified from Adafruit example to make it a state machine
void rainbow()
{
static uint16_t j = 0;
static unsigned long lastUpdate;
if (now - lastUpdate < 40)
return;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i + j) & 255));
}
strip.show();
j++;
if (j >= 256) j = 0;
lastUpdate = now; // time for next change to the display
}
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);
}
void Dropping() {
if (blinkTON.timerOn(true)) {
blinkTON.timerOn(false);
toggleState = !toggleState;
if (toggleState) {
pipeStatus.setPixelColor(pipeCollector[pipeIndex], PIPE_RED);
} else {
pipeStatus.setPixelColor(pipeCollector[pipeIndex], PIPE_GREEN);
}
pipeStatus.show();
}
}
// Random Collector for pipes
void RandomCollector() {
for (uint8_t index = 0; index < numberOfPipes; index++) {
uint8_t randomNumber = random(index, numberOfPipes);
uint8_t temp = pipeCollector[index];
pipeCollector[index] = pipeCollector[randomNumber];
pipeCollector[randomNumber] = temp;
pipeStatus.setPixelColor(index, PIPE_GREEN);
}
pipeStatus.show();
}
void PrintPipeInfo() {
if (pipeIndex == 0) {
Serial.println("\nNew Game...");
}
Serial.print(millis()); Serial.print(" dropping ");
Serial.print(pipeCollector[pipeIndex]); Serial.print(" delay ");
Serial.print(pipeTON.getTimeDelay()); Serial.print(" mS to next");
Serial.println("");
}
And thank you for all of your feedback.
MicroBeaut (μB)
I did. The large difference to the OP's code is your freely running loop(). There really should not be any loops in the loop() function, certainly not ones that take any significant time.
That obvsly full speed loop allowed me to transport a hack I had developed elsewhere. See
It was hard-geared for 10 hammers, so I turned that down in the *.ino.
Remains: anything to do with starting and stopping a game, and doing that at a level of difficulty.
Also remains is taking a closer look at your code - TBH I just looked until I could install the display hammers stuff.
L8R
a7
Please see the main topics that have been changed,
TimerOn
object and the moveHammers
function by adding the `pause' parameter to support the Pause function. When the Pause function is added, the elapsed time will be paused.To increase the level, just put a Level simulator button .
Please click this link to run the Arduino emulator, and see the below code,
/*
Solution For:
Topics: Combine multiple millis() to control relais and neopixel simultanious
Category: Programming Questions
Link: https://forum.arduino.cc/t/combine-multiple-millis-to-control-relais-and-neopixel-simultanious/1099091
Sketch: AuCP_RandomPipes.ino (https://wokwi.com/projects/358817800677315585)
Created: 11-Mar-2023 (GMT+7)
MicroBeaut (μB)
10-Mar-2023: changed for changes 10-Mar-2023?!
https://wokwi.com/projects/358876197860297729
By: @alto777
12-Mar-2023: Changed,
- Modified TimerOn by adding getTimeDelay() function and renaming "done" to "isDone"
- Defined Pipe color status
- Added RandomCollector() subroutine for pipes
- Added Dropping() subroutine
https://wokwi.com/projects/359013222335749121
By: @MicroBeaut
12-Mar-2023: added hack hammer display machinery 23-Mar-2023
https://wokwi.com/projects/359050337059862529
By: @alto777
13-Mar-2023: - Added the start/pause and stop pushbutton
- Fixed the timerOn to support pressing pause.
- Added the LimitObject for the level time delay
- Added the 'pause' parameter to the "timerOn" function
- added the 'pause' parameter to the "moveHammers" function for actual velocity.
- Added the game level (still not define a role to increase the level)
- Added the level simulator button
https://wokwi.com/projects/359062656089169921
By: @MicroBeaut
*/
# include "hammerz.h"
# include "RepeatButton.h"
# include <Adafruit_NeoPixel.h>
# define PIN 8 // Neopixel pin with PWM
# define PIPESTS_PIN 9 // NeoPixel Pipe Status pin
# define START_PIN 7 // Star/Pause Game
# define STOP_PIN 6 // Stop Game
# define LEVEL_PIN 5 // Level simulator pin
# define GAME_STOP 0
# define GAME_START 1
# define GAME_PAUSE 2
# define GAME_MAX_LEVEL 6
# define NUMPIXELS 12 // number of LEDs on strip
Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
Adafruit_NeoPixel pipeStatus(NUMPIXELS, PIPESTS_PIN, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
# define PIPE_GREEN pipeStatus.Color(0, 255, 0) // Pipe Available
# define PIPE_RED pipeStatus.Color(255, 0, 0) // Pipe Dropping
# define PIPE_BLACK pipeStatus.Color(0, 0, 0) // Pipe Droped
/*
===========================================================================
Class: TimerOn
The rising edge of input "input" starts a timer of duration "time delay."
The next falling edge of input "input" initializes the timer.
When the elapsed time is greater than or equal to "time delay",
the timer stops, and the output changes from FALSE to TRUE.
When the "'pause" is FALSE, the elapsed time will be updated by summed with a delta time for each scan.
When the TRUE, elapsed time will be paused.
===========================================================================
*/
class TimerOn {
private:
unsigned char prevInput: 1;
unsigned char output: 1;
unsigned long elapsedTime;
unsigned long prevTime;
unsigned long timeDelay;
public:
TimerOn() {
prevInput = false;
output = false;
// timeDelay = 500000UL; 500 microseconds
}
// Convert milliseconds to microseconds
void setTimeDelay(long msTimeDelay) {
timeDelay = msTimeDelay * 1000UL;
}
// Timer ON Condition
bool timerOn(bool input = true, bool pause = false) {
unsigned long currTime = micros();
unsigned long deltaTime = currTime - prevTime;
if (input) {
if (!prevInput) {
prevTime = currTime;
elapsedTime = 0;
} else if (!output) {
if (!pause) elapsedTime += deltaTime;
if (elapsedTime >= timeDelay) {
elapsedTime = timeDelay;
output = true;
}
}
} else {
output = false;
}
prevTime = currTime;
prevInput = input;
return output;
}
uint16_t getTimeDelay() {
return timeDelay / 1000UL;
}
bool isDone() {
return output;
}
};
typedef struct {
long minLimit;
long maxLimit;
} LimitObject;
//#define RANDOM_MIN 1000L // Random Min x milliseconds
//#define RANDOM_MAX 7000L // Random Max y milliseconds
#define BLINK_DELAY 500 // 500 microseconds
const uint8_t numberOfPipes = 10; // Number of Pipes
// TimerOn pipeTimerOns[numberOfPipes]; // Timer On objects
TimerOn pipeTON; // Pipe Drop Timer On
uint8_t pipeCollector[numberOfPipes]; // Pipe Index Collector
uint8_t pipeIndex; // Pipe Index
TimerOn blinkTON; // Blinking Timer On
bool toggleState;
LimitObject levelTimeLimit[] = {
{1000L, 7000L},
{750L, 5250L},
{500, 3500L},
{375, 2625},
{250, 1750},
{187, 1312}
};
RepeatButton startButton;
RepeatButton stopButton;
RepeatButton levelButton;
uint8_t gameLevel = 0;
uint8_t gameState;
bool startState;
bool pauseState;
//TimerOn myTO;
void OnStartKeyPressed(ButtonEvents e); // Declare the OnKeyPressed Callback Function
void OnStopKeyPressed(ButtonEvents e); // Declare the OnKeyPressed Callback Function
void OnLevelKeyPressed(ButtonEvents e); // Declare the OnKeyPressed Callback Function
void RunGame();
void rainbow();
uint32_t Wheel(byte WheelPos);
void RandomCollector();
void SetTimeDelay();
void Dropping();
void PrintPipeInfo();
void setup() {
Serial.begin(115200);
Serial.println(" timer focus \n");
setupHammers();
resetHammers();
randomSeed(analogRead(0));
strip.begin();
pipeStatus.begin();
// strip check - are we talking or what?
if (1) {
strip.setPixelColor(7, 0xffffff);
strip.show();
delay(777);
}
for (uint8_t index = 0; index < numberOfPipes; index++) {
// long newDelay = random(RANDOM_MIN, RANDOM_MAX); // Initial Random Time n milliseconds
// pipeTimerOns[index].setTimeDelay(newDelay); // Set delay time to object
pipeCollector[index] = index;
}
blinkTON.setTimeDelay(BLINK_DELAY); // Set Blink Time Delay
//uint16_t newDelay = random(levelTimeLimit[gameLevel].minLimit, levelTimeLimit[gameLevel].maxLimit); // Initial Random Time n milliseconds
//pipeTON.setTimeDelay (newDelay); // Initial time delay for first pipe
RandomCollector(); // Randomize Pipe
pipeIndex = 0; // Initil Pipe Index
//PrintPipeInfo(); // Print dropping info. for first pipe
toggleState = true;
startButton.buttonMode(START_PIN, INPUT_PULLUP); // Pushbutton mode
startButton.onKeyPressed(OnStartKeyPressed); // Callback event on key press
stopButton.buttonMode(STOP_PIN, INPUT_PULLUP); // Pushbutton mode
stopButton.onKeyPressed(OnStopKeyPressed); // Callback event on key press
levelButton.buttonMode(LEVEL_PIN, INPUT_PULLUP); // Pushbutton mode
levelButton.onKeyPressed(OnLevelKeyPressed); // Callback event on key press
gameLevel = 0; // Initial level 0
}
unsigned long now;
void loop() {
now = millis();
startButton.repeatButton();
stopButton.repeatButton();
levelButton.repeatButton();
Dropping();
RunGame();
moveHammers(pauseState, pipeTON.getTimeDelay());
displayHammers();
rainbow();
}
void OnStartKeyPressed(ButtonEvents e) {
switch (e) {
case keyPress:
bool prevStartState = startState;
gameState = gameState != GAME_START ? GAME_START : GAME_PAUSE;
startState = true;
pauseState = gameState == GAME_PAUSE;
if (startState == !prevStartState) {
pipeIndex = 0;
SetTimeDelay();
PrintPipeInfo();
}
}
}
void OnStopKeyPressed(ButtonEvents e) {
switch (e) {
case keyPress:
bool prevStartState = startState;
gameState = GAME_STOP;
startState = false;
if (!startState == prevStartState) {
RandomCollector();
resetHammers();
}
break;
}
}
void OnLevelKeyPressed(ButtonEvents e) {
switch (e) {
case keyPress:
gameLevel = (gameLevel + 1 ) % GAME_MAX_LEVEL;
Serial.print("\nLevel:=");
Serial.print(gameLevel + 1);
Serial.println(", speed will be effective for the next hammer");
break;
}
}
void SetTimeDelay() {
uint16_t newDelay = random(levelTimeLimit[gameLevel].minLimit, levelTimeLimit[gameLevel].maxLimit); // Initial Random Time n milliseconds
pipeTON.setTimeDelay (newDelay); // Set hammer time delay
}
void RunGame() {
if (pipeTON.timerOn(startState, pauseState)) {
pipeTON.timerOn(false); // Reset timer
//uint16_t newDelay = random(levelTimeLimit[gameLevel].minLimit, levelTimeLimit[gameLevel].maxLimit); // New Random Time n milliseconds
//pipeTON.setTimeDelay(newDelay); // Set new Delay Time
SetTimeDelay(); // Set hammer time delay
pipeStatus.setPixelColor(pipeCollector[pipeIndex], PIPE_BLACK); // Pipe dropped
pipeIndex = (pipeIndex + 1) % numberOfPipes; // Increase Pipe Index modular with numberOfPipes
if (pipeIndex == 0) RandomCollector(); // Random for new game
pipeStatus.setPixelColor(pipeCollector[pipeIndex], PIPE_RED); // Set status for pipe
pipeStatus.show(); // Update NeoPixel
// for (uint8_t index = 0; index < numberOfPipes; index++) {
// pipeTimerOns[index].timerOn(true); // True = Enable or Start, False = Disable or Stop or Reset
// if (pipeTimerOns[index].isDone()) { // Timer On Done?
// pipeTimerOns[index].timerOn(false); // Reset Timer On
// randomSeed(analogRead(0)); // Use an unconnected pin for randomSeed()
// long newDelay = random(RANDOM_MIN, RANDOM_MAX); // New Random Time n milliseconds
// pipeTimerOns[index].setTimeDelay(newDelay); // Set delay time to object
PrintPipeInfo(); // Print dropping info.
// }
}
}
// modified from Adafruit example to make it a output machine
void rainbow()
{
static uint16_t j = 0;
static unsigned long lastUpdate;
if (now - lastUpdate < 40)
return;
for (int i = 0; i < strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i + j) & 255));
}
strip.show();
j++;
if (j >= 256) j = 0;
lastUpdate = now; // time for next change to the display
}
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);
}
void Dropping() {
if (blinkTON.timerOn(startState, pauseState)) {
blinkTON.timerOn(false);
toggleState = !toggleState;
if (toggleState) {
pipeStatus.setPixelColor(pipeCollector[pipeIndex], PIPE_RED);
} else {
pipeStatus.setPixelColor(pipeCollector[pipeIndex], PIPE_GREEN);
}
pipeStatus.show();
}
}
/*
To initialize an array a of n elements to a randomly shuffled copy of source, both 0-based:
for i from 0 to n − 1 do
j ← random integer such that 0 ≤ j ≤ i
if j ≠ i
a[i] ← a[j]
a[j] ← source[i]
*/
// Random Collector for pipes
void RandomCollector() {
for (uint8_t index = 0; index < numberOfPipes; index++) {
uint8_t randomNumber = random(index + 1);
pipeCollector[index] = pipeCollector[randomNumber];
pipeCollector[randomNumber] = index;
}
for (uint8_t index = 0; index < numberOfPipes; index++)
pipeStatus.setPixelColor(index, PIPE_GREEN);
pipeStatus.show();
if (0) {
for (uint8_t index = 0; index < numberOfPipes; index++) {
Serial.print(pipeCollector[index]); Serial.print(" ");
pipeStatus.setPixelColor(index, PIPE_GREEN);
}
Serial.println("");
}
}
// // Random Collector for pipes
// void RandomCollector0() {
// for (uint8_t index = 0; index < numberOfPipes; index++) {
// uint8_t randomNumber = random(index, numberOfPipes);
// uint8_t temp = pipeCollector[index];
// pipeCollector[index] = pipeCollector[randomNumber];
// pipeCollector[randomNumber] = temp;
// }
// for (uint8_t index = 0; index < numberOfPipes; index++) {
// Serial.print(pipeCollector[index]); Serial.print(" ");
// pipeStatus.setPixelColor(index, PIPE_GREEN);
// }
// Serial.println("");
// pipeStatus.show();
// }
void PrintPipeInfo() {
if (pipeIndex == 0) {
Serial.print("\nNew Game Level:=");
Serial.println(gameLevel + 1);
resetHammers();
}
dropHammer(pipeCollector[pipeIndex]);
Serial.print(millis()); Serial.print(" dropping ");
Serial.print(pipeCollector[pipeIndex]); Serial.print(" delay ");
Serial.print(pipeTON.getTimeDelay()); Serial.print(" mS to next");
Serial.println("");
}
μB
Again, I did. I'm out the door soon, soon as my beach buddy swings by: she who must not be kept waiting...
I wrote the hammerz thing planning to eventually mimic gravity. That is to say, all hammers will drop with gravity, not just at a timed rate (like my original) or a selected rate based on the delay to next (I think that's what you are doing).
Also by doing that, you are preventing two hammers to be in the air at the same time. Which is good if they are to fall at different rates, but removes a "feature" which is being faced with multiple dropping hammers.
Not yet seen, nor written for that matter, is my rail gun and launch control system, a fully non-blocking hack to add in which will
afford a rail gun aiming bar, click '<' and '>' to position the gun, only moves so fast and
a missile launch button '=' which will shoot a 1 kg tungsten slug up at the falling hammer
Hit a hammer before it hits the floor and that columb is green a point.
Errors (firing at not moving or complete columns) or missing turns a columb red, no point.
Another 10 LED strip, three buttons and I am aiming (see what I did there?) for minimal involvement with the existing game code.
Don't hold your breath on that - we have a spectacular day and a forecast for several, time stolen from enjoying that will be time lost.
I may never.
a7
The Umbrella Academy hacked our way through adding a rail gun.
Sry, @hamarn, we should have added it to your progress but didn't want to undo anything.
The rail gun lets you aim and fire at the falling hammers. I do not recommend learning from the code, but the game play isn't entirely stupid. I think with some tuning and physics might end up being an amusing challenge.
The red "FIRE" button is also for the start game. The loop() uses a switch/case to be in one of three distinct game phases: resting and ready, two seconds set! and then go which runs through the N pipes or hammers or whatever.
I got off work late this week . I'll try this weekend.
μB
@alto777
It was done on firing. I have changed the dead color to green. And fire from yellow to red.
Hammers, pipes and railgun.
@hamarn Nice work!
Now there's a bunch of constants and defines, perhaps they could all be in one header file - the colors of the various actor-states, the timing of the drops and speed thereof &c.
If anyone builds this, they will need to fix all the colors as the wokwi isn't the best when it comes to simulated LEDs.
As it is now, I don't get to enjoy too much the firing up red traveller. I'll turn it down to old man reflexes speed.
Also - perhaps firing at nothing should show a useless missile going up and off the hammer field.
Maybe no missile launches while that plays out which...
...might mean that making the missile move up significantly faster would be good.
There would be tunings that might make more than one missile in the air at a time be desirable. TBH I did not look too closely at the new firing logic.
The game is now at the point where real world switches would be nice. I've done some experiments with physics on this, and mostly it makes it hard to play on the wokwi.
Side project: Use the USB-HID on a Leonardo and make a little controller box. Make the sketch in the wokwi hear/listen to keystrokes and buttons. I wonder if some kind of game controller would already be a huge jumpt start on that.
I can only say life too short.
a7
@alto777; As promised, I uploaded a video to youtube to show you the end result of the game I made with your help;
Thank you a lot again for your help. Could not have done it without you!
The kids also enjoy it alot (and keep getting better at it).
The sketch has changed a bit based on some testing. This is the latest and working sketch for anyone that wants to use it;
//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
//with many thanks to the help from the arduino forum and especially to alto777
//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 now; // Deze is voor de timer van de buizen vallen (current time)
unsigned long CurrentTime;
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 counter;
//variabelen voor random generators
long nextTime; //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 startwaarde
//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 PINforControl1 12 //Neopixel pin with PWM
#define PINforControl2 13 //Neopixel pin with PWM
#define PINforControl3 17 //Neopixel pin with PWM
#define NUMPIXELS1 12 // number of LEDs on strip
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip1(NUMPIXELS1, PINforControl1, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
Adafruit_NeoPixel strip2(NUMPIXELS1, PINforControl2, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
Adafruit_NeoPixel strip3(NUMPIXELS1, PINforControl3, NEO_GRB + NEO_KHZ800); // Declare our NeoPixel strip object
int getRandomEntry() {
if (remainingNumbers == 0)
remainingNumbers = arraySize; // Start over with a full array
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);
randomSeed(analogRead(A4 * A5));
//Neopixel startup;
strip1.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip2.begin();
strip3.begin();
wipe();
//strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
pinMode(LED_BUILTIN, OUTPUT); //onboard LED
pinMode(Btn_LED_Easy, OUTPUT);
pinMode(Btn_LED_Hard, OUTPUT);
pinMode(Btn_LED_Reset, OUTPUT);
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);
//turn off all magnets
digitalWrite(Pipe_1, HIGH);
digitalWrite(Pipe_2, HIGH);
digitalWrite(Pipe_3, HIGH);
digitalWrite(Pipe_4, HIGH);
digitalWrite(Pipe_5, HIGH);
digitalWrite(Pipe_6, HIGH);
digitalWrite(Pipe_7, HIGH);
digitalWrite(Pipe_8, HIGH);
digitalWrite(Pipe_9, HIGH);
digitalWrite(Pipe_10, HIGH);
//prepare for the game
digitalWrite(Btn_LED_Reset, LOW);
digitalWrite(Btn_LED_Easy, HIGH);
digitalWrite(Btn_LED_Hard, HIGH);
colorWipeDelay(strip1.Color(255, 50, 0), 100); // Orange
colorWipeDelay(strip2.Color(255, 50, 0), 100); // Orange
colorWipeDelay(strip3.Color(255, 50, 0), 100); // Orange
AllMagnetsOn(); //Alle magneten inschakelen
colorWipeDelay(strip1.Color(0, 255, 0), 100); // Green
colorWipeDelay(strip2.Color(0, 255, 0), 100); // Green
colorWipeDelay(strip3.Color(0, 255, 0), 100); // Green
delay(2000);
colorWipeDelay(strip1.Color(0, 0, 0), 100); // Off
colorWipeDelay(strip2.Color(0, 0, 0), 100); // Off
colorWipeDelay(strip3.Color(0, 0, 0), 100); // Off
wipe(); // off
digitalWrite(Btn_LED_Easy, LOW);
digitalWrite(Btn_LED_Hard, LOW);
int counter; // Used to count how many pipes have dropped, when all pipes have dropped, game is finished.
}
void loop() {
now = millis();
rainbow(); // Flowing rainbow cycle along the whole strip with NeoPixel; 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
delay(50); //debounce button
Level = 10;
Serial.println("Button Easy is pressed");
counter = 0;
while (counter < arraySize) // when counter = arraySize; exit loop
{
BtnLedEasyPuls();
rainbowCycle();
RunGame();
}
}
if (digitalRead(Btn_Hard) == LOW) { //als knop "Moeilijk" wordt ingedrukt; set moeilijkheid naar Moeilijk en start spel //this code must yet be corrected based on the Btn_Easy code
delay(50); //debounce button
Level = 4;
Serial.println("Button Hard is pressed");
counter = 0;
while (counter < arraySize) // when counter = arraySize; exit loop
{
BtnLedHardPuls();
theaterChaseRainbow();
RunGame();
}
}
}
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 RunGame() {
static unsigned long lastTime = 3000;
static int nextTime = random(100, 1000);
now = millis();
if (now - lastTime < (nextTime*Level))
return;
// select and drop random
randPipe = getRandomEntry();
digitalWrite(randPipe + 2, HIGH); // laat de honden los!
Serial.print("Random delay value: ");
Serial.println(nextTime * Level);
Serial.print("Pipe nr: ");
Serial.print(randPipe);
Serial.print(", Pin nr: ");
Serial.println(randPipe + 2);
Serial.print("Pipe counter value; ");
Serial.print(counter);
Serial.print(", array size; ");
Serial.println(arraySize);
Serial.println();
// schedule
nextTime = random(100, 1000) ;
lastTime = now;
counter++; //counts every time a pipe has dropped
}
void BtnLedEasyPuls() {
unsigned long now = millis();
if (now - prevTimeT1 > 200) {
prevTimeT1 = now;
// 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 now = millis();
if (now - prevTimeT1 > 200) {
prevTimeT1 = now;
// 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_Hard, ledStateBtn);
digitalWrite(Btn_LED_Reset, ledStateRst);
}
}
void rainbow() { // modified from Adafruit example to make it a state machine
static uint16_t j = 0;
static unsigned long lastUpdate;
if (now - lastUpdate < 15)
return;
for (int i = 0; i < strip1.numPixels(); i++) {
strip1.setPixelColor(i, Wheel((i + j) & 255));
strip2.setPixelColor(i, Wheel((i + j) & 255));
strip3.setPixelColor(i, Wheel((i + j) & 255));
}
strip1.show();
strip2.show();
strip3.show();
j++;
if (j >= 256) j = 0;
lastUpdate = now; // time for next change to the display
}
void rainbowCycle() { // modified from Adafruit example to make it a state machine
static uint16_t j = 0;
static unsigned long lastUpdate2;
if (now - lastUpdate2 < 20)
return;
for (int i = 0; i < strip1.numPixels(); i++) {
strip1.setPixelColor(i, Wheel(((i * 256 / strip1.numPixels()) + j) & 255));
strip2.setPixelColor(i, Wheel(((i * 256 / strip1.numPixels()) + j) & 255));
strip3.setPixelColor(i, Wheel(((i * 256 / strip1.numPixels()) + j) & 255));
}
strip1.show();
strip2.show();
strip3.show();
j++;
if (j >= 256 * 5) j = 0;
lastUpdate2 = now; // 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;
static unsigned long lastUpdate3;
if (now - lastUpdate3 < 30)
return;
if (on) {
for (int i = 0; i < strip1.numPixels(); i = i + 3) {
strip1.setPixelColor(i + q, Wheel((i + j) % 255)); //turn every third pixel on
strip2.setPixelColor(i + q, Wheel((i + j) % 255)); //turn every third pixel on
strip3.setPixelColor(i + q, Wheel((i + j) % 255)); //turn every third pixel on
}
} else {
for (int i = 0; i < strip1.numPixels(); i = i + 3) {
strip1.setPixelColor(i + q, 0); //turn every third pixel off
strip2.setPixelColor(i + q, 0); //turn every third pixel off
strip3.setPixelColor(i + q, 0); //turn every third pixel off
}
}
on = !on; // toggel pixelse on or off for next time
strip1.show(); // display
strip2.show(); // display
strip3.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;
}
lastUpdate3 = now; // 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 < strip1.numPixels(); i++) {
strip1.setPixelColor(i, c);
strip2.setPixelColor(i, c);
strip3.setPixelColor(i, c);
strip1.show();
strip2.show();
strip3.show();
delay(wait);
}
}
void wipe() { // clear all LEDs
for (int k = 0; k < strip1.numPixels(); k++) {
strip1.setPixelColor(k, strip1.Color(0, 0, 0));
strip2.setPixelColor(k, strip2.Color(0, 0, 0));
strip3.setPixelColor(k, strip3.Color(0, 0, 0));
}
}
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip1.Color(255 - WheelPos * 3, 0, WheelPos * 3);
return strip2.Color(255 - WheelPos * 3, 0, WheelPos * 3);
return strip3.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip1.Color(0, WheelPos * 3, 255 - WheelPos * 3);
return strip2.Color(0, WheelPos * 3, 255 - WheelPos * 3);
return strip3.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip1.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
return strip2.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
return strip3.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}