[SOLVED] Black Out detector, avoiding voltage drop false alert

Hi all,

I'm developing a Blackout detector using Arduino UNO + GSM Shield. The Arduino is supplied with a 12V power pack and a 9V battery pack as backup.

I read the Vin pin over a voltage divider, so if the Arduino is powered by the 12V power pack I will read a value (say 4V) otherwise a lower one.

So far this is working, but since I'm reading the voltage at every loop this method is prone to voltage drop false alert.
To avoid this I've come out with the following idea:

  // read the input on the analogical probe pin
  int sensorValue = analogRead(voltageProbePin);
  // Convert the analogical reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  unsigned long readTime = millis();
  while(millis() - readTime < voltageReadInterval ){
	// idle, wait voltageReadInterval before make a second reading.
  }
  // Performing a second reading. This will help prevent false blackout alert due to voltage drop
  sensorValue = analogRead(voltageProbePin);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float newVoltage = sensorValue * (5.0 / 1023.0);

Is it a bad idea? Any suggestions?

Thank you!

How is it prone to false alerts? Is the 12V wandering around a bit?

Or are you making other analog readings very soon before this one? Usual solution to that is to do two readings back to back and just not use the first one, this gives the cap that is part of the ADC sample & hold circuit time to settle, especially if your resistor divider circuit uses high value resistors (as in more than 5K each).

CrossRoads:
How is it prone to false alerts? Is the 12V wandering around a bit?

No, the the 12V power pack is stable, but sometimes very short (human imperceptible) voltage drop happens in my house. So i don't want to waste SMS for a few milliseconds voltage drop.

CrossRoads:
Or are you making other analog readings very soon before this one?

No this is the only analog read I perform in the entire loop.

Power the Barrel jack from 2 sources thru diodes. As soon as the higher one drops its diode becomes reverse biased and the other one provides power.
Use low Vf schottky diodes, such as 0.45V at 1A:

CrossRoads:
Power the Barrel jack from 2 sources thru diodes. As soon as the higher one drops its diode becomes reverse biased and the other one provides power.
Use low Vf schottky diodes, such as 0.45V at 1A:
1N5817-TP Micro Commercial Co | Discrete Semiconductor Products | DigiKey

That's exactly how the things are wired up! Maybe I explained myself not so well.
The actual loop code is the following:

  boolean blackout = false;
  const float alertThreshold = 3.9;
  .............................
  void loop(){
  // read the input on the analogical probe pin
  int sensorValue = analogRead(voltageProbePin);
  // Convert the analogical reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
   if(voltage < alertThreshold ){
    // there is a power loss 
    blackOut = true;
  }else{
    // there is power
    blackOut = false;
  }
  ..............................
}

