Gartenbewässerung: Arduino Relais per 1-Wire steuern

Auch draußen im Garten gibt es einiges zu tun. Um uns das regelmäßige gießen zu ersparen, soll diese Aufgabe von der Haussteuerung übernommen werden. Da in der Garage sowieso schon ein 1-Wire Kabel mit Sensor zur Temperaturüberwachung liegt und dort auch die Wasserpumpe angeschlossen ist, ist es naheliegend, auch die Bewässerung per 1-Wire zu steuern. Ursprünglich sollte es dazu eine Komponente von Esera werden, doch diese hat mit unserem Pokey 1-Wire Master leider nicht kommuniziert. Deswegen kam die Idee, das Ganze mit dem Arduino als 1-Wire Slave umzusetzen. Wie das geht, zeigt dieser Eintrag.

Hardware-Komponenten

Als erstes muss natürlich im Garten die Technik verbaut sein, die die Bewässerung ermöglicht. Neben zahlreichen Schläuchen und einigen Tropfern sind dies vor allem Magnetventile von Hunter, die die Wasserzufuhr regeln. Diese Ventile werden mit 24V Wechselstrom gesteuert, wofür wir ein zentrales Netzteil benötigen. Liegt Spannung an so öffnet sich das Ventil.

Relais-Board

Um die Ventile zu steuern verwenden wir pro Ventil ein Relais. Für den Arduino gibt es verschiedene fertige Relais-Platinen, die die Aufgabe meistern. Wir haben eine gewählt, die eine 5V Steuerspannung für die Relais verwendet. Dies ist praktisch da der Arduino auch mit 5V versorgt wird. So reichen uns die 5V Versorgungsspannung aus dem aus dem 1-Wire Kabel, um das Relaisboard und den Arduino zu betreiben. Ein weiteres Netzteil ist somit nicht erforderlich. Da wir nur 24 AC schalten wollen verzichten wir auf eine galvanische Trennung.

Arduino Nano

Um das Ganze in der Garage anbauen zu können, haben wir eine Hutschiene montiert und benötigen für das Relais und den Arduino eine Halterung. Für den Arduino Nano haben wir dabei ein Breakout Board gefunden, welches alle Pins in Schraubklemmen umwandelt und zusätzlich auch Löcher für eine Hutschienen-Halterung mitbringt.

Als Verbindungskabel vom Arduino Breakout Board zum Relais-Board nutzen wir ein Flachband-Kabel. Das 1-Wire Kabel wird direkt an den Arduino angeschlossen und versorgt auch gleich das Relais-Board.

Des weiteren wollen wir eine Funksteckdose, zur Ansteuerung der Wasserpumpe, einbinden. Hierzu verwenden wir ein Funkmodul für den Arduino.

Hier nochmal die Hardware zusammengefasst:

  • Arduino Nano (Clone)
  • Breakout Board mit Schraubklemmen für den Arduino Nano
  • Board mit 16 Relais (5V Steuerspannung) für Arduino
  • 433Mhz Funkmodul (fs1000a)
  • Hunter-Ventile für die Schaltung der Bewässerungsstränge
  • 24V AC Netzteil für die Hunter-Ventile
  • Hutschiene und Hutschienenhalterung für alle Komponenten
  • jede menge Kabel

Schema

Fritzing Schema für die Verbindung von Arduino mit dem Relais Board, dem Funkmodul und 1-Wire
Die Pinbelegung der Arduino Pins zum Relais haben sich bei uns Aufgrund der Einbaurichtung ergeben. Die Schraubklemme, für externe Spannungsversorgung, ist auf dem Relaisboard ist nicht verbunden.

Die Arduino Software

Die Software besteht im wesentlichen aus 4 Komponenten:

  • Relais Logik
  • 1-Wire Slave Simulation
  • Steuerung der Funksteckdose
  • LED zur Anzeige von 1-Wire Kommunikation

Relais Logik

Die Schaltung der Pins im Arduino ist sehr einfach. Auf Basis der Relais Library von Rafael haben wir das Ganze umgesetzt. Dazu haben wir die Bibliothek noch etwas vereinfacht, da wir immer nur Normally-Open Relais haben. Eine Funktion zum setzen des Zustandes ist auch noch dazu gekommen.

Wir erstellen dazu ein Array von Relay Objekten und weisen den entsprechenden Arduino Pin zu. Pin 13 verwenden wir nicht, da dieser mit der L_LED verbunden ist. Pin 2 ist für das Funkmodul (Funksteckdose) reserviert. Ein einfaches Testprogramm kann so aussehen:

#include "Relay.h"

#define NUMBER_OF_RELAYS        16

// Relais Objekte anlegen und Pin zuweisen
Relay relays[NUMBER_OF_RELAYS] = {A5, A4, A3, A2, 12, 11, 10, 9, A1, A6, 3, 4, 5, 6, 7, 8};

void setup() 
{
  for (int i = 0; i < NUMBER_OF_RELAYS; i++)
    relays[i].init();
}

void loop() 
{
  // Beispiel Relais 1 einschalten
  relays[1].setRelay(true);
  delay(1000);
  relays[1].setRelay(false);
  delay(1000);  
}

1-Wire Slave Simulation

Etwas aufwändiger ist die Simulation des 1-Wire Slaves. Hierfür nutzen wir die OneWireHub Bibliothek. Diese kann unterschiedliche 1-Wire Slaves simulieren. Wir haben uns für die Simulation des DS2408 entschieden, da dieser dazu dient, 8 Kanäle lesen und schreiben zu können. Da wir insgesamt bis zu 16 Relais schalten wollen, simulieren wir einfach zwei DS2408. Jeder Kanal des simulierten DS2408 wird genau einem Relais zugeordnet. Für die Basisfunktionalität des DS2408 reicht folgender Beispielcode:

#include "OneWireHub.h"
#include "DS2408.h"
#define NUMBER_OF_DS2408_PINS   8

constexpr uint8_t pin_onewire   { A0 };

auto hub = OneWireHub(pin_onewire);
// 1-Wire Slaves mit Adressen definieren:
auto ds2408_1 = DS2408( DS2408::family_code, 0x00, 0x00, 0x20, 0x24, 0xda, 0x00);
auto ds2408_2 = DS2408( DS2408::family_code, 0x00, 0x00, 0x21, 0x24, 0xda, 0x00);

void setup() 
{
// DS2408 Ausgänge initialisieren
  for (int i = 0; i < NUMBER_OF_DS2408_PINS; i++)
  {
      ds2408_1.setPinState(i, false);
      ds2408_2.setPinState(i, false);
  }
          
  hub.attach(ds2408_1);
  hub.attach(ds2408_2);
}

void loop() 
{
  // following function must be called periodically
  hub.poll();
  // this part is just for debugging (USE_SERIAL_DEBUG in OneWire.h must be enabled for output)
  if (hub.hasError()) hub.printError();
}

Um die 1-Wire Slaves auf ankommende Befehle zu überwachen wird in der Loop Schleife der Zustand dauerhaft abgefragt. Wenn ein Schreib-Befehl ankommt, in unserem Beispiel also der Befehl „0xA5“ (Write), wird das passende Relais gesetzt. Wir haben die OneWireHub Bibliothek, bzw. die enthaltet DS2408 Simulation etwas erweitert: Die neue Funktion getLastReceivedCmd() liefert das letzte empfange 1-Wire Kommando zurück. Immer wenn das ein 1-Wire „Write“ Kommando ankommt so führen wir den Mappingcode aus. Das die ersten 8 Relay Objekte (also Index 0 bis 7) werden dem ersten DS2408 zugeordnet. Die restlichen Objekte dem zweiten.

Die Funktion getPinState() liefert ein BYTE zurück. Jedes Bit entspricht einen Kanal im DS2408. Je nach Zustand wird das entsprechende Relaisobjekt aktiviert oder deaktiviert. Folgender Code zeigt wie der DS2408 mit den Relais „verbunden“ werden können:

void loop() 
{
    hub.poll();
    uint8_t u8Ds1LastReceivedCmd = ds2408_1.getLastReceivedCmd();
    uint8_t u8Ds2LastReceivedCmd = ds2408_2.getLastReceivedCmd();
  
    if (u8Ds1LastReceivedCmd == 0x5A || u8Ds2LastReceivedCmd == 0x5A)
    {
        uint8_t u8Ds1NewPinState = ds2408_1.getPinState(); 
        uint8_t u8Ds2NewPinState = ds2408_2.getPinState(); 
        uint8_t u8PinBit = 1;

        for (int i = 0; i < NUMBER_OF_DS2408_PINS; i++)
        { 
             if (u8Ds1NewPinState & u8PinBit) 
                   relays[i].setRelay(true);  
             else
                   relays[i].setRelay(false);  

             if (u8Ds2NewPinState & u8PinBit) 
                   relays[8+i].setRelay(true);  
             else
                   relays[8+i].setRelay(false);  

             u8PinBit <<= 1;
        }
    }

Dies ist die einfachste Variante der Relaissteuerung und wir haben sie im fertigen Programm erweitert. So ist es sinnvoll, die Anzahl der parallel geschalteten Relais zu begrenzen (bei zu viel geöffneten Magnetventile würde der Wasserdruck zu stark abfallen).

Steuerung der Funksteckdose

Weiterhin wollen wir gerne noch die Wasserpumpe schaltbar machen. Wir haben uns dabei für eine einfache Funksteckdose entschieden, da die Pumpe am anderen Ende der Garage ist. Diese funktionieren über 433Mhz und es gibt entsprechende Funkmodule (fs1000a und mx-rm-5V) für den Arduino für sehr wenig Geld zu kaufen. Das „mx-rm-5V“ Empfangsmodul verwenden wir einmalig um das Funkprotokoll der Steckdose auszulesen. Auf dem Modul ist bereits eine sehr kleine Antenne aufgelötet. Zumindest in unseren Fall reicht die Reichweite aus. Die passende Bibliothek findet ihr in der Bibliotheksverwaltung unter „Rc-Switch“.

Wie man das in wenigen Minuten einrichtet ist in diesem Blog sehr gut beschrieben: https://daniel-ziegler.com/arduino/mikrocontroller/2017/06/16/Funksteckdose-arduino/

Dieses kleine Beispiel schaltet die Funksteckdose in der Schleife kurz ein und wieder aus:

/*
  Example for different sending methods
  https://github.com/sui77/rc-switch/
*/

#include <RCSwitch.h>
RCSwitch sender = RCSwitch();

void setup() {
  Serial.begin(9600);
  sender.enableTransmit(2);  // An Pin 2

  sender.setProtocol(1);
  sender.setPulseLength(350);

  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {

      // sende "ein" Kommando (für spezifische Funksteckdose)
      digitalWrite(LED_BUILTIN, HIGH);
      sender.sendTriState("000000FFFF0F");

      delay(1000);

     // sende "aus" Kommando (für spezifische Funksteckdose)
      digitalWrite(LED_BUILTIN, LOW);
      sender.sendTriState("000000FFFFF0");

      delay(1000);
}

LED zur Anzeige von 1-Wire Kommunikation

Außerdem verwenden wir noch die auf den Arduino Board aufgelötet L_LED um einen Blinkcode, jeweils für 1-Wire „Lesen“ (kurz, kurz, kurz, kurz) und „Schreiben“ (lang, lang) anzuzeigen. Ähnlich wie es bei Netzwerkbuchsen der Fall ist. Im Beispielcode testen wir den Blinkcode für Schreiben:

bool      bEnableBlining;
uint8_t   u8BlinkingCounter;
uint8_t   u8CurrentBlinkingCounter;
uint8_t   u8LedState;
uint8_t   u8LedTiming;
uint32_t u32NextMillis;
uint8_t   u8Ds1LastReceivedCmd;

void      StatemachineBlinking(void);
void      IndicateCmdBlinking(uint8_t u8NewCmd);

void setup() 
{
  // L_LED (Red)
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  bEnableBlining = false;

  // test 1-wire "Write"
  u8Ds1LastReceivedCmd = 0x5A;
}

void loop() 
{
  IndicateCmdBlinking(u8Ds1LastReceivedCmd);
  u8Ds1LastReceivedCmd = 0;
  StatemachineBlinking();
}

void IndicateCmdBlinking(uint8_t u8NewCmd)
{
  if (bEnableBlining == false) // is finished?
  {
    if (u8NewCmd == 0xF0 || u8NewCmd == 0xF5) // OneWire Read PIO Registers or Channel-Access Read
    {      
      u8LedTiming = 50;      
      u8BlinkingCounter = 4;
      bEnableBlining = true;
    }
    else if (u8NewCmd == 0x5A) // OneWire Channel-Access Write
    {           
      u8LedTiming = 200;      
      u8BlinkingCounter = 2;
      bEnableBlining = true;
    }

    if (bEnableBlining == true)
    {
      u32NextMillis = millis() + u8LedTiming; 
      u8LedState = HIGH;
      digitalWrite(LED_BUILTIN, u8LedState);
      u8CurrentBlinkingCounter = 0;
      bEnableBlining = true;    
    }
  }  
}

void StatemachineBlinking(void)
{
  if (bEnableBlining == true)
  {
    if (millis() > u32NextMillis)
    {
      if (u8LedState == HIGH)
        u8LedState = LOW;
      else
        u8LedState = HIGH;
      
      u32NextMillis = millis() + u8LedTiming;
      u8CurrentBlinkingCounter++;
      if (u8CurrentBlinkingCounter > u8BlinkingCounter)
      {
        u8LedState = LOW;
        bEnableBlining = false; // finished
      }
      digitalWrite(LED_BUILTIN, u8LedState);
    }
  }
}

Das komplette Programm für den Arduino mit allen Komponenten findet ihr am Ende des Blogs.

Inbetriebnahme über 1-Wire

Bevor wir den Arduino in die Garage verbauen und in unser 1-Wire Netztwerk bringen, wollen wir diesen erst noch richtig testen. Für den Test auf dem Schreibtisch haben wir als erstes das Tool OneWire Viewer (1-Wire Master) vom Maxim Integrated ausprobiert. Einfache 1-Wire Slaves wie dem DS18B20 (Temperatur) hat das Tool auch erkannt. Komplexere Chips wie den DS2408 haben wir damit aber leider nicht zum Laufen bekommen. Außerdem hat unser 1-Wire USB Adapter mit der Software unter Windows 10 nicht mehr funktioniert. Anscheinend ist es nur zu Windows 7 kompatibel. Also ist der OneWire Viewer nicht mehr zu empfehlen.

Daher sind wir sehr schnell auf den Pokeys als 1-Wire Master gewechselt. Dieser unterstützt in der Software (Pokeys) leider auch nicht den DS2408. Aber per 1-Wire über UDP sprechen konnten wir so notwendigen Befehle selbst zusammenbauen und erfolgreich mit Arduino kommunizieren. Wie das ganze in Python aussieht, folgt in Kürze.

Das fertige Programm auf Github:

https://github.com/blog-h7d/1wire-irrigation

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.