First Time project


#1

I am working a cool project because my wife likes to kill plants. I have basically a DHT sensor, moisture sensor, LCD, and solenoid valve. What I am trying to figure out is how to build the code that takes readings from the moisture sensor and waters if the moisture level gets below a value and the waters for a few seconds and cuts off. the LCD will display the temp/humidity values from the DHT sensor and will list that last completed water cycle. Of course, this will loop indefinitely.


#2

image

Here is the image of the circuit since I figured out what I needed from building it on circuito. Thanks.


#3

dear leo,

you need a help in your code?!
or you need a full code?!


#4

I need the basic code but I do want to understand exactly what is going on. For a hobbyist, I really jumped into the deep end on this.


#5

I got the serial coding as circuito provides that but I thought it will provide the basic code as well but I misintrepted that. Here is what they gave me:

// Include Libraries
#include “Arduino.h”
#include “DHT.h”
#include “LiquidCrystal_PCF8574.h”
#include “SoilMoisture.h”
#include “SolenoidValve.h”

// Pin Definitions
#define DHT_PIN_DATA 2
#define SOILMOISTURE5V_PIN_SIG A3
#define SOLENOIDVALVE_PIN_COIL1 3

// There are several different versions of the LCD I2C adapter, each might have a different address.
//#define LCD_ADDRESS 0x3F
#define LCD_ADDRESS 0x27

// Define LCD characteristics
#define LCD_ROWS 2
#define LCD_COLUMNS 16
#define SCROLL_DELAY 150
#define BACKLIGHT 255
// object initialization
DHT dht(DHT_PIN_DATA);
LiquidCrystal_PCF8574 lcdI2C;
SoilMoisture soilMoisture5v(SOILMOISTURE5V_PIN_SIG);
SolenoidValve solenoidValve(SOLENOIDVALVE_PIN_COIL1);

// define vars for testing menu
const int timeout = 10000; //define timeout of 10 sec
char menuOption = 0;
long time0;

// Setup the essentials for your circuit to work. It runs first every time your circuit is powered with electricity.
void setup()
{
// Setup Serial which is useful for debugging
// Use the Serial Monitor to view printed messages
Serial.begin(9600);
while (!Serial) ; // wait for serial port to connect. Needed for native USB
Serial.println(“start”);

dht.begin();
// initialize the lcd
lcdI2C.begin(LCD_COLUMNS, LCD_ROWS, LCD_ADDRESS, BACKLIGHT); 
menuOption = menu();

}

// Main logic of your circuit. It defines the interaction between the components you selected. After setup, it runs over and over again, in an eternal loop.
void loop()
{

if(menuOption == '1') {
// DHT22/11 Humidity and Temperature Sensor - Test Code
// Reading humidity in %
float dhtHumidity = dht.readHumidity();
// Read temperature in Fahrenheit, for Celsius use .readTempC()
float dhtTempC = dht.readTempF();
Serial.print(F("Humidity: ")); Serial.print(dhtHumidity); Serial.print(F(" [%]\t"));
Serial.print(F("Temp: ")); Serial.print(dhtTempC); Serial.println(F(" [F]"));

}
else if(menuOption == '2') {
// LCD Display Screen 16x2 I2C - Test Code
// The LCD Screen will display the text of your choice.
lcdI2C.clear();                          // Clear LCD screen.
lcdI2C.print("  Circuito.io  ");                   // Print print String to LCD on first line
lcdI2C.selectLine(2);                    // Set cursor at the begining of line 2
lcdI2C.print("     Rocks!  ");                   // Print print String to LCD on second line
delay(1000);

}
else if(menuOption == '3') {
// Soil Moisture Sensor - Test Code
int soilMoisture5vVal = soilMoisture5v.read();
Serial.print(F("Val: ")); Serial.println(soilMoisture5vVal);

}
else if(menuOption == '4') {
// 12V Solenoid Valve - 3/4'' - Test Code
// The solenoid valve will turn on and off for 500ms (0.5 sec)
solenoidValve.on(); // 1. turns on
delay(500);       // 2. waits 500 milliseconds (0.5 sec). Change the value in the brackets (500) for a longer or shorter delay in milliseconds.
solenoidValve.off();// 3. turns off
delay(500);       // 4. waits 500 milliseconds (0.5 sec). Change the value in the brackets (500) for a longer or shorter delay in milliseconds.

}



if (millis() - time0 > timeout)
{
    menuOption = menu();
}

}

