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;
}