Fix My Tilt (Simon Says) Game

Hi!

I have tried to figure this out myself but am a bit in above my head here.
What I already have is an arduino game of Simon Says (Simon. The memory game to test your concentration. Play my simon free online at www.freegames.ws) but instead of the 4 buttons I use tilt in 4 directions measured by an accelerometer (MPU6050). My problem is that when the sensor is tilted past the treshold it keeps returning values for that side or 'button'. What I want to achieve is that once the sensor is tilted and 1 value is returned, the sensor has to be returned into neutral position, before being able to return a second tilt value. For example: when the sequence is 'right-right-left' the movement of the sensor should be 'neutral-right-neutral-right-neutral-left-neutral'.

This is my code responsible for reading out and smoothing the sensor values:

#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"

MPU6050 accelgyro;

int16_t ax, ay, az;

int getTilt()
{
  // read raw accel measurements from device
  accelgyro.getAcceleration(&ax, &ay, &az);
     
  if (ax < -7000) 
  {
    return 4;
  }
  if (ax > 7000) 
  {
    return 3;
  }
  if (az < -7000)
  {
    return 2; 
  }
  if (az > 7000)
  {
    return 1;    
  }
   return 0;

// Array Filter to cancel out sensor fluctuations
int getTiltAverage() {
  int arrayLength = 20;
  int tiltValues[arrayLength];
  int zeros = 0,ones = 0, twos = 0, threes = 0, fours = 0;
  for (int i=0; i < arrayLength; i++) { tiltValues[i] = getTilt(); delay(15); }
  for (int i=0; i < arrayLength; i++) {
    switch (tiltValues[i]) {
    case 0:
      zeros++;
      break;
    case 1:
      ones++;
      break;
    case 2:
      twos++;
      break;
    case 3:
      threes++;
      break;
    case 4:
      fours++;
      break;
    }
  }
  //90% of getTilt() returns has to be identical to rule out any unwanted fluctuations from the sensor
  if ((ones/arrayLength) > 0.9) return 1;
  if ((twos/arrayLength) > 0.9) return 2;
  if ((threes/arrayLength) > 0.9) return 3;
  if ((fours/arrayLength) > 0.9) return 4;
  if ((zeros/arrayLength) > 0.9) return 0;
  return -1;
}
}

From the programming questions I received the following response but after looking into it, I am not able to implement this in my code:

If you want to smooth the returned values, you can just use an decaying average for each value you read from the sensor - that only needs one global/static variable for each value, and no re-reading.

The second part of your problem is that you want to wait until the input returns to center before you accept another input. To do that I'd define an enumerated type with values for each possible position, and a global/static variable holding the current reported position. Each time your function to read the state is called, you will read the device and recalculate the smoothed values, determine the instantaneous position (as an enumerated value) by comparing the smoothed values against your thresholds, and compare the instantaneous position against the reported position to decide whether to update the reported position. Finally, return the current reported position.

I'd implement that with one function to read the sensor and smooth the values, another function to do the threshold comparisons and return the position as an enumerated value, and finally the function which is called to update and return the currently reported position. All together you would be looking at about a dozen lines of code.

I really hope someone is willing to give me a hand here, so that I can get to the next step in my project. I would like to reward the helper while I am on a student budget but we will figure something out surely :).

Reflex:

  if ((ones/arrayLength) > 0.9) return 1;

I think the mixture of integer and float math will be a problem. Try:

  if (ones > (0.9*arrayLength)) return 1;

The array to filter out the fluctuations actually does its job.
My problem is with the 'return to neutral first' part.

  int arrayLength = 20;
  int tiltValues[arrayLength];
  int zeros = 0,ones = 0, twos = 0, threes = 0, fours = 0;
  for (int i=0; i < arrayLength; i++) { tiltValues[i] = getTilt(); delay(15); }
  for (int i=0; i < arrayLength; i++) {
    switch (tiltValues[i]) {
    case 0:
      zeros++;
      break;
    case 1:
      ones++;
      break;
    case 2:
      twos++;
      break;
    case 3:
      threes++;
      break;
    case 4:
      fours++;
      break;
    }
  }

There is no reason to store all the values in an array and then walk the array. Just increment zeros, ones, twos, threes, or fours, as each reading comes in.

This is my code responsible for reading out and smoothing the sensor values:

And the code that uses that data?

The array to filter out the fluctuations actually does its job.
My problem is with the 'return to neutral first' part.

It's useful to explain what the problem is. And, there is nothing in the code that I've seen that provides context for the 'return to neutral first' phrase.

Reflex:
The array to filter out the fluctuations actually does its job.
My problem is with the 'return to neutral first' part.

Perhaps instead of showing the part of the code that works you should show the part of the code that doesn't.

Reflex:
I really hope someone is willing to give me a hand here, so that I can get to the next step in my project. I would like to reward the helper while I am on a student budget but we will figure something out surely :).

Break it down into really simple steps.

How do you know when the user has moved the device to the right?
How do you know when the user has returned the device to neutral?

Here is my suggestion:

Software for buttons usually has 3 events:

KeyDown
KeyUp
KeyPress

and they are fired in that sequence.

In other words: if you press down a key (KeyDown event), and it is still pressed down (no KeyUp event yet), a KeyPress event is not generated. It is only when you release the key (KeyUp), that the KeyUp and KeyPress events are generated.

Let's create our own events, and call them:

TiltDown - the device is not neutral (save this tilt as LastTilt)
TiltUp - the device is neutral (and LastTilt is NOT neutral)
TiltComplete - TiltUp has occurred

So, "remember" that they tilted in a certain direction, then carry out your game processing as soon as they return to neutral (TiltUp, and therefore TiltComplete).

Once the TiltComplete is achieved, you can deal the tilt.

" 'right-right-left' the movement of the sensor should be 'neutral-right-neutral-right-neutral-left-neutral'."

You can do this with just a few variables:

SimonCtr // 3 for right-right-left
SimonArray

TiltSequence array
TiltCtr
LastTilt byte
CurrentTilt byte

Psuedo code:

void loop() {
while (getTilt != NEUTRAL) { // wait for them to level the device };

currentTilt = getTilt;

if (CurrentTilt is NEUTRAL) {
if (LastTilt is not NEUTRAL) { // TiltUp has occurred
//a tilt (the LastTilt) has occurred (TiltComplete).
TiltSequence[TiltCtr++] = LastTilt;
LastTilt = NEUTRAL;
}
}
else {
LastTilt = CurrentTilt; // TiltDown has occurred - remember the tilt!
}
if (SimonCtr == TiltCtr) {
// compare all TiltCtr tilts with the Simon tilts to see if they did it right. - you can do this on the fly as well, your choice
}
}

HTH