Go Down

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

MrGlasspoole

May 13, 2017, 10:43 amLast 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 recognisedunsigned long turnOnDelay = 3000;        // Wait to turn on relayunsigned long turnOffDelay = 3000;       // Turn off relay after this timebool relayReady = false;                 // Flag for load recognisedbool 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

#1
May 13, 2017, 11:03 am
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

#2
May 13, 2017, 11:30 am
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 pmLast 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

#4
May 13, 2017, 01:30 pm
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

#5
May 13, 2017, 01:31 pm
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 ifend 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

#6
May 13, 2017, 01:35 pm
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 recognisedvoid setup(){}void loop(){}void automatic(){double Irms = checkPower.calcIrms(1480);   // Calculate Irms onlystatic 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 pmLast 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 outputconst uint32_t SERIAL_BAUD = 9600;   // Set serial 0 speedEnergyMonitor checkPower;            // Create an instanceconst uint8_t RELAY = 8;   // Relay circuit is connected to D8const uint8_t SENSOR = A3; // ECS1030-L72 is connected to A3// Define switch pinsconst uint8_t pinS[] = {3, 4};                      // Array of used switch pinsuint8_t pinSCount = sizeof(pinS) / sizeof(pinS[0]); // Count used switch pins// Define unused pinsconst uint8_t pin[] = {2, 5, 6, 7, 9, 10, 11, 12, 13}; // Array of unused digital pinsconst uint8_t pinA[] = {A0, A1, A2, A4, A5, A6, A7};   // Array of unused analog pinsuint8_t pinCount = sizeof(pin) / sizeof(pin[0]);       // Count unused digital pinsuint8_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 pmLast 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

#9
May 13, 2017, 04:20 pm
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

#10
May 13, 2017, 08:08 pm
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

#11
May 13, 2017, 09:31 pm
I made no changes since the last one...

Robin2

#12
May 13, 2017, 10:58 pm
I made no changes since the last one...

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 amLast 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

#14
May 14, 2017, 11:52 am
`Serial.println(currentMillis);`
`Irms < 0.03`