1-- NeoTrellis
https://www.adafruit.com/product/3954
2-- Rubber button pad
https://www.adafruit.com/product/1611
3-- Arduino Nano ESP32
https://store-usa.arduino.cc/products/nano-esp32-with-headers
4-option A-prefered-- JST PH 2mm 4-Pin Female Socket Connector
https://www.adafruit.com/product/3950
4-option B-- JST PH 2mm 4-Pin Male Header Cable with jumper cables if female JST is out of stock --- note. you will connect the female jumpers to the JST to have a female connector to attach to the Nano.
https://www.adafruit.com/product/3955
https://www.adafruit.com/product/266
1-- place the Nano ESP32 into the slot in the main part case with the pins facing up
2-- slot the Nano ESP32 Case Top into the slot in the main part case securing the Nano in place.
3-- press fit the NeoTrellis into the nt-tray
1-- insert the JST into the connector on the NeoTrellis
2-- connect ESP32 3v3 -to- NeoTrellis Vin
3-- connect ESP32 GND -to- NeoTrellis GND
4-- connect ESP32 A4/SDA -to- NeoTrellis SDA
5-- connect ESP32 A5/SCL -to- NeoTrellis SCL
This should only be done after the wiring. I would recommend doing a systems test with the basic example in the Adafruit seesaw--NeoTrellis library before final assembly
1-- snap the nt-tray/NeoTrellis part into the main part case making sure that no wires get caught under the nt-tray/NeoTrellis part.
2-- lay the Rubber button pad on top of the NeoTrellis making sure that the pad lays flat on top.
3-- snap the nt-top on top securing everything inside.
if you have any questions about the assembly please comment!
1--libraries--install the BlockNot library https://github.com/EasyG0ing1/BlockNot. and the Adafruit Seesaw library github.com/adafruit/Adafruit_Seesaw.
2--Code--
//////////////////////////////////////////////////////////////
// whack-a-mole game //
//////////////////////////////////////////////////////////////
#include "Adafruit_NeoTrellis.h" //librairies
#include <BlockNot.h>
BlockNot moleTimer(random(1000, 2000)); //non blocking timers for different aspects of the game
BlockNot blueTimer(random(400, 1200));
BlockNot levelFourTimer(1000);
BlockNot levelEightTimer(100); //<--CHANGE THIS TO MAKE LEVEL EIGHT HARDER OR EASIER<-- lower is harder higher is easier. timing is in milliseconds
Adafruit_NeoTrellis trellis;
////////////////////////////////////////////////////////////// variables
int randNum1; // random numbers for different uses
int randNum2; // random numbers for different uses
int count1; // multipurpose counter variable
int count2 = 0; // multipurpose counter variable
int blueCount; // counts how many blue pixels there are
int blueAmount; // hom many blue there should be
int redPoints = 0; // how many red have been pressed
int speedPoints = 0; //variable for game speed and other game events
int levelFourCount = 0; //counter for how many times you have pressed a button during the level four challenge
bool redCountOff = HIGH; //boolean to turn of red press counting during chalenges
////////////////////////////////////////////////////////////// buttons
//define a callback for key presses
TrellisCallback blink(keyEvent evt) {
if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) {
// Check is the pad pressed?
if (trellis.pixels.getPixelColor(evt.bit.NUM) == 16711680) { // if button pressed is red
trellis.pixels.setPixelColor(evt.bit.NUM, 0); //turn key black
trellis.pixels.show();
if (redCountOff == HIGH) {
redPoints++; // each time you press a red button add to counter
Serial.println("redPoint up");
}
if (redPoints > 20) { // if you have pressed red twenty times increase speedPoints
speedPoints++;
if (speedPoints == 4) { // if are on speed level four do a special challenge
levelFour();
}
if (speedPoints == 8) { // if are on speed level eight do a special challenge
levelEight();
}
redPoints = 0;
}
} else if (trellis.pixels.getPixelColor(evt.bit.NUM) == 255) { // if button pressed is blue
lose();
} else { // if button pressed is black
trellis.pixels.setPixelColor(random(0, 16), 255, 0, 0); // make more red appear
trellis.pixels.setPixelColor(random(0, 16), 255, 0, 0);
trellis.pixels.setPixelColor(random(0, 16), 255, 0, 0);
trellis.pixels.setPixelColor(random(0, 16), 255, 0, 0);
trellis.pixels.setPixelColor(random(0, 16), 255, 0, 0);
trellis.pixels.setPixelColor(random(0, 16), 255, 0, 0);
trellis.pixels.setPixelColor(random(0, 16), 255, 0, 0);
trellis.pixels.show();
}
} else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) {
// or is the pad released?
// do nothing when button is released
}
return 0;
}
////////////////////////////////////////////////////////////// setup
void setup() {
Serial.begin(9600); //begin serial connection
if (!trellis.begin()) { //error messages
Serial.println("Could not start trellis, check wiring?");
while (1) delay(1);
} else {
Serial.println("NeoPixel Trellis started");
}
//activate all keys and set callbacks
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) {
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
trellis.registerCallback(i, blink);
}
//do a little animation to show we're on
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
trellis.pixels.setPixelColor(i, Wheel(map(i, 0, trellis.pixels.numPixels(), 0, 255)));
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) {
trellis.pixels.setPixelColor(i, 0x000000);
trellis.pixels.show();
delay(50);
}
}
////////////////////////////////////////////////////////////// loop
void loop() {
trellis.read();
delay(15);
////////////////////////////////////////////////////////////// moletimer
if (moleTimer.TRIGGERED) { // if moletimer triggerd
Serial.println("moleTimer TRIGGERED");
moleTimer.setDuration(random(200 - (speedPoints * 12), 2100 - (speedPoints * 130))); //make a new random duration until next moletimer trigger
randNum1 = random(0, 16); //random color for new pixel
for (count1 = 0; trellis.pixels.getPixelColor(randNum1) != 0; count1++) { //repeat if the pixel is already a color
// statement(s);
randNum1 = random(0, 16);
if (count1 > 20) { // if you have tried twenty times to fill a pixel quit so you cant get in an endless loop.
break;
}
}
if (trellis.pixels.getPixelColor(randNum1) == 0) { //make sure the chosen pixel is black just to be sure
trellis.pixels.setPixelColor(randNum1, 255, 0, 0);
trellis.pixels.show();
}
}
for (uint16_t i = 0; i < trellis.pixels.numPixels() && trellis.pixels.getPixelColor(i) != 0; i++) { //check if the screen is filled with colored pixels
if (i == 15) {
lose(); //lose animation and reset the game
}
}
////////////////////////////////////////////////////////////// bluetimer
if (blueTimer.TRIGGERED) { // if bluetimer is triggered
Serial.println("blueTimer TRIGGERED");
blueTimer.setDuration(random(400 - (speedPoints * 12), 1200 - (speedPoints * 45))); //make a new random duration until next bluetimer trigger
randNum1 = random(0, 16); //random color for new pixel
for (count1 = 0; trellis.pixels.getPixelColor(randNum1) != 0; count1++) { //repeat if the pixel is already a color
// statement(s);
randNum1 = random(0, 16);
if (count1 > 20) { // if you have tried twenty times to fill a pixel quit so you cant get in an endless loop.
break;
}
}
if (trellis.pixels.getPixelColor(randNum1) == 0) { //make sure the chosen pixel is black just to be sure
trellis.pixels.setPixelColor(randNum1, 0, 0, 255);
trellis.pixels.show();
}
blueCount = 0;
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) { //check how many blue pixels are currently on screen
if (trellis.pixels.getPixelColor(i) == 255) {
blueCount++;
}
}
if (speedPoints <= 5) { //change how many blue pixels are on screen depending on how far you are through the game
blueAmount = 2;
}
if (speedPoints >= 6) {
blueAmount = 3;
}
if (speedPoints >= 12) {
blueAmount = 4;
}
if (blueCount > blueAmount) { // if you have more blue on screen then there should be
randNum1 = random(0, 16);
for (count1 = 0; trellis.pixels.getPixelColor(randNum1) != 255; count1++) { //pick a random pixel until you find one thats blue
// statement(s);
randNum1 = random(0, 16);
}
trellis.pixels.setPixelColor(randNum1, 255, 0, 0); // turn the random pixel red
trellis.pixels.show();
}
}
}
////////////////////////////////////////////////////////////// animation
void lose() {
moleTimer.stop(); //stop timers
blueTimer.stop();
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { //turn off buttons
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING, false);
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING, false);
}
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) { //wipe screen with white
trellis.pixels.setPixelColor(i, 255, 255, 255);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 0; i < speedPoints; i++) { //wipe as many pixels as you have speedPoints
trellis.pixels.setPixelColor(i, Wheel(map(i, 0, trellis.pixels.numPixels(), 0, 255)));
trellis.pixels.show();
delay(1000 / speedPoints);
}
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) { // wipe with black
trellis.pixels.setPixelColor(i, 0);
trellis.pixels.show();
delay(50);
}
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { //turn buttons on
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
trellis.registerCallback(i, blink);
}
trellis.read();
redPoints = 0; // reset points
speedPoints = 0;
moleTimer.start(WITH_RESET); //start timers
blueTimer.start(WITH_RESET);
}
////////////////////////////////////////////////////////////// level four challenge
void levelFour() {
redCountOff = LOW;
count2 = 0;
moleTimer.stop(); //stop timers
blueTimer.stop();
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { //turn off buttons
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING, false);
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING, false);
}
for (uint16_t i = 0; i < 4; i++) { // wipe the screen with certain shades of green and blue
trellis.pixels.setPixelColor(i, 0, 255, 0);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 4; i < 8; i++) { // wipe the screen with certain shades of green and blue
trellis.pixels.setPixelColor(i, 0, 255, 128);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 8; i < 12; i++) { // wipe the screen with certain shades of green and blue
trellis.pixels.setPixelColor(i, 0, 255, 255);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 12; i < 16; i++) { // wipe the screen with certain shades of green and blue
trellis.pixels.setPixelColor(i, 0, 128, 255);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) { // wipe with black
trellis.pixels.setPixelColor(i, 0);
trellis.pixels.show();
delay(50);
}
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { //turn buttons on
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
trellis.registerCallback(i, blink);
}
levelFourTimer.start(WITH_RESET);
levelFourCount = 0; // reset the counter
while (levelFourCount < 30) { // repeat until you have pressed 30 times
levelFourCount++;
levelFourTimer.setDuration(1000 - (levelFourCount * 12)); // timer speed is one second minus time for each button pressed
randNum1 = random(0, 16); // random red pixel
for (count1 = 0; trellis.pixels.getPixelColor(randNum1) != 0; count1++) {
// statement(s);
randNum1 = random(0, 16);
if (count1 > 20) {
break;
}
}
if (trellis.pixels.getPixelColor(randNum1) == 0) { // make sure its black
trellis.pixels.setPixelColor(randNum1, 255, 0, 0);
trellis.pixels.show();
}
randNum1 = random(0, 16); // and random blue pixel
for (count1 = 0; trellis.pixels.getPixelColor(randNum1) != 0; count1++) {
// statement(s);
randNum1 = random(0, 16);
if (count1 > 20) {
break;
}
}
if (trellis.pixels.getPixelColor(randNum1) == 0) { // make sure its black
trellis.pixels.setPixelColor(randNum1, 0, 0, 255);
trellis.pixels.show();
}
while (levelFourTimer.TRIGGERED != true) { // wait while checking for button presses
trellis.read();
delay(15);
}
count2 = 0;
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) { //check for if there are any red buttons left on screen
if (trellis.pixels.getPixelColor(i) == 16711680) {
count2++;
}
if (count2 >= 1) { // if there are lose and break out of the function
lose();
break;
}
}
if (count2 >= 1) {
break;
}
trellis.pixels.clear(); // clear the pixels
trellis.pixels.show();
}
moleTimer.start(WITH_RESET); //restart timers
blueTimer.start(WITH_RESET);
redCountOff = HIGH;
}
////////////////////////////////////////////////////////////// level eight challenge
void levelEight() {
redCountOff = LOW;
moleTimer.stop(); //stop timers
blueTimer.stop();
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { //turn off buttons
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING, false);
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING, false);
}
for (uint16_t i = 0; i < 4; i++) { // wipe the screen with certain shades of red and blue
trellis.pixels.setPixelColor(i, 255, 0, 0);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 4; i < 8; i++) { // wipe the screen with certain shades of red and blue
trellis.pixels.setPixelColor(i, 255, 0, 128);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 8; i < 12; i++) { // wipe the screen with certain shades of red and blue
trellis.pixels.setPixelColor(i, 255, 0, 255);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 12; i < 16; i++) { // wipe the screen with certain shades of red and blue
trellis.pixels.setPixelColor(i, 128, 0, 255);
trellis.pixels.show();
delay(50);
}
for (uint16_t i = 0; i < trellis.pixels.numPixels(); i++) { // wipe with black
trellis.pixels.setPixelColor(i, 0);
trellis.pixels.show();
delay(50);
}
for (int i = 0; i < NEO_TRELLIS_NUM_KEYS; i++) { //turn buttons on
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING);
trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING);
trellis.registerCallback(i, blink);
}
levelFourTimer.start(WITH_RESET);
count2 = 0;
for (int i = 0; i <= 3; i++) { // three repeats
for (int i = 0; i < trellis.pixels.numPixels(); i++) { //wipe with red while you try and get rid of the red
trellis.pixels.setPixelColor(i, 255, 0, 0); // light up the pixel
trellis.pixels.show();
while (levelEightTimer.TRIGGERED != true) { // wait whil checking the buttons
delay(15);
trellis.read();
for (count2 = 0; count2 < trellis.pixels.numPixels() && trellis.pixels.getPixelColor(count2) == 16711680; count2++) { // if screen is covered in red you lose
Serial.println(count2);
if (count2 == 16) {
lose(); // lose
Serial.println("lose level eight");
break; // break out of function
}
}
if (count2 == 16) {
break;
}
}
if (count2 == 16) {
break;
}
}
if (count2 == 16) {
break;
}
}
moleTimer.start(WITH_RESET); //restart timers
blueTimer.start(WITH_RESET);
redCountOff = HIGH;
}
////////////////////////////////////////////////////////////// rainbow color
// Input a value 0 to 255 to get a color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) { //not sure exactly how this works because I took this from the example sketch but it returns a color
if (WheelPos < 85) {
return trellis.pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if (WheelPos < 170) {
WheelPos -= 85;
return trellis.pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return trellis.pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
return 0;
}
3--upload--I had to use the gpio pin numbering for some reason on this project along with the esptool uploader to fix upload problems and errors. this may change for other Arduino Nano ESP32 devices as arduino updates their devices and IDEs.
4--What does this code do?--This game is similar to whack a mole. The player attempts to press the red buttons and avoid the blue. The game slowly progresses by getting faster. If the screen fills up with red and blue the player loses. When you lose the screen first wipes completely with white and then wipes pixels with a rainbow affect showing how many points the player had. If you get to level four the screen wipes green and a minigame challenge starts. In the challenge the screen shows two pixels at a time. One red and one blue. The player must press the red one before the timer runs out or else they lose. This happens 30 times while slowly getting faster. If you get to level eight a harder challenge happens. The screen wipes with red pixels three times and the player must try to clear the red pixels while this happens. If you find the level eight challenge to be too difficult change the BlockNot levelEightTimer
on line eleven. making the variable higher makes the game easier and lower makes it harder. the timer is in milliseconds between each pixel appearing in level eight.
Feel free to comment any questions or problems and remix any part of this project however you like whether it is changing and updating my already existing code or making a completely new projet. Have fun experimenting with this project. and feel free to code whatever you want onto it.
The author remixed this model.
I combined two of the parts and added a case for the Arduino Nano ESP32 that I designed. I wrote new code and removed unnecessary plastic in the case that was meant to hold a battery