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);
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.
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:
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;
}
}
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?
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...
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
}
}
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.