|
LightLib
PROS library for VEX V5: EKF/MCL localization, RAMSETE path following, high-level chassis API
|
This tutorial covers include/subsystems.hpp — the single header where every motor, sensor, and pneumatic on the robot is declared. If a subsystem isn't in this file, no other file can see it. If it is in this file, every .cpp automatically gets access to it without extra includes.
Robot code has two natural levels: "what hardware exists" (subsystems) and "what to do with it" (autons, opcontrol). LightLib keeps the first level in one place using inline globals:
Because they're inline, every translation unit that includes this header sees the same object — no extern, no separate .cpp definition. Add a motor here, use it in autons.cpp and main.cpp without touching anything else.
The drive chassis is the one exception: it's built in
main.cppfrom the#defines at the top of that file (so the motor-port macros are visible at construction time) and re-exposed here asextern Drive chassis;.
Three things to know:
pros::Motor(-12) is port 12, reversed. Don't hand-flip signs in your code — set it once at declaration and trust it everywhere else.MotorGroup runs multiple motors as one.** Calling Score.move(127) runs both motors in {17, 7} at full forward. You can still address them individually as Top and Bottom if needed (this is exactly what the template does for the scoring roller).pros::Motor constructor doesn't know — you set the gearset later with Intake.set_gearing(pros::E_MOTOR_GEAR_GREEN) if it's not the default red.set_brake_mode is the one most teams forget. A lift on COAST falls under gravity; a lift on HOLD stays put. The template's Lift snaps to a preset and needs HOLD — that's why user_initialize() in main.cpp sets it explicitly.
light::Piston is a thin wrapper around pros::adi::DigitalOut that remembers its toggle state. Two methods you'll use:
button_toggle is the magic — it tracks "was this button pressed last
loop?" so a held button doesn't rapid-fire toggle. The whole opcontrol loop in main.cpp is built around this:
Press B once → wings extend. Press B again → retract. Held B → no oscillation.
The pistons need to be in a known state at the start of every match. That's default_positions() in src/autons.cpp:
Called once from initialize(), before either autonomous() or opcontrol(). Put every piston in here with the state you want at the sound of the buzzer.
The template includes a higher-level subsystem — a lift that snaps to preset angles when the operator releases the joystick. The declaration shows the pattern for any custom mechanism:
How it works:
DIGITAL_UP/DIGITAL_DOWN held), it runs the motor manually.snap_angles_deg and runs a P-controller until it's within tolerance_deg, then holds.In opcontrol(), you feed it one signed input per loop:
The 0-port motor / sensor declarations are placeholders. Until you swap them for real ports, the lift is harmless: PROS treats port 0 as a no-op device.
kP** — too low, the lift creeps to the snap angle. Too high, it overshoots and oscillates. Start at 1.0–2.0.tolerance_deg** — how close is "close enough." 1° is tight; 3° is forgiving and won't hunt forever near the target.max_snap_speed** — clamps the motor output during snapping (out of 127). 80 is gentle; 127 is a slap.A single global the color-sort logic reads to decide which rings to reject. Set it at the top of your auton or in user_autonomous():
Not setting it leaves it as NEUTRAL (sort nothing), which is fine but means your color-sort code is doing nothing useful.
The template assumes a tank drive (left side / right side). If you're on a mecanum, X-drive, or H-drive instead, the bottom of subsystems.hpp has commented-out skeletons:
Uncomment ONE block, fill in the ports, and set DRIVE_TYPE in main.cpp to 4 (mecanum/X) or 5 (H-drive). Both expose a similar API — drive, strafe, turn_to, plus per-axis PID setters and an opcontrol(throttle, strafe, turn) call you put in your while loop.
You can run a holonomic drive alongside the tank chassis (e.g. a dev-bot with a strafe pod), but only one set of motors should answer to the joystick at a time. Use
DRIVE_TYPEto pick.
Suppose you want a two-motor flywheel on ports 13 and 14 (one reversed), runnable from R1 / R2.
1. Declare in subsystems.hpp:
2. Use in main.cpp opcontrol:
3. Use in autons.cpp:
That's it — three files touched, two of them only on lines you'd be writing anyway.
set(false) as your "go" call. Don't fight it in code by sprinkling !s; pick a convention and document it in the comment next to the declaration.MotorGroup({13, -14}) are independent of the signs you used for pros::Motor FlywheelBot(-14) above — the group rebuilds its own motor objects from the ports you give it. Pick one place to declare the reversal and don't double up.Wings non-inline. Almost always a copy-paste of an old extern declaration; delete the duplicate.