Trying to introduce hysteresis to several switch points from analog input

Good afternoon good and helpful people of the web.

I have a problem that I have been struggling to correct this afternoon. The program should read an analog input on A0 (which it shows when I open the Serial Monitor). It should then switch ON one or more of three leds when the ADC conversion integer (0-1023) exceeds a set value.

My problem is that I cannot seem to get the hysteresis to work, and the outputs will flicker if the input varies around the switch point. Please may I have some help/pointers to make this work.

The relevant code is below:

//The electrical supply to this flat trips at about 40A, and cannot be upgraded easily. This code is intended to shed low priority electrical
//loads in favour of keeping on the higher priority ones. The Arduino will take several readings of an analogue input at regular time intervals using 
//millis(). These readings (called samples here) represent the electrical loadings of all appliances except the heating (6kW electric boiler).
//Thus when appliance loads exceed approx. 4kW we will restrict or inhibit heating to keep total power load below 10kW (40A).


int ctPin = A0;                   //pin connected to current transformer (CT)
int currentCurrent = 0;           //present value of current measurement
int sampleCount = 0;              //number of current readings taken. Used to calc average
int total = 0;                    //the cumulative total of several current samples
int count = 0;                    //the number of samples to average
int averageCurrent = 0;           //the result of averaging
unsigned long millisLast = 0;     //Time of last reading
unsigned long interval = 0;       //Duration since last reading taken
unsigned long samplePeriod = 100; //Period between taking current readings in milliseconds
const int relayStage1 = 10;             //minimum load shedding relay
const int relayStage2 = 11;             //medium load shedding relay
const int relayStage3 = 12;             //maximum load shedding relay
const int resetDetectTrip = 2;          //pushbutton used in diagnostics


void setup()
{
  Serial.begin(9600);         //start Serial in case we need to print debugging info
  pinMode (resetDetectTrip, INPUT);   //resets the time interval since peak current was detected
  pinMode (ctPin, INPUT);       //pin mode to allow analogue readings
  pinMode (relayStage1, OUTPUT);//pin mode for outputs to relays
  pinMode (relayStage2, OUTPUT);
  pinMode (relayStage3, OUTPUT);
  millisLast = millis();        //This is so that interval is relative to the time now
}


void loop()
{
  interval = millis() - millisLast;           //time interval since last reading taken
  
  if (interval >= samplePeriod)                 //if true it's time to take a reading of current
   {
   currentCurrent = analogRead(ctPin);  //read the CT input and return the value as an integer 0 - 1023
   total = total + currentCurrent;       //Each new sample added to a starting total of zero
   count ++;                            //increment count value to use for average value later
   }

   if (count >= 10)
   {
    averageCurrent = total / count;   //used in the decision making code later
    Serial.println(averageCurrent);
    total = 0;                        //reset ready for the next samples
    count = 0;                        //reset ready for the next samples too
   }

   loadReduction();                   //call function to turn off electrical items based on the values of
                                      //averageCurrent and userPreferences and otherModifiers which haven't been coded yet!
}
 
void loadReduction()                          //function to energise relays through Arduino outputs, add hysteresis, and
                                              //take the decisions as to what stays on and what is turned off.
                                              //There are 3 discrete load reducing settings and actions.
    {
      static int hysteresis3 = 0;
      static int hysteresis2 = 0;
      static int hysteresis1 = 0;                    //set all hysteresis values to zero initially
      int actionStage3 = 1000;                       //Empirical setting, the highest load reduction action required.
      int actionStage2 = 600;
      int actionStage1 = 300;
      
      if (relayStage3 == HIGH)                         //each action has its own hysteresis value
      {
        Serial.println("relay 3 is ON");
        hysteresis3 = 50;
      }
      if (relayStage2 == HIGH)
      {
        hysteresis2 = 40;
      }
      if (relayStage1 == HIGH)
      {
        hysteresis1 = 30;
      }
      
      if (averageCurrent > (actionStage3 - hysteresis3))  //hysteresis3 is 0 on first test, 30 if relay already energised
                                                        //Used to prevent rapid on-off switching of outputs
      {
        digitalWrite (relayStage3, HIGH);               //Energises outputs to shed the maximum load
      }
      else digitalWrite (relayStage3, LOW);             //de-energises relay and returns hysteresis to zero
      
      
      if (averageCurrent > (actionStage2 - hysteresis2))   //hysteresis is 0 on first test, 20 if relay already energised
                                                        //Used to prevent rapid on-off switching of outputs
      {
        digitalWrite (relayStage2, HIGH);               //Energises outputs to shed a medium load
      }
       else digitalWrite (relayStage2, LOW);
       
       
      if (averageCurrent > (actionStage1 - hysteresis1))    //hysteresis is 0 on first test, 10 if relay already energised
                                                        //Used to prevent rapid on-off switching of outputs
      {
        digitalWrite (relayStage1, HIGH);               //Energises outputs to shed the minimum load
      }
       else digitalWrite (relayStage1, LOW);

     }


     
void detectTripValue()                //function to record the maximum current measured,
                                      //useful to adjust loadReduction switch points in case of nuisance trips
                                      //Record the time interval since the last maxValue was measured
      {                                //DO NOT reset time interval at setup()!!
                                      //Reset time interval only after pushbutton is pressed

      }

