• Specification
    • 1. Introduction
    • 2. Basic concepts
    • 3. Data structure description language
    • 4.1 CAN bus transport layer
    • 4.2 UDP bus transport layer
    • 4.3 MAVLink bus transport layer
    • 4.4 CANFD bus transport layer
    • 5. Application level conventions
    • 6. Application level functions
    • 7. List of standard data types
    • 8. Hardware design recommendations
    • dsdl
      • ardupilot
      • uavcan
  • Implementations
    • Libuavcan
      • Platforms
      • Tutorials
        • 1. Library build configuration
        • 2. Node initialization and startup
        • 3. Publishers and subscribers
        • 4. Services
        • 5. Timers
        • 6. Time synchronization
        • 7. Remote node reconfiguration
        • 8. Custom data types
        • 9. Node discovery
        • 10. Dynamic node ID allocation
        • 11. Firmware update
        • 12. Multithreading
        • 13. CAN acceptance filters
      • FAQ
    • Pydronecan
      • Examples
        • Automated ESC enumeration
        • Dump All Messages
        • ESC throttle control
      • Tutorials
        • 1. Setup
        • 2. Basic usage
        • 3. Advanced usage
        • 4. Parsing DSDL definitions
    • AP Periph
    • Libcanard
    • dronecan dsdlc
  • GUI Tool
    • Overview
    • Examples
    • User guide
  • Examples
    • Simple sensor node
  • Discussions
Implementations /  Libuavcan /  Tutorials /  5. Timers

Timers

This application demonstrates how to use software timers.

The code

C++11

This is the recommended example. It requires C++11 or a newer version of the C++ standard.

#include <iostream>
#include <unistd.h>
#include <uavcan/uavcan.hpp>

extern uavcan::ICanDriver& getCanDriver();
extern uavcan::ISystemClock& getSystemClock();

constexpr unsigned NodeMemoryPoolSize = 16384;
typedef uavcan::Node<NodeMemoryPoolSize> 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] << " <node-id>" << 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;
        }
    }
}

C++03

This example shows how to work-around the lack of lambdas and std::function<> in C++03.

#include <iostream>
#include <sstream>
#include <unistd.h>
#include <uavcan/uavcan.hpp>
#include <uavcan/util/method_binder.hpp>    // Sic!

extern uavcan::ICanDriver& getCanDriver();
extern uavcan::ISystemClock& getSystemClock();

/**
 * This class demonstrates how to use uavcan::MethodBinder and uavcan::TimerEventForwarder 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 NodeWithTimers
{
    static const unsigned NodeMemoryPoolSize = 16384;

    uavcan::Node<NodeMemoryPoolSize> node_;

    // Instantiation of uavcan::MethodBinder
    typedef uavcan::MethodBinder<NodeWithTimers*, void (NodeWithTimers::*)(const uavcan::TimerEvent&)>
        TimerCallbackBinder;

    // Now we can use the newly instantiated timer type
    uavcan::TimerEventForwarder<TimerCallbackBinder> periodic_timer_;
    uavcan::TimerEventForwarder<TimerCallbackBinder> one_shot_timer_;

    void periodicCallback(const uavcan::TimerEvent& event)
    {
        std::cout << "Periodic: scheduled_time: " << event.scheduled_time
                  << ", real_time: " << event.real_time << std::endl;

        uavcan::MonotonicTime one_shot_deadline = event.scheduled_time + uavcan::MonotonicDuration::fromMSec(200);
        one_shot_timer_.startOneShotWithDeadline(one_shot_deadline);
    }

    void oneShotCallback(const uavcan::TimerEvent& event)
    {
        std::cout << "One-shot: scheduled_time: " << event.scheduled_time
                  << ", real_time: " << event.real_time << std::endl;
    }

public:
    NodeWithTimers() :
        node_(getCanDriver(), getSystemClock()),
        periodic_timer_(node_, TimerCallbackBinder(this, &NodeWithTimers::periodicCallback)),
        one_shot_timer_(node_, TimerCallbackBinder(this, &NodeWithTimers::oneShotCallback))
    {
        node_.getLogger().setLevel(uavcan::protocol::debug::LogLevel::DEBUG);
    }

    int start(uavcan::NodeID self_node_id, const std::string& node_name)
    {
        node_.setNodeID(self_node_id);
        node_.setName(node_name.c_str());
        return node_.start();
    }

    void runForever()
    {
        periodic_timer_.startPeriodic(uavcan::MonotonicDuration::fromMSec(1000));   // Start cannot fail

        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] << " <node-id>" << std::endl;
        return 1;
    }

    NodeWithTimers node;

    const int node_start_res = node.start(std::atoi(argv[1]), "org.uavcan.tutorial.timers_cpp03");
    if (node_start_res < 0)
    {
        std::ostringstream os;
        os << "Failed to start the node; error: " << node_start_res;
        throw std::runtime_error(os.str());
    }

    node.runForever();
}

Running on Linux

Both versions of the application can be built with the following CMake script:

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)


Libuavcan

  • Platforms
  • Tutorials
    • 1. Library build configuration
    • 2. Node initialization and startup
    • 3. Publishers and subscribers
    • 4. Services
    • 5. Timers
    • 6. Time synchronization
    • 7. Remote node reconfiguration
    • 8. Custom data types
    • 9. Node discovery
    • 10. Dynamic node ID allocation
    • 11. Firmware update
    • 12. Multithreading
    • 13. CAN acceptance filters
  • FAQ

License

This work is licensed under a Creative Commons Attribution 4.0 International License.
Much of the content of this site is based upon prior work by Pavel Kirienko and the UAVCAN Development Team.
  • https://dronecan.org/discord
  • https://github.com/DroneCAN
  • Report a problem with this website

Generated Sun, 02 Jun 2024 21:47:21 +0000 © DroneCAN development team