Go Down

Topic: If value is higher than for given time (Read 596 times) previous topic - next topic

MrGlasspoole

May 13, 2017, 10:43 am Last Edit: May 13, 2017, 10:56 am by MrGlasspoole
Millis is killing me again and after 4 hours of trial and error i need to ask.

I have a current sensor and need on and off delay.
If the sensor measures more then x ampere for 3 seconds then turn on the relay.
If the sensor then measures less then x ampere, wait for 3 seconds and then turn off the relay.

I guess the problem is that "loadRecognisedTime" is updated all the time and not only when load is recognized?


Code: [Select]
unsigned long loadRecognisedTime = 0;  // Time when load was recognised
unsigned long turnOnDelay = 3000;        // Wait to turn on relay
unsigned long turnOffDelay = 3000;       // Turn off relay after this time
bool relayReady = false;                 // Flag for load recognised
bool relayState = false;                 // Relay is on or not.

void automatic() {

  double Irms = checkPower.calcIrms(1480);   // Calculate Irms only
  unsigned long currentMillis = millis();

  if(Irms >= 0.03) {                         // If RMS is higher then 0.03A
    loadRecognisedTime = currentMillis;
    relayReady = true;
  }

  if (relayReady) {
    if ((unsigned long)(currentMillis - loadRecognisedTime) >= turnOnDelay) {
      digitalWrite(RELAY, HIGH);         // turn relay on
    }
  }

}

Robin2

To test if something is HIGH for a certain length of time you need to save the value of millis() every time you detect a LOW. Then if the difference between millis() and the saved value exceeds your interval you can be sure that t was HIGH throughout the interval. That may seem perverse, but it works. Something like this pseudo code ...
Code: [Select]
if(Irms < 0.03) {
    lastLowTime = currentMillis;
}
if (millis() - lastLowTime >= interval) {
   // has been > 0,03 for interval so do stuff
}


...R
Two or three hours spent thinking and reading documentation solves most programming problems.

UKHeliBob

Quote
I guess the problem is that "loadRecognisedTime" is updated all the time and not only when load is recognized?
So change the program so that it only saves the millis() value when the load becomes more than the threshold value rather than when it is more than the threshold value.  The StateChangeDetection example in the IDE will show you the principle.
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

MrGlasspoole

#3
May 13, 2017, 12:51 pm Last Edit: May 13, 2017, 01:32 pm by MrGlasspoole
This code now is half working. But if i remove the load the test LED (instead of relay) is blinking.
1 second off, 3 seconds on...

Code: [Select]
void automatic() {

  double Irms = checkPower.calcIrms(1480);   // Calculate Irms only
  unsigned long currentMillis = millis();

  if (loadState == false) { // There is no load

    if(Irms < 0.03) {
      noLoadTime = currentMillis;
    }

    if (millis() - noLoadTime >= turnOnDelay) {  // If RMS is higher then 0.03A longer then 3 seconds
      digitalWrite(RELAY, HIGH);                 // turn relay on
      loadState = true;
    }

  }

  if (loadState == true) { // There is load
  
    if(Irms > 0.03) {
      loadRemoved = currentMillis;
    }

    if ((millis() - loadRemoved) >= turnOffDelay) {
      digitalWrite(RELAY, LOW);
      loadState = false;
    }
    
  }

}

void loop() {
 
  uint8_t switchState_1 = digitalRead(3);  // Read the switch value (pin D3)
  uint8_t switchState_2 = digitalRead(4);  // Read the switch value (pin D4)
 
  if(switchState_1 == LOW) {           // If pin 3 is low
    digitalWrite(RELAY, HIGH);         // turn relay on
    loadState = false;                 // and reset load detection state
  } else if (switchState_2 == LOW) {   // If pin 4 is low
    loadState = false;                 // reset load detection state
    automatic();                       // and run the automatic loop
  } else {
    digitalWrite(RELAY, LOW);          // Turn relay off
    loadState = false;                 // and reset load detection state
  }

}


There is a 3 state switch connected to D3 and D4.
If D3 is low turn on relay.
If D4 is low go to detection mode.
If D3 and D4 are high do nothing.

So change the program so that it only saves the millis() value when the load becomes more than the threshold value
That is what i try to figure out since hours.

Robin2

You need to post your complete program.

I don't understand what you mean by "But if i remove the load the test LED (instead of relay) is blinking.
1 second off, 3 seconds on...
". Please describe what happens in both situations

Put in some temporary Serial.print() statements to let you follow the progress of the program.

,,,R
Two or three hours spent thinking and reading documentation solves most programming problems.

UKHeliBob

Try this alternative approach.  Obviously you need to write it properly and use your own variable and function names.

Code: [Select]

start of loop()
  currentLoad = readTheSensor()
  if not timing and currentLoad > threshold
    startTime = millis()
    timing = true
  end if
  else
    timing = false
  end else

  if timing and currentTime - startTime >= period
    //the load has been above the threshold for longer than period.  Do something
  end if
end of loop()
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

wildbill

Try this:
Code: [Select]

const float CurrentThreshold=0.03;
const unsigned long TimeThreshold=3000UL;
const byte RELAY=2;
unsigned long loadRecognisedTime = 0;  // Time when load (or lack of it) was recognised

void setup()
{
}

void loop()
{
}

void automatic()
{
double Irms = checkPower.calcIrms(1480);   // Calculate Irms only
static bool PrevLoadOn=false;
bool LoadOn = Irms > CurrentThreshold;
if(LoadOn != PrevLoadOn)
  {
  loadRecognisedTime=millis();
  LoadOn=PrevLoadOn;  
  }
if(millis()-loadRecognisedTime > TimeThreshold)
    digitalWrite(RELAY,LoadOn ? HIGH : LOW);
}

Compiled (without the calcIrms piece), not tested. Obviously you'll need to fit automatic & my constants into your own sketch.

MrGlasspoole

#7
May 13, 2017, 01:37 pm Last Edit: May 13, 2017, 03:06 pm by MrGlasspoole
I don't understand what you mean by "But if i remove the load the test LED (instead of relay) is blinking.
The load from the current sensor...

If there is load (ampere detected) the relay is on (instead of a relay i use a LED for testing).
If i remove the load the relay is not of - it goes on for 3 seconds and then off for 1 second constantly.

I did edit my last posting because there is also a switch.

EDIT
The one from Robin is the only one that is half working. Can't get the others do anything.
Code: [Select]
#include <EmonLib.h>   // Get it here: github.com/openenergymonitor/EmonLib

/**************************************************************
* MISC SETUP                                                  *
**************************************************************/
#define DEBUG 1                      // Set to 1 for serial port output
const uint32_t SERIAL_BAUD = 9600;   // Set serial 0 speed
EnergyMonitor checkPower;            // Create an instance

const uint8_t RELAY = 8;   // Relay circuit is connected to D8
const uint8_t SENSOR = A3; // ECS1030-L72 is connected to A3

// Define switch pins
const uint8_t pinS[] = {3, 4};                      // Array of used switch pins
uint8_t pinSCount = sizeof(pinS) / sizeof(pinS[0]); // Count used switch pins

// Define unused pins
const uint8_t pin[] = {2, 5, 6, 7, 9, 10, 11, 12, 13}; // Array of unused digital pins
const uint8_t pinA[] = {A0, A1, A2, A4, A5, A6, A7};   // Array of unused analog pins
uint8_t pinCount = sizeof(pin) / sizeof(pin[0]);       // Count unused digital pins
uint8_t pinACount = sizeof(pinA) / sizeof(pinA[0]);    // Count unused analog pins

/**************************************************************
* SETUP                                                       *
**************************************************************/
void setup() {

  #ifdef DEBUG
    Serial.begin(SERIAL_BAUD); // Initialize serial communications
    while (!Serial) {;}        // Wait for serial port to connect
  #endif // END DEBUG
 
  for (uint8_t i = 0; i < pinCount; i++) {
    pinMode(pin[i], OUTPUT);    // Set unused digital pins as output
    digitalWrite(pin[i], LOW);  // Set unused digital pins state to low
  }

  for (uint8_t i = 0; i < pinACount; i++) {
    pinMode(pinA[i], OUTPUT);    // Set unused analog pins as output
    digitalWrite(pinA[i], LOW);  // Set unused analog pins state to low
  }

  for (uint8_t i = 0; i < pinSCount; i++) {
    pinMode(pinS[i], INPUT_PULLUP);  // Set switch pins as input and enable internal pull-up resistor
  }

  pinMode(RELAY, OUTPUT);     // Set Relay pin (D8) as output
  digitalWrite(RELAY, LOW);   // Set Relay to off

  checkPower.current(SENSOR, 20);  // Sensor input pin (A3) and calibration

}

/**************************************************************
* AUTOMATIC LOOP                                              *
**************************************************************/
unsigned long noLoadTime = 0;
unsigned long loadRemoved = 0;
unsigned long turnOnDelay = 3000;
unsigned long turnOffDelay = 3000;
bool loadState = false;

