diff --git a/api/CanMsg.h b/api/CanMsg.h index a8e32e5b..cc24d859 100644 --- a/api/CanMsg.h +++ b/api/CanMsg.h @@ -48,12 +48,13 @@ class CanMsg : public Printable static uint8_t constexpr MAX_DATA_LENGTH = 8; static uint32_t constexpr CAN_EFF_FLAG = 0x80000000U; + static uint32_t constexpr CAN_RTR_FLAG = 0x40000000U; /* remote transmission request (RTR) */ static uint32_t constexpr CAN_SFF_MASK = 0x000007FFU; /* standard frame format (SFF) */ static uint32_t constexpr CAN_EFF_MASK = 0x1FFFFFFFU; /* extended frame format (EFF) */ - CanMsg(uint32_t const can_id, uint8_t const can_data_len, uint8_t const * can_data_ptr) - : id{can_id} + CanMsg(uint32_t const can_id, uint8_t const can_data_len, uint8_t const * can_data_ptr, bool const rtr = false) + : id{can_id | (rtr ? CAN_RTR_FLAG : 0)} , data_length{min(can_data_len, MAX_DATA_LENGTH)} , data{0} { @@ -87,14 +88,14 @@ class CanMsg : public Printable virtual size_t printTo(Print & p) const override { - char buf[20] = {0}; + char buf[24] = {0}; size_t len = 0; /* Print the header. */ if (isStandardId()) - len = snprintf(buf, sizeof(buf), "[%03" PRIX32 "] (%d) : ", getStandardId(), data_length); + len = snprintf(buf, sizeof(buf), "[%03" PRIX32 "%s] (%d) : ", getStandardId(), isRtr() ? " RTR" : "", data_length); else - len = snprintf(buf, sizeof(buf), "[%08" PRIX32 "] (%d) : ", getExtendedId(), data_length); + len = snprintf(buf, sizeof(buf), "[%08" PRIX32 "%s] (%d) : ", getExtendedId(), isRtr() ? " RTR" : "", data_length); size_t n = p.write(buf, len); /* Print the data. */ @@ -121,12 +122,18 @@ class CanMsg : public Printable bool isExtendedId() const { return ((id & CAN_EFF_FLAG) == CAN_EFF_FLAG); } + bool isRtr() const { + return ((id & CAN_RTR_FLAG) == CAN_RTR_FLAG); + } + bool isRemote() const { + return isRtr(); + } /* * CAN ID semantics (mirroring definition by linux/can.h): * |- Bit 31 : frame format flag (0 = standard 11 bit, 1 = extended 29 bit) - * |- Bit 30 : reserved (future remote transmission request flag) + * |- Bit 30 : remote transmission request flag (1 = RTR frame) * |- Bit 29 : reserved (future error frame flag) * |- Bit 0-28 : CAN identifier (11/29 bit) */ @@ -147,6 +154,14 @@ inline uint32_t CanExtendedId(uint32_t const id) { return (CanMsg::CAN_EFF_FLAG | (id & CanMsg::CAN_EFF_MASK)); } +inline uint32_t CanStandardRtrId(uint32_t const id) { + return (CanMsg::CAN_RTR_FLAG | (id & CanMsg::CAN_SFF_MASK)); +} + +inline uint32_t CanExtendedRtrId(uint32_t const id) { + return (CanMsg::CAN_EFF_FLAG | CanMsg::CAN_RTR_FLAG | (id & CanMsg::CAN_EFF_MASK)); +} + /************************************************************************************** * NAMESPACE **************************************************************************************/ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d8050249..31774a49 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -42,6 +42,7 @@ set(TEST_SRCS src/CanMsg/test_CanStandardId.cpp src/CanMsg/test_isExtendedId.cpp src/CanMsg/test_isStandardId.cpp + src/CanMsg/test_isRtr.cpp src/CanMsg/test_operator_assignment.cpp src/CanMsg/test_printTo.cpp src/CanMsgRingbuffer/test_available.cpp diff --git a/test/src/CanMsg/test_isRtr.cpp b/test/src/CanMsg/test_isRtr.cpp new file mode 100644 index 00000000..7165631d --- /dev/null +++ b/test/src/CanMsg/test_isRtr.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2026 Arduino. All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +/************************************************************************************** + * INCLUDE + **************************************************************************************/ + +#include + +#include + +/************************************************************************************** + * NAMESPACE + **************************************************************************************/ + +using namespace arduino; + +/************************************************************************************** + * TEST CODE + **************************************************************************************/ + +TEST_CASE ("\"isRtr\" should return true for standard RTR CAN ID", "[CanMsg-isRtr-01]") +{ + CanMsg const msg_std_rtr(CanStandardRtrId(0x020), 0, nullptr); + REQUIRE(msg_std_rtr.isRtr() == true); + REQUIRE(msg_std_rtr.isRemote() == true); + REQUIRE(msg_std_rtr.isStandardId() == true); + REQUIRE(msg_std_rtr.isExtendedId() == false); +} + +TEST_CASE ("\"isRtr\" should return true for extended RTR CAN ID", "[CanMsg-isRtr-02]") +{ + CanMsg const msg_ext_rtr(CanExtendedRtrId(0x020), 0, nullptr); + REQUIRE(msg_ext_rtr.isRtr() == true); + REQUIRE(msg_ext_rtr.isRemote() == true); + REQUIRE(msg_ext_rtr.isStandardId() == false); + REQUIRE(msg_ext_rtr.isExtendedId() == true); +} + +TEST_CASE ("\"isRtr\" should return true when created with rtr constructor flag", "[CanMsg-isRtr-03]") +{ + CanMsg const msg_std_rtr_flag(CanStandardId(0x020), 0, nullptr, true); + REQUIRE(msg_std_rtr_flag.isRtr() == true); + REQUIRE(msg_std_rtr_flag.isRemote() == true); +} + +TEST_CASE ("\"isRtr\" should return false for normal data frames", "[CanMsg-isRtr-04]") +{ + CanMsg const msg_std(CanStandardId(0x020), 0, nullptr); + REQUIRE(msg_std.isRtr() == false); + REQUIRE(msg_std.isRemote() == false); + + CanMsg const msg_ext(CanExtendedId(0x020), 0, nullptr); + REQUIRE(msg_ext.isRtr() == false); + REQUIRE(msg_ext.isRemote() == false); +} diff --git a/test/src/CanMsg/test_printTo.cpp b/test/src/CanMsg/test_printTo.cpp index a434bdf4..9c3a77fe 100644 --- a/test/src/CanMsg/test_printTo.cpp +++ b/test/src/CanMsg/test_printTo.cpp @@ -44,3 +44,23 @@ TEST_CASE ("Print CAN frame with extended ID", "[CanMsg-printTo-2]") REQUIRE(mock._str == "[00000020] (4) : DEADC0DE"); } + +TEST_CASE ("Print CAN frame with standard RTR ID", "[CanMsg-printTo-3]") +{ + CanMsg const std_rtr_msg(CanStandardRtrId(0x20), 0, nullptr); + + PrintMock mock; + mock.print(std_rtr_msg); + + REQUIRE(mock._str == "[020 RTR] (0) : "); +} + +TEST_CASE ("Print CAN frame with extended RTR ID", "[CanMsg-printTo-4]") +{ + CanMsg const ext_rtr_msg(CanExtendedRtrId(0x20), 0, nullptr); + + PrintMock mock; + mock.print(ext_rtr_msg); + + REQUIRE(mock._str == "[00000020 RTR] (0) : "); +}