Turn LED ON by using an accelerometer on RC car

Hi Everyone,

Seeking some assistance and help.

I am working on a project where i have a 3 axis accelerometer fitted on a RC car. The purpose is to demonstrate when the car impacts an objects, a LED is activated. I am using an Arduino Uno and MMA8451 accelerometer for this project.
I am looking to adapt my code to set g-force limits (example 0.4g) to temporarily activate a orange LED's for 5 seconds when the car impacts an object,then go off. If this Orange LED is activated three times a second (Red) LED is permanently activated and stays ON until the program or button is reset.

The current code I am using operates differently, Red LED comes on at the 2nd impact instead of the 4th impact. (see below)

1st impact - Orange LED ON for 5 seconds then go's OFF.
2nd impact - Orange LED ON for 10 seconds then go's OFF, After 5 seconds of this the Red LED comes ON and stays ON.
3rd impact - Orange LED ON for 5 seconds then go's OFF.
4th impact - Orange LED ON for 5 seconds then go's OFF

Can anybody help with adapting the below code so the LED will come ON when g-force limit is reached ?

#include <Wire.h>
#include <Adafruit_MMA8451.h>
#include <Adafruit_Sensor.h>

//#define TEST_RESULTANT      1               //comment out to exclude resultant vector from g check
#define MAX_G               3.0             //g limit over which an "event" is logged (org LED etc) (individual axes)
#define MAX_G_RESULTANT     3.0             //g limit over which an "event" is logged (org LED etc) (resultant vector only)
#define ORG_LED_ONTIME      5000ul          //mS orange LED flash time when G limit exceeded
#define EVENTS_LIMIT        3.0             //# of times G limit can be exceeded before RED LED comes on
#define ERROR_BLINK         300ul           //mS blink time for error condition
//
#define SAMPLE_RATE         5000            //uS (microseconds) per sample
#define LOGGING_RATE        100             //mS between logging file writes 

const int pinORANGE =       13;             //pin driving orange LED. Logic assumes HIGH turns LED on
const int pinRED =          12;             //pin driving red LED. Logic assumes HIGH turns LED on

Adafruit_MMA8451 mma = Adafruit_MMA8451();

char
   szStr[50];

//accumulators for x, y and z axes averaging
//samples are taken once per millisecond
//average is written to SD card at 100mS intervals 
float
   xg_f,
   yg_f,
   zg_f,    
   xg_sum,
   yg_sum,
   zg_sum;
unsigned long
   sample_count;
bool
   bGLimitExceeded;
   
void setup() 
{
   // Open serial communications and wait for port to open:
   Serial.begin(9600);
   pinMode( pinORANGE, OUTPUT );
   digitalWrite( pinORANGE, LOW );         //LED off.
   pinMode( pinRED, OUTPUT );
   digitalWrite( pinRED, LOW );            //LED off.
       
   Serial.println("Adafruit MMA8451 OK");
   if (!mma.begin()) 
   {
       Serial.println("###Failed to start MMA8451.");
       ErrorLED();
       
   }//if
   else
       Serial.println("MMA8451 found!");
 
   mma.setRange(MMA8451_RANGE_4_G);        //need +/-4g for checking >3g...
   Serial.print(" Range = "); Serial.print(2 << mma.getRange());  
   Serial.println("G");
   
   bGLimitExceeded = false;
   xg_sum = 0.0;
   yg_sum = 0.0;
   zg_sum = 0.0;
   sample_count = 0;
   
}//setup

//if an error occurs during init, LEDs alternate blinking rapidly
void ErrorLED( void )
{
   bool
       bLEDState = false;
   unsigned long
       timeBlink = 0;
   unsigned long
       timeNow;

   while( true )
   {
       timeNow = millis();
       //time for a blink?
       if( timeNow - timeBlink >= ERROR_BLINK )
       {
           //yes, save time
           timeBlink = timeNow;
           //toggle flag indicating LED state
           bLEDState ^= true;
           //update orange and red LEDs
           digitalWrite( pinORANGE, (bLEDState)?HIGH:LOW );
           digitalWrite( pinRED, (bLEDState)?LOW:HIGH );
           
       }//if
   
   }//while
   
}//ErrorLED

