ADXL345 + slepp

It is driving me crazy for a week now.... :frowning:

I am trying to read ADXL345 only when activated interrupt is sent. All the remaining time my arduino needs to spend sleeping.

But.. it's working only for a few moments and only after complete power down prior to test - i need to literally unplug it to reset everything. Like ADXL stops dead. After a few moments of working.
I do not know how to solve that. It's working ok if i remove the sleep command.

   LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
#include <LowPower.h>
#include <SparkFun_ADXL345.h>         // SparkFun ADXL345 Library


/*********** COMMUNICATION SELECTION ***********/
/*    Comment Out The One You Are Not Using    */
//ADXL345 adxl = ADXL345(10);           // USE FOR SPI COMMUNICATION, ADXL345(CS_PIN);
ADXL345 adxl = ADXL345();             // USE FOR I2C COMMUNICATION
boolean volatile intADXL = false;
double roll;
double pitch;
double xyz[3];

/****************** INTERRUPT ******************/
/*      Uncomment If Attaching Interrupt       */
int interruptPin = 3;                 // Setup pin 2 to be the interrupt pin (for most Arduino Boards)

/******************** SETUP ********************/
/*          Configure ADXL345 Settings         */
void setupADXL() {
  pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ADXL_ISR, RISING);   // Attach Interrupt
  adxl.powerOn();                     // Power on the ADXL345
  adxl.setRangeSetting(4);           // Give the range settings
  adxl.setRate(100);
  adxl.setInterruptLevelBit(0);
  adxl.setNoFifo(); //custom  - disable FIFO - bypass mode
  adxl.setActivityXYZ(1, 1, 1);       // Set to activate movement detection in the axes "adxl.setActivityXYZ(X, Y, Z);" (1 == ON, 0 == OFF)
  adxl.setActivityThreshold(17);      // 62.5mg per increment   // Set activity   // Inactivity thresholds (0-255)
  adxl.setImportantInterruptMapping(2, 2, 2, 1, 2);     // Sets "adxl.setEveryInterruptMapping(single tap, double tap, free fall, activity, inactivity);"
  adxl.InactivityINT(0);
  adxl.ActivityINT(1);
  adxl.FreeFallINT(0);
  adxl.doubleTapINT(0);
  adxl.singleTapINT(0);
}

void setup() {
  Serial.begin(9600);                 // Start the serial terminal
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  bb();
  setupADXL();
}

void loop() {
  roll  = (atan2(-xyz[1], xyz[2]) * 180.0) / M_PI;
  pitch = (atan2(xyz[0], sqrt(xyz[1] * xyz[1] + xyz[2] * xyz[2])) * 180.0) / M_PI;
  Serial.print ("Roll = " + (String)roll); Serial.println ("     |Pitch = " + (String)pitch);
  if (!intADXL) {
    Serial.println ("powering down");
    delay (500);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  }

  if (intADXL)
  {
    intADXL = false;
    byte interrupts = 0;
    interrupts = adxl.getInterruptSource();

    //Activity
    if (adxl.triggered(interrupts, ADXL345_ACTIVITY)) {
      Serial.println("*** ACTIVITY ***");
      bb();
      adxl.get_Gxyz(xyz);
    }
  }
}


void bb() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(10);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
}


void ADXL_ISR() {
  intADXL = true;
}
  1. adxl does not send "interrupt" it it sending HIGH signal forever.

  2. I can read data in a loop when digitalRead(interruptPin) == HIGH but it does not help.
    I think because there can be an effect that int will be rised in the middle of arduino going to sleep, so the interrupt will be missed = there will be no reaction to rising signal on a pin.
    ....

  3. It is working in this version - so clearly I am facing some kind of raceing condition
    //edit: no it's not - it just worked longer....

#include <LowPower.h>
#include <SparkFun_ADXL345.h>         // SparkFun ADXL345 Library


/*********** COMMUNICATION SELECTION ***********/
/*    Comment Out The One You Are Not Using    */
//ADXL345 adxl = ADXL345(10);           // USE FOR SPI COMMUNICATION, ADXL345(CS_PIN);
ADXL345 adxl = ADXL345();             // USE FOR I2C COMMUNICATION
boolean volatile intADXL = false;
double roll;
double pitch;
double xyz[3];
byte interrupts = 0;
/****************** INTERRUPT ******************/
/*      Uncomment If Attaching Interrupt       */
int interruptPin = 2;

/******************** SETUP ********************/
/*          Configure ADXL345 Settings         */
void setupADXL() {
  //pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ADXL_ISR, HIGH);   // Attach Interrupt
  adxl.powerOn();                     // Power on the ADXL345
  adxl.setRangeSetting(4);           // Give the range settings
  adxl.setRate(100);
  adxl.setInterruptLevelBit(0);
  adxl.setNoFifo(); //custom  - disable FIFO - bypass mode
  adxl.setActivityXYZ(1, 1, 1);       // Set to activate movement detection in the axes "adxl.setActivityXYZ(X, Y, Z);" (1 == ON, 0 == OFF)
  adxl.setActivityThreshold(17);      // 62.5mg per increment   // Set activity   // Inactivity thresholds (0-255)
  adxl.setImportantInterruptMapping(2, 2, 2, 1, 2);     // Sets "adxl.setEveryInterruptMapping(single tap, double tap, free fall, activity, inactivity);"
  adxl.InactivityINT(0);
  adxl.ActivityINT(1);
  adxl.FreeFallINT(0);
  adxl.doubleTapINT(0);
  adxl.singleTapINT(0);
}

void setup() {
  Serial.begin(115200);                 // Start the serial terminal
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  bb();
  setupADXL();
  if (digitalRead(interruptPin) == HIGH) intADXL = true;
}

void loop() {
  roll  = (atan2(-xyz[1], xyz[2]) * 180.0) / M_PI;
  pitch = (atan2(xyz[0], sqrt(xyz[1] * xyz[1] + xyz[2] * xyz[2])) * 180.0) / M_PI;
  Serial.print ("Roll = " + (String)roll); Serial.println ("     |Pitch = " + (String)pitch);
  if (digitalRead(interruptPin) == LOW) {
    LowPower.powerDown(SLEEP_FOREVER, ADC_ON, BOD_OFF);
  }
  else {
    while (  digitalRead(interruptPin) == HIGH) {
      adxl.get_Gxyz(xyz);
      adxl.getInterruptSource();
    }
  }
}


void bb() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(10);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
}


void ADXL_ISR() {
  //intADXL = true;
}

detachInterrupt in the ISR function. You're level-triggering the interrupt, meaning it will be called repeatedly as long as the pin is HIGH and nothing else will be done. Reattach the interrupt before going to sleep.

In fact, ONLY attach the interrupt immediately before going to sleep. You don't need it active when you're awake.

And then you have to sleep in the very next instruction. You can't call a function to do it. Otherwise you may get the interrupt before going to sleep and then it never wakes up because the interrupt isn't 'new'.

but you understood that adxl provides unit step signal as interrupt? Not a dirac delta signal that I was expecting?
Anyway I will give it a try :slight_smile: Thank you

EDIT:

Still not ok - it's working for a short time and freezes

#include <LowPower.h>
#include <SparkFun_ADXL345.h>         // SparkFun ADXL345 Library


/*********** COMMUNICATION SELECTION ***********/
/*    Comment Out The One You Are Not Using    */
//ADXL345 adxl = ADXL345(10);           // USE FOR SPI COMMUNICATION, ADXL345(CS_PIN);
ADXL345 adxl = ADXL345();             // USE FOR I2C COMMUNICATION
boolean volatile intADXL = false;
double roll;
double pitch;
double xyz[3];
byte interrupts = 0;
/****************** INTERRUPT ******************/
/*      Uncomment If Attaching Interrupt       */
int interruptPin = 2;

/******************** SETUP ********************/
/*          Configure ADXL345 Settings         */
void setupADXL() {
  //pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ADXL_ISR, RISING);   // Attach Interrupt
  adxl.powerOn();                     // Power on the ADXL345
  adxl.setRangeSetting(4);           // Give the range settings
  adxl.setRate(100);
  adxl.setInterruptLevelBit(0);
  adxl.setNoFifo(); //custom  - disable FIFO - bypass mode
  adxl.setActivityXYZ(1, 1, 1);       // Set to activate movement detection in the axes "adxl.setActivityXYZ(X, Y, Z);" (1 == ON, 0 == OFF)
  adxl.setActivityThreshold(17);      // 62.5mg per increment   // Set activity   // Inactivity thresholds (0-255)
  adxl.setImportantInterruptMapping(2, 2, 2, 1, 2);     // Sets "adxl.setEveryInterruptMapping(single tap, double tap, free fall, activity, inactivity);"
  adxl.InactivityINT(0);
  adxl.ActivityINT(1);
  adxl.FreeFallINT(0);
  adxl.doubleTapINT(0);
  adxl.singleTapINT(0);
}

void setup() {
  Serial.begin(115200);                 // Start the serial terminal
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  setupADXL();
}

void loop() {
  //delay(1000);
  roll  = (atan2(-xyz[1], xyz[2]) * 180.0) / M_PI;
  pitch = (atan2(xyz[0], sqrt(xyz[1] * xyz[1] + xyz[2] * xyz[2])) * 180.0) / M_PI;
  Serial.print ("Roll = " + (String)roll); Serial.println ("     |Pitch = " + (String)pitch);
  if (digitalRead(interruptPin) == LOW) {
    adxl.getInterruptSource();
    adxl.ActivityINT(0);
    adxl.ActivityINT(1);
    Serial.println ("SLEEP NOW");
    attachInterrupt(digitalPinToInterrupt(interruptPin), ADXL_ISR, RISING);   // Attach Interrupt
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  }
  else {
    while (  digitalRead(interruptPin) == HIGH) {
      //Serial.println (">>>>>>HIGH");
      adxl.get_Gxyz(xyz);
      adxl.ActivityINT(0);
      //adxl.getInterruptSource();
    }
    intADXL = false;
  }
}



void ADXL_ISR() {
  detachInterrupt(digitalPinToInterrupt(interruptPin));
  intADXL = true;
}

I think I have a solution. It's not elegant but works.
So I .... borrowed the library code and fixed one part: I added attachInt before going to sleep, and detached when Int is received.

#include <SparkFun_ADXL345.h>         // SparkFun ADXL345 Library
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>


/*********** COMMUNICATION SELECTION ***********/
/*    Comment Out The One You Are Not Using    */
//ADXL345 adxl = ADXL345(10);           // USE FOR SPI COMMUNICATION, ADXL345(CS_PIN);
ADXL345 adxl = ADXL345();             // USE FOR I2C COMMUNICATION
boolean volatile intADXL = false;
double roll;
double pitch;
double xyz[3];
byte interrupts = 0;
/****************** INTERRUPT ******************/
/*      Uncomment If Attaching Interrupt       */
int interruptPin = 2;

enum period_t
{
  SLEEP_15MS,
  SLEEP_30MS,
  SLEEP_60MS,
  SLEEP_120MS,
  SLEEP_250MS,
  SLEEP_500MS,
  SLEEP_1S,
  SLEEP_2S,
  SLEEP_4S,
  SLEEP_8S,
  SLEEP_FOREVER
};

enum bod_t
{
  BOD_OFF,
  BOD_ON
};

enum adc_t
{
  ADC_OFF,
  ADC_ON
};

enum timer5_t
{
  TIMER5_OFF,
  TIMER5_ON
};

enum timer4_t
{
  TIMER4_OFF,
  TIMER4_ON
};

enum timer3_t
{
  TIMER3_OFF,
  TIMER3_ON
};

enum timer2_t
{
  TIMER2_OFF,
  TIMER2_ON
};

enum timer1_t
{
  TIMER1_OFF,
  TIMER1_ON
};

enum timer0_t
{
  TIMER0_OFF,
  TIMER0_ON
};

enum spi_t
{
  SPI_OFF,
  SPI_ON
};

enum usart0_t
{
  USART0_OFF,
  USART0_ON
};

enum usart1_t
{
  USART1_OFF,
  USART1_ON
};

enum usart2_t
{
  USART2_OFF,
  USART2_ON
};

enum usart3_t
{
  USART3_OFF,
  USART3_ON
};

enum twi_t
{
  TWI_OFF,
  TWI_ON
};

enum usb_t
{
  USB_OFF,
  USB_ON
};

enum idle_t
{
  IDLE_0,
  IDLE_1,
  IDLE_2
};

#define  lowPowerBodOn(mode) \
  do {            \
    set_sleep_mode(mode); \
    cli();        \
    sleep_enable();   \
    sei();        \
    sleep_cpu();      \
    sleep_disable();    \
    sei();        \
  } while (0);


#define  lowPowerBodOff(mode)\
  do {            \
    set_sleep_mode(mode); \
    cli();        \
    attachInterrupt(digitalPinToInterrupt(interruptPin), ADXL_ISR, RISING);   \
    sleep_enable();   \
    sleep_bod_disable(); \
    sei();        \
    sleep_cpu();      \
    sleep_disable();    \
    sei();        \
  } while (0);


#ifndef sleep_bod_disable
#define sleep_bod_disable()                     \
  do {                                \
    unsigned char tempreg;                          \
    __asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t"       \
                         "ori %[tempreg], %[bods_bodse]" "\n\t"     \
                         "out %[mcucr], %[tempreg]" "\n\t"      \
                         "andi %[tempreg], %[not_bodse]" "\n\t"     \
                         "out %[mcucr], %[tempreg]"           \
                         : [tempreg] "=&d" (tempreg)          \
                         : [mcucr] "I" _SFR_IO_ADDR(MCUCR),       \
                         [bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
                         [not_bodse] "i" (~_BV(BODSE)));      \
  } while (0)
#endif

void  pDown(period_t period, adc_t adc, bod_t bod)
{
  if (adc == ADC_OFF) ADCSRA &= ~(1 << ADEN);

  if (period != SLEEP_FOREVER)
  {
    wdt_enable(period);
    WDTCSR |= (1 << WDIE);
  }
  if (bod == BOD_OFF)
  {

    lowPowerBodOff(SLEEP_MODE_PWR_DOWN);
  }
  else
  {

    lowPowerBodOn(SLEEP_MODE_PWR_DOWN);
  }

  if (adc == ADC_OFF) ADCSRA |= (1 << ADEN);
}






/******************** SETUP ********************/
/*          Configure ADXL345 Settings         */
void setupADXL() {
  //pinMode(interruptPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPin), ADXL_ISR, RISING);   // Attach Interrupt
  adxl.powerOn();                     // Power on the ADXL345
  adxl.setRangeSetting(4);           // Give the range settings
  adxl.setRate(100);
  adxl.setInterruptLevelBit(0);
  adxl.setNoFifo(); //custom  - disable FIFO - bypass mode
  adxl.setActivityXYZ(1, 1, 1);       // Set to activate movement detection in the axes "adxl.setActivityXYZ(X, Y, Z);" (1 == ON, 0 == OFF)
  adxl.setActivityThreshold(17);      // 62.5mg per increment   // Set activity   // Inactivity thresholds (0-255)
  adxl.setImportantInterruptMapping(2, 2, 2, 1, 2);     // Sets "adxl.setEveryInterruptMapping(single tap, double tap, free fall, activity, inactivity);"
  adxl.InactivityINT(0);
  adxl.ActivityINT(1);
  adxl.FreeFallINT(0);
  adxl.doubleTapINT(0);
  adxl.singleTapINT(0);
}

void setup() {
  Serial.begin(115200);                 // Start the serial terminal
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  setupADXL();
}

void loop() {
  //delay(1000);
  roll  = (atan2(-xyz[1], xyz[2]) * 180.0) / M_PI;
  pitch = (atan2(xyz[0], sqrt(xyz[1] * xyz[1] + xyz[2] * xyz[2])) * 180.0) / M_PI;
  Serial.print ("Roll = " + (String)roll); Serial.println ("     |Pitch = " + (String)pitch);
  if (digitalRead(interruptPin) == LOW) {
    adxl.getInterruptSource();
    adxl.ActivityINT(0);
    adxl.ActivityINT(1);
    Serial.println ("SLEEP NOW");
    delay(200);
    pDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  }
  else {
    while (  digitalRead(interruptPin) == HIGH) {
      //Serial.println (">>>>>>HIGH");
      adxl.get_Gxyz(xyz);
      adxl.ActivityINT(0);
      //adxl.getInterruptSource();
    }
    intADXL = false;
  }
}



void ADXL_ISR() {
  detachInterrupt(digitalPinToInterrupt(interruptPin));
  cli();
  intADXL = true;
}

Do not cli() in the ISR. That'll mess millis() timing and Serial right up.

Yes, thanks - removed that - still works ok :slight_smile:

So I have been trying to clean up a bit my code. In this part below I commented out the disable/enable part of interrupt definition on adxl.
I added this part by intuition, it's not supported by any datasheet facts - I was just trying to make it work by doing everything I could find.
And guess what - it stops working.

if (digitalRead(interruptPin) == LOW) {
    adxl.getInterruptSource();
    //adxl.ActivityINT(0);
    //adxl.ActivityINT(1);

Do you have any idea what would be the better approach?