Mesh (Argon/Xenon) detecting USB power loss

Hi all,

I’m attempting to develop a circuit which needs to retain variable information after removing power (which may last up to a week or two). I’m powering my mesh from USB and plan to connect a LiPo back-up. My plan is to detect the power loss and back up the necessary data to EEPROM for the next restart, then go to a deep sleep.

I’ve found posts on how to detect the power loss on the Gen 2 devices (through PCIM), but it does not appear that the mesh devices have this ability. Does anyone know how to do this (preferably software solution only. I’d rather not dedicate a pin and interrupt.)

Also, the notes seem to indicate that sleep is not functioning yet on mesh. Is that accurate? If so, is there a workaround? Thanks, all.

@dadick83, sleep modes have NOT been developed yet for mesh devices as the nRF52840 supports a much richer set than the Gen 2 devices.

The Argon and Xenon had a different power management circuit than the Boron (which is like the Electron). However, both the Argon and Xenon have a VUSB_DET signal which is presented at P0.12 of the rRF52840 so I would say that yes, you will be able to detect if USB is applied or not.

6 Likes

Thanks for the response but looking at both the Argon and Xenon datasheet pinouts, it looks like P0.12 is not independently available (it appears from your reference to VUSB_DET it may be labelled as VBUS, and that DOES provide the USB voltage out). I assume I can tie VBUS to a digital pin and set an interrupt on that pin, but that would be a hardware solution, not a software-only method. Can an interrupt be set on that pin (it doesn’t appear so in the docs)? Is there another way?

It looks to me that P0.12 is the correct pin for the Xenon and Argon. It should be available if you read the PWR pin like this:

bool powerState = digitalRead(PWR);

It compiled, but it didn’t work for me, so I’m not sure what’s wrong, but that’s the theory of how it should work.

2 Likes

@rickkas7, I wonder if it is setup for analog instead of digital input.

I thought about that, but I don’t think so. Note the difference between BATT and PWR. But I could be reading this incorrectly (or the comment could be wrong).

/* BATT          - 24 */ { NRF_PORT_0, 5,  PIN_MODE_NONE, PF_NONE, 3,                PWM_INSTANCE_NONE, PWM_CHANNEL_NONE, 8, EXTI_CHANNEL_NONE},
/* PWR           - 25 */ { NRF_PORT_0, 12, PIN_MODE_NONE, PF_NONE, ADC_CHANNEL_NONE, PWM_INSTANCE_NONE, PWM_CHANNEL_NONE, 8, EXTI_CHANNEL_NONE},

@rickkas7, I agree. I can’t seem to find any references to PWR anywhere in the code base. Perhaps setting the pinMode first will do the trick?

YES! Normally the default is INPUT, but in this case you do indeed need to set it for it to work:

#include "Particle.h"

SYSTEM_THREAD(ENABLED);

SerialLogHandler logHandler;

bool lastPowerState = false;

void setup() {
	pinMode(PWR, INPUT);
	pinMode(D7, OUTPUT);
}

void loop() {
	bool powerState = digitalRead(PWR);
	if (powerState != lastPowerState) {
		lastPowerState = powerState;
		Particle.publish("powerState", (powerState ? "on" : "off"), PRIVATE);
		Log.info("powerState=%d", powerState);
		digitalWrite(D7, powerState);
	}
}
4 Likes

Thank you! I’m still new at Particle products, so I’m still trying to get a handle on the docs. While I understand your code, I have a couple questions, if you don’t mind.

1, is the include particle library necessary and what does it do? Can you point me to a reference for it, I can’t find that. (I’m assuming there’s other functions in there that I’m missing out on) :smile:
2. What was the purpose for the log.info entry? Would that be used in conjunction with the publish, or only useful if I have a terminal connected via serial?

Thanks again.

If you use a .cpp file you need to include particle.h. If you use a .ino file (as is typically the case in Particle Web IDE), then you can omit it, as it’s transparently included for you.

The Log.info does write to the USB debug serial port. I just threw all three in there, just in case, for debugging purposes (cloud publish, serial Log.info, and the D7 LED).

I realize that under normal circumstances unplugging the USB power would also disconnect USB serial which wouldn’t make for a very good test, but I have a special USB cable with a power switch in it.

Gotcha. Thanks for the education.

@rickkas7, any chance this carries over to the Boron? I gave it a shot and it is throwing the following when trying to compile “‘PWR’ was not declared in this scope”. I have double checked that I included Particle.h.

I am creating a deep freezer monitor and I am powering Boron via USB for normal operation with battery for backup if power is lost. My goal is to detect if power is loss so I am trying to poll the USB power status.

Thank you.

The Boron uses a dedicated fuel gauge (which Argon and Xenon lack) and a special charging controler (the same PMIC as on Electron) which should be used instead.

Hence you could try the way it would be done with the Electron

I wouldn’t use that Electron code because Device OS already monitors the power interrupt and having both running can cause problems.

Instead use the diagnostics helper to read the system diagnostic value:

Get the value for DIAG_ID_SYSTEM_POWER_SOURCE. It will be one of:

POWER_SOURCE_UNKNOWN = 0,
POWER_SOURCE_VIN = 1,
POWER_SOURCE_USB_HOST = 2,
POWER_SOURCE_USB_ADAPTER = 3,
POWER_SOURCE_USB_OTG = 4,
POWER_SOURCE_BATTERY = 5

So basically:

int32_t powerSource = DiagnosticHelper::getValue(DIAG_ID_SYSTEM_POWER_SOURCE);

if (powerSource == POWER_SOURCE_USB_HOST || powerSource == POWER_SOURCE_USB_ADAPTER) {
    // Is powered by USB
}

4 Likes

@rickkas7 this is awesome, thank you very much!

Hello rickkas7,
I was trying to use this code on a Boron, but I get an error message saying “‘PWR’ was not declared in this scope”. - is this code not working on the Boron?

What I am trying to achieve is the following: I have a system running on a Boron which I want, in order to save battery power, to go automatically into sleep mode when the battery is not charging, i.e. when the system is not connected to USB/a charger. This is because I wanted to use something like:

powerState = digitalRead(PWR);

and then start a count-down timer, if the system is not charging anymore. Is there any other or better way to do that?

@hbierau, if you’re writting code specifically for the Boron, it may be easier to use the FuelGauge.

Your Boron Logic to determine IF Sleeping is required can be based on State Of Charge (SOC), or the battery’s Voltage, as they are directly related to each other.

It can be as simple as:

if  ( fuel.getSoC() < 70 )   {  // Less than 70% of Battery Capacity Remaining
      System.sleep( {}, {}, sleepTime);
} 

Nope, the PWR "pin" does not exist on a Boron since it has dedicated chips for that (as @Rftop has pointed out).