// Menu function for selecting the components to be tested
// Follow serial monitor for instrcutions
char menu()
{

Serial.println(F("\nWhich component would you like to test?"));
Serial.println(F("(1) DHT22/11 Humidity and Temperature Sensor"));
Serial.println(F("(2) LCD Display Screen 16x2 I2C"));
Serial.println(F("(3) Soil Moisture Sensor"));
Serial.println(F("(4) 12V Solenoid Valve - 3/4''"));
Serial.println(F("(menu) send anything else or press on board reset button\n"));
while (!Serial.available());

// Read data from serial monitor if received
while (Serial.available()) 
{
    char c = Serial.read();
    if (isAlphaNumeric(c)) 
    {
        if(c == '1') 
			Serial.println(F("Now Testing DHT22/11 Humidity and Temperature Sensor"));
		else if(c == '2') 
			Serial.println(F("Now Testing LCD Display Screen 16x2 I2C"));
		else if(c == '3') 
			Serial.println(F("Now Testing Soil Moisture Sensor"));
		else if(c == '4') 
			Serial.println(F("Now Testing 12V Solenoid Valve - 3/4''"));
        else
        {
            Serial.println(F("illegal input!"));
            return 0;
        }
        time0 = millis();
        return c;
    }
}

}


#6

Hi Leo

How much of the provided code do you understand? Would it help for someone to start by explaining what Circuito provided?

Sounds like a fun project by the way. Even if you’re good at taking care of plants, you might be away for a while and it’d be good to know that your favourite plant is being watered sufficiently.


#7

The screenshot of the circuit isn’t necessary; you can share a link of the URL/address of the circuit and we’ll all be able to see the circuit as well as the code it generates. It’s really very clever; it builds the circuit dynamically based on the selected components.

Could you do that please so that we can all see exactly which components you chose and what code Circuito provides for this?


#8

Sorry for the late replay. Here is the link:

https://www.circuito.io/static/reply/index.html?solutionId=5bfeb061564869002fa91128&solutionPath=storage.circuito.io


#9

Thanks Leo.

