LIS3DH: Can't read axis orientation values when combining examples 1_WakeOnMove and 2_Samples

electron
Tags: #<Tag:0x00007fe22bd9f890>

#1

@rickkas7 I have attempted to combine the example code you provide for interfacing the adafruit LIS3DH accelerometer with the Particle Electron via SPI. I have been able to successfully implement examples 1_WakeOnMove and 2_Samples independently following your GitHub instruction, but I would like to implement them together. I’m attempting to keep the state machine structure of example 1 and integrate the essentials of example 2 into a new state I’ve created, and I’ve fiddled with it for a good amount of hours so far, and the same problem keeps occuring: when the program reaches the state in the state machine where I want to print out orientation values, the values that are printed are essentially garbage values (0,0,0 / 0,0,-256 / -256,-512,0 / etc). I looked at how each example configured the registers of the device in the setup, and there was this extra line in example 1: config.setLowPowerWakeMode(16);. When I comment out that line, I am able to print out accurate orientation data. I looked into your LIS3DH library to figure out what the method setLowPowerWakeMode() is doing, and I’m assuming at this point that one of the registers is being set in that method in a way that disrupts the continuous feeding of (correct) orientation values to the Electron (or disrupts the interpretation of the data the Electron is receiving).

I’ve searched for documentation on Particle Electron registers but haven’t found much. Any help on this would be appreciated!


#2

Can you show how you have combined the two samples and how you added the extra state?


#3

The two modes are mutually exclusive. You can either be in sampling mode or wake-on-move mode, but not both at the same time. You should make your code call setLowPowerWakeMode() before going to sleep. When you wake and want to take samples, set it back into accelerometer mode using setAccelMode().


#4

In the code below, the state WAKEUP_STATE waits to receive another interrupt from the accelerometer (via example 5_Movement code) once the Electron has been woken up from an initial interrupt (via 1_WakeOnMove code). If it does not receive an interrupt in 15 seconds, it goes back to sleep.

I’ve added PRE_WAKEUP_STATE and PRE_SLEEP_STATE in order to call setAccelMode() and setLowPowerWakeMode() respectively. However, after waking up from sleep and going through PRE_WAKEUP_STATE and WAKEUP_STATE (during which the code in 2_Samples is called), the data from the accelerometer is still inaccurate.

@ScruffR , the extra states added are WAKEUP_STATE, PRE_WAKEUP_STATE, and PRE_SLEEP_STATE.


#include "Particle.h"

#include "LIS3DH.h"

// Example of Wake On Move with the AssetTracker and the Electron
//
// Official project location:
// https://github.com/rickkas7/LIS3DH


// System threading is required for this project
SYSTEM_THREAD(ENABLED);

// GLOBALS ADDED
int led = D7;
int buzzer = A1;
int movements = 0;
int sampleZ = 0;


void movementInterruptHandler();
// Global objects
FuelGauge batteryMonitor;
LIS3DHSPI accel(SPI, A2, WKP);
LIS3DHConfig config;

bool sensorsInitialized;
unsigned long lastPrintSample = 0;
volatile bool movementInterrupt = false;
uint8_t lastPos = 0;

// This is the name of the Particle event to publish for battery or movement detection events
// It is a private event.
//const char *eventName = "accel";

// Various timing constants
const unsigned long MAX_TIME_TO_PUBLISH_MS = 60000; // Only stay awake for 60 seconds trying to connect to the cloud and publish
const unsigned long TIME_AFTER_PUBLISH_MS = 4000; // After publish, wait 4 seconds for data to go out
const unsigned long TIME_AFTER_BOOT_MS = 5000; // At boot, wait 5 seconds before going to sleep again (after coming online)
const unsigned long TIME_PUBLISH_BATTERY_SEC = 22 * 60; // every 22 minutes send a battery update to keep the cellular connection up
const unsigned long WAIT_FOR_SECOND_HIT = 15000; // Wait 10 seconds for another impact before publishing
const unsigned long PRINT_SAMPLE_PERIOD = 100;
const unsigned long GET_STATE_TIME = 5000;

const uint8_t movementThreshold = 16;

// Stuff for the finite state machine
enum State { ONLINE_WAIT_STATE, RESET_STATE, WAKEUP_STATE, PUBLISH_STATE, SLEEP_STATE, SLEEP_WAIT_STATE, BOOT_WAIT_STATE, PRE_WAKEUP_STATE, PRE_SLEEP_STATE };
State state = ONLINE_WAIT_STATE;
unsigned long stateTime = 0;
int awake = 0;



