Opentime of my valve not respected

Hello guys,

I have a problem with my code that controls a valve that needs to be open for x seconds every y seconds.

My interval time is 90 seconds and that works but its always ± 6 seconds behind so it becomes an interval of 96 seconds. This is not that much of a problem as i can just choose 84 seconds as intervalTime as solution.

I have a feeling the same problem applies on my openTime but that is more of a issue.
When i select an openTime of 1000,1500,2500 milliseconds it doenst matter it always comes around a 5 seconds openTime.

Does anyone know how i can fix that?

#include <ThingSpeak.h>
#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {  0x00, 0xxx, 0xxx, 0xxx, 0xxx, 0x0f };
IPAddress ip(192, 168, 140, 13);    // Your IP Address
EthernetClient client;

unsigned long myChannelNumber = 139266;
const char * myWriteAPIKey = "P0FGTCXXXXXXXX5B";

int trig = 7;
int echo = 6;

#include <DHT.h>

#define DHTPIN 9
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);

int chk;
float H; // slaat de vochtigheid op (humidity)
float T; // slaat de temperatuur op (temperature)
float h;
float t;
float d;
float v;

#include <LiquidCrystal.h>

LiquidCrystal lcd(A0, A1, 5, 4, 3, 2);

int valve = 8;  // This is the IO number of the valve

unsigned long intervalTime = 90000; //The interval of the valve opening in milliseconds
unsigned long openTime = 1000 ; // Time the valve has to be open on milliseconds

unsigned long lastOpenOrClose = 0;
unsigned long updateSensor = 0;

void setup() {
  lcd.begin(16, 2);
  dht.begin();

  Ethernet.begin(mac, ip);
  ThingSpeak.begin(client);

  pinMode(valve, OUTPUT);
  if (openTime > intervalTime) { // This is an illegal state so limit opentime to intervaltime
    openTime = intervalTime;
  }

  Serial.begin(9600);
  Serial.println(F("Aan het opstarten"));
  digitalWrite(valve, HIGH);
  lastOpenOrClose = millis();

  pinMode(trig, OUTPUT);
  pinMode(echo, INPUT);
}

void loop() {

  if (digitalRead(valve) == HIGH && millis() - lastOpenOrClose >= openTime) {
    digitalWrite(valve, LOW);
    lastOpenOrClose = millis();
  }

  if (digitalRead(valve) == LOW && millis() - lastOpenOrClose >= intervalTime) {
    digitalWrite(valve, HIGH);
    lastOpenOrClose = millis();
  }

  if (millis() - updateSensor > 2000)
  {
    updateSensor = millis();
    Serial.println("\n"); // Makes space between the reading on serial monitor

    H = dht.readHumidity();
    T = dht.readTemperature();

    Serial.print("Read sensor: ");
    switch (chk)
    {
      case 0: Serial.println("OK"); break;
      case -1: Serial.println("Checksum error"); break;
      case -2: Serial.println("Time out error"); break;
      default: Serial.println("Unknown error"); break;
    }

    Serial.print("Vochtigheid (%): ");
    Serial.print(H, 1);

    Serial.print("  Temperatuur (oC): ");
    Serial.println(T, 1);

    lcd.setCursor(0, 0);
    lcd.print("Temp: ");
    lcd.print(T, 1);
    lcd.print("C");

    lcd.setCursor(0, 1);
    lcd.print("RV: ");
    lcd.print(H, 1);
    lcd.print("%");

    long t = 0, h = 0, d = 0, v = 25; // met t = time, h = height, d= distance, v = volume

    digitalWrite(trig, LOW); //Transmitting the pulse
    delayMicroseconds(2);
    digitalWrite(trig, HIGH);
    delayMicroseconds(10);
    digitalWrite(trig, LOW);

    t = pulseIn(echo, HIGH); // waiting for the pulse

    h = t / 58; // afstand berekenen
    h = h - 6; // Correctie van verschil sensor en max water niveau
    h = 14 - h; // Water niveau van 0 - 50 cm

    d = (100 / 14) * h; // Afstand in %, 0 - 100%

    v = (v * d) / 100 ; // volume water resterend in reservoir

    Serial.print("Waterniveau: ");
    Serial.print(d); // versturen naar computer
    Serial.println("%");

    Serial.print("Volume water in het reservoir: ");
    Serial.print(v); // versturen naar computer
    Serial.println(" liter");

    ThingSpeak.setField(1, T);
    ThingSpeak.setField(2, H);
    ThingSpeak.setField(3, v);
    ThingSpeak.setField(4, d);

    ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);

  }

  delay(2000);

}

