# Arduino Stepper Motor Controller A state-machine based Arduino sketch for controlling a 28BYJ-48 stepper motor via Serial communication. The system supports both **Manual** and **Automatic** modes, allowing for relative angle movement, speed control, and configurable wait intervals. ## Hardware Requirements - **Microcontroller**: Arduino Uno / Mega / Nano - **Motor Driver**: ULN2003 (or compatible) - **Stepper Motor**: 28BYJ-48 (or compatible bipolar/unipolar stepper) ### Pin Connections | Pin | Function | Arduino Pin | |-----|----------|-------------| | IN1 | Step 1 | 9 | | IN2 | Step 2 | 10 | | IN3 | Step 3 | 11 | | IN4 | Step 4 | 12 | ## System Configuration ### Constants - **STEPS_PER_REVOLUTION**: `2048` (Standard for 28BYJ-48 with gear reduction) - **DEGREES_PER_STEP**: Calculated as `360.0 / 2048` (~0.1758°) ### Global Variables | Variable | Type | Description | |----------|------|-------------| | `autoMode` | `enum` | `AUTO` or `MANUAL` | | `targetAngle` | `float` | Target angle in degrees (relative to current position) | | `turnSpeed` | `float` | Speed in degrees per second | | `_currState` | `enum` | `MOVE` or `WAIT` | | `waitInterval` | `float` | Time in seconds to wait in `WAIT` state (only active in AUTO mode) | | `_waitTime` | `unsigned long` | Timestamp of when WAIT state began | | `_stepInterval` | `float` | Time in seconds between individual steps | | `_stepTime` | `unsigned long` | Timestamp of the last step | | `_stepCount` | `int` | Total steps to move for the current target | | `_stepsRemaining` | `int` | Steps remaining in the current move sequence | | `_directionCW` | `bool` | `true` for Clockwise, `false` for Counter-Clockwise | ## Serial Command Interface The controller listens to the Serial port (9600 baud). Commands must start with a letter followed by a value. No spaces are required. | Command | Format | Description | |---------|--------|-------------| | **Mode Switch** | `m0` / `m1` | `m0`: Set to **AUTO** (cycles MOVE/WAIT).
`m1`: Set to **MANUAL** (moves once, then waits indefinitely). | | **Move Angle** | `a` | Move `` degrees relative to current position. Supports negative values for reverse direction.
*Example*: `a90` (move 90° CW), `a-45` (move 45° CCW). | | **Set Speed** | `s` | Set speed in degrees per second.
*Example*: `s10` (10 deg/s). | | **Set Wait Time** | `i` | Set the duration (in seconds) to wait in `WAIT` state when in AUTO mode.
*Example*: `i5` (wait 5 seconds). | ## Operational Logic ### State Machine The system operates in two primary states: 1. **MOVE**: - The motor steps at the calculated `_stepInterval`. - `_stepsRemaining` decrements with each step. - When `_stepsRemaining` hits 0, the system transitions to **WAIT**. 2. **WAIT**: - The motor stops. - `_waitTime` is recorded. - **In AUTO Mode**: If `millis() - _waitTime > waitInterval`, the system transitions back to **MOVE** (repeats cycle). - **In MANUAL Mode**: The system stays in **WAIT** indefinitely until a new Serial command is received. ```cpp autoMode // AUTO or MANUAL. If AUTO, cycles between MOVE and WAIT state. If MANUAL, goes into MOVE state, but stays in WAIT indefinitely until a new command is sent targetAngle // The target angle in degrees, relative to the current position turnSpeed // The turn speed in degrees per second _currState // MOVE or WAIT waitInterval // Time to stay in wait state in AUTO mode, in seconds _waitTime // Variable to compare against millis() _stepInterval // Time between motor steps, in seconds _stepTime // Variable to compare against millis() _stepCount // Number of steps remaining _stepRemaining // LOOP --- READ SERIAL --- if serial.Available string = serial.readString switch(string[0]) case 'm': if autoMode != string[1] autoMode = AUTO if string[1] == 0, MANUAL if string[1] == 1 // Start moving following existing parameters, relative to current position _currState = MOVE _waitTime = 0 _stepTime = millis() _stepsRemaining = _stepCount case 'a': // Move this many degrees relative to current position targetAngle = strToFloat(string[1:]) // TODO: Recalculate step count _currState = MOVE _waitTime = 0 _stepTime = millis() _stepsRemaining = _stepCount case 's': // Sets the step interval turnSpeed = strToFloat(string[1:]) // TODO: Recalculate step interval case 'i': // Sets the wait interval waitInterval = strToFloat(string[1:]) --- Execution Logic --- if _stepsRemaining == 0 _currState = WAIT _waitTime = millis() if autoMode = AUTO and _currState == WAIT and millis() - _waitTime > waitInterval _currState = MOVE _stepsRemaining = _stepCount _stepTime = millis() if currState == MOVE if millis() - _stepTime > _stepInterval RunStep _stepTime = millis() _stepsRemaining -= 1 ``` ### Initialization - **Default Mode**: `AUTO` - **Default State**: `MOVE` - **Default Wait Interval**: `8.0` seconds - **Default Speed**: `5.0` degrees/second ## Step Sequence Logic The controller supports bidirectional movement by using two sequences: - **Clockwise (CW)**: Standard 4-step sequence. - **Counter-Clockwise (CCW)**: Reverse of the CW sequence. The direction is determined by the sign of the `targetAngle` when the `a` command is received. ## Usage Examples 1. **Move 90 degrees clockwise at 10 deg/s**: ```text s10 a90