Position-Input Actuator Control with an Arduino

Precision Position Control: Building Serial-Responsive Actuator Systems with Arduino

Programming a microcontroller to achieve precise position control of an electric linear actuator represents one of the most rewarding projects for both DIY enthusiasts and engineering professionals. While off-the-shelf control boxes offer plug-and-play convenience, Arduino-based solutions provide unparalleled flexibility, allowing you to implement custom motion profiles, integrate sensors, and build sophisticated automation systems tailored to your exact specifications.

This tutorial presents a complete, field-tested Arduino sketch developed by Nicola Buccoliero, a mechanical engineer from Università Campus Bio-Medico in Rome, during collaborative research with the University of Pennsylvania. The code demonstrates how to implement serial-commanded position control using feedback actuators equipped with Hall effect sensors. By typing a target position into the Arduino Serial Monitor, you can command your actuator to move to any point along its stroke with repeatable accuracy—a fundamental building block for applications ranging from automated test equipment to robotic systems and adjustable furniture.

What makes this approach particularly valuable is its simplicity combined with robustness. The system reads feedback pulses from the actuator's internal sensor, counts position changes in real-time, and automatically determines the correct direction of travel. Whether you're building a standing desk, automated camera slider, or laboratory positioning stage, understanding this foundational control method will accelerate your project development.

How Do You Control a Linear Actuator with an Arduino?

Understanding Feedback Actuators and Position Sensing

Before diving into the code, it's essential to understand how feedback actuators enable position control. Unlike standard actuators that operate on simple power-on/power-off commands, feedback-equipped models incorporate internal sensors that provide real-time position data. Most commonly, these sensors are Hall effect devices that detect the magnetic field from a rotating magnet attached to the actuator's drive screw.

As the actuator extends or retracts, the internal screw rotates, causing the Hall sensor to output digital pulses—typically 1 to 10 pulses per millimeter of travel, depending on the lead screw pitch and gear ratio. By counting these pulses, your Arduino can determine both the actuator's current position and direction of movement. This feedback mechanism transforms an otherwise "dumb" motor into a precision positioning system without requiring expensive external encoders or complex installation.

The three-wire configuration typical of feedback actuators includes power positive, power negative (ground), and a feedback signal wire. The feedback signal is a 5V digital output that pulses high and low as the actuator moves, making it directly compatible with Arduino digital input pins without additional level-shifting circuitry. This plug-and-play compatibility is one reason Arduino has become the go-to platform for custom actuator control projects.

Hardware Requirements and Wiring Configuration

To implement this position control system, you'll need several key components. First, select an appropriate linear actuator with built-in feedback. FIRGELLI's feedback-equipped models are ideal for this application, offering force ratings from 10 lbs to over 400 lbs and stroke lengths from 2 to 36 inches, all with integrated position sensing.

The motor driver circuit requires an H-bridge capable of handling your actuator's current draw. Most linear actuators operate on 12V DC and draw 2-6 amps depending on load, so selecting a driver with adequate current rating is critical. Popular choices include the L298N dual H-bridge module or discrete MOSFET drivers for higher-current applications. The Arduino's PWM outputs (pins 10 and 11 in this code) control the H-bridge, allowing bidirectional motor control with variable speed capability.

Essential Wiring Connections

  • Arduino Pin 10 (motorPin1): Connects to H-bridge input 1 for forward direction control
  • Arduino Pin 11 (motorPin2): Connects to H-bridge input 2 for reverse direction control
  • Arduino Pin 3 (sensorPin): Connects to the actuator's feedback signal wire
  • Arduino 5V and GND: Power the H-bridge logic (motor power comes from separate 12V supply)
  • External 12V Power Supply: Connects to H-bridge motor power terminals and actuator power wires

Proper grounding is critical—ensure the Arduino ground and the power supply ground are connected together. This common ground reference prevents signal integrity issues and erratic behavior. Never attempt to power the actuator directly from the Arduino; the motor current far exceeds the Arduino's capability and will damage the board.

