Ich korrigiere mich. Es war spät und aufregend dazu
Danke für den Link, hier werden am Anfang einige Probleme vorgestellt, die auftauchen, wenn es ein Problem mit den Speicher auf dem Microcontroller gibt. Ich habe das jetzt im Hinterkopf und werde mich daran erinnern, wenn Probleme auftreten .
Die Steuerung ist jetzt fertig. Im Aufbau haben sich ein paar kleine Veränderungen ergeben (Stromversorgung via Meanwell RD-35A und Versetzung des Elko näher zum Schrittmotortreiber).
Ich habe den Sketch auf der Steuerung hochgeladen und es schien alles auf Anhieb zu funktionieren. Lediglich die Schaltlogik musste ich anpassen, da es sich bei dem 4- Kanal- Relais um ein Low- Trigger- Modell handelt.
Jetzt werde ich das Gerüst zusammenschweißen, alle Teile auslasern und dann alles zusammenbauen - ich bin schon ganz gespannt ob alles funktioniert und wie es wirkt .
// aufgetretener Fehler nach Hochladen des Sketches: Schrittmotor drehte nicht, habe dann den Schrittmotor- Probe- Sketch auf dem Probe- Schrittmotor- Aufbau ausprobiert, hier funktionierte der Elektromotor dann und anschließend auch wieder auf der primären Steuerung // Fehlerursache unklar
// Stromgrenze = VREF × 2 // Wenn man also z. B. einen Schrittmotor nutzt, der für 1 A ausgelegt ist, kann man die Stromgrenze auf 1 A einstellen, indem man die Referenzspannung auf 0,5 V einstellt. // Quelle: https://starthardware.org/stepper-motor-mit-dem-drv8825-steuern/ Anleitung zum Einstellen: https://www.youtube.com/watch?v=qI2NulbQGOQ
// Alarm 1 (Licht) und Alarm 2 (Bewässerungsumdrehung) starten um 6:00 // wenn das System nach 6:00 gestartet wird, bleibt das Licht bis zum nächsten Tag um 6:00 aus und auch die Bewässerungsrunde findet erst am nächsten Tag statt
// Edit aus eigenem Programm zum Drehen des Rades: wenn ich die micros()- funktion in der zweiten if- Anweisung durch millis() ersetzte und die Zeit entsprechend kürze, funktioniert der Sketch, keine Ahnung warum das so ist
//Definierungen für das RTC- Modul zum Auslösen der Alarme ( für Licht und Bewaesserung)
#include <Wire.h> // Bibliothek für I2C-Kommunikation // für RTC- Modul und für 1602 LCD Display
#include <DS3231.h> // Bibliothekt für das RTC- Modukl
DS3231 clock; // definiert ein Objekt namens "clock" vom Typ "DS3231". Dieses Objekt wird später im Sketch verwendet, um auf die Funktionen und Methoden der DS3231-Echtzeituhr zuzugreifen.
RTCDateTime dt; // definiert ein Objekt namens "dt" vom Typ "RTCDateTime". Dieses Objekt wird später im Sketch verwendet, um die aktuellen Zeit- und Datumsinformationen von der Echtzeituhr abzurufen und zu speichern.
// Definierungen zum Drehen des Rades
const byte dir_Pin = 9;
const byte step_Pin = 10;
// Definierungen zum Mitzählen der Umdrehungen
const byte REED_Pin = 2; // Pin, an dem der Reed-Schalter angeschlossen ist
unsigned int lastUmdrehungen = 0;
unsigned int Umdrehungen = 0; // gezählte Umdrehungen
// Definierungen zur Bewaesserung
const byte SchwimmerschalterNaehrloesungBecken_Pin = 3;
const byte SchwimmerschalterNaehrloesungTank_Pin = 4;
const byte Magnetventil_Pin = 6;
const byte Pumpe_Pin = 5;
bool PumpeIstAn = false; // davon ausgehen, dass Pumpe aus ist
bool MagnetventilGeschlossen = false; // davon ausgehen, dass Magnetventil offen ist
bool NaehrloesungTankIstVoll = false; // davon ausgehen, dass Tank leer ist
// Definierungen zum Licht
const byte Licht1_Pin = 7;
const byte Licht2_Pin = 8;
bool Lichtzyklus = false; // davon ausgehen, dass Licht1 aus ist
const byte Pir_Pin = 12;
// Festlegen der Schaltlogik von Pumpe, Licht1 und Licht2
const bool stDseAn = LOW; // Wenn Steckdose falsch schaltet Logik tauschen // Ich benutze ein Low- Level- Trigger- SSR, deshalb so
const bool stDseAus = !stDseAn;
// Festlegen der Schaltlogik des Magnetventils
const bool MvstDseAn = LOW; // Wenn Steckdose falsch schaltet Logik tauschen
const bool MvstDseAus = !stDseAn;
//Definierungen zum DHT22
#include <DHT.h>
#include <DHT_U.h>
#define DHTPIN 11 //==> Der DHT22 Sensor wird an Pin 3 angeschlossen
#define DHTTYPE DHT22 //==> Es handelt sich um einen DHT22 Sensor
DHT dht(DHTPIN, DHTTYPE); // Der Sensor wird ab jetzt mit "dht" angesprochen
// Definierungen zu LCD1602
#include <LiquidCrystal_I2C.h> // LiquidCrystal Bibliothek laden für LCD Display
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); // so wenn ein 1602 display genutzt wird
// Definierungen für Temperatur und Luftfeuchtigkeit
int16_t sensorWertTemp = 0; // Sensorwert auf 0 setzen
int16_t altSensorWertTemp = 0; // alten Sensorwert auf 0 setzen
int16_t sensorWertHygro = 0; // Sensorwert auf 0 setzen
int16_t altSensorWertHygro = 0; // alten Sensorwert auf 0 setzen
void setup() {
Serial.begin(9600);
dht.begin(); //DHT22 Sensor starten
lcd.init(); // für den LCD- Monitor // initialisiert den LCD-Monitor, d.h. sie sorgt dafür, dass der Monitor richtig konfiguriert ist und bereit ist, Daten anzuzeigen
lcd.backlight(); // für den LCD- Monitor // schaltet die Hintergrundbeleuchtung des LCD-Monitors ein, so dass die Anzeige lesbar wird. Ohne diese Zeile wäre die Anzeige dunkel und es wäre schwierig, die darauf angezeigten Informationen abzulesen.
// einmaliges Auslesen der Startzeit und Ausgeben auf dem LCD
clock.begin(); // initialisiert die Verbindung zum DS3231-Modul und bereitet es auf die weitere Verwendung vor. Sie muss aufgerufen werden, bevor weitere Methoden und Funktionen des clock-Objekts verwendet werden könne
clock.setDateTime(__DATE__, __TIME__); // Setze die Zeit des Sketch-Kompilierens
dt = clock.getDateTime(); // speichert die aktuelle Zeit und Datum von der DS3231-Echtzeituhr in der Variablen "dt" vom Typ "RTCDateTime" Die Methode gibt ein RTCDateTime-Objekt zurück, das die aktuelle Zeit und Datum enthält.
lcd.setCursor(0, 1);
lcd.print(dt.day); lcd.print("-");
lcd.print(dt.month); lcd.print("-");
lcd.print(dt.year); lcd.print(" ");
// Initialisierungen zum Drehen des Rades
pinMode(step_Pin, OUTPUT);
pinMode(dir_Pin, OUTPUT);
// Intitialsierungen für Mitzaehlen der Umdrehungen
pinMode(REED_Pin, INPUT_PULLUP); // interneren Pullup- Widerstand des Pin 2 aktivieren
// Initialsierungen für Bewaesserung
pinMode(SchwimmerschalterNaehrloesungBecken_Pin, INPUT_PULLUP); // Entprellen halte ich für unnötig
pinMode(SchwimmerschalterNaehrloesungTank_Pin, INPUT_PULLUP); // Entprellen halte ich für unnötig
pinMode(Magnetventil_Pin, OUTPUT);
pinMode(Pumpe_Pin, OUTPUT);
// Initialisierungen für Licht
pinMode(Licht1_Pin, OUTPUT);
pinMode(Licht2_Pin, OUTPUT);
pinMode(Pir_Pin, INPUT);
// Initialisierungen für DS3231 RTC- Modul
Serial.println("Initialize DS3231");
;
clock.begin();
// Deaktiviere Alarme und lösche Alarme für dieses Beispiel, da Alarme durch eine Batterie unterstützt werden.
// Unter normalen Bedingungen sollten die Einstellungen nach dem Ausschalten des Stroms und Neustart des Mikrocontrollers zurückgesetzt werden.
// d.h. im Klartext: Alles zurücksetzen am Anfang, weil das RTC durch eine Batterie unterstützt wird, wenn der Strom getrennt ist.
clock.armAlarm1(false);
clock.armAlarm2(false);
clock.clearAlarm1();
clock.clearAlarm2();
// Manuell (Jahr, Monat, Tag, Stunde, Minute, Sekunde) // heißt das ich muss dem RTC ganz am Anfang den aktuellen Zeitpunkt manuell mitteilen?
clock.setDateTime(2014, 4, 25, 0, 0, 0);
// Setze Alarm für Licht- Jede 06h:0m:0s an jedem Tag
// setAlarm1(Date or Day, Hour, Minute, Second, Mode, Armed = true)
clock.setAlarm1(0, 6, 0, 0, DS3231_MATCH_H_M_S);
// Setze Alarm für BereitschaftBewaesserung- Jede 8h:0m an jedem Tag // ich lasse, die Sekunden weg, weil ich davon ausgehe, dass ich das bei Alarm 2 muss...
// setAlarm1(Date or Day, Hour, Minute, Mode, Armed = true)
clock.setAlarm2(0, 6, 0, DS3231_MATCH_H_M);
// Festlegen der Ausgangszustände der Aktoren
digitalWrite(Magnetventil_Pin, stDseAus); // Magnetventil öffnen
digitalWrite(Pumpe_Pin, stDseAus); // Pumpe ausschalten
digitalWrite(Licht1_Pin, stDseAus); // Licht ausschalten
digitalWrite(Licht2_Pin, stDseAus); // Licht ausschalten
digitalWrite(dir_Pin, HIGH); // Motordrehrichtung im Uhrzeigersinn ( LOW wäre gegen den Uhrzeigersinn)
}
void loop() {
RadDrehen();
UmdrehungenZaehlen(); // Umdrehungen mitzaehlen
checkAlarms(); // Überprüfe Alarmeinstellungen
licht(); // Licht an- und ausschalten und teilweise mit Pir-Sensor
bewaesserung();
getSensors();
setDisplay();
}
void RadDrehen() {
// Verzögerungszeit zwischen den Schritten, Minimalwert des jeweiligen Motors darf nicht unterschritten werden; wenn ich die Verzögerunszeit nach oben schalte, komme ich auf weniger Umdrehungen!// Minimalwert ist 950 Mikrosekunden für den Vollschrittmodus und den NEMA15-Motor.
// Minimalwert ist 35 für den Sechzehntelschrittmodus; d.h. umso mehr Schritte, umso kürzer darf die Verzögerungspause sein
// Eine Sekunde entspricht 1.000.000 Mikrosekunden und 1000 Millisekunden. delay(); wird z.B. immer in Millisekunden angegeben
static uint32_t lastStepTime = 0;
switch (digitalRead(step_Pin)) {
case HIGH:
if (micros() - lastStepTime >= 1000000) {
digitalWrite(step_Pin, LOW); // ...Schritt nach vorne
lastStepTime = micros();
}
break;
case LOW:
if (micros() - lastStepTime >= 950) {
digitalWrite(step_Pin, HIGH); // Impuls für einen...
lastStepTime = micros();
}
break;
}
}
void UmdrehungenZaehlen() {
// HIER KÖNNTE ES PROBLEME GEBEN, DIE VERSION VON KOLAHA IST VERMUTLICH BESSER! // ICH GLAUBE ES WERDEN SO DIE GANZE ZEIT UMDREHUNGEN GEZÄHLT!!!
static bool lastReadState = LOW;
static uint32_t lastReadTime = 0;
static byte DEBOUNCE_DELAY = 50; // Entprellungszeit in Millisekunden
if (digitalRead(REED_Pin) == HIGH) // Ausgelöst?
{
if (lastReadState == false) // Nicht gemerkt?
{
Umdrehungen++; // Implementiere Umdrehungen, d.h. addiere die Variable Umdrehungen mit +1
Serial.print("Umdrehungen: ");
Serial.println(Umdrehungen); // Zeige die Umdrehungen auf dem seriellen Monitor an
lastReadState = true; // Status merken
}
} else if (millis() - lastReadTime > DEBOUNCE_DELAY) // Wenn nicht mehr ausgelöst UND debouncezeit abgelaufen
{
lastReadState = false;
}
}
void checkAlarms() {
dt = clock.getDateTime(); // liest das aktuelle Datum und die Uhrzeit vom RTC-Modul (Real Time Clock) aus und speichert sie in der Variable dt als Objekt der Klasse RTCDateTime.
Serial.println(clock.dateFormat("d-m-Y H:i:s - l", dt)); // Ausgabe auf dem seriellen Monitor
RTCAlarmTime a1;
RTCAlarmTime a2;
if (clock.isArmed1()) { //
a1 = clock.getAlarm1();
Serial.print("Alarm1 wird ausgelöst ");
switch (clock.getAlarmType1()) {
case DS3231_MATCH_H_M_S:
Serial.print("wenn Stunden, Minuten und Sekunden übereinstimmen: ");
Serial.println(clock.dateFormat("__ H:i:s", a1));
break;
default:
Serial.println("UNBEKANNTE REGEL");
break;
}
} else {
Serial.println("Alarm1 ist deaktiviert.");
}
if (clock.isArmed2()) {
a2 = clock.getAlarm2();
Serial.print("Alarm2 wird ausgelöst ");
switch (clock.getAlarmType2()) {
case DS3231_MATCH_H_M:
Serial.print("wenn Stunden und Minuten übereinstimmen:");
Serial.println(clock.dateFormat("__ H:i:s", a2));
break;
default:
Serial.println("UNBEKANNTE REGEL");
break;
}
} else {
Serial.println("Alarm2 ist deaktiviert.");
}
}
void licht() {
static uint32_t LichtZeitmerker = 0; // "static" bedeutet, dass die Variable nur einmal deklariert wird und während der Programmausführung unverändert bleibt(Nein. Das ist was anderes). "uint32_t" steht für "unsigned integer" und definiert eine Ganzzahlvariable ohne Vorzeichen mit einer Größe von 32 Bit.
if (Lichtzyklus == true) {
if (millis() - LichtZeitmerker >= 57600000UL) { // wenn das Licht angeschaltet ist und 16 h lang an war -- Licht ausschalten
digitalWrite(Licht1_Pin, stDseAus);
digitalWrite(Licht2_Pin, stDseAus);
Lichtzyklus = false;
} else {
if (clock.isAlarm1()) { // wenn der Alarm 1 ausgelöst und das Licht ausgeschaltet ist
Lichtzyklus = true;
Serial.println("Lichtzyklus ist an");
LichtZeitmerker = millis(); // Zeit merken, die das Licht an ist
}
}
}
if (Lichtzyklus == true) {
digitalWrite(Licht1_Pin, stDseAn);
if (digitalRead(Pir_Pin == LOW)) {
digitalWrite(Licht2_Pin, stDseAn);
} else {
digitalWrite(Licht2_Pin, stDseAus); // wenn der Pir- Sensor auslöst (HIGH) Licht1 ausschalten
}
}
}
void bewaesserung() {
// Auffüllen des Nährlösungsbeckens
if (clock.isAlarm2() && lastUmdrehungen != Umdrehungen && PumpeIstAn == false && MagnetventilGeschlossen == false) { // wenn BereitschatBewaesserung aktiviert, eine neue Umdrehung gezählt wurde, Pumpe aus und Magnetventil offen...
digitalWrite(Magnetventil_Pin, MvstDseAn); // Magnetventil schließen
MagnetventilGeschlossen = true; // merken, dass Magnetventil geschlossen ist
if (NaehrloesungTankIstVoll == false) { // nur wenn der Tank voll ist... // nur wenn der Tank voll ist Pumpe starten
digitalWrite(Pumpe_Pin, stDseAn); // Pumpe starten
PumpeIstAn = true; // merken, dass Pumpe an ist
}
lastUmdrehungen = Umdrehungen;
Serial.println("Nährlösungsbecken auffüllen");
}
// Nährlösungsbecken aufgefüllt
if (PumpeIstAn == true && SchwimmerschalterNaehrloesungBecken_Pin == LOW) { // wenn die Pumpe eingeschaltet ist und SchwimmerschalterNaehrloesungBecken_Pin auslöst, d.h. das Becken voll ist
digitalWrite(Pumpe_Pin, stDseAus); // Pumpe ausschalten
PumpeIstAn = false; // merken, dass Pumpe aus ist
Serial.println("Nährlösungsbecken aufgefüllt");
}
// Nährlösungsbecken leeren
if (MagnetventilGeschlossen == true && lastUmdrehungen != Umdrehungen) { // wenn BewaesserungsUmdrehung aktiviert ist ( d.h. MagnetventilGeschlossen) und eine neue Umdrehung gezählt wurde
lastUmdrehungen = Umdrehungen;
digitalWrite(Magnetventil_Pin, MvstDseAus); // Magnetventil öffnen, sodass die Nährlösung abfließen kann
MagnetventilGeschlossen = false; // merken, dass Magnetventil offen ist
Serial.println("Nährlösungsbecken leeren");
}
//Notabschaltung Pumpe
if (digitalRead(SchwimmerschalterNaehrloesungTank_Pin) == LOW) { // wenn SchwimmerschalterNaehrloesungTank oben ist...
NaehrloesungTankIstVoll = true; // merken, dass der Naehrloesungstank voll ist
} else { // wenn der SchwimmerschalterNaehrloesungTank unten ist...
NaehrloesungTankIstVoll = false; // merken, dass der NaehrloesungsTank leer ist
}
}
void getSensors() {
sensorWertTemp = dht.readTemperature();
sensorWertHygro = dht.readHumidity();
}
void setDisplay() {
bool TempisNew = false;
bool HygroisNew = false;
bool UmdrehungenisNew = false;
if (altSensorWertTemp != sensorWertTemp) {
TempisNew = true;
altSensorWertTemp = sensorWertTemp;
}
if (altSensorWertHygro != sensorWertHygro) {
HygroisNew = true;
altSensorWertHygro = sensorWertHygro;
}
if (lastUmdrehungen != Umdrehungen) {
UmdrehungenisNew = true;
}
if (TempisNew) {
lcd.setCursor(0, 0);
if (sensorWertTemp < 10) {
lcd.print(' ');
}
lcd.print(sensorWertTemp); // Temperatur anzeigen
lcd.print("\337C"); // Grad
}
if (HygroisNew) {
lcd.setCursor(5, 0);
if (sensorWertHygro < 10) {
lcd.print(' ');
}
lcd.print(sensorWertHygro); // LF anzeigen
lcd.print(" %");
}
if (UmdrehungenisNew) {
lcd.setCursor(11, 1);
lcd.print(Umdrehungen); // Temperatur anzeigen
}
}
Der rotierende Garten ist jetzt weitestgehend fertig gestellt .
![IMG_20230604_204553|375x500]
(upload://m0Kz9Iz99PQkxraBdakGHQKLuWn.jpeg)
Bei der hinteren Spiegelwand musste ich statt eines normalen Spiegels auf einen Acrylglasspiegel zurückgreifen, der sich beim Lasern leider etwas gewölbt hat, daher ist der Infinity Effekt ein bisschen verkürzt, ich werde das aber erstmal so lassen .
Ich werde es jetzt doch erstmal mit Basilikum ausprobieren, weil ich zu Beginn des Projektes mehrere Basilikum Pflanzen wachsen ließ von denen ich in Kürze genug Stecklinge schneiden könnte.
Außerdem scheint es mit dem Basilikum in diesem rotierenden Garten auch gut zu klappen: New Omega Garden 80 watt basil time lapse - YouTube
Wird Dein Stepper-Motor so heiss, dass der nen Kühlkörper benötigt'?
Ich habe ihn so bekommen, weiß nicht ob er zwingend nötig ist aber schaden tut er auch nicht
Liebe community,
Es gibt leider ein Problem mit der Funktion zum Zählen der Umdrehungen:
void UmdrehungenZaehlen() {
static bool lastReadState = LOW;
static uint32_t lastReadTime = 0;
static byte DEBOUNCE_DELAY = 50; // Entprellungszeit in Millisekunden
if (digitalRead(REED_Pin) == HIGH) // Ausgelöst?
{
if (lastReadState == false) // Nicht gemerkt?
{
Umdrehungen++; // Implementiere Umdrehungen, d.h. addiere die Variable Umdrehungen mit +1
Serial.print("Umdrehungen: ");
Serial.println(Umdrehungen); // Zeige die Umdrehungen auf dem seriellen Monitor an
lastReadState = true; // Status merken
}
} else if (millis() - lastReadTime > DEBOUNCE_DELAY) // Wenn nicht mehr ausgelöst UND debouncezeit abgelaufen
{
lastReadState = false;
}
}
Immer wenn der Magnet, der am Rad befestigt ist, an dem Reedschalter vorbeifährt, werden genau zwei Umdrehungen gezählt. Ich habe jetzt lange versucht das Problem zu lösen, komme aber nicht weiter und würde mich sehr freuen wenn mir jemand helfen kann :).
LG Leonardo
Falsches Magnet , falsch befestigt oder prellt stärker als angenommen
Ich habe ein bisschen mit der Position des Magneten und der Enprellzeit ( DEBOUNCE_DELAY) rumgespielt.
Mit dieser Position des Magneten und diesen Einstellungen in der Funktion zum Umdrehungen zählen, funktioniert es jetzt:
void UmdrehungenZaehlen() {
static bool lastReadState = LOW;
static uint32_t lastReadTime = 0;
static byte DEBOUNCE_DELAY = 2000; // Entprellungszeit in Millisekunden // mit "if (micros() - lastStepTime >= 6000)" und "static byte DEBOUNCE_DELAY = 2000;" und der richtigen Position des Magneten (aufgestellt) funktioniert es und zählt nicht mehr doppelt!
if (digitalRead(REED_Pin) == HIGH) // Ausgelöst?
{
if (lastReadState == false) // Nicht gemerkt?
{
Umdrehungen++; // Implementiere Umdrehungen, d.h. addiere die Variable Umdrehungen mit +1
Serial.print("Umdrehungen: ");
Serial.println(Umdrehungen); // Zeige die Umdrehungen auf dem seriellen Monitor an
lastReadState = true; // Status merken
}
} else if (millis() - lastReadTime > DEBOUNCE_DELAY) // Wenn nicht mehr ausgelöst UND debouncezeit abgelaufen
{
lastReadState = false;
}
}
Ich habe es lange für einen Fehler im Sketch gehalten, danke für den Tipp fony!
Ich bin gerade dabei ein paar Veränderungen/ Vereinfachungen/ Verbesserungen an dem rotierenden Garten vorzunehmen, bevor ich die Minze (doch kein Basilikum;) ) einsetze.
-
Das Magnetventil wird im geschlossenen Zustand sehr heiß und verbrennt den Schlauch. Deshalb werde ich eine andere Pumpe einbauen Aquarienpumpe EHEIM compactON 1000 bei HORNBACH kaufen, die das Wasser vom Tank nach oben pumpt und durch die das Wasser einfach wieder zurück fließen kann, wenn die Pumpe ausgeschaltet wird. Die Pumpe wird die komplette Bewässerungsrunde durchlaufen. Das überschüssige Wasser wird währenddessen durch einen Überlauf zurück in den Tank fließen. Das hat übrigens auch den Vorteil, dass das Wasser mit Sauerstoff angereichert wird.
Ich habe das Prinzip bereits mit einer anderen Pumpe, deren maximale Förderhöhe aber leider nicht ausreicht ausprobiert, es funktioniert ideal! -
Der Bewässerungszyklus wird nicht mehr mit Hilfe des RTC- Moduls gesteuert sondern mit Hilfe einer Art digitalen Zeitschaltuhr in der Funktion bewaesserung() (Zeile 159).
-
Das Licht wird mit einer analogen Zeitschaltuhr gesteuert.
Ich habe den Sketch für die anstehenden Veränderungen bereits umgeschrieben und aus den verschachtelten if- Bedingungen in void bewaesserung() eine state machine gebaut.
Ich würde mich sehr über Kritik zum Sketch freuen:) !
// aufgetretener Fehler nach Hochladen des Sketches: Schrittmotor drehte nicht, habe dann den Schrittmotor- Probe- Sketch auf dem Probe- Schrittmotor- Aufbau ausprobiert, hier funktionierte der Elektromotor dann und anschließend auch wieder auf der primären Steuerung // Fehlerursache unklar
// Stromgrenze = VREF × 2 // Wenn man also z. B. einen Schrittmotor nutzt, der für 1 A ausgelegt ist, kann man die Stromgrenze auf 1 A einstellen, indem man die Referenzspannung auf 0,5 V einstellt. // Quelle: https://starthardware.org/stepper-motor-mit-dem-drv8825-steuern/ Anleitung zum Einstellen: https://www.youtube.com/watch?v=qI2NulbQGOQ
// Das Licht wird mit einer Zeitschaltuhr gesteuert, die
// Edit aus eigenem Programm zum Drehen des Rades: wenn ich die micros()- funktion in der zweiten if- Anweisung durch millis() ersetzte und die Zeit entsprechend kürze, funktioniert der Sketch, keine Ahnung warum das so ist
//Definierungen für das RTC- Modul zum Auslösen der Alarme ( für Licht und Bewaesserung)
#include <Wire.h> // Bibliothek für I2C-Kommunikation // für RTC- Modul und für 1602 LCD Display
#include <DS3231.h> // Bibliothekt für das RTC- Modukl
DS3231 clock; // definiert ein Objekt namens "clock" vom Typ "DS3231". Dieses Objekt wird später im Sketch verwendet, um auf die Funktionen und Methoden der DS3231-Echtzeituhr zuzugreifen.
RTCDateTime dt; // definiert ein Objekt namens "dt" vom Typ "RTCDateTime". Dieses Objekt wird später im Sketch verwendet, um die aktuellen Zeit- und Datumsinformationen von der Echtzeituhr abzurufen und zu speichern.
// Definierungen zum Drehen des Rades
const byte dir_Pin = 9;
const byte step_Pin = 10;
// Definierungen zum Mitzählen der Umdrehungen
const byte REED_Pin = 2; // Pin, an dem der Reed-Schalter angeschlossen ist
unsigned int lastUmdrehungen = 0;
unsigned int Umdrehungen = 0; // gezählte Umdrehungen
// Definierungen zur Bewaesserung
const byte SchwimmerschalterNaehrloesungBecken_Pin = 3;
const byte SchwimmerschalterNaehrloesungTank_Pin = 4;
const byte Pumpe_Pin = 5;
// Definierungen zum Licht
const byte Licht1_Pin = 7; // dauerhaft an für Zeitschaltuhr!
const byte Licht2_Pin = 8; // dauerhaft an für Zeitschaltuhr!
// Festlegen der Schaltlogik der Pumpe und des Lichtes
const bool stDseAn = LOW; // Wenn Steckdose falsch schaltet Logik tauschen // Ich benutze ein Low- Level- Trigger- SSR, deshalb so
const bool stDseAus = !stDseAn;
//Definierungen zum DHT22
#include <DHT.h>
#include <DHT_U.h>
#define DHTPIN 11 //==> Der DHT22 Sensor wird an Pin 3 angeschlossen
#define DHTTYPE DHT22 //==> Es handelt sich um einen DHT22 Sensor
DHT dht(DHTPIN, DHTTYPE); // Der Sensor wird ab jetzt mit "dht" angesprochen
// Definierungen zu LCD1602
#include <LiquidCrystal_I2C.h> // LiquidCrystal Bibliothek laden für LCD Display
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); // so wenn ein 1602 display genutzt wird
// Definierungen für Temperatur und Luftfeuchtigkeit
int16_t sensorWertTemp = 0; // Sensorwert auf 0 setzen
int16_t altSensorWertTemp = 0; // alten Sensorwert auf 0 setzen
int16_t sensorWertHygro = 0; // Sensorwert auf 0 setzen
int16_t altSensorWertHygro = 0; // alten Sensorwert auf 0 setzen
void setup() {
Serial.begin(9600);
dht.begin(); //DHT22 Sensor starten
lcd.init(); // für den LCD- Monitor // initialisiert den LCD-Monitor, d.h. sie sorgt dafür, dass der Monitor richtig konfiguriert ist und bereit ist, Daten anzuzeigen
lcd.backlight(); // für den LCD- Monitor // schaltet die Hintergrundbeleuchtung des LCD-Monitors ein, so dass die Anzeige lesbar wird. Ohne diese Zeile wäre die Anzeige dunkel und es wäre schwierig, die darauf angezeigten Informationen abzulesen.
// einmaliges Auslesen der Startzeit und Ausgeben auf dem LCD
clock.begin(); // initialisiert die Verbindung zum DS3231-Modul und bereitet es auf die weitere Verwendung vor. Sie muss aufgerufen werden, bevor weitere Methoden und Funktionen des clock-Objekts verwendet werden könne
clock.setDateTime(__DATE__, __TIME__); // Setze die Zeit des Sketch-Kompilierens
dt = clock.getDateTime(); // speichert die aktuelle Zeit und Datum von der DS3231-Echtzeituhr in der Variablen "dt" vom Typ "RTCDateTime" Die Methode gibt ein RTCDateTime-Objekt zurück, das die aktuelle Zeit und Datum enthält.
lcd.setCursor(0, 1);
lcd.print(dt.day); lcd.print("-");
lcd.print(dt.month); lcd.print("-");
lcd.print(dt.year); lcd.print(" ");
// Initialisierungen zum Drehen des Rades
pinMode(step_Pin, OUTPUT);
pinMode(dir_Pin, OUTPUT);
// Intitialsierungen für Mitzaehlen der Umdrehungen
pinMode(REED_Pin, INPUT_PULLUP); // interneren Pullup- Widerstand des Pin 2 aktivieren
// Initialsierungen für Bewaesserung
pinMode(Pumpe_Pin, OUTPUT);
// Initialisierungen für Licht
pinMode(Licht1_Pin, OUTPUT);
pinMode(Licht2_Pin, OUTPUT);
// Initialisierungen für DS3231 RTC- Modul
Serial.println("Initialize DS3231");
;
clock.begin();
// Festlegen der Ausgangszustände der Aktoren
digitalWrite(Pumpe_Pin, stDseAus); // Pumpe ausschalten
digitalWrite(Licht1_Pin, stDseAn); // Licht permanent einschalten für Zeitschaltuhr
digitalWrite(Licht2_Pin, stDseAn); // Licht permanent einschalten für Zeitschaltuhr
digitalWrite(dir_Pin, HIGH); // Motordrehrichtung im Uhrzeigersinn ( LOW wäre gegen den Uhrzeigersinn)
}
void loop() {
RadDrehen();
UmdrehungenZaehlen(); // Umdrehungen mitzaehlen
bewaesserung();
getSensors();
setDisplay();
}
void RadDrehen() {
// Verzögerungszeit zwischen den Schritten, Minimalwert des jeweiligen Motors darf nicht unterschritten werden; wenn ich die Verzögerungszeit nach oben schalte, komme ich auf weniger Umdrehungen!// Minimalwert ist 950 Mikrosekunden für den Vollschrittmodus und den NEMA15-Motor.
// Minimalwert ist 35 für den Sechzehntelschrittmodus; d.h. umso mehr Schritte, umso kürzer darf die Verzögerungspause sein
// Minimalwert ist 18 für Zweiunddreißigstelschrittmodus, ""
// Im Handbuch von AZ-Delivery für das DRV8825 Schrittmotor- Treiber-Modul wird dieselbe Pausenlänge zwischen HIGH und LOW genommen (usDelay), d.h. ich kann bei case HIGH einfach die Pause auf bis zu 950 verkürzen um einen schnelleren Lauf zu erzeugen
// Mikroschritt- Modi: Halbschrittmodus: ca. 15 % weniger Drehmoment, noch kleinere Schritte, noch weniger Drehmoment, ca. 30% weniger?
// Im Microstep- Modus hat der Schrittmotor bis zu 30 Prozent weniger Drehmoment
// Eine Sekunde entspricht 1.000.000 Mikrosekunden und 1000 Millisekunden. delay(); wird z.B. immer in Millisekunden angegeben
// DER STEPPERMOTOR LÄUFT GERADE IM VOLLSCHRITTMODUS!
static uint32_t lastStepTime = 0;
switch (digitalRead(step_Pin)) {
case HIGH:
if (micros() - lastStepTime >= 6000) { // 1000000 hier ein guter Wert für sehr langsamen Lauf // 1000 zum Testen!
digitalWrite(step_Pin, LOW); // ...Schritt nach vorne
lastStepTime = micros();
}
break;
case LOW:
if (micros() - lastStepTime >= 950) {
digitalWrite(step_Pin, HIGH); // Impuls für einen...
lastStepTime = micros();
}
break;
}
}
void UmdrehungenZaehlen() {
static bool lastReadState = LOW;
static uint32_t lastReadTime = 0;
static byte DEBOUNCE_DELAY = 2000; // Entprellungszeit in Millisekunden // mit "if (micros() - lastStepTime >= 6000)" und "static byte DEBOUNCE_DELAY = 2000;" und der richtigen Position des Magneten (aufgestellt) funktioniert es und zählt nicht mehr doppelt!
if (digitalRead(REED_Pin) == HIGH) // Ausgelöst?
{
if (lastReadState == false) // Nicht gemerkt?
{
Umdrehungen++; // Implementiere Umdrehungen, d.h. addiere die Variable Umdrehungen mit +1
Serial.print("Umdrehungen: ");
Serial.println(Umdrehungen); // Zeige die Umdrehungen auf dem seriellen Monitor an
lastReadState = true; // Status merken
}
} else if (millis() - lastReadTime > DEBOUNCE_DELAY) // Wenn nicht mehr ausgelöst UND debouncezeit abgelaufen
{
lastReadState = false;
}
}
void bewaesserung() {
enum {warten, AlarmAusgeloest, PumpeAus, PumpeAn};
static byte status = warten; // Ausgangsstatus festlegen
const unsigned long Intervall = 30000; // 12 * 60 * 60 * 1000 = 12 Stunden // zu Testzwecken kann ich hier ein kürzeres Intervall angeben (z.B. 60000 = 1 Minute)
unsigned long lastmillisfuerAlarm = 0; // Variable zum Speichern der letzten Ausführungszeit
boolean Alarm = false;
boolean PumpeIstAn = false; // davon ausgehen, dass Pumpe aus ist
boolean NaehrloesungTankIstVoll = false; // davon ausgehen, dass Tank leer ist
switch (status) {
case warten:
if (!Alarm && (millis() - lastmillisfuerAlarm >= Intervall)) { // Überprüfen, ob das Intervall erreicht ist
lastmillisfuerAlarm = millis(); // Zeit zurücksetzen
Serial.println(" Alarm ausgelöst");
status = AlarmAusgeloest;
}
break;
case AlarmAusgeloest:
if (lastUmdrehungen != Umdrehungen && PumpeIstAn == false) { // wenn BereitschatBewaesserung aktiviert d.h. der Alarm aktiviert wurde, eine neue Umdrehung gezählt wurde und Pumpe aus
if (NaehrloesungTankIstVoll == false) { // nur wenn der Tank voll ist...
digitalWrite(Pumpe_Pin, stDseAn); // Pumpe starten
PumpeIstAn = true;
Serial.println("Pumpe angeschaltet");
status = PumpeAn;
lastUmdrehungen = Umdrehungen; // merken, dass Pumpe an ist
}
break;
case PumpeAn:
if (lastUmdrehungen != Umdrehungen) { // wenn eine neue Umdrehung gezählt wurde
digitalWrite(Pumpe_Pin, stDseAus); // Pumpe ausschalten, sodass die Nährlösung auf demselben Weg abfließen kann von wo sie gekommen ist
PumpeIstAn = false;
Alarm = false; // wenn die BewässerungsUmdrehung abgeschlossen ist wird der Alarm zurückgesetzt, bis die digitale Zeitschaltuhr wieder den Alarm auslöst
Serial.println(" Pumpe ausgeschaltet/Alarm deaktiviert...gehe warten");
status = warten;
}
break;
}
}
//Notabschaltung Pumpe
if (digitalRead(SchwimmerschalterNaehrloesungTank_Pin) == LOW) { // wenn SchwimmerschalterNaehrloesungTank oben ist...
NaehrloesungTankIstVoll = true; // merken, dass der Naehrloesungstank voll ist
} else { // wenn der SchwimmerschalterNaehrloesungTank unten ist...
NaehrloesungTankIstVoll = false; // merken, dass der NaehrloesungsTank leer ist
}
}
void getSensors() {
sensorWertTemp = dht.readTemperature();
sensorWertHygro = dht.readHumidity();
}
void setDisplay() {
bool TempisNew = false;
bool HygroisNew = false;
bool UmdrehungenisNew = false;
if (altSensorWertTemp != sensorWertTemp) {
TempisNew = true;
altSensorWertTemp = sensorWertTemp;
}
if (altSensorWertHygro != sensorWertHygro) {
HygroisNew = true;
altSensorWertHygro = sensorWertHygro;
}
if (lastUmdrehungen != Umdrehungen) {
UmdrehungenisNew = true;
}
if (TempisNew) {
lcd.setCursor(0, 0);
if (sensorWertTemp < 10) {
lcd.print(' ');
}
lcd.print(sensorWertTemp); // Temperatur anzeigen
lcd.print("\337C"); // Grad
}
if (HygroisNew) {
lcd.setCursor(5, 0);
if (sensorWertHygro < 10) {
lcd.print(' ');
}
lcd.print(sensorWertHygro); // LF anzeigen
lcd.print(" %");
}
if (UmdrehungenisNew) {
lcd.setCursor(11, 1);
lcd.print(Umdrehungen); // Temperatur anzeigen
}
}
Des Weiteren habe ich eine Befürchtung: Wenn alle 24 Steinwollwürfel in den rotierenden Garten kommen und sich mit Wasser vollsaugen, könnte das den Schrittmotor überlasten. Denkt Ihr das auch?
Mit meiner derzeitigen Ansteuerung des Schrittmotors im Sketch und ohne viel Last kommt es übringes gerade schon zu Mucken des Schrittmotors: Zeitweise läuft er langsamer, unruhig, stottert...
Sind das Indizien für eine falsche Ansteuerung oder bereits für eine Überlastung?
Kurzes Update:
Der Garten läuft, die Minze wächst.
Der rotierende Garten wird inzwischen mit einem AC- Elektrogetriebemotor mit folgenden Spezifikationen angetrieben: 240 VAC ; 90 Watt ; 0,72 / 0,42 A
Ich nehme mal an, dass es sich bei 0,72 A um den Anlaufstrom handelt.
Der Stromverbrauch des rotierenden Gartens ist unnötig hoch, wenn der Elektromotor konstant durchläuft/ sich das Rad die ganze Zeit dreht, was gerade der Fall ist.
Daher möchte ich das Rad durch Ein- und Ausschalten des Elektromotors in Schritten drehen mit Hilfe eines SSR- Relais, das 240 VAC und 10 Ampere schalten kann.
Hier ist der Schaltplan. Kann ich den Elektromotor so ansteuern oder brauche ich noch eine Freilaufdiode o.ä.?
Welches Lauf/Pause-Verhältnis schwebt Dir vor? Die meisten AC-Motoren mögen ein laufendes kurzes Ein-/Ausschalten nicht. Also eher sowas wie 1 Minute derehen und 5 Minuten Pause.
Die Motoren-Spezis mögen mich korrigieren, wenn ich falsch liege.
Gruß Tommy
Gruß Tommy
Ich hätte gerne folgendes Intervall: Motor an für 17,37 Sekunden und aus für 5,625 Minuten. Ist ansonsten alles korrekt bei der Verdrahtung? Habe ich keine Sicherheitsvorkehrung vergessen ?
Ist ja gleichmäßig verteilt. Der Motor muß also keine Kraft fürs Drehen aufwenden; nur fürs Beschleunigen bzw abbremsen.
Nimm ein 25A SSR. Da bist Du auf der sicheren Seite. Das größere kostet nicht viel mehr.
Grüße Uwe
NEIN!
Der ist um ein Vielfaches höher. Ich schätze mal um die 3,5A (+x)
Daher ist das:
ein Problem.
Auch in anderer Hinsicht. Ist der Motor für die Betriebsart S2 (Kurzzeitbetrieb) überhaupt geeignet?
Kannst Du mal ein scharfes Bild vom Typenschild machen?
Nein.
Dein Motor ist ein 3P.
Da dürfte also ein Drehstromstecker dran sein...
warum nimmst du keinen Getriebemotor, 12 oder 24V, die drehen langsam und haben viel Kraft.
Da hast Du theoretisch Recht, ich habe aber praktisch die Erfahrung gemacht, dass es leichter ist die Pflanzen in den Containern während des Drehens abzunehmen, sodass es zu einer Unwucht im Rad kommt, was der kleine Schrittmotor, den ich anfangs hatte vermutlich nicht gepackt hätte.
Ja, die Wasseraufnahme hab ich nicht bedacht.
Grüße Uwe
Die Zeichen sind leider ins Typenschild "gestampft", deshalb gelingt es mir das nicht so richtig gut zu fotografieren :/, ich hoffe Du kannst alles erkennen
Ist der Motor für die Betriebsart S2 (Kurzzeitbetrieb) überhaupt geeignet?
Shit, wie ich sehe ist er Betriebsart S1
Da dürfte also ein Drehstromstecker dran sein...
Ich habe den Motor gebraucht gekauft, mit eine Stecker für 240VAC und so läuft er gerade
Du meinst vermutlich so eine Art Scheibenwischermotor. Ich bin mit den U/ Min und mit der Kraft des zur Zeit verwendeten Motors sehr zufrieden, ich hoffe gerade noch den irgendwie verwenden zu können :).