Skip to content

6. Controlling a Continuous Rotation Servo Motor with PWM

This guide explains how to control a continuous rotation servo motor using Pulse Width Modulation (PWM) on the Raspberry Pi. Unlike standard servos that move to specific angles, continuous rotation servos spin continuously with controllable direction and speed.

Introduction to FS90R

The FS90R is a continuous rotation micro servo that allows for: - Full 360° continuous rotation in both directions - Speed control through PWM signal width - Compact size, making it ideal for small robotic projects - Easy control using the same PWM interface as standard servos

Required Hardware

  • Raspberry Pi (any model with hardware PWM support)
  • FS90R continuous rotation servo motor
  • Jumper wires
  • Breadboard (optional, but recommended)
  • External 5V power supply (recommended for reliable operation)

Hardware Setup

This guide uses GPIO 12 (PWM Channel 0) for servo connection:

  1. Connect the signal wire (orange for FS90R) to GPIO 12
  2. Connect the ground wire (brown for FS90R) to GND
  3. Connect the power wire (red for FS90R) to 5V
graph LR
    subgraph RPi["Raspberry Pi"]
        GPIO12["GPIO 12 (PWM)"]
        V5["5V"]
        GND["GND"]
    end

    subgraph FS90R["FS90R Continuous Servo"]
        Signal["Signal (Orange)"]
        Power["Power (Red)"]
        Ground["Ground (Brown)"]
    end

    GPIO12 --- Signal
    V5 --- Power
    GND --- Ground

    style FS90R fill:#9b59b6

Note: For more reliable operation, it's recommended to power the servo from an external 5V source while sharing common ground with the Raspberry Pi.

Continuous Rotation Servo Basics

The FS90R operates differently from standard servos:

  • PWM Control Range: 700μs to 2300μs
  • Stop Position: 1500μs ±45μs
  • Dead Band: 90μs (pulse range where no movement occurs)
  • Clockwise Rotation: Pulses between 1500μs and 700μs
  • Counter-Clockwise Rotation: Pulses between 1500μs and 2300μs
  • Speed Control: Proportional to how far the pulse width is from 1500μs

PWM Setup Configuration

Before writing code, you need to activate PWM on your Raspberry Pi:

Enabling PWM on GPIO Pins

  1. Edit the configuration file:

    sudo nano /boot/firmware/config.txt
    
  2. Add the following line at the end of the file:

    dtoverlay=pwm,pin=12,func=4
    
  3. Save the file (Ctrl+X, then Y, then Enter) and reboot:

    sudo reboot
    

Simple Code Example

This minimalist program demonstrates controlling the FS90R using PWM:

#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <csignal>

// Global flag for clean program termination
volatile bool running = true;

// Handle Ctrl+C
void signalHandler(int) {
    running = false;
}

// Write value to sysfs file
bool writeSysfs(const std::string& path, const std::string& value) {
    std::ofstream file(path);
    if (!file) return false;
    file << value;
    return file.good();
}

int main() {
    // Register signal handler
    std::signal(SIGINT, signalHandler);

    const std::string pwmChip = "/sys/class/pwm/pwmchip0";
    const std::string pwm = pwmChip + "/pwm0";

    // Setup PWM
    std::cout << "Setting up PWM..." << std::endl;
    writeSysfs(pwmChip + "/export", "0");
    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    writeSysfs(pwm + "/enable", "0");
    writeSysfs(pwm + "/period", "20000000");  // 20ms (50Hz)
    writeSysfs(pwm + "/duty_cycle", "1500000");  // 1.5ms = stop
    writeSysfs(pwm + "/enable", "1");

    std::cout << "FS90R Control Demo" << std::endl;
    std::cout << "Press Ctrl+C to exit" << std::endl;

    // Control loop
    while (running) {
        // Clockwise rotation (maximum speed)
        std::cout << "Clockwise rotation..." << std::endl;
        writeSysfs(pwm + "/duty_cycle", "700000");  // 0.7ms
        std::this_thread::sleep_for(std::chrono::seconds(2));
        if (!running) break;

        // Stop
        std::cout << "Stopping..." << std::endl;
        writeSysfs(pwm + "/duty_cycle", "1500000");  // 1.5ms
        std::this_thread::sleep_for(std::chrono::seconds(1));
        if (!running) break;

        // Counter-clockwise rotation (maximum speed)
        std::cout << "Counter-clockwise rotation..." << std::endl;
        writeSysfs(pwm + "/duty_cycle", "2300000");  // 2.3ms
        std::this_thread::sleep_for(std::chrono::seconds(2));
        if (!running) break;

        // Stop
        std::cout << "Stopping..." << std::endl;
        writeSysfs(pwm + "/duty_cycle", "1500000");  // 1.5ms
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    // Cleanup
    std::cout << "Cleaning up..." << std::endl;
    writeSysfs(pwm + "/enable", "0");
    writeSysfs(pwmChip + "/unexport", "0");

    return 0;
}

Compiling and Running

  1. Save the code to a file, e.g., fs90r_simple.cpp

  2. Compile the program:

    g++ -o fs90r_simple fs90r_simple.cpp -std=c++11 -pthread
    
  3. Run the program with root privileges:

    sudo ./fs90r_simple
    
  4. Press Ctrl+C to exit the program at any time.

How It Works

  1. PWM Initialization: Sets up PWM with a 20ms period (50Hz)
  2. Control Pattern: Cycles through clockwise rotation, stop, counter-clockwise rotation, and stop
  3. Direction Control:
    • 0.7ms pulse (700,000ns): Maximum clockwise speed
    • 1.5ms pulse (1,500,000ns): Stop position
    • 2.3ms pulse (2,300,000ns): Maximum counter-clockwise speed
  4. Safe Shutdown: Returns servo to stop position and cleans up resources when exiting

Troubleshooting

  1. Servo Not Moving: Check connections and power supply
  2. Permission Issues: Make sure to run with sudo
  3. Movement Problems: Some servos may need pulse width adjustments (try ±50μs)

Applications and Project Ideas

  • Small wheeled robots
  • Rotating platforms
  • Conveyor belts
  • Camera pan systems
  • Kinetic art projects