Complete Code Walkthrough and Functionality

The following Arduino sketch implements a robust position control system that responds to serial commands. The code uses a pulse-counting algorithm to track actuator position and automatically determines the direction needed to reach any target position.

// Input / Output Pins
const int motorPin1 = 10; // Pin to control the motor forward
const int motorPin2 = 11; // Pin to control the motor backward
const int sensorPin = 3; // Pin to read the sensor

// Variables
int speed = 255; // Adjust to change actuator speed (0-255)
int sensorValue = 0;
int previousSensorValue = 0;
int motorChangeCount = 0;
int lastStoppedCount = 0;
int targetNumber = 0;
bool motorEnabled = false;
bool waitForCommand = true;

void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(sensorPin, INPUT);
  Serial.begin(115200);
}

void loop() {
  if (!motorEnabled && Serial.available() > 0) {
    targetNumber = Serial.parseInt();
    if (targetNumber != 0) {
      Serial.print("Target number: ");
      Serial.println(targetNumber);
      motorEnabled = true;
      analogWrite(motorPin1, speed); // Move forward
      analogWrite(motorPin2, 0); // Stop
      
      // Set the counter to the stopped value, considering the difference between targetNumber and lastStoppedCount
      if (targetNumber > lastStoppedCount) {
        motorChangeCount = lastStoppedCount + 1; // Start from +1
      } else if (targetNumber < lastStoppedCount) {
        motorChangeCount = lastStoppedCount - 1; // Start from -1
      } else {
        motorChangeCount = lastStoppedCount; // Keep the same value
      }
      
      waitForCommand = true; // Wait for a new input from the serial
    }
  }
  
  if (motorEnabled) {
    sensorValue = digitalRead(sensorPin);
    
    if (sensorValue == 1 && previousSensorValue == 0) {
      if (motorChangeCount < targetNumber) {
        motorChangeCount++;
      } else if (motorChangeCount > targetNumber) {
        motorChangeCount--;
      }
      
      Serial.print("Change from 0 to 1: ");
      Serial.print(motorChangeCount);
      Serial.print(" - Target number: ");
      Serial.println(targetNumber);
    }
    
    previousSensorValue = sensorValue;
    
    if (motorChangeCount < targetNumber) {
      analogWrite(motorPin1, speed); // Move forward
      analogWrite(motorPin2, 0);
    } else if (motorChangeCount > targetNumber) {
      analogWrite(motorPin1, 0);
      analogWrite(motorPin2, speed); // Move backward
    } else {
      analogWrite(motorPin1, 0); // Stop the motor when the motor count corresponds to the target number
      analogWrite(motorPin2, 0);
      motorEnabled = false;
      Serial.println("Motor stopped. Awaiting a new number.");
      lastStoppedCount = motorChangeCount; // Store the value of motorChangeCount when the motor stops
      waitForCommand = true; // Wait for new instructions
    }
  }
}

// Code by Nicola Buccoliero

Initialization and Setup Phase

The setup() function configures the Arduino's pins and establishes serial communication at 115200 baud—a high-speed rate that ensures responsive command processing. The motor control pins (10 and 11) are set as outputs, while the sensor input (pin 3) is configured to read the feedback signal. This initialization happens once when the Arduino powers up or resets.

Serial Command Monitoring

The main loop continuously monitors the Serial port for incoming commands. When you type a number into the Serial Monitor and press enter, the parseInt() function extracts that integer value and assigns it to the targetNumber variable. This command-based interface makes the system intuitive to use and easy to integrate with higher-level control systems or user interfaces.

The code validates that the received number is non-zero before acting on it, preventing accidental null commands from triggering motion. Once a valid target is received, the system enables the motor and begins intelligent direction determination based on comparing the target with the current position.

Real-Time Position Tracking and Pulse Counting

The heart of the position control system is the edge detection algorithm that monitors the feedback sensor. By storing the previous sensor state and comparing it to the current reading, the code detects rising edges (transitions from 0 to 1) which indicate the actuator has moved one increment. This edge-detection approach is more reliable than simple state reading because it counts discrete movement events rather than continuous signal levels.

Each detected pulse increments or decrements the motorChangeCount variable depending on the direction of travel. The Serial output during motion provides real-time feedback, allowing you to observe the position counting in action—invaluable for debugging and understanding system behavior.

Automatic Direction Control and Motion Logic

The code's most elegant feature is its automatic direction determination. By continuously comparing motorChangeCount with targetNumber, the system decides whether to drive the motor forward, reverse, or stop. If the current count is less than the target, forward motion is commanded. If greater, reverse motion engages. When the counts match, both motor outputs are set to zero, halting the actuator precisely at the target position.

This comparative logic handles both extension and retraction commands with the same code path, eliminating the need for separate routines for different directions. The speed variable (set to 255 for full speed) can be reduced to slow motion for applications requiring gentler acceleration or increased precision, though lower speeds may result in reduced torque with some industrial actuators.

Understanding Initialization and Coordinate Systems

This code assumes the actuator begins at the fully retracted position, which the system treats as position "0". This home position establishes the coordinate system for all subsequent movements. When you command the actuator to position 1000, it will extend until the pulse counter reaches 1000, then stop. Commanding position 500 from there will cause retraction back to the 500-pulse mark.

An important feature is the system's ability to handle negative position values. If you initialize the code with the actuator partially or fully extended, you can input negative integers to command retraction beyond the startup position. For example, entering -1200 will cause the system to retract 1200 pulses from wherever it started. This flexibility is useful when you don't have a hard home position or want to establish zero at mid-stroke for bidirectional travel.

For applications requiring absolute position accuracy across power cycles, consider adding a limit switch at the fully retracted position. On startup, you could add a homing routine that retracts until the switch closes, then sets motorChangeCount to zero. This ensures consistent position reference regardless of where the actuator was when powered down.

Calibration, Speed Adjustment, and Performance Optimization

Every actuator model has a different pulse-per-millimeter ratio based on its lead screw pitch and gear reduction. To calibrate your system, manually extend the actuator a known distance (say, 100mm measured with a ruler), note the pulse count displayed in the Serial Monitor, and calculate the ratio. For a typical 12V linear actuator with 2mm lead screw pitch, you might see approximately 3-5 pulses per millimeter.

Adjusting Speed and Motion Characteristics

The speed variable (defaulted to 255) controls motor drive voltage through PWM. At 255, the motor receives full 12V power for maximum speed and force. Reducing this value slows motion—useful for delicate positioning or when moving fragile payloads. However, be aware that reducing speed below approximately 150 (depending on load) may cause the actuator to stall, as DC motors produce less torque at lower voltages.

For applications requiring different speeds for different moves, you could modify the code to accept two-part commands: a target position and a speed value. This would enable fast rough positioning followed by slow final approach for enhanced precision.

Reducing Overshoot and Improving Stop Accuracy

Mechanical inertia can cause the actuator to overshoot the target position by a pulse or two, especially at high speeds or with heavy loads. To minimize this, consider implementing a deceleration zone. When the position count comes within 50-100 pulses of the target, reduce the speed variable to perhaps 100-150, allowing the actuator to approach the final position more gradually. This two-stage speed profile significantly improves stop accuracy without excessively slowing the overall move.

Integration Strategies and Real-World Applications

This position control foundation can be extended for numerous automation projects. The serial command interface makes it straightforward to integrate with PC software, Raspberry Pi controllers, or even smartphone apps via Bluetooth modules. For multi-actuator systems like adjustable standing desks or camera gantries, multiple instances of this code can run on a single Arduino Mega, which offers enough pins to control several actuators simultaneously.

Synchronized Motion Control

When controlling multiple actuators that must move in coordination (such as a four-corner lifting platform), you'll need to expand the code to manage multiple position counters and motor driver outputs. The key is commanding all actuators to their target positions simultaneously, then checking all position counters in the main loop. Only when all actuators reach their targets should the system accept new commands, ensuring synchronized stopping.

Adding Limit Switches and Safety Features

Production applications should incorporate safety limit switches at both ends of travel to prevent over-extension or over-retraction in case of pulse counting errors. These switches can be wired to additional Arduino input pins with the code modified to immediately stop motor drive if either switch activates. This failsafe protects both the actuator and your mechanism from damage due to software errors or unexpected conditions.

Current sensing is another valuable safety addition. By monitoring motor current draw with a sensor like the ACS712, you can detect mechanical obstructions or binding. If current exceeds a threshold for more than a brief moment, the code can halt motion and flag an error—preventing damage and improving system reliability.

Troubleshooting Common Issues and Solutions

If your actuator doesn't respond to commands, first verify power connections. The most common issue is insufficient power supply capacity—actuators can draw peak currents of 5-8 amps during startup and stall conditions. A power supply rated for at least 5A continuous output is recommended for 12V actuators in the 50-200 lb force range.

Addressing Position Counting Errors

If the position count drifts over time or doesn't match actual actuator position, check the feedback signal wiring. Long wire runs can pick up electrical noise that causes false pulse detection. Using shielded cable for the feedback wire and routing it away from motor power wires reduces noise. Additionally, adding a small capacitor (0.1µF) between the sensor pin and ground can filter high-frequency noise.

Another cause of counting errors is marginal power supply voltage under load. As the actuator strains against heavy loads, voltage sag can cause the feedback sensor to malfunction. Monitoring supply voltage with a multimeter during operation can reveal if this is occurring—voltage should remain above 11.5V even under full load.

Fixing Erratic Motion or Unexpected Direction Changes

If the actuator randomly changes direction or behaves unpredictably, examine the H-bridge connections. Swapped motor polarity wires won't prevent operation but will invert the direction logic, making "forward" commands drive reverse and vice versa. The feedback system will still count pulses but the motor will fight against the target, causing oscillation. Simply swapping the two motor power wires at the H-bridge output corrects this.

Advanced Modifications and Feature Enhancements

Once you have the basic system operational, several enhancements can expand functionality. Implementing acceleration and deceleration ramps creates smoother motion profiles that reduce mechanical stress and improve positioning accuracy. This involves gradually increasing the speed variable from zero to the target speed over a defined number of pulses, then ramping down as the target approaches.

Adding Position Memory and Presets

Using the Arduino's EEPROM library, you can store frequently-used positions in non-volatile memory. This allows you to define preset positions (home, position 1, position 2, etc.) that persist across power cycles. A simple command protocol like "S1" to save current position as preset 1, and "G1" to go to preset 1, makes operation more user-friendly for repeated movements.

Implementing Closed-Loop PID Control

For applications requiring maximum precision, implementing a PID (Proportional-Integral-Derivative) control loop provides significant improvements. Instead of simple on/off motor control, PID varies the motor speed proportionally to the position error, reducing overshoot and settling time. Arduino PID libraries make implementation straightforward, though tuning the PID parameters requires some experimentation for optimal performance with your specific actuator and load.

Conclusion: Building on the Foundation

This position control system demonstrates the fundamental principles of motion control with feedback actuators and Arduino microcontrollers. By understanding pulse counting, edge detection, and directional logic, you've gained the foundation to build sophisticated automation systems tailored to your exact requirements. The code's simplicity makes it an excellent starting point, while its modular structure allows for extensive customization and enhancement.

Whether you're developing a one-off prototype or engineering a small production run, Arduino-based actuator control offers flexibility that pre-configured controllers cannot match. As you expand the system with additional sensors, safety features, and motion profiles, you'll find the development process both challenging and highly rewarding—exactly what makes motion control projects so engaging for engineers and makers alike.

Frequently Asked Questions

Which FIRGELLI actuators work with this Arduino code?

This code works with any FIRGELLI feedback actuator that includes a Hall effect position sensor. Models such as the feedback-equipped versions of our premium, industrial, and track actuators all provide the necessary feedback signal wire. The actuator's voltage rating (typically 12V) must match your power supply, and your H-bridge motor driver must be rated for the actuator's peak current draw—usually 3-6 amps for standard models and up to 10 amps for high-force industrial actuators. The stroke length, force rating, and speed are independent of the control code functionality, so you can select the mechanical specifications needed for your application while using this same control approach.

How do I determine the correct speed value for my application?

The speed variable controls PWM duty cycle from 0 (stopped) to 255 (full voltage). Start with 255 for maximum speed and force, then reduce gradually if you need slower motion. Most actuators maintain good torque down to speed values around 150-180, below which they may stall under load. For applications requiring precise positioning, consider a two-stage approach: use speed 255 for rapid transit, then switch to 100-150 when within 50-100 pulses of the target for smooth final approach. The optimal speed depends on your load weight, mechanical friction, and required positioning accuracy. Testing with your actual mechanism is the best way to determine ideal values—start high and reduce until motion characteristics meet your needs.

How do I convert pulse counts to actual distance measurements?

The pulse-to-distance ratio depends on your actuator's lead screw pitch and gear reduction. To calibrate, manually measure a known extension distance (use a ruler to measure 100mm or 4 inches), then divide the pulse count displayed in the Serial Monitor by that distance. For example, if extending 100mm shows 350 pulses, your ratio is 3.5 pulses/mm. You can then create lookup tables or equations to convert between pulses and real-world measurements. Store this ratio as a constant in your code, allowing you to command positions in millimeters rather than raw pulse counts. Note that this ratio is consistent for a given actuator model but may vary slightly between individual units due to manufacturing tolerances, so calibrating each actuator individually provides the best accuracy.

Can I control multiple actuators simultaneously with one Arduino?

Yes, but you'll need sufficient I/O pins and appropriate code modifications. An Arduino Uno has enough pins to control 2-3 actuators (each needs 2 motor control pins and 1 feedback pin), while an Arduino Mega can handle 10 or more. The code must be expanded to manage multiple position counters, motor enable flags, and target positions—essentially duplicating the tracking variables and motor control logic for each actuator. For synchronized motion where multiple actuators must move in coordination (like a four-corner lifting platform), command all actuators to their targets simultaneously, then check all position counters in the main loop, only accepting new commands when all have reached their targets. Consider using arrays to store position data and loops to process multiple actuators efficiently rather than duplicating code blocks.

What size power supply do I need for Arduino actuator control?

Your power supply must meet both voltage and current requirements. Most linear actuators operate on 12V DC, though some models use 24V—verify your specific actuator's rating. For current capacity, check the actuator's specifications for peak current draw, typically 3-8 amps depending on force rating and stroke speed. Choose a power supply rated for at least 150% of the peak current to provide headroom for startup surges and prevent voltage sag under load. For a single 12V actuator drawing 4 amps peak, a 12V 6A power supply is appropriate. Multiple actuators require summing the individual current requirements. The Arduino itself can be powered from the same 12V supply using a voltage regulator or separate 5V USB power—never power the actuator through the Arduino's output pins as the current will destroy the board.

How accurate is position control with this pulse-counting method?

Position accuracy depends on several factors: the actuator's pulses-per-millimeter ratio, mechanical backlash, load consistency, and control algorithm tuning. Typical feedback actuators provide 1-5 pulses per millimeter, meaning raw resolution of 0.2-1mm per pulse. Repeatable positioning accuracy is usually within ±2-3 pulses (approximately ±1mm for most actuators), though mechanical backlash can add 0.5-2mm variation when changing directions. For applications requiring higher precision, consider adding deceleration zones as the target approaches, implementing PID control algorithms, or selecting actuators with higher-resolution feedback sensors. Micro linear actuators with fine-pitch lead screws can achieve sub-millimeter accuracy with proper control implementation. For the highest precision, external linear encoders or linear guides with integral measurement systems provide resolution down to 0.01mm or better.

Share This Article
Tags: