Autor: Dipl.-Ing. Wilfried Klaas
Board: Inteaduino, Arduino Duemilanove
Für unsere 152VO Klasse brauchen wir natürlich auch eine Starteruhr für den für den standesgemässen (fliegenden) Rennbegin.
Im Schiffsmodell.net Forum entstand die Idee, hier der konstituierende Thread: www.schiffsmodell.net/showthread.php
Meine Projektseite liegt hier: Projektseite
Hier möchte ich ein bisschen auf die Besonderheiten des Programms eingehen.
#include <RCReceive.h> #define debug #include <debug.h> #include <makros.h> /* Starter Uhr für Bootsrennen der 152vo'er Klasse. Version 0.2 - Änderung RC Receiver Bibliothek... Es ist ein Testprogramm integriert. Dieses wird aktiviert, wenn beim Einschalten der Uhr der Starttaster gedrückt wird. Zunächst werden die LED's der Reihe nach getestet. Dann der Lautsprecher und das RC System (Funksignal wird an den LED's ausgegeben.) Zuletzt wird die Uhr mechanisch getestet. Erst 20 Schritte vorwärts dann 20 SChritte zurück und dann auf den Nullpunkt. Die Uhr ist aufgebaut mit 1 Schrittmotor für die Zeitanzeige. Weiterhin sind 5 LED-Strahler vorhanden, die die Zeit ab Start der Uhr anzeigen. Gestartet wird mit Hilfe einer Standartfernsteuerung. Reihenfolge 2 weiße LED's, 1 gelbe, 2 grüne Ablauf: Modus Normal: Reset: Alles auf null, 3x Beep, Schrittmotor aus Test auf Start, -> ja Zeiger auf null LED W1 geht an, Beep. Uhr läuft, nach 1 Minute: LED W1 geht aus, LED W2 geht an, nach 2 Minuten: LED W2 geht aus, LED Y3 geht an, letzte Minute Beep nach 3 Minuten: 2xLED Grün gehen an, Beep, 10 sec warten, Schrittmotor 20 zurück, reset. Modus Dragrace: Reset Test auf Start, -> ja Zeiger auf null, Uhr läuft 15 Sekunden vor Ende, Beep. noch 3 Sekunden: Beep, LED W1 geht an noch 2 Sekunden: Beep, LED W2 geht an, LED W1 geht aus noch 1 Sekunde: Beep, LED Y3 geht an, LED W2 geht aus, Start: 2xLED Grün gehen an, Beep, 10 sec warten, Schrittmotor 20 zurück, reset. Hardware Pin Belegung LED Steuerung: LED W1 = 14 (A0) LED W2 = 15 LED Y3 = 16; LED G4 = 17; LED G5 = 18; (A4) Schrittmotor mit EasyDriver 4.4, Halbschrittverfahren: SM_STEP = 8; Schrittmotor Takt SM_DIR = 7; Schrittmotor Richtung SM_SLEEP = 6; Schrittmotor Ein/Aus SM_SWITCH = 5; Schrittmotor Nullpunkt LS Standardfernsteuerung RC_PIN = 0; RC Pin ist dann Pin 2, 1=> Pin 3 (ext. Interrupt 0/1) Kleiner Verstärker mit Druckkammersystem BEEP_PIN = 3; Lautsprecherpin Kleiner Taster für den Testbetrieb SWITCH = 4; Handstarttaster SW_CLOCK_MODE = 1; Schalter für den Uhrmodus. */ // Program im Debugmodus kompilieren, dann werden zus. Ausgaben auf die serielle Schnittstelle geschrieben. //#define debug /* Programmkonstanten */ // Pin Nummern LED's const byte LED_W1 = 14; const byte LED_W2 = 15; const byte LED_Y3 = 16; const byte LED_G4 = 17; const byte LED_G5 = 18; const byte LED_BOARD = 13; // Pin Nummern für den Schrittmotor const byte SM_STEP = 8; const byte SM_DIR = 7; const byte SM_SLEEP = 6; const byte SM_SWITCH = 5; // Pin nummern Taster Start und schalter ClockMode const byte SW_START = 4; const byte SW_CLOCK_MODE = 12; // Schrittmotor Konstanten const byte S = 200; // Motor: Schritte pro Umdrehung const byte F = 2; // Faktor der Motorkarte: 1,2,4,8 für Voll, Halb Viertel oder 1/8 Schritt const byte G = 4; // Geschwindigkeit in U/min d.h. di emaximale Geschwindigkeit der Uhr soll das 4-fache // einer normalen Uhr betragen. // Brechnugnen const word Su = S * F; // Schritte pro Umdrehung an die SM-Karte // word Ss = G * Su / 60; // Schritte pro Sekunde // word Ps = 1000 / Ss; // Pausenzeit zwischen 2 Schritten in MSec // word Ph = Ps / 2; const word Ph = 30000 / (G * S * F ); const boolean SM_DIRECTION = false; // Empirisch ermittelte Werte für den Empfänger, sind an den jeweiligen Empfänger anzupassen const word StartRCValue = 170; const word ResetRCValue = 90; const byte PIN_RC = 2; // Einstellunge für die Hupe const byte BEEP_PIN = 3; const word BEEP_HZ = 500; /* Konstanten zur Steuerung */ const word MIN = 600; const word MIN_1 = MIN; const word MIN_2 = MIN * 2; const word MIN_3 = MIN * 3; const word SEC_5 = MIN_3 - 50; const word SEC_4 = MIN_3 - 40; const word SEC_3 = MIN_3 - 30; const word SEC_2 = MIN_3 - 20; const word SEC_1 = MIN_3 - 10; const word DO_RESET = MIN_3 + 200; const word DRAG_SEC_5 = 550; const word DRAG_SEC_4 = 560; const word DRAG_SEC_3 = 570; const word DRAG_SEC_2 = 580; const word DRAG_SEC_1 = 590; const word DRAG_SEC_0 = 600; const word DRAG_RESET = 800; /* Variablenbereich */ // uhr wurde gestartet. boolean started; // der 1 Minuten Beep wurde abgegeben boolean oneBeep; // der Start Beep wurde abgegeben boolean longBeep; // ein Reset soll ausgelöst werden. boolean doReset; // Starttime ist in 1/10 sekunden word startTime; byte RcStart; byte RcReset; unsigned long nexttime; word deltaTime; word steps; word done; boolean firstStep; //word saveDelta; word lastStepUsed; boolean doTestSystem; boolean dragMode; boolean lastBeeps[5]; RCReceive rcReceiver; void setup() { // initialize the digital pin as an output. // Pin 13 has an LED connected on most Arduino boards: pinMode(LED_BOARD, OUTPUT); pinMode(LED_W1, OUTPUT); pinMode(LED_W2, OUTPUT); pinMode(LED_Y3, OUTPUT); pinMode(LED_G4, OUTPUT); pinMode(LED_G5, OUTPUT); // Ausgang für die Hupe pinMode(BEEP_PIN, OUTPUT); // Ausgänge und Eingang für die Motorelektronik pinMode(SM_STEP, OUTPUT); pinMode(SM_DIR, OUTPUT); pinMode(SM_SLEEP, OUTPUT); pinMode(SM_SWITCH, INPUT); digitalWrite(SM_SWITCH, HIGH); // Eingang für Starttaster pinMode(SW_START, INPUT); digitalWrite(SW_START, HIGH); // Eingang für den Modusschalter pinMode(SW_CLOCK_MODE, INPUT); digitalWrite(SW_CLOCK_MODE, HIGH); // RC Receiver in Interruptvariante rcReceiver.attachInt(PIN_RC); // Serielle Schnittstelle einstellen initDebug(); // while (!Serial) ; delay(100); // prints title with ending line break dbgOutLn("152 Uhr V0.2"); // Uhr zurück stellen clockReset(); doTestSystem = digitalRead(SW_START) == 0; dragMode = digitalRead(SW_CLOCK_MODE) == 0; // 1 kurzer Beep zum Anzeigen, das Uhr betriebsbereit beep(); if (doTestSystem) { // 3 Beeps zum Anzeigen des Testmoduses delay(350); beep(); delay(350); beep(); } else if (dragMode) { // 2 Beeps Modus Dragrace delay(350); beep(); } delay(350); } void loop() { if (!started) { boolean newDragMode = digitalRead(SW_CLOCK_MODE) == 0; if (newDragMode != dragMode) { // 1 kurzer Beep zum Anzeigen, das Uhr betriebsbereit beep(); dragMode = newDragMode; if (dragMode) { // 2 Beeps Modus Dragrace delay(350); beep(); } delay(350); } } if (doTestSystem) { // hier geht's nur hin, wenn beim starten bereits die Start taste gedrückt wurde. testSystem(); doTestSystem = digitalRead(SW_START) == 0; } else { // Steppermotor evt. ausschalten; word now = getTime(); // testen ob die starttaste oder per RC das Startsignal gegeben wurde. testStart(); // Uhr läuft ? if (started) { startStepper(); deltaTime = getTime() - startTime; #ifdef debug if ((deltaTime % 10) == 0) { dbgOut("d:"); dbgOutLn(deltaTime); } #endif // Lichter einstellen if (dragMode) { showDragLights(); } else { showLights(); } if (firstStep) { // Zeiten einstellen if (dragMode) { nexttime = millis() + 60000; steps = Su; } else { nexttime = millis() + 180000; steps = 3 * Su; } done = 0; firstStep = false; dbgOut("first:"); dbgOut(nexttime); dbgOut(","); dbgOut(steps); dbgOut(","); dbgOut(done); dbgOut(","); dbgOutLn(firstStep); } // Uhr stellen doSteps(); // Beeps ausgeben if (dragMode) { doDragBeeps(); if (deltaTime >= DRAG_RESET) { doReset = true; } } else { doBeeps(); if (deltaTime >= DO_RESET) { doReset = true; } } } // evt. Reset ausführen if(doReset) { clockReset(); } } } /* Beeps im normalen Modus */ void doBeeps() { if(!oneBeep) { if (deltaTime >= MIN_2) { beep(); oneBeep = true; } } // letzten 5 Sekunden if(!lastBeeps[4]) { if (deltaTime >= SEC_5) { beep(); lastBeeps[4] = true; } } if(!lastBeeps[3]) { if (deltaTime >= SEC_4) { beep(); lastBeeps[3] = true; } } if(!lastBeeps[2]) { if (deltaTime >= SEC_3) { beep(); lastBeeps[2] = true; } } if(!lastBeeps[1]) { if (deltaTime >= SEC_2) { beep(); lastBeeps[1] = true; } } if(!lastBeeps[0]) { if (deltaTime >= SEC_1) { beep(); lastBeeps[0] = true; } } // Start des Rennfeldes if (!longBeep) { if (deltaTime >= MIN_3) { longbeep(); longBeep = true; } } } /* Beeps im Deragmodus */ void doDragBeeps() { // letzten 5 Sekunden if(!lastBeeps[4]) { if (deltaTime >= DRAG_SEC_5) { beep(); lastBeeps[4] = true; } } if(!lastBeeps[3]) { if (deltaTime >= DRAG_SEC_4) { beep(); lastBeeps[3] = true; } } if(!lastBeeps[2]) { if (deltaTime >= DRAG_SEC_3) { beep(); lastBeeps[2] = true; } } if(!lastBeeps[1]) { if (deltaTime >= DRAG_SEC_2) { beep(); lastBeeps[1] = true; } } if(!lastBeeps[0]) { if (deltaTime >= DRAG_SEC_1) { beep(); lastBeeps[0] = true; } } // Start des Rennfeldes if (!longBeep) { if (deltaTime >= DRAG_SEC_0) { longbeep(); longBeep = true; } } } /* Uhr zurück auf Anfang stellen */ void clockReset() { dbgOut("reset"); doReset = false; started = false; firstStep = true; digitalWrite(LED_W1, LOW); digitalWrite(LED_W2, LOW); digitalWrite(LED_Y3, LOW); digitalWrite(LED_G4, LOW); digitalWrite(LED_G5, LOW); digitalWrite(SM_STEP, LOW); digitalWrite(SM_DIR, LOW); SteppStepper(50,true); oneBeep = false; longBeep = false; dbgOutLn("Stepper sleep"); digitalWrite(SM_SLEEP, LOW); // calibStepper(); RcStart = 3; RcReset = 3; for (int i = 0; i < 5; i++) { lastBeeps[i] = false; } } /* Wir warten etwas auf den RC Empfänger */ void syncRC() { dbgOut("wait on RC"); digitalWrite(LED_BOARD, HIGH); // set the LED on delay(1000); // wait for a second dbgOut("off"); digitalWrite(LED_BOARD, LOW); // set the LED off delay(1000); // wait for a second } /* Diese Routine testet darauf, ob ein Start oder Reset ausgelöst worden ist. */ void testStart() { int switchIn = digitalRead(SW_START); // switchIn = 0; byte RcValue = rcReceiver.getValue(); byte smSwitch = digitalRead(SM_SWITCH); dbgOut("rc:"); dbgOut(RcValue); dbgOut(", in:"); dbgOut(switchIn); dbgOut(", LS:"); dbgOutLn(smSwitch); if ((RcValue > StartRCValue) || (switchIn == 0)) { // entprellen RcReset = 3; if ((RcStart > 3) && !started) { startStepper(); started = true; startTime = getTime(); // WKLA: Änderungen wegen Dragmode // digitalWrite(LED_W1, HIGH); digitalWrite(LED_W1, LOW); digitalWrite(LED_W2, LOW); digitalWrite(LED_Y3, LOW); digitalWrite(LED_G4, LOW); digitalWrite(LED_G5, LOW); beep(); dbgOut("started at "); dbgOutLn(startTime); } else { // entprellen RcStart -= 1; } } else if ((RcValue > 1000) && (RcValue < ResetRCValue)) { // entprellen RcStart = 3; if (RcReset > 3) { beep(); delay(500); beep(); delay(500); beep(); delay(500); clockReset(); } else { // entprellen RcReset -= 1; } } else { RcStart = 3; RcReset = 3; } } void showLights() { if ((deltaTime > 0) && (deltaTime <= MIN_1)) { // 1. Minute digitalWrite(LED_W1 , HIGH); } if ((deltaTime > MIN_1) && (deltaTime <= MIN_2)) { // 2. Minute digitalWrite(LED_W1 , LOW); digitalWrite(LED_W2 , HIGH); } if ((deltaTime > MIN_2) && (deltaTime <= MIN_3)) { // 3. Minute digitalWrite(LED_W2 , LOW); digitalWrite(LED_Y3 , HIGH); } if ((deltaTime > MIN_3) && (deltaTime <= DO_RESET)) { // Start digitalWrite(LED_Y3 , LOW); digitalWrite(LED_G4 , HIGH); digitalWrite(LED_G5 , HIGH); } if (deltaTime > DO_RESET) { // Reset digitalWrite(LED_W1 , LOW); digitalWrite(LED_W2 , LOW); digitalWrite(LED_Y3 , LOW); digitalWrite(LED_G4 , LOW); digitalWrite(LED_G5 , LOW); } } void showDragLights() { if ((deltaTime > 0) && (deltaTime <= DRAG_SEC_3)) { // 1. Minute digitalWrite(LED_W1 , LOW); } if ((deltaTime > DRAG_SEC_3) && (deltaTime <= DRAG_SEC_2)) { // 3 Sekunden digitalWrite(LED_W1 , HIGH); } if ((deltaTime > DRAG_SEC_2) && (deltaTime <= DRAG_SEC_1)) { // 2 Sekudnen digitalWrite(LED_W1 , LOW); digitalWrite(LED_W2 , HIGH); } if ((deltaTime > DRAG_SEC_1) && (deltaTime <= DRAG_SEC_0)) { // 1 Sekunde digitalWrite(LED_W2 , LOW); digitalWrite(LED_Y3 , HIGH); } if ((deltaTime > DRAG_SEC_0) && (deltaTime <= DRAG_RESET)) { // Start digitalWrite(LED_Y3 , LOW); digitalWrite(LED_G4 , HIGH); digitalWrite(LED_G5 , HIGH); } if (deltaTime > DRAG_RESET) { digitalWrite(LED_G4 , LOW); digitalWrite(LED_G5 , LOW); } } /* aktuelle Zeit in 1/10 Sekunde */ word getTime() { long temp = millis(); // Starttime ist in 1/10 sekunden return temp / 100; } /* Kurzer Ton. */ void beep() { dbgOutLn("B"); tone(BEEP_PIN, BEEP_HZ, 250); } /* Langer Ton. */ void longbeep() { dbgOutLn("LB"); tone(BEEP_PIN, BEEP_HZ, 1000); } /* Uhr stellen. Hier wird jetzt der Zeiger nach der Uhr gestellt. Zunächst wird die vergangene Zeit gemessen, dann die Anzahl der Schritte berechnet, und diese dann ausgeführt. */ void doSteps() { if (millis() < nexttime) { unsigned long misTime = nexttime - millis(); unsigned long missingSteps = (misTime * Su) / 60000 ; word offset = steps - missingSteps; // Wenn's was zutun gibt, dann die Uhr jetzt stellen if (offset > 0) { SteppStepper(offset, SM_DIRECTION); } steps -= offset; done += offset; dbgOut("su: "); dbgOut(Su); dbgOut("m: "); dbgOut(misTime / 1000); dbgOut(", o: "); dbgOut(offset); dbgOut(", d: "); dbgOut(done); dbgOut(", m: "); dbgOutLn(missingSteps); } } /* Schrittmotor um die Anzahl an Schritte laufen lassen. @param steps Anzahl der Schritte @param: forward vorwärts oder rückwärts */ void SteppStepper(byte steps, boolean forward) { digitalWrite(SM_DIR, forward); while (steps > 0) { digitalWrite(SM_STEP, HIGH); delay(20); digitalWrite(SM_STEP, LOW); steps -= 1; if (steps > 0) { //delay(Ph-50); delay(Ph); } } } /* Starten des Schrittmotortreibers */ void startStepper() { dbgOutLn("start stepper"); lastStepUsed = getTime(); // Wenn ausgeschaltet, if (digitalRead(SM_SLEEP) == 0) { // einschalten digitalWrite(SM_SLEEP, HIGH); // und auf null stellen calibStepper(); } //digitalWrite(SM_SLEEP, HIGH); } /* Uhr zurück laufen lassen, bis die Lichtschranke anspricht. Dann 1 Sekunde warten. */ void calibStepper() { dbgOutLn("calib stepper"); byte smSwitch = digitalRead(SM_SWITCH); while (smSwitch == 1) { SteppStepper(1, !SM_DIRECTION); delay(Ph); smSwitch = digitalRead(SM_SWITCH); } delay(1000); } /* Systemtest ausführen. */ void testSystem() { dbgOutLn("Testing clock"); dbgOut("Su: "); dbgOutLn(Su); dbgOut("Ph:"); dbgOutLn(Ph); testLED(); testBeep(); testStepper(); testRC(); delay(500); } /* Schrittmotor testen. */ void testStepper() { dbgOutLn("Step Test"); dbgOutLn("wakeup"); digitalWrite(SM_SLEEP, HIGH); dbgOutLn("100 for"); SteppStepper(100, true); delay(500); dbgOutLn("100 back"); SteppStepper(100, false); delay(2000); dbgOutLn("test NP"); int i = 0; byte smSwitch = digitalRead(SM_SWITCH); while (smSwitch == 1) { SteppStepper(1, !SM_DIRECTION); delay(Ph); smSwitch = digitalRead(SM_SWITCH); } delay(500); dbgOutLn("100 back"); SteppStepper(100, false); smSwitch = digitalRead(SM_SWITCH); while (smSwitch == 1) { SteppStepper(1, !SM_DIRECTION); delay(Ph); smSwitch = digitalRead(SM_SWITCH); } SteppStepper(50,true); dbgOutLn("sleep"); digitalWrite(SM_SLEEP, LOW); } /* RC Verbindung testen. */ void testRC() { dbgOutLn("RC Test"); tone(BEEP_PIN, BEEP_HZ, 100); delay(200); tone(BEEP_PIN, BEEP_HZ, 100); delay(100); digitalWrite(LED_W1, LOW); digitalWrite(LED_W2, LOW); digitalWrite(LED_Y3, LOW); int i = 0; while ( i < 10) { digitalWrite(LED_BOARD, HIGH); // set the LED on int RcValue = rcReceiver.getValue(); if (rcReceiver.hasError()) { dbgOutLn("kein RC"); delay(100); digitalWrite(LED_BOARD, LOW); // set the LED off delay(900); // wait for a second } else if (RcValue < ResetRCValue) { dbgOutLn("Reset RC"); delay(500); digitalWrite(LED_Y3, HIGH); digitalWrite(LED_BOARD, LOW); // set the LED off delay(500); // wait for a second } else if (RcValue < StartRCValue) { dbgOutLn("RC Null"); delay(500); digitalWrite(LED_W1, HIGH); digitalWrite(LED_BOARD, LOW); // set the LED off delay(500); // wait for a second } else if (RcValue > StartRCValue) { dbgOutLn("RC Start"); delay(500); digitalWrite(LED_W2, HIGH); digitalWrite(LED_BOARD, LOW); // set the LED off delay(500); // wait for a second } digitalWrite(LED_BOARD, LOW); // set the LED off digitalWrite(LED_W1, LOW); digitalWrite(LED_W2, LOW); digitalWrite(LED_Y3, LOW); i++; } } void testLED() { dbgOutLn("LED Test"); dbgOutLn("LED W1"); digitalWrite(LED_W1, HIGH); delay(250); digitalWrite(LED_W1, LOW); delay(500); dbgOutLn("LED W2"); digitalWrite(LED_W2, HIGH); delay(250); digitalWrite(LED_W2, LOW); delay(500); dbgOutLn("LED Y3"); digitalWrite(LED_Y3, HIGH); delay(250); digitalWrite(LED_Y3, LOW); delay(500); dbgOutLn("LED G4"); digitalWrite(LED_G4, HIGH); delay(250); digitalWrite(LED_G4, LOW); delay(500); dbgOutLn("LED G5"); digitalWrite(LED_G5, HIGH); delay(250); digitalWrite(LED_G5, LOW); clockReset(); } void testBeep() { dbgOutLn("Beep Test"); dbgOutLn("long"); longbeep(); delay(1200); dbgOutLn("beep"); beep(); delay(500); dbgOutLn("Beep Test ready"); }