#include <Wire.h> // includes the Wire library, used for I2C communication
#include <LiquidCrystal_I2C.h> // includes the LiquidCrystal_I2C library, used for interfacing with an I2C-enabled LCD screen
#include <DHT.h> // includes the DHT library, used for interfacing with DHT temperature and humidity sensors
#include <stdio.h> // includes the standard input/output library, used for basic input/output operations
#define DHTPIN 8 // defines the DHTPIN as pin 8
#define DHTTYPE DHT11 // defines the DHTTYPE as DHT22
DHT dht(DHTPIN, DHTTYPE); // creates a DHT object called dht and initializes it with the DHTPIN and DHTTYPE values
LiquidCrystal_I2C lcd(0x27, 16, 2); // creates a LiquidCrystal_I2C object called lcd and initializes it with the I2C address (0x27), number of columns (16), and number of rows (2) of the LCD screen
const byte ldrPin = A0; // defines ldrPin as analog pin A0
int period = 3000;
unsigned long millisreset = 0;
int delayTime = 25; // declares and initializes the variable delayTime as an integer with a value of 25
const int soilPin = A1; // defines soilPin as analog pin A1
const int soilPower = 13; // defines soilPower as digital pin 13
unsigned int AnalogValue; // defines Analog value used for ldr light sensor
void setup() {
lcd.init(); // initializes the lcd screen
lcd.backlight(); // turns on the backlight for the lcd screen
dht.begin(); // initializes the DHT sensor
Serial.begin(9600); // initializes serial communication with a baud rate of 9600
pinMode(ldrPin, INPUT_PULLUP); // sets the ldr pin as an input with a pullup resistor enabled
}
int readSoil() {
digitalWrite(soilPower, HIGH); // turns on the soil moisture sensor by setting the soilPower pin to HIGH
delay(10); // waits for 10 milliseconds
int val = analogRead(soilPin); // reads the analog signal value from the soil moisture sensor
digitalWrite(soilPower, LOW); // turns off the soil moisture sensor by setting the soilPower pin to LOW
return val; // returns the current moisture value
}
/*
for (initialization; condition; increment) {
// code to be executed
}
The initialization statement is executed only once at the beginning of the loop,
and is used to set the initial value of a counter variable.
The condition statement is evaluated at the beginning of each iteration,
and if it is true, the code inside the loop is executed. If it is false, the loop is exited.
The increment statement is executed at the end of each iteration, and is used to update the counter variable.
In this example, the loop runs len amount of times, where len is equal to the legnth of the Temp variable
because the condition statement i < len is true for the first (amount) of times values
of i (0, 1, 2, 3, and so on). The increment statement i++ increases the value of i by 1 after each iteration.
*/
// This function clears the specified number of characters on the specified row of the LCD
// Parameters:
// - lcd: an instance of the LiquidCrystal_I2C class representing the LCD
// - len: an integer representing the number of characters to clear on the specified row
// - row: an integer representing the row on which the characters should be cleared
// - delayTime: an integer representing the delay time in milliseconds between clearing each character
void clearlcd(LiquidCrystal_I2C lcd, int start, int len, int row) {
// Iterate over each character to be cleared
for (byte i = start; i < len + start; i++) {
// Set the cursor to the current character on the specified row
// we need len + start, for example start may be 12 and len 3 that means for the condition 12 <3+12 in order
// for the for statement to continue running. if we did not include +start the statement would not run
lcd.setCursor(i, row);
// Print a space character to clear the current character
//we have to use -1 for the row as inputting 0 for the top row gives the integer a value of
// nothing so it wont work so we must use -1 therefore when we set the row we use 1 above the row we want.
lcd.print(" ");
// Wait for the specified delay time before clearing the next character
delay(25);
}
}
// creates a function clearlcd(lcd, where to start, how long, what row (on the lcd))
// A void function is a function in programming that does not return a value. It is used to perform a specific task or set of tasks that do not require any return values.
// We use a function in order to remove something that repeats in a function. if it doesnt repeat it isnt nessacary
// the reason we can resuse 'i' in every function is because creating a variable inside a function is only avaliable inside that function
void loop() {
// Declare and initialize variables for temperature, humidity, and heat index
float f = dht.readTemperature(true); // Read temperature in Fahrenheit
float h = dht.readHumidity(); // Read humidity
float t = dht.readTemperature(); // Read temperature in Celsius
// Declare character arrays for storing text strings
char Temp[16] = "Temp: "; // Store the text "Temp: "
char H[16] = "Humi: "; // Store the text "Humi: "
char t_str[16]; // Store the temperature as a string
char h_str[16]; // Store the humidity as a string
// Convert the float values for temperature and humidity into string values using dtostrf()
// This is necessary because strcat() requires both arguments to be of type char*
dtostrf(t, 3, 2, t_str); // Convert the temperature to a string with 3 digits total and 1 digits after the decimal point
dtostrf(h, 3, 2, h_str); // Convert the humidity to a string with 3 digits total and 1 digits after the decimal point
// Concatenate the temperature and humidity strings with their corresponding labels
strcat(Temp, t_str); // Append the temperature string to the end of the Temp string
strcat(H, h_str); // Append the humidity string to the end of the Humi string
if (isnan(h) || isnan(t) || isnan(f)) { // checks if the sensor is working and is recieving data
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
float hif = dht.computeHeatIndex(f, h); // compute the heat index in fahrenheit
float hic = dht.computeHeatIndex(t, h, false); // compute the heat index in celsius
for (byte i = 0; i < strlen(Temp); i++) { // iterates through each character in the Temp string: gets the length of the Temp string
delay(25); // waits for a specified delay time
lcd.setCursor(i, 0); // sets the cursor to the current position on the first row of the lcd screen
lcd.print(Temp[i]); // prints the current character of the Temp string to the lcd screen
lcd.setCursor(i, 1); // sets the cursor to the current position on the second row of the lcd screen
lcd.print(H[i]); // prints the current character of the H string to the lcd screen
}
lcd.setCursor(10, 0);
delay(25);
lcd.print("C");
delay(25);
lcd.print(char(223));
lcd.setCursor(10, 1);
lcd.print("%");
clearlcd(lcd, 12, 3, 0); // Runs the ClearLCD function that starts at 12 on the lcd, clears 3 and on row 0
/* LDR SENSOR */
char printLL[12] = "Light Level"; // creates a character array called printLL with the value "Light Level"
char LDR_Readings[16];
const char* LDR_MESSAGES[] = {" - Dark - ", " - Dim - ", " - Light - ", " - Bright - ", "- Very Bright -"};
AnalogValue = analogRead(A0); // setting analogValue to whatever the the value of the analog pin A0 is.
int index = map(AnalogValue, 0, 1023, 0, 4); // map the sensor value to an index variable
strcpy(LDR_Readings, LDR_MESSAGES[index]); // get the corresponding message from the array
/*
These lines are the modified if statements that set the appropriate message based on the AnalogValue reading.
If the AnalogValue is less than 10, the message array is assigned the string " - Dark - ".
If the AnalogValue is between 10 and 200, the message array is assigned the string " - Dim - ", and so on.
Note that the strcpy function is used to copy the appropriate message into the message array.
This function copies the contents of the second argument (the string) into the first argument (the message array).
*/
millisreset = millis();
while (millis() < millisreset + period) {
}
clearlcd(lcd, 0, 3, 0);
for (byte i = 0; i < strlen(printLL); i++) { // iterates through each character in the Light Level string: gets the length of the Light Level string
lcd.setCursor(i + 2, 0); // sets the cursor to the next position on the first row of the lcd screen
delay(25); // waits for a specified delay time
lcd.print(printLL[i]); // prints the current character of the Light Level string to the lcd screen
}
clearlcd(lcd, 0, 5, 1);
for (byte i = 0; i < strlen(LDR_Readings); i++) { // iterates through each character in the LDR readings string
lcd.setCursor(i, 1); // sets the cursor to the next position on the second row of the lcd screen
delay(25); // waits for a specified delay time
lcd.print(LDR_Readings[i]); // prints the current character of the LDR readings string to the lcd screen
}
/*
using the function displayLDR(...); it allows us to easily print a function into the if statements
(see above for info on displayLDR() )
Here, the readSoil() function returns an integer value, which is first converted
into a string using the String() function and stored in the soilValue variable.
Then, the for loop iterates over each character of the soilValue string and prints
it to the LCD screen using lcd.print(soilValue[i]), followed by a 25 millisecond
delay using delay(25).
*/
char soil[16]; // set a character array to soil
char moisture[16] = "Soil Moisture"; // creates a character array called moisture with the value "Soil Moisture"
millisreset = millis();
while (millis() < millisreset + period) {
}
String SoilMoisture = String(moisture);
for (byte i = 0; i < SoilMoisture.length(); i++) {
lcd.setCursor(i + 1, 0);
lcd.print(moisture[i]);
delay(25);
}
if (readSoil() < 200) {
char soil[16] = "WATER!";
} else if (readSoil() > 600) {
char soil[16] = "TOO WET!";
} else {
char soil[16] = "Good";
}
for (byte p = 0; p < 10; p++) {
lcd.setCursor(p + 4, 1);
lcd.print(soil[p]);
delay(25);
}
millisreset = millis();
while (millis() < millisreset + period) {
}
}