Donnerwetter 2.0 – Gewitterwarnungen mit MQTT und Twitter

Der Pragmatische Architekt  –  0 Kommentare

In der Serie kam des Öfteren zur Sprache, wie sich Wetter- und Umweltdaten erfassen lassen. Bisher war von Temperatur, Luftfeuchtigkeit, Luftdruck und Feinstaub die Rede. Jetzt stoßen Gewitterwarnungen hinzu.

Als besonderes "Schmankerl" arbeitet die vorgestellte Implementierung mit MQTT und einem Twitter-Account, um die Gewitterwarnungen auch über das Netz zu verbreiten und sich damit das Label "Internet der Dinge" zu verdienen.

Von Blitz und Donner

Gewitter gehören seit Alters her zu den Wetterereignissen, die zugleich faszinieren und erschrecken. Heute wissen wir fast alles über die physikalischen Grundlagen von Blitzen. Gewitter präzise vorherzusagen, ist jedoch nicht ohne weiteres möglich. In vielen Situationen ist es daher nützlich, zumindest rechtzeitig Warnungen vor heranziehenden Gewitterfronten zu erhalten. Und genau dafür soll das heutige Projekt dienen, das einen speziell für Gewittererkennung konzipierten IC verwendet.

Es ist wieder an der Zeit, die Maker-Werkstatt zu betreten.

(Bild: wikipedia.org)

Der Gewittersensor AS3935

Der AS3935 Franklin Lightning Sensor des österreichischen Herstellers ams (austriamicrosystems) kann mithilfe eines empfindlichen Radioempfängers elektrische Emissionen aufspüren, die von entfernten Gewittern stammen. Die Gewitterfront darf dabei bis zu 40 Kilometer vom Sensor entfernt liegen. Da Entladungen von Blitzen immer impulsartig mit sich verkleinernden Wellenbergen (Spikes) ablaufen, besitzt der Sensor die Möglichkeit, anhand eines Algorithmus echte Gewitter von menschengemachten Störungen zu unterscheiden. Zu solchen Störeinflüssen gehören zum Beispiel Mikrowellen oder Motoren. Für die Gewittererkennung kann der IC Entladungen zwischen Wolken oder zwischen Wolken und Terrain erkennen. Seine Antenne lässt sich zu diesem Zweck entweder manuell kalibrieren und tunen oder automatisch über eine Autotuningfunktion.

(Nebenbei bemerkt: Das Tuning soll interne Kondensatoren so konfigurieren, dass 500 KHz Resonanzfrequenz entstehen. Die RC-Oszillatoren TRCO und SRCO oszillieren dabei mit 1,1 mHz beziehungsweise 33 kHz.)

Der Gewittersensor AS3935 im Blockschaltbild (Bild: ams.com)

Wie funktioniert der AS3935 intern? Sobald ein Blitz auftritt, entsteht eine elektromagnetische Welle. Dieser Impuls trifft auf eine externe Antenne des AS3935, die mit rund 500 kHz und einer Bandbreite von 33 kHz arbeitet. Der Baustein AFE (Analog Front-End), mitte-links im Blockdiagramm, demoduliert und verstärkt die empfangenen Signale. Übersteigt das Signal einen Schwellwert, sorgt ein Watchdog für die weitere Analyse des Ereignisses. In dieser Analyse stellt der Baustein mit einem nicht im Detail beschriebenen Algorithmus fest, ob es sich tatsächlich um ein gewitterbedingtes Ereignis handelt. Falls ja, ermittelt der Franklin-Sensor die Energie (Energy Calculation) und errechnet eine Schätzung über die Entfernung des Gewitters (Statistical Distance Evaluation). Ein Interrupt wird nun am angeschlossenen Mikrocontroller-Board ausgelöst. Berücksichtigung findet dabei auch der Noise-Floor, also die Menge des festgestellten elektrischen Grundrauschens (Noise Floor Evaluation). Übersteigt dieser Pegel eine gewisse einstellbare Höhe, stellt der Chip das Erkennen von Gewittern ein, da sich diese nicht mehr mit genügend hoher Sicherheit aufspüren lassen.

Entsprechende Boards zum Anschluss an den Arduino oder andere Mikrocontroller gibt es zum Beispiel von Embedded Adventures (Name: MOD-1016), von MikroElektronika oder von PlayingWithFusion zu Preisen von rund 25 Euro. Der Chip selbst kommt übrigens bei Abnahmen von 1000 Stück auf einen Preis von US-$ 3,55. Falls Sie also groß in das Geschäft mit der Gewittererkennung einsteigen wollen ...

Als Schnittstellen zu externer Hardware stellt der AS3935 sowohl eine I2C - als auch eine SPI-Anbindung zur Verfügung.

Das Projekt

Doch genug des Vorgeplänkels. Jetzt soll es endlich in medias res gehen. Die Idee des vorliegenden Projekts besteht darin, ein MOD-1016-Board über I2C an ein Arduino Mega-Board plus Ethernet-Shield anzuschließen. Genauso gut ließe sich allerdings auch ein anderes Arduino-Board oder ein WiFi-Shield verwenden. Natürlich gilt dies auch für den Einsatz eines alternativen Gewittersensor-Breakout-Boards.

Das Breakout-Board MOD-1016 lässt sich an Boards wie Arduino oder Raspberry Pi betreiben. (Bild: embeddedadventures.com)

Wer das MOD-1016 lieber per SPI verwenden möchte, greift zum Lötkolben und ändert den gelöteten Jumper entsprechend (siehe am Board links unten, neben dem GND-Eingang).

Was soll das fertige System am Ende leisten? Ein Arduino erkennt Gewittermeldungen des MOD-1016, sendet diese Information an einen MQTT-Kanal MyWeatherChannel. Ein JavaScript-Programm entnimmt diese Nachrichten aus MyWeatherChannel und leitet sie als Tweets weiter, die interessierte Benutzer über ein beliebiges Endgerät empfangen können.

Die fertige Schaltung ist nicht weiter spannend. Der Anschluss MOD-1016 mit Mega gestaltet sich folgendermaßen:

  • Spannungseingang des Mega mit 3,3 V an Vcc des MOD-1016
  • GND von Mega an GND des MOD-1016
  • A5 des Mega an SCL des MOD-1016
  • A4 des Mega an SDA des MOD-1016
  • Digitaler Eingang 2 des Mega an IRQ des MOD-1016
Anschluss des MOD-1016 per IIC an Mega plus Ethernet-Shield

Programmdesign

Systementwurf mit MOD-1016 Breakout-Board, Arduino MEGA, Ethernet-Shield

Das Design des IoT-Systems lässt sich am besten anhand des folgenden detaillierteren Szenarios illustrieren. Dabei enthält, wie bereits erwähnt, ein an den Arduino Mega angeschlossenes MOD-1016-Breakout-Board den eigentlichen Sensor des Typs AS3935 für die Gewittererkennung.

  1. Erkennt das Board ein potenzielles Gewitter, löst es am Mega einen externen Interrupt aus. Das passiert an dessen digitalen Eingang 2.
  2. Der auf dem Mega laufende Sketch erkennt das Ereignis, ermittelt mit Hilfe des MOD-1016 genauere Information, verpackt diese als MQTT-Nachricht, und sendet sie an die Warteschlange MyWeatherChannel im lokalen MQTT-Server.
  3. Ein unter Node.js laufendes JavaScript-Programm wartet auf alle in MyWeatherChannel eingehenden Nachrichten. Kommt eine solche Nachricht an, holt das JavaScript-Programm die Nachricht aus der Warteschlange.
  4. Anschließend konvertiert es den Nachrichteninhalt in einen Tweet und leitet diesen an den entsprechenden Twitter-Account weiter.
  5. Benutzer können die Tweets nun zum Beispiel über ihr Smartphone empfangen.

Sobald der Sensor seine Arbeit beginnt oder Gewitter erkannt werden, treffen Tweets im Twitter-Account ein:

Die Startnachricht wird getweeted sobald der Sensor live geht

Bibliotheken für den Zugriff auf AS3935

Der Chip lässt sich relativ komplex und sehr flexibel über Register steuern. Zum Glück stellen die BoardHhersteller entsprechende Demos und Bibliotheken zur Verfügung.

Weitere Libs stammen von Makern:

Das vorliegende Projekt gibt sich mit dem schlichten Beispielsketch von Board-Hersteller Embedded Adventures zufrieden und nutzt diesen als Basis für den eigenen Code. Wer das Ganze noch wesentlich ausgefeilter gestalten möchte, findet in den angegebenen Bibliotheken entsprechende Code-Beispiele.

Zur Installation binden wir die Bibliothek des Herstellers (embedded Adventures) auf gewohntem Weg über die Arduino IDE ein:

  • Download der Bibliothek aus GitHub
  • Einbinden der Bibliothek in die Arduino IDE über Sketch | Include Library | Add .ZIP Library

Programmatischer Twitter-Zugriff

Um Twitter nutzen zu können, bedarf es einer entsprechenden Kennung. Die Anmeldung auf Twitter erfolgt über https://apps.twitter.com. Voraussetzung ist ein Twitter-Account beziehungsweise ein Entwicklerkonto auf https://dev.twitter.com.

Auf https://apps.twitter.com legen wir eine neue App an. Dabei erhalten wir alle benötigten Sicherheitsschlüssel: Consumer Key, Consumer Key Secret, Access Token Key, Access Token Key Secret. Diese Daten benötigt das JavaScript-Programm weiter unten.

Twitter eine neue App bekanntgeben

Server für lokales MQTT

Um mit MQTT arbeiten zu können, ist ein MQTT-Server notwendig. In einem vergangenen Artikel über MQTT habe ich bereits beschrieben, wie sich zu diesem Zweck der freie mosquito-Server unter Linux, OS X und Windows einsetzen lässt. Ebenso illustriert der Artikel, wie Entwickler mithilfe des grafischen Werkzeugs MQTT.fx beobachten können, was am MQTT-Server beziehungsweise in den Nachrichtenwarteschlangen geschieht.

Das Werkzeug MQTT.fx hilft, die MQTT-Nachrichtentransfers zu beobachten

All you need is Code

Noch ist die "Installationsorgie" nicht abgeschlossen. Auch auf der JavaScript-Seite arbeitet das Projekt mit Bibliotheken. Wie immer, kommt JavaScript unter Node.js zum Einsatz.

Zunächst müssen wir die Bibliothek für den Zugriff auf MQTT installieren:

npm install mqtt --save

Danach installieren wir noch die Bibliothek zum Zugriff auf Twitter:

npm install twitter

Nachdem der Import dieser Bibliotheken abgeschlossen ist, lässt sich mit den eigentlichen Programmierarbeiten beginnen. Der nachfolgende Sketch abonniert am MQTT-Server Nachrichten aus MyWeatherChannel. Kommen solche Nachrichten an mqttClient.on('message', .... ), werden sie an Twitter weitergereicht: twitterClient.post('statuses/update', {status: message.toString()}.

Insgesamt lautet der JavaScript-Code:

// MQTT Bibliothek einbinden:
var mqtt = require('mqtt');

// Verbindung zu lokalem MQTT-Server (mosquito) aufbauen:
var mqttClient = mqtt.connect('mqtt://localhost:1883');

// Twitter-Bibliothek einbinden:
var Twitter = require('twitter');

// Eigene Zugriffsdaten müssen hier eingefügt werden:
var twitterClient = new Twitter({
consumer_key : '...',
consumer_secret : '...',
access_token_key : '...',
access_token_secret : '...'
});

// Verbindung zum Broker aufbauen:
mqttClient.on('connect', function () {
// Nachrichten aus MyWeatherChannel abonnieren:
mqttClient.subscribe('MyWeatherChannel');
});

// mqttClient.on enthält eine Ereignisbehandlung für eingehende Nachrichten:
mqttClient.on('message', function (topic, message) {
// Eingegangene Nachricht auslesen:
console.log(message.toString());

// MQTT-Nachricht an Twitter weiterreichen
twitterClient.post('statuses/update', {status: message.toString()},
function(error, tweet, response) { // Fehlerfall???
if (!error) {
console.log(tweet);
}
});
});

Bei Verwendung des MQTT-Servers an einer anderen URL müssen Sie im Code die URL entsprechend ändern (rot markierter Teil):

var mqttClient  = mqtt.connect('mqtt://localhost:1883');

Arduino-Code

Der Code für Arduino ist ebenfalls relativ einfach gehalten. Lassen Sie mich zuerst die verwendeten Methoden im Sketch kurz erläutern.

  • setup() nimmt die grundlegenden Einstellungen vor.
  • alert() ist die ISV (Interrupt Service Routine), die der MOD-1016 am Arduino auslöst.
  • loop() ermittelt kontinuierlich, ob die Ausnahmebehandlungsroutine am Eingang 2 vom MOD-1016/AS3935 ausgelöst wurde, und ob es sich tatsächlich um ein Gewitter handelt. Die Information leitet der Sketch per MQTT an den Server mosquito weiter.
  • reconnectMQTT() stellt die Verbindung mit MQTT her.
  • translateIRQ() identifiziert die Art des vom AS3935 festgestellten Ereignisses, etwa Gewitter oder Fehlalarm.
  • distance() versucht die Entfernung der Gewitterfront zu ermitteln.

Im Programm sollten Sie die IP-Adressen ip und server an Ihre eigene Konfiguration anpassen.

  • ip: dem Mega+Ethernet-Shield zugewiesene IP-Adresse.
  • server: IP-Adresse des MQTT-Servers.

Der Sketch gestaltet sich damit insgesamt wie folgt:

#include <SPI.h>                  // SPI wegen Ethernet Shield
#include <Ethernet.h> // Nutzung Ethernet Shield
#include <PubSubClient.h> // Import MQTT-Bibliothek von Paho

#include <Wire.h> // IIC Interface
#include <AS3935.h> // MOD-1016 Bibliothek

#define IRQ_pin 2 // Gewittererkennung ueber Pin 2
// => Interrupt 0

volatile bool detected = false; // Gewitter entdeckt?

// MAC-Adresse der Ethernet Card
byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xED };

IPAddress ip(192,168,178,72); // Eigene IP-Adresse
IPAddress server(192,168,178,40); // Adresse MQTT-Server


EthernetClient ethClient; // Ethernet Client
PubSubClient client(ethClient); // MQTT Client

//////////////////////////////////////////////////////////////
//
// reconnectMQTT()
// baut Verbindung zum MQTT-Server auf und
// sendet MQTT-Nachricht im Erfolgsfall
//
//////////////////////////////////////////////////////////////


void reconnectMQTT() {
// Warten bis MQTT-Verbindung verfuegbar:
while (!client.connected()) {
Serial.println("Verbindungsaufbau mit MQTT");
// Verbunden:
if (client.connect("arduinoClient")) {
Serial.println("Verbunden!");
// Verbindungsaufbau hat geklappt => MQTT-Nachricht senden
client.publish("MyWeatherChannel","Gewittererkennung wurde aktiviert");
delay(2000); // warten
} else {
// Im Fehlerfall Rueckmeldung
Serial.print("Verbindungsfehler. Fehlercode = ");
Serial.println(client.state());
Serial.println("Naechster Versuch in 5 Sekunden");
// 5 Sekunden Wartezeit vor Neuversuch einhalten
delay(5000);
}
}
}

////////////////////////////////////////////////////////////////
//
// setup()
// Initialisierung Hardware
//
////////////////////////////////////////////////////////////////

void setup()
{
// Serieller Monitor mit maximaler Geschwindigkeit
Serial.begin(115200);
// Warten bis serielle Verbindung etabliert ist
while (!Serial) {}

// MQTT Server ohne Verschluesselung auf Port 1883
client.setServer(server, 1883);

// Ethernet Verbindung etablieren
Ethernet.begin(mac, ip);
// Kleine Verschnaufpause
delay(1500);

// IIC - Verbindung zum MOD-1016 Breakout Board aufbauen:
Wire.begin();
// Board schickt bei Gewittern Interrupts an Pin 2
mod1016.init(IRQ_pin);
// Parameter des AS3935 setzen:
//autoTuneCaps(IRQ_pin); // optionales Auto-Tuning


// Die 4 wurde vom Boardhersteller auf meinem Board angegeben:
mod1016.setTuneCaps(4);

// Eigene Einstellungen:
mod1016.setIndoors();
mod1016.setNoiseFloor(5);


Serial.print("Tuning: ");
Serial.println(mod1016.getTuneCaps(), HEX);
Serial.print("Innen/Aussen: ");
Serial.println(mod1016.getAFE(), BIN);
Serial.print("Rauschen: ");
Serial.println(mod1016.getNoiseFloor(), HEX);

delay(2000);

// Ueber IRQ_pin erfolgen Gewittermeldungen
pinMode(IRQ_pin, INPUT);
attachInterrupt(digitalPinToInterrupt(IRQ_pin), alert, RISING);
Serial.println("ISR fuer Gewittererkennung wurde eingetragen");
}


////////////////////////////////////////////////////////////////
//
// loop()
// Ereignisse feststellen und verarbeiten
//
////////////////////////////////////////////////////////////////
void loop()
{
if (!client.connected()) { // MQTT-Verbindung steht nicht mehr =>
reconnectMQTT();
}
if (detected) { // Gewitter wurde gemeldet
// Echtes Gewitter oder andere Ursache?
String msg = translateIRQ(mod1016.getIRQ());
// String in char[] verwandeln:
const int BUFSIZE = 64;
char buf[BUFSIZE];
Serial.println(msg); // Ausgabe zu Debugzwecken
msg.toCharArray(buf, BUFSIZE);
// Nachricht auf MyWeatherChannel publizieren
client.publish("MyWeatherChannel", buf);
detected = false; // Variable wieder auf Ausgangswert zuruecksetzen
}
client.loop(); // weiter geht es
}


////////////////////////////////////////////////////////////////
//
// alert()
// wird aufgerufen wenn MOD-1016 potenzielles Gewitter per
// externem Interrupt an Pin 2 meldet. Es handelt sich also
// um die Interruptbehandlungsroutine
//
////////////////////////////////////////////////////////////////
void alert() {
detected = true;
Serial.println("Event detected");
client.publish("MyWeatherChannel","Event detected");
}

////////////////////////////////////////////////////////////////
//
// translateIRQ()
// nimmt vom Board zurueckgelieferten Wert und meldet
// ob Ereignis nur Noise war,
// eine Stoerquelle vorliegt,
// oder ein echtes Gewitter entdeckt wurde
//
////////////////////////////////////////////////////////////////

String translateIRQ(uns8 irq) {
switch(irq) {
case 1:
return "Rauschen entdeckt";
break;
case 4:
return "Stoerquelle entdeckt";
break;
case 8:
return String("Gewitter entdeckt - ") + String(distance());
break;
}
}


////////////////////////////////////////////////////////////////
//
// distance()
// ermittelt die Entfernung des Gewitters
//
////////////////////////////////////////////////////////////////
String distance() {
int distance = mod1016.calculateDistance();
if (distance == -1)
return "Gewitter ausserhalb der Reichweite";
else if (distance == 1)
return "Distanz nicht ermittelbar";
else if (distance == 0)
return "Gewitter direkt oberhalb";
else { // genauere Distanz ermitteln:
return String("Gewitter ist etwa ")
+ String(distance)
+ String(" km entfernt");
}
}

10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0

(Bild: wikipedia.org)

Da nun alle Teile komplett vorliegen, lautet die Startsequenz für unser neues Internet-der-Dinge-Ding:

  1. mosquito starten (am besten über MQTT.fx)
  2. Über MQTT.fx eine Warteschlange namens MyWeatherChannel einrichten
  3. Über das Terminal bzw. die Konsole das JavaScript-Programm starten: node <name-javascript-programm>.js
  4. Sketch über Arduino IDE auf Arduino übertragen und starten
  5. Tagelang auf Gewitter warten

Das Projekt ist noch ausbaufähig. Schön wäre es beispielsweise, zu Testzwecken Gewitter simulieren zu können. Zumindest können Sie aber Störquellen simulieren, indem Sie zum Beispiel einen starken Magneten oder Mikrowellen in die Nähe der Antenne bringen.

Zusammenfassung

Was diese Folge auf Basis eines Franklin-Gewittersensors demonstriert hat, lässt sich auch für andere Zwecke einsetzen: Ein Sketch liest über ein Arduino-Board angeschlossene Sensoren aus, verpackt die Messdaten als MQTT-Nachrichten und versendet sie an einen lokalen MQTT-Server, von wo ihre Weiterleitung an Twitter erfolgt. Natürlich könnten auf dem umgekehrten Weg auch Nachrichten zum Gerät gelangen, etwa Befehle an die Lichtsteuerung zu Hause.

Zum Schluss noch ein paar Informationen für die, die sich speziell für das Thema Gewitterwarner interessieren. Es gibt auch Standalone-Gewitterwarner zu kaufen. Von ELV existiert ein Bausatz für ein Gerät, das ebenfalls auf dem AS3935 basiert. Vom Franzis-Verlag werden Bausätze angeboten, die einen simplen Radioempfänger verwenden.

Jedenfalls sollten Sie jetzt in der Lage sein, zukünftige Donnerwetter auch über weitere Entfernungen rechtzeitig aufzuspüren.

Der Gewitterwarner GW1 von ELV enthält einen AS3935 (Bild: ELV)