At the moment the blackout variable come true when the Arduino is powered by the battery pack, so no current in my house. By the way, sometimes happens a voltage drop in my house (but it's not a black out!), the Arduino is powered by the battery pack for very few milliseconds and the blackout variable come true. When the blackout variable is true the system send me an SMS alert and in that case is a waste.

At this point I'm asking you if the double voltage read repeated at different times is a good idea or not. The new code that I've in mind is the following:

void loop(){

unsigned long voltageReadInterval = 3000;

// read the input on the analogical probe pin
  int sensorValue = analogRead(voltageProbePin);
  // Convert the analogical reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  unsigned long readTime = millis();
  while(millis() - readTime < voltageReadInterval ){
	// idle, wait voltageReadInterval before make a second reading.
  }
  // Performing a second reading. This will help prevent false blackout alert due to voltage drop
  sensorValue = analogRead(voltageProbePin);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float newVoltage = sensorValue * (5.0 / 1023.0);
  
  
  // checking the voltage and update the status
  if(voltage < alertThreshold && newVoltage < alertThreshold){ // if the voltage is below the threshold for two different       //reading, assume blackout
    // there is a power loss 
    blackOut = true;
  }else{
    // there is power
    blackOut = false;
  }


}

Thank you.

I don't understand what the problem is here.

If I got it right, you can detect a voltage drop, but want to send an SMS if it is longer than (say) 5 seconds?

Can't you note the time (with millis) when you detect a voltage drop, and if it is still low 5 seconds later, send the alert? And if it ever goes high again, reset this time interval?

Something like:

int sensorValue = analogRead(voltageProbePin);
float voltage = sensorValue * (5.0 / 1023.0);
if (voltage < LOW_VALUE)
  {
  delay (5000);
  sensorValue = analogRead(voltageProbePin);
  voltage = sensorValue * (5.0 / 1023.0);
  if (voltage < LOW_VALUE)
    {
    // voltage was low for 5 seconds, send an alert
   ...
    }
  }

I'm trying to avoid the delay function. The following code should do the same work as delay(3000).

unsigned long voltageReadInterval  = 3000;
readTime = millis();
while(millis() - readTime < voltageReadInterval ){
	// idle, wait voltageReadInterval before make a second reading.
  }

The things that doesn't convincing me is the double read and the idle time...

EngineerWithTheGear:
I'm trying to avoid the delay function. The following code should do the same work as delay(3000).

unsigned long voltageReadInterval  = 3000;

readTime = millis();
while(millis() - readTime < voltageReadInterval ){
// idle, wait voltageReadInterval before make a second reading.
  }




The things that doesn't convincing me is the double read and the idle time...

Very good, but your code is exactly the same as:

delay (3000);

So what does that achieve? You don't have the word "delay" in the code?

EngineerWithTheGear:
The things that doesn't convincing me is the double read and the idle time...

Why?

This has given me the following idea;

unsigned long blackoutTimer = 0; // keeps in mind how much time the blackout is lasting
unsigned long lastVoltageUpTime = 0; // keeps in mind the last time the voltage has been up

void loop(){
// read the input on the analogical probe pin
  int sensorValue = analogRead(voltageProbePin);
  // Convert the analogical reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  unsigned long readTime = millis();
  // checking the voltage and update the status
  if(voltage < alertThreshold){
    // there is a power loss 
	unsigned long blackoutDuration = readTime - lastVoltageUpTime; // compute the amount of time to add at the   //blackoutTimer
	blackoutTimer = blackoutTimer + blackoutDuration; // update the blackout duration
	if(blackoutTimer > blackoutTimeLimit){ // if the blackout duration exceed the limit, update the status
		blackOut = true;
	}else{
		// do nothing, (a blackout = false here may cause false power back alert due to millis() rollover?)
	}
  }else{
    // there is power
    blackOut = false; // update the status
    blackoutTimer = 0; // reset the blackout timer
    lastVoltageUpTime = readTime; // update voltageUp time
  }
}

That doesn't look fine to me, please take a look at the code update I've posted above. What do you think?

I think you are ignoring my suggestion.

Please help me to find why.

You are suggesting me to check the voltage, if the voltage is below the threshold wait few seconds and make a second read. If the second read too is below the threshold send the alert. Right?

I'm proposing a different implementation of that. I'm reading the voltage at every loop, if the voltage is below the threshold then I start a kind of timer. This timer will be increased every loop in which the voltage is below the threshold and will reset if the voltage is above the threshold. The blackout is signaled only if the blackout timer has exceed an amount of time of few seconds (The alert will be sent only one time I've extra logic after that will handle this).

You are suggesting me to check the voltage, if the voltage is below the threshold wait few seconds and make a second read. If the second read too is below the threshold send the alert. Right?

Yes.

This timer will be increased every loop in which the voltage is below the threshold and will reset if the voltage is above the threshold.

What do you mean "this timer will be increased"? Don't you simply want to detect if the black out exceeds a certain interval? If you have other stuff to do, then delay() is not appropriate. However the code you posted does not indicate that.

Your original code had what amounted to a delay in it:

 unsigned long readTime = millis();
  while(millis() - readTime < voltageReadInterval ){
	// idle, wait voltageReadInterval before make a second reading.
  }

So what is the massive objection to my suggestion? Take a reading, if it is low voltage, delay 5 seconds and then take a second reading? Simple, and more-or-less what you originally had.

The previous code I've posted is broken...
In the end I've deployed the following code, and it's working correctly.

void loop(){
  // read the input on the analogical probe pin
  int sensorValue = analogRead(voltageProbePin);
  // Convert the analogical reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  unsigned long loopTime = millis();
  
  if(voltage < alertThreshold){ // if the device is powered by the battery pack
    // then there is a power loss
	unsigned long blackOutDuration = loopTime - lastVoltageUpTime; // computing the power loss duration.
	if(blackOutDuration > 30000UL){ // if the power loss exceed 30 sec
		blackOut = true; // a blackout has occurred
	}else{
		// if the power loss duration is below 30 seconds, nothing to do.
	}
  }else{
    // there is power
	blackOut = false; // set the status to no black out
	lastVoltageUpTime = loopTime; // update the last voltage up time
  }
}

Actually I'm checking if the power loss is lasting for more than 30 seconds. I think it is better than make a read, wait few seconds and then make a second read.

I will flag this as resolved. Thank you all for the advice.