diff --git a/rotating-display-controller/rotating-display-controller.ino b/rotating-display-controller/rotating-display-controller.ino index 99df830..1fd9ea2 100644 --- a/rotating-display-controller/rotating-display-controller.ino +++ b/rotating-display-controller/rotating-display-controller.ino @@ -18,13 +18,17 @@ int stepSequence[4][4] = { const int STEPS_PER_REVOLUTION = 2048; // 28BYJ-48 standard (512 steps * 4 gear reduction) const float DEGREES_PER_STEP = 360.0 / STEPS_PER_REVOLUTION; +// Speed Limits (Clamping) +const float MAX_TURN_SPEED = 45.0; // Maximum safe speed in deg/s +const float MIN_TURN_SPEED = 0.1; // Minimum safe speed to avoid div by zero + // --- Global Variables --- enum State { MOVE, WAIT }; enum Mode { AUTO, MANUAL }; Mode autoMode = AUTO; // Default: AUTO -float targetAngle = 0.0; // Target angle in degrees -float turnSpeed = 5.0; // Speed in degrees per second +float targetAngle = 15.0; // Target angle in degrees +float turnSpeed = 45.0; // Speed in degrees per second State _currState = MOVE; // Default: MOVE @@ -49,10 +53,6 @@ void setStep(int a, int b, int c, int d) { } void updateStepInterval() { - if (turnSpeed <= 0) { - _stepInterval = 0.1; // Fallback to avoid div by zero - return; - } // Calculate steps per second: speed / degrees_per_step float stepsPerSecond = turnSpeed / DEGREES_PER_STEP; _stepInterval = 1.0 / stepsPerSecond; // Seconds per step @@ -104,6 +104,34 @@ void runStep() { } } +// --- Serial Output Helpers --- + +void printStatus() { + Serial.print("Mode: "); + Serial.print(autoMode == AUTO ? "AUTO" : "MANUAL"); + Serial.print(" | State: "); + Serial.print(_currState == MOVE ? "MOVE" : "WAIT"); + Serial.print(" | Steps Left: "); + Serial.print(_stepsRemaining); + Serial.print(" | Speed: "); + Serial.print(turnSpeed); + Serial.print(" deg/s | Wait: "); + Serial.print(waitInterval); + Serial.println("s"); +} + +void printCommandAck(char cmd, String payload) { + Serial.print("> Command '"); + Serial.print(cmd); + Serial.print(payload); + Serial.println("' received."); +} + +void printError(String msg) { + Serial.print("ERROR: "); + Serial.println(msg); +} + // --- Setup --- void setup() { // Initialize pins @@ -116,6 +144,8 @@ void setup() { Serial.begin(9600); while (!Serial) { ; } // Wait for serial port (optional for native USB) + Serial.println("=== Stepper Motor Controller Initialized ==="); + // Initial State Setup autoMode = AUTO; waitInterval = 8.0; @@ -127,10 +157,16 @@ void setup() { _waitTime = millis(); // Initial calculation (move 0 degrees to set defaults) - calculateStepsForAngle(0.0); - // Ensure we start in MOVE if we want immediate action, or WAIT if idle - // Based on your logic: "initialized in MOVE state" + calculateStepsForAngle(targetAngle); _currState = MOVE; + + Serial.println("Ready. Commands: m[0|1], a[deg], s[deg/s], i[sec]"); + Serial.println(" (Speed clamped between "); + Serial.print(MIN_TURN_SPEED); + Serial.print(" and "); + Serial.print(MAX_TURN_SPEED); + Serial.println(" deg/s)"); + printStatus(); } // --- Loop --- @@ -140,27 +176,46 @@ void loop() { String command = Serial.readString(); command.trim(); // Remove whitespace - if (command.length() < 2) return; // Need at least 'a' + number + if (command.length() < 2) { + printError("Invalid command format (too short)."); + return; + } char cmdType = command.charAt(0); String payload = command.substring(1); + printCommandAck(cmdType, payload); + switch (cmdType) { case 'm': // 'm0' for AUTO, 'm1' for MANUAL if (payload.length() > 0) { char modeChar = payload.charAt(0); - if (modeChar == '0' && autoMode != AUTO) { - autoMode = AUTO; - _currState = MOVE; - _stepsRemaining = _stepCount; - _stepTime = millis(); - } else if (modeChar == '1' && autoMode != MANUAL) { - autoMode = MANUAL; - _currState = MOVE; // Start moving immediately on switch - _stepsRemaining = _stepCount; - _stepTime = millis(); + if (modeChar == '0') { + if (autoMode != AUTO) { + autoMode = AUTO; + _currState = MOVE; + _stepsRemaining = _stepCount; + _stepTime = millis(); + Serial.println(" -> Mode switched to AUTO. Starting move cycle."); + } else { + Serial.println(" -> Already in AUTO mode."); + } + } else if (modeChar == '1') { + if (autoMode != MANUAL) { + autoMode = MANUAL; + _currState = MOVE; // Start moving immediately on switch + _stepsRemaining = _stepCount; + _stepTime = millis(); + Serial.println(" -> Mode switched to MANUAL. Moving once, then waiting."); + } else { + Serial.println(" -> Already in MANUAL mode."); + } + } else { + printError("Invalid mode. Use 'm0' (AUTO) or 'm1' (MANUAL)."); } + } else { + printError("Missing mode argument (0 or 1)."); } break; @@ -168,7 +223,20 @@ void loop() { // 'a90' -> move 90 degrees { float angle = payload.toFloat(); - calculateStepsForAngle(angle); + if (payload.length() == 0 || (angle == 0 && payload != "0")) { + printError("Invalid angle format."); + } else { + calculateStepsForAngle(angle); + targetAngle = angle; + Serial.print(" -> Moving "); + Serial.print(_stepCount); + Serial.print(" steps ("); + Serial.print(targetAngle); + Serial.println(" degrees)."); + if (_stepCount == 0) { + Serial.println(" -> No movement required (0 degrees)."); + } + } } break; @@ -176,10 +244,27 @@ void loop() { // 's10' -> 10 deg/s { float speed = payload.toFloat(); - if (speed > 0) { + if (payload.length() == 0 || speed <= 0) { + printError("Invalid speed. Must be > 0."); + } else { turnSpeed = speed; + + // Logic to check if clamping occurred + float actualSpeed = turnSpeed; + if (actualSpeed > MAX_TURN_SPEED) { + turnSpeed = MAX_TURN_SPEED; + Serial.print(" -> WARNING: Speed clamped to max "); + } else if (actualSpeed < MIN_TURN_SPEED) { + turnSpeed = MIN_TURN_SPEED; + Serial.print(" -> WARNING: Speed clamped to min "); + } + updateStepInterval(); - // If currently in MOVE, the new interval applies on next step + Serial.print(" -> Speed set to "); + Serial.print(turnSpeed); + Serial.print(" deg/s (Step interval: "); + Serial.print(_stepInterval * 1000, 2); + Serial.println(" ms)."); } } break; @@ -188,12 +273,24 @@ void loop() { // 'i5' -> 5 seconds wait { float interval = payload.toFloat(); - if (interval >= 0) { + if (payload.length() == 0 || interval < 0) { + printError("Invalid interval. Must be >= 0."); + } else { waitInterval = interval; + Serial.print(" -> Wait interval set to "); + Serial.print(waitInterval); + Serial.println(" seconds."); } } break; + + default: + printError("Unknown command. Use m, a, s, or i."); + break; } + + // Print status after every command for immediate feedback + printStatus(); } // --- EXECUTION LOGIC --- @@ -202,6 +299,14 @@ void loop() { if (_currState == MOVE && _stepsRemaining == 0) { _currState = WAIT; _waitTime = millis(); + Serial.println("--- State: MOVE -> WAIT ---"); + if (autoMode == AUTO) { + Serial.print(" Waiting "); + Serial.print(waitInterval); + Serial.println(" seconds before next cycle..."); + } else { + Serial.println(" Waiting indefinitely for new command (MANUAL mode)."); + } } // 2. Check if we should restart in AUTO mode @@ -210,6 +315,10 @@ void loop() { _currState = MOVE; _stepsRemaining = _stepCount; _stepTime = millis(); // Reset step timer to prevent burst + Serial.println("--- State: WAIT -> MOVE ---"); + Serial.print(" Auto-restart! Moving "); + Serial.print(_stepsRemaining); + Serial.println(" steps."); } // If MANUAL: Stay in WAIT indefinitely until new command } @@ -221,6 +330,14 @@ void loop() { runStep(); _stepTime = currentTime; _stepsRemaining--; + + // Optional: Print progress every 10 steps to avoid spam + // if (_stepCount > 0 && (_stepCount - _stepsRemaining) % 10 == 0) { + // Serial.print(" Step: "); + // Serial.print(_stepCount - _stepsRemaining); + // Serial.print("/"); + // Serial.println(_stepCount); + // } } } } \ No newline at end of file