Automated Greenhouse project: Difference between revisions

From Sensors in Schools
Jump to navigation Jump to search
Line 555: Line 555:
import requests
import requests
import datetime
import datetime
import plotly
import plotly.graph_objects as go
import pandas
import time
import time


Line 569: Line 566:
         mylist = line.split(',')
         mylist = line.split(',')
         try:
         try:
             voltage = int(mylist[0])
             temperature = float(mylist[0])
             print(voltage)
             print(voltage)
             fanOn = int(mylist[1])
             soilMoisture = float(mylist[1])
            except:
        except:
                voltage = 0
            temperature = 0.0
                fanOn = 0
            soilMoisture = 0.0
             voltage_correct = voltage * (5.0 / 1023.0) * 2
             voltage_correct = voltage * (5.0 / 1023.0) * 2
            print('The solar panel voltage = {0:.3f}'.format(voltage_correct))
 
            print(f'Fan status is {fanOn}')
        print(f'temperature is {temperature} and soil moisture is {soilMoisture}')
            #print(f'The cycle is {cycle}')
            print(f'datestamp is {date_stamp}')
            voltage_string = '{0:.3f}'.format(voltage_correct)
            fanOn_string = str(fanOn)
            #cycle = cycle + 1
         time.sleep(1)
         time.sleep(1)
           





Revision as of 22:43, 24 June 2023

Automated Greenhouse project

Authors

For more information contact Frederic Cherqui, Adam Simankowicz or Edmond Lascaris

Soil Moisture sensor

Applications

Soil moisture of trees, garden beds and agriculture. Sensors originally deployed in Africa to educate farmers about irrigation of food crops

How the Soil moisture sensor works

  • Soil moisture sensor circuit explained by Matthew Driver

Components

  • Arduino Uno microcontroller using two analog pins with 10 bit analog to digital converters (ADC).
  • Two 10K 0.1% precision resistors
  • Chameleon soil moisture sensor

Power supply voltage (Vcc)

  • This design is independent of voltage.
  • In this circuit the power will be supplied from a Raspberry Pi using the Serial USB cable.

Analog pins

  • The analog pins on the Arduino microcontroller are dual purpose.
  • They can measure a voltage between 0 and Vcc (when their mode is set as an analog INPUT)
  • They can also but can also output Vcc (5V) or drain to ground (when their mode is set to digital OUTPUT)
  • Both of these states are set using the pinmode function in the Arduino code.

States

  • To measure the soil moisture the sensor circuit is placed into three different states:
    • Forward current
    • Reverse current (the flow of electrons is reversed from the perspective of the sensor)
    • No current

Schematic

  • A schematic of the soil moisture sensor circuit is shown below.
  • Pin X and Pin Y are the two analog input pins on the Arduino microcontroller.

Excitation and and measuring (states applied)

Forward current flow

  • To begin the measurement a Forward current is applied to the sensor
  • The Forward current state is applied for 250us (microseconds) by setting the pins to:
    • pinMode(Pin_Y,INPUT)
    • pinMode(Pin_X,OUTPUT)
    • digitalWrite(Pin_X, HIGH)
  • The voltage is then measured at pin Y and stored in a variable.

No current flow

  • A No current state is then applied for 250us by setting the pins to:
    • digitalWrite(Pin X, LOW)
  • The result is all pins are at ground so no current flows.
  • Note that the INPUT pin acts as a drain, which is equivalent to ground.

Reverse current flow

  • Then Reverse current state is then applied for 250us (microseconds) by setting the pins to:
    • pinMode(Pin_X,INPUT)
    • pinMode(Pin_Y,OUTPUT)
    • digitalWrite(Pin_Y, HIGH)
  • The voltage is then measured at pin X and stored in variable.

Sensor resistance calculation

  • The Arduino convert the analog input to a digital value between 0 and 1023 where 1023 is the Vcc voltage (5V), *
  • Sensor Resistance = Rs = 10K * (1023 - Vs) / Vs
    • where Vs is the average of forward and reverse current values
  • Temperature will change the measured value
  • A simple formula allows for soil temperature changes to be corrected.
    • Formula: Ra = R0 * (1 + (Temp - 22) * 0 .018 )
  • Lastly, the soil moisture resistance is converted to soil moisture tension and expressed in kPa pressure units.
  • This is a measure of how much pressure plants need to apply to extract moisture from soil.
  • The relationship is shown in the graph below.

Interpreting Soil Moisture sensor values

  • The Soil Moisture Sensor measures how hard it is for plants to draw water from soil.
  • This soil moisture sensor measures resistance and then converts this value to soil moisture tension in kPa.
  • This higher the soil moisture tension (kPa), the harder it is for plants to extract moisture from the soil.
  • Soil moisture tension from 20 - 30 kPa - 2-6 kOhms - signifies wet soil - ideal for broccoli, celery, lettuce and onion
  • Soil moisture tensions from 30 - 45 kPa - 6-21 kOhms - is moist soil - ideal for beans, cabbage, carrot, capsicum, corn, cucumber, eggplant, melons, potato and tomato.
  • Soil moisture tenions from 45 - 60 kPa - 20-50 kOhms - is dry soil - ideal for beet, peas, sweet potato and pumpkin.

Water content

Software installation requirements

  • Atom installed on PC or Mac with Pymakr package installed
  • Arduino IDE installed

Arduino IDE Code

const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0;       // Constant value of known resistor in k Ohms


  float ReadSensor();
  int sensorVoltageX, sensorVoltageY;               // Measured sensor voltage from 0 to 1023
  float sensorVoltage; 


void setup() {
   Serial.begin(9600);
  
}

void loop(){

  // Average of sensorVoltageX, sensorVoltageY
  
  //Forward current through sensor
  pinMode(sensorYAnalogPin, INPUT);                //Set the pin as drain
  pinMode(sensorXAnalogPin, OUTPUT);
  digitalWrite(sensorXAnalogPin, HIGH);             // set the voltage supply on
  sensorVoltageY = analogRead(sensorYAnalogPin);    // read the sensor voltage takes 125us
  Serial.println("sensorY "+String(sensorVoltageY));
  digitalWrite(sensorXAnalogPin, LOW);              // set the voltage supply off

  delayMicroseconds(250); //small delay before reversing polarity (mandatory according to Matt!)

  //Reverse current through sensor
  pinMode(sensorXAnalogPin, INPUT);                 //Set the pin as drain
  pinMode(sensorYAnalogPin, OUTPUT);
  digitalWrite(sensorYAnalogPin, HIGH);             // set the voltage supply on
  sensorVoltageX = analogRead(sensorXAnalogPin);    // read the sensor voltage takes 125us
  Serial.println("sensorX "+String(sensorVoltageX));
  digitalWrite(sensorYAnalogPin, LOW);              // set the voltage supply off
  
  
  sensorVoltage = (sensorVoltageY + sensorVoltageX) / 2;
  delayMicroseconds(250); //small delay before reversing polarity (mandatory according to Matt!)

  //Calculate resistance 
  float value= float(knownResistor) * ( 1023 - sensorVoltage ) / sensorVoltage;

  Serial.println("---resistance: "+String(value)+" kOhms\n");
  delay(300000);  // 5 minutes - use one number
}

Serial Monitor output

Multi-Tab Sketch for Soil Moisture

  • Main file auto-soil1

Code for Multi-Tab Sketch

const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0;       // Constant value of known resistor in k Ohms
float resistance;

void setup() {
  Serial.begin(9600);
}

void loop() {
  resistance = ReadSensor();
  Serial.print("Resistance: ");
  Serial.print(resistance);
  Serial.println(" kOhms");
  delay(5000); // Use one number for delay
}
  • Tab created and named readSoil

Code for Multi-Tab Sketch for Soil Moisture

float ReadSensor() {
                         
  int sensorVoltageX, sensorVoltageY;               // Measured sensor voltage from 0 to 1023
  float sensorVoltage;                              // Average of sensorVoltageX, sensorVoltageY
  
  //Forward current through sensor
  pinMode(sensorYAnalogPin, INPUT);                //Set the pin as drain
  pinMode(sensorXAnalogPin, OUTPUT);
  digitalWrite(sensorXAnalogPin, HIGH);             // set the voltage supply on
  sensorVoltageY = analogRead(sensorYAnalogPin);    // read the sensor voltage takes 125us
  digitalWrite(sensorXAnalogPin, LOW);              // set the voltage supply off

  //Reverse current through sensor
  pinMode(sensorXAnalogPin, INPUT);                 //Set the pin as drain
  pinMode(sensorYAnalogPin, OUTPUT);
  digitalWrite(sensorYAnalogPin, HIGH);             // set the voltage supply on
  sensorVoltageX = analogRead(sensorXAnalogPin);    // read the sensor voltage takes 125us
  digitalWrite(sensorYAnalogPin, LOW);              // set the voltage supply off
  sensorVoltage = (sensorVoltageY + sensorVoltageX) / 2;

  //Calculate resistance 
  return float( float(knownResistor) * ( 1023 - sensorVoltage ) / sensorVoltage );

}

Delays in Arduino

  • To make delays more human readable add the following code to your Arduino sketch.
  • Try this example obtained from O'Rielly creating delays
const long oneSecond = 1000;  // a second is a thousand milliseconds
const long oneMinute = oneSecond * 60;
const long fiveMinute = oneMinute * 5;
const long oneHour   = oneMinute * 60;
const long oneDay    = oneHour * 24;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  Serial.println("delay for 1 millisecond");
  delay(1);
  Serial.println("delay for 1 second");
  delay(oneSecond);
  Serial.println("delay for 1 minute");
  delay(oneMinute);
  Serial.println("delay for 5 minutes");
  delay(fiveMinute);
  Serial.println("delay for 1 hour");
  delay(oneHour);
  Serial.println("delay for 1 day");
  delay(oneDay);
  Serial.println("Ready to start over");
}

Combining both Temperature and Soil Moisture Codes

Arduino code for Soil and Temperature readings - image

Arduino code for Soil and Temperature readings - code

#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 4   // Data wire is conntected to the Arduino digital pin 4
OneWire oneWire(ONE_WIRE_BUS);  // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature sensor
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0;       // Constant value of known resistor in k Ohms
float resistance;

void setup() {
  Serial.begin(9600);
  // Start up the library
  sensors.begin();
}

void loop() {
  // Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
  sensors.requestTemperatures(); 
  Serial.print("Celsius temperature: ");

  Serial.println(sensors.getTempCByIndex(0)); //0 refers to the first IC on the wire
  delay(1000);
  resistance = ReadSensor();
  Serial.print("Resistance: ");
  Serial.print(resistance);
  Serial.println(" kOhms");
  delay(300000);
}

Arduino code to support Soil Moisture readings - image

Arduino code to support Soil Moisture readings - code

#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 4   // Data wire is conntected to the Arduino digital pin 4
OneWire oneWire(ONE_WIRE_BUS);  // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature sensor
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0;       // Constant value of known resistor in k Ohms
float resistance;

void setup() {
  Serial.begin(9600);
  // Start up the library
  sensors.begin();
}

void loop() {
  // Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
  sensors.requestTemperatures(); 
  Serial.print("Celsius temperature: ");

  Serial.println(sensors.getTempCByIndex(0)); //0 refers to the first IC on the wire
  delay(1000);
  resistance = ReadSensor();
  Serial.print("Resistance: ");
  Serial.print(resistance);
  Serial.println(" kOhms");
  delay(300000);
}

Example output of data in Serial Monitor

Sending Temperature and Soil Moisture data to Raspberry Pi

  • Data can be sent from the Arduino to the Raspberry pi using the Serial cable.
  • Data is separated by commas, without spaces.
  • In this example temperature and soil moisture data will be sent.
  • A typical output will look like this 13.5, 0.85
    • 13.5 degress Centigrade
    • 0.85 k Ohms resistance for the Soil moisture sensor

Arduino code to send data to Raspberry Pi - image

Arduino code to send data to Raspberry Pi - code

#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 4   // Data wire is conntected to the Arduino digital pin 4
OneWire oneWire(ONE_WIRE_BUS);  // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); // Pass our oneWire reference to Dallas Temperature sensor
const int sensorXAnalogPin = A4;
const int sensorYAnalogPin = A5;
const float knownResistor = 10.0;       // Constant value of known resistor in k Ohms
float resistance;
float temperature;

void setup() {
  Serial.begin(9600);
  // Start up the library
  sensors.begin();
}

void loop() {
  // Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
  sensors.requestTemperatures(); 
  Serial.print(sensors.getTempCByIndex(0)); // new line character removed

  resistance = ReadSensor();
  Serial.print(",");
  Serial.println(resistance);
  delay(5 * 60 * 1000);
}

Example output using Serial Monitor

Python code on the Raspberry Pi to read Arduino data

Python - code to constantly collect data from Arduino - code

#!/usr/bin/env python3
import serial


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()

    while True:
        if ser.in_waiting > 0:
            line = ser.readline().decode('utf-8').rstrip()
            print(line)

Python - code to constantly collect data from Arduino - image

Python - Collecting data from Arduino and adding a datestamp - code

#!/usr/bin/env python3
import serial
import datetime


if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()

    while True:
        if ser.in_waiting > 0:
            line = ser.readline().decode('utf-8').rstrip()
            print(line)
            
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'This python program was run at this time {date_stamp}')
            
            data = str(line) + "," + date_stamp + "\n"
            print(f'The data is =  {data}')

Python - Collecting data from Arduino and adding a datestamp - image

Python - saving data to a file - image

Python - saving data to a file - code

#!/usr/bin/env python3
import serial
import datetime
import time

if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()

    while True:
        time.sleep(0.1)  # added a small delay in loop
        if ser.in_waiting > 0:
            line = ser.readline().decode('utf-8').rstrip()
            print(line)
            
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'This python program was run at this time {date_stamp}')
            
            data = str(line) + "," + date_stamp + "\n"
            print(f'The data is =  {data}')
         
            f = open('/home/pi/auto-greenhouse/greenhouse_data.txt','a')
            f.write(data)
            f.close()


Python - with graphing in Plotly

#!/usr/bin/env python3
import serial
import datetime
import plotly
import plotly.graph_objects as go
import pandas
import time

if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()

    while True:
        time.sleep(0.1)
        if ser.in_waiting > 0:
            line = ser.readline().decode('utf-8').rstrip()
            print(line)
            
            now = datetime.datetime.now()
            date_stamp = now.strftime("%Y-%m-%d %H:%M:%S")
            print(f'This python program was run at this time {date_stamp}')
            
            data = str(line) + "," + date_stamp + "\n"
            print(f'The data is =  {data}')

            f = open('/home/pi/auto-greenhouse/greenhouse_data.txt','a')
            f.write(data)
            f.close()
            
            # import and plot data text file
            df = pandas.read_csv('/home/pi/auto-greenhouse/greenhouse_data.txt')

            fig = go.Figure(data = go.Scatter(mode='markers',
                                                x=df.date_time,
                                                y=df.soil_moisture,
                                                marker=dict(
                                                    color="LightSkyBlue",
                                                    size=20)
                                                )
                                        )
            fig.update_layout(title="Soil Moisture in Greenhouse",
                                xaxis_title = 'Time',
                                yaxis_title = 'Soil Moisture',
                                font=dict(
                                    size=20,
                                    color="RebeccaPurple"
                                )
                            )

            plotly.offline.plot(fig,
                                filename="/home/pi/auto-greenhouse/soil_moisture_plot.html",
                                auto_open=False)

            print("Soil Moisture data plotted")

Python - Splitting data and using try and except statements

#!/usr/bin/env python3
import serial
import requests
import datetime
import time

if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()

    if ser.in_waiting > 0:
        line = ser.readline().decode('utf-8').rstrip()
        print(f"separated using commas {line.split(',')}")
        mylist = line.split(',')
        try:
            temperature = float(mylist[0])
            print(voltage)
            soilMoisture = float(mylist[1])
        except:
            temperature = 0.0
            soilMoisture = 0.0
            voltage_correct = voltage * (5.0 / 1023.0) * 2

        print(f'temperature is {temperature} and soil moisture is {soilMoisture}')
        time.sleep(1)


    data = ""
    data = date_stamp + "," + voltage_string + "," + fanOn_string + '\n'
    f = open('/home/pi/Arduino/solar_arduino_data.txt','a')
    f.write(data)
    f.close()

Python - Dweeting Data

#!/usr/bin/env python3
import serial
import requests
import datetime
import time

if __name__ == '__main__':
    ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
    ser.reset_input_buffer()

    if ser.in_waiting > 0:
        line = ser.readline().decode('utf-8').rstrip()
        print(f"separated using commas {line.split(',')}")
        mylist = line.split(',')
        try:
            temperature = float(mylist[0])
            print(voltage)
            soilMoisture = float(mylist[1])
        except:
            temperature = 0.0
            soilMoisture = 0.0
            voltage_correct = voltage * (5.0 / 1023.0) * 2

        print(f'temperature is {temperature} and soil moisture is {soilMoisture}')
        time.sleep(1)
            
    print("Preparing dweet")
    dweet_dict = {}
    dweet_dict.update({"temperature": str(temperature)})
    dweet_dict.update({"soilMoisture": str(soilMoisture)})
    url = "https://dweet.io/dweet/for/SMC-auto-greenhouse1?"
    x = requests.post(url, json=dweet_dict)
    print(x.text)