7

This code compares the analog input value against two thresholds, having three voltage regions. Then it will turn on an LED according to what region the read voltage is in.

The problem is that when the voltage read is very close to the threshold, the noise will disturb the output, resulting in an uncontrolled switching between LEDs.

I would need to add some sort of HYSTERESIS around my thresholds to avoid the uncontrolled switching, but I have no idea how to write this into the code language. Would anyone be able to help me writing the threshold bit?

This is the full code:

// these constant won't change:
const int BatteryVoltagePin = 14; // the pin that battery voltage is attached to
const int GreenLedPin = 3; // the pin that the green LED (On charge) is attached to
const int OrangeLedPin = 4; // the pin that the orange LED (no risk) is attached to
const int RedLedPin = 5; // the pin that the red LED (low battery) is attached to
// Variables will change:
int currentVoltage ; // current voltage value
int previousVoltage ; // previous voltage value 
void setup() {
 // initialize the battery voltage pin as a input:
 pinMode(BatteryVoltagePin, INPUT);
 // initialize the green LED as an output:
 pinMode(GreenLedPin, OUTPUT);
 // initialize the orange LED as an output:
 pinMode(OrangeLedPin, OUTPUT);
 // initialize the red LED as an output:
 pinMode(RedLedPin, OUTPUT);
}
void loop() {
 // read the voltage across the battery voltage pin:
 currentVoltage = digitalRead(BatteryVoltagePin);
 // compare the current voltage value to its previous value
 if (currentVoltage != previousVoltage) 
 delay(10);
 // value above maximum threshold
 {
 if ((( ( analogRead(BatteryVoltagePin) ) > ( 699 ) )) 
 {
 digitalWrite(GreenLedPin, HIGH);
 }
 else
 {
 digitalWrite(GreenLedPin, LOW );
 }
 }
 //value between two thresholds (middle region)
 {
 if (( ( ( analogRead(BatteryVoltagePin) ) <= ( 699 ) ) && ( ( analogRead(BatteryVoltagePin) ) >= ( 628 ) ) )) 
 { 
 digitalWrite(OrangeLedPin, HIGH );
 }
 else
 {
 digitalWrite(OrangeLedPin, LOW );
 }
 }
 // value below minimum threshold
 {
 if (( ( analogRead(BatteryVoltagePin) ) < ( 628 ) )) 
 {
 digitalWrite(RedLedPin, HIGH);
 }
 else
 {
 digitalWrite(RedLedPin, LOW );
 }
 }
 // save the current state as the last state, 
 //for next time through the loop
 previousVoltage = currentVoltage;
 }
asked Mar 8, 2015 at 14:31
2
  • This kinda sounds like a case where you may need debouncing... Have you already done this? Commented Mar 8, 2015 at 16:07
  • no I haven't. Could you explain this in more details please? Commented Mar 8, 2015 at 16:22

4 Answers 4

7

Adding hysteresis behavior to your code is not difficult. You just need to store the state you're in and make the thresholds for transitioning into another state dependent on that.

You can use an enum to store the state:

enum BatteryStates {
 Red, Orange, Green
};

Then, instead of defining and checking against one threshold value, define two thresholds for each transition, separated by so much that you don't observe the undesired flip-flopping behavior.

For example, when transitioning from Green to Orange you could do something like this:

if ((state == Green) && (battery < 690))
 state = Orange;

And the other direction would use a slightly higher threshold

if ((state == Orange) && (battery > 700))
 state = Green;

so that small fluctuations of the voltage do not cause flip-flopping.

(Note that my code assumes

int battery = analogRead(BatteryVoltagePin);

The variable state should be declared and initialized like so:

enum BatteryStates state = Green;

Instead of an enum you could use a simple int and map each state to a number (e.g. red = 1, orange = 2, green = 3), but using enum makes the code easier to read and write. (In fact enum does use integer numbers.)

I might also use a switch statement instead of an if chain to implement the transitions, but that's a matter of taste.)

answered Mar 8, 2015 at 17:13
4
  • Thanks for your answer!! But I do not know how to use the statmement enum. It looks like arduino IDE does not recognise it. Could you please explain this in more details? Commented Mar 8, 2015 at 20:17
  • 1
    enum is not highlighted by the syntax highlighting. But it is accepted by the compiler. I have added some details. Commented Mar 9, 2015 at 5:26
  • Many thanks for your help! However, I am new to programming and I cannot make full sense of all this. I do understand the logic behind it... but when I try to put it together it does not work. If you could post a little working example that does compile, perhaps initialising all the variables etc, it would help a lot! Commented Mar 9, 2015 at 11:06
  • I am happy to help with concrete questions but I need to ask you to do your part: Try to solve the problem yourself first and if you fail describe where you need help. Commented Mar 10, 2015 at 21:39
2

This includes hysteresis and uses some loops and arrays to simplify and compact the code, since the logic for each range is essentially the same. More ranges are easy to include by extending the Cutoffs and LedPins arrays (one more led than cutoff).

const int VoltagePin = 14;
const int Cutoffs[] = { 628 , 699 };
const int NumCutoffs = sizeof(Cutoffs)/sizeof(int);
const int Hysteresis = 5;
const int LedPins[NumCutoffs+1] = {5,4,3};
int Level = 0; 
int Voltage = 0;
void setup()
{
 for (int i=0; i<=NumCutoffs; ++i) {
 pinMode(LedPins[i],OUTPUT);
 }
}
void loop()
{
 Voltage = analogRead(VoltagePin);
 while (Level < NumCutoffs && Voltage >= Cutoffs[Level]) {
 ++Level;
 }
 while (Level > 0 && Voltage < Cutoffs[Level-1] - Hysteresis) {
 --Level;
 }
 for (int i=0; i<=NumCutoffs; ++i) {
 digitalWrite(LedPins[i], i == Level);
 }
 delay(20);
}
answered Jul 16, 2015 at 23:27
1
  • Your original code had digitalRead() instead of analogRead(). The voltage line should be connected to an analog pin (like A0) and then VoltagePin should be the corresponding pin. Hysteresis should be tuned for your application. Commented Jul 17, 2015 at 5:10
2

The state machine is a solid approach.

For this simple case, you can also solve this by implementing small dead bands around the change levels. When the value is in a range near the transition threshold, do nothing. Only when the value is certainly above or below the transition, make the change. Your code would be:

// Variables will change:
int currentVoltage = 0; // current voltage value
int previousVoltage = 0; // previous voltage value 
void loop() {
 int deadband = 5;
 // on very first loop, start with no dead band
 // this is necessary in case starting voltage is in 
 // middle of a transition range
 if (0 == previousVoltage) {
 deadband = 0;
 }
 // read the voltage across the battery voltage pin:
 currentVoltage = analogRead(BatteryVoltagePin);
 // test the voltage against the three ranges
 // value above maximum threshold
 if ((699+deadband) < currentVoltage) { 
 digitalWrite(GreenLedPin, HIGH);
 digitalWrite(OrangeLedPin, LOW );
 digitalWrite(RedLedPin, LOW );
 }
 //value between two thresholds (middle region)
 if (((628+deadband) < currentVoltage) && (currentVoltage <= (699-deadband))) { 
 digitalWrite(GreenLedPin, LOW);
 digitalWrite(OrangeLedPin, HIGH);
 digitalWrite(RedLedPin, LOW);
 }
 // value below minimum threshold
 if (currentVoltage < (628-deadband)) {
 digitalWrite(GreenLedPin, LOW);
 digitalWrite(OrangeLedPin, LOW );
 digitalWrite(RedLedPin, HIGH);
 }
 // Note that at this point, none of the if cases above
 // might have been true. When the voltage is right near the
 // thresholds, do nothing. Only when the voltage is definitively
 // in the range does the LED state change.
 // save the current state as the last state, 
 //for next time through the loop
 previousVoltage = currentVoltage;
 delay(10);
 return;
}

In function, this will achieve the same result as hysteresis.

answered Dec 19, 2015 at 11:04
2

Here is what I do. I detect whether the voltage is rising or falling and adjust the threshold based on that.

int ledPin = 13; //pin 13 will be connected to the MOSFET SS relay
void setup() {
 Serial.begin(9600);
 pinMode(ledPin, OUTPUT);
}
void loop() {
 int sensorValue = analogRead(A0); // This is the main battery voltage sensor
 int RLA1; //this will be connected to the MOSFET SS relay
 float threshold; // To enable some hysteresis and prevent the relay from chattering, we move the threshhold, depending on whether we are going up or down
 float Vmainold; // Used to detect whether we are going up or down
 float Vmain; // Main battery voltage.
 float Vaux; // Auxillary battery voltage
 Vmain = sensorValue / 204.6 + 9.8; // Add back the ~10V that the zener shifts for higher resolution
 if (Vmain > Vmainold) threshold = 13.2; // If the voltage is rising, set threshhold to 13.2
 else
 if (Vmain < Vmainold) && (Vmainold > 12.8) threshold = 12.8 // If the voltage is falling and previous voltage is above TH, set theshhold to 12.8
 else
 if (Vmain = Vmainold) threshold = 13.2; // If the voltage is stable, set threshhold to 13.2
 if (Vmain >= threshold) RLA1 = 1; // Charging
 else
 if (Vmain < threshold) { RLA1 = 0; Vmainold = Vmain; } // Not connected
 delay(1000);
 Serial.print(Vmain);
 Serial.print(" Main ");
 Serial.print(Vmainold);
 Serial.print(" old ");
 Serial.println();
 Vmainold = Vmain;
 digitalWrite(ledPin, RLA1);
}

`

James Waldby - jwpat7
8,9203 gold badges20 silver badges33 bronze badges
answered Dec 18, 2015 at 22:45

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.