Relais an Digitalausgang 0&1 programmieren

Hallo zusammen!

Das ist mein erster Post, also seid gnädig mit mir, wenn dieser Post oder auch der Code "holprig" sind.

Mein 11-jähriger Sohn hat zu Weihnachten unbedingt ein Arduino Starterset haben wollen und auch bekommen. Das letzte Mal, dass ich irgendwas programmiert habe, war vor ca. 25 Jahren im Studium, muss mich da also gerade mit ihm zusammen reinwurschteln.

Nach den ersten nachgebauten Tutorials will er nun eine Pflanzenbewässerung für 2 Pflanzen bauen. Wir habe das ganze in Teilen Relais ansteuern, Fühler auslesen, Fühlerwert auf deriellem Monitor und LCD Display ausgeben) gebaut und programmiert, und heute alles zusammengefügt.

Wenn einer der Feuchtefühler einen Schwellenwert (hier mal testweise 510) überschreitet, soll das dazugehörige Relais schalten.
Über Bewässerungsdauer etc und Abfrageintervalle machen wir uns später Gedanken...

Das Programm und unsere Schaltung funktioniert, wie gewünscht, aber nur an den digitalen Ausgängen 2&3 oder höher.

Nun haben wir folgendes Problem:
Schreibe ich das Programm um, auf Ausgänge 0&1, zieht Relais 1 nicht an und Relais 2 schaltet dauerhaft hin und her.
Kann man nicht gleichzeitig digitale Eingänge und Ausgänge mit der gleichen Ziffer nutzen?

Hier der Code:

// Kalibrieren der Erdfeuchtefühler
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

//-----Hardware Adressierung-----
LiquidCrystal_I2C lcd(0x27,16,2);

// Definition der analogen Eingänge
#define Fuehler1 A0     //analoger Eingang 0 bekommt den Namen Fuehler1
#define Fuehler2 A1     //analoger Eingang 1 bekommt den Namen Fuehler2

void setup() {
// Seriellen Monitor starten
Serial.begin(9600);

// LCD 16x2 initialisieren
lcd.init();

pinMode (A0, INPUT);
pinMode (A1, INPUT);
pinMode (2, OUTPUT);      //digitaler Ausgang Relais1 wird als Ausgabekanal definiert
pinMode (3, OUTPUT);      //digitaler Ausgang Relais2 wird als Ausgabekanal definiert
digitalWrite(0,HIGH);     //digitaler Ausgang Relais1 wird zum Start auf HIGH gesetzt, d.h. Relais ist ausgeschaltet
digitalWrite(3,HIGH);     //digitaler Ausgang Relais2 wird zum Start auf HIGH gesetzt, d.h. Relais ist ausgeschaltet


}

void loop() {
  // Analoge Eingänge auslesen
int Messwert1 = analogRead(A0);   //der analoge Wert von Fuehler1 wird ausgelesen (max 1023)
int Messwert2 = analogRead(A1);   //der analoge Wert von Fuehler1 wird ausgelesen (max 1023)

  // Schreibt den Messwert auf den seriellen Monitor
Serial.print("Messwert1: "); 
Serial.println(Messwert1);
Serial.print("Messwert2: ");
Serial.println(Messwert2);
  
  
  lcd.backlight();
 //Nachricht ausgeben
  lcd.setCursor(0,0);         //setzt den Cursor an die 1. Stelle der 1. Zeile des Displays
  lcd.print("Fuehler 1: ");   //schreibt das Wort "Fuehler1:" an diese Stelle
  lcd.setCursor(11,0);        //setzt den Cursor an die 11. Stelle der 1. Zeile des Displays
  lcd.print(Messwert1);       //schreibt den Messwert von Fühler1 an diese Stelle
  lcd.setCursor(0,1);         //setzt den Cursor an die 1. Stelle der 2. Zeile des Displays
  lcd.print("Fuehler 2: ");   //schreibt das Wort "Fuehler2:" an diese Stelle
  lcd.setCursor(11,1);        //setzt den Cursor an die 11. Stelle der 2. Zeile des Displays
  lcd.print(Messwert2);       //schreibt den Messwert von Fühler2 an diese Stelle

  
if (Messwert1 > 510)          //wenn der Messwert von Fühler1 über 510 ist 
{
  digitalWrite(2,LOW);  //wird das Relais1 eingeschaltet 
}
if (Messwert1 <= 510)         //wenn der Messwert von Fühler1 kleiner oder gleich 510 ist 
{
  digitalWrite(2,HIGH); //wird das Relais1 ausgeschaltet 
}


if (Messwert2 > 510)          //wenn der Messwert von Fühler2 über 510 ist 
{
  digitalWrite(3,LOW);  //wird das Relais2 eingeschaltet 
}
if (Messwert2 <= 510)         //wenn der Messwert von Fühler2 kleiner oder gleich 510 ist 
{
  digitalWrite(3,HIGH); //wird das Relais2 ausgeschaltet
}

delay(5000);

}



Ich hoffe, Ihr versteht meine Frage und danke schon mal für Eure Mithilfe!
Grüße, Billa

An den IO-pins 0 und 1 hängt die serielle Schnittstelle über die der Arduino Uno mit dem Computer kommuniziert. Deshalb sollte man diese beiden IO-pins nicht benutzen.
Sie werden schon von der seriellen Schnittstelle benutzt.

Das ist der Grund warum die sich scheinbar so "merkwürdig" verhalten.
Wenn man mehr Uasgänge benutzen will, dann nimmt man dafür sogenannte IO-expander
Sehr gebräuchlich ist MCP2307. Noch praktischer ist der SX1509, weil er auch Tastaturen abfragen kann und LEDs dimmen kann.

Einige Tips fürs programmieren:

Benenne alles und jedes mit einem sinnvollen und selbsterklärenden Namen.
So wie in diesem Code. Da habe ich fast alle Kommentare herausgelöscht und trotzdem wirst du verstehen was der Code macht, weil alles selbsterklärende Namen hat.

Außerdem hat das zum Beispiel bei IO-pins den Vorteil, dass man den Zahlenwert
nur an einer einzigen Stelle ändern muss und dann wird durch die Verwendung des Namens automatisch überall die neue Zahl eingesetzt.

// Kalibrieren der Erdfeuchtefühler
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); //-----Hardware Adressierung-----
const byte ersteZeile  = 0;
const byte zweiteZeile = 1;

// Definition der analogen Eingänge
const byte Fuehler1 = A0;     
const byte Fuehler2 = A1;     

const byte relais1Pin = 2;
const byte relais2Pin = 3;

const byte Ein = LOW;
const byte Aus = HIGH;


void setup() {
  // Seriellen Monitor starten
  Serial.begin(9600);
  Serial.println("Setup-Start");

  // LCD 16x2 initialisieren
  lcd.init();

  // pinMode (Fuehler1, INPUT); pinMode wird für analog benutze eingänge nicht benötigt
  // pinMode (Fuehler2, INPUT);
  pinMode (relais1Pin, OUTPUT);      
  pinMode (relais2Pin, OUTPUT);      
  digitalWrite(relais1Pin, Aus);    
  digitalWrite(relais2Pin, Aus);    
}

void loop() {
  // Analoge Eingänge auslesen
  int Messwert1 = analogRead(Fuehler1);   
  int Messwert2 = analogRead(Fuehler2);   

  // Schreibt den Messwert auf den seriellen Monitor
  Serial.print("Messwert1: ");
  Serial.print(Messwert1);
  Serial.print(" Messwert2: ");
  Serial.println(Messwert2);


  lcd.backlight();
  //Nachricht ausgeben
  lcd.setCursor(0, ersteZeile);
  lcd.print("Fuehler 1: ");   // schreibt das Wort "Fuehler1:" an diese Stelle
  
  lcd.setCursor(11, ersteZeile);       
  lcd.print(Messwert1);       // schreibt den Messwert von Fühler1 an diese Stelle

  lcd.setCursor(0, zweiteZeile);       
  lcd.print("Fuehler 2: ");   // schreibt das Wort "Fuehler2:" an diese Stelle
  
  lcd.setCursor(11, zweiteZeile);       
  lcd.print(Messwert2);       // schreibt den Messwert von Fühler2 an diese Stelle


  if (Messwert1 > 510) {           // wenn der Messwert von Fühler1 über 510 ist  
    digitalWrite(relais1Pin, Ein); 
  }
  else {                           // wenn der Messwert von Fühler1 kleiner oder gleich 510 ist  
    digitalWrite(relais1Pin, Aus); 
  }


  if (Messwert2 > 510) {         //wenn der Messwert von Fühler2 über 510 ist  
    digitalWrite(relais2Pin, Ein); 
  }
  else { //wenn der Messwert von Fühler2 kleiner oder gleich 510 ist
    digitalWrite(relais2Pin, Aus); 
  }

  delay(5000);
}

Hallo
Beim Uno werden die beiden Pins 0 und 1 für die serielle Schnittstelle benutzt.

In der Regel steht da auch RX in TX dran. Man kann die beiden Pins theoretisch auch für was anderes benutzen , aber dann halt nicht mehr die Schnittstelle nutzen.

Stephan war schneller :wink:

Super! Danke für Eure Antworten.

Nutzt das LCD Display auch die serielle Schnittstelle oder nur der serielle Monitor?

Das mit den selbsterklärenden Namen hatte ich schon versucht, bin aber teilweise gescheitert.Mit diesem Beispiel bekomme ich das aber hin, danke dafür.

Nein das nutzt die I2c Schnittstelle

da hätte ich ja auch selber drauf kommen kommen, war wohl schon zu spät.

Hab jetzt auch selbsterklärende Namen genommen. Da ich das Serial Display nicht brauche, hab ich auch den Teil entfernt. Ohne USB gehen jetzt auch Kanal 0 & 1

Wenn ich das weiter spinne und auf noch mehr Fühler / Relais erweitere, macht´s dann Sinn für die Relais & Fühler Arrays zu benutzen?

Ja!

Was die Pins 0 & 1 betrifft. Du brauchst das serial Display zum Debuggen ... Darum solltest du die nicht, wie Dir schon geraten wurde, für etwas anderes verwenden.

Es kommt immer wieder vor, wenn ein Programm sich nicht wie erwartet verhält, an bestimmten Stellen im Code eine serielle Ausgabe von Variablenwerten / Zuständen einzufügen. Dieser Möglichkeit solltest Du dich nicht berauben.

Unbedingt! Es ist sehr schlechter Stil Variablen durchzunummerieren wenn man statt dessen Arrays verwenden kann.

In dem Zusammenhang macht es auch Sinn, über Strukturen nachzulesen und verschiedene Variablen in eine Struktur zu geben. Auch Strukturen lassen sich als Array anlegen.

Wenn du dazu Hilfe benötigst gib bescheid.

So nebenbei ... für deine LCD Print Ausgaben solltest du das F-Makro nutzen.

Wie schon geschrieben wurde, die Pins D0 und D1 (nicht Kanal) solltest du besser frei lassen. Irgendwann fällt dir das auf die Füße und du musst wieder alles umbauen.

Welches "serial Display" meinst du ?
Wenn der Serielle Monitor gemeint ist, dann solltest du den aber drin lassen, da der wunderbar zur Fehlersuche (debugging) geeignet ist.

mit Struktur, Array, F-Makro und
auto range based for loop (also Ergänzung zur sicherlich bekannten for Schleife)

/*
    https://forum.arduino.cc/t/relais-an-digitalausgang-0-1-programmieren/1346979/9
    2025-01-25 by noiasca
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);    // die meisten Displays haben 0x27 oder 0x3F

// Struktur eines Messkreises festlegen
struct Circuit {
  const uint8_t inPin;                 // Fühler Pin
  const uint8_t outPin;                // Relais Pin
};
// Array für die Messkreise anlegen
Circuit circuit[] {
  { A0, 2 },                           // Messkreis 0: inPin, outPin
  { A1, 3 },                           // Messkreis 1: inPin, outPin
  //{A2, 4},                           // jeder weitere Messkreis ist lediglich eine Zeile
  //{A3, 5}
};
constexpr int threashold = 510;        // Grenzwert gegen den der Messwert verglichen wird
constexpr int off = LOW;               // falls das Relaismodul LOW active wäre, könnte man hier diese Konstante zentral ändern 
constexpr int on = HIGH;               // falls das Relaismodul LOW active wäre, könnte man hier diese Konstante zentral ändern 

void setup() {
  Serial.begin(115200);  // 2025 kann man schon 115200 nutzen
  Serial.println(F("measure inputs - switch outputs"));

  Wire.begin();                       
  lcd.init();
  lcd.backlight();
  lcd.println(F("Start"));

  for (auto &c : circuit) {            // c wird bei jedem Durchlauf eine Referenz auf EIN Element des Arrays
    pinMode(c.outPin, OUTPUT);
    digitalWrite(c.outPin, off);
  }
}

void loop() {
  for (size_t i = 0; i < sizeof(circuit) / sizeof(circuit[0]); i++) {   
    int measurement = analogRead(circuit[i].inPin);
    // Ausgabe Serial
    Serial.print(F("Messwert"));
    Serial.print(i + 1);               // Arrays beginnen mit dem Zählen mit 0, wenn man die Ausgabe ab "1" will muss man addieren
    Serial.print(F(": "));
    Serial.println(measurement);
    // Ausgabe LCD (für die ersten 2 Werte - weil das LCD nur zwei Zeilen hat)
    if (i <= 1) {
      lcd.setCursor(0, i);
      lcd.print(F("Fuehler "));
      lcd.print(i + 1);
      lcd.print(F(": "));
      lcd.print(measurement);
      lcd.print(F("    "));            // überschreibt eventuell übriggebliebene Ziffern der letzten Ausgabe
    }
    // Ausgang Schalten 
    if (measurement > threashold) { 
      digitalWrite(circuit[i].outPin, on);
    } else {
      digitalWrite(circuit[i].outPin, off);
    }
  }
  delay(5000);  // dirty delay - Todo: Umbauen gemäß "Blink Without Delay"
}
//

und wenn du ein ü im Fixtext schreiben willst könntest du

      lcd.print(F("F\xF5hler "));

schreiben, da bei den meisten LCDs an der Zeichensatz-Stelle 0xF5 das ü enthalten ist.

oder du nimmst meine LCD Library:
https://werner.rothschopf.net/202003_arduino_liquid_crystal_umlaute.htm

dann geht auch

      lcd.print(F("Fühler "));

Ja, ich meinte den seriellen Monitor. Ich hab den seriellen Moitor in meiner nächsten Version auch nicht entfernt sondern nur mit // "ausgegraut", man weiß ja nie. 0 & 1 laase ich frei.

1 Like

Puh, das sind aber ganz schön viele Hausaufgaben. Ne ganze Menge davon verstehe ich (noch) nicht, versuche aber erst mal, mir das selber zu erarbeiten.

Auf jeden Fall schon mal ein großes Dank für die wahnsinnig schnelle Unterstützung!

einfach die fett markierten Begriffe plus "C++" googeln dann wird das schon.
Alles besser als mit copy paste Programmzeilen duplizieren.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.