Wettbewerbsbeitrag: Wassermelder mit Twitter-Alarmmeldung

Beitrag zum Kalenderwettbewerb 2015 von Bernhard Kaiser & Michael Gaus

 

Bei diesem Projekt handelt es sich um einen Wassermelder, der optisch, akustisch und per Twitter-Meldung Alarm gibt, sobald an den Sensorkontakten Wasser detektiert wird. Er kann z.B. zur Überwachung auf austretendes Wasser aus Waschmaschine oder Spülmaschine dienen und helfen, rechtzeitig vor größeren Überschwemmungen eingreifen zu können. Als Sensorkontakt können einfach 2 Drähte mit abisolierten Enden genommen werden, so wie es auch auf dem Foto zu sehen ist. Verwendet wurden ausschließlich die Bauteile der beiden Adventskalender.

Schaltpl

Schaltplan

Im Ruhezustand der Schaltung sperrt die Darlingtonstufe bestehend aus den beiden Transistoren T1 und T2. Bei Benetzung mit Wasser am Sensorkontakt J1 entsteht eine leitfähige Verbindung, sodass die Darlingtonstufe durchschaltet und die rote LED1 eingeschaltet wird. Zusätzlich wird durch den LOW-Pegel am Kollektor das IoT-Modul (NanoESP) über den Digitaleingang D2 getriggert, sodass dann per Software eine Twitter-Meldung als weltweiter Alarm abgesetzt wird. Damit jedoch nicht die ganze Welt im Klartext das Wasserleck mitbekommt, erfolgt die Twitter-Textmeldung in bester Geheimdienst-Manier:
„Achtung, alle Mann an Bord! Das Boot ist bereit zum Ablegen!“ :-)
Zusätzlich wird per Software ein optisches (gelbe LED2 blinkt) und akustisches Alarmsignal per Piezo solange erzeugt, bis der Sensorkontakt keine leitfähige Verbindung mehr hat. Somit ist der Alarm auch direkt lokal vor Ort seh- und hörbar.

Als Basis für die Software wurde das Projekt „Alarmanlage“ (Tag 21 des IoT-Adventskalenders) verwendet und entsprechend abgeändert.
Ganz oben im Quellcode müssen für die 3 Defines SSID, PASSWORD und TwitterKEY anstatt der Platzhalter die entsprechenden Werte eingetragen werden. Benötigt werden ein Thingspeak und ein Twitter Account, so wie im IoT-Adventskalender beschrieben.

Download: Arduino Programm

Noch ein Hinweis zur Software:
Die Arduino-Stringverarbeitung verwendet dynamisch allokierten Heap-Speicher, wobei es zu einer starken Fragmentierung kommen kann. Dies kann zu RAM-Problemen führen. Da in den Adventskalender-Beispielen sehr viele Strings angelegt und hintereinander gehängt werden, kann dies teilweise zu scheinbar unerklärlichem Softwareverhalten führen und sogar Softwareabstürze zur Folge haben, da es zum Überschreiben von RAM-Inhalten kommen kann. Als beispielsweise in dieser Softwareanpassung für den Wassermelder der zu sendende Twitter-Text länger gemacht wurde als im Adventskalender-Beispiel, hat das Versenden der Twitter-Meldung zunächst nicht mehr funktioniert. Um statisch verwendetes RAM zu sparen, sodass mehr RAM für den dynamischen Heap zur Verfügung steht, wurden deshalb hier Strings, die als Debugausgabe auf der seriellen Schnittstelle ausgegeben werden, ins Flash gelegt mit folgendem Mechanismus: statt „Textausgabe“ wird verwendet: F(„Textausgabe“).
Beispiel:
debug(F(„WLAN Connected“));

Der Quelltext
/*
Wassermelder Alarm
SSID, PASSWORD und TwitterKEY muessen angepasst werden
*/

#define SSID "[Your SSID]"
#define PASSWORD "[Your Password]"
#define TwitterKEY "[Your Key]"

#define SENSOR_INPUT 7  // Wasser-Sensor ist am digital input D2 abgeschlossen
#define LED_ALARM    9  // Alarm-LED ist am digital input D3 angeschlossen
#define PIEZO 8

#define LED_WLAN 13

#define DEBUG true

#include <SoftwareSerial.h>
SoftwareSerial esp8266(11, 12); // RX, TX