Something appears to be wrong for me; when I try edit that project (https://www.circuito.io/app?components=512,9442,10167,10456,11021,13322,417986) it reverts to a default circuit with just an Arduino and USB cable. :frowning: But I can see the code so we can help you with that now.

How much of the code do you understand?


#10

Yes, we have a bug with the “edit” feature from the project guide, that’s the reason it’s not working. But you can still see the circuit in the project guide itself


#11

I understand the testing code that is built. I guess my real problem is really where to start with the coding. Everything is assigned and defined, thanks to the test code from the circuit building. Just need help getting started and then hopefully I can get it from there with little help.


#12

Hi Leo

Sorry for the delay in helping you, but my current job is pretty full on at the moment. Long days working intensely on code throughout the day. It’s just a bit tiring to be honest.

Hopefully I’ll get a chance to help soon. In the mean time, could you describe the behaviour you want from the code given the components you’ve chosen?

Cheers
Bernhard


#13

Basically I want to take the readings from the moisture sensor and water for a set number of seconds via the coil.Then I want the LCD screen to display the current temp & humidity sensor via the DHT sensor, along with when the last watering occurred. I also need the LCD to display when the system is currently watering. It sounds simplistic in my head but with my limited experience with coding its been draining on my brain.


#14

Hi Leo

My apologies again for the delay in responding.

I’ll try explain my thought process as I go through from an idea like yours to the code. I hope that’s the most useful thing to you and others trying to get into this.

I start by taking the plain English and converting it into individual requirements as follows:

  1. Take the readings from the moisture sensor
  2. Water for a set number of seconds via the coil
  3. LCD screen to display:
    • the current temp & humidity sensor via the DHT sensor
    • when the last watering occurred
    • when the system is currently watering

Arduino code runs in a loop. The loop is typically “infinite” in the sense that we don’t “exit” the loop; we run for as long as there is power. So with that in mind, let’s convert some of the above into logic inside that loop. The loop in the provided code does something different; listening for input from you and testing components. You might want to leave that in for testing purposes, or remove it. I’m going to remove it for brevity and clarity in seeing what we’re doing here, but there is valuable code in there to read the values from the components/devices and to show text on the LCD so we’ll use that again.

Rather than go straight for a completed working program, I often start with a stripped down version that does some of what I want so that I can easily test it and make incremental improvements until it’s all working. As an example, nobody builds a car from scratch these days; the gearbox arrives complete, with the meshing of the gears already tested and verified.

void loop()
{
	/* *********************************************** */
	/* Read all the values from the components we have */
	/* *********************************************** */
	// DHT22/11 Humidity and Temperature Sensor - Read humidity in %
	float humidity = dht.readHumidity();
	// Read temperature in Celsius, for Fahrenheit use .readTempF()
	float tempC = dht.readTempC();
	// Soil Moisture Sensor - Test Code
	int soilMoisture5vVal = soilMoisture5v.read();
	
	/* *********************************************** */
	/* Show the values on the LCD                      */
	/* *********************************************** */
	// Note: sprintf is a function to format a string
	// We have floats for the humidity and temp. Arduino's sprintf function doesn't include support for floats.
	// We'll simplify things by converting to integers. We're aiming for a string like "12% 16C nV" to show humidity, temp, and soil "Voltage".
	// We'll format an array of characters (a string) with the values using sprintf, then print that with the LCD library
	// Line 1 can be sensor values, and hopefully we can get the rest of the feedback into line 2 (later)

	char buffer[16];
	sprintf(buffer, "%02d\% %02dC %dV", int(humidity), int(tempC), soilMoisture5vVal);
	lcdI2C.clear();
	lcdI2C.print(buffer);
	lcdI2C.selectLine(2);
	lcdI2C.print("");

	// Wait for a second before updating again
	delay(1000);
}

The above loop is simple enough. It’ll read all the values and print them on the LCD once a second. It doesn’t sound like much but it verifies that all the components are correctly connected, working, and gives you an idea of the values you can expect. You’ll need to know these values to set limits for the code to know when to add water.

To add water, we’ll use the solenoid code that was provided, but we’ve removed for now:

Let’s see the above working before we move onto the more advanced stuff like timing and watering when necessary if you’re okay with that.


#15

Assuming the code works, we need to expand on and fulfill the remaining requirements:

  1. DONE: Take the readings from the moisture sensor
  2. Water for a set number of seconds via the coil
  3. LCD screen to display:
    • DONE: the current temp & humidity sensor via the DHT sensor
    • when the last watering occurred
    • when the system is currently watering

I assume the system should only water when required. For this, you 'll need to know the soilMoisture5vVal threshold at which you would water the plant. With the above program you’ll have noticed the value before the V, and what that is when you would give it some water. I’ll assume that value is 128. In the code you downloaded, before the setup function you’ll see code such as the following:

const int timeout = 10000;       //define timeout of 10 sec
char menuOption = 0;
long time0;

You’ll need to add a few of your own constants and variables here. Let’s define the voltage that you deem too dry, i.e. the plant needs water. We’ll also add variables for when it was that it last watered, and whether it’s watering now. You’ll need to determine how long the solenoid needs to stay open to water the plant sufficiently. This is defined in waterDurationSeconds where I’ve assumed 3 seconds is enough.

const int tooDry = 128;
const int waterDurationSeconds = 3;
bool isWatering = false;
long lastWateredTime = 0;

Let’s talk about how we’ll use these.

  1. We’ll compare the value in soilMoisture5vVal with the tooDry constant. If the soilMoisture5vVal is below that level, then the soil is too dry and we’ll set isWatering to true.
  2. If isWatering is true, we’ll (a) water the plant, and (b) set the lastWateredTime to the current time.
  3. We’ll show the time since watering and isWatering status on the LCD.

It’s worth mentioning here that the Arduino does not have a clock in the human sense of a clock. It has a clock that measures time since it was powered up, but that has no idea what your current local time is. So our times will be “elapsed time” since watering, not your local time of day/night.

The documentation says that the millis function:

so it’s important not to use the elapsed time to drive logic since that logic will fail after 50 days.

For clarity, here’s the code that we’ll add:

	isWatering = false;
	if (soilMoisture5vVal < tooDry) {
		solenoidValve.on();
		delay(waterDurationSeconds * 1000);
		solenoidValve.off();

		isWatering = true;
		// millis is the time since starting up. See https://www.arduino.cc/reference/en/language/functions/time/millis/
		lastWateredTime = millis();
	}

	sprintf(buffer, "%s %d Mins", (isWatering ? "W" : " "), (millis() - lastWateredTime)/1000/60);

Here’s the complete loop function with the first version and this new code added:

void loop()
{
	/* *********************************************** */
	/* Read all the values from the components we have */
	/* *********************************************** */
	// DHT22/11 Humidity and Temperature Sensor - Read humidity in %
	float humidity = dht.readHumidity();
	// Read temperature in Celsius, for Fahrenheit use .readTempF()
	float tempC = dht.readTempC();

	// Soil Moisture Sensor - Test Code
	int soilMoisture5vVal = soilMoisture5v.read();

	/* *********************************************** */
	/* Water the plant if necessary                    */
	/* *********************************************** */
	isWatering = false;
	if (soilMoisture5vVal < tooDry) {
		solenoidValve.on();
		delay(waterDurationSeconds * 1000);
		solenoidValve.off();

		isWatering = true;
		// millis is the time since starting up. See https://www.arduino.cc/reference/en/language/functions/time/millis/
		lastWateredTime = millis();
	}

	/* *********************************************** */
	/* Show the values on the LCD                      */
	/* *********************************************** */
	// Note: sprintf is a function to format a string
	// We have floats for the humidity and temp. Arduino's sprintf function doesn't include support for floats.
	// We'll simplify things by converting to integers. We're aiming for a string like "12% 16C nV" to show humidity, temp, and soil "Voltage".
	// We'll format an array of characters (a string) with the values using sprintf, then print that with the LCD library
	// Line 1 can be sensor values, and hopefully we can get the rest of the feedback into line 2

	char buffer[16];
	lcdI2C.clear();
	sprintf(buffer, "%02d\% %02dC %dV", int(humidity), int(tempC), soilMoisture5vVal);
	lcdI2C.print(buffer);
	lcdI2C.selectLine(2);
	sprintf(buffer, "%s %d Mins", (isWatering ? "Watering" : "        "), (millis() - lastWateredTime)/1000/60);
	lcdI2C.print(buffer);

	// Wait for a second before updating again
	delay(1000);
}

I hope this works, I’ve not tested any of it. I’ve just typed it up in Notepad++. :blush:


#16

@anat I built up the circuit again in circuito and see that the components are

https://www.circuito.io/app?components=9442,10167,10456,11021,13322,417986

By a process of deduction, I found that the IDs correspond to these components:

9442: Wall socket power supply
10167: Humidty and Temp sensor
10456: Solenoid
13322: Soil moisture sensor
417986: LCD 16x2 I2C

So I guess the 9442 conflicts with 512 since 512,11021 are the basic circuit with, I assume, 11201 being the Uno since a nano is 11022. I assume 512 is a different power source than the 9442 wall socket and the circuit doesn’t support more than one power source. Is that correct?


#17

I apparently didn’t do something write when I typed it up. I got a lot of errors during the compiling as listed below:

Firmware:118:3: error: ‘isWatering’ was not declared in this scope

isWatering = false;

^

Firmware:119:27: error: ‘tooDry’ was not declared in this scope

if (soilMoisture5vVal < tooDry) {

                       ^

Firmware:121:11: error: ‘waterDurationSeconds’ was not declared in this scope

 delay(waterDurationSeconds * 1000);

       ^

Firmware:126:5: error: ‘lastWateredTime’ was not declared in this scope

 lastWateredTime = millis();

 ^

Firmware:143:85: error: ‘lastWateredTime’ was not declared in this scope

sprintf(buffer, “%s %d Mins”, (isWatering ? “Watering” : " "), (millis() - lastWateredTime)/1000/60);

                                                                                 ^

Using library Wire at version 1.0 in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire
exit status 1
‘isWatering’ was not declared in this scope


#18

Yeah, looks like you just missed defining those variables. Search this page for them and you’ll see they need to be added where the other global variables are defined.


#19

Ok, so I defined the variables, if I got that right but I am still getting a bunch of errors that I don’t quite understand. Here are the variables I defined:

#define DHT_PIN_DATA 2
#define SOILMOISTURE5V_PIN_SIG A3
#define SOLENOIDVALVE_PIN_COIL1 3
#define isWatering
#define tooDry
#define waterDurationSeconds
#define lastWateredTime

Here is the errors I am still going:

:\Users\Nate\Documents\College\ET450 Capstone\Firmware\Firmware.ino: In function ‘void loop()’:

Firmware:121:14: error: expected primary-expression before ‘=’ token

isWatering = false;

          ^

Firmware:122:33: error: expected primary-expression before ‘)’ token

if (soilMoisture5vVal < tooDry) {

                             ^

Firmware:124:34: error: invalid type argument of unary ‘*’ (have ‘int’)

 delay(waterDurationSeconds * 1000);

                              ^

Firmware:127:16: error: expected primary-expression before ‘=’ token

 isWatering = true;

            ^

Firmware:129:21: error: expected primary-expression before ‘=’ token

 lastWateredTime = millis();

                 ^

Firmware:146:45: error: expected primary-expression before ‘?’ token

sprintf(buffer, “%s %d Mins”, (isWatering ? “Watering” : " "), (millis() - lastWateredTime)/1000/60);

                                         ^

Firmware:146:100: error: expected primary-expression before ‘)’ token

sprintf(buffer, “%s %d Mins”, (isWatering ? “Watering” : " "), (millis() - lastWateredTime)/1000/60);

                                                                                                ^

Using library Wire at version 1.0 in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire
exit status 1
expected primary-expression before ‘=’ token

Thanks for all the help.


#20

You must be in the States; I seem to get your replies just as I’m going to bed.

Could you attach a zip of your entire .ino file please? The variables should not be defined with #define. It’ll be clearer if I just fix the file.