Use and compile WiringPi with ROS on Raspberry Pi

[NOTE] The WiringPi library is not supported anymore by its initial author, however there is a fork on GitHub where the open source community is continuing to provide support updates. I am leaving this tutorial as it was first published, because it might still be useful, but be aware that some information might not be correct. [/NOTE]

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.


You are learning ROS?

Check out ROS For Beginners and learn ROS step by step.


#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!

Did you find this tutorial useful?

Do you want to learn how to program with ROS?

If yes, this course is for you:

ROS For Beginners - A Step By Step Course

>> ROS For Beginners - A Step By Step Course <<