Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@ managed_components/
# docker interop harness build tree (bind-mounted, built in-container)
pc/build-linux/
python/env/

# Locally generated documentation output (Doxygen/Sphinx build of doc/ -> docs/).
docs/

# Local example-build helper script (not part of the repo).
build_examples.sh
31 changes: 31 additions & 0 deletions components/rtps_embedded/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,34 @@ idf_component_register(
base_component cdr task thread_pool socket
)

# Select the RTPS static-limits profile from Kconfig (see Kconfig in this
# component). The "embedded" profile is the default and is byte-identical to the
# historical behavior: it defines no RTPS_CONFIG_HEADER, so config.hpp selects
# rtps/config_esp32.hpp via ESP_PLATFORM. The relaxed "host" / "host_large"
# profiles override the profile header. These are capacity-only caps and do not
# change any bytes on the wire.
if(CONFIG_RTPS_LIMITS_PROFILE_HOST)
target_compile_definitions(${COMPONENT_LIB} PUBLIC RTPS_CONFIG_HEADER="rtps/config_desktop.hpp")
elseif(CONFIG_RTPS_LIMITS_PROFILE_HOST_LARGE)
target_compile_definitions(${COMPONENT_LIB} PUBLIC RTPS_CONFIG_HEADER="rtps/config_host_large.hpp")
Comment thread
finger563 marked this conversation as resolved.
endif()

# Storage policy is orthogonal to the limits profile above: dynamic (heap,
# grow-on-full) storage is an explicit ESP opt-in (default static) so a relaxed
# limits profile never silently switches the MCU to heap-backed history. The
# limits headers no longer define RTPS_STORAGE_DYNAMIC themselves; it is set here
# on ESP and defaulted on in config.hpp for host/PC builds.
if(CONFIG_RTPS_STORAGE_DYNAMIC)
target_compile_definitions(${COMPONENT_LIB} PUBLIC RTPS_STORAGE_DYNAMIC)
endif()

# Best-effort DATA_FRAG fragmentation is opt-in on ESP targets (Kconfig, default
# off) so the MCU pays nothing for it by default. When enabled, define
# RTPS_ENABLE_FRAGMENTATION (compiles the fragment send/reassembly paths) and the
# reassembly cap RTPS_MAX_SAMPLE_SIZE (256 KB on the embedded profile). The
# facade's max payload size rises to this when fragmentation is enabled.
if(CONFIG_RTPS_ENABLE_FRAGMENTATION)
target_compile_definitions(${COMPONENT_LIB} PUBLIC
RTPS_ENABLE_FRAGMENTATION RTPS_MAX_SAMPLE_SIZE=262144)
endif()

60 changes: 60 additions & 0 deletions components/rtps_embedded/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
menu "RTPS (rtps_embedded)"

choice RTPS_LIMITS_PROFILE
prompt "RTPS static limits profile"
default RTPS_LIMITS_PROFILE_EMBEDDED
help
Selects the compile-time capacity limits (profile header) used by the
rtps_embedded engine. Only the capacity caps differ between profiles.
The storage MODEL (static vs dynamic) is a separate knob
(RTPS_STORAGE_DYNAMIC below) and defaults to fully-static on ESP for
every profile - selecting a relaxed profile does NOT enable heap
storage. These caps are pure capacity limits and do NOT change any
bytes on the wire.

config RTPS_LIMITS_PROFILE_EMBEDDED
bool "embedded (tight MCU caps)"
help
Tight caps sized for an ESP32-class MCU
(rtps/config_esp32.hpp). This is the default and the smallest
footprint.

config RTPS_LIMITS_PROFILE_HOST
bool "host (relaxed static caps)"
help
Relaxed static caps suitable for small-to-medium DDS graphs
(rtps/config_desktop.hpp). Larger footprint than embedded.

config RTPS_LIMITS_PROFILE_HOST_LARGE
bool "host_large (generous static caps)"
help
Generous static caps for large DDS graphs
(rtps/config_host_large.hpp). Significantly larger footprint;
only appropriate on targets with plenty of RAM.
endchoice

config RTPS_STORAGE_DYNAMIC
bool "Use dynamic (heap) history/queue storage"
default n
help
By default the engine uses fully-static, zero-heap history and queue
storage on ESP targets (std::array), independent of the limits
profile above: when a history fills it drops the oldest sample.
Enable this to use heap-backed storage (std::deque) that instead
GROWS on demand to retain samples. Off by default so the MCU keeps
deterministic, zero-heap storage; enable only on targets that can
afford heap growth. Does not change any bytes on the wire.

config RTPS_ENABLE_FRAGMENTATION
bool "Enable best-effort DATA_FRAG fragmentation"
default n
help
Enables sending and receiving samples larger than a single RTPS DATA
submessage (> ~64 KB) by splitting them into best-effort DATA_FRAG
submessages and reassembling them on receive (interoperates with
FastDDS / ROS 2). Default OFF so the MCU pays no code or memory cost
for it in the common (small-sample) case. When ON, reassembly is
bounded by RTPS_MAX_SAMPLE_SIZE (256 KB on the embedded profile) and
the participant's max payload size rises accordingly.

endmenu
6 changes: 5 additions & 1 deletion components/rtps_embedded/include/rtps/common/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ namespace rtps {
// TODO move types to where they are needed!

typedef uint16_t Ip4Port_t;
typedef uint16_t DataSize_t;
// Internal whole-sample / payload size type. Widened to 32-bit so a single
// sample can exceed 64 KB internally (prerequisite for DATA_FRAG). This is an
// INTERNAL type only: on-the-wire per-submessage length fields (e.g.
// SubmessageHeader::octetsToNextHeader) stay 16-bit per the RTPS spec.
typedef uint32_t DataSize_t;
typedef int8_t ParticipantId_t; // With UDP only 120 possible

enum class EntityKind_t : uint8_t {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/*
The MIT License
Copyright (c) 2019 Lehrstuhl Informatik 11 - RWTH Aachen University
Modifications Copyright (c) 2026 ATDev
Copyright (c) 2026 ATDev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
Expand All @@ -18,9 +17,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE

This file is part of embeddedRTPS.

Author: i11 - Embedded Software, RWTH Aachen University
This file is part of the espp embeddedRTPS port.
*/

#ifndef RTPS_ESPPTRANSPORT_H
Expand Down
12 changes: 12 additions & 0 deletions components/rtps_embedded/include/rtps/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,16 @@ Author: i11 - Embedded Software, RWTH Aachen University
#endif
#endif

// Storage POLICY, orthogonal to the limits profile selected above. Dynamic
// (heap-backed, grow-on-full std::deque) storage is the default on host/PC
// builds; on ESP it is an explicit opt-in (Kconfig RTPS_STORAGE_DYNAMIC ->
// -DRTPS_STORAGE_DYNAMIC), so the MCU keeps zero-heap, deterministic history by
// default no matter which limits profile is chosen. The limits headers set
// capacity caps only and never enable dynamic storage by themselves. Define
// RTPS_STORAGE_STATIC to force static storage on a host build. Neither policy
// changes any bytes on the wire.
#if !defined(RTPS_STORAGE_DYNAMIC) && !defined(RTPS_STORAGE_STATIC) && !defined(ESP_PLATFORM)
#define RTPS_STORAGE_DYNAMIC
#endif

#endif // RTPS_CONFIG_H
75 changes: 53 additions & 22 deletions components/rtps_embedded/include/rtps/config_desktop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ namespace rtps {

#define IS_LITTLE_ENDIAN 1

// NOTE: this header sets capacity LIMITS only. The storage POLICY (static
// std::array vs heap-backed growable std::deque) is orthogonal and controlled
// centrally by RTPS_STORAGE_DYNAMIC, selected in config.hpp: dynamic by default
// on host/PC builds, explicit opt-in on ESP (Kconfig, default static). Selecting
// this profile therefore does NOT by itself switch storage to dynamic - which
// matters on ESP, where a relaxed limits profile must not silently enable heap
// storage. See storages/StorageArray.hpp. Capacity only - never touches wire bytes.

namespace Config {
const VendorId_t VENDOR_ID = {13, 37};
const std::array<uint8_t, 4> IP_ADDRESS = {192, 168, 4, 1}; // Needs to be set in lwipcfg.h too.
Expand All @@ -40,26 +48,49 @@ const std::array<uint8_t, 4> IP_ADDRESS = {192, 168, 4, 1}; // Needs to be set i
// participant share an identity, which breaks discovery between them.
const GuidPrefix_t BASE_GUID_PREFIX = GUID_RANDOM;

// ---------------------------------------------------------------------------
// "host" limits profile (DEFAULT for non-ESP builds).
//
// RELAXED, fully-static capacity caps suitable for a compute host (laptop /
// Jetson / server) out-of-the-box. This is NOT a tiny profile: it is sized for
// real (small-to-medium) DDS graphs while keeping the deterministic,
// compile-time-static allocation model. For very large graphs select the
// "host_large" profile (config_host_large.hpp) via RTPS_LIMITS_PROFILE.
//
// NOTE: these are pure capacity caps and do NOT affect any bytes on the wire.
// ---------------------------------------------------------------------------
const uint8_t DOMAIN_ID = 0; // 230 possible with UDP
const uint8_t MAX_NUM_PARTICIPANTS = 2;
const uint8_t NUM_STATELESS_WRITERS = MAX_NUM_PARTICIPANTS + 1; // Required + Additional
const uint8_t NUM_STATELESS_READERS = MAX_NUM_PARTICIPANTS + 1; // Required + Additional
const uint8_t NUM_STATEFUL_READERS = 4; // 1-4 required per participant depending on what they do
// and to whom they match
const uint8_t NUM_STATEFUL_WRITERS = 4; // 1-4 required per participant depending on what they do
// and to whom they match
const uint8_t NUM_WRITERS_PER_PARTICIPANT = 4;
const uint8_t NUM_READERS_PER_PARTICIPANT = 4;
const uint8_t NUM_WRITER_PROXIES_PER_READER = 3;
const uint8_t NUM_READER_PROXIES_PER_WRITER = 3;

const uint8_t MAX_NUM_UNMATCHED_REMOTE_WRITERS = 100;
const uint8_t MAX_NUM_UNMATCHED_REMOTE_READERS = 10;

const uint8_t MAX_NUM_READER_CALLBACKS = 5;

// Reassembly / large-sample cap (bytes). A sample larger than this is refused on
// publish and any partial reassembly exceeding it is dropped. On host this is the
// growable (Slice B deque) upper bound. Kept in sync with the facade's
// max_payload_size via the RTPS_MAX_SAMPLE_SIZE build macro when fragmentation is
// enabled. Capacity only - never touches wire bytes.
#ifndef RTPS_MAX_SAMPLE_SIZE
#define RTPS_MAX_SAMPLE_SIZE (8u * 1024u * 1024u) // 8 MB
#endif
const DataSize_t MAX_SAMPLE_SIZE = RTPS_MAX_SAMPLE_SIZE;

const uint8_t MAX_NUM_PARTICIPANTS = 8;
const uint8_t NUM_STATELESS_WRITERS = 16;
const uint8_t NUM_STATELESS_READERS = 16;
const uint8_t NUM_STATEFUL_READERS = 32;
const uint8_t NUM_STATEFUL_WRITERS = 32;
const uint8_t NUM_WRITERS_PER_PARTICIPANT = 16;
const uint8_t NUM_READERS_PER_PARTICIPANT = 16;
const uint8_t NUM_WRITER_PROXIES_PER_READER = 8;
const uint8_t NUM_READER_PROXIES_PER_WRITER = 8;

// uint16_t (not uint8_t): these bound SEDP MemoryPool<> sizes and the host
// value (256) already exceeds the 255 uint8_t range; host_large goes higher
// still. MemoryPool<TYPE, uint32_t SIZE> widens the value, so uint16_t is safe.
const uint16_t MAX_NUM_UNMATCHED_REMOTE_WRITERS = 256;
const uint16_t MAX_NUM_UNMATCHED_REMOTE_READERS = 128;

const uint8_t MAX_NUM_READER_CALLBACKS = 8;

const uint8_t HISTORY_SIZE_STATELESS = 2;
const uint8_t HISTORY_SIZE_STATEFUL = 10;
const uint8_t HISTORY_SIZE_STATEFUL = 16;

const uint8_t MAX_TYPENAME_LENGTH = 64;
const uint8_t MAX_TOPICNAME_LENGTH = 64;
Expand All @@ -73,22 +104,22 @@ const uint16_t SF_WRITER_HB_PERIOD_MS = 2000;
const uint16_t SPDP_RESEND_PERIOD_MS = 1000;
const uint8_t SPDP_CYCLECOUNT_HEARTBEAT = 2; // skip x SPDP rounds before checking liveliness
const uint8_t SPDP_WRITER_PRIO = 3;
const uint8_t SPDP_MAX_NUMBER_FOUND_PARTICIPANTS = 5;
const uint8_t SPDP_MAX_NUM_LOCATORS = 5;
const uint8_t SPDP_MAX_NUMBER_FOUND_PARTICIPANTS = 32;
const uint8_t SPDP_MAX_NUM_LOCATORS = 8;
const Duration_t SPDP_DEFAULT_REMOTE_LEASE_DURATION = {
100, 0}; // Default lease duration for remote participants, usually
// overwritten by remote info
const Duration_t SPDP_MAX_REMOTE_LEASE_DURATION = {
180, 0}; // Absolute maximum lease duration, ignoring remote participant info

const int MAX_NUM_UDP_CONNECTIONS = 10;
const int MAX_NUM_UDP_CONNECTIONS = 16;

const int THREAD_POOL_NUM_WRITERS = 2;
const int THREAD_POOL_NUM_READERS = 2;
const int THREAD_POOL_WRITER_PRIO = 3;
const int THREAD_POOL_READER_PRIO = 3;
const int THREAD_POOL_WORKLOAD_QUEUE_LENGTH_USERTRAFFIC = 10;
const int THREAD_POOL_WORKLOAD_QUEUE_LENGTH_METATRAFFIC = 10;
const int THREAD_POOL_WORKLOAD_QUEUE_LENGTH_USERTRAFFIC = 32;
const int THREAD_POOL_WORKLOAD_QUEUE_LENGTH_METATRAFFIC = 32;

constexpr int OVERALL_HEAP_SIZE = THREAD_POOL_NUM_WRITERS * THREAD_POOL_WRITER_STACKSIZE +
THREAD_POOL_NUM_READERS * THREAD_POOL_READER_STACKSIZE +
Expand Down
9 changes: 9 additions & 0 deletions components/rtps_embedded/include/rtps/config_esp32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ const std::array<uint8_t, 4> IP_ADDRESS = {192, 168, 4,
const GuidPrefix_t BASE_GUID_PREFIX{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13};

const uint8_t DOMAIN_ID = 0; // 230 possible with UDP

// Reassembly / large-sample cap (bytes) when RTPS_ENABLE_FRAGMENTATION is opted
// in on this MCU (Kconfig, default off). Bounded (256 KB) - a fragmented sample
// larger than this is refused/dropped. Unused when fragmentation is off.
#ifndef RTPS_MAX_SAMPLE_SIZE
#define RTPS_MAX_SAMPLE_SIZE (256u * 1024u) // 256 KB
#endif
const DataSize_t MAX_SAMPLE_SIZE = RTPS_MAX_SAMPLE_SIZE;

const uint8_t NUM_STATELESS_WRITERS = 5;
const uint8_t NUM_STATELESS_READERS = 5;
const uint8_t NUM_STATEFUL_READERS = 5;
Expand Down
Loading