Using an accelerometer and publishing when a sleeping Photon is moved

This is a simple sample project that uses an ADXL362 accelerometer to detect when the Photon is moved and wake it up from sleep and publish an event. It uses a Particle Power Shield to power the Photon from a 3.7V LiPo battery.

The ADXL362 is a low-cost 3-DOF (3-axis, 3-degrees of freedom) SPI accelerometer that’s extremely low power. In the wake-on-move mode used here, it uses 270 nA. Yes, nano-amps!

The sensor is part of the Particle Electron Sensor Kit, but you can get one from [SparkFun] (https://www.sparkfun.com/products/11446) or on eBay.

If you are building this source, you will need to use two libraries. They are both in the Community Libraries on the Particle Build (Web IDE), and also here on github:

This code wasn’t designed for use on the Electron, but by removing the PowerShield and using [FuelGauge] (https://docs.particle.io/reference/firmware/electron/#fuelgauge) instead, it should work fine.

Under normal operation about 10 seconds after you boot or flash the code, the Photon will go to sleep. When movement is detected it will wake up, publish an event with data something like:

1,4.06,98.29

That means the movement was detected (1) and the battery voltage is 4.06 volts and the state of charge is 98.29 %.

Then the Photon will go back to sleep (even if it’s still in motion). If there’s no Wi-Fi or cloud connnection, it will try for 30 seconds then go to sleep, discarding the data.

It will also publish a battery status event at boot and every 4 hours; the first comma-separated field will be 0 (no movement).

The official location of the project and the source code is here:

9 Likes

This project been running for 2 weeks and the LiPo is down to 39.75% SoC and 3.7 volts but it’s still happily sitting around waiting to be shaken and woken up. I wake it up several times a day when I walk by.

I also updated the Github link above with code for the Electron with the ADXL362, Electron with the Asset Tracker (LIS3DH), and the Particle Internet Button (ADXL362) to do the shake to wake feature. It works quite well!

2 Likes

I’m surprised you haven’t got any comment here!

What a great contribution to the community. Very likely I’ll be trying some of your code in the coming weeks, I’ll keep you posted!

Thanks a lot for your time developing and specially sharing it.

1 Like

Hello rickkas7,

Thanks for this code, works very well. I am have adapted for sensing the movement using PIR sensor.

customer needs that the device should withstand for months. So I have designed the way to wake up the device when there is a motion detected. I have connected my pir sensor output to A7 pin so that device wakes up if there is a motion. However, they required that the device should publish an event every 6 hours. So that i have differentiated with publish event every 6 hours and publishing variable by adding when sensor value =1 (so that we could find there is a motion).

But in my case, it publishing variable at the same time as publishing event every 6 hours. I need to differentiate in showing output if there is a motion. Kindly anyone advise me please.
I am getting sensor value as high every alternate time( 00 1 1 001 1 001 1 ), I am confused about it.

this is the adapted code:

#include <Ubidots.h>

#include "application.h"
SYSTEM_THREAD(ENABLED);

#define TOKEN "BBFF-a1aXeKjT3QlzwOnae53ovppXPolR8k" // Put here your Ubidots TOKEN

Ubidots ubidots(TOKEN);
const char *eventName = "HeartBeat";
const char *eventBattery = "LowBattery";

// Various timing constants
const unsigned long MAX_TIME_TO_PUBLISH_MS = 120000; // Only stay awake for 60 seconds trying to connect to the cloud and publish
const unsigned long TIME_AFTER_PUBLISH_MS = 10000; // After publish, wait 4 seconds for data to go out
const unsigned long TIME_AFTER_BOOT_MS = 10000; // At boot, wait 5 seconds before going to sleep again (after coming online)
const unsigned long TIME_PUBLISH_BATTERY_SEC = 5 * 60; // every 4 hours, send a battery update
int count;

 float value;
// Stuff for the finite state machine
enum State { ONLINE_WAIT_STATE, RESET_STATE, RESET_WAIT_STATE, PUBLISH_STATE, SLEEP_STATE, SLEEP_WAIT_STATE, BOOT_WAIT_STATE, LOW_BAT_STATE, ALERT_STATE };
State state = ONLINE_WAIT_STATE;
unsigned long stateTime = 0;
int awake = 0;

void setup() {
	Serial.begin(9600);
	FuelGauge fuel;
    value = fuel.getVCell();
    Particle.variable("SensorData", awake);
}
 
void loop() {

	switch(state) {
	case 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("resetting sensor");

		if (!(analogRead(A7))) {
			Serial.println("Movement not found");
			state = SLEEP_STATE;
			break;
		}

		state = BOOT_WAIT_STATE;
		break;

	case PUBLISH_STATE:
		if (Particle.connected()) {
		    
			char data[32];
			snprintf(data, sizeof(data), "%d,%.02f,%.02f", awake, value);
			Particle.publish(eventName, data, 60);
			ubidots.add("SENSORDATA", awake);
            ubidots.add("BATTERYLEVEL",value);
            ubidots.sendAll();
			stateTime = millis();
			state = SLEEP_WAIT_STATE;
			while(analogRead(A7)){
			    for(count=0; count ++;){
			        Serial.print(count);
			        snprintf(data, sizeof(data), "%d", count);
			        Particle.publish("Motioncount", data, 60);
			    }
			}
		} else {
			// Haven't come online yet
			if (millis() - stateTime >= MAX_TIME_TO_PUBLISH_MS) {
				// Took too long to publish, just go to sleep
				state = SLEEP_STATE;
			}
		}
		break;

	case SLEEP_WAIT_STATE:
		if (millis() - stateTime >= TIME_AFTER_PUBLISH_MS) {
			state = 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 = PUBLISH_STATE;
		}
		break;

	case SLEEP_STATE:
		// Sleep
		System.sleep(A7, RISING, TIME_PUBLISH_BATTERY_SEC, SLEEP_NETWORK_STANDBY);
		awake = ((analogRead(A7)) != 0);
		Serial.printlnf("awake=%d", awake);
		state = PUBLISH_STATE;
		stateTime = millis();
		break;
		
	case LOW_BAT_STATE:
	if(value <= 3.3){
	    state = ALERT_STATE;
	    
	}
	else{
	    state = PUBLISH_STATE;
        ubidots.sendAll();
	 
	}
	 break;
	 
	 case ALERT_STATE:
		if (Particle.connected()) {
			char data1[32];
			snprintf(data1, sizeof(data1), "%d,%.02f", value);
			Particle.publish(eventBattery, data1, 60);
			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 = SLEEP_STATE;
			}
		}
		break;
	}
		
}

Thank you for posting this project— it was just what I’m looking for.
I have several backyard beehives I’d like to monitor.
Some questions:

  1. Is there any reason it wouldn’t work with a cheaper accelerometer? Say the LIS3DH? https://www.sparkfun.com/products/13963
  2. Is there a way to use multiple accelerometers on one photon? (So I could wire up all my beehives to one photon?)

Yes, there’s a LIS3DH wake-on-move example here:

You can connect two LIS3DH using I2C to a single I2C port directly. If you use an I2CMultiplexer you can connect as many as you want.

Or you can use the LIS3DH in SPI mode. Each LIS3DH needs a separate SS line, but you can have as many connected to the SPI bus as you want, as long as you have enough spare GPIO to give each one a separate SS line.

Note, however that there’s a limit to how long the wires can be for I2C or SPI, usually around 1 meter. For longer distances, I use two P82B715s (one on each end of the wires) to extend I2C to 30 meters.

1 Like

Thanks. If I connected multiple LIS3DH via I2C using an I2CMultiplexer, do you think I would have a problem using a Powershield as well? (I believe Powershield uses D0 and D1 on a photon for I2C.) I’m really new to IoT and electronics in general so forgive me if this is a basic question.

I think you’ll be OK. The PowerShield MAX17043 Fuel Gauge uses I2C address 0x36 and the TCA9548A uses address 0x70. And the LIS3DH uses address 0x18, I think. As long as the address are different you should be fine.

Is there a place I can go to find someone who I can pay to code the i2c wake on move using the multiplexor for me? I’ve tried but I can’t seem to find the time to keep trying.

Can anyone confirm whether this code works with the Gen3 now that Sleep feature is available?

It kind of depends on the accelerometer you’ve selected, but yes, wake on move is compatible with Gen 3 devices (Argon, Boron, Xenon).

Note that you cannot use the LIS3DH accelerometer on the AssetTracker (v1 or v2) with the classic adapter and a Gen 3 device! The SPI pins are not mapped in a usable way using the classic adapter and the accelerometer cannot be used. The GPS does work in the classic adapter.

2 Likes