Moin zusammen,
ich habe mir eine Bewässerungsanlage gebaut. Das ganze soll so Funktionieren: Zwei Feuchtigkeitssensoren pro Pflanze werden von einem Arduino Nano (Slave) verarbeitet und nach Anfrage von dem Master (auch ein Arduino Nano) Seriell als Datenpaket verpackt und verschickt. Die Anlage lief vorher ohne den Slave. Über ein I2c angeschlossenes Display kann man über den Master nun die Anlage beobachten und steuern.
Nun zum Problem: Das Display zeigt nur ein schwarzes Bild, wenn ich den neuen Mastercode rein lade. Auch der Nano reagiert kaum noch auf die Veränderung von den Eingängen. Lade ich nur den ersten Teil rein, fehlt nur die Variable " Display_Data" sonst sieht man alles auf dem Display.
Ich vermute mal die Kommunikation wird irgendwie gestört. Ich habe bereits "Software Seriell" und jetzt "AltSoftSerial" ausprobiert. Einen Defekt kann ich ausschließen, da der alte Code ohne Slave funktioniert. Ich bin aber leider jetzt mit meinem Latein am Ende. Kann mir jemand helfen?
Hier der Code vom Master:
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <AltSoftSerial.h>
#define OLED_RESET -1
#define OLED_WIDTH 128
#define OLED_HEIGHT 64
// Diplaygroeße:
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, OLED_RESET);
// Serielle Schnittstelle definieren
//SoftwareSerial mySerial(10, 11); // RX, TX
AltSoftSerial mySerial; // RX = D8, TX = D9
int S1 = 7;
int S0 = 6;
int Hand = 11;
int Auto = 10;
int IRPin = A6;
int Led_Schaltschrank_R = 12;//Leuchte im Schaltschrank Rot
int P1 = 4; // Leuchte unten Rechts
int P2 = 5; // Leuchte unten Links
int M1 = 2; // Pumpe 1 (ohne Markierung) // Hier passt etwas nicht M1 = BGE 34
int M2 = 3; // Pumpe 2 (mit Markierung) // Hier passt etwas nicht M2 = BGE 12
//Variablen:
unsigned long alte_blink_Millis = 0;
unsigned long aktuelle_Millis = 0;
const long blink_Intervall = 1000;
int LED_Status = 0;
unsigned long WasserZeitMillis = 0; // Laufzeit Automatik
unsigned long Start_Zeit = 0; // Speichert die Startzeit für Auto Betrieb
unsigned long Zeit_Puffer = 0; // Zwischenspeicher für die Zeit
int WasserZeitStunden = 0; // Laufzeit Automatik in Stunden für das Display
unsigned long Pumpenpause_M1 = 0;
unsigned long Pumpenpause_M2 = 0;
unsigned long Pumpenlaufzeit_M1 = 0;
unsigned long Pumpenlaufzeit_M2 = 0;
unsigned long Anfragezeit = 0; // Zeit zum Senden einer Anfrage
unsigned long Pakete = 0; //Paketzähler
bool Slave_bereit = 0; // Slave bereit?
String StrSLAVEber = "Slavebereit";
String STOEsens = "STOEsens"; // Slavesensorik gestört
int Pos_Flanke_M1 = false; // Speicher Positive Flanke M1
int Pos_Flanke_M2 = false; // Speicher Positive Flanke M2
bool Quittierung = false; // Quittierung Störungen
bool Start = false; // Automatik
bool Stoerung = true; // Störung
bool Handbetrieb = false; // Handbetrieb
bool HandM1 = false; // Pumpe M1 in Hand steuern
bool HandM2 = false; // Pumpe M2 in Hand steuern
int Pumpzaehler = 0; // Anzahl der Pumpvorgänge zählen und abbilden
int Display_Case = 0; // Hier wird ausgewählt, was in der Variable stehen soll
String Display_Data = " "; // Speicher für das Display
bool Wasser_nio = false; // Wasser auffüllen
int sollwert = 0; // Sollwert Feuchte für die Pflanzenbewässerung
int MessBGE12 = 0; // Messwert BGE12
int MessBGE34 = 0; // Messwert BGE34
void setup() {
//Eingänge:
pinMode(S1, INPUT_PULLUP);
pinMode(S0, INPUT_PULLUP);
pinMode(Hand, INPUT_PULLUP);
pinMode(Auto, INPUT_PULLUP);
//Ausgänge:
pinMode(P1, OUTPUT);
pinMode(P2, OUTPUT);
pinMode(M1, OUTPUT);
pinMode(M2, OUTPUT);
digitalWrite(M1, LOW);
digitalWrite(M2, LOW);
digitalWrite(P1, LOW);
digitalWrite(P2, LOW);
// Pumpenpause für die Pumpen für den ersten Durchlauf auf 5 Minuten setzen
Pumpenpause_M1 = 180000;
Pumpenpause_M2 = 180000;
sollwert = 450;
Serial.begin(9600); // Schnittstelle USB
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
mySerial.begin(9600); // Schnittstelle zum Nano
mySerial.println("Masterbereit"); // Nano aufwecken
}
void loop() {
aktuelle_Millis = millis();
// Funktion aufrufen
langsamer_Blinker ();
// Sensorikstörung
// Quittierungen bei Störungen
if ((digitalRead(Hand) == true) && (digitalRead(Auto) == true) && (digitalRead(S1) == false) && (digitalRead(S0) == true)) {
// Quittierung aktivieren
Quittierung = true;
}
else {
// Quittierung nicht aktiv
Quittierung = false;
}
// Störung zurücksetzen, wenn Quittierung aktiv ist
if ((Stoerung == true) && (Start == false) && (Quittierung == true)) {
Stoerung = false; // Störung zurücksetzen, wenn Quittierung aktiv ist
}
// Quittierung des Pumpzählers (Reset des Pumpzählers)
if ((Start == false) && (digitalRead(Auto) == false) && (digitalRead(S1) == false) && (digitalRead(S0) == true)) {
Pumpzaehler = 0; // Pumpzähler zurücksetzen
}
// Kommunikation: Überprüfen, ob Slave bereit ist
if (mySerial.available() && (Slave_bereit == false)) {
String Slave = mySerial.readStringUntil('\n');
Slave.trim(); // Leerzeichen und \r entfernen
Serial.println("Slave: " + String(Slave)); // Slave ist ready
if (StrSLAVEber.equals(Slave)) {
Slave_bereit = true;
}
}
// Kommunikation: Paket empfangen
if (Slave_bereit == true) {
// Anfrage an den Slave senden
// Zeit für eine Anfrage, aktuell 500ms
if ((millis() - Anfragezeit) >= 500) {
Anfragezeit = millis();
// Anfrage schicken
mySerial.println("REQ");
delay(10); // Warte auf Antwort
// Antwort vom Nano verarbeiten
if (mySerial.available()) {
String antwort = mySerial.readStringUntil('\n');
antwort.trim(); // Leerzeichen und \r entfernen
// Beispielformat: "AA|355|355|370"
int sep1 = antwort.indexOf('|');
int sep2 = antwort.indexOf('|', sep1 + 1);
int sep3 = antwort.indexOf('|', sep2 + 1);
// Wenn der erste "|" nicht vorne steht und ein weiterer "|" daneben
if (sep1 > 0 && sep2 > sep1 && sep3 > sep2 && (!StrSLAVEber.equals(antwort))) {
// neue String bilden und mit substring in die einzelnen Bestandteile zerlegen substring(from, to)
String startkennung = antwort.substring(0, sep1);
String messwertStr_BGE12 = antwort.substring(sep1 + 1, sep2);
String messwertStr_BGE34 = antwort.substring(sep2 + 1, sep3);
String pruefsummeStr = antwort.substring(sep3 + 1);
// Messwert in Int wandeln
MessBGE12 = messwertStr_BGE12.toInt();
MessBGE34 = messwertStr_BGE34.toInt();
// Prüfsumme in Int wandeln.: strtol: string to long (const char), ...Str.c_str() wandelt String in C String (const char), 16 steht für HEX
int pruefsumme = strtol(pruefsummeStr.c_str(), NULL, 16); // HEX nach int
Pakete = Pakete + 1; // Pakte zählen
// Prüfsumme überprüfen
if ((MessBGE12 + MessBGE34 + 170) != pruefsumme) {
Serial.println("Ungültige Prüfsumme");
mySerial.println("STOEno"); // flasche Prüfsumme
}
// Ausgabe zur Kontrolle
Serial.println("Empfangenes Paket:");
Serial.println("Kennung: " + startkennung);
Serial.println("Messwert 12: " + String(MessBGE12));
Serial.println("Messwert 34: " + String(MessBGE34));
Serial.println("Prüfsumme (HEX): " + pruefsummeStr);
Serial.print("Prüfsumme (dezimal): ");
Serial.println(pruefsumme);
Serial.println(Pakete);
}
}
}
} else {
mySerial.println("STOEno");
Slave_bereit = false;
delay(50);
}
//Handbetrieb auswählen
if ((!digitalRead(Hand) == true) && (digitalRead(Auto) == true) && (Stoerung == false) && (Wasser_nio == false)) {
Handbetrieb = true;
Display_Case = 3; // Hand angewählt
}
// Handbetrieb ausschalten
if ((digitalRead(Hand) == true) || (!digitalRead(Auto) == true) || (Stoerung == true)) {
Handbetrieb = false;
if ((Stoerung == false) && (Wasser_nio == false) && (Start == false)) {
Display_Case = 1; // Display: Anlage Startbereit/Ready (Ohne Störung)
}
}
// Pumpe 1 in Hand steuern
if ((Handbetrieb == true) && (!digitalRead(S1) == true) && (Start == false)) {
HandM1 = true;
} else {
HandM1 = false;
}
// Pumpe 2 in Hand steuern
if ((Handbetrieb == true) && (digitalRead(S0) == true) && (Start == false)) {
HandM2 = true;
} else {
HandM2 = false;
}
// Startbedingung
if ((!digitalRead(S1) == true) && (!digitalRead(Auto) == true) && (digitalRead(Hand) == true) && (Stoerung == false) && (Wasser_nio == false)) {
Start = true; // Starten, wenn keine Störung und Schalter betätigt
Display_Case = 2; // Display: Anlage gestartet
// Wasserzeitlaufzeit in Z. 231 geschrieben
}
// Stoppbedingung
if ((digitalRead(S0) == true) || (digitalRead(Auto) == true) || (!digitalRead(Hand) == true) || (Stoerung == true) || (Wasser_nio == true)) {
Start = false; // Stoppen bei Fehler oder weiteren Bedingungen
if ((Stoerung == false) && (Wasser_nio == false) && (Handbetrieb == false)) {
Display_Case = 1; // Display: Anlage Startbereit/Ready (Ohne Störung)
}
}
// Aktoren bei Störung
if (Stoerung == true) {
digitalWrite(Led_Schaltschrank_R, LED_Status);
Display_Case = 0; // Display: Anlage in Störung
}
else {
digitalWrite(Led_Schaltschrank_R, LOW);
}
// Schaltschrankbeleuchtung steuern
if ((analogRead(IRPin) < 300) || ((analogRead(IRPin) >= 340) && (analogRead(IRPin) < 360))) {
digitalWrite(Led_Schaltschrank_R, HIGH); // Schaltschrankbeleuchtung Grün einschalten
}
else {
digitalWrite(Led_Schaltschrank_R, LOW); // Schaltschrankbeleuchtung Grün ausschalten
}
// Zeit berechnen, wenn Automatik an ist
if (Start == true) {
// Start_Zeit setzen, wenn Automatik an ist
if (Start_Zeit == 0) {
Start_Zeit = millis();
}
WasserZeitMillis = millis() - Start_Zeit + Zeit_Puffer; // Laufzeit schreiben in Wasserzeit -> nach 7 Tagen wird Wasser_nio ausgelöst
}
// Zwischenzeitspeicher schreiben und WasserZeitMillis zurücksetzen
if ((Start == false) && (WasserZeitMillis >= 1)) {
Zeit_Puffer = WasserZeitMillis; // Zwischenzeitpuffer schreiben
WasserZeitMillis = 0;
Start_Zeit = 0; // Startzeit Automatik zurücksetzen
}
// Wasserzeit in Stunden umrechnen (Betriebsstunden berechnen)
WasserZeitStunden = WasserZeitMillis / 3600000;
// Bitte wasser nachfüllen Variable Wassernio setzen: Wenn Pumpzähler 6 erreicht oder eine Woche Automatik lief
if ((Pumpzaehler >= 6) || (WasserZeitMillis >= 604800000)) {
Wasser_nio = true;
}
// Wasser_nio zurücksetzen
if (Wasser_nio == true) {
// Der Pumpzähler muss erst noch zurückgesetzt werden!
if ((Quittierung == true) && (Pumpzaehler == 0)) {
Wasser_nio = false;
WasserZeitMillis = 0; // Wasserzeit zurücksetzen
WasserZeitStunden = 0; // Wasserzeit Stunden zurücksetzen
Zeit_Puffer = 0; // Zwischenzeitspeicher zurücksetzen
Start_Zeit = 0; // Startzeit Automatik zurücksetzen
}
}
// Sollwert einstellen, wenn Störung, Wasser_nio, Automatik und Handbetrieb aus sind
if ((Stoerung == false) && (Wasser_nio == false) && (Handbetrieb == false) && (Start == false)) {
// Sollwert Einstellen: Erhöhen
if ((digitalRead(Hand) == true) && (digitalRead(Auto) == true) && (digitalRead(S1) == false) && (digitalRead(S0) == false)) {
sollwert = sollwert + 25;
}
// Sollwert Einstellen: Senken
if ((digitalRead(Hand) == true) && (digitalRead(Auto) == true) && (digitalRead(S1) == true) && (digitalRead(S0) == true)) {
sollwert = sollwert - 25;
}
}
// Ausgang: der LED bei Wassernio blinken lassen
if (Wasser_nio == true) {
Display_Case = 4; // Wasser_nio anzeigen
}
// Ausgang: P1 einschalten
if ((Start == true) || (HandM2 == true)) {
digitalWrite(P1, HIGH); // Led Einschalten
}
// Ausgang: P1 ausschalten
if ((Start == false) && (HandM2 == false)) {
digitalWrite(P1, LOW);
}
//Ausgang: P2 einschalten
if (HandM1 == true) {
digitalWrite(P2, HIGH);
}
// Ausgang: P2 ausschalten
if (HandM1 == false) {
digitalWrite(P2, LOW);
}
// Ausgang: Pumpe M1 einschalten
if ((HandM1 == true) || ((MessBGE34 > sollwert) && (Start == true) && (millis() - Pumpenpause_M1 >= 180000) && (digitalRead(M1) == LOW))) {
digitalWrite(M1, HIGH);
Pumpenlaufzeit_M1 = millis();
// Positve Flanke erkennen und Zähler erhöhen, Flankenimpuls speichern
if ((Pos_Flanke_M1 == false) && (HandM1 == false)) {
Pumpzaehler++;
Pos_Flanke_M1 = true;
}
}
// Ausgang: Pumpe M1 ausschalten
if ((MessBGE34 < (sollwert - 1)) && (Start == false) && (HandM1 == false) || (digitalRead(M1) == HIGH) && (millis() - Pumpenlaufzeit_M1 >= 3500) && (HandM1 == false) || (Stoerung == true) || (Wasser_nio == true)) {
// Pumpenlaufzeit für kleine Pflanze angepasst
digitalWrite(M1, LOW);
Pumpenlaufzeit_M1 = 0;
Pumpenpause_M1 = millis();
}
// Flanke M1 zurücksetzen wenn die Pumpe ausgeschaltet wird
if ((digitalRead(M1) == false) && (Pos_Flanke_M1 == true)) {
Pos_Flanke_M1 = false;
}
// Ausgang: Pumpe M2 einschalten
if ((HandM2 == true) || ((MessBGE12 > sollwert) && (Start == true) && (millis() - Pumpenpause_M2 >= 180000) && (digitalRead(M2) == LOW))) {
digitalWrite(M2, HIGH);
Pumpenlaufzeit_M2 = millis();
// Positve Flanke M2 erkennen und Zähler erhöhen, Flankenimpuls speichern
if ((Pos_Flanke_M2 == false) && (HandM2 == false)) {
Pumpzaehler++;
Pos_Flanke_M2 = true;
}
}
// Ausgang: Pumpe M2 ausschalten
if ((MessBGE12 < (sollwert - 1)) && (Start == false) && (HandM2 == false) || (digitalRead(M2) == HIGH) && (millis() - Pumpenlaufzeit_M2 >= 6000) && (HandM2 == false) || (Stoerung == true) || (Wasser_nio == true)) {
// Pumpenlaufzeit für die greoße Pflanze angepasst
digitalWrite(M2, LOW);
Pumpenlaufzeit_M2 = 0;
Pumpenpause_M2 = millis();
}
// Flanke M2 zurücksetzen wenn die Pumpe ausgeschaltet wird
if ((digitalRead(M2) == false) && (Pos_Flanke_M2 == true)) {
Pos_Flanke_M2 = false;
}
// Störungen vor Wasser_NIO anzeigen
if ((Stoerung == true) && (Wasser_nio == true)) {
// Zeige Störungen an
Display_Case = 0; // Störung zuerst anzeigen
}
switch (Display_Case) {
case 0:
Display_Data = "Failure";
break;
case 1:
Display_Data = "Ready";
break;
case 2:
Display_Data = "Auto";
break;
case 3:
Display_Data = "Hand";
break;
case 4:
Display_Data = "WassrNIO";
break;
}
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(1, 1);
display.println(Display_Data);
display.setTextSize(1);
display.setCursor(98, 1);
display.println("Soll:");
display.setTextSize(1);
display.setCursor(103, 15);
display.println(sollwert);
display.setTextSize(1);
display.setCursor(1, 25);
display.println("Pumpzaehler:");
display.setTextSize(1);
display.setCursor(75, 25);
display.println(Pumpzaehler);
// Betriebsstunden anzeigen, wenn Start aktiv ist
if ((Start == true) || (Wasser_nio == true)) {
display.setTextSize(1);
display.setCursor(103, 25);
display.println(WasserZeitStunden);
display.setTextSize(1);
display.setCursor(121, 25);
display.println("h");
}
display.setTextSize(1);
display.setCursor(1, 40);
display.println("Avg. Value BGE 12:");
display.setTextSize(1);
display.setCursor(110, 40);
display.println(MessBGE12);
display.setTextSize(1);
display.setCursor(1, 50);
display.println("Avg. Value BGE 34:");
display.setTextSize(1);
display.setCursor(110, 50);
display.println(MessBGE34);
display.display();
}
void langsamer_Blinker() {
if (aktuelle_Millis - alte_blink_Millis >= blink_Intervall) {
alte_blink_Millis = aktuelle_Millis;
if (LED_Status == LOW) {
LED_Status = HIGH;
}else {
LED_Status = LOW;
}
}
}
Hier noch der Code vom Slave:
#include <SoftwareSerial.h>
// Verwende 10 (RX), 11 (TX) für SoftwareSerial auf dem Nano
SoftwareSerial mySerial(11, 10); // RX, TX
//0,1
int BGE1 = A2; // Sensor 1 (mit Markierung)
int BGE2 = A3; // Sensor 2 (mit Markierung)
int BGE3 = A1; // Sensor 3 (ohne Markierung)
int BGE4 = A0; // Sensor 4 (ohne Markierung)
int Summe_1 = 0; // Summe von BGE 12
int Ergebnis_1 = 0; // Ergebnis von Summe 1
int Summe_2 = 0; // Summe von BGE 34
int Ergebnis_2 = 0; // Ergebnis von Summe 2
int STOE = 0;
int P_STOE = 3; // Störung LED
int P_Paket = 4; // Grüne LED für Betrieb-Pakete
int P_Eingriff = 5; // Blaue LED für Eingriff
int Messwert_BGE12 = 0;
int Messwert_BGE34 = 0;
byte AA = 0xAA;
int Pruf = 0;
String Anfrage = "REQ";
String STOESLV = "STOEno";
String Quit = "QUIT";
String MASRR = "Masterbereit";
unsigned long aktuelle_Millis = 0;
unsigned long alte_blink_Millis = 0;
bool LED_Status = 0;
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600); // Für Diagnose im Monitor
mySerial.begin(9600); // SoftwareSerial-Verbindung
Serial.println("Slavebereit");
mySerial.println("Slavebereit");
pinMode(P_STOE, OUTPUT);
digitalWrite(P_STOE, LOW);
pinMode(P_Paket, OUTPUT);
digitalWrite(P_Paket, LOW);
pinMode(P_Eingriff, OUTPUT);
digitalWrite(P_Eingriff, LOW);
}
void loop() {
aktuelle_Millis = millis();
// Sensorikstörung
if ((analogRead(BGE1) < 75) || (analogRead(BGE2) < 75) || (analogRead(BGE3) < 75) || (analogRead(BGE4) < 75)) {
// Variable Störung setzen
STOE = 1; // Sensorik Störung
}
// Pflanzensensoren auswerten und Mittelwert bilden, je feuchter die Erde desto, niedriger ist der Messwert.
for (int i = 0; i < 3; i++) {
Summe_1 += analogRead(BGE1);
Summe_1 += analogRead(BGE2);
// Beide Sensordaten addieren mit += (Hinzufügen und speichern)
// kurz warten
delay(3);
}
// Ergebnis bilden und Wert zurücksetzen
Ergebnis_1 = Summe_1 / 6;
Summe_1 = 0;
// Pflanzensensoren BGE 3 und 4 auswerten und Mittelwert bilden, je feuchter die Erde desto, niedriger ist der Messwert.
for (int i = 0; i < 3; i++) {
Summe_2 += analogRead(BGE3);
Summe_2 += analogRead(BGE4);
// Beide Sensordaten addieren mit += (Hinzufügen und speichern)
// kurz warten
delay(3);
}
// Ergebnis bilden und Wert zurücksetzen
Ergebnis_2 = Summe_2 / 6;
Summe_2 = 0;
if (mySerial.available()) {
String command = mySerial.readStringUntil('\n');
command.trim();
// Bei "STOEno" oder "Masterbereit" Verbindungsabbruch, wieder neu anmelden
if (STOESLV.equals(command) || MASRR.equals(command)) {
for (int i = 0; i < 2; i++) {
mySerial.print("Slavebereit");
Serial.println("Slavebereit"); // debugg
delay(30);
if (mySerial.available()) {
String cmd = mySerial.readStringUntil('\n');
cmd.trim();
if (Anfrage.equals(cmd)) {
break;
}
}
}
}
// Paket schicken
if (Anfrage.equals(command) && (STOE == 0)) {
digitalWrite(P_Paket, LOW);
Messwert_BGE12 = Ergebnis_1;
Messwert_BGE34 = Ergebnis_2;
delay (3);
Pruf = Messwert_BGE12 + Messwert_BGE34 + 170; // AA (Hex) sind 170 (dez)
// Prüfsumme in Hex umwandeln:
String PrufHex = String(Pruf, HEX); // String(..) ist ein String erzeuger (Konstruktor)
PrufHex.toUpperCase(); // in Großbuchstaben verwandeln
// Paket erzeugen:
String antwort = "AA|" + String(Messwert_BGE12) + "|" + String(Messwert_BGE34) + "|" + PrufHex + "\n";
mySerial.print(antwort); // Sende an Master
//Serial.print("Sende: ");
Serial.print(antwort); // Debug am Monitor
digitalWrite(P_Paket, HIGH);
}
// Störung Sensorik übermitteln
if (STOE == 1) {
for (int j = 0; j < 5; j++) {
mySerial.print("STOEsens");
if (Quit.equals(command)) { // nicht mehr antworten, wenn ein "QUIT" kommt
STOE = 0; // Störung zurücksetzen
break;
}
}
}
// Störungsled Blinken lassen
if (STOE == 1) {
digitalWrite(P_STOE, LED_Status);
digitalWrite(P_Eingriff, LED_Status);
}
}
}