Use and compile WiringPi with ROS on Raspberry Pi

In this tutorial I’ll show you how to use and compile WiringPi in your ROS Cpp nodes, so you can talk to hardware devices from your ROS application. Make sure to read this introduction to WiringPi before if you don’t know the library well.

The WiringPi library is a great tool for Raspberry Pi (4, 3B+, 3B, and earlier) – whether you want to use simple GPIOs to trigger a LED, communicate via UART, I2C, SPI with some sensors, Arduino boards, and so on.

It would be too bad if you couldn’t use this library within ROS. Fortunately, you can, and with no further suspense, let’s get started!

Write a Cpp node with WiringPi

First make sure that you have correctly installed ROS and the WiringPi library on your Raspberry Pi (if you’re using Ubuntu, a few extra steps will be required for WiringPi).

Now, let’s create a small Cpp node to blink a LED with WiringPi functions.

#include <ros/ros.h>
#include <wiringPi.h>

#define LED_PIN 0 // change pin number here

int main (int argc, char **argv)
{
    ros::init(argc, argv, "test_wiringpi_ros");
    ros::NodeHandle nh;

    wiringPiSetupGpio();
    pinMode(LED_PIN, OUTPUT);
    ROS_INFO("GPIO has been set as OUTPUT.");

    while (ros::ok())
    {
        digitalWrite(LED_PIN, HIGH);
        ROS_INFO("Set GPIO HIGH");
        ros::Duration(1.0).sleep();
        digitalWrite(LED_PIN, LOW);
        ROS_INFO("Set GPIO LOW");
        ros::Duration(1.0).sleep();
    }
}

Nothing really unusual here. To use WiringPi in your node, you just need to include the headers and then use the functions from the library.

Compile a Cpp ROS node with WiringPi

If you add an executable for this node in your CMakeLists.txt like you usually do (with no reference to wiringPi), and compile with catkin_make, you’ll get this error:

[ 50%] Building CXX object robot_test/CMakeFiles/test_wiringpi_ros.dir/src/test_wiringpi_ros.cpp.o
[100%] Linking CXX executable ~/catkin_ws/devel/lib/robot_test/test_wiringpi_ros
CMakeFiles/test_wiringpi_ros.dir/src/test_wiringpi_ros.cpp.o: In function `main':
test_wiringpi_ros.cpp:(.text+0x58): undefined reference to `wiringPiSetupGpio'
test_wiringpi_ros.cpp:(.text+0x60): undefined reference to `pinMode'
test_wiringpi_ros.cpp:(.text+0x166): undefined reference to `digitalWrite'
test_wiringpi_ros.cpp:(.text+0x282): undefined reference to `digitalWrite'
collect2: error: ld returned 1 exit status
robot_test/CMakeFiles/test_wiringpi_ros.dir/build.make:113: recipe for target '~/catkin_ws/devel/lib/robot_test/test_wiringpi_ros' failed
make[2]: *** [~/catkin_ws/devel/lib/robot_test/test_wiringpi_ros] Error 1
CMakeFiles/Makefile2:747: recipe for target 'robot_test/CMakeFiles/test_wiringpi_ros.dir/all' failed
make[1]: *** [robot_test/CMakeFiles/test_wiringpi_ros.dir/all] Error 2
Makefile:138: recipe for target 'all' failed
make: *** [all] Error 2
Invoking "make -j4 -l4" failed

What a lovely error message.

Here is what you need to write in the CMakeLists.txt of your package, so that the node will successfully compile:

add_executable(test_wiringpi_ros src/test_wiringpi_ros.cpp)
target_link_libraries(test_wiringpi_ros ${catkin_LIBRARIES} -lwiringPi)

If you compile a Cpp program using the g++ command line tools, you have to add -lwiringPi to link to the WiringPi library installed on your computer. Here, that’s the same thing: you need to add -lwiringPi in the target_link_libraries() for the executable depending on WiringPi.

You can now compile, and… It works!

[ 50%] Building CXX object robot_test/CMakeFiles/test_wiringpi_ros.dir/src/test_wiringpi_ros.cpp.o
[100%] Linking CXX executable ~/catkin_ws/devel/lib/robot_test/test_wiringpi_ros
[100%] Built target test_wiringpi_ros
$ rosrun your_package test_wiringpi_ros 
[ INFO] [1567083461.247978207]: GPIO has been set as OUTPUT.
[ INFO] [1567083461.248199720]: Set GPIO HIGH
[ INFO] [1567083462.248422755]: Set GPIO LOW
...

Compile a node with WiringPi only on ARM systems

If you want to run the same node on both an ARM board (Raspberry Pi) and an x86 computer (a laptop for example), and if this node includes WiringPi, well you’ll have some compilation problems.

For some reasons, this node could have a different behavior on ARM and non ARM architectures. Or you could decide to simply “disable” all WiringPi related stuff as soon as you’re not on an ARM system.

You’ll have to modify both the code and the CMakeLists.txt.

In your code, add some preprocessor directives to only keep the WiringPi code on ARM:

#include <ros/ros.h>

#ifdef __arm__
#include <wiringPi.h>
#endif

#define LED_PIN 0 // change pin number here

int main (int argc, char **argv)
{
    ros::init(argc, argv, "test_wiringpi_ros");
    ros::NodeHandle nh;

#ifdef __arm__

    wiringPiSetupGpio();
    pinMode(LED_PIN, OUTPUT);
    ROS_INFO("GPIO has been set as OUTPUT.");

    while (ros::ok())
    {
        digitalWrite(LED_PIN, HIGH);
        ROS_INFO("Set GPIO HIGH");
        ros::Duration(1.0).sleep();
        digitalWrite(LED_PIN, LOW);
        ROS_INFO("Set GPIO LOW");
        ros::Duration(1.0).sleep();
    }

#else

    // here you define the behavior of your node on non ARM systems

#endif
}

Note that the code here is not necessarily a practical example of what will happen in your real life projects. But, let’s say you have a device that you want to control either from USB (non ARM) or from GPIOs (ARM), this tip can be very useful for you.

On your CMakeLists.txt:

add_executable(test_wiringpi_ros src/test_wiringpi_ros.cpp)

EXECUTE_PROCESS( COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE )

if (${ARCHITECTURE} MATCHES "arm")
    target_link_libraries(test_wiringpi_ros ${catkin_LIBRARIES} -lwiringPi)
else()
    target_link_libraries(test_wiringpi_ros ${catkin_LIBRARIES})
endif()

You can now compile your node on a non ARM architecture, and it will work!

Leave a Comment