Vertically Inclined: UTRA Hacks Closed Challenge Submission
A modular robot control system built with Arduino framework and PlatformIO for autonomous navigation and task execution across multiple stations.
Awarded 2nd Place Overall and 1st for Top Track Performance.
Devpost: https://devpost.com/software/vertically-inclined
This project provides a software control hub for a mobile robot platform designed to execute various station-based tasks. The architecture emphasizes clean separation of concerns, software-driven configuration, and deterministic behavior execution.
- Microcontroller: Arduino UNO R4 Minima
- Motor Driver: L298N dual H-bridge motor controller
- Sensors:
- Ultrasonic distance sensor (HC-SR04)
- Color sensor (TC3200)
- Actuators:
- Servo motor
- DC motors
- EN_A: 9
- IN1_A: 2
- IN2_A: 3
- EN_B: 10
- IN1_B: 4
- IN2_B: 5
- Ultrasonic TRIG: 11
- Ultrasonic ECHO: 12
UTRA-main/
├── include // Non-library header files
│ ├── constants.hpp // Pins and other constants
│ ├── devices.hpp // Device constructors
│ ├── README
│ └── stations.hpp
├── lib // Custom libraries
│ ├── Arm // Servo arm library
│ │ ├── arm.cpp
│ │ └── arm.hpp
│ ├── Chassis // Chassis library
│ │ ├── chassis.cpp
│ │ └── chassis.hpp
│ ├── ColorSensor // Color sensor library
│ │ ├── ColorSensor.cpp
│ │ └── ColorSensor.h
│ └── README
├── platformio.ini // External dependencies
├── README.md
├── src // Source code
│ ├── build
│ │ └── Debug
│ ├── main.cpp // main.cpp
│ └── stations // Station autonomous routines
│ ├── station_a.cpp
│ ├── station_b.cpp
│ ├── station_c.cpp
│ └── station_d.cpp
└── test
└── README
The following libraries are automatically managed by PlatformIO:
L298N(v2.0.3) - Motor driver controlDDBot(v1.1.3) - Differential drive control (wrapped by custom chassis class)Servo(v1.3.0) - Servo motor control (wrapped by custom arm class)NewPing(v1.9.7) - Ultrasonic sensor interface
- PlatformIO installed (via VSCode extension or CLI)
pio runpio run --target uploadpio device monitorTo change which station the robot executes:
- Open
src/main.cpp - Modify the
currentStationconstant:constexpr Station currentStation = Station::STATION_A; - Available stations:
Station::STATION_AStation::STATION_BStation::STATION_CStation::STATION_D
- Re-upload the code to the robot
No other code changes are required.
Each station has its own subdirectory with implementation and header files:
// Example: stations.hpp
void runStationA();
// Example: stations/station_a.cpp
#include "stations.hpp"
void runStationA() {
Serial.println("Running Station A");
// Move to blue tape
while (chassis.readColor() != ColorName::BLUE) {
chassis.followLine(ColorName::GREEN, false);
delay(50);
}
// ...
}Full doxygen documentation available for each method
void moveTank(int leftSpeed, int rightSpeed, int time = 0, bool stopAfter = true);
void stop();
void followLine(ColorName lineColor, bool followLeft, std::pair<int, int> speeds = {170, 255}, bool reverse = false);ColorName readColor();
ColorName getBufferedColor();
int readDistance();
std::queue<int> getDistanceReadings();- Software-Driven Configuration: Change behavior by modifying a single constant
- Modular Architecture: Each station is self-contained and isolated
- Deterministic Execution: All logic runs once in
setup(), not inloop() - Clean Separation: Hardware pins centralized in
constants.hpp, device initialization centralized indevices.hpp
- Forwards declare new method (e.g.
runStationE()) instations.hpp - Create new source file (e.g.
station_e.cpp) insrc/stations/with source implementation - Add enum value to
Stationinstations.hpp:STATION_E - Update switch statement in
main.cpp
The robot waits 2 seconds after setup() begins, allowing time for:
- Serial connection establishment
- Drivetrain pinmode setting
- Arm resetting
Monitor serial output at 115200 baud for debugging information.
This project is a winning submission of the UTRA (University of Toronto Robotics Association) 2026 hackathon.