mbot_streckenfahren
                Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
| Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
| mbot_streckenfahren [2025/03/01 09:58] – torsten.roehl | mbot_streckenfahren [2025/03/02 14:38] (aktuell) – [Erklärungen zum Quellcode] torsten.roehl | ||
|---|---|---|---|
| Zeile 7: | Zeile 7: | ||
| Nach der Kalibrierung kann der mBot eine beliebig eingegebene Strecke vorwärts oder rückwärts fahren.// | Nach der Kalibrierung kann der mBot eine beliebig eingegebene Strecke vorwärts oder rückwärts fahren.// | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | |||
| + | |||
| + | * siehe Abschnitt ☛ [[hello_mbot_infrared|Hello mBot Infrared]] | ||
| + | * siehe Abschnitt ☛ [[hello_mbot_programmorganisation|Hello mBot Programmorganisation]] | ||
| + | |||
| + | |||
| ====== Grundgerüst ====== | ====== Grundgerüst ====== | ||
| Zeile 16: | Zeile 25: | ||
| Die spezifischen Action-Funktionen (z. B. für Bewegung oder Sensoren) müssen jedoch noch individuell implementiert werden.// | Die spezifischen Action-Funktionen (z. B. für Bewegung oder Sensoren) müssen jedoch noch individuell implementiert werden.// | ||
| - | <WRAP center round tip 90%> | + | <WRAP center round tip 95%> | 
| - | 👉 Im nächsten Abschnitt werden die Methoden implementiert, | + | ☛ Im nächsten Abschnitt werden die Methoden implementiert, | 
| + | |||
| + | |||
| + | Um eine elegante Steuerung zu ermöglichen, | ||
| + | |||
| + | siehe Abschnitt ☛ [[hello_mbot_blockierung|Blockierend vs. Nichtblockierende Mehoden]] | ||
| </ | </ | ||
| Zeile 24: | Zeile 39: | ||
| ==== 🎮 | ==== 🎮 | ||
| - | Die Steuerung erfolgt über fünf Tasten (**E**, **C**, **D**, **F**, **B**) sowie eine Zahleneingabe mit Bestätigung über die Setting-Taste | + | Die Steuerung erfolgt über fünf Tasten (**E**, **C**, **D**, **F**, **B**) sowie eine Zahleneingabe mit Bestätigung über die **Setting-Taste** | 
| - | ^ Taste           ^ Funktion | + | ^ Taste           ^ Funktion | 
| - | | **E** (Escape) | 🛑 **Stopp** – Beendet aktuelle Aktion | | | + | | **E** (Escape) | 🛑 **Stopp** – Beendet aktuelle Aktion |Jederzeit aufrufbar | 
| - | | **C** (Calibration) | ⚙️ **Kalibrierung starten** | | | + | | **C** (Calibration) | ⚙️ **Kalibrierung starten** | Erforderlich, | 
| | **D** (Distance) | 📏 **Distanz eingeben** (1–3 Stellen) | Erweiterter Eingabemodus!| | | **D** (Distance) | 📏 **Distanz eingeben** (1–3 Stellen) | Erweiterter Eingabemodus!| | ||
| - | | **F** (Forward) | ▶️ **Vorwärts fahren** (eingestellte Distanz) | | | + | | **F** (Forward) | ▶️ **Vorwärts fahren** (eingestellte Distanz) | Nach Distanz-Eingabe und Kalibrierung | 
| - | | **B** (Backward) | ◀️ **Rückwärts fahren** (eingestellte Distanz) | | | + | | **B** (Backward) | ◀️ **Rückwärts fahren** (eingestellte Distanz) |Nach Distanz-Eingabe und Kalibrierung | 
| Zeile 87: | Zeile 102: | ||
| }; | }; | ||
| ExState exState | ExState exState | ||
| - | int | + | int | 
| - | String | + | String | 
| - | int | + | int | 
| void setup() { | void setup() { | ||
| Zeile 131: | Zeile 146: | ||
| if (ir.decode()) { | if (ir.decode()) { | ||
| uint32_t code = getIRCode(); | uint32_t code = getIRCode(); | ||
| - | // vermeiden wiederholender eingabe | + | // Vermeidung wiederholter Eingaben | 
| unsigned long now = millis(); | unsigned long now = millis(); | ||
| if (code == lastCode && (now - lastTime < COOL_DOWN)) | if (code == lastCode && (now - lastTime < COOL_DOWN)) | ||
| Zeile 286: | Zeile 301: | ||
| </ | </ | ||
| + | |||
| + | ==== Erklärungen zum Quellcode ==== | ||
| + | |||
| + | |||
| + | === Hauptprogramm (loop) === | ||
| + | '' | ||
| + | |||
| + | - **Befehl einlesen** → '' | ||
| + | - **Zustand bestimmen** → '' | ||
| + | - **Aktion ausführen** → Je nach Zustand eine '' | ||
| + | |||
| + | Die FSM sorgt dafür, dass nur **gültige Zustandswechsel** erfolgen. | ||
| + | |||
| + | |||
| + | === IR-Befehl auslesen (read) === | ||
| + | '' | ||
| + | |||
| + | - **IR-Code empfangen** | ||
| + | - **Entprellung** → Mehrfacher Tastendruck wird ignoriert | ||
| + | - **Befehl in Zustand umwandeln** | ||
| + | - **Falls Zahleneingabe aktiv** → '' | ||
| + | - Bestätigt eine Zahleneingabe mit der **Setting-Taste**. | ||
| + | |||
| + | |||
| + | === Zustandssteuerung (decode) === | ||
| + | '' | ||
| + | |||
| + | - '' | ||
| + | - '' | ||
| + | - '' | ||
| + | - '' | ||
| + | - '' | ||
| + | |||
| + | Bleibt der Befehl **unverändert**, | ||
| + | |||
| + | === Zahleneingabe === | ||
| + | - '' | ||
| + | - '' | ||
| + | - '' | ||
| + | |||
| + | Die eingegebene Zahl wird in **inputDistance** gespeichert und auf **max. 200 Einheiten** begrenzt. | ||
| + | |||
| + | === Aktionen (Platzhalter) === | ||
| + | Die **Action-Funktionen** enthalten bisher nur serielle Ausgaben: | ||
| + | |||
| + | - '' | ||
| + | - '' | ||
| + | - '' | ||
| + | - '' | ||
| + | - '' | ||
| + | |||
| + | Um den Roboter tatsächlich zu bewegen, müssen die **action**-Funktionen implementiert werden. | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| ====== Programm Steckenfahren ====== | ====== Programm Steckenfahren ====== | ||
| + | ==== 🎮 Verwendung des Programms ==== | ||
| + | //Hier ist das vollständige Programm. Die Bedienung bleibt dieselbe wie beim Grundgerüst. Der einzige Unterschied besteht darin, dass die Action-Funktionen nun implementiert wurden. | ||
| + | \\ | ||
| + | Das Programm erfordert, dass die Variable '' | ||
| + | // | ||
| + | |||
| + | |||
| + | ^ Step 1: Kalibrierung ^ | ||
| + | |{{ : | ||
| + | |< | ||
| + | |||
| + | 🔹 Vorgehensweise: | ||
| + | * 1️⃣ Stellen Sie den **mBot** auf die erste schwarze Linie. | ||
| + | * 2️⃣ Kontrollieren Sie, dass die Sensoren des **Liniensensors** auf der schwarzen Linie befinden! | ||
| + | * Tipp: Überprüfen Sie dies mit Hilfe der Hilfs-LEDs am Sensor. ☛ [[hello_mbot_linesensor|Hello mBot LineSensor]] | ||
| + | * 3️⃣ Drücken Sie **C**, um die Kalibrierung zu starten. | ||
| + | |||
| + | Nach der Kalibrierung kann der mBot für die Distanzfahrt verwendet werden. Die Kalibrierung kann bei Bedarf jederzeit wiederholt werden. | ||
| + | </ | ||
| + | |||
| + | ^ Step 2: Roboter fahren lassen 8-) ^ | ||
| + | |{{ : | ||
| + | |Nach der Kalibrierung kann eine Distanz (**D**) eingegeben werden und anschließend beliebig oft mit **F** oder **B** vorwärts bzw. rückwärts gefahren werden. Drücken Sie **E**, um es vorzeitig zu beenden.| | ||
| + | |||
| + | |||
| + | |||
| + | <WRAP center round tip 95%> | ||
| + | </ | ||
| - | FIXME  <color # | ||
| - | </ | ||
| ==== Quellcode (engl. Sourcecode) ==== | ==== Quellcode (engl. Sourcecode) ==== | ||
| Zeile 304: | Zeile 402: | ||
| MeDCMotor motor1(M1); | MeDCMotor motor1(M1); | ||
| MeDCMotor motor2(M2); | MeDCMotor motor2(M2); | ||
| + | |||
| + | // IR-Entprellen | ||
| + | static uint8_t lastCode | ||
| + | static unsigned long lastTime = 0; | ||
| + | const unsigned long COOL_DOWN = 1000; // in Millisekunden, | ||
| // Allgemein | // Allgemein | ||
| - | unsigned long calibrationTravelTime | + | unsigned long calibrationTravelTime; | 
| - | int calibrationLength | + | unsigned long currentTravelTime | 
| - | int travelDistance | + | // ADJUST AREA START | 
| - | unsigned long currentTravelTime; | + | int calibrationDistance | 
| - | int speed = 180; // Motor speed | + | int speed | 
| + | int dir = 1; // change to -1 if forward drive backwards! | ||
| + | // ADJUST AREA END | ||
| // actionCalibration... | // actionCalibration... | ||
| unsigned long ac_movingStartTimeForward | unsigned long ac_movingStartTimeForward | ||
| - | unsigned long ac_movingTimeForward = 0; | + | unsigned long ac_movingTimeForward | 
| unsigned long ac_movingStartTimeBackward = 0; | unsigned long ac_movingStartTimeBackward = 0; | ||
| - | unsigned long ac_movingTimeBackward = 0; | + | unsigned long ac_movingTimeBackward | 
| - | + | bool ac_isMoving | |
| - | bool ac_isMoving = false; | + | bool ac_isForwardMoving | 
| - | bool ac_isForwardMoving = false; | + | |
| bool ac_isBackwardMoving = false; | bool ac_isBackwardMoving = false; | ||
| - | bool ac_calibrationDone = false; | + | bool ac_calibrationDone | 
| + | // motor movement | ||
| + | bool mm_firstRunMotor = true; | ||
| + | bool mm_motorActive | ||
| + | unsigned long mm_motorStartTime = 0; | ||
| - | // actionForward | + | //FSM | 
| - | unsigned long af_StartTime = 0; | + | |
| - | bool af_done | + | |
| - | unsigned long previousMillis = 0; | + | |
| - | bool firstRun = true; | + | |
| enum State { | enum State { | ||
| STATE_OFF, | STATE_OFF, | ||
| + | STATE_BACKWARD, | ||
| STATE_CALIBRATION, | STATE_CALIBRATION, | ||
| - |  | + |  | 
| - |  | + |  | 
| }; | }; | ||
| - | State state = STATE_OFF; | + | |
| + | State state | ||
| State lastState = STATE_OFF; | State lastState = STATE_OFF; | ||
| + | |||
| + | enum ExState { | ||
| + | EX_IDLE, | ||
| + | EX_WAIT_FOR_INPUT | ||
| + | }; | ||
| + | ExState exState | ||
| + | |||
| + | int | ||
| + | String | ||
| + | int | ||
| + | |||
| void setup() { | void setup() { | ||
| led.setpin(13); | led.setpin(13); | ||
| + | // | ||
| ir.begin(); | ir.begin(); | ||
| } | } | ||
| void loop() { | void loop() { | ||
| - | // step: command | + | // step: command | 
| int cmd = read(); | int cmd = read(); | ||
| - | // step: state | + | // step: state bestimmen | 
| state  = decode(cmd); | state  = decode(cmd); | ||
| - | // step: action | + | // step: zustandsabhängige Aktion | 
| switch (state) { | switch (state) { | ||
| case STATE_CALIBRATION: | case STATE_CALIBRATION: | ||
| Zeile 355: | Zeile 472: | ||
| break; | break; | ||
| case STATE_FORWARD: | case STATE_FORWARD: | ||
| - | // | + |  | 
| break; | break; | ||
| case STATE_BACKWARD: | case STATE_BACKWARD: | ||
| - | // | + |  | 
| + | break; | ||
| + | case STATE_DISTANCE: | ||
| + | actionDistance(); | ||
| break; | break; | ||
| case STATE_OFF: | case STATE_OFF: | ||
| - | // | + |  | 
| break; | break; | ||
| } | } | ||
| } | } | ||
| - | |||
| /* | /* | ||
| - | Funktionen | + | --------------------------- | 
| + |  | ||
| + | --------------------------- | ||
| */ | */ | ||
| + | void setLED(int r, int g, int b) { | ||
| + | led.setColorAt(0, | ||
| + | led.setColorAt(1, | ||
| + | led.show(); | ||
| + | } | ||
| int read() { | int read() { | ||
| if (ir.decode()) { | if (ir.decode()) { | ||
| uint32_t code = getIRCode(); | uint32_t code = getIRCode(); | ||
| + | |||
| + | // Vermeidung wiederholter Eingaben | ||
| + | unsigned long now = millis(); | ||
| + | if (code == lastCode && (now - lastTime < COOL_DOWN)) | ||
| + | return -1; | ||
| + | lastCode = code; | ||
| + | lastTime = now; | ||
| + | |||
| + | // | ||
| switch (code) { | switch (code) { | ||
| - | case IR_BUTTON_E: | + | case IR_BUTTON_E: | 
| - | case IR_BUTTON_C:  buzzer.tone(1200, | + |  | 
| - | case IR_BUTTON_B: { | + |  | 
| - | buzzer.tone(1200, | + |  | 
| - | // TODO read value | + | case IR_BUTTON_B: | 
| - |  | + |  | 
| - | break; | + |  | 
| - |  | + |  | 
| - | case IR_BUTTON_F: | + | case IR_BUTTON_C: | 
| - | buzzer.tone(1200, | + | buzzer.tone(1200, | 
| - | // TODO read value | + | return 2; // STATE_CALIBRATION | 
| - |  | + | break; | 
| - |  | + | case IR_BUTTON_D: | 
| - | } | + |  | 
| + | return 3; // STATE_DISTANCE | ||
| + | break; | ||
| + | case IR_BUTTON_F: | ||
| + | buzzer.tone(1200, | ||
| + |  | ||
| + | break; | ||
| - | } | + | // Ziffern 0–9 | 
| + | case IR_BUTTON_0: | ||
| + | case IR_BUTTON_1: | ||
| + | case IR_BUTTON_2: | ||
| + | case IR_BUTTON_3: | ||
| + | case IR_BUTTON_4: | ||
| + | case IR_BUTTON_5: | ||
| + | case IR_BUTTON_6: | ||
| + | case IR_BUTTON_7: | ||
| + | case IR_BUTTON_8: | ||
| + | case IR_BUTTON_9: | ||
| + | // Taste ' | ||
| + | case IR_BUTTON_SETTING: | ||
| + | finalizeInput(); | ||
| + | return -1; // Kein direkter Zustandswechsel | ||
| + | break; | ||
| + | } | ||
| } | } | ||
| - | return -1; // unknow cmd | + | return -1; // keine bekannte/ | 
| } | } | ||
| - | uint32_t | + | uint32_t getIRCode() { | 
| uint32_t value = ir.value; | uint32_t value = ir.value; | ||
| - | value = value >> 16 & 0xff; | + | value = (value >> 16) & 0xff; | 
| return value; | return value; | ||
| } | } | ||
| Zeile 404: | Zeile 561: | ||
| State decode(int cmd) { | State decode(int cmd) { | ||
| switch (cmd) { | switch (cmd) { | ||
| - | case 0: lastState = STATE_OFF; return STATE_OFF; | + | case 0: | 
| - | case 1: lastState = STATE_CALIBRATION | + |  | 
| - | case 2: lastState = STATE_FORWARD; return | + |  | 
| - | case 3: lastState = STATE_BACKWARD; return | + | case 1: | 
| + |  | ||
| + |  | ||
| + | case 2: | ||
| + |  | ||
| + |  | ||
| + | case 3: | ||
| + |  | ||
| + |  | ||
| + | case 4: | ||
| + | lastState = STATE_FORWARD; | ||
| + | return STATE_FORWARD; | ||
| } | } | ||
| return lastState; | return lastState; | ||
| } | } | ||
| - | void calculateTravelTime() { | + | /* | 
| + | --------------------------- | ||
| + | Eingabemodus für EX-Befehle | ||
| + | --------------------------- | ||
| + | */ | ||
| + | void initInput() { | ||
| + | buzzer.tone(1000, | ||
| + | exState | ||
| + | inputNumber = 0; | ||
| + | inputBuffer = ""; | ||
| + | setLED(0, 0, 255); // LED BLUE | ||
| + | } | ||
| - | double v = (1.0 * calibrationLength) / (1.0 * calibrationTravelTime); | + | void handleInput(int number) { | 
| - | currentTravelTime = v * travelDistance; | + | |
| + | if (exState != EX_WAIT_FOR_INPUT) return; | ||
| + | |||
| + | // Maximal 3 Stellen erlauben | ||
| + | if (inputNumber >= 3) { | ||
| + | buzzer.tone(300, | ||
| + | delay(200); | ||
| + | buzzer.tone(300, | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // Zahl anhängen | ||
| + | inputBuffer += String(number); | ||
| + | inputNumber++; | ||
| + | buzzer.tone(1200, | ||
| } | } | ||
| + | |||
| + | void finalizeInput() { | ||
| + | if (exState == EX_WAIT_FOR_INPUT) { | ||
| + | exState = EX_IDLE; | ||
| + | buzzer.tone(1200, | ||
| + | setLED(0, 0, 0); // LED OFF | ||
| + | } | ||
| + | } | ||
| + | |||
| + | /* | ||
| + | --------------------------- | ||
| + | Funktionen ...actionXXX | ||
| + | --------------------------- | ||
| + | */ | ||
| void actionCalibration() { | void actionCalibration() { | ||
| Zeile 423: | Zeile 629: | ||
| return; | return; | ||
| - |  | + |  | 
| - | led.setColorAt(0, | + | |
| - | led.show(); | + | |
| int sensorState = lineFinder.readSensors(); | int sensorState = lineFinder.readSensors(); | ||
| Zeile 432: | Zeile 636: | ||
| motor1.stop(); | motor1.stop(); | ||
| motor2.stop(); | motor2.stop(); | ||
| - | motor1.run(-speed); | + | motor1.run(-dir * speed); | 
| - | motor2.run(speed); | + | motor2.run( | 
| ac_movingStartTimeForward = millis(); | ac_movingStartTimeForward = millis(); | ||
| ac_isMoving = true; | ac_isMoving = true; | ||
| Zeile 447: | Zeile 652: | ||
| motor2.stop(); | motor2.stop(); | ||
| ac_movingTimeForward = millis() - ac_movingStartTimeForward; | ac_movingTimeForward = millis() - ac_movingStartTimeForward; | ||
| - | + |  | |
| - |  | + | motor2.run(-dir * speed); | 
| - | motor2.run(-speed); | + | |
| ac_movingStartTimeBackward = millis(); | ac_movingStartTimeBackward = millis(); | ||
| do { | do { | ||
| Zeile 455: | Zeile 659: | ||
| } while ( sensorState != S1_OUT_S2_OUT); | } while ( sensorState != S1_OUT_S2_OUT); | ||
| ac_isBackwardMoving = false; | ac_isBackwardMoving = false; | ||
| - | ac_isForwardMoving = true; | + | ac_isForwardMoving | 
| } | } | ||
| Zeile 465: | Zeile 669: | ||
| calibrationTravelTime = (ac_movingTimeForward + ac_movingTimeBackward) / 2.0; | calibrationTravelTime = (ac_movingTimeForward + ac_movingTimeBackward) / 2.0; | ||
| ac_calibrationDone = true; | ac_calibrationDone = true; | ||
| - | led.setColorAt(1, | ||
| - | led.setColorAt(0, | ||
| - | led.show(); | ||
| buzzer.tone(1200, | buzzer.tone(1200, | ||
| + | lastState = STATE_OFF; | ||
| } | } | ||
| + | } | ||
| + | |||
| + | void motorMovement(int go_forward ) { | ||
| + | unsigned long currentMillis = millis(); | ||
| + | |||
| + | // Wenn der Motor aktuell läuft, prüfen wir, ob die Zeit abgelaufen ist | ||
| + | if (mm_motorActive) { | ||
| + | if (currentMillis - mm_motorStartTime >= currentTravelTime) { | ||
| + | motor1.stop(); | ||
| + | motor2.stop(); | ||
| + | mm_motorActive = false; | ||
| + | state = STATE_OFF; | ||
| + | lastState | ||
| + | } | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | if (!mm_firstRunMotor) | ||
| + | return; | ||
| + | |||
| + | setLED(0, 255, 0); // LED GREEN | ||
| + | motor1.run(-go_forward * speed); | ||
| + | motor2.run( go_forward * speed); | ||
| + | mm_motorStartTime = currentMillis; | ||
| + | mm_motorActive | ||
| + | mm_firstRunMotor | ||
| } | } | ||
| void actionForward() { | void actionForward() { | ||
| + | motorMovement(dir); | ||
| } | } | ||
| void actionBackward() { | void actionBackward() { | ||
| + | motorMovement(-dir); | ||
| } | } | ||
| - | void actionDistance(){ | ||
| - | } | ||
| - | void actionOff() { | ||
| - | led.setColorAt(1, 0, 0, 0); | + | void actionDistance() { | 
| - |  | + |  | 
| - |  | + |  | 
| + | if (exState == EX_WAIT_FOR_INPUT) | ||
| + | return; | ||
| + | // step: ...calculate distance | ||
| + | inputDistance = max(0, min(200, inputBuffer.toInt())); | ||
| + | |||
| + | // step: calculate the travel time (for non blocking-method) | ||
| + | double v = (1.0 * calibrationDistance) / (1.0 * calibrationTravelTime); | ||
| + | currentTravelTime = inputDistance / v; | ||
| + | // Serial.print(" | ||
| + | // Serial.println(inputDistance); | ||
| + | } | ||
| + | |||
| + | void actionOff() { | ||
| + | setLED(0, 0, 0); // LED OFF | ||
| // reset | // reset | ||
| motor1.stop(); | motor1.stop(); | ||
| Zeile 490: | Zeile 731: | ||
| // reset state calibration | // reset state calibration | ||
| - | ac_isMoving = false; | + | ac_isMoving | 
| - | ac_isForwardMoving = false; | + | ac_isForwardMoving | 
| ac_isBackwardMoving = false; | ac_isBackwardMoving = false; | ||
| - | ac_calibrationDone = false; | + | ac_calibrationDone | 
| - | // reset state forward | + | // reset movement | 
| - | + |  | |
| - |  | + | mm_motorActive | 
| + |  | ||
| + | // reset state distance | ||
| + | exState | ||
| } | } | ||
| </ | </ | ||
| + | ==== Erklärungen zum Quellcode ==== | ||
| + | === ⚙️ Setup (setup()) === | ||
| + | Initialisiert die IR-Fernbedienung und setzt den LED-Pin. | ||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | === 🔄 Loop (loop()) === | ||
| + | - **Liest IR-Signale ('' | ||
| + | - **Bestimmt den aktuellen Zustand ('' | ||
| + | - **Führt die passende Aktion aus (`actionXXX()`)** | ||
| + | |||
| + | === 🚦 Zustandsbezogene Aktionen (actionXXX()) ==== | ||
| + | * **'' | ||
| + | * **'' | ||
| + | * **'' | ||
| + | * **'' | ||
| + | |||
| + | === ⌨️ Eingabeverarbeitung (handleInput(), | ||
| + | - Ermöglicht die Eingabe einer numerischen Distanz per IR-Fernbedienung. | ||
| + | - '' | ||
| + | - '' | ||
| + | |||
| + | === ⚙️ Die ADJUST AREA === | ||
| + | Hier können **drei zentrale Parameter** angepasst werden: | ||
| + | * **'' | ||
| + | * **'' | ||
| + | * **'' | ||
| + | * Falls das Fahrzeug rückwärts fährt, auf '' | ||
| + | |||
| + | Die Werte beeinflussen das Fahrverhalten direkt und sollten je nach Umgebung angepasst werden. | ||
| + | |||
| - | === Erklärungen zum Quellcode === | ||
mbot_streckenfahren.1740823124.txt.gz · Zuletzt geändert:  von torsten.roehl
                
                