Studica Robotics¶
Welcome to the Studica Robotics documentation page. Here you will find lots of information and tutorials regarding WorldSkills.
Hint
Python is currently only available on the Raspbian OS located on the VMX. Examples of using Python can be found in /usr/local/src/hal_python_examples/
Getting Started¶
Congratulations on joining the worldskills mobile robotics enviroment. On this documentation site you will find lots of content relating to the worldskills mobile robotics collection. This covers hardware and the software side of the collection.
Below are the first steps to getting your robot moving.
Software¶
Zero to Moving Robot¶
Software Setup¶
Important
Online download instructions removed due to bad link on VS Code install. Please use the offilne instructions and the USB in your kit.
Note
This setup is for Java/C++ only
Offline Installer¶
Important
If the USB in your kit does not contain the correct files, they can be downloaded here.
Warning
Windows 7: You must install the standalone version of .NET Version 4.62+ which can be found here. Before preceding!
The offline installation files will be located in the USB provided inside the collection. Locate and run the file named WPILibInstaller_Windows64-2020.3.2.exe
or WPILibInstaller_Windows32-2020.3.2.exe
based on your OS.

Installing for All Users will require admin privileges and install for all users on the machine.
Note
Software will be installed to C:\Users\Public\wpilib\YYYY
. YYYY Corresponds to the currently suppored year.
Installing VS Code
Due to licensing reasons with VS Code the installer does not contain it bundled in. To overcome this hit the Select/Download VS Code button.
![]()
This will open up the selector tool.
![]()
Select the Select Existing Download option and then select the file
OfflineVsCodeFiles-1.41.1.zip
. This will change back to the installer window and Execute Install can be run.![]()
What was just Installed
Visual Studio Code - The preferred and supported IDE for robot code development.
C++ Compiler - Toolchains required for building C++ code.
Java JDK/JRE - Specific version of the JDK/JRE that is used to build code.
Gradle - Specific version of Gradle used for building and deploying Java or C++ code
WPILib Tools - Tools used for robot enhancement
WPILib Dependencies - OpenCV, etc.
VS Code Extensions - WPILib extensions for robot code development
Important
The installer creates a separate version of VS Code for robotics development, even if VS Code is already installed locally. This is done to prevent workflows from breaking.
Note
This section and all macOS examples was completed and tested using macOS High Sierra
The macOS installation requires multiple individual steps to be completed.
VS Code Install
VS Code needs to be installed before the extensions are installed. The preferred version of VS Code is
1.41.1
which can be found in the provided USB stick in the macOS folder. The file is calledVSCode-darwin-stable.zip
. Double click on the zip folder if it’s not extracted already and drag theVisual Studio Code
into the Applications folder.![]()
After dragging to the Applications folder the VS Code Icon will be visible in Applications
![]()
WPILib Install
The WPILib file
WPILib_Mac-2020.3.2.tar.gz
can be found in the macOS folder in the USB provided.Double click on the
WPILib_Mac-2020.3.2.tar.gz
to remove the.gz
extension. Double click again on theWPILib_Mac-2020.3.2.tar
to remove the.tar
extension. Drag theWPILib_Mac-2020.3.2
folder into Downloads.![]()
Open the terminal and run these commands
mkdir wpilib/2020 cp -R ~/Downloads/WPILib_Mac-2020.3.2/ ~/wpilib/2020This will create the appropriate directories for WPILib and move the contents of
WPILib_Mac-2020.3.2
to the~/wpilib/2020
folder. When done the folder structure should look like this.![]()
The tools need to be updated so they can be used. Run the commands below to do so.
cd ~/wpilib/2020/tools python ToolsUpdater.pyAn example of using the terminal is shown below.
![]()
Installing Extensions
For VS Code to work properly the WPILib extensions need to be installed. Open VS Code and use the shortcut
Cmd-Shift-P
to open the command pallet. Type in the commandExtensions: Install from VSIX
.![]()
Navigate to the
~/wpilib/2020/vsCodeExtensions
folder, selectCpp.vsix
and hit install.![]()
Repeat this step for all the
vsix
files located in~/wpilib/2020/vsCodeExtensions
.They must be completed in this order:
Cpp.vsix
JavaLang.vsix
JavaDeps.vsix
JavaDebug.vsix
WPILib.vsix
Note
On the bottom right of the VS Code window popups will show saying if the installation is complete. Wait until there is a completed popup before preceding with the next extension. Also when installing the JavaLang.vsix there may be an error shown. This should be ignored for now
Getting VS Code to use Java 11
VS Code needs to be pointed to where the WPILib Java Home is. This is simply done by running the following command
WPILib: Set VS Code Java Home to FRC Home
.![]()
What was just Installed
Visual Studio Code - The preferred and supported IDE for robot code development.
C++ Compiler - Toolchains required for building C++ code.
Java JDK/JRE - Specific version of the JDK/JRE that is used to build code.
Gradle - Specific version of Gradle used for building and deploying Java or C++ code
WPILib Tools - Tools used for robot enhancement
WPILib Dependencies - OpenCV, etc.
VS Code Extensions - WPILib extensions for robot code development
Note
This section and all Linux examples was completed and tested using Ubuntu Desktop 20.04 LTS
The Linux installation requires multiple individual steps to be completed.
Installing VS Code
VS Code needs to be installed before the extensions are installed. The preferred version of VS Code is
1.41.1
which can be found in the Linux folder in the USB provided.Using the file
code_1.41.1-1576681836_amd64.deb
, right click on the file and selectOpen With Other Application
and choseSoftware Install
. When software install opens verify the Version number as1.41.1
and hitInstall
.![]()
There should be an Authentication prompt asking for the user to input their password. After the Authentication window the install will start and should only take a minute.
WPILib Installation
The WPILib file
WPILib_Linux-2020.3.2.tar.gz
can be found in the Linux folder on the provided USB. Place the file in the Downloads folder. Right click on theWPILib_Linux-2020.3.2.tar.gz
and selectExtract Here
. This will extract the contents and they can be moved.Open Terminal and run these commands.
mkdir -p ~/wpilib/2020 mv -v ~/Downloads/WPILib_Linux-2020.3.2/* ~/wpilib/2020 python3 ~/wpilib/2020/tools/ToolsUpdater.pyThis will move everything to the correct location and run the updater for the tools.
VS Code Extensions
For VS Code to be used for robotics the extensions from WPILib need to be installed.
Open VS Code using terminal by typing in
code
.To open the command palette use
Ctrl+Shift+P
or hitF1
.In the command palette run the command
Extensions: Install From VSIX
.![]()
Extensions can be found in
~/wpilib/2020/vsCodeExtensions
![]()
Install the Extensions in this Order
Cpp.vsix
JavaLang.vsix
JavaDeps.vsix
JavaDebug.vsix
WPILib.vsix
Note
After installing an extension it’s recommended to close and reopen VS Code.
Getting VS Code to use Java 11
VS Code needs to be pointed to where the WPILib Java Home is. This is simply done by running the following command
WPILib: Set VS Code Java Home to FRC Home
.![]()
Vulkan Installation
For the simulation GUI to run, Vulkan is required. To install Vulkan there is a
libvulkan1_1.2.131.2-1_amd64.deb
file located in the Linux folder on the USB. Right click on the file and selectOpen With Other Application
and choseSoftware Install
. This will then bring up the software install screen where you will hitInstall
, and the driver will then proceed to install.
What was just Installed
Visual Studio Code - The preferred and supported IDE for robot code development.
C++ Compiler - Toolchains required for building C++ code.
Java JDK/JRE - Specific version of the JDK/JRE that is used to build code.
Gradle - Specific version of Gradle used for building and deploying Java or C++ code
WPILib Tools - Tools used for robot enhancement
WPILib Dependencies - OpenCV, etc.
VS Code Extensions - WPILib extensions for robot code development
Vulkan - Low overhead graphics API
Programming¶
Getting to Know VS Code¶
This guide will show you how to navigate VS Code along with some helpful hints
Microsoft’s Visual Studio Code is the supported IDE for C++ and Java development for WorldSkills Mobile Robotics. This section introduces some of the basics of using Visual Studio Code and the WPILib extension.
Welcome Page¶

When Visual Studio Code first opens, you are presented with a Welcome page. On this page you will find some quick links that allow you to customize Visual Studio Code as well as several links to help documents and videos that may help you learn about the basics of the IDE as well as some tips and tricks.
You may also notice a small WPILib logo way up in the top right corner. This is one way to access the features provided by the WPILib extension (discussed further below).
The icons on the left edge make up the “Activity Bar”. Clicking on the icons will open the “Sidebar” which offers more functions.
The icon on top opens the “Explorer” sidebar which shows all the files that you can edit. This should already be open when you open the IDE using the FRC VSCode shortcut.
The squarish icon on bottom opens the “Extension” sidebar, which lets you manage software extensions for VSCode.
Hitting control-B (or command-B on a Mac) will toggle away the sidebar, so you can make full use of screen space.
Holding the control key down and hitting the back-quote character will open a terminal panel on the lower part of the screen. (The back-quote character is usually in the upper left corner of your keyboard, just under the Esc key). You’ll probably need to get comfortable with terminals and command lines when developing with VSCode.
Hitting control-back-quote again will toggle the terminal away.
The F1 key will open the “Command Palette” at the top of the screen. Also, you can hit control-shift-P on Windows or Linux. Macintosh users can hit Command-Shift-P. The Command Palette lets you invoke special commands inside VSCode. It’s pretty good about offering you suggestions for what you want to do. Many of these functions are available from menus or from keyboard shortcuts, but you’ll find yourself using the Command Palette a lot.
Almost everything in VSCode is going to be controlled through the command palette, everything from making a new project in WPILib to changing the setting of the editor, to debug, to building, etc.
The FRC extension for WPILib adds new commands to the Command Palette for building robot code and reconfiguring your robot development environment.
Everything that you add to the editor is going to be done though extensions, so everything that you want to install for example such as C++ or the debugger for Java is also installed here. Also, the WPILib which is the basic library.
The second portion if the UI will be the sidebar, so in the sidebar you have all your files, so any files that you have open, or projects that you have open. The folder will be here which is basically your project and anything that you are currently editing or have open will appear as well.
Creating a Project¶
This guide will show ho to create a Java or C++ project for use in robotics
Open the appropriate VS Code FRC VS Code 2020
and hit CTRL + Shift + P
. This will open the command palette in VS Code. Consult the Getting to know VS Code section if you are unsure of what to do!
In the command palette search for the command WPILib: Create a new project
. An example is shown below.

This will open the project creator window

Start by clicking on the Select a project type (Example or Template) button and select
template
Chose a programing language by selecting the Select a language button. In this case
Java
For Select a project base select
Command Robot
Chose a folder location to store the project
Enter an appropriate project name
Enter your team number
An example of a filled out project creator is shown below

Hit Generate Project to finilize the creation of the project. There will be a prompt as shown to open in a new window or the current window. A new window will open another instance of VS Code whereas the current window will close the any open project you have and place this project in the currently opened VS Code window.

Note
The project will then automaticaly build the for the first time. If the build is not successful constult the troubleshooting section
The VS Code window should now look like this and a Java project has been created!

Caution
Anti-virus might need to be disabled in order for a C++ project to compile
Open the appropriate VS Code FRC VS Code 2020
and hit CTRL + Shift + P
. This will open the command pallete in VS Code. Consult the Getting to know VS Code section if you are unsure of what to do!
In the command pallete search for the command WPILib: Create a new project
. An example is shown below.

This will open the project creator window

Start by clicking on the Select a project type (Example or Template) button and select
template
Chose a programing language by selecting the Select a language button. In this case
cpp
For Select a project base select
Command Robot
Chose a folder location to store the project
Enter an appropriate project name
Enter your team number
An example of a filled out project creator is shown below

Hit Generate Project to finilize the creation of the project. There will be a prompt as shown to open in a new window or the current window. A new window will open another instance of VS Code whereas the current window will close the any open project you have and place this project in the currently opened VS Code window.

Note
The project will then automaticaly build the for the first time. If the build is not successful constult the troubleshooting section
The VS Code window should now look like this and a Java project has been created!

Configuring the project for VMXpi¶
This guide will show the steps required to configure the project to be deployed to the VMXpi.
Important
This extension is also used to cache updates to be sent to the VMXpi. It is important to make sure this extension and the version of build.gradle
is up to date.
Installing VMXpi Extension¶
A VSCode extension was created to manage the build.gradle
file in the project folder. The extension allows for the project to swap between the roboRIO and VMXpi as targets for deployment.
To install the extension head over to the Extensions tab on the left panel or hit Ctrl + Shift + X
.

In the search bar, search for VMX
.

Click on the extension to open the extension page in the main window.

Click on Install
to install the extension.
The installation will be successful when you see the VMXpi logo pop up next to the WPILib logo.

Using the Extension¶
There are four commands in the extension palette.

Update WPILib Version
will update to the current GradleRIO version for the VMXpiChange the deploy target to VMX-Pi (from RoboRIO)
will update thebuild.gradle
file to use the VMXpi as a targetChange the deploy target to RoboRIO (from VMX-Pi)
will update thebuild.gradle
file to use the roboRIO as a targetsVerify the Project's build.grade file
checks if everything is good to go with the file
To switch the project over for the VMXpi, the command Change the deploy target to VMX-Pi (from RoboRIO)
needs to be run. After running, it will auto rebuild the project and cache any libraries that are missing.
Installing the Raspbain Toolchain (c++ only)¶
For c++ the Raspbian Toolchain is required for building and deploying to the VMX-pi.
Option 1¶
The first option is to use the VMX-pi Extension. Open the extension and use the command Change the deploy target to VMX-pi (from RoboRIO)
. This process will then install a bunch of files including the toolchain.
Option 2¶
The second option is to download toolchain manually. Open the WPILib extension and use the run a command in gradle command.

In the window then use the command installRaspbianToolchain

Base Project Outline¶
Robot¶
There are multiple sections of Robot.java
, below discusses each one and the purpose
Title Block
1 2 3 4 5 6 | /*----------------------------------------------------------------------------*/ /* Copyright (c) 2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ |
Hint
The title block is important as it displays licenses, authors, last edited dates, etc…
Some nice things to have in any title block would be the author
and date
. This would provide the next person coming to see who wrote what and when they wrote it.
Imports
The import section holds all the imports of various libraries that are required for use in the current class.
8 9 10 11 12 13 14 15 16 | package frc.robot; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj.command.Command; import edu.wpi.first.wpilibj.command.Scheduler; import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import frc.robot.commands.ExampleCommand; import frc.robot.subsystems.ExampleSubsystem; |
Class Declaration
In every Java program the class needs to be declared. Below is the class declaration for the Robot.java
class.
18 19 20 21 22 23 24 25 | /** * The VM is configured to automatically run this class, and to call the * functions corresponding to each mode, as described in the TimedRobot * documentation. If you change the name of this class or the package after * creating this project, you must also update the build.gradle file in the * project. */ public class Robot extends TimedRobot { |
Declaring Objects
This section declares objects that are required for use later in the class.
26 27 28 29 30 | public static ExampleSubsystem m_subsystem = new ExampleSubsystem(); public static OI m_oi; Command m_autonomousCommand; SendableChooser<Command> m_chooser = new SendableChooser<>(); |
Robot Initialization
This is where any code is to be run when the robot is first booting up.
32 33 34 35 36 37 38 39 40 41 42 | /** * This function is run when the robot is first started up and should be * used for any initialization code. */ @Override public void robotInit() { m_oi = new OI(); m_chooser.setDefaultOption("Default Auto", new ExampleCommand()); // chooser.addOption("My Auto", new MyAutoCommand()); SmartDashboard.putData("Auto mode", m_chooser); } |
Robot Periodic
Warning
Code here is run every robot packet and is not controlled by the Enable/Disable buttons.
Robot periodic is a good section to add code for diagnostics or anything that requires constant polling.
44 45 46 47 48 49 50 51 52 53 54 | /** * This function is called every robot packet, no matter the mode. Use * this for items like diagnostics that you want ran during disabled, * autonomous, teleoperated and test. * * <p>This runs after the mode specific periodic functions, but before * LiveWindow and SmartDashboard integrated updating. */ @Override public void robotPeriodic() { } |
Disabled Initialization
When ever the robot is put into a disabled state it enters here first.
56 57 58 59 60 61 62 63 | /** * This function is called once each time the robot enters Disabled mode. * You can use it to reset any subsystem information you want to clear when * the robot is disabled. */ @Override public void disabledInit() { } |
Disabled Periodic
Code that will run every robot packet when the robot is disabled.
65 66 67 68 | @Override public void disabledPeriodic() { Scheduler.getInstance().run(); } |
Autonomous Initialization
Code that is run at the start of an autonomous run.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | /** * This autonomous (along with the chooser code above) shows how to select * between different autonomous modes using the dashboard. The sendable * chooser code works with the Java SmartDashboard. If you prefer the * LabVIEW Dashboard, remove all of the chooser code and uncomment the * getString code to get the auto name from the text box below the Gyro * * <p>You can add additional auto modes by adding additional commands to the * chooser code above (like the commented example) or additional comparisons * to the switch structure below with additional strings & commands. */ @Override public void autonomousInit() { m_autonomousCommand = m_chooser.getSelected(); /* * String autoSelected = SmartDashboard.getString("Auto Selector", * "Default"); switch(autoSelected) { case "My Auto": autonomousCommand * = new MyAutoCommand(); break; case "Default Auto": default: * autonomousCommand = new ExampleCommand(); break; } */ // schedule the autonomous command (example) if (m_autonomousCommand != null) { m_autonomousCommand.start(); } } |
Autonomous Periodic
Code that is run every robot packet during the autonomous run.
Teleop Initialization
Code that is run at the start of a teleoperated run.
106 107 108 109 110 111 112 113 114 115 | @Override public void teleopInit() { // This makes sure that the autonomous stops running when // teleop starts running. If you want the autonomous to // continue until interrupted by another command, remove // this line or comment it out. if (m_autonomousCommand != null) { m_autonomousCommand.cancel(); } } |
Teleop Periodic
Code that is run every robot packet when in a periodic run.
117 118 119 120 121 122 123 | /** * This function is called periodically during operator control. */ @Override public void teleopPeriodic() { Scheduler.getInstance().run(); } |
Test Periodic
Code that is run every robot packet when in a test run.
125 126 127 128 129 130 | /** * This function is called periodically during test mode. */ @Override public void testPeriodic() { } |
Constants¶
The Constants.java
class is one of the most used classes in the whole project. Although there is nothing inside the base class, once filled with constants it makes changing something electrical on the robot very easy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /*----------------------------------------------------------------------------*/ /* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ package frc.robot; /** * The Constants class provides a convenient place for teams to hold robot-wide numerical or boolean * constants. This class should not be used for any other purpose. All constants should be * declared globally (i.e. public static). Do not put anything functional in this class. * * <p>It is advised to statically import this class (or one of its inner classes) wherever the * constants are needed, to reduce verbosity. */ public final class Constants { } |
Whats nice about the constants class is that we can map out a whole robot here and if a change is ever made electrically, such as putting motor 0 in motor 1’s port its as simple as changing the constant here and not having to do it in every class that we have.
Below is an example of a constants class used in a robot.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | package frc.robot; public class ElectricalConstants { /** * Drive Constants */ //Right Drive public static final int RIGHT_DRIVE_FRONT = 3; public static final int RIGHT_DRIVE_BACK = 2; //Left Drive public static final int LEFT_DRIVE_FRONT = 0; public static final int LEFT_DRIVE_BACK = 1; /** * Drive Encoders */ //Right Encoder public static final int RIGHT_DRIVE_FRONT_ENCODER_A = 2; public static final int RIGHT_DRIVE_FRONT_ENCODER_B = 3; public static final int RIGHT_DRIVE_BACK_ENCODER_A = 0; public static final int RIGHT_DRIVE_BACK_ENCODER_B = 1; //Left Encoder public static final int LEFT_DRIVE_FRONT_ENCODER_A = 4; public static final int LEFT_DRIVE_FRONT_ENCODER_B = 5; public static final int LEFT_DRIVE_BACK_ENCODER_A = 6; public static final int LEFT_DRIVE_BACK_ENCODER_B = 7; /** * Encoder Constants */ //Radius of drive wheel in inches public static final int wheelRadius = 2; //Encoder Pulses per rotation public static final int pulsePerRotation = 1120;//280 //Gear Ratio between encoder and wheel public static final double gearRatio = 1/1; //Pulse per Rotation ofthe wheel public static final double encdoerPulseRatio = pulsePerRotation * gearRatio; //Distance per tick public static final double encoderDistPerTick = (Math.PI * 2 * wheelRadius) / encdoerPulseRatio; //Encoder Reverse public static final boolean rightDriveEncoderReverse = false; public static final boolean leftDriveEncoderReverse = false; } |
Adding Vendor Libraries¶
Adding a vendor library is simple. Open VS Code then the command palette using Ctrl+Shift+P
or F1
and type the following command WPILib: Manage Vendor Libraries
. This will open a list of choices as shown.

Manage current libraries - Shows the current libraries installed and allows you to remove them.
Check for updates(offline) - will check if there is an update for a library in the offline folder.
Check for updates(online) - will check if there is an update for a library online.
Install new libraries(offline) - will install a new library in the offline folder.
Install new libraries(online) - will install a new library from the internet.
For this guide select Install new libraries(online).
There are multiple vendor libraries available. The ones supported on the VMXpi are listed below.
Kauailabs’ NavX Library
Studica’s Titan Library
Autonomous¶
This section contains everything required to creating an autonomous routine for a robot. Start by reading over the auto basics before looking at the examples. This will help you better understand the command base autonomous.
Auto Basics¶
Below are some of the basic concepts to command based autonomous. With some execptions each concept has a Java and C++ example.
Command Groups¶
Sequential Command Group¶
The SequentialCommandGroup
is the most popular command group. Works by running a list of commands in sequential order. Starts with the first command in the list then the second and so on.
Warning
The SequentialCommandGroup
will not finish unless all the commands finish. Also if a command in the list does not finish the next command in line will not start.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; public class Example extends SequentialCommandGroup { public Example() { addCommands( //Drive Forward new DriveForward(), //Drive Reverse new DriveReverse()); } } |
1 2 3 4 5 6 7 8 9 10 11 12 | #prama once #include <frc2/command/CommandHelper.h> #include <frc2/command/SequentialCommandGroup.h> class Example : public frc2::CommandHelper<frc2::SequentialCommandGroup, Example> { public: Example(void); }; |
1 2 3 4 5 6 7 8 9 10 11 12 | #include "commands/Example.h" Example::Example(void) { AddCommands( //Drive Forward DriveForward(), //Drive Backwards DriveReverse()); } |
In the above example using SequentialCommandGroup
the command DriveForward()
will be exeuted first and when complete the command DriveReverse()
will be executed.
Note
If DriveForward()
does not end then DriveReverse()
will never start.
Parallel Command Group¶
The ParallelCommandGroup
is just like the SequentialCommandGroup
except that all the commands run at the same time. The command group will only finish when all commands are finished.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import edu.wpi.first.wpilibj2.command.ParallelCommandGroup; public class Example extends ParallelCommandGroup { public Example() { addCommands( //Drive Forward new DriveForward(), //Drive Reverse new DriveReverse()); } } |
1 2 3 4 5 6 7 8 9 10 11 12 | #prama once #include <frc2/command/CommandHelper.h> #include <frc2/command/ParallelCommandGroup.h> class Example : public frc2::CommandHelper<frc2::ParallelCommandGroup, Example> { public: Example(void); }; |
1 2 3 4 5 6 7 8 9 10 11 12 | #include "commands/Example.h" Example::Example(void) { AddCommands( //Drive Forward DriveForward(), //Drive Backwards DriveReverse()); } |
In the above example using ParallelCommandGroup
the commands DriveForward()``and ``DriveReverse()
will be executed at the same time.
Note
If DriveForward()
and DriveReverse()
do not complete then whatever calls Example() will never move on.
Parallel Race Group¶
The ParallelRaceGroup
is similar to the ParallelCommandGroup
except that a race condition is being created. All commands start at the same time but when one command is finished it interrupts all the other commands running and ends the command group.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import edu.wpi.first.wpilibj2.command.ParallelRaceGroup; public class Example extends ParallelRaceGroup { public Example() { addCommands( //Drive Forward new DriveForward(), //Drive Reverse new DriveReverse()); } } |
1 2 3 4 5 6 7 8 9 10 11 12 | #prama once #include <frc2/command/CommandHelper.h> #include <frc2/command/ParallelRaceGroup.h> class Example : public frc2::CommandHelper<frc2::ParallelRaceGroup, Example> { public: Example(void); }; |
1 2 3 4 5 6 7 8 9 10 11 12 | #include "commands/Example.h" Example::Example(void) { AddCommands( //Drive Forward DriveForward(), //Drive Backwards DriveReverse()); } |
In the above example using ParallelRaceGroup
the commands DriveForward()
and DriveReverse()
will be executed at the same time.
Note
If DriveForward()
or DriveReverse()
complete before the other than the other will be interrupted and stop running.
Java Only Benefits¶
Java has some unique features in it’s language base and one of those features is Static Factory Methods. This allows a simplar way to declare command groups.
The sequence()
static method allows for a sequential command group.
The parallel()
static method allows for a parallel command group
The race()
static method allows for a parallel race group.
1 2 3 4 5 6 7 8 9 10 11 12 | import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; public class Example extends SequentialCommandGroup { public Example() { addCommands( race(new DriveForward(), new ElevatorUP()), parallel(new ShootObject(), new LineupGoal()), sequence(new DriveReverse(), new StrafeRight())); } } |
If we analyze this command group we can break down what is happening.
We have
race(new DriveForward(), new ElevatorUP())
this will create aParallelRaceGroup
that has the two commandsDriveForward()
andElevatorUP()
run at the same time in a race. When one finishes it will stop the other.As the main command group is the
SequentialCommandGroup
we then move on to the next command which is aParallelCommandGroup
.The
ParallelCommandGroup
ofparallel(new ShootObject(), new LineupGoal())
tells us thatShootObject()
andLineupGoal()
happen at the same time. When both are complete it will pass to the next command group.The last command group here is the
sequence(new DriveReverse(), new StrafeRight())
which is also aSequentialCommandGroup
. This group is telling the robot toDriveReverse()
and when that is done toStrafeRight()
.
Conditional Command¶
The ConditionalCommand
will run one command or another based on a condtion that must be met.
Example¶
1 2 3 4 5 | // Base parameters new ConditionalCommand(trueCommand, falseCommand, boolean condition); // Use case new ConditionalCommand(new DriveForward(), new DriveReverse(), isLimitHit()); |
1 | frc2::ConditionalCommand(trueCommand, falseCommand, [&limit] {return isLimitHit();}); |
Wait Command¶
The WaitCommand()
is useful for a when a timed wait period is required.
Example¶
1 2 | // Waits 10 seconds new WaitCommand(10); |
1 2 | // Waits 10 seconds frc2::WaitCommand(10.0_s); |
Wait Until Command¶
The WaitUntilCommand
is an upgraded version of WaitCommand
as a boolean condition can be added.
Example¶
1 2 3 4 5 | // Waits 10 seconds new WaitUntilCommand(10); // Waits for limit switch to be true new WaitUntilCommand(isLimitHit()); |
1 2 3 4 5 | // Waits 10 seconds frc2::WaitUntilCommand(10.0_s); // Waits for limit switch to be true frc2.WaitUntilCommand([&Limit] {return isLimitHit();}); |
Command Decorators¶
Command decorators take the base command and add additonal functionalities to it.
withTimeout()¶
Adds a timeout to the command. When the timeout expires the command will be interrupted and end.
1 2 | // Add a 10 second timeout new command.withTimeout(10); |
1 2 | // Add a 10 second timeout command.WithTimeout(10.0_s); |
withInterrupt()¶
Adds a condition that will interrupt the command.
1 | new command.withInterrupt(isLimitHit()); |
1 | command.WithInterrupt([&limit]{return isLimitHit();}); |
andThen()¶
Adds a method that is executed after the command ends.
1 | new command.andThen(() -> System.out.println("Command Finished")); |
1 | command.AndThen([] {std::cout<< "Command Finished";}); |
beforeStarting()¶
Adds a method that is executed before the command starts.
1 | new command.beforeStarting(() -> System.out.println("Command Starting")); |
1 | command.BeforeStarting([] {std::cout<< "Command Starting"}); |
Flowcharting¶
Coming soon!!!
Simple Auto Example¶
Note
It’s best to download the example and follow along. The example project can be downloaded here.
This simple auto example will show the steps required to creating a very simple autonomous to spin the motor at 50% speed for 5 seconds. Below are the indivdual steps required to creating the simple autonomous routine.
Constants¶
The Constants
class will hold the two constants required for the Motor definitions on the Titan Quad Motor Controller.
1 2 3 4 5 6 7 8 9 10 | package frc.robot; public final class Constants { /** * Motor Constants */ public static final int TITAN_ID = 42; public static final int MOTOR = 2; } |
The constant
TITAN_ID
is the CAN ID for the Titan Quad. Out of box the ID is 42The constant
MOTOR
is the motor port on the Titan Quad that the motor is attached to. In this case that is M2.
DriveTrain¶
For the autonomous to work a subsystem needs to be defined and implemented. For this example only a single motor needs to be created and a way to set the motor speed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | package frc.robot.subsystems; //Vendor imports import com.studica.frc.TitanQuad; //WPI imports import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc.robot.Constants; /** * DriveTrain class * <p> * This class creates the instance of the Titan and enables and sets the speed of the defined motor. */ public class DriveTrain extends SubsystemBase { /** * Motors */ private TitanQuad motor; /** * Constructor */ public DriveTrain() { //Motors motor = new TitanQuad(Constants.TITAN_ID, Constants.MOTOR); } /** * Sets the speed of the motor * <p> * @param speed range -1 to 1 (0 stop) */ public void setMotorSpeed(double speed) { motor.set(speed); } } |
Lines
4
-8
are the imports required. The TitanQuad library for the motor, SubsystemBase for the subsystem, and Constants for the motor parameters.Line
20
is the creation of the motor object.Lines
25
-29
is the constructor required for creating an instance of the subsystem.Line
28
creates a new instance of the TitanQuad and assigns that instance to the M2 port.Line
36
-39
is the mutator method to set the speed of the motor.
AutoCommand¶
The AutoCommand class is used to create the inline command stackup for autonomous routines. To learn more about inline command stackups have a look at the Auto Basics section.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package frc.robot.commands.auto; //WPI imports import edu.wpi.first.wpilibj2.command.Command; import edu.wpi.first.wpilibj2.command.SequentialCommandGroup; /** * AutoCommand Class * <p> * This class is used to create the inline command stackup for autonomous routines */ public abstract class AutoCommand extends SequentialCommandGroup { /** * Base Constructor */ public AutoCommand() { super(); } /** * Overloaded Constructor to create inline commands * <p> * @param cmd The cmd to be executed */ public AutoCommand(Command ... cmd) { super(cmd); } } |
Lines
4
&5
are the required imports for Command and SequentialCommandGroup.Lines
17
-20
are the base constructor with no parameters.Lines
27
-30
is the constructor that will be used for most if not all autonomous routines. The parameter will be the inline string of commands to be run.
SimpleDrive¶
SimpleDrive
is the command that controls the subsystem output. Commands can be called again and again which makes them perfect for autonomous routines.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | package frc.robot.commands.driveCommands; //WPI imports import edu.wpi.first.wpilibj2.command.CommandBase; //RobotContainer import import frc.robot.RobotContainer; //Subsystem imports import frc.robot.subsystems.DriveTrain; /** * SimpleDrive class * <p> * This class drives a motor at 50% speed until the command is ended */ public class SimpleDrive extends CommandBase { //Grab the subsystem instance from RobotContainer private static final DriveTrain drive = RobotContainer.drive; /** * Constructor */ public SimpleDrive() { addRequirements(drive); // Adds the subsystem to the command } /** * Runs before execute */ @Override public void initialize() { } /** * Called continously until command is ended */ @Override public void execute() { drive.setMotorSpeed(0.5); // Set motor speed to 50% } /** * Called when the command is told to end or is interrupted */ @Override public void end(boolean interrupted) { drive.setMotorSpeed(0.0); // Stop motor } /** * Creates an isFinished condition if needed */ @Override public boolean isFinished() { return false; } } |
Lines
4
-10
are the imports required.Line
20
grabs the instance of theDriveTrain
subsystem defined and instantiated inRobotContainer
.Lines
25
-28
are the constructor.Line
27
says that this command requires the subsystemdrive
which is the handle for the subsystemDriveTrain
.Lines
33
-37
is the initialize section of the command. In this case there is nothing to initialize so it is left blank.Lines
42
-46
is the execute section of the command. As long as the command is active anything in here will run every robot packet (20ms).Line
45
is setting the motor speed to0.5
which is equal to50%
speed.Lines
51
-55
is the end section of the command. When the command is scheduled to end or is interrupted this method is called.Line
54
sets the motor speed to0.0
this will stop the motor. It is a good idea to always add a stop motor instruction here unless its not required.Lines
60
-64
is the isFinished section of the command. This method can be called to check if the command is finished or not. Useful if you wanted to put a stop condition based on sensor feedback here. For example using the sharp sensor to sence distance and it hits the threshold.
DriveMotor¶
The DriveMotor
class is the class used to create and send the inline auto command to AutoCommand
. The DriveMotor
class is also the command that will be called by the auto scheduler.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package frc.robot.commands.auto; // import the SimpleDrive command import frc.robot.commands.driveCommands.SimpleDrive; /** * DriveMotor class * <p> * This class creates the inline auto command to drive the motor */ public class DriveMotor extends AutoCommand { /** * Constructor */ public DriveMotor() { /** * Calls the SimpleDrive command and adds a 5 second timeout * When the timeout is complete it will call the end() method in the SimpleDrive command */ super(new SimpleDrive().withTimeout(5)); } } |
Line
4
is the only import required for this auto command.Lines
16
-23
is the constructor and auto command.Line
22
is the inline auto command. In this case we are running an instance ofSimpleDrive
and giving it a5
second timeout. For this example the timeout gives us the 5 second runtime we are looking for in running the motor at 50% for 5 seconds.
Note
A command can end before a timeout. Sometimes it’s a good idea to add timeouts in case a sensor gives bad data or there was a unhandled error.
RobotContainer¶
The RobotContainer
holds the instances of subsystems and helps organize commands.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | /*----------------------------------------------------------------------------*/ /* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ package frc.robot; //Java imports import java.util.HashMap; import java.util.Map; //WPI imports import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.Command; //Command imports import frc.robot.commands.auto.AutoCommand; import frc.robot.commands.auto.DriveMotor; //Subsystem imports import frc.robot.subsystems.DriveTrain; /** * RobotContainer Class * <p> * This class is used for creating the instances of subsystems and organizing commands */ public class RobotContainer { //Define subsystems public static DriveTrain drive; //Define the auto selector public static SendableChooser<String> autoChooser; public static Map<String, AutoCommand> autoMode = new HashMap<>(); /** * Constructor */ public RobotContainer() { //Create an instance of subsystems drive = new DriveTrain(); } /** * Used for getting the autonomous command to be executed * @return autonmous command to execute */ public Command getAutonomousCommand() { String mode = RobotContainer.autoChooser.getSelected(); SmartDashboard.putString("Chosen Auto Mode", mode); return autoMode.getOrDefault(mode, new DriveMotor()); } } |
Lines
11
-24
are the required imports.Lines
34
-38
create the objects for required subsystems and functions.Lines
43
-47
is theRobotContainer
constructor.Line
46
creates the instance of the subsystemDriveTrain
.Lines
53
-58
is the call to return the autonomous routine that we want the scheduler to run.
Robot¶
There are only few changes required in the main Robot
class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import edu.wpi.first.wpilibj.smartdashboard.SendableChooser; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import frc.robot.commands.auto.DriveMotor; /** * This function is called once each time the robot enters Disabled mode. */ @Override public void disabledInit() { //Check to see if autoChooser has been created if(null == RobotContainer.autoChooser) { RobotContainer.autoChooser = new SendableChooser<>(); } //Add the default auto to the auto chooser RobotContainer.autoChooser.setDefaultOption("Drive Motor", "Drive Motor"); RobotContainer.autoMode.put("Drive Motor", new DriveMotor()); SmartDashboard.putData(RobotContainer.autoChooser); } |
Lines
1
-3
are the required imports.Lines
8
-20
are the modifications made to the previously emptydisabledInit()
.Lines
12
-15
check if autoChooser has beend created yet. If not then it creates autoChooser as a new SendableChooser.Lines
17
-19
add the default option to autoChooser and add autoChooser to the smartdashboard.
Running an Autonomous Routine¶
The code for the Simple Auto Example is now complete. However, we would now like to test and make sure our autonomous routine works as it should.
Deploy the code to the VMX¶
Connect to the VMX and deploy the code. To deploy code hit F1
and type in WPILib: Deploy Robot Code
. This will then deploy the code to the VMX. A successful deploy will look like this.
Opening Control Center¶
Open up Control Center. Enter your robots IP address. Out of box IP address is 10.12.34.2.
This will also open up shuffleboard automatically and connect it to the robot server.
Operating Control Center¶
Control Center and shuffleboard should now be open and viewable.
On the shuffleboard window only SendableChooser[0]
will be visable. Chosen Auto Mode
will become visable after the robot is enabled at least once.
Hit a
on the keyboard to switch to Autonomous mode. The Control Center should show that it’s in Autonomous Disabled mode.
If you click on the drop down of SendableChooser[0]
you can see that there is only one option. In future autonomous examples we will be adding more options and they will be selectable in this drop down.
Running the Autonomous Routine¶
While in autonmous mode hit e
to enable the robot and start the autonomous routine. The motor should now spin at 50% for 5 seconds based on the timeout that we set earlier. After 5 seconds the motor will stop. Notice that the robot is still enabled even though the motor has stopped and there is no code running. Hit d
to disabled the robot again. If you hit e
again the motor will spin again for 5 seconds.
Going Further¶
Try modifing the
DriveMotor
command to run the motor for a longer period of time.Try modifing the
SimpleDrive
command to allow for a custom speed to be passed through from theDriveMotor
command.Enabled the robot and while the motor is still spinnig hit the disabled key
d
and see what happens.
Attention
The LabVIEW image only currently works on Raspberry Pi 4’s that do not have a UKCA marking on the bottom. A new image is in the works but will not be available until late Q4 of 2023.
LabVIEW Setup¶
Installing LabVIEW on VMX¶
LabVIEW for VMX requires a different SD-card image than that which comes standard on the VMX.
Flashing the image¶
Once downloaded a flashing software is required to flash the image to the SD Card. The recommended software to do this is Etcher.
Important
It is highly recommended to use a Samsung 32GB or 64GB EVO Plus micro SD card.
To start flashing the SD card, first plug the SD card into your computer. Open Etcher
, you will notice that it has auto-detected the SD card. If it has not detected the SD card, you can manually select and find it.

Hit Select image
and find the HG_WSI_X.X.X.X-XXX.zip
file that was downloaded before.

Flash
will now be available. Hit Flash to start flashing the SD card image to the SD card. Note this can take a while depending on your computer.

After flashing, Etcher will automatically start to validate the flash to ensure that the flash was successful.

When complete, the SD card will be auto ejected and can be stuck directly back into the VMX.

Installing LabVIEW on Computer¶
LabVIEW for VMX uses the 2020 LabVIEW Community Edition.
Downloading the installation package¶
Note
The LabVIEW download is 1.91GB
and can be downloaded here
This download links to a specific version of LabVIEW tested to work with the image on the VMX.
Installation¶
Important
The LabVIEW Community Edition will only install on the C
drive. Ensure that there is sufficient space before installing.
Extract the contents of the
ni-labview-2020-community-86_xxx.iso
to an empty folder.Right-click on the
Install.exe
and run as administrator.Go through the license agreement, accept the conditions, and hit
Next
.Click
Next
to install NI Package Manager.After installation of NI Package Manager, install LabVIEW 2020. Hit
Select All
and thenNext
.Go through more license agreements, accept the conditions, and hit
Next
.There will be an option to Disable Windows Fast Startup. This is optional but unchecking
Disable Windows fast startup
is recommended.Click
Next
again.Installation will take some time. Please be patient.
If a software update box pops up during the install, select
No
.At the end of the installation, the activation process will pop up. Activation can be done by signing into your NI Account and tying the community edition to your account. If you do not wish to activate now, just hit cancel.
Once complete, the computer will need to be restarted.
LabVIEW 2020 can now be found on the computer.
Installing the Toolkits¶
There are two toolkits required. HG_WSI_Toolkit
and HG_WSI_Vision_Toolkit
.
Both Toolkits are based on LabVIEW developed by Guangzhou High Genius for the World Skills Competition. It provides different levels of driver functions and tools for various peripheral I/O of VMX-PI. It can not only access advanced features but also carry out low-level programming. These ready-made driver function interfaces, in addition to the standard analog input, analog output, digital I/O, I2C, SPI, PWM, encoder, and other interface driver functions. It also provides a wide variety of tools to enhance playability significantly.
Installing¶
Open VI Package Manager (VIPM)
In
VIPM
hitFile
->Open Package File(s)
.Find the
high_genius_lib_hg_wsi_toolkit_xxx.vip
that was downloaded before.A window will pop up asking
Add to Library
orAdd to Library & Install
. SelectAdd to Library & Install
.LabVIEW will open if not already opened, and the toolkit will be installed. After installation, another window will open, showing the results of the install. There should be a
No Errors
message stated under theStatus
tab. HitFinish
when complete.Repeat steps
2
to5
for thehigh_genius_lib_hg_vision_toolkit_xxx.vip
.
Setting up the WiFi¶
WiFi and Ethernet are already set up on the LabVIEW image. However, sometimes the WiFi SSID and password must be changed.
Connecting to the WiFi¶
When the VMX is powered on, the default WiFi of the LabVIEW image will be active.

WiFi Settings
SSID: high-genius
PASS: high-genius
Connect to the WiFi by selecting high-genius
and using high-genius
as the password.

Changing the WiFi¶
Open LabVIEW 2020 Community Edition and select
File
->Open Project
.Select the
HG_WSI_Tool xx.lvproj
that was downloaded above.Connect the project to the VMX Target by right-clicking on
Raspberry Pi (172.16.0.1)
and selectingConnect
.A window will pop up showing the project trying to establish a connection.
The tiny green dot next to the
Raspberry Pi (172.16.0.1)
should now be bright green.Click on the plus next to the
Raspberry Pi (172.16.0.1)
and double click onWIFI Setup.vi
.The WiFi Setup vi will open up.
Change the
Name
andPassword
to what is required for you.Important
The Name and Password must be more
8
digits.Here the Name / SSID will be set to
StudicaLabVIEW
, and the password will be set topassword
.Select the
Restart after final?
push button.This will reboot the VMX after the WiFi has been set.
Hit the run button to execute the change.
A window will pop up saying the connection has been lost. Hit
ok
.Checking the WiFi again, we can see that the change has occurred.
To check that everything is still working, repeat steps
3
to5
to check that the project can still connect to the VMX.
LabVIEW Toolkit¶
High Genius Toolkit¶
The High Genius Toolkit or HG_WSI_Toolkit
is the LabVIEW toolkit to interact with all the inputs and outputs of the VMX. This includes sensors, pushbuttons, LEDs, motor control, and human interaction with joysticks.
The toolkit has three main sections.
Joystick¶
The joystick library takes the joystick data from the computer and sends it over UDP(WiFi) to the robot to be driven around using a joystick.
There is two vis in the joystick library.
joystick
Robot-joystick

vi |
attributes |
---|---|
joystick |
gets placed on a vi that runs on the computer |
Robot-joystick |
gets placed in the main vi loop for the robot |
Note
These vis have no inputs or outputs. They should just be placed in the main loops on the computer and robot.
SubVIs¶
The SubVIs are some basic and device management functions.
There is four vis in the SubVIs library.
Init
Reset
Update VMX Data
Delay with Stop

vi |
attributes |
---|---|
Init |
Global initialization |
Reset |
Return to original state |
Update VMX Data |
Terminal communication |
Delay with Stop |
Delay Time |
Init, Reset, and Update VMX Data are all simple function calls with error inputs
and error outputs
.
Delay with Stop has error inputs
and error outputs``and includes an input for delay time in ``(ms)
.

VMX Library¶
Hint
All example images can be dragged and dropped into LabVIEW.
The VMX Library holds all the classes and underlying functions in the toolkit.
The Library has two simple functions and ten seperate sections contating specifc code for those functions.
vi |
attributes |
---|---|
Create ID |
Creates a port |
Delete ID |
Deletes a port |
Note
Example use of the Create ID and Delete ID will be shown in the sections below.
The ten seperate specifc sections are
Digital Input and Output¶
Handles the Digital inputs and outputs on the VMX.
Important
If using the High-Current Digital I/O Bus, it can only be configured as all inputs
or all outputs
(default).

vi |
Attributes |
---|---|
Digital Input and Output |
Digital signal initialization |
Read |
Digital signal reading |
Write |
Digital signal writing |
Digital Input and Output (vi)¶

Is a class that contains the code for reading and writing to the digital I/O bus. Has only a HG_LIB
output.
Read¶

A vi that allows for reading the digital state of the input pin specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
Digital Input and Output in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
Digital Input and Output out |
Output |
The output cluster to go to Delete ID |
Boolean |
Output |
The boolean value read on the digital pin |
error out |
Output |
The error output cluster |
Write¶

A vi that allows for writing a high or low to the output pin specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
Digital Input and Output in |
Input |
The input cluster from Create ID |
Control |
Input |
The boolean value to write to the digital pin |
error in (no error) |
Input |
The error input cluster |
Digital Input and Output out |
Output |
The output cluster to go to Delete ID |
error out |
Output |
The error output cluster |
This example will read a digital signal from a Pushbutton connected to digital port 11 on the VMX.

Analog Input¶
Handles the analog inputs of the VMX.

vi |
Attributes |
---|---|
Analog In |
Analog signal initialization |
Read |
Analog signal reading |
Read¶

A vi that allows for reading the analog value of the input pin specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
Analog In in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
Analog In out |
Output |
The output cluster to go to Delete ID |
Data |
Output |
The raw value from the ADC |
IR |
Output |
The converted value displaying distance in mm |
error out |
Output |
The error output cluster |
This example will read an analog signal from a Sharp IR Sensor connected to analog port 0 (Digital port 22) on the VMX.

Titan Encoder¶
Handles the Encoder ports on the Titan.
Note
While the Titan Encoder inputs are accurate on the Titan, with the CAN bus propagation delay, there will be a delay between the actual count and the count read on the VMX. If you require immediate counts plug your encoders into the FlexDIO ports directly on the VMX.

vi |
Attributes |
---|---|
Encoder |
Titan Encoder initialization |
Read |
Titan Encoder reading |
Read RPM |
Titan Encoder rpm reading |
Reset |
Titan Encoder reset |
TitanENC¶

Is a class that contains the code for reading the encoder ports on the Titan. Has only a HG_LIB
output.
Read¶

A vi that allows for reading the encoder count from the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
TitanENC in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
TitanENC out |
Output |
The output cluster to go to Delete ID |
ENC |
Output |
The raw encoder count |
ENC/dt |
Output |
The delta change in encoder count |
error out |
Output |
The error output cluster |
Read_RPM¶

A vi that allows for reading the encoder rpm count from the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
TitanENC in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
TitanENC out |
Output |
The output cluster to go to Delete ID |
rpm |
Output |
The rpm of the motor reported by the Titan |
error out |
Output |
The error output cluster |
Reset¶

A vi that allows for resetting the encoder count from the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
TitanENC in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
TitanENC out |
Output |
The output cluster to go to Delete ID |
error out |
Output |
The error output cluster |
Pulse / Ultrasonic¶
Handles the pulse of the Ultrasonic sensor.

vi |
Attributes |
---|---|
Pulse |
Pulse initialization |
Pulse |
Pulse output |
Pulse Init¶

Is a class that contains the code for sending out the Ultrasonic pulse on the VMX. Has only a HG_LIB
output.
Pulse Output¶

A vi that allows for sending out the Ultrasonic pulse on the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
Pulse in |
Input |
The input cluster from Create ID |
Pulse |
Input |
The pulse to set |
error in (no error) |
Input |
The error input cluster |
Pulse out |
Output |
The output cluster to go to Delete ID |
error out |
Output |
The error output cluster |
Note
This example requires Pulse and ISQ.
This example will Pulse the Ultrasonic sensor on digital port 12 and receive the echo on digital port 8 then calulate the distance.

Interupt Status Queue / Ultrasonic¶
Handles the echo of the Ultrasonic sensor.

vi |
Attributes |
---|---|
ISQ |
ISQ initialization |
Read |
ISQ reading |
Pulse Init¶

Is a class that contains the code for the return pulse of the Ultrasonic pulse on the VMX. Has only a HG_LIB
output.
Pulse Output¶

A vi that allows for collecting the return pulse of the Ultrasonic pulse on the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
ISQ in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
ISQ out |
Output |
The output cluster to go to Delete ID |
PING |
Output |
The distance calculated by the ultrasonic sensor in mm |
error out |
Output |
The error output cluster |
Note
This example requires Pulse and ISQ.
This example will Pulse the Ultrasonic sensor on digital port 12 and receive the echo on digital port 8 then calulate the distance.

VMX Encoder¶
Handles the Encoder ports on the VMX. There are five encoder ports on the VMX.
FlexDIO 0,1
FlexDIO 2,3
FlexDIO 4,5
FlexDIO 6,7
FlexDIO 8,9 (LabVIEW currently does not use this one)

vi |
Attributes |
---|---|
Encoder |
Encoder initialization |
Read |
Encoder reading |
Reset |
Encoder reset |
Encoder¶

Is a class that contains the code for reading the encoder ports on the VMX. Has only a HG_LIB
output.
Read¶

A vi that allows for reading the encoder count from the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
Encoder in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
Encoder out |
Output |
The output cluster to go to Delete ID |
ENC |
Output |
The raw encoder count |
ENC/dt |
Output |
The delta change in encoder count |
error out |
Output |
The error output cluster |
Reset¶

A vi that allows for resetting the encoder count from the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
Encoder in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
Encoder out |
Output |
The output cluster to go to Delete ID |
error out |
Output |
The error output cluster |
CAN Bus¶
Handles the CAN bus communication on the VMX. Specifically used to control the speed of a motor on the Titan.

vi |
Attributes |
---|---|
CAN |
CAN bus initialization |
Write |
CAN bus writing |
CAN¶

Is a class that contains the code for setting the motor speed on the Titan using the CAN bus on the VMX. Has only a HG_LIB
output.
Read¶

A vi that allows for setting the motor speed from the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
CAN in |
Input |
The input cluster from Create ID |
PWM |
Input |
Motor to control |
Duty Cycle |
Input |
Speed to set the motor at |
INA |
Input |
One of the Directional registers for motor direction |
INB |
Input |
One of the Directional registers for motor direction |
error in (no error) |
Input |
The error input cluster |
CAN out |
Output |
The output cluster to go to Delete ID |
error out |
Output |
The error output cluster |
PWM¶
Handles the PWM outputs on the VMX.

vi |
Attributes |
---|---|
PWM |
PWM initialization |
Write |
PWM writing |
PWM (vi)¶

Is a class that contains the code for setting the PWM value on the PWM ports on the VMX. Has only a HG_LIB
output.
Write¶

A vi that allows for writing the PWM value to the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
PWM in |
Input |
The input cluster from Create ID |
PWM |
Input |
The duty cycle to set the PWM to |
error in (no error) |
Input |
The error input cluster |
PWM out |
Output |
The output cluster to go to Delete ID |
error out |
Output |
The error output cluster |
This example shows the writing of a PWM value to a Servo on digital port 14 to make it move.

IIC / Cobra¶
Handles the IIC communication to the external ADC connected to the Cobra sensor.

vi |
Attributes |
---|---|
IIC |
IIC initialization |
Read |
IIC reading |
Read¶

A vi that allows for reading of the Cobra on the port specified by the Create ID
vi.
Name |
I/O |
Attribute |
---|---|---|
IIC in |
Input |
The input cluster from Create ID |
error in (no error) |
Input |
The error input cluster |
IIC out |
Output |
The output cluster to go to Delete ID |
QTI |
Output |
An array with all four values of the Cobra |
error out |
Output |
The error output cluster |

LabVIEW VMX-Library Available Pins¶
Function |
LabVIEW Interface |
VMX Interface |
---|---|---|
ENC |
0 |
FlexDIO 0,1 |
ENC |
1 |
FlexDIO 2,3 |
ENC |
2 |
FlexDIO 4,5 |
ENC |
3 |
FlexDIO 6,7 |
IMU |
0 |
|
AI |
22 |
22 |
AI |
23 |
23 |
AI |
24 |
24 |
AI |
25 |
25 |
CAN |
2 |
CAN |
PWM |
14 |
14 |
PWM |
15 |
15 |
PWM |
16 |
16 |
PWM |
17 |
17 |
PWM |
18 |
18 |
Pulse |
12 |
12 |
Pulse |
13 |
13 |
ISQ |
8 |
8 |
ISQ |
10 |
10 |
IIC |
0 |
IIC |
DO |
19 |
19 |
DO |
20 |
20 |
DO |
21 |
21 |
DI |
9 |
9 |
DI |
11 |
11 |
Function |
LabVIEW Interface |
---|---|
M0 Limit High |
0 |
M0 Limit Low |
1 |
M1 Limit High |
2 |
M1 Limit Low |
3 |
M2 Limit High |
4 |
M2 Limit Low |
5 |
M3 Limit High |
6 |
M3 Limit Low |
7 |
M0 Encoder |
0 |
M1 Encoder |
1 |
M2 Encoder |
2 |
M3 Encoder |
3 |

High Genius Vision Toolkit¶
Using LabVIEW¶
This section will go over how to use LabVIEW for VMX and deploying code to the VMX.
Creating a LabVIEW Project¶
Creating a LabVIEW for VMX project is very easy and quick to do.
Open LabVIEW 2020 Community Edition
Hit
Create Project
Select
Blank Project
then hitFinish
A Blank Project should now pop up
Save the project to a location of your choice
Right-click on
Your Project Name.lvproj
and selectNew
->Targets and Devices...
Under Targets and Devices, select
New target or device
, then under Targets and Device Types, select the drop-down forLINX
and select theRaspberry Pi 2 B
. HitOK
when done.Save the project, and the project is now wholly created.
Connecting to the VMX¶
For LabVIEW for VMX, there are two ways to connect to the VMX.
Ethernet with the IP ADDRESS
172.22.11.2
WiFi with the IP ADDRESS
172.16.0.1
To set the correct IP ADDRESS right click on the Raspberry Pi 2 B (0.0.0.0)
target and select Properties

In the IP Address / DNS Name box, put either the Ethernet
or WiFi
IP Address and hit ok.
Note
In this case, the WiFi address was used.

The IP Address should now be correctly shown in the Target Title.

To test the connection, connect to the VMX in the way specified in the IP Address chosen. In this case, WiFi was selected so we would connect to the VMX over WiFi.

Right-click on the Raspberry Pi 2 B (172.16.0.1)
target and select Connect

A window will pop up to display the connection information.

The connection should now be complete, and the little green indicator next to the Raspberry Pi
logo should be illuminated. This signals a connection has been established with the VMX.

Training Platform¶
The following code is the LabVIEW project for the VMX Training Platform.
With the project created and the project connected to the VMX, code can now be written.
Creating the Main vi¶
Right click on the Raspberry Pi 2 B (172.16.0.1)
target and select New
-> VI

Two new windows will pop up, Front Panel
and Block Diagram
.

On the Front Panel
(Grey window), hit File
-> Save As
and save the vi as Main.vi

Adding Titan Code¶
Referring back to the Titan Code to move the motor in the toolkit docs page. Adding the code for M1
on the Titan should be very simple.

Note
In the docs page for the toolkit, we use M0, but here it needs to be changed to M1.
Adding Servo Code¶
Referring to the Servo Code to move the servo in the toolkit docs page, the code can be added to our motor code easily.

The servo will be connected to digital port 14.
Adding Sharp IR Code¶
Referring to the Analog Input Code to read the Sharp IR Sensor in the toolkit docs page, the code can be added.

The Sharp IR sensor will be on port 22 of the VMX.
Adding Ultrasonic Code¶
Referring back to the Pulse Code & ISQ Code to read the Ultrasonic Sensor in the toolkit docs page, the code can be added.

The Ultrasonic sensor will use digital port 12 for the pulse and digital port 8 for the echo.
Add Cobra Code¶
Referring back to the IIC Code for the Cobra, the values can be easily read.

The Cobra is plugged into the ADC, which is plugged into the IIC port on the VMX.
Adding an LED Output¶
Referring back to the Digital Output Code an LED can be turned on.


For this example, the loop counter will be used to turn the light on digital port 21 on and off every 500 ms.
Adding a Digital Input¶
Referring back to the Digital Input Code a push button can be read.

Here a pushbutton in a Normally Open
configuration is used to turn an indicator on the front panel on and off.
Note
The VMX has internal pullups for inputs.
Full Code Example¶
Below is all the code above in one image that can be dragged into LabVIEW.
Note
The image might have to be saved first then dragged in.

Deploying Code to the VMX¶
To deploy code to the VMX, ensure that there is a connection first. See Connecting to the VMX if no connection is present.
In the upper left-hand corner, there will be an arrow facing right.

Press the arrow, and the code will deploy to the VMX.
Note
If you are connected in LabVIEW, the Front Panel will be usable for diagnosing functions and seeing values.
Important
Deploying sets the current code as the startup code. Meaning this code will run automatically when the VMX is powered on again.
Getting Started¶
Welcome to the ROS library for the Studica robot platform, allowing ROS functionality for a variety of Studica’s hardware. Compared to the current codebase this directly uses the VMXPi HAL, more information on the VMXPi HAL API can be found at VMX-pi C++ HAL.
To see indepth examples of the VMXPi HAL, your RPi has a lot in the /usr/local/src/vmxpi/
directory.
ROS Setup¶
Installing ROS Manually¶
Open a terminal window and run the installNoetic.sh
script to install ROS Noetic. For more information, visit ROS Noetic.
./installNoetic.sh
Note
Running the installNoetic.sh
script takes approximately 3 hours, this includes most of the required tools and dependencies needed for the ROS Package, however this does not include catkin-tools
.
To install the required catkin-tools
, run the following command:
sudo apt install python3-catkin-tools python3-osrf-pycommon
Using the ROS Image¶
To get started, navigate to the ROS Image and download the ROSImage.zip file. Unzip the downloaded zip file and refer to the section on flashing image files to an SD card here.
Note
The ROSImage
file includes all the required tools and dependencies needed for the ROS Package, however this will require approximately 4.8Gb of disk space.
Insert the SD card into the VMX-pi and continue with the instructions below.
Creating a ROS Workspace¶
Create a directory
catkin_ws
by runningmkdir -p ~/catkin_ws/src
, preferrably in/home/pi
.
mkdir -p ~/catkin_ws/src
Then
cd catkin_ws
and runcatkin init
to initialize the workspace environment.
cd catkin_ws
catkin init
Next, run
catkin build
.
catkin build
This will create two additional build
and devel
folders in the catkin_ws
directory.
Now clone the VMX-ROS repo into the
src
folder.
git clone https://github.com/studica/VMX-ROS.git
Lastly, run
catkin build
once again to build the newly cloned repository in the catkin workspace.
catkin build -cs
Building ROS¶
CMakeLists.txt¶
The CMakeLists.txt
file is the input to CMake, which is a system that manages the build process for ROS in a compiler-independent manner. To access this software, CMakeLists.txt configuration files are created that detail how the code should be built, these in turn generate the standard makefiles for compiling a program on Linux operating systems like Rasbian for the VMX-pi. In the catkin_ws
, the CMakeLists.txt used is a standard CMakeLists.txt file with a few more restrictions.
Structure¶
Required CMake Version (cmake_minimum_required)
Package Name (
project()
)Find other CMake/Catkin packages needed for build (
find_package()
)Message/Service/Action Generators (
add_message_files()
,add_service_files()
,add_action_files()
)Invoke message/service/action generation (
generate_messages()
)Specify package build info export (
catkin_package()
)Libraries/Executables to build (
add_library()
/add_executable()
/target_link_libraries()
)
Writing a CMakeLists.txt file¶
For the purposes of this demonstration we will use the CMakeLists.txt file for the main vmxpi_ros_bringup
package for reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | cmake_minimum_required(VERSION 3.0.2) project(vmxpi_ros_bringup) ## Compile as C++11, supported in ROS Kinetic and newer # add_compile_options(-std=c++11) ## Find catkin macros and libraries ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) ## is used, also find other catkin packages find_package(catkin REQUIRED COMPONENTS roscpp rospy dynamic_reconfigure vmxpi_ros vmxpi_ros_titan vmxpi_ros_cobra vmxpi_ros_sharp vmxpi_ros_ultrasonic vmxpi_ros_navx vmxpi_ros_servo vmxpi_ros_io ) ################################### ## catkin specific configuration ## ################################### ## The catkin_package macro generates cmake config files for your package ## Declare things to be passed to dependent projects ## INCLUDE_DIRS: uncomment this if your package contains header files ## LIBRARIES: libraries you create in this project that dependent projects also need ## CATKIN_DEPENDS: catkin_packages dependent projects also need ## DEPENDS: system dependencies of this project that dependent projects also need catkin_package( # INCLUDE_DIRS include # LIBRARIES vmxpi_ros_bringup # CATKIN_DEPENDS roscpp rospy # DEPENDS system_lib ) ########### ## Build ## ########### ## Specify additional locations of header files ## Your package locations should be listed before other locations include_directories( include ${catkin_INCLUDE_DIRS} ../vmxpi_ros_titan/include ../vmxpi_ros_navx/include ../vmxpi_ros_sensors/vmxpi_ros_cobra/include ../vmxpi_ros_sensors/vmxpi_ros_sharp/include ../vmxpi_ros_sensors/vmxpi_ros_ultrasonic/include ../vmxpi_ros_servo/include ../vmxpi_ros_utils/include ../vmxpi_ros_io/include ../vmxpi_ros/include /usr/local/include/vmxpi ) add_library(vmxpi_hal SHARED IMPORTED GLOBAL) set_target_properties(vmxpi_hal PROPERTIES IMPORTED_LOCATION "/usr/local/lib/vmxpi/libvmxpi_hal_cpp.so") add_library(navx_ros_wrapper SHARED IMPORTED GLOBAL) # set_target_properties(navx_ros_wrapper PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libnavx_ros_wrapper.so") set_target_properties(navx_ros_wrapper PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libnavx_ros_wrapper.so) add_library(titandriver_ros_wrapper SHARED IMPORTED GLOBAL) # set_target_properties(titandriver_ros_wrapper PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libtitandriver_ros_wrapper.so") set_target_properties(titandriver_ros_wrapper PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libtitandriver_ros_wrapper.so) add_library(titandriver_ros SHARED IMPORTED GLOBAL) # set_target_properties(titandriver_ros PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libtitandriver_ros.so") set_target_properties(titandriver_ros PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libtitandriver_ros.so) add_library(cobra_ros SHARED IMPORTED GLOBAL) set_target_properties(cobra_ros PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libcobra_ros.so) add_library(sharp_ros SHARED IMPORTED GLOBAL) # set_target_properties(sharp_ros PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libsharp_ros.so") set_target_properties(sharp_ros PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libsharp_ros.so) add_library(servo_ros SHARED IMPORTED GLOBAL) # set_target_properties(servo_ros PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libservo_ros.so") set_target_properties(servo_ros PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libservo_ros.so) add_library(ultrasonic_ros SHARED IMPORTED GLOBAL) # set_target_properties(ultrasonic_ros PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libultrasonic_ros.so") set_target_properties(ultrasonic_ros PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libultrasonic_ros.so) add_library(iowd_ros SHARED IMPORTED GLOBAL) # set_target_properties(iowd_ros PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libiowd_ros.so") set_target_properties(iowd_ros PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libiowd_ros.so) add_library(digitalin_ros SHARED IMPORTED GLOBAL) # set_target_properties(digitalin_ros PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libdigitalin_ros.so") set_target_properties(digitalin_ros PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libdigitalin_ros.so) add_library(digitalout_ros SHARED IMPORTED GLOBAL) # set_target_properties(digitalout_ros PROPERTIES IMPORTED_LOCATION "/home/pi/catkin_ws/devel/lib/libdigitalout_ros.so") set_target_properties(digitalout_ros PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/../../../devel/lib/libdigitalout_ros.so) add_executable(test_node src/test_node.cpp) target_link_libraries(test_node PRIVATE vmxpi_hal navx_ros_wrapper titandriver_ros titandriver_ros_wrapper cobra_ros sharp_ros servo_ros ultrasonic_ros iowd_ros digitalin_ros digitalout_ros ${catkin_LIBRARIES} ) add_dependencies(test_node navx_ros_wrapper titandriver_ros titandriver_ros_wrapper cobra_ros sharp_ros servo_ros ultrasonic_ros iowd_ros digitalin_ros digitalout_ros ${PROJECT_NAME}_gencfg) add_executable(main_node src/main.cpp) target_link_libraries(main_node PRIVATE vmxpi_hal navx_ros_wrapper titandriver_ros titandriver_ros_wrapper cobra_ros sharp_ros servo_ros ultrasonic_ros iowd_ros digitalin_ros digitalout_ros ${catkin_LIBRARIES} ) add_dependencies(main_node navx_ros_wrapper titandriver_ros titandriver_ros_wrapper cobra_ros sharp_ros servo_ros ultrasonic_ros iowd_ros digitalin_ros digitalout_ros ${PROJECT_NAME}_gencfg) |
Explaining the File¶
Before starting any CMakeLists.txt file, the first thing to add is the version of CMake. Catkin requires version 2.8.3 or higher.
cmake_minimum_required(VERSION 3.0.2)
The next section is specifying the package name using the CMake
project()
function, here is where thevmxpi_ros_bringup
package is declared. In CMake, the project name can be referenced using the${PROJECT_NAME}
variable.
project(vmxpi_ros_bringup)
Using the CMake
find_package()
function, we specify the packages that the project needs to find before building.catkin REQUIRED
must be passed to this function, from the code-block below, there are other dependencies added to this package such asroscpp
,rospy
, and the various other packages in Studica’s ROS library needed for this wrapper package. Note, the “wet” packages must be turned into components of catkin using theCOMPONENTS
argument.
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
dynamic_reconfigure
vmxpi_ros
vmxpi_ros_titan
vmxpi_ros_cobra
vmxpi_ros_sharp
vmxpi_ros_ultrasonic
vmxpi_ros_navx
vmxpi_ros_servo
vmxpi_ros_io
)
When a package is found following the function call, this leads to the generation of environment variables that can be utilized later in the CMake script. The environment variables indicate the locations of the headers and source files for the packages, the libraries that the package depends on, as well as the path to those libraries. The naming convention follows <PACKAGE NAME>_<PROPERTY>, for example:
<NAME>_FOUND - Set to true if the library is found, otherwise false
<NAME>_INCLUDE_DIRS or <NAME>_INCLUDES - The include paths exported by the package
<NAME>_LIBRARIES or <NAME>_LIBS - The libraries exported by the package
Remember, catkin packages are not components of catkin, they must be specified as compnents using CMake’s components feature to save time. Calling find_package()
on catkin packages is beneficial since their files, paths, and libraries are added as catkin_variables as mentioned earlier.
The
catkin_package()
macro generates cmake config files for your package. This is required to declare things to be passed to dependent projects. Note, this function must be called before theadd_library()
oradd_executable()
.
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES vmxpi_ros_bringup
# CATKIN_DEPENDS roscpp rospy
# DEPENDS system_lib
)
INCLUDE_DIRS - The exported include paths (i.e. cflags) for the package
LIBRARIES - The exported libraries from the project
CATKIN_DEPENDS - Other catkin projects that this project depends on
DEPENDS - Non-catkin CMake projects that this project depends on.
Uncommenting the lines in the code-block above, this indicates that exported headers go in the include folder of the package. We know the ${PROJECT_NAME}
variable is the value passed in the project()
function from before, roscpp
and rospy
are packages needed in order to build/run this package, and finally the package depends on system_lib
.
Specify additional locations of header files, the current packages
/include/
directory should be listed before other/include
locations.
include_directories(
include
${catkin_INCLUDE_DIRS}
../vmxpi_ros_titan/include
../vmxpi_ros_navx/include
../vmxpi_ros_sensors/vmxpi_ros_cobra/include
../vmxpi_ros_sensors/vmxpi_ros_sharp/include
../vmxpi_ros_sensors/vmxpi_ros_ultrasonic/include
../vmxpi_ros_servo/include
../vmxpi_ros_utils/include
../vmxpi_ros_io/include
../vmxpi_ros/include
/usr/local/include/vmxpi
)
The add_library() CMake function is used to specify libraries to build, the
SHARED IMPORTED GLOBAL
arguments set the type of library to be created. For non-Windows platforms like Rasbian, the primary library file for aSHARED
library is the.so
file, theGLOBAL
option extends the scope of the target (vmxpi_hal
) in the directory it is created and beyond.
add_library(vmxpi_hal SHARED IMPORTED GLOBAL)
Imported targets are used to convert files outside of a CMake project into logical targets inside of the project. The
set_target_properties()
function gives the ability to set the properties of the target depending on the options passed after the target. Here, the imported location of the target is pointed to the imported targetlibvmxpi_hal_cpp.so
file created earlier viaadd_library()
in/usr/local/lib/vmxpi/libvmxpi_hal_cpp.so
.
set_target_properties(vmxpi_hal PROPERTIES IMPORTED_LOCATION "/usr/local/lib/vmxpi/libvmxpi_hal_cpp.so")
Specify an executable target to be built with the
add_executable()
function.
add_executable(test_node src/test_node.cpp)
Set the libraries that an executable target links against using the
target_link_libraries
. ThePRIVATE
option indicates that all the following will be used for the current target only, meaning thetest_node
target is linked against the shared libraries (.so
since Rasbian is Linux-based) of the other packages.
target_link_libraries(test_node PRIVATE
vmxpi_hal
navx_ros_wrapper
titandriver_ros
titandriver_ros_wrapper
cobra_ros
sharp_ros
servo_ros
ultrasonic_ros
iowd_ros
digitalin_ros
digitalout_ros
${catkin_LIBRARIES}
Add dependencies using
add_dependencies()
to the target (test_node
) defined in theadd_executable()
call prior, this is done for targets that depend on other targets that need messages, services, and actions to be built. Essentially, messages from other packages inside the catkin workspace need a dependency added to their generation targets, this is often the case as one of the primary uses of ROS is this message-passing aspect between packages.
add_dependencies(test_node
navx_ros_wrapper
titandriver_ros
titandriver_ros_wrapper
cobra_ros
sharp_ros
servo_ros
ultrasonic_ros
iowd_ros
digitalin_ros
digitalout_ros
${PROJECT_NAME}_gencfg)
The macros
add_message_files(...)
,add_service_files(...)
,add_action_files(...)
,generate_messages(...)
were not included in the example for thevmxpi_ros_bringup
package, but the functions must come BEFORE thecatkin_package()
macro in this order:
find_package(catkin REQUIRED COMPONENTS ...)
add_message_files(...)
add_service_files(...)
add_action_files(...)
generate_messages(...)
catkin_package(...)
add_message_files(...)
, add_service_files(...)
, add_action_files(...)
handle messages, services, and actions respectively, followed by a call to invoke generation:
generate_messages(...)
Note
It is important to adhere to the structure of the CMakeLists.txt file as outlined above. Refer to CMakeLists.txt for more information.
Configuring CMakeLists.txt¶
The previous section analyzed the major sections of a CMakeLists.txt
file, luckily most of the work is already done when the repository is cloned. The main things to remember when it is time to build your programs are to generate executables, set dependencies, and set libraries to link the target against. To do this, add the following lines at the end of the vmxpi_ros_bringup
CMakeLists.txt file:
add_executable(...)
target_link_libraries(...)
add_dependencies(...)
Note
The CMakeLists.txt file has already been configured to build the main_node
executable with all the currently available packages in Studica’s ROS library, hence you can simply begin writing your program in main.cpp
.
Configuring the ROS Environment¶
Permanently source the setup.bash files by running the following:
echo "source /opt/ros/noetic/setup.bash" >> ~/.profile
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
echo "source /home/pi/catkin_ws/devel/setup.bash" >> ~/.profile
echo "source /home/pi/catkin_ws/devel/setup.bash" >> ~/.bashrc
Close the terminal and open a new one.
Navigate to the work space
cd catkin_ws/src
Change the name of the
VMX-ROS
folder tovmxpi_ros
mv /home/pi/catkin_ws/src/VMX-ROS/ /home/pi/catkin_ws/src/vmxpi_ros
To build the packages run
catkin build -cs
. Note, this may take a while as the command builds all the packages in the catkin workspace.
catkin build -cs
Note
This process may take a couple minutes if running for the first time.
With everything built, you can begin running the node.
Running the Package¶
In a new terminal, run roscore
roscore
The following should appear:
... logging to /home/pi/.ros/log/634b1d0a-4664-11ec-90e3-dca63268e7bc/roslaunch-raspberrypi-18104.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.
started roslaunch server http://raspberrypi:38727/
ros_comm version 1.15.11
SUMMARY
========
PARAMETERS
* /rosdistro: noetic
* /rosversion: 1.15.11
NODES
auto-starting new master
process[master]: started with pid [18113]
ROS_MASTER_URI=http://raspberrypi:11311/
setting /run_id to 634b1d0a-4664-11ec-90e3-dca63268e7bc
process[rosout-1]: started with pid [18136]
started core service [/rosout]
roscore
is the first command that should be run to allow for ROS nodes to communicate. The command essentially prepares your system by launching the pre-requisite nodes and programs needed for a ROS system.
Tip
Run rosclean check
to check the disk usage of ROS log files. If disk usage >1GB, run rosclean purge
to clear existing ROS log files.
In another terminal, run
sudo su
to run commands as root.
sudo su
Running a command with the sudo
prefix is required for commands that require superuser privileges.
Caution
Switching to the superuser (root) can be dangerous, it grants access to “super powers” like the ability to modify or delete any file in any directory on the system, hence one should be careful with the commands run under the root account.
As the root user run:
echo "source /opt/ros/noetic/setup.bash" >> ~/.profile
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
echo "source /home/pi/catkin_ws/devel/setup.bash" >> ~/.profile
echo "source /home/pi/catkin_ws/devel/setup.bash" >> ~/.bashrc
Close the root terminal and reopen it. Navigate to
cd catkin_ws/src
and runsudo su
once again.
cd catkin_ws/src
sudo su
Now, run the following command to start the nodes in the launch file.
roslaunch vmxpi_ros_bringup wrapper.launch
Configuring the Launch File¶
Navigate to the launch file directory in the file explorer and open the wrapper.launch
file.
cd /home/pi/catkin_ws/src/vmxpi_ros/vmxpi_ros_bringup/launch
nano wrapper.launch
From the image above, there are three nodes in the xml launch file. The camera_node
and the opencv_node
are both commented out using the xml syntax <!-- Comment -->
. Remove these tags to have these nodes run when the launch file is called, or add them when not in use to save resources.
Tip
Observe the resource usage by running htop
.
Using ROS¶
The ROS Package¶
What is ROS?¶
ROS (Robot Operating System) is an open-source collection of software frameworks for robot development, it allows for functionalities such as low-level device control, message-passing between processes, and package management. The tools and libraries available make it possible to build, write, and run code across multiple computers. The main advantage of ROS is its peer-to-peer network, this allows for communication across multiple nodes and devices without requiring an auxilliary server computer or server software. This means processes distributed across various machines can interact using the ROS communication framework.
Why Noetic?¶
Essentially, a distribution (distro) is a set of ROS packages rolled up into a release, there are various distributions of ROS each with different functionalities to suit the needs of different robots.
ROS Noetic is currently the final and latest version of ROS 1 available with an EOL date set for May 2025, this means another distribution of the operating system will not be released for ROS 1. However, Noetic is an LTS release meaning it will have support throughout its lifetime though no major functionality will be added. Moreover, ROS 2 is only available on Ubuntu and not Raspbian, which is the official supported operating system for the Raspberry Pi. Raspbian is required as the VMX-pi HAL Library for the Raspberry Pi only supports the operating system. Below is a summary of distros released prior to ROS Noetic:
General Overview¶
Exchanging information with ROS can take many forms, whether it be asynchronously streaming data over topics, or using ROS services via a request/response messaging system.
ROS Master¶
ROS master can be thought of as the main message-passing server that tracks the network addresses of all the other nodes. It informs subscribers about nodes publishing on a particular topic in order for the subscriber and publisher to establish a peer-to-peer connection. The nodes must know the location of ROS master on startup via ROS_MASTER_URI
, which is the enviroment variable responsible for this. Conveniently this is automatically set by default when the ROSDISTRO(noetic) setup file is sourced. For more information on sourcing setup files, refer to the Getting Started
section.
Subscribers and Publishers¶
Like previously mentioned, the subscribe/publish messaging model is one of the main ways ROS is used. For example, let’s assume we have a camera on our robot and we want a way to read, process, and ouput the image feed from the camera for navigation or object tracking. To begin, the nodes must register with ROS master, this is done before the nodes can establish a peer-to-peer communication whith eachother before message-passing can occur. After registering, the Camera Node will advertise the image data to a trivial topic called /image_feed
while the Image Processing Node will subscribe to /image_feed
.
With Peer-to-Peer connection now established, its time for the Image Processing Node to process the incoming video stream and output to another topic called /image/output_video
.
Another subscriber can be written to view the video feed by writing a callback to the image output topic, however ROS has a framework known as rqt with many plugins like rqt_image_view
, that provide a GUI for displaying images using image transport.
Note
Refer to the RQT section for more information on the rqt GUI and its plugins.
Services and Clients¶
Services/Clients are another way of passing messages, ROS services follow the basic request-response style remote procedure call (RPCs). Any node can call a service, these are referred to as clients, services are useful when a quick operation is desired. Similar to the subscriber/publisher, a ROS node provides a service under a string name that is registered with ROS Master. For example let’s take a service, /set_motor_speed
, to set a motor speed using this, clients will send a request
containing the desired motor speed value by calling the service and await an ensuing response.
VMX-pi ROS Package¶
After following the steps in the Getting Started section, now you are ready to start using the ROS library for the Studica Robot Platform. ROS functionality has been implemented for a variety of Studica’s hardware, refer to Studica’s Roscpp API for more information on the classes and methods available. Below are the ROS pacakages:
The next sections will go over using the ROS package to write simple subscribers and publishers, as well as writing simple services and clients to pass messages between nodes.
Subscribers and Publishers¶
Writing the Subscriber Node¶
For the purposes of this demonstration we will use the Sharp IR sensor’s ROS Library for reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //Include the Sharp Library #include "Sharp_ros.h" double sharp_dist; // Returns the distance value reported by the Sharp IR sensor void sharp_dist_callback(const std_msgs::Float32::ConstPtr& msg) { sharp_dist = msg->data; } int main(int argc, char **argv) { ros::init(argc, argv, "sharp_sub_node"); ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system ros::Subsriber sharpDist_sub; // Subscribing to Sharp distance topic to access the distance data sharpDist_sub = nh.subscribe("channel/22/sharp_ir/dist", 1, sharp_dist_callback); ros::spin(); //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
Explaining the Code¶
Let’s go over each section of the code.
#include "Sharp_ros.h"
Sharp_ros.h
is the header for Studica’s Sharp sensor ROS library.
double sharp_dist;
This is the variable that accepts the sensor data from the messages being published on the channel/22/sharp_ir/dist
topic.
void sharp_dist_callback(const std_msgs::Float32::ConstPtr& msg)
{
sharp_dist = msg->data;
}
This is the callback fuction that will get called when a new message has arrived on the channel/22/sharp_ir/dist
topic. This message in particular is a Float32
type passed on a boost shared_ptr.
ros::init(argc, argv, "sharp_sub_node");
Used to initialize ROS and must be called before using any other parts of the ROS system. Its parameters must be argc
and argv
such that it can perform any ROS arguments or name remappings provided in the command line. The name of the node is also specified here.
ros::NodeHandle nh;
Internal reference to the ROS node that the program will use to interact with the ROS system.
ros::Subsriber sharpDist_sub;
Constructs a ROS subscriber object called sharpDist_sub
.
sharpDist_sub = nh.subscribe("channel/22/sharp_ir/dist", 1, sharp_dist_callback);
This line calls the subscribe()
method, this is used to inform the ROS Master node that we want to accept messages being streamed on a certain topic. The particular topic is declared in the first argument, in this case channel/22/sharp_ir/dist
. The second argument is where we set the capacity of the queue, this is important for cases where messages are being sent faster than they are being recieved and processed. 1
is the queue size, meaning if the size of the queue is greater than one, old messages will start being discarded as new ones arrive. The final parameter passed is the callback function that gets called whenever a new message arrives on the topic. The sharpDist_sub
object is maintained until all copies of it are destroyed, in this case the channel/22/sharp_ir/dist
topic will be automatically unsubcribed from.
Note
ROS Master acts as a registry where nodes establish peer-to-peer connections in order to pass messages, it keeps track of what nodes are publishing and nodes that are subscribing.
ros::spin();
ros::spin()
will enter a loop, pumping callbacks to obtain the latest sensor data.
Writing the Publisher Node¶
Important
For using Studica’s ROS Library, publishers have already been implemented where relevant sensor information has been organized into topics. This section is for creating a publisher node from scratch.
For the purposes of this demonstration we will use the Sharp IR sensor’s ROS Library for reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //Include the Sharp Library #include "Sharp_ros.h" int main(int argc, char* argv[]) { ros::init(argc, argv, "sharp_pub_node"); ros::NodeHandle nh; ros::Publisher sharp_dist_pub; sharp_dist_pub = nh->advertise<std_msgs::Float32>("channel/22/sharp_ir/dist", 1); ros::Rate loop_rate(50); while (ros::ok()) { std_msgs::Float32 msg; msg.data = GetIRDistance(); sharp_dist_pub.publish(msg); loop_rate.sleep(); } |
Explaining the Code¶
Let’s go over each section of the code.
Note
Lines that have already been explained above will be ignored.
ros::Subsriber sharp_dist_pub;
Constructs a ROS subscriber object called sharp_dist_pub
.
sharp_dist_pub = nh->advertise<std_msgs::Float32>("channel/22/sharp_ir/dist", 1);
This line calls the advertise()
method, this is used to inform the ROS Master node that we are going to be publishing distance messages over a certain topic. The particular topic is declared in the first argument, in this case channel/22/sharp_ir/dist
. The second argument is where we set the capacity of the queue, this is important for cases where messages are being sent faster than they are being recieved and processed. 1
is the queue size, meaning if the size of the queue is greater than one, old messages will start being discarded as new ones arrive. The sharp_dist_pub
object is maintained until all copies of it are destroyed, in this case the channel/22/sharp_ir/dist
topic will automatically stop advertising messages.
ros::Rate loop_rate(50);
The ros::Rate
class creates an object that allows us to set a frequency that we would like to run the while()
loop at. This is used in conjunction with the sleep()
method by tracking the time in between calls to Rate::sleep()
and sleeping for the appropriate duration to set the loop frequency. For this example, the loop will run at 50Hz.
while (ros::ok()) {
ros::ok()
will return false if:
a SIGINT is received
(Ctrl-C)
we have been kicked off the network by another node with the same name
ros::shutdown()
is evokedall
ros::NodeHandle(s)
have been destroyed
std_msgs::Float32 msg;
Message datatype of Float32
.
msg.data = GetIRDistance();
The message variable is passed with information from the GetIRDistance
accessor function that is included in Sharp_ros.h
sharp_dist_pub.publish(msg);
Once filled with sensor data, the publish object advertises the message to the channel/22/sharp_ir/dist
distance topic for any nodes connected.
loop_rate.sleep();
Like previously mentioned, this is used to sleep for a duration that allows the loop_rate
object to maintain a frequency of 50
specified in its declaration.
Services and Clients¶
Writing the Service Node¶
For the purposes of this demonstration we will use the NavX’s ROS Library for reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //Include the NavX Library #include "navX_ros_wrapper.h" bool navXROSWrapper::GetUpdateRate(vmxpi_ros::IntRes::Request &req, vmxpi_ros::IntRes::Response &res) { res.data = ahrs->GetActualUpdateRate(); return true; } int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "update_rate_server"); ros::NodeHandle nh; ros::ServiceServer update_rate = nh.advertiseService("get_update_rate", &navXROSWrapper::GetUpdateRate); ros::spin(); return 0; } |
Explaining the Code¶
Let’s go over each section of the code.
#include "navX_ros_wrapper.h"
navX_ros_wrapper.h
is the header for Studica’s NavX sensor ROS library.
bool navXROSWrapper::GetUpdateRate(vmxpi_ros::IntRes::Request &req, vmxpi_ros::IntRes::Response &res)
This function provides the service for obtataining the update rate. From the parameters passed, we can observe that the request and respose is of service type IntRes
that is defined in the IntRes.srv
file located in the srv folder of vmxpi_ros
.
To declare more services, run:
cd /home/pi/catkin_ws/src/vmxpi_ros/vmxpi_ros/srv
cat > [Service Name].srv
Enter the request and response types separated by a ---
line, for example:
int32 data
---
int32 response
Press Ctrl-D
to save and exit the text file.
Confirm the creation of the .srv
file by running:
rossrv show [Service Name]
Also add the newly created .srv
file to the add_service_files
in CMakeLists.txt as such:
## Generate services in the 'srv' folder
add_service_files(
FILES
Int.srv
IntRes.srv
Float.srv
FloatRes.srv
MotorSpeed.srv
StopMode.srv
StringRes.srv
[Service Name].srv //New service file
)
Below is an example of running the above commands:
Note
For more information on creating .srv
service types, visit the Creating a ROS Msg and Srv tutorial.
{
res.data = ahrs->GetActualUpdateRate();
return true;
}
Here the GetActualUpdateRate()
accessor method included in the navX_ros_wrapper.h
header is stored in the response variable and the service returns true.
ros::ServiceServer update_rate = nh.advertiseService("get_update_rate", &navXROSWrapper::GetUpdateRate);
The service is created and advertised over ROS.
Writing the Client Node¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //Include the NavX Library #include "navX_ros_wrapper.h" int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "update_rate_client"); ros::NodeHandle nh; ros::ServiceClient update_rate_client = nh.serviceClient<vmxpi_ros::IntRes>("get_update_rate"); vmxpi_ros::IntRes srv; if (update_rate_client.call(srv)); { ROS_INFO("Update Rate: %ld", (long int)srv.response.data); } else { ROS_ERROR("Failed to call service get_update_rate"); } return 0; } |
Explaining the Code¶
Let’s go over each section of the code.
Note
Lines that have already been explained above will be ignored.
ros::ServiceClient update_rate_client = nh.serviceClient<vmxpi_ros::IntRes>("get_update_rate");
This creates the get_update_rate
client, which will be used to call the service later.
vmxpi_ros::IntRes srv;
Since we are only receiving a response from the service, there is no need to stuff srv
with information in its request member.
update_rate_client.call(srv);
This is where the service is called, if the call succeeds a value of true
is returned and srv.response
will contain a valid value, otherwise false
is returned meaning the value of srv.response
will be invalid.
Programming With ROS¶
In the vmxpi_ros_bringup/src
directory there is a main.cpp
file, this is the blank project file where the robot code should go. The empty main.cpp
file has been configured to accept and pass messages between the various packages available in Studica’s ROS library.
To access these classes, simply include their respective headers and begin programming.
Note
An executable for main.cpp
has been generated and added to the launch file. For more on launch files see the Running the Package section.
Example Code¶
Opening the main.cpp
file, there is already an implementation of the Ultrasonic Distance Sensor using the header for the Ultrasonic Ros library. From analyzing the code, you can see that the program constructs an ultrasonic sensor object and directly returns the distances in microseconds, centimeters, or inches.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | //Include the Ultrasonic Library #include "Ultrasonic_ros.h" double ultrasonic_cm; // Returns the distance value reported by the Ultrasonic Distance sensor void ultrasonic_cm_callback(const std_msgs::Float32::ConstPtr& msg) { ultrasonic_cm = msg->data; } int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "ultrasonic_node"); /** * Constructor * Ultrasonic's ros threads (publishers and services) will run asynchronously in the background */ ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system VMXPi vmx(true, (uint8_t)50); //realtime bool and the update rate to use for the VMXPi AHRS/IMU interface, default is 50hz within a valid range of 4-200Hz ros::Subscriber ultrasonicCM_sub; UltrasonicROS ultrasonic(&nh, &vmx, 8, 9); //channel_index_out(8), channel_index_in(9) ultrasonic.Ultrasonic(); //Sends an ultrasonic pulse for the ultrasonic object to read // Use these to directly access data uint32_t raw_distance = ultrasonic.GetRawValue(); // returns distance in microseconds // or can use uint32_t cm_distance = ultrasonic.GetDistanceCM(raw_distance); //converts microsecond distance from GetRawValue() to CM // or can use uint32_t inch_distance = ultrasonic.GetDistanceIN(raw_distance); //converts microsecond distance from GetRawValue() to IN // Subscribing to Ultrasonic distance topic to access the distance data ultrasonicCM_sub = nh.subscribe("channel/9/ultrasonic/dist/cm", 1, ultrasonic_cm_callback); //This is subscribing to channel 9, which is the input channel set in the constructor ros::spin(); //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
Because an executable has already been generated for main.cpp
, there is no need to modify its CMakeLists.txt
or the launch file. Refer to previous sections on building and running the code.
Note
This is a similar code block to the example shown in the Roscpp
codeblock under the Ulrasonic Distance Sensor section.
RQT¶
What is RQT?¶
RQT is one of the many software frameworks of ROS that developers use to implement various plugins to form a graphical user interface (GUI) for the ROS system. These GUIs can be open as individual windows in rqt making it simple to manage various processes at the same time. Running the ROS image srcript installNoetic.sh
includes the rqt tool, there are common plugins already available available such as rqt_image_view
for displaying images, rqt_graph
for viewing the network of node graphs, and rqt_plot
for a visual representation of a 2-D plot.
In the package repository, there is a perspective file that includes GUIs for Dynamic Reconfigure, Image View, and Node Graph plugins. To open this perspective file, refer to the following instructions below.
There are two ways of loading the vmxpi_ros_rqt.perspective
file:
RQT GUI
Run
rqt
in the command line ensuringroscore
is running in another terminal in the background
rqt
Navigate to the Perspectives tab
Select
Import...
from the dropdownNavigate to the location of the
vmxpi_ros_rqt.perspective
file and open it
From the dashboard, three windows should appear containing the Dynamic Reconfigure, Image View, and Node Graph GUIs.
From the Command Line
Open a terminal and run
roscore
roscore
Open another terminal and run
rqt --perspective-file "/home/pi/catkin_ws/src/vmxpi_ros/vmxpi_ros_rqt.perspective"
After running the command, the rqt dashboard will appear with the GUIs of the same three plugins already opened as dockable windows.
Control Station¶
There are two versions of Control Station, a full GUI and a simple console based version.
Control Station Console¶
The Control Station Console is a simple console based version of Control Station.
Installation¶
Installing¶
Once downloaded, run the Control-Station-Console.exe
this will start the install.
The install will then run through the license agreement, and you can choose your installation path.
The install will now install onto your computer. It will be complete when you see this page.
Operation¶
Using the Control Station Console¶
The control station console is an easy to use program to enable and disable the VMX. To start using the control station console open the Control Station Console.exe
on your desktop or start menu.
The console will ask you to enter your robots IP address. If your team number is 1234
then using the format 10.xx.xx.2
your IP address will be 10.12.34.2
. If you have an ethernet connection to the robot the IP address will be 172.22.11.2
.
The console will then connect the Shuffleboard key to your IP address and launch Shuffleboard for you.
Control Station Console Main Screen¶
Battery Voltage Indicator - This will tell you the current voltage of the battery. When the battery starts to approach 11.5V it is time to replace with a charged battery.
Robot Current State - This is the state indicator for the robot. As pictured the robot is in Teleoperated and is Disabled. When the robot is enabled you will see a
Teleoperated Enabled
status instead.IP Address you punched in and what is being used.
Quit (q) - press
q
on the keyboard to quit.Set enabled (e,d) - press
e
to enable the robot and pressd
to disable the robot.Set Control Mode (o,a,t) - Sets the control mode, currently as pictured the console is in Teleoperated mode.
o is Teleoperated
a is Autonomous
t is Test
Status Indicators - These are some flags to show you that connections are present. There are three flags
Robot Comms
,Robot Code
, andJoysticks
.Robot Comms will indicate that the console is talking with the robot.
Robot Code will indicate that there is valid code running on the robot.
Joysticks will indicate that there is a joystick plugged in.
Networking¶
Tip
Out of the box, the VMX will emit a Wi-Fi with an SSID of WorldSkills-1234
and requires a password. The password is password
.
The VMX allows for four different types of network connections.
Wi-Fi Access Point (AP)
Wi-Fi Client
Ethernet
Direct Desktop Connection
Important
It is always recommended to be in Wi-Fi AP mode. This is a factory default out of the box.
Remote Desktop Connection¶
In AP, Client, and Ethernet mode, the VMX can be connected to a remote desktop connection. The preferred remote connection is VNC Viewer, which can be downloaded here. VNC Viewer has the benefit of being able to see the desktop of the VMX without the need for cables plugged into the VMX.
When first opening the VNC Viewer, it will look like this:

To access the VMX in the address bar, type in the IP address of the VMX.

There will be an identity check error; hit continue.

The login screen will now be visible to login use pi
for username and raspberry
for the password.

You should now have access to the VMX desktop.

Cannot currently show the desktop¶

The Cannot currently show the desktop error occurs as the VMX has been set to console boot mode. To fix this, a remote ssh session is required. Using an application such as PuTTY will allow for an ssh connection.
To start open PuTTY, change the connection type to SSH and enter the VMX’s IP address.

A terminal will pop up and ask for the login credentials. Just as before, the username is pi
and the password is raspberry
.
Note
PuTTY uses standard networking encryption, so when typing in the password, there will be no text on the screen.

Once logged in, the VMX’s terminal will be shown. To make the changes required, we will use raspi-config. Type the following command in to get to raspi-config.
sudo raspi-config
This will open raspi-config. Navigate using the arrow keys on the keyboard to 3 Boot Options
.

Select B1 Desktop / CLI
.

Choose B4 Desktop Autologin
. This will tell the VMX to boot up into the desktop and auto-login for us.

It should now be at the main screen of raspi-config again. There is one last step to fix the error. Select 7 Advanced Options
.

Select A5 Resolution

And choose any resolution that you want but the default.

Hit ESC
to get back to the terminal and run the command below to restart the VMX.
sudo reboot now
Once rebooted, the VMX should now be accessed by the VNC viewer with the desktop visible.
Ethernet¶
The Ethernet port is always available and always on the same IP address. It uses the IP address of 172.22.11.2
.
Wi-Fi Access Point (AP)¶
This is the default always recommended mode to be in. In this mode, the VMX will create its own Wi-Fi and allow a computer to connect.

In AP mode, the IP address uses the format of 10.XX.YY.2
where XXYY corresponds to a four-digit team number. Out of the box, the team number is set to 1234, which will give an IP address of 10.12.34.2
.
To change this configuration or put the VMX back into AP mode, run the following command below.
setupWifiAP.sh SSID TEAMNUMBER PASSWORD
Where:
SSID is the prefix for the name of the Wi-Fi.
TEAMNUMBER is the four-digit team number.
PASSWORD is an optional add-on that allows you to create a password for the Wi-Fi.
Important
At a worldskills competition, passwords will be required!
Out of the box, the Wi-Fi for the VMX will be WorldSkills-1234
, and the password is password
. In this case the SSID = WorldSkills
, the TEAMNUMBER = 1234, and the password = password
. To get this, the command will look like this.
setupWifiAP.sh WorldSkills 1234 password
Wi-Fi Client¶
Wi-Fi Client mode allows the VMX to connect to the internet.
To get into client mode, run the command:
setupWifiClient.sh
Important
Remember when done in client mode to switch back to AP mode.
Direct Desktop Connection¶
The direct desktop connection is using the VMX as an average computer. This would entail plugging a keyboard and mouse into the VMX USB ports and then a micro HDMI cable into one of the HDMI ports on the VMX. Usually, this is required when the IP address is unknown, or there is an issue where the networking is not working, and more troubleshooting is needed.
Connecting Sensors and Actuators¶
The VMX Robotics Controller provides a large number of electrical power and signal “pins” which connect to external devices including Sensors and Actuators.
Note
This summary of VMX IO configuration is sufficient for most robot Programming uses; more detailed information is available in the Hardware Reference Manual.

VMX Connector Blocks¶
VMX provides several different Connector Blocks.
Connector Block |
Connector Type |
Location on VMX |
---|---|---|
Flex DIO Header |
3-pin PWM-style |
Left-side Top |
High Current DIO Header |
3-pin PWM-style |
Left-side mid |
Analog Input Header |
3-pin PWM-style |
Left-side bottom |
Comm DIO Connectors |
4-pin JST GH |
Bottom-left |
Flex DIO Connectors |
4-pin JST GH |
Bottom-middle |
CAN Connector |
2-wire Weidmuller |
Bottom-right |
Three (3) types of Connectors are used:

3-pin PWM-style Connector¶

4-pin JST GH Connector¶

2-Wire CAN Wire¶
3-pin PWM-style Connectors, JST GH Connectors and Breakout Boards and CAN Wires are available for purchase online.
FlexDIO Connectors¶
FlexDIO Connectors are a set of four locking JST GH connectors (4 pins each) with power, ground, signal A and signal B on each connector. These connectors are designed to support Quadrature Encoders, but may also be configured for use as Digital Inputs, Interrupts, Digital Outputs, PWM Generators or Counters.

FlexDIO Connectors¶
FlexDIO Header¶
The FlexDIO Header provides 4 sets of power, ground, and a single signal channel. The signals may be configured to support Quadrature Encoders, Digital Inputs, Interrupts, Digital Outputs, PWM Generators or Counters. Note that only 2 of the pins on this header support Quadrature Encoders, see below for details.

FlexDIO Header¶
CAN Connector¶
The CAN Connector accepts a pair of wires (CANH and CANL signals) with bare ends, which connect to a CAN bus.

CAN Connector¶
High-Current DIO Header¶
The High-Current DIO Header provides 10 sets of power, ground, and a single signal channel. The signals may be configured to support Digital Inputs, Interrupts, Digital Outputs, PWM Generators or Relays.

High-Current DIO Header¶
Note
The High-Current DIO Header may be configured in either Output or Input Direction, see below for details.
Analog Input Header¶
The Analog Input Header provides 4 sets of power, ground, and a single signal channel. The signals may be configured to support Analog Accumulation and/or Analog-triggered Interrupts.

Analog Input Header¶
CommDIO Connectors¶
The three (3) CommDIO Connectors are three locking JST GH connectors (4 pins each) with different sets of power/ground/signals. Each connector may be configured to communicate using the corresponding digital communication protocol. Alternatively, the Input Channels may be configured for use as Digital Inputs or Interrupts; Output Channels may be configured for use as Digital Outputs or PWM.

CommDIO Connectors¶
Each of the four pins on each connector have a different definition, depending upon the type:
I/O Channel Type Pin 1 |
Pin 2 |
Pin 3 |
Pin 4 |
|
---|---|---|---|---|
I2C |
Ground |
Power (5 or 3.3V) |
SDA [OUTPUT] |
SCL [OUTPUT] |
TTL UART |
Ground |
Power (5 or 3.3V) |
TX [OUTPUT] |
RX [INPUT] |
SPI |
SCK [OUTPUT] |
MOSI [OUTPUT] |
MISO [INPUT] |
CS [OUTPUT] |
Note
Unlike the I2C and TTL UART Connectors, the SPI connector has 4 signal pins and does not provide power and ground.
Output Voltage Selection¶
Either 5 or 3.3V power output for external devices (both power and signal level) may be selected for Flex, High Current and Comm DIOs and also for power pins on the Analog Input Header.

Output Voltage Selection Jumpers¶
Caution
If any of the external devices connected to pins in any of these groups are not 5V tolerant, ensure the voltage selection jumper is set to 3.3V to avoid damage to the external device.
Note
The Output Voltage Selection Jumper can only be accessed by opening the VMX enclosure.
High Current DIO Channel Direction configuration¶
The entire bank of High Current DIOs can be either all outputs (default), or all inputs. Direction selection is performed in hardware via the High Current DIO Input/Output Jumper. If the jumper is present, all High Current DIOs function as outputs, otherwise they function as inputs.
Output Configuration: 10 High Current DIO Pins are Digital Outputs Input Configuration: 10 High Current DIO Pins are Digital Inputs

High Current DIO Channel Direction Jumper¶
The High Current Direction setting impacts the behavior of PWM, Relay and Digital IO Channels, described further below. Therefore this setting is one of the first things to verify in case of improper operation of the High Current DIO Channels.
Tip
Use the default Direction (Output) unless your configuration requires more digital inputs.
Note
The Output Voltage Selection Jumper can only be accessed by opening the VMX enclosure.
WPI Channel Addressing¶
When programming a robot application using the WPI Library, logical WPI Channel Numbers are used; these WPI Channel Numbers are different than the VMX Pin Numbers described in Connecting Sensors and Actuators.
Important
WPI Channel Addressing is impacted by whether the High-Current DIO Direction Selection Jumper is set to OUTPUT or INPUT.
Similarly, WPI Channel Identifiers are also used to address Digital Communications Ports.
High Current DIO Header OUTPUT DIRECTION (Default)¶
When the VMX High Current DIO Direction is set to OUTPUT, the various WPI Library Channel types (Analog Input, PWM, Relay, Digital IO) must be addressed as described in this section.
Analog Input Channel Addressing¶
Four (4) Pins on the VMX Analog Input Header are addressable via four (4) WPI Library Analog Input Channel Addresses.

WPI Library Analog Input Channel Addressing¶
PWM Channel Addressing¶
28 VMX pins are usable for PWM and are addressable via 28 WPI Library PWM Channel Addresses.

FlexDIO Header and High-Current DIO Header WPI Library PWM Channel Addressing¶

CommDIO and FlexDIO Connector WPI Library PWM Channel Addressing¶
Note
The High-Current Output Direction must be set to OUTPUT to use pins on the High-Current Header as PWM Generators.
Digital I/O (DIO) Channel Addressing¶
30 VMX pins are usable for Digital I/O Channels and are addressable via 30 WPI Libray DIO Channel Addresses.
Note
All FlexDIO pins are direction-selectable in software.
Note
When configured in the OUTPUT Direction, the High Current DIO Pins only have Output Capability.
Note
Each CommDIO Pin is either an Output or an Input.

FlexDIO Header and High-Current DIO Header WPI Library DIO Channel Addressing¶

CommDIO and FlexDIO Connector WPI Library DIO Channel Addressing¶
Limits on Quadrature Encoders and Counters¶
Quadrature Encoder Configuration¶
Up to 5 Quadrature Encoders are supported. Quadrature Encoders A & B Inputs must be connected to adjacent pairs of FlexDIO Digital Input Channels; the following FlexDIO Digital Input Channel pairs may be used for Quadrature Encoders:
DI 0 and 1
DI 2 and 3
DI 4 and 5
DI 6 and 7
DI 8 and 9
The lower-numbered channel of each pair should be connected to Quadature Encoder Channel A, and the higher-numbered channel should be connected to Quadrature Encoder Channel B.
Counter Configuration¶
Up to 6 Counters are supported. Each Counter is internally connected to a adjacent pairs of FlexDIO Digital Input Channels. The following FlexDIO Digital Input Channel pairs may be used for Counters:
Counter Input Channel Pair |
Supported WPI Library Counter Modes |
---|---|
DI 0 & 1 |
kTwoPulse 1, kSemiPeriod, kExternalDirection |
DI 2 & 3 |
kTwoPulse 1, kSemiPeriod, kExternalDirection |
DI 4 & 5 |
kTwoPulse 1, kSemiPeriod, kExternalDirection |
DI 6 & 7 |
kTwoPulse 1, kSemiPeriod, kExternalDirection |
DI 8 & 9 |
kTwoPulse 1, kSemiPeriod, kExternalDirection |
DI 10 & 11 |
kTwoPulse 1, kSemiPeriod |
- 1(1,2,3,4,5,6)
kTwoPulse mode using two separate input signals (e.g., one “Up” input signal and a separate “Down” input signal) are not supported. However, a single input configured as both “Up” and “Down” is supported.
Note
kPulseLength mode is not supported on any VMX Counter. By extension, this implies that the “Direction Sensitive” mode of the WPI Library’s “Geartooth” class is not supported.
Note
If configuring a counter to use one input channel (e.g., kTwoPulse or kSemiPeriod modes), the unused input channel in that Counter’s Channel Pair may be configured in software for other uses (including Digital Input, Interrupt, Digital Output), although it may not be configured for PWM Generation or PWM Capture.
High Current DIO Header INPUT DIRECTION¶
When the High Current DIO Direction Jumper is set to INPUT, the WPI Library Channel Addressing is impacted as follows:
WPI Library PWM Channels 0-9 are NOT PRESENT in INPUT MODE.
WPI Library Relay Channels are NOT PRESENT in INPUT MODE.
WPI Library Digital IO Channels on the HiCurrDIO Header are in INPUT MODE ONLY in INPUT MODE, as shown below:

High-Current DI Header WPI Library DIO Channel Addressing (when in INPUT MODE)¶
Digital Communication Port Addressing¶
VMX provides several different types of Digital Communications Ports:
Serial Ports
I2C Port
SPI Port
Serial Ports¶
The WPI Library SerialPort class includes Serial Port Identifiers, as follows:
Serial Port Identifier |
Type |
Notes |
---|---|---|
kOnboard |
RS-232 Port |
Not implemented on VMX |
kMXP |
TTL UART |
VMX CommDIO “UART” Connector |
kUSB |
USB Serial Port |
Raspberry Pi “top-left” USB port; aka ‘kUSB1’ |
kUSB1 |
USB Serial Port |
Raspberry Pi “top left” USB port |
kUSB2 |
USB Serial Port |
Raspberry Pi “bottom left” USB port |
Note
As can be seen in the table above, both kUSB and kUSB1 identifiers map to the same physical connector, and thus cannot be used simultaneously.
The Raspberry Pi 4 provides multiple USB Ports which support the USB Serial Port standard; the WPI Serial Port Identifiers which are mapped to these USB Ports are shown below:

Raspberry Pi USB Port WPI Library Serial Port Addressing¶
TTL UART Communication Speeds¶
Available TTL UART Communication speeds can be as high as 230400 bits/sec. Note that the TTL UART-capable device connected to VMX may only communicate at a lower speed than 230400 kbps; consult the external device technical documentation for further details.
USB Serial Port Communication Speeds¶
USB Serial Port Communication Speeds can be much higher than TTL UART Communication Speeds, and are variable depending upon USB bus usage and the capabilities of the connected device; users do not specify USB Serial Port communication speeds.
I2C Port¶
VMX provides one I2C port.
The WPI Library I2C class includes two (2) I2C Port identifiers, as follows:
Serial Port Identifier |
Type |
Notes |
---|---|---|
kOnboard |
I2C Fast Mode[2]_ |
VMX CommDIO “I2C” Connector |
kMXP |
I2C Fast Mode[2]_ |
VMX CommDIO “I2C” Connector |
- 2
See I2C Communication Speeds section below.
Note
As can be seen in the table above, both kOnboard and kMXP identifiers map to the same physical connector, and thus cannot be used simultaneously.
Note
The Raspberry Pi 4B supports I2C clock-stretching, however previous versions of Raspberry Pi do not. If the I2C device accessed requires I2C clock stretching, Raspberry Pi 4B is required.
I2C Communication Speeds¶
By default, the Raspberry Pi I2C bus speed is 100Khz (“Standard Mode”). To change the bus speed to 400Khz (Fast Mode) follow these I2C bus speed configuration instructions. Note that the I2C-capable device connected to VMX may only communicate at a lower speed than 400Khz; consult the external device technical documentation for further details.
SPI Port¶
VMX provides one SPI port.
The WPI Library “SPI” class includes five (5) SPI Port identifiers, as follows:
Serial Port Identifier |
Type |
Notes |
---|---|---|
kOnboardCS0 |
4-wire SPI |
VMX CommDIO “SPI” Connector |
kOnboardCS1 |
4-wire SPI |
VMX CommDIO “SPI” Connector |
kOnboardCS2 |
4-wire SPI |
VMX CommDIO “SPI” Connector |
kOnboardCS3 |
4-wire SPI |
VMX CommDIO “SPI” Connector |
kMXP |
4-wire SPI |
VMX CommDIO “SPI” Connector |
Note
As can be seen in the table above, each kOnboardx and kMXP identifiers map to the same physical connector, and thus cannot be used simultaneously.
SPI Communication Speeds¶
The Raspberry pi supports a wide range of SPI speeds.
By default, the WPI Library SPI class defaults to 500Khz, but this can be increased as necessary.
Although higher speeds are theoretically possible, 16Mhz is considered a safe maximum speed, and lower is recommended due since very fast signals can be easily degraded. Note that the SPI-capable device connected to VMX may only communicate at a lower speed than 16Mhz; consult the external device technical documentation for further details.
Note that the actual speed may not match the requested speed; more information on the actual speeds is contained within the Raspberry Pi SPI Documentation.
Configuring and Testing the SR Pro Camera¶
Once the SR Pro Camera has been installed onto the VMX Robotics Controller, the next step is to ensure the camera is configured correctly and to verify the camera images can be accessed on the Raspberry Pi.
Configuring Raspberry Pi’s Camera Interface¶
By default, the VMX Robotics Controller Raspberry Pi SD Card enables the Raspberry Pi Camera interface. This can be verified from the Raspberry Pi “Preferences->Raspberry Pi Configuration” Menu Item:

Raspberry Interface Configuration Dialog¶
The “Camera” interface should be enabled; if this setting is changed, the Raspberry Pi must be rebooted for the change to take effect.
Testing the Camera¶
To verify basic camera option, remove the SR Pro Lens cap, and acquire a still image by running the raspistill command from a Raspberry Pi terminal:

Acquiring an Image to File with raspistill¶
Once acquired, the image can be viewed from the Raspberry Pi Desktop.

Viewing the image captured with raspistill¶
NOTE: If your VMX Robotics Controller has an actively running Robot Program that is accessing the camera, raspistill will fail to acquire an image and output an error message.
If you are confident the SR Pro Camera is securely installed and still encounter an error message when Testing the Camera using raspistill, it’s possible a Robot Program running on the VMX Robotics Controller already has access to the SR Pro Camera; in this case, you can shut down any actively running Robot Program by issuing this command at a Raspberry Pi console:
frcKillRobot.sh
To restart the robot program, you can later run this command:
frcRunRobot.sh
Raspberry Pi Camera Video Device ID¶
When writing code to access the SR Pro Camera, the Video Input Device number must be used to address the Camera.
By default, when the SR Pro Camera is the only Video Input Device connected to the Raspberry Pi, it’s Video Input Device number is 0.
The Raspberry Pi raspistill utility can verify this, as it will display the Video Input Device number when the “-v” (verbose) option is provided as shown below:

Displaying the Video Input Device number using raspistil’s verbose mode.¶
Simple Camera Test Script¶
The following python script, which runs directly on the Raspberry Pi, can be used to acquire video from the SR Pro Camera; this script uses the OpenCV Library to acquire images from the SR Pro Camera, convert them to grayscale, and render the images to the Raspberry Pi display. This script continues to run until the “ESCAPE” key is pressed.
Note that in the script code below, the “0” parameter to the cv2.VideoCapture() function indicates that Video Input Device 0 should be used.
Since both OpenCV and Python are pre-installed on the VMX Robotics Controller SD-Card, no other software must be installed to run the following script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import numpy as np import cv2 cap = cv2.VideoCapture(0) cap.set(3,640) # Width cap.set(4,480) # Height while(True): ret, raw = cap.read() raw = cv2.flip(raw, -1) gray = cv2.cvtColor(raw, cv2.COLOR_BGR2GRAY) cv2.imshow('raw', raw) cv2.imshow('gray', gray) k = cv2.waitKey(30) & 0xff if k == 27: # 'ESC' to exit break cap.release() cv2.destroyAllWindows() |
Simply save the above code to a file with a “.py” extension (for example “sr_pro_test.py”), and then execute it from a Raspbery Pi console by running this command:
python sr_pro_test.py
Accessing the SR Pro Camera using the WPI Library¶
The SR Pro Camera may be accessed from a Robot Program via the WPI Library CameraServer class, which works with the Raspberry Pi’s V4L2 driver to both configure and acquire camera data.
The Video Input Device number described above is used to specify the video camera in the WPI Library CameraServer class, as shown below:
// Creates UsbCamera and MjpegServer and connects them int sr_pro_video_input_device_number = 0; CameraServer.getInstance().startAutomaticCapture(sr_pro_video_input_device_number);
WPI Library-based vision processing techniques are documented in the WPI Libary vision processing documentation.
Updating Firmware¶
In certain cases you may need to update the VMX firmware; use the following instructions to accomplish updating the VMX firmware.
Requirements¶
VMX Circuit Board (rev. 5.35 or higher)
PC with USB 2.0 port running Windows 7 or greater.
Micro-USB Cable
Updating the Firmware¶
Download the VMX Tools for Windows latest build.
Unpack the contents of the vmx-pi.zip file and run the setup.exe program, which will install the tools as well as all necessary device drivers for communicating over USB with the VMX-pi, as well as some additional tools. In addition, the setup program will install the latest firmware at the following location:
<HomeDirectory>\vmx-pi\firmware
For example, if your user name is Robot, the directory name will be C:UsersRobotvmx-pifirmware.
Within that directory, the firmware file will be named using this pattern:
vmx-pi_X.Y.ZZZ.hex
(X = Major Version Number Y = Minor Version Number Z = Revision Number)
Press and hold down the “CAL” button on the VMX circuit board. While holding this button down, connect a USB-micro cable from a Windows PC to the VMX circuit board. Use the micro-usb connector immediately to the left of the VMX power connector. Applying power when the “CAL” button is held down places the board into “bootloader” mode, at which point the firmware can be loaded.
From your Start Menu, select “Kauai Labs” and then click on the VMXFirmwareUpdater menu item, and follow the directions included in the program.
Once you have downloaded the firmware, you can use the “Currently-loaded Firmware Version” tab of the VMXFirmwareUpdater to verify the version number you have just installed.
VMX OS Image¶
The VMX contains a specifically built version of raspbian buster that will run on a raspberry pi. The prefered raspberry pi is the 4B however, it will work on the 3B+ and Zero W.
The OS image can be downloaded here.
Note
The image download is 4GB!
Once downloaded a flashing software is required to flash the image to the SD Card. The recommended software to do this is Etcher.
Important
It is highly recommended to use a Samsung 32GB EVO Plus micro SD card.
Flashing¶
To start flashing the SD card first plug the SD card into your computer. Open Etcher
, you will notice that it has auto detected the SD card. If it has not detected the SD card you can manually select and find it.

Hit Select image
and find the SD.img.gz
file that was downloaded before.

Flash
will now be available. Hit Flash to start flashing the SD card image to the SD card. Note this can take a while depending on your computer. The image has been compressed from 32GB to 2.07GB, which also helps in the flashing time.

After flashing Etcher will automatically start to validate the flash to ensure that the flash was successful.

When complete the SD card will be auto ejected and can be stuck directly back into the VMX.

Troubleshooting¶
VMX LEDs¶
VMX provides several LEDS to indicate when it is operating normally and whether certain exceptional conditions are occuring.

VMX Normal Operation LED States¶
During normal operation, four (4) Green LEDs should be lit, as follows:
LED |
Location |
Meaning |
---|---|---|
S1 |
Middle |
Data being received from navX-Sensor |
S2 |
Middle |
Communication with navX-Sensor occurring |
3.3V |
Middle |
VMX processor power valid |
CAN Status |
Right |
CAN Circuitry receiving/sending CAN messages |

VMX Orange CAL LED (navX-Sensor Factory Calibration in Progress)¶
During Factory and Startup Calibration of the navX-Sensor, the Orange CAL LED will flash. During this time the VMX must be held still.

VMX Red Fault LED (Overcurrent or Short Circuit Detected)¶
If VMX’s External Power Supply (which supplies power to the VMX Power Pins on the FlexDIO Connectors and Headers, High-Current DIO Header, Analog Input Header and CommDIO Connectors) is flashing, this indicates that the VMX current protection circuitry is detecting either an over-current or a short-circuit condition.
Important
If the Fault LED is flashing, power to the External Power Pins will be removed; this condition must be resolved before power will be reapplied to these pins.
Titan Quad¶
The Titan Quad is a motor controller with four DC motor outputs that operate on the CAN Bus. Developed for World Skills but adapted for other uses.
Map¶
Power input. Input requires a 12VDC battery, and two ports are available connected in parallel. Both ports can be used for increasing the capacity or as a battery in, battery out.
Power output. Outputs 12VDC out to other devices such as, VMXpi or Servo Power Block.
Voltage indicators. There is a reverse power indicator (red) that will light up if the voltage is connected in reverse. The other two indicators display the voltage rails 5V and 3.3V.
Fusebox. Before voltage can be applied to the motors or power outputs (2), an appropriate fuse must be inserted into the box. Motors take 20A fuses, and power outputs take 5 - 15A fuses.
RGB Status Light.
DFU USB - used to communicate with the computer for updates and configuration.
CAN-BUS Input - High side (yellow) and Low side (green) inputs.
M1 - Motor 1 output.
M0 - Motor 0 output.
M3 - Motor 3 output.
M2 - Motor 2 output.
Boot - used only when an error occurs, and Titan cannot communicate with the computer and needs a firmware upgrade.
NeoPixel - addressable LED output
DotStar - addressable LED output
Pin 13/ L for LED microcontroller
RX/TX - LEDs for microcontroller
LED i2c - com port for microcontroller
LED USB - used to communicate with the computer for uploading code
Encoder port - Quadrature encoder input
Limit H - High limit switch input. (Limits are pulled high and use hardware debouncing)
Limit L - Low limit switch input. (Limits are pulled high and use hardware debouncing)
Electrical Characteristics¶
Function |
Min |
Nom |
Max |
---|---|---|---|
Input Voltage |
10VDC |
12VDC |
14VDC |
Output Voltage |
10VDC |
12VDC |
14VDC |
Motor Output Amperage |
0A |
— |
20A |
Motor Frequency |
0Hz |
15.6KHz |
20KHz |
Encoder Voltage Output |
4.5V |
5V |
5.5V |
Limit Switch Output |
4.5V |
5V |
5.5V |
LED Voltage Output |
4.5V |
5V |
5.5V |
LED Output Amperage |
0A |
— |
6A |
Programming the Titan¶
Motor Setup¶
1 2 3 4 5 6 7 8 | //import the TitanQuad Library import com.studica.frc.TitanQuad; //Create the TitanQuad Object private TitanQuad motor; //Constuct a new instance motor = new TitanQuad(TITAN_CAN_ID, TITAN_MOTOR_NUMBER); |
Note
TITAN_CAN_ID
is the CAN id for the Titan, by defualt it is 42. TITAN_MOTOR_NUMBER
is the motor port to be used. Valid range is 0 - 3
, this corresponds to the M0 - M3 on the Titan.
1 2 3 4 5 6 | //Include the TitanQuad Library #include <studica/TitanQuad.h> //Constuct a new instance private: studica::TitanQuad motor{TITAN_CAN_ID, TITAN_MOTOR_NUMBER}; |
Note
TITAN_CAN_ID
is the CAN id for the Titan, by defualt it is 42. TITAN_MOTOR_NUMBER
is the motor port to be used. Valid range is 0 - 3
, this corresponds to the M0 - M3 on the Titan.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //Include the TitanQuad Library #include "TitanDriver_ros_wrapper.h" int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "titan_node"); ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system VMXPi vmx(true, (uint8_t)50); //realtime bool and the update rate to use for the VMXPi AHRS/IMU interface, default is 50hz within a valid range of 4-200Hz TitanDriverROSWrapper titan(&nh, &vmx); ros::spin() //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
Note
TITAN_CAN_ID
is the CAN id for the Titan, by defualt it is 42. TITAN_MOTOR_NUMBER
is the motor port to be used. Valid range is 0 - 3
, this corresponds to the M0 - M3 on the Titan.
Setting Motor Speed¶
1 2 3 4 5 6 7 8 9 | /** * Sets the speed of a motor * <p> * @param speed range -1 to 1 (0 stop) */ public void setMotorSpeed(double speed) { motor.set(speed); } |
1 2 3 4 5 6 7 8 9 | /** * Sets the speed of a motor * <p> * @param speed range -1 to 1 (0 stop) */ void ClassName::SetMotorSpeed(double speed) { motor.Set(speed); } |
1 2 3 4 5 6 7 8 9 10 11 12 /** * Sets the speed of a motor by sending a request to the motor-speed server * speed range -1.0 to 1.0 (0 stop) */ ros::ServiceClient set_m_speed = nh->serviceClient<vmxpi_ros::MotorSpeed>("titan/set_motor_speed"); vmxpi_ros::MotorSpeed msg; msg.request.speed = rightSpeed; msg.request.motor = 0; set_m_speed.call(msg);
Note
This is a demonstration of calling the motor speed service using the set_motor_speed
server.
Full Example¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package frc.robot.subsystems; //Subsystem Base import import edu.wpi.first.wpilibj2.command.SubsystemBase; //Titan import import com.studica.frc.TitanQuad; public class Example extends SubsystemBase { /** * Motors */ private TitanQuad motor; public Example() { //Motors motor = new TitanQuad(TITAN_CAN_ID, TITAN_MOTOR_NUMBER); } /** * Sets the speed of a motor * <p> * @param speed range -1 to 1 (0 stop) */ public void setMotorSpeed(double speed) { motor.set(speed); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #pragma once //Include SubsystemBase #include <frc2/command/SubsystemBase.h> //Include Titan Library #include "studica/TitanQuad.h" class Example : public frc2::SubsystemBase { public: Example(); void SetMotorSpeed(double speed); private: studica::TitanQuad motor(TITAN_CAN_ID, TITAN_MOTOR_NUMBER); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //Include Header #include "subsystems/Example.h" //Constructor Example::Example(){} /** * Sets the speed of a motor * <p> * @param speed range -1 to 1 (0 stop) */ void Example::SetMotorSpeed(double speed) { motor.Set(speed); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | //Include the TitanQuad Library #include "TitanDriver_ros_wrapper.h" double motor1_speed; // Returns the speed of motor 1 void motor1_speed_callback(const std_msgs::Float32::ConstPtr& msg) { motor1_speed = msg->data; } int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "titan_node"); /** * Constructor * Titan's ros threads (publishers and services) will run asynchronously in the background */ ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system VMXPi vmx(true, (uint8_t)50); //realtime bool and the update rate to use for the VMXPi AHRS/IMU interface, default is 50hz within a valid range of 4-200Hz ros::ServiceClient set_m_speed; ros::Subscriber motor1_speed_sub; TitanDriverROSWrapper titan(&nh, &vmx); /** * Sets the speed of a motor by sending a request to the motor-speed server * speed range -1.0 to 1.0 (0 stop) */ set_m_speed = nh.serviceClient<vmxpi_ros::MotorSpeed>("titan/set_motor_speed"); vmxpi_ros::MotorSpeed msg; msg.request.speed = 1.0; //Setting the motor 0 speed to 1.0 msg.request.motor = 0; set_m_speed.call(msg); // Subscribing to Motor 1 speed topic to access the speed data motor1_speed_sub = nh.subscribe("titan/motor1/speed", 1, motor1_speed_callback); ros::spin(); //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
Important
Subscribe to Titan topics to access the data being published and write callbacks to pass messages between various processes.
Note
Calling the frcKillRobot.sh
script is necessary since the VMXPi HAL uses the pigpio library, which unfortunately can only be used in one process. Thus, everything that interfaces with the VMXPi must be run on the same executable. For more information on programming with ROS, refer to: ROS Tutorials.
Download Update App¶
To download the latest update app V2.0.0.14
click here.
Important
This app is still in early development and there will be bugs. Report any bugs here(link not made yet).
After running the installer there will be a prompt as shown below.
Note
Don’t panic it is not a virus!
Hit more info.
This will then show the Run Anyway
button. Hit that button to start the install.
Note
Admin is required.
After accepting admin privileges the EULA will pop up. You can read through it if you wish or hit next.
Hit next for the next few prompts and the install will start. When complete you will see this page.
Using the Update App¶
The Studica Update and Config app was created to allow users to get the most out of their TitanQuad motor controller.
Settings¶
The landing or Settings page allows for the setting of the custom CAN ID, Encoder Ticks per rev for the motor you are using, the current limit for the motors, Motor idle mode, S-curve sensitivity, and limit switch control.

When the app finds a valid device, it will be displayed in the drop-down menu.
Note
The default CAN ID out of the box is 42.
CAN ID¶
The CAN ID is the unique id of the motor controller on the CAN bus. The valid range is 1 - 62.
Encoder Resolution¶
Is the counts per revolution of the encoder you are using on your motor. For example, the Studica Maverick has a CPR of 732, whereas the Pitsco Torquenado has a CPR of 1440.
Current Limit¶
This is the limit you want for the amount of current to flow to the motors. Valid range 0 - 20A.
Idle Mode¶
When false, this sets the motors to coast mode, and when high, the motors are in break mode.
S-Curve Sensitivity¶
Sets the sensitivity level of the S-Curve formula.
Limit Switches¶
Control panel for limit switch configuration. There are two limit switch ports per motor on the Titan, a high and a low.
Parameters
Enable - simple enable and disable
NO/NC - let the microcontroller know if you are using a NO contact or a NC contact (inverts the output)
Automatic Bounce back - upon making contact with the limit switch, the motor will move in the opposite direction just a bit.
Save Configuration¶
Saves the current settings to the TitanQuad. A prompt will confirm that settings have been saved.

Restore Factory Defaults¶
Will restore the TitanQuad to it’s recommended factory settings.
Firmware¶
Important
Internet connection is required to download firmware!
Every so often, a firmware upgrade is required to fix a bug or include new functionality.

To update the firmware, navigate to the firmware tab and then hit check for updates.
Note
If the button is greyed out, you are not connected to the TitanQuad, hit connect in the upper right-hand corner.
A prompt will appear as it checks the version on the TitanQuad to the server version.

If there is an update, another prompt will ask if you would like to download the new firmware.

Once downloaded, you can hit Upgrade Firmware
to flash the new firmware to the TitanQuad.

Note
To tell if the TitanQuad is in update mode check to see if the power indicators are green and the status light is off.
When complete, there will be an indicator saying that the firmware upgraded was completed.

System Info¶
System Information is used for diagnosing and contacting support.

Important
The Unique ID is required for any support tickets.
Titan Status Light¶
Below are the various status light blink codes and the meaning behind them.
Function |
Blink 1 |
Blink 2 |
Blink 3 |
---|---|---|---|
Titan Off / Update |
![]() |
![]() |
![]() |
No Communication |
![]() |
![]() |
![]() |
CAN Detected, Robot Disabled |
![]() |
![]() |
![]() |
CAN Detected, Robot Enabled |
![]() |
![]() |
![]() |
Fault Detected |
![]() |
![]() |
![]() |
Titan Off / Update¶
When the Titan is off, there will be no flashing light. The light will also be off if set to update mode. If the Titan is on, not in update mode, and the light is off, there could be a problem with the microcontroller or the LED.
No Communication¶
Is typically seen during bootup. When the Titan receives any CAN packet that is not blocked by the filter, the flashing blue will switch over to CAN Detected. If the light is still flashing blue when it should be in CAN Detected, either the CAN ID on the Titan is set incorrectly or the CAN ID set in the robot code is incorrect.
Important
If the CAN ID on the Titan is changed through the config app, the Titan needs to be rebooted for the configuration on the Titan to be set correctly.
CAN Detected, Robot Disabled¶
The flashing lights of RED, GREEN, BLUE, resembles that the CAN bus is detected; however, the robot is disabled. To get out of this state, the robot must be enabled via the driver station. If the robot is enabled and the status light is still showing this state, there is no communication between the driver station and the VMXpi.
CAN Detected, Robot Enabled¶
The blinking purple displays that the robot is enabled and allows for the motors to be moved.
Fault Detected¶
This state will occur if there is a fault error on one of the gates for driving the motors. This could be but not limited to: thermal shutdown, current overflow, voltage cutoff, and gate failure.
Troubleshooting¶
Page to describe Titan troubleshooting problems
Cobra¶
The Cobra Line Follower provides an array of line IR reflective sensors to be used for detecting a line. The Cobra uses four QRE1113 sensors for detecting the line.

Function |
Min |
Nom |
Max |
---|---|---|---|
Input Voltage |
3.3VDC |
5VDC |
5VDC |
Current |
25mA |
70mA |
100mA |
Sensing Distance |
2mm |
3mm |
— |
Analog Module¶
To plug the Cobra into the VMXpi the analog module is required.

The Cobra will plug directly into the analog module. The module will then use the provided JST SH to JST GH cable to connect to the i2c
port on the VMXpi.
Programming the Cobra¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //import the Cobra Library import com.studica.frc.Cobra; //Create the Cobra Object private Cobra cobra; //Constuct a new instance cobra = new Cobra(); // or if sensor is using 3.3V cobra = new Cobra(3.3F); //Can then use these accssors to get data cobra.getVoltage(channel); //returns a float cobra.getRawValue(channel); //returns a double |
The accessor methods will output either the voltage (0 - 5V) or the raw ADC value (0 - 2047).
1 2 3 4 5 6 7 8 9 10 11 | //Include the Cobra Library #include "studica/Cobra.h" //Constructors studica::Cobra cobra{}; // or if sensor is using 3.3V studica::Cobra cobra{3.3F}; //Use these to access data cobra.GetVoltage(channel); //returns a float cobra.GetRawValue(channel); //returns a double |
The accessor functions will output either the voltage (0 - 5V) or the raw ADC value (0 - 2047).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | //Include the Cobra Library #include "Cobra_ros.h" double channel_1_V; // Returns the channel 1 voltage value reported by the Cobra sensor void c1_v_callback(const std_msgs::Float32::ConstPtr& msg) { channel_1_V = msg->data; } int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "cobra_node"); /** * Constructor * Cobra's ros threads (publishers and services) will run asynchronously in the background */ ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system VMXPi vmx(true, (uint8_t)50); //realtime bool and the update rate to use for the VMXPi AHRS/IMU interface, default is 50hz within a valid range of 4-200Hz ros::Subscriber c1_v_sub; CobraROS cobra(&nh, &vmx); //default device address is 0x48 and default voltage is 5.0F // or can use CobraROS cobra(&nh, &vmx, deviceAddress); // or if sensor is using 3.3V, refVoltage(3.3F) CobraROS cobra(&nh, &vmx, deviceAddress, refVoltage); // Use these to directly access data float voltage = cobra.GetVoltage(channel); //returns a float int raw_cobra = cobra.GetRawValue(channel); //returns an int // Subscribing to a Cobra voltage topic to access the voltage data c1_v_sub = nh.subscribe("cobra/c1/voltage", 1, c1_v_callback); ros::spin(); //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
The accessor functions will output either the voltage (0 - 5V) or the raw ADC value (0 - 2047).
Important
Subscribe to Cobra topics to access the data being published and write callbacks to pass messages between various processes.
Note
Calling the frcKillRobot.sh
script is necessary since the VMXPi HAL uses the pigpio library, which unfortunately can only be used in one process. Thus, everything that interfaces with the VMXPi must be run on the same executable. For more information on programming with ROS, refer to: ROS Tutorials.
Ultrasonic Distance Sensor¶
The Ultrasonic Distance Sensor has been updated from a 3-pin to a 4-pin sensor. This was done to create better compatiblity between multiple different control systems.

Function |
Min |
Nom |
Max |
---|---|---|---|
Input Voltage |
— |
— |
5VDC |
Current |
— |
15mA |
— |
Range |
2cm |
— |
400cm |
Measure Angle |
— |
15° |
— |
Frequency |
— |
40Hz |
— |
Trigger Pulse |
— |
10μS TTL |
— |
Programming the Ultrasonic Distance Sensor¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //import the Ultrasonic Library import edu.wpi.first.wpilibj.Ultrasonic; //Create the Ultrasonic Object private Ultrasonic sonar; //Constuct a new instance sonar = new Ultrasonic(Trigger, Echo); //Create an accessor method public double getDistance() { return sonar.getRangeInches(); // or can use return sonar.getRangeMM(); } |
The accessor methods will then output the range in either inches or mm.
Note
The valid digital pairs for Trigger and Echo pins are (Trigger, Echo) (0,1)
, (2,3)
, (4,5)
, (6,7)
, (8, 9)
, (10,11)
1 2 3 4 5 6 7 8 9 10 11 12 13 | //Include the Ultrasonic Library #include "frc/Ultrasonic.h" //Constructors frc::Ultrasonic sonar{Trigger, Echo}; //Create an accessor function double getDistance(void) { return sonar.GetRangeInches(); // or can use return sonar.GetRangeMM(); } |
The accessor functions will then output the range in either inches or mm.
Note
The valid digital pairs for Trigger and Echo pins are (Trigger, Echo) (0,1)
, (2,3)
, (4,5)
, (6,7)
, (8, 9)
, (10,11)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | //Include the Ultrasonic Library #include "Ultrasonic_ros.h" double ultrasonic_cm; // Returns the distance value reported by the Ultrasonic Distance sensor void ultrasonic_cm_callback(const std_msgs::Float32::ConstPtr& msg) { ultrasonic_cm = msg->data; } int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "ultrasonic_node"); /** * Constructor * Ultrasonic's ros threads (publishers and services) will run asynchronously in the background */ ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system VMXPi vmx(true, (uint8_t)50); //realtime bool and the update rate to use for the VMXPi AHRS/IMU interface, default is 50hz within a valid range of 4-200Hz ros::Subscriber ultrasonicCM_sub; UltrasonicROS ultrasonic(&nh, &vmx, 8, 9); //channel_index_out(8), channel_index_in(9) ultrasonic.Ultrasonic(); //Sends an ultrasonic pulse for the ultrasonic object to read // Use these to directly access data uint32_t raw_distance = ultrasonic.GetRawValue(); // returns distance in microseconds // or can use uint32_t cm_distance = ultrasonic.GetDistanceCM(raw_distance); //converts microsecond distance from GetRawValue() to CM // or can use uint32_t inch_distance = ultrasonic.GetDistanceIN(raw_distance); //converts microsecond distance from GetRawValue() to IN // Subscribing to Ultrasonic distance topic to access the distance data ultrasonicCM_sub = nh.subscribe("channel/9/ultrasonic/dist/cm", 1, ultrasonic_cm_callback); //This is subscribing to channel 9, which is the input channel set in the constructor ros::spin(); //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
The accessor functions will then output the range in either microseconds, inches, or cm.
Important
The valid digital pairs for Trigger and Echo pins are (Trigger, Echo) (0,1)
, (2,3)
, (4,5)
, (6,7)
, (8, 9)
, (10,11)
. Subscribe to Ultrasonic topics to access the data being published and write callbacks to pass messages between various processes.
Note
Calling the frcKillRobot.sh
script is necessary since the VMXPi HAL uses the pigpio library, which unfortunately can only be used in one process. Thus, everything that interfaces with the VMXPi must be run on the same executable. For more information on programming with ROS, refer to: ROS Tutorials.
Sharp IR Distance Sensor¶
The Sharp GP2Y0A21YK is one of the most reliable and accurate sensors in the collection. The Sharp IR has many benefits that make it one of the best sensors for a robot for distance tracking.

Function |
Min |
Nom |
Max |
---|---|---|---|
Input Voltage |
4.5VDC |
5V |
7VDC |
Output Voltage |
-0.3VDC |
— |
VIN + 0.3VDC |
Sensing Range |
10cm |
— |
80cm |
Current |
— |
30mA |
40mA |
Operating Temperature |
-10°C |
— |
60°C |
Storage Temperature |
-40°C |
— |
70°C |
Programming the Sharp IR Sensor¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //import the Analog Library import edu.wpi.first.wpilibj.AnalogInput; //Create the Analog Object private AnalogInput sharp; //Constuct a new instance sharp = new AnalogInput(port); //Create an accessor method public double getDistance() { return (Math.pow(sharp.getAverageVoltage(), -1.2045)) * 27.726; } |
The accessor method will output the range in cm.
Note
The valid Analog ports are 0-3
1 2 3 4 5 6 7 8 9 10 11 12 | //Include the Analog and Math Library #include "frc/AnalogInput.h" #include <cmath> //Constructors frc::AnalogInput sharp{port}; //Create an accessor function double getDistance(void) { return (pow(sharp.GetAverageVoltage(), -1.2045)) * 27.726; } |
The accessor function will output the range in cm.
Note
The valid Analog ports are 0-3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //Include the Sharp Library #include "Sharp_ros.h" double sharp_dist; // Returns the distance value reported by the Sharp IR sensor void sharp_dist_callback(const std_msgs::Float32::ConstPtr& msg) { sharp_dist = msg->data; } int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "sharp_node"); /** * Constructor * Sharp's ros threads (publishers and services) will run asynchronously in the background */ ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system VMXPi vmx(true, (uint8_t)50); //realtime bool and the update rate to use for the VMXPi AHRS/IMU interface, default is 50hz within a valid range of 4-200Hz ros::Subscriber sharpDist_sub; SharpROS sharp(&nh, &vmx); // or can use SharpROS sharp(&nh, &vmx, channel); //Use these to directly access the data double dist_cm = sharp.GetIRDistance(); //converts the average voltage read, outputs the range in cm double voltage = sharp.GetRawVoltage(); //returns the average voltage // Subscribing to Sharp distance topic to access the distance data sharpDist_sub = nh.subscribe("channel/22/sharp_ir/dist", 1, sharp_dist_callback); ros::spin(); //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
The valid Analog channels are 22-25
. These are different from the WPI Analog Input Channels.
Important
Subscribe to Sharp topics to access the data being published and write callbacks to pass messages between various processes.
Note
Calling the frcKillRobot.sh
script is necessary since the VMXPi HAL uses the pigpio library, which unfortunately can only be used in one process. Thus, everything that interfaces with the VMXPi must be run on the same executable. For more information on programming with ROS, refer to: ROS Tutorials.
Limit Switches¶
Reading a Digital Input¶
1 2 3 4 5 6 7 8 9 10 11 | //import the DigitalInput Library import com.wpi.first.wpilibj.DigitalInput; //Create the DigitalInput Object private DigitalInput input; //Constuct a new instance input = new DigitalInput(port); //Can then use these accssor to get data input.get(); //Will return true for a high signal and false for a low signal |
1 2 3 4 5 6 7 8 | //Include the DigitalInput Library #include "frc/DigitalInput.h" //Constructors frc::DigitalInput input{port}; //Use these to access data input.Get(); //Will return true for a high signal and false for a low signal |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //Include the DigitalInput Library #include "DI_ros.h" int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "digitalin_node"); /** * Constructors * Create the DigitalInput object * DI ros threads (publishers and services) will run asynchronously in the background */ ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system VMXPi vmx(true, (uint8_t)50); //realtime bool and the update rate to use for the VMXPi AHRS/IMU interface, default is 50hz within a valid range of 4-200Hz DigitalInputROS digital_in(&nh, &vmx, channel); digital_in.Get(); //Will return true for a high signal and false for a low signal ros::spin(); //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
Important
Subscribe to DI topics to access the data being published and write callbacks to pass messages between various processes.
Encoders¶
Encoders are a sensor placed normally on a shaft to provide feedback to controller. This feedback allows for the detection of position, speed and direction of motion control system. There are two types of encoders; absolute and incremental. Absolute encoders report back a location specfic position. Incremental encoders only indicate that there has been a change in postion and what that change was. In robotics we tend to mostly use incremental encoders as they are easier to use and have some more benefical advandages than that of the absolute encoder.
The encoders in the worldskills collection are built into the motors already. This makes it easier for designing drive systems as an external encoder does not need to be designed in.
Distance Formula¶
Math¶
There is a lot of math assosiated with encoders. Before the encoder class can be used the distance per tick has to be calculated. The formula can be given as:
Where:
r
= wheel radiusticksPerRev
= encoder pulses on the output shaft of the motorgearRatio
= an external gear ratio used.
Example¶
Lets look at an example using the Maverick with the 100mm omni wheel attached directly on the shaft of the motor.
r
= 51 mm (actual measured value)ticksPerRev
= 1464 (encoder counts per 1 revolution of the motor output shaft)gearRatio
= 1:1
Therefor we can conculde that the distancePerTick for the Maverick using the 100mm omni wheels is 0.218881455.
Application¶
Now that we have the distancePerTick we can calculate the distance traveled. This is simply formulated by:
Where:
distancePerTick
= 0.218881455encoderCount
= is the incremental count from the encoder
Lets look at a few examples:
One Wheel Rotation
encoderCount = 1464
Note
The distance measured is in mm as the radius was specficed in mm.
Ten Wheel Rotations
encoderCount = 14640
Code¶
Now that we know the math behind it, let’s look at how to program the encoder for distance measurement.
Constants.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /** * Motor Constants */ public static final int TITAN_ID = 42; public static final int MOTOR = 2; /** * Encoder Constants */ //Radius of drive wheel in mm public static final int wheelRadius = 51; //Encoder pulses per rotation of motor shaft public static final int pulsePerRotation = 1464; //Gear ratio between motor shaft and output shaft public static final double gearRatio = 1/1; //Pulse per rotation combined with gear ratio public static final double encoderPulseRatio = pulsePerRotation * gearRatio; //Distance per tick public static final double distancePerTick = (Math.PI * 2 * wheelRadius) / encoderPulseRatio; |
Subsystem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import com.studica.frc.TitanQuad; import com.studica.frc.TitanQuadEncoder; public class Subsystem { /** * Motors */ private TitanQuad motor; /** * Sensors */ private TitanQuadEncoder encoder; public Subsystem() { //Motors motor = new TitanQuad(Constants.TITAN_ID, Constants.MOTOR); //Sensors encoder = new TitanQuadEncoder(motor, Constants.MOTOR, Constants.distancePerTick); } /** * Gets the distance traveled of the motor * <p> * @return the distance traveled */ public double getEncoderDistance() { return encoder.getEncoderDistance(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #include <studica/TitanQuad.h> #include <studica/TitanQuadEncoder.h> #include <cmath> class Subsystem : public frc2::SubsystemBase { public: Subsystem(); double GetEncoderDistance (void); private: /** * Motor Constants */ #define TITAN_ID 42 #define MOTOR_N 2 /** * Encoder Constants */ //Radius of drive wheel in mm #define wheelRadius 51 //Encoder pulses per rotation of motor shaft #define pulsePerRotation 1464 //Gear ratio between motor shaft and output shaft #define gearRatio 1/1 //Pulse per rotation combined with gear ratio #define encoderPulseRatio pulsePerRotation * gearRatio //Distance per tick #define distancePerTick (M_PI * 2 * wheelRadius) / encoderPulseRatio /** * Objects */ studica::TitanQuad motor{TITAN_ID, MOTOR_N}; studica::TitanQuadEncoder encoder{motor, MOTOR_N, distancePerTick}; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include "subsystems/Subsystem.h" Subsystem::Subsystem(){}; /** * Gets the distance traveled of the motor * <p> * @return the distance traveled */ double Subsystem::GetEncoderDistance (void) { return encoder.GetEncoderDistance(); } |
Speed¶
Besides distance, the encoder can also provide the speed of the motor. Speed can be represented in two main ways rpm
and m/s
. Both have advantages and disadvantages but are also easy to implement.
Rotations Per Minuite (RPM)¶
The RPM
is the number of revolutions of the motor shaft every minute. For example, the Maverick DC Motor has a nominal RPM of 100. However, all motors will rarely rotate at the same speed. With the encoder, some math and the RPM can be calculated to use in formulas if required.
Important
The RPM does not consider any gear ratios or the size of the output object, i.e., wheel.
Fortunately, the Titan has an internal RPM count, so no external math is required. It is as simple as calling the getRPM() functions.
Constants.java
1 2 3 4 5 | /** * Motor Constants */ public static final int TITAN_ID = 42; public static final int MOTOR = 2; |
Subsystem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import com.studica.frc.TitanQuad; public class Subsystem { /** * Motors */ private TitanQuad motor; public Subsystem() { //Motors motor = new TitanQuad(Constants.TITAN_ID, Constants.MOTOR); } /** * Gets the RPM of the motor * <p> * @return the RPM of the motor */ public double getRPM() { return motor.getRPM(Constants.MOTOR); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <studica/TitanQuad.h> class Subsystem : public frc2::SubsystemBase { public: Subsystem(); double GetRPM (void); private: /** * Motor Constants */ #define TITAN_ID 42 #define MOTOR_N 2 /** * Objects */ studica::TitanQuad motor{TITAN_ID, MOTOR_N}; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include "subsystems/Subsystem.h" Subsystem::Subsystem(){}; /** * Gets the RPM of the motor * <p> * @return the RPM of the motor */ double Subsystem::GetRPM (void) { return encoder.GetRPM(MOTOR_N); } |
Tip Speed or Velocity¶
RPM is excellent to have, but it does not give the actual speed of the object, such as a wheel. RPM only gives the speed of the motor shaft. In comes a simple formula to convert RPM to Tip Speed
or Velocity
.
Math¶
Where
D
= Diameter of wheel in metersπ
= piS
= rpm60
= conversion from minutes to seconds
Example¶
Diameter
of the wheel is0.102m
.π
is π.S
is the nominal speed of the Maverick at100rpm
.
Application¶

When looking at the diagram above, the speed is only 0.0314m/s if using just RPM. When calculating for Y
the proper speed is given at 0.53407m/s. There is a clear difference between the two speeds. This can conclude that while the RPM is excellent, it is better to incorporate the adjusted Tip Speed or Velocity in equations to give more accuracy.
Attention
The SR-Pro Camera was discontinued and replaced with the 3D-Depth Camera. The depth camera acts as a normal camera when plugged in via USB. Depth features and more are coming soon. For those not using the WPILib framework, an SDK is available here for depth features: SDK
SR Pro Camera¶
Installing the Ribbon Cable¶
There are a few steps required to install the ribbon cable to communicate with the SR Pro camera.
Important
Take your time while completing this tutorial as the ribbon cable is fragile.
Setup¶
The first thing is to have the VMX and HDMI to ribbon cable adapter ready, as pictured above.
Removing the Ribbon Cable from the HDMI Adapter¶
The CSI holder needs to be opened up to remove the ribbon cable from the HDMI adapter board. As highlighted above with the red circles, the ribbon cable is held down by a tab that needs to be opened. Gently pull the tab open on either end to open the CSI holder.
The ribbon cable should now be able to gently be removed.
Removing the screws from the VMX¶
Six M2.5 x 8mm Philips head screws hold the VMX together. All six screws need to be removed to service the VMX.
To start, flip the VMX over and remove the four screws located on the bottom of the VMX, as shown above.
Important
Place the screws in a safe spot where they won’t get lost.
The last two screws are located inside the VMX next to the IO headers (highlighted above). To access the inside, remove the lid of the VMX and set it aside.
The VMX can now be removed from the case.
Disassembling the VMX Boards¶
The VMX consists of two boards, the VMX-pi and the Raspberry Pi4 B+. The two boards are separated by a 12mm standoff and a GPIO header, as pictured above. Gently separate the two boards to get access to the Raspberry Pi.
The two boards separated are shown above.
Plugging in the Ribbon Cable¶
The ribbon cable will sit inside the Raspberry Pi camera CSI connector.
The CSI connector on the Raspberry Pi is highlighted above.
Note
Do not use the other CSI connector on the Raspberry Pi as that is for display output.
Open the CSI connector, as shown above. If the CSI connector is closed, the ribbon cable will not be able to be seated.
When open, insert the ribbon cable.
Warning
Pay attention to the orientation of the pins on the ribbon cable. If installed incorrectly, it will short the camera or the pi itself. In this case, the pins on the ribbon cable should be facing the micro HDMI ports.
The CSI connector tab can now be pushed down to lock the ribbon cable in place. Give the ribbon cable a gentle tug to make sure it is secure.
Reassembling the VMX¶
Notice the slot pictured above on the VMX-pi board. This is where the ribbon cable will slot through when the board is placed back onto the Raspberry Pi.
When the boards are placed back together, it should look like the above two pictures.
Place the VMX back into the bottom case and install the two screws highlighted above. Once the two screws are in place, turn the VMX on to its side and install the bottom 4 screws.
The lid can now be installed on the VMX. Note the highlighted area is where the ribbon cable will slot through.
To install the lid, turn it on its side, as shown above.
Turn the lid down to close. Note that the ribbon cable should slide through the slot on the lid before closing.
The VMX is now be assembled with the ribbon cable installed.
Adding the HDMI Adatper¶
Note
Pay attention location of the pins on the ribbon cable.
The HDMI Adapter Board is shown above in the correct orientation for the ribbon cable. The CSI connector tab should be open so that the ribbon cable can be inserted.
Once the ribbon cable is inserted, the tab can be closed. The installation of the ribbon cable into the VMX is now complete.
Reading a Barcode¶
This guide will instruct on reading a barcode or QR Code from the VMX and relaying the data back to the robot code. Follow the pages below in order, to have everything work as intended.
VMX¶
The Python Scripts used here can be downloaded from here.
Setup Dependencies on the VMX¶
Before anything can be done, some packages and dependencies must be installed.
Switching to WiFi Client Mode¶
To install packages, the VMX must be connected to the internet. The easiest way to do this is to put the VMX into client mode. Open Terminal and run the command below to enter client mode.
setupWifiClient.sh
This will change the VMX from an access point to a client. In client mode, the VMX can then be connected to your local WiFi.
Note
The robot manager and connection to the control station does not work in this mode.
Packages to Install¶
Three packages need to be installed.
Pyzbar¶
This package is used to read the barcode data. In the terminal, run the following commands:
sudo apt-get install libzbar0
sudo pip install pyzbar
sudo pip install pyzbar[scripts]
pynetworktables¶
This package is what is used to communicate with the robot and shuffleboard.
sudo pip install pynetworktables
Watchdog¶
This package acts as a watchdog that is used to check filesystem changes. The main reason for this package is that currently, Pyzbar and pynetworktables do not interact appropriately.
sudo pip install watchdog
Going back to WiFi AP Mode¶
After these packages are installed, it is good to go back to WiFi AP mode to prevent issues down the line.
setupWifiAP.sh
Barcode Python Script¶
The script for reading a barcode is quite simple and easy to use. There are five sections to the script, and each is explained below.
Imports¶
2 3 | import cv2 as cv from pyzbar.pyzbar import decode |
Lines 2
and 3
are the imports required for this script. Line 2
imports open cv, which is used for taking the picture with the camera. Line 3
imports pyzbar, which is used for decoding the snapshot taken by the camera.
Variable Initialization¶
The BarcodeData
and BarcodeType
variables need to be initialized to prevent an error if no barcode is found.
6 7 | BarcodeData = 'No Barcode Found' BarcodeType = 'null' |
Snapping an Image¶
This section will snap a single image from the camera.
10 11 12 13 14 15 | cap = cv.VideoCapture(0) cap.set(3, 640) # Width cap.set(4, 480) # Height ret, raw = cap.read() raw = cv.flip(raw, -1) cap.release() |
Line
10
creates the camera and is using port 0Line
11
sets the WidthLine
12
sets the HeightLine
13
snaps the imageLine
14
rotates the image 180 as the way the camera is mounted it is upside downline
15
closes the camera resources so that the script is not leaving the camera hanging.
Barcode Decoding¶
In this section, the image will be decoded to reveal barcode data.
18 19 20 21 | for barcode in decode(raw): BarcodeData = barcode.data.decode("utf-8") BarocdeType = barcode.type #print("Found {} barcode: {}".format(BarcodeType, BarcodeData)) #For debugging |
Line
18
is the for loop that will run through all the found barcodes.Line
19
assigns the barcode data to BarcodeDataLine
20
assigns the barcode type to BarcodeTypeLine
21
is used as a debug print statement that will print the results to the terminal.
Note
If there are multiple barcodes, it will only take the last one found, as each loop overrides the previous results.
Write to File¶
A simple text file is used to pass the barcode data from this script to the watchdog script.
23 24 25 26 27 | file = open('/home/pi/barcodes.txt', 'w') file.write(BarcodeData) file.write('/n') file.write(BarcodeType) file.close() |
Line
23
opens the file in write modeLine
24
writes the barcode data to the first line of the fileLine
25
moves the file to the next lineLine
26
writes the barcode type to the second line of the fileLine
27
closes the file.
Full Script¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #imports import cv2 as cv from pyzbar.pyzbar import decode #initialize variables to prevent errors if no barcode found BarcodeData = 'No Barcode Found' BarcodeType = 'null' # Snap an image cap = cv.VideoCapture(0) cap.set(3, 640) # Width cap.set(4, 480) # Height ret, raw = cap.read() raw = cv.flip(raw, -1) cap.release() # process image and output barcode data to file for barcode in decode(raw): BarcodeData = barcode.data.decode("utf-8") BarcodeType = barcode.type #print("Found {} barcode: {}".format(barcode.type, barcode.data.decode("utf-8"))) # For debugging file = open('/home/pi/barcodes.txt', 'w') file.write(BarcodeData) file.write('\n') file.write(BarcodeType) file.close() |
Watchdog Listener Script¶
The watchdog listener monitors changes in the barcodes.txt file and sends those changes back to the robot and shuffleboard. The watchdog listener is also used to read the networktables and see if a new barcode must be read.
Imports¶
4 5 6 7 8 9 | from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler from networktables import NetworkTables from networktables.util import ntproperty import threading import os |
There are a few more imports for this script over the barcode script.
Line
4
is the observer import and is used to create the listener for a file.Line
5
is the FileSystemEventHandler which creates functions that check for changes to filesLine
6
is the main NetworkTables import, used to send and receive info from the robot.Line
7
is the ntproperty import and is used to create tables and properties.Line
8
is the thread import to create threads.Line
9
is the os import and used for sending commands to the terminal.
Create Barcodes File¶
The watchdog script will be run as a startup script, which makes it a good idea to create the barcodes.txt file if it does not exist to avoid errors when reading the file.
12 13 | f = open('/home/pi/barcodes.txt', 'w') f.close() |
Line
12
will create the barcodes.txt if it does not existLine
13
closes the file to prevent issues of the file being open when it should be closed
Connect to NetworkTables¶
Before anything can happen, a connection to NetworkTables needs to be established. NetworkTables does not run right away and needs some time for the server and client to start. The below code handles this.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #Create thread to make sure networktables is connected cond = threading.Condition() notified = [False] #Create a listener def connectionListener(connected, info): with cond: notified[0] = True cond.notify() #Instantiate NetworkTables NetworkTables.initialize(server="10.12.34.2") NetworkTables.addConnectionListener(connectionListener, immediateNotify=True) #Wait until connected with cond: if not notified[0]: cond.wait() |
The above may look complicated, but it is pretty simple.
Line
16
creates a conditional threadLine
17
creates a boolean arrayLine
20
defines a new function call connectionListener; this function listens for a connection and changes the condition when connected.Lines
21 - 23
is a statement that when connected to update conditionsLine
26
will initialize a connectionLine
27
adds a listener to check if connectedLines
30 - 32
will hang until a connection is made
Important
Make sure the IP address used in line 26 matches the IP address of the VMX WiFi AP.
Create the Vision Tables¶
To successfully send data between the watchdog and the robot code, it is good to create a couple of properties to hold this data. The properties can be read on the watchdog side and the robot side.
35 36 37 38 39 40 | ntBarcodeData = ntproperty('/Vision/barcodeData', "null") ntBarcodeType = ntproperty('/Vision/barcodeType', "null") ntReadBarcode = ntproperty('/Vision/readBarcode', False) #Get Table table = NetworkTables.getTable('Vision') |
Lines
35 & 36
create the barcode properties.Line
37
creates the command property used for executing the barcode script for a new barcode.Line
40
assigns theVision
table to a variable for later use.
The first value of property is the key and the second is the default value. The default value also creates the property type. In the above cases ntBarcodeData
& ntBarcodeType
will be strings, whereas ntReadBarcode
is a boolean.
Note
When creating a table, the key of the table must always start with a /
.
File System Handler¶
To optimize the code that it does not open, read, close the code continuously. A FileSystemEventHandler can be used. In this case, the use of watchdog is perfect. This way, nothing will happen unless the file is modified. If there is no modification to the file, i.e., a new barcode is read, it will do nothing.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 | class MyHandler(FileSystemEventHandler): def on_modified(self, event): try: file = open('./home/pi/barcodes.txt', 'r') table.putString('barcodeData', file.readline()) table.putString('barcodeType', file.readline()) file.close() except: pass #when file is not created yet event_handler = MyHandler() observer = Observer() observer.schedule(event_handler, path='./home/pi/barcodes.txt', recursive=False) observer.start() |
Line
43
creates a class that uses the FileSystemEventHandlerLine
44
creates the function on_modified which is an extension of the FileSystemEventHandler. This function will be called when the event handler detects a modification to the file.Line
45 & 50
is used to catch errors.Line
46
opens the barcodes.txt in read mode.Line
47
will read the first line of the file and add it as the barcodeData data.Line
48
will read the second line of the file and add it as the barcodeType data.Line
49
closes the file to ensure no issues.Line
53
creates the event handlerLine
54
creates the observerLine
55
configures the observer with the event handler and file to watchLine
56
starts the observer thread
Forever Loop¶
This script needs to run forever and handle a flag sent from the robot to take a new barcode reading.
59 60 61 62 63 64 65 66 | while(True): if table.getBoolean('readBarcode', False) == True: table.putBoolean('readBarcode', False) os.system('python3 /home/pi/readBarcode.py') try: pass except KeyboardInterrupt: observer.stop() |
Line
59
is the while loop that never endsLine
60
checks to see if the robot code is requesting a new barcode scan.Line
61
flips the readBarcode flag to False to prevent a double read.Line
62
runs thereadBarcode.py
scriptLines
63 - 66
are used if the script ever wants to end. In this case, a KeyboardInterrupt is required to end. As this script will run as a startup script, the observer should never end.
Setting the Script to run as at Startup¶
Setting the watchdog script to run at startup is very simple.
In terminal open rc.local
sudo nano /etc/rc.local
Scroll to the bottom with the arrow keys.
Above exit 0
put:
python3 /home/pi/watchdogListener.py &
To save, hit CTRL + X
then Y
and hit Enter
.
Important
Including the &
at the end will fork the process to the background and prevent other processes from hanging.
It is now possible to reboot, and the script will be running.
Note
There will be no indication that the script is running.
Full Script¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #!/usr/bin/python3 #imports from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler from networktables import NetworkTables from networktables.util import ntproperty import threading import os #Create the barcodes file f = open('/home/pi/barcodes.txt', 'w') f.close() #Create thread to make sure networktables is connected cond = threading.Condition() notified = [False] #Create a listener def connectionListener(connected, info): with cond: notified[0] = True cond.notify() #Instantiate NetworkTables NetworkTables.initialize(server="10.12.34.2") NetworkTables.addConnectionListener(connectionListener, immediateNotify=True) #Wait until connected with cond: if not notified[0]: cond.wait() #Create the vision Table ntBarcodeData = ntproperty('/Vision/barcodeData', "null") ntBarcodeType = ntproperty('/Vision/barcodeType', "null") ntReadBarcode = ntproperty('/Vision/readBarcode', False) #Get Table table = NetworkTables.getTable('Vision') #Create the system handler class MyHandler(FileSystemEventHandler): def on_modified(self, event): try: file = open('./home/pi/barcodes.txt', 'r') table.putString('barcodeData', file.readline()) table.putString('barcodeType', file.readline()) file.close() except: pass #when file is not created yet event_handler = MyHandler() observer = Observer() observer.schedule(event_handler, path='./home/pi/barcodes.txt', recursive=False) observer.start() #The forever loop while(True): if table.getBoolean('readBarcode', False) == True: table.putBoolean('readBarcode', False) os.system('python3 /home/pi/readBarcode.py') try: pass except KeyboardInterrupt: observer.stop() |
VS Code¶
The VS Code Project used here can be downloaded from here.
Vision Subsystem¶
The Vision Subsystem will house the core of the code. It is always a good idea to have a Vision subsystem that will hold all the getters and setters for the vision on the robot.
VisionSubsystem.java¶
Imports¶
1 2 3 4 5 6 7 | package frc.robot.subsystems; import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.networktables.NetworkTableInstance; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; |
Line
1
is the package setup, and will assign this class to the package.Line
3
is the import for networktables; this allows us to talk with the vision Scripts.Line
4
is the import for network table entries and allows us to read the data from the table.Line
5
is the import for the networktables instance and is used to get the table.Line
6
is the import for SmartDashboard, which will be used to put values for user display.Line
7
is the import for SubsystemBase, which is required to have this class become a subsystem.
Class¶
9 | public class VisionSubsystem extends SubsystemBase |
Line
9
creates the classVisionSubsystem
and extends SubsystemBase to have this class be a subsystem.
Objects¶
11 12 13 | private NetworkTableInstance inst = NetworkTableInstance.getDefault(); private NetworkTable table = inst.getTable("Vision"); private NetworkTableEntry data; |
Line
11
creates the NetworkTableInstanceLine
12
creates the tableLine
13
creates the table entry reference
Constructor¶
15 16 17 18 | public VisionSubsystem() { SmartDashboard.putBoolean("Get New Barcode", false); } |
Line
15
is the constructor and will create the VisionSubsystem when called.Line
17
Will create an entry in the smartdashboard calledGet New Barcode
and sets its default value to false.
Setter¶
20 21 22 23 | public void readBarcode() { table.getEntry("readBarcode").setBoolean(true); } |
Line
20
is the setter that is called when a new barcode should be read.Line
22
will update the readBarcode flag in networktables to true. This, in turn, will tell the vision scripts to read a new barcode and update the data keys.
Getter¶
25 26 27 28 29 | public void printBarcode() { data = table.getEntry("barcodeData"); SmartDashboard.putString("Barcode Data", data.getString("Nothing was read")); } |
Line
25
is the method that will be called to get the current value of the barcodeData entry.Line
27
assigns the entry to the data object.Line
28
places the string value of data to the dashboard.
Periodic Loop¶
The periodic loop is used to check the current value of the barcodeData entry for every robot loop.
31 32 33 34 35 | @Override public void periodic() { printBarcode(); } |
Line
31
is the required Override to tell the compiler to use this periodic method and not the one built into SubsystemBase.Line
32
is the periodic method.Line
34
will call theprintBarcode
method every robot loop.
Full Subsystem Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package frc.robot.subsystems; import edu.wpi.first.networktables.NetworkTable; import edu.wpi.first.networktables.NetworkTableEntry; import edu.wpi.first.networktables.NetworkTableInstance; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class VisionSubsystem extends SubsystemBase { private NetworkTableInstance inst = NetworkTableInstance.getDefault(); private NetworkTable table = inst.getTable("Vision"); private NetworkTableEntry data; public VisionSubsystem() { SmartDashboard.putBoolean("Get New Barcode", false); } public void readBarcode() { table.getEntry("readBarcode").setBoolean(true); } public void printBarcode() { data = table.getEntry("barcodeData"); SmartDashboard.putString("Barcode Data", data.getString("Nothing was read")); } @Override public void periodic() { printBarcode(); } } |
Robot Container Part 1¶
The Robot Container is used to create an instance of subsystems that can be shared across commands. This saves resources and speeds up the processes. In part 1, only the subsystem will be declared and instantiated.
Imports¶
8 9 10 | package frc.robot; import frc.robot.subsystems.VisionSubsystem; |
Line
8
adds the robot container to the robot package.Line
10
imports the VisionSubsystem.
Instantiation¶
28 | vision = new VisionSubsystem(); |
Line
28
creates the instance of VisionSubsystem and assigns it to vision.
Full Code Part 1¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /*----------------------------------------------------------------------------*/ /* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ package frc.robot; import frc.robot.subsystems.VisionSubsystem; /** * This class is where the bulk of the robot should be declared. Since Command-based is a * "declarative" paradigm, very little robot logic should actually be handled in the {@link Robot} * periodic methods (other than the scheduler calls). Instead, the structure of the robot * (including subsystems, commands, and button mappings) should be declared here. */ public class RobotContainer { // The robot's subsystems and commands are defined here... public static VisionSubsystem vision; /** * The container for the robot. Contains subsystems, OI devices, and commands. */ public RobotContainer() { vision = new VisionSubsystem(); } } |
Vision Command¶
The Vision Command is used to tell the VisionSubsystem code what to do and when to do it.
VisionCommand.java¶
Imports¶
1 2 3 4 5 6 | package frc.robot.commands; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.CommandBase; import frc.robot.RobotContainer; import frc.robot.subsystems.VisionSubsystem; |
Line
1
adds VisionCommand to the correct package.Line
3
imports the SmartDashboard, used to display data and get a button input.Line
4
imports the CommandBase, used to make this class part of the command framework.Line
5
imports the RobotContainer so that instances of subsystems can be shared.Line
6
imports the VisionSubsystem which is needed so the command can operate.
Class¶
8 | public class VisionCommand extends CommandBase |
Line
8
creates the class for VisionCommand and extends the CommandBase framework with it.
Objects, Instances, and Variables¶
10 11 12 | private static final VisionSubsystem vision = RobotContainer.vision; boolean getNewBarcode; |
Line
10
creates the VisionSubsystem object and assigns the instance of VisionSubsystem from the one created in RobotContainer.Line
12
is a simple boolean flag used to see if the user wants to get a new barcode reading.
Constructor¶
Note
After adding this step, there will be an error shown. Ignore this error as it will be fixed in RobotContainer part 2.
14 15 16 17 | public VisionCommand () { addRequirements(vision); } |
Line
14
is the constructor for the VisionCommand class.Line
16
tells the CommandBase that VisionCommand requires a VisionSubsystem instance to run.
Initialize¶
If there is any code that needs to be initialized, it will go in here. But as that is not required, this is an empty method.
19 20 | @Override public void initialize(){} |
Execute¶
The execute method is what is called every time the command is called. Meaning that the code to be run continuously should be in here.
22 23 24 25 26 27 28 29 30 31 32 | @Override public void execute() { getNewBarcode = SmartDashboard.getBoolean("Get New Barcode", false); if (getNewBarcode) { vision.readBarcode(); SmartDashboard.putBoolean("Get New Barcode", false); } } |
Line
23
is the execute method.Line
25
assigns the boolean value taken fromGet New Barcode
on the dashboard to getNewBarcode. The second parameter in the getBoolean call is the default value if nothing can be found.Line
27
is a conditional statement that checks if getNewBarcode is true. (Button was pushed)Line
29
will call the readBarcode method from VisionSubsystem, which in turn will call the scripts on the VMX.Line
30
sets the getNewBarcode button on the dashboard tofalse
to prevent a continuous call to the scripts when only one call is required.
End¶
The end method is called when the command is interrupted or is finished as there are no motors or anything safety-related called by this command. The end method can remain blank.
34 35 | @Override public void end(boolean interrupted){} |
isFinished¶
Is finished is a method that is used to create an end condition for the command. As this command should run all the time and never end, a false statement will be returned.
37 38 39 40 41 | @Override public boolean isFinished() { return false; } |
Full VisionCommand Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | package frc.robot.commands; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import edu.wpi.first.wpilibj2.command.CommandBase; import frc.robot.RobotContainer; import frc.robot.subsystems.VisionSubsystem; public class VisionCommand extends CommandBase { private static final VisionSubsystem vision = RobotContainer.vision; boolean getNewBarcode; public VisionCommand () { addRequirements(vision); } @Override public void initialize(){} @Override public void execute() { getNewBarcode = SmartDashboard.getBoolean("Get New Barcode", false); if (getNewBarcode) { vision.readBarcode(); SmartDashboard.putBoolean("Get New Barcode", false); } } @Override public void end(boolean interrupted){} @Override public boolean isFinished() { return false; } } |
Robot Container Part 2¶
In part 2, the error is going to be fixed. The error occurs as the VisionSubsystem and VisionCommand are linked in the command. However, they are not linked on the subsystem level.
Imports¶
In the imports section, one more import will be added.
8 9 10 11 | package frc.robot; import frc.robot.subsystems.VisionSubsystem; import frc.robot.commands.VisionCommand; |
Line
11
is the addition as VisionCommand needs to be imported.
Constructor¶
The last change is in the constructor, where the default command for the VisionSubsystem will be set.
27 28 29 30 31 32 | public RobotContainer() { vision = new VisionSubsystem(); vision.setDefaultCommand(new VisionCommand()); } |
Line
31
assigns a default command for the VisionSubsystem and that default command is VisionCommand
Full Code Part 2¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /*----------------------------------------------------------------------------*/ /* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ package frc.robot; import frc.robot.subsystems.VisionSubsystem; import frc.robot.commands.VisionCommand; /** * This class is where the bulk of the robot should be declared. Since Command-based is a * "declarative" paradigm, very little robot logic should actually be handled in the {@link Robot} * periodic methods (other than the scheduler calls). Instead, the structure of the robot * (including subsystems, commands, and button mappings) should be declared here. */ public class RobotContainer { // The robot's subsystems and commands are defined here... public static VisionSubsystem vision; /** * The container for the robot. Contains subsystems, OI devices, and commands. */ public RobotContainer() { vision = new VisionSubsystem(); vision.setDefaultCommand(new VisionCommand()); } } |
Robot¶
In Robot.java
there will be errors as the autonomous code was removed from the RobotContainer.
It is as simple as just removing all the lines with red underlines. The full robot code with errors removed is shown below.
Full Robot Code¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | /*----------------------------------------------------------------------------*/ /* Copyright (c) 2017-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ package frc.robot; import edu.wpi.first.wpilibj.TimedRobot; import edu.wpi.first.wpilibj2.command.CommandScheduler; /** * The VM is configured to automatically run this class, and to call the functions corresponding to * each mode, as described in the TimedRobot documentation. If you change the name of this class or * the package after creating this project, you must also update the build.gradle file in the * project. */ public class Robot extends TimedRobot { private RobotContainer m_robotContainer; /** * This function is run when the robot is first started up and should be used for any * initialization code. */ @Override public void robotInit() { // Instantiate our RobotContainer. This will perform all our button bindings, and put our // autonomous chooser on the dashboard. m_robotContainer = new RobotContainer(); } /** * This function is called every robot packet, no matter the mode. Use this for items like * diagnostics that you want ran during disabled, autonomous, teleoperated and test. * * <p>This runs after the mode specific periodic functions, but before * LiveWindow and SmartDashboard integrated updating. */ @Override public void robotPeriodic() { // Runs the Scheduler. This is responsible for polling buttons, adding newly-scheduled // commands, running already-scheduled commands, removing finished or interrupted commands, // and running subsystem periodic() methods. This must be called from the robot's periodic // block in order for anything in the Command-based framework to work. CommandScheduler.getInstance().run(); } /** * This function is called once each time the robot enters Disabled mode. */ @Override public void disabledInit() { } @Override public void disabledPeriodic() { } /** * This autonomous runs the autonomous command selected by your {@link RobotContainer} class. */ @Override public void autonomousInit() { } /** * This function is called periodically during autonomous. */ @Override public void autonomousPeriodic() { } @Override public void teleopInit() { } /** * This function is called periodically during operator control. */ @Override public void teleopPeriodic() { } @Override public void testInit() { // Cancels all running commands at the start of test mode. CommandScheduler.getInstance().cancelAll(); } /** * This function is called periodically during test mode. */ @Override public void testPeriodic() { } } |
Deploying To The Robot¶
A few steps will be followed to deploy the code to the VMX / Robot.
Ensure Target is set to VMX¶
Important
The VMX must be connected to the internet for this step to work!
Using the WSR VMX
extension, the command VMX WSR: Set the deploy target to VMX (from RoboRIO)
will be used.
This will ensure that the project is configured for the VMX and run a build to download and cache the correct files for the VMX.
Connect to the VMX WiFi AP¶
Once the development computer is connected to the VMX via the VMX WiFi AP, the code can be deployed to the VMX. To deploy the code us the extension WPILib
and use the command WPILib: Deploy Robot Code
. The command will deploy the compiled code onto the VMX for the robot manager to run.
Team Number is not 1234¶
If the VMX team number is not 1234, the team number will have to be set correctly. To set the team number correctly, use the WPILib
extension and the command WPILib: Set Team Number
. The command palette will ask for a team number to be entered, and then save the team number hit Enter
. It is a good idea to rebuild the project code if the team number is changed. To rebuild the project code us the extension WPILib
and the command WPILib: Build Robot Code
Testing it out¶
Time to test it out.
Reading a Barcode¶
Once code is deployed to the robot and the vision scripts are set up correctly, it is possible to read a barcode or QR Code. Using the Control Station Console
the code can be enabled for the system to work.
Shuffleboard Setup¶
When connected to the VMX via WiFi AP and the Control Station Console
is launched, and the correct IP address is used. Shuffleboard will launch and connect to the network tables stream of data.
On launch, it should look similar to this.

There will be two widgets in the middle. A widget called Get New Barcode
and a widget called Barcode Data
. The Get New Barcode
widget will have a red value. The widget is currently an indicator showing a false value. Barcode Data
is also an indicator widget; however, it displays a string value of null.
On the left of the window, the Vision tables and properties created by the watchdogListener.py
can be seen.
Changing Get New Barcode to a Button¶
Having Get New Barcode
as an indicator won’t work here. To fix this in Shuffleboard, the widget can be configured as a toggle button.
Right, click on the red part of the widget, select Show as...
and chose Toggle Button
.

This changes the Get New Barcode
to a toggle button that can be pressed to get a new barcode.

Enable the Robot¶
The Get New Barcode
button will do nothing until the robot is enabled. Using Control Station Console
ensure that it is in Teleoperated mode (press o
on the keyboard). When in the correct mode and connected to the robot, hit e
on the keyboard to enable the robot.

Once enabled, the Shuffleboard will be able to be used.
Reading Barcodes¶
For this demo, there are two types of barcodes being used, a CODE 128 barcode with the text Another Barcode
, and a QR Code with the text QR Code Text
.
Test 1¶
While connected to the VMX and the robot enabled. Hitting the Get New Barcode
button returns the result.

The result is QR Code Text
, and if looking at the left panel, the type is also shown to be QRCODE
. This is the robot successfully reading a QR Code.
Test 2¶
The CODE 128 barcode was placed in front of the QR Code, and Get New Barcode
was pressed again.

The result changed to show the data as Another Barcode
and on the left panel, the type is CODE128
.
This demonstrates an easy way to read a barcode or QR code. This also demonstrates the framework for creating vision applications with the VMX. The VMX runs the OpenCV, TensorFlow, or custom scripts and relays the info back to the robot code via network tables.
Servo Motors¶
The collection now has a Multi-Mode Smart Servo included. The new servo will replace the old servos and provide more functionality than before. The multi-mode servo allows for continuous and standard operation of the servo motor. In continuous mode, the servo will spin proportionally based on input in the CW or CCW direction. The max speed the servo will spin is 50rpm. In standard mode, the servo will act as a regular servo and have a range of motion of 300°. That is 150° CW and 150° CCW.

Servo Specs¶
Function |
Range |
---|---|
Size |
40mm x 20.1mm x 38.3mm x 54mm |
Weight |
64g |
Gear Type |
Steel |
Bearing |
Dual Ball Bearings |
Spline |
25T |
Case |
Nylon & Fiberglass |
Connector Wire |
750mm ± 5mm (White, Red, Black) |
Motor |
Metal Brush Motor |
Water Resistance |
No |
Function |
4.8V |
6.0V |
---|---|---|
Idle Current |
5mA |
7mA |
No Load Speed |
0.25sec/60° |
0.2sec/60° |
Running Current |
130mA |
150mA |
Stall Torque |
180.85oz-in |
300oz-in |
Stall Current |
1500mA |
1800mA |
Function |
Spec |
---|---|
Command Signal |
Pulse Width Modulation |
Amplifier Type |
Digital Comparator |
Pulse Width Range |
500μS ~ 2500μS |
Neutral Position |
1500μS |
Range of Motion |
300° ± 5° |
Dead band width |
4μS |
Rotating Direction |
CW |
Function |
Range |
---|---|
Storage Temperature |
-30°C ~ 80°C |
Operating Temperature |
-15°C ~ 70°C |
Function |
Range |
---|---|
Temperature |
25°C ± 5°C |
Humidity |
65% ± 10% |
Programming¶
Standard Servo¶
1 2 3 4 5 6 7 8 9 10 11 | //import the Servo Library import com.studica.frc.Servo; //Create the Servo Object private Servo servo; //Constuct a new instance servo = new Servo(port); //Can then use this mutator to set the servo angle servo.setAngle(degrees); //Range 0° - 300° |
The mutator method will allow you to set the angle of the servo
1 2 3 4 5 6 7 8 | //Include the Servo Library #include "studica/Servo.h" //Constructor studica::Servo servo{port}; //Use this function to set the servo angle servo.SetAngle(degrees); //Range 0° - 300° |
The function will allow you to set the angle of the servo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | //Include the Servo Library #include "Servo_ros.h" double servo_angle; // Returns the angle value set by the Servo motor void servo_angle_callback(const std_msgs::Float32::ConstPtr& msg) { servo_angle = msg->data; } int main(int argc, char **argv) { system("/usr/local/frc/bin/frcKillRobot.sh"); //Terminal call to kill the robot manager used for WPILib before running the executable. ros::init(argc, argv, "servo_node"); /** * Constructor * Servo's ros threads (publishers and services) will run asynchronously in the background */ ros::NodeHandle nh; //internal reference to the ROS node that the program will use to interact with the ROS system VMXPi vmx(true, (uint8_t)50); //realtime bool and the update rate to use for the VMXPi AHRS/IMU interface, default is 50hz within a valid range of 4-200Hz ros::ServiceClient setAngle; ros::Subscriber servo_angle_sub; ServoROS servo(&nh, &vmx, channel); // Use these to directly access data servo.GetAngle(); //returns a double; servo.GetMinAngle(); //returns a double servo.GetMaxAngle(); //returns a double // Using the set_angle service, channel index is declared in the constructor setAngle = nh.serviceClient<vmxpi_ros::Float>("channel/channel_index/servo/set_angle"); // Declaring message type vmxpi_ros::Float msg; // Setting the servo angle float angle = 45.0; //Range -150° - 150° msg.request.data = angle; setAngle.call(msg); // Subscribing to Servo angle topic to access the angle data servo_angle_sub = nh.subscribe("channel/channel_index/servo/angle", 1, servo_angle_callback); //channel_index is the input channel set in the constructor ros::spin(); //ros::spin() will enter a loop, pumping callbacks to obtain the latest sensor data return 0; } |
Important
Subscribe to Servo topics to access the data being published and write callbacks to pass messages between various processes.
Note
Calling the frcKillRobot.sh
script is necessary since the VMXPi HAL uses the pigpio library, which unfortunately can only be used in one process. Thus, everything that interfaces with the VMXPi must be run on the same executable. For more information on programming with ROS, refer to: ROS Tutorials.
Continuous Servo¶
1 2 3 4 5 6 7 8 9 10 11 | //import the Servo Continuous Library import com.studica.frc.ServoContinous; //Create the Servo Continuous Object private ServoContinous servo; //Constuct a new instance servo = new ServoContinuous(port); //Can then use this mutator to set the servo speed servo.set(speed); //Range -1 - 1 (0 Stop) |
The mutator method will allow you to set the speed of the servo
1 2 3 4 5 6 7 8 | //Include the Servo Library #include "studica/ServoContinuous.h" //Constructor studica::ServoContinuous servo{port}; //Use this function to set the servo angle servo.Set(speed); //Range -1 - 1 (0 Stop) |
The function will allow you to set the speed of the servo
Maverick DC Motor¶
The Maverick DC Motor is an upgraded 12VDC motor that allows for more torque than the previous motors used in the worldskills collections.

Motor Specs¶
Function |
Min |
Nom |
Max |
---|---|---|---|
Input Voltage |
— |
12VDC |
— |
Gear Ratio |
— |
1:61 |
— |
No Load RPM |
88 |
100 |
112 |
No Load Current |
— |
600mA |
— |
Rated Speed |
68 |
80 |
92 |
Rated Current |
— |
— |
2.2A |
Rated Torque |
— |
139oz-in |
— |
Stall Current |
— |
— |
11A |
Stall Torque |
708oz-in |
— |
— |
Direction |
— |
CW |
— |
Encoder Voltage |
4 |
— |
5 |
Encoder Current |
— |
6mA |
— |
Encoder CPR |
— |
6 |
— |
Note
With a CPR of 6
and a gear ratio of 1:61
the encoder counts per revolution on the output shaft will be \(\begin{equation}6*61*4 = 1464\end{equation}\)
Servo Power Block¶
The Servo Power Block allows for the proper power to be supplied to the servo motors on a robot.

Servo Smart Programmer¶
The Servo Smart Programmer allows for the configuration and programming of the Studica Multi-Mode Smart Servo.

Using the Smart Servo Programmer¶
Standard Mode¶
Setting the Servo to Standard Mode
Connect the battery and servo to the programmer
Set the selection switch to S on the top left of the programmer
On the battery pack turn on the power
Press the
P
button for 5 seconds (All LEDs will flash when ready to let go)
Testing Standard Mode
Connect the battery and servo to the programmer
Set the selection switch to S on the top left of the programmer
On the battery pack turn on the power
Press the
S
button to set the servo to sweep modeThe Servo will now turn from -150° to 150°
Press the
S
button for a second time to enter manual modePressing the
L
button will move the servo to -150°Pressing the
P
button will move the servo to 0°Pressing the
R
button will move the servo to 150°Pressing the
S
button will turn the programmer off
Important
Remember to turn off the battery pack by sliding the power switch to off
Continuous Mode¶
Setting the Servo to Continuous Mode
Connect the battery and servo to the programmer
Set the selection switch to C on the top left of the programmer
On the battery pack turn on the power
Press the
P
button for 5 seconds (All LEDs will flash when ready to let go)
Testing Continuous Mode
Connect the battery and servo to the programmer
Set the selection switch to C on the top left of the programmer
On the battery pack turn on the power
Press the
S
button to set the servo to sweep modeThe Servo will now constantly turn between 360° CW and 360° CCW
Press the
S
button for a second time to enter manual modePressing the
L
button will move the servo in CW direction at 50rpmPressing the
P
button will stop the servoPressing the
R
button will move the servo in CCW direction at 50rpmPressing the
S
button will turn the programmer off
Important
Remember to turn off the battery pack by sliding the power switch to off
Unit 1: Introduction to Programming¶
What is computer programming? Why do we do it? How does programming apply to robots?
Lesson 1: Introduction¶
Objectives
Introduction¶
This curriculum was created for a better understanding of programming for those that do not get or would like to understand programming. This curriculum will teach basic to advanced principles in Java. Java is a high-level programing language that is perfect for beginners. Java contains many of the basic and advanced principles in all languages, thus making Java the first starting step for many. The general saying is that once you learn one language, the others become easier to adapt. Any programmer will tell you that to be successful, you have to continually learn new languages and adapt to changes in a language you already know. Knowing many languages allows you to use the correct language in the right situation. Some languages operate better in specific conditions and on specific operating systems and machines. What makes Java good is that it is platform-independent. Thus meaning the same code can mostly be run on any device. This will be explained in the subsequent sections.
Note
Some content in this curriculum will be very bland. This information must still be absorbed for better understanding.
What is a Machine?¶
A machine, sometimes known as a computer is a device that has hardware and software. The hardware layer consists of physical nature. This is what can be seen by the human eye and felt. Software is the invisible layer that instructs the hardware on what to do. Knowing how hardware works, is not required; however, it will, in turn, make any code written more efficient and better. In this chapter, we will discuss how the hardware and software layers interact.
Components of a Machine¶

A machine has six core components:
Central Processing Unit
CPU
Memory
RAM
Storage
Data
Input Devices
keyboard
andmouse
Output Devices
speakers
andmonitors
Communication
ethernet
andWiFi
Central Processing Unit¶
The central processing unit CPU
is the brain of the whole operation. The CPU gets instructions from memory and acts on those instructions. When a CPU acts on instruction from memory, it is called executing. There are two primary components of a CPU, the control unit CU
and the arithmetic logic unit ALU
. The control unit will instruct the components on what and when to do something. The arithmetic unit will perform any mathematical or logical operation. Below is a breakdown of the underlying architecture of a CPU.

A CPU is very advanced and has two main aspects of rating, speed, and amount of cores. The speed of a CPU is measured in hertz Hz
. 1 Hz is equal to 1 cycle per second. Some of today’s high-end CPUs can hit 5 GHz, which is 5,000,000,000 cycles per second. The Z3, which was the first programmable digital computer, only had a speed of 4 - 5 Hz. The CPU’s of today have multiple cores to increase the processing power. The picture above shows a single-core CPU as there is only one ALU and CU. Today’s CPUs have 2,4,6,8,16,32+ cores. Shown below is a multicore processor with basic architecture.

Memory¶
Memory is information stored for immediate use by a CPU. Before a program can be executed by the CPU, it must be moved to memory. Memory generally has two components, an address, and data. The data in memory is only one byte long and always has a unique address. Because memory bytes can be accessed in any order, memory is commonly referred to as random-access memory RAM
. Below is a simple diagram showing the two components of RAM and how it interacts with the CPU.

Storage¶
Memory is a volatile type of data storage. This means that when the machine is powered off, all data stored in memory will be erased. To overcome this, machines use storage devices. Storage devices allow for data to be stored permanently. Some common types of storage are hard drives HD
, solid-state drives SSD
, and universal serial bus flash drives USB
. Each type of drive has its own pros and cons. HD’s allow for a large amount of storage; however, they are slow compared to the other types. This is because HD’s are mostly mechanical and have moving parts. An SSD is extremely quick and has no moving parts; however, SSD’s are more expensive and have less storage than HD’s. USBs have a small amount of storage but are cheap and offer portability.
Input Devices¶
Input devices allow the user to communicate with the machine. The two most common input devices are the keyboard and the mouse. The keyboard allows the user to type in data on to the machine. Keyboards bind a keypress to a particular language to understand what is being sent by the user. On most English keyboards, they use ASCII codes to interoperate what was pressed by the user. The mouse is simply a pointer that allows the user to click on-screen objects and perform actions.
Output Devices¶
Output devices allow the machine to communicate with the user. Some conventional output devices are monitors and speakers. Monitors provide a graphical interface for the user. Although its good to note that not all monitors are graphical, some are simple text-based interfaces. Speakers allow the machine to output sound to the user. This is particularly useful if there is an error somewhere or simply acknowledge an action taken by the user.
Communication¶
There are multiple forms and layers of communication completed by a machine. A mouse uses different ways of communication, depending on the mouse. A wired mouse will use a USB port and communicate using serial communication. A wireless mouse will use a technology known as Bluetooth. Bluetooth allows for short-range wireless communication. Some other common forms are ethernet and WiFi. Ethernet and WiFi are mostly the same and follow the same OSI layer protocols. The main difference is that ethernet is a wired communication, whereas WiFi is wireless communication. Ethernet will provide a more stable and, most of the time, a faster connection than WiFi. This is because there can be other radio waves or outside noise impacting the strength of WiFi.
The Programming Language¶
A machine does not understand any human languages. Therefore programs are written in languages the machine can interoperate and use. Before a machine can use instructions outlined in a program, that program is required to be translated into a language, the machine CPU can execute.
There are three main programming language levels:
Machine Language
Assembly Language
High-Level Language
Machine Language¶
Machine language is the most primitive instructions used by a machine. It is sometimes referred to as the native language. Machine language can be complicated to understand as it is only represented in binary code. It is possible to edit and make programs in raw binary code; however, it is not ideal and can lead down a rabbit hole very fast if something is wrong.
Assembly Language¶
Due to the nature of machine language being very hard to write and read in, assembly was created. Assembly uses a short word known as mnemonic to represent each of the machine language instructions. For example add
(addition), sub
(subtraction), and mov
(move). Assembly makes programming the machine easier; however, the machine can not read assembly language. To convert assembly to machine language, a program called an assembler was created. This will take the mnemonic instructions and convert them into the binary code used by the CPU.
High-Level Language¶
Assembly is almost still one for one the same as machine language just wrapped to be more readable. This makes assembly still very difficult to program in and requires excellent knowledge of the CPU architecture. This is why high-level languages were created. High-Level languages are close to English, which allows for better readability and use. As stated in the introduction, there are many languages, and each one serves a specific purpose and is better suited based on the application. Some of the most popular high-level languages in order of popularity on GitHub (The worlds leading software platform) in 2019.
JavaScript - Used in web development
Python - Scripting language useful for short programs
Java - Object-orientated language widely used for platform independent applications
PHP - Scripting language for web development
C# - Object-orientated language developed by Microsoft and used for desktop applications
C++ - Object-orientated language based on C
TypeScript - Superset of JavaScript, allows for static typing
Shell - Scripting language used for running tasks on a command-line interpreter
C - Very close to assembly but has the ease of a high-level language
Ruby - Similar to Python but is mostly used for web applications
The data for the above list can be viewed in the GitHub year of review report found here.
Programs written in high-level languages are called source code. A machine cannot natively run source code. Source code must be translated into binary code for the CPU to execute the source code. A program called a compiler is used to compile the source code into usable binary code for the CPU.
End of Lesson Exercises¶
Answer the following questions on a piece of paper to fully understand the lesson content. Some questions might require you to research further.
What is a CPU and what unit is the speed measured in?
List the six components of a machine and provide an explanation of each.
What unit is memory measured in?
How many cores does the CPU on your computer have?
There are eight bits to a byte, how many bits are in 8 MB (megabytes)?
Why is memory referred to as RAM?
What is the major difference between memory and storage?
What are some other input and output devices that machines can have?
What is the language used by the CPU to execute instructions?
Why was the assembly and the assembler created?
List 10 other high-level languages and give an explanation of each.
What is the compiler for C++ called?
GitHub calls their year end report “The State of the ______”.
Lesson 2: A Simple Java Program¶
The Java Language¶
Java was developed by Sun Microsystems in 1991. The team at Sun Microsystems was led by James Gosling. Gosling, who is often referred to as “Dr. Java,” is a Canadian computer scientist who is best known for being the founder of Java. Java was initially called Oak and only became Java in 1995 with its first public implementation. In 2010 Sun Microsystems was bought by Oracle. The main draw to Java was the “Write once, run anywhere” promise. Java is a fully-featured programming language that is used to develop applications in many environments such as web applications, mobile phones, robotics, desktop software, and servers. Java currently runs on billions of different devices worldwide, and some devices are not even on this planet anymore. Most of us use Java every day without even knowing it. If you have an Android phone, you are using Java. Software for Android devices is developed using Java.
Java can be broken up into 5 major sections:
Java Language Specification
Java API
Java JDK
Java JRE
Java JVM
Java Language Specification¶
The Java Language Specification is a technical definition of the Java programming language. This includes the syntax and semantics. The constantly updated and current specification can be found on Oracles website here.
Java API¶
The Java API (application program interface) is the library that contains all the predefined classes and interfaces required for creating a Java program. The Java API is grows every release of a new Java version. The most to date and current version can be found on Orcales website here.
Java JDK¶
The Java JDK (Java Development Toolkit) contains all the tools needed for Java development. This would include the JRE, an interpreter, a compiler, an archiver, and other tools such as the document generator. The JDK can be downloaded from Oracles website here.
Java JRE¶
The Java JRE (Java Runtime Environment) takes the Java code and starts the JVM. The JRE is essentially the minimal requirements for deploying a Java program.
Java JVM¶
The Java JVM (Java Virtual Machine) reads the Java program bytecode and executes it. For any device to run Java a JVM is required on that device. The main benefit of the JVM is that it allows any Java code to be deployed.
Simple Java Program¶
The code used for any beginner in a new language is “Hello World!”. Lets look below at this simple program.
HelloWorld.java¶
1 2 3 4 5 6 7 8 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |
When executed we will get the output below in the console window.
Hello World!
Lets break down this simple program line by line.
Line 1: is the class identifier. Every Java program requires at least one class to be defined. Conventionally every class must start with an uppercase letter. The class name used in the example is HelloWorld
. Notice how it is one word and not multiple words. If we used Hello World
a syntax error would pop up.
Note
The class name HelloWorld
is the same as the file name HelloWorld.java
. Java is case sensitive and these must be the same to avoid a compilation errors.
Line 2: This is the opening brace {
. Braces group the components of a program. To close the group a closing brace }
like on line 8 is required.
Line 3: is the main definition. The main method is required for a Java program to execute. There may be multiple methods in a class but the main is the entry point during execution.
Line 4: This is the opening brace {
for the main method.
Line 5: This is a comment. Comments are little notes left by a programmer that do not get compiled. Comments will be covered more in depth in a later chapter.
Line 6: Contains the print statement. System.out.println
is a statement in Java. This particular statement will display the contents inside the ()
parentheses. On line 6 inside the () parentheses we have the String “Hello World!”. A String is a term for a sequence of characters. A String is always enclosed in " "
quotations. On this line any line of text placed inside the ” ” will be output to the console.
Important
Notice at the end of line 6 there is a ;
Semicolon. In Java Semicolons are required to end a statement. In the case of line 6 we have the statement System.out.println("Hello World!")
then to end it there is the ;
.
Line 7: Is the closing brace }
for the main method.
Line 8: Is the closing brace }
for the class.
Lets Create some more Simple Programs¶
HelloWorldVersionTwo.java¶
1 2 3 4 5 6 7 8 9 10 | public class HelloWorldVersionTwo { public static void main(String[] args) { //Display messages on the console System.out.println("Hello World!"); System.out.println("We added some more print statements"); System.out.println("Wohoo!"); } } |
Output
Hello World!
We added some more print statements
Wohoo!
LetsDoSomeMath.java¶
1 2 3 4 5 6 7 8 9 10 | public class LetsDoSomeMath { public static void main(String[] args) { //Show how some expressions work System.out.println("Let's do some math!"); System.out.print("10 + 2 - 5 = "); System.out.println(10 + 2 - 5); } } |
Output
Let's do some math!
10 + 2 - 5 = 7
Let’s look at why there is only two lines in the console and what happened.
On line 7 notice how it’s System.out.print
and not System.out.println
. println
will move the cursor to the start of the next line after displaying it’s statement. print
does not move to the next line after displaying the statement.
On line 8 the math 10 + 2 - 5 =
is inside quotations " "
thus identifying it a String and not an expression.
On line 9 there is no quotations " "
so the statement inside the parentheses 10 + 2 - 5
is now considered an expression and will be evaluated. As the expression is in a print statement the result will be outputted to the console.
How a Simple Java Program is Executed¶
Let’s look back at a simple Java program.
HelloWorld.java¶
1 2 3 4 5 6 7 8 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |
Output
Hello World!
What we will look at it in this chapter is how the source code in HelloWorld outputs to the console.
The general process is outlined in the graphic below.

The process starts by taking the HelloWorld.java
source code file and sending it through the Java compiler. After going through the compiler there is a new file create called HelloWorld.class
this is the Java bytecode file that the JVM will be able to interpret. After the .class
file is created it will join the the JRE and call the JVM. The JVM will then output to the console window displaying the text Hello World!
.
Let’s look at this process in even more detail. The source code is shown at the top of this chapter.
The compiler is called by using
javac HelloWorld.java
This will run and then create the HelloWorld.class
file if there are no errors.
HelloWorld.class¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Compiled from "HelloWorld.java" public class HelloWorld { public HelloWorld(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3 // String Hello World! 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return } |
The bytecode looks completely different than the source code. There are some similarities that can be spotted. The HelloWorld.class can be executed by the JVM when ever required. Now that the bytecode is created when the JVM is called it will create the console window output.
Output
Hello World!
Now we know how we get from the source code to the compiled output in the console window.
End of Lesson Exercises¶
Answer the following questions on a piece of paper to fully understand the lesson content. Some questions might require you to research further.
Who owns Java now?
What is the programming language used by Android?
What version is the current Java language specification?
Explain the difference between JDK and JRE.
Is Java case sensitive?
Java contains keywords, can you list 20 of them?
What is a statement? How do you end a statement in Java?
What is the difference between print and println?
What is the filename extension for a Java source and bytecode?
What is the command to compile and run a Java program?
Can the Java bytecode run on any device or machine?
Are there any limitations to the Java compiler?
Lesson 3: Practices and Errors¶
Good Practices¶
In Java, a whole program can be done on one line; however, this would make it incredibly hard to read, understand, and maintain. To be readable, code is spread out over multiple lines, and a concept known as white space is used. White space is the area between and around programming elements. Let’s look at the Hello World example.
The first example will have everything on one line.
1 | public class HelloWorld{public static void main(String[] args){/*Display message Hello World! on the console*/System.out.println("Hello World!");}} |
Notice how it’s hard to see what is going on. There is an inline comment which is ok, but there are better ways to comment. In conclusion, it is tough to interpret.
In this example, proper white space and multiple lines are used to separate everything.
1 2 3 4 5 6 7 8 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |
This code is now readable and it is easy to follow along.
Indentation Style¶
There are two popular styles of indentation in programming. K&R (Kernighan and Ritchie) and Allman (Eric Allman).
K&R Style¶
1 2 3 4 5 6 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |
Allman Style¶
1 2 3 4 5 6 7 8 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |
There are benefits and drawbacks to both methods. K&R allows for saving space but can get be very hard to debug. Allman lines the braces {}
up; this allows for easy debugging; however, it does add extra lines of code. The choice of which one is better comes down to a personal decision. Programmers have long fought over which style is better, but at the end of the day, there is only one rule, be consistent. Don’t switch between styles in a project, always maintain the same style throughout the whole project. Mixing styles will cause your brain to use more overhead when trying to debug an error and lead to much frustration.
Hint
Most of the documentation here will use Allman style as it allows for more comfortable reading on new programmers.
Indentation¶
Code should always be indented after a brace {
. Some examples are shown below.
Bad Indentation
1 2 3 4 5 6 7 8 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |
This code has no indentation.
Good Indentation
1 2 3 4 5 6 7 8 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |
This code has good indentation. Notice that after the {
in the highlighted lines the next block is indented.
Spacing¶
While not necessary, spacing allows for easier readability within code.
Bad Spacing
1 | System.out.println(6*10%5/6.2+"Math is crazy!"); |
Good Spacing
1 | System.out.println(6 * 10 % 5 / 6.2 + "Math is crazy!"); |
Both forms are acceptable; however, the second is a better practice and allows for easier debugging.
Commenting¶
Documentation is fundamental in programming. A good saying is to always comment as if someone else needed to use your code and understand what is going on. There are three types of comments; line, block, and Javadoc. Adding comments to your code will not change the functionality of the code. Comments are not compiled and included in the Java bytecode.
Line¶
Line comments are the most common type of commenting. A line comment is achieved by using a //
before any line you want to comment or comment out. Some examples are shown below.
1 2 3 4 5 6 7 | // This is a basic line comment System.out.println("Hello World!"); // Line comments can be placed after code as well // Anything after the // will be commented out and excluded this is useful for disabling lines of code // System.out.println("Hello World!"); |
If we were to run the code above only the first print statement will be printed to the console. The second print statement has been commented out and will be ignored by the compiler.
Block¶
Block comments are useful when multiple lines of comments are required. Block comments can also be used to comment out a whole section of code. Block comments start with /*
and to end the comment use *\
. Some examples are shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /* * This is a block comment * <- Sometimes we add a * or the ide will auto add a * to show a new line in the comment block */ int x = 10 /* Block comments can be used inline as well but not preferred */ + 20; /* The code below is commented out public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } */ |
Always remember to close the block comment with */
otherwise all the code after the starting /*
will be commented out.
Javadoc¶
Javadoc comments are a particular type of comment. When documentation is generated for a Java project, a Javadoc comment will follow into the docs. Javadoc comments are generally used at the beginning of the program in the title block and at the beginning of every class and method. A Javadoc comment is similar to a block comment with one change. To start a Javadoc comment, use /**
notice the double *. To end a Javadoc use */
. Some examples are shown below.
1 2 3 4 5 6 7 8 9 10 11 | /** * This is an example of a Javadoc comment */ /** * Javadoc comments have some special features called tags * Here are some examples of tags * @param variable variable description * @return whatever the return statement is * @author authors name */ |
Javadoc comments are very useful and powerful. For a full list of tags and how they are used consult the Javadoc tag conventions here.
Errors¶
There are three types of errors in programming; Syntax, Logic and Runtime.
Syntax Errors¶
Any error that is detected by the compiler is called a Syntax error. Syntax errors are due to issues in how the code is constructed. Some common syntax errors are misspelled words, forgetting braces {}
and or semi-colons ;
.
Some examples are below.
1 2 3 4 5 6 7 8 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!); } } |
This example will give 3 errors on compilation.
On line 6 it will give an unclosed string literal
and a ';' expected
error. On line 8 it will give a reached end of file while parsing }
error.
The only actual error is the first one on line 6: unclosed string literal
. The other errors are the chain effect of the first error. To fix this error have a look at line 6.
6 | System.out.println("Hello World!);`` |
The error is that a string was started but never completed. Remember a string requires an opening "
and closing "
quotation. To fix this line a end "
quotation is needed. The "
is inserted between the !
and the )
.
6 | System.out.println("Hello World!"); |
Logic Errors¶
Logic errors are when a program is supposed to do one thing but does another. Logic errors are very common and sometimes can lead to long debugging sessions. One of the most common logic errors is comparisons. An example is shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class LogicError { public static void main(String[] args) { boolean x = false; if (x = false) { System.out.println("X is False!"); } else { System.out.println("X is True!"); } } } |
Output
X is True!
The code compiles but the output is wrong. The correct output should have been X is False!
. The logic error is on line 7. Comparisons and if statements will be covered in later chapters. But for now a logical error is being shown.
On line 7 we have:
7 | if (x = false) |
Comparisons require a ==
not =
.
By changing line 7 to this:
7 | if (x == false) |
We get the successful output:
X is False!
Runtime Errors¶
Runtime errors happen while the Java code is running. Some common runtime errors are input errors, logical errors that cause the program to crash and infinite loops.
A simple runtime error to show is a division by zero. This is shown below.
1 2 3 4 5 6 7 | public class RuntimeError { public static void main(String[] args) { System.out.println(1 / 0); } } |
The output console will display this error:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at RuntimeError.main(RuntimeError.java:5)
End of Lesson Exercises¶
Answer the following questions on a piece of paper to fully understand the lesson content. Some questions might require you to research further.
Convert this code on one line to the proper format of multiple lines.
1
public class OneLiner{public static void main(String[] args){System.out.println("This is a one liner!");}}
Convert this K&R indentation to Allman indentation.
1 2 3 4 5 6 7
public class Indentation { public static void main(String[] args) { System.out.println("Some"); System.out.println("Random"); System.out.println("Text"); } }
Find the names for 5 other indentation styles.
Space out the expressions here properly.
1
System.out.println(10+10+2*4/6%50*22-100);
Comment out line 5 and 7 of the code below.
1 2 3 4 5 6 7 8 9 10
public class Indentation { public static void main(String[] args) { System.out.println("Thats"); System.out.println("Random"); System.out.println("Stuff"); System.out.println(":)"); } }
Find and give a description of 8 tags from Javadoc comments.
Describe all three types of errors.
Find more examples of errors online and how the errors were fixed.
Unit 2: Starting Java¶
Lesson 1: Installing Java¶
Downloading the JDK¶
The Java JDK is required for most development in Java.
Installing the JDK¶
The latest JDK can be found on the Oracle website here. Select the correct install for your OS.
Note
An Oracle account will be required to download JDK 11
. This is due to the recent release of JDK 14
. The IDE we use in the curriculum does support the new JDK; however, the workaround is something we won’t be covering in this curriculum.
Run the JDK installation file, this will require Admin privileges.

The installation wizard will open. Follow the prompts and install the JDK. When complete you should see a window like the one below.

Adding Java to PATH¶
Sometimes the JAVA_HOME is not set correctly after installing the JDK. To verify if you have the JAVA_HOME set correctly open the command prompt and type in the following command.
java
If it is not set correctly you will see the following error:
'java' is not recognized as an internal or external command,
operable program or batch file.
To fix this we need to add JAVA_HOME to the PATH in environment variables. To get to environment variables hit the WIN
+ R
key to open run. In run type "SystemPropertiesAdvanced"
and hit enter. This will open the Advanced tab in System Properties.

Select Environment Variables
.

In the first section User variables
, Find the variable Path
and select it and hit Edit...
.

Hit New
and enter the following C:\Program Files\Java\jdk-11.0.7\bin
.
Important
If you installed the JDK in a different location use that path instead.
Repeat the same process above for the System variables
.
To verify that the Path is set correctly open the command prompt again and enter the command java
. There should be a response as shown below.
C:\Windows\system32>java
Usage: java [options] <mainclass> [args...]
(to execute a class)
or java [options] -jar <jarfile> [args...]
(to execute a jar file)
or java [options] -m <module>[/<mainclass>] [args...]
java [options] --module <module>[/<mainclass>] [args...]
(to execute the main class in a module)
or java [options] <sourcefile> [args]
(to execute a single source-file program)
Arguments following the main class, source file, -jar <jarfile>,
-m or --module <module>/<mainclass> are passed as the arguments to
main class.
where options include:
-cp <class search path of directories and zip/jar files>
-classpath <class search path of directories and zip/jar files>
--class-path <class search path of directories and zip/jar files>
A ; separated list of directories, JAR archives,
and ZIP archives to search for class files.
-p <module path>
--module-path <module path>...
A ; separated list of directories, each directory
is a directory of modules.
--upgrade-module-path <module path>...
A ; separated list of directories, each directory
is a directory of modules that replace upgradeable
modules in the runtime image
--add-modules <module name>[,<module name>...]
root modules to resolve in addition to the initial module.
<module name> can also be ALL-DEFAULT, ALL-SYSTEM,
ALL-MODULE-PATH.
--list-modules
list observable modules and exit
-d <module name>
--describe-module <module name>
describe a module and exit
--dry-run create VM and load main class but do not execute main method.
The --dry-run option may be useful for validating the
command-line options such as the module system configuration.
--validate-modules
validate all modules and exit
The --validate-modules option may be useful for finding
conflicts and other errors with modules on the module path.
-D<name>=<value>
set a system property
-verbose:[class|module|gc|jni]
enable verbose output for the given subsystem
-version print product version to the error stream and exit
--version print product version to the output stream and exit
-showversion print product version to the error stream and continue
--show-version
print product version to the output stream and continue
--show-module-resolution
show module resolution output during startup
-? -h -help
print this help message to the error stream
--help print this help message to the output stream
-X print help on extra options to the error stream
--help-extra print help on extra options to the output stream
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]
enable assertions with specified granularity
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>]
disable assertions with specified granularity
-esa | -enablesystemassertions
enable system assertions
-dsa | -disablesystemassertions
disable system assertions
-agentlib:<libname>[=<options>]
load native agent library <libname>, e.g. -agentlib:jdwp
see also -agentlib:jdwp=help
-agentpath:<pathname>[=<options>]
load native agent library by full pathname
-javaagent:<jarpath>[=<options>]
load Java programming language agent, see java.lang.instrument
-splash:<imagepath>
show splash screen with specified image
HiDPI scaled images are automatically supported and used
if available. The unscaled image filename, e.g. image.ext,
should always be passed as the argument to the -splash option.
The most appropriate scaled image provided will be picked up
automatically.
See the SplashScreen API documentation for more information
@argument files
one or more argument files containing options
-disable-@files
prevent further argument file expansion
--enable-preview
allow classes to depend on preview features of this release
To specify an argument for a long option, you can use --<name>=<value> or
--<name> <value>.
Writing your first Java Program¶
With the JDK installed we can now create our first Java program.
Creating the Simple Java Program¶
Open notepad and enter in the Simple Java Program. If you have forgotten the code is listed below.
1 2 3 4 5 6 7 8 | public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |

Important
Remember to save the file as HelloWorld.java
.
Compiling the Simple Java Program¶
To compile HelloWorld.java
open command prompt as Administrator.
Navigate to the location of HelloWorld.java
. An example is shown below.

Once navigated to the correct folder you can verify HelloWorld.java
is there by using the command dir
.
Once HelloWorld.java
is verified to be in the folder run the following command.
javac HelloWorld.java
Running the Simple Java Program¶
While still in the location of the HelloWorld.java
in the command prompt run the command:
java HelloWorld

Tip
If you would like view the Java bytecode you can run the command javap -c HelloWorld.class
.
Installing an IDE¶
Writing programs in notepad and compiling them works and is ok for small programs. But for creating and managing large projects a piece of software called and Integrated Development Environment IDE
is required. The IDE used for this curriculum is NetBeans.
Download NetBeans 11.3 here.

Launch the installer and go through the prompts.
Once installed open NetBeans and this prompt will come up.

Hit next
and go through the plugin installer. It is recommended to install all the default plugins listed. When complete the IDE window should look like this.

Lesson 2: Writing Some Programs¶
Using the IDE¶
Now that the IDE is installed we can start writing some Java code without needing the command prompt.
Creating a Project¶
The first step is to create a Java project. Open NetBeans and select New Project
. This can be done by going to File>New Project
, using the shortcut Ctrl + Shift + N
or by clicking on the icon as shown.

This will open up the project creation window.

We are going to use Java with Maven
and select Java Application
for projects.

Hint
Maven is a project automation tool used for building Java projects. You can find out more here

For Project Name use FirstProject
and for Group Id: use com.edu
.

This will create the project. In the project window on the left you will see a project displayed called FirstProject
. This is the project we just created.
Adding a Java file to the Project¶
To create a Java file right click on com.edu.firstproject
and select New > Java Class
.

This will open another window for creating the Java Class.

The Class Name should be HelloWorld
. Hit Finish when ready and the Java file will be created and added to the project.

The HelloWorld.java
file will automatically open for editing. Let’s break down what each section of the default template is.
1 2 3 4 5 | /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ |
This is the License header. License headers are normally required when you are going to release code or projects to the public. For personal use they are not really required.
6 | package com.edu.firstproject; |
Packages are a way of organizing Java classes into a namespace. This is mostly useful for when you create a library and the library requires multiple classes.
8 9 10 11 | /** * * @author james */ |
This is the Javadoc comment for the HelloWorld Class. Notice how the author tag is automatically added for you. This is useful for tracking who created what. Some projects can get very big and tracking down the author can be crucial.
12 13 14 | public class HelloWorld { } |
This is the class definition. This is created by default to prevent errors with the class name not matching the filename. Notice how the class is currently using the indentation style of K&R. We will change this to Allman by moving the starting brace {
to line 13. We should get this:
12 13 14 15 | public class HelloWorld { } |
Adding the Simple Java Program¶
Let’s add the simple Java program, compile it, then run it.
To add the simple Java program to the HelloWorld.java we can see that we are only missing one section. The contents of of the HelloWorld class.
Add this to the contents of the HelloWorld class
1 2 3 4 5 | public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } |
The HelloWorld.java
should now fully look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.edu.firstproject; /** * * @author james */ public class HelloWorld { public static void main(String[] args) { //Display message Hello World! on the console System.out.println("Hello World!"); } } |
To compile the project we will hit the build icon or use F11
.

You should notice a window popup on the bottom of the IDE. This is the output console.

Note
On first build maven might take a few seconds as it will downloading dependencies need for compilation.
To run the project and see some output we hit the green arrow by the build icon. Alternatively F6
can be used as well.

A popup will come up asking you to select the main class.

Select com.edu.firstproject.HelloWorld
and hit the Select Main Class
button. The project will then run and the output can be seen in the console window as displayed below.

Exercises¶
Below are a list of exercises to complete. Answers will be provided in the next section.
Important
To learn properly it is recommended to attempt the exercises before looking at the answers.
Hint
All programs except the challenge will only require System.out.println();
or System.out.print();
.
Write a program that will display the following in the output console:
Hello World!
It is very nice to meet you.
This is one of my first Java programs.
Write a program that will display the following in the output console:
RRRRR OOO BBBBB OOO TTTTT
R R O O B B O O T
RRRR O O BBBB O O T
R R O O B B O O T
RR R OOO BBBBB OOO T
Write a program that will create a table like this:
x x^2 x^3 x^4
1 1 1 1
2 4 8 16
3 9 27 81
4 16 64 256
5 25 125 625
Write a program that will display the result of:
\[1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9\]Write a program that will disapply the result of:
\[\begin{equation} \frac{10.6 * 4.0 - 3.2 * 1.0}{20.6 - 1.8} \end{equation}\]Write a program that converts 30°C to Fahrenheit
Hint
The formula is \(\begin{equation}\frac{9}{5}*C+32\end{equation}\)
Challenge Question¶
Note
Challenge questions are here to stump you and test your problem solving skills. The content in a challenge question will be covered in later units.
Add onto question 6 by having the user input a value in Celsius to be converted.
Example the user inputted 30°C
Enter the temperature to convert in °C: 30
30°C is equal to 86°F
Note
A user might input a value such as 30.5
Exercise Answers¶
Question 1:¶
1 2 3 4 5 6 7 8 9 10 | public class QuestionOne { public static void main(String[] args) { //Display some messages on the console System.out.println("Hello World!"); System.out.println("It is very nice to meet you."); System.out.println("This is one of my first Java programs."); } } |
Question 2:¶
1 2 3 4 5 6 7 8 9 10 11 12 | public class QuestionTwo { public static void main(String[] args) { //Display message Robot on the console System.out.println("RRRRR OOO BBBBB OOO TTTTT"); System.out.println(" R R O O B B O O T"); System.out.println(" RRRR O O BBBBB O O T"); System.out.println(" R R O O B B O O T"); System.out.println("RR R OOO BBBBB OOO T"); } } |
Question 3:¶
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class QuestionThree { public static void main(String[] args) { //Display a table on the console System.out.println("x x^2 x^3 x^4"); System.out.println("1 1 1 1"); System.out.println("2 4 8 16"); System.out.println("3 9 27 81"); System.out.println("4 16 64 256"); System.out.println("5 25 125 625"); } } |
Question 4:¶
1 2 3 4 5 6 7 8 9 | public class QuestionFour { public static void main(String[] args) { //Display some math on the console System.out.print("1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = "); System.out.println(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9); } } |
Output
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = 45
Question 5:¶
1 2 3 4 5 6 7 8 | public class QuestionFive { public static void main(String[] args) { //Display some math on the console System.out.println((10.6 * 4.0 - 3.2 * 1.0) / (20.6 - 1.8)); } } |
Output
2.085106382978723
Question Six¶
1 2 3 4 5 6 7 8 9 | public class QuestionSix { public static void main(String[] args) { //Display some math on the console System.out.print((9.0 / 5.0) * 30 + 32); System.out.println("°F"); } } |
Output
86°F
Important
If you got 62°F as your answer there is a logic error in your code. In Java \(\begin{equation}\frac{9}{5}\end{equation}\) would result in 1. This is due to integer division. Integers do not allow decimal points. \(\begin{equation}\frac{9}{5}\end{equation}\) should be 1.8 but the result is 1 as .8 is discarded. To eliminate integer division we add .0 to the integer as shown in the answer.
Challenge Question¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.edu.firstproject; import java.util.Scanner; /** * * @author james */ public class ChallengeQuestion { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter the temperature to convert in °C: "); double temp = input.nextDouble(); System.out.println(temp + "°C is equal to " + ((9.0 / 5.0) * temp + 32) + "°F"); } } |
Example Output
Enter the temperature to covert in °C: 21.5
21.5°C is equal to 70.7°F
Unit 3: Java Essentials¶
Lesson 1: Data Types and Variables¶
Data Types¶
Most programing languages use data types to tell the compiler how the program is using data. Using the correct data type is always important. Good programmers always try to optimize the code as much as possible. The easiest way to optimize code is to use the correct data type. Data types are in many shapes and sizes. Java has eight primitive data types.
Name |
Range |
Size |
---|---|---|
byte |
-128 -> 127 |
8-bit signed |
short |
-32768 -> 32767 |
16-bit signed |
int |
-2147483648 -> 2147483647 |
32-bit signed |
long |
-9223372036854775808 -> 9223372036854775807 |
64-bit signed |
float |
±3.4028235E+38 |
32-bit IEEE 754 |
double |
±1.79769313486231570E+308 |
64-bit IEEE 754 |
char |
0 -> 65536 |
16-bit unsigned |
boolean |
true or false |
1-bit |
Note
Java uses signed data types except for char’s. The difference between unsigned and signed data types is that unsigned cannot hold negative values. However unsigned data types have larger positive values.
Primitive data types are split into two categories Integers
and Floating Points
. Integers are used for whole numbers, whereas floating points are used for numbers that have decimal points.
Integers¶
Byte¶
Bytes are the smallest type of integers. They hava range of -128 to 127. When an integer is required and the value will be in that range it is best to use a byte as it will save memory.
1 2 | byte variable = 42; System.out.println(variable); |
Output
42
Short¶
Shorts are used when the integer will fall in between -32768 to 32767.
1 2 | short variable = -4242; System.out.println(variable); |
Output
-4242
Int¶
Int is the most used data type for integers. Unless there is a specific reason to use one of the other integer types int
is always preferred and used. Int’s have a range of -2147483648 to 2147483647.
1 2 | int variable = 123456789; System.out.println(variable); |
Output
123456789
Long¶
Long data types are used when an int
is not big enough. Longs have a range of -9223372036854775808 to 9223372036854775807.
Note
Longs require an L
to be added at the end of the value.
1 2 | long variable = 9999999999L; System.out.println(variable); |
Output
9999999999
Floating Points¶
float¶
Floats are used for numbers that have decimals. The range for floats is ±3.4028235E+38.
Note
floats require an f
to be added at the end of the value.
1 2 | float variable = 1.2345f; System.out.println(variable); |
Output
1.2345
Note
floats are good when a precision of six to seven decimal points are required.
double¶
Doubles are used for numbers that have lots of decimals. Unlike floats, doubles have a precision of fifteen decimal points.
1 2 | double variable = 42.42; System.out.println(variable); |
Output
42.42
Note
Unlike floats the value of a double does not require a d
at the end.
Scientific Numbers¶
In Java Floating Points can be scientific numbers.
1 2 3 4 | double variable = 42.42e6; float variable1 = 42.42e-2f; System.out.println(variable); System.out.println(variable1); |
Output
424200.0
0.4242
Boolean¶
Booleans are a special data type as they don’t hold a numeric value. Booleans only have two options, true or false.
1 2 3 4 | boolean variable = true; boolean variable1 = false; System.out.println(variable); System.out.println(variable1); |
Output
true
false
Char¶
Char is the short form for character. char
will store a single character. Char’s use a single quotations ' '
to identify.
1 2 | char variable = 'A'; System.out.println(variable); |
Output
A
Variables¶
Variables are a way of storing data that can be used and changed throughout the program.
Variable Declaration¶
Before a variable may be used it must be declared. This tells the compiler to allocate memory for the variable. The format for declaring a variable is shown below.
dataType variableName;
dataType is the data type that the variable holds.
variableName is the name of the variable you wish to give.
Some examples of real world variables:
1 2 3 | int number; double area; char letter; |
Variables of the same data type can be declared together.
Instead of:
1 2 3 | int a; int b; int c; |
Use:
1 | int a, b, c; |
Variables should have initial values. It is easy to do this when declaring them.
1 2 3 4 5 | int number = 1; double area = 42.5; char letter = 'B'; int a = 1, b = 2, c = 3; |
Variable Naming¶
Variables follow a camel case naming structure. This means that the first word is lower case and any preceding word in the variable has a capital first letter.
1 2 3 4 5 6 7 | // Bad naming of variables ie. no camelCase int Hellothere; double thatsreallycool; // Good naming of variables using camelCase int helloThere; double thatsReallyCool; |
If you notice it becomes easier to read the different words in the variable.
Variables containing more than one word should be joined together.
1 2 3 4 5 6 7 | // Bad variable naming int hello_There; double thats-really-cool; // Good variable naming int helloThere; double thatsReallyCool; |
Variables must always start with a lowercase letter, an underscore _
or a $
sign. Variables cannot start with a number or any other symbol.
1 2 3 4 | // Acceptable starts of variables int hello; double _variable; long $money; |
Variables should always be descriptive but not to long and match the function.
1 2 3 4 5 6 7 8 9 | // Good Example // Variables for calculating Pythagorean theorem double a, b, c; // Bad Example // Variables for calculating Pythagorean theorem double edge, longerEdge, reallyLongEdge; |
Constants¶
Constants are a special type of variable that cannot change during the operation of the program. Constants are useful for values that wont change or don’t need to change.
To declare a constant we use:
final dataType CONSTANT_NAME = valueOfConstant;
final
tells the compiler that this variable cannot be changed.
Note
Unlike variables constants use all caps for naming. Also if more than one word is in the constant name we use an underscore _
to separate them.
Some examples
1 2 | final int CONTROLLER_AXIS = 1; final double PI = 3.14159265358979; |
Using Variables in code¶
Variables make programing easy lets go through some examples.
1 2 3 4 5 6 7 8 | public class Variables { public static void main(String[] args) { int x = 1; System.out.println(x); } } |
Output
1
Line 5 holds the variable and its initial value. The variable is x
, the data type is int
and the value of the variable is 1
.
Line 6 is the output of the variable. When printing a variable the quotations " "
are not used.
1 2 3 4 5 6 7 8 | public class Variables { public static void main(String[] args) { final double PI = 3.14159265358979; System.out.println("The Value of pi to 15 decimal places is: " + PI); } } |
Output
The Value of pi to 15 decimal places is: 3.14159265358979
Line 5 holds the variable which is being used as a constant.
Line 6 is the output. Notice how this time we are mixing a String with a variable. The String “The Value of pi to 15 decimal places is: ” and variable PI are joined by using +
.
1 2 3 4 5 6 7 8 9 10 | public class Variables { public static void main(String[] args) { double x = 6.84; System.out.println("Original Variable x: " + x); x = 10.8; System.out.println("Changed Variable x: " + x); } } |
Output
Original Variable x: 6.84
Changed Variable x: 10.8
In this example we define the variable x
and give it the value of 6.84
on line 5. On line 7 we assign x a new value of 10.8
.
Exercises¶
Below are a list of exercises to complete. Answers will be provided in the next section.
Important
To learn properly it is recommended to attempt the exercises before looking at the answers.
Write a program that displays each data type. An example output is shown.
byte: 125
short: 32000
int: 500000
long: 9999999999
float: 10.4
double: -50112.56
char: A
boolean: true
Write a program that changes the value of a variable 3 times. An example output is shown.
Original: 105
Change 1: 110
Change 2: -15
Change 3: 12
Add the following variables together.
int x = 10;
int y = 12;
int z = 42;
Example output
10 + 12 + 42 = 64
Challenge Question¶
Note
Challenge questions are here to stump you and test your problem solving skills. The content in a challenge question will be covered in later units.
Create a Pythagorean calculator. The user will input values for
a
andb
. The calculator should respond with the length of the hypotenuse.
The formula for Pythagorean theorem is: \(a^2 + b^2 = c^2\)
Example
Welcome to the Pythagorean Calculator!!!
Enter the length of side A: 3
Enter the length of side B: 4
Calculating
The length of the hypotenuse is: 5
Exercise Answers¶
Question 1:¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class QuestionOne { public static void main(String[] args) { byte d1 = 125; short d2 = 32000; int d3 = 500000; long d4 = 9999999999L; float d5 = 10.4f; double d6 = -50112.56; char d7 = 'A'; boolean d8 = true; System.out.println("byte: " + d1); System.out.println("short: " + d2); System.out.println("int: " + d3); System.out.println("long: " + d4); System.out.println("float: " + d5); System.out.println("double: " + d6); System.out.println("char: " + d7); System.out.println("boolean: " + d8); } } |
Question 2:¶
Note
There are two solutions to this question
1 2 3 4 5 6 7 8 9 10 11 12 | public class QuestionTwo { public static void main(String[] args) { int x = 105; System.out.println("Original: " + x); System.out.println("Change 1: " + (x = 110)); System.out.println("Change 2: " + (x = -15)); System.out.println("Change 3: " + (x = 12)); } } |
or
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class QuestionTwo { public static void main(String[] args) { int x = 105; System.out.println("Original: " + x); x = 110; System.out.println("Change 1: " + x); x = -15; System.out.println("Change 2: " + x); x = 12; System.out.println("Change 3: " + x); } } |
Question 3:¶
1 2 3 4 5 6 7 8 9 10 11 | public class QuestionThree { public static void main(String[] args) { int x = 10; int y = 12; int z = 42; System.out.println(x + " + " + y + " + " + z + " = " + (x + y + z)); } } |
Challenge Question¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.edu.unit3; import java.util.Scanner; /** * * @author james */ public class ChallengeQuestion { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.println("Welcome to the Pythagorean Calculator!!!\n"); System.out.print("Enter the length of side A: "); double a = input.nextDouble(); System.out.print("Enter the length of side B: "); double b = input.nextDouble(); System.out.println("Calculating"); double result = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)); System.out.println("The length of the hypotenuse is: " + result); } } |
Output
Welcome to the Pythagorean Calculator!!!
Enter the length of side A: 3
Enter the length of side B: 4
Calculating
The length of the hypotenuse is: 5.0
Lesson 2: Type Casting and Operators¶
Type Casting¶
Type casting is process of assigning a value of one data type to another data type. There are two types of casts in Java, Widening and Narrowing.
Widening Casting¶
Widening casts are done automatically. A widening cast only happens when going from a smaller data type to a larger data type. The list from smallest to largest is listed below.
byte
short
char
int
long
float
double
Note
The data type boolean cannot be type casted.
Examples¶
int
-> long
1 2 3 4 5 | int typeInt = 42; long typeLong = typeInt; System.out.println(typeInt); System.out.println(typeLong); |
Output
42
42
byte
-> double
1 2 3 4 5 | byte typeByte = 2; double typeDouble = typeByte; System.out.println(typeByte); System.out.println(typeDouble); |
Output
2
2.0
Narrowing Casting¶
Narrowing casting is done when a larger data type needs to be converted to a smaller data type.
Examples
float
-> short
1 2 3 4 5 | float typeFloat = -42.27; short typeShort = (short) typeFloat; System.out.println(typeFloat); System.out.println(typeShort); |
Output
-42.27
-42
Operators¶
Operators are functions that act upon elements to create new elements. There are 5 types of operator groups in Java.
Arithmetic
Assignment
Bitwise
Comparison
Logical
Arithmetic¶
Arithmetic operators are common mathematical operators.
Operator |
Meaning |
Example |
Result |
---|---|---|---|
+ |
Addition |
10 + 5 |
15 |
- |
Subtraction |
50 - 8 |
42 |
* |
Multiplication |
4 * 6 |
24 |
/ |
Division |
2.0 / 1.0 |
1.0 |
++ |
Increment by 1 |
z++ |
z + 1 |
– |
Decrement by 1 |
z– |
z - 1 |
% |
Modulo |
5 % 2 |
1 |
Assignment¶
Assignment operators are used to store values in defined variables.
Operator |
Meaning |
Example |
Example expanded |
---|---|---|---|
= |
Assign |
x = 10 |
x = 10 |
*= |
Multiply the current value by |
x *= 5 |
x = x * 5 |
/= |
Divide the current value by |
x /= 5 |
x = x / 5 |
%= |
Mudulo the current value by |
x %= 5 |
x = x % 5 |
+= |
Add to the current value by |
x += 5 |
x = x + 5 |
-= |
Subtract the current value by |
x -= 5 |
x = x - 5 |
<<= |
Shift the current value left by |
x <<= 8 |
x = x << 8 |
>>= |
Shift the current value right by |
x >>= 8 |
x = x >> 8 |
&= |
Bitwise AND the current value by |
x &= 0xFF |
x = x & 0xFF |
^= |
Bitwise XOR the current value by |
x ^= 0xFF |
x = x ^ 0xFF |
|= |
Bitwise OR the current value by |
x |= 0xFF |
x = x | 0xFF |
Bitwise¶
Bitwise operators manipulate the individual bits of numbers.
X = 10 and Y = 2
Operator |
Meaning |
Example |
Result |
---|---|---|---|
~ |
Unary NOT |
~X |
-11 |
& |
AND |
X & Y |
2 |
| |
OR |
X | Y |
10 |
^ |
XOR |
X ^ Y |
8 |
>> |
Shift Right |
X >> 1 |
5 |
<< |
Shift Left |
X << 1 |
20 |
>>> |
Shift right zero fill |
X >>> 1 |
5 |
Comparison¶
Comparison operators are used to compare two values.
X = 5 and Y = 2
Operator |
Meaning |
Example |
Result |
---|---|---|---|
== |
Equal to |
X == Y |
false |
!= |
Not Equal |
X != Y |
true |
> |
Greater than |
X > Y |
true |
< |
Less than |
X < Y |
false |
>= |
Greater than or equal to |
X >= Y |
true |
<= |
Less than or equal to |
X <= Y |
false |
Exercises¶
Below are a list of exercises to complete. Answers will be provided in the next section.
Important
To learn properly it is recommended to attempt the exercises before looking at the answers.
What are the results of these casts?
double X;
short Y = 10;
X = Y;
System.out.println(X);
long Z;
int F = 1234567;
Z = F;
System.out.println(Z);
Add to the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class QuestionTwo { public static void main(String[] args) { double X = 12345.54321789; // Show X as a double System.out.println("X as a double: " + X); // Show X as a float // Show X as an integer // Show X as a byte } } |
What are the results of these operations:
int X = 5 + 2;
int Y = 20 / 2;
int Z = 10 % 3;
X %= 2;
Y /= 5;
Z *= 2;
X == 2;
Y > 1;
Z <= 5;
(5 > 2) && (2 > 2)
(5 <= 5) || (2 == 2)
!(5 <= 6) || (2 == 2) && (3 > 3)
Challenge Question¶
Note
Challenge questions are here to stump you and test your problem solving skills. The content in a challenge question will be covered in later units.
Create a sales tax calculator. The user is required to input the value of the item. The output may only have 2 decimal places and the sales tax rate is 13%.
Example output
Enter purchase amount: $299.99
Sales Tax is: $38.99
Exercise Answers¶
Question 1:¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class QuestionOne { public static void main(String[] args) { double X; short Y = 10; X = Y; System.out.println(X); long Z; int F = 1234567; Z = F; System.out.println(Z); } } |
Output
10.0
1234567
Question 2:¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class QuestionTwo { public static void main(String[] args) { double X = 12345.54321789; // Show X as a double System.out.println("X as a double: " + X); // Show X as a float System.out.println("X as a float: " + (float) X); // Show X as an integer System.out.println("X as an integer: " + (int) X); // Show X as a byte System.out.println("X as a byte: " + (byte) X); } } |
Output
X as a double: 12345.54321789
X as a float: 12345.543
X as an integer: 12345
X as a byte: 57
Note
It would be good to notice that the accuracy of X drops as the cast goes to a smaller sized data type.
Question 3:¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class QuestionThree { public static void main(String[] args) { int X = 5 + 2; int Y = 20 / 2; int Z = 10 % 3; System.out.println("X = " + X + " Y = " + Y + " Z = " + Z); X %= 2; Y /= 5; Z *= 2; System.out.println("X = " + X + " Y = " + Y + " Z = " + Z); System.out.println("(X == 2) = " + (X == 2) + " (Y > 1) = " + (Y > 1) + " (Z <= 5) = " + (Z <= 5)); System.out.println("(5 > 2) && (2 > 2) = " + ((5 > 2) && (2 > 2))); System.out.println("(5 <= 5) || (2 == 2) = " + ((5 <= 5) || (2 == 2))); System.out.println("!(5 <= 6) || (2 == 2) && (3 > 3) = " + (!(5 <= 6) || (2 == 2) && (3 > 3))); } } |
Output
X = 7 Y = 10 Z = 1
X = 1 Y = 2 Z = 2
(X == 2) = false (Y > 1) = true (Z <= 5) = true
(5 > 2) && (2 > 2) = false
(5 <= 5) || (2 == 2) = true
!(5 <= 6) || (2 == 2) && (3 > 3) = false
Challenge Question¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.edu.unit3; import java.util.Scanner; /** * * @author james */ public class ChallengeQuestion { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Enter purchase amount: $"); double purchaseAmount = input.nextDouble(); double tax = purchaseAmount * 0.13; System.out.println("Sales tax is: $" + (int)(tax * 100) / 100.0); } } |
Output
Enter purchase amount: $299.99
Sales Tax is: $38.99
Lesson 3: Strings¶
Unit 4: Inputs and Methods¶
Lesson 1: Inputs from the User¶
Scanner¶
Receiving user data is an important part of coding to interact with the user. To receive input from the user, the Scanner
class is used. This is found under the java.util
package.
Hence we will need to import the library first,
1 | import java.util.Scanner; |
Note
This is the most important step and also the easiest to forget. If the Scanner
class is not implemented, all its function and instances cannot be used.
Now we can use the Scanner class by creating an object of the class.
1 | Scanner input = new Scanner(System.in); |
The line above creates a Scanner instance input that will read the user input depending on the methods called. The following are the eight most common methods to retrieve user input depending on the data type.
Methods |
Return Type |
---|---|
|
Byte |
|
Short |
|
Int |
|
Long |
|
Float |
|
Double |
|
String |
|
Boolean |
Important
It is important that the input type matches the method’s data type, or else you will get an exception/error message.
Setting up with Scanner class¶
1 2 3 4 5 6 7 8 9 10 11 12 | import java.util.Scanner; // Import Scanner library class TestClass { public static void main(String[] args) { // Create an instance of the Scanner class Scanner input = new Scanner(System.in); // Source code as follows } } |
Byte¶
1 2 3 4 5 | System.out.println("Enter a byte integer:"); // Reading the input as byte data type byte aByte = input.nextByte(); System.out.println("aByte = " + aByte); |
Output
1 2 3 | Enter a byte integer: 5 aByte = 5 |
Short¶
1 2 3 4 5 | System.out.println("Enter a short integer:"); // Reading the input as short data type short aShort = input.nextShort(); System.out.println("aShort = " + aShort); |
Output
1 2 3 | Enter a short integer: 50 aShort = 50 |
Int¶
1 2 3 4 5 | System.out.println("Enter a integer:"); // Reading the input as a int data type int aInt = input.nextInt(); System.out.println("aInt = " + aInt); |
Output
1 2 3 | Enter a integer: 100 aInt = 100 |
Long¶
1 2 3 4 5 | System.out.println("Enter a long integer:"); // Reading the input as a long data type long aLong = input.nextLong(); System.out.println("aLong = " + aLong); |
Output
1 2 3 | Enter a long integer: 12345 aLong = 12345 |
Float¶
1 2 3 4 5 | System.out.println("Enter a float:"); // Reading the input as a float data type float aFloat = input.nextFloat(); System.out.println("aFloat = " + aFloat); |
Output
1 2 3 | Enter a float: 95.43 aFloat = 95.43 |
Double¶
1 2 3 4 5 | System.out.println("Enter a double:"); // Reading the input as a double data type double aDouble = input.nextDouble(); System.out.println("aDouble = " + aDouble); |
Output
1 2 3 | Enter a double: 97584.45 aDouble = 97584.45 |
String¶
1 2 3 4 5 | System.out.println("Enter a string:"); // Reading the input as a string data type String aString = input.nextLine(); System.out.println("aString = " + aString); |
Output
1 2 3 | Enter a string: Hello World aString = Hello World |
Boolean¶
1 2 3 4 5 | System.out.println("Enter a boolean:"); // Reading the input as a boolean variable boolean aBoolean = input.nextBoolean(); System.out.println("aBoolean = " + aBoolean); |
Output
1 2 3 | Enter a boolean: true aBoolean = true |
Example¶
The following block of code shows an example of using the Scanner library.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import java.util.Scanner; // Import Scanner library class TestClass { public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.print("Please enter your name: "); String name = input.nextLine(); System.out.println("Hi " + name + ", what is your favourite number?"); int num = input.nextInt(); System.out.println("Your favourite number is " + num + "."); } } |
Output
1 2 3 4 | Please enter your name: Jack Hi Jack, what is your favourite number? 7 Your favourite number is 7. |
Exercises¶
Exercise Answers¶
Style Guide¶
Filenames¶
Only lowercase alphanumeric characters, -
(minus) symbol and the .rst
extension should be used.
Examples:
style-guide.rst
index.rst
a-super-long-filename-that-is-to-long.rst
Preferred Editor¶
It is preferred to use Notepad++ as the text editor for creating files. When creating a .rst
file tabs need to be replaced with a space indentation of 3
.
This can be accomplished easilly with Notepad++ by going to Settings/Preferences/Language
. In Language look to the right side for Tab Settings
, select [Default]
then check Replace by space
and set the Tab size: to 3
. An example image is shown below.

Important
All text should be on the same line. To make it easier to read turn on text wrap. In Notepad++ this feature is enabled by going to View/Word wrap
Indentation and Blank Lines¶
Indentations should always match the previous level of indentation unless a new content block is created.
There should always be 1
blank line between everything. Except for lists.
.. tabs:: Example
some stuff
.. note:: some other stuff
.. image:: images/fake-image-1.png
:align: center
Note
The highlight lines are the 1
blank line. Also note how there is no blank line between .. image::
and :align:
as they are related and not seperate blocks.
Naming Conventions¶
To match other documentation use the following case for these terms exactly:
roboRIO
LabVIEW
myRIO
Visual Studio Code or VS Code
macOS
Linux
VMXpi
Images¶
Images are easy to add and give a visual aspect to the user.
.. image:: images/example-image.png
Images should always be aligned to the center.
.. image:: images/example-image.png
:align: center
If an image is to big or needs to be resized options such as width can be used to scale the image.
.. image:: images/example-image.png
:align: center
:width: 1000
Image Files¶
Location¶
Images should be stored in the same directory as the file using the image, located in a sub-directory images
.
docs/Contributing/style-guide <- is the file
docs/Contributing/images/style-guide-1.png <- image location
File Types¶
Supported image types:
.png
.jpg
.gif
Note
If including a .gif
image a .png
static version of the same name is required to be included in the images
folder. This is required for a proper pdf
build.
If using a .gif
the format for the image would be this:
.. image:: images/example-image.*
:align: center
Naming Conventions¶
Images should be named corresponding to the name of the file using it and incremented with a number enumerated to the end. Examples are shown below.
Filename style-guide.rst
would have the images
style-guide-1.png
style-guide-2.png
Filename another-example.rst
would have the images
another-example-1.gif
another-example-1.png
Headings¶
Headings are signified with an underline with a specific symbol along with the heading character length. The following are the symbol levels to create heading:
=
used for document titles and should only be used once
Document Title
==============
-
signifies the chapters or sections
Chapter or Section
------------------
^
signifies a new sub-section
New Sub-Section
^^^^^^^^^^^^^^^
~
signifies a sub-sub-section
Sub-Sub-Section
~~~~~~~~~~~~~~~
Note
If a heading more than a sub-sub-section is required then in most cases it should be written another way
Links¶
Links should be formated to be anonymous hyperlinks. The format of which is shown below.
`Link <https://google.com>`__
This will come out as: Link
Note
The anonymous link has a few sections. First the `
, then the text the link will attach to in this case Link
, the link itself in <>
, another `
, and finally at the end there are TWO underscores __
.
Code Blocks¶
To create a block of code, use the code-block
directive.
Important
Line numbers are required for any block of code that contatains code. This is shown below. An exception for not having line numbers is when the code-block is just used for unformated text.
.. code-block:: (language)
:linenos:
Source code
Here is a simple Java example.
.. code-block:: java
:linenos:
System.out.println("Hello to whomever is reading this.");
Will come out as:
1 | System.out.println("Hello to whomever is reading this."); |
To higlight certain lines to stand out the :emphasize-lines:
is used.
.. code-block:: java
:linenos:
:emphasize-lines: 2,4
System.out.println("Hello to whomever is reading this.");
System.out.println("I hope you learn something.");
System.out.println("Its real important.");
System.out.println("For success.");
Will come out as:
1 2 3 4 | System.out.println("Hello to whomever is reading this."); System.out.println("I hope you learn something."); System.out.println("Its real important."); System.out.println("For success."); |
Hint
The use of 2,3,4
is useful for single lines but for ranges 2-4
would work better. They can also be joined I.E. 2,4,6-10,12
.
Lists¶
There are two types of lists and they are easy to use.
- This is
- a simple
- bullet lists
1. This is
2. a simple
3. numeric list
This is
a simple
bullet lists
This is
a simple
numeric list
Note
List’s don’t require the 1
line blank space in-between like the other functions
Tabs¶
Tabs are a useful tool with many uses.
A common use case in this documentation is Java and C++ tabs.
.. tabs::
.. tab:: Java
.. code-block:: java
:linenos:
System.out.println("Hello World!");
.. tab:: C++
.. code-block:: c++
:linenos:
std::cout << "Hello World!";
Would come out looking like:
1 | System.out.println("Hello World!"); |
1 | std::cout << "Hello World!"; |
For more information, vist Sphinx tabs.
Admonitions¶
Admonitions are a popup to indicate a warning or important information. The following are the possible admonitions; attention, caution, danger, error, hint, important, note, tip and warning. To utilize a admonition use the keywords admonition as a directive.
For ease of use place descriptions on the same line as the admonition.
Yes
.. attention:: Description
No
.. attention::
Description
Examples of each:
Attention
This is the attention admonition
Caution
This is the caution admonition
Danger
This is the danger admonition
Error
This is the error admonition
Hint
This is the hint admonition
Important
This is the important admonition
Note
This is the note admonition
Tip
This is the tip admonition
Warning
This is the warning admonition