### Install PyDroneCAN using PIP Source: https://dronecan.github.io/Implementations/Pydronecan/Tutorials/1 Instructions for installing the PyDroneCAN library using the Python Package Installer (PIP). This covers the standard installation command and an alternative for environments where the 'pip' executable might not be directly available or when specific Python versions need to be targeted (e.g., Python 3 with 'pip3'). ```Shell pip install dronecan ``` ```Shell python -m pip install dronecan ``` -------------------------------- ### UAVCAN Service Client with C++03 MethodBinder for Callbacks Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/4 This comprehensive example demonstrates how to implement a UAVCAN service client in C++03, specifically addressing the lack of modern C++ features like lambdas and `std::function`. It utilizes `uavcan::MethodBinder` for managing service call callbacks and showcases node initialization, service request setup, client instantiation, and asynchronous service call handling within a class structure. The `main` function provides an example of how to instantiate and run this client. ```C++ #include #include #include #include extern uavcan::ICanDriver& getCanDriver(); extern uavcan::ISystemClock& getSystemClock(); using uavcan::protocol::file::BeginFirmwareUpdate; /** * This class demonstrates how to use uavcan::MethodBinder with service clients in C++03. * In C++11 and newer standards it is recommended to use lambdas and std::function<> instead, as this approach * would be much easier to implement and to understand. */ class Node { static const unsigned NodeMemoryPoolSize = 16384; uavcan::Node node_; /* * Instantiation of MethodBinder */ typedef uavcan::MethodBinder&) const> BeginFirmwareUpdateCallbackBinder; void beginFirmwareUpdateCallback(const uavcan::ServiceCallResult& res) const { if (res.isSuccessful()) { std::cout << res << std::endl; } else { std::cerr << "Service call to node " << static_cast(res.getCallID().server_node_id.get()) << " has failed" << std::endl; } } public: Node(uavcan::NodeID self_node_id, const std::string& self_node_name) : node_(getCanDriver(), getSystemClock()) { node_.setNodeID(self_node_id); node_.setName(self_node_name.c_str()); } void start() { const int start_res = node_.start(); if (start_res < 0) { throw std::runtime_error("Failed to start the node: " + std::to_string(start_res)); } } void execute(uavcan::NodeID server_node_id) { /* * Initializing the request structure */ BeginFirmwareUpdate::Request request; request.image_file_remote_path.path = "/foo/bar"; /* * Initializing the client object */ uavcan::ServiceClient client(node_); client.setCallback(BeginFirmwareUpdateCallbackBinder(this, &Node::beginFirmwareUpdateCallback)); /* * Calling */ const int call_res = client.call(server_node_id, request); if (call_res < 0) { throw std::runtime_error("Unable to perform service call: " + std::to_string(call_res)); } /* * Spinning the node until the call is completed */ node_.setModeOperational(); while (client.hasPendingCalls()) // Whether the call has completed (doesn't matter successfully or not) { const int res = node_.spin(uavcan::MonotonicDuration::fromMSec(10)); if (res < 0) { std::cerr << "Transient failure: " << res << std::endl; } } } }; int main(int argc, const char** argv) { if (argc < 3) { std::cerr << "Usage: " << argv[0] << " " << std::endl; return 1; } const uavcan::NodeID self_node_id = std::stoi(argv[1]); const uavcan::NodeID server_node_id = std::stoi(argv[2]); Node node(self_node_id, "org.uavcan.tutorial.clientcpp03"); node.start(); node.execute(server_node_id); return 0; } ``` -------------------------------- ### Install PySerial for SLCAN Backend Source: https://dronecan.github.io/Implementations/Pydronecan/Tutorials/1 Installs the PySerial library, which is a prerequisite for using the SLCAN backend to communicate with CAN adapters over a serial port (e.g., USB CDC ACM virtual serial port). ```bash pip install pyserial ``` -------------------------------- ### Start UAVCAN Firmware Update Service Server Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/13 Sets up and starts a service server for `uavcan::protocol::file::BeginFirmwareUpdate` requests. The server prints the incoming request, then responds with a generic `ERROR_UNKNOWN` and a custom optional error message. Error handling is included to catch and report server startup failures. ```C++ using uavcan::protocol::file::BeginFirmwareUpdate; uavcan::ServiceServer srv(node); int srv_start_res = srv.start( [&](const uavcan::ReceivedDataStructure& req, BeginFirmwareUpdate::Response& rsp) { std::cout << req << std::endl; rsp.error = rsp.ERROR_UNKNOWN; rsp.optional_error_message = "I am filtered"; }); if (srv_start_res < 0) { throw std::runtime_error("Failed to start the server; error: " + std::to_string(srv_start_res)); } ``` -------------------------------- ### Initialize and Start DroneCAN Node in C++ Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/11 This snippet demonstrates how to initialize a DroneCAN node, set its ID and name, and start it. It includes error handling for the node startup process, throwing a runtime error if initialization fails. ```C++ uavcan::Node<16384> node(getCanDriver(), getSystemClock()); node.setNodeID(self_node_id); node.setName("org.uavcan.tutorial.updater"); const int node_start_res = node.start(); if (node_start_res < 0) { throw std::runtime_error("Failed to start the node; error: " + std::to_string(node_start_res)); } ``` -------------------------------- ### Install DroneCAN GUI Tool Dependencies and Application on Debian-based Linux Source: https://dronecan.github.io/GUI_Tool/Overview This script installs the required system packages and Python libraries for the DroneCAN GUI tool on Debian-based Linux distributions (e.g., Ubuntu, Mint). It uses `apt-get` to install Python 3 development tools, NumPy, PyQt5, and Git, followed by `pip3` to install the `dronecan` library and the GUI tool directly from its GitHub repository. ```Shell sudo apt-get install -y python3-pip python3-setuptools python3-wheel sudo apt-get install -y python3-numpy python3-pyqt5 python3-pyqt5.qtsvg git-core sudo pip3 install dronecan sudo pip3 install git+https://github.com/DroneCAN/gui_tool@master ``` -------------------------------- ### Install PyDroneCAN Library Source: https://dronecan.github.io/Implementations/Pydronecan/Tutorials This snippet provides the command to install the PyDroneCAN library using pip, the standard package installer for Python. This step is essential for setting up the development environment and accessing PyDroneCAN functionalities. ```Shell pip install dronecan ``` -------------------------------- ### UAVCAN C++ Client for Firmware Update Service Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/4 This C++ code demonstrates a UAVCAN client application that initiates a firmware update service call. It shows how to set up a UAVCAN node, initialize a `ServiceClient` for `uavcan.protocol.file.BeginFirmwareUpdate`, configure a callback for asynchronous response handling, and manage service call parameters like timeouts and priorities. The example also illustrates how to make a service request and provides methods for managing concurrent calls. ```C++ #include #include #include /* * This example uses the service type uavcan.protocol.file.BeginFirmwareUpdate. */ #include extern uavcan::ICanDriver& getCanDriver(); extern uavcan::ISystemClock& getSystemClock(); constexpr unsigned NodeMemoryPoolSize = 16384; typedef uavcan::Node Node; static Node& getNode() { static Node node(getCanDriver(), getSystemClock()); return node; } int main(int argc, const char** argv) { if (argc < 3) { std::cerr << "Usage: " << argv[0] << " " << std::endl; return 1; } const uavcan::NodeID self_node_id = std::stoi(argv[1]); const uavcan::NodeID server_node_id = std::stoi(argv[2]); auto& node = getNode(); node.setNodeID(self_node_id); node.setName("org.uavcan.tutorial.client"); const int node_start_res = node.start(); if (node_start_res < 0) { throw std::runtime_error("Failed to start the node; error: " + std::to_string(node_start_res)); } /* * Initializing the client. Remember that client objects are noncopyable. * Note that calling client.init() is not necessary - the object can be initialized ad hoc during the first call. */ using uavcan::protocol::file::BeginFirmwareUpdate; uavcan::ServiceClient client(node); const int client_init_res = client.init(); if (client_init_res < 0) { throw std::runtime_error("Failed to init the client; error: " + std::to_string(client_init_res)); } /* * Setting the callback. * This callback will be executed when the call is completed. * Note that the callback will ALWAYS be called even if the service call has timed out; this guarantee * allows to simplify error handling in the application. */ client.setCallback([](const uavcan::ServiceCallResult& call_result) { if (call_result.isSuccessful()) // Whether the call was successful, i.e. whether the response was received { // The result can be directly streamed; the output will be formatted in human-readable YAML. std::cout << call_result << std::endl; } else { std::cerr << "Service call to node " << static_cast(call_result.getCallID().server_node_id.get()) << " has failed" << std::endl; } }); /* * C++03 WARNING * The code above will not compile in C++03, because it uses a lambda function. * In order to compile the code in C++03, move the code from the lambda to a standalone static function. * Use uavcan::MethodBinder<> to invoke member functions. */ /* * Service call timeout can be overridden if needed, though it's not recommended. */ client.setRequestTimeout(uavcan::MonotonicDuration::fromMSec(200)); /* * It is possible to adjust priority of the outgoing service request transfers. * According to the specification, the services are supposed to use the same priority for response transfers. * Default priority is medium, which is 16. */ client.setPriority(uavcan::TransferPriority::OneHigherThanLowest); /* * Calling the remote service. * Generated service data types have two nested types: * T::Request - request data structure * T::Response - response data structure * For the service data structure, it is not possible to instantiate T itself, nor does it make any sense. */ BeginFirmwareUpdate::Request request; request.image_file_remote_path.path = "/foo/bar"; /* * It is possible to perform multiple concurrent calls using the same client object. * The class ServiceClient provides the following methods that allow to control execution of each call: * * int call(NodeID server_node_id, const RequestType& request) * Initiate a new non-blocking call. * * int call(NodeID server_node_id, const RequestType& request, ServiceCallID& out_call_id) * Initiate a new non-blocking call and return its ServiceCallID descriptor by reference. * The descriptor allows to query the progress of the call or cancel it later. * * void cancelCall(ServiceCallID call_id) * Cancel a specific call using its descriptor. * * void cancelAllCalls() * Cancel all calls. * * bool hasPendingCallToServer(NodeID server_node_id) const * Whether the client object has pending calls to the given server at the moment. * * unsigned getNumPendingCalls() const * Returns the total number of pending calls at the moment. * * bool hasPendingCalls() const * Whether the client object has any pending calls at the moment. */ ``` -------------------------------- ### Example Console Output for DroneCAN Node ID Allocation Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/10 This snippet shows a typical console output when running the DroneCAN dynamic node ID allocatee example. It illustrates messages related to node ID preference, hardware version details, the allocation progress, and the final assigned dynamic node ID. ```text No preference for a node ID value. To assign a preferred node ID, pass it as a command line argument: ./allocatee major: 0 minor: 0 unique_id: [68, 192, 139, 99, 94, 5, 244, 188, 76, 138, 148, 82, 94, 146, 130, 178] certificate_of_authenticity: "" Allocation is in progress................................................... Dynamic node ID 125 has been allocated by the allocator with node ID 1 ``` -------------------------------- ### UAVCAN C++ Node and KeyValue Publisher Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/3 This C++ code demonstrates the creation of a UAVCAN node and a publisher for `uavcan.protocol.debug.KeyValue` messages. It initializes the node with a specified ID, sets its name, and then configures a publisher. The example shows how to set transmission timeout and priority for outgoing messages. It continuously spins the node and broadcasts randomly generated key-value pairs, illustrating message construction and publication. The code requires the UAVCAN library and its dependencies. ```cpp #include #include #include #include /* * We're going to use messages of type uavcan.protocol.debug.KeyValue, so the appropriate header must be included. * Given a data type named X, the header file name would be: * X.replace('.', '/') + ".hpp" */ #include // uavcan.protocol.debug.KeyValue extern uavcan::ICanDriver& getCanDriver(); extern uavcan::ISystemClock& getSystemClock(); constexpr unsigned NodeMemoryPoolSize = 16348; typedef uavcan::Node Node; static Node& getNode() { static Node node(getCanDriver(), getSystemClock()); return node; } int main(int argc, const char** argv) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " " << std::endl; return 1; } const int self_node_id = std::stoi(argv[1]); auto& node = getNode(); node.setNodeID(self_node_id); node.setName("org.uavcan.tutorial.publisher"); /* * Dependent objects (e.g. publishers, subscribers, servers, callers, timers, ...) can be initialized only * if the node is running. Note that all dependent objects always keep a reference to the node object. */ const int node_start_res = node.start(); if (node_start_res < 0) { throw std::runtime_error("Failed to start the node; error: " + std::to_string(node_start_res)); } /* * Create the publisher object to broadcast standard key-value messages of type uavcan.protocol.debug.KeyValue. * Keep in mind that most classes defined in the library are not copyable; attempt to copy objects of * such classes will result in compilation failure. * A publishing node won't see its own messages. */ uavcan::Publisher kv_pub(node); const int kv_pub_init_res = kv_pub.init(); if (kv_pub_init_res < 0) { throw std::runtime_error("Failed to start the publisher; error: " + std::to_string(kv_pub_init_res)); } /* * This would fail because most of the objects - including publishers - are noncopyable. * The error message may look like this: * "error: ‘uavcan::Noncopyable::Noncopyable(const uavcan::Noncopyable&)’ is private" */ // auto pub_copy = kv_pub; // Don't try this at home. /* * TX timeout can be overridden if needed. * Default value should be OK for most use cases. */ kv_pub.setTxTimeout(uavcan::MonotonicDuration::fromMSec(1000)); /* * Priority of outgoing tranfers can be changed as follows. * Default priority is 16 (medium). */ kv_pub.setPriority(uavcan::TransferPriority::MiddleLower); /* * Running the node. */ node.setModeOperational(); while (true) { /* * Spinning for 1 second. * The method spin() may return earlier if an error occurs (e.g. driver failure). * All error codes are listed in the header uavcan/error.hpp. */ const int spin_res = node.spin(uavcan::MonotonicDuration::fromMSec(1000)); if (spin_res < 0) { std::cerr << "Transient failure: " << spin_res << std::endl; } /* * Publishing a random value using the publisher created above. * All message types have zero-initializing default constructors. * Relevant usage info for every data type is provided in its DSDL definition. */ uavcan::protocol::debug::KeyValue kv_msg; // Always zero initialized kv_msg.value = std::rand() / float(RAND_MAX); /* * Arrays in DSDL types are quite extensive in the sense that they can be static, * or dynamic (no heap needed - all memory is pre-allocated), or they can emulate std::string. * The last one is called string-like arrays. * ASCII strings can be directly assigned or appended to string-like arrays. * For more info, please read the documentation for the class uavcan::Array<>.- */ kv_msg.key = "a"; // "a" kv_msg.key += "b"; // "ab" kv_msg.key += "c"; // "abc" /* * Publishing the message. */ const int pub_res = kv_pub.broadcast(kv_msg); if (pub_res < 0) { std::cerr << "KV publication failure: " << pub_res << std::endl; } } } ``` -------------------------------- ### C++ libuavcan Airspeed Subscriber for DroneCAN Testing Source: https://dronecan.github.io/Examples/Simple_sensor_node This C++ program demonstrates how to subscribe to DroneCAN airspeed messages using the libuavcan library. It initializes a DroneCAN node, sets up a subscriber for 'TrueAirspeed' messages, and prints received data to standard output. The example is designed for testing alternative DroneCAN stack implementations against the reference implementation. ```C++ /* * This program subscribes to airspeed messages using libuavcan, and prints them into stdout in YAML format. * It can be used to test alternative implementations of the DroneCAN stack against the reference implementation. * * GCC invocation command: * g++ libuavcan_airspeed_subscriber.cpp -std=c++11 -lrt -luavcan * * Author: Pavel Kirienko * License: CC0, no copyright reserved * Language: C++11 */ #include #include #include #include uavcan_linux::NodePtr initNode(const std::vector& ifaces, uavcan::NodeID nid, const std::string& name) { auto node = uavcan_linux::makeNode(ifaces); node->setNodeID(nid); node->setName(name.c_str()); if (node->start() < 0) { throw std::runtime_error("Failed to start DroneCAN node"); } node->setModeOperational(); return node; } template static void printMessage(const uavcan::ReceivedDataStructure& msg) { std::cout << "[" << DataType::getDataTypeFullName() << "]\n" << msg << "\n---" << std::endl; } template static std::shared_ptr> makePrintingSubscriber(const uavcan_linux::NodePtr& node) { return node->makeSubscriber(&printMessage); } static void runForever(const uavcan_linux::NodePtr& node) { auto sub_true_airspeed = makePrintingSubscriber(node); while (true) { const int res = node->spin(uavcan::MonotonicDuration::getInfinite()); if (res < 0) { node->logError("spin", "Error %*", res); } } } int main(int argc, const char** argv) { if (argc < 3) { std::cout << "Usage:\n\t" << argv[0] << " [can-iface-name-N...]" << std::endl; return 1; } const int self_node_id = std::stoi(argv[1]); std::vector iface_names(argv + 2, argv + argc); uavcan_linux::NodePtr node = initNode(iface_names, self_node_id, "org.uavcan.example.airspeed_subscriber"); runForever(node); return 0; } ``` -------------------------------- ### Implement Periodic and One-Shot Timers with libuavcan C++11 Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/5 This C++11 example demonstrates how to initialize a DroneCAN node and implement both periodic and one-shot timers using the `uavcan::Timer` class. It shows how to set callbacks, start timers with specific durations or deadlines, and handle timer events, including logging and stopping timers. The example requires C++11 or newer and uses `libuavcan` for CAN communication. ```C++ #include #include #include extern uavcan::ICanDriver& getCanDriver(); extern uavcan::ISystemClock& getSystemClock(); constexpr unsigned NodeMemoryPoolSize = 16384; typedef uavcan::Node Node; static Node& getNode() { static Node node(getCanDriver(), getSystemClock()); return node; } int main(int argc, const char** argv) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " " << std::endl; return 1; } const int self_node_id = std::stoi(argv[1]); auto& node = getNode(); node.setNodeID(self_node_id); node.setName("org.uavcan.tutorial.timers_cpp11"); const int node_start_res = node.start(); if (node_start_res < 0) { throw std::runtime_error("Failed to start the node; error: " + std::to_string(node_start_res)); } /* * Creating timers. * Timers are objects that instruct the libuavcan core to pass control to their callbacks either periodically * at specified interval, or once at some specific time point in the future. * Note that timer objects are noncopyable. * * A timer callback accepts a reference to an object of type uavcan::TimerEvent, which contains two fields: * - The time when the callback was expected to be invoked. * - The actual time when the callback was invoked. * * Timers do not require initialization and never fail (because of the very simple logic). * * Note that periodic timers do not accumulate phase error over time. */ uavcan::Timer periodic_timer(node); uavcan::Timer one_shot_timer(node); periodic_timer.setCallback([&](const uavcan::TimerEvent& event) { node.logInfo("Periodic Timer", "scheduled_time: %*, real_time: %*", event.scheduled_time.toMSec(), event.real_time.toMSec()); // Timers can be checked whether there are pending events if (one_shot_timer.isRunning()) { node.logError("Periodic Timer", "Someone started the one-shot timer! Period: %*", one_shot_timer.getPeriod().toMSec()); one_shot_timer.stop(); // And stopped like that } /* * Restart the second timer for one shot. It will be stopped automatically after the first event. * There are two ways to generate a one-shot timer event: * - at a specified time point (absolute) - use the method startOneShotWithDeadline(); * - after a specified timeout (relative) - use the method startOneShotWithDelay(). * Here we restart it in absolute mode. */ auto one_shot_deadline = event.scheduled_time + uavcan::MonotonicDuration::fromMSec(200); one_shot_timer.startOneShotWithDeadline(one_shot_deadline); }); one_shot_timer.setCallback([&](const uavcan::TimerEvent& event) { node.logInfo("One-Shot Timer", "scheduled_time: %*, real_time: %*", event.scheduled_time.toMSec(), event.real_time.toMSec()); }); /* * Starting the timer at 1 Hz. * Start cannot fail. */ periodic_timer.startPeriodic(uavcan::MonotonicDuration::fromMSec(1000)); /* * Node loop. */ node.getLogger().setLevel(uavcan::protocol::debug::LogLevel::DEBUG); node.setModeOperational(); while (true) { const int res = node.spin(uavcan::MonotonicDuration::fromMSec(1000)); if (res < 0) { std::cerr << "Transient failure: " << res << std::endl; } } } ``` -------------------------------- ### Python DroneCAN Node Initialization and Message Dumping Source: https://dronecan.github.io/Implementations/Pydronecan/Examples/Dump_All_Messages This Python script demonstrates how to initialize a DroneCAN node, set up a node monitor to track online devices, optionally start a Dynamic Node ID (DNA) allocator server, and register a handler to print all received DroneCAN messages to the console in human-readable YAML format. The script runs the node indefinitely until interrupted. ```Python # Initializing a node monitor, so we can see what nodes are online node_monitor = dronecan.app.node_monitor.NodeMonitor(node) if args.dna_server: # optionally start a DNA server dynamic_node_id_allocator = dronecan.app.dynamic_node_id.CentralizedServer(node, node_monitor) # callback for printing all messages in human-readable YAML format. node.add_handler(None, lambda msg: print(dronecan.to_yaml(msg))) # Running the node until the application is terminated or until first error. try: node.spin() except KeyboardInterrupt: pass ``` -------------------------------- ### UAVCAN Node Main Loop Example (C) Source: https://dronecan.github.io/Examples/Simple_sensor_node The `main` function serves as the entry point for a UAVCAN node application. It parses command-line arguments for the node ID and CAN interface name, initializes the CAN bus, and then enters an infinite loop. Within this loop, it periodically calls `compute_true_airspeed` and `publish_true_airspeed`, updating the node's health status based on publication results, and also publishes the node's status. ```C int main(int argc, char** argv) { /* * Node initialization */ if (argc < 3) { puts("Args: "); return 1; } uavcan_node_id = (uint8_t)atoi(argv[1]); if (uavcan_node_id < 1 || uavcan_node_id > 127) { printf("%i is not a valid node ID\n", (int)uavcan_node_id); return 1; } if (can_init(argv[2]) < 0) { printf("Failed to open iface %s\n", argv[2]); return 1; } /* * Main loop */ enum node_health health = HEALTH_OK; for (;;) { float airspeed = 0.0F; float airspeed_variance = 0.0F; const int airspeed_computation_result = compute_true_airspeed(&airspeed, &airspeed_variance); if (airspeed_computation_result == 0) { const int publication_result = publish_true_airspeed(airspeed, airspeed_variance); health = (publication_result < 0) ? HEALTH_ERROR : HEALTH_OK; } else { health = HEALTH_ERROR; } const uint16_t vendor_specific_status_code = rand(); // Can be used to report vendor-specific status info (void)publish_node_status(health, MODE_OPERATIONAL, vendor_specific_status_code); usleep(500000); } } ``` -------------------------------- ### CMake Build Script for UAVCAN Applications Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/3 This CMake script defines the build process for UAVCAN-based applications, including a publisher and two subscriber examples (one for C++03 compatibility). It sets minimum CMake version requirements, finds the UAVCAN library, configures C++ compiler flags (e.g., C++11 standard), and links necessary libraries like `uavcan` and `rt` for real-time operations. It assumes platform-specific functions are provided externally. ```CMake cmake_minimum_required(VERSION 2.8) project(tutorial_project) find_library(UAVCAN_LIB uavcan REQUIRED) set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -std=c++11") add_executable(publisher publisher.cpp ${CMAKE_SOURCE_DIR}/../2._Node_initialization_and_startup/platform_linux.cpp) target_link_libraries(publisher ${UAVCAN_LIB} rt) add_executable(subscriber subscriber.cpp ${CMAKE_SOURCE_DIR}/../2._Node_initialization_and_startup/platform_linux.cpp) target_link_libraries(subscriber ${UAVCAN_LIB} rt) add_executable(subscriber_cpp03 subscriber_cpp03.cpp ${CMAKE_SOURCE_DIR}/../2._Node_initialization_and_startup/platform_linux.cpp) target_link_libraries(subscriber_cpp03 ${UAVCAN_LIB} rt) ``` -------------------------------- ### Publishing Periodic Throttle Setpoints in DroneCAN GUI Console Source: https://dronecan.github.io/GUI_Tool/User_guide This Python example demonstrates how to set up a periodic task using the `periodic()` convenience function in the DroneCAN GUI Tool's console. It calculates sine and cosine based setpoints and broadcasts a `uavcan.equipment.esc.RawCommand` message every 0.05 seconds. This is suitable for continuous data publishing without blocking the console, ensuring efficient background operation. ```Python import math, time def publish_throttle_setpoint(): sp_sin = int(512 * (math.sin(time.time()) + 2)) sp_cos = int(512 * (math.cos(time.time()) + 2)) message = uavcan.equipment.esc.RawCommand(cmd=[sp_sin, sp_cos, sp_sin, sp_cos]) broadcast(message) periodic(0.05, publish_throttle_setpoint) ``` -------------------------------- ### C++ UAVCAN Node Initialization and Configuration Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/10 Demonstrates how to initialize a UAVCAN node, set its ID, name, and hardware version using a unique identifier. It also shows how to start the node and includes error handling for potential startup failures. ```C++ /* * Configuring the node. */ uavcan::Node<16384> node(getCanDriver(), getSystemClock()); node.setNodeID(self_node_id); node.setName(NodeName.c_str()); const auto unique_id = getUniqueID(self_node_id); // Using the node ID as instance ID uavcan::protocol::HardwareVersion hwver; std::copy(unique_id.begin(), unique_id.end(), hwver.unique_id.begin()); std::cout << hwver << std::endl; // Printing to stdout to show the values node.setHardwareVersion(hwver); // Copying the value to the node's internals const int node_start_res = node.start(); if (node_start_res < 0) { throw std::runtime_error("Failed to start the node; error: " + std::to_string(node_start_res)); } ``` -------------------------------- ### Build DroneCAN C++03 and C++11 Examples with CMake Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/5 This CMake script configures the build process for both the C++03 and C++11 versions of the DroneCAN timer application. It defines the minimum CMake version, sets the project name, finds the required UAVCAN library, and specifies compiler flags. The script then adds executables for both `node_cpp11` and `node_cpp03`, linking them against the UAVCAN library and the `rt` (real-time) library. ```CMake cmake_minimum_required(VERSION 2.8) project(tutorial_project) find_library(UAVCAN_LIB uavcan REQUIRED) set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -std=c++11") # Make sure to provide correct path to 'platform_linux.cpp'! See earlier tutorials for more info. add_executable(node_cpp11 node_cpp11.cpp ${CMAKE_SOURCE_DIR}/../2._Node_initialization_and_startup/platform_linux.cpp) target_link_libraries(node_cpp11 ${UAVCAN_LIB} rt) add_executable(node_cpp03 node_cpp03.cpp ${CMAKE_SOURCE_DIR}/../2._Node_initialization_and_startup/platform_linux.cpp) target_link_libraries(node_cpp03 ${UAVCAN_LIB} rt) ``` -------------------------------- ### Start UAVCAN Airspeed Data Subscriber Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/13 Initializes and starts a subscriber for `uavcan::equipment::air_data::TrueAirspeed` messages. It prints the received message to standard output and includes error handling for subscriber startup failure, throwing a `std::runtime_error` if the operation fails. ```C++ int airspd_sub_sub_start_res = airspd_sub.start([&](const uavcan::equipment::air_data::TrueAirspeed& msg) { std::cout << msg << std::endl; }); if (airspd_sub_sub_start_res < 0) { throw std::runtime_error("Failed to start the key/value subscriber; error: " + std::to_string(airspd_sub_sub_start_res)); } ``` -------------------------------- ### UAVCAN Subscriber Implementation for C++03 Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/3 This C++ code demonstrates how to implement a UAVCAN subscriber in environments constrained to older C++ standards (prior to C++11). It utilizes `uavcan::MethodBinder` to associate subscriber callbacks with class member functions, providing a workaround for the absence of modern C++ features like lambdas and `std::function`. The example sets up a UAVCAN node, subscribes to `LogMessage` and `KeyValue` topics, and processes incoming data. ```C++ #include #include #include #include #include #include extern uavcan::ICanDriver& getCanDriver(); extern uavcan::ISystemClock& getSystemClock(); /** * This class demonstrates how to use uavcan::MethodBinder with subscriber objects in C++03. * In C++11 and newer standards it is recommended to use lambdas and std::function<> instead, as this approach * would be much easier to implement and to understand. */ class Node { static const unsigned NodeMemoryPoolSize = 16384; uavcan::Node node_; /* * Instantiations of uavcan::MethodBinder<> */ typedef uavcan::MethodBinder LogMessageCallbackBinder; typedef uavcan::MethodBinder&) const> KeyValueCallbackBinder; uavcan::Subscriber log_sub_; uavcan::Subscriber kv_sub_; void logMessageCallback(const uavcan::protocol::debug::LogMessage& msg) const { std::cout << "Log message:\n" << msg << std::endl; } void keyValueCallback(const uavcan::ReceivedDataStructure& msg) const { std::cout << "KV message:\n" << msg << std::endl; } public: Node(uavcan::NodeID self_node_id, const std::string& self_node_name) : node_(getCanDriver(), getSystemClock()), log_sub_(node_), kv_sub_(node_) { node_.setNodeID(self_node_id); node_.setName(self_node_name.c_str()); } void run() { const int start_res = node_.start(); if (start_res < 0) { throw std::runtime_error("Failed to start the node: " + std::to_string(start_res)); } const int log_sub_start_res = log_sub_.start(LogMessageCallbackBinder(this, &Node::logMessageCallback)); if (log_sub_start_res < 0) { throw std::runtime_error("Failed to start the log subscriber; error: " + std::to_string(log_sub_start_res)); } const int kv_sub_start_res = kv_sub_.start(KeyValueCallbackBinder(this, &Node::keyValueCallback)); if (kv_sub_start_res < 0) { throw std::runtime_error("Failed to start the KV subscriber; error: " + std::to_string(kv_sub_start_res)); } node_.setModeOperational(); while (true) { const int res = node_.spin(uavcan::MonotonicDuration::getInfinite()); if (res < 0) { std::cerr << "Transient failure: " << res << std::endl; } } } }; int main(int argc, const char** argv) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " " << std::endl; return 1; } const int self_node_id = std::stoi(argv[1]); Node node(self_node_id, "org.uavcan.tutorial.subscriber_cpp03"); node.run(); } ``` -------------------------------- ### Initialize and Start DroneCAN FileServer in C++ Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/11 This code initializes a DroneCAN file server using `uavcan_posix::BasicFileServerBackend`. The file server is responsible for serving firmware images to nodes during an update process. Error handling is included for the server's startup. ```C++ uavcan_posix::BasicFileServerBackend file_server_backend(node); uavcan::FileServer file_server(node, file_server_backend); const int file_server_res = file_server.start(); if (file_server_res < 0) { throw std::runtime_error("Failed to start the file server: " + std::to_string(file_server_res)); } ``` -------------------------------- ### C++ Initializing UAVCAN Distributed Server Dependencies Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/10 Illustrates the setup of the file-based event tracer and storage backend, which are essential components for the UAVCAN distributed dynamic node ID server. Includes robust error handling for initialization failures. ```C++ /* * Initializing the event tracer - refer to the Centralized Allocator example for details. */ uavcan_posix::dynamic_node_id_server::FileEventTracer event_tracer; const int event_tracer_res = event_tracer.init("uavcan_db_distributed/event.log"); // Using a hard-coded path here. if (event_tracer_res < 0) { throw std::runtime_error("Failed to start the event tracer; error: " + std::to_string(event_tracer_res)); } /* * Initializing the storage backend - refer to the Centralized Allocator example for details. */ uavcan_posix::dynamic_node_id_server::FileStorageBackend storage_backend; const int storage_res = storage_backend.init("uavcan_db_distributed"); // Using a hard-coded path here. if (storage_res < 0) { throw std::runtime_error("Failed to start the storage backend; error: " + std::to_string(storage_res)); } ``` -------------------------------- ### Initialize and Start DroneCAN FirmwareUpdateTrigger in C++ Source: https://dronecan.github.io/Implementations/Libuavcan/Tutorials/11 This snippet sets up the `uavcan::FirmwareUpdateTrigger` which monitors node information and initiates firmware updates for nodes requiring them. It uses a custom `ExampleFirmwareVersionChecker` for application-specific logic and includes error handling for its startup. ```C++ ExampleFirmwareVersionChecker checker; uavcan::FirmwareUpdateTrigger trigger(node, checker); const int trigger_res = trigger.start(node_info_retriever); if (trigger_res < 0) { throw std::runtime_error("Failed to start the firmware update trigger: " + std::to_string(trigger_res)); } ``` -------------------------------- ### Initialize Linux SocketCAN Interface Source: https://dronecan.github.io/Examples/Simple_sensor_node This function initializes a SocketCAN interface on Linux. It opens a raw CAN socket, resolves the interface index using `SIOCGIFINDEX`, and binds the socket to the specified CAN interface name. Returns 0 on success, -1 on failure. ```C int can_init(const char* can_iface_name) { // Open the SocketCAN socket const int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (sock < 0) { return -1; } // Resolve the iface index struct ifreq ifr; (void)memset(&ifr, 0, sizeof(ifr)); (void)strncpy(ifr.ifr_name, can_iface_name, IFNAMSIZ); const int ioctl_result = ioctl(sock, SIOCGIFINDEX, &ifr); if (ioctl_result < 0) { return -1; } // Assign the iface struct sockaddr_can addr; (void)memset(&addr, 0, sizeof(addr)); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; // Bind the socket to the iface const int bind_result = bind(sock, (struct sockaddr*)&addr, sizeof(addr)); if (bind_result < 0) { return -1; } can_socket = sock; return 0; } ```