Welke simpele degelijke pendel module zou ik het beste kunnen aanschaffen?
#include <Wire.h>#include <Adafruit_GFX.h>#include <Adafruit_SSD1306.h>// OLED display instellingen#define SCREEN_WIDTH 128#define SCREEN_HEIGHT 64#define OLED_RESET -1Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);// Constants voor pinnen - Märklin wisselstroom versieconst int lightSensorPin1 = 2; // Lichtsluis station 1 (digital)const int lightSensorPin2 = 3; // Lichtsluis station 2 (digital)const int lightSensorPin3 = 10; // Lichtsluis stopplaats 3 (digital)const int lightSensorPin4 = 11; // Lichtsluis stopplaats 4 (digital)const int relayPin = 4; // Relais voor richting (digital)const int pwmPin = 9; // PWM voor snelheid (moet PWM pin zijn)const int accelPotPin = A0; // Aanloopvertraging potentiometerconst int decelPotPin = A1; // Remvertraging potentiometerconst int waitPotPin = A2; // Wachttijd potentiometerconst int manualControlPin = 6; // Handbediening schakelaar (digital)const int ledPin1 = 7; // LED 1: Station 1 -> Station 2const int ledPin2 = 8; // LED 2: Station 2 -> Station 1const int statusLedPin = 13; // Status LED (built-in)// Märklin specifieke instellingenconst int MIN_PWM = 30; // Minimale PWM voor Märklin (i.v.m. startspanning)const int MAX_PWM = 255; // Maximale PWMconst int DIRECTION_DELAY = 500; // Vertraging bij richtingsverandering (ms)// LED knipper instellingenconst unsigned long SLOW_BLINK_INTERVAL = 800; // Langzaam knipperen (800ms)const unsigned long FAST_BLINK_INTERVAL = 200; // Snel knipperen (200ms)const unsigned long VERY_FAST_BLINK_INTERVAL = 100; // Zeer snel knipperen (100ms)// Stopplaats instellingenconst int STOP_TIME = 3000; // Wachttijd stopplaatsen (3 seconden)// Variabelenint currentSpeed = 0;int targetSpeed = 0;int previousSpeed = 0;bool movingForward = true;unsigned long lastUpdateTime = 0;unsigned long waitStartTime = 0;unsigned long directionChangeTime = 0;unsigned long lastSensorTime = 0;unsigned long manualOverrideTime = 0;unsigned long lastDisplayUpdate = 0;unsigned long lastLEDUpdate = 0;bool waiting = false;bool changingDirection = false;bool manualControl = false;bool emergencyStopActive = false;bool sensorError = false;bool ledState = false;int cycleCount = 0;unsigned long totalRunTime = 0;unsigned long programStartTime = millis();// Trein toestandenenum TrainState { MOVING_TO_STATION2, WAITING_AT_STATION2, MOVING_TO_STATION1, WAITING_AT_STATION1, STOPPING_AT_POINT3, STOPPING_AT_POINT4, MANUAL_CONTROL, EMERGENCY_STOP};TrainState currentState = MOVING_TO_STATION2;TrainState previousState = MOVING_TO_STATION2;void setup() { // Pin modes instellen pinMode(lightSensorPin1, INPUT_PULLUP); pinMode(lightSensorPin2, INPUT_PULLUP); pinMode(lightSensorPin3, INPUT_PULLUP); pinMode(lightSensorPin4, INPUT_PULLUP); pinMode(relayPin, OUTPUT); pinMode(pwmPin, OUTPUT); pinMode(manualControlPin, INPUT_PULLUP); pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); pinMode(statusLedPin, OUTPUT); // OLED display initialiseren if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1306 allocation failed")); for(;;); } display.clearDisplay(); display.setTextColor(SSD1306_WHITE); display.setTextSize(1); display.setCursor(0,0); display.println("Märklin Pendeltrein"); display.println("4 Stopplaatsen"); display.display(); delay(2000); // Seriële communicatie voor debugging Serial.begin(9600); Serial.println("Märklin Pendeltrein met 4 Stopplaatsen Gestart"); Serial.println("============================================="); // Start richting station 2 digitalWrite(relayPin, HIGH); movingForward = true; updateLEDs(false); // Start met minimale snelheid analogWrite(pwmPin, MIN_PWM); delay(1000); programStartTime = millis(); displayStartScreen();}void loop() { unsigned long currentTime = millis(); // Lees bedieningselementen bool manualControlRequest = digitalRead(manualControlPin) == LOW; // Handmatige overname detectie if (manualControlRequest && !manualControl) { enableManualControl(); } else if (!manualControlRequest && manualControl) { disableManualControl(); } if (manualControl) { handleManualControl(); updateDisplay(currentTime); updateLEDControl(currentTime); return; } if (emergencyStopActive) { handleEmergencyStop(); updateDisplay(currentTime); updateLEDControl(currentTime); return; } // Lees potentiometers int acceleration = map(analogRead(accelPotPin), 0, 1023, 20, 200); int deceleration = map(analogRead(decelPotPin), 0, 1023, 20, 200); int waitTime = map(analogRead(waitPotPin), 0, 1023, 5, 120) * 1000; // Lichtsluis detectie met debouncing bool atStation1 = readSensorWithDebounce(lightSensorPin1, currentTime); bool atStation2 = readSensorWithDebounce(lightSensorPin2, currentTime); bool atPoint3 = readSensorWithDebounce(lightSensorPin3, currentTime); bool atPoint4 = readSensorWithDebounce(lightSensorPin4, currentTime); // Sensor status monitoring checkSensorStatus(currentTime); // Update totale looptijd if (currentState == MOVING_TO_STATION1 || currentState == MOVING_TO_STATION2 || currentState == STOPPING_AT_POINT3 || currentState == STOPPING_AT_POINT4) { totalRunTime += (currentTime - lastUpdateTime); } // Uitgebreide State machine met 4 stopplaatsen switch (currentState) { case MOVING_TO_STATION2: targetSpeed = MAX_PWM; digitalWrite(statusLedPin, HIGH); if (atPoint3 && movingForward) { currentState = STOPPING_AT_POINT3; waitStartTime = currentTime; waiting = true; targetSpeed = 0; logEvent("Aangekomen bij stopplaats 3"); } else if (atStation2) { previousState = currentState; currentState = WAITING_AT_STATION2; waitStartTime = currentTime; waiting = true; targetSpeed = 0; cycleCount++; updateLEDs(false); logEvent("Aangekomen bij station 2"); } break; case STOPPING_AT_POINT3: targetSpeed = 0; digitalWrite(statusLedPin, (millis() % 1000 < 500)); if (currentTime - waitStartTime >= STOP_TIME && !waiting) { currentState = MOVING_TO_STATION2; targetSpeed = MAX_PWM; logEvent("Vertrokken van stopplaats 3"); } if (waiting && currentTime - waitStartTime >= 1000) { waiting = false; } break; case WAITING_AT_STATION2: targetSpeed = 0; digitalWrite(statusLedPin, (millis() % 1000 < 500)); if (currentTime - waitStartTime >= waitTime && !waiting && !changingDirection) { changingDirection = true; directionChangeTime = currentTime; digitalWrite(relayPin, LOW); movingForward = false; logEvent("Richting veranderd naar station 1"); } if (changingDirection && currentTime - directionChangeTime >= DIRECTION_DELAY) { changingDirection = false; previousState = currentState; currentState = MOVING_TO_STATION1; targetSpeed = MIN_PWM; updateLEDs(true); logEvent("Vertrek van station 2"); } if (waiting && currentTime - waitStartTime >= 1000) { waiting = false; } break; case MOVING_TO_STATION1: targetSpeed = MAX_PWM; digitalWrite(statusLedPin, HIGH); if (atPoint4 && !movingForward) { currentState = STOPPING_AT_POINT4; waitStartTime = currentTime; waiting = true; targetSpeed = 0; logEvent("Aangekomen bij stopplaats 4"); } else if (atStation1) { previousState = currentState; currentState = WAITING_AT_STATION1; waitStartTime = currentTime; waiting = true; targetSpeed = 0; cycleCount++; updateLEDs(false); logEvent("Aangekomen bij station 1"); } break; case STOPPING_AT_POINT4: targetSpeed = 0; digitalWrite(statusLedPin, (millis() % 1000 < 500)); if (currentTime - waitStartTime >= STOP_TIME && !waiting) { currentState = MOVING_TO_STATION1; targetSpeed = MAX_PWM; logEvent("Vertrokken van stopplaats 4"); } if (waiting && currentTime - waitStartTime >= 1000) { waiting = false; } break; case WAITING_AT_STATION1: targetSpeed = 0; digitalWrite(statusLedPin, (millis() % 1000 < 500)); if (currentTime - waitStartTime >= waitTime && !waiting && !changingDirection) { changingDirection = true; directionChangeTime = currentTime; digitalWrite(relayPin, HIGH); movingForward = true; logEvent("Richting veranderd naar station 2"); } if (changingDirection && currentTime - directionChangeTime >= DIRECTION_DELAY) { changingDirection = false; previousState = currentState; currentState = MOVING_TO_STATION2; targetSpeed = MIN_PWM; updateLEDs(true); logEvent("Vertrek van station 1"); } if (waiting && currentTime - waitStartTime >= 1000) { waiting = false; } break; } // Snelheidsregeling if (currentTime - lastUpdateTime >= 50) { previousSpeed = currentSpeed; updateSpeedControl(acceleration, deceleration, currentTime); lastUpdateTime = currentTime; } // LED aansturing updaten updateLEDControl(currentTime); // Display updaten (elke 500ms) if (currentTime - lastDisplayUpdate >= 500) { updateDisplay(currentTime); lastDisplayUpdate = currentTime; } // Status rapportage elk uur static unsigned long lastReportTime = 0; if (currentTime - lastReportTime >= 3600000) { generateStatusReport(); lastReportTime = currentTime; } delay(10);}// LED aansturing functies (zelfde als voorheen)void updateLEDControl(unsigned long currentTime) { static unsigned long lastBlinkTime = 0; unsigned long blinkInterval = 0; bool shouldBlink = false; if (emergencyStopActive) { blinkInterval = VERY_FAST_BLINK_INTERVAL; shouldBlink = true; } else if (currentState == MOVING_TO_STATION1 || currentState == MOVING_TO_STATION2 || currentState == STOPPING_AT_POINT3 || currentState == STOPPING_AT_POINT4) { if (currentSpeed > previousSpeed) { blinkInterval = SLOW_BLINK_INTERVAL; shouldBlink = true; } else if (currentSpeed < previousSpeed && currentSpeed > MIN_PWM + 10) { blinkInterval = FAST_BLINK_INTERVAL; shouldBlink = true; } else { shouldBlink = false; } } else if (currentState == WAITING_AT_STATION1 || currentState == WAITING_AT_STATION2) { blinkInterval = SLOW_BLINK_INTERVAL; shouldBlink = true; } if (shouldBlink && blinkInterval > 0) { if (currentTime - lastBlinkTime >= blinkInterval) { ledState = !ledState; updateLEDs(ledState); lastBlinkTime = currentTime; } } else { updateLEDs(!shouldBlink); }}void updateLEDs(bool enable) { if (emergencyStopActive) { digitalWrite(ledPin1, enable); digitalWrite(ledPin2, enable); } else if (manualControl) { digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, LOW); } else if (currentState == MOVING_TO_STATION2 || currentState == STOPPING_AT_POINT3) { digitalWrite(ledPin1, enable); digitalWrite(ledPin2, LOW); } else if (currentState == MOVING_TO_STATION1 || currentState == STOPPING_AT_POINT4) { digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, enable); } else { digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, LOW); }}// Display functies met extra stopplaatsenvoid displayStartScreen() { display.clearDisplay(); display.setTextSize(2); display.setCursor(0,0); display.println(" MARKLIN"); display.println(" 4 STOP"); display.println(" PLAATSEN"); display.setTextSize(1); display.setCursor(0,50); display.println("Route: 1-3-2-4-1"); display.display(); delay(3000);}void updateDisplay(unsigned long currentTime) { display.clearDisplay(); // Header met status en locatie display.setTextSize(1); display.setCursor(0,0); display.print("Status: "); display.println(getStateName(currentState)); // Huidige locatie display.setCursor(70,0); display.print("Loc: "); display.println(getLocationName(currentState)); // Horizontale lijn display.drawLine(0, 10, 128, 10, SSD1306_WHITE); // Potentiometer waarden int accelValue = map(analogRead(accelPotPin), 0, 1023, 1, 10); int decelValue = map(analogRead(decelPotPin), 0, 1023, 1, 10); int waitValue = map(analogRead(waitPotPin), 0, 1023, 5, 120); display.setCursor(0,15); display.print("Optrek: "); display.print(accelValue); display.println(" sec"); display.setCursor(0,25); display.print("Afrem: "); display.print(decelValue); display.println(" sec"); display.setCursor(0,35); display.print("Wachten: "); display.print(waitValue); display.println(" sec"); // Stopplaats wachttijd display.setCursor(0,45); display.print("Stop: "); display.print(STOP_TIME / 1000); display.println(" sec"); // Snelheidsbalk int speedPercent = map(currentSpeed, 0, 255, 0, 100); display.setCursor(0,55); display.print("Snelh: "); display.print(speedPercent); display.print("% "); display.print(currentSpeed > previousSpeed ? "▲" : (currentSpeed < previousSpeed ? "▼" : "■")); // Snelheidsbalk tekenen int barWidth = map(currentSpeed, 0, 255, 0, 100); display.drawRect(50, 55, 70, 8, SSD1306_WHITE); display.fillRect(50, 55, barWidth * 70 / 100, 8, SSD1306_WHITE); display.display();}String getStateName(TrainState state) { switch(state) { case MOVING_TO_STATION1: return "Naar Stat 1"; case MOVING_TO_STATION2: return "Naar Stat 2"; case WAITING_AT_STATION1: return "Wacht Stat 1"; case WAITING_AT_STATION2: return "Wacht Stat 2"; case STOPPING_AT_POINT3: return "Stop Plaats 3"; case STOPPING_AT_POINT4: return "Stop Plaats 4"; case MANUAL_CONTROL: return "Handmatig"; case EMERGENCY_STOP: return "NOODSTOP"; default: return "Onbekend"; }}String getLocationName(TrainState state) { switch(state) { case MOVING_TO_STATION1: return "1->2"; case MOVING_TO_STATION2: return "2->1"; case WAITING_AT_STATION1: return "Stat 1"; case WAITING_AT_STATION2: return "Stat 2"; case STOPPING_AT_POINT3: return "Stop 3"; case STOPPING_AT_POINT4: return "Stop 4"; case MANUAL_CONTROL: return "Hand"; case EMERGENCY_STOP: return "STOP"; default: return "Onbekend"; }}// Overige functies blijven hetzelfdebool readSensorWithDebounce(int sensorPin, unsigned long currentTime) { static unsigned long lastSensorTime1 = 0, lastSensorTime2 = 0, lastSensorTime3 = 0, lastSensorTime4 = 0; static bool lastState1 = HIGH, lastState2 = HIGH, lastState3 = HIGH, lastState4 = HIGH; bool currentState = digitalRead(sensorPin) == LOW; unsigned long* lastTime; bool* lastState; switch(sensorPin) { case lightSensorPin1: lastTime = &lastSensorTime1; lastState = &lastState1; break; case lightSensorPin2: lastTime = &lastSensorTime2; lastState = &lastState2; break; case lightSensorPin3: lastTime = &lastSensorTime3; lastState = &lastState3; break; case lightSensorPin4: lastTime = &lastSensorTime4; lastState = &lastState4; break; default: return false; } if (currentState != *lastState) { *lastTime = currentTime; *lastState = currentState; } return (currentTime - *lastTime > 100) && currentState;}void checkSensorStatus(unsigned long currentTime) { static unsigned long lastSensorCheck = 0; if (currentTime - lastSensorCheck > 5000) { // Eenvoudige conflict detectie int activeSensors = 0; if (digitalRead(lightSensorPin1) == LOW) activeSensors++; if (digitalRead(lightSensorPin2) == LOW) activeSensors++; if (digitalRead(lightSensorPin3) == LOW) activeSensors++; if (digitalRead(lightSensorPin4) == LOW) activeSensors++; if (activeSensors > 1) { sensorError = true; triggerEmergencyStop("Sensor conflict"); } else { sensorError = false; } lastSensorCheck = currentTime; }}void updateSpeedControl(int acceleration, int deceleration, unsigned long currentTime) { if (!changingDirection && !emergencyStopActive) { if (currentSpeed < targetSpeed) { int speedIncrease = min(acceleration / 20, targetSpeed - currentSpeed); currentSpeed += speedIncrease; if (currentSpeed > 0 && currentSpeed < MIN_PWM) currentSpeed = MIN_PWM; } else if (currentSpeed > targetSpeed) { int speedDecrease = min(deceleration / 20, currentSpeed - targetSpeed); currentSpeed -= speedDecrease; if (currentSpeed < MIN_PWM && targetSpeed == 0) currentSpeed = 0; } } else { currentSpeed = 0; } analogWrite(pwmPin, max(currentSpeed, MIN_PWM));}void enableManualControl() { previousState = currentState; currentState = MANUAL_CONTROL; manualControl = true; manualOverrideTime = millis(); updateLEDs(false); logEvent("Handmatige bediening");}void disableManualControl() { manualControl = false; currentState = previousState; updateLEDs(true); logEvent("Automatisch hervat");}void handleManualControl() { digitalWrite(statusLedPin, (millis() % 500 < 250)); updateLEDs(false); if (millis() - manualOverrideTime > 300000) disableManualControl();}void triggerEmergencyStop(String reason) { emergencyStopActive = true; currentState = EMERGENCY_STOP; targetSpeed = 0; currentSpeed = 0; analogWrite(pwmPin, 0); logEvent("NOODSTOP: " + reason);}void handleEmergencyStop() { digitalWrite(statusLedPin, (millis() % 200 < 100));}void logEvent(String message) { Serial.print("["); Serial.print(millis() / 1000); Serial.print("s] "); Serial.println(message);}void generateStatusReport() { Serial.println("\n=== STATUS RAPPORT ==="); Serial.print("Totale bedrijfstijd: "); Serial.print((millis() - programStartTime) / 3600000); Serial.println(" uur"); Serial.print("Aantal cycli: "); Serial.println(cycleCount); Serial.print("Huidige staat: "); Serial.println(getStateName(currentState)); Serial.println("====================\n");}void serialEvent() { while (Serial.available()) { char command = Serial.read(); switch(command) { case 's': triggerEmergencyStop("Serieel commando"); break; case 'r': emergencyStopActive = false; currentState = MOVING_TO_STATION2; break; case 'm': enableManualControl(); break; case 'a': disableManualControl(); break; case '?': generateStatusReport(); break; } }}