Homepage

Herbert Lischka


Linux

Inhalt

Im folgenden stelle ich mein Linux-System vor:

Raspberry Pi

Ziel und Zweck:
Ich habe mir einen Raspberry Pi II zugelegt, um damit die
Bewässerung meines Gartens zu automatisieren.

Das will ich mit folgenden Komponenten erreichen:

  • Raspberry Pi II Hardware mit 8 über I²C angeschlossene Relais
  • Eine Wasserverteilerbox von Gardena mit 8 Stück 24V~ Magnetventilen für die einzelnen Bewässerungsbereiche
  • pilight als zentrale Softwarekomponente zur Steuerung der Hardware mit einer HTML-GUI
  • die Pilight-App, damit ich mit dem Tablet und einer Android-App die Steuerung bequemer erledigen kann
  • eine Logik, die die Entscheidung trifft "heute giessen"

die grobe Struktur von pilight

  Hardwarethread <--> JSON Data <--> Protocolthread <--> JSON Data <--> GUI 
		

Ein Hardwarethread liest oder schreibt auf/von GPIO's des Raspberry Pi, interpretiert die Daten und liefert/empfängt die Daten in strukturierter Form als JSON Konstrukte.
Ein zugehöriger Protokollthread prüft die Vollständigkeit der Daten im Vergleich zur eingestellten Konfiguration und liefert die Daten wiederum als JSON Konstrukt an die GUI/den Hardwarethread.
Die zugehörige GUI stellt die Daten dar und nimmt Eingaben des Benutzers entgegen, die dann wiederum an den Protokollthread ausgeliefert werden...

Die Entscheidungslogik

Die Entscheidung giessen oder nicht hängt von mehreren Kriterien ab:

  • wie viel hat es innerhalb der vergangenen 48 Stunden geregnet?
  • wie warm ist es heute?
  • besteht Frostgefahr?

Am vertracktesten ist es, die akkumulierte Regenmenge der vergangenen 48 Stunden festzustellen.
Ich habe dazu die Wetterdaten der Agrarmetereologie Baden-Württemberg angezapft, die in meinem Wohnort eine Wetterstation betreiben.
Ein perl-Script liest die Seiten aus, sammelt die Daten und liefert sie bei Aufruf in Form eines JSON-Konstruktes aus.
Bei der Datenabfrage wird auch die Entscheidung generiert giessen ja oder nein.

#!/usr/bin/perl -w
#
# liest aus der Wetterstation in Markelsheim die Daten aus
#

use LWP::Simple;
use HTML::Strip;
use Cpanel::JSON::XS qw(encode_json decode_json);


# ------------ globals --------------------------
my ($option) = @ARGV;
my $debug = 0;
my $json = 0;
my $url = 'http://217.5.174.45/cgi-bin/wetterakt.pl?c=2&sid=141&tid=1,99,10,11,99,15,16,99,21&ts=1';
my $file = '/tmp/wetter.txt';
my $logfile = '/var/log/getNiederschlag.log';
my $hs = HTML::Strip->new();

# ------------- main program ------------------
parseOptions();

my $content = readContent( $url );
my @todayRain = parseNiederschlag( $content );
my @actualTemperature = parseTemperatur( $content );

my $Std = getIndexOfLast23oClock( getArrayOfDateTimeStamps( $content ) );

# set offset in url to get next time value of 23 o'clock of previous day
$content = readContent( $url . "&sp=$Std" );
my @yesterdayRain = parseNiederschlag( $content );

my @lines = readFile( $file );
my ($min, $max) = parseMinMaxTemperatures(@lines);

my $beregnen = beregnen($yesterdayRain[0], $todayRain[0], $actualTemperature[0], $min, $max);
writeToFile($logfile, 'Regenmenge gestern ', $yesterdayRain[0], ' mm, Regenmenge heute ', $todayRain[0],
 ' mm, Aktuelle Temperatur ', $actualTemperature[0], ' °C, minimale Temperatur heute ', $min,
 ' °C, maximale Temperatur ', $max, ' °C, Beregnen (1=ja, 0=nein): ', $beregnen) if ($debug!=4);
exit($beregnen) if ($json == 0);
json($yesterdayRain[0], $todayRain[0], $actualTemperature[0], $min, $max, $beregnen) if ($json == 1);
exit(0);
# ------------- end of main program ------------------

# ------------ functions ------------------------
sub parseOptions {
  if (defined $option) {
    if (($option eq "-h") or ($option eq "--help")) {
      print "Usage: $0 [-h | --help for help or -d | --debug for debugging or -v[vv] | --verbose[=2..3] for verbosity]\n";
      print "     : -d | --debug; don't write to file, but give max verbosity\n";
      print "     : -j | --json; give results in json\n";
      print "     : -v | --verbose; give results only\n";
      print "     : -vv | --verbose=2; give results and steps between\n";
      print "     : -vvv | --verbose=3; give all data and max verbosity\n\n";
      exit;
    }
    if (($option eq "-d") or ($option eq "--debug")) {
      $debug = 4; # don't write to file, but give max verbosity
    }
    if (($option eq "-v") or ($option eq "--verbose")) {
      $debug = 1; # results only
    }
    if (($option eq "-j") or ($option eq "--json")) {
      $debug = 0; # results only in json format
      $json = 1;
    }
    if (($option eq "-vv") or ($option eq "--verbose=2")) {
      $debug = 2; # results and steps between
    }
    if (($option eq "-vvv") or ($option eq "--verbose=3")) {
      $debug = 3; # all data, max verbosity
    }
  }
}
sub stripHTMLandNonPrintables {
  my $arg = shift;
  $arg = $hs->parse( $arg );
  $arg =~ s/[^\x00-\x7f]//g;
  print "stripHTMLandNonPrintables: -->$arg<--\n\n" if ($debug>2);
  return $arg;
}

sub getArrayOfDateTimeStamps {
  my $arg = shift;
  if ($arg =~ /.*?new Array\((.*?)\);/g ) {
    $arg = stripHTMLandNonPrintables( $1 );
    print "getArrayOfDateTimeStamps: -->$arg<--\n\n" if ($debug>2);
  }
  return split(/\,/, $arg);
}

sub getIndexOfLast23oClock {
  my @arg = @_;
  my $i = $#arg - 1;
  for (;$i>0;$i--) {
    if ($arg[$i] =~ /.*?23 Uhr/) {
      last;
    }
  }
  print "getIndexOfLast23oClock: -->$i+1<--\n\n" if ($debug>1);
  return $i+1;
}

sub readContent {
  my $arg = shift;
  my $content = get $arg;
  die "Couldn't get $arg" unless defined $content;
  print "readContentr( $arg ): -->$content<--\n\n" if ($debug>2);
  $content =~ s/[^[:print:]]//g;
  return $content;
}

sub readFile {
  my $arg = shift;
  my @result = ();
  open my $INFILE, $arg
    or die "Cannot open $arg: $!";
  while (<$INFILE>) {
    chomp;
    push @result, $_;
    print "readFile( $arg ): -->$_<--\n\n" if ($debug>2);
  }

  close $INFILE;
  return @result;
}

sub parseMinMaxTemperatures {
  my ($line1, $line2) = @_;
  # die maximale Temperatur steht in line2
  my $start = rindex($line2, 'C / ');
  my $end = rindex($line2, ' C;Niederschlag');
  my $max_temp = substr($line2,$start+4,$end-$start-4);

  # die minimale Temperatur steht in line2
  $start = rindex($line2, 'Temperatur:');
  $end = rindex($line2, ' C / ');
  my $min_temp = substr($line2,$start+12,$end-$start-12);
  return ($min_temp, $max_temp);
}

sub parseNiederschlag {
  my $arg = shift;
  my @DatUhr = ();
  my $cnt = 0;
  my $jetzt = 0;
  my @result = ();

  while ($arg =~ /(.*?)<\/td>/g ) {
    my $clean_text = stripHTMLandNonPrintables( $2 );
    if ($clean_text =~ /.?, (\d\d\.\d\d\.) (\d\d) Uhr/) {
      $DatUhr[$cnt] = "$1 $2 Uhr: ";
      print "parseNiederschlag#1: -->$DatUhr[$cnt]<--\n\n" if ($debug>2);
      $cnt = $cnt + 1;
    }
    if ($clean_text =~ / Niederschlag \( ab 0 Uhr\)/) {
      $cnt = 0;
      $jetzt = 1;
      print "parseNiederschlag#2: -->$jetzt<--\n\n" if ($debug>2);
    }
    if ($jetzt == 1) {
      if ($clean_text =~ / (\d*.\d) mm/) {
        print "parseNiederschlag#3: -->$DatUhr[$cnt]$1 mm<--\n\n" if ($debug>=2);
        # $result[$cnt] = "$DatUhr[$cnt]$1 mm";
        $result[$cnt] = "$1";
        $cnt = $cnt + 1;
      }
    }
  }
  return @result;
}

sub parseTemperatur {
  my $arg = shift;
  my @DatUhr = ();
  my $cnt = 0;
  my $jetzt = 0;
  my @result = ();

  while ($arg =~ /(.*?)<\/td>/g ) {
    my $clean_text = stripHTMLandNonPrintables( $2 );
    if ($clean_text =~ /.?, (\d\d\.\d\d\.) (\d\d) Uhr/) {
      $DatUhr[$cnt] = "$1 $2 Uhr: ";
      print "parseTemperatur#1: -->$DatUhr[$cnt]<--\n\n" if ($debug>2);
      $cnt = $cnt + 1;
    }
    if ($clean_text =~ /Temperatur 2 m/) {
      $cnt = 0;
      $jetzt = 1;
      print "parseTemperatur#2: -->$jetzt<--\n\n" if ($debug>2);
    }
    if ($jetzt == 1) {
      if ($clean_text =~ /([ |-]\d*.\d) C/) {
        print "parseTemperatur#3: -->$DatUhr[$cnt]$1 °C<--\n\n" if ($debug>=2);
        # $result[$cnt] = "$DatUhr[$cnt]$1 °C";
        $result[$cnt] = "$1";
        $cnt = $cnt + 1;
      }
    }
  }
  return @result;
}

sub beregnen {
  my ($rain_yesterday, $rain_today, $temperature, $min, $max) = @_;
  my $result = 1;

  # Auswertung:
  #    Maximaltemperatur <= 15 °C: beregnen wenn gestern und heute weniger als 2 mm Regen gefallen ist
  #    Maximaltemperatur <= 25 °C: beregnen wenn gestern und heute weniger als 5 mm Regen gefallen ist
  #    Maximaltemperatur > 25 °C: beregnen wenn gestern und heute weniger als 8 mm Regen gefallen ist
  #    Minimaltemperatur < 5 °C: nicht beregnen

  my $grenzwert = 2;
  $grenzwert = 5 if ($max > 15);
  $grenzwert = 8 if ($max > 25);
  $result = 0 if (($rain_today + $rain_yesterday) > $grenzwert);
  #   unterhalb von 5°C nicht beregnen
  $result = 0 if ($min < 5);

  return $result;
}

sub writeToFile {
  my $filename = shift;
  my @arg = @_;
  use Time::Piece;

  my $time = localtime->cdate;
  open my $OUTFILE, '>>', $filename
    or die "Cannot open $filename: $!";
  print "writeToFile($filename) at $time: @arg\n" if ($debug>=1);
  print { $OUTFILE } "$time: @arg\n"
    or die "Cannot write to $filename, $!";
  close $OUTFILE
    or die "Cannot close $filename, $!";
}

sub json {
  my ($rain_yesterday, $rain_today, $temp, $min_temp, $max_temp, $beregnen) = @_;

  my $data = {
    main => {
      temp => float($temp),
      temp_min => float($min_temp),
      temp_max => float($max_temp),
      rain => float($rain_today),
      rain_yesterday => float($rain_yesterday),
      beregnen => float($beregnen),
    },
  };
  print "application/json\n";
  print encode_json $data;
}

sub float {
  my $val = 0.0 + shift;
  return $val;
}

		

Da die für heute prognostizierten Maximal- und Minimal-Temperaturen nicht in den Stundenmittelwerten geliefert werden, habe ich diese Daten zyklisch (alle 2 Stunden) abgefragt und in einer Datei gespeichert.

#!/usr/bin/perl -w
#
# liest aus der Wetterstation in Markelsheim die Daten aus
#

use LWP::Simple;
use HTML::Strip;

# -------------------------- globals -------------------------------
my ($option) = @ARGV;
my $debug = 0;
my $url = 'http://217.5.174.45/cgi-bin/wetterhp.js.pl?c=2&lid=141&pid=3&ppid=7&d=1';
my $files = '/tmp/wetter.txt';
my $hs = HTML::Strip->new();

# -------------------------- main program --------------------------

parseOptions();

my @lines = readFile( $files );
my $content = readContent( $url );

deleteFile( $files ) if ($debug != 4);

my @result = parseTemperatur($content,$lines[0],$lines[1]);
writeToFile( $files, @result ) if ($debug != 4);
# -------------------------- end of main program --------------------------

# ------------ functions ------------------------
sub parseOptions {
  if (defined $option) {
    if (($option eq "-h") or ($option eq "--help")) {
      print "Usage: $0 [-h | --help for help or -d | --debug for debugging or -v[vv] | --verbose[=2..3] for verbosity]\n";
      print "     : -d | --debug; don't write to file, but give max verbosity\n";
      print "     : -v | --verbose; give results only\n";
      print "     : -vv | --verbose=2; give results and steps between\n";
      print "     : -vvv | --verbose=3; give all data and max verbosity\n\n";
      exit;
    }
    if (($option eq "-d") or ($option eq "--debug")) {
      $debug = 4; # don't write to file, but give max verbosity
    }
    if (($option eq "-v") or ($option eq "--verbose")) {
      $debug = 1; # results only
    }
    if (($option eq "-vv") or ($option eq "--verbose=2")) {
      $debug = 2; # results and steps between
    }
    if (($option eq "-vvv") or ($option eq "--verbose=3")) {
      $debug = 3; # all data, max verbosity
    }
  }
}

sub stripHTMLandNonPrintables {
  my $arg = shift;
  $arg = $hs->parse( $arg );
  $arg =~ s/[^\x00-\x7f]//g;
  $arg =~ s/\\n./;/g;
  $arg =~ s/ ;/;/g;
  $arg =~ s/;;/;/g;
  $arg =~ s/;.\(c\).*wetter-bw.de//;
  print "stripHTMLandNonPrintables: -->$arg<--\n\n" if ($debug>2);
  return $arg;
}

sub readContent {
  my $arg = shift;
  my $content = get $arg;
  die "Couldn't get $arg" unless defined $content;
  print "readContent( $arg ): -->$content<--\n\n" if ($debug>2);
  print "readContent( $arg ): OK\n\n" if ($debug==1);
  $content =~ s/[^[:print:]]//g;
  return $content;
}

sub readFile {
  my $arg = shift;
  my @result = ();
  open my $INFILE, $arg
    or die "Cannot open $arg: $!";
  while (<$INFILE>) {
    chomp;
    push @result, $_;
    print "readFile( $arg ): -->$_<--\n\n" if ($debug>2);
  }

  close $INFILE;
  return @result;
}

sub deleteFile {
  my $arg = shift;
  unlink $arg
    or die "Cannot unlink $arg, $!";
}

sub writeToFile {
  my $files = shift;
  my @arg = @_;

  open my $OUTFILE, '>>', $files
    or die "Cannot open $files: $!";
  foreach (@arg) {
    print "writeToFile($files): $_\n" if ($debug>=1);
    print { $OUTFILE } "$_\n"
      or die "Cannot write to $files, $!";
  }
  close $OUTFILE
    or die "Cannot close $files, $!";
}

sub parseTemperatur {
  my ($content,$line1,$line2) = @_;
  my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat,
    $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time);
  my @result = ();
  my $clean_text;

  while ($content =~  /document.writeln\(\'(.*?)\'\)\;/g ) {
    $clean_text = stripHTMLandNonPrintables( $1 );

    if (length $clean_text > 5) {
        if ( not ($Stunden == 0) ) {
            print "parseTemperatur: put line1: -->$line1<-- Stunden:$Stunden\n" if ($debug>1);
            push @result, "$line1";
        } else { # um 0 Uhr ersetze die Zeile 1 durch Zeile 2
            print "parseTemperatur: put line2: -->$line2<-- Stunden:$Stunden\n" if ($debug>1);
            push @result, "$line2";
        }
        if (not ($clean_text =~ /nicht verfgbar/)) {
            print "parseTemperatur: put new line2: -->$clean_text<--\n\n" if ($debug>1);
            push @result, "$clean_text";
        } else {
            print "put line2: -->$line2<--\n\n" if ($debug>=1);
            push @result, "$line2";
        }
    }
  }
  return @result;
}
	
		

Die JSON-Daten werden in dem von mir erstellten Protocolthread pilight/libs/pilight/protocols/API/meinwetter.c zyklisch abgefragt.



Top | Zurück