void setup() {
	Serial.begin(9600);
	pinMode(led, OUTPUT);
	pinMode(buzzer, OUTPUT);
	attachInterrupt(WKP, movementInterruptHandler, RISING);
	delay(5000);
	
	// This accel.setup was placed in setup() in 2_Samples example, but happens in RESET_STATE
	
	// bool setupSuccess = accel.setup(config);
	// Serial.printlnf("setupSuccess=%d", setupSuccess);
}




void loop() {

	switch(state) {
	    
	case ONLINE_WAIT_STATE:
	    Serial.println("ONLINE_WAIT_STATE");
		if (Particle.connected()) {
			state = RESET_STATE;
		}
		if (millis() - stateTime > 5000) {
			stateTime = millis();
			Serial.println("waiting to come online");
		}
		break;


	case RESET_STATE: 
		Serial.println("RESET_STATE");
		
		// the line config.setLowPowerWakeMode(16) used to be here, moved to PRE_SLEEP_STATE
        
		if (!accel.setup(config)) {
			Serial.println("accelerometer not found");
			state = SLEEP_STATE;
			break;
		}

		state = BOOT_WAIT_STATE;
		break;
		
		
	case PRE_WAKEUP_STATE:
	    Serial.println("PRE_WAKEUP_STATE");
	    
	    // upon wakeup, setAccelMode
	    config.setAccelMode(LIS3DH::RATE_100_HZ);
	    
	    state = WAKEUP_STATE;
	
	    break;
		
		
	case WAKEUP_STATE:
	    //Serial.println("WAKEUP_STATE");
	
	    digitalWrite(led, HIGH);
	    
	    // run example 2_Samples
	    sampleZ = getAccelData();
	    //Serial.printlnf("sampleZ = %d", sampleZ);
	    
	    if(sampleZ > 1400){
	        upsideDown++;
	    }
	    
	    if(upsideDown >= 50){
	        // blink and buzz pattern
	        digitalWrite(led, HIGH);
		    digitalWrite(buzzer, HIGH);
    		delay(1000);
    		digitalWrite(led,LOW);
    		digitalWrite(buzzer, LOW);
    		delay(1000);
    		digitalWrite(led, HIGH);
		    digitalWrite(buzzer, HIGH);
    		delay(1000);
    		digitalWrite(led,LOW);
    		digitalWrite(buzzer, LOW);
    		delay(1000);
    		digitalWrite(led, HIGH);
		    digitalWrite(buzzer, HIGH);
    		delay(1000);
    		digitalWrite(led,LOW);
    		digitalWrite(buzzer, LOW);
    		delay(1000);
    		// go to sleep
    		upsideDown = 0;
    		state = PRE_SLEEP_STATE;
    		break;
	    }
	
	    if (movementInterrupt) {
    		accel.clearInterrupt();
            movements++;
    		Serial.println("movementInterrupt");
    
    		// Recalibrate the accelerometer for possibly being in a new orientation.
    		// Wait up to 15 seconds for it to be stationary for 2 seconds.
    		bool ready = accel.calibrateFilter(2000, 15000);
    		Serial.printlnf("calibrateFilter ready=%d", ready);
    
    		movementInterrupt = false;
    	}
    	
    	if(movements >= 2){
    	    state = PUBLISH_STATE;
    	    movements = 0;
    	}
    	
    	if(millis() - stateTime > WAIT_FOR_SECOND_HIT){
	        digitalWrite(led, LOW);
	        state = SLEEP_STATE;
	    }
	
	    break;


	case PUBLISH_STATE:
	    Serial.println("PUBLISH_STATE");
		if (Particle.connected()) {
			// The publish data contains 3 comma-separated values:
			// whether movement was detected (1) or not (0) The not detected publish is used for battery status updates
			// cell voltage (decimal)
			// state of charge (decimal)
			char data[128];
			float cellVoltage = batteryMonitor.getVCell();
			float stateOfCharge = batteryMonitor.getSoC();
			snprintf(data, sizeof(data), "IMPACT DETECTED. Cell Voltage: %.02f, State of charge: %.02f", cellVoltage, stateOfCharge);
            Serial.println("PUBLISHING");
			//Particle.publish("twilio_sms", data, PRIVATE);

			// Wait for the publish to go out
			stateTime = millis();
			state = SLEEP_WAIT_STATE;
		}
		else {
			// Haven't come online yet
			if (millis() - stateTime >= MAX_TIME_TO_PUBLISH_MS) {
				// Took too long to publish, just go to sleep
				state = PRE_SLEEP_STATE;
			}
		}
		break;


	case SLEEP_WAIT_STATE:
	    Serial.println("SLEEP_WAIT_STATE");
	
	    // LED blink pattern indicates a publish has occured
	    digitalWrite(led, HIGH);
		delay(50);
		digitalWrite(led,LOW);
		delay(50);
		
		if (millis() - stateTime >= TIME_AFTER_PUBLISH_MS) {
			state = PRE_SLEEP_STATE;
			
		}
		break;


	case BOOT_WAIT_STATE:
		if (millis() - stateTime >= TIME_AFTER_BOOT_MS) {
			// To publish the battery stats after boot, set state to PUBLISH_STATE
			// To go to sleep immediately, set state to SLEEP_STATE
			state = PRE_SLEEP_STATE;
		}
		break;
		
		
	case PRE_SLEEP_STATE:
	    Serial.println("PRE_SLEEP_STATE");
	
	    // before going to sleep, setLowPowerWakeMode
	    config.setLowPowerWakeMode(16);
	    
	    state = SLEEP_STATE;
	    
	    break;


	case SLEEP_STATE:
	    Serial.println("SLEEP_STATE");
		// Wait for Electron to stop moving for 2 seconds so we can recalibrate the accelerometer
		accel.calibrateFilter(2000);

		Serial.println("going to sleep");

		// Sleep
		System.sleep(WKP, RISING, TIME_PUBLISH_BATTERY_SEC, SLEEP_NETWORK_STANDBY);

		// This delay should not be necessary, but sometimes things don't seem to work right
		// immediately coming out of sleep.
		delay(500);

		awake = ((accel.clearInterrupt() & LIS3DH::INT1_SRC_IA) != 0);

		Serial.printlnf("awake=%d", awake);

		state = PRE_WAKEUP_STATE;

		stateTime = millis();
		break;

	}

}

/**
 * 
 * @brief ISR for when Electron is awake, to sense movement while awake (example 5_Movement)
 *
 */
void movementInterruptHandler() {
	movementInterrupt = true;
}


/**
 * 
 * @brief runs example 2_Samples and returns the sample.z value, called in WAKEUP_STATE
 *
 */
int getAccelData(){
    int sampleOut = 0;
 
    // Begin code from example 2_Samples
    if (millis() - lastPrintSample >= PRINT_SAMPLE_PERIOD) {
		lastPrintSample = millis();

		LIS3DHSample sample;
		if (accel.getSample(sample)) {
			Serial.printlnf("%d,%d,%d", sample.x, sample.y, sample.z);
		}
		else {
			Serial.println("no sample");
		}
		sampleOut = sample.z;
	}
	return sampleOut;
}


#5

You need to call accel.setup(config) again after setting the config in PRE_WAKEUP_STATE and PRE_SLEEP_STATE.


#6

I added the call to accel.setup(config) in each of the states, but still getting the same xyz values from the accelerometer.

case PRE_WAKEUP_STATE:
	    Serial.println("PRE_WAKEUP_STATE");
	    
	    // upon wakeup, setAccelMode
	    config.setAccelMode(LIS3DH::RATE_100_HZ);
	    setupSuccess = accel.setup(config);
    	Serial.printlnf("setupSuccess=%d", setupSuccess);
	    
	    state = WAKEUP_STATE;
	
	    break;
case PRE_SLEEP_STATE:
	    Serial.println("PRE_SLEEP_STATE");
	
	    // before going to sleep, setLowPowerWakeMode
	    config.setLowPowerWakeMode(16);
	    setupSuccess = accel.setup(config);
	    Serial.printlnf("setupSuccess=%d", setupSuccess);
	    
	    state = SLEEP_STATE;
	    
	    break;

Accelerometer readings:

64,-64,-64
64,0,-64
0,0,-64
0,-128,0
128,-128,-64
128,-64,0
0,64,64
64,-64,-64
0,-64,64
64,64,-128
0,0,0
0,0,0
-64,-64,-128
0,-64,64
0,0,0
64,-64,-128
0,-64,0
64,0,0

When running 2_Samples, the average z value is above 1600 for reference.


#7

Sorry about that. It’s a bug. I released version LIS3DH version 0.2.4 with the fix.

0.2.4

  • When calling setAccelMode, clear all of the settings that are set by setLowPowerWakeMode so you can switch between modes at runtime.

#8

Thank you! The updated library worked. I appreciate the help!