The Common Chicken Runtime Engine v3.4.0
The CCRE solves the problem of writing elegant and maintainable robot software by using a dataflow model and taking care of the infrastructure for your project so that you can focus on the important parts of your code.
Here’s an example of a robot piloted with Arcade Drive:
Drive.arcade(FRC.joystick1,
FRC.talon(1, FRC.MOTOR_FORWARD),
FRC.talon(2, FRC.MOTOR_REVERSE));
Or, something more interesting: an example of a shifting drive train:
BooleanOutput shifter = FRC.makeSolenoid(2);
shifter.setFalseWhen(FRC.startTele);
shifter.setTrueWhen(FRC.joystick1.onPress(3));
shifter.setFalseWhen(FRC.joystick1.onPress(1));
Features:
Dataflow-based programming
Modern framework for FRC robot code
An emulator to test robot code without a robot
A high-quality publish-subscribe networking system
Robust error handling
No dependency on WPILib plugins
Here’s what you’ll find in this document:
1 Quickstart Guide
1.1 Prerequisites
You have to know how to program in Java. (You don’t have to know all of the ins-and-outs of Java, but you have to have a solid basic understanding.)
You have to have the latest version of Eclipse installed.
You need to have the Java 8 JDK (Java Development Kit) installed. Do not install the JRE! It won’t have everything you need. Install the JDK.
1.2 Installing the CCRE
Short version: import all of the projects from the CCRE repository into Eclipse and Build All.
Long version:
Download the latest CCRE release either as a tar.gz or a zip depending on your system. You can also see all downloads here.
Extract that archive into a directory of your choice.
Open Eclipse and select a new workspace. (Selecting a new workspace is optional if this is your first installation of the CCRE.)

Go to File -> Import... and select Existing Projects into Workspace.

Choose the directory where you extracted the archive to.

Select all of the CCRE projects and press finish.

Press Project -> Build All.

1.3 Creating a new project
Creating a new project is easy.
Simply copy and paste the TemplateRobot project, and give it a new name like MyFirstRobot.



Open src/robot/RobotTemplate.java in your new project.

Replace 0000 with your team number.

Now press Project -> Build Project, and once it finishes, you’re done!
After the first time, your project will be rebuilt every time you deploy the code to the robot.

1.4 Basic code
We’ll start with something simple. You should see this code in your project:
@Override
public void setupRobot() {
// Robot setup code goes here.
}
Logger.info("Hello, World!");
Now, let’s test this code in the emulator. Open the dropdown next to the External Tools button and select "MyFirstRobot Emulate."

If the emulator doesn’t appear properly, close it and rebuild the entire CCRE by pressing Project -> Clean... -> Clean All Projects -> OK and then Project -> Build All.
[INFO] (RobotTemplate.java:20) Hello, World!
Example:

If so, congratulations! You appear to have installed the CCRE correctly and written a very simple program!
Of course, that’s not particularly interesting, so let’s move on to actual motors and joysticks.
1.5 Single Joystick, Single Motor
Let’s set up a Talon speed controller and control it with the Y axis of a Joystick.
FloatOutput motor = FRC.talon(0, FRC.MOTOR_FORWARD);
FloatInput yAxis = FRC.joystick1.axisY();
yAxis.send(motor);
You will need to import all of the classes referenced by this piece of code. Eclipse has an easy way to do this:

You can also press Control+1 while your (keyboard) cursor is over the error to pop up the window faster.

Import the rest of the missing classes and then you can run your program in the emulator!

X = 1, Y = 2.
You could try to drag this while the robot is DISABLED, but the motor wouldn’t be able to change.

1.6 Running your code on a real robot
(If you don’t want to run your code on a real robot yet, skip this section and come back later.)
The kinds of Speed Controllers are:
Talons (latest)
Jaguars
Victors (oldest)
For the current example, the motor port number was chosen arbitrarily: a Talon on PWM port 0. However, your real robot might vary from this. You should figure out which motor you want to run on your robot, and figure out its port number and which kind of speed controller it uses.
It’s possible that your robot uses CAN speed controllers. Try to choose motors that aren’t connected with CAN, as CAN motors are harder to deal with. Don’t worry, we’ll get to CAN motors later.
Once you know which kind of speed controller you’re using (Talon, Victor, Jaguar), you may need to replace talon with victor or jaguar. You may also need to replace the 0 with the correct port number.
Once you have the configuration correct, open the dropdown next to the External Tools button and select "MyFirstRobot Deploy."

Make sure that the output ends with BUILD SUCCESSFUL, and then you can connect your Driver Station to the robot, connect a Joystick, enable your robot, and move the motor by moving the Joystick!
Congratulations! You now know how to use the basics of the CCRE!
2 Introduction to Dataflow programming
When thinking about how a robot should react to stimuli, often the mental model that you generate is about the flow of data:

With traditional methods, you might think to implement that control system like this:

However, this technique is really easy to get wrong. That example has the bug where if the red button is pressed for very long - more than 20 milliseconds, which isn’t very long - then the program will blow up the world multiple times!
Yes, that can be fixed, but the code gets more complicated:
This is hard to scale for multiple reasons, including an inability to have inline state. For each button, you need a variable defined in a completely different location in the file (which becomes more significant with larger files) and you have to reference multiple places to figure out what your code does.

With the number of things you have to think about in a practical system, this quickly becomes an ineffective strategy: you can do it, but you will probably have a hard time keeping it understandable.
The CCRE solves this by aligning the code more closely with the original model. Recall what we had earlier:

The CCRE would express this as:
This is slightly simplified, but not by much.

Or, in practice:
red_button.onPress().and(key_input).send(blow_up_world);
And that’s the reason that we built the CCRE.
2.1 Channels
The CCRE is built around the concept of "channels." A channel has an implementation that handles one side of the channel, and any number of users that talk to the implementation over the channel. Neither side has to know much about the details of the other side.
There are six kinds of channels, along two axes.
Event | Boolean | Float | |
Input | EventInput | BooleanInput | FloatInput |
Output | EventOutput | BooleanOutput | FloatOutput |
With an Output, the users of the channel can send messages over the channel to the implementation behind it.
EventOutput: messages have no associated data. They simply represent the request that the implementor should cause a defined thing to happen. We call this "firing" the EventOutput.
start_driving_forward.event();
BooleanOutput: messages have an associated value of true or false. The message represents the request that the implementor change something to a state representable by a binary choice.
light_bulb.set(false);
light_bulb.set(true);
FloatOutput: messages have an associated real number value - often, but not always, in the range of -1.0 to 1.0. The message represents the request that the implementor change something to some potentially intermediate state.
motor.set(0.0f);
motor.set(1.0f);
motor.set(-1.0f);
motor.set(0.6f);
With an Input, the users of the channel can request the present state of the channel from the implementation (if any), and ask the implementation to tell them when the value represented by the input changes.
EventInput: there is no associated value. Users to be notified when something happens - we call this the EventInput being either fired or produced.
You would want to use an Instinct module for autonomous code like this.
// when the match starts, start the robot driving forward
match_start.send(start_driving_forward);
Don’t use System.out.println(x) in the CCRE. Logging, which you’ll learn later, works better.
// when the match ends, say so
match_end.send(() -> System.out.println("Match has ended!"));
BooleanInput: the associated value is a boolean. Users can ask if the current state is true or false, and ask to be told when it changes.
// as long as the light switch is flipped, turn on the light bulb
light_switch.send(light_bulb);
Don’t use System.out.println(x) for this. Readouts for the user are best done through Cluck, which you’ll learn later.
light_switch.send((is_flipped) ->
System.out.println("The light switch is flipped: " + is_flipped));
// whenever the light switch position is changed, say so
You would probably want to use transforms for this.
// this runs once - you'd want it somewhere where it would run repeatedly.
if (light_switch.get()) {
// We're wasting power!
} else {
// It's too dark in here!
}
FloatInput: the associated value is a real number value - often, but not always, in the range of -1.0 to 1.0. Users can ask for the current value, and ask to be told when it changes.
// control a motor with a joystick axis
joystick_axis.send(motor);
See previous margin note about System.out.println(x)
joystick_axis.send((current_position) ->
System.out.println("The current Joystick position: " + current_position));
// report the new position of the Joystick whenever someone moves it
This could be done with something from the transformation toolset, which we’ll see later.
// this runs once - you'd want it somewhere where it would run repeatedly.
if (sewage_level.get() > 1000.0f) {
// open drainage valve
}
2.2 Hardware
The CCRE includes an API that provides access to an FRC robot’s hardware via channels.
For example:
BooleanOutput led = FRC.makeDigitalOutput(7);
FloatOutput test_motor = FRC.talon(3, FRC.MOTOR_FORWARD, 0.2f);
EventInput start_match = FRC.startTele;
BooleanInput button = FRC.joystick1.button(3);
FloatInput axis = FRC.joystick6.axis(3);
See Hardware Access below for more info on robot hardware.
2.3 Transforming channels
It turns out that, often, you want to do similar things with your channels. So, correspondingly, all channels have a variety of built-in methods to help you on your journey!
The simplest example is probably send, which works for all three varieties of channels:
// this also works if you replace Boolean with Event or Float in both places.
BooleanInput x = /* ... */;
BooleanOutput y = /* ... */;
x.send(y);
This connects the input to the output, so that y is updated with the current value of x both immediately and whenever x changes. In the case of events, send causes the EventOutput to be fired whenever the EventInput is produced.
Another simple example is onPress:
BooleanInput bumper = FRC.makeDigitalInput(3);
bumper.onPress().send(stop_motors);
This creates an EventInput that fires when the BooleanInput becomes true.
Also useful are setWhen (along with setFalseWhen and setTrueWhen, and getSetEvent):
driving_forward.setTrueWhen(FRC.joystick1.onPress(2));
stop_motors = driving_forward.getSetEvent(false);
See Dataflow Transformations for more info.
2.4 Cells
Unlike in normal Java, you don’t use variables to store state in CCRE dataflow code. Instead, you use Cells, which are similar, but also act as Inputs and Outputs.
For example:
BooleanCell cell = new BooleanCell();
// ...
cell.get();
// ...
cell.set(true);
// ...
cell.get();
// ...
cell.set(false);
// ...
cell.get();
You can use this with dataflow:
BooleanCell cell = new BooleanCell();
some_boolean_input.send(cell);
cell.onPress().send(do_something);
Cells exist for Events, Booleans, and Floats. For Events, rather than store a value, they simply propagate events:
EventCell cell = new EventCell();
cell.send(do_something);
cell.send(do_something_else);
x_happened.send(cell);
y_happened.send(cell);
This example would cause do_something and do_something_else to be fired whenever x_happened is produced or y_happened is produced.
Sometimes, you want to connect together methods that aren’t easy to connect together. For example:
send_some_data_to_an_output(???);
do_something_based_on_an_input(???);
How would you connect these? There’s no implementation to provide either end. Luckily, we can use Cells!
// this also works for events and floats with EventCell and FloatCell.
BooleanCell intermediate_channel = new BooleanCell();
send_some_data_to_an_output(intermediate_channel);
do_something_based_on_an_input(intermediate_channel);
You can also use them to connect far-away sections of your code.
BooleanCell some_shared_value = new BooleanCell();
// ... somewhere ...
some_input.send(some_shared_value);
// ... somewhere else ...
some_shared_value.send(some_output);
3 Review of advanced Java concepts
There are a number of features of Java which are heavily used by the CCRE that you might not be familiar with. Below is a quick catalogue of them.
3.1 Anonymous classes
Sometimes you might want to implement an Output that does something unique, so you can’t use anything built-in. You could put this somewhere else:
public class CatPettingEventOutput implements EventOutput {
private final Cat cat_to_pet;
public CatPettingEventOutput(Cat cat_to_pet) {
this.cat_to_pet = cat_to_pet;
}
@Override
public void event() {
// pet a cat
}
}
and later:
EventOutput pet_fluffy = new CatPettingEventOutput(fluffy);
But then the class definition is far away from the actual use, and your code gets clogged up with all of your classes. Clearly, there has to be an easier way:
EventOutput pet_fluffy = new EventOutput() {
public void event() {
// pet fluffy
}
};
Useful!
3.2 Lambdas
Let’s go one step further. In Java 8, there’s a new feature called Lambdas which can replace some simple anonymous classes with even shorter code.
Instead of these:
EventOutput pet_fluffy = new EventOutput() {
public void event() {
// pet fluffy
}
};
BooleanOutput fluffy_cage_light = new BooleanOutput() {
public void set(boolean light_on) {
// turn on or off the light in fluffy's cage
}
};
FloatOutput fluffy_heater_temp = new FloatOutput() {
public void set(float temperature) {
// change the thermostat on fluffy's cage
}
};
You can now simply say:
EventOutput pet_fluffy = () -> {
// pet fluffy
};
BooleanOutput fluffy_cage_light = (light_on) -> {
// turn on or off the light in fluffy's cage
};
FloatOutput fluffy_heater_temp = (temperature) -> {
// change the thermostat on fluffy's cage
};
Also, if you only have a single statement in the lambda, you can omit the {}:
EventOutput pet_fluffy = () -> {
do_pet_fluffy();
};
can become:
EventOutput pet_fluffy = () -> do_pet_fluffy();
Even nicer!
4 The software environment
There are a number of components that you will be working with.
4.1 The Driver Station
Unfortunately, the DS software only runs on Microsoft Windows.
The Driver Station software connects over the robot’s wireless network to the roboRIO (the brain of the robot) and tells it:
If it should be enabled
What mode it should be in
The current positions of all Joysticks
It also talks to the Field Management System (FMS) if you’re playing in a real competition.
The software looks like this:

The laptop might looks something like this: (depending on your team)

I’m only showing the left half of the DS - the right half is relatively unimportant.
Let’s go over the tabs available on the DS:

On the first page, you can select the mode of the robot, as well as whether or not it’s enabled.
On the third page, you can change the team number, which needs to be correct for you to be able to connect properly.
On the fourth page, you can see the current plugged-in Joysticks, change their ordering (by dragging them), and see the current inputs on any Joystick by selecting it.
And much more, but that’s the most important stuff.
4.1.1 Modes
There are many different conceptualizations of what a "mode" is. The core three are Autonomous Mode, Teleoperated Mode, and Test Mode.
From the robot’s perspective, there’s also disabled mode. (You can also think of it as Enabled versus Disabled and the three fundamental modes.)
When in disabled mode, nothing on the robot should move. Safe!
From the driver station’s perspective, there is also Practice mode, which is useful in theory but not much in practice. (heh.) This mode simply sequences through the other modes in the standard order.
There’s also the emergency stop mode, which is entered by pressing the spacebar (in practice) or the physical e-stop button (on the real field.) Once the robot enters emergency stop mode, it is disabled until the robot is physically turned off and on again.
Note that the spacebar will cause an emergency stop regardless of whether or not the driver station is the current window. If you edit code on your driver station, make sure to disable the robot first. You don’t want to emergency stop the robot whenever you type a space into your program.
4.1.2 Keyboard shortcuts
There are a few important keyboard shortcuts:
The spacebar is the emergency-stop button. Once you press it, the robot stops running until you reboot it physically.
The enter key is the disable key. It is the same as pressing the disable button on the first tab of the driver station.
If you press the keysequence []\ , the robot will enable. This is the same as pressing the enable button on the first tab of the driver station.
Always keep your hand near the disable key when enabling the robot.
4.1.3 Joysticks
The driver station can have up to six Joysticks attached to it. Each Joystick is an individual physical device. Examples of Joysticks:
This is a Joystick:

This is ALSO a Joystick:

This? Another Joystick. Not two Joysticks - just one.

Each Joystick has some number of axes - each axis measures a value from -1.0 to 1.0. It also has some number of buttons - each button can be either pressed or released.
Standard axes are the position of a Joystick on the forward-backward (Y) axis and the left-right (X) axis.
For example, a trigger on a Joystick is a button, and an altitude control wheel (as on the base of the first Joystick) is an axis.
The X axis on a Joystick is usually axis #1, and the Y axis is usually axis #2.
If you work with something with multiple XY sticks, this may vary.
On an xbox controller, for example, #1 & #2 are the axes on left stick, and for the right stick, the X axis is #5 and the Y axis is #6. The trigger axes are #3 (left) and #4 (right.)
4.2 The match sequence
Here’s how a match goes in normal FRC competitions:
All six teams from both alliances place their robots on the field, powered up.
They connect their driver stations to the FMS (field management system.)
The FMS takes control of the driver stations, preventing the user from controlling the robot mode.
When the match starts, after all robots have powered up and connected properly (or are bypassed if not) and the field personel start the match, the FMS enables each of the robots in autonomous mode.
Fifteen seconds later (depending on the game), the robots momentarily change into disabled mode.
Either momentarily afterward, or perhaps a few seconds afterward depending on the game, the robots are enabled in teleoperated mode.
Around two minutes later, the robots are disabled and the match ends.
There is no way for a team to disable a robot during this time except to emergency-stop the robot. The robot can be forcibly disabled by the referees if it displays unsafe behaviour.
Autonomous mode requires some different programming techniques to write well. See Autonomous and Instinct Modules below.
4.3 Safety
There are some important guidelines that you need to follow, even if you’re working on software:
Always wear safety glasses when working on a robot. You probably like your eyes.
Before enabling a robot, always yell CLEAR and wait for people to step away from the robot. Yell CLEAR multiple times if necessary - but don’t enable the robot if people could be hit by it.
When you first test an autonomous mode, the robot will probably move faster than you expect. Set your speeds lower than you think you’ll need to.
Before enabling a robot, confirm that all of the Joysticks are free. Do not set any of them on seating surfaces.
When enabling a robot, always hover your fingers over the enter (disable) key. This will work regardless of what application currently has focus.
The first time you test a robot, or test any potentially dangerous behavior, place the robot "on blocks" - put bricks/wood blocks/something under the drive frame so that the wheels don’t touch anything and can spin freely. This prevents it from running into anyone.
Remember that following safety procedures are the difference between getting work done and being in the hospital.
4.4 The roboRIO

image sourced from http://khengineering.github.io/RoboRio/faq/roborio/
The roboRIO is FRC’s next-generation robot controller, from National Instruments. It supplants the previous cRIO, and runs Linux with PREEMPT_RT patches on an ARM processor.
It contains a set of ports for interfacing with PWM-controlled devices, CAN bus devices, I2C devices, SPI devices, RS232 (serial) devices, miscellaneous digital I/O devices, relays, analog inputs, USB devices, and networked devices over Ethernet.
You aren’t allowed to control anything on your robot (except for nonfunctional components like LEDs) with any other controller than the roboRIO, so teams have to use it as their main controller.
See below in this document for some of the devices that attach to the roboRIO.
4.4.1 The cRIO
The cRIO was the previous platform used as a robot controller. It ran VxWorks instead of Linux, and the processor was PowerPC instead of ARM. This made it extremely hard to get any software for it. The only JVM we could use was the Squawk JVM, which only supported Java 1.3. (We’re on Java 8 now.) The CCRE pioneered using retrotranslation technology to allow us to use some Java 5 features on the cRIO, but even with those the system was much harder to use than the modern roboRIO.
4.5 Downloading code
To download code, as we said before, you can easily deploy code to the robot:

To see how this works behind the scenes, see Deployment.
4.6 Speed controllers
A speed controller stands between the Power Distribution Panel (PDP) and individual motors, and varies the speed of the motor based on a signal from the roboRIO. There are two primary ways to control a motor: via PWM or via CAN.
4.6.1 PWM (Pulse-width modulation)
With PWM, you can drive Talon SRs, Talon SRXes, Victors, and Jaguars.
A Victor:

A Jaguar (also controllable over CAN):

A Talon:

A Talon SRX (also controllable over CAN):

For a discussion of how PWM works, see the Wikipedia article. The important attributes for us are:
A PWM device can’t send you any data.
PWM ranges vary by kind of motor, so if you set up your output for a Talon, you can’t run a Victor on that output.
PWM as a control channel is bidirectional. You can run the motor in either direction.
PWM is simple. It is usually easy to get to work and fixing a broken PWM connection is as easy as replacing a cable.
See Hardware Access for information on how to communicate with PWM speed controllers.
4.6.2 CAN (Control area network)
With CAN, you can drive Talon SRXes and CAN Jaguars.
A Jaguar (also controllable over PWM):

A Talon SRX (also controllable over PWM):

For a discussion of how CAN works, see the Wikipedia article. The relevant attributes:
CAN as a protocol is bidirectional. You can get information from a CAN device about current voltage, current, faults, and more.
CAN needs to be specialized to the specific kind of speed controller - you have to know whether the CAN device is a CAN Jaguar or a Talon SRX, and a setup for one won’t work for the other.
CAN can be controlled in terms of absolute voltage, fractional voltage, current, and other control modes. Fractional voltage is the easiest because it’s simply -1.0 to 1.0 just like other motors.
Legacy CAN cables, such as those used for CAN Jaguars, are flaky and unreliable. Luckily, the newer CAN cables are much more reliable.
CAN is more complicated and requires more testing and configuration, but should provide more diagnostics in practice.
See Hardware Access for information on how to communicate with CAN speed controllers.
4.7 Sensors
Sensors can be classified by how they connect to the roboRIO:
4.7.1 Digital Inputs
Digital Inputs are in the GPIO (general purpose IO) section of the roboRIO. These report either HIGH (true) or LOW (false) at any specific time. For obvious reasons, these correspond to BooleanInputs.
Touch sensors, light sensors, magnetic switches, and pressure switches are examples of simple digital inputs. In one state, they are pressed/activated, and in another state, they are released/deactivated. IMPORTANT: False is not necessary deactivated and true is not necessarily activated! Many (perhaps the majority) of sensors are true by default and only false when activated. Make sure to check your specific sensors to see how they function!
Encoders are bidirectional rotation sensors: every certain fraction of a rotation, they send a directioned tick back to the roboRIO (this is very simplified, of course), which tells the roboRIO that the encoder has spun more. The FPGA totals these ticks and provides you with a sum, which you can access from your code. These are provided as FloatInputs even though they are discrete. Unlike most sensors, these require two digital input ports to function. There are specific APIs for working with encoders.
Gear tooth sensors are a simplified form of encoders that don’t tell you direction - only that motion is occurring. They only require a single digital input port and are easier to install, so they are sometimes used instead of encoders when you only care about measuring speed or distance without direction.
4.7.2 Analog Inputs
Analog inputs are in a dedicated section of the roboRIO. They provide a value in volts from the connected sensor. It is likely that you will need to scale this reading to something more useful to you.
Often, analog sensors are linear: they have a point at which they are zero and a point at which they are one. (The normalize method is very useful for handling these.)
A pressure sensor is a good example that measures the pressure of a robot’s pneumatic system. A certain base voltage is produced for a pressure of 1 atm by the pressure sensor, and a certain amount more is produced for each psi above that.
Gyros (gyroscopes) provide an analog value based on the rotation speed, which is integrated in the FPGA. There are specific APIs for working with gyros.
Other analog sensors include accelerometers and current sensors.
4.7.3 Internal Sensors to the roboRIO
The roboRIO also contains a set of internal accelerometers. It also contains sensors to measure voltages and currents on various internal rails, so you can check the health of the battery. These are FloatInputs.
4.7.4 PDP Sensors
The PDP, or Power Distribution Panel, provides data over the CAN bus to the roboRIO, which tells it the current battery voltage and the present current consumption on each port of the PDP. Useful!
4.7.5 RS232 Sensors
Sensors can also be connected over some of the other data buses. (These are: RS232, SPI, I2C, etc.) These usually require custom drivers to be written to use them.
Currently, the CCRE contains a driver for the UM7LT, which is a heading sensor from CH Robotics that tells us the current orientation of the robot. Note, however, that it requires a lot of calibration to use.
4.8 WiFi and Networking
Here’s a brief overview of how the networking works on a robot when not on a competition field:

Each element in this chain is a distinct device with an assigned IPv4 (internet protocol version 4) address, which is made up of four numbers from 0 to 255 (inclusive.)
Computers, in general, can only communicate via knowing each others’ IP address - in the case of the internet, usually IP addresses are acquired via the Domain Name System (DNS). DNS lets your computer ask, for example, for an IP address for team1540.org, and might get back 65.39.205.61 or something else depending on changes to the IP address of the server.
On your local network, unlike on the internet, IP addresses are local. Since most IP addresses refer to specific machines on the wide internet, certain ranges are reserved for private IP addresses:
10.0.0.0 - 10.255.255.255 - 10.0.0.0/8 172.16.0.0 - 172.31.255.255 - 172.16.0.0/12 192.168.0.0 - 192.168.255.255 - 192.168.0.0/16
For example, the IP address 192.168.1.1 is a valid local IP address. In the FRC control system, IP addresses are assigned based on team number, in the form 10.TE.AM.0/24 (which means, for example, 10.15.40.0 through 10.15.40.255 for team 1540. For shorter team numbers, it might be 10.2.54.XX or 10.0.11.XX.)
Within that range, certain addresses are reserved: (for this example, we use team 1540 addresses.)
10.15.40.0 Reserved, as it ends in 0. Don't use it. 10.15.40.1 The robot's wireless access point. This is usually a D-Link DAP-1522 Rev B wireless radio. 10.15.40.2 Previously reserved for the cRIO. It can be used by the roboRIO if you wish. 10.15.40.3 - 10.15.40.19 Unused. Allocate static addresses from these. 10.15.40.20 - 10.15.40.199 Assigned by the DHCP server on the roboRIO. (See below.) 10.15.40.200 - 10.15.40.254 Unused. Allocate static addresses from these. 10.15.40.255 Reserved, as it ends in 255. Don't use it.
There are two primary ways for a computer to get an IP address: a statically-allocated address or a DHCP-allocated address.
DHCP (Dynamic Host Configuration Protocol) lets your computer, after it has joined a network, ask others on the network "Please give me an address!" and a DHCP server on the network will tell it the address. Pretty much every normal wireless or wired network that you would plug your computer into uses DHCP.
A static address is set manually on the computer.
DHCP has advantages in terms of it being easier to configure on each computer... but it means that IP addresses can change over time, requires a running DHCP server, and other issues. A static address, by contrast, must be manually configured as to not conflict with any other address, but doesn’t require as much infrastructure.
With the old 2014 cRIO control system, everything used static addresses. Today, with the 2015 roboRIO control system, everything but the wireless AP uses DHCP (as provided BY the wireless AP.) To solve the issue of the DHCP addresses changing, the roboRIO uses mDNS, which is a version of DNS that allows computers on a local network to find each other based on local names. The roboRIO is usually roboRIO-NNNN-FRC.local. (For example, roboRIO-1540-FRC.local.) In 2015, this was roboRIO-NNNN.local, but that was changed for 2016.
4.8.1 The FMS and port filtering
In a competition, instead of laptops connecting directly to wireless APs, the wireless radios are reprogrammed to act as wireless bridges, and then when they are on the field, the field generates access points for the robots in the current match, and the bridges connect to those APs. The laptops then connect over a wired network through the FMS to the robots.
This is mostly transparent, but has two major effects:
Most TCP and UDP ports are blocked! This means that you must limit your traffic to a fixed set of ports. See below.
The wireless radio is no longer in AP mode and doesn’t host a DHCP server, which can cause problems connecting directly to the robot.
A listing of available ports, up to date as of the 2015 challenge:
TCP 1180
TCP 1735
UDP 1130 & 1140 (but used for the driver station, so you can’t use these)
TCP 80 & 443 (but these are below 1024, so you can’t bind them on the roboRIO)
UDP/TCP 554 (which has the same problem)
UDP/TCP 5800-5810 (designated for team use)
The CCRE implementation for FRC hosts Cluck servers on 1540, 1735, 5800, and 5805 by default, and 5800 is used as the default port to connect to.
(See the next section for info on Cluck.)
4.9 Cluck
The CCRE includes a publish-subscribe networking system, designed to seamlessly integrate with the rest of the CCRE channel system.
The basic idea is that a group of computers connect to each other over Cluck transports, and can exchange messages over this. Specifically, one side can publish channels and another side can then subscribe to those channels.
For example, in the previous example of CCRE usage, you could put the blowing-up-the-world part onto its own computer/robot:

It’s actually about this easy to connect things with actual code. See The Cluck Pub/Sub System.
4.9.1 The Poultry Inspector
In usual use, you don’t need to connect multiple robots or other pieces of code together with Cluck - often, the main use is to publish a bunch of channels and inspect them via the Poultry Inspector.

Don’t worry - it’s not as complicated as the image may make it look. That’s just with a lot of channels displayed.
See Detailed guide to the Poultry Inspector below for more details on how to use it.
4.9.2 Network Tables & Smart Dashboard
WPILib, the "official" framework for writing robot code, has a similar (but, in our minds, insufficiently powerful) system called NetworkTables, and an equivalent to the Poultry Inspector called SmartDashboard.
We don’t use these, due to feeling that the engineering behind them is not sufficiently robust for our purposes.
5 Detailed guide to the CCRE
This section is designed to provide a detailed guide on how to use just about everything in the CCRE!
It’s organized into the following sections:
5.1 Documentation format
The following formats are used when describing constructors, methods, or fields:
|
This constructor could be used, for example, as:
BooleanCell cell = new BooleanCell(false);
|
This method could be used, for example, as:
BooleanInput button = /* ... some code goes here ... */;
EventInput press = button.onPress();
|
This method could be used, for example, as:
float x = 10.0f;
double y = Math.sin(x);
|
This field could be used, for example, as:
EventOutput output = EventOutput.ignored;
output.event();
5.2 Flow versus Setup
There are two conditions in which CCRE code can run: flow mode and setup mode. When the robot first starts, it is running in setup mode while your code sets up its functionality. Once this finishes, the robot goes into flow mode and executes the control that has been set up.
Every method in the CCRE is tagged as either flow or setup. For example, the documentation might say:
|
for flow mode or perhaps
|
Flow mode versus setup mode is more about where you call a method from rather than exactly when it occurs, but in general you can understand the modes based on whether or not the robot is done setting up yet.
for setup mode. Note the text that says "flow" or "setup".
Except in very specific cases, your code should never call a setup method from flow mode, or a flow method from setup mode. The main exception is that sometimes you want to preinitialize the value of something, and so may call a flow method from setup mode in such cases.
5.3 Dataflow Channels
As touched on before, a channel represents the ability to communicate in some fashion with an implementation. Channels can either be inputs or outputs, and carry values that are either booleans, floats, or events.
5.3.1 Outputs
Outputs are, at a fundamental level, something with a state that can be changed, but not necessarily queried.
|
|
|
You can call these methods to change the current state of the output. All control of outputs will at some level reduce to these methods.
The "flow block" annotation here specifies that the code in the block starting on that line is in flow mode.
|
|
}; |
|
|
}; |
|
|
}; |
You can implement an output with the above code. You can replace // do something with the flow mode code to execute when the output is controlled.
Please note that, most of the time, you shouldn’t need to implement your own channels! Usually there is already an implementation for you. See Dataflow Transformations.
5.3.2 Inputs
|
|
For many inputs, you can access the current value at any time with get(). This calculates the current value at the time that you call get().
|
|
}); |
|
|
}); |
|
|
}); |
More importantly, you can listen for changes on an input. This works for all inputs. You may notice that these contain a part suspiciously similar to defining outputs: this is because you subscribe to a value by telling an input to send all values to an output, which you can implement yourself.
|
|
|
The send family of methods are all used to connect an input to an output with the same type of data. So, whenever the value of the input changes (for a value-based input, like BooleanInput or FloatInput) or the input is produced (for EventInput), then target will received that value or event.
See below for CancelOutput.
|
protected boolean shouldProduce() { |
|
} |
}; |
|
protected boolean apply() { |
|
} |
}; |
|
protected float apply() { |
|
} |
}; |
You can easily implement an input with the above code. You can replace /* value */ with the flow mode code to calculate the value of the input.
apply() will be called once when the input is created, and then exactly once for each time that one of inputs is updated. The value provided by apply will be returned by get.
Since the value only updates when one of inputs updates, make sure that you don’t access anything not included in that list. You can figure out most of what you need in that list by looking at the inputs you call get() on.
Please note that, most of the time, you shouldn’t need to implement your own channels! Usually there is already an implementation for you. See Dataflow Transformations.
|
|
|
For EventInput, onUpdate and send are essentially identical.
See below for CancelOutput.
Here, "setup block" means that the code in this block runs in setup mode.
|
public CancelOutput onUpdate(EventOutput target) { |
|
return /* a CancelOutput that cancels the connection */; |
} |
}; |
|
public CancelOutput onUpdate(EventOutput target) { |
|
return /* a CancelOutput that cancels the connection */; |
} |
public boolean get() { |
|
} |
}; |
|
public CancelOutput onUpdate(EventOutput target) { |
|
return /* a CancelOutput that cancels the connection */; |
} |
public float get() { |
|
} |
}; |
If you want more control over your inputs, you can implement them directly. You implement onUpdate to specify when the input changes, and implement get to specify the value.
WARNING: You probably don’t want to use this! It’s hard to implement, and you might make a mistake. new DerivedEventInput and friends, as detailed above are better for almost all implementations.
5.3.3 CancelOutputs
The send and onUpdate methods also return an CancelOutput. This allows you to cancel a send after it is sent. For example:
CancelOutput cancellator = an_input.send(an_output);
// ... much later ...
cancellator.cancel();
|
You call the cancel method to cancel whatever the CancelOutput represents. Note that although this class is similar to EventOutput, it is used in the setup mode, rather than flow mode.
|
|
}; |
You may need to implement a CancelOutput yourself, which can be done as shown here. Note, of course, that the body is a setup block, not a flow block.
|
This combines the CancelOutput you call it on with another CancelOutput, so that the resulting CancelOutput cancels both of these CancelOutputs.
5.3.4 Channel Cells
A Cell is the dataflow equivalent of a variable. In the case of a BooleanCell or a FloatCell, it simply holds a value that can be used as a BooleanOutput or FloatOutput (to modify the value) or as a BooleanInput or FloatInput (to read the value.) As a EventCell, it simply propagates events through itself, and can be used as either an EventInput (to receive events) or EventOutput (to send events.)
The general idea of a channel that is both an Input and an Output is called an IO, for example BooleanIO or EventIO. All Cells are IOs.
|
|
|
|
|
|
The asInput() and asOutput() methods allow you to use an IO as just an Input or just an Output. Calling x.asInput() will simply return x, but in a variable of a different type, so this is equivalent to casting.
BooleanIO x = new BooleanCell();
x.asInput() == x == (BooleanInput) x;
However, sometimes you need to force which side you use the Cell or IO as, which is why this functionality is provided. While you can cast, usage of asInput() and asOutput() is checked at compile time, so any misuses will be found before you try to run your code. With casting, a mistake might not be found until you run code on your robot.
|
|
| output) |
|
Sometimes, you want to combine an input and output for the same thing into a single IO. You can simply use compose(input, output) to do this.
|
You can use toggle() to toggle the value of a BooleanIO.
BooleanIO something = /* ... */;
// ...
something.toggle();
// is equivalent to
something.set(!something.get());
|
You can get an event that lets you toggle the value of a BooleanIO, as if you had called toggle.
EventInput something_happened = /* ... */;
BooleanIO something_controllable = /* ... */;
something_happened.send(something_controllable.eventToggle();
This would toggle something_controllable whenever something_happened.
|
This tells the BooleanIO to toggle itself whenever when fires.
EventInput something_happened = /* ... */;
BooleanIO something_controllable = /* ... */;
something_controllable.toggleWhen(something_happened);
|
This lets you create a new anonymous EventIO that simply passes events through itself.
EventIO passthrough_event = new EventCell();
// ...
passthrough_event.send(some_output);
// ...
some_input.send(passthrough_event);
|
This lets you create a new anonymous EventIO that simply passes events through itself.
This sends any events that occur to all of the outputs in the constructor arguments, in addition to any other places.
EventIO passthrough_event = new EventCell(a, b, c);
// is equivalent to
EventIO passthrough_event = new EventCell();
passthrough_event.send(a);
passthrough_event.send(b);
passthrough_event.send(c);
|
This lets you create a new anonymous BooleanIO that simply stores a value. The initial value is false.
BooleanCell some_value = new BooleanIO();
// ...
some_value.send(some_output);
// ...
some_input.send(some_value);
|
This lets you create a new anonymous BooleanIO that simply stores a value. The initial value is default.
BooleanCell some_value = new BooleanIO(true);
// ...
some_value.send(some_output);
// ...
some_input.send(some_value);
|
This lets you create a new anonymous BooleanIO that simply stores a value. The initial value is default.
BooleanCell some_value = new BooleanCell(true);
// ...
some_value.send(some_output);
// ...
some_input.send(some_value);
|
This lets you create a new anonymous BooleanIO that simply stores a value.
This sends the value to all of the outputs in the constructor arguments, in addition to any other places.
BooleanIO passthrough_value = new BooleanCell(a, b, c);
// is equivalent to
BooleanIO passthrough_value = new BooleanCell();
passthrough_value.send(a);
passthrough_value.send(b);
passthrough_value.send(c);
|
This lets you create a new anonymous FloatIO that simply stores a value. The initial value is 0.
FloatCell some_value = new FloatIO();
// ...
some_value.send(some_output);
// ...
some_input.send(some_value);
|
This lets you create a new anonymous FloatIO that simply stores a value. The initial value is default.
FloatCell some_value = new FloatIO(3.2f);
// ...
some_value.send(some_output);
// ...
some_input.send(some_value);
|
This lets you create a new anonymous FloatIO that simply stores a value. The initial value is default.
FloatCell some_value = new FloatCell(0);
// ...
some_value.send(some_output);
// ...
some_input.send(some_value);
|
This lets you create a new anonymous FloatIO that simply stores a value.
This sends the value to all of the outputs in the constructor arguments, in addition to any other places.
FloatIO passthrough_value = new FloatCell(a, b, c);
// is equivalent to
FloatIO passthrough_value = new FloatCell();
passthrough_value.send(a);
passthrough_value.send(b);
passthrough_value.send(c);
5.4 Dataflow Transformations
In practice, most ways that you might want to transform a channel are ways that someone else has also wanted to. For example, one might convert a BooleanInput to an EventInput for when the BooleanInput becomes true. This is very useful for checking button or sensor presses.
Since these transformations appear repeatedly, the CCRE provides easy ways to perform these transformations.
However, don’t let the term "transformation" confuse you: when you call output.negate(), for example, nothing changes about output. Rather a new channel is returned by output.negate():
FloatOutput negated_output = output.negate();
By "transformation", we usually mean a relationship between an existing channel and a newly-created channel, but sometimes we include things that aren’t technically transformations but are similarly useful and related to channels.
This section will explain various ways to transform a channel.
5.4.1 EventOutputs
|
This combines two EventOutputs into a single EventOutput, so that firing the result would fire both of the outputs.
EventOutput merged = a.combine(b);
// then this would be equivalent:
merged.event();
// to this:
a.event();
b.event();
|
This combines a set of EventOutputs into a single EventOutput, so that firing the result would fire all of the outputs.
EventOutput merged = EventOutput.combine(a, b);
// then this would be equivalent:
merged.event();
// to this:
a.event();
b.event();
|
|
filter(allow) filters an EventOutput so that events are only propagated further when allow.get() == true.
filter(deny) is similar, but events are only propagated when deny.get() == false.
EventOutput maybe_blow_up_world = blow_up_world.filter(has_authorization);
// then this would be equivalent:
maybe_blow_up_world.event();
// to this:
if (has_authorization.get()) {
blow_up_world.event();
}
|
Debouncing limits how often an event can occur, such that if an event occurs a short time after another event, it will be ignored. The debouncing time is given by minimumMilliseconds, which is the minimum number of milliseconds after one event before another event can pass through.
This is used to handle buttons, switches, or sensors that tend to "bounce" and trigger multiple times when you press them once.
The timeout is relative to the last event that was passed through; events that are ignored do not delay the timeout. This way, if an event occurs repeatedly at a period smaller than minimumMilliseconds, there will be about one event every minimumMilliseconds.
|
This is the same as send, but in the opposite direction.
EventInput a;
EventOutput b;
// these are equivalent:
a.send(b);
b.on(a);
|
This EventOutput ignores all events sent to it.
EventOutput.ignored.event();
// is equivalent to doing absolutely nothing.
|
cell converts a EventOutput into a EventIO.
This is equivalent to wrapping a new EventCell around this EventOutput, but can be more efficient in some cases, and is sometimes more concise.
EventCell ec = new EventCell();
EventIO ec2 = ec.cell();
// then ec2 == ec
EventOutput eo = /* some normal output */;
EventIO ec3 = eo.cell();
// then ec3 is equivalent to new EventCell(eo)
5.4.2 EventInputs
|
This is an EventInput that is fired whenever either of the EventInputs are fired.
EventInput c = a.or(b);
// this is equivalent:
c.send(output);
// to:
a.send(output);
b.send(output);
|
|
This is an EventInput limited to only firing when allow.get() == true or deny.get() == false, depending on which version you use.
So, each time that the original EventInput fires, allow or deny is polled, and if the value is the expected value, the result EventInput fires.
EventInput c = a.and(b);
// this is equivalent:
c.send(output);
// to:
a.send(output.filter(b));
|
Debouncing limits how often an event can occur, such that if an event occurs a short time after another event, it will be ignored. The debouncing time is given by minimumMilliseconds, which is the minimum number of milliseconds after one event before another event can pass through.
This is used to handle buttons, switches, or sensors that tend to "bounce" and trigger multiple times when you press them once.
The timeout is relative to the last event that was passed through; events that are ignored do not delay the timeout. This way, if an event occurs repeatedly at a period smaller than minimumMilliseconds, there will be about one event every minimumMilliseconds.
|
This is an EventInput that never occurs.
EventInput.never.send(output);
// is equivalent to doing absolutely nothing!
5.4.3 BooleanOutputs
|
This is an inverted version of the original BooleanOutput.
BooleanOutput inv = a.invert();
// so then this is equivalent
inv.set(false);
// to this:
a.set(true);
|
This combines two BooleanOutputs into a single BooleanOutput, so that setting the result to a value would set both of the original outputs to that value.
BooleanOutput merged = a.combine(b);
// then this would be equivalent:
merged.set(true);
// to this:
a.set(true);
b.set(true);
|
This combines a set of BooleanOutputs into a single BooleanOutput, so that setting the result to a value would set all of the original outputs to that value.
BooleanOutput merged = BooleanOutput.combine(a, b);
// then this would be equivalent:
merged.set(true);
// to this:
a.set(true);
b.set(true);
|
This limits a BooleanOutput such that changes in its value propagate when, and only when, update is fired.
EventCell b = new EventCell();
BooleanOutput c = a.limitUpdatesTo(b);
// ...
c.set(true);
b.event();
|
|
This provides an EventOutput that sets this BooleanOutput to some value, either a constant value or a value fetched in the moment from a BooleanInput.
EventOutput o = a.eventSet(true);
// and then:
o.event();
// is equivalent to:
a.set(true);
With a BooleanInput parameter:
EventOutput o = a.eventSet(input);
// and then:
o.event();
// is equivalent to:
a.set(input.get());
|
|
|
|
When when fires, this BooleanOutput will be set to value or value.get(), depending on type.
In the case of setTrueWhen(when) or setFalseWhen(when), the boolean given in the name takes the place of value.
EventCell event = new EventCell();
output.setWhen(true, event);
// and then:
event.event();
// is equivalent to:
output.set(true);
With a BooleanInput parameter:
EventCell event = new EventCell();
output.setWhen(input, event);
// and then:
event.event();
// is equivalent to:
output.set(input.get());
With a constant name:
EventCell event = new EventCell();
output.setFalseWhen(event);
// and then:
event.event();
// is equivalent to:
output.set(false);
|
| EventOutput toTrue) |
This combines two EventOutputs into a BooleanOutput. When the value sent to the BooleanOutput changes to true, toTrue will be fired, and when it changes to false, toFalse will be fired.
BooleanOutput b = BooleanOutput.polarize(forFalse, forTrue);
// and then:
b.set(true);
// would be equivalent to:
forTrue.event();
// IF b was last set to false.
BooleanOutput b = BooleanOutput.polarize(forFalse, forTrue);
// ...
b.set(true);
// and then a subsequent
b.set(true);
// would be equivalent to doing nothing.
|
|
filter filters a BooleanOutput so that it only changes when allow.get() == true.
filterNot filters a BooleanOutput so that it only changes when deny.get() == false.
As long as allow.get() == true, each change to the returned BooleanOutput will modify the original BooleanOutput.
When allow changes to false, the current value is preserved.
As long as allow.get() == false, each change to the returned BooleanOutput will be remembered, but not propagated.
When allow changes to true, the last remembered value will be propagated.
BooleanOutput filtered = original.filter(allow);
// ...
allow.set(true);
filtered.set(true);
filtered.set(false);
filtered.set(true);
allow.set(false);
filtered.set(false);
filtered.set(true);
filtered.set(false);
allow.set(true);
original.filterNot(deny) is equivalent to original.filter(deny.not()).
|
This is a BooleanOutput that ignores all values sent to it.
BooleanOutput.ignored.set(false);
// is equivalent to absolutely nothing!
|
cell converts a BooleanOutput into a BooleanIO and sets its initial value.
This is equivalent to wrapping a new BooleanCell around this BooleanOutput, but can be more efficient in some cases, and is sometimes more concise.
BooleanCell bc = new BooleanCell();
BooleanIO bc2 = c.cell(true);
// then bc2 == bc and bc is set to true
BooleanOutput bo = /* some normal output */;
BooleanIO bc3 = bo.cell(false);
// then bc3 is equivalent to new BooleanCell(bo)
5.4.4 BooleanInputs
|
A BooleanInput that is always true and never changes.
|
A BooleanInput that is always false and never changes.
|
A BooleanInput that is always value and never changes.
|
Provides a BooleanInput that is always the inverse of the original BooleanInput.
This means that input.not().get() == !input.get().
|
Provides a BooleanInput that is true if and only if both BooleanInputs are true.
This means that a.and(b).get() == (a.get() && b.get()).
|
a.andNot(b) is equivalent to a.and(b.not().
|
Provides a BooleanInput that is true when one of the BooleanInputs is true and the other is false.
This means that a.and(b).get() == (a.get() ^ b.get()).
|
Provides a BooleanInput that is true if either of the BooleanInputs are true.
This means that a.and(b).get() == (a.get() || b.get()).
|
a.orNot(b) is equivalent to a.or(b.not()).
|
|
|
onPress provides a EventInput that is produced whenever this BooleanInput changes from false to true.
onRelease is the same, but for when true changes to false.
onChange is similar, but happens whenever any change occurs.
BooleanCell input = new BooleanCell(false);
EventInput press = input.onPress();
EventInput release = input.onRelease();
EventInput change = input.onChange();
// ...
input.set(true);
input.set(false);
input.set(false);
input.set(true);
input.set(true);
input.set(false);
input.set(true);
|
|
|
onPress fires event whenever this BooleanInput changes from false to true.
onRelease is the same, but for when true changes to false.
onChange is similar, but happens whenever any change occurs.
BooleanCell input = new BooleanCell(false);
input.onPress(press);
input.onRelease(release);
input.onChange(change);
// ...
input.set(true);
input.set(false);
input.set(false);
input.set(true);
input.set(true);
input.set(false);
input.set(true);
|
|
|
|
toFloat provides a FloatInput with a value selected from one of two other floats or FloatInputs.
BooleanCell input = new BooleanCell(false);
FloatInput converted = input.toFloat(0.0f, 0.75f);
// ...
// converted is 0.0
input.set(true);
// converted is 0.75
input.set(false);
// converted is 0.0
|
|
|
|
select provides a BooleanInput with a value selected from one of two other booleans or BooleanInputs.
BooleanCell input = new BooleanCell(false);
BooleanInput converted = input.select(a, b);
// ...
// converted is a.get()
input.set(true);
// converted is b.get()
input.set(false);
// converted is a.get()
|
|
filterUpdates provides a BooleanInput that follows the value of the original BooleanInput while allow.get() == true, and holds the last value while allow.get() == false.
a.filterUpdatesNot(b) is equivalent to a.filterUpdates(b.not()).
BooleanCell unlocked = new BooleanCell();
BooleanInput lockable = original.filterUpdates(unlocked);
// ...
unlocked.set(true);
original.set(true);
original.set(false);
original.set(true);
original.set(false);
original.set(true);
unlocked.set(false);
original.set(false);
original.set(true);
original.set(false);
unlocked.set(true);
original.set(true);
original.set(false);
unlocked.set(false);
original.set(true);
5.4.5 FloatOutputs
|
This is a negated version of the original FloatOutput.
FloatOutput inv = a.negate();
// so then this is equivalent
inv.set(1.0f);
// to this:
a.set(-1.0f);
|
This is either equivalent to the original FloatOutput or a negated version of the original FloatOutput.
FloatOutput inv = a.negateIf(neg);
// so then this is equivalent
inv.set(1.0f);
// to this:
a.set(-1.0f);
// if neg.get() == true or
a.set(1.0f);
// if neg.get() == false
|
This combines two FloatOutputs into a single FloatOutput, so that setting the result to a value would set both of the original outputs to that value.
FloatOutput merged = a.combine(b);
// then this would be equivalent:
merged.set(0.3f);
// to this:
a.set(0.3f);
b.set(0.3f);
|
This combines a set of FloatOutputs into a single FloatOutput, so that setting the result to a value would set all of the original outputs to that value.
FloatOutput merged = FloatOutput.combine(a, b);
// then this would be equivalent:
merged.set(0.3f);
// to this:
a.set(0.3f);
b.set(0.3f);
|
|
This provides an EventOutput that sets this FloatOutput to some value, either a constant value or a value fetched in the moment from a FloatInput.
EventOutput o = a.eventSet(0.7f);
// and then:
o.event();
// is equivalent to:
a.set(0.7f);
With a FloatInput parameter:
EventOutput o = a.eventSet(input);
// and then:
o.event();
// is equivalent to:
a.set(input.get());
|
|
When when fires, this FloatOutput will be set to value or value.get(), depending on type.
EventCell event = new EventCell();
output.setWhen(0.3f, event);
// and then:
event.event();
// is equivalent to:
output.set(0.3f);
With a FloatInput parameter:
EventCell event = new EventCell();
output.setWhen(input, event);
// and then:
event.event();
// is equivalent to:
output.set(input.get());
|
Provides a FloatOutput whose values go through a deadzone filter before reaching the original FloatOutput.
Usually, you want to apply deadzones on your inputs rather than outputs. See FloatInput.deadzone for the equivalent FloatInput version of this method.
The idea of a deadzone is that it chops out values close to zero.
Usually, a centered Joystick gives values of something like 0.047, not 0.000. You often want to interpret this as zero, because - for example - you usually might not want motors to move in this case.
By using a deadzone of 0.1, any values from around -0.09999999 to around 0.09999999 are converted to exactly zero.
|
Adds a ramping layer on top of a FloatOutput.
The idea of ramping is that immediately changing from, for example, 0 feet per second to 16 feet per second can be very stressing on a robot’s drivetrain - or other similar components. You usually want to "ramp up" to the desired speed, for example at four feet per second per second.
You configure ramping with a maximum delta and an event to update ramping. The idea is that whenever updateWhen is fired, the actual output is moved closer to the most recent target value. The maximum amount by which it can change is limit.
A recommended ramping setup, in absence of actual testing, is addRamping(0.1f, FRC.constantPeriodic). Since FRC.constantPeriodic is a ten-millisecond loop, this means that it takes 100 milliseconds to go from stopped to full speed. You may need to tweak this lower or higher based on actual testing.
|
This provides a FloatOutput such that the derivatives of the data sent to it are sent to the original FloatOutput.
You probably want to use FloatInput.derivative instead. It usually makes more sense.
For those of you who don’t know calculus, this essentially converts a position to a speed. So you could send the total count of encoder ticks through viaDerivative and you would get the encoder speed.
FloatCell speed = new FloatCell();
encoder.send(speed.viaDerivative());
WARNING: There is a bug in the implementation that is very hard to solve. Usually, a sensor position "wiggles" slightly due to measurement noise. However, if this doesn’t happen, the derivative doesn’t know when to update the speed, and the speed will perpetually be the last measured speed. It’s hard to find a way to fix this, but we’re working on it. There is a workaround:
FloatCell speed = new FloatCell();
speed.viaDerivative().setWhen(encoder, FRC.sensorPeriodic);
|
|
filter filters a FloatOutput so that it only changes when allow.get() == true.
filterNot filters a FloatOutput so that it only changes when deny.get() == false.
As long as allow.get() == true, each change to the returned FloatOutput will modify the original FloatOutput.
When allow changes to false, the current value is preserved.
As long as allow.get() == false, each change to the returned FloatOutput will be remembered, but not propagated.
When allow changes to true, the last remembered value will be propagated.
FloatOutput filtered = original.filter(allow);
// ...
allow.set(true);
filtered.set(0.3f);
filtered.set(0.0f);
filtered.set(0.6f);
allow.set(false);
filtered.set(0.3f);
filtered.set(0.0f);
filtered.set(0.5f);
allow.set(true);
original.filterNot(deny) is equivalent to original.filter(deny.not()).
|
|
|
|
fromBoolean provides a BooleanOutput that controls a FloatOutput with a value selected from one of two floats or FloatInputs.
FloatCell output = new FloatCell(0.3f);
BooleanOutput converted = output.fromBoolean(0.0f, 0.75f);
// ...
// converted is 0.3
converted.set(true);
// converted is 0.75
converted.set(false);
// converted is 0.0
converted.set(true);
// converted is 0.75
converted.set(false);
// converted is 0.0
|
This is a FloatOutput that ignores all values sent to it.
FloatOutput.ignored.set(1.0f);
// is equivalent to absolutely nothing!
|
cell converts a FloatOutput into a FloatIO and sets its initial value.
This is equivalent to wrapping a new FloatCell around this FloatOutput, but can be more efficient in some cases, and is sometimes more concise.
FloatCell fc = new FloatCell();
FloatIO fc2 = fc.cell(0.0f);
// then fc2 == fc and fc is set to 0.0f
FloatOutput fo = /* some normal output */;
FloatIO fc3 = fo.cell(0.0f);
// then fc3 is equivalent to new FloatCell(fo)
5.4.6 FloatInputs
|
Provides a FloatInput that is always equal to value and never changes.
FloatInput i = FloatInput.always(17.0f);
// ...
i.get();
|
A FloatInput that is always equal to zero. Equivalent to FloatInput.always(0).
FloatInput i = FloatInput.zero;
// ...
i.get();
|
|
|
|
|
|
|
|
|
|
|
|
These arithmetic methods allow you to perform arithmetic on the values of FloatInputs.
For example, a.plus(b) has the value of a.get() + b.get() or a.get() + b depending on which version you use.
The Rev methods reverse the order of the operands. Since addition and multiplication are commutative, plusRev and multipliedByRev are unnecessary and do not exist.
a.plus(b)
a.minus(b)
a.minusRev(b)
a.multipliedBy(b)
a.dividedBy(b)
a.dividedByRev(b)
An example:
FloatInput real_drive_speed =
original_drive_speed.multipliedBy(is_kid_mode.toFloat(0.5f, 1.0f));
// and then:
real_drive_speed.get()
// is either
original_drive_speed.get() * 1.0f
// or
original_drive_speed.get() * 0.5f
|
|
|
|
|
|
|
|
| maximum) |
|
|
|
|
These comparison methods allow you to compare a channel against fixed or channel-based values.
a.atLeast(b) has the value of a.get() >= b. a.atMost(b) has the value of a.get() <= b. a.outsideRange(b,c) has the value of a.get() < b || a.get() > c. a.inRange(b,c) has the value of b <= a.get() && a.get() <= c.
BooleanInput low_on_pressure = pressure.atMost(40);
// and then
low_on_pressure.get()
// is equivalent to:
pressure.get() <= 40
|
Provides a FloatInput that is the negated version of this FloatInput.
FloatInput negated = original.negated();
// and then:
negated.get()
// is equivalent to
-original.get()
|
Provides a FloatInput that is either equivalent to this FloatInput or the negated version of this FloatInput, based on the negate argument.
FloatInput negated = original.negatedIf(neg);
// and then:
negated.get()
// is equivalent to
-original.get()
// if neg.get() == true or
original.get()
// if neg.get() == false
|
Provides a FloatInput that is the absolute value version of this FloatInput.
FloatInput abs = original.absolute();
// and then:
abs.get()
// is equivalent to
Math.abs(original.get())
|
Provides an EventInput that fires whenever this FloatInput changes by any amount.
FloatCell cell = new FloatCell(0);
EventInput change = cell.onChange();
// ...
cell.set(3.2f);
cell.set(3.2f);
cell.set(0.0f);
|
Provides an EventInput that fires whenever this FloatInput changes by at least magnitude from the last time that it changed.
FloatCell cell = new FloatCell(0);
EventInput change = cell.onChangeBy(1.0f);
// ...
cell.set(1.1f);
cell.set(30.0f);
cell.set(29.9f);
cell.set(29.1f);
cell.set(29.0f);
cell.set(29.9f);
cell.set(28.1f);
cell.set(27.9f);
|
Provides a FloatInput with the value of the original FloatInput, but with a deadzone applied.
The idea of a deadzone is that it chops out values close to zero.
Usually, a centered Joystick gives values of something like 0.047, not 0.000. You often want to interpret this as zero, because - for example - you usually might not want motors to move in this case.
By using a deadzone of 0.1, any values from around -0.09999999 to around 0.09999999 are converted to exactly zero.
FloatInput deadzoned = original.deadzone(0.1f);
// ...
original.set(1.0f);
original.set(0.1f);
original.set(0.0999f);
original.set(0.05f);
original.set(0.0f);
original.set(-0.05f);
original.set(-0.0999f);
original.set(-0.1f);
original.set(-1.0f);
|
|
|
|
Linearly maps from two configurable values to the range of zero to one. zeroV becomes 0.0f, and oneV becomes 1. (zeroV + oneV) / 2 would become 0.5f. This linear map extends through all of the real numbers: it is converted into simply an addition and a multiplication.
You can specify two constant values, two values based on channels, or a combination of the two.
If you had a pressure sensor that outputted 1.1 Volts for an empty tank, and 4.6 Volts for a full tank, you can map these to the range 0 to 1, and use them as a fraction.
You could say:
FloatInput sensor = /* ... */;
FloatInput pressureFraction = sensor.normalize(1.1f, 4.6f);
// pressureFraction would be 0 when the sensor's voltage is 1.1 volts,
// and it would be 1 when the sensor's voltage is 4.6 volts.
// if the sensor reported 4.8 volts, the fraction would be about 1.06.
|
Adds a ramping layer on top of a FloatInput.
The idea of ramping is that immediately changing from, for example, 0 feet per second to 16 feet per second can be very stressing on a robot’s drivetrain - or other similar components. You usually want to "ramp up" to the desired speed, for example at four feet per second per second.
You configure ramping with a maximum delta and an event to update ramping. The idea is that whenever updateWhen is fired, the actual output is moved closer to the most recent target value. The maximum amount by which it can change is limit.
A recommended ramping setup, in absence of actual testing, is withRamping(0.1f, FRC.constantPeriodic). Since FRC.constantPeriodic is a ten-millisecond loop, this means that it takes 100 milliseconds to go from stopped to full speed. You may need to tweak this lower or higher based on actual testing.
|
| target) |
Provides an event that performs incremental ramping based on this FloatInput and controlling target as an output.
The idea of ramping is that immediately changing from, for example, 0 feet per second to 16 feet per second can be very stressing on a robot’s drivetrain - or other similar components. You usually want to "ramp up" to the desired speed, for example at four feet per second per second.
You configure ramping with a maximum delta and an event to update ramping. The idea is that whenever the returned EventOutput is fired, the actual output is moved closer to the most recent target value. The maximum amount by which it can change is limit.
It’s usually easier to use FloatInput.withRamping or FloatOutput.addRamping, which are easier to use.
|
|
filterUpdates provides a FloatInput that follows the value of the original FloatInput while allow.get() == true, and holds the last value while allow.get() == false.
a.filterUpdatesNot(b) is equivalent to a.filterUpdates(b.not()).
BooleanCell unlocked = new BooleanCell();
FloatInput lockable = original.filterUpdates(unlocked);
// ...
unlocked.set(true);
original.set(5.0f);
original.set(2.0f);
original.set(-1.2f);
unlocked.set(false);
original.set(-0.5f);
original.set(6.1f);
original.set(3.1f);
unlocked.set(true);
original.set(0.3f);
original.set(0.8f);
unlocked.set(false);
original.set(1.0f);
5.5 Drive Code Implementations
Drive Code is the part of robot code that controls the drive base in response to the state of the driver’s Joystick.
There are three main types of drive code:
Tank Drive
Extended Tank Drive
Arcade Drive
Mecanum Drive
|
| leftOut, FloatOutput rightOut) |
This sets up tank drive code for the given axes and motors.
In Tank Drive, the driver has two Joysticks or axes, which can be moved forward and back to control the sides of the robot independently: the left axis controls the left side of the robot, and the right axis controls the right side of the robot.
FloatInput left_axis = FRC.joystick1.axisY();
FloatInput right_axis = FRC.joystick2.axisY();
FloatOutput left_motor = FRC.talon(0, FRC.MOTOR_FORWARD);
FloatOutput right_motor = FRC.talon(1, FRC.MOTOR_REVERSE);
// and then the key:
Drive.tank(left_axis, right_axis, left_motor, right_motor);
This method is very simple, and is equivalent to:
left_axis.send(left_motor);
right_axis.send(right_motor);
|
| FloatInput forward, FloatOutput leftOut, FloatOutput rightOut) |
This is similar to tankDrive, but takes an additional forward parameter that is added to the two axes, so that it can be used for exact movement forward and backward.
It is equivalent to:
left_axis.plus(forward).send(left_motor);
right_axis.plus(forward).send(right_motor);
|
| leftOut, FloatOutput rightOut) |
|
| rightOut) |
This sets up two axes to control the robot with Arcade Drive.
Arcade Drive is also known as single-joystick drive: it allows the robot to be controlled by being pushed in any direction. Forward for forward, backward for backward, left to turn left, and right to turn right. This is done based on one axis for left-right, and one axis for forward-backward. For some, it is the most intuitive control scheme.
FloatInput sideways_axis = FRC.joystick1.axisX();
FloatInput forward_axis = FRC.joystick1.axisY();
FloatOutput left_motor = FRC.talon(0, FRC.MOTOR_FORWARD);
FloatOutput right_motor = FRC.talon(1, FRC.MOTOR_REVERSE);
Drive.arcade(sideways_axis, forward_axis, left_motor, right_motor);
The second form of this method is the same as the first, but it uses the X and Y axis from a single Joystick.
Drive.arcade(FRC.joystick1, left_motor, right_motor);
This piece of drive code is fairly simple, and is equivalent to:
forward.plus(sideways).send(leftOut);
forward.minus(sideways).send(rightOut);
|
| rotate, FloatOutput leftFrontMotor, FloatOutput leftBackMotor, |
| FloatOutput rightFrontMotor, FloatOutput rightBackMotor) |
This sets up Mecanum Drive on four motors with channels to control forward-backward, left-right (strafe), and left-right (rotate.)
Mecanum drive works with very special wheels (mecanum wheels) to allow the robot to maneuver itself in any direction - not just forward, backward, and rotating. The code to do so is complicated, but it’s all included in this method.
The forward and strafe channels control motion in the XY plane, and the rotate channel controls rotation.
FloatInput strafe_axis = FRC.joystick1.axisX();
FloatInput forward_axis = FRC.joystick1.axisY();
FloatInput rotate_axis = FRC.joystick2.axisX();
// ... motors ...
Drive.mecanum(forward_axis, strafe_axis, rotate_axis,
left_front_motor, left_back_motor, right_front_motor, right_back_motor);
5.6 Autonomous and Instinct Modules
In autonomous mode, your code tends to need behavior somewhat different from teleoperated code. While control of actuators usually still needs the same dataflow setup, the sequencing required for autonomous mode is not easy to implement with dataflow.
Therefore, the CCRE provides an imperative autonomous mode system, using units of imperative code called InstinctModules.
An "imperative block" is the same as a flow block, but can also use methods marked as "imperative" methods, which are methods that may pause execution.
|
@Override |
protected void autonomousMain() throws Throwable { |
|
} |
}); |
This is the basic structure of your autonomous code, for when you only need a single sequence. Your imperative code goes in the internal block. The important difference about code here is that it is allowed to "block", or wait for an event to occur. Normal flow mode code cannot wait at all. To allow this to work, InstinctModule bodies run in separate threads.
When autonomous mode ends, your code will get aborted by an AutonomousModeOverException or an InterruptedException. Do not attempt to handle these exceptions.
An example of an autonomous mode:
FRC.registerAutonomous(new InstinctModule() {
@Override
protected void autonomousMain() throws Throwable {
leftMotor.set(0.5f);
rightMotor.set(0.5f);
waitForTime(5000);
leftMotor.set(0.0f);
rightMotor.set(0.0f);
}
});
This example autonomous mode runs drive motors forward at half speed for five seconds.
5.6.1 Autonomous methods
There are a variety of methods available that let your code wait for various conditions. While these methods are waiting, the rest of your code (that is outside of this InstinctModule) will continue normally.
These methods can only be used from within an InstinctModule.
|
|
This method waits for milliseconds milliseconds to elapse before continuing. Note that most waits, especially short waits, will be overapproximated: they will in practice wait slightly longer than you expect.
The second form, which takes a FloatInput, is approximately equivalent to waitForTime(seconds.get() * 1000). It exists entirely for convenience when using variable delays. Note that any changes to the value of the FloatInput that occur while this method is waiting will be ignored.
piston.set(true);
waitForTime(1000);
piston.set(false);
This example autonomous mode will extend a piston for one second, and then retract it.
|
|
|
|
waitUntil waits until BooleanInput’s value is true. It is not guaranteed to resume if the value changes to true and very quick changes back to false.
The second form of waitUntil is similar, but will also stop waiting once timeout seconds have ellapsed. It will return true if the condition became true, and false if it timed out instead.
waitUntilNot is the inverse: it waits until a BooleanInput’s value is false.
drive_motors.set(0.3f);
waitUntil(bumper_sensor);
drive_motors.set(0.0f);
Given that bumper_sensor has been defined outside of the InstinctModule as a touch sensor on the robot, this example autonomous mode will drive forward until it hits something.
|
This method waits for a specific event to fire. If the event fires before this method is called, it will be ignored.
drive_motors.set(0.3f);
waitForEvent(stop_button);
drive_motors.set(0.0f);
|
|
This method waits until one of a list of BooleanInputs becomes true. It also takes an optional timeout after which it will return regardless of the values of any of the BooleanInputs.
It returns the index of the BooleanInput that became true, starting at zero, or -1 on a timeout.
switch (waitUntilOneOf(5000, left_bumper, right_bumper)) {
case -1:
// ...
break;
case 0:
// ...
break;
case 1:
// ...
break;
}
|
|
These methods wait for a FloatInput to satisfy a particular comparison: either at least or at most some value.
waitUntilAtLeast(air_pressure, 70.0f);
5.6.2 A Dire Warning
Note, very specifically, that you cannot use setup mode methods inside an InstinctModule! This means that the following is NOT ALLOWED:
FRC.registerAutonomous(new InstinctModule() {
@Override
protected void autonomousMain() throws Throwable {
// ...
// THIS IS NOT ALLOWED. DO NOT EVER DO THIS.
waitUntil(some_condition.not());
// ...
}
});
You will have major issues if you attempt to do this. Instead, try this:
BooleanInput not_some_condition = some_condition.not();
FRC.registerAutonomous(new InstinctModule() {
@Override
protected void autonomousMain() throws Throwable {
// ...
// This is allowed.
waitUntil(not_some_condition);
// ...
}
});
In this specific case, the following would be recommended instead:
FRC.registerAutonomous(new InstinctModule() {
@Override
protected void autonomousMain() throws Throwable {
// ...
// This is recommended.
waitUntilNot(some_condition);
// ...
}
});
5.6.3 Instinct Modules outside of Autonomous
You can also use InstinctModules in other contexts besides autonomous modes, because they can be useful for semi-automatic control of parts of a robot.
BooleanCell run_this_instinct_module = new BooleanCell(false); |
|
@Override |
protected void autonomousMain() throws Throwable { |
|
} |
}); |
You can pass any BooleanInput you want to the constructor, and the module will run while that BooleanInput is true.
|
Equivalently to the previous example, you can construct an InstinctModule without any parameters and then call setShouldBeRunning later with the same value.
This means that FRC.registerAutonomous(module) is actually just a call to module.setShouldBeRunning!
|
Besides setShouldBeRunning, you can also use controlIO, which provides you with a BooleanIO representing whether the module should be running.
Note that you cannot provide multiple sources of control to an InstinctModule, across all of the three ways.
InstinctModule mod = new InstinctModule() { /* ... */ };
BooleanIO run = mod.controlIO;
run.set(true);
run.toggle();
run.toggle();
run.set(true);
run.set(false);
5.6.4 Instinct MultiModules
In progress.
5.7 Hardware Access
In progress.
5.8 Logging Framework
In progress.
5.9 Concurrency Best Practices
In progress.
5.10 StateMachine
In progress.
5.11 PID controllers
In progress.
5.12 Time and Timing
In progress. (Including Time, scheduling, Ticker, ExpirationTimer, PauseTimer.)
5.13 Extended Motors and CAN
In progress.
5.14 Remote Configuration
In progress.
5.15 Error Handling
In progress.
5.16 Control Bindings
In progress.
5.17 Tunable Values
In progress.
5.18 Deployment
How does this work behind the scenes?
First, this goes through the DeploymentEngine, which dispatches to the deploy() DepTask in the default Deployment class:
@DepTask
public static void deploy() throws Exception {
Artifact result = DepRoboRIO.buildProject(robotMain);
// code slightly abbreviated for clarity.
DepRoboRIO.RIOShell rshell = DepRoboRIO.discoverAndVerify(robot.RobotTemplate.TEAM_NUMBER);
rshell.archiveLogsTo(DepProject.root());
rshell.downloadAndStart(result);
}
To find the roboRIO, it checks roboRIO-NNNN-FRC.local (where NNNN is your team number), 172.22.11.2, 10.XX.YY.2, and roboRIO-NNNN.local (where XXYY is your team number.)
To be able to talk to your robot to change the code, your laptop needs to be on the same network as the robot, and you need to be able to connect to the robot. (Try running ping roboRIO-NNNN-FRC.local to see if it can be reached, if you’re having any problems.)
5.18.1 SSH
Sometimes, the robot breaks and you need to figure out what’s going on. I’m not going to go into all of the details of this but I’ll overview how you start.
You can connect to the roboRIO over SSH (aka Secure SHell) - you simply point your SSH client at roboRIO-NNNN-FRC.local (where NNNN is your team number) and enter the correct username and password.
On Linux or Mac OS X, you can do this from the command line with pre-installed tools:
On Windows, you can use PuTTY.
$ ssh admin@roboRIO-1540-FRC.local Password: $ do stuff on the robot
If you are familiar with nix-like systems, it may surprise you that the superuser account (uid 0) is named ’admin’, not ’root’.
The default password is the blank password, and the username is either ’admin’ or ’lvuser’.
Once you’re on the robot, you want to look in /home/lvuser, where you should find the user program and some related files, including the most recent log files.
5.18.2 Deployment API
In progress.
5.19 Storage Framework
In progress.
5.20 Serial I/O
In progress.
5.21 Networking Framework
In progress.
5.22 Phidget control panels
In progress.
5.23 Special sensors
(UM7LT.) In progress.
5.24 The Cluck Pub/Sub System
In progress.
5.24.1 Cluck with complexity
In progress.
5.25 Detailed guide to the Poultry Inspector
In progress.
5.26 Detailed guide to the Emulator
In progress.
6 CCRE recipes
In progress.
7 Versioning policies of the CCRE
In progress.
8 Javadoc Link
You can browse the Javadoc!
9 Maintainer’s guide
In progress.
9.1 Hardware access
The CCRE provides interfaces to the underlying hardware via WPILib’s JNI layer, which attaches to the WPILib Hardware Abstraction Layer (in C++), which attaches to the NI ChipObject proprietary library, which attaches to the NiFPGA interface to the FPGA (field-programmable gate array) device that manages the communication between the higher-level code and the I/O ports.
In other words, here’s how hardware access works (CCRE at the left - other systems also shown):

9.2 CCRE Philosophy
In progress.