LST Robotics Logo

Learning • Sensors • Tech

LST Robotics

Lessons
← Back to C++ Coding Track

C++ Coding Track · Lesson 10

Simple Servo Movement

In this lesson we control a servo motor using a basic autonomous program. A servo doesn't spin continuously like a normal DC motor — it moves to a specific angle. That makes it useful for accurate movement in mechanisms like robotic arms, selectors, flaps, or steering systems.

Version 1 — Angles Stored as Constants

Version 1

We begin by setting up our constants in Constants.h. This is where we store values such as the servo port and the angles we want the servo to move to. Storing them as constants makes the code easier to understand and update later.

Constants.h

#pragma once
// Defines a namespace to hold constant values
namespace constant
{
  // Titan controller CAN ID
  static constexpr int TITAN_ID = 42;
  // Communication frequency for the Titan controller
  static constexpr int frequency = 15600;
  // PWM port used by the servo motor
  static constexpr int DIF_SERVO = 0;
  // Maximum servo angle in degrees
  static constexpr double SERVO_MAX_ANGLE = 300.0;
  // Servo positions for this example
  static constexpr double SERVO_START_ANGLE = 0.0;
  static constexpr double SERVO_TARGET_ANGLE = 300.0;
}

In Robot.h we create the servo object (telling the robot which PWM port the servo is on) and add a timer, because we want the servo to move based on time during autonomous.

Robot.h

#pragma once
#include <frc/TimedRobot.h>
#include <frc2/command/Command.h>
#include "RobotContainer.h"
#include <frc/Servo.h>
#include <frc/Timer.h>
#include "Constants.h"
class Robot : public frc::TimedRobot {
public:
  void RobotInit() override;
  void RobotPeriodic() override;
  void DisabledInit() override;
  void DisabledPeriodic() override;
  void AutonomousInit() override;
  void AutonomousPeriodic() override;
  void TeleopInit() override;
  void TeleopPeriodic() override;
  void TestPeriodic() override;
private:
  // Servo object connected to PWM port 0
  frc::Servo diffServo{constant::DIF_SERVO};
  // Timer used to control movement timing in autonomous
  frc::Timer m_timer;
};

In Robot.cpp, we set the servo to its starting angle in RobotInit() so it begins in a known position. In AutonomousInit() we reset and start the timer, and set the servo to the starting angle again. In AutonomousPeriodic() we check the timer: for the first 2 seconds we move to the target angle, then return to 0 — a simple back-and-forth movement.

Robot.cpp

#include "Robot.h"
void Robot::RobotInit()
{
  // Set the servo to its starting position when the robot first turns on
  diffServo.SetAngle(constant::SERVO_START_ANGLE);
}
void Robot::RobotPeriodic() {}
void Robot::DisabledInit() {}
void Robot::DisabledPeriodic() {}
void Robot::AutonomousInit()
{
  // Reset and start the timer at the beginning of autonomous
  m_timer.Reset();
  m_timer.Start();
  // Move the servo to the starting angle
  diffServo.SetAngle(constant::SERVO_START_ANGLE);
}
void Robot::AutonomousPeriodic()
{
  // For the first 2 seconds, move the servo to the target angle
  if (m_timer.Get() < 2.0)
  {
    diffServo.SetAngle(constant::SERVO_TARGET_ANGLE);
  }
  else
  {
    // After 2 seconds, return the servo to 0 degrees
    diffServo.SetAngle(constant::SERVO_START_ANGLE);
  }
}
void Robot::TeleopInit() {}
void Robot::TeleopPeriodic() {}
void Robot::TestPeriodic() {}
#ifndef RUNNING_FRC_TESTS
int main()
{
  return frc::StartRobot<Robot>();
}
#endif

Version 2 — Angle Set Directly in the Code

Version 2

In this version we don't rely on multiple constants or external inputs. Instead we define the angle and timing directly inside the autonomous code, making it very clear where changes should be made.

Constants.h (slightly changed)

// Constants.h
#pragma once
namespace constant
{
  static constexpr int TITAN_ID = 42;
  static constexpr int frequency = 15600;
  // PWM port for the servo
  static constexpr int DIF_SERVO = 0;
  static constexpr double SERVO_MAX_ANGLE = 300.0;
}

Robot.h

// Robot.h
#pragma once
#include <frc/TimedRobot.h>
#include <frc/Servo.h>
#include <frc/Timer.h>
#include <frc/smartdashboard/SmartDashboard.h>
#include "Constants.h"
class Robot : public frc::TimedRobot {
public:
  void RobotInit() override;
  void RobotPeriodic() override;
  void DisabledInit() override;
  void DisabledPeriodic() override;
  void AutonomousInit() override;
  void AutonomousPeriodic() override;
  void TeleopInit() override;
  void TeleopPeriodic() override;
  void TestPeriodic() override;
private:
  frc::Servo diffServo{constant::DIF_SERVO};
  frc::Timer m_timer;
};

Here RobotInit() is left empty because we handle setup inside autonomous. In AutonomousInit() we reset and start the timer and set the servo to 0 degrees. In AutonomousPeriodic() we define two values you can change: the target angle and how long to wait there.

Robot.cpp

#include "Robot.h"
void Robot::RobotInit()
{}
void Robot::RobotPeriodic()
{
}
void Robot::DisabledInit() {}
void Robot::DisabledPeriodic() {}
void Robot::AutonomousInit()
{
  m_timer.Reset();
  m_timer.Start();
  // Start at 0 degrees
  diffServo.SetAngle(0);
}
void Robot::AutonomousPeriodic()
{
  // ===== CHANGE YOUR ANGLE HERE =====
  double angle = 300.0;
  // ===== CHANGE YOUR WAIT TIME HERE =====
  double waitTime = 3.0;
  if (m_timer.Get() < waitTime)
  {
    diffServo.SetAngle(angle);
  }
  else
  {
    diffServo.SetAngle(0);
  }
}
void Robot::TeleopInit() {}
void Robot::TeleopPeriodic() {}
void Robot::TestPeriodic() {}
#ifndef RUNNING_FRC_TESTS
int main()
{
  return frc::StartRobot<Robot>();
}
#endif
Challenge. Try implementing this with two servo motors.
Wiring note. The servos are connected to a servo power block. The servos plug into the output side of the power block, and the control (input) cables run from the power block to the specific pins you select on the VMX-pi. You can find a detailed explanation of this setup in the training book.
← Control Panel Start & Stop Buttons Ultrasonic Sensor →