|
1 | | -# socketcan_adapter |
2 | | -Socketcan Driver Library for Linux based PCs and ROS2 nodes |
| 1 | +# SocketCAN Adapter |
3 | 2 |
|
4 | | -# Build |
5 | | -Socketcan adapter can be built with the ros2 ament toolchain. All requirements can be installed via rosdep |
| 3 | +A modular SocketCAN driver library and ROS2 integration for Linux-based systems. This repository provides both a standalone C++ library and ROS2 wrapper for easy integration with robotics applications. |
6 | 4 |
|
7 | | -Install the dependencies! |
| 5 | +## Architecture |
| 6 | + |
| 7 | +This package is split into two complementary components: |
| 8 | + |
| 9 | +### [socketcan_adapter](./socketcan_adapter/README.md) |
| 10 | +Pure C++ SocketCAN library with no ROS dependencies. |
| 11 | +- Core SocketCAN functionality |
| 12 | +- Thread-safe operations |
| 13 | +- Callback-based asynchronous processing |
| 14 | +- Configurable filters and error handling |
| 15 | +- Can be used in non-ROS projects |
| 16 | + |
| 17 | +### [socketcan_adapter_ros](./socketcan_adapter_ros/README.md) |
| 18 | +ROS2 wrapper providing nodes and launch files. |
| 19 | +- ROS2 lifecycle node |
| 20 | +- Integration with `can_msgs` |
| 21 | +- Launch files with parameter support |
| 22 | +- Depends on the core `socketcan_adapter` library |
| 23 | + |
| 24 | +## Quick Start |
| 25 | + |
| 26 | +### Build Both Packages |
8 | 27 |
|
9 | 28 | ```bash |
10 | | -rosdep install -i -y --from-paths socketcan_adapter |
| 29 | +# Install dependencies |
| 30 | +rosdep install -i -y --from-paths . |
| 31 | + |
| 32 | +# Build everything |
| 33 | +colcon build --packages-up-to socketcan_adapter_ros |
11 | 34 | ``` |
12 | 35 |
|
13 | | -Build it! |
| 36 | +### Launch ROS2 CAN Bridge |
14 | 37 |
|
15 | 38 | ```bash |
16 | | -colcon build --packages-up-to socketcan_adapter |
| 39 | +ros2 launch socketcan_adapter_ros socketcan_bridge_launch.py |
17 | 40 | ``` |
18 | 41 |
|
19 | | -# Library |
20 | | -## Classes of Note |
21 | | -### CanFrame |
22 | | -`CanFrame` Class - This class wraps the C-level `can_frame` structure, encapsulating CAN message details like the CAN ID, data, timestamp, and frame type (DATA, ERROR, or REMOTE). By providing a robust API for creating and managing CAN frames, CanFrame simplifies interaction with raw CAN data and offers utilities like ID masking, setting error types, and timestamp management. |
| 42 | +### Use Core Library in C++ |
23 | 43 |
|
24 | | -Example highlights: |
| 44 | +```c++ |
| 45 | +#include "socketcan_adapter/socketcan_adapter.hpp" |
25 | 46 |
|
26 | | -- Flexible constructors for `can_frame` struct and raw data inputs. |
27 | | -- Functions to modify frame type, ID type (standard/extended), and length. |
28 | | -- Helper methods to access CAN frame data, ID, and timestamp. |
| 47 | +using namespace polymath::socketcan; |
29 | 48 |
|
30 | | -Does not implement CanFD yet. |
| 49 | +SocketcanAdapter adapter("can0"); |
| 50 | +adapter.openSocket(); |
| 51 | +// ... see socketcan_adapter README for full example |
| 52 | +``` |
31 | 53 |
|
32 | | -### SocketcanAdapter |
33 | | -`SocketcanAdapter` Class - The `SocketcanAdapter` abstracts and manages socket operations for CAN communication. It initializes and configures the socket, applies filters, and handles CAN frame transmission and reception. The adapter offers error handling, thread-safe operations, and optional callback functions for asynchronous frame and error processing. |
| 54 | +## Requirements |
34 | 55 |
|
35 | | -Key features: |
| 56 | +### System Requirements |
| 57 | +- Linux with SocketCAN support |
| 58 | +- C++17 compatible compiler |
| 59 | +- CMake 3.8+ |
36 | 60 |
|
37 | | -- Configurable receive timeout and threading for reception. |
38 | | -- `setFilters` and setErrorMaskOverwrite to apply CAN filters and error masks. |
39 | | -- A callback-based system for handling received frames and errors asynchronously. |
40 | | -- Supports multiple send and receive methods, including `std::shared_ptr` for efficient memory management. |
41 | | -- Together, `CanFrame` and `SocketcanAdapter` simplify interaction with CAN networks, allowing developers to focus on - high-level application logic instead of low-level socket and data handling. |
| 61 | +### ROS2 Requirements (for ROS package only) |
| 62 | +- ROS2 Humble or later |
| 63 | +- can_msgs package |
42 | 64 |
|
43 | | -## Sample Usage |
| 65 | +## Testing |
44 | 66 |
|
45 | | -```c++ |
46 | | -#include "socketcan_adapter/socketcan_adapter.hpp" |
47 | | -#include "socketcan_adapter/can_frame.hpp" |
48 | | -#include <iostream> |
49 | | -#include <thread> |
50 | | -#include <vector> |
| 67 | +### Core Library Tests |
51 | 68 |
|
52 | | -using namespace polymath::socketcan; |
| 69 | +```bash |
| 70 | +colcon test --packages-select socketcan_adapter |
| 71 | +``` |
53 | 72 |
|
54 | | -int main() { |
55 | | - // Initialize SocketcanAdapter with the CAN interface name (e.g., "can0") |
56 | | - SocketcanAdapter adapter("can0"); |
57 | | - |
58 | | - // Open the CAN socket |
59 | | - if (!adapter.openSocket()) { |
60 | | - std::cerr << "Failed to open CAN socket!" << std::endl; |
61 | | - return -1; |
62 | | - } |
63 | | - |
64 | | - // Step 1: Set up a filter to allow only messages with ID 0x123 |
65 | | - std::vector<struct can_filter> filters = {{0x123, CAN_SFF_MASK}}; |
66 | | - if (auto error = adapter.setFilters(filters)) { |
67 | | - std::cerr << "Error setting filters: " << *error << std::endl; |
68 | | - return -1; |
69 | | - } |
70 | | - |
71 | | - // Step 2: Set up a callback function to handle received CAN frames |
72 | | - adapter.setOnReceiveCallback([](std::unique_ptr<const CanFrame> frame) { |
73 | | - std::cout << "Received CAN frame with ID: " << std::hex << frame->get_id() << std::endl; |
74 | | - auto data = frame->get_data(); |
75 | | - std::cout << "Data: "; |
76 | | - for (const auto& byte : data) { |
77 | | - std::cout << std::hex << static_cast<int>(byte) << " "; |
78 | | - } |
79 | | - std::cout << std::endl; |
80 | | - }); |
81 | | - |
82 | | - // Step 3: Start the reception thread |
83 | | - if (!adapter.startReceptionThread()) { |
84 | | - std::cerr << "Failed to start reception thread!" << std::endl; |
85 | | - adapter.closeSocket(); |
86 | | - return -1; |
87 | | - } |
88 | | - |
89 | | - // Step 4: Prepare a CAN frame to send |
90 | | - canid_t raw_id = 0x123; |
91 | | - std::array<unsigned char, CAN_MAX_DLC> data = {0x11, 0x22, 0x33, 0x44}; |
92 | | - uint64_t timestamp = 0; // Placeholder timestamp |
93 | | - CanFrame frame(raw_id, data, timestamp); |
94 | | - |
95 | | - // Step 5: Send the CAN frame |
96 | | - if (auto error = adapter.send(frame)) { |
97 | | - std::cerr << "Failed to send CAN frame: " << *error << std::endl; |
98 | | - } else { |
99 | | - std::cout << "Sent CAN frame with ID: " << std::hex << raw_id << std::endl; |
100 | | - } |
101 | | - |
102 | | - // Keep the application running for 10 seconds to allow for frame reception |
103 | | - std::this_thread::sleep_for(std::chrono::seconds(10)); |
104 | | - |
105 | | - // Step 5: Clean up - close the socket and stop the reception thread |
106 | | - adapter.joinReceptionThread(); |
107 | | - adapter.closeSocket(); |
108 | | - |
109 | | - return 0; |
110 | | -} |
| 73 | +### Hardware Tests |
| 74 | +Set `CAN_AVAILABLE=1` to enable hardware-dependent tests: |
111 | 75 |
|
| 76 | +```bash |
| 77 | +CAN_AVAILABLE=1 colcon test --packages-select socketcan_adapter |
112 | 78 | ``` |
113 | 79 |
|
114 | | -# ROS2 Node |
115 | | -To make usage even easier, this package comes with a ROS2 node with default settings! |
| 80 | +## Virtual CAN Setup |
116 | 81 |
|
117 | | -## Launch |
| 82 | +For testing without hardware: |
118 | 83 |
|
119 | 84 | ```bash |
120 | | -ros2 launch socketcan_adapter socketcan_bridge_launch.py |
| 85 | +sudo modprobe vcan |
| 86 | +sudo ip link add dev vcan0 type vcan |
| 87 | +sudo ip link set up vcan0 |
121 | 88 | ``` |
122 | 89 |
|
123 | | -launch args: |
124 | | -- `can_interface`: can interface to connect to (default: 0) |
125 | | -- `can_error_mask`: can error mask (default: 0x1FFFFFFF aka everything allowed) |
126 | | -- `can_filter_list`: can filters (default: []) |
127 | | -- `join_filters`: use joining logic for filters (default: false) |
128 | | -- `auto_configure`: automatically configure the lifecycle node |
129 | | -- `auto_activate`: automatically activate the lifecycle node post configuration |
| 90 | +## License |
| 91 | + |
| 92 | +Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full license text. |
| 93 | + |
| 94 | +## Authors & Maintainers |
| 95 | + |
| 96 | +- **Zeerek Ahmad** - Original author - zeerekahmad@hotmail.com |
| 97 | +- **Polymath Robotics Engineering Team** - Maintainers - engineering@polymathrobotics.com |
0 commit comments