# Basic Motor Position Control Application

In this application, you will learn how to control a DC motor’s **exact position** in degrees using the **Position Mode** of the SMD Red module. You will enter a target angle between `0` and `360`, and the motor will rotate precisely to that point using encoder feedback.

**About Tools and Materials:**

[SMD Red](https://docs.acrome.net/electronics/smd-red) ([Purchase Here](https://www.robotshop.com/products/acrome-smd-red-smart-brushed-motor-driver-with-speed-position-and-current-control-modes))

[SMD USB Gateway](https://docs.acrome.net/electronics/gateway-modules/usb-gateway-module) ([Purchase Here](https://www.robotshop.com/products/acrome-usb-gateway-module-acrome-smd-products))

[Arduino Gateway Module](https://docs.acrome.net/electronics/gateway-modules/arduino-gateway-module) ([Purchase Here](https://www.robotshop.com/products/acrome-arduino-gateway-shield-module-acrome-smd-products))

[BDC Motor](https://docs.acrome.net/electronics/electrical-motors/brushed-dc-motors-bdc) ([Purchase Here](https://www.robotshop.com/products/acrome-12v-brushed-dc-motor-with-built-in-encoder-100-rpm-speed))

## **Step 1: Hardware & Software Overview** <a href="#step-1-hardware-and-software-overview" id="step-1-hardware-and-software-overview"></a>

#### **Project Key Components**

1. [SMD Red](https://docs.acrome.net/electronics/smd-red): Controls the brushed DC motor and reads encoder feedback for accurate position control.
2. [BDC Motor](https://docs.acrome.net/electronics/electrical-motors/brushed-dc-motors-bdc): Converts electrical energy to mechanical rotation and provides position feedback via encoder signals.

#### **Project Key Features**

1. **Precise angle-based control**: Move the motor shaft to an exact angle between 0° and 360°.
2. **Real-time encoder feedback**: Monitor actual shaft position with high-resolution encoder data.
3. **Tunable PID position control**: Customize responsiveness and stability using:\
   `set_control_parameters_position`
4. **Encoder-integrated control loop**: Closed-loop control ensures accurate positioning over time.

## **Step 2: Assemble** <a href="#step-2-assemble" id="step-2-assemble"></a>

#### **Getting Started**

**Hardware Setup**

* Connect the SMD to the PC or Arduino board using [USB Gateway Module](https://acrome.gitbook.io/acrome-smd-docs/electronics/gateway-modules/usb-gateway-module) or [Arduino Gateway Module](https://acrome.gitbook.io/acrome-smd-docs/electronics/gateway-modules/arduino-gateway-module).
* Connect the 100 RPM [BDC Motor](https://docs.acrome.net/electronics/electrical-motors/brushed-dc-motors-bdc) with Encoder to the motor ports of the SMD Red.
* Make sure that the SMD is powered and all connections are correct.

#### **Project Wiring Diagram**

<figure><img src="https://1077748559-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LuxEcL3mxZNc5Aa92N6%2Fuploads%2F3fSkR64pSgDlFZHLQs28%2FBasic%20Motor%20Control%20Application%20Using%20PWM%20Input.png?alt=media&#x26;token=4a48a90d-2dbc-4b6a-bd52-c073e293a97d" alt=""><figcaption></figcaption></figure>

## Step 3: Run & Test <a href="#step-3-run-and-test" id="step-3-run-and-test"></a>

#### Run the Script

After launching the script, you will be prompted to enter a desired angle between `0` and `360` degrees.\
The motor will rotate to the target position using encoder feedback and stop.

## Codes

{% tabs %}
{% tab title="Python Code" %}
{% code lineNumbers="true" %}

```python
from smd.red import *  # Import SMD Red control library
from serial.tools.list_ports import comports  # For listing available serial ports
from platform import system  # To detect the operating system
import time  # For sleep/delay
import math  # For mathematical operations

# Automatically detect and return the correct USB port connected to SMD Red
def USB_Port():
    ports = list(comports())
    usb_names = {
        "Windows": ["USB Serial Port"],
        "Linux": ["/dev/ttyUSB"],
        "Darwin": ["/dev/cu."]  # macOS ports usually start with /dev/cu.
    }
    os_name = system()
    for port, desc, _ in ports:
        if any(name in port or name in desc for name in usb_names.get(os_name, [])):
            return port
    return None

def main():
    port = USB_Port()
    if not port:
        print("No port found.")  # If no port is found, exit the program
        return

    master = Master(port)  # Create a Master object to control SMD Red
    motor_id = 1  # ID of the connected motor
    CPR = 6533  # Encoder Counts Per Revolution

    # Attach the motor and configure its basic parameters
    master.attach(Red(motor_id))
    master.set_shaft_cpr(motor_id, CPR)  # Set encoder resolution
    master.set_shaft_rpm(motor_id, 100)  # Set nominal RPM
    master.set_control_parameters_position(motor_id, 0.5, 0.0, 20.0)  # Set PID gains for position control
    master.set_operation_mode(motor_id, OperationMode.Position)  # Enable position control mode
    master.enable_torque(motor_id, True)  # Enable torque so the motor can move

    while True:
        try:
            # Get target angle from user input and keep it in [0, 360) range
            angle = float(input("Enter target angle (0-360°): ")) % 360

            # Convert angle in degrees to encoder counts
            target = angle * (CPR / 360)

            # Send position command to the motor
            master.set_position(motor_id, target)

            # Wait for movement to complete
            time.sleep(0.5)

            # Read actual encoder position
            current = master.get_position(motor_id)

            # Convert encoder counts back to degrees
            actual = current * (360 / CPR)

            # Print both target and actual angles
            print(f"Target: {angle:.2f}°, Actual: {actual:.2f}°\n")

        except KeyboardInterrupt:
            # On user interrupt (Ctrl+C), disable torque and stop the motor
            master.enable_torque(motor_id, False)
            break

# Entry point of the script
if __name__ == "__main__":
    main()
```

{% endcode %}
{% endtab %}

{% tab title="Arduino Code" %}
{% code lineNumbers="true" %}

```cpp
#include <Acrome-SMD.h>
#define BAUDRATE 115200         // Serial communication speed
#define CPR 6533                // Counts per revolution of the encoder
#define ID 1                    // ID of the SMD Red module

Red master(ID, Serial, BAUDRATE);  // Create SMD Red object

void setup() {
  Serial.begin(115200);             // Start serial monitor
  master.begin();                   // Initialize communication with SMD Red
  master.torqueEnable(1);          // Enable motor torque
  master.setOperationMode(PositionControl);  // Set operation mode to Position Control
}

void loop() {
  // Read joystick X and Y values from module 1
  int joystickX = master.getJoystickX(1);
  int joystickY = master.getJoystickY(1);

  // If joystick is moved beyond dead zone
  if (abs(joystickX) > 10 || abs(joystickY) > 10) {
    // Calculate angle based on joystick direction
    float angle = atan2(joystickY / 100.0, joystickX / 100.0);
    float angleDegrees = fmod(degrees(angle) + 360.0, 360.0);  // Normalize angle to 0–360°
    
    // Convert angle in degrees to encoder position (CPR)
    int position = angleDegrees * (CPR / 360.0);

    master.setpoint(1, position);  // Send position command to motor

    // Debug output
    Serial.print("Target Angle: "); Serial.println(angleDegrees);
    Serial.print("Target CPR: "); Serial.println(position);
  }

  delay(100);  // Small delay for stability
}
```

{% endcode %}
{% endtab %}
{% endtabs %}
