Basic Power Loss Detection - Electron

I’m futzing with my new toy :slight_smile: and thought about a basic power-loss detection setup.

Electronics:

A simple pull-down resistor between pin D1 and ground.
Jumper from Vin (USB 5.0V) to pin D1.

The interrupt works, waking the Electron when I disconnect the USB cable from my computer, easy peasey.

But I cannot re-connect to send my alert via pushover. Can anyone see what’s wrong in my code?

the setup() publish works
disconnecting power “wakes” the Electron
it goes back to sleep after reconnection of power…

#define REMIND_INTERVAL_MINS 1 //60
int toggleLed(String command);
const int triggerPin = D1;
void setup()
{
  pinMode(triggerPin,INPUT);  // External Pulldown Resistor attached
  Particle.publish("pushover", "Particle Power Loss Detector Started");
}

void loop()
{
  System.sleep(triggerPin, FALLING);
  while(digitalRead(triggerPin) == LOW)  // send message every 30mins until power is restored or battery croaks
  {
    //Cellular.on();
    Cellular.connect();
    while(!Cellular.ready())
    {
        //Particle.process();
    };
    Particle.publish("pushover", "Power Loss Detected!!!");
    Cellular.disconnect();
    System.sleep(REMIND_INTERVAL_MINS * 60); // test every REMIND_INTERVAL_MINS minutes
  }
  Cellular.connect();
  Particle.publish("pushover", "Particle Power Restored!!");
  Cellular.disconnect();
}

I guess I’m not understanding what is necessary to get back onto Cellular and transmit.

@BulldogLowell, it may be a separate problem, but I’m finding that the duration the electron is asleep seems to affect reconnect to Particle https://github.com/spark/firmware/issues/888

thanks @bpr .

I also read this which (as @BDub explains) is disconcerting… suggests that power-cycle of cellular radio cannot be un-done in code with the current firmware…

A fix seems to be coming…

@BulldogLowell Try this:

//@BulldogLowell
//must use develop branch with PR#845 fix merged
#include "application.h"
#define REMIND_INTERVAL_MINS 1 //60
int toggleLed(String command);
const int triggerPin = D1;
const int dummytriggerPin = D3;
uint32_t start;

//10k PULLDOWN resistors on D1 & D3
void setup()
{
  pinMode(triggerPin,INPUT);  // External Pulldown Resistor attached
  pinMode(dummytriggerPin,INPUT);  // External Pulldown Resistor attached
  Particle.publish("e1PO", "Started");
}

void loop()
{
  System.sleep(triggerPin, FALLING);
  while(digitalRead(triggerPin) == LOW)  // send message every 30mins until power is restored or battery croaks
  {

    start = millis();
    while (millis() - start < 1000UL) {Particle.process();}


    Particle.publish("e1PO", "Loss Detected");

    System.sleep(dummytriggerPin, RISING, REMIND_INTERVAL_MINS * 30); // 30 secs test
  }

  start = millis();
  while (millis() - start < 1000UL) {Particle.process();}

  Particle.publish("e1PO", "Power Restored");

}

@bpr, Since I don’t have local tool-chain… I’ll have to look for instructions here on the forum, I guess.

@bpr,

I could not find the toolchain instructions, but I noticed that after the initial failed notification, I received some of the subsequent times… using your adaptation of my earlier code, adding your fuel gauge piece and using the Wev IDE build:

#define REMIND_INTERVAL_MINS 1 //60
int toggleLed(String command);
const int triggerPin = D1;
const int resetPin = D3;

FuelGauge fuel;

char publishString[64] = "";

void setup()
{
  pinMode(triggerPin,INPUT);  // External Pulldown Resistor attached
  pinMode(resetPin, INPUT); 
  sprintf(publishString, "Power Detector Started, Fuel:%.1f%%", fuel.getSoC());
  Particle.publish("pushover", publishString);
}

void loop()
{
  System.sleep(triggerPin, FALLING);
  while(digitalRead(triggerPin) == LOW)  // send message every 30mins until power is restored or battery croaks
  {
    unsigned long start = millis();
    while (millis() - start < 1000UL) 
    {
      Particle.process();
    }
    sprintf(publishString, "Power Loss Detected, Fuel:%.1f%%", fuel.getSoC());
    Particle.publish("pushover", publishString);
    System.sleep(resetPin, RISING, REMIND_INTERVAL_MINS * 60); // test every X minutes
  }
    unsigned long start = millis();
    while (millis() - start < 1000UL) 
    {
      Particle.process();
    }
  Particle.publish("pushover", "Particle Power Restored!!");
}

@BulldogLowell I’m getting good results with (though may run afoul of an apparent 25 minute timeout issue “bug”:

//@BulldogLowell
//must use develop branch with PR#845 fix merged
#include "application.h"

const int triggerPin = D1;
uint32_t start;
//prevent wasting data on serial identical alerts
bool alreadyOFFAlerted = false;
bool alreadyONAlerted = false;

//10k PULLDOWN resistors on D1
void setup()
{
  pinMode(triggerPin,INPUT);  // External Pulldown Resistor attached
}

void loop()
{
  System.sleep(triggerPin, CHANGE);

  start = millis();
  while (millis() - start < 10UL) {Particle.process();}

  if ((digitalRead(triggerPin) == LOW) && (!alreadyOFFAlerted))  
  {
    alreadyONAlerted = false;

    start = millis();
    while (millis() - start < 2000UL) {Particle.process();}

    if(!alreadyOFFAlerted) {  Particle.publish("e1", "PowOff");}

    alreadyOFFAlerted = true;
  }

if ((digitalRead(triggerPin) == HIGH) && (!alreadyONAlerted))
{
    alreadyOFFAlerted = false;
    start = millis();
    while (millis() - start < 2000UL) {Particle.process();}

    Particle.publish("e1", "PowOn");

    alreadyONAlerted = true;
}
}

Edit: Made some timing tweaks
Edit2: Could move System.sleep to bottom of loop with waketime added for periodic monitoring, e.g.,

  start = millis();
  while (millis() - start < 1000UL) {Particle.process();}

#ifdef Repeat_Status
//won't yet work beyond 24-25 min
System.sleep(triggerPin, CHANGE, 20);//reports every 20 secs 
  alreadyONAlerted = false;
  alreadyOFFAlerted = false;
#else
  System.sleep(triggerPin, CHANGE);
#endif

  start = millis();
  while (millis() - start < 1000UL) {Particle.process();}

}//loop
1 Like

@bpr,

Not here… I gotta figure out how to do the local build with the Git repo (mac).

It seems to me that this should be an easy one… and one that was conceived of during testing.

:wink:

@bpr Hey how did you go about adding all the programs required to build locally? Are you using Windows? I’m on Windows 8.

I use this Toolchain for Windows Installer but the Mingw.zip program did not install correctly so I need to install that manually. You can choose to not install that via clicking the box next to that program.

Are there any instructions you are following that taught you the flow of building locally?

I’m on Windows 10 now. I’m a little fuzzy on the whole process, it’s been a little while. This thread documents most of my stumbling around to the final success: Photon, local build, on Windows
I lucked out and somehow manually installed it all without @mumblepins well-regarded installer
Turned out to essentially be the same to set up the toolchain on 10 as on win 7, i think, though I found and installed a more recent version of gcc (5.2.1 20151202).
The key for me was the path order stuff as mentioned in that thread

@bpr,

So, I noticed that I was getting pretty consistent messages at startup. Reviewing the documentation on sleep (which seems to skirt the whole wakeup part, ironically) I scrolled lower to reset() and voilà. It occurred to me that I could try to just trigger a reset. Well it simplified everything and it **works.

latest:

const int triggerPin = D1;

FuelGauge fuel;

char publishString[128] = "";

void setup()
{
  pinMode(triggerPin,INPUT);  // External Pulldown Resistor attached
  int startupTime = millis();
  while(millis() - startupTime < 2000UL)  // allowing time to take digitalRead()
  {
      Particle.process();
  }
  sprintf(publishString, "Power %s, Fuel:%.1f%%", (digitalRead(triggerPin) == HIGH) ? "Restored" : "NOT Detected", fuel.getSoC() );
  Particle.publish("pushover", publishString);
}

void loop()
{
  System.sleep(triggerPin, CHANGE);
  System.reset();
}

** by works, I mean my Electron seems to take a lot of time negotiating with the cellular network, occasionally timing out (burst of red flashing) but eventually attaches and I get the message.

1 Like

Nice and simple. Just don’t use it if it’s likely to trigger often. The data use penalty for renegotiating the cellular connection is very very high.

@bpr,

Well, it is hard to optimize when one cannot figure out how to simply reconnect to cellular!

Right now I am using a SIM card from my unlimited data plan, and I’m not sure If I will even use this application.

Trying to come up with a use for my new Electron, I thought to myself, “what is the easiest and most obvious use of this device” and came up with this one.

BUT, I know that I will enjoy the ride up the learning curve again!

thanks for your ongoing assistance!

Yea same here. One use I have is a simple mail box delivery detector but that’s not going to work how I want it until System.sleep() is working correctly. I have not gotten any feedback from @Bdub on the latest System.sleep issue where it will not work after 25 mins but considering its a critical feature I’m sure it will get fixed sometime in the future :smile:

1 Like

@RWB, @bpr

I’m not seeing any Elites chiming in on this issue… maybe the groundhog didn’t see its shadow.

1 Like

Bump.

Is there a way to programmatically detect Electron loss of USB power source (without requiring external circuitry)?

There is another thread that might be intersting in this connection

1 Like

Found it. PMIC::isPowerGood(). Thanks

1 Like