Detecting a change in state...

so, i have some code and its detecting accelerometer data.

what i need to happen;

when the accelerometer is static (still) for 5 seconds, something is triggered until movement is detected.

im using a variable (state) with numbers for letting me know whats happening.

state 12 is movement, states 1-6 are the x, y, z, inverted x, y, z,

at the moment, if the unit is flat, state=8.

//#include <Wire.h>
#include <ADXL345.h>
#include "FastLED.h"

ADXL345 adxl; //variable adxl is an instance of the ADXL345 library

#define NUM_LEDS 12
#define DATA_PIN 3
#define CLOCK_PIN 13
#define BRIGHTNESS 255

struct CRGB leds[NUM_LEDS];

int accuracy = 50;
int var = 1;  // initial sequence
int sensitivity = 210;
int fadeIn = 20;
int fadeOut = 10;
int state = 1;
int staticThreshold = 5;

// game variables
int gameOn = 3000;
int difficulty = 10000;

// core xyz
int x, y, z;

// mapped xyz
int xa, ya, za, xb, yb, zb;
int xaOld, yaOld, zaOld, xbOld, ybOld, zbOld;

// led xyz
int fr, ba, le, ri, to, bo;

// HSV colour values
//0 red, 32 orange, 64 yellow, 96 green, 128 aqua, 160 blue, 192 purple, 224 pink, 255 red
int ledColour = 160;
int resetColour = 160;

// time variables
unsigned long previousMillisSensor = 0;        // will store last time sensor was updated
const long intervalSensor = 50;           // interval at which to check sensor
unsigned long previousMillisLeds = 0;        // will store last time LED was updated
const long intervalLeds = 10;           // interval at which to operate leds
unsigned long previousMillisBreathe = 0;        // will store last time sensor was updated
const long intervalBreathe = 5000;           // interval at which to check sensor
int breatheTime = 1;    // interval for inactivity in seconds

void setup()
{
  adxl.powerOn();

  //set activity/ inactivity thresholds (0-255)
  adxl.setActivityThreshold(75); //62.5mg per increment
  adxl.setInactivityThreshold(75); //62.5mg per increment
  adxl.setTimeInactivity(breatheTime); // how many seconds of no activity is inactive?
 
  //look of activity movement on this axes - 1 == on; 0 == off 
  adxl.setActivityX(1);
  adxl.setActivityY(1);
  adxl.setActivityZ(1);
 
  //look of inactivity movement on this axes - 1 == on; 0 == off
  adxl.setInactivityX(1);
  adxl.setInactivityY(1);
  adxl.setInactivityZ(1);
 
  //look of tap movement on this axes - 1 == on; 0 == off
  adxl.setTapDetectionOnX(0);
  adxl.setTapDetectionOnY(0);
  adxl.setTapDetectionOnZ(1);
 
  //set values for what is a tap, and what is a double tap (0-255)
  adxl.setTapThreshold(50); //62.5mg per increment
  adxl.setTapDuration(15); //625μs per increment
  adxl.setDoubleTapLatency(80); //1.25ms per increment
  adxl.setDoubleTapWindow(200); //1.25ms per increment
 
  //set values for what is considered freefall (0-255)
  adxl.setFreeFallThreshold(7); //(5 - 9) recommended - 62.5mg per increment
  adxl.setFreeFallDuration(45); //(20 - 70) recommended - 5ms per increment
 
  //setting all interupts to take place on int pin 1
  //I had issues with int pin 2, was unable to reset it
  adxl.setInterruptMapping( ADXL345_INT_SINGLE_TAP_BIT,   ADXL345_INT1_PIN );
  adxl.setInterruptMapping( ADXL345_INT_DOUBLE_TAP_BIT,   ADXL345_INT1_PIN );
  adxl.setInterruptMapping( ADXL345_INT_FREE_FALL_BIT,    ADXL345_INT1_PIN );
  adxl.setInterruptMapping( ADXL345_INT_ACTIVITY_BIT,     ADXL345_INT1_PIN );
  adxl.setInterruptMapping( ADXL345_INT_INACTIVITY_BIT,   ADXL345_INT1_PIN );
 
  //register interupt actions - 1 == on; 0 == off  
  adxl.setInterrupt( ADXL345_INT_SINGLE_TAP_BIT, 1);
  adxl.setInterrupt( ADXL345_INT_DOUBLE_TAP_BIT, 1);
  adxl.setInterrupt( ADXL345_INT_FREE_FALL_BIT,  1);
  adxl.setInterrupt( ADXL345_INT_ACTIVITY_BIT,   1);
  adxl.setInterrupt( ADXL345_INT_INACTIVITY_BIT, 1);
  
//  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
  delay(2000);
  FastLED.addLeds<LPD8806, DATA_PIN, CLOCK_PIN, BRG>(leds, NUM_LEDS);
  FastLED.clear();
  FastLED.setBrightness(BRIGHTNESS);
}

