5

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;
 }
  • This kinda sounds like a case where you may need debouncing... Have you already done this? – Paul Mar 8 '15 at 16:07
  • no I haven't. Could you explain this in more details please? – Andy Mar 8 '15 at 16:22
6

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.)

  • 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? – Andy Mar 8 '15 at 20:17
  • 1
    enum is not highlighted by the syntax highlighting. But it is accepted by the compiler. I have added some details. – fuenfundachtzig Mar 9 '15 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! – Andy Mar 9 '15 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. – fuenfundachtzig Mar 10 '15 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);
}
  • 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. – Warren MacEvoy Jul 17 '15 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.

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);
}

`

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.