In diesem Sketch wird der FS20WUE mit seiner Voreinstellung - der Binärausgabe der Messdaten - betrieben. Im Programm müssen die Zeichen der seriellen Schnittstelle empfangen und ausgewertet werden. Abhängig von der Auswertung werden dann lesbare Texte auf dem Serial Monitor ausgegeben. Dieser Sketch ist damit die Vorstufe für einen eigene, komfortable Wetterstation.

Der Sketch läuft auf Arduino Uno und Leonardo (Mega2560 habe ich nicht getestet).

Hardware

Seit kurzem bietet Fa. ELV ein Empfängermodul für seine Wettersensoren. Es hat einen UART, der Befehle empfängt und Wetterdaten ausgibt. Dieser UART kann mit dem UART des Arduino verbunden werden. Beim FS20WUE müssen lediglich zwei fertig bestückte SMD-Platinen mit 4 Kontaktstiften zusammengelötet werden. Dieses Modul wird dann auf einem Protoshield eingelötet, welches auf den Arduino gesteckt wird. Beim Uno werden die Digital Pins 10 (Rxd) und 11 (TxD) verwendet:

FS20WUE Bestückungsplan

Die Ausgänge 1 und 2 von Stecker ST2 können an Arduino auch mit Eingängen für externe Interrupts verbunden werden. Sobald ein FS20- oder Wetterpaket eintrifft, wird ein Interrupt ausgelöst und die Abarbeitung der verschiedenen Pakete kann begonnen werden. In diesem Sketch werden keine Interrupts, sondern Polling verwendet.

Im Sketch sind Hinweise, welche anderen Pins geeignet sind.

Die benötigten Teile (Stand 08/2012):

Hersteller/Produkt Preis Firma  Link
Arduino Leonardo 21,30 € Physical Computing Link
Prototype Shield Uno R3 13,20 € Physical Computing  Link
ELV-Empfänger FS20WUE 14,95 € ELV  Link
ELV-Kombisensor KS300-4 69,95 € ELV Link
ELV-Sensor S300TH 14,95 €  ELV Link 
Summe: 134,35 €    

 

FS20WUE auf Arduino Uno

 

 

 

 

Der FS20WUE steckt in Buchsenleisten, welche auf das Protoshield gelötet wurden. Der rote Draht ist die Antenne, welche durch die mitgelieferten Drahthalter von der Platine ferngehalten wird.

FS20WUE auf Arduino Uno (2)

 

 

 

 

 

Man kann auch niedrigere Buchsenleisten nehmen, dann wid das Sandwich nicht so hoch.

FS20WUE auf Arduino Uno (3)

 

 

 

 

 

Links unten sind die zwei Leuchtdioden zu sehen, welche den Empfang von Wetterdaten und FS20-Paketen anzeigen. Die Interruptausgänge des FS20WUE finden keine Anwendung.

 

Funktionen

Der FS20WUE wird in diesem Sketch auf Binärausgabe eingestellt und so konfiguriert, dass er Staus-, FS20 und Wetterdaten empfangen kann. Alle empfangenen Pakete werden dekodiert und mit Meldungen auf dem Serial Monitor der IDE 1.0 oder einem Terminalprogramm ausgegeben. Dafür benötigt man auf dem Arduino zwei serielle Schnittstellen. Die Hardware-Schnittstelle wird für die Sketchausgabe auf dem PC verwendet, während man einen weiteren seriellen Port über zwei Digital-Pins unter Zuhilfenahme der Bibliothek "SoftSerial" aus der IDE bildet. Beide seriellen Ports sind auf 4800Baud eingestellt, dies ist die Voreinstellung des FS20WUE. Die von ELV gelieferte Beschreibung hat in den Befehlstabellen einige Tippfehler, man kann aber durch den Beschreibungstext herausfinden, was gemeint ist. In diesem Sketch sind die korrigierten Befehle verwendet worden.

FS20WUE-Protokoll

Das Protokoll der FS20WUE liefert bei Binärausgabe vier verschiedene Pakettypen zurück. Jedes der farbigen Kästchen symbolisiert ein Byte, die Zahlen im Byte sind der Hexcode (0x=00...0F, XX=00...FF). Alle Pakete haben einen Header, welcher aus 3 Byte besteht

  • Start-Byte 0x02
  • Paketlänge (ohne Start und Length)
  • ID (Unterscheidung der Pakettypen)

Paket Status

Der Status wird vom FS20WUE automatisch gemeldet, wenn in einem Kommando ein Fehler war, oder wenn er mit einem eigenen Befehl angefordert wird.

FS20-Pakete haben die ID 0xA1, gefolgt von Hauscode, Adresse, Kommando und Kommandoerweiterung.

Sensoren vom Typ 1 können nur Temperatur und Luftfeuchtigkeit liefern. Die letzten fünf Bytes sind deshalb nur Platzhalter (Dummies), um die Paketauswertung zu vereinfachen. Von diesen Sensoren können maximal 8 Stück eingesetzt werden: Typ=1/Adresse=0 bis Typ=1/Adresse=7.

FS20-Paket

Es kann nur einen Sensor vom Typ 7 geben, der die unveränderliche Adresse 0x01 hat. Dies wurde von ELV so festgelegt. Bei diesem Kombisensor sind alle Messwerte verfügbar und können ausgewertet werden. "Rain Amount" ist die kumulierte Regenmenge in Wippenschlägen (je 295ml). "Rain" ist die Regen-Soforterkennung (0=kein Regen, 1=Regen).

Software

Die Software muss mit der IDE 1.01 oder höher kompiliert werden. Sie läuft auf den Arduinos Uno und Leonardo. Den Mega 2560 habe ich nicht getestet, weil er zu teuer für dieses kleine Programm ist.

Sensorenzuordnung

Der Sketch ist so aufgebaut, dass er die Maximalzahl von Funksensoren unterstützt. Laut ELV fürfen dies ein Kombisensor und bis zu 8 einfache Sensoren sein. Die Messwerte liegen in einem Array, dessen Index die Sensornummer ist:

Sensor# Typ Addresse Sensor
0 1 0 S300TH u.a.
1 1 1 S300TH u.a.
2 1 2 S300TH u.a.
3 1 3 S300TH u.a.
4 1 4 S300TH u.a.
5 1 5 S300TH u.a.
6 1 6 S300TH u.a.
7 1 7 S300TH u.a.
8 7 1 KS200/300

Jeder der Messwerte Temperatur, Feuchte, Wind, Regen und Sofortkennung Regen sind in Arrays mit 9 Elementen untergebracht, auch wenn dies bei den einfachen Sensoren Platzverschwendung ist.

Sketch

Der Sketch ist relativ umfangreich, weil er jede Menge Ausgaben auf dem Serial Monitor vornimmt. Für die Demo ist dies jedoch sinnvoll.

/*********************************************************************************************** 
 * Sketch:  DemoWetterdatenReceiver_02.ino
 * Author:  A. Kriwanek: http://www.kriwanek.de/arduino/wetterstation.html
 * Version: 1.00  14.08.2012/20:19
 *
 * This sketch controls the weather data receiver FS20 WUE from ELV Electronics. This radio
 * receiver is able to decode the weather sensors KS200/KS300, S300IA, S300TA, AS200 and PS50.
 * Additional it receives messages from FS20 senders (e.g. switches). This sketch sets the
 * FS20WUE parameters for receiving weather and FS20 data and displaying them on the serial
 * monitor or any other terminal program. The FS20WUE commands are interpreted in hexadecimal
 * form (not as text output)
 *
 * Rain-Sensor in KS300: 1 count = 295ml/m²
 * Hardware Serial: Send received data to PC with terminal program
 * SoftSerial: Receives weather and FS20 data from FS20WUE (Pin 1
 * RxD is digital pin 10 (connect to TX of FS20WUE)
 * TxD is digital pin 11 (connect to RX of FS20WUE)
 *
 * Note:
 * Not all pins on the Mega and Mega 2560 support change interrupts, 
 * so only the following can be used for RxD: 
 * 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69
 *
 * Not all pins on the Leonardo support change interrupts, 
 * so only the following can be used for RxD: 
 * 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).
 * 
 * This sketch is free software: Arduino DemoWetterdatenReceiver_02 von Andreas Kriwanek steht unter 
 * einer Creative Commons Namensnennung-Weitergabe unter gleichen Bedingungen 3.0 Unported Lizenz.
 *
 **********************************************************************************************/
#include <SoftwareSerial.h>             // From IDE 1.01

//#define DEBUG_SKETCH                    // Uncomment this for debugging!

//=============================================================================================
// Define values and create objects:
//---------------------------------------------------------------------------------------------
SoftwareSerial mySerial(10, 11);        // RxD, TxD

byte rawData[14];                       // Array for holding the received bytes
byte rawPointer;                        // Pointer to raw data array
byte maxPointer;                        // Maximum pointer length of a received package
char rawChar;                           // Received binary chaaracter
byte rawLength;                         // Lentgh of data container in FS20WUE telegram
byte packageType;                       // 0= invalid, 1=Status, 2=Weather, 3=FS20

byte sensorNumber;                      // Number of sensor 0...8
float Temp[9];                          // Temperature sensor 0...8
float Hygro[9];                         // Humidity sensor 0...8
float Rain[9];                          // Rain im mm
byte RainFall[9];                       // Rain=1, no rain=0
float Speed[9];                         // Windspeed in km/h

byte i;                                 // Helper variable

// Commands for the ELV Weather Receiver FS20WUE. There are some typos in the documentation of the
// receiver. This are the right commands:

char* cmdStat  = "\x02\x01\xF0";        // Returns status of weather data receiver
char* cmdWDOn  = "\x02\x02\xF2\x01";    // Receive weather data and transmit immediately
char* cmdFSOn  = "\x02\x02\xF1\x01";    // Receive FS20 data and transmit immediately
char* cmdText  = "\x02\x02\xFB\x01";    // All output in text format
char* cmdBin  = "\x02\x02\xFB\x00";     // All output in binary format

//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
void setup()  
{
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  Serial.println(">>> Weather Data Receiver FS20WUE (Binary Mode) <<<");

  // Set the FS20WUE default data rate on the SoftwareSerial port
  mySerial.begin(4800);
  delay(100);
  mySerial.print(cmdWDOn);              // Switch on weather packet receive
  delay(10);                            // Always use 10msec delay after each command
  mySerial.print(cmdFSOn);              // Switch on FS20 packet receive
  delay(10);                            // Always use 10msec delay after each command
  mySerial.print(cmdStat);              // Send status inquiry
  delay(10);                            // Always use 10msec delay after each command

  rawPointer = 0;                       // Reset pointer
  rawLength = 0;
  maxPointer= 0;
  packageType = 0;
  Serial.println("FS20WUE parameters loaded, waiting for data...");
  Serial.println("=============================================================================");
  delay(3000);
}
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
void loop()
{
  if (mySerial.available()){             // New character available
    receiveData();
  }
  if (Serial.available()) {              // Read from PC serial interface (terminal emulation)
    mySerial.write(Serial.read());       // Write to FS20WUE
  }
}
//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
void receiveData(void) {
  rawChar = mySerial.read();                       // Read character from FS20WUE

  if(rawPointer>=1) {                              // For all other characters after start byte:
    rawData[rawPointer] = byte(rawChar);           // Write received character into array
    rawPointer += 1;                               // Increment data array pointer
#ifdef DEBUG_SKETCH
    Serial.println("Data received:");
#endif
  }

  if(rawPointer==0 && rawChar == 2) {              // Start of new packet detected:
    rawData[0] = 0x02;                             // Set start character
    rawPointer += 1;                               // Increment pointer
#ifdef DEBUG_SKETCH
    Serial.println("Start Byte detected:");
#endif
  }

  if(rawPointer==3) {                              // Time for valid header check:
    if(rawData[0]==0x02 && rawData[1]==0x05 && rawData[2]==0xA0) {
      //Valid header for status package
      packageType = 1;
      maxPointer = 6;
#ifdef DEBUG_SKETCH
      Serial.println("Valid Status Packet Header detected:");
#endif
    } 
    if(rawData[0]==0x02 && rawData[1]==0x06 && rawData[2]==0xA1) {
      //Valid header for FS20 package
      packageType = 2;
      maxPointer = 7;
#ifdef DEBUG_SKETCH
      Serial.println("Valid FS20 Packet Header detected:");
#endif
    } 
    if(rawData[0]==0x02 && rawData[1]==0x0C && rawData[2]==0xA2) {
      //Valid header for weather package
      packageType = 3;
      maxPointer = 14;
#ifdef DEBUG_SKETCH
      Serial.println("Valid Packet Header detected:");
#endif
    } 
    if(packageType==0) {
      packageType = 0;
      rawPointer = 0;
      maxPointer = 0;
#ifdef DEBUG_SKETCH
      Serial.println("Invalid Packet Header detected:");
#endif
    }
  }
  // For debugging only:
#ifdef DEBUG_SKETCH
  Serial.print("Received Byte=");
  Serial.print(byte(rawChar),HEX);
  Serial.print(", rawPointer=");
  Serial.print(rawPointer-1);
  Serial.print(", packageType=");
  Serial.println(packageType);
  Serial.println("--------------------------------------------------------------");
#endif
  if(rawPointer==7  && packageType==1) printSData();        // Print status data
  if(rawPointer==8  && packageType==2) printFData();        // Print FS20 data
  if(rawPointer==14 && packageType==3) printWData();        // Print weather data
}

//---------------------------------------------------------------------------------------------
void printSData() {
  // Printout of received bytes:
#ifdef DEBUG_SKETCH
  Serial.print("Binary Status Packet: ");
  for(i=0; i<=6;i++) {
    Serial.print(rawData[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif

  switch (rawData[3]) {
  case 0:  // Status
    Serial.print("Status=OK");
    break;
  case 2:
    Serial.print("Status=Unknown command ID");
    break;
  case 3:
    Serial.print("Status=Wrong command length");
    break;
  case 4:
    Serial.print("Status=Invalid parameter");
    break;
  case 6:
    Serial.print("Status=Invalid start byte");
    break;
  case 8:
    Serial.print("Status=Package timeout");
    break;
  default: 
    Serial.print("Status=Unknown number");
  }
  switch (rawData[4]) {
  case 0:  // Status
    Serial.print(", Baudrate=4800bps");
    break;
  case 1:
    Serial.print(", Baudrate=9600bps");
    break;
  case 2:
    Serial.print(", Baudrate=14.400bps");
    break;
  case 3:
    Serial.print(", Baudrate=19.200bps");
    break;
  case 4:
    Serial.print(", Baudrate=38.400bps");
    break;
  default: 
    Serial.print(", Baudrate=unknown");
  }
  Serial.print(", FS20 packets=");
  Serial.print(rawData[5],DEC);
  Serial.print(", Weather packets=");
  Serial.println(rawData[6],DEC);
  Serial.println("=============================================================================");
  packageType = 0;
  rawPointer = 0;
}

//---------------------------------------------------------------------------------------------
void printFData() {
  // Printout of received bytes:
#ifdef DEBUG_SKETCH
  Serial.print("Binary FS20 Packet: ");
  for(i=0; i<=7;i++) {
    Serial.print(rawData[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif

  Serial.print("HouseCode=");
  Serial.print(rawData[3],HEX);
  Serial.print(rawData[4],HEX);

  Serial.print(", Address=");
  Serial.print(rawData[5],HEX);

  Serial.print(", Command=");
  Serial.print(rawData[6],HEX);

  Serial.print(", CommandExtension=");
  Serial.println(rawData[7],HEX);
  Serial.println("=============================================================================");

  packageType = 0;
  rawPointer = 0;
}

//---------------------------------------------------------------------------------------------
void printWData() {
  if(rawData[3]==1) sensorNumber = rawData[4];                       // All other sensors
  if(rawData[3]==7) sensorNumber = 8;                                // Sensor KS300

  Temp[sensorNumber]  = float(int(rawData[5])*256 + rawData[6])/10;  // Temperature of sensor 0...8, 2's Complement!
  Hygro[sensorNumber] = float(rawData[7]*256 + rawData[8])/10;       // Humidity sensor 0...8
  Speed[sensorNumber] = float(rawData[9]*256 + rawData[10])/10;      // Wind speed sensor 0...8
  Rain[sensorNumber]  = float(rawData[11]*256 + rawData[12])*0.295;  // Rain amount sensor 0...8
  RainFall[sensorNumber] = rawData[13];                              // Rain fall flag sensor 0...8

  // Printout of received bytes:
#ifdef DEBUG_SKETCH
  Serial.print("Binary Weather Packet: ");
  for(i=0; i<=13;i++) {
    Serial.print(rawData[i],HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif

  // Printout of calculated values:
  if(rawData[3]==7) Serial.print("Sensor=KS200/KS300");
  if(rawData[3]==1) Serial.print("Sensor=S300TH/S300IA/ASH2200/PS50");
  Serial.print(", Type=");
  Serial.print(rawData[3],HEX);
  Serial.print(", Address=");
  Serial.print(rawData[4],HEX);
  Serial.print(", SensorNumber=");
  Serial.println(sensorNumber,DEC);
  Serial.print("Temp=");
  Serial.print(Temp[sensorNumber],1);
  Serial.print("C, rel.Hum.=");
  Serial.print(Hygro[sensorNumber],1);
  Serial.print("%");
  if(rawData[3]==7){
    Serial.print(", Wind=");
    Serial.print(Speed[sensorNumber],1);
    Serial.print("km/h, Rain=");
    Serial.print(Rain[sensorNumber],1);          // 295ml/m² per pulse
    Serial.print("Liter, ");
    if(RainFall[sensorNumber]==0) Serial.println("No Rain");
    if(RainFall[sensorNumber]==1) Serial.println("Raining");
  } 
  else {
    Serial.println();
  }
  Serial.println("=============================================================================");
  packageType = 0;
  rawPointer = 0;
}

Ausgabe auf dem Serial Monitor

Nach dem Öffnen des Serial Monitor beginnt der Programmstart. Im Setup() wird der FS20WUE konfiguriert und eine Statusmeldung abgegeben. Anschliessend folgen Ausgaben der empfangenen Sensoren (im unteren Bild farbig markiert):

Ausgabe des Programms auf dem Serial Monitor

Sketchausgabe

Statusausgabe FS20WUE

FS20-Schaltbefehl

 

FS20-Schaltbefehl

 

Ausgabe Sensor 7, Adresse 1

Ausgabe Sensor 7, Adresse 1

Ausgabe Sensor 7, Adresse 1

 

Ausgabe Sensor 1, Adresse 0

Diese formatierte Ausgabe wird durch den FS20WUE vorgenommen, der in den Textmodus geschaltet wurde. Die Sprache der FS20WUE-Ausgabe lässt sich nicht umstellen, die Ausgabe ist immer in Deutsch. Der Arduino-Sketch dazu ist minimal. Diese Demo eignet sich gut für Reichweitentests oder um neu gekaufte Sensoren zu erproben.

Wenn man im Sketch bei

//#define DEBUG_SKETCH                    // Uncomment this for debugging!

die Kommentarzeichen "//" entfernt und neu kompiliert, dann wird die Ausgabe ausführlicher:

DEBUG-Anzeige des Sketch

Fazit

Der Empfänger ist sehr empfindlich und empfängt auch Daten von ungünstig plazierten Sensoren (andere Zimmer, geschlossene Jalousien) ohne Probleme. Alle ELV-Sensoren verwenden das selbe Ausgabeformat. Auch ein Sensor, der nur Temperatur und Luftfeuchtigkeit messen kann, gibt zusätzlich die anderen Werte aus (diese sind dann immer auf Null). In weiteren Sketches wird eine Ausgabe auf einem LCD-Display und auf der Website COSM.COM gezeigt. Diese Sketche sind in Arbeit!

Dieser Demosketch befindet sich im Downloadbereich!