void automatic() {

  double Irms = checkPower.calcIrms(1480);   // Calculate Irms only
  unsigned long currentMillis = millis();

  if (loadState == false) { // There is no load
       
    if(Irms < 0.03) {
      noLoadTime = currentMillis;
    }

    if (millis() - noLoadTime >= turnOnDelay) {  // If RMS is higher then 0.03A longer then 3 seconds
      digitalWrite(RELAY, HIGH);                 // turn relay on
      loadState = true;
    }

  }

  if (loadState == true) { // There is load
 
    if(Irms > 0.03) {
      loadRemoved = currentMillis;
    }

    if ((millis() - loadRemoved) >= turnOffDelay) {
      digitalWrite(RELAY, LOW);
      loadState = false;
    }
   
  }

  #ifdef DEBUG
    Serial.print(Irms*230.0);  // Apparent power
    Serial.print(" ");
    Serial.println(Irms);      // Irms
  #endif // END DEBUG

}

/**************************************************************
* MAIN LOOP                                                   *
**************************************************************/
void loop() {
 
  uint8_t switchState_1 = digitalRead(3);  // Read the switch value (pin D3)
  uint8_t switchState_2 = digitalRead(4);  // Read the switch value (pin D4)
 
  if(switchState_1 == LOW) {           // If pin 3 is low
    digitalWrite(RELAY, HIGH);         // turn relay on
    loadState = false;                 // and reset load detection state
  } else if (switchState_2 == LOW) {   // If pin 4 is low
    loadState = false;                 // reset load detection state
    automatic();                       // and run the automatic loop
  } else {
    digitalWrite(RELAY, LOW);          // Turn relay off
    loadState = false;                 // and reset load detection state
  }

  #ifdef DEBUG
    Serial.println(loadState);      // Irms
  #endif // END DEBUG

}

The two problems are:
1. The turn off delay is not working
2. If i switch the switch to other states and go back to automatic and there is load/current then the relay goes instantly on without the 3 seconds delay.

Robin2

#8
May 13, 2017, 03:41 pm Last Edit: May 13, 2017, 03:47 pm by Robin2
You say that half is working but I can't figure out which half.

In your Original Post you say that the relay should only go ON when the amps are high for over 3 seconds. Then you say that if it detects low amps it should turn the relay OFF after 3 seconds.

The latter sounds like it should be triggered by a low amps reading  but you are checking for a high amps reading with
Code: [Select]
if(Irms > 0.03) {
      loadRemoved = currentMillis;


...R
Two or three hours spent thinking and reading documentation solves most programming problems.

MrGlasspoole

The half that is working is the "on delay" with the exception that if i disconnect pin 4 from low and reconnect it to low the relay turns on instantly.

From printing currentMillis it seems like currentMillis is not starting from zero after stopping and starting the automatic loop again with pin 4? I guess that's the problem with the relay turning on instantaneously?

The off delay:
If i change the off part to "Irms < 0.03" the on part also stops working.

Robin2

From printing currentMillis it seems like currentMillis is not starting from zero after stopping and starting the automatic loop again with pin 4? I guess that's the problem with the relay turning on instantaneously?

The off delay:
If i change the off part to "Irms < 0.03" the on part also stops working.
You have mentioned at least 2 changes to the program. Post the latest version so that I can relate your comments to it.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

MrGlasspoole

I made no changes since the last one...

Robin2

I made no changes since the last one...
That is not what your Reply #9 says

Quote
From printing currentMillis it seems like currentMillis is not starting from zero
Quote
If i change the off part to "Irms < 0.03" the on part also stops working
Why are you wasting our time?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

756E6C

#13
May 14, 2017, 02:16 am Last Edit: May 14, 2017, 02:33 am by 756E6C
Maybe something like this simple on/off delay sketch, connect a pushbutton from GND to pin 4 and see if it works like you want:
Code: [Select]
unsigned long strtOnDly, strtOffDly;
const byte RELAY = 8, btn = 4;
int on_off_Delay = 3000;
bool relayReady = false;

void setup(){
  pinMode(btn, INPUT_PULLUP);
  pinMode(RELAY,OUTPUT);
}
void loop(){
  relayReady = !digitalRead(btn); 
  if(relayReady == true)
    strtOffDly = millis(); // reset Off Delay timer
  else strtOnDly = millis(); // reset On Delay timer
  if(relayReady && millis() - strtOnDly > on_off_Delay)
    digitalWrite(RELAY,HIGH);
  else if(millis() - strtOffDly > on_off_Delay)
    digitalWrite(RELAY,LOW);
}

MrGlasspoole

Adding
Code: [Select]
Serial.println(currentMillis);
is a code change?

And no
Code: [Select]
Irms < 0.03
is not in the code because as i wrote it does not work.

Go Up