I recently bought an ADXL345 accelerometer and I'm currently trying to make some interesting things with it. I successfully managed to interface it with an arduino uno, get some simple readings, calculate angles etc. I even managed to make the Arduino sleep(based on Mr. Gammon's materials on this topic) for some time, wake up, take readings and sleep again.
BUT...what I wanna do now is:
I want to detect Inactivity -> put Arduino to sleep and when an interrupt(Activity occurs) from the ADXL345 is detected -> wake up -> take readings, proccess them etc -> when Inactivity is detected again go to sleep and wait for the interrupt. My wiring for interrupts is (INT1 from ADXL345 to D3 Pin of Arduino UNO).
My code is something like this:
#include <Wire.h>
#include <ADXL345.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
ADXL345 adxl;
//WATCHdog interrupt
ISR(WDT_vect)
{
wdt_disable(); //disable watchdog
}
void sleepNow()
{
Serial.println("Start sleep");
delay(50);
adxl.getInterruptSource();
ADCSRA = 0;
// clear various "reset" flags
MCUSR = 0;
// allow changes, disable reset
WDTCSR = bit (WDCE) | bit (WDE);
// set interrupt mode and an interval
WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0); // set WDIE, and 1 second delay
wdt_reset(); // pat the dog
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_enable();
// Do not interrupt before we go to sleep, or the
// ISR will detach interrupts and we won't wake.
noInterrupts ();
// will be called when pin D2 goes low
attachInterrupt (1, wakeNow, CHANGE);
EIFR = bit (INTF1);
// turn off brown-out enable in software
// BODS must be set to one and BODSE must be set to zero within four clock cycles
MCUCR = bit (BODS) | bit (BODSE);
// The BODS bit is automatically cleared after three clock cycles
MCUCR = bit (BODS);
// We are guaranteed that the sleep_cpu call will be done
// as the processor executes the next instruction after
// interrupts are turned on.
interrupts (); // one cycle
sleep_cpu (); // one cycle
//Serial.println("Stop sleep");
}
void wakeNow()
{
sleep_disable();
detachInterrupt(1);
}
void setup(void)
{
Serial.begin(115200);
if (!adxl.begin())
{
Serial.println("Something is wrong!");
delay(1000);
}
else
{
Serial.println("ALL GOOD :D");
adxl.setRange(ADXL345_RANGE_16G);
adxl.setDataRate(ADXL345_DATARATE_800HZ);
}
//set activity/ inactivity thresholds (0-255)
adxl.setActivityThreshold(20); //62.5mg per increment
adxl.setInactivityThreshold(75); //62.5mg per increment
adxl.setTimeInactivity(20); // how many seconds of no activity is inactive?
//look of activity movement on this axes - 1 == on; 0 == off
adxl.setXActivity(1);
adxl.setXActivity(1);
adxl.setXActivity(1);
//look of inactivity movement on this axes - 1 == on; 0 == off
adxl.setXInactivity(1);
adxl.setYInactivity(1);
adxl.setYInactivity(1);
//setting all interupts to take place on int pin 1
adxl.setInterruptMapping( ADXL345_ACTIVITY, ADXL345_INT1);
adxl.setInterruptMapping( ADXL345_INACTIVITY, ADXL345_INT1);
//register interupt actions - 1 == on; 0 == off
adxl.setInterrupt( ADXL345_ACTIVITY, 1);
adxl.setInterrupt( ADXL345_INACTIVITY, 1);
}
void loop(void)
{
byte interrupts = adxl.getInterruptSource();
if (adxl.triggered(interrupts, ADXL345_INACTIVITY)) {
Serial.flush();
sleepNow();
Serial.flush();
}
/* after sleep take readings or whatever */
delay(1000);
}
The problem is that no matter how much I move, tilt, shake the ADXL345 while Arduino is asleep nothing happens...Arduino sleeps for 8sec(aprox.), wakes up - if while awake something happens I get the readings and then it goes back to sleep. I tried to watch wheter or not the interrupt triggers on the osciloscope(on the INT1 Pin from ADXL) and what I get is: when the Inactivity is detected the INT1 goes HIGH, then LOW -> Arduino sleeps but after that if I shake the sensor, nothing happens anymore...
Any help will be very much appreciated...
I would suggest looking at other sources of "guidance".
That program is excessively complicated and appears to be doing things that are completely unnecessary, for example, you should not detach, then reattach interrupts.
I would look for simpler code, such as found here.
Better yet, study the data sheet and the library code, so that you understand what every line of that program is doing (or not), and remove all the junk.
So just to be sure I'm not misunderstood, I did read the official documentation for ADXL345 sensor( Datasheet). And I quite understand how this sensor works and how can I "program" it for reading and generate interrupts(even though I can't make this part work).
I understand that, according to its documentation, I have to follow this steps:
-initialize it
-put it in 'Measurement mode'
-'setup its Range and DataRate(although not mandatory)
set the coresponding Thresholds and axis to look for events and samples
and the "trickier" part prepare it for generating interrupts;
According to data sheet:
Interrupts are enabled by setting the appropriate bit in the
INT_ENABLE register (Address 0x2E) and are mapped to
either the INT1 pin or the INT2 pin based on the contents of the
INT_MAP register (Address 0x2F). When initially configuring the
interrupt pins, it is recommended that the functions and interrupt
mapping be done before enabling the interrupts. When changing
the configuration of an interrupt, it is recommended that the
interrupt be disabled first, by clearing the bit corresponding to that
function in the INT_ENABLE register, and then the function be
reconfigured before enabling the interrupt again. Configuration
of the functions while the interrupts are disabled helps to prevent
the accidental generation of an interrupt before desired.
The interrupt functions are latched and cleared by either reading the
data registers (Address 0x32 to Address 0x37) until the interrupt
condition is no longer valid for the data-related interrupts or by
reading the INT_SOURCE register (Address 0x30) for the
remaining interrupts. This section describes the interrupts that
can be set in the INT_ENABLE register and monitored in the
INT_SOURCE register.
So, in my code I mapped the INT1 pin to interrupts generated by Activity(and Inactivity but lets keep it simple) and then I enabled it(but only the INT_ACTIVITY bit). After this I think I have to clear the registers before any measurements(by reading the INT_SOURCE reg and this is what adxl.getInterruptSource() does. Further, I dont get it why in the "sleep and wake" part, the interrupt never occurs. I have to confess that I worked before with interrupts from digital sensors and arduino but here I can't really tell what Im doing wrong. To be honest I'm not 100% sure that I have to attach the interrupt there (in the sleepNow() function) and detach it in the wakeNow() or if I have to clear the INT_SOURCE register at every iteration in loop().
Regarding that link you suggested, I also tried that "approach"...no good results. I also tried another sensor(to eliminate the possibility of a hardware problem).
Thank again for you feedback.
So...lets suppose I only attach the int in my setup(), then I clear the INT_SOURCE registers of ADXL345. I guess that in my sleepNow(), the interrupt from INT1 will be linked through the interrupts() function and also I eliminate the detach() from wakeNow(). Anyway, still not working. What else do I'm missing?
The interrupts() function turns all interrupts on. The "linking" between an interrupt source and an interrupt action (routine) is done by attachInterrupt().
By the way, the following is NOT the correct way to use the attachInterrupt() function:
#include <Wire.h>
#include <ADXL345.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
ADXL345 adxl;
//WATCHdog interrupt
ISR(WDT_vect)
{
wdt_disable(); //disable watchdog
}
void setup(void)
{
Serial.begin(115200);
pinMode(3, INPUT);
if (!adxl.begin())
{
Serial.println("Something is WRONG!");
delay(1000);
}
else
{
Serial.println("ALL GOOD!");
adxl.setRange(ADXL345_RANGE_16G);
adxl.setDataRate(ADXL345_DATARATE_800HZ);
adxl.setLowPower(0);
//set activity/ inactivity thresholds (0-255)
adxl.setActivityThreshold(20); //62.5mg per increment
//look of activity movement on this axes - 1 == on; 0 == off
adxl.setXActivity(1);
adxl.setXActivity(1);
adxl.setXActivity(1);
attachInterrupt (digitalPinToInterrupt(3), wakeNow, CHANGE);
adxl.getInterruptSource();
//setting all interupts to take place on int pin 1
adxl.setInterruptMapping( ADXL345_ACTIVITY, ADXL345_INT1);
//register interupt actions - 1 == on; 0 == off
adxl.setInterrupt( ADXL345_ACTIVITY, 1);
}
}
void loop(void)
{
sleepNow();
/* DO STUFF */
delay(2000);
}
void sleepNow()
{
Serial.println("Start sleep");
delay(50);
// Do not interrupt before we go to sleep, or the
// ISR will detach interrupts and we won't wake.
noInterrupts ();
// clear various "reset" flags
MCUSR = 0;
// allow changes, disable reset
WDTCSR = bit (WDCE) | bit (WDE);
// set interrupt mode and an interval
WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0); // set WDIE, and 1 second delay
wdt_reset(); // pat the dog
byte old_ADCSRA = ADCSRA;
ADCSRA = 0;
ADMUX = 0; //turn of internal VREF
// Digital Input Disable Register on analogue pins
DIDR0 = bit (ADC0D) | bit (ADC1D) | bit (ADC2D) | bit (ADC3D) | bit (ADC4D) | bit (ADC5D);
DIDR1 = bit (AIN1D) | bit (AIN0D);
// attachInterrupt (digitalPinToInterrupt(1), wakeNow, CHANGE);
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_enable();
EIFR = bit (INTF1);
// turn off brown-out enable in software
// BODS must be set to one and BODSE must be set to zero within four clock cycles
MCUCR = bit (BODS) | bit (BODSE);
// The BODS bit is automatically cleared after three clock cycles
MCUCR = bit (BODS);
// We are guaranteed that the sleep_cpu call will be done
// as the processor executes the next instruction after
// interrupts are turned on.
interrupts (); // one cycle
sleep_cpu (); // one cycle
// cancel sleep as a precaution
sleep_disable();
ADCSRA = old_ADCSRA;
}
void wakeNow()
{
sleep_disable();
}
Does the revised version work? If not, please describe what happens with this version.
Isolate the problem. To test the sleep and wake from interrupt functions on the Arduino, you don't need an ADXL. A pushbutton will do and the program can be vastly simplified.
Nope. I go to sleep and it wakes up after 8 sec even if in that interval I move the sensor no interrupt its generated.
jremington:
Why are you using the watchdog timer?
In my final application, I need to sleep for a longer period of time(1 hour at least), wake up after an hour, check if smth happened and take the appropiate actions and then go back to sleep. So I have to cycle the sleep part(8 sec sleep, 2 or less sec awake, then back to sleep)...and I discovered this is the best way to cycle between sleep and awake - using WTD timer. Also if an event occurs I need to wake up the processor and look what happend...this is why I need both the interrupt and the WTD timer to get "me" out of sleep.