Ich verzweifel noch an diesem Code! - Arduino Uno Schattenbahnhof

Hallo liebe Arduino-Gemeinde!

Ich habe mein erstes Projekt mit dem Arduino Uno nun schon sehr weit gebracht: Eine Schattenbahnhofssteuerung auf unserer Märklin-Anlage. Sowohl hardware- als auch softwareseitig hat bisher alles bestens funktioniert. Aber nun habe ich zwei neue Funktionen eingebaut (Funktionen nicht im programmiertechnischen Sinn) und der Arduino Uno dreht völlig durch. Das äußert sich vor allem darin, dass über die serielle Schnittstelle nur verworrene Zeichenfolgen auf dem PC ankommen und ich wohl irgendwie im void setup stecken bleibe (obwohl die neuen Features gar nichts mit der void setup zu tun haben). Wenn ich dann die Serial.print() aus dem neuen Teil auskommentiere, dann startet das Programm auf dem Arduino Uno zunächst normal, jedoch springt er irgendwann mitten im Betrieb wieder in die void setup, ähnlich einem Reset. Bin damit wirklich am verzweifeln, denn wenn ich den neuen Teil komplett auskommentiere, läuft alles bestens!

Ich poste hier mal den Code, welcher nur den neuen Teil beinhaltet. Den gesamten Code hänge ich in den Anhang.
An dieser Stelle entschuldige ich mich direkt mal für meine wahrscheinlich stümperhafte Programmierung. Ich habe teilweise viel Copy-Paste nutzen müssen und hätte sicherlich die ein oder andere Funktion anlegen können. Nun ist alles in der "void setup" und "void loop" untergebracht.

Code (nur neuer Teil):

  //beim Drücken des roten Tasters: Durchfahrtsmodus (neu in Version 1.1)
	else if (digitalRead(buttonR)==LOW){ //wenn Rot gedrückt wird...
		delay(100);//Entprellen
		Serial.println("Rot gedrueckt. Durchfahrtsmodus gestartet.");
		Serial.print("Durchfahrt auf gleis: "); 
		Serial.println(einfahrender);
		while(digitalRead(buttonR)==HIGH){ //solange Rot nicht wieder gedrückt wird..
			switch(einfahrender){ //je nachdem, welches Gleis frei ist
			case 1:
				digitalWrite(gleis1,HIGH); //Schalte Fahrstrom auf Gleis 1 dauerhaft ein
				digitalWrite(LED1,LOW); // LED 1 blinkt
				delay(50);
				digitalWrite(LED1,HIGH);
				delay(50);
				break;
			case 2:
				digitalWrite(gleis2,HIGH); //Schalte Fahrstrom auf Gleis 2 dauerhaft ein
				digitalWrite(LED2,LOW); // LED 2 blinkt
				delay(50);
				digitalWrite(LED2,HIGH);
				delay(50);
				break;
			case 3:
				digitalWrite(gleis3,HIGH); //Schalte Fahrstrom auf Gleis 3 dauerhaft ein
				digitalWrite(LED3,LOW); // LED 3 blinkt
				delay(50);
				digitalWrite(LED3,HIGH);
				delay(50);
				break;
			}
		}
		delay(100);//Entprellen
		Serial.println("Rot erneut gedrueckt. Durchfahrtsmodus beendet.");
		digitalWrite(gleis1,LOW); //nach erneutem Drücken von Rot, überall Fahrstrom abschalten
		digitalWrite(gleis2,LOW);
		digitalWrite(gleis3,LOW);
		Serial.println("Fahrstrom auf allen Gleisen deaktiviert");
		Serial.print("naechstes einfahrendes gleis: "); Serial.println(einfahrender);
		Serial.print("naechstes ausfahrendes gleis: "); Serial.println(ausfahrender);
		Serial.println("Warte auf einfahrenden Zug..."); 
	}//zurück in kontinuierlichen Betrieb
  
	//beim Drücken des grünen Tasters: Zug abfahren lassen. (neu in Version 1.1)
	else if (digitalRead(buttonG)==LOW){ //wenn Grün gedrückt wird...
		delay(100);//Entprellen
		Serial.println("Grün gedrückt. Lasse Zug ausfahren...");
		ausfahrender++; //Ausfahrtsgleis wird erhöht, damit nahtlos im kontinuierlichen Betrieb weiter gemacht werden kann
		if (ausfahrender>3)
		  ausfahrender=1;
		switch(ausfahrender){
		case 1:
			Serial.println("Zug von Gleis 1 faehrt ab...");
			digitalWrite(gleis1,HIGH); //Zug von Gleis 1 fährt ab
			delay(4000);
			digitalWrite(gleis1,LOW);
			digitalWrite(LED1,LOW); //LED 1 aus
			gleisBelegt[0]=false;
			Serial.println("Zug von Gleis 1 abgefahren");
			Serial.println("Stelle Weichen fuer einfahrt auf Gleis 1...");
			digitalWrite(weicheA_L,HIGH); // Weichen werden für Einfahrt auf Gleis 1 gestellt
			delay(200);
			digitalWrite(weicheA_L,LOW);
			Serial.println("Weiche A auf links gestellt");
			einfahrender = 1; //Lege dieses Gleis für nächste Einfahrt fest
			ausfahrender = 2; //Lege nächstes Gleis für nächste Abfahrt fest
			break;
		case 2:
			Serial.println("Zug von Gleis 2 faehrt ab...");
			digitalWrite(gleis2,HIGH); //Zug von Gleis 2 fährt ab
			delay(4000);
			digitalWrite(gleis2,LOW);
			digitalWrite(LED2,LOW); //LED 2 aus
			gleisBelegt[1]=false;
			Serial.println("Zug von Gleis 2 abgefahren");
			Serial.println("Stelle Weichen fuer einfahrt auf Gleis 2...");
			digitalWrite(weicheA_G,HIGH); // Weichen werden für Einfahrt auf Gleis 2 gestellt
			delay(200);
			digitalWrite(weicheA_G,LOW);
			digitalWrite(weicheB_L,HIGH);
			delay(200);
			digitalWrite(weicheB_L,LOW);
			Serial.println("Weiche A auf gerade gestellt, Weiche B auf links gestellt");
			einfahrender = 2; //Lege dieses Gleis für nächste Einfahrt fest
			ausfahrender = 3; //Lege nächstes Gleis für nächste Abfahrt fest
		  break;
		case 3:
			Serial.println("Zug von Gleis 3 faehrt ab...");
			digitalWrite(gleis3,HIGH); //Zug von Gleis 3 fährt ab
			delay(4000);
			digitalWrite(gleis3,LOW);
			digitalWrite(LED3,LOW); //LED 3 aus
			gleisBelegt[2]=false;
			Serial.println("Zug von Gleis 3 abgefahren");
			Serial.println("Stelle Weichen fuer einfahrt auf Gleis 3...");
			digitalWrite(weicheA_G,HIGH); // Weichen werden für Einfahrt auf Gleis 3 gestellt
			delay(200);
			digitalWrite(weicheA_G,LOW);
			digitalWrite(weicheB_G,HIGH);
			delay(200);
			digitalWrite(weicheB_G,LOW);
			Serial.println("Weiche A auf gerade gestellt, Weiche B auf gerade gestellt");
			einfahrender = 3; //Lege dieses Gleis für nächste Einfahrt fest
			ausfahrender = 1; //Lege nächstes Gleis für nächste Abfahrt fest
			break;
		}
		Serial.println("Zugausfahrt beendet. Rueckkehr in kontinuierlichen Betrieb.");
		Serial.print("naechstes einfahrendes gleis: ");Serial.println(einfahrender);
		Serial.print("naechstes ausfahrendes gleis: ");Serial.println(ausfahrender);
		Serial.println("Warte auf einfahrenden Zug...");
	}//Rückkehr in kontinuierlichen Betrieb

Ich hoffe, ihr könnt mir helfen! =(

Vielen Dank schon mal für eure Tipps,
lg

SBhf_gesamtV1_1.ino (17.5 KB)

Mein erster Gedanke:

RAM-Verbrauch!
Du hast sehr viele Strings in Serial.print() Anweisungen, die belegen alle RAM, vom dem der UNO nicht viel hat.
Wenn du anstatt
Serial.println("Beispieltext");
Serial.println(F("Beispieltext")); schreibst, dann wird der String nicht ins RAM kopiert, sondern bleibt nur im Flash.

Das könnte helfen.
Und mit dieser Funktion:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

kannst du dir dein freies RAM anzeigen lassen.

guntherb:
Mein erster Gedanke:

RAM-Verbrauch!

Und wieder was gelernt! Dass die Strings nicht einfach nur im Flash liegen ist mir beim besten Willen nicht in den Sinn gekommen :slight_smile:
Wird sofort ausprobiert, die dort zu lassen!

Das liegt daran, dass AVRs eine modifizierte Harvard Architektur haben. RAM und ROM haben also anders auf dem PC getrennte Adressräume und ein normaler Befehl kann nicht so einfach ins Flash greifen. Deshalb werden am Anfang alle String-Literale aus dem Flash ins RAM kopiert.

Diese im Flash zu behalten ist etwas Aufwand, aber dazu gibt es eine ganze Reihe Makros und Hilfsfunktionen. Es gibt z.B. auch für viele der Standard String Funktionen _P Versionen, die direkt mit Strings im Flash arbeiten.

Ich habs gerade mal mit dem mega ausprobiert.

Deinen Sketch genommen und mit "Alle Ersetzen" alle (" ersetzt mit (F(" und alle ") ersetzt mit "))

Laut freeRam() sind das 1972byte weniger!!!!
Da waren nur noch 76 byte frei für Variable!

Im UNO sind jetzt wieder 1797byte frei.

Oha, getestet habe ich es nicht, aber eingesetzt habe ich die F auch schon. Kein Wunder, dass er rumzickt.

Morgen oder heute abend kann ich das ganze wieder an der Modellbahn austesten.

Danke bis hierhin!

Ohne Deinen Sketch angesehen zu haben möchte ich weitere Gründe aufführen die auch ein solch komisches und unerwartetet Ergebnis bringen können.

  • Arrays über ihre größe hinaus benutzen. (Ein array[5] mit array[5], array[6] usw benutzen) Dabei werden die Speicherzellen die im RAM nach den für array[5] reservierten stehen beschrieben. Im Endeffekt werden andere Variablen überschrieben.
  • Zeiger (pointer) Das gleiche kann bei falsch angewendeten Zeigern passieren.

PS Für nicht-Eisenbahner: was ist ein Schattenbahnhof?

Grüße Uwe

Hi!

Der überlaufene RAM war offensichtlich wirklich die Quelle des Übels. Habe es eben getestet und nun funktioniert es hervoragend!

Also Arrays habe ich nur eins und das auch immer ordentlich benutzt. Pointer habe ich keine eingebaut. Ich könnte das ganze Programm wahrscheinlich noch mal neu schreiben und dann vernünpftig Arrays, Functions und Pointer verwenden. Es geht ja wahrscheinlich alles wesentlich übersichtlicher. Aber da es nun läuft, wird das wahrscheinlich eh nichts. Jetzt gilt es erstmal abzuchecken, ob ich auch wirklich jeden Zustand des Bahnhofs im Programm abgedeckt habe.

Ein Schattenbahnhof liegt im Tunnel der Anlage und ist für den Zuschauer nicht sichtbar. Dort fährt dann ein Zug ein und ein anderer wieder aus. Somit ist der erste Zug für den Zuschauer zunächst verschwunden. So entsteht dann eine größere Vielfalt an Zügen. Z.b. sieht das so aus:

Meiner hat allerdings nur drei Gleise :frowning:

Danke für die Tipps und danke auch an Serenifly für die Infos über den Arduino.

Hallo,
vielleicht kannst Du mir den Code mal schicken ?
Ich habe das genau so vor und möchte lieber einen Arduino einsetzen als eine fertige Lösung :wink:
Gruß
ARTy