void loop() {

  //getInterruptSource clears all triggered actions after returning value
  //so do not call again until you need to recheck for triggered actions
  byte interrupts = adxl.getInterruptSource();
  
  readXyz();

//  xaOld=xa; xbOld=xb; yaOld=ya; ybOld=yb; zaOld=za; zaOld=za; 

  showLeds();

  // movement detection   
  unsigned long currentMillisSensor = millis();
  if (currentMillisSensor - previousMillisSensor >= intervalSensor) {
  previousMillisSensor = currentMillisSensor;

  if ( abs(xaOld-xa>staticThreshold)
    || abs(xbOld-xb>staticThreshold)
    || abs(ybOld-yb>staticThreshold)
    || abs(ybOld-yb>staticThreshold) 
    || abs(zaOld-za>staticThreshold) 
    || abs(zbOld-zb>staticThreshold) ) {
    state=12;
    xaOld=xa; xbOld=xb; yaOld=ya; ybOld=yb; zaOld=za; zaOld=za; 
    }
  serialText();
  } // end of timed sensor check


  // check for NO movement
  unsigned long currentMillisBreathe = millis();
  if (currentMillisBreathe - previousMillisBreathe >= intervalBreathe) {
  previousMillisBreathe = currentMillisBreathe;
    state=2;
    serialText();
  }

//switch (state) {
//  case 1: // showLeds
//  showLeds();
//  serialText();
//  Serial.print("**breaLeds**");
//  break;
//  case 2: // breatheLeds
//  breatheLeds();
//  serialText();
//  Serial.print("**showLeds**");
//  break;
//}

} // end of void loop

void serialText() {
  Serial.print(le); Serial.print(" ");
  Serial.print(ri); Serial.print(" | ");
  Serial.print(ba); Serial.print(" ");
  Serial.print(fr); Serial.print(" | ");
  Serial.print(to); Serial.print(" ");
  Serial.print(bo); Serial.print(" || ");
  Serial.print(" movement: "); Serial.print(movement); Serial.print(" || ");
  Serial.print(" state: "); Serial.print(state); Serial.print(" || ");
  Serial.println();
}

void showLeds() {
  unsigned long currentMillisLeds = millis();
  if (currentMillisLeds - previousMillisLeds >= intervalLeds) {
  previousMillisLeds = currentMillisLeds;
      for (int i = 0; i < 2; i++) {
      leds[i]    =   CHSV( ledColour, 255, ba); // back
      leds[i+2]  =   CHSV( ledColour, 255, le); // left
      leds[i+4]  =   CHSV( ledColour, 255, fr); // front
      leds[i+6]  =   CHSV( ledColour, 255, ri); // right
      leds[i+8]  =   CHSV( ledColour, 255, to); // top
      leds[i+10] =   CHSV( ledColour, 255, bo); // bottom
    FastLED.show();
    }
  } // end of showLeds timed loops
} // end of showLeds

void breatheLeds() {
  unsigned long currentMillisLeds = millis();
  if (currentMillisLeds - previousMillisLeds >= intervalLeds) {
  previousMillisLeds = currentMillisLeds;
    float breathe = (exp(sin(millis()/1000.0*PI)) - 0.36787944)*108.0;
      for (int i = 0; i < 2; i++) {
      leds[i]    =   CHSV( ledColour, 255, breathe*ba); // back
      leds[i+2]  =   CHSV( ledColour, 255, breathe*le); // left
      leds[i+4]  =   CHSV( ledColour, 255, breathe*fr); // front
      leds[i+6]  =   CHSV( ledColour, 255, breathe*ri); // right
      leds[i+8]  =   CHSV( ledColour, 255, breathe*to); // top
      leds[i+10] =   CHSV( ledColour, 255, breathe*bo); // bottom
    FastLED.setBrightness(breathe);
    FastLED.show();
    }
  } // end of breatheLeds timed loop
} // end of breatheLeds

