3

Here data is updated after every 2 seconds. But when the condition is true then the valve and motor process starts and data is updated only after completion of that part (i.e., data is updated after 25 seconds). I want data updation and condition checking simultaneously.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define ONE_WIRE_BUS 4 #define TEMPERATURE_PRECISION 9

#define motor 6 #define valve 7

OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire);

const float thld = 35;

#define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void setup() { Serial.begin (115200); pinMode(motor, OUTPUT); pinMode(valve, OUTPUT); delay(10);

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F(&quot;SSD1306 allocation failed&quot;));
for(;;);

} delay(2000);

display.setTextColor(WHITE); } void loop() {
sensors.requestTemperatures(); float temp = sensors.getTempCByIndex(0);
Serial.println(temp); Serial.println((char)176); Serial.println("C | "); if ( temp > thld) { digitalWrite (valve, LOW); delay (5000); digitalWrite (motor, LOW); delay (10000);

digitalWrite (motor, HIGH); delay (6000); digitalWrite (valve, HIGH); delay (2000); } else { digitalWrite (motor, HIGH); digitalWrite (valve, HIGH); }

display.clearDisplay(); display.setCursor(0, 0); display.setTextSize(1); display.println("Temperature");
display.setCursor(0, 30); display.println("Soil & Water Engg."); display.setTextSize(2); display.setCursor(0,10); display.println(temp); display.setCursor(75, 10); display.println("C"); display.setCursor(0,40); display.display();

delay (2000); }

3 Answers3

6

You need to think about this with a real-world analogue.

I like to use boiling an egg...

There are three ways you can boil an egg:

Blocking operation

  1. Put the egg in the boiling water
  2. Stand there staring at it for three minutes or so
  3. Take the egg out of the water and eat it

Timer operation

  1. Put the egg in the boiling water
  2. Set a timer for 3 minutes time
  3. Go off and do something else
  4. When the alarm goes off take the egg out and eat it

Time polling

  1. Put the egg in the boiling water and record the current time
  2. Go off and do something else, every so often glancing at your watch and comparing the time to the time you started
  3. When three minutes have passed take the egg out and eat it

Currently you are using the blocking operation where you're stood staring at the egg until it's time to do something. You need to change your code to use one of the other methods.

Most times the time polling method is good enough. You may get some jitter where whatever else it is you are doing causes a delay before you get to glance at your watch, but that is rarely an issue. For greater precision (better for higher frequency checking) you use a timer which triggers an interrupt so you always do things at more precise intervals.

There is an example in the IDE called BlinkWithoutDelay which implements the time polling in a very simplistic way - record the time, then compare that time and the current time, and when it's elapsed change the LED's state, recording a new time.

Majenko
  • 105,851
  • 5
  • 82
  • 139
0

The general approach for non-blocking code is to use millis() and compare durations:

bool active = false;
unsigned long start = 0;
const unsigned long duration = 25000; // 25 seconds

void loop() { unsigned long now = millis(); if (start_condition) { active = true; start = now; turn_on(); } if (active && now - start >= duration) { active = false; turn_off(); } }

Properly written non-blocking code never uses delay() but instead records time stamps and compares durations to see when to do something.

One trap for beginners: Never compare times, only durations! Because millis() wraps around (after 2^32 msec which is ~49.7 days) you can have a start time that is greater than the current time, but if you subtract 2 times it always gives a proper duration (as long as the duration is itself less than 50 days)

So the only correct way to do time checks with millis is:

(later_time - earlier_time) compared-with duration

There is a library I like that makes all of this so easy: "Ticker"

One subtlety with Ticker is the resolution - it can use milliseconds or microseconds, and the time can be specified in milliseconds or microsecoonds.

The default is to use microsecond resolution with millisecond specification:

Ticker x(callback, time_in_millisec); // repeat defaults to 0 (continuous), resolution to MICROS

This only works for durations up to 70 minutes, for longer durations you have to use milliseconds:

Ticker x(callback, time_in_millisec, /*repeat=*/0, /*resolution=*/MILLIS);

When specifying a callback for ticker you can use a function that takes no args and no return value:

void mycallback() {dostuff();}
Ticker x(mycallback, ....);

Or you can use a C++ lambda expression to create an unnamed function that does the same thing.

Ticker x([](){dostuff();}, ....);

Here is your code rewritten with Ticker with some assumptions on my part about what you intend, you should be able to adapt the ideas

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Ticker.h>

#define ONE_WIRE_BUS 4 #define TEMPERATURE_PRECISION 9

#define motor 6 #define valve 7

OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire);

// use a separatethreshold for on/off to give some hysteresis const float on_thld = 36; const float off_thld = 35;

#define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

void check_sensors();

// This Ticker will call check_sensors every 2seconds Ticker ticker_sensors(check_sensors, 2000, 0);

// When we turn on the valves, we'll use this to schedule turning on the motor 5 seconds later (a 1-time event each time we do start()) Ticker ticker_motor_start({ digitalWrite(motor, LOW); }, 5000, 1); // if you dislike this syntax, you could use: // void turn_on_motor() {digitalWrite(motor, LOW);} // Ticker ticker_motor_start(turn_on_motor, 500, 1);

// When we turn off the motor, we'll use this to schedule turning off the valve 5 seconds later (a 1-time event each time we do start()) Ticker ticker_valve_off({ digitalWrite(valve, HIGH); }, 5000, 1);

void check_sensors() { sensors.requestTemperatures(); float temp = sensors.getTempCByIndex(0); Serial.println(temp); Serial.println((char)176); Serial.println("C | "); if (temp > on_thld) { if (ticker_motor_start.state() != RUNNING) { // cancel any pending valve turn-off ticker_valve_off.stop();

        // turn on valve and schedule motor turn-on
        digitalWrite(valve, HIGH);
        ticker_motor_start.start();
    }
} else if (temp &lt; off_thld) {
    if (ticker_motor_start.state() == RUNNING) {
        // turn off motor and schedule valves to turn off
        digitalWrite(motor, HIGH);
        ticker_valve_off.start();
    }
}

display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.println(&quot;Temperature&quot;);
display.setCursor(0, 30);
display.println(&quot;Soil &amp; Water Engg.&quot;);
display.setTextSize(2);
display.setCursor(0,10);
display.println(temp);
display.setCursor(75, 10);
display.println(&quot;C&quot;);
display.setCursor(0,40);
display.display();

}

void setup() { Serial.begin(115200); digitalWrite(motor, HIGH); // set value before enabling output digitalWrite(valve, HIGH); // set value before enabling output pinMode(motor, OUTPUT); pinMode(valve, OUTPUT);

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F(&quot;SSD1306 allocation failed&quot;));
    for(;;);
}

display.setTextColor(WHITE);
ticker_sensors.start();

}

void loop() { // update all the tickers - if they're not running it's a no-op // They only check times and decide when to call their callbacks during update() ticker_sensors.update(); ticker_motor_start.update(); ticker_valve_off.update(); }

Rudy Albachten
  • 131
  • 1
  • 5
0

Not all processes are time-based, so for completeness

loop:
  read the time;
  if time to run process1,
    process1();
  if time to run process2,
    process2();
  poll external event1;
  if event1 completed,
     external1();
  poll external event2;
  if event2 completed,
     external2();
end;
JRobert
  • 15,407
  • 3
  • 24
  • 51