Smart Garden

Il progetto Smart Garden, nasce dall’idea di permettere a chiunque di poter coltivare attraverso la tecnica Idroponica in modo semplice nella propria abitazione, attraverso un sistema che permetta il monitoraggio e la gestione dei parametri vitali della pianta, tramite l’internet delle cose (IoT). 

La coltivazione Idroponica

La coltivazione idroponica è una tecnica antichissima, risalente ai Babilonesi ed agli Aztechi, e consiste in una tecnica di coltura in acqua, e quindi senza utilizzo di suolo, in cui la terra viene sostituita da un substrato inerte; nel nostro caso sarà utilizzata l’argilla espansa, immersa completamente in una soluzione di acqua e sostanze nutritive necessarie per la crescita delle piante.

Il grande vantaggio offerto dall’agricoltura idroponica è senza dubbio la possibilità di coltivare ovunque, riducendo gli spazi e al tempo stesso utilizzando un basso quantitativo di acqua (fino al 90% in meno rispetto alla coltivazione in suolo), oltre a poter essere condotta costantemente tutto il giorno e tutto l’anno senza il vincolo della stagionalità, rendendola così perfetta per l’ambiente casalingo.

Un altro vantaggio che valorizza questo tipo di coltivazione è la possibilità di fornire i nutrienti tramite l’acqua, evitando e diminuendo la presenza di sostanze nocive, oltre a ciò andremo ad evitare molte malattie legate a parassiti tipici delle coltivazioni tradizionali. Senza il terreno infatti  eviteremo la presenza di metalli pesanti e di animali pericolosi e erbe infestanti, diminuendo così anche l’uso di diserbanti, erbicidi e altre sostanze chimiche. 

Il progetto

Per il monitoraggio delle grandezze fisico/chimiche del sistema, che per rendere al meglio la coltura delle piante con il sistema idroponico devono essere controllate e mantenute entro range di valori, ci siamo serviti di un microcontrollore Arduino UNO, che attraverso una programmazione in linguaggio C++ ci permette di rilevare i valori di pH, TDS (Total Dissolved Solution) e Temperatura della soluzione acquosa e Temperatura, Umidità, Intensità luminosa e CO2 dell’ambiente.

Per sviluppare la nostra idea ci siamo quindi concentrati sulla ricerca dei singoli sensori che ci sarebbero serviti, studiando il loro funzionamento per poi andare a sviluppare i programmi utili per le fasi di calibrazione e di test dei singoli sensori. 

Nello schema funzionale riportato si può vedere l’utilizzo da parte di Arduino dei Sensori ad esso collegati in ingresso, ed il suo controllo attraverso i Relè, degli Attuatori utilizzati (Pompa ricircolo acqua e Luce a frequenza solare) per la gestione del sistema.

Il microcontrollore Arduino è quindi interfacciato attraverso un collegamento di tipo seriale con il microcomputer Raspberry che, con un programma in linguaggio Python, gestisce la lettura/scrittura dei dati del sistema su Database SQL installato su Server Web, per la visualizzazione online dei valori rilevati e la gestione dell’ambiente di coltivazione.

Il microcomputer ha anche il compito di gestire la comunicazione con l’utente attraverso un BOT di messaggistica Telegram, che ci permette di ricevere informazioni sul sistema e di controllarlo.

Tutti i dispositivi sono alimentati da un alimentatore con Vout di 12v per Arduino ed i Sensori, mentre il Raspberry utilizza un alimentatore di 5v – 3A.

La struttura

La realizzazione della struttura di Smart Garden è stata da noi pensata partendo da un contenitore in plastica sul quale abbiamo successivamente applicato un supporto progettato con CAD 3D e stampato in ABS, per l’alloggiamento dei Led per l’illuminazione e dei vari sensori.La progettazione è stata effettuata tramite l’applicazione Fusion 360 (Autodesk) e la stampa, utilizzando come slicer Flash Print, attraverso la stampante 3D Creator3 di Flash Forge..

La stampa è stata realizzata in ABS ed i pezzi che compongono il braccio di supporto dei Leds e dei Sensori di Temperatura e Umidità dell’ambiente e di CO2, sono assemblati ed al loro interno vi è anche ‘alloggiamento dei cavi.

I dispositivi elettronici utilizzati sono stati alloggiati nell’intercapedine tra il fondo del contenitore e la vaschetta contenente l’acqua

La vaschetta è alloggiata all’interno del contenitore e ad essa è applicato un supporto, anch’esso stampato in 3D, per i sensori immersi in acqua.
All’interno della vaschetta vi è anche una pompa ad immersione per il ricircolo dell’acqua.

Il contenitore è quindi chiuso nella parte superiore da un coperchio sul quale sono stati fatti sei fori per l’alloggiamento delle vaschette a rete che contengono le piante ed il supporto inerte

I rimanenti sensori di Temperatura e Umidità ambiente e di CO2 sono stati alloggiati, insieme ai Leds rossi e blu nella parte superiore del braccio stampato.
La colorazione e le proporzioni in numero dei leds sono studiate per poter avere una illuminazione che riproduca le stesse frequenze della luce solare, per permettere alle piante di poter crescere al meglio.

Arduino

Il ruolo dell’Arduino UNO nel progetto è quello di rilevare i dati dai Sensori, e quindi di inviarli sulla Seriale a Raspberry, secondo un protocollo che prevede il carattere ‘!’ come inizio e ‘/’ come fine sequenza (es: !7.80|513.05|23.00|60.57|27.00|38.80|428.57/, in modo che chi la riceve possa associare correttamente tutti i valori alle grandezze corrispondenti.

Inoltre ad ogni invio da parte del Raspberry della stringa contenente lo stato degli Attuatori come letti sul database (es: 0-1), attiva o meno ogni singolo dispositivo attraverso lo stadio di potenza interfacciato con il Relè.

Per permettere all’Arduino di essere più veloce possibile nell’attuazione dei comandi ricevuti, la struttura del programma in C++ è sviluppata utilizzando Threads (o meglio pseudo threads) con l’uso della libreria Arduino Thread, dove i due processi paralleli implementati agiscono uno per la lettura sensori e l’invio dei dati al Db e l’altro resta in ascolto al Raspberry per la gestione della Luce Leds e della Pompa di ricircolo.

Il Programma Arduino in C++

/* ************************************************
 * Programma lettura sensori Arduino UNO
 * e comunicazione Seriale con Raspberry:
 *  - Ph H2O
 *  - TDS H2O (Total Solution Dissolved)
 *  - Temperatura H2O (DS18B20)
 *  - Umidità e Temperatura amb (DHT11)
 *  - CO2 amb (MQ135) 
 *  - Luminosità amb (FR)
 * e gestione attuatori:
 *  - Luci LED
 *  - Pompa
 *
 * Stringa invio/ricezione dati al Raspberry:
 * !7.80|513.05|23.00|60.57|27.00|38.80|428.57/
 *
 * Collegamenti Arduino:
 * A0 TDS         13 LEDs
 * A1 CO2         12 Pompa
 * A2 Ph           4 DHT11
 * A3 FR           2 DS18B20
 * ********************************************** */ 

 #include <Thread.h>

// Inizializzazione
int prima_volta =  1;

//struttura dati
struct package
{
  float ph_h2o = 0.0;
  float tds_h2o = 0.0;
  float t_h2o = 0.0;
  float co2_amb = 0.0;
  float t_amb = 0.0;
  float h_amb = 0.0;
  float l_amb = 0.0;
};
typedef struct package Package;
Package dati;

//comunicazione seriale con Raspberry
int incoming;
int counter = 0;
char rxSeriale[5];

// Variabili Ph
const int pin_ph = A2;
//--------------------------------------------------------------------

//Librerie TDS
const int pin_TDS = A0;  //valori accetabili 300;500
#define VREF 5.0      // analog reference voltage(Volt) of the ADC  //libraries
#define SCOUNT  30    // sum of sample point 
//--------------------------------------------------------------------

//Librerie CO2
#include "MQ135.h" 
#define RLOAD 22.0    // I VALORI DEVONO ESSERE SETTATI NELLA LIBRERIA
#define RZERO 6.70    // I VALORèI DEVONO ESSERE SETTATI NELLA LIBRERIA
const int pin_CO2 = A1;
MQ135 gasSensor = MQ135(pin_CO2);
//--------------------------------------------------------------------

//Librerie DHT
#include "DHT.h"
#define DHTPIN 4     
#define DHTTYPE DHT11  
DHT dht(DHTPIN, DHTTYPE);
//--------------------------------------------------------------------

//Librerie Temp H2O DS18B20
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2 // Pin Arduino a cui colleghiamo il pin DQ del sensore
const int pinTemp = LED_BUILTIN; // Utilizzo del LED su scheda
OneWire oneWire(ONE_WIRE_BUS); // Imposta la connessione OneWire
DallasTemperature sensoreT(&oneWire); // Dichiarazione dell'oggetto sensore
//-----------------------------------------------------------------------

//Variabili TDS
int analogBuffer[SCOUNT];    // store the analog value in the array, read from ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0, copyIndex = 0;
float averageVoltage = 0, temperature = 25;
//--------------------------------------------------------------------

//Variabili CO2
int val; 
int sensorValue = 0; //0 
//--------------------------------------------------------------------

//Variabili Lux
int pinFR = A3;
int valoreFOTORES;
float Int_Luminosa;
int lux_Ambiente = 435;
int valoreFOTORES_AMBIENTE = 260; //da verificare a 435 lux
double m = -1.75; //da calibrare
//--------------------------------------------------------------------

//Variabili Pompa
const int pinPomp = 12;
static unsigned long tempo = 0;
unsigned long rit = 60000;
//--------------------------------------------------------------------

//Variabili LEDS
const int pinLeds= 13; // LOW->Leds ON; HIGH->Leds OFF

//------------------------------------------------------

//Threads
Thread Sensori_Th = Thread();
Thread Attuatori_Th = Thread();

// Funzioni di callback
void lettura_Sensori(){
Serial.println("---------------------- lettura_Sensori");  
  //---------------------------------------------------------------------------------------------------------
  // Sensore TDS H2O

  while(analogBufferIndex < SCOUNT) {
    analogBuffer[analogBufferIndex] = analogRead(pin_TDS);    //read the analog value and store into the buffer
    analogBufferIndex++;
  }
  //stampa valori per debug
  for(int i=0; i<analogBufferIndex; i++) {
    //Serial.print(analogBuffer[i]);
    //Serial.print("-");
    analogBufferTemp[i] = analogBuffer[i]; //copio il Buffer nel BufferTemp
  }
  //Serial.println("");

  analogBufferIndex = 0;

  averageVoltage = getMedianNum(analogBufferTemp, SCOUNT) * (float)VREF / 1024.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value
  float compensationCoefficient = 1.0 + 0.02 * (temperature - 25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
  float compensationVoltage = averageVoltage / compensationCoefficient; //temperature compensation
  float valTDScalc = (133.42 * compensationVoltage * compensationVoltage * compensationVoltage - 255.86 * compensationVoltage * compensationVoltage + 857.39 * compensationVoltage) * 0.5; //convert voltage value to tds value
  if(isnan(valTDScalc)) dati.tds_h2o = 0.0;
  else dati.tds_h2o = valTDScalc;

  //Serial.print("TDS H2O--> "); Serial.print(dati.tds_h2o); Serial.println("ppm");
  
  //--------------------------------------------------------------------------------------------------------
  // Sensore DHT (LOOP)
    
  dati.h_amb = dht.readHumidity(); //valore Umidità in %
  dati.t_amb = dht.readTemperature(); //valore Temperatura in °C
  
  //Serial.print("Umidita' ambiente--> "); Serial.print(dati.h_amb); Serial.println("%");
  //Serial.print("Temperatura ambiente--> "); Serial.print(dati.t_amb); Serial.println("C ");
  //delay(1000);
  //---------------------------------------------------------------------------------------------------------------

  // Sensore CO2

  val = analogRead(pin_CO2);  
  float zero = gasSensor.getRZero();  
  dati.co2_amb = gasSensor.getPPM(); 
  //Serial.print ("CO2 ambiente--> "); Serial.print (dati.co2_amb); Serial.print("ppm"); 
  //Serial.print (" (rzero: "); Serial.print (zero); Serial.println (")");
  //delay(1000);

  //------------------------------------------------------------------------------

  //Sensore LUX

  valoreFOTORES = analogRead(pinFR);                                                                                                           
  dati.l_amb = (valoreFOTORES - 1023)/m;                                                     
  //Serial.print("Intensita' Luminosa ambiente--> "); Serial.print(dati.l_amb); Serial.print("lux"); 
  //Serial.print(" (Valore FR dell'ambiente: "); Serial.print(valoreFOTORES); Serial.println(")");                                                                           
  //delay(1000);

  //------------------------------------------------------------------------------

  // Sensore Temp H2O

  sensoreT.requestTemperatures(); // richiesta lettura temperatura
  dati.t_h2o = sensoreT.getTempCByIndex(0);

  //Serial.print("Temperatura H2O--> "); Serial.print(dati.t_h2o); Serial.println("C");
  //delay(1000);

  //------------------------------------------------------------------------------
  
  //Sensore Ph

  float adc = analogRead(pin_ph);
  //Serial.println(adc);
  dati.ph_h2o = -0.043*adc + 40.46;   
  //Serial.print("Livello Ph--> "); Serial.println(dati.ph_h2o);                     
  //delay(1000);

  //------------------------------------------------------

  //invio al Raspberry dati senori
  invioSeriale(int(dati.ph_h2o),int(dati.tds_h2o),int(dati.t_h2o),int(dati.co2_amb),int(dati.t_amb),int(dati.h_amb),int(dati.l_amb)); //invio seriale valori Sensori

}

void lettura_Attuatori(){
  Serial.println("lettura_Attuatori");
	// Inizializzazione attuatori
  if(prima_volta == 1) {
    digitalWrite(pinPomp, HIGH);//spegnimento Pompa
    digitalWrite(pinLeds, HIGH);//spegnimento Leds    
    prima_volta = 0;
  }
  //------------------------------------------------------------------------------
 
 //Pompa H2O
/*
  if(millis() >= (tempo + rit))
  {
      digitalWrite(pinPomp, LOW);
      delay(4000); // --> Tempo per cui rimane accesa la pompa.
      digitalWrite(pinPomp, HIGH);
      tempo = millis();
  }
*/
  //lettura della risposta seriale di Raspberry per Attuatori
  if (Serial.available() > 0) {
    while(counter<=3){
      incoming = Serial.read();
      rxSeriale[counter] = incoming;
      counter++;
    }
    counter=0;
    Serial.println("------------------------");    
    Serial.print("rxSeriale[]: ");Serial.print(rxSeriale[0]);Serial.print(rxSeriale[2]);
    // stringa da passare con Raspberry: 'L-0-1'
    if(rxSeriale[0]=='1') digitalWrite(pinPomp, LOW); //accensione Pompa
    if(rxSeriale[0]=='0') digitalWrite(pinPomp, HIGH); //spegnimento Pompa
    if(rxSeriale[2]=='1') digitalWrite(pinLeds, LOW); //accensione Illuminazione
    if(rxSeriale[2]=='0') digitalWrite(pinLeds, HIGH); //spegnimento Illuminazione 
  }
}

void invioSeriale(int Ph_h2o,int TDS_h2o,int T_h2o,int CO2_amb,int T_amb,int H_amb,int L_amb){ //protocollo di invio: !H|L/
  Serial.print("!");
  Serial.print(dati.ph_h2o);
  Serial.print("|");
  Serial.print(dati.tds_h2o);
  Serial.print("|");
  Serial.print(dati.t_h2o);
  Serial.print("|");
  Serial.print(dati.co2_amb);
  Serial.print("|");
  Serial.print(dati.t_amb);
  Serial.print("|");
  Serial.print(dati.h_amb);
  Serial.print("|");
  Serial.print(dati.l_amb);
  Serial.println("/");
}

// Sensore TDS 
int getMedianNum(int bArray[], int iFilterLen)
{
  int bTab[iFilterLen];
  for (byte i = 0; i < iFilterLen; i++)
    bTab[i] = bArray[i];
  int i, j, bTemp;
  for (j = 0; j < iFilterLen - 1; j++)
  {
    for (i = 0; i < iFilterLen - j - 1; i++)
    {
      if (bTab[i] > bTab[i + 1])
      {
        bTemp = bTab[i];
        bTab[i] = bTab[i + 1];
        bTab[i + 1] = bTemp;
        
      }
    }
  }
  if ((iFilterLen & 1) > 0)
    bTemp = bTab[(iFilterLen - 1) / 2];
  else
    bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
  return bTemp;
}


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

	pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pin_TDS, INPUT);    //  TDS Pin
  pinMode(pin_CO2, INPUT);    //  CO2 Pin
  dht.begin();                //  DHT Pin
  sensoreT.begin();           //  Inizializzazione del sensore T
  pinMode(pinTemp, OUTPUT); 
  pinMode(pinPomp, OUTPUT);
  pinMode(pinLeds, OUTPUT);

	Attuatori_Th.onRun(lettura_Attuatori);
	Attuatori_Th.setInterval(500);
  Sensori_Th.onRun(lettura_Sensori);
	Sensori_Th.setInterval(2000);
}

void loop(){

	if(Sensori_Th.shouldRun())
		Sensori_Th.run();

	if(Attuatori_Th.shouldRun())
		Attuatori_Th.run();
}

Raspberry

Il microcomputer Raspberry Pi3 B+ utilizza, con un programma in linguaggio Python, sviluppato anche in questo caso implementando alcuni threads che in modo parallelo gestiscono:

  • l’interfacciamento con Arduino attraverso la comunicazione seriale
  • l’inserimento dei dati delle grandezze fisico/chimiche monitorate sul database posizionato su Server Web
  • la lettura dello stato degli attuatori memorizzato sul DB e quindi l’invio ad Arduino per la loro gestione
  • il BOT di Telegram per il monitoraggio e controllo del sistema attraverso la messaggistica.

La connessione alla rete da parte di Raspberry avviene attraverso il WiFi, ed il programma python va in esecuzione in modo automatico all’avvio del sistema.
Nella configurazione del sistema operativo Linux installato è stato anche attivato un Server VNC che ci permette di collegarci da remoto in rete locale da qualunque computer con un Client VNC.

Il programma Raspberry in Python

#!/usr/bin/env python

import os
import time
import datetime
import serial
import requests
import telepot
from sys import exit
import cv2
import numpy as np
import threading
import mysql.connector as mysql
from mysql.connector import Error
import socket

global T_amb
global H_amb
global L_amb
global CO2_amb
global T_h2o
global Ph_h2o
global TDS_h2o
global H_temp
global L_temp
global valori
global array_valori
global string_valori
global r_inserimento_string
global r_inserimento_bytes
global r_lettura_string
global r_lettura_bytes
global url
global arduino
global ctrl
global id_a
global chat_id
global bot
global request
global db_connection_Sensori
global db_connection_Attuatori

id_a = [160285307] #BOT id

T_amb = 0
H_amb = 0
L_amb = 0
CO2_amb = 0
T_h2o = 0
Ph_h2o = 0
TDS_h2o = 0
H_temp = 0
L_temp = 0
counter = 0
valori = []
array_valori = []
string_valori = []
r_string = ''
r_bytes = ''
url = ''
ctrl = 0
T_pompa = 0
flag_L = 0
flag_TDS = 0
flag_pH = 0
primo_msg_L = 0
primo_msg_TDS = 0
primo_msg_TDS2 = 0
primo_msg_pH = 0
primo_msg_pH2 = 0
chat_id = 160285307

#set seriale Arduino
arduino = serial.Serial(
    port='/dev/ttyACM0',
    baudrate = 9600,
    parity = serial.PARITY_NONE,
    stopbits = serial.STOPBITS_ONE,
    bytesize = serial.EIGHTBITS,
    timeout = 1
)

def handle(msg):
    global bot
    global id_a
    global L_amb
    global chat_id
    global valori
    #chat_id = msg['chat']['id']
    #print('chat_id:',chat_id) # 160285307
    command = msg['text']
    sender = msg['from']['id']
    if sender in id_a:
        if command == 'Sensori':
            bot.sendMessage(chat_id, 'Ph_h2o=' + valori[0].decode('ASCII') + '; TDS_h2o=' + valori[1].decode('ASCII') + 'ppm; T_h2o=' + valori[2].decode('ASCII') + 'C°; CO2_amb=' + valori[3].decode('ASCII') + 'C°; T_amb=' + valori[4].decode('ASCII') + 'C°; H_amb=' + valori[5].decode('ASCII') + '%; L_amb=' +valori[6].decode('ASCII') + 'lux')
        elif command == 'Ph_h2o':
            bot.sendMessage(chat_id, 'Ph_h2o=' + valori[0].decode('ASCII'))
        elif command == 'Tds_h2o':
            bot.sendMessage(chat_id, 'TDS_h2o=' + valori[1].decode('ASCII') + 'ppm')
        elif command == 'T_h2o':
            bot.sendMessage(chat_id, 'T_h2o=' + valori[2].decode('ASCII') + 'C°')
        elif command == 'Co2_amb':
            bot.sendMessage(chat_id, 'CO2_amb=' + valori[3].decode('ASCII') + 'ppm')
        elif command == 'T_amb':
            bot.sendMessage(chat_id, 'T_amb=' + valori[4].decode('ASCII') + 'C°')
        elif command == 'H_amb':
            bot.sendMessage(chat_id, 'H_amb=' + valori[5].decode('ASCII') + '%')
        elif command == 'L_amb':
            bot.sendMessage(chat_id, 'L_amb=' + valori[6].decode('ASCII') + 'lux')
        elif command == 'Accendi pompa':
            # Definisco le query
            query_attiva_attuatore = 'UPDATE smart_garden_attuatori SET H=1 WHERE id=1'
            # query stato Attuatori
            execute_query(db_connection_Attuatori,query_attiva_attuatore)
            bot.sendMessage(chat_id, 'Pompa accesa: temporizzazione 10s')
        elif command == 'Spegni pompa':
            # Definisco le query
            query_attiva_attuatore = 'UPDATE smart_garden_attuatori SET H=0 WHERE id=1'
            # query stato Attuatori
            execute_query(db_connection_Attuatori,query_attiva_attuatore)
            bot.sendMessage(chat_id, 'Pompa spenta')
        elif command == 'Accendi illuminazione':
            # Definisco le query
            query_attiva_attuatore = 'UPDATE smart_garden_attuatori SET L=1 WHERE id=1'
            # query stato Attuatori
            execute_query(db_connection_Attuatori,query_attiva_attuatore)
            bot.sendMessage(chat_id, 'Illuminazione accesa')
        elif command == 'Spegni illuminazione':
            # Definisco le query
            query_attiva_attuatore = 'UPDATE smart_garden_attuatori SET L=0 WHERE id=1'
            # query stato Attuatori
            execute_query(db_connection_Attuatori,query_attiva_attuatore)
            bot.sendMessage(chat_id, 'Illuminazione spenta')
        elif command == 'Come stai?':
            if float(L_amb) < 300:
                bot.sendMessage(chat_id, 'Ho poca luce!')
            else:
                bot.sendMessage(chat_id, 'Sto bene, grazie')
        elif command == 'Reboot':
            os.system('sudo shutdown -r now')
        elif command == 'Test':
            bot.sendMessage(chat_id, 'Ok')
        else:
            bot.sendMessage(chat_id, 'Non sei autorizzato a darmi ordini!')
            
def execute_query(connection, query):
    connection.connect()
    cursor = connection.cursor()
    try:
        cursor.execute(query)
        connection.commit()
        print("Query successful")
    except Error as err:
        print(f"Error: '{err}'")

def read_query(connection, query):
    connection.connect()
    cursor = connection.cursor()
    result = None
    try:
        cursor.execute(query)
        result = cursor.fetchall()
        return result
    except Error as err:
        print(f"Error: '{err}'")
        
def get_ip_address():
    ip_address = '';
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8",80))
    ip_address = s.getsockname()[0]
    s.close()
    return ip_address

# -------------------------------------------------


class Telegram (threading.Thread):  
    def __init__(self):
        threading.Thread.__init__(self)
        self._running = True

    def terminate(self):  
        self._running = False
        
    def run(self):
        #Telegram
        global bot
        global chat_id
        global L_amb
        global T_pompa
        global flag_L
        global flag_TDS
        global flag_pH
        global primo_msg_L
        global primo_msg_TDS
        global primo_msg_TDS2
        global primo_msg_pH
        global primo_msg_pH2
        
        bot = telepot.Bot('5882672680:AAGxAPIE4x_5PgymXSUsAB30r863rd5do-g') #TelegramBotToken Smart_Garden_ITT
        bot.message_loop(handle)
        print('Sono in ascolto per Telegram..')
        
        while True:
            # Temporizzazione Pompa
            if T_pompa == 1:
                print('T_pompa=',T_pompa)
                bot.sendMessage(chat_id, 'Pompa spenta')
                # Definisco le query
                query_attiva_attuatore = 'UPDATE smart_garden_attuatori SET H=0 WHERE id=1'
                # query stato Attuatori
                execute_query(db_connection_Attuatori,query_attiva_attuatore)
                T_pompa = 0
                print('T_pompa=',T_pompa)
            # controllo illuminazione   
            if flag_L == 1 and primo_msg_L == 0:
                bot.sendMessage(chat_id, 'Illuminazione insufficiente')
                primo_msg_L = 1
                flag_L = 0
            # controllo TDS
            if flag_TDS == 1 and primo_msg_TDS == 0:
                bot.sendMessage(chat_id, 'Sali insufficienti')
                primo_msg_TDS = 1
                flag_TDS = 0
            elif flag_TDS == 2 and primo_msg_TDS2 == 0:
                bot.sendMessage(chat_id, 'Sali eccessivi')
                primo_msg_TDS2 = 1
                flag_TDS = 0
            # controllo TDS
            if flag_pH == 1 and primo_msg_pH == 0:
                bot.sendMessage(chat_id, 'pH basso')
                primo_msg_pH = 1
                flag_pH = 0
            elif flag_pH == 2 and primo_msg_pH2 == 0:
                bot.sendMessage(chat_id, 'pH alto')
                primo_msg_pH2 = 1
                flag_pH = 0
                
class Arduino_sensori (threading.Thread):  
    def __init__(self):
        threading.Thread.__init__(self)
        self._running = True

    def terminate(self):  
        self._running = False  

    def run(self):
        global T_amb
        global H_amb
        global L_amb
        global CO2_amb
        global T_h2o
        global TDS_h2o
        global Ph_h2o
        global valori
        global array_valori
        global string_valori
        global arduino
        global ctrl
        global counter
        global flag_L
        global flag_TDS
        global flag_pH
        global primo_msg_L
        global primo_msg_TDS
        global primo_msg_TDS2
        global primo_msg_pH
        global primo_msg_pH2
        
        while self._running:
            # Gestione invio dati al Raspberry
            # aspetto che ci sia comunicazione da parte di Arduino
            while arduino.in_waiting:
                RxData = arduino.read()
                #print(RxData,end='')
                if RxData==b'!':
                    ctrl=1 #inizio valori inviati
                    del array_valori[:] #cancello tutti gli elementi dell'array
                if RxData==b'/':
                    ctrl=0 #fine valori inviati
                if ctrl==1 and RxData!=b'!':
                    array_valori.append(RxData)
                if ctrl==0 and counter==0:
                    string_valori=b''.join(array_valori) #stringa ottenuta dall'array_valori
                    valori=string_valori.split(b'|') #array dei valori di ph_acq,tds_acq,T_acq,CO2_amb,T_amb,H_amb,L_amb ricevuti da Arduino
            
            #print("arduino->",end='')
            #print(valori)
            
            # Verifico se il valore della illuminazione è insufficiente
            if valori != b'' and float(valori[6]) < 200: flag_L = 1
            else:
                flag_L = 0
                primo_msg_L = 0
            # Verifico il valore della TDS
            if valori != b'' and float(valori[1]) < 300: flag_TDS = 1
            elif valori != b'' and float(valori[1]) > 500: flag_TDS = 2
            else:
                flag_TDS = 0
                primo_msg_TDS = 0
                primo_msg_TDS2 = 0
            # Verifico il valore del pH 
            if valori != b'' and float(valori[0]) < 5.5: flag_pH = 1
            elif valori != b'' and float(valori[0]) > 6.5: flag_pH = 2
            else:
                flag_pH = 0
                primo_msg_pH = 0
                primo_msg_pH2 = 0
            

            time.sleep(1)
            counter=0
            
class Request_SQL_Sensori (threading.Thread):  
    def __init__(self):
        threading.Thread.__init__(self)
        self._running = True

    def terminate(self):  
        self._running = False  

    def run(self):
        global T_amb
        global H_amb
        global L_amb
        global CO2_amb
        global T_h2o
        global TDS_h2o
        global Ph_h2o
        global valori
        global db_connection_Sensori
        
        while self._running:
        
            # Ritardo all'esecuzione
            time.sleep(60)
                    
            # Definisco le query
            query_select_valori = """
            SELECT *
            FROM smart_garden
            """ 
            # Vars
            n_max_records = 30

            # Se Arduino ha passato i valori e sono corretti
            print('Sensori:',valori[0].decode('ASCII'),valori[1].decode('ASCII'),valori[2].decode('ASCII'),valori[3].decode('ASCII'),valori[4].decode('ASCII'),valori[5].decode('ASCII'),valori[6].decode('ASCII'))
            #print(type(valori[2].decode('ASCII'))) #str
            if len(valori) != 0: # and valori[2].decode('ASCII') != 'inf':
                Ph_h2o = valori[0].decode('ASCII') # Decodifica ASCII da StrByte a Str
                TDS_h2o = valori[1].decode('ASCII')
                T_h2o = valori[2].decode('ASCII')
                CO2_amb = valori[3].decode('ASCII')
                T_amb = valori[4].decode('ASCII')
                H_amb = valori[5].decode('ASCII')
                L_amb = valori[6].decode('ASCII')
                # Inserimento valori sul DB
                # -------------------------
                P_H = Ph_h2o
                TDS_H = TDS_h2o
                T_H = T_h2o
                CO2_A = CO2_amb
                T_A = T_amb
                H_A = H_amb
                L_A = L_amb
                print('Sensori:',Ph_h2o,TDS_h2o,T_h2o,CO2_amb,T_amb,H_amb,L_amb)
                _d = datetime.datetime.now()
                data = str(_d.day)+'/'+str(_d.month)+'/'+str(_d.year)
                ora = str(_d.hour)+':'+str(_d.minute)
                #print(data,ora)

                list_valori = read_query(db_connection_Sensori,query_select_valori)
                n_records = len(list_valori)
                print(n_records)
                if n_records < n_max_records:
                    cron = n_records + 1
                    query_insert = 'INSERT INTO smart_garden (cron, ph_acq, tds_acq, t_acq, co2_amb, t_amb, h_amb, l_amb, data, ora) VALUES (' + str(
                        cron) + ',' + str(
                        P_H) + ',' + str(TDS_H) + ',' + str(T_H) + ',' + str(CO2_A) + ',' + str(T_A) + ',' + str(H_A) + ',' + str(
                        L_A) + ',' + '"' + data + '"' + ',' + '"' + ora + '"' + ')'
                    print('query_insert:',query_insert)
                    insert_valori = execute_query(db_connection_Sensori, query_insert)
                else: # Se il numero dei records == 30
                    cron = n_records
                    # Elimino il record più vecchio
                    query_delete = 'DELETE FROM smart_garden WHERE cron = 1'
                    delete_record = execute_query(db_connection_Sensori, query_delete)

                    # Modifico i cron
                    # Prelevo i cron dei records
                    query_select_records = 'SELECT * FROM smart_garden'
                    query_records = read_query(db_connection_Sensori, query_select_records)
                    # Diminuisco i cron attuali di 1
                    for i in range(len(query_records)):
                        # print(query_records[i])
                        id_record = query_records[i][0]
                        new_cron = query_records[i][1] - 1
                        print('id_record:',id_record,'new_cron:', new_cron)
                        # Update dei nuovi valori dei cron
                        update_cron = 'UPDATE smart_garden SET cron=' + str(new_cron) + ' WHERE id=' + str(id_record) + ''
                        query_update_cron = execute_query(db_connection_Sensori, update_cron)
                    # Inserisco nuovo record
                    query_insert = 'INSERT INTO smart_garden (cron, ph_acq, tds_acq, t_acq, co2_amb, t_amb, h_amb, l_amb, data, ora) VALUES (' + str(
                        cron) + ',' + str(
                        P_H) + ',' + str(TDS_H) + ',' + str(T_H) + ',' + str(CO2_A) + ',' + str(T_A) + ',' + str(H_A) + ',' + str(
                        L_A) + ',' + '"' + data + '"' + ',' + '"' + ora + '"' + ')'
                    print('query_insert:',query_insert)
                    insert_valori = execute_query(db_connection_Sensori, query_insert)

class Request_SQL_Attuatori (threading.Thread):  
    def __init__(self):
        threading.Thread.__init__(self)
        self._running = True

    def terminate(self):  
        self._running = False  

    def run(self):
        global r_string
        global r_bytes
        global arduino
        global db_connection_Attuatori
        
        while self._running:
            
            # Definisco le query
            query_select_attuatori = 'SELECT * FROM smart_garden_attuatori WHERE id = 1'
            
            # Lettura stato degli Attuatori
            # -----------------------------
            # query stato Attuatori
            query_list_attuatori = read_query(db_connection_Attuatori,query_select_attuatori)
            H = query_list_attuatori[0][1]
            L = query_list_attuatori[0][2]
            print('Attuatori:',H,L)
            
            # Preparazione stringa da inviare ad Arduino
            r_string=''+str(H)+'-'+str(L)+''
            r_bytes=r_string.encode()
            #print(r_bytes)
            # invio ad Arduino gli stati degli attuatori
            arduino.write(r_bytes)
            
            # Ritardo all'esecuzione
            time.sleep(1)
    
if __name__ == "__main__":
    try:
        # enter your server IP address/domain name
        HOST = "185.114.108.129" # or "domain.com"
        # database name, if you want just to connect to MySQL server, leave it empty
        DATABASE = "progetti"
        # this is the user you create
        USER = "progetti_user"
        # user password
        PASSWORD = "*T4n^R2uh"
        # connect to MySQL server
        db_connection_Sensori = mysql.connect(host=HOST, database=DATABASE, user=USER, password=PASSWORD)
        db_connection_Attuatori = mysql.connect(host=HOST, database=DATABASE, user=USER, password=PASSWORD)
        print("db_connection_Sensori connected to:", db_connection_Sensori.get_server_info())
        print("db_connection_Attuatori connected to:", db_connection_Attuatori.get_server_info())
              
        # Leggo indirizzo IP Raspberry e SSID Rete WiFi
        IP = get_ip_address()
        SSID = os.popen("sudo iwgetid -r").read()
        print('IP:',IP,'SSID',SSID)
        # Definisco le query
        query_IP = 'UPDATE smart_garden_attuatori SET IP=' + '"' + str(IP) + '"' + ' WHERE id=1'
        query_SSID = 'UPDATE smart_garden_attuatori SET SSID=' + '"' + str(SSID) + '"' + ' WHERE id=1'
        # query stato Attuatori
        execute_query(db_connection_Attuatori,query_IP)
        execute_query(db_connection_Attuatori,query_SSID)
        
        # Gestione threads
        # ------------------------------
        # Create new threads
        Telegram_thread = Telegram()
        Request_SQL_Sensori_thread = Request_SQL_Sensori()
        Request_SQL_Attuatori_thread = Request_SQL_Attuatori()
        Arduino_sensori_thread = Arduino_sensori()

        # Start new Threads
        Telegram_thread.start()
        Request_SQL_Sensori_thread.start()
        Request_SQL_Attuatori_thread.start()
        Arduino_sensori_thread.start()
        
        #while True:
        #    pass
            
    # per uscita con interrupt da tastiera dal programma (control+C)
    except KeyboardInterrupt:
        exit()

Telegram

Il BOT, cioè l’applicazione creata per permettere al programma python di interagire con la chat Telegram, è stato creato con BotFather, il quale, una volta impostato, rilascia un Token univoco che, solo per chi lo conosce, ne permette l’utilizzo (Come creare un BOT di Telegram).

I messaggi codificati all’interno del programma permettono di monitorare i valori letti dai vari Sensori, di attivare o meno l’Illuminazione e la Pompa di ricircolo dell’acqua.
E’ anche possibile porre alcune domande sullo ‘stato della propria salute al sistema‘, che risponderà in base al valore delle grandezze rilevate.

In modo automatico sarà invece il sistema stesso ad inviare messaggi di allerta quando i valori dei parametri essenziali escono dal range di confort.

Monitoraggio Web

Per poter monitorare i valori del sistema acquisiti dai sensori è stata predisposto in link Web che visualizza i dati presenti sul database.
Questo file PHP esegue le query sulla tabelle del DB e formatta la visualizzazione degli stessi.