Pages: [1] 2   Go Down
Author Topic: Statusinformation via Ethernet Shield als eMail senden.  (Read 4941 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo zusammen,
versuche verzweifelt ein bestehenden Sketch (Quelle: Arduino Praxiseinstieg Kapitel VIII) der beim Start eine Email direkt vom Arduino Board / Ethernet Shield gesendet wird so zu ändern, dass ein analoger Wert z.B. ein LDR, NTC oder ähnliches eine Mail sendet. Kurz: Wie im Bsp. wenn der LDR Wert >800 ist soll eine EMail versendet werden. Es soll die Info "Licht ist aus" als EMail gesendet werden.
Bekomme den Sketch leider nicht so hin. Habe das was ich eingefügt / verändert habe rot markiert. Hoffe ich habe wenigstens den richtigen Ansatz gefunden.

Gruß

//
// Ethernet - Mailsenden
// Datei: tb_ethernet_mailsenden
// Datum: 11.02.2010/TB
//
// Bemerkung: Email senden direkt vom Arduino Board

// Status: offen
// Auth-Angaben sind nicht klar.

#include <SPI.h>
#include <Ethernet.h>

// Ethernet-Einstellungen
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 0, 99 }; // IP Arduino-Board
byte gateway[] = { 192, 168, 0, 1 }; // IP Router
byte subnet[] = { 255, 255, 255, 0 };
byte server[] = { 213, 165, 64, 20 }; // IP Mailserver
Client client(server, 25); // Standardport des Mailservers

int valLDR = 0;
int LDR = 0;


void setup()
{
 Ethernet.begin(mac, ip);
 Serial.begin(9600);

 delay(1000);

 if (client.connect()) {
 Serial.println("connecting...");

valLDR = analogRead(LDR);
  if (valLDR > 800)

   Serial.println("connected");
   
   
   
   
   // Text nach HELO nicht relevant
   client.println("HELO HalloServer");
   client.println("MAIL FROM: hiersteht@meineemail.de");
   client.println("RCPT TO: hierdie@empfängermail.de");
   client.println("DATA");
   // Empfänger-Adresse
   client.println("TO: hierdie@empfängermail.de");
   // Titel des Mails
   client.println("SUBJECT: Arduino sendet Email");
   client.println();
   // Inhaltstext des Mails
   client.println("Das Licht ist aus.");
   // Kennzeichnung Ende des Emails
   client.println(".");
   // Abmelden
   client.println("QUIT");
 } else {
   Serial.println("connection failed");
 }
}

void loop()
{
 if (client.available()) {
   char c = client.read();
   Serial.print(c);
 }

 if (!client.connected()) {
   Serial.println();
   Serial.println("disconnecting.");
   client.stop();
   for(; ; )
     ;
 }
}
Logged

Hamburg, Germany
Offline Offline
Sr. Member
****
Karma: 6
Posts: 291
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Soll denn die LDR-Abfrage wirklich nur einmal im Setup durchgeführt werden? Oder wäre es vielleicht besser im loop? Dann könnte man sich auch mal fürs debugging die Werte ausgeben lassen, die das LDR ausspuckt.

markbee
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hatte den Code auch schon mal im Loop, leider auch erfolglos..
Der unveränderte / ursprüngliche Sketch funktioniert übrigens - Der Mailserver wird erreicht, die EMail gesendet.
« Last Edit: June 27, 2011, 03:28:58 pm by belon » Logged

Hamburg, Germany
Offline Offline
Sr. Member
****
Karma: 6
Posts: 291
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Die Frage ist ja, ob der Wert überhaupt >800 in der Abfrage im Setup wird. Ich würde mir mal fürs debugging die werte die am LDR ankommen ausgeben lassen.
Weiterhin steht die analog-Abfrage im setup in einer if-Schleife, das ist fürs Ausprobieren vielleicht auch nicht so günstig.

markbee
Logged


Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Die Werte des LDR habe ich in vorher ausgelesen, der Wert 800 wird bei Abdunklung erreicht.

Wo und welcher Codeschnipsel genau gesetzt wird ist ja mein Problem.

Vielen Dank auch für die Antworten, ich hoffe ich habe die auch richtig verstanden.
Bin was Arduino programmieren angeheht noch nicht ganz so fit.

« Last Edit: June 27, 2011, 04:14:55 pm by belon » Logged

Heidelberg
Offline Offline
Full Member
***
Karma: 1
Posts: 207
Arduino rocks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Nur weil irgendein Buch da etwas gut heisst, Mail Server sind eine Sache für sich.

Als erstes sieh dir mal dieses Beispiel an: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235534880/3

Den ganzen Verbindungskrams da ohne Pause rauszuballern halte ich nicht für gut. Wenn das nicht fruchtet sollte man die Übertragungsart ändern.
Logged

 

CH
Offline Offline
God Member
*****
Karma: 19
Posts: 703
Book Writer "Arduino Praxiseinstieg"
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hallo zusammen,

ich bin der Autor des erwähnten Buches "Arduino Praxiseinstieg".
Das Originalbeispiel zeigt wie man Emails direkt verschicken kann. Der Sketch ist getestet und , wie User Belon bestätigt, funktioniert.

Der gesamte Mailversand läuft im Beispiel im Setup ab damit sichergestellt ist, dass nur einmal ein Mail verschickt wird.

Für das erweiterte Beispiel von Belon mit LDR-Abfrage sollte der Mailversand ins loop() verschoben werden und nur bei Wert grösser 800 ein Mail generiert werden. Den Status will man vermutlich nicht nur einmalig abfragen.

Debugging:
Beim Debugging kann man die aktuellen Variablenwerte auf die serielle Schnittstelle ausgeben. Zum Test darf man auch eine Variable mit einem Wert setzen um eine Entscheidung zu prüfen.

Also:
Code:
//valLDR = analogRead(LDR);
valLDR = 850;

Quote
Nur weil irgendein Buch da etwas gut heisst, Mail Server sind eine Sache für sich.
In irgendeinem meinem Buch wird diese Art des Mailversandes beschrieben, was nicht heissen soll dass man es so machen muss. Im gleichen Buch wird 2 Seiten später auch noch erklärt wie man Mails via PHP-Funktion verschickt.

So kann jeder Leser selber entscheiden, was für seine Anwendung besser passt.  smiley-wink
Logged

Hamburg, Germany
Offline Offline
Sr. Member
****
Karma: 6
Posts: 291
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Ich hab' mir den code gerade nochmal angeschaut. Nach der if-Abfrage für den LDR steht keine geschweifte Klammer, daher wird nur die nachfolgende Zeile ausgeführt, oder?

markbee
Logged


Hamburg, Germany
Offline Offline
Full Member
***
Karma: 3
Posts: 192
Hello world!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

byte server[] = { 213, 165, 64, 20 }; // IP Mailserver
Client client(server, 25); // Standardport des Mailservers

Der Mailserver sagt:
Quote from: Mailserver
telnet 213.165.64.20 25
220 mail.gmx.net GMX Mailservices ESMTP {mp046}
MAIL From: <sui@gmx.de>
550 5.7.0 Need to authenticate via POP3 first {mp046}

Der erwartet also eine Authentifizierung, in diesem Fall SMTP after POP. Das heißt du musst erstmal dein Postfach per POP3 abrufen bevor du Mails versenden darfst. Wenn sich da in den letzten 8 Jahren nichts geändert hat, sollte es zum Testen reichen wenn du erstmal mit einem Rechner (der nach Außen die gleiche IP hat wie dein Arduino) Dein gmx-Postfach abrufst. Anschliessend hast du ein Zeitfenster von 10 Minuten oder so in denen Du über den Mailserver auch Mails versenden darfst.

Dann könntest Du POP3 analog wie SMTP auch im Sketch implementieren und immer vor dem Mailversenden ausführen. (Server ist pop.gmx.net Port 110. Client.println("USER sui@gmx.de"); Client.println("PASS [passwort]"); Client.println("QUIT"); )


Es gibt heutzutage eigentlich gar keine Mailserver, die ohne irgendeine Form von Authentifizierung Mails versenden.
Logged


Hamburg, Germany
Offline Offline
Full Member
***
Karma: 3
Posts: 192
Hello world!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Nicht ganz sauber, ohne Fehlerbehandlung und ungetestet:

Code:
// Ethernet-Einstellungen
byte popserver[] = { 212, 227, 17, 185 }; // IP Mailserver
Client popclient(popserver, 110); // Standardport des Mailservers

// ...

void setup() {
  // ...
  pop3();
  if (client.connect()) {
  // ...
}

void pop3() {
  if (popclient.connect()) {
    popclient.println("USER sui@gmx.de");
    popclient.println("PASS passwort");
    popclient.println("QUIT");
  }
}

Logged


Hamburg, Germany
Offline Offline
Full Member
***
Karma: 3
Posts: 192
Hello world!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

ich bin der Autor des erwähnten Buches "Arduino Praxiseinstieg".

Cool, freut mich. Eins meiner absoluten Lieblingsbücher des letzten Jahres, hat mir den Einstieg sehr erleichtert und ich schlag auch immer mal wieder was nach... Danke smiley-wink
Logged


CH
Offline Offline
God Member
*****
Karma: 19
Posts: 703
Book Writer "Arduino Praxiseinstieg"
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Cool, freut mich. Eins meiner absoluten Lieblingsbücher des letzten Jahres, hat mir den Einstieg sehr erleichtert und ich schlag auch immer mal wieder was nach... Danke
Herzlichen Dank für den positiven Feedback.  smiley
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 8
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Vielen Dank an sui für die Ergänzung!!
Es ist wohl tatsächlich so das man erst Mails abrufen muss um senden zu können.
Dies ist aber grundsätzlich nicht das Problem.

Leider führten die Tipps, auch die von df6ih (FW:Need help getting Arduino to send email),  noch nicht zum Erfolg.
Es geht, wie gesagt, nicht nur um die Verbindung / Versand der EMail.

Das was nicht funktioniert ist die Einbindung des Codes zum Weiterleiten von Statusinformationen.
Auch nach dem ich die LDR Abfrage ins loop verschoben habe sendet er nix.
« Last Edit: June 28, 2011, 05:03:23 pm by belon » Logged

Hamburg, Germany
Offline Offline
Full Member
***
Karma: 3
Posts: 192
Hello world!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Wie markbee schon bemerkt hat, wird bei "if (valLDR > 800)" nur die Zeile ausgeführt die "connected" ausgibt - eine Mail sollte dagegen immer versendet werden. Ist das so?

Ansonsten hier noch ein kleiner Crashkurs in Clean-Code-Developement, der das Debugging vieleicht erleichtert smiley
(Mangels Arduino + Ethernetshield hier vor Ort leider ungetestet)

1.) Refactoring... Separation of Concerns + Single Responsibility Principle
Codeabschnitte in einzelne Funktionen auslagern, jede Funktion sollte dabei genau eine Aufgabe übernehmen. Im konkreten Fall gehört das Mailabholen, Mailsenden und die Prüfung der Eingaben jeweils in eine eigene Funktion mit aussagekräftigem Namen.

Vorteile: Eine Funktion mit einer Aufgabe ist leichter zu verstehen, finden oder nachträglich zu bearbeiten. Man hat abgeschlossene kleine Abschnitte die man einzeln (in der professionellen Softwareentwicklung dann auch automatisiert) testen  kann. Wenn sie für sich alleine erstmal funktionieren kann man sie auch ausblenden + vergessen (also wie ich erst kürzlich hier im Forum gelernt habe in der Arduino-IDE in Tabs auslagern), womit sie den Lesefluss nicht mehr stören. Weiterhin hat man eine Abstraktionsebene eingeführt, bei der die konkrete Implementierung leicht austauschbar ist - die Funktion "boolean isLight()" kann man mit einem LDR, einer Fotodiode oder Kamera lösen ohne die Logik der gesamten Applikation zu beeinträchtigen; die Funktion "sendMail()" könnte man auch mit der PHP-Methode ersetzen.

In die mail-Funktionen gehört dann auch das Einlesen der Antwort vom Mailserver, und nicht wie in allen gängigen Beispielen erst im Loop was ja prinzipbedingt nur einmal während der gesamten Laufzeit des Sketches funktionieren würde, da ja nur einmal eine einzige Clientverbindung im setup() gemacht wird. Überhaupt ist das ein sehr fragwürdiges Vorgehen, die Verbindung im setup() zu öffnen und im loop() die Antwort auszulesen. Das könnte man bei einem Chatclient, wo die Verbindung einmal geöffnet wird und dann weiterhin immer offen bleibt so machen, aber hier ergibt das nicht viel Sinn. Vieleicht steckt da auch irgendwo der Fehler, wenn im setup() die Bedingung nicht erfüllt ist, passiert auch später einfach nichts mehr.

