Project tutorial
My twist on the Twister game

My twist on the Twister game © MIT

uno, mpr121, addressable led and Serial mp3 player and some aluminum foil

  • 499 views
  • 0 comments
  • 0 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)
4966285
Solder Wire, Lead Free

Apps and online services

About this project

Kesem, My 4yr old son is home for over a month now due to the corona virus out there. I was looking for more ways to keep him active and entertained at the same time.

I started with one LED and 6 panels that were connected to an MPR121

If you never used one before, I got a great video for that

The code is pretty straight forward.To communicate with the mp3 I use software serial on pins 8 and 9. This module is really easy to use and theaudio feedback is a great add on to the game.

I use the FastLED library for the addressables. And the bare conductive library for the MPR121.

Each time a game starts a random color is selected, and shown on the LED.

void setSystemState(byte newState) {
 //set newState
 systemState = newState;
 switch (newState) {
   case SYS_STATE_START:
     currentColorIndex = 0;
     currentColor[currentColorIndex] = getRandomColor(99);
     Serial.println(currentColor[currentColorIndex]);
     leds[currentColorIndex] = colour[currentColor[currentColorIndex]];
     for (byte i = 1; i < NUM_LEDS; i++)
       leds[i] = CRGB::Black;
     FastLED.show();
     break;
   case SYS_STATE_WIN:
     serialmp3_play(5);
     blinkSuccess();
     setSystemState(0);
     Serial.println("system state  - success");
     break;
   case SYS_STATE_FAIL:
     serialmp3_play(6);
     blinkFail();
     setSystemState(0);
     Serial.println("system state  - fail");
     break;
 } //end switch
}

If the correct pad is touched another color is added and shown.

If a wrong pad is touched or a pad is left – it’s fail

And when all 4 are touched correctly – it’s a win

if (MPR121.touchStatusChanged() && systemState == 0) {
   MPR121.updateTouchData();
   for (int i = 0; i < NUM_BUTTONS; i++) {
     if (MPR121.isNewTouch(i)) {
       if (buttonColor[i] == currentColor[currentColorIndex]) {
         addColor();
         break;
       }
       else
       {
         setSystemState(SYS_STATE_FAIL);
         break;
       } //end if
     } else if (MPR121.isNewRelease(i)) {
       for (byte z = 0; z < currentColorIndex; z++) {
         if (currentColor[z] == buttonColor[i]) {
           setSystemState(SYS_STATE_FAIL);
           break;
         } //end if
       } //end for
     } //end if
   } //end for
 } //end if

When I started the project what I had in mind was creating a big Simone like game, with more than just 4 colors and spread across the room – so I can have Kesem run around. The major obstacle was the wiring, and I had no desire to go into wireless. So the next best thing was to create a surface – which is what I ended up doing.

After seeing his Ninja move, I realized I got a better result, one that will help Kesem work on his balance and planning of moves.

I had a few suggestions given, like adding a physical rolling element for the color picking, making it look similar to the Twister game. Also adding hand/foot instructions.And I thought of adding points as well, for success vs fails.

Code

BIG_simon.inoArduino
This is the main INO, you will need the seria_mp3.h as well.
/*
 *  Code by Tal Ofer  - talofer99@hotmail.com
 *  
 *  
 */
#include <FastLED.h>
#include <MPR121.h>
#include <Wire.h>
#include "serial_mp3.h"

#define NUM_LEDS 4
#define DATA_PIN 3
#define NUM_BUTTONS 12

// Define the array of leds
CRGB leds[NUM_LEDS];

// Define the array of leds
#define COLOR_BLUE 0
#define COLOR_GREEN 1
#define COLOR_YELLOW 2
#define COLOR_RED 3

// Define system state
#define SYS_STATE_START 0
#define SYS_STATE_WIN 1
#define SYS_STATE_FAIL 2



CRGB colour[] = {CRGB::Blue, CRGB::Green,  CRGB::Yellow, CRGB::Red};
byte buttonColor[NUM_BUTTONS] = {COLOR_BLUE, COLOR_BLUE, COLOR_BLUE, COLOR_GREEN, COLOR_GREEN, COLOR_GREEN, COLOR_YELLOW, COLOR_YELLOW, COLOR_YELLOW, COLOR_RED, COLOR_RED, COLOR_RED};

byte currentColor[NUM_LEDS];
byte currentColorIndex = 0;
byte systemState;

void setup()
{
  Serial.begin(115200);
  Serial.println("System started");
  randomSeed(analogRead(A0) + analogRead(A1) + analogRead(A2) + analogRead(A3));
  Wire.begin();

  // 0x5C is the MPR121 I2C address on the Bare Touch Board
  if (!MPR121.begin(0x5A)) {
    Serial.println("error setting up MPR121");
    switch (MPR121.getError()) {
      case NO_ERROR:
        Serial.println("no error");
        break;
      case ADDRESS_UNKNOWN:
        Serial.println("incorrect address");
        break;
      case READBACK_FAIL:
        Serial.println("readback failure");
        break;
      case OVERCURRENT_FLAG:
        Serial.println("overcurrent on REXT pin");
        break;
      case OUT_OF_RANGE:
        Serial.println("electrode out of range");
        break;
      case NOT_INITED:
        Serial.println("not initialised");
        break;
      default:
        Serial.println("unknown error");
        break;
    }
    while (1);
  }

  // pin 4 is the MPR121 interrupt on the Bare Touch Board
  MPR121.setInterruptPin(2);

  // this is the touch threshold - setting it low makes it more like a proximity trigger
  // default value is 40 for touch
  MPR121.setTouchThreshold(40);

  // this is the release threshold - must ALWAYS be smaller than the touch threshold
  // default value is 20 for touch
  MPR121.setReleaseThreshold(20);

  // initial data update
  MPR121.updateTouchData();

  // start fast led
  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);

  serialmp3_init();
  delay(200);
  serialmp3_set_vol(15);
  delay(200);


  setSystemState(SYS_STATE_START);
}

void loop()
{
  mp3_SerialProcess();


  if (MPR121.touchStatusChanged() && systemState == 0) {
    MPR121.updateTouchData();
    for (int i = 0; i < NUM_BUTTONS; i++) {
      if (MPR121.isNewTouch(i)) {
        
        if (buttonColor[i] == currentColor[currentColorIndex]) {
          addColor();
          break;
        }
        else
        {
          setSystemState(SYS_STATE_FAIL);
          break;
        } //end if

      } else if (MPR121.isNewRelease(i)) {
        
        for (byte z = 0; z < currentColorIndex; z++) {
          if (currentColor[z] == buttonColor[i]) {
            setSystemState(SYS_STATE_FAIL);
            break;
          } //end if
        } //end for
        
      } //end if
    } //end for
  } //end if
}

void addColor() {
  currentColorIndex++;
  if (currentColorIndex < NUM_LEDS)
  {
    currentColor[currentColorIndex] = getRandomColor(currentColor[currentColorIndex - 1]);
    Serial.println(currentColor[currentColorIndex]);
    leds[currentColorIndex] = colour[currentColor[currentColorIndex]];
    FastLED.show();
  }
  else
  {
    setSystemState(SYS_STATE_WIN);
  }

}


void setSystemState(byte newState) {
  //set newState
  systemState = newState;
  switch (newState) {
    case SYS_STATE_START:
      currentColorIndex = 0;
      currentColor[currentColorIndex] = getRandomColor(99);
      Serial.println(currentColor[currentColorIndex]);
      leds[currentColorIndex] = colour[currentColor[currentColorIndex]];
      for (byte i = 1; i < NUM_LEDS; i++)
        leds[i] = CRGB::Black;
      FastLED.show();
      break;
    case SYS_STATE_WIN:
      serialmp3_play(5);
      blinkSuccess();
      setSystemState(0);
      Serial.println("system state  - success");
      break;
    case SYS_STATE_FAIL:
      serialmp3_play(6);
      blinkFail();
      setSystemState(0);
      Serial.println("system state  - fail");
      break;

  } //end switch
}

byte getRandomColor(byte avoidColor) {
  byte newColor;
  do {
    newColor = random(0, 4);
  } while (avoidColor == newColor);
  serialmp3_play(newColor + 1); // play color name
  return newColor;
}



void blinkSuccess() {
  for (byte z = 0; z < 3; z++) {
    for (byte i = 0; i < NUM_LEDS; i++)
      leds[i] = CRGB::Green;
    FastLED.show();
    delay(500);
    for (byte i = 0; i < NUM_LEDS; i++)
      leds[i] = CRGB::Black;
    FastLED.show();
    delay(500);
  }
}

void blinkFail() {
  for (byte z = 0; z < 3; z++) {
    for (byte i = 0; i < NUM_LEDS; i++)
      leds[i] = CRGB::Red;
    FastLED.show();
    delay(500);
    for (byte i = 0; i < NUM_LEDS; i++)
      leds[i] = CRGB::Black;
    FastLED.show();
    delay(500);
  }
}
serial_mp3.hArduino
#include <SoftwareSerial.h>
#include <Arduino.h>


//should connect to TX of the Serial MP3 Player module
#ifndef ARDUINO_RX
#define ARDUINO_RX   9
#endif
//connect to RX of the module
#ifndef ARDUINO_TX
#define ARDUINO_TX   8
#endif


#define CMD_SEL_DEV 0X09
#define DEV_TF 0X02

#define CMD_SET_DAC 0X1A
#define DAC_ON  0X00
#define DAC_OFF 0X01

#define CMD_VOLUME_UP 0X04
#define CMD_VOLUME_DOWN 0X05
#define CMD_SET_VOLUME 0X06

#define CMD_PLAY 0X0D
#define CMD_PLAY_W_INDEX 0X08
#define CMD_PLAY_W_VOL 0X22
#define CMD_PLAY_FOLDER_FILE 0X0F
#define CMD_STOP_PLAY 0X16
#define CMD_PAUSE 0X0E
#define CMD_SINGLE_CYCLE_PLAY 0X08
#define CMD_SINGLE_CYCLE 0X19
#define SINGLE_CYCLE_ON 0X00
#define SINGLE_CYCLE_OFF 0X01

#define CMD_NEXT_SONG 0X01
#define CMD_PREV_SONG 0X02

#define CMD_SLEEP_MODE 0X0A
#define CMD_WAKE_UP 0X0B
#define CMD_RESET 0X0C

#define CMD_FOLDER_CYCLE 0X17
#define CMD_SHUFFLE_PLAY 0X18

#define CMD_GROUP_DISPLAY 0X21

SoftwareSerial mp3_serial(ARDUINO_RX, ARDUINO_TX);


static int8_t Send_buf[8] = {
  0
}
;

byte serialmp3_incomingPointer = 0;
String serialmp3_answer = "";


void serialmp3_sendCommand(int8_t command, int16_t dat) {
  delay(20);
  Send_buf[0] = 0x7e; //starting byte
  Send_buf[1] = 0xff; //version
  Send_buf[2] = 0x06; //the number of bytes of the command without starting byte and ending byte
  Send_buf[3] = command; //
  Send_buf[4] = 0x01; //0x00 = no feedback, 0x01 = feedback
  Send_buf[5] = (int8_t)(dat >> 8);//datah
  Send_buf[6] = (int8_t)(dat); //datal
  Send_buf[7] = 0xef; //ending byte
  for (uint8_t i = 0; i < 8; i++) {
    mp3_serial.write(Send_buf[i]) ;
  }
}

void serialmp3_init() {
  mp3_serial.begin(9600);
  //Wait chip initialization is complete
  delay(500);
  //wait for 200ms
  serialmp3_sendCommand(CMD_SEL_DEV, DEV_TF);//select the TF card
  delay(200);

  serialmp3_answer.reserve(50);
}

void serialmp3_set_vol(byte vol) {
  serialmp3_sendCommand(CMD_SET_VOLUME, vol);
}

void serialmp3_play(byte folder, byte track) {
  //play the first song with volume 15 class: 0X0F01
  // serialmp3_sendCommand(CMD_PLAY_W_VOL, int(volume<< 8) | track);
  serialmp3_sendCommand(CMD_PLAY_FOLDER_FILE, folder << 8 | track); //>>>
}

void serialmp3_play(byte track) {
  serialmp3_play(1, track);
}

void serialmp3_stop() {
  serialmp3_sendCommand(CMD_STOP_PLAY, 0);
}

void serialmp3_pause() {
  serialmp3_sendCommand(CMD_PAUSE, 0);
}

void serialmp3_resume() {
  serialmp3_sendCommand(CMD_PLAY, 0);
}

void serialmp3_next() {
  serialmp3_sendCommand(CMD_NEXT_SONG, 0);
}

void serialmp3_prev() {
  serialmp3_sendCommand(CMD_PREV_SONG, 0);
}


// serial event - for incoming data


// answer buffer
static uint8_t serialmp3_ansbuf[10] = {0};
// send buffer
static int8_t serialmp3_Send_buf[8] = {0};
// flag for playing
boolean serialmp3_isPlaying = false;


String serialmp3_sbyte2hex(uint8_t b)
{
  String shex;
  shex = "0X";

  if (b < 16) shex += "0";
  shex += String(b, HEX);
  shex += " ";
  return shex;
}



void mp3_SerialProcess() {
  if (mp3_serial.available()) {
    uint8_t b = mp3_serial.read();
    if (b == 0x7E) {
      serialmp3_incomingPointer = 0;
      serialmp3_answer = "";
    }
    serialmp3_ansbuf[serialmp3_incomingPointer] = b;
    serialmp3_incomingPointer++;
    serialmp3_answer += serialmp3_sbyte2hex(b);
    if (b == 0xef) {
      switch (serialmp3_ansbuf[3]) {
        case 0x3A:
          serialmp3_answer += " -> Memory card inserted.";
          break;

        case 0x3D:
          serialmp3_isPlaying = false;
          serialmp3_answer += " -> Completed play num " + String(serialmp3_ansbuf[6], DEC);
          break;

        case 0x4C:
          serialmp3_answer += " -> Playing: " + String(serialmp3_ansbuf[6], DEC);
          break;

        case 0x41:
          serialmp3_answer += " -> Data recived correctly. ";
          break;
      } //end switch


      Serial.println(serialmp3_answer);
    } //end if
  } //enf if 
} //end void serialevent2

Schematics

Fritzign file
Fritzing bb me9kd9f0kx

Comments

Similar projects you might like

Space Trash Game using Arduino and OLED Display

Project tutorial by Pi BOTS MakerHub

  • 1,008 views
  • 2 comments
  • 9 respects

LCD Game

Project showcase by Team Iron_SalsaStudio

  • 16,351 views
  • 16 comments
  • 26 respects

Arduino LCD Video Game

Project tutorial by xXarduino_11Xx

  • 6,051 views
  • 3 comments
  • 21 respects

Arduino Controlled LED Multi-Player Game on Arduino "Shield"

Project tutorial by 22warehamD

  • 2,784 views
  • 3 comments
  • 11 respects

Play Dinosaur Game Using Arduino and Python3

Project tutorial by Jalal_Mansoori

  • 2,256 views
  • 1 comment
  • 7 respects

Interactive Pong PC Game with ADXL335 Accelerometer

Project tutorial by Mirko Pavleski

  • 1,156 views
  • 0 comments
  • 4 respects
Add projectSign up / Login