diff --git a/README.md b/README.md new file mode 100644 index 0000000..767a509 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +## piGarden + +Bash script to manage an irrigation system built with a Raspberry Pi + +## Official documentation + +Documentation of piGarden and build system irrigation with Raspberry Pi can be found on the [www.lejubila.net/](http://www.lejubila.net/) + +## License + +This script is open-sourced software under GNU GENERAL PUBLIC LICENSE Version 2 diff --git a/conf/piGarden.conf.example b/conf/piGarden.conf.example new file mode 100644 index 0000000..ec163ff --- /dev/null +++ b/conf/piGarden.conf.example @@ -0,0 +1,84 @@ +# Log file +LOG_FILE="/home/pi/piGarden/log/piGarden.log" +LOG_FILE_MAX_SIZE=1048576 # 1MB + +# Status directory +STATUS_DIR="/home/pi/piGarden/state" + +# Posizione gpio +GPIO="/usr/local/bin/gpio" + +# Posizione js +JQ="/usr/local/bin/jq" + +# Percorso curl +CURL="/usr/bin/curl" + +# Percors wc +WC="/usr/bin/wc" + +# Percorso gzip +GZIP="/bin/gzip" + +# Percors mv +MV="/bin/mv" + +# Id gpio usati per simulare il doppio deviatore con cui eseguire l'alimentazione alle elettrovalvole +SUPPLY_GPIO_1=2 +SUPPLY_GPIO_2=3 + +# Stato dei due gpio per impartire l'alimentazione positiva alle elettrovalvole (aperta) +SUPPLY_GPIO_POS=0 + +# Stato dei due gpio per impartire l'alimentazione negativa alle elettrovalvole (chiusa) +SUPPLY_GPIO_NEG=1 + +# Stato di ingresso da assegnare al gpio per chiudere il rele +RELE_GPIO_CLOSE=0 + +# Stato di ingresso da assegnare al gpio per aprire il rele +RELE_GPIO_OPEN=1 + +# Id del gpio usato per collegare il sensore di rilevamento pioggia +RAIN_GPIO=25 + +# Valore in ingresso sul gpio definito in RAIN_GPIO che indica lo stato di pioggia +RAIN_GPIO_STATE=0 + + + + + +# Numero totale di elettrovalvole +EV_TOTAL=6 + +# Definizione delle elettrovalvole +EV1_ALIAS="1" #"posteriore_sinistra" +EV1_GPIO=17 + +EV2_ALIAS="2" #"posteriore_destra" +EV2_GPIO=27 + +EV3_ALIAS="3" # +EV3_GPIO=22 + +EV4_ALIAS="4" # +EV4_GPIO=18 + +EV5_ALIAS="5" # +EV5_GPIO=23 + +EV6_ALIAS="6" # +EV6_GPIO=24 + +# Definisce l'api key e il luogo per recuperare lo stato meteo online +WUNDERGROUND_KEY="" +WUNDERGROUND_LOCATION="IY/Monsummano" # http://www.wunderground.com/weather/api/d/docs?d=resources/country-to-iso-matching&MR=1 + +# Blocca l'irrigazione se l'ultima pioggia rilevata online è avvenuta nell'ultima quantità di tempo inserita. +# Il tempo è espresso in secondi. Quindi inserendo 86400, se nelle ultime 24 ore ha pioviuto viene bloccata l'irrigazione. Inserendo il valore zero nonviene eseguito nessun controllo. +NOT_IRRIGATE_IF_RAIN_ONLINE=86400 + +# Il parametro è simile a quello precedente, il controllo però anziché essere fatto attingendo a wunderground, viene eseguito direttamente sul sensore se installato. Inserendo il valore zero nonviene eseguito nessun controllo. +NOT_IRRIGATE_IF_RAIN_SENSOR=86400 + diff --git a/debug/debug1.sh b/debug/debug1.sh new file mode 100644 index 0000000..8c65815 --- /dev/null +++ b/debug/debug1.sh @@ -0,0 +1,5 @@ +# Insert in this file debug code callable with +# garden.sh debug1 +# +echo "Debug 1" + diff --git a/debug/debug2.sh b/debug/debug2.sh new file mode 100644 index 0000000..e7de5e3 --- /dev/null +++ b/debug/debug2.sh @@ -0,0 +1,5 @@ +# Insert in this file debug code callable with +# garden.sh debug2 +# +echo "Debug 1" + diff --git a/log/.gitignore b/log/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/log/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/piGarden.sh b/piGarden.sh new file mode 100755 index 0000000..69609d8 --- /dev/null +++ b/piGarden.sh @@ -0,0 +1,482 @@ +#!/bin/bash +# +# Bash script to manage an irrigation system built with a Raspberry Pi +# Author: david.bigagli@gmail.com +# Url: https://github.com/lejubila/piGarden +# + +# +# Inizializza le elettrovalvole e l'alimentazione +# +function initialize { + + log_write "Run initialize" + + # Imposta l'alimentazione con voltaggio negativo e setta i gpio in scrittura + $GPIO -g write $SUPPLY_GPIO_1 0 + $GPIO -g write $SUPPLY_GPIO_2 0 + $GPIO -g mode $SUPPLY_GPIO_1 out + $GPIO -g mode $SUPPLY_GPIO_2 out + + # Elimina tutti gli stati delle elettrovalvole preesistenti + rm -f "$STATUS_DIR"/* + + # Inizializza i gpio delle elettrovalvole e ne chiude l'alimentazione + for i in $(seq $EV_TOTAL) + do + g=EV"$i"_GPIO + $GPIO -g write ${!g} RELE_GPIO_OPEN # chiude l'alimentazione all'elettrovalvole + $GPIO -g mode ${!g} out # setta il gpio nella modalita di scrittura + ev_set_state $i 0 + done + + # Chiude tutte le elettrovalvole + for i in $(seq $EV_TOTAL) + do + a=EV"$i"_ALIAS + al=${!a} + ev_close $al + done + + # Inizializza il sensore di rilevamento pioggia + if [ -n "$RAIN_GPIO" ]; then + $GPIO -g mode $RAIN_GPIO in + log_write "Rain sensor initialized" + else + log_write "Rain sensor not present" + fi + + log_write "End initialize" + +} + +# +# Commuta un elettrovalvola nello stato aperto +# $1 alias elettrovalvola +# $2 se specificata la string "force" apre l'elettrovalvola anche se c'é pioggia +# +function ev_open { + if [ ! "$2" = "force" ]; then + if [[ "$NOT_IRRIGATE_IF_RAIN_ONLINE" -gt 0 && -f $STATUS_DIR/last_rain_online ]]; then + local last_rain=`cat $STATUS_DIR/last_rain_online` + local now=`date +%s` + local dif=0 + let "dif = now - last_rain" + if [ $dif -lt $NOT_IRRIGATE_IF_RAIN_ONLINE ]; then + log_write "Solenoid '$1' not open for rain (online check)" + return + fi + fi + + check_rain_sensor + if [[ "$NOT_IRRIGATE_IF_RAIN_SENSOR" -gt 0 && -f $STATUS_DIR/last_rain_sensor ]]; then + local last_rain=`cat $STATUS_DIR/last_rain_sensor` + local now=`date +%s` + local dif=0 + let "dif = now - last_rain" + if [ $dif -lt $NOT_IRRIGATE_IF_RAIN_SENSOR ]; then + log_write "Solenoid '$1' not open for rain (sensor check)" + return + fi + fi + fi + + log_write "Solenoid '$1' open" + supply_positive + #gpio_alias2number $1 + ev_alias2number $1 + EVNUM=$? + ev_number2gpio $EVNUM + g=$? + $GPIO -g write $g $RELE_GPIO_CLOSE + sleep 1 + $GPIO -g write $g $RELE_GPIO_OPEN + ev_set_state $EVNUM 1 +} + +# +# Commuta un elettrovalvola nello stato chiuso +# $1 alias elettrovalvola +# +function ev_close { + log_write "Solenoid '$1' close" + supply_negative + #$GPIO_alias2number $1 + ev_alias2number $1 + EVNUM=$? + ev_number2gpio $EVNUM + g=$? + $GPIO -g write $g $RELE_GPIO_CLOSE + sleep 1 + $GPIO -g write $g $RELE_GPIO_OPEN + ev_set_state $EVNUM 0 +} + +# +# Imposta l'alimentazione delle elettrovalvole con voltaggio positivo +# +function supply_positive { + $GPIO -g write $SUPPLY_GPIO_1 $SUPPLY_GPIO_POS + $GPIO -g write $SUPPLY_GPIO_2 $SUPPLY_GPIO_POS +} + +# +# Imposta l'alimentazione delle elettrovalvole con voltaggio negativo +# +function supply_negative { + $GPIO -g write $SUPPLY_GPIO_1 $SUPPLY_GPIO_NEG + $GPIO -g write $SUPPLY_GPIO_2 $SUPPLY_GPIO_NEG +} + +# +# Scrive un messaggio nel file di log +# $1 log da scrivere +# +function log_write { + if [ -e "$LOG_FILE" ]; then + local actualsize=$($WC -c <"$LOG_FILE") + if [ $actualsize -ge $LOG_FILE_MAX_SIZE ]; then + $GZIP $LOG_FILE + $MV $LOG_FILE.gz $LOG_FILE.`date +%Y%m%d%H%M`.gz + fi + fi + + echo -e "`date`\t\t$1" >> $LOG_FILE +} + +# +# Imposta lo stgato di una elettrovalvola +# $1 numero dell'elettrovalvola +# $2 stato da scrivere +# +function ev_set_state { + echo "$2" > "$STATUS_DIR/ev$1" +} + +# +# Legge lo stato di una elettrovalvola +# +function ev_get_state { + return `cat "$STATUS_DIR/ev$1"` +} + +# +# Passando un alias di un'elettrovalvola recupera il numero gpio associato +# $1 alias elettrovalvola +# +function gpio_alias2number { + for i in $(seq $EV_TOTAL) + do + g=EV"$i"_GPIO + a=EV"$i"_ALIAS + gv=${!g} + av=${!a} + if [ "$av" == "$1" ]; then + return $gv + fi + done + + log_write "ERROR solenoid alias not found: $1" + exit 1 +} + +# +# Recupera il numero di una elettrovalvola in base all'alias +# $1 alias dell'elettrovalvola +# +function ev_alias2number { + for i in $(seq $EV_TOTAL) + do + a=EV"$i"_ALIAS + av=${!a} + if [ "$av" == "$1" ]; then + return $i + fi + done + + log_write "ERROR solenoid alias not found: $1" + exit 1 +} + +# +# Recupera il numero di gpio associato ad una elettrovalvola +# $1 numero elettrovalvola +# +function ev_number2gpio { +# echo "numero ev $1" + i=$1 + g=EV"$i"_GPIO + gv=${!g} +# echo "gv = $gv" + return $gv +} + +# +# Mostra lo stato di tutte le elettrovalvole +# +function ev_status_all { + for i in $(seq $EV_TOTAL) + do + a=EV"$i"_ALIAS + av=${!a} + ev_get_state $i + echo -e "$av: $?" + done +} + +# +# Mostra lo stato di una elettrovalvola +# $1 alias elettrovalvola +# +function ev_status { + ev_alias2number $1 + i=$? + ev_get_state $i + local state=$? + echo -e "$state" + return $state +} + +# +# Controlla se se piove tramite http://api.wunderground.com/ +# +function check_rain_online { + # http://www.wunderground.com/weather/api/d/docs?d=resources/phrase-glossary&MR=1 + $CURL http://api.wunderground.com/api/$WUNDERGROUND_KEY/conditions/q/$WUNDERGROUND_LOCATION.json > /tmp/check_rain_online.json + local weather=`cat /tmp/check_rain_online.json | $JQ -M ".current_observation.weather"` + local local_epoch=`cat /tmp/check_rain_online.json | $JQ -M -r ".current_observation.local_epoch"` + #echo $weather + if [ "$weather" = "null" ]; then + log_write "check_rain_online - failed read online data" + else + log_write "check_rain_online - weather=$weather, local_epoch=$local_epoch" + #if [[ "$weather" == *"Clear"* ]]; then + if [[ "$weather" == *"Rain"* ]]; then + #echo "ECCOMI!!!!!" + echo $local_epoch > "$STATUS_DIR/last_rain_online" + return $local_epoch + fi + fi +} + +# +# Controlla se se piove tramite sensore +# +function check_rain_sensor { + + if [ -n "$RAIN_GPIO" ]; then + local s=`$GPIO -g read $RAIN_GPIO` + if [ "$s" = "$RAIN_GPIO_STATE" ]; then + local local_epoch=`date +%s` + echo $local_epoch > "$STATUS_DIR/last_rain_sensor" + log_write "check_rain_sensor - now it's raining ($local_epoch)" + return $local_epoch + else + log_write "check_rain_sensor - now is not raining" + fi + else + log_write "Rain sensor not present" + fi + +} + +# +# Chiude tutte le elettrovalvole se sta piovendo +# Eseguie il controllo in tempo reale sul sensore hardware e sui dati dell'ultima chiamata eseguita online +# +function close_all_for_rain { + + local close_all=0 + local now=`date +%s` + + if [[ "$NOT_IRRIGATE_IF_RAIN_ONLINE" -gt 0 && -f $STATUS_DIR/last_rain_online ]]; then + local last_rain=`cat $STATUS_DIR/last_rain_online` + local dif=0 + let "dif = now - last_rain" + if [ $dif -lt $NOT_IRRIGATE_IF_RAIN_ONLINE ]; then + close_all=1 + fi + fi + + if [[ "$NOT_IRRIGATE_IF_RAIN_SENSOR" -gt 0 && -f $STATUS_DIR/last_rain_sensor ]]; then + local last_rain=`cat $STATUS_DIR/last_rain_sensor` + local dif=0 + let "dif = now - last_rain" + if [ $dif -lt $NOT_IRRIGATE_IF_RAIN_SENSOR ]; then + close_all=1 + fi + fi + + if [ "$close_all" = "1" ]; then + for i in $(seq $EV_TOTAL) + do + local a=EV"$i"_ALIAS + local al=${!a} + ev_status $al + local state=$? + #echo "$al = $state" + if [ "$state" = "1" ]; then + ev_close $al + log_write "close_all_for_rain - Close solenod '$al' for rain" + fi + done + fi + +} + +function close_all { + + for i in $(seq $EV_TOTAL) + do + local a=EV"$i"_ALIAS + local al=${!a} + ev_status $al + local state=$? + #echo "$al = $state" + if [[ "$state" = "1" || "$1" = "force" ]]; then + ev_close $al + log_write "close_all - Close solenod '$al' for rain" + fi + done + +} + +function list_alias { + + for i in $(seq $EV_TOTAL) + do + local a=EV"$i"_ALIAS + local al=${!a} + echo $al + done + +} + +function json_status { + local json="" + for i in $(seq $EV_TOTAL) + do + local a=EV"$i"_ALIAS + local av=${!a} + ev_status $av > /dev/null + local sv=$? + if [ -n "$json" ]; then + json="$json," + fi + json="$json\"$i\":{\"name\":\"$av\",\"state\":$sv}" + done + json="\"zones\":{$json}" + + json="{$json}" + + echo $json + + # {"zones":{"1":{"name":"Zona_1","state":1},"2":{"name":"Zona_2","state":0}}} + +} + + +function show_usage { + echo -e "Usage:" + echo -e "\t$NAME_SCRIPT init\t\tinitialize supply and solenoid in closed state" + echo -e "\t$NAME_SCRIPT open alias [force]\topen a solenoid" + echo -e "\t$NAME_SCRIPT close alias\t\tclose a solenoid" + echo -e "\t$NAME_SCRIPT list_alias\t\tview list of aliases solenoid" + echo -e "\t$NAME_SCRIPT ev_status alias\tshow status solenoid" + echo -e "\t$NAME_SCRIPT ev_status_all \tshow status solenoids" + echo -e "\t$NAME_SCRIPT json_status \tshow status in json format" + echo -e "\t$NAME_SCRIPT check_rain_online \tcheck rain from http://api.wunderground.com/" + echo -e "\t$NAME_SCRIPT check_rain_sensor \tcheck rain from hardware sensor" + echo -e "\t$NAME_SCRIPT close_all_for_rain \tclose all solenoid if it's raining" + echo -e "\t$NAME_SCRIPT close_all [force]\tclose all solenoid" + echo -e "\t$NAME_SCRIPT debug1 [parameter]|[parameter]|..]\tRun debug code 1" + echo -e "\t$NAME_SCRIPT debug2 [parameter]|[parameter]|..]\tRun debug code 2" +} + +function debug1 { + . "$DIR_SCRIPT/debug/debug1.sh" +} + +function debug2 { + . "$DIR_SCRIPT/debug/debug1.sh" +} + +DIR_SCRIPT=`dirname $0` +NAME_SCRIPT=${0##*/} +CONFIG_ETC="/etc/piGarden.conf" + +if [ -f $CONFIG_ETC ]; then + . $CONFIG_ETC +else + echo -e "Config file not found in $CONFIG_ETC" + exit 1 +fi + +case "$1" in + init) + initialize + ;; + + open) + if [ "empty$2" == "empty" ]; then + echo -e "Alias solenoid not specified" + fi + ev_open $2 $3 + ;; + + close) + if [ "empty$2" == "empty" ]; then + echo -e "Alias solenoid not specified" + fi + ev_close $2 + ;; + + list_alias) + list_alias + ;; + + ev_status) + ev_status $2force + ;; + + ev_status_all) + ev_status_all + ;; + + json_status) + json_status + ;; + + check_rain_online) + check_rain_online + ;; + + check_rain_sensor) + check_rain_sensor + ;; + + close_all_for_rain) + close_all_for_rain + ;; + + close_all) + close_all $2 + ;; + + debug1) + debug1 $2 $3 $4 $5 + ;; + + debug2) + debug2 $2 $3 $4 $5 + ;; + + + *) + show_usage + exit 1 + ;; +esac + + + diff --git a/state/.gitignore b/state/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/state/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file