void TakeSamples()
{
   float
       intermediate,
       resultant;
   static unsigned long
       timeSample = 0;
   unsigned long
       timeNow;

   timeNow = micros();
   if( (timeNow - timeSample ) < SAMPLE_RATE )
       return;
   timeSample = timeNow;

   mma.read();
   xg_f = (float)mma.x/2048.0;
   yg_f = (float)mma.y/2048.0;
   zg_f = (float)mma.z/2048.0;
   
   xg_sum = xg_sum + xg_f;
   yg_sum = yg_sum + yg_f;
   zg_sum = zg_sum + zg_f;
   sample_count = sample_count + 1;

   //look for excursions over pre-set limits for LED flag
   //include check of resultant vector; can comment this out
   //if not desired
#ifndef TEST_RESULTANT
   resultant = 0.0; 
#else
   resultant = sqrt( pow(xg_f,2) + pow(yg_f,2) + pow(zg_f,2) );
#endif

   if( (abs(xg_f) > MAX_G ) ||
           (abs(yg_f) > MAX_G ) ||
               (abs(zg_f) > MAX_G ) ||
                   resultant > MAX_G_RESULTANT )
   {
       bGLimitExceeded = true;
          
   }//if
   
}//TakeSamples

#define LED_IDLE        0
#define LED_TIME        1
void LED_StateMachine( void )
{
   static int
       nEvents = 0;
   static byte
       stateLED = LED_IDLE;
   static unsigned long
       timeLED;
   unsigned long
       timeNow;

   switch( stateLED )
   {
       case    LED_IDLE:
           if( bGLimitExceeded )
           {
               bGLimitExceeded = false;
               nEvents++;
               if( nEvents >= EVENTS_LIMIT )
                   digitalWrite( pinRED, HIGH );
                   
               digitalWrite( pinORANGE, HIGH );
               timeLED = millis();
               stateLED = LED_TIME;
               
           }//if
       break;

       case    LED_TIME:
           if( (millis() - timeLED) >= ORG_LED_ONTIME )
           {
               digitalWrite( pinORANGE, LOW );
               stateLED = LED_IDLE;
               
           }//if
       break;
   
   }//switch
   
}//LED_StateMachine

void Logging( void )
{
   float
       x, y, z;
       
   static unsigned long
       timeLogging = 0;
   unsigned long
       timeNow;

   timeNow = millis();
   if( (timeNow - timeLogging) < LOGGING_RATE )
       return;
   timeLogging = timeNow;

   x = xg_sum / (float)sample_count;
   y = yg_sum / (float)sample_count;
   z = zg_sum / (float)sample_count;
   xg_sum = 0.0;
   yg_sum = 0.0;
   zg_sum = 0.0;
   sample_count = 0;
 
    
   Serial.print("\tX:\t"); Serial.print(x); 
   Serial.print("\tY:\t"); Serial.print(y); 
   Serial.print("\tZ:\t"); Serial.println(z); 

}//Logging

void loop() 
{
   //samping, logging and LED control done in their own functions
   TakeSamples();
   Logging();
   LED_StateMachine();
     
}//loop

Arduino Schematic - 3.pdf (134 KB)

can you clarify the states ?

assume you start in a IDLE state

do you mean that after 1st impact, orange led on for 5 seconds and then off IF NO IMPACT DURING THOSE 5 SECONDS and going back to IDLE state?

Otherwise if second impact within 5 seconds of the first one, I'm unclear what needs to happen? Are you seconds "cumulative" or you just stay Orange for 5 more seconds and then turn red ?

and then what if no impact happens during the second stage? do you just go back to the IDLE stage?

same for the others

Hi,
With the RC car running in normal idle condition (driving slowly) there is no LED ON. If the car then impacts an object and the accelerometer reach g-force greater then 0.4g, it temporarily actives the Orange LED for 5 seconds then de-activates it. If the car impacts an object for a 2nd and 3rd time each time activating the Orange LED for 5 seconds then deactivating, then on the 4th impact both the Orange and Red LED is activated at impact but Red stay activated until the Ardunio UNO reset button is pressed.
There is no time limit between the Orange LED's been activated, so what I want is,

1st impact - Orange LED ON for 5 seconds then go's OFF.
2nd impact - Orange LED ON for 5 seconds then go's OFF,
3rd impact - Orange LED ON for 5 seconds then go's OFF.
4th impact - Orange LED ON for 5 seconds then go's OFF also Red LED ON same time but stays on until reset.

But instead the code is given me,

1st impact - Orange LED ON for 5 seconds then go's OFF.
2nd impact - Orange LED ON for 10 seconds then go's OFF, After 5 seconds of this the Red LED comes ON and stays ON.
3rd impact - Orange LED ON for 5 seconds then go's OFF.
4th impact - Orange LED ON for 5 seconds then go's OFF

What do I need to change in the code to fix it ?

Hi Stephen

Try this piece and feedback, I edited to include a counter for impact.

#include <Wire.h>
#include <Adafruit_MMA8451.h>
#include <Adafruit_Sensor.h>

//#define TEST_RESULTANT      1               //comment out to exclude resultant vector from g check
#define MAX_G               3.0             //g limit over which an "event" is logged (org LED etc) (individual axes)
#define MAX_G_RESULTANT     3.0             //g limit over which an "event" is logged (org LED etc) (resultant vector only)
#define ORG_LED_ONTIME      5000ul          //mS orange LED flash time when G limit exceeded
#define EVENTS_LIMIT        3.0             //# of times G limit can be exceeded before RED LED comes on
#define ERROR_BLINK         300ul           //mS blink time for error condition
//
#define SAMPLE_RATE         5000            //uS (microseconds) per sample
#define LOGGING_RATE        100             //mS between logging file writes 

const int pinORANGE =       13;             //pin driving orange LED. Logic assumes HIGH turns LED on
const int pinRED =          12;             //pin driving red LED. Logic assumes HIGH turns LED on

Adafruit_MMA8451 mma = Adafruit_MMA8451();

char
   szStr[50];

//accumulators for x, y and z axes averaging
//samples are taken once per millisecond
//average is written to SD card at 100mS intervals 
float
   xg_f,
   yg_f,
   zg_f,    
   xg_sum,
   yg_sum,
   zg_sum;
unsigned long
   sample_count;
bool
   bGLimitExceeded;
int bGLimitCount = 1;   
void setup() 
{
   // Open serial communications and wait for port to open:
   Serial.begin(9600);
   pinMode( pinORANGE, OUTPUT );
   digitalWrite( pinORANGE, LOW );         //LED off.
   pinMode( pinRED, OUTPUT );
   digitalWrite( pinRED, LOW );            //LED off.
       
   Serial.println("Adafruit MMA8451 OK");
   if (!mma.begin()) 
   {
       Serial.println("###Failed to start MMA8451.");
       ErrorLED();
       
   }//if
   else
       Serial.println("MMA8451 found!");
 
   mma.setRange(MMA8451_RANGE_4_G);        //need +/-4g for checking >3g...
   Serial.print(" Range = "); Serial.print(2 << mma.getRange());  
   Serial.println("G");
   
   bGLimitExceeded = false;
   xg_sum = 0.0;
   yg_sum = 0.0;
   zg_sum = 0.0;
   sample_count = 0;
   
}//setup

//if an error occurs during init, LEDs alternate blinking rapidly
void ErrorLED( void )
{
   bool
       bLEDState = false;
   unsigned long
       timeBlink = 0;
   unsigned long
       timeNow;

   while( true )
   {
       timeNow = millis();
       //time for a blink?
       if( timeNow - timeBlink >= ERROR_BLINK )
       {
           //yes, save time
           timeBlink = timeNow;
           //toggle flag indicating LED state
           bLEDState ^= true;
           //update orange and red LEDs
           digitalWrite( pinORANGE, (bLEDState)?HIGH:LOW );
           digitalWrite( pinRED, (bLEDState)?LOW:HIGH );
           
       }//if
   
   }//while
   
}//ErrorLED

void TakeSamples()
{
   float
       intermediate,
       resultant;
   static unsigned long
       timeSample = 0;
   unsigned long
       timeNow;

   timeNow = micros();
   if( (timeNow - timeSample ) < SAMPLE_RATE )
       return;
   timeSample = timeNow;

   mma.read();
   xg_f = (float)mma.x/2048.0;
   yg_f = (float)mma.y/2048.0;
   zg_f = (float)mma.z/2048.0;
   
   xg_sum = xg_sum + xg_f;
   yg_sum = yg_sum + yg_f;
   zg_sum = zg_sum + zg_f;
   sample_count = sample_count + 1;

   //look for excursions over pre-set limits for LED flag
   //include check of resultant vector; can comment this out
   //if not desired
#ifndef TEST_RESULTANT
   resultant = 0.0; 
#else
   resultant = sqrt( pow(xg_f,2) + pow(yg_f,2) + pow(zg_f,2) );
#endif

   if( (abs(xg_f) > MAX_G ) ||
           (abs(yg_f) > MAX_G ) ||
               (abs(zg_f) > MAX_G ) ||
                   resultant > MAX_G_RESULTANT )
   {
    bgbGLimitCount +=1;
    if(bgbGLimitCount > 3)//once it's greater than 4
    {
       bGLimitExceeded = true;
    }
          
   }//if
   
}//TakeSamples

#define LED_IDLE        0
#define LED_TIME        1
void LED_StateMachine( void )
{
   static int
       nEvents = 0;
   static byte
       stateLED = LED_IDLE;
   static unsigned long
       timeLED;
   unsigned long
       timeNow;

   switch( stateLED )
   {
       case    LED_IDLE:
           if( bGLimitExceeded )
           {
               bGLimitExceeded = false;
               nEvents++;
               if( nEvents >= EVENTS_LIMIT )
               {
                   digitalWrite( pinRED, HIGH ); 
               digitalWrite( pinORANGE, HIGH );
               }
               timeLED = millis();
               stateLED = LED_TIME;
               
           }//if
       break;

       case    LED_TIME:
           if( (millis() - timeLED) >= ORG_LED_ONTIME )
           {
               digitalWrite( pinORANGE, LOW );
               stateLED = LED_IDLE;
               
           }//if
       break;
   
   }//switch
   
}//LED_StateMachine

void Logging( void )
{
   float
       x, y, z;
       
   static unsigned long
       timeLogging = 0;
   unsigned long
       timeNow;

   timeNow = millis();
   if( (timeNow - timeLogging) < LOGGING_RATE )
       return;
   timeLogging = timeNow;

   x = xg_sum / (float)sample_count;
   y = yg_sum / (float)sample_count;
   z = zg_sum / (float)sample_count;
   xg_sum = 0.0;
   yg_sum = 0.0;
   zg_sum = 0.0;
   sample_count = 0;
 
    
   Serial.print("\tX:\t"); Serial.print(x); 
   Serial.print("\tY:\t"); Serial.print(y); 
   Serial.print("\tZ:\t"); Serial.println(z); 

}//Logging

void loop() 
{
   //samping, logging and LED control done in their own functions
   TakeSamples();
   Logging();
   LED_StateMachine();
     
}//loop