[/code]

You can start by removing the delay(2000).

How long does pulseIn() wait?

To make the intervals more precise, don’t just use the current value of millis() when resetting lastOpenOrClose. Add the interval time. That way if you are late, the lateness doesn’t accumulate.

MorganS: You can start by removing the delay(2000).

How long does pulseIn() wait?

To make the intervals more precise, don't just use the current value of millis() when resetting lastOpenOrClose. Add the interval time. That way if you are late, the lateness doesn't accumulate.

I removed the delay(2000). This changed the openTime to about 4 seconds and the intervalTime is almost on the mark. +- 91 seconds

I guess pulseIn() waits just as long until it gets a signal back, i used code from an instructable so im not sure about this.

Im not understanding the last bit about adding intervalTime to the millis(). Not a coder btw, im making a project for my thesis.

Here’s how I do a simple “duty cycle”.

unsigned long startTime;
int interval = 9000, onTime = 1000;
bool onState;
void setup() {
  pinMode(13,OUTPUT);
  startTime = millis();
  
}

void loop() {
  if(millis() - startTime < onTime)
    onState = true;
  else onState = false;

  if(millis() - startTime > interval)
    startTime = millis();
  digitalWrite(13,onState); 
}

outsider:
Here’s how I do a simple “duty cycle”.

unsigned long startTime;

int interval = 9000, onTime = 1000;
bool onState;
void setup() {
  pinMode(13,OUTPUT);
  startTime = millis();
 
}

void loop() {
  if(millis() - startTime < onTime)
    onState = true;
  else onState = false;

if(millis() - startTime > interval)
    startTime = millis();
  digitalWrite(13,onState);
}

The “duty cycle” that i used (blink without delay):

int valve = 8;  // This is the IO number of the valve

unsigned long intervalTime = 90000; //The interval of the valve opening in milliseconds
unsigned long openTime = 2000 ; // Time the valve has to be open on milliseconds

unsigned long lastOpenOrClose = 0;

void setup() {

  pinMode(valve, OUTPUT);
  if (openTime > intervalTime) { // This is an illegal state so limit opentime to intervaltime
    openTime = intervalTime;
  }

  Serial.begin(9600);
  Serial.println(F("Aan het opstarten"));
  digitalWrite(valve, HIGH);
  lastOpenOrClose = millis();
}

