wollewald / ADXL345_WE

Arduino Library for the ADXL345 accelerometer. I2C and SPI are implemented.
https://wolles-elektronikkiste.de/en/adxl345-the-universal-accelerometer-part-1
MIT License
40 stars 9 forks source link

checkInterrupt after MCU sleep #6

Closed chron0 closed 2 years ago

chron0 commented 2 years ago

Hey Wolfgang, thank you for sharing this lib! I'm currently playing with it and struggle with the checkInterrupt call on an ESP32 after put to deep sleep:

   log_d("Setting up ADXL345...");

  if (!myAcc.init()) 
  {
    log_d("ADXL345 not connected!");
  }

  if(myAcc.checkInterrupt(myAcc.readAndClearInterrupts(), ADXL345_ACTIVITY))
  {
    log_d("ADXL345 - Motion registered at: %s", myAcc.getActTapStatusAsString());
  }
  else
  {
    log_d("ADXL345 - No Motion registered");
  }

  myAcc.setCorrFactors(-230.0, 225.0, -227.0, 238.0, -280.0, 227.0);

  myAcc.setDataRate(ADXL345_DATA_RATE_100);
  myAcc.setRange(ADXL345_RANGE_2G);

  myAcc.setActivityParameters(ADXL345_AC_MODE, ADXL345_XYZ, 0.0675);

  myAcc.setInterrupt(ADXL345_ACTIVITY, INT_PIN_1);

  myAcc.readAndClearInterrupts();

The strange thing is: When I put if(myAcc.checkInterrupt(myAcc.readAndClearInterrupts(), ADXL345_ACTIVITY)) into my main loop without going to sleep - then moving the box will successfully get the trigger data from the ADXL345 - but when i put it into my setup routine and query the ADXL345 after a wakeup it never seems to get anything from myAcc.checkInterrupt.

The ADXL345 is always powered on and should register movement and save it until the esp wakes up again and pulls the state and resets that interrupt. Am I doing it totally wrong or am I missing something here?

wollewald commented 2 years ago

Hi @chron0 , interrupts are always a source of surprises! I have no spontaneous idea. This is something I need to play with myself. You could do me a favour if you send me the complete sketch ot the two versions. Either you list it here or you can send it to wewald@gmx.de.

chron0 commented 2 years ago

interrupt reading working:


#include <Arduino.h>
#include "ADXL345_WE.h"

ADXL345_WE myAcc = ADXL345_WE(ADXL345_I2CADDR);

////////////////////////////////////////////////////

void setup()
{
  Serial.begin(115200);

  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  log_d("Setting up ADXL345...");

  if (!myAcc.init()) 
  {
    log_d("ADXL345 not connected!");
  }
  myAcc.setCorrFactors(-230.0, 225.0, -227.0, 238.0, -280.0, 227.0);

  myAcc.setDataRate(ADXL345_DATA_RATE_100);
  myAcc.setRange(ADXL345_RANGE_2G);
  myAcc.setActivityParameters(ADXL345_AC_MODE, ADXL345_XYZ, 0.0675);
  myAcc.setInterrupt(ADXL345_ACTIVITY, INT_PIN_1);
  myAcc.readAndClearInterrupts();

  while(1) 
  {
    xyzFloat g = myAcc.getGValues();
    Serial.print("g-x   = ");
    Serial.print(g.x);
    Serial.print("  |  g-y   = ");
    Serial.print(g.y);
    Serial.print("  |  g-z   = ");
    Serial.println(g.z);

      //byte actTapSource = myAcc.getActTapStatus();
      //Serial.println(actTapSource, BIN);
      String axes = myAcc.getActTapStatusAsString();
      byte intSource = myAcc.readAndClearInterrupts();
      if(myAcc.checkInterrupt(intSource, ADXL345_ACTIVITY)){
        Serial.print("Activity at: ");
        Serial.println(axes);
      }

    delay(1000);
    myAcc.readAndClearInterrupts();

  }

}

void loop()
{

}

vs. not working:

#include <Arduino.h>
#include "ADXL345_WE.h"

unsigned int TX_INTERVAL = 10;

ADXL345_WE myAcc = ADXL345_WE(ADXL345_I2CADDR);

void setup()
{
  Serial.begin(115200);

  Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);

  log_d("Setting up ADXL345...");

  if (!myAcc.init()) 
  {
    log_d("ADXL345 not connected!");
  }

  if(myAcc.checkInterrupt(myAcc.readAndClearInterrupts(), ADXL345_ACTIVITY))
  {
    log_d("ADXL345 - Motion registered at: %s", myAcc.getActTapStatusAsString());
  }
  else
  {
    log_d("ADXL345 - No Motion registered");
  }

  myAcc.setCorrFactors(-230.0, 225.0, -227.0, 238.0, -280.0, 227.0);

  myAcc.setDataRate(ADXL345_DATA_RATE_100);
  myAcc.setRange(ADXL345_RANGE_2G);

  myAcc.setActivityParameters(ADXL345_AC_MODE, ADXL345_XYZ, 0.0675);
  myAcc.setInterrupt(ADXL345_ACTIVITY, INT_PIN_1);
  myAcc.readAndClearInterrupts();

  log_d("Wait to go to sleep (simulate work)");
  delay(5000);

  log_d("Entering deep sleep for %d sec", TX_INTERVAL);
  esp_sleep_enable_timer_wakeup(TX_INTERVAL * 1000000);
  Serial.flush();
  esp_deep_sleep_start();

}

void loop()
{

}

the main difference being, in my use case the esp32 will be sent immediately back to sleep after running thru the setup routine once - if i keep it forced in the while(1) i get data from the interrupt function when I move the test case around. I've played with the position of the code before or after the ADXL setup function calls since I thought the interrupt registers may would be overwritten and therefore appear empty when the check comes along but so far I'm banging my head at it....

wollewald commented 2 years ago

I'll have look at it. Try to do it within the next days.

chron0 commented 2 years ago

Thx, looking forward to it. Maybe also noteworthy: Only vcc, gnd, sda and scl are connected to the MCU. I did not connect the int1/int2 to wake up the MCU as this is basically already wired to the PMU of the system (AXP192). It should basically just wake up, connect to ACC, read interrupt state, if motion was detected set a flag for further processing, clear interrupts, do other stuff, go to sleep again. So any motion the ACC detects should remain in its registers until the MCU wakes up, reads and resets them again - i thought this was a feasible use case...

wollewald commented 2 years ago

Hi @chron0, I think I know the issue. After deep sleep the ESP32 restarts and the init() function of my lib will be called. And the init() Function contains a call of readAndClearInterrupts(); in line 93 of ADXL345_WE.cpp. You could try to comment out this line. I don't want to delete it, because this could lead to problems or confusions for others who expect to start with a "clean" system after they have uploaded a modified sketch. Usually I even implement a device reset in the init functions of my libraries, but the ADXL345 does not offer a software reset.

chron0 commented 2 years ago

oh perfect, that makes total sense, I'll try it in my local copy of your lib so that you dont have to change the status quo there and give you feedback tomorrow if that worked or hack a "noreset" bool flag into the init. i wasnt sure if it was a lib problem or a adxl problem, so I didnt look more deeply into the libs code. Thank you very much for the hint.

chron0 commented 2 years ago

i didnt change anything in the lib after all - i just put a separate init function for the adxl into my setup code that will only be called when they MCU is actually reset and bypassed when the mcu is coming back from sleep, thereby expecting a properly configured adxl and triggers to read which are not reset beforehand.

It seems to work now but I still get either too many false positives or not enough response atm. In the end the purpose is to have a motion sensor on a bike, if someone is moving it it should trigger and send a lora message. nevertheless, out of scope for this issue and your feedback has solved it. Thanks again.