Creating a new device driver
Creating a new device driver to work with the ethercat_driver_ros2
is fairly easy. You should do this if you need to create a driver for a specific device or a specific device profile that is not yet supported. If you create a driver for a device profile we are happy to integrate the package into this repository - simply create a PR.
What you need to do
To create a dedicated driver for an EtherCAT module, you need to create a new package containing the module driver plugin and register it so that it can be called by the ethercat_driver_ros2
.
How to do it
Create a package
Create a new package using the standard ros2 pkg commands. Make sure you add the following dependencies:
ethercat_interface
pluginlib
Create your plugin class
To be loaded by the ethercat_driver_ros2
, the new module plugin needs to inherit from the EcSlave
class and implement some of its virtual functions. To do so create in your package src
folder a new file my_ec_device_driver.cpp
:
#include "ethercat_interface/ec_slave.hpp"
namespace ethercat_plugins
{
class MyEcDeviceDriver : public ethercat_interface::EcSlave
{
public:
MyEcDeviceDriver()
: EcSlave(<vendor_id>, <product_id>) {}
virtual ~MyEcDeviceDriver() {}
// data processing method that will be called cyclically for every channel
// the index corresponds to the values registered in domains_
virtual void processData(size_t index, uint8_t * domain_address)
{
// Your process data logic goes here
}
virtual const ec_sync_info_t * syncs() {return &syncs_[0];}
virtual size_t syncSize()
{
return sizeof(syncs_) / sizeof(ec_sync_info_t);
}
virtual const ec_pdo_entry_info_t * channels()
{
return channels_;
}
virtual void domains(DomainMap & domains) const
{
domains = domains_;
}
// configure the slave module with urdf parameters
// and link ros2_control command and stat interface
virtual bool setupSlave(
std::unordered_map<std::string, std::string> slave_paramters,
std::vector<double> * state_interface,
std::vector<double> * command_interface)
{
state_interface_ptr_ = state_interface;
command_interface_ptr_ = command_interface;
paramters_ = slave_paramters;
// Your module setup logic goes here
return true;
}
private:
// configure module channels
ec_pdo_entry_info_t channels_[X] = {
{<index>, <sub_index>, <type>},
};
// configure module pdos
ec_pdo_info_t pdos_[X] = {
{<index>, <nbr_channels>, <channel_ptr>},
};
// configure module syncs
ec_sync_info_t syncs_[X] = {
{<index>, <type>, <nbr_pdos>, <pdos_ptr>, <watchdog>},
{0xff}
};
// configure module domain
DomainMap domains_ = {
{0, {0}} // index of channels that should call processData()
};
};
} // namespace ethercat_plugins
#include <pluginlib/class_list_macros.hpp>
PLUGINLIB_EXPORT_CLASS(ethercat_plugins::MyEcDeviceDriver, ethercat_interface::EcSlave)
Export your plugin
In the package root directory create a plugin description file ethercat_plugins.xml
:
<library path="ethercat_plugins">
<class name="ethercat_plugins/MyEcDeviceDriver"
type="ethercat_plugins::MyEcDeviceDriver"
base_class_type="ethercat_interface::EcSlave">
<description>Description of the device driver.</description>
</class>
</library>
Modify your CMakeLists.txt
file so that it looks like this:
cmake_minimum_required(VERSION 3.8)
project(<your_package>)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_ros REQUIRED)
find_package(ethercat_interface REQUIRED)
find_package(pluginlib REQUIRED)
file(GLOB_RECURSE PLUGINS_SRC src/*.cpp)
add_library(${PROJECT_NAME} ${PLUGINS_SRC})
target_compile_features(${PROJECT_NAME} PUBLIC c_std_99 cxx_std_17) # Require C99 and C++17
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
ament_target_dependencies(
${PROJECT_NAME}
"ethercat_interface"
"pluginlib"
)
pluginlib_export_plugin_description_file(ethercat_interface ethercat_plugins.xml)
install(
DIRECTORY include/
DESTINATION include
)
install(
TARGETS ${PROJECT_NAME}
EXPORT export_${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
ament_export_include_directories(
include
)
ament_export_libraries(
ethercat_plugins
)
ament_export_targets(
export_${PROJECT_NAME}
)
ament_package()