• Specification
    • 1. Introduction
    • 2. Basic concepts
    • 3. Data structure description language
    • 4. CAN bus transport layer
    • 5. Application level conventions
    • 6. Application level functions
    • 7. List of standard data types
    • 8. Hardware design recommendations
  • 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
    • Pyuavcan
      • Examples
        • Automated ESC enumeration
        • ESC throttle control
      • Tutorials
        • 1. Setup
        • 2. Basic usage
        • 3. Advanced usage
        • 4. Parsing DSDL definitions
    • Libcanard
    • Uavcan.rs
  • GUI Tool
    • Overview
    • Examples
    • User guide
  • Examples
    • Simple sensor node
  • Forum
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)

Obsolete

This website is dedicated to the experimental version of the protocol known as UAVCAN v0 that is now obsolete. To learn more about the stable release UAVCAN v1.0, please visit the main website.

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.
  • Discussion forum
  • GitHub organization
  • Report a problem with this website

Generated Thu, 17 Feb 2022 16:27:38 +0000 © UAVCAN development team