First Time project


#21

Firmware.ino (6.7 KB)

I don’t have zipping capabilities on this computer so hopefully the ino file is small enough.


#22

I’m so sorry Leo/Nathan; I see where I’ve confused you. When I said you need to define the variables, you assumed they would be defined with #define. But the code block I wrote was the definition of the variables:

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

I’ve corrected the definitions and the loop (you still had all the original code in there as well. This compiles now for me:

Sketch uses 8,338 bytes (25%) of program storage space. Maximum is 32,256 bytes.
Global variables use 574 bytes (28%) of dynamic memory, leaving 1,474 bytes for local variables. Maximum is 2,048 bytes.

Please let us know how you get on. :slight_smile:

Attached: Firmware.ino (3.4 KB)

And included in full for people to copy-paste if they can’t download:

// 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;

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

// Setup the essentials for your circuit to work. It runs first every time your circuit is powered with electricity.
void setup()
{
	dht.begin();
	// initialize the lcd
	lcdI2C.begin(LCD_COLUMNS, LCD_ROWS, LCD_ADDRESS, BACKLIGHT);
}

// 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()
{
	/* *********************************************** */
	/* 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);
}

#23

It works! I left the circuit to run and it works thus far over the last hour. The only thing I am trying to figure out is changing the LCD output to make it a bit easier for the user. For instance when printing sensor values, I am trying to get “Temp 72F Humid 98%” when reading the sensor but when I try changing things I get errors again.


#24

Excellent news! :smiley:

Let’s dive into this message formatting a little…

sprintf(buffer, "%02d\% %02dC %dV", int(humidity), int(tempC), soilMoisture5vVal);

We have a buffer of 16 characters (since the LCD is only 16 characters wide). And we use sprintf to format a string into that array of characters.

The format string ("%02d\% %02dC %dV") tells sprintf how to format the values that come after the format string. We’re passing it three values; int(humidity), int(tempC), and soilMoisture5vVal. The format string uses a percent sign to indicate that the value should be put there. I’ll explain each of the three formats we’re defining:

  1. %02d\% - The first % means insert the value of the item here. The 02d tells it that it’s an integer, and to format it with a leading zero (02 decimal places) if necessary. The \ tells it to not treat the next character (%) normally. We say that it “escapes” the character. That is to say, it will print the % character, not use it as an indicator that a value should appear here.
  2. %02dC - again, two digit integer value, and then the letter “C”.
  3. %dV - format an integer value and then print “V” after it.

So to format it as you’ve mentioned; the format string should be as follows:

sprintf(buffer, "Temp %02dC Humid %02d\% ", int(tempC), int(humidity));

But that doesn’t fit in 16 characters. So I’d suggest a different format:

sprintf(buffer, "%02d°C Humid %02d\% ", int(tempC), int(humidity));

(There’s a bigger change to get Fahrenheit so I’m sticking to just the format change for now.)


#25

Thinking a little more about this, and if you don’t include the voltage of the soil moisture, you won’t have a reading to see when you think the soil is dry enough to need water. So for now (during the evaluation period) I’d leave the display less user friendly, but more “debug friendly”. :slight_smile:

Another thought is that the loop will currently update every second. Yes, every single second it’s going to read all the values and decide whether to water the plant. Do you see an issue with this? What if it takes a few seconds for the soil to get moist after watering?

You could build something in to avoid watering more frequently than every “n” minutes, but you might as well also slow this whole thing down since you don’t need to see the second-by-second updates of the temperature and humidity. Sampling less often uses less power (although I doubt you’re running on batteries), but we should always try make our systems efficient to reduce costs and wastage.

At the very bottom of the program are these lines:

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

I’d suggest changing them to these:

// Wait before updating again
delay(300000); // 300'000 is 5 minutes.

#26

It has been working great all day. Thank you for all your assistance and help! I am so excited about being able to actually finish my first project. I just going to test it over the next few days before I fabricate a case of some type for it.


#27

On the topic of power consumption and making the battery last if you’re not using an adapter from mains supply, there’s a new blog just out that covers this. It’s well worth a read: