Purdue Robotics Operating System  2b10
Simple, high-performance operating system for the VEX Cortex Microcontroller
 All Files Functions Typedefs Macros Pages
Welcome to the Purdue Robotics Operating System (PROS)!

Table of Contents

Overview and API Reference

PROS is a lightweight and fast alternative operating system for the VEX Cortex Microcontroller. It features multitasking, low-level control, and Wiring compatible functions to harness the full power of the Cortex.

Detailed, function by function PROS documentation can be found in the file references for API.h and main.h.

Installation

Windows

Table of Contents

Linux

Table of Contents

Mac OS X

Table of Contents

PROS License Agreement

Copyright (c) 2011-2013, Purdue University ACM SIG BOTS. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PURDUE UNIVERSITY ACM SIG BOTS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Table of Contents

Getting started

Table of Contents

Creating a new project

Table of Contents

Writing code

Table of Contents

Compiling the project

Table of Contents

Uploading the project

Table of Contents

Debugging and feedback

Table of Contents

Project management

Table of Contents

Updating a project

Projects by default remain on the version of PROS that was used to create them. Therefore, users of different versions of PROS will still run that project in the same way. However, individual old projects can be updated to incorporate new PROS features using the VEX > Switch project to PROS .... Check the confirmation dialog to ensure that the correct project is selected for upgrade!

switch_version.png
Update a project to another revision of PROS

PROS also automatically checks for new versions. This check occurs on startup and can be disabled by using Window > Preferences... (Windows/Linux) or Eclipse > Preferences (Mac), then selecting Startup and Shutdown under the General tab and deselecting PROS Project Manager and Update:

disable_update_check.png
Disabling the automatic update check

Table of Contents

PROS Tutorial

C Basics

PROS is a library built on C. This document does not aspire to teach beginners C. Excellent tutorials are available on the Internet by searching for "C" in a browser, bringing up results such as this tutorial on C.

Note that not all information presented in C tutorials is relevant to programming VEX robots with PROS. For example, pointers are used only occasionally, and there is little need for topics such as recursion and file I/O.

Table of Contents

PROS Project Structure

PROS projects are internally composed of three parts: the user code, the PROS library and the header files.

The PROS library is a single file containing the core PROS routines. This file does not need to be changed. If there appears to be an issue with a core PROS function, please file an issue on the PROS website.

The header files are all found in the include directory. One header file, API.h, is required to declare the PROS library functions, and also serves as a quick reference. The other file, main.h, is intended for declaring functions and variables shared between the user code files. New header files can be created in the include directory, as long as the name ends with .h.

new_header.png
Creating a new header file

User code has the actual sequential instructions that govern the robot's behavior. PROS by default splits the user code up into autonomous (auto.c), driver control (opcontrol.c), and initialization (init.c) files. Code in one file can talk to code in another file using declarations in the header files. New user code files can be created in the src directory, as long as the name ends with .c it will be compiled with the others.

new_source.png
Creating a new source file

All user code files should start with

#include "main.h"

to make the critical declarations in API.h and main.h available in each file.

pros_defaultproject.png
PROS default project structure

While more complicated than some environments, splitting up code grants powerful modularity and code reusability, especially when combined with Source control.

Table of Contents

Declaring Functions and Variables

Functions and variables must be declared before use, as described in many of the C Basics tutorials. Functions and variables can be declared in the typical C manner:

int sensorValue;
void DoIt(int x) {
motorSet(5, x);
}
int getSensorValue(void) {
DoIt(127);
return sensorValue;
}

Doing so will make the variables and functions local to the file in which they exist.

Some functions and variables are useful in more than one location (e.g. a function to use motorSet on all the robot's drive motors with the same value). Such functions can be declared in any of the three files (or a custom file in the src directory). To make them accessible to any other user code file, prototype the function in main.h.

Functions can be prototyped by copying the function's declaration line into main.h and replacing the function code with ;.

// Prototype the "getSensorValue()" function declared above (in main.h)
int getSensorValue(void);
// Prototype the "DoIt()" function declared above (in main.h)
void DoIt(int x);

Any function can be prototyped in main.h regardless of which user code file in which it appears. Variables are a little more difficult; the word extern must be placed in front of the declaration line when it is copied into main.h.

// Prototype the "sensorValue" variable declared above (in main.h)
extern int sensorValue;

For pieces of code intended for frequent reuse, it may be better to create a separate header file with the related declarations, and to include that file in main.h. Therefore, large pieces of code involving look-up tables, control algorithms, or other complex structures can be quickly ported between projects by copying one .c user code file and one .h header file.

library_files.png
Custom libraries with one header and one source file

Table of Contents

Examples and templates

Sensors

Most sensors follow a similar pattern. The sensor is initialized in initialize and used in multiple places, including autonomous and operatorControl. Typical sensor initialization code thus looks like:

Analog Yaw-Rate Gyro

init.c:

// Multiple gyros can be declared
Gyro gyro;
void initialize() {
// ...
// If gyro reads inaccurately, change "0" to desired sensitivity
// See documentation on gyroInit for up-to-date sensitivity details
gyro = gyroInit(gyro_port_number, 0);
// ...
}

main.h:

// Variable name matches gyro name(s) in init.c
extern Gyro gyro;

opcontrol.c or auto.c:

void ...() {
// ...
// Get gyro reading in degrees
int heading = gyroGet(gyro);
// Reset gyro to zero
gyroReset(gyro);
// ...
}

Quadrature Encoder

init.c:

// Multiple encoders can be declared
Encoder encoder;
void initialize() {
// ...
encoder = encoderInit(top_port_number, bottom_port_number, is_reversed);
// ...
}

main.h:

// Variable name matches encoder name(s) in init.c
extern Encoder encoder;

opcontrol.c or auto.c:

void ...() {
// ...
// Get encoder reading in degrees
int counts = encoderGet(encoder);
// Reset encoder to zero
encoderReset(encoder);
// ...
}

Table of Contents

Ultrasonic Sensor

init.c:

// Multiple ultrasonics can be declared
Ultrasonic sonar;
void initialize() {
// ...
sonar = ultrasonicInit(orange_port_number, yellow_port_number);
// ...
}

main.h:

// Variable name matches ultrasonic name(s) in init.c
extern Ultrasonic sonar;

opcontrol.c or auto.c:

void ...() {
// ...
// Get ultrasonic reading in centimeters
int distance = ultrasonicGet(sonar);
// ...
}

Table of Contents

Integrated Motor Encoders

init.c:

void initialize() {
// ...
// Check count to ensure all IMEs are plugged in!
int count = imeInitializeAll();
// ...
}

main.h:

// It may be wise to use #define to establish friendly IME names
// Closest IME to the Cortex (first on chain) gets #0, IME connected to that one gets #1,
// and so on
#define IME_LEFT_MOTOR 0
#define IME_RIGHT_MOTOR 1

opcontrol.c or auto.c:

void ...() {
// ...
// Get IME tick count in the "counts" variable
// (conversion to rotations varies depending on the motor type in use)
int counts;
imeGet(0, &counts);
// Or if #define was used:
imeGet(IME_LEFT_MOTOR, &counts);
// Reset IME to zero
imeReset(IME_RIGHT_MOTOR);
// ...
}

Table of Contents

Buttons, Limit Switches

init.c:

void initializeIO() {
// One of the few things done in initializeIO()
pinMode(switch_pin, INPUT);
}

opcontrol.c or auto.c:

void ...() {
// ...
// digitalRead() will return LOW if PRESSED and HIGH if RELEASED
if (digitalRead(switch_pin) == LOW) {
// ...
}
// ...
}

Table of Contents

Actuators

VEX Speaker

init.c:

void initialize() {
}

opcontrol.c or auto.c:

void ...() {
// ...
// Play a tune on the speaker
speakerPlayRtttl("Super Mario:d=4,o=5,b=100:16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,"
"8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,"
"8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,"
"8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,"
"8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,8p.,"
"8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,"
"8p,16d#6,8p,16d6,8p,16c6");
// Play a polyphonic tune on the speaker with 2 tracks
const char * rtttl[] = {
"Super Mario:d=4,o=5,b=100:"
"16e6,16e6,32p,8e6,16c6,8e6,8g6,8p,8g,8p,"
"8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,"
"8c6,16p,8g,16p,8e,16p,8a,8b,16a#,8a,16g.,16e6,16g6,8a6,16f6,8g6,8e6,16c6,16d6,8b,16p,"
"8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,"
"8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16c7,16p,16c7,16c7,8p.,"
"8p,16g6,16f#6,16f6,16d#6,16p,16e6,16p,16g#,16a,16c6,16p,16a,16c6,16d6,"
"8p,16d#6,8p,16d6,8p,16c6",
"Super Mario:d=4,o=4,b=100:"
"16d,16d,32p,8d,16d,8d,8g5,8p,8g,8p,"
"8g5,16p,8e,16p,8c,16p,8f,8g,16f#,8f,16e.,16c5,16e5,8f5,16d5,8e5,8c5,16a,16b,8g,16p,"
"8g5,16p,8e,16p,8c,16p,8f,8g,16f#,8f,16e.,16c5,16e5,8f5,16d5,8e5,8c5,16a,16b,8g,16p,"
"8c,16p,16g,8p,8c5,8f,16p,16c5,16c5,16c5,8f,"
"8c,16p,16e,8p,16g,16c5,p.,8g,"
"8c,16p,16g,8p,8c5,8f,16p,16c5,16c5,16c5,8f,"
"8c,16g#,8p,16a#,8p,16c5",
NULL
};
// ...
}

Table of Contents

Pneumatic Solenoids

init.c:

void initializeIO() {
// One of the few things done in initializeIO()
digitalWrite(solenoid_pin, LOW);
pinMode(solenoid_pin, OUTPUT);
}

opcontrol.c or auto.c:

void ...() {
// ...
// Change solenoid to HIGH or LOW
// The resulting pnematic motion varies depending on tube connections
digitalWrite(solenoid_pin, new_value);
// ...
}

Table of Contents

VEX Motors or Servos

opcontrol.c or auto.c:

void ...() {
// ...
// Turn motor on at given power level
// 0 is stop, -127 is full reverse, 127 is full forward
// For a VEX servo, rotates to a position depending on value
// 0 is centered, -127 is fully one way, 127 is fully the other way
motorSet(motor_port, new_value);
// ...
}

Table of Contents

VEX LCD

init.c:

void initialize() {
// ...
// Select "uart1" or "uart2" as appropriate
// ...
}

opcontrol.c or auto.c:

void ...() {
// ...
// Print formatted text to LCD (line #1) like printf
// supposing that "x" is an existing integer variable
lcdPrint(uart1, 1, "X is %d", x);
// Print plain text to LCD (much faster than lcdPrint)
lcdSetText(uart1, 2, "Hello World");
// ...
}

Table of Contents

Tasks

Tasks are a great tool to do multiple things at once, but they can be difficult to use properly. Each task has a priority and a stack size. Most of the time, tasks should have the default stack size of TASK_DEFAULT_STACK_SIZE. The higher the priority, the more crucial the task is considered; tasks of higher priority will always run in preference to lower priority tasks, unless the higher priority task is using delay() or some other waiting action.

Tasks are created using taskCreate(), which invokes a user function in the new task:

// This function will execute concurrently with other tasks on the system
// The parameter will receive the argument given in taskCreate()
void startTask(void *ignore) {
// ...
}
// Creates task which will call the function "startTask()" with default priority and
// stack size; the argument will be "NULL"

Table of Contents

Best Task Practices

Table of Contents

Synchronization

One problem which often beguiles tasks is synchronization. If two tasks try to read the same sensor or control the same motor at the same time, unexpected behavior may occur.

Tasks can be designed never to conflict over motors or sensors:

void Task1(void *ignore) {
// update motor 2 and 4
}
void Task2(void *ignore) {
// update motor 5 and 6
}

If this is impossible, PROS features two types of elements, mutexes and semaphores, that can be used to coordinate tasks. Mutexes stand for mutual exclusion; only one task can hold a mutex at any given time. Other tasks trying to use the mutex must wait for the first task to finish. For this reason, mutexes must always be released after use.

// Create a mutex
Mutex mutex = mutexCreate();
// Acquire the mutex; other tasks using this command will wait until mutex is released
// timeout can specify the maximum time to wait, or MAX_DELAY to wait forever
// If the timeout expires, "false" will be returned, otherwise "true"
mutexTake(mutex, timeout);
// Release the mutex for other tasks
mutexGive(mutex);

Semaphores are like signals - one task can take the semaphore to wait for a coordination signal from another task which gives it. If multiple tasks wait on the same semaphore, the highest priority one will continue per signal given.

// Create a semaphore
// Warning: semaphore starts available, so the first call to semaphoreTake will return
// immediately
Semaphore semaphore = semaphoreCreate();
// Waits for semaphore to be signalled
// timeout can specify the maximum time to wait, or MAX_DELAY to wait forever
// If the timeout expires, "false" will be returned, otherwise "true"
semaphoreTake(semaphore, timeout);
// Signals the semaphore
semaphoreGive(semaphore);

Table of Contents

Common tasks

Typical operator control loop

while (1) {
// Read joystick values
int joystick1 = joystickGetAnalog(1, 3);
int joystick2 = joystickGetAnalog(1, 4);
// Optionally process joystick values
// Send joystick outputs to motors
motorSet(2, joystick1);
motorSet(9, joystick2);
// Joystick and motor values refreshed every 20 ms
delay(20);
}
}

Run a particular function in a loop in a new task

// delta_time is in milliseconds, 1000 milliseconds per second
// Function calls will stop if robot is disabled or switched into another mode
taskRunLoop(functionName, delta_time);

Test autonomous without a competition switch

// Plug a limit switch or button into digital port 1
// If this button is being held down when the robot is turned on, autonomous will
// be run before switching to driver control
if (digitalRead(1) == LOW)
while (1) {
// ...
}
}

Table of Contents

PROS Coding FAQ

Build-time Problems

Table of Contents

Run-time Problems

Table of Contents

Advanced topics

Source control

Initial setup

Table of Contents

Use with an online service

Adding a project to source control

Table of Contents

Committing changes

Table of Contents

Using source control

Table of Contents

Using PROS without the PROS IDE

Using PROS on the command line

PROS can be used with just command line utilities, in Windows, Linux, and Mac OS X. Make sure to install the PROS IDE before trying, as important tools such as the ARM cross compiler are installed on the system path when installing the base PROS IDE. Any text editor can be used to edit source code and header files.

pros_cmdline.png
Using the make utility to compile PROS projects on the command line

Table of Contents

Using PROS with Visual Studio

A copy of Visual Studio and Windows is required. Visual Studio 2010 was used for this document, but the same or similar directions should apply to Visual Studio 2008 or 2013.

Table of Contents