void loop() {

  if (digitalRead(valve) == HIGH && millis() - lastOpenOrClose >= openTime) {
    digitalWrite(valve, LOW);
    lastOpenOrClose = millis();
  }

  if (digitalRead(valve) == LOW && millis() - lastOpenOrClose >= intervalTime) {
    digitalWrite(valve, HIGH);
    lastOpenOrClose = millis();

This worked perfectly at start but i guess somewhere along the line with adding extra code of the distance sensor and thingspeak stream code it broke

Instead of writing

  lastOpenOrClose = millis();

You can add the time that has just elapsed:

  lastOpenOrClose += openTime;

Yes, pulseIn() just waits. It's a little bit stupid but it does a good job for simple programs.

Ok so i changed a bit of my code.
Apparently its the ThingsSpeak.writefields() that was conflicting with my interval (probably because it updating so much?)
So i seperated it from the updateSensor piece with this so it only gets updated once every 15 seconds:

if (millis() - updateThingSpeak > 15000)
{
ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
updateThingSpeak = millis();
}

With this the intervals get respected although there are a few flukes sometimes where suddenly its open for 4 seconds

But now the arduino stops streaming after a while, sometimes after 1 stream sometimes after 6 minutes

It must have something to do with the other millis() functions i think.
Sometimes when i reset the arduino the valve just stays open, and then when i reset again its fixed.

Full code:

#include <ThingSpeak.h>
#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {  0x00, 0xxx, 0xxx, 0x03, 0x5f, 0x0f };
EthernetClient client;

unsigned long myChannelNumber = 139266;
const char * myWriteAPIKey = "P0FGTXXX1XGN5B";

int trig = 7;
int echo = 6;

#include <DHT.h>

#define DHTPIN 9
#define DHTTYPE DHT22

DHT dht(DHTPIN, DHTTYPE);

int chk;
float H; // slaat de vochtigheid op (humidity)
float T; // slaat de temperatuur op (temperature)

#include <LiquidCrystal.h>

LiquidCrystal lcd(A0, A1, 5, 4, 3, 2);

int valve = 8;  // This is the IO number of the valve

unsigned long intervalTime = 120000; //The interval of the valve opening in milliseconds
unsigned long openTime = 2000 ; // Time the valve has to be open on milliseconds

unsigned long lastOpenOrClose = 0;
unsigned long updateSensor = 0;
unsigned long updateThingSpeak = 0;

void setup() {
  lcd.begin(16, 2);
  dht.begin();

  Ethernet.begin(mac);
  ThingSpeak.begin(client);

  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    while (true);
  }

  pinMode(valve, OUTPUT);
  if (openTime > intervalTime) { // This is an illegal state so limit opentime to intervaltime
    openTime = millis();
  }

  Serial.begin(9600);
  Serial.println(F("Aan het opstarten"));
  digitalWrite(valve, HIGH);
  lastOpenOrClose = millis();

  pinMode(trig, OUTPUT);
  pinMode(echo, INPUT);
}

void loop() {

  if (digitalRead(valve) == HIGH && millis() - lastOpenOrClose >= openTime) {
    digitalWrite(valve, LOW);
    lastOpenOrClose = millis();
  }

  if (digitalRead(valve) == LOW && millis() - lastOpenOrClose >= intervalTime) {
    digitalWrite(valve, HIGH);
    lastOpenOrClose = millis();
  }

  if (millis() - updateSensor > 5000)
  {
    updateSensor = millis();
    Serial.println("\n"); // Makes space between the reading on serial monitor

    H = dht.readHumidity();
    T = dht.readTemperature();

    Serial.print("Read sensor: ");
    switch (chk)
    {
      case 0: Serial.println("OK"); break;
      case -1: Serial.println("Checksum error"); break;
      case -2: Serial.println("Time out error"); break;
      default: Serial.println("Unknown error"); break;
    }

    Serial.print("Vochtigheid (%): ");
    Serial.print(H, 1);

    Serial.print("  Temperatuur (oC): ");
    Serial.println(T, 1);

    lcd.setCursor(0, 0);
    lcd.print("Temp: ");
    lcd.print(T, 1);
    lcd.print("C");

    lcd.setCursor(0, 1);
    lcd.print("RV: ");
    lcd.print(H, 1);
    lcd.print("%");

    long t = 0, h = 0, d = 0, v = 25; // met t = time, h = height, d= distance, v = volume

    digitalWrite(trig, LOW); //Transmitting the pulse
    delayMicroseconds(2);
    digitalWrite(trig, HIGH);
    delayMicroseconds(10);
    digitalWrite(trig, LOW);

    t = pulseIn(echo, HIGH); // waiting for the pulse

    h = t / 58; // afstand berekenen
    h = h - 6; // Correctie van verschil sensor en max water niveau
    h = 14 - h; // Water niveau van 0 - 50 cm

    d = (100 / 14) * h; // Afstand in %, 0 - 100%

    v = (v * d) / 100 ; // volume water resterend in reservoir

    Serial.print("Waterniveau: ");
    Serial.print(d); // versturen naar computer
    Serial.println("%");

    Serial.print("Volume water in het reservoir: ");
    Serial.print(v); // versturen naar computer
    Serial.println(" liter");

    ThingSpeak.setField(1, T);
    ThingSpeak.setField(2, H);
    ThingSpeak.setField(3, v);
    ThingSpeak.setField(4, d);
  }

  if (millis() - updateThingSpeak > 15000)
  {
    ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
    updateThingSpeak = millis();
  }
}