Ionuț-Alexandru BaltariuPavăl Mihaela-IrinaBejenariu Razvan-Marius
Published © MIT

Flower Monitoring using Raspberry Pi & Arduino

Simple project that uses a Raspberry Pi 3 Model B and an Arduino in order to read data from sensors and display it on a flask website.

BeginnerFull instructions provided4 hours1,994
Flower Monitoring using Raspberry Pi & Arduino

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
Arduino UNO
Arduino UNO
×1
Gravity: DHT11 Temperature Humidity Sensor For Arduino
DFRobot Gravity: DHT11 Temperature Humidity Sensor For Arduino
×1
Male/Female Jumper Wires
Male/Female Jumper Wires
×3
Female/Female Jumper Wires
Female/Female Jumper Wires
×5
Soil Humidity Sensor
×1
Buzzer
Buzzer
×1

Software apps and online services

Python 3.9
Flosk Web Server
PuTTY - SSH Client
or one might use the default terminal on a Linux or Mac machine with ssh installed.
Python modules: flask, json, serial, threading, Adafruit_DHT, time, smtplib, datetime, os, email.mime.text
Raspberry Pi OS

Story

Read more

Schematics

Whole Project Schematic

It's worth mentioning that the Arduino is connected to the RPi3 via the lower USB on the right stack.

Code

Flask Server

Python
from flask import Flask, render_template
import time
import json
import serial
import threading
import Adafruit_DHT
import time
import smtplib
import datetime
import os
from buzzer import *
from email.mime.text import MIMEText

app = Flask(__name__)

last_soil_moisture_value = 0
last_temperature_value = 0
last_air_humidity_value = 0
last_sent_email_hour = 0
last_sent_email_hour_th = 0
DHT_SENSOR = Adafruit_DHT.DHT11
DHT_PIN = 4
email = 'iotflowermonitoring@gmail.com'
email_password = str(os.environ.get("iot_password"))
mutex = threading.Lock()


@app.route('/')
def index():
    return render_template('index.html', soil_humidity=str(last_soil_moisture_value),
                           temperature=str(last_temperature_value),
                           air_humidity=str(last_air_humidity_value))


@app.route('/', methods=['POST'])
def on_play():
    setup()
    play(final_countdown_melody, final_countdown_tempo, 0.30, 1.2000)
    return index()


def get_values_from_serial():
    global last_soil_moisture_value
    global last_sent_email_hour
    global email_password
    ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
    while True:
        try:
            read_serial = ser.readline()
            read_json = json.loads(read_serial.decode())
            if read_json['moisture'] is not None:
                last_soil_moisture_value = float(read_json['moisture']) * 100
                if abs(last_sent_email_hour - datetime.datetime.now().hour) >= 1:
                    if last_soil_moisture_value < 50:
                        last_sent_email_hour = datetime.datetime.now().hour
                        msg = "Your flower has low soil moisture (" + str(
                            last_soil_moisture_value) + "%)! Water it urgently!"
                        send_mail(msg)
                    elif last_soil_moisture_value > 80:
                        last_sent_email_hour = datetime.datetime.now().hour
                        msg = "Your flower has high soil moisture (" + str(
                            last_soil_moisture_value) + "%)! Stop watering!"
                        send_mail(msg)

            print(last_soil_moisture_value)
        except json.decoder.JSONDecodeError:
            pass
        except serial.serialutil.SerialException:
            pass


def read_temperature_and_humidity():
    global last_air_humidity_value
    global last_temperature_value
    global last_sent_email_hour_th
    while True:
        air_humidity, temperature = Adafruit_DHT.read(DHT_SENSOR, DHT_PIN)
        if air_humidity is None and temperature is None:
            print("error")
        else:
            last_air_humidity_value = air_humidity
            last_temperature_value = temperature
            if (temperature < 18 or temperature >= 30 or air_humidity < 25 or air_humidity > 50) and \
                    abs(last_sent_email_hour_th - datetime.datetime.now().hour) >= 1:
                msg = "Temperature/air humidity in flower room too low/high:" + str(temperature) + "*C and " + str(
                    air_humidity) + "%"
                last_sent_email_hour_th = datetime.datetime.now().hour
                send_mail(msg)
        time.sleep(3)


def send_mail(message):
    global mutex

    mutex.acquire()

    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    server.login(email, email_password)

    email_content = MIMEText(message, 'plain')
    email_content['Subject'] = 'Flower Monitoring Report'

    server.sendmail(email, "ionut.alexandru.baltariu@gmail.com", email_content.as_string())
    server.quit()

    print("Sent mail!")

    mutex.release()


moisture_thread = threading.Thread(target=get_values_from_serial)
dht11_thread = threading.Thread(target=read_temperature_and_humidity)

if __name__ == '__main__':
    moisture_thread.start()
    dht11_thread.start()
    app.run(host='0.0.0.0')

Arduino Code

Arduino
Directly introduce code into an Arduino sketch then upload it to your board. Make sure the moisture sensor signal pin is connected to A0 of the Arduino.
int sensorPin = A0; 
int sensorValue;  
int limit = 300; 
String json ="";

float moisture_percent = 0;

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

void loop() {
 sensorValue = analogRead(sensorPin); 
 sensorValue = map(sensorValue, 150, 700, 0, 100);
 moisture_percent = (1-(float)sensorValue/100);
 json = "{\"moisture\":\""+String(moisture_percent)+"\"}";
 Serial.println(json);
 delay(1000); 
}

Buzzer Code

Python
File is being used by the Flask Server
import RPi.GPIO as GPIO
import time

buzzer_pin = 17

notes = {
    'B0': 31,
    'C1': 33, 'CS1': 35,
    'D1': 37, 'DS1': 39,
    'EB1': 39,
    'E1': 41,
    'F1': 44, 'FS1': 46,
    'G1': 49, 'GS1': 52,
    'A1': 55, 'AS1': 58,
    'BB1': 58,
    'B1': 62,
    'C2': 65, 'CS2': 69,
    'D2': 73, 'DS2': 78,
    'EB2': 78,
    'E2': 82,
    'F2': 87, 'FS2': 93,
    'G2': 98, 'GS2': 104,
    'A2': 110, 'AS2': 117,
    'BB2': 123,
    'B2': 123,
    'C3': 131, 'CS3': 139,
    'D3': 147, 'DS3': 156,
    'EB3': 156,
    'E3': 165,
    'F3': 175, 'FS3': 185,
    'G3': 196, 'GS3': 208,
    'A3': 220, 'AS3': 233,
    'BB3': 233,
    'B3': 247,
    'C4': 262, 'CS4': 277,
    'D4': 294, 'DS4': 311,
    'EB4': 311,
    'E4': 330,
    'F4': 349, 'FS4': 370,
    'G4': 392, 'GS4': 415,
    'A4': 440, 'AS4': 466,
    'BB4': 466,
    'B4': 494,
    'C5': 523, 'CS5': 554,
    'D5': 587, 'DS5': 622,
    'EB5': 622,
    'E5': 659,
    'F5': 698, 'FS5': 740,
    'G5': 784, 'GS5': 831,
    'A5': 880, 'AS5': 932,
    'BB5': 932,
    'B5': 988,
    'C6': 1047, 'CS6': 1109,
    'D6': 1175, 'DS6': 1245,
    'EB6': 1245,
    'E6': 1319,
    'F6': 1397, 'FS6': 1480,
    'G6': 1568, 'GS6': 1661,
    'A6': 1760, 'AS6': 1865,
    'BB6': 1865,
    'B6': 1976,
    'C7': 2093, 'CS7': 2217,
    'D7': 2349, 'DS7': 2489,
    'EB7': 2489,
    'E7': 2637,
    'F7': 2794, 'FS7': 2960,
    'G7': 3136, 'GS7': 3322,
    'A7': 3520, 'AS7': 3729,
    'BB7': 3729,
    'B7': 3951,
    'C8': 4186, 'CS8': 4435,
    'D8': 4699, 'DS8': 4978
}

final_countdown_melody = [
    notes['A3'], notes['E5'], notes['D5'], notes['E5'], notes['A4'],
    notes['F3'], notes['F5'], notes['E5'], notes['F5'], notes['E5'], notes['D5'],
    notes['D3'], notes['F5'], notes['E5'], notes['F5'], notes['A4'],
    notes['G3'], 0, notes['D5'], notes['C5'], notes['D5'], notes['C5'], notes['B4'], notes['D5'],
    notes['C5'], notes['A3'], notes['E5'], notes['D5'], notes['E5'], notes['A4'],
    notes['F3'], notes['F5'], notes['E5'], notes['F5'], notes['E5'], notes['D5'],
    notes['D3'], notes['F5'], notes['E5'], notes['F5'], notes['A4'],
    notes['G3'], 0, notes['D5'], notes['C5'], notes['D5'], notes['C5'], notes['B4'], notes['D5'],
    notes['C5'], notes['B4'], notes['C5'], notes['D5'], notes['C5'], notes['D5'],
    notes['E5'], notes['D5'], notes['C5'], notes['B4'], notes['A4'], notes['F5'],
    notes['E5'], notes['E5'], notes['F5'], notes['E5'], notes['D5'],
    notes['E5'],
]

final_countdown_tempo = [
    1, 16, 16, 4, 4,
    1, 16, 16, 8, 8, 4,
    1, 16, 16, 4, 4,
    2, 4, 16, 16, 8, 8, 8, 8,
    4, 4, 16, 16, 4, 4,
    1, 16, 16, 8, 8, 4,
    1, 16, 16, 4, 4,
    2, 4, 16, 16, 8, 8, 8, 8,
    4, 16, 16, 4, 16, 16,
    8, 8, 8, 8, 4, 4,
    2, 8, 4, 16, 16,
    1,
]


def buzz(frequency, length):  # create the function "buzz" and feed it the pitch and duration)

    if frequency == 0:
        time.sleep(length)
        return
    period = 1.0 / frequency  # in physics, the period (sec/cyc) is the inverse of the frequency (cyc/sec)
    delayValue = period / 2  # calcuate the time for half of the wave
    numCycles = int(length * frequency)  # the number of waves to produce is the duration times the frequency

    for i in range(numCycles):  # start a loop from 0 to the variable "cycles" calculated above
        GPIO.output(buzzer_pin, True)  # set pin 27 to high
        time.sleep(delayValue)  # wait with pin 27 high
        GPIO.output(buzzer_pin, False)  # set pin 27 to low
        time.sleep(delayValue)  # wait with pin 27 low


def setup():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(buzzer_pin, GPIO.IN)
    GPIO.setup(buzzer_pin, GPIO.OUT)

def play(melody, tempo, pause, pace=0.800):
    for i in range(0, len(melody)):  # Play song

        noteDuration = pace / tempo[i]
        buzz(melody[i], noteDuration)  # Change the frequency along the song note

        pauseBetweenNotes = noteDuration * pause
        time.sleep(pauseBetweenNotes)

Project Code on Github

Credits

Ionuț-Alexandru Baltariu

Ionuț-Alexandru Baltariu

1 project • 5 followers
Student at Faculty of Automatic Control and Computer Engineering, Iași, Romania.
Pavăl Mihaela-Irina

Pavăl Mihaela-Irina

0 projects • 2 followers
Bejenariu Razvan-Marius

Bejenariu Razvan-Marius

0 projects • 2 followers

Comments