Regards, GM

This is a good example of why not to post snippets.

But from this I already see problems

      if (relayStage3 == HIGH)                         //each action has its own hyteresis value
[...]
        digitalWrite (relayStage3, HIGH);

Can you spot it as well?

So please post ALL the code.

Few extra free tips:

  • Loop up arrays
  • Watch your indentation
  • Place an { directly after or the line after a function or whatever. No empty/comment lines in between. Makes the code terrible hard to read. If you have Multi line comments about a function, simply place it in front.

You made a mistake, comparing relayStageX instead of digitalRead(relayStageX)

Yes, you also should consider using arrays for all your variables indexed with 1, 2 and 3:
the loadreduction function would be simplified to

void loadReduction() {
 //function to energise relays through Arduino outputs, add hysteresis, and
 //take the decisions as to what stays on and what is turned off.
 //There are 3 discrete load reducing settings and actions.
 int hysteresis[4] = {0};
 int actionStage[4] = {0, 1000, 600, 300};

 for (byte i = 1; i < 4; i++) {
    if (digitalRead(relayStage[i])) hysteresis[i] = 100 * i;
   if (averageCurrent > actionStage[i] - hysteresis[i]) digitalWrite (relayStage[i], HIGH);
   //Energises outputs to shed the maximum load
   else digitalWrite (relayStage[i], LOW);
   //hysteresis3 is 0 on first test, 300 if relay already energised
 }
}

I kept your numbering from 1 to 3, so you feel like home... :slight_smile:

You can even simplify the digitalWrites using ternary logic :

void loadReduction() {
  //function to energise relays through Arduino outputs, add hysteresis, and
  //take the decisions as to what stays on and what is turned off.
  //There are 3 discrete load reducing settings and actions.
  int hysteresis[4] = {0};
  int actionStage[4] = {0, 1000, 600, 300};

  for (byte i = 1; i < 4; i++) {
    if (digitalRead(relayStage[i])) hysteresis[i] = 100 * i;
    digitalWrite (relayStage[i], averageCurrent > actionStage[i] - hysteresis[i] ? HIGH : LOW);
  }
}

septillion:
This is a good example of why not to post snippets.

But from this I already see problems

      if (relayStage3 == HIGH)                         //each action has its own hysteresis value

[...]
       digitalWrite (relayStage3, HIGH);




Can you spot it as well?

So please post ALL the code...

Original Post edited to include all the code as you requested, but sad to say I cannot see the glaring error you are trying to point out to me!

My logic is saying 'If relay 3 is ON (and the if statement is TRUE) then change the value of hysteresis from 0 to 50, then test if the measured current is greater than the trigger value MINUS the hysteresis value (50). If it is greater then energise the relay output, else (when current falls 50 below the trigger point) turn that same output OFF.'
Once the relay output is OFF then the trigger point returns to that without the hysteresis...

The problem remains that the hysteresis always stays at zero, as observed by me on the serial monitor.

Thank you all, it now works by comparing the digital read of the output's condition with TRUE. At the risk of transgressing the 'no snippets' rule the corrected snippet is posted below.

 if (digitalRead (relayStage3) == HIGH)                         //each action has its own hysteresis value
      {
        hysteresis3 = 150;
      }
      else hysteresis3 = 0;
      
      if (digitalRead (relayStage2) == HIGH)                         //each action has its own hysteresis value
      {
        hysteresis2 = 100;
      }
      else hysteresis2 = 0;
      
      if (digitalRead (relayStage1) == HIGH)                         //each action has its own hysteresis value
      {
        hysteresis1 = 50;
      }
      else hysteresis1 = 0;
      
      if (averageCurrent > (actionStage3 - hysteresis3))  //hysteresis3 is 0 on first test, 150 if relay already energised
                                                        //Used to prevent rapid on-off switching of outputs
      {
        digitalWrite (relayStage3, HIGH);               //Energises outputs to shed the maximum load
      }
      else digitalWrite (relayStage3, LOW);             //de-energises relay and returns hysteresis to zero
      
      
      if (averageCurrent > (actionStage2 - hysteresis2))   //hysteresis is 0 on first test, 100 if relay already energised
                                                        //Used to prevent rapid on-off switching of outputs
      {
        digitalWrite (relayStage2, HIGH);               //Energises outputs to shed a medium load
      }
       else digitalWrite (relayStage2, LOW);
       
       
      if (averageCurrent > (actionStage1 - hysteresis1))    //hysteresis is 0 on first test, 50 if relay already energised
                                                        //Used to prevent rapid on-off switching of outputs
      {
        digitalWrite (relayStage1, HIGH);               //Energises outputs to shed the minimum load
      }

and whilst it may not look pretty, at least I have a working code (snippet) that I can tidy up piecemeal once I learn a bit more code.

Thank you lesept for this:
'You made a mistake, comparing relayStageX instead of digitalRead(relayStageX)'.

As for the other tips on using arrays and the like, I'm not that good yet; still learning though.

Over and out, for now.

GM

Glorymill:
[...] but sad to say I cannot see the glaring error you are trying to point out to me!

lesept pointed out the same error. That's why I'm an advocate for good variable names. If you would have called it 'relayPin1' you would probably not have made the same mistake :wink: