Statusinformation via Ethernet Shield als eMail senden.

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(; ; )
;
}
}

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

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.

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

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.

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.

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:

//valLDR = analogRead(LDR);
valLDR = 850;

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. :wink:

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

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

Der Mailserver sagt:

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.

Nicht ganz sauber, ohne Fehlerbehandlung und ungetestet:

// 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");
  }
}

Webmeister:
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 :wink:

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. :slight_smile:

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.

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 :slight_smile:
(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.

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:

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 :wink:

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.

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.

Hui noch jmd aus HH.

Da kann man ja fast ein Usertreffen machen :wink:

markbee

markbee:
Da kann man ja fast ein Usertreffen machen :wink:

Es wäre nicht das erste :wink:
=> http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1288375564/0

Vieleicht wäre eine HH-Mailingliste angebracht um sich besser zu organisieren, das Forum hier lesen bestimmt nur die wenigsten Leute regelmäßig.

Hallo sui,
vielen Dank für die Mühe und Zeit mir den Crashkurs zu geben. Da ich aber bis vor Kurzem noch gar nichts mit Programmieren zu tun hatte,
muss ich mich hier erst intesiver einarbeiten, um dies alles anwenden und umsetzen zu können.
Danke nochmal!

Gruß