Team 1790 Code Guide

Getting Started

Before you start coding, make sure you understand the basics of your codebase.

What You Have

Your robot code is split into three main files:

FilePurposeCan You Edit?
main.cppHardware setup, driver control, main program flowYES - Most of your work happens here
1790Autonomous.hAutonomous routines and mode selectionYES - Add and edit autonomous modes
1790Common.hHelper functions, status display, combosMOSTLY NO - Only add motor externs when adding new motors

Your First Task: Understand the Default Setup

Open main.cpp and find these sections:

1. Hardware Declarations (Lines ~40-60)

This is where all motors and sensors are defined. Currently set up:

2. Autonomous Modes (Lines ~90-95)

Currently three modes: Disabled, Near Score, Far Score. You'll add more here.

3. Driver Control (Lines ~115-160)

This is where button presses control the robot. Default controls:

Safe Zones for Editing:
  • Adding new motors in main.cpp hardware section
  • Changing button controls in usercontrol() function
  • Adding new autonomous routines in 1790Autonomous.h
  • Adjusting drive speeds and turn rates
DO NOT EDIT: Competition callbacks, pre_auton setup, main() function, or most of 1790Common.h. These are tested and working.

Testing Your Code

After making changes:

  1. Build your code in VS Code (Ctrl+Shift+B or Cmd+Shift+B)
  2. Check for errors in the terminal
  3. Download to robot
  4. Test on practice field - never test a new control scheme at competition

Understanding Code Structure

Your code follows a specific flow. Understanding this helps you know where to add new features.

Program Flow

1. Robot Powers On
   ↓
2. main() runs → Sets up competition callbacks
   ↓
3. pre_auton() runs ONCE
   - Calibrates IMU (required for accurate turns)
   - Sets up optical sensor
   - Displays status on controller
   ↓
4. Wait for Competition Switch or Field Control
   ↓
5a. AUTONOMOUS MODE                 5b. DRIVER CONTROL MODE
    - Runs for 15 seconds               - Runs for 1:45
    - Uses runSelectedAutonMode()       - Uses usercontrol() loop
    - No driver input allowed           - Reads joysticks/buttons every 20ms
    - Pre-programmed movements          - Responds to driver commands
   ↓                                   ↓
6. Match Ends → All motors stop

Key Concepts

1. Externs vs Declarations

When you add a new motor, you need TWO things:

// In main.cpp - DECLARE the motor (creates it)
motor catapultMotor = motor(PORT10, ratio18_1, false);

// In 1790Common.h - EXTERN the motor (lets other files use it)
extern motor catapultMotor;

Think of it like this: Declaration says "this motor exists," extern says "other files can use this motor."

2. Motor Groups

When multiple motors do the same job, group them:

// Individual motors
motor leftIntakeMotor = motor(PORT9, ratio6_1, false);
motor rightIntakeMotor = motor(PORT3, ratio6_1, false);

// Group them for easier control
motor_group intakeMotors = motor_group(leftIntakeMotor, rightIntakeMotor);

// Now control both at once
intakeMotors.spin(forward);  // Both motors spin forward

3. The allMotors Vector

This is critical for status monitoring. Every motor MUST be added here:

std::vector<MotorInfo> allMotors = {
  {&leftFrontMotor, 1},
  {&leftRearMotor, 15},
  // ... add every motor with its port number
};

This lets your code automatically check for disconnected or overheating motors.

Where to Add Your Code

What You're AddingWhere It GoesFile
New motor/sensor hardwareHardware setup section (~line 40)main.cpp
Motor externsSTUDENT EDIT HERE section (~line 18)1790Common.h
Driver button controlsInside usercontrol() loop (~line 130)main.cpp
New autonomous routineAdd function after runFarScoreAuton()1790Autonomous.h
New autonomous mode nameautonModeNames array (~line 92)main.cpp

Add & Control Motors

Motors are the core of your robot. Here's how to add new ones for mechanisms like lifts, catapults, or conveyors.

Step-by-Step: Adding a Conveyor Motor

Step 1 — Declare in main.cpp

// Add after intakeMotors section (around line 50)
motor conveyorMotor = motor(PORT10, ratio18_1, false);  // 18:1 for torque

// Add to allMotors vector for monitoring
std::vector<MotorInfo> allMotors = {
  {&leftFrontMotor, 1},
  {&leftRearMotor, 15},
  {&rightFrontMotor, 8},
  {&rightRearMotor, 19},
  {&leftIntakeMotor, 9},
  {&rightIntakeMotor, 3},
  {&conveyorMotor, 10},  // ADD THIS LINE
};

Step 2 — Add extern in 1790Common.h

// In STUDENT EDIT HERE section (around line 18)
extern motor leftFrontMotor;
extern motor leftRearMotor;
extern motor_group leftDriveMotors;  
extern motor rightFrontMotor;
extern motor rightRearMotor;
extern motor_group rightDriveMotors; 
extern motor leftIntakeMotor;
extern motor rightIntakeMotor;
extern motor_group intakeMotors;
extern motor conveyorMotor;  // ADD THIS LINE

Step 3 — Control in Driver Mode

// In main.cpp, inside usercontrol() while loop (around line 140)

// Set default velocity after motor declaration
conveyorMotor.setVelocity(60, percent);  // Slower for control
conveyorMotor.setStopping(hold);  // Hold position when stopped

// Button control example - Up/Down on D-pad
if (ControllerDriver.ButtonUp.pressing()) {
    conveyorMotor.spin(forward, 60, percent);  // Move blocks up
} else if (ControllerDriver.ButtonDown.pressing()) {
    conveyorMotor.spin(reverse, 40, percent);  // Reverse if needed
} else {
    conveyorMotor.stop(hold);  // Hold position
}

Step 4 — Use in Autonomous

// In 1790Autonomous.h, inside an auton function
void runNearScoreAuton() {
  Brain.Screen.print("Running Near Score");
  
  // Drive to goal
  smartDrive.driveFor(forward, 24, inches);
  
  // Run conveyor to score
  conveyorMotor.spinFor(forward, 2.0, seconds);  // Score for 2 seconds
  
  // Back away
  smartDrive.driveFor(reverse, 12, inches);
}

Example: 4-Motor Lift System

For scoring in the Center Goal's upper section, you might need a lift:

// In main.cpp - Declare all lift motors
motor leftLiftFront = motor(PORT11, ratio18_1, true);   // Reversed
motor leftLiftRear = motor(PORT12, ratio18_1, true);
motor rightLiftFront = motor(PORT13, ratio18_1, false);
motor rightLiftRear = motor(PORT14, ratio18_1, false);

// Group for synchronized control
motor_group liftMotors = motor_group(leftLiftFront, leftLiftRear, 
                                     rightLiftFront, rightLiftRear);

// Add all four to allMotors vector
{&leftLiftFront, 11}, {&leftLiftRear, 12}, 
{&rightLiftFront, 13}, {&rightLiftRear, 14},

// In 1790Common.h - Add externs
extern motor leftLiftFront;
extern motor leftLiftRear;
extern motor rightLiftFront;
extern motor rightLiftRear;
extern motor_group liftMotors;

// In usercontrol() - Control with left shoulder buttons
liftMotors.setStopping(hold);  // IMPORTANT: Hold prevents lift from falling
liftMotors.setVelocity(50, percent);  // Slower for heavy lifts

if (ControllerDriver.ButtonL1.pressing()) {
    liftMotors.spin(forward);  // Lift up
} else if (ControllerDriver.ButtonL2.pressing()) {
    liftMotors.spin(reverse);  // Lift down
} else {
    liftMotors.stop(hold);  // Hold position (prevents drop)
}
Brake Modes Explained:
  • coast: Motor spins freely (good for intakes)
  • brake: Motor resists motion (good for drivetrains)
  • hold: Motor actively maintains position (REQUIRED for lifts/arms)

Official Docs: vex::motormotor_group

Add & Control Digital Outputs

Digital outputs control pneumatics, LEDs, or relays. They're simple on/off devices.

Example: Pneumatic Block Descore Arm

// In main.cpp - Declare pneumatic solenoid
digital_out blockDescoreArm = digital_out(PORT21, false);  // Starts open (false)

// In 1790Common.h - Add extern
extern digital_out blockDescoreArm;

// In usercontrol() - Toggle with X button
static bool clampClosed = false;
static bool prevXState = false;

if (ControllerDriver.ButtonX.pressing() && !prevXState) {
    clampClosed = !clampClosed;  // Toggle state
    blockDescoreArm.set(clampClosed);  // true = close, false = open
    
    // Haptic feedback for driver
    if (clampClosed) {
        ControllerDriver.rumble("..");  // Short rumble = closed
    } else {
        ControllerDriver.rumble(".");   // Single pulse = open
    }
}
prevXState = ControllerDriver.ButtonX.pressing();  // Track for next loop
Pneumatics Best Practices:
  • Always test for air leaks before matches
  • Use toggle control (not hold) to save air
  • Add rumble feedback so driver knows state
  • Consider LED indicators for complex mechanisms

Official Docs: vex::digital_out

Driver Controls

Current Default Setup

ControlActionCode Name
Left Stick YForward / BackwardAxis3
Right Stick XTurn (Arcade)Axis1
R1Intake IN (hold)ButtonR1
L2Intake OUT (hold)ButtonL2
R2 (press)Toggle continuous intakeButtonR2

Customizing Controls

Example: Precision Mode (Half Speed)

Useful for precise positioning near Goals:

// Add this in usercontrol() loop
static bool precisionMode = false;

if (ControllerDriver.ButtonLeft.pressing()) {
    precisionMode = true;
    smartDrive.setDriveVelocity(40, percent);   // Half speed
    smartDrive.setTurnVelocity(40, percent);
} else {
    precisionMode = false;
    smartDrive.setDriveVelocity(80, percent);   // Full speed
    smartDrive.setTurnVelocity(80, percent);
}

Example: Smart Intake with Optical Sensor

Stop intake automatically when Block is detected:

// In usercontrol() - replaces basic R1 control
if (ControllerDriver.ButtonR1.pressing()) {
    intakeMotors.spin(forward);
    
    // Stop automatically when a team-color Block is fully loaded (wait up to 5 sec)
    if (isTeamColorDetected(5000)) {
        intakeMotors.stop();  // Block loaded – stop intake
        ControllerDriver.rumble(".");  // Success feedback
    }
} else if (ControllerDriver.ButtonL2.pressing()) {
    intakeMotors.spin(reverse);  // Manual outtake
} else {
    intakeMotors.stop();
}
Testing Controls: Have your driver practice on the practice field for at least 30 minutes before changing any controls. Bad controls at competition waste time.

Official Docs: smartdrive::arcade

Autonomous Mode Selection

Before every match, you'll select which autonomous routine to run, your alliance color, and starting position.

Selection Process

Use these button combinations on the driver controller:

ComboActionDisplay Updates
Up + XCycle autonomous mode"NEAR" → "FAR" → "OFF"
Right + YToggle alliance color"RED" ↔ "BLUE"
Down + BToggle starting side"LEFT" ↔ "RIGHT"
CRITICAL TIMING: Make all selections BEFORE connecting to field control. Once connected, selections are locked to prevent accidental changes. The controller will show "LOCKED FIELD CTRL" if you try to change after connecting.

Understanding Selection Display

The controller screen shows your current settings:

Line 1:  NEAR | RED | LEFT
         ^^^^   ^^^   ^^^^
         Mode   Color Side

What Each Setting Does

Automatic IMU Calibration

After making any selection changes, wait 5 seconds. The robot automatically recalibrates the IMU (inertial sensor) to ensure accurate turns. You'll see "WAIT: Calibrating IMU" on the brain screen.

Match Day Checklist:
  1. Power on robot (but don't connect to field yet)
  2. Check controller screen for current settings
  3. Use combos to set correct mode, color, and side
  4. Wait 5 seconds for IMU calibration
  5. Verify "READY FOR MATCH!" appears (no errors)
  6. Connect to field control

Common Selection Mistakes

Programming Autonomous

Autonomous is 15 seconds of pre-programmed movement. This section shows you how to create and modify autonomous routines.

Current Autonomous Modes

Your code has three modes by default:

Adding a New Autonomous Mode

Let's add a "Park Only" mode for safe points:

Step 1 — Update numberOfAutonModes

// In main.cpp, around line 90
const int numberOfAutonModes = 4;  // Changed from 3 to 4

Step 2 — Add Mode Name

// In main.cpp, around line 93
std::string autonModeNames[numberOfAutonModes] = {
  "Disabled",
  "Near Score",
  "Far Score",
  "Park Only"  // ADD THIS
};

Step 3 — Create Function in 1790Autonomous.h

// Add forward declaration at top (around line 18)
void runNearScoreAuton();
void runFarScoreAuton();
void runParkOnlyAuton();  // ADD THIS

// Add case in switch statement (around line 33)
void runSelectedAutonMode() {
  switch (selectedAutonMode) {
    case 0:
      Brain.Screen.print("Auton Disabled");
      wait(15000, msec);
      break;
    case 1:
      runNearScoreAuton();
      break;
    case 2:
      runFarScoreAuton();
      break;
    case 3:  // ADD THIS CASE
      runParkOnlyAuton();
      break;
    default:
      Brain.Screen.print("Unknown Auton");
      wait(15000, msec);
      break;
  }
  smartDrive.stop();
}

// Add function definition at bottom of file
void runParkOnlyAuton() {
  Brain.Screen.print("Park Only");
  smartDrive.setDriveVelocity(60, percent);
  
  // Drive straight to Park Zone
  smartDrive.driveFor(forward, 48, inches);
  
  // Done - robot is parked
}

Example: Complex Scoring Routine

Here's a more advanced autonomous that scores multiple Blocks:

void runMultiScoreAuton() {
  Brain.Screen.print("Multi-Score Routine");
  smartDrive.setDriveVelocity(70, percent);
  smartDrive.setTurnVelocity(50, percent);
  
  // 1. Grab pre-loaded Block
  intakeMotors.spinFor(forward, 0.5, seconds);
  
  // 2. Drive to nearest Goal
  smartDrive.driveFor(forward, 30, inches);
  
  // 3. Turn to face Goal (mirrored for side/color)
  smartDrive.turnFor(rightTurnDirection(), 45, degrees);
  
  // 4. Score Block
  intakeMotors.spinFor(reverse, 1.0, seconds);  // Outtake
  
  // 5. Back up to grab another Block
  smartDrive.driveFor(reverse, 20, inches);
  
  // 6. Turn toward second Block
  smartDrive.turnFor(leftTurnDirection(), 90, degrees);
  
  // 7. Drive and intake
  intakeMotors.spin(forward);  // Start intake
  smartDrive.driveFor(forward, 24, inches);
  wait(500, msec);  // Let Block settle
  intakeMotors.stop();
  
  // 8. Return to Goal and score again
  smartDrive.turnFor(rightTurnDirection(), 90, degrees);
  smartDrive.driveFor(forward, 20, inches);
  intakeMotors.spinFor(reverse, 1.0, seconds);
  
  // 9. Park
  smartDrive.driveFor(reverse, 10, inches);
}
Autonomous Strategy Tips:
  • Start simple - one Block scored is better than zero
  • Use rightTurnDirection() and leftTurnDirection() so your auton works from both sides
  • Test every auton 10+ times before competition
  • Have a backup mode (like Park Only) in case primary fails
  • Practice with field tiles - carpet vs tiles changes friction
Autonomous Win Point: Check the game manual for specific tasks needed to earn the Autonomous Win Point. This is worth fighting for - it's a tiebreaker in rankings.

SmartDrive Movement

SmartDrive uses the inertial sensor for accurate straight lines and turns. This is essential for reliable autonomous.

Basic Movement Commands

Drive Forward/Backward

// Drive 24 inches forward
smartDrive.driveFor(forward, 24, inches);

// Drive 18 inches backward
smartDrive.driveFor(reverse, 18, inches);

// Drive 2 feet forward (same as 24 inches)
smartDrive.driveFor(forward, 2, feet);

Turn in Place

// Turn 90 degrees right
smartDrive.turnFor(right, 90, degrees);

// Turn 45 degrees left
smartDrive.turnFor(left, 45, degrees);

// Turn 180 degrees to reverse direction
smartDrive.turnFor(right, 180, degrees);

Set Speed Before Movement

// Slower for precision near Goals
smartDrive.setDriveVelocity(40, percent);
smartDrive.driveFor(forward, 6, inches);  // Careful approach

// Faster for crossing field
smartDrive.setDriveVelocity(90, percent);
smartDrive.driveFor(forward, 48, inches);  // Speed run

Advanced: Non-Blocking Movement

Sometimes you want to do something WHILE driving:

// Start driving (doesn't wait)
smartDrive.drive(forward, 80, percent);

// Spin intake while driving
intakeMotors.spin(forward);
wait(2000, msec);  // Drive and intake for 2 seconds

// Stop both
smartDrive.stop();
intakeMotors.stop();

Mirrored Turns for Alliance/Side

Use these helper functions so your auton works from any starting position:

// Always turns "right" relative to your starting position
smartDrive.turnFor(rightTurnDirection(), 90, degrees);

// Always turns "left" relative to your starting position
smartDrive.turnFor(leftTurnDirection(), 45, degrees);

These functions automatically flip the turn direction based on:

Tuning for Your Robot

If your robot doesn't drive straight or turn accurately:

Check Wheel Measurements

// In main.cpp, around line 60 - measure YOUR robot
double wheelDiameter = 3.25;  // Measure wheel size in inches
double wheelBase = 12.25;     // Distance between left/right wheels
double trackWidth = 12.25;    // Distance between front/rear wheels

Calibrate on Field

  1. Place robot on field tiles
  2. Mark starting position
  3. Run: smartDrive.driveFor(forward, 48, inches);
  4. Measure actual distance traveled
  5. If off by more than 1 inch, adjust wheelDiameter
Why SmartDrive is Better: Without the inertial sensor, small motor differences cause veering. SmartDrive auto-corrects, keeping your robot straight. Always use SmartDrive in autonomous.

Official Docs: vex::smartdrive

Optical Sensor (Color Detection)

The optical sensor detects colors - useful for identifying team vs opponent Blocks.

Basic Detection

Your code has helper functions in 1790Common.h:

Check for Team Block

// Returns true if sensor sees your alliance's color
if (isTeamColorDetected()) {
    // Do something with team Block
    intakeMotors.spin(forward);
}

// Or wait up to 1 second for team color
if (isTeamColorDetected(1000)) {
    Brain.Screen.print("Found team Block!");
}

Check for Opponent Block

// Returns true if sensor sees opponent's color
if (isOpponentColorDetected()) {
    // Reject opponent Block
    intakeMotors.spin(reverse);
    wait(500, msec);
    intakeMotors.stop();
}

Example: Smart Intake with Sorting

// In usercontrol() - replace basic R1 control
if (ControllerDriver.ButtonR1.pressing()) {
    intakeMotors.spin(forward);
    
    // Check for opponent Block
    if (isOpponentColorDetected()) {
        // Reject opponent Block
        intakeMotors.spin(reverse, 100, percent);
        wait(500, msec);  // Reverse for 0.5 seconds
        ControllerDriver.rumble("- -");  // Alert driver
    }
    
    // Check for team Block loaded
    if (isTeamColorDetected()) {
        intakeMotors.stop();  // Stop when loaded
        ControllerDriver.rumble(".");  // Single pulse = success
    }
} else {
    intakeMotors.stop();
}

Example: Autonomous Block Detection

void runSmartScoreAuton() {
  Brain.Screen.print("Smart Score Routine");
  
  // Approach Block area
  smartDrive.driveFor(forward, 24, inches);
  
  // Start intake and look for team color
  intakeMotors.spin(forward);
  
  if (isTeamColorDetected(2000)) {  // Wait up to 2 seconds
    // Found team Block - keep it
    wait(500, msec);  // Let it settle
    intakeMotors.stop();
    
    // Drive to Goal and score
    smartDrive.driveFor(forward, 30, inches);
    intakeMotors.spinFor(reverse, 1.0, seconds);
  } else {
    // Didn't find team Block - abort
    intakeMotors.stop();
    Brain.Screen.print("No team Block found");
  }
}

How the Optical Sensor Works

The sensor measures "hue" - a number from 0-359 representing color:

Raw Hue Reading

// Get raw hue value
double hue = opticalSensor.hue();
Brain.Screen.print("Hue: %.1f", hue);

// Check if object is close enough
if (opticalSensor.isNearObject()) {
    Brain.Screen.print("Object detected");
}
Sensor Positioning: Mount the optical sensor 1-2 inches from where Blocks pass through intake. Too far = unreliable detection. Too close = Blocks jam sensor.
Lighting Matters: Optical sensors can struggle under bright gym lights or in dim areas. Test your detection logic at your competition venue if possible.

Official Docs: vex::optical

Troubleshooting

When something goes wrong, start here. Most issues have quick fixes.

Build Errors

Error: "motor was not declared in this scope"

Cause: You forgot to add the extern in 1790Common.h

Fix: Add extern motor motorName; in the STUDENT EDIT section of 1790Common.h

Error: "conflicting declaration"

Cause: You declared the same motor twice, or didn't match main.cpp and 1790Common.h

Fix: Check that motor names are identical in both files. Check spelling.

Error: "numberOfAutonModes does not match array size"

Cause: You added a mode name but didn't increase numberOfAutonModes

Fix: Count your mode names and update the number. If you have 4 names, use const int numberOfAutonModes = 4;

Controller Screen Errors

"[IMU] Missing"

Cause: Inertial sensor not connected or wrong port

Fix: Check port 16 wiring. IMU must be firmly clicked in. Try different cable.

"Hot Motors" or "Warm Motors"

Cause: Motors overheating from stalling or high load

Fix:

"Motor Disconnected - Ports: X,Y"

Cause: Motor unplugged or bad connection

Fix: Reconnect motors on listed ports. Check for damaged cables.

Robot Behavior Issues

Robot Drives in Circles

Cause: Motors reversed incorrectly

Fix: In main.cpp, flip the true/false on left or right motors:

// If robot veers right, try reversing left motors
motor leftFrontMotor = motor(PORT1, ratio6_1, true);   // Change to true
motor leftRearMotor = motor(PORT15, ratio6_1, true);

Autonomous Drives Wrong Distance

Cause: Incorrect wheel diameter setting

Fix: Measure your wheel and update in main.cpp:

double wheelDiameter = 3.25;  // Your actual wheel size

Test by commanding 48 inches and measuring. Adjust diameter until accurate.

Turns are Inaccurate

Cause: IMU not calibrated or wheelBase/trackWidth wrong

Fix:

  1. Ensure IMU calibrated (wait 5 seconds after power on)
  2. Measure wheelBase and trackWidth precisely
  3. Update values in main.cpp
  4. Test on field surface (carpet vs tiles affects turns)

Intake Doesn't Grab Blocks

Cause: Speed too low, wrong direction, or mechanical issue

Fix:

Optical Sensor Not Detecting Colors

Cause: Too far from object, light not on, or ambient lighting

Fix:

// Ensure light is on (should be in pre_auton)
opticalSensor.setLight(ledState::on);
opticalSensor.setLightPower(100, percent);

// Check if object is near
if (opticalSensor.isNearObject()) {
    Brain.Screen.print("Object detected at %.1f hue", opticalSensor.hue());
}

Position sensor 1-2 inches from where Blocks pass.

Competition Day Issues

"LOCKED FIELD CTRL" When Trying to Change Settings

Cause: Already connected to field control

Fix: Disconnect from field, change settings, wait for IMU calibration, reconnect

Autonomous Doesn't Run

Possible causes:

Battery Dies Too Fast

Causes: Too many motors, motors stalling, battery old

Fix:

Getting Help

If you're stuck:

  1. Check the error message carefully - it usually tells you exactly what's wrong
  2. Search the VEX Forums - others have solved your problem
  3. Ask your teammates - fresh eyes help
  4. Check VEX API docs for correct function usage
  5. Ask other teams at competition - we all help each other
Pro Tip: Keep a "known good" version of your code backed up. If you break something, you can always revert. Use Git or copy files regularly.