void readXyz () {
  adxl.readAccel(&x, &y, &z); //read the accelerometer values and store them in variables  x,y,z

  xa = map(x, -255, 255, 255, 0); // left
  xb = map(x, -255, 255, 0, 255); // right
  ya = map(y, -255, 255, 255, 0); // front
  yb = map(y, -255, 255, 0, 255); // back
  za = map(z, -255, 255, 0, 255); // top
  zb = map(z, -255, 255, 255, 0); // bottom
  
  if (xa < sensitivity) le -= fadeOut;  if (xa > sensitivity) le += fadeIn; if (le<0) le=0; if (le>255) le=255; state=3;
  if (ya < sensitivity) fr -= fadeOut;  if (ya > sensitivity) fr += fadeIn; if (fr<0) fr=0; if (fr>255) fr=255; state=4;
  if (za < sensitivity) to -= fadeOut;  if (za > sensitivity) to += fadeIn; if (to<0) to=0; if (to>255) to=255; state=5;
  if (xb < sensitivity) ri -= fadeOut;  if (xb > sensitivity) ri += fadeIn; if (ri<0) ri=0; if (ri>255) ri=255; state=6;
  if (yb < sensitivity) ba -= fadeOut;  if (yb > sensitivity) ba += fadeIn; if (ba<0) ba=0; if (ba>255) ba=255; state=7;     
  if (zb < sensitivity) bo -= fadeOut;  if (zb > sensitivity) bo += fadeIn; if (bo<0) bo=0; if (bo>255) bo=255; state=8;

//  byte interrupts = adxl.getInterruptSource();

}

whats happening with the current code, is that i get (every 50ms as requested) state 8 (sitting still), when i move the unit i get state 12 (movement) but then it returns to 8 for static (as expected) and then every 5 seconds i get a state 2.

so if i have something look for state 2, as soon as it starts it gets voided as immediately state 8 is triggered.

First, you need to indent properly. It makes it much harder to read, and that's what you're asking us to do. At a minimum, use the Auto Format feature in the IDE. Just press ctrl-T:

<snip>

  readXyz();

  //  xaOld=xa; xbOld=xb; yaOld=ya; ybOld=yb; zaOld=za; zaOld=za;

  showLeds();

  // movement detection
  unsigned long currentMillisSensor = millis();
  if (currentMillisSensor - previousMillisSensor >= intervalSensor) {
    previousMillisSensor = currentMillisSensor;

    if ( abs(xaOld-xa>staticThreshold)
      || abs(xbOld-xb>staticThreshold)
      || abs(ybOld-yb>staticThreshold)
      || abs(ybOld-yb>staticThreshold)
      || abs(zaOld-za>staticThreshold)
      || abs(zbOld-zb>staticThreshold) ) {
      state=12;
      xaOld=xa;
      xbOld=xb;
      yaOld=ya;
      ybOld=yb;
      zaOld=za;
      zaOld=za;
    }
    serialText();
  } // end of timed sensor check


  // check for NO movement
  unsigned long currentMillisBreathe = millis();
  if (currentMillisBreathe - previousMillisBreathe >= intervalBreathe) {
    previousMillisBreathe = currentMillisBreathe;
    state=2;
    serialText();
  }

  //switch (state) {
  //  case 1: // showLeds
  //  showLeds();
  //  serialText();
  //  Serial.print("**breaLeds**");
  //  break;
  //  case 2: // breatheLeds
  //  breatheLeds();
  //  serialText();
  //  Serial.print("**showLeds**");
  //  break;
  //}

} // end of void loop

void serialText() {
  Serial.print(le);
  Serial.print(" ");
  Serial.print(ri);
  Serial.print(" | ");
  Serial.print(ba);
  Serial.print(" ");
  Serial.print(fr);
  Serial.print(" | ");
  Serial.print(to);
  Serial.print(" ");
  Serial.print(bo);
  Serial.print(" || ");
  Serial.print(" movement: ");
  Serial.print(movement);
  Serial.print(" || ");
  Serial.print(" state: ");
  Serial.print(state);
  Serial.print(" || ");
  Serial.println();
}

void showLeds() {
  unsigned long currentMillisLeds = millis();
  if (currentMillisLeds - previousMillisLeds >= intervalLeds) {
    previousMillisLeds = currentMillisLeds;
    for (int i = 0; i < 2; i++) {
      leds[i]    =   CHSV( ledColour, 255, ba); // back
      leds[i+2]  =   CHSV( ledColour, 255, le); // left
      leds[i+4]  =   CHSV( ledColour, 255, fr); // front
      leds[i+6]  =   CHSV( ledColour, 255, ri); // right
      leds[i+8]  =   CHSV( ledColour, 255, to); // top
      leds[i+10] =   CHSV( ledColour, 255, bo); // bottom
      FastLED.show();
    }
  } // end of showLeds timed loops
} // end of showLeds

void breatheLeds() {
  unsigned long currentMillisLeds = millis();
  if (currentMillisLeds - previousMillisLeds >= intervalLeds) {
    previousMillisLeds = currentMillisLeds;
    float breathe = (exp(sin(millis()/1000.0*PI)) - 0.36787944)*108.0;
    for (int i = 0; i < 2; i++) {
      leds[i]    =   CHSV( ledColour, 255, breathe*ba); // back
      leds[i+2]  =   CHSV( ledColour, 255, breathe*le); // left
      leds[i+4]  =   CHSV( ledColour, 255, breathe*fr); // front
      leds[i+6]  =   CHSV( ledColour, 255, breathe*ri); // right
      leds[i+8]  =   CHSV( ledColour, 255, breathe*to); // top
      leds[i+10] =   CHSV( ledColour, 255, breathe*bo); // bottom
      FastLED.setBrightness(breathe);
      FastLED.show();
    }
  } // end of breatheLeds timed loop
} // end of breatheLeds

void readXyz () {
  adxl.readAccel(&x, &y, &z); //read the accelerometer values and store them in variables  x,y,z

    xa = map(x, -255, 255, 255, 0); // left
  xb = map(x, -255, 255, 0, 255); // right
  ya = map(y, -255, 255, 255, 0); // front
  yb = map(y, -255, 255, 0, 255); // back
  za = map(z, -255, 255, 0, 255); // top
  zb = map(z, -255, 255, 255, 0); // bottom

  if (xa < sensitivity) le -= fadeOut;
  if (xa > sensitivity) le += fadeIn;
  if (le<0) le=0;
  if (le>255) le=255;
  state=3;
  if (ya < sensitivity) fr -= fadeOut;
  if (ya > sensitivity) fr += fadeIn;
  if (fr<0) fr=0;
  if (fr>255) fr=255;
  state=4;
  if (za < sensitivity) to -= fadeOut;
  if (za > sensitivity) to += fadeIn;
  if (to<0) to=0;
  if (to>255) to=255;
  state=5;
  if (xb < sensitivity) ri -= fadeOut;
  if (xb > sensitivity) ri += fadeIn;
  if (ri<0) ri=0;
  if (ri>255) ri=255;
  state=6;
  if (yb < sensitivity) ba -= fadeOut;
  if (yb > sensitivity) ba += fadeIn;
  if (ba<0) ba=0;
  if (ba>255) ba=255;
  state=7;
  if (zb < sensitivity) bo -= fadeOut;
  if (zb > sensitivity) bo += fadeIn;
  if (bo<0) bo=0;
  if (bo>255) bo=255;
  state=8;

  //  byte interrupts = adxl.getInterruptSource();

}

Pretty, no? Now then...

I'm not sure this is doing what you meant:

    if ( abs(xaOld-xa>staticThreshold)
      || abs(xbOld-xb>staticThreshold) ...

Let's work from the inside out:

    xaOld-xa

This does a subtraction, and returns an int. Then you compare that int with another int:

    int > staticThreshold

which results in a bool, or a true/false value. Conditional statements like if and while require a boolean expression like that. But you then take the absolute value of that boolean result:

    abs(true)

Because the abs function takes an int, the compiler converts the true/false value to 1 or 0, respectively, and passes that int to the abs function:

    abs(1)

So abs will return 1 or 0 for the if to test:

    if ( 1

But the if statement requires a bool, so the compiler converts the 1 or 0 to a true or false value, respectively:

    if ( true

This effectively reduces your original code to this:

    if ( (xaOld-xa>staticThreshold)
      || (xbOld-xb>staticThreshold) ...

The abs portion is effectively ignored. I think you meant to do this:

    if ( (abs( xaOld-xa ) > staticThreshold)
      || (abs( xbOld-xb ) > staticThreshold) ...

so that the test compares the absolute value of the difference between the old and new values. Honestly, spaces are cheap. Add them to make it more readable. And add those extra parentheses so the evaluation order is completely obvious to the reader, and the compiler. You may be getting warnings about that, anyway.

Next, let's look at your "states" code... Auto Format will split multi-statement lines like this:

  if (xa < sensitivity) le -= fadeOut;  if (xa > sensitivity) le += fadeIn; if (le<0) le=0; if (le>255) le=255; state=3;

...into single-statement lines like this:

  if (xa < sensitivity) le -= fadeOut;
  if (xa > sensitivity) le += fadeIn;
  if (le<0) le=0;
  if (le>255) le=255;
  state=3;
  if (ya < sensitivity) fr -= fadeOut;
  if (ya > sensitivity) fr += fadeIn;
  if (fr<0) fr=0;
  if (fr>255) fr=255;
  state=4;
  if (za < sensitivity) to -= fadeOut;
  if (za > sensitivity) to += fadeIn;
  if (to<0) to=0;
  if (to>255) to=255;
  state=5;
  if (xb < sensitivity) ri -= fadeOut;
  if (xb > sensitivity) ri += fadeIn;
  if (ri<0) ri=0;
  if (ri>255) ri=255;
  state=6;
  if (yb < sensitivity) ba -= fadeOut;
  if (yb > sensitivity) ba += fadeIn;
  if (ba<0) ba=0;
  if (ba>255) ba=255;
  state=7;
  if (zb < sensitivity) bo -= fadeOut;
  if (zb > sensitivity) bo += fadeIn;
  if (bo<0) bo=0;
  if (bo>255) bo=255;
  state=8;

Unfortunately, this is nonsense. It will always set state to 8, because none of the state assignments are in an if/else test. The last assignment to 8 is always executed.

I think you need to read about the if statement and using curly braces, "{}", to have multiple statements in the if/else sections. Perhaps you meant something like:

  if (xa < sensitivity)
    le -= fadeOut;
  else
    le += fadeIn;
  if (le<0)
    le=0;
  else if (le>255)
    le=255;

But I have no idea when the state variable should change. From your description, they sound more like flags, not states. If it's moving, you want state 12. But if it's moving, one of the accel values is positive or negative (i.e., non-zero), which you want to be "states" 1-6. Contradictory.

Cheers,
/dev

hey!

not sure how then, but the following line is actually working

if ( abs(xaOld-xa>staticThreshold)

however, i was on the belief that abs() returned positive integers, to which is not the case, i have replaced the code with the following and is working as it should be.

if ( (xaOld-xa)>staticThreshold )

state in my code is a flag for showing me what the program is currently doing, and i had realised the error with the queue of state changes and then always returning the last change, and have tweaked the code to return the state change when the value hits 255 (on), this is now working correctly.

the states specifically point to a direction that the accelerometer is facing, top 3, bottom 4, left 5, etc, and state 12 is the detection of movement.

the led linked to the state is a solid light, but then when the unit is not moved for a period of time then the led starts fading. as i am constantly detecting a position state, the 5 second code works, but only for a fraction of time every 5 seconds.

also, is there any benefit for using if () else () over multiple if() commands? or good practice.

thanks for your help!

Evaluating one if statement instead of multiple saves time and code space. It may lead to a different result as well. Specifics would help, but if you have specifics then you can do the time and space benchmarks.

not sure how then, but the following line is actually working

It is working for some definition of work. It does not do what you seem to think it is doing.

however, i was on the belief that abs() returned positive integers, to which is not the case

Nonsense. abs() DOES return ONLY positive integers (or 0).

hey.

i introduced a new function, and a whole new set of variables and ive got the result i wanted;

void stateRead() {
  unsigned long currentMillisBreathe = millis(); // initialise time

  stateOld=state; // set state
  
  if (state==12) { mode=2; previousMillisBreathe = currentMillisBreathe; } // movement detected, change mode, reset timer
 
 if (stateOld==state && (currentMillisBreathe - previousMillisBreathe >= intervalBreathe) ) {
  previousMillisBreathe = currentMillisBreathe;
  mode=1; // check to see if the current state is the same in after the timeframe, if so, change mode, reset timer
  } 
} // end of void stateRead

There is no reason to use Millis in the name of the variable if it contributes nothing. currentBreatheTime and previousBreatheTime make a lot more sense to me.

Magic numbers, like 12, contribute nothing but confusion. Look into #define statements or enums to use names in place of magic numbers.