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.