How to detect when an object (watering can) gets put back on its place.

Dear forum members,

At the moment I'm following a course given by Mark Rober about mechanical and electrical engineering. A big part of this course if focused on learning to program with arduino. I do have some experience with electrical engineering but am very new to arduino. But because of this course I got very excited about arduino and everything that is possible and I would like to learn a lot more about it.
Now the goal of this part of the course of Mark is to break or enforce a bad/good habit. The habit I chose is watering the plants which I always forget.
My idea was to make a weekly timer to indicate when it is time to water the plants. And also to detect if this actually gets done by registering the presence of the watering can. So the alarm should only stop after I take the watering can and especially after the watering can gets placed back after the plants have had water. This to give a positive ore negative feedback. Negative feedback to keep the alarm going until the watering can is returned. And a positive when it is back on its place.
For this project I'm using an Arduino Uno r3. For the timer I use a DS1302 RTC module which I found a nice tutorial about already. And to detect the presence of the watering can I'm still doubting to use the HC-SR04 Ultrasonic Sensor or the IR-08H obstacle avoidance sensor. For the feedback I'm gonna use the 8x8 led display.
Now the problem is that I can not find a way to detect the replacement of the watering can! I can detect when it is there and when it is not there but I want to detect the moment that it goes from absent to present.
If someone could point me in the right direction it would be a great help.
Kind regards,
Dirk-Jan

dirk--jan:
I can detect when it is there and when it is not there but I want to detect the moment that it goes from absent to present.
If someone could point me in the right direction it would be a great help.

The usual way to do that is to compare the latest sensor value with the previous one to see if it has changed. Something like this pseudo code

previousSensorValue = sensorValue;
sensorValue = code to read sensor
if (sensorValue shows object absent and previousSensorValue shows object present) {
  // object has just been removed.
}

A micro-switch that gets pressed when the watering can in in place seems to me a simple way to detect its presence.

...R

Mark Rober class: Monthly.

The DS1302 RTC is not very good. The chip, the module, the libraries, the wiring, about everything about it is not good. Boo! Statler & Waldorf - YouTube.
The DS3231 is a good RTC.

A 8x8 led display requires current. Are those 64 leds of 20 mA ? Then the total current can be 1.28 Amp. The Arduino 5V pin can not supply that. The 5V pin is to power sensors.

May I say that you are overthinking this ? No one wants an alarm that stops when the watering can is taken.
You can measure the soil humidity with a capacitive sensor: Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor : ID 4026 : $7.50 : Adafruit Industries, Unique & fun DIY electronics and kits.
The weight of a watering can or a flower pot can be measured with a load cell. There are many other ways.
[Update] Robin2 wrote about a micro-switch. The most simple solution is often the best.

Plants don't need a little water every week. They need water when they need it. It is best to let the water run through the soil so the excess salts will get out: How NOT to Water Your Houseplants: 10 Plant-Saving Tips! - YouTube.
I have flower pots from 1 liter to 30 liters in size. Some plants need the soil to be totally dry before watering, other plants require a constant moist soil.

What just happened :o how did we get from an Arduino subject into talking about plants ::slight_smile:

Robin2:
A micro-switch that gets pressed when the watering can in in place seems to me a simple way to detect its presence.

+1 KISS Principle.

Thanks for the great feedback guys! These are some very good tips.

@Robin2 Maybe I'm not understanding it properly but in this example it registers the watering can while being absent? I'll study more about this method.

@Koepel That is indeed the course I'm following :slight_smile: Can really advice it to people who don't have a lot of experience with microcontrollers, it's very fun.
I'll order the DS3231, the DS1302 was already in the starting kit of arduino.
I'm not sure about the amount of amps the leds draw however there are some projects on the arduino.cc website that do connect the 8x8 led matrix directly to the uno board. But since my 8x8 matrix has 16 lose pins it needs 16 pins as well to connect to the arduino board so this might occupy to much space on the board to leave place for other sensors and such. To think in line of the KISS principle i'll might drop this idea and find another way to give feedback.
For the capacitive sensor I don't have great experience with those. In the past I tried this but after a few weeks they were all corroded.
A weekly reminder I think is practical since otherwise I forget and the plants never get any water:p Tried to put it in an agenda as well but keep dismissing the alarm somehow:s

dirk–jan:
@Robin2 Maybe I’m not understanding it properly but in this example it registers the watering can while being absent?

I’m not sure what you mean by that.

if you mean that the program is continually checking even when the watering can is absent, then that is correct, and normal behaviour.

In Reply #1 I did not include code for detecting when the watering can is returned to its resting place, it would be very similar.

…R

Dear forum members,

I continued experimenting with the RTC and the ultrasonic distance module. I continued with the DS1302 since the DS3231 did not arrive yet and I was to excited to continue to wait for it to arrive.

With the ultrasonic distance module the arduino onboard led will now turn on when something is close by (<10cm) and off when it us further away (>10cm).
I was trying to use the RTC to use the distance measurement only for a few seconds a minute (just to figure out how it works). For example every minute after 30 seconds till 40 seconds.
The following code I used to accomplish this. However now when the serial monitor reaches the 30 seconds of the minute the led will turn on but the arduino stops counting the time and the led will not turn of anymore.
If possible I would love to have your experts opinion of what I’m doing wrong here and how to improve the code.

#include <virtuabotixRTC.h>


virtuabotixRTC myRTC(6, 7, 8);


const int trigPin = 4;
const int echoPin = 3;

float duration,distance;


void setup() {
 pinMode(LED_BUILTIN, OUTPUT); 
 pinMode(trigPin, OUTPUT);
 pinMode(echoPin, INPUT);
 Serial.begin(9600);
 myRTC.setDS1302Time(15,22,21,7,14,1,2018);
}

void loop() {
 digitalWrite(trigPin, LOW);
 delayMicroseconds(2);
 digitalWrite(trigPin, HIGH);
 delayMicroseconds(10);
 digitalWrite(trigPin, LOW);

myRTC.updateTime();

duration = pulseIn(echoPin, HIGH);
distance = (duration*.0343)/2;

 Serial.print("Current Date / Time: ");
 Serial.print(myRTC.dayofmonth); //You can switch between day and month if you're using American system
 Serial.print("/");
 Serial.print(myRTC.month);
 Serial.print("/");
 Serial.print(myRTC.year);
 Serial.print(" ");
 Serial.print(myRTC.hours);
 Serial.print(":");
 Serial.print(myRTC.minutes);
 Serial.print(":");
 Serial.println(myRTC.seconds);
Serial.print("Distance: ");
Serial.println(distance);
delay(1000);


  if(distance < 10){ 
  while(myRTC.seconds >=30){
    digitalWrite(LED_BUILTIN, HIGH); }
  }
  else (distance >10);{
    while(myRTC.seconds >=40)
    digitalWrite(LED_BUILTIN, LOW);
  }

}

DS3231

This isn’t how you use else:

  else (distance > 10);

Try replacing it with:

  else

Hi Wildbill,
Thanks a lot for your reply!
I used your tip and modified the code. However now when the distance is <10cm the led will not turn on but after 30 seconds the serial monitor will keep counting.
When the distance is bigger than >10cm after 30 seconds the arduino freezes again.

  if(distance >10){ 
    while(myRTC.seconds >=30){
      digitalWrite(LED_BUILTIN, HIGH); }}
  else
  digitalWrite(LED_BUILTIN, LOW);

Do you have any idea why this happening?

This is a problem:

    while (myRTC.seconds >= 30)
    {
      digitalWrite(LED_BUILTIN, HIGH);
    }

It looks as though the time library needs a call to updateTime to set the time variables and you have that in your code. However, in that while loop, seconds will never change, hence the lockup you see.

You could add a call to updateTime in that loop and that would fix it. I don’t really see the point of the loop at all though.

Thanks for your help.

I discovered that when I change ‘while’ for ‘if’ it does work and nothing freezes (:

With this code, after 30 seconds, when the distance is <10 the led will turn on. And at 40 seconds off again.

if (distance <10){
  if(myRTC.seconds >=30){
    digitalWrite(LED_BUILTIN, HIGH);}}
else
digitalWrite(LED_BUILTIN, LOW);


if (myRTC.seconds >40){
  digitalWrite(LED_BUILTIN, LOW);}

Please always show the full sketch, even if you have only changed a comma.

Now that I'm talking about comma's, can you please make the text layout look better ?
So straighten your mustache and also make the text look better :grin:
Start by pressing Ctrl+T in the Arduino IDE. Then put every indent, every comma, every space, every new line, at the right place.
Find a style that seems best for you.
You do that for yourself, not for me. Finding a bug will be easier when a sketch shows clearly its structure.

I like the text open and spacious, but that is just my personal preference.

There is a library for the ultrasonic sensors that might be of interest: teckel12 / Arduino New Ping / wiki / Home — Bitbucket.

How about adding a buzzer or beeper to your project ?

Thanks Koepel!

I’m new with arduino and programming overall. Tips in how to improve my coding are always welcome!

At the moment I’m tying to figure out how to implement the tip from Robin2. To detect that the watering bin is placed back he says to compare the previous value with the current value.

So in the code I’m trying to turn a led on when the current distance is smaller than the previous distance and turn the led off when the distance is bigger than the previous distance.

Now is the problem: the led doesn’t do anything.

Hopefully someone can tell me what is going wrong here?

[code]
#include <virtuabotixRTC.h>


virtuabotixRTC myRTC(6, 7, 8);


const int trigPin = 4;
const int echoPin = 3;


float duration, distance;


void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Serial.begin(9600);
  myRTC.setDS1302Time(25, 22, 21, 7, 14, 1, 2018);
}


void loop() 
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);



  myRTC.updateTime();

  duration = pulseIn(echoPin, HIGH);
  distance = (duration * .0343) / 2;


  Serial.print("Current Date / Time: ");
  Serial.print(myRTC.dayofmonth); //You can switch between day and month if you're using American system
  Serial.print("/");
  Serial.print(myRTC.month);
  Serial.print("/");
  Serial.print(myRTC.year);
  Serial.print(" ");
  Serial.print(myRTC.hours);
  Serial.print(":");
  Serial.print(myRTC.minutes);
  Serial.print(":");
  Serial.println(myRTC.seconds);
  Serial.print("Distance: ");
  Serial.println(distance);
  delay(1000);


  int previousdistance = distance;


  if (distance < previousdistance) 
  {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(2000);
  }
  
  if (previousdistance > distance) 
  {
    digitalWrite(LED_BUILTIN, LOW);
    delay(2000);
  }

}

[/code]

You set previous distance to distance and then check to see if they're different. See the problem?

Make previous global and set it after your if statements.

dirk--jan:
So in the code I'm trying to turn a led on when the current distance is smaller than the previous distance and turn the led off when the distance is bigger than the previous distance.

It should be like this

 myRTC.updateTime();

  float previousdistance = distance; // save the value before you get the new value
                           // also it must be a float to match the definition of distance
  duration = pulseIn(echoPin, HIGH);
  distance = (duration * .0343) / 2;


  Serial.print("Current Date / Time: ");

...R

On a different topic some Delta_G wrote something similar which helped explain what happens in the code.

(Storing sensor value as previous and comparing it with current value Feb 24, 2017 comment #11)

I changed the code which you can see below.

Now it does recognize when the currentDistance is lower < than the previousDistance.
However the LED will turn on for just a second.

Going back to the project of Mark. When the watering can gets placed back on its original place, currentDistance < previousDistance, I want the matrix display to show an image for a period of time.

My question is: how can I keep the led turned on for a specific time after the distance changed?

When I add a delay after digitalWrite(LED_BUILTIN, HIGH) this will interfere with the timing of the RTC moduel.

[code]
#include <virtuabotixRTC.h>


virtuabotixRTC myRTC(6, 7, 8);


const int trigPin = 4;
const int echoPin = 3;


float duration, distance;

float previousDistance = 0;

float currentDistance = 0;


void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Serial.begin(9600);
  myRTC.setDS1302Time(25, 22, 21, 7, 14, 1, 2018);
}


void loop()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);



  myRTC.updateTime();

  duration = pulseIn(echoPin, HIGH);
  distance = (duration * .0343) / 2;


  Serial.print("Current Date / Time: ");
  Serial.print(myRTC.dayofmonth); //You can switch between day and month if you're using American system
  Serial.print("/");
  Serial.print(myRTC.month);
  Serial.print("/");
  Serial.print(myRTC.year);
  Serial.print(" ");
  Serial.print(myRTC.hours);
  Serial.print(":");
  Serial.print(myRTC.minutes);
  Serial.print(":");
  Serial.println(myRTC.seconds);
  Serial.print("Distance: ");
  Serial.println(distance);
  delay(1000);


  currentDistance = distance;


  if (currentDistance <= previousDistance / 2)
  {
    digitalWrite(LED_BUILTIN, HIGH);
  }
  else
    digitalWrite(LED_BUILTIN, LOW);


  previousDistance = currentDistance;


}

[/code]

During a delay, nothing else can be done. That is often no problem for a simple sketch. Your RTC module will keep the right time, regardless of how long the delay is.

Note: During a delay, the interrupts still run. That means that serial data that is being received is still received by the Serial library and put in a buffer.

If you are ready to shake off your chicken down, then you can solve every problem with millis(). Well, maybe not every problem, but at least your coding problems.

The millis() returns the number of milliseconds. Checking millis() is like checking the clock if something needs to be done.
Do you know a hardware "single shot timer" ? That can be made with millis() in software. See my example: millis_single_delay.ino.

Start by reading the Blink Without Delay page: https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay.

We use millis() to blink leds, to check if there is a timeout, to keep relays on for a certain time, to slowly move a servo motor, and all of that at the same time and also check buttons all the time.

Hi Guys,

The course of Mark Rober is finished and so is the final version of the code of the watering can.
I wanted to thank everyone here on the forum for your help! With your feedback you guys send me in the right direction and I was able to finish the project. I really enjoy working with arduino and will definitely do more projects with it.

It was still difficult to implement the blink without delay but I was able to manage with the if, if else and else function. (:
Just to show I added the final code here on the bottom. Here is also a video of the code in practice: https://youtu.be/dH_BsF70ZzE
Now the led matrix will alternating the letters w a t e r. When the watering can (box) gets removed the letters stop, when the box returns a smiley will show op.

[code]
#include <LedControl.h>
#include <virtuabotixRTC.h>

virtuabotixRTC myRTC(6, 7, 8);


const int trigPin = 4;
const int echoPin = 3;

float duration, distance;

float previousDistance = 0;
float currentDistance = 0;

int DIN = 12;
int CS = 11;
int CLK = 10;

byte w[8] =     {0xC3, 0xDB, 0xDB, 0xDB, 0xDB, 0xFF, 0x7E, 0x38,};
byte a[8] =     {0x3C, 0x7E, 0x66, 0x66, 0x7E, 0x7E, 0x66, 0x66,};
byte t[8] =     {0x7E, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,};
byte e[8] =     {0x3E, 0x7E, 0x60, 0x7C, 0x7C, 0x60, 0x7E, 0x3E,};
byte r[8] =     {0x7C, 0x7E, 0x66, 0x66, 0x7C, 0x7C, 0x66, 0x66,};

byte smile[8] =   {0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C};
byte neutral[8] = {0x3C, 0x42, 0xA5, 0x81, 0xBD, 0x81, 0x42, 0x3C};
byte frown[8] =   {0x3C, 0x42, 0xA5, 0x81, 0x99, 0xA5, 0x42, 0x3C};


LedControl lc = LedControl (DIN, CLK, CS, 0);


void setup()
{
  lc.shutdown(0, false);
  lc.setIntensity(0, 1);
  lc.clearDisplay(0);

  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Serial.begin(9600);
  myRTC.setDS1302Time(25, 22, 21, 7, 14, 1, 2018);
}

void loop()
{
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  duration = pulseIn(echoPin, HIGH);
  distance = (duration * .0343) / 2;

  myRTC.updateTime();

  Serial.print("Current Date / Time: ");
  Serial.print(myRTC.dayofmonth); //You can switch between day and month if you're using American system
  Serial.print("/");
  Serial.print(myRTC.month);
  Serial.print("/");
  Serial.print(myRTC.year);
  Serial.print(" ");
  Serial.print(myRTC.hours);
  Serial.print(":");
  Serial.print(myRTC.minutes);
  Serial.print(":");
  Serial.println(myRTC.seconds);
  Serial.print("Distance: ");
  Serial.println(distance);
  delay(1000);


  currentDistance = distance;

int j = 0;

  if (myRTC.dayofweek == 7 && myRTC.hours >= 9 && myRTC.hours < 22 )
  {
    if (currentDistance <= previousDistance / 2)
    {
      while (j <3)
      {
        printByte(smile);
        delay(5000);
        lc.clearDisplay(0);
        delay(1000);
        j++;        
      }
      delay(10000);
    }
    else if (distance < 10)
    {
      printEduc8s();
    }
    else
    {
      lc.clearDisplay(0);
    }
  }

  previousDistance = currentDistance;
}

void printEduc8s()
{
  digitalRead(distance);
  printByte(w);
  delay(1000);
  printByte(a);
  delay(1000);
  printByte(t);
  delay(1000);
  printByte(e);
  delay(1000);
  printByte(r);
  delay(0);
}

void printByte(byte character [])
{
  int i = 0;
  for (i = 0; i < 8; i++)
  {
    lc.setRow(0, i, character[i]);
  }
}

[/code]

Thanks a lot!!
Cheers,
Dirk-Jan

Thanks for sharing. Very nice :smiley:

For using millis(), start with the Blink Without Delay with the blinking led. When you understand the Blink Without Delay, then perhaps you can use it in your own sketch some day.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.