Auf Anraten ein neues (schon mal dagewesenes) Thema. Ich habe es in "zeigt her eure..." begonnen und setze es hier ein, da es wohl interessante Diskussionen gibt.
Also, Grundprinzip: 2 Kreise mit je einem Hallsensor bestückt. Funktion: Hall 1 lässt Lok 1 langsam bremsen, kurze Pause, Lok 2 fährt langsam in andere Richtung los. Hall 2 macht es dann umgekehrt. Ich habe mich am millis (Hurra, mit Erfolg) probiert. Habe auch den Weg gewählt die Aktionen der Halls in eigene Funktionen zu packen. Mich interessiert eure Meinung und wo ich was kürzer, effizienter, eleganter... machen kann. Ich bin gerade dabei eine kleine Anlage auf Nanosteuerung umzustellen (belebte Häuser, Weichenschaltung) und eben automatisierte Abläufe.
Hier der Sketch
/*
DC-Motorsteuerung mit L298N zum Regeln
von Anfahr- und Bremsverhalten mit 2 Hallsensoren,
mit Pause zwischen dem Brems- und Anfahrstart
von Schorsch für ZFI
23.05.21
*/
// Definition Hallpins und Hallstatus
const int hallSensorPin = 2;
const int hallSensorPin2 =3;
int hallStatus = LOW; // 1. Hall aktiv
int hallStatus2=HIGH; // 2. Hall paasiv
// Gleichstrommotor 1
int GSM1 = 10;
int in1 = 9;
int in2 = 8;
// Gleichstrommotor 2
int GSM2 = 5; //IN B
int in3 = 7; // IN 3
int in4 = 6; // IN 4
unsigned long anfang = 0;
const long warte = 2000; //Pause zwische M1 und M2 im Loop
void setup()
{
Serial.begin(9600);
pinMode(GSM1, OUTPUT);
pinMode(GSM2, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
pinMode(hallSensorPin, INPUT);
pinMode(hallSensorPin2, INPUT);
if (hallStatus ==LOW)
{ // Beschleunigen M1 als Zustand nach Einschalten
for (int i = 0; i <=240; i += 20) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW); // Motor 1 beginnt zu rotieren
analogWrite(GSM1, i); // Motor 1 beschleunigt
delay(100);
}
}
}
void HALL1A() // Funktion für Hallsensor1
{
// Bremsen M2
if (hallStatus ==LOW)
{
for (int i = 240; i >=0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW); // Motor 2 beginnt zu rotieren
analogWrite(GSM2, i); // Motor 2 bremst
delay(500);
}
}
// Beschleunigen M1
if (hallStatus ==LOW)
{
anfang =millis();
while (millis()-anfang < warte); //nach dem Bremsen M1 warten=Halt, dann
for (int i = 0; i <=240; i += 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW); // Motor 1 beginnt zu rotieren
analogWrite(GSM1, i); // Motor 1 beschleunigt
delay(100);
}
}
}
void HALL1B() //Funktion für Hallsensor 2
{
// Bremsen M1
if (hallStatus2 ==LOW)
{
for (int i = 240; i >=0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW); // Motor 1 beginnt zu rotieren
analogWrite(GSM1, i); // Motor 1 bremst
delay(500);
}
}
anfang =millis();
while (millis()- anfang < warte);
// Beschleunigen M2
if (hallStatus2 ==LOW)
{
for (int i = 0; i <=240; i += 40)
{ //in 40-iger Schritten bis Maximalgeschwindigkeit
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW); // Motor 2 beginnt zu rotieren
analogWrite(GSM2, i); // Motor 2 beschleunigt
delay(100);
}
}
}
//=============================
void loop()
{
hallStatus=digitalRead(hallSensorPin);
hallStatus2=digitalRead(hallSensorPin2);
HALL1A();
HALL1B();
}
Was fest vergeben wird als const definieren.
Was nur zwei Zustände hat als bool definieren.
Was als byte geht als byte definieren.
Vermeide mischen von Typen (long / unsigned long) Da wo es geht versuchen immer unsigned einzusetzen.
Kommentare sollen beschreiben zu was das da ist oder was das machen soll.
Variablen sollten nach Möglichkeit beschreibende Namen haben. GSM1 kann ich mir auch als GleiSMelder ausdenken, aber andere denken da vielleicht an das Simkartenmodul SIM700L. ;)
Das ist was schönes um darus ne Struktur zu machen... Da wirst noch viel Spass haben...
Hier mal die Zuweisungen.
bool hallStatus = LOW; // 1. Hall aktiv
bool hallStatus2=HIGH; // 2. Hall paasiv
// Gleichstrommotor 1
const byte GSM1 = 10;
const byte in1 = 9;
const byte in2 = 8;
// Gleichstrommotor 2
const byte GSM2 = 5; //IN B
const byte in3 = 7; // IN 3
const byte in4 = 6; // IN 4
unsigned long anfang = 0;
const unsigned long warte = 2000; //Pause zwische M1 und M2 im Loop
Und wenn der Threadowner nun mal den kompletten Sketch entsprechend anpasst und auch mal STRG-T in der IDE drückt, dann könnte man sich in dieser Reihenfolge vorantasten
struct
Array
keine blockierenden Abschnitte (keine delay...)
Vereinfachung auf OOP
Vieleicht noch ein kleiner optischer Hinweis, ändere doch den Titel im ersten Post auf "Modellbahn Basteleien" - erstens muss MoBa bei den Arduinern nicht so als Abkürzung bekannt sein, und wenn doch, verbindet das der Insider Arduiner eher mit den "MoBa" Tools - und die verwendest du ja noch nicht in deinem Sketch.
So, habe korrigiert. Titel und Variablentypen. STRG+T zum Formatieren und danke für die Hinweise. Habe mich nochmal zu den Variablentypen belesen, um nachzuvollziehen, wozu das Ganze. Macht jetzt mehr Sinn.
Zum GSM-Namen - da steht es eigentlich als Kommentar drüber
Abgesehen davon, ist denn das Auslagern der Hall-Funktionen in eigene Funktionen sinnvoll? Meine Überlegung war dabei, dass ich so leichter Änderungen vornehmen kann, als wenn das im Loop direkt steht. Außerdem hatte ich das Gefühl, dass es so strukturierter wirkt.
Hier nochmal der korrigierte Sketch:
/*
DC-Motorsteuerung mit L298N zum Regeln
von Anfahr- und Bremsverhalten mit 2 Hallsensoren,
mit Pause zwischen dem Brems- und Anfahrstart
von Schorsch für ZFI
23.05.21
*/
// Definition Hallpins und Hallstatus
const int hallSensorPin = 2;
const int hallSensorPin2 = 3;
bool hallStatus = LOW; // 1. Hall aktiv
bool hallStatus2 = HIGH; // 2. Hall paasiv
// Gleichstrommotor 1
const byte GSM1 = 10;
const byte in1 = 9;
const byte in2 = 8;
// Gleichstrommotor 2
const byte GSM2 = 5; //IN B
const byte in3 = 7; // IN 3
const byte in4 = 6; // IN 4
unsigned long anfang = 0;
const unsigned long warte = 2000; //Pause zwische M1 und M2 im Loop
void setup()
{
Serial.begin(9600);
pinMode(GSM1, OUTPUT);
pinMode(GSM2, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
pinMode(hallSensorPin, INPUT);
pinMode(hallSensorPin2, INPUT);
if (hallStatus == LOW)
{ // Beschleunigen M1 als Zustand nach Einschalten
for (int i = 0; i <= 240; i += 20) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW); // Motor 1 beginnt zu rotieren
analogWrite(GSM1, i); // Motor 1 beschleunigt
delay(100); // delay(100) steuert die Geschwindigkeit des Hochzählens im for
}
}
}
void HALL1A() // Funktion für Hallsensor1
{
// Bremsen M2
if (hallStatus == LOW)
{
for (int i = 240; i >= 0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW); // Motor 2 beginnt zu rotieren
analogWrite(GSM2, i); // Motor 2 bremst
delay(500);
}
}
// Beschleunigen M1
if (hallStatus == LOW)
{
anfang = millis();
while (millis() - anfang < warte); //nach dem Bremsen M1 warten=Halt, dann
for (int i = 0; i <= 240; i += 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW); // Motor 1 beginnt zu rotieren
analogWrite(GSM1, i); // Motor 1 beschleunigt
delay(100);
}
}
}
void HALL1B() //Funktion für Hallsensor 2
{
// Bremsen M1
if (hallStatus2 == LOW)
{
for (int i = 240; i >= 0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW); // Motor 1 beginnt zu rotieren
analogWrite(GSM1, i); // Motor 1 bremst
delay(500);
}
}
anfang = millis();
while (millis() - anfang < warte);
// Beschleunigen M2
if (hallStatus2 == LOW)
{
for (int i = 0; i <= 240; i += 40)
{ //in 40-iger Schritten bis Maximalgeschwindigkeit
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW); // Motor 2 beginnt zu rotieren
analogWrite(GSM2, i); // Motor 2 beschleunigt
delay(100);
}
}
}
//=============================
void loop()
{
hallStatus = digitalRead(hallSensorPin);
hallStatus2 = digitalRead(hallSensorPin2);
HALL1A();
HALL1B();
}
Das wird noch besser.
Die Variablendeklaration für den Hallstatus brauchst Du nicht da, wo sie jetzt ist.
Wenn Du die nur in den Funktionen abfragst, dann am Beispiel von einem Zweig:
void HALL1A() // Funktion für Hallsensor1
{
bool hallStatus = digitalRead(hallSensorPin);
// Bremsen M2
if (hallStatus == LOW)
{
for (int i = 240; i >= 0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW); // Motor 2 beginnt zu rotieren
analogWrite(GSM2, i); // Motor 2 bremst
delay(500);
}
}
// Beschleunigen M1
if (hallStatus == LOW)
{
anfang = millis();
while (millis() - anfang < warte); //nach dem Bremsen M1 warten=Halt, dann
for (int i = 0; i <= 240; i += 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW); // Motor 1 beginnt zu rotieren
analogWrite(GSM1, i); // Motor 1 beschleunigt
delay(100);
}
}
}
Wobei mir noch nicht klar ist, warum Du beides auf low abfragst.
Und dann hauen Dir die ganzen delays die Füsse weg.
Mir sieht das sehr nach einer Schrittkette aus.
Ich hab nur noch nicht verstanden, ob die beiden (hall1a / hall1b) zusammengehören oder getrennt zu betrachten sind.
die Grundidee von Funktionen ist schon in Ordnung. Aber wenn du dir deinen code ansiehst, findest du nicht dass sich die zwei Funktionen sehr ähneln und sich nur in den verwendeten Variablen unterscheiden sonst aber das gleiche machen?
Wenn dir das auch Verbesserungswürdig erscheint, dann gib bescheid.
(P.S.: schau dir noch mal die Variablengröße deiner hallSensorPinX an...)
Hallo,
ich hab jetzt nicht die Ahnung von Modellbahn und was da so benötigt wird. Ich würde das auch in eine Schrittkette packen, wobei mir derzeit nicht klar ist wann denn die erste Lok wieder losfahren kann. Eventuell ein 3. Sensor auf dem Gleis der Lok2 wenn diese die Weiche wieder freigegeben hat , oder halt einfach nach einer Wartezeit. (Anschlusszug im Bahnhof)
Schrittkette
warten auf Sensor1
Lok 1 bremsen
Wartezeit
Lok2 beschleunigen
(optional) Wartezeit
zurück auf 1.
für Beschleunigen und bremsen gibt´s eine Funktion mit den Parametern für die Pins und die "Rampenzeit" und Beschleunigen/Bremsen Als Rückgabewert der Funktion ein bool "Rampe fertig. Das delay() ersetzen durch millis()
alles gekauft, aber dennoch würde ich einen Anfänger in diesen Schritten zum Thema führen:
Schritt:
0 Variablen Zusammenräumen
1 Arrays, For Schleifen, sizeof
2 Strukturen
3 ein wenig Ordnung bringen in den Ablauf - die finite State Machine
4 von der einfachen Strucktur zur Objektorientierten Programmierung
schön langsam und eins nach dem anderen, wenn der TO so weit ist...
@my_xy_projekt: Das mit dem Verlagern der Variablen für den Hallstatus erschließt sich mir nicht. bringt das, Speicherplatz?? LOW-Abfrage: weil bei LOW durchgeschaltet wird, also wenn der Magnet den Hallsensor passiert. Das ist ja nur ein kurzer Moment und reicht zum Auslösen der Folgeaktionen. Die delays steuern nur das Tempo des Hochzählens/Runterzählens für das Anfahren/Bremsen. Beim Testen ist mir nichts auf die Füße gefallen, geht einfach nur. Hall1A und Hall1B sind getrennt zu betrachten. Das sind quasi "Kurz-Taster". @Rentner Heinz
es gibt keine Weiche, stell dir 2 Gleisovale vor. Da gibt es einen Bahnhofsbereich. In den fährt ein Lok von rechts und eine von links ein. Ohne alles würden die einfach nur kreiseln. Jetzt ist aber vor jeder Einfahrt pro Kreis in Fahrtrichtung ein Hallsensor im Gleis verbaut. Magnet unter der Lok1 löst das Low kurz aus. diese beginnt zu bremsen bis zu Halt, dann wird der Hallstatus des anderen Gleises auf Low gesetzt und Lok2 beschleunigt. Irgendwann kommt sie über dem Hallsensor2 vorbei, fährt bremsend bis Halt in ihren Bahnhofsbereich und Gleis1 (Hallsensor 1) wird wieder Low, Lok 1 beschleunigt...
Klar kann man das mit Rampe machen, oder auch map. Ich habe das for gewählt, weil ich darin über die Schrittweite (+40 oder anderes) bequem die Anfahr/Bremsgeschwindigkeit regeln kann. Die Fahrspannung ist ja konstant. Ich muss dazu sagen, das ich in Spur Z unterwegs bin. Die kleinen Loks reagieren alle ganz unterschiedlich auf Spannungsänderung. So kann ich bei Lokwechsel schneller die "Empfindlichkeit" anpassen. Ach, die Pins habe ich so benannt, weil sie genauso auf dem L298N-Treibermodul beschriftet sind, erleichtert das verkabeln @noiasca
ganz ehrlich? von den 5 Punkten habe ich den Punkt 0 und 5 verstanden.
Aber die Hilfe für eine Vereinfachung der Funktionen ("gib Bescheid") nehme ich dankbar an.
Und an alle: Das ganze ist eine Tischanlage, der Gleisplan sieht verwirrend aus, ist im Endergebnis aber nur 2 getrennte Gleisreise., Wer will guggt hier: https://papperlapapp220.de/Modelle/Anlagen/wild,-wild-West/
Hi,
es geht nicht um den Speicherplatz, sondern um die Gültigkeit der Variablen.
Nur da wo sie tatsächlich gebraucht werden, diese auch deklarieren und verwenden.
Spart Dir später suchen, wenn die versehentlich an anderer Stelle verändert werden.
Was beim LOW passieren soll, habe ich schon verstanden. Nur eben nicht warum Du den Status 2mal abfragst.
Einmal LOW ist eben LOW. ein zweites if braucht es da nicht.
Das mit den delay() fällt Dir irgendwann auf die Füsse, weil Du in der Zeit nichts anderes machen kannst.
Ansonsten bin ich noch immer dabei das als Schrittkette zu machen und nicht die ganzen Bedingungen verketten.
Die Liste dient ja nur dazu einen Fahrplan zu haben, den man gehen kann.
Übrigens die Liste hat zwar 5 Punkte aber sie geht nur von 0 bis 4.
Das ist der erste große Punkt den du nachfolgend verstehen musst:
C++ beginnt beim Zählen mit 0 ... (nicht mit 1).
Immer dann wenn du versuchst, Variablen durchzunummerieren, dann "schreit" das förmlich nach Arrays.
Angewandt auf deinen Sketch würde das in etwa so aussehen:
/*
DC-Motorsteuerung mit L298N zum Regeln
von Anfahr- und Bremsverhalten mit 2 Hallsensoren,
mit Pause zwischen dem Brems- und Anfahrstart
von Schorsch für ZFI
Schritt:
0 Variablen Zusammenräumen
1 Arrays, For Schleifen <--
2 Strukturen
3 ein wenig Ordnung bringen in den Ablauf - die finite State Machine
4 von der einfachen Strucktur zur Objektorientierten Klasse
*/
const byte hallSensorPin[] = {2, 3}; // Definition Hallpins und Hallstatus
bool hallStatus[] = {LOW, LOW}; // Hall aktiv oder inaktiv
const byte gsm[] = {10, 5}; // Pin für Gleich Strom Motor - Variablen beginnen immer klein
const byte in1[] = {9, 8}; // Pin zum L298N
const byte in2[] = {7, 6}; // Pin zum L298N
const size_t anzMotoren = sizeof(gsm); // wir lassen uns die größe des Byte arrays der gsm
unsigned long anfang = 0;
const unsigned long warte = 2000; //Pause zwische M1 und M2 im Loop
void setup()
{
Serial.begin(9600);
for (size_t i = 0; i < anzMotoren; i++)
{
pinMode(gsm[i], OUTPUT);
pinMode(in1[i], OUTPUT);
pinMode(in2[i], OUTPUT);
pinMode(hallSensorPin[i], INPUT);
}
if (hallStatus[0] == LOW)
{ // Beschleunigen M1 als Zustand nach Einschalten
for (int i = 0; i <= 240; i += 20) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1[0], HIGH);
digitalWrite(in2[0], LOW); // Motor 1 beginnt zu rotieren
analogWrite(gsm[0], i); // Motor 1 beschleunigt
delay(100); // delay(100) steuert die Geschwindigkeit des Hochzählens im for
}
}
}
void hall(byte bremser, byte anfahrer) // Funktion für Hallsensor1
{
// den Bremser bremsen
if (hallStatus[anfahrer] == LOW)
{
for (int i = 240; i >= 0; i -= 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1[bremser], HIGH);
digitalWrite(in2[bremser], LOW); // Motor bremser beginnt zu rotieren
analogWrite(gsm[bremser], i); // Motor bremser bremst
delay(500);
}
}
// den Anfahrer anfahren
if (hallStatus[anfahrer] == LOW)
{
anfang = millis();
while (millis() - anfang < warte); //nach dem Bremsen warten=Halt, dann
for (int i = 0; i <= 240; i += 40) //in 40-iger Schritten bis Maximalgeschwindigkeit
{
digitalWrite(in1[anfahrer], HIGH);
digitalWrite(in2[anfahrer], LOW); // Motor beginnt zu rotieren
analogWrite(gsm[anfahrer], i); // Motor beschleunigt
delay(100);
}
}
}
//=============================
void loop()
{
hallStatus[0] = digitalRead(hallSensorPin[0]);
hallStatus[1] = digitalRead(hallSensorPin[1]);
hall(0, 1);
hall(1, 0);
}
wenn ich mich nicht vertippt habe, dann macht das das gleiche wie dein Sketch.
Vieleicht findest Fehler - dann bitte ausbessern und neu posten.
Der Sketch ist um gut 20 Zeilen kürzer.
Schön immer noch nicht, aber darum gehts nun noch nicht.
Aktuell gehts mir nur um die Arrays.
Und wenn du dich fragst, warum ich in1 und in2 nicht angegriffen habe ... nun ja, da bin ich der Meinung das ist eher ein direction und enable, egal, lassen wir mal die Beschriftung vom ersten Eingang...
Der Sinn von Funktionen ist eigentlich dass man keinen duplizierten Code hat. Wenn man also mehrmals das Gleiche macht, sollte man nicht einfach zwei Funktionen schreiben die jeweils nur andere Variablen/Konstanten verwenden. Sondern die verwendeten Werte als Parameter übergeben
z.B. vereinfacht:
void loop()
{
schalten(in1, in2, GSM1);
}
void schalten(byte pin1, byte pin2, byte speed)
{
...
digitalWrite(pin1, HIGH); //es bringt eigentlich nichts das in einer for-Schleife immer wieder zu tun
digitalWrite(pin2, LOW);
analogWrite(speed, i);
...
}
Das geht auch mit Variablen die in der Funktion verändert werden. Allerdings muss man diese unbedingt als Referenz übergeben wenn man den Wert beim nächsten Aufruf wieder braucht
@my_xy_projekt
Variablen - verstanden. Status abfrage 2 mal, auch. Zur Erklärung: Ich habe lange gesessen und "häppchenweise" probiert. Erst mal 1 Magnet und M1 soll beschleunigen. Dann das Bremsen ausprobiert, beides zusammengeführt und da haben wir den Salat, beim Kopieren rutscht das eben mal mit rein, grhhh. Wird korrigiert. Danke. @noiasca
war mir klar, das mit 0 beginnen, hätte exakt schreiben müssen "den 1 .und 5. Punkt..." Aber recht hast du. Arrays habe ich schon angewendet, ich nutze das schon auf der Anlage für die Beleuchtung der Häuser. Da bot sich das an, weil ich da auch gleich Gruppen von Häusern befeuern konnte. Ich taste mich seit Weihnachten in die Materie rein, muss aber an der richtigen Stelle immer erst auf die Idee kommen. Da ist dein Hinweis mit "immer wenn nummeriert wird..." sehr praktisch. Ich werde meinen Sketch mal umbauen, aber schön laaangsam, soll ja funktionieren. @Serenifly
Das probiere ich in der Kopie der Kopie der Kopie aus. Danke, Ideen braucht der Mensch
Das freut mich, aber warum eigentlich? Dein Programm funktioniert doch und warum willst Du Zeit mit Programmieren verschwenden, wo Du doch so tolle Anlagen bauen kannst. Warum will der erfolgreiche Bildhauer plötzlich Maler werden? Was kannst Du mit Deinen bisherigen Fähigkeiten nicht realisieren, was fehlt Dir?
Du möchtest das Andreaskreuz in die Weichensteuerung integrieren? Dann muß sie blockadearm programmiert werden:
const byte akreuzPin = 13; // Pin für Andreaskreuz
const uint32_t KURZ = 200, LANG = 500;
uint32_t jetzt = millis(); // aktuelle Zeit in ms nach Reset
void setup()
{
pinMode(akreuzPin, OUTPUT);
}
void akreuz()
{
static uint32_t intervall = 0;
static uint32_t vorhin = 0;
static byte schritt = 0;
if (jetzt - vorhin >= intervall) {
vorhin = jetzt;
switch (schritt++) {
case 0:
digitalWrite(akreuzPin, HIGH);
intervall = LANG;
break;
case 1:
digitalWrite(akreuzPin, LOW);
intervall = KURZ;
break;
case 2:
digitalWrite(akreuzPin, HIGH);
intervall = KURZ;
break;
case 3:
digitalWrite(akreuzPin, LOW);
intervall = KURZ;
schritt = 0;
break;
}
}
}
void loop()
{
jetzt = millis();
akreuz();
}
Dieses Programm arbeitet blockadearm mit millis().
Extra für Modellbahner hat @MicroBahner die MobaTools entwickelt, die auch einen blockadearmen Zeitgeber "Eieruhr" enthalten:
#include <MobaTools.h> // Kann mittels Arduino-IDE installiert werden.
MoToTimer Akreuz;
const byte akreuzPin = 13; // Pin für Andreaskreuz
const uint32_t KURZ = 200, LANG = 500;
void setup()
{
pinMode(akreuzPin, OUTPUT);
}
void akreuz()
{
static byte schritt = 0;
if (!Akreuz.running()) {
switch (schritt++) {
case 0:
digitalWrite(akreuzPin, HIGH);
Akreuz.setTime(LANG); // "Eieruhr" aufziehen
break;
case 1:
digitalWrite(akreuzPin, LOW);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
break;
case 2:
digitalWrite(akreuzPin, HIGH);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
break;
case 3:
digitalWrite(akreuzPin, LOW);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
schritt = 0;
break;
}
}
}
void loop()
{
akreuz();
}
Inzwischen haben die Frauen bemerkt, wie viel sicherer so ein blinkender Übergang ist (auch wenn er derzeit ständig blinkt) und sie wollen mehr Andreaskreuze! Also tun die Männer, was sie immer tun, sie bauen neue. Dazu wird die Funktion in die Methode einer Klasse gepackt und diese Klasse durch mehrere Instanzen genutzt. Da in dieser Gegend wegen der Wiedererkennung blinkende Übergänge unterschiedliche Blinkzeiten haben sollen, können die Instanzen unterschiedliche Zeiten für KURZ oder LANG haben:
#include <MobaTools.h> // Kann mittels Arduino-IDE installiert werden.
class AndreasKreuze {
private: // Die folgenden Objekte sind nur innerhalb der Klasse zugänglich. Am Anfang einer Klasse überflüssig.
const byte akreuzPin;
const uint32_t KURZ, LANG;
MoToTimer Akreuz;
byte schritt = 0;
public:
AndreasKreuze(const byte _akreuzPin, const uint32_t _KURZ, const uint32_t _LANG)
: akreuzPin(_akreuzPin), KURZ(_KURZ), LANG(_LANG)
{}
void init() {
pinMode(akreuzPin, OUTPUT);
}
void blinken() {
if (!Akreuz.running()) {
switch (schritt++) {
case 0:
digitalWrite(akreuzPin, HIGH);
Akreuz.setTime(LANG); // "Eieruhr" aufziehen
break;
case 1:
digitalWrite(akreuzPin, LOW);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
break;
case 2:
digitalWrite(akreuzPin, HIGH);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
break;
case 3:
digitalWrite(akreuzPin, LOW);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
schritt = 0;
break;
}
}
}
};
AndreasKreuze andreasKreuze[] {
//akreuzPin, KURZ, LANG;
{13, 200, 500}, // am Bahnhof von Norden
{8, 200, 500}, // am Bahnhof von Süden
{9, 500, 200}, // am Wasserturm
{10, 222, 222} // am Saloon
};
void setup()
{
for (AndreasKreuze &a : andreasKreuze) a.init(); // alle Instanzen durchlaufen die Methode init
}
void loop()
{
for (AndreasKreuze &a : andreasKreuze) a.blinken(); // alle Instanzen durchlaufen die Methode blinken
}
Geht das so in Deine Richtung hinsichtlich Interesse am Programmieren?
Hallo agmue,
vielen Dank für die Tipps. Link erstellt. Kurz zur Erklärung. Seit 2011 bin ich dem Modellbahnwahn in Spur Z verfallen. Ich bin kein Fan von Fertigteilen, das kannst du auf meiner Webseite sehen. Außer den Häusern mache ich auch in Elektrik/Elektronik und habe bis vor kurzem mit Leiterplatte, Relais, Poti & Co. hantiert. Jede Anlage verlangt nach individuellen Einstellungen. Dann kam der Nano-Weihnachtskalender... Ich habe mir die MoBa-Tools angesehen. Prima, aber wenn man was individuelles braucht, muss man anpassen. Das bedeutet, dass man versteht was da ist, um es ändern zu können. Deshalb fange ich "unten" an und erfinde dabei das berühmte Fahrrad zum 2. Mal. Und ich mache es nicht nur für mich, sondern auch für andere Einsteiger aus der Z-Fan-Gemeinde und stelle es hier zur Verfügung. Arduino für Anfänger. Sicher gibt es besseres aber die Leute sind dankbar.
Deine Erklärung mit dem Andreaskreuz ist Klasse. So liebe ich das, danke.
Und Es gab berühmte Maler, die waren auch gute Bildhauer
Den Schraubendreher, den Hammer, die Bohrmaschine, den Kleber ... erfindest Du alles neu? Manche Werkzeuge wie Programmbibliotheken darf man ruhig als Hilfsmittel akzeptieren, finde ich. Ihre Anwendung muß man allerdings verstehen, das kann ich nur bekräftigen.
Deine Anlage ist so einmalig wie das dazugehörige Programm. Landschaftsbau und Programmieren sind kreative Prozesse und passen daher gut zusammen.
Freut mich, Du darfst da gerne was in Deine Anleitung übernehmen.
Nachdem die Andreaskreuze (ich verlege sie auf die Pins A0 bis A3) schön blinken, sollten auch Züge fahren.
Mir fällt auf, daß in1 bis in4 nicht verändert werden, weshalb man sie auf feste Potentiale legen könnte, was µC-Pins frei macht für neue Aufgaben.
In Deinem Programm in #1 sehe ich die Zustände BREMSEN, PAUSE, BESCHLEUNIGEN, denen ich noch den Zustand "WARTEN auf Sensorimpuls" hinzufüge. Mit der Schrittkette und den MobaTools als Zeitverwalter ergibt sich dieses Programm für die zwei Züge:
#include <MobaTools.h> // Kann mittels Arduino-IDE installiert werden.
MoToTimer Zeit;
// Definition Hallpins und Hallstatus
const byte hallSensorPin1 = 2;
const byte hallSensorPin2 = 3;
// Gleichstrommotor 1
const byte GSM1 = 10;
const byte in1 = 9;
const byte in2 = 8;
// Gleichstrommotor 2
const byte GSM2 = 5; //IN B
const byte in3 = 7; // IN 3
const byte in4 = 6; // IN 4
void setup()
{
Serial.begin(9600);
Serial.println("\nStart");
pinMode(hallSensorPin1, INPUT_PULLUP);
pinMode(hallSensorPin2, INPUT_PULLUP);
// Motor 1 Richtung H-Brücke
pinMode(GSM1, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
// Motor 2 Richtung H-Brücke
pinMode(GSM2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);
}
void HALL1A() // Funktion für Hallsensor 1
{
const uint32_t PAUSENZEIT = 2000;
const byte BESCHLEUNIGUNG = 40;
const byte MAXGESCHWINDIGKEIT = 240;
static byte geschwindigkeit = 0;
enum struct Schritte {WARTEN, BREMSEN, PAUSE, BESCHLEUNIGEN}; // Benennung und Numerierung der Schritte der Schrittkette
static Schritte schritt = Schritte::BESCHLEUNIGEN;
switch (schritt) {
case Schritte::WARTEN:
if (!digitalRead(hallSensorPin1)) {
schritt = Schritte::BREMSEN;
}
break;
case Schritte::BREMSEN:
if (!Zeit.running()) {
if (geschwindigkeit >= BESCHLEUNIGUNG) {
geschwindigkeit -= BESCHLEUNIGUNG;
analogWrite(GSM2, geschwindigkeit); // Motor 2 bremst
Zeit.setTime(500);
Serial.print("geschwindigkeit GSM2: "); Serial.println(geschwindigkeit);
} else {
geschwindigkeit = 0;
analogWrite(GSM2, geschwindigkeit); // Motor 2 steht
Zeit.setTime(PAUSENZEIT);
schritt = Schritte::PAUSE;
}
}
break;
case Schritte::PAUSE:
if (!Zeit.running()) {
schritt = Schritte::BESCHLEUNIGEN;
}
break;
case Schritte::BESCHLEUNIGEN:
if (!Zeit.running()) {
if (geschwindigkeit <= MAXGESCHWINDIGKEIT - BESCHLEUNIGUNG) {
geschwindigkeit += BESCHLEUNIGUNG;
analogWrite(GSM1, geschwindigkeit); // Motor 1 beschleunigt
Zeit.setTime(100);
Serial.print("geschwindigkeit GSM1: "); Serial.println(geschwindigkeit);
} else {
geschwindigkeit = MAXGESCHWINDIGKEIT;
analogWrite(GSM1, geschwindigkeit); // Motor 1 auf maximaler Geschwindigkeit
Zeit.setTime(PAUSENZEIT);
schritt = Schritte::WARTEN;
}
}
break;
}
}
void HALL1B() //Funktion für Hallsensor 2
{
const uint32_t PAUSENZEIT = 2000;
const byte BESCHLEUNIGUNG = 40;
const byte MAXGESCHWINDIGKEIT = 240;
static byte geschwindigkeit = MAXGESCHWINDIGKEIT;
enum struct Schritte {WARTEN, BREMSEN, PAUSE, BESCHLEUNIGEN}; // Benennung und Numerierung der Schritte der Schrittkette
static Schritte schritt = Schritte::WARTEN;
switch (schritt) {
case Schritte::WARTEN:
if (!digitalRead(hallSensorPin2)) {
schritt = Schritte::BREMSEN;
}
break;
case Schritte::BREMSEN:
if (!Zeit.running()) {
if (geschwindigkeit >= BESCHLEUNIGUNG) {
geschwindigkeit -= BESCHLEUNIGUNG;
analogWrite(GSM1, geschwindigkeit); // Motor 1 bremst
Zeit.setTime(500);
Serial.print("geschwindigkeit GSM1: "); Serial.println(geschwindigkeit);
} else {
geschwindigkeit = 0;
analogWrite(GSM1, geschwindigkeit); // Motor 1 steht
Zeit.setTime(PAUSENZEIT);
schritt = Schritte::PAUSE;
}
}
break;
case Schritte::PAUSE:
if (!Zeit.running()) {
schritt = Schritte::BESCHLEUNIGEN;
}
break;
case Schritte::BESCHLEUNIGEN:
if (!Zeit.running()) {
if (geschwindigkeit <= MAXGESCHWINDIGKEIT - BESCHLEUNIGUNG) {
geschwindigkeit += BESCHLEUNIGUNG;
analogWrite(GSM2, geschwindigkeit); // Motor 2 beschleunigt
Zeit.setTime(100);
Serial.print("geschwindigkeit GSM2: "); Serial.println(geschwindigkeit);
} else {
geschwindigkeit = MAXGESCHWINDIGKEIT;
analogWrite(GSM2, geschwindigkeit); // Motor 2 auf maximaler Geschwindigkeit
Zeit.setTime(PAUSENZEIT);
schritt = Schritte::WARTEN;
}
}
break;
}
}
//=============================
void loop()
{
HALL1A();
HALL1B();
}
Die Funktionen sehen doch recht gleich aus, also kann man sie mit Hilfe von Feldern zusammenfassen:
Wenn nun die Züge durch die Landschaft fahren, sollten auch die Andreaskreuze blinken. Weil das konzeptionell bereits eingeplant ist, lassen sich beide Programme einfach kombinieren:
#include <MobaTools.h> // Kann mittels Arduino-IDE installiert werden.
MoToTimer Zeit;
// Definition Hallpins und Hallstatus
const byte hallSensorPin[] = {2, 3};
// Gleichstrommotor
const byte GSM[] = {10, 5};
const byte in1[] = {9, 7};
const byte in2[] = {8, 6};
class AndreasKreuze {
private: // Die folgenden Objekte sind nur innerhalb der Klasse zugänglich. Am Anfang einer Klasse überflüssig.
const byte akreuzPin;
const uint32_t KURZ, LANG;
MoToTimer Akreuz;
byte schritt = 0;
public:
AndreasKreuze(const byte _akreuzPin, const uint32_t _KURZ, const uint32_t _LANG)
: akreuzPin(_akreuzPin), KURZ(_KURZ), LANG(_LANG)
{}
void init() {
pinMode(akreuzPin, OUTPUT);
}
void blinken() {
if (!Akreuz.running()) {
switch (schritt++) {
case 0:
digitalWrite(akreuzPin, HIGH);
Akreuz.setTime(LANG); // "Eieruhr" aufziehen
break;
case 1:
digitalWrite(akreuzPin, LOW);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
break;
case 2:
digitalWrite(akreuzPin, HIGH);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
break;
case 3:
digitalWrite(akreuzPin, LOW);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
schritt = 0;
break;
}
}
}
};
AndreasKreuze andreasKreuze[] {
//akreuzPin, KURZ, LANG;
{A3, 200, 500}, // am Bahnhof von Norden
{A2, 200, 500}, // am Bahnhof von Süden
{A1, 500, 200}, // am Wasserturm
{A0, 222, 222} // am Saloon
};
void setup()
{
Serial.begin(9600);
Serial.println("\nStart");
for (byte j = 0; j < sizeof(hallSensorPin); j++) {
pinMode(hallSensorPin[j], INPUT_PULLUP);
pinMode(GSM[j], OUTPUT);
pinMode(in1[j], OUTPUT);
pinMode(in2[j], OUTPUT);
digitalWrite(in1[j], HIGH);
digitalWrite(in2[j], LOW);
}
for (AndreasKreuze &a : andreasKreuze) a.init(); // alle Instanzen durchlaufen die Methode init
}
void HALL(byte hallA) // Funktion für Hallsensor
{
byte hallB = 1;
if (hallA == 1) hallB = 0;
const uint32_t PAUSENZEIT = 2000;
const uint8_t BESCHLEUNIGUNG = 40;
const uint16_t BREMSINTERVALL = 500;
const uint16_t BESCHLEUNIGUNGSINTERVALL = 100;
const byte MAXGESCHWINDIGKEIT = 240;
static byte geschwindigkeit[] = {0, 0};
enum struct Schritte {WARTEN, BREMSEN, PAUSE, BESCHLEUNIGEN}; // Benennung und Numerierung der Schritte der Schrittkette
static Schritte schritt[] = {Schritte::BESCHLEUNIGEN, Schritte::WARTEN};
switch (schritt[hallA]) {
case Schritte::WARTEN:
if (!digitalRead(hallSensorPin[hallA])) {
schritt[hallA] = Schritte::BREMSEN;
}
break;
case Schritte::BREMSEN:
if (!Zeit.running()) {
if (geschwindigkeit[hallB] > BESCHLEUNIGUNG) {
geschwindigkeit[hallB] -= BESCHLEUNIGUNG;
analogWrite(GSM[hallB], geschwindigkeit[hallB]); // Motor bremst
Serial.print("hallB: "); Serial.print(hallB); Serial.print("\tgeschwindigkeit[hallB]: "); Serial.println(geschwindigkeit[hallB]);
Zeit.setTime(BREMSINTERVALL);
} else {
geschwindigkeit[hallB] = 0;
analogWrite(GSM[hallB], geschwindigkeit[hallB]); // Motor steht
Serial.print("hallB: "); Serial.print(hallB); Serial.print("\tgeschwindigkeit[hallB]: "); Serial.println(geschwindigkeit[hallB]);
Zeit.setTime(PAUSENZEIT);
schritt[hallA] = Schritte::PAUSE;
}
}
break;
case Schritte::PAUSE:
if (!Zeit.running()) {
schritt[hallA] = Schritte::BESCHLEUNIGEN;
}
break;
case Schritte::BESCHLEUNIGEN:
if (!Zeit.running()) {
if (geschwindigkeit[hallA] < MAXGESCHWINDIGKEIT - BESCHLEUNIGUNG) {
geschwindigkeit[hallA] += BESCHLEUNIGUNG;
analogWrite(GSM[hallA], geschwindigkeit[hallA]); // Motor beschleunigt
Serial.print("hallA: "); Serial.print(hallA); Serial.print("\tgeschwindigkeit[hallA]: "); Serial.println(geschwindigkeit[hallA]);
Zeit.setTime(BESCHLEUNIGUNGSINTERVALL);
} else {
geschwindigkeit[hallA] = MAXGESCHWINDIGKEIT;
analogWrite(GSM[hallA], geschwindigkeit[hallA]); // Motor 1 auf maximaler Geschwindigkeit
Serial.print("hallA: "); Serial.print(hallA); Serial.print("\tgeschwindigkeit[hallA]: "); Serial.println(geschwindigkeit[hallA]);
Zeit.setTime(PAUSENZEIT);
schritt[hallA] = Schritte::WARTEN;
}
}
break;
}
}
void loop()
{
HALL(0);
HALL(1);
for (AndreasKreuze &a : andreasKreuze) a.blinken(); // alle Instanzen durchlaufen die Methode blinken
}
Wie man sieht, kann so ein Nano mehrere Dinge quasie gleichzeitig erledigen. Es sind auch noch Kapazitäten frei (benutzt 10% des Programmspeicherplatzes, 17% des dynamischen Speichers) beispielsweise für ein flackerndes Lagerfeuer oder dergleichen. Mittels Portexpandern wie MAX23017 oder MAX23S17 sind auch noch mehr Pins möglich.
TODO: Natürlich sollten die Andreaskreuze nur blinken, wenn sich ein Zug nähert. Dazu wären aber zusätzliche Sensoren notwendig.
Hallo @agmue ,
das ist der Wahnsinn. Vielen Dank, ich werde mich einfuchsen und das Ganze auf einer kleinen Testanlage ausprobieren. Sowohl die Motoren/Blink Sketche, als auch den Einbau von Weichenschaltung mit Relais/Servo. Die habe ich als "Einzelstücke" schon fertig, wie auch verschiedene "Blinkereien", Schrittmotoren-Sketche für die geplanten Aktionen von Funktionsmodellen wie Karussellen, Kränen, Windmühlen etc. Ich habe jetzt erst mal satt zu tun. Ich werde bestimmt etwas von dir verwenden und das auch in meinen Anleitungen deklarieren, wenn es dir recht ist. Vielleicht hast du ja einen direkten Link, den ich einbauen kann.
Ich melde mich auf jeden Fall, besonders wenn es Probleme gibt, sei gerüstet.
Bei meinem Kohlekranfunktionsmodell habe ich einen automatischen Ablauf implementiert, der zum Drehen einen Schrittmotor verwendet und zum Heben ein Servo. Ein befreundeter Modellbahner hat ein wunderschönes Kranmodell auf diese Art animiert.
Kannst Du gerne verwenden, aber das geistige Eigentum ist doch eher vernachlässigbar gering, daher erscheint mir meine Erwähnung nicht notwendig.
Moin agmue,
jetzt hast du den Salat, natürlich habe ich Fragen!! Ich bastele mich gerade durch deine Beispiele, die natürlich funktionieren. Aber zum besseren Verständnis, aus dem void akreuz:
Blockquote
if (jetzt - vorhin >= intervall) {
vorhin = jetzt;
switch (schritt++) {
case 0:
digitalWrite(akreuzPin, HIGH);
intervall = LANG;
break;
case 1:
digitalWrite(akreuzPin, LOW);
intervall = KURZ;
break;
Blockquote
Das bedeutet, das im 1 Falle steht if (0-0>=0),
setze vorhin (0) = jetzt (0)
erhöhe schritt um 1
falls das 0 ist;
schalte den akreuzPin auf an mit dem Intervall lang (500)
break =und brich ab
usw bis zum 4. Fall case = 3
Wo ich Verständnisproblem habe ist der Einstieg ins if. wird den mit dem switch nicht gleich durch schritt++ aus 0 eine 1 ???
Und was genau bewirkt das break? Laut Referenz heißt es dort:
break wird benutzt, um eine do, for oder while-Schleife zu verlassen, ohne dass die Bedingung zum Schleifenabbruch erfüllt ist. Das erschließt sich mir gar nicht. Hilf mal bitte dem Arduino-DAU
Und im gleichen Zusammenhang mit dem MoToTimer Akreuz:
void akreuz()
{
static byte schritt = 0;
if (!Akreuz.running()) {
switch (schritt++) {
case 0:
digitalWrite(akreuzPin, HIGH);
Akreuz.setTime(LANG); // "Eieruhr" aufziehen
break;
case 1:
digitalWrite(akreuzPin, LOW);
Akreuz.setTime(KURZ); // "Eieruhr" aufziehen
break;
Das if(!Akreuz.running() heißt dann wohl: Wenn der Timer Akreuz NICHT läuft, ... und dann wieder das switch. Auch das stellt mich vor ein Rätsel. Wenn ich das verstanden habe, gehts mit der Klasse weiter.
Ich danke schon mal.