const byte thingPost[] PROGMEM = {
  80, 79, 83, 84, 32, 42, 85, 82, 76, 42, 32, 72, 84, 84, 80, 47, 49, 46, 49, 10, 72, 111, 115, 116, 58, 32, 97, 112, 105, 46, 116, 104, 105, 110, 103, 115, 112, 101, 97, 107, 46, 99, 111, 109, 10, 67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 58, 32, 99, 108, 111, 115, 101, 10, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 120, 45, 119, 119, 119, 45, 102, 111, 114, 109, 45, 117, 114, 108, 101, 110, 99, 111, 100, 101, 100, 10, 67, 111, 110, 116, 101, 110, 116, 45, 76, 101, 110, 103, 116, 104, 58, 32, 42, 76, 69, 78, 42, 10, 10, 42, 65, 80, 80, 69, 78, 68, 42, 10
};


void setup() {
  Serial.begin(19200);
  Serial.println("Wassermelder");

  esp8266.begin(19200);

  if (!espConfig()) serialDebug();
  else digitalWrite(LED_WLAN, HIGH);

  pinMode(SENSOR_INPUT, INPUT);
  pinMode(LED_ALARM, OUTPUT);
}

void loop() 
{
  if(digitalRead(SENSOR_INPUT) == LOW)
  {
    alarm();
  }
  else
  {
    debug(F("Alles im trockenen Bereich."));
  }
  delay(500);
}

void alarm(void)
{
  debug(F("Achtung, alle Mann an Bord! Das Boot ist bereit zum Ablegen!"));

  sendTwitterPost(TwitterKEY, "Achtung, alle Mann an Bord! Das Boot ist bereit zum Ablegen!");

  do
  {
    digitalWrite(LED_ALARM, HIGH);
    tone(PIEZO, 400, 500);
    delay(500);
    digitalWrite(LED_ALARM, LOW);
    tone(PIEZO, 800, 500);
    delay(500);
  }
  while (digitalRead(SENSOR_INPUT) == LOW);

}

boolean sendTwitterPost(String key, String twitterMsg)
{
  boolean succes = true;
  String  Host = "api.thingspeak.com";
  String Url = "/apps/thingtweet/1/statuses/update";
  String msg = "&status=" + twitterMsg;
  succes &= sendCom("AT+CIPSTART="TCP","" + Host + "",80", "OK");

  String postRequest = createThingPost(Url, key, msg);
  if (sendCom("AT+CIPSEND=" + String(postRequest.length()), ">"))
  {
    esp8266.print(postRequest);
    debug (postRequest);
    esp8266.find("SEND OK");
    succes &= sendCom("AT+CIPCLOSE", "OK");
  }
  else
  {
    succes = false;
  }
  return succes;
}


//-----------------------------------------ThingsSpeak Functions------------------------------------

boolean sendThingPost(String key, int value)
{
  boolean succes = true;
  String  Host = "api.thingspeak.com";
  String msg = "field1=" + String(value);
  succes &= sendCom("AT+CIPSTART="TCP","" + Host + "",80", "OK");

  String postRequest = createThingPost("/update", key, msg);

  if (sendCom("AT+CIPSEND=" + String(postRequest.length()), ">"))
  {
    esp8266.print(postRequest);
    esp8266.find("SEND OK");
    if (!esp8266.find("CLOSED")) succes &= sendCom("AT+CIPCLOSE", "OK");
  }
  else
  {
    succes = false;
  }
  return succes;
}

String createThingPost(String url, String key, String msg)
{
  String xBuffer;

  for (int i = 0; i <= sizeof(thingPost); i++)
  {
    char myChar = pgm_read_byte_near(thingPost + i);
    xBuffer += myChar;
  }

  String append = "api_key=" + key + "&" + msg;

  xBuffer.replace("*URL*", url);
  xBuffer.replace("*LEN*", String( append.length()));
  xBuffer.replace("*APPEND*", append);

  return xBuffer;
}

String createThingGet(String url, String key)
{
  String xBuffer;

  for (int i = 0; i <= sizeof(thingPost); i++)
  {
    char myChar = pgm_read_byte_near(thingPost + i);
    xBuffer += myChar;
  }

  String append = "api_key=" + key;
  xBuffer.replace("POST", "GET");
  xBuffer.replace("*URL*", url);
  xBuffer.replace("*LEN*", String( append.length()));
  xBuffer.replace("*APPEND*", append);

  return xBuffer;
}

String createThingGet(String url, String key, String msg)
{
  String xBuffer;

  for (int i = 0; i <= sizeof(thingPost); i++)
  {
    char myChar = pgm_read_byte_near(thingPost + i);
    xBuffer += myChar;
  }

  String append = "api_key=" + key + "&" + msg;

  xBuffer.replace("POST", "GET");
  xBuffer.replace("*URL*", url);
  xBuffer.replace("*LEN*", String( append.length()));
  xBuffer.replace("*APPEND*", append);

  return xBuffer;
}


//-----------------------------------------Config ESP8266------------------------------------

boolean espConfig()
{
  boolean succes = true;

  esp8266.setTimeout(5000);
  succes &= sendCom("AT+RST", "ready");
  esp8266.setTimeout(1000);

  if (configStation(SSID, PASSWORD)) {
    succes &= true;
    debug(F("WLAN Connected"));
    debug(F("My IP is:"));
    debug(sendCom(F("AT+CIFSR")));
  }
  else
  {
    succes &= false;
  }
  //shorter Timeout for faster wrong UPD-Comands handling
  succes &= sendCom(F("AT+CIPMUX=0"), "OK");
  succes &= sendCom(F("AT+CIPMODE=0"), "OK");

  return succes;
}

boolean configTCPServer()
{
  boolean succes = true;

  succes &= (sendCom(F("AT+CIPMUX=1"), "OK"));
  succes &= (sendCom(F("AT+CIPSERVER=1,80"), "OK"));

  return succes;

}

boolean configTCPClient()
{
  boolean succes = true;

  succes &= (sendCom("AT+CIPMUX=0", "OK"));
  //succes &= (sendCom("AT+CIPSERVER=1,80", "OK"));

  return succes;

}


boolean configStation(String vSSID, String vPASSWORT)
{
  boolean succes = true;
  succes &= (sendCom("AT+CWMODE=1", "OK"));
  esp8266.setTimeout(20000);
  succes &= (sendCom("AT+CWJAP="" + String(vSSID) + "","" + String(vPASSWORT) + """, "OK"));
  esp8266.setTimeout(1000);
  return succes;
}

boolean configAP()
{
  boolean succes = true;

  succes &= (sendCom(F("AT+CWMODE=2"), "OK"));
  succes &= (sendCom("AT+CWSAP="NanoESP","",5,0", "OK"));

  return succes;
}

boolean configUDP()
{
  boolean succes = true;

  succes &= (sendCom(F("AT+CIPMODE=0"), "OK"));
  succes &= (sendCom(F("AT+CIPMUX=0"), "OK"));
  succes &= sendCom("AT+CIPSTART="UDP","192.168.255.255",90,91,2", "OK"); //Importand Boradcast...Reconnect IP
  return succes;
}




//-----------------------------------------------Controll ESP-----------------------------------------------------

boolean sendUDP(String Msg)
{
  boolean succes = true;

  succes &= sendCom("AT+CIPSEND=" + String(Msg.length() + 2), ">");    //+","192.168.4.2",90", ">");
  if (succes)
  {
    succes &= sendCom(Msg, "OK");
  }
  return succes;
}


boolean sendCom(String command, char respond[])
{
  esp8266.println(command);
  if (esp8266.findUntil(respond, "ERROR"))
  {
    return true;
  }
  else
  {
    debug("ESP SEND ERROR: " + command);
    return false;
  }
}

String sendCom(String command)
{
  esp8266.println(command);
  return esp8266.readString();
}



//-------------------------------------------------Debug Functions------------------------------------------------------
void serialDebug() {
  while (true)
  {
    if (esp8266.available())
      Serial.write(esp8266.read());
    if (Serial.available())
      esp8266.write(Serial.read());
  }
}

void debug(String Msg)
{
  if (DEBUG)
  {
    Serial.println(Msg);
  }
}

[collapse]

Der Beitrag Wettbewerbsbeitrag: Wassermelder mit Twitter-Alarmmeldung erschien zuerst auf Elektronik Dachbude.

Source: fkainka.de/pretzelboard

1 Comment

  1. Rose

    Bonjour Matt,Je ne comprends pas votre question : l&iotus;rnstallaqion Nuage de givre est présentement à la place des Festivals (juste à côté de la Place des Arts).

    Reply

Leave a Comment

Your email address will not be published. Required fields are marked *