Code:
boolean sendMail() {
 if (client.connect()) {
   client.println("HELO HalloServer");
   client.println("MAIL FROM: hiersteht@meineemail.de");
   client.println("RCPT TO: hierdie@empfängermail.de");
   client.println("DATA");
   client.println("TO: hierdie@empfängermail.de");
   client.println("SUBJECT: Arduino sendet Email");
   client.println();
   client.println("Das Licht ist aus.");
   client.println(".");
   client.println("QUIT");
   while (client.available()) {
     char c = client.read();
     Serial.print(c);
   }
   client.stop();
   return true;
 } else {
   return false;
 }
}


boolean getMail() {
  if (popclient.connect()) {
    popclient.println("USER sui@gmx.de");
    popclient.println("PASS passwort");
    popclient.println("QUIT");
    while (client.available()) {
     char c = client.read();
     Serial.print(c);
    }
    popclient.stop();
    return true;
  } else {
    return false;
  }
}

boolean isLight() {
  int valLDR = analogRead(LDR);
  return (valLDR > 800);
}

2.) Testen

Die Funktionen lassen sich jetzt komfortabel einzeln testen:

Code:
void setup() {
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
  delay(1000);

  if (getMail()) {
    Serial.println("Mail abholen erfolgreich");
  } else {
    Serial.println("Mail abholen fehlgeschlagen");
  }

  if (sendMail()) {
    Serial.println("Mail senden erfolgreich");
  } else {
    Serial.println("Mail senden fehlgeschlagen");
  }
}

void loop() {
  if (isLight()) {
    Serial.println("Es ist hell");
  } else {
    Serial.println("Es ist dunkel");
  }
  delay(1000);
}

3.) Zusammenbauen

So, die eigentliche Applikationslogik besteht jetzt - und hier zeigt sich sehr schön der Vorteil von sauberem Code - aus einem wirklich übersichtlichen, leicht lesbaren und selbsterklärenden 4-Zeiler. Sollte da tatsächlich noch ein Fehler drin sein, lässt der sich sicher einfach finden smiley-wink

Code:
void loop() {
  if (isLight()) {
    getMail();
    sendMail();
  }
}

4. Noch mehr Refactoring
Eigentlich sind wir hier ja schon fertig, es sei denn das ganze soll noch weiter aufgeräumt werden...

sendMail() müsste eigentlich aufgegliedert werden in sendMail(), sendMailSMTP(), sendMailSMTPafterPOP() weil das getMail() nicht zur Applikationslogik gehört und aus dem loop() rausfliegen müsste. Vieleicht verwendest du aber auch einen Mailserver ohne/anderer Authentifizierung oder einen Webmailer, dann entsprechende sendMailBasicAuth() oder sendMailPHPWebmailer() oder so implementieren und in sendMail() aufrufen.
Code:
boolean sendMail() {
  return sendMailSMTPafterPOP();
}

boolean sendMailSMTPafterPOP() {
  if (!getMail()) {
    return false;
  }
  return sendMailSMTP();
}

boolean sendMailSMTP() {
  // so wie vorher sendMail()
}

- Aus sendMail() wird dann sicher noch ein "Don't repeat yourself"-wiederverwendbares sendMail(string To, string Subject, string Body).

- Die Mailzugangsdaten gehören eigentlich am Anfang in Konstanten definiert, genauso wie der Schwellenwert 800 für den LDR.

- Für ein besseres Verständniss würde ich die Variablen server + client in SMTPServer + SMTPClient umbenennen, analog dazu POPServer + POPClient.

- Fehlerbehandlung: Die Antworten der Mailserver könnte man in den Mailfunktionen auch verarbeiten.
Logged


Hamburg, Germany
Offline Offline
Sr. Member
****
Karma: 6
Posts: 291
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hui noch jmd aus HH.

Da kann man ja fast ein Usertreffen machen smiley-wink

markbee
Logged


Pages: [1] 2   Go Up
Jump to: