Note: This tutorial assumes that you have completed the previous tutorials: Hello World Publisher. |
![]() |
CMake with rosserial_arduino
Description: This tutorial shows how to use the CMake build system with rosserial_arduino.Tutorial Level: ADVANCED
Next Tutorial: Servo Controller
Contents
When you are doing large software projects, the Arduino IDE quickly becomes unwieldy. You often want to be able to compile your project from the command line or use a different IDE like Eclipse where you can use autocompletion. By building your rosserial_arduino project using CMake, you can take advantage of both of these tools.
For this tutorial, we are going to make a hello world rosserial_arduino package.
Warning
Although we wont be using the Arduino IDE directly, you must have it installed. This CMake build system uses the libraries and tools from the Arduino IDE installation. Follow the Arduino IDE Setup Tutorial before continuing.
Making Your Project
Making your rosserial_arduino project is just like making any other ROS package. You create a package on your ROS_PACKAGE_PATH:
roscreate-pkg helloworld rosserial_arduino std_msgs
As usual, use roscreate-pkg to create a package named helloworld which depends on rosserial_arduino and std_msgs. You must depend on rosserial_arduino and since we are going to use std_msgs/String messages, the package depends on std_msgs.
Source Code
Copy the the source code below and make a file called src/chatter.cpp in your helloworld package.
1 /*
2 * rosserial Publisher Example
3 * Prints "hello world!"
4 */
5
6 #include <WProgram.h>
7 #include <ros.h>
8 #include <std_msgs/String.h>
9
10 ros::NodeHandle nh;
11
12 std_msgs::String str_msg;
13 ros::Publisher chatter("chatter", &str_msg);
14
15 char hello[] = "hello world!";
16
17 void setup()
18 {
19 nh.initNode();
20 nh.advertise(chatter);
21 }
22
23 void loop()
24 {
25 str_msg.data = hello;
26 chatter.publish( &str_msg );
27 nh.spinOnce();
28 delay(1000);
29 }
This program is almost exactly the same as the hello world covered in the Publisher Tutorial. The only difference is
6 ros::NodeHandle nh;
When you are compiling a cpp file outside of the Arduino IDE, you need to explicitly include a header file which contains all of the Arduino functions (digitalRead, analogRead, delay, etc.).
If you are unsure if you are going to be using a file with the Arduino IDE versus CMake, just add this line at the top of your file. It never hurts and it makes sure your file is always compatible with non-Arduino IDE build systems.
CMakeLists.txt
Open the CMakeLists.txt in your package directory and compare it to the CMakeList below.
1 cmake_minimum_required(VERSION 2.4.6)
2 include($ENV{ROS_ROOT}/core/rosbuild/rosbuild.cmake)
3
4 rosbuild_find_ros_package(rosserial_arduino)
5 include(${rosserial_arduino_PACKAGE_PATH}/cmake_scripts/rosserial.cmake)
6 #====================================================================#
7 # Chatter #
8 #====================================================================#
9
10
11 set(FIRMWARE_NAME chatter)
12
13 set(${FIRMWARE_NAME}_BOARD atmega328) # Arduino Target board
14 set(${FIRMWARE_NAME}_SRCS src/chatter.cpp )
15 set(${FIRMWARE_NAME}_PORT /dev/ttyUSB0) # Serial upload port
16 generate_ros_firmware(${FIRMWARE_NAME})
With rosserial_arduino, we are cross compiling our source code for the AVR processor. As a result, we cannot use the standard ROS CMake file directly. We take ROS's standard first two lines, delete the rest, and then include the special rosserial_arduino cmake functions.
The included rosserial.cmake will setup the compiler, create the generate_ros_firmware function, and attempt to find your Arduino installation.
Next, the CMakeLists generates the ROS firmware. It does so using the generate generate_ros_firmware function. This function uses a declarative programming style. You describe your function in variables named ${FIRMWARE}_${SUFFIX}. The cpp files are stored in chatter_SRCS. The Arduino's serial port is stored in chatter_PORT. The arduino board type, atmega328, is stored chatter_BOARD. The board type can be any type defined in hardware/arduino/boards.txt of your Arduino installation. Typically this would be uno, mega2560, or atmega328. generate_ros_firmware access these variables and creates your targets. This setup is shown in
Once you set up the variables describing the firmware, it generates the targets for compiling and programming that target. You can have as many targets as you would like in a project. The compile target is always the name of the firmware and the upload target is always the TARGET_NAME-upload. We will be able to compile chatter by entering
make chatter
and program an atmega328 type Arduino like the Duemilanove using
make chatter-upload
Now, copy the above CMakeLists into your package CMakeLists.txt file and then in the package directory run
cmake .
If you have you Arduino installation in a non-standard location, you will receive the errors
-- The arduino sdk path is ARDUINO_SDK_PATH-NOTFOUND -- REQUIRED_VARS (missing: ARDUINO_SDK_PATH ARDUINO_SDK_VERSION)
when you go to generate your Makefile. ARDUINO_SDK_PATH is the path to your Arduino IDE folder. To fix the problem, you can either set the ARDUINO_SDK_PATH before including rosserial.cmake
set(ARDUINO_SDK_PATH <path_to_arduino_installation>) include(${rosserial_arduino_PACKAGE_PATH}/cmake_scripts/rosserial.cmake)
or move the installation to a folder on your application path.
Testing
Connect your Arduino and make sure that the chatter_PORT corresponds to the serial port of your Arduino. Program your Arduino using
make chatter-upload
Now you can use the Arduino! Start up the roscore ,the rosserial_python serial_node.py , and rostopic echo to see it in action.
roscore
rosrun rosserial_python serial_node.py /dev/ttyUSB0
rostopic echo chatter
Additional Info
generate_ros_firmware
The generate_ros_firmware macro takes care of all of the heavy lifting necessary for your rosserial_arduino project. Make sure to define any additional variables that you need.
# - Generate firmware for rosserial_arduino Devices # generate_ros_firmware(TARGET_NAME) # TARGET_NAME - Name of target # Creates a Arduino firmware target. # # The target options can be configured by setting options of # the following format: # ${TARGET_NAME}${SUFFIX} # The following suffixes are availabe: # _SRCS # Sources # _HDRS # Headers # _LIBS # Libraries to linked in # _BOARD # Board name (such as uno, mega2560, atmega328 ...) # _PORT # Serial port, for upload and serial targets [OPTIONAL] # _AFLAGS # Override global Avrdude flags for target # _SERIAL # Serial command for serial target [OPTIONAL] # _NO_AUTOLIBS # Disables Arduino library detection # _NO_DEFAULT_COM # Disables a default communication implementation
Using Arduino Libraries
generate_ros_firmware will automatically link to any Arduino library like Wire or Servo that is found in your Arduino IDE libraries folder. It does not know about libraries in your sketchbook.
Finding rosserial_arduino cmake
rosserial_arduino CMake implementation is found under rosserial_arduino/cmake_scripts. It is based on the arduino-cmake project. You should never need to edit these files. You just need to include them into your CMakeLists.txt.