Code Overview and Integration π¶
In this section, you will find the code used to run the sensors for our AgroLink project π±. Each sensor contributes vital data to the system, allowing for real-time decisions based on environmental factors. Here’s a breakdown of each sensor’s code, what it does, and how the data helps the user optimize their indoor garden.
We’ll start by looking at the individual codes for each sensor and explain how they come together in the merged code.
How to Use Arduino IDE with AgroLink π¶
To get started with the AgroLink system, you’ll need the Arduino IDE software to upload the code to the microcontrollers (Arduino Uno and Arduino MKR WiFi 1010).
-
Steps:
- Connect the Arduino board to your computer via USB.
- Open the Arduino IDE and paste the code.
- Select the correct board (Arduino Uno or MKR WiFi 1010) and COM port.
- Click Upload to transfer the code to the microcontroller.
- Use the Serial Monitor (9600 baud rate) to see real-time sensor readings and debugging messages.
Water Level Sensor π¶
The water level sensor detects the amount of water in the bird sink. It outputs different readings depending on whether the sink is empty, low, medium, or full.
Code Explanation: This code reads the analog signal from the water level sensor and prints its state to the serial monitor.
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup() {
Serial.begin(9600);
}
void loop() {
int value = analogRead(A1);
if (value == 0){
Serial.println("Empty");
} else if (value > 1 && value < 200) {
Serial.println("Low");
} else if (value > 201 && value < 220) {
Serial.println("Medium");
} else if (value > 220){
Serial.println("High");
}
}
What the User Sees:
- The water level (empty, low, medium, or full) is printed in the Serial Monitor.
Pin Arrangement:
- Sensor Pin: A1
Temperature & Humidity Sensor π‘οΈπ§ (DHT11)¶
This code reads data from the DHT11 sensor to measure the temperature and humidity in the environment.
#include <DHT11.h>
DHT11 dht11(2);
void setup() {
Serial.begin(9600);
}
void loop() {
int temperature = 0;
int humidity = 0;
int result = dht11.readTemperatureHumidity(temperature, humidity);
if (result == 0) {
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.print(" Β°C\tHumidity: ");
Serial.print(humidity);
Serial.println(" %");
} else {
Serial.println(DHT11::getErrorString(result));
}
}
What the User Sees:
- Temperature and humidity values are printed in degrees Celsius and percentage.
Pin Arrangement:
- Data Pin: Digital Pin 2
Light Intensity Sensor βοΈ/π¶
The light intensity sensor measures ambient light levels, indicating whether it’s light or dark.
void setup() {
Serial.begin(9600);
}
void loop() {
int analogValue = analogRead(A3);
Serial.print("Analog reading: ");
Serial.print(analogValue);
if (analogValue < 400) {
Serial.println(" - Light");
}
else (analogValue < 800); {
Serial.println(" - Dark");
}
}
What the User Sees:
- The sensor displays whether the environment is “Light” or “Dark”.
Pin Arrangement:
- Analog Pin: A3
Air Quality Sensor π¬οΈ¶
The air quality sensor monitors the levels of pollutants in the air and prints the data in PPM (Parts Per Million).
int sensorPin=A2;
int sensorData;
void setup() {
Serial.begin(9600);
pinMode(sensorPin, INPUT);
}
void loop() {
sensorData = analogRead(sensorPin);
Serial.print("Air Quality:");
Serial.print(sensorData, DEC);
Serial.println(" PPM");
delay(100);
}
What the User Sees:
- Air quality readings in PPM.
Pin Arrangement:
- Analog Pin: A2
Proximity Sensor (APDS9960) π¦¶
The proximity sensor detects the presence of a bird near the feeder or bird sink.
#include <Arduino_APDS9960.h>
void setup() {
Serial.begin(9600);
if (!APDS.begin()) {
Serial.println("Error initializing APDS-9960 sensor!");
}
}
void loop() {
if (APDS.proximityAvailable()) {
int proximity = APDS.readProximity();
Serial.println(proximity);
}
delay(100);
}
What the User Sees:
- The proximity sensor returns values from 0 (close) to 255 (far), helping to detect when a bird is near.
Pin Arrangement:
- Sensor Interface: I2C Communication
Soil Moisture Sensor π±¶
This sensor monitors the soil moisture level, providing crucial information to determine whether the plants need watering.
#define wetSoil 277
#define drySoil 380
#define sensorPin A0
void setup() {
Serial.begin(9600);
}
void loop() {
int soilValue = analogRead(sensorPin);
if (soilValue < wetSoil) {
Serial.println("Soil is too wet");
} else if (soilValue >= wetSoil && soilValue < drySoil) {
Serial.println("Soil moisture is perfect");
} else {
Serial.println("Soil is too dry");
}
}
What the User Sees:
- Soil moisture status: “Too Wet”, “Perfect”, or “Too Dry”.
Pin Arrangement:
- Analog Pin: A0
Servo Control (Valve for Water Flow) π§¶
The servo motor controls the valve that opens or closes based on the moisture level, effectively managing water flow.
#include <Servo.h>
Servo myservo;
void setup() {
myservo.attach(8);
myservo.write(30);
delay(1000);
myservo.write(180);
delay(1000);
}
void loop() {
// no further actions needed
}
What the User Sees:
- The servo opens or closes the valve based on sensor data (soil moisture).
Pin Arrangement:
- Pin: Digital Pin 8
Merged Code for Action Scenarios π οΈπΏ¶
This code governs the system’s actions based on sensor inputs. It controls the water pump for the bird sink, the servo for the valve (which controls water flow to the plants), and integrates the soil moisture sensor, water level sensor, and proximity sensor to manage these actions.
Code:¶
// Pin Definitions
#define relayPin 7 // Relay for pump (active-low)
#define servoPin 8 // Servo for valve
#define soilMoisturePin A0 // Soil moisture sensor
#define waterLevelPin A1 // Water level sensor
#include <Servo.h> // Library for controlling the servo
#include <Arduino_APDS9960.h> // Proximity sensor library
#include <LiquidCrystal_I2C.h> // LCD for displaying info
LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address
Servo myservo; // Create servo object
// Define moisture levels
#define wetSoil 277 // Soil moisture value when soil is wet
#define drySoil 380 // Soil moisture value when soil is dry
// Define water levels
#define emptyWater 0 // Value for empty water level
#define waterDetected 200 // Value when water is detected
// Proximity sensor readings
int proximityReadings[10];
int proximityReadIndex = 0;
int totalProximity = 0;
bool birdDetected = false;
bool relayActive = false;
// Soil moisture average readings
const int numSoilReadings = 30;
int soilReadings[numSoilReadings];
int soilReadIndex = 0;
int totalSoilMoisture = 0;
int averageSoilMoisture = 0;
void setup() {
Serial.begin(9600);
pinMode(relayPin, OUTPUT); // Pump control via relay
digitalWrite(relayPin, HIGH); // Turn off pump initially
myservo.attach(servoPin); // Attach servo to pin
myservo.write(180); // Initial position: valve closed (180Β°)
lcd.init(); // Initialize LCD
lcd.backlight();
// Initialize proximity sensor and array
for (int i = 0; i < 10; i++) {
proximityReadings[i] = 0;
}
for (int i = 0; i < numSoilReadings; i++) {
soilReadings[i] = 0;
}
// Initialize proximity sensor (APDS9960)
if (!APDS.begin()) {
Serial.println("Error initializing proximity sensor!");
}
}
void loop() {
// Read water level
int waterLevel = analogRead(waterLevelPin);
// Read soil moisture and average the readings
totalSoilMoisture -= soilReadings[soilReadIndex];
soilReadings[soilReadIndex] = analogRead(soilMoisturePin);
totalSoilMoisture += soilReadings[soilReadIndex];
soilReadIndex = (soilReadIndex + 1) % numSoilReadings;
averageSoilMoisture = totalSoilMoisture / numSoilReadings;
// Read proximity sensor and calculate average proximity
if (APDS.proximityAvailable()) {
int proximity = APDS.readProximity();
if (proximity != -1) {
totalProximity -= proximityReadings[proximityReadIndex];
proximityReadings[proximityReadIndex] = proximity;
totalProximity += proximityReadings[proximityReadIndex];
proximityReadIndex = (proximityReadIndex + 1) % 10;
}
}
int averageProximity = totalProximity / 10;
birdDetected = (averageProximity >= 0 && averageProximity <= 155);
// Check water status (empty/full)
String waterStatus = (waterLevel > waterDetected) ? "Detected" : "Not Detected";
// Handle relay (pump) based on proximity and water detection
if (birdDetected && waterStatus == "Not Detected") {
relayActive = true;
digitalWrite(relayPin, LOW); // Activate pump (relay ON)
} else {
relayActive = false;
digitalWrite(relayPin, HIGH); // Deactivate pump (relay OFF)
}
// Control the servo (valve) based on soil moisture levels
if (averageSoilMoisture < wetSoil) {
// Too wet: Close valve
Serial.println("Soil too wet, closing valve...");
myservo.write(30); // Close valve (pinch tube)
} else if (averageSoilMoisture >= wetSoil && averageSoilMoisture < drySoil) {
// Perfect soil moisture: Keep valve closed
Serial.println("Soil moisture is perfect, keeping valve closed...");
myservo.write(30); // Close valve
} else {
// Too dry: Open valve
Serial.println("Soil too dry, opening valve...");
myservo.write(180); // Open valve (allow water flow)
}
// Display results on Serial Monitor
Serial.println("\n=========================");
Serial.println("Sensor Readings:");
Serial.println("=========================");
Serial.print("Water Level: ");
Serial.println(waterLevel);
Serial.print("Water Status: ");
Serial.println(waterStatus);
Serial.print("Soil Moisture (average): ");
Serial.println(averageSoilMoisture);
if (averageSoilMoisture < wetSoil) {
Serial.println("Soil Status: Too Wet");
} else if (averageSoilMoisture >= wetSoil && averageSoilMoisture < drySoil) {
Serial.println("Soil Status: Perfect");
} else {
Serial.println("Soil Status: Too Dry");
}
Serial.print("Proximity (avg): ");
Serial.println(averageProximity);
if (birdDetected) {
Serial.println("Bird detected near the sink.");
} else {
Serial.println("No bird detected.");
}
if (relayActive) {
Serial.println("Pump Status: ON (water being pumped)");
} else {
Serial.println("Pump Status: OFF");
}
Serial.println("=========================\n");
delay(1000); // Wait before next reading
}
Explanation of the Code π¶
-
Water Pump Control (Relay):
- The pump is controlled by a relay connected to pin 7. The relay is activated if:
- A bird is detected by the proximity sensor.
- The water level is not detected (i.e., the sink is empty).
- When the bird is present and the sink needs water, the pump is turned on to fill the sink.
- The pump is controlled by a relay connected to pin 7. The relay is activated if:
-
Soil Moisture Sensor:
- The soil moisture sensor is connected to analog pin A0.
- The code averages soil moisture readings over time to reduce noise and inaccuracies.
- Based on the moisture level:
- If the soil is too dry, the servo valve opens to allow water to flow to the plants.
- If the soil is too wet or the moisture is perfect, the valve remains closed.
-
Proximity Sensor:
- The proximity sensor is used to detect the presence of a bird near the sink.
- The average of multiple readings ensures that quick, temporary changes (like a passing shadow) donβt trigger the pump unnecessarily.
- If a bird is detected, the system pumps water to the sink to provide drinking or bathing water.
-
Servo Valve Control:
- The servo, connected to pin 8, controls the 3D-printed valve for water distribution.
- The valve opens when the soil is too dry, allowing water to flow from the bird sink to the plants.
- The valve closes when the soil is adequately moist or too wet, preventing overwatering.
-
Water Level Sensor:
- The water level sensor, connected to analog pin A1, monitors the amount of water in the bird sink.
- If the water level is low or empty, the pump is activated to refill the sink.
Action Scenarios Based on Binary Logic π οΈπΏ¶
-
Scenario 1: Bird Detected, Sink Water Level Low, Soil Moisture Dry
- Action:
- The pump is activated to fill the sink (since the bird is present and the sink water level is low).
- Simultaneously, the servo valve opens to allow water to flow to the plants (because the soil moisture is too dry).
- Why: This ensures the bird gets water and the plants are hydrated.
- Action:
-
Scenario 2: Bird Detected, Sink Water Level High, Soil Moisture Dry
- Action:
- The pump remains off (since the sink is already full).
- The servo valve opens to let water flow to the plants (due to the dry soil).
- Why: The bird has enough water, but the plants need irrigation.
- Action:
-
Scenario 3: Bird Detected, Sink Water Level Low, Soil Moisture Perfect
- Action:
- The pump is activated to fill the sink.
- The servo valve remains closed (since the soil moisture is perfect).
- Why: The system conserves water by only filling the bird sink, while the plants don’t need watering.
- Action:
-
Scenario 4: Bird Detected, Sink Water Level High, Soil Moisture Perfect
- Action:
- The pump remains off.
- The servo valve remains closed.
- Why: Everything is already in an optimal stateβno water needed for either the bird or the plants.
- Action:
-
Scenario 5: No Bird Detected, Sink Water Level Low, Soil Moisture Dry
- Action:
- The pump remains off (since no bird is present).
- The servo valve opens to allow water flow to the plants (due to dry soil).
- Why: The system prioritizes watering the plants without wasting water for the bird sink.
- Action:
-
Scenario 6: No Bird Detected, Sink Water Level High, Soil Moisture Dry
- Action:
- The pump remains off.
- The servo valve opens to water the plants.
- Why: Since no bird is detected and the sink is full, the plants get irrigated without refilling the bird sink.
- Action:
-
Scenario 7: No Bird Detected, Sink Water Level High, Soil Moisture Perfect or Too Wet
- Action:
- The pump remains off.
- The servo valve stays closed.
- Why: Everything is optimal; no need to waste water on the plants or bird sink.
- Action:
Future Improvements: Integration with the Blynk App π±π¶
While the current version of AgroLink automates all actions based on the sensors using pre-programmed logic, there is potential for even greater control and flexibility. In the future, we plan to integrate the Blynk app to provide users with the following features:
- Manual Overrides: Users could manually control the pump and servo valve via the app, allowing for on-demand watering or filling the bird sink.
- Custom Alerts: The app could send notifications when certain thresholds are reached, such as low water level in the sink or overly dry soil.
- Real-Time Monitoring: Users could track sensor data (such as proximity, soil moisture, and water levels) remotely, giving them peace of mind that the system is functioning correctly without being physically present.
By introducing the Blynk app in future iterations, the system can become more user-friendly, adaptable, and responsive to changing environmental conditions, making AgroLink even more sustainable and efficient.
Updated Action Merged Code (Aligned with Binary Logic Scenarios)¶
// Pin Definitions
#define relayPin 7 // Relay for pump
#define servoPin 8 // Servo for valve
#define soilMoisturePin A0 // Soil moisture sensor
#define waterLevelPin A1 // Water level sensor
#define proximityPin A2 // Proximity sensor
#include <Servo.h>
#include <Arduino_APDS9960.h> // Proximity sensor library
#include <LiquidCrystal_I2C.h> // LCD display library
LiquidCrystal_I2C lcd(0x27, 16, 2); // Initialize LCD
Servo myservo; // Servo object for valve control
// Define sensor threshold values
#define wetSoil 277 // Threshold for wet soil
#define drySoil 380 // Threshold for dry soil
#define waterEmpty 0 // Empty water level
#define waterLow 200 // Low water level
#define birdCloseProximity 155 // Threshold for detecting bird proximity
// Proximity sensor variables
int proximityReadings[10];
int proximityReadIndex = 0;
int totalProximity = 0;
bool birdDetected = false;
// Soil moisture averaging variables
const int numSoilReadings = 30;
int soilReadings[numSoilReadings];
int soilReadIndex = 0;
int totalSoilMoisture = 0;
int averageSoilMoisture = 0;
// Water level status
String waterStatus = "Unknown";
void setup() {
Serial.begin(9600);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, HIGH); // Initially turn off the relay (pump off)
myservo.attach(servoPin); // Attach the servo to pin 8
myservo.write(180); // Initially set the valve to "closed"
lcd.init();
lcd.backlight();
// Initialize proximity and soil readings arrays
for (int i = 0; i < 10; i++) {
proximityReadings[i] = 0;
}
for (int i = 0; i < numSoilReadings; i++) {
soilReadings[i] = 0;
}
// Initialize the APDS-9960 proximity sensor
if (!APDS.begin()) {
Serial.println("Error initializing APDS-9960 sensor!");
}
}
void loop() {
// Read water level sensor
int waterLevel = analogRead(waterLevelPin);
// Read soil moisture and calculate a 30-reading average
totalSoilMoisture -= soilReadings[soilReadIndex];
soilReadings[soilReadIndex] = analogRead(soilMoisturePin);
totalSoilMoisture += soilReadings[soilReadIndex];
soilReadIndex = (soilReadIndex + 1) % numSoilReadings;
averageSoilMoisture = totalSoilMoisture / numSoilReadings;
// Read proximity sensor and calculate a 10-reading average
if (APDS.proximityAvailable()) {
int proximity = APDS.readProximity();
if (proximity != -1) {
totalProximity -= proximityReadings[proximityReadIndex];
proximityReadings[proximityReadIndex] = proximity;
totalProximity += proximityReadings[proximityReadIndex];
proximityReadIndex = (proximityReadIndex + 1) % 10;
}
}
int averageProximity = totalProximity / 10;
birdDetected = (averageProximity >= 0 && averageProximity <= birdCloseProximity);
// Determine the water level status
if (waterLevel > waterLow) {
waterStatus = "Detected";
} else {
waterStatus = "Not Detected";
}
// Binary logic to control actions based on combined sensor data
// Scenario 1: Bird is detected and water level is empty -> Pump water to the sink
if (birdDetected && waterLevel == waterEmpty) {
digitalWrite(relayPin, LOW); // Turn on the pump (start water flow)
Serial.println("Bird detected, water empty -> Starting pump");
}
// Scenario 2: Bird is detected and water level is low -> Keep water level stable, no action
else if (birdDetected && waterLevel > waterEmpty && waterLevel <= waterLow) {
digitalWrite(relayPin, HIGH); // Turn off pump (stop water flow)
Serial.println("Bird detected, water low -> No action");
}
// Scenario 3: No bird detected and water level is high -> Turn off the pump
else if (!birdDetected && waterLevel > waterLow) {
digitalWrite(relayPin, HIGH); // Ensure pump is off
Serial.println("No bird detected, water high -> Pump off");
}
// Scenario 4: Soil is too dry -> Open valve to water the plants
if (averageSoilMoisture > drySoil) {
myservo.write(180); // Open valve (allow water flow to plants)
Serial.println("Soil too dry -> Opening valve to water plants");
}
// Scenario 5: Soil is perfect -> Close the valve
else if (averageSoilMoisture >= wetSoil && averageSoilMoisture <= drySoil) {
myservo.write(30); // Close valve (stop water flow)
Serial.println("Soil moisture perfect -> Closing valve");
}
// Scenario 6: Soil is too wet -> Keep valve closed
else if (averageSoilMoisture < wetSoil) {
myservo.write(30); // Keep valve closed
Serial.println("Soil too wet -> Keeping valve closed");
}
// Print sensor data and actions to Serial Monitor
Serial.println("\n=========================");
Serial.println("Sensor Readings & Actions:");
Serial.println("=========================");
Serial.print("Water Level: ");
Serial.println(waterLevel);
Serial.print("Water Status: ");
Serial.println(waterStatus);
Serial.print("Soil Moisture (average): ");
Serial.println(averageSoilMoisture);
Serial.print("Proximity (average): ");
Serial.println(averageProximity);
if (birdDetected) {
Serial.println("Bird detected");
} else {
Serial.println("No bird detected");
}
Serial.println("=========================\n");
// Delay for readability
delay(1000);
}
How This Code Aligns with the Binary Logic¶
This updated code fully aligns with the binary logic scenarios we discussed earlier. It integrates sensor readings from the proximity sensor, water level sensor, and soil moisture sensor to control the actions of the water pump (relay) and the valve (servo) based on specific conditions.
Hereβs a breakdown of the scenarios it handles:
-
Bird detected & water empty: Starts the water pump to fill the bird sink.
-
Bird detected & water low: Does nothing (maintains current water level).
-
No bird detected & water high: Ensures the water pump is off to conserve water.
-
Soil too dry: Opens the valve to irrigate plants.
-
Soil perfect: Closes the valve, maintaining soil moisture.
-
Soil too wet: Keeps the valve closed to prevent overwatering.
Future Improvements¶
In the future, we can integrate the Blynk app to allow users to monitor sensor readings and control the pump and valve remotely, adding an extra layer of automation and control to the system. This would make the system more user-friendly and interactive, especially for users who want real-time updates on their phone.
Note: I havenβt yet tested this new code with the actual system, but based on the binary logic, it should function correctly in theory. Testing will confirm if any adjustments are needed.
Merged Code for Data Reading π‘οΈπ¬οΈπ‘¶
This merged code reads and processes data from the air quality sensor, DHT11 temperature & humidity sensor, and light intensity sensor. It outputs the readings to the serial monitor, giving the user a comprehensive overview of the environmental conditions.
// Libraries
#include <DHT11.h> // For temperature & humidity sensor
#include <Wire.h> // For I2C communication (if needed for future use)
// Pin Definitions
#define airQualityPin A2 // Air quality sensor pin
#define lightSensorPin A3 // Light intensity sensor pin
#define dhtPin 2 // Temperature & humidity sensor pin (DHT11)
// DHT11 sensor object
DHT11 dht11(dhtPin);
void setup() {
Serial.begin(9600); // Initialize serial communication
// Check if the DHT11 sensor is properly initialized
Serial.println("Initializing Sensors...");
}
void loop() {
// **Air Quality Reading**
int airQualityValue = analogRead(airQualityPin);
Serial.print("Air Quality (PPM): ");
Serial.println(airQualityValue); // Display air quality value in PPM
// **Light Intensity Reading**
int lightValue = analogRead(lightSensorPin);
Serial.print("Light Intensity: ");
if (lightValue < 400) {
Serial.println("Bright"); // Light intensity is high (bright)
} else if (lightValue < 800) {
Serial.println("Dim"); // Light intensity is moderate (dim)
} else {
Serial.println("Dark"); // Light intensity is low (dark)
}
// **Temperature & Humidity Reading**
int temperature = 0;
int humidity = 0;
int result = dht11.readTemperatureHumidity(temperature, humidity);
if (result == 0) {
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.println(" Β°C");
Serial.print("Humidity: ");
Serial.print(humidity);
Serial.println(" %");
} else {
Serial.println(DHT11::getErrorString(result)); // Display error message if sensor reading fails
}
// Delay between each reading
delay(2000); // Wait for 2 seconds before next reading
}
Code Explanation¶
- Air Quality Sensor: The air quality sensor reads the pollutant levels in Parts Per Million (PPM) and prints the value on the Serial Monitor.
- Light Intensity Sensor: Based on the analog reading from the light sensor, the system will output whether the environment is bright, dim, or dark.
- Temperature & Humidity Sensor: The DHT11 sensor provides real-time readings of the temperature (in Β°C) and humidity (in %), helping monitor environmental conditions for optimal plant growth.
Pin and Analog Arrangements π οΈ¶
-
Air Quality Sensor:
- Pin: Analog pin A2 is used to read the air quality sensor’s output.
- Purpose: Measures air pollution levels to ensure clean air for both plants and the indoor environment.
-
Light Intensity Sensor:
- Pin: Analog pin A3 is used to monitor light intensity.
- Purpose: Provides data on the amount of light available for plant growth.
-
Temperature & Humidity Sensor (DHT11):
- Pin: Digital pin 2 is connected to the DHT11 sensor for reading temperature and humidity.
- Purpose: Offers essential data on the indoor temperature and humidity, ensuring ideal conditions for both the bird and plants.
How the Data Helps the User¶
- Air Quality Data: Helps users monitor indoor air quality, which is crucial for the health of the plants and the overall indoor environment.
- Light Intensity Data: Allows users to adjust lighting conditions if the plants need more sunlight or artificial light to thrive.
- Temperature & Humidity Data: Offers critical information to manage air conditioning, fans, or misting systems to maintain an optimal growing environment.
This merged code provides the user with all the necessary environmental data in one place, helping them make better decisions for a healthier and